// Play managed publishing: what waits in Publishing overview as "Changes ready to publish" (button "Publish N changes"). // The Developer API has no publishing-state endpoint; `play status` decides it the same way portfolio.playReleases does: // a completed production release that the public store page does not show yet (404, or an older version) while // play.managedPublishing is on is "In review / ready to publish". Pure. Non-track changes (Data safety, listing, content // rating) are listed by the console page only; the API cannot see them. import { playReleases } from './portfolio'; import { consoleUrl } from './console-links'; export const PUBLISH_PLAN_HINT = 'storebound console plan play-publish-changes'; /** The owner's final button: "Publish N changes" (N is the page's count) and its confirm dialog. */ export const PUBLISH_BUTTON = 'Publish N changes'; export const PUBLISH_CONFIRM = 'Publish'; export interface ReadyChange { track: string; version: string | null; build: string | null; rollout: string; liveVersion: string | null } /** * Ready only when the public store page POSITIVELY shows an older version than the completed production release * (both parsed and compared; playReleases sets liveVersion only then) and the page read is fresh. A 404, a failed or * stale read, "Varies with device", a same or newer version, or an unparseable version is never "ready": a release * already published (managed publishing, "Last published on ...") must not be reported as waiting. */ const isReady = (r: ReturnType[number]): boolean => r.track === 'production' && r.managed === true && r.phase === 'review' && !!r.liveVersion && !r.pageStaleAt && !!r.version; /** True when a completed managed production release exists but the store page cannot tell whether it is published. */ export function readyUndetermined(data: unknown): boolean { const rs = playReleases(data); return (data as { managedPublishing?: unknown })?.managedPublishing === true && rs.some(r => r.track === 'production' && r.state === 'completed' && !isReady(r) && r.phase !== 'live'); } /** Ready-to-publish production releases from the `play status --json` data ({ tracks, publicPage, managedPublishing }). */ export function readyToPublish(data: unknown): ReadyChange[] { return playReleases(data).filter(isReady).map(r => ({ track: r.track ?? 'production', version: r.version, build: r.build, rollout: r.fraction !== null && r.fraction < 1 ? `${Math.round(r.fraction * 1000) / 10}% staged` : 'full rollout', liveVersion: r.liveVersion ?? null, })); } /** Status lines for `play status` / `status`: the items and the hint (empty when nothing is ready). */ export function readyLines(items: ReadyChange[]): string[] { if (!items.length) return []; return [` ready to publish (managed publishing): ${items.map(i => `${i.track} ${i.version ?? '?'} (${i.build ?? '?'}) ${i.rollout}${i.liveVersion ? `, live: ${i.liveVersion}` : ''}`).join(' | ')}; non-track changes (Data safety, listing) show only in the console`, ` next: ${PUBLISH_PLAN_HINT} (${consoleUrl('play.publishing')})`]; }