apple: macOS (tvOS, visionOS) versions are no longer invisible: every version and review submission lookup is per platform; status, listing, listing pull, resubmit, submit, release and screenshots take --platform and otherwise pick the platform that has the version they need (refuse naming both when several do); status, validate, release status, the report and the desktop label versions with their platform; MCP and owner actions get platform; pitfall 77
This commit is contained in:
parent
1736300784
commit
47dfd372fe
30 changed files with 423 additions and 127 deletions
61
cli/apple/platform.ts
Normal file
61
cli/apple/platform.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// App Store platforms of one app record. An app can have an iOS and a macOS (tvOS, visionOS) version at the same time,
|
||||
// each with its own state and its own review submission (pitfall 77): every version lookup filters by platform, and a
|
||||
// command that needs one version picks the platform that has it, or asks for --platform when several do.
|
||||
import type { Asc, J } from './asc';
|
||||
|
||||
export type AscPlatform = 'IOS' | 'MAC_OS' | 'TV_OS' | 'VISION_OS';
|
||||
export const ASC_PLATFORMS: AscPlatform[] = ['IOS', 'MAC_OS', 'TV_OS', 'VISION_OS'];
|
||||
const FLAG: Record<string, AscPlatform> = { ios: 'IOS', macos: 'MAC_OS', tvos: 'TV_OS', visionos: 'VISION_OS' };
|
||||
const LABEL: Record<AscPlatform, string> = { IOS: 'iOS', MAC_OS: 'macOS', TV_OS: 'tvOS', VISION_OS: 'visionOS' };
|
||||
/** The console's URL segment of a platform's version page. */
|
||||
export const CONSOLE_SEGMENT: Record<AscPlatform, string> = { IOS: 'ios', MAC_OS: 'macos', TV_OS: 'appletvos', VISION_OS: 'visionos' };
|
||||
|
||||
/** --platform ios|macos|tvos|visionos (also the API names IOS, MAC_OS, ...) -> the API's platform; undefined when not
|
||||
* given; throws on anything else. Pure. */
|
||||
export function parsePlatform(v: string | undefined): AscPlatform | undefined {
|
||||
if (v == null || v === '') return undefined;
|
||||
const k = v.toLowerCase().replace(/[_\s-]/g, '');
|
||||
const p = FLAG[k] ?? FLAG[k.replace(/^appletv/, 'tv')];
|
||||
if (!p) throw new Error(`--platform: ios | macos | tvos | visionos (got "${v}")`);
|
||||
return p;
|
||||
}
|
||||
|
||||
/** "iOS", "macOS", ... for any platform string (unknown ones as they are). Pure. */
|
||||
export const platformLabel = (p: string | undefined | null): string => LABEL[(p ?? 'IOS') as AscPlatform] ?? String(p);
|
||||
|
||||
export const platformOf = (v: J): AscPlatform => (v?.attributes?.platform ?? 'IOS') as AscPlatform;
|
||||
export const stateOf = (v: J): string => v?.attributes?.appVersionState ?? v?.attributes?.appStoreState ?? '';
|
||||
/** "0.8.7 (macOS)": a version as people read it. */
|
||||
export const versionLabel = (v: J): string => `${v?.attributes?.versionString ?? '?'} (${platformLabel(platformOf(v))})`;
|
||||
|
||||
/** The appStoreVersions list path of one platform. */
|
||||
export const versionsPath = (appId: string, platform: AscPlatform, limit = 50): string =>
|
||||
`/v1/apps/${appId}/appStoreVersions?filter[platform]=${platform}&limit=${limit}`;
|
||||
|
||||
/**
|
||||
* The platform a command works on. `flag` (--platform) wins. Else the platforms that have a version `needs` accepts:
|
||||
* one -> that one; several -> an error naming them all (ask for --platform); none -> the only platform the app has
|
||||
* versions on, else iOS. Pure.
|
||||
*/
|
||||
export function pickPlatform(versions: J[], needs: ((v: J) => boolean) | ((v: J) => boolean)[], flag: AscPlatform | undefined, what: string): AscPlatform {
|
||||
if (flag) return flag;
|
||||
// several tests: the first one any version passes decides (e.g. "this version string, editable", then "editable")
|
||||
if (Array.isArray(needs)) {
|
||||
for (const n of needs) if (versions.some(n)) return pickPlatform(versions, n, undefined, what);
|
||||
return pickPlatform(versions, () => false, undefined, what);
|
||||
}
|
||||
const hits = [...new Set(versions.filter(needs).map(platformOf))];
|
||||
if (hits.length === 1) return hits[0]!;
|
||||
if (hits.length > 1) {
|
||||
const which = versions.filter(needs).map(v => `${versionLabel(v)} ${stateOf(v)}`).join(', ');
|
||||
throw new Error(`${what} on more than one platform (${which}): run it again with --platform ${hits.map(p => Object.keys(FLAG).find(k => FLAG[k] === p)).join(' or --platform ')}`);
|
||||
}
|
||||
const all = [...new Set(versions.map(platformOf))];
|
||||
return all.length === 1 ? all[0]! : 'IOS';
|
||||
}
|
||||
|
||||
/** Every App Store version of the app on every platform (only to pick the platform; the command then reads its
|
||||
* platform's list with filter[platform]). */
|
||||
export async function allVersions(c: Asc, appId: string): Promise<J[]> {
|
||||
return c.all(`/v1/apps/${appId}/appStoreVersions?filter[platform]=${ASC_PLATFORMS.join(',')}&limit=200`);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue