price changes of existing products need --allow-price-changes (play products, play/apple subscriptions, prices import): --yes and flow run --apply --yes confirm creates, first prices and other updates only; without the flag the run writes the rest, names each held change and exits 3 (declined), dry runs mark them owner, flows refuse the flag; lib/price-gate.ts, tests/price-gate.test.ts. CLI wording: console fill leaves what it cannot open or fill to the calling agent's browser tool (console plan / verify) instead of 'yourself'; other 'by hand' output points at the config; do-it-dont-ask test scans cli/ output
This commit is contained in:
parent
b5b8f2da9d
commit
e01a27d7b7
33 changed files with 414 additions and 117 deletions
41
cli/lib/price-gate.ts
Normal file
41
cli/lib/price-gate.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Price changes of existing (live) products: the owner's call, never confirmed by --yes. Used by play products,
|
||||
// play subscriptions, apple subscriptions and prices import (apple iap never changes the prices of an existing product).
|
||||
// Imported only by those commands, so --allow-price-changes is a flag of theirs alone (tools/gen-flags.ts).
|
||||
import { confirm, type Ctx, declined } from './ctx';
|
||||
|
||||
/** The flag that lets a write command change the price of an existing product. `flow run` never adds it and refuses it in steps. */
|
||||
export const ALLOW_PRICE_CHANGES = 'allow-price-changes';
|
||||
|
||||
/** A held price change of one existing product: per region / territory, current -> wanted. */
|
||||
export interface HeldPrice { id: string; changes: { region: string; from: string; to: string }[] }
|
||||
|
||||
/**
|
||||
* Price changes of existing (live) products are the owner's call. `--yes` (also the one `flow run --apply --yes` adds
|
||||
* to write steps) confirms creates, first prices and other updates, never these. They go with --allow-price-changes,
|
||||
* or when the owner answers the question in a terminal (no --yes), or as a desktop owner action. Nothing to change,
|
||||
* true. When false the command applies everything else and ends with holdPriceChanges().
|
||||
*/
|
||||
export async function allowPriceChanges(ctx: Ctx, count: number, question: string): Promise<boolean> {
|
||||
if (!count || ctx.args.has('allow-price-changes')) return true;
|
||||
const yes = ctx.args.has('yes');
|
||||
if (process.env.STOREBOUND_SERVE === '1' || (process.stdin.isTTY && !yes)) return confirm(withoutYes(ctx), question);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** The end of a run that left price changes alone: one line per product, then declined (exit DECLINED_EXIT), so a flow stops there. */
|
||||
export function holdPriceChanges(held: HeldPrice[]): void {
|
||||
if (!held.length) return;
|
||||
for (const h of held) console.error(`price change on ${h.id} (${priceDiff(h.changes)}) needs the owner: re-run with --${ALLOW_PRICE_CHANGES}`);
|
||||
declined(`${held.length} price change(s) of existing products need the owner (re-run with --${ALLOW_PRICE_CHANGES}); everything else was applied`);
|
||||
}
|
||||
|
||||
/** "US USD 2.99 -> USD 3.99, DE ..." (at most 4 regions, then "+N more"). Pure. */
|
||||
export function priceDiff(changes: HeldPrice['changes']): string {
|
||||
const shown = changes.slice(0, 4).map(c => `${c.region} ${c.from} -> ${c.to}`).join(', ');
|
||||
return changes.length > 4 ? `${shown}, +${changes.length - 4} more` : shown;
|
||||
}
|
||||
|
||||
/** The same context with --yes hidden: for questions --yes must not answer. */
|
||||
export function withoutYes(ctx: Ctx): Ctx {
|
||||
return { ...ctx, args: { ...ctx.args, has: (n: string) => n !== 'yes' && ctx.args.has(n) } };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue