41 lines
2.8 KiB
TypeScript
41 lines
2.8 KiB
TypeScript
// App Review information as pure parts: the appStoreReviewDetails attributes from the config, the review contact and
|
|
// the demo account, plus the notes (review notes, sign-in instructions, where account deletion is). Apple: the notes
|
|
// field takes up to 4000 bytes; a demo account must keep working during the whole review (Guideline 2.1).
|
|
// https://developer.apple.com/help/app-store-connect/reference/app-information/platform-version-information
|
|
// https://developer.apple.com/documentation/appstoreconnectapi/appstorereviewdetail/attributes-data.dictionary
|
|
import type { ShipConfig } from './config';
|
|
import { type DemoAccount, maskSecret, maskUser } from './credentials';
|
|
|
|
export const NOTES_MAX_BYTES = 4000;
|
|
|
|
/** Notes for the reviewer: apple.info.reviewNotes, then reviewAccess.instructions, then where account deletion is. */
|
|
export function reviewNotes(config: Pick<ShipConfig, 'apple' | 'reviewAccess' | 'accounts'>): string {
|
|
const parts = [config.apple?.info?.reviewNotes?.trim() ?? ''];
|
|
const ins = config.reviewAccess?.instructions?.trim();
|
|
if (ins && !parts[0]!.includes(ins)) parts.push(`Sign-in: ${ins}`);
|
|
const del = config.accounts?.enabled ? config.accounts.inApp?.trim() : '';
|
|
if (del && !parts.join('\n').includes(del)) parts.push(`Account deletion: ${del}`);
|
|
return parts.filter(Boolean).join('\n\n');
|
|
}
|
|
|
|
export interface ReviewDetailPlan {
|
|
attributes: Record<string, string | boolean | null>;
|
|
/** Log line without secrets. */
|
|
summary: string;
|
|
}
|
|
|
|
/**
|
|
* Attributes for POST / PATCH appStoreReviewDetails. With reviewAccess.signInRequired the demo account is required
|
|
* (throws when it is missing, so a submission never goes out with "sign-in required" and no account); without it the
|
|
* demo fields are cleared.
|
|
*/
|
|
export function reviewDetailPlan(config: Pick<ShipConfig, 'apple' | 'reviewAccess' | 'accounts'>, contact: Record<string, string>, demo: DemoAccount | null): ReviewDetailPlan {
|
|
const notes = reviewNotes(config);
|
|
const bytes = new TextEncoder().encode(notes).length;
|
|
if (bytes > NOTES_MAX_BYTES) throw new Error(`review notes are ${bytes} bytes; App Store Connect takes ${NOTES_MAX_BYTES}`);
|
|
const required = !!config.reviewAccess?.signInRequired;
|
|
if (required && !demo) throw new Error('reviewAccess.signInRequired is true but there is no demo account: add "reviewDemoAccount": { "username", "password" } to .storebound/credentials.json (or set REVIEW_DEMO_USER / REVIEW_DEMO_PASSWORD); see the review-access skill');
|
|
const attributes = { ...contact, demoAccountRequired: required, demoAccountName: required ? demo!.username : null, demoAccountPassword: required ? demo!.password : null, notes };
|
|
const summary = `${required ? `demo account ${maskUser(demo!.username)} / ${maskSecret(demo!.password)}` : 'no demo account (no sign-in needed)'}, notes ${bytes} bytes`;
|
|
return { attributes, summary };
|
|
}
|