- apple testflight wait --build N (progress, timeout, exit 0/1/7) and expire --keep N | --older-than D (dry run, asks) - apple iap-submit: IAP / subscription / group versions into a review submission; first of a type only with the app version (--with-version), --no-submit keeps the draft; read-only MCP plan - apple listing pull, play listing pull (diff, --write), catalog pull (config proposal, never rewritten) - whats-new --languages / check / apply: one file per language, agent or hook translation, per-store limits - apple offer-codes, win-back, promoted, preorder; Play pre-registration as a guide (no API) - apple cpp and apple events: list, create, upload, submit (asks) - reports: Apple sales / finance, Play earnings / sales into .storebound/reports with summary and CSV - triage: TestFlight crash / feedback and Play error reports grouped by signature, device, OS - aso rank: iTunes Search API ranks and competitors, rate limited - ads scan: AdAttributionKit / SKAdNetwork Info.plist and source checks - tests against recorded, sanitized answers; skills, README, CHANGELOG
32 lines
1.6 KiB
TypeScript
32 lines
1.6 KiB
TypeScript
// Line diff for "pull" commands (live store text vs the local files). Pure, no dependency: longest common subsequence
|
|
// over lines, printed like a unified diff without hunks (" " same, "-" only local, "+" only on the store).
|
|
|
|
export function lineDiff(before: string, after: string): string[] {
|
|
const a = before.split('\n'), b = after.split('\n');
|
|
if (a.length * b.length > 4_000_000) return [...a.map(l => `-${l}`), ...b.map(l => `+${l}`)];
|
|
const n = a.length, m = b.length;
|
|
const lcs: Uint32Array[] = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
|
|
for (let i = n - 1; i >= 0; i--) for (let j = m - 1; j >= 0; j--) lcs[i]![j] = a[i] === b[j] ? lcs[i + 1]![j + 1]! + 1 : Math.max(lcs[i + 1]![j]!, lcs[i]![j + 1]!);
|
|
const out: string[] = [];
|
|
let i = 0, j = 0;
|
|
while (i < n && j < m) {
|
|
if (a[i] === b[j]) { out.push(` ${a[i]}`); i++; j++; }
|
|
else if (lcs[i + 1]![j]! >= lcs[i]![j + 1]!) out.push(`-${a[i++]}`);
|
|
else out.push(`+${b[j++]}`);
|
|
}
|
|
while (i < n) out.push(`-${a[i++]}`);
|
|
while (j < m) out.push(`+${b[j++]}`);
|
|
return out;
|
|
}
|
|
|
|
/** Only the changed lines with `context` unchanged lines around them ("..." between gaps). Empty when equal. */
|
|
export function compactDiff(before: string, after: string, context = 2): string[] {
|
|
const d = lineDiff(before, after);
|
|
if (!d.some(l => l[0] !== ' ')) return [];
|
|
const keep = new Set<number>();
|
|
d.forEach((l, i) => { if (l[0] !== ' ') for (let k = i - context; k <= i + context; k++) keep.add(k); });
|
|
const out: string[] = [];
|
|
let last = -1;
|
|
d.forEach((l, i) => { if (!keep.has(i)) return; if (last >= 0 && i > last + 1) out.push('...'); out.push(l); last = i; });
|
|
return out;
|
|
}
|