@wix/himalaya-cli 0.1153.0 → 0.1155.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs
CHANGED
|
@@ -850,7 +850,7 @@ var init_file_store = __esm({
|
|
|
850
850
|
}
|
|
851
851
|
return removed;
|
|
852
852
|
};
|
|
853
|
-
return sweep("bundles", new Set([...refs.bundles].map((
|
|
853
|
+
return sweep("bundles", new Set([...refs.bundles].map((sha2) => `${sha2}.bundle.js`))) + sweep("docs", new Set([...refs.docs].map((hash) => `${hash}.json`))) + sweep("assets", refs.assets);
|
|
854
854
|
}
|
|
855
855
|
};
|
|
856
856
|
}
|
|
@@ -6758,15 +6758,58 @@ function walk(node, registry, path2, out2) {
|
|
|
6758
6758
|
});
|
|
6759
6759
|
}
|
|
6760
6760
|
}
|
|
6761
|
+
const type = typeof obj3.type === "string" ? obj3.type : "";
|
|
6762
|
+
const colorProps = Object.prototype.hasOwnProperty.call(COLOR_TOKEN_PROPS, type) ? COLOR_TOKEN_PROPS[type] : void 0;
|
|
6763
|
+
const typoProps = Object.prototype.hasOwnProperty.call(TYPOGRAPHY_TOKEN_PROPS, type) ? TYPOGRAPHY_TOKEN_PROPS[type] : void 0;
|
|
6764
|
+
if ((colorProps || typoProps) && obj3.props && typeof obj3.props === "object") {
|
|
6765
|
+
const props = obj3.props;
|
|
6766
|
+
const check = (spec, kind) => {
|
|
6767
|
+
const [arrayKey, leaf] = spec.includes("[].") ? spec.split("[].") : [void 0, spec];
|
|
6768
|
+
const sites = arrayKey ? (Array.isArray(props[arrayKey]) ? props[arrayKey] : []).map((el, i) => [`${path2}.props.${arrayKey}[${i}].${leaf}`, el && typeof el === "object" ? el[leaf] : void 0]) : [[`${path2}.props.${leaf}`, props[leaf]]];
|
|
6769
|
+
for (const [at2, v] of sites) {
|
|
6770
|
+
if (typeof v !== "string" || v === "") continue;
|
|
6771
|
+
if (kind === "color" ? !v.startsWith("#") && !registry.colors.has(v) : !registry.typography.has(v)) {
|
|
6772
|
+
out2.push({ path: at2, kind, name: v });
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6775
|
+
};
|
|
6776
|
+
for (const spec of colorProps ?? []) check(spec, "color");
|
|
6777
|
+
for (const spec of typoProps ?? []) check(spec, "typography");
|
|
6778
|
+
}
|
|
6761
6779
|
for (const [key2, value] of Object.entries(obj3)) {
|
|
6762
6780
|
walk(value, registry, `${path2}.${key2}`, out2);
|
|
6763
6781
|
}
|
|
6764
6782
|
}
|
|
6765
|
-
var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA;
|
|
6783
|
+
var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA, COLOR_TOKEN_PROPS, TYPOGRAPHY_TOKEN_PROPS;
|
|
6766
6784
|
var init_tokens = __esm({
|
|
6767
6785
|
"../../core/ts/src/tokens.ts"() {
|
|
6786
|
+
"use strict";
|
|
6768
6787
|
SYSTEM_FONT_FAMILY = "$system";
|
|
6769
6788
|
TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
|
|
6789
|
+
COLOR_TOKEN_PROPS = {
|
|
6790
|
+
Badge: ["token"],
|
|
6791
|
+
Card: ["background"],
|
|
6792
|
+
Image: ["tint", "placeholder"],
|
|
6793
|
+
Tag: ["token"],
|
|
6794
|
+
Icon: ["tint"],
|
|
6795
|
+
Toggle: ["trackOn", "trackOff", "thumb"],
|
|
6796
|
+
ProgressBar: ["tint", "trackColor"],
|
|
6797
|
+
ListItem: ["titleColor", "subtitleColor", "overlineColor"],
|
|
6798
|
+
Slider: ["tint"],
|
|
6799
|
+
ActivityIndicator: ["tint"],
|
|
6800
|
+
Label: ["color"],
|
|
6801
|
+
Gauge: ["tint", "rings[].tint"],
|
|
6802
|
+
Rating: ["color"],
|
|
6803
|
+
VoiceNote: ["tint"],
|
|
6804
|
+
Marquee: ["color"],
|
|
6805
|
+
Fab: ["tint"]
|
|
6806
|
+
};
|
|
6807
|
+
TYPOGRAPHY_TOKEN_PROPS = {
|
|
6808
|
+
ListItem: ["titleStyle", "subtitleStyle"],
|
|
6809
|
+
RichText: ["runs[].style"],
|
|
6810
|
+
Label: ["style"],
|
|
6811
|
+
Marquee: ["style"]
|
|
6812
|
+
};
|
|
6770
6813
|
}
|
|
6771
6814
|
});
|
|
6772
6815
|
|
|
@@ -6791,6 +6834,7 @@ function resolveTranslation(value, catalog, sourceCatalog) {
|
|
|
6791
6834
|
var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
|
|
6792
6835
|
var init_bindings = __esm({
|
|
6793
6836
|
"../../core/ts/src/bindings.ts"() {
|
|
6837
|
+
"use strict";
|
|
6794
6838
|
BINDING_SIGILS = ["service", "flag", "item", "t"];
|
|
6795
6839
|
BINDING_SIGIL_RE = new RegExp(
|
|
6796
6840
|
`^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
|
|
@@ -7859,6 +7903,47 @@ var init_validator = __esm({
|
|
|
7859
7903
|
}
|
|
7860
7904
|
});
|
|
7861
7905
|
|
|
7906
|
+
// ../../core/dev-server/src/token-suggest.ts
|
|
7907
|
+
function suggestTokenNames(name, known, max = 3) {
|
|
7908
|
+
if (name.length > MAX_SUGGEST_LEN) return [];
|
|
7909
|
+
const n = name.toLowerCase();
|
|
7910
|
+
const scored = [];
|
|
7911
|
+
for (const k of known) {
|
|
7912
|
+
if (k.length > MAX_SUGGEST_LEN) continue;
|
|
7913
|
+
const l = k.toLowerCase();
|
|
7914
|
+
const d = editDistance(n, l);
|
|
7915
|
+
let score;
|
|
7916
|
+
if (d <= 2) score = d;
|
|
7917
|
+
else if (l.startsWith(n) || n.startsWith(l)) score = 3;
|
|
7918
|
+
else if (l.includes(n) || n.includes(l)) score = 4;
|
|
7919
|
+
else if (d <= Math.floor(Math.max(n.length, l.length) / 3)) score = 5 + d;
|
|
7920
|
+
else continue;
|
|
7921
|
+
scored.push([k, score]);
|
|
7922
|
+
}
|
|
7923
|
+
scored.sort((a, b) => a[1] - b[1] || a[0].localeCompare(b[0]));
|
|
7924
|
+
const best = scored.length && scored[0][1] <= 2 ? scored.filter(([, sc]) => sc <= 2) : scored;
|
|
7925
|
+
return best.slice(0, max).map(([k]) => k);
|
|
7926
|
+
}
|
|
7927
|
+
function editDistance(a, b) {
|
|
7928
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
7929
|
+
for (let i = 1; i <= a.length; i++) {
|
|
7930
|
+
let diag = prev[0];
|
|
7931
|
+
prev[0] = i;
|
|
7932
|
+
for (let j = 1; j <= b.length; j++) {
|
|
7933
|
+
const up = prev[j];
|
|
7934
|
+
prev[j] = Math.min(prev[j] + 1, prev[j - 1] + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
7935
|
+
diag = up;
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
return prev[b.length];
|
|
7939
|
+
}
|
|
7940
|
+
var MAX_SUGGEST_LEN;
|
|
7941
|
+
var init_token_suggest = __esm({
|
|
7942
|
+
"../../core/dev-server/src/token-suggest.ts"() {
|
|
7943
|
+
MAX_SUGGEST_LEN = 64;
|
|
7944
|
+
}
|
|
7945
|
+
});
|
|
7946
|
+
|
|
7862
7947
|
// ../../core/dev-server/src/l10n-strings.ts
|
|
7863
7948
|
function isUserVisibleCopy(s) {
|
|
7864
7949
|
if (typeof s !== "string" || s.length === 0 || s.length > 200) return false;
|
|
@@ -10243,6 +10328,15 @@ function renderedWixMediaReferences(descriptor) {
|
|
|
10243
10328
|
regions.forEach((region) => collectWixMediaReferences(region.value, region.path, found, false, false, "", region.atRoot));
|
|
10244
10329
|
return found;
|
|
10245
10330
|
}
|
|
10331
|
+
function unknownTokenMessage(kind, name, registry) {
|
|
10332
|
+
const base = `unknown ${kind} token "${name}" (not in tokens.json)`;
|
|
10333
|
+
const group = registry ? { color: registry.colors, typography: registry.typography, spacing: registry.spacing, radius: registry.radius }[kind] : void 0;
|
|
10334
|
+
if (!group || group.size === 0) return base;
|
|
10335
|
+
const near = suggestTokenNames(name, group);
|
|
10336
|
+
const all = [...group].sort();
|
|
10337
|
+
const list = all.length > 14 ? `${all.slice(0, 14).join(", ")}, \u2026 (${all.length})` : all.join(", ");
|
|
10338
|
+
return `${base}${near.length ? ` \u2014 did you mean ${near.map((n) => `"${n}"`).join(" or ")}?` : ""} Defined ${kind} tokens: ${list}`;
|
|
10339
|
+
}
|
|
10246
10340
|
function validateScreenDescriptor(screenId, descriptor, opts = {}) {
|
|
10247
10341
|
const issues = [];
|
|
10248
10342
|
const strict = opts.strict ?? false;
|
|
@@ -10286,10 +10380,11 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
|
|
|
10286
10380
|
indexIssue(at2, issue2);
|
|
10287
10381
|
}
|
|
10288
10382
|
}
|
|
10383
|
+
const tokenRegistry = opts.tokens ? buildTokenRegistry(opts.tokens) : void 0;
|
|
10289
10384
|
for (const t of v.tokenViolations) {
|
|
10290
10385
|
if (t.name.startsWith("$")) continue;
|
|
10291
10386
|
if (pathCrossesOpaqueAuthorJson(t.path)) continue;
|
|
10292
|
-
issues.push({ severity: hygiene, pass: "tokens", path: t.path, message:
|
|
10387
|
+
issues.push({ severity: hygiene, pass: "tokens", path: t.path, message: unknownTokenMessage(t.kind, t.name, tokenRegistry) });
|
|
10293
10388
|
}
|
|
10294
10389
|
for (const u of v.unknownComponents) {
|
|
10295
10390
|
if (pathCrossesOpaqueAuthorJson(u.path)) continue;
|
|
@@ -10833,6 +10928,8 @@ var init_validate_descriptor_core = __esm({
|
|
|
10833
10928
|
"../../core/dev-server/src/validate-descriptor.core.ts"() {
|
|
10834
10929
|
init_validator();
|
|
10835
10930
|
init_validator();
|
|
10931
|
+
init_tokens();
|
|
10932
|
+
init_token_suggest();
|
|
10836
10933
|
init_l10n_strings();
|
|
10837
10934
|
init_a11y_contract();
|
|
10838
10935
|
init_bindings();
|
|
@@ -14385,8 +14482,8 @@ function rgbToHex([r, g, b]) {
|
|
|
14385
14482
|
async function normalizeSourceIcon(input, opts) {
|
|
14386
14483
|
const sharp = await sharpOrThrow();
|
|
14387
14484
|
const size = opts.size ?? 1024;
|
|
14388
|
-
const
|
|
14389
|
-
const inner = Math.max(1, Math.round(size *
|
|
14485
|
+
const fit2 = opts.fit ?? NORMALIZE_FIT_FRACTION;
|
|
14486
|
+
const inner = Math.max(1, Math.round(size * fit2));
|
|
14390
14487
|
const pad3 = Math.round((size - inner) / 2);
|
|
14391
14488
|
let trimmed;
|
|
14392
14489
|
try {
|
|
@@ -18280,7 +18377,7 @@ function agentCovers(agent, cli) {
|
|
|
18280
18377
|
const gap = Math.abs(Date.parse(agent.ts) - Date.parse(cli.ts));
|
|
18281
18378
|
if (!(gap <= AGENT_WINS_WITHIN_MS)) return false;
|
|
18282
18379
|
if (cli.step && agent.step === cli.step) return true;
|
|
18283
|
-
return [...shasOf(cli.images)].some((
|
|
18380
|
+
return [...shasOf(cli.images)].some((sha2) => agentShas.has(sha2));
|
|
18284
18381
|
}
|
|
18285
18382
|
function visibleEntries(entries) {
|
|
18286
18383
|
return entries.filter((e) => !isCliEntry(e) || !entries.some((a) => agentCovers(a, e)));
|
|
@@ -34587,7 +34684,8 @@ var init_renderNode = __esm({
|
|
|
34587
34684
|
if (v.startsWith("$item")) {
|
|
34588
34685
|
const key2 = v.replace(/^\$item\.?/, "");
|
|
34589
34686
|
const resolved2 = ctx.itemScope ? key2 ? ctx.itemScope[key2] : ctx.itemScope : void 0;
|
|
34590
|
-
|
|
34687
|
+
if (resolved2 != null) return asTextL10n(resolved2, ctx);
|
|
34688
|
+
return ctx.itemScope ? null : bindingPlaceholder(v, ctx);
|
|
34591
34689
|
}
|
|
34592
34690
|
if (ctx.provider) return /* @__PURE__ */ jsx8(Bound, { ctx, binding: v, render: (val) => asTextL10n(val, ctx) });
|
|
34593
34691
|
return bindingPlaceholder(v, ctx);
|
|
@@ -40512,9 +40610,9 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
|
|
|
40512
40610
|
evidence: { runId: `dev:${state.startedAt}:${screenId}`, ts: (/* @__PURE__ */ new Date()).toISOString() }
|
|
40513
40611
|
});
|
|
40514
40612
|
}
|
|
40515
|
-
async function fontFaceBytesBySha(_appDir,
|
|
40613
|
+
async function fontFaceBytesBySha(_appDir, sha2) {
|
|
40516
40614
|
try {
|
|
40517
|
-
return await catalogFaceBytes(
|
|
40615
|
+
return await catalogFaceBytes(sha2);
|
|
40518
40616
|
} catch {
|
|
40519
40617
|
}
|
|
40520
40618
|
return void 0;
|
|
@@ -42875,13 +42973,13 @@ function createDevServer(opts = {}) {
|
|
|
42875
42973
|
return handleAppIcon(req, res, state);
|
|
42876
42974
|
}
|
|
42877
42975
|
if (url.split("?")[0].startsWith("/v1/asset/") && req.method === "GET") {
|
|
42878
|
-
const
|
|
42976
|
+
const sha2 = url.split("?")[0].slice("/v1/asset/".length);
|
|
42879
42977
|
const q = new URLSearchParams(url.split("?")[1] ?? "");
|
|
42880
42978
|
const assetApp = q.get("app") ?? state.appName;
|
|
42881
42979
|
const assetDir = state.app && assetApp === state.app.name ? state.app.dir : resolveAppDir(assetApp);
|
|
42882
|
-
const hit = assetDir ? collectAssets(assetDir).find((a) => assetSha256(readFileSync44(a.path)) ===
|
|
42980
|
+
const hit = assetDir ? collectAssets(assetDir).find((a) => assetSha256(readFileSync44(a.path)) === sha2) : void 0;
|
|
42883
42981
|
if (!hit && assetDir) {
|
|
42884
|
-
const fontBytes = await fontFaceBytesBySha(assetDir,
|
|
42982
|
+
const fontBytes = await fontFaceBytesBySha(assetDir, sha2);
|
|
42885
42983
|
if (fontBytes) {
|
|
42886
42984
|
res.writeHead(200, {
|
|
42887
42985
|
"content-type": "font/ttf",
|
|
@@ -42899,7 +42997,7 @@ function createDevServer(opts = {}) {
|
|
|
42899
42997
|
if (serve) return await proxyToServe(serve, req, res);
|
|
42900
42998
|
}
|
|
42901
42999
|
res.writeHead(404, { "content-type": "application/json" });
|
|
42902
|
-
res.end(JSON.stringify({ error: "asset_not_found", app: assetApp, sha }));
|
|
43000
|
+
res.end(JSON.stringify({ error: "asset_not_found", app: assetApp, sha: sha2 }));
|
|
42903
43001
|
return;
|
|
42904
43002
|
}
|
|
42905
43003
|
const bytes2 = readFileSync44(hit.path);
|
|
@@ -43996,6 +44094,7 @@ __export(screen_crawl_exports, {
|
|
|
43996
44094
|
isUnservedRequest: () => isUnservedRequest,
|
|
43997
44095
|
layeredTransport: () => layeredTransport,
|
|
43998
44096
|
mapBundleFrame: () => mapBundleFrame,
|
|
44097
|
+
missingItemFields: () => missingItemFields,
|
|
43999
44098
|
netTransport: () => netTransport,
|
|
44000
44099
|
parseWorkerAction: () => parseWorkerAction,
|
|
44001
44100
|
recordScreenTraffic: () => recordScreenTraffic,
|
|
@@ -44612,6 +44711,96 @@ function renderedNameGaps(descriptor, state) {
|
|
|
44612
44711
|
}
|
|
44613
44712
|
return [...found.values()];
|
|
44614
44713
|
}
|
|
44714
|
+
function gateTrue(v) {
|
|
44715
|
+
if (typeof v === "boolean") return v;
|
|
44716
|
+
if (typeof v === "number") return v !== 0;
|
|
44717
|
+
if (typeof v === "string") {
|
|
44718
|
+
const l = v.trim().toLowerCase();
|
|
44719
|
+
return l === "true" || l === "1";
|
|
44720
|
+
}
|
|
44721
|
+
return false;
|
|
44722
|
+
}
|
|
44723
|
+
function rowScopeOf(el, i) {
|
|
44724
|
+
return el && typeof el === "object" && !Array.isArray(el) ? { ...el, __index: i } : { value: el, __index: i };
|
|
44725
|
+
}
|
|
44726
|
+
function missingItemFields(descriptor, state) {
|
|
44727
|
+
const provider = new FakeServiceProvider(state);
|
|
44728
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
44729
|
+
const report3 = (m) => {
|
|
44730
|
+
if (!out2.has(`${m.at}|${m.binding}`)) out2.set(`${m.at}|${m.binding}`, m);
|
|
44731
|
+
};
|
|
44732
|
+
const judgeList = (template, templateAt, rawRows, depth) => {
|
|
44733
|
+
if (!rawRows.length) return;
|
|
44734
|
+
const rows = rawRows.map(rowScopeOf);
|
|
44735
|
+
const seen = new Set(rows.flatMap((r) => Object.keys(r)).filter((k) => k !== "__index"));
|
|
44736
|
+
const walk2 = (v, rel, collapse) => {
|
|
44737
|
+
if (typeof v === "string") {
|
|
44738
|
+
for (const m of v.matchAll(ITEM_PATH_RE)) {
|
|
44739
|
+
const path2 = m[1];
|
|
44740
|
+
const at2 = joinPath(templateAt, rel);
|
|
44741
|
+
if (path2.includes(".")) {
|
|
44742
|
+
report3({ at: at2, binding: m[0], field: path2, rowFields: [...seen].sort(), rows: rows.length, collapseWhenEmpty: collapse, dotted: true });
|
|
44743
|
+
continue;
|
|
44744
|
+
}
|
|
44745
|
+
if (path2 === "__index" || seen.has(path2)) continue;
|
|
44746
|
+
report3({ at: at2, binding: m[0], field: path2, rowFields: [...seen].sort(), rows: rows.length, collapseWhenEmpty: collapse });
|
|
44747
|
+
}
|
|
44748
|
+
return;
|
|
44749
|
+
}
|
|
44750
|
+
if (Array.isArray(v)) return v.forEach((x, i) => walk2(x, `${rel}[${i}]`, collapse));
|
|
44751
|
+
if (!v || typeof v !== "object") return;
|
|
44752
|
+
const o = v;
|
|
44753
|
+
const hp = o.modifiers?.hiddenPath;
|
|
44754
|
+
if (typeof hp === "string" && hp.startsWith("$item.")) {
|
|
44755
|
+
const gate = hp.slice("$item.".length);
|
|
44756
|
+
if (rows.every((r) => gateTrue(pathValue(r, gate)))) return;
|
|
44757
|
+
}
|
|
44758
|
+
const c = collapse || o.collapseWhenEmpty === true;
|
|
44759
|
+
const nested = repeaterOf(o);
|
|
44760
|
+
for (const [k, x] of Object.entries(o)) {
|
|
44761
|
+
const at2 = rel ? `${rel}.${k}` : k;
|
|
44762
|
+
if (k === "id" && typeof x === "string") continue;
|
|
44763
|
+
if (nested && k === "props") {
|
|
44764
|
+
for (const [pk, px] of Object.entries(x)) if (pk !== "items") walk2(px, `${at2}.${pk}`, c);
|
|
44765
|
+
continue;
|
|
44766
|
+
}
|
|
44767
|
+
walk2(x, at2, c);
|
|
44768
|
+
}
|
|
44769
|
+
if (nested && depth < A11Y_LIST_DEPTH) {
|
|
44770
|
+
const innerAt = joinPath(templateAt, rel ? `${rel}.props.items[0]` : "props.items[0]");
|
|
44771
|
+
rows.forEach((r) => {
|
|
44772
|
+
const innerRows = nested.itemsPath.startsWith("$item.") ? pathValue(r, nested.itemsPath.slice("$item.".length)) : void 0;
|
|
44773
|
+
if (Array.isArray(innerRows)) judgeList(nested.template, innerAt, innerRows, depth + 1);
|
|
44774
|
+
});
|
|
44775
|
+
}
|
|
44776
|
+
};
|
|
44777
|
+
walk2(template, "", false);
|
|
44778
|
+
};
|
|
44779
|
+
const scan = (v, at2) => {
|
|
44780
|
+
if (Array.isArray(v)) return v.forEach((x, i) => scan(x, `${at2}[${i}]`));
|
|
44781
|
+
if (!v || typeof v !== "object" || isActionEnvelope(v)) return;
|
|
44782
|
+
const o = v;
|
|
44783
|
+
const list = repeaterOf(o);
|
|
44784
|
+
if (list) {
|
|
44785
|
+
const rows = list.itemsPath.startsWith("$service:") ? provider.value(list.itemsPath) : void 0;
|
|
44786
|
+
if (Array.isArray(rows)) judgeList(list.template, joinPath(at2, "props.items[0]"), rows, 1);
|
|
44787
|
+
return;
|
|
44788
|
+
}
|
|
44789
|
+
for (const [k, x] of Object.entries(o)) {
|
|
44790
|
+
if (!at2 && k === "input") continue;
|
|
44791
|
+
scan(x, at2 ? `${at2}.${k}` : k);
|
|
44792
|
+
}
|
|
44793
|
+
};
|
|
44794
|
+
scan(descriptor, "");
|
|
44795
|
+
return [...out2.values()];
|
|
44796
|
+
}
|
|
44797
|
+
function repeaterOf(o) {
|
|
44798
|
+
if (typeof o.type !== "string" || !o.props || typeof o.props !== "object") return null;
|
|
44799
|
+
const p = o.props;
|
|
44800
|
+
const itemsPath = typeof p.itemsPath === "string" ? p.itemsPath : typeof p.items_path === "string" ? p.items_path : null;
|
|
44801
|
+
const template = Array.isArray(p.items) ? p.items[0] : void 0;
|
|
44802
|
+
return itemsPath && template !== void 0 ? { itemsPath, template } : null;
|
|
44803
|
+
}
|
|
44615
44804
|
function collectObservations(descriptorOrPayload, state) {
|
|
44616
44805
|
const out2 = [];
|
|
44617
44806
|
const walk2 = (node) => {
|
|
@@ -45110,6 +45299,17 @@ async function crawlScreenInner(opts) {
|
|
|
45110
45299
|
});
|
|
45111
45300
|
}
|
|
45112
45301
|
}
|
|
45302
|
+
for (const m of missingItemFields(descriptor, state)) {
|
|
45303
|
+
findings.push({
|
|
45304
|
+
screen: screenId,
|
|
45305
|
+
trigger,
|
|
45306
|
+
invariant: "item-field-missing",
|
|
45307
|
+
severity: m.collapseWhenEmpty ? "warning" : "error",
|
|
45308
|
+
message: m.dotted ? `row binding "${m.binding}" is a dotted path \u2014 row templates bind FLAT row fields only, so it does not resolve on any renderer` : `row binding "${m.binding}" reads "${m.field}", which none of the list's ${m.rows} row(s) carries \u2014 it renders blank on every renderer`,
|
|
45309
|
+
at: m.at,
|
|
45310
|
+
hint: m.dotted ? `have the worker flatten it onto the row (e.g. emit "${m.field.replace(/\./g, "_")}") and bind that. Row fields: ${m.rowFields.join(", ") || "none"}` : `row fields: ${m.rowFields.join(", ") || "none"} \u2014 rename the binding or have the worker emit "${m.field}"`
|
|
45311
|
+
});
|
|
45312
|
+
}
|
|
45113
45313
|
for (const site of sites) {
|
|
45114
45314
|
if (parseWorkerAction(site.actionId)) continue;
|
|
45115
45315
|
const before = { ...site.params ?? {}, ...site.args ?? {} };
|
|
@@ -45733,7 +45933,7 @@ async function crawlScreenRelations(opts) {
|
|
|
45733
45933
|
const sites = actionSitesForDescriptor(descriptor).filter(
|
|
45734
45934
|
(s) => parseWorkerAction(s.actionId) && !opts.skipAction?.(s.actionId, s.componentId)
|
|
45735
45935
|
);
|
|
45736
|
-
const
|
|
45936
|
+
const nameOf2 = (s) => parseWorkerAction(s.actionId).action;
|
|
45737
45937
|
const serviceOf = (s) => parseWorkerAction(s.actionId).service;
|
|
45738
45938
|
const boot = () => bootScreenWorker({
|
|
45739
45939
|
bundleJs,
|
|
@@ -45791,7 +45991,7 @@ async function crawlScreenRelations(opts) {
|
|
|
45791
45991
|
)
|
|
45792
45992
|
];
|
|
45793
45993
|
const trig = (s) => ({ tap: s.componentId, key: s.key, action: s.actionId });
|
|
45794
|
-
for (const s of sites.filter((s2) => REFRESH_ACTION.test(
|
|
45994
|
+
for (const s of sites.filter((s2) => REFRESH_ACTION.test(nameOf2(s2)) || s2.key === "onPullToRefresh")) {
|
|
45795
45995
|
const after = await replay([s]);
|
|
45796
45996
|
if (!after) continue;
|
|
45797
45997
|
checked++;
|
|
@@ -45804,12 +46004,12 @@ async function crawlScreenRelations(opts) {
|
|
|
45804
46004
|
invariant: "refresh-not-idempotent",
|
|
45805
46005
|
severity: "warning",
|
|
45806
46006
|
axis: "relation",
|
|
45807
|
-
message: `${
|
|
46007
|
+
message: `${nameOf2(s)} from a fresh boot settles on a different state than the boot itself, under identical data: ${summarizeDiff(baseSig, sig)}`,
|
|
45808
46008
|
hint: "a refresh that appends instead of replacing, or clears a flag the loader set \u2014 REPLACE collections on refresh; ids, *At and _counters are already ignored by this comparison"
|
|
45809
46009
|
});
|
|
45810
46010
|
}
|
|
45811
46011
|
}
|
|
45812
|
-
for (const s of sites.filter((s2) => LOAD_MORE_ACTION.test(
|
|
46012
|
+
for (const s of sites.filter((s2) => LOAD_MORE_ACTION.test(nameOf2(s2)) || s2.key === "onLoadMore")) {
|
|
45813
46013
|
const target = listPathOf(s.itemsPath);
|
|
45814
46014
|
const after = target ? await replay([s]) : null;
|
|
45815
46015
|
if (!after) continue;
|
|
@@ -45825,13 +46025,13 @@ async function crawlScreenRelations(opts) {
|
|
|
45825
46025
|
axis: "relation",
|
|
45826
46026
|
at: p,
|
|
45827
46027
|
actual: `${baseLens[p]} \u2192 ${n}`,
|
|
45828
|
-
message: `${
|
|
46028
|
+
message: `${nameOf2(s)} shrank ${p} from ${baseLens[p]} to ${n} rows \u2014 a "load more" that replaces the page instead of appending it`,
|
|
45829
46029
|
hint: "append the new page to the existing rows (and de-duplicate by id); the user scrolled to see MORE"
|
|
45830
46030
|
});
|
|
45831
46031
|
}
|
|
45832
46032
|
}
|
|
45833
46033
|
}
|
|
45834
|
-
for (const s of sites.filter((s2) => FILTER_ACTION.test(
|
|
46034
|
+
for (const s of sites.filter((s2) => FILTER_ACTION.test(nameOf2(s2)) && !isOptionSelection({ ...s2.params, ...s2.args }))) {
|
|
45835
46035
|
if (screenListPaths.length !== 1) continue;
|
|
45836
46036
|
const after = await replay([s]);
|
|
45837
46037
|
if (!after) continue;
|
|
@@ -45847,18 +46047,18 @@ async function crawlScreenRelations(opts) {
|
|
|
45847
46047
|
axis: "relation",
|
|
45848
46048
|
at: p,
|
|
45849
46049
|
actual: `${baseLens[p]} \u2192 ${n}`,
|
|
45850
|
-
message: `${
|
|
46050
|
+
message: `${nameOf2(s)} grew ${p} from ${baseLens[p]} to ${n} rows \u2014 a filter/search/sort can narrow or reorder a list, never enlarge it`,
|
|
45851
46051
|
hint: "filter from the FULL set you keep aside, and assign the narrowed result \u2014 not a concatenation"
|
|
45852
46052
|
});
|
|
45853
46053
|
}
|
|
45854
46054
|
}
|
|
45855
46055
|
}
|
|
45856
|
-
const declaredNames = sites.map(
|
|
46056
|
+
const declaredNames = sites.map(nameOf2);
|
|
45857
46057
|
for (const a of sites) {
|
|
45858
|
-
const partner = inversePartner(
|
|
46058
|
+
const partner = inversePartner(nameOf2(a), declaredNames);
|
|
45859
46059
|
if (!partner) continue;
|
|
45860
|
-
if (isUndoAction(
|
|
45861
|
-
const b = sites.find((s) =>
|
|
46060
|
+
if (isUndoAction(nameOf2(a))) continue;
|
|
46061
|
+
const b = sites.find((s) => nameOf2(s) === partner && serviceOf(s) === serviceOf(a) && s !== a);
|
|
45862
46062
|
if (!b) continue;
|
|
45863
46063
|
const undoIsRowBound = isRowBound(b);
|
|
45864
46064
|
const after = await replay([a, b], (i, bootState, prev) => {
|
|
@@ -45878,15 +46078,15 @@ async function crawlScreenRelations(opts) {
|
|
|
45878
46078
|
invariant: "inverse-pair-diverges",
|
|
45879
46079
|
severity: "warning",
|
|
45880
46080
|
axis: "relation",
|
|
45881
|
-
message: `${
|
|
45882
|
-
hint: `${
|
|
46081
|
+
message: `${nameOf2(a)} \u2192 ${nameOf2(b)} does not return to the boot state: ${summarizeDiff(baseSig, sig)}`,
|
|
46082
|
+
hint: `${nameOf2(b)} should undo exactly what ${nameOf2(a)} did \u2014 check which row it removes (by the id it was given) and every field ${nameOf2(a)} touched; ids, *At and _counters are already ignored`
|
|
45883
46083
|
});
|
|
45884
46084
|
}
|
|
45885
46085
|
}
|
|
45886
46086
|
return { findings: dedupe(findings), checked, byKind };
|
|
45887
46087
|
});
|
|
45888
46088
|
}
|
|
45889
|
-
var ACTION_KEYS, UNTRACKED_ITEMS, NESTED_ITEMS_SEP, REJECT_ALL_MESSAGE, VLQ, WIX_DATA_QUERY, WIX_DATA_ANY, CAPABILITY_ACTION, detached, CRAWL_CLOCK, MEMBER_IDENTITY_ACTION, BLANK_WHEN_BOUND, CONTROL_ROLES, NAME_KEYS, A11Y_ROWS_PER_LIST, A11Y_LIST_DEPTH, STALE_SNAPSHOT_HINT, UNBINDABLE_ROW;
|
|
46089
|
+
var ACTION_KEYS, UNTRACKED_ITEMS, NESTED_ITEMS_SEP, REJECT_ALL_MESSAGE, VLQ, WIX_DATA_QUERY, WIX_DATA_ANY, CAPABILITY_ACTION, detached, CRAWL_CLOCK, MEMBER_IDENTITY_ACTION, BLANK_WHEN_BOUND, CONTROL_ROLES, NAME_KEYS, A11Y_ROWS_PER_LIST, A11Y_LIST_DEPTH, ITEM_PATH_RE, STALE_SNAPSHOT_HINT, UNBINDABLE_ROW;
|
|
45890
46090
|
var init_screen_crawl = __esm({
|
|
45891
46091
|
"../../core/dev-server/src/screen-crawl.ts"() {
|
|
45892
46092
|
init_validate_descriptor_core();
|
|
@@ -45945,6 +46145,7 @@ var init_screen_crawl = __esm({
|
|
|
45945
46145
|
NAME_KEYS = new Set(ACCESSIBLE_NAME_KEYS);
|
|
45946
46146
|
A11Y_ROWS_PER_LIST = 20;
|
|
45947
46147
|
A11Y_LIST_DEPTH = 3;
|
|
46148
|
+
ITEM_PATH_RE = /\$item\.([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
45948
46149
|
STALE_SNAPSHOT_HINT = 'if every path already does, look for a stale-snapshot write-back: `return s` (or `{ ...s }`) after an await or from an early-return guard writes back the state as it was BEFORE the await, loading flag included \u2014 to change nothing, return `undefined` instead (not `null` or `{}`); see `himi docs tier-5-add-a-screen`, "Should this result be committed at all?"';
|
|
45949
46150
|
UNBINDABLE_ROW = Symbol("unbindable-row");
|
|
45950
46151
|
}
|
|
@@ -46495,10 +46696,10 @@ function hydrationServices(tier5Dir, bundleName, input = {}) {
|
|
|
46495
46696
|
const js = readBuiltBundle(tier5Dir, bundleName);
|
|
46496
46697
|
return js ? servicesFromBundleJs(js, input) : null;
|
|
46497
46698
|
}
|
|
46498
|
-
function releaseIdFor(
|
|
46499
|
-
if (
|
|
46699
|
+
function releaseIdFor(sha2, content) {
|
|
46700
|
+
if (sha2 === "dev") return contentReleaseId(content);
|
|
46500
46701
|
const stored = JSON.parse(JSON.stringify(content));
|
|
46501
|
-
return `${
|
|
46702
|
+
return `${sha2}-${releaseContentId(stored).slice(0, 12)}`;
|
|
46502
46703
|
}
|
|
46503
46704
|
function rebindReleaseId(manifest) {
|
|
46504
46705
|
const { releaseId: _releaseId, gitSha: gitSha2, shellSha: _shellSha, ...content } = manifest;
|
|
@@ -47400,12 +47601,12 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
47400
47601
|
// never re-fetch, and the "replace an asset without a store release" claim would be false.
|
|
47401
47602
|
...Object.keys(assets).length ? { assets } : {}
|
|
47402
47603
|
};
|
|
47403
|
-
const
|
|
47604
|
+
const sha2 = gitSha();
|
|
47404
47605
|
const buildable = shellSha();
|
|
47405
47606
|
const manifest = {
|
|
47406
|
-
releaseId: releaseIdFor(
|
|
47407
|
-
gitSha:
|
|
47408
|
-
...buildable && buildable !==
|
|
47607
|
+
releaseId: releaseIdFor(sha2, content),
|
|
47608
|
+
gitSha: sha2,
|
|
47609
|
+
...buildable && buildable !== sha2 ? { shellSha: buildable } : {},
|
|
47409
47610
|
...content
|
|
47410
47611
|
};
|
|
47411
47612
|
await store.putManifest(app, manifest);
|
|
@@ -47488,8 +47689,8 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
|
|
|
47488
47689
|
return { ...schedule, bundleHash: target.bundleHash };
|
|
47489
47690
|
}) } : {}
|
|
47490
47691
|
};
|
|
47491
|
-
const
|
|
47492
|
-
const manifest = { releaseId: contentReleaseId(content), gitSha:
|
|
47692
|
+
const sha2 = gitSha();
|
|
47693
|
+
const manifest = { releaseId: contentReleaseId(content), gitSha: sha2, ...content };
|
|
47493
47694
|
await store.putManifest(app, manifest);
|
|
47494
47695
|
return { manifest, failedToBuild: [], missingBundles: [], auth: {}, tierCapViolations: [] };
|
|
47495
47696
|
}
|
|
@@ -48467,14 +48668,14 @@ function observeShots(dir, memory, now) {
|
|
|
48467
48668
|
const archived = archiveFile(dir, memory, join43(shotsDir, f));
|
|
48468
48669
|
if (!archived) continue;
|
|
48469
48670
|
seen.add(id);
|
|
48470
|
-
const { sha, ext } = archived;
|
|
48671
|
+
const { sha: sha2, ext } = archived;
|
|
48471
48672
|
const prev = memory.shots[id];
|
|
48472
48673
|
if (!prev) {
|
|
48473
|
-
memory.shots[id] = { sha, ext, firstSeen: now, changedAt: now, versions: 1, present: true };
|
|
48474
|
-
memory.timeline.push({ ts: now, id, sha, ext });
|
|
48475
|
-
} else if (prev.sha !==
|
|
48476
|
-
memory.shots[id] = { ...prev, sha, ext, changedAt: now, versions: prev.versions + 1, present: true };
|
|
48477
|
-
memory.timeline.push({ ts: now, id, sha, ext });
|
|
48674
|
+
memory.shots[id] = { sha: sha2, ext, firstSeen: now, changedAt: now, versions: 1, present: true };
|
|
48675
|
+
memory.timeline.push({ ts: now, id, sha: sha2, ext });
|
|
48676
|
+
} else if (prev.sha !== sha2) {
|
|
48677
|
+
memory.shots[id] = { ...prev, sha: sha2, ext, changedAt: now, versions: prev.versions + 1, present: true };
|
|
48678
|
+
memory.timeline.push({ ts: now, id, sha: sha2, ext });
|
|
48478
48679
|
} else if (!prev.present) {
|
|
48479
48680
|
memory.shots[id] = { ...prev, present: true };
|
|
48480
48681
|
}
|
|
@@ -48957,12 +49158,12 @@ function planFromPayload(payload, base, selectors) {
|
|
|
48957
49158
|
if (wantBundles.size) {
|
|
48958
49159
|
patch.bundles = {};
|
|
48959
49160
|
for (const name of wantBundles) {
|
|
48960
|
-
const
|
|
48961
|
-
const b64 =
|
|
48962
|
-
if (!
|
|
49161
|
+
const sha2 = local.bundles?.[name]?.sha256;
|
|
49162
|
+
const b64 = sha2 ? payload.bundles?.[sha2] : void 0;
|
|
49163
|
+
if (!sha2 || !b64) {
|
|
48963
49164
|
return { ok: false, error: { error: "selector_matched_nothing", selector: `bundle:${name}`, message: `bundle "${name}" has no built bytes in this release \u2014 run \`himi build\` first.`, bundles: localBundles } };
|
|
48964
49165
|
}
|
|
48965
|
-
patch.bundles[
|
|
49166
|
+
patch.bundles[sha2] = b64;
|
|
48966
49167
|
touched.push(`bundle:${name}`);
|
|
48967
49168
|
}
|
|
48968
49169
|
}
|
|
@@ -48985,13 +49186,13 @@ function planFromPayload(payload, base, selectors) {
|
|
|
48985
49186
|
return { ok: false, error: { error: "selector_matched_nothing", selector: `screen:${id}`, message: `screen "${id}" has no built descriptor in this release.`, screens: localScreens } };
|
|
48986
49187
|
}
|
|
48987
49188
|
const localKey = local.screens?.[id]?.bundle ?? null;
|
|
48988
|
-
const
|
|
48989
|
-
const carriedHere =
|
|
49189
|
+
const sha2 = resolveBundleSha(local, localKey);
|
|
49190
|
+
const carriedHere = sha2 ? patch.bundles?.[sha2] !== void 0 : false;
|
|
48990
49191
|
const baseKeyForSha = (s) => {
|
|
48991
49192
|
for (const [k, e] of Object.entries(base.bundles ?? {})) if (e?.sha256 === s) return k;
|
|
48992
49193
|
return null;
|
|
48993
49194
|
};
|
|
48994
|
-
const baseKey =
|
|
49195
|
+
const baseKey = sha2 ? baseKeyForSha(sha2) : null;
|
|
48995
49196
|
if (localKey !== null && !carriedHere && !baseKey) {
|
|
48996
49197
|
return {
|
|
48997
49198
|
ok: false,
|
|
@@ -49003,7 +49204,7 @@ function planFromPayload(payload, base, selectors) {
|
|
|
49003
49204
|
}
|
|
49004
49205
|
};
|
|
49005
49206
|
}
|
|
49006
|
-
const bundleRef = carriedHere ?
|
|
49207
|
+
const bundleRef = carriedHere ? sha2 : baseKey;
|
|
49007
49208
|
patch.screens[id] = { descriptor, bundle: bundleRef };
|
|
49008
49209
|
if (bundleRef && bundleRef !== (base.screens?.[id]?.bundle ?? null)) rebound[id] = bundleRef;
|
|
49009
49210
|
touched.push(`screen:${id}`);
|
|
@@ -50992,7 +51193,7 @@ function shotStateFor(dir, image) {
|
|
|
50992
51193
|
}
|
|
50993
51194
|
}
|
|
50994
51195
|
function recordedShots(dir, screenId) {
|
|
50995
|
-
return Object.entries(readRecord(dir)).filter(([
|
|
51196
|
+
return Object.entries(readRecord(dir)).filter(([sha2, r]) => /^[0-9a-f]{64}$/.test(sha2) && !!r && r.screen === screenId && typeof r.at === "string" && ["content", "error", "loading", "empty"].includes(r.state)).map(([sha2, r]) => ({ sha256: sha2, at: r.at, state: { state: r.state, ...r.initial === true ? { initial: true } : {}, ...r.offline === true ? { offline: true } : {} } })).sort((a, b) => a.at < b.at ? 1 : a.at > b.at ? -1 : 0);
|
|
50996
51197
|
}
|
|
50997
51198
|
function offlineArtifact(state) {
|
|
50998
51199
|
if (!state || state.state === "content") return false;
|
|
@@ -51266,7 +51467,7 @@ var init_studio_reel = __esm({
|
|
|
51266
51467
|
};
|
|
51267
51468
|
isObj3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
51268
51469
|
str4 = (v) => typeof v === "string" && v ? v : null;
|
|
51269
|
-
historyName = (
|
|
51470
|
+
historyName = (sha2, ext) => typeof sha2 === "string" && typeof ext === "string" && HISTORY_NAME.test(`${sha2}.${ext}`) ? `${sha2}.${ext}` : null;
|
|
51270
51471
|
cited = (p) => [
|
|
51271
51472
|
...p.frames.map((f) => f.file),
|
|
51272
51473
|
...p.finals.flatMap((f) => f.file ? [f.file] : []),
|
|
@@ -51515,10 +51716,10 @@ function stillBuilding(dir, image, now) {
|
|
|
51515
51716
|
if (!entries.length) return void 0;
|
|
51516
51717
|
const times = entries.map((e) => Date.parse(e.ts)).filter(Number.isFinite);
|
|
51517
51718
|
if (!times.length || !(now.getTime() - Math.max(...times) > STILL_BUILDING_AFTER_MS)) return void 0;
|
|
51518
|
-
const
|
|
51719
|
+
const sha2 = sha256Of2(image);
|
|
51519
51720
|
const latest = entries[entries.length - 1];
|
|
51520
|
-
if (Array.isArray(latest.images) && latest.images.some((i) => !!i && typeof i === "object" && i.sha256 ===
|
|
51521
|
-
return journal(dir, "still-building", withHomeCaveat(STILL_BUILDING_MESSAGE, homeCaveat(dir, image)), { ref: `home-${
|
|
51721
|
+
if (Array.isArray(latest.images) && latest.images.some((i) => !!i && typeof i === "object" && i.sha256 === sha2)) return void 0;
|
|
51722
|
+
return journal(dir, "still-building", withHomeCaveat(STILL_BUILDING_MESSAGE, homeCaveat(dir, image)), { ref: `home-${sha2.slice(0, 16)}`, image, now });
|
|
51522
51723
|
} catch {
|
|
51523
51724
|
return void 0;
|
|
51524
51725
|
}
|
|
@@ -51805,10 +52006,10 @@ function homeShotForPostcard(dir, opts = {}) {
|
|
|
51805
52006
|
}
|
|
51806
52007
|
if (opts.repeatOk) return pick2;
|
|
51807
52008
|
try {
|
|
51808
|
-
const
|
|
52009
|
+
const sha2 = sha256Of2(pick2.image);
|
|
51809
52010
|
const entries = readProgress(dir, { all: true }).entries;
|
|
51810
52011
|
const latest = entries[entries.length - 1];
|
|
51811
|
-
const shown2 = Array.isArray(latest?.images) && latest.images.some((i) => !!i && typeof i === "object" && i.sha256 ===
|
|
52012
|
+
const shown2 = Array.isArray(latest?.images) && latest.images.some((i) => !!i && typeof i === "object" && i.sha256 === sha2);
|
|
51812
52013
|
if (shown2) return { skipped: "the latest postcard already shows this home picture" };
|
|
51813
52014
|
} catch {
|
|
51814
52015
|
return { skipped: "the home shot could not be read" };
|
|
@@ -58601,16 +58802,16 @@ ${releaseId}`;
|
|
|
58601
58802
|
if (assetMatch) {
|
|
58602
58803
|
if (!app) return json4(400, { error: "missing_app" });
|
|
58603
58804
|
if (!store.getAsset) return json4(501, { error: "assets_unsupported" });
|
|
58604
|
-
const
|
|
58605
|
-
if (!isSafeSegment(
|
|
58805
|
+
const sha2 = assetMatch[1];
|
|
58806
|
+
if (!isSafeSegment(sha2)) return json4(400, { error: "bad_asset" });
|
|
58606
58807
|
const m = pin ? await pinnedManifest(app, pin) : await manifest(app);
|
|
58607
|
-
const record2 = Object.values(m?.assets ?? {}).find((a) => a.sha256 ===
|
|
58808
|
+
const record2 = Object.values(m?.assets ?? {}).find((a) => a.sha256 === sha2);
|
|
58608
58809
|
if (!record2) return json4(404, { error: "asset_not_found", app });
|
|
58609
|
-
const etag = strongEtagForSha256(
|
|
58810
|
+
const etag = strongEtagForSha256(sha2);
|
|
58610
58811
|
if (request.headers.get("if-none-match") === etag) {
|
|
58611
58812
|
return new Response(null, { status: 304, headers: { etag, "cache-control": BUNDLE_CACHE_CONTROL } });
|
|
58612
58813
|
}
|
|
58613
|
-
const bytes2 = await store.getAsset(app,
|
|
58814
|
+
const bytes2 = await store.getAsset(app, sha2);
|
|
58614
58815
|
if (!bytes2) return json4(404, { error: "asset_not_found", app });
|
|
58615
58816
|
return new Response(bytes2, {
|
|
58616
58817
|
status: 200,
|
|
@@ -59187,21 +59388,21 @@ var init_preview_assets_generated = __esm({
|
|
|
59187
59388
|
"../../packages/serve/src/preview-assets.generated.ts"() {
|
|
59188
59389
|
init_preview_assets_format();
|
|
59189
59390
|
PREVIEW_ASSET_MANIFEST = [
|
|
59190
|
-
{ key: "assets/index-
|
|
59191
|
-
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset:
|
|
59192
|
-
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset:
|
|
59193
|
-
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset:
|
|
59194
|
-
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
59195
|
-
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset:
|
|
59196
|
-
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset:
|
|
59197
|
-
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
59198
|
-
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset:
|
|
59199
|
-
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
59200
|
-
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset:
|
|
59201
|
-
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset:
|
|
59202
|
-
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
59203
|
-
{ key: "index.html", contentType: "text/html; charset=utf-8", offset:
|
|
59204
|
-
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset:
|
|
59391
|
+
{ key: "assets/index-CKwdlTbT.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1127499, sha256: "24d1f9f9ee47c8d4ea8f88ebfca0b78f46d3e64d26b2dd202ae97cbdcf3ede2b" },
|
|
59392
|
+
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1127507, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
|
|
59393
|
+
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1132117, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
|
|
59394
|
+
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1620701, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
|
|
59395
|
+
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1711625, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
|
|
59396
|
+
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1802609, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
|
|
59397
|
+
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1893589, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
|
|
59398
|
+
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1984461, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
|
|
59399
|
+
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2075441, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
|
|
59400
|
+
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2184077, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
|
|
59401
|
+
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2293161, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
|
|
59402
|
+
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2402209, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
|
|
59403
|
+
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2509909, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
|
|
59404
|
+
{ key: "index.html", contentType: "text/html; charset=utf-8", offset: 2598017, length: 4947, sha256: "1d32807684d4cf8de017b14f9b1c6faf05c08f04a8d5d19372d4122c0c66b0bf" },
|
|
59405
|
+
{ key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2602964, length: 17344, sha256: "d057298d75e7373e5e2c4d11b7d1bcbea88e03af555ba63d97641c78509c58e4" }
|
|
59205
59406
|
];
|
|
59206
59407
|
}
|
|
59207
59408
|
});
|
|
@@ -59824,7 +60025,7 @@ var init_iap_client = __esm({
|
|
|
59824
60025
|
"../store-release/iap-client.ts"() {
|
|
59825
60026
|
init_register_bundle();
|
|
59826
60027
|
ASC_ORIGIN = "https://api.appstoreconnect.apple.com";
|
|
59827
|
-
MAX_PAGES =
|
|
60028
|
+
MAX_PAGES = 100;
|
|
59828
60029
|
MAX_READ_ATTEMPTS = 4;
|
|
59829
60030
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
59830
60031
|
MAX_RETRY_WAIT_MS = 6e4;
|
|
@@ -59983,6 +60184,9 @@ var init_iap_client = __esm({
|
|
|
59983
60184
|
});
|
|
59984
60185
|
|
|
59985
60186
|
// ../store-release/iap-api.ts
|
|
60187
|
+
function appleProductType(type) {
|
|
60188
|
+
return APPLE_PRODUCT_TYPES[type];
|
|
60189
|
+
}
|
|
59986
60190
|
function single(doc) {
|
|
59987
60191
|
return doc.data && !Array.isArray(doc.data) ? doc.data : void 0;
|
|
59988
60192
|
}
|
|
@@ -60036,10 +60240,12 @@ async function readOptional(read) {
|
|
|
60036
60240
|
function createIapApi(client2) {
|
|
60037
60241
|
return new AppStoreConnectIapApi(client2);
|
|
60038
60242
|
}
|
|
60039
|
-
var INITIAL_PRICE_REF, PAGE, linkage, related, enc, AppStoreConnectIapApi;
|
|
60243
|
+
var APPLE_PRODUCT_TYPES, PRICE_POINT_PAGE, INITIAL_PRICE_REF, PAGE, linkage, related, enc, AppStoreConnectIapApi;
|
|
60040
60244
|
var init_iap_api = __esm({
|
|
60041
60245
|
"../store-release/iap-api.ts"() {
|
|
60042
60246
|
init_iap_client();
|
|
60247
|
+
APPLE_PRODUCT_TYPES = { nonConsumable: "NON_CONSUMABLE", consumable: "CONSUMABLE" };
|
|
60248
|
+
PRICE_POINT_PAGE = "limit=8000";
|
|
60043
60249
|
INITIAL_PRICE_REF = "${initial-price}";
|
|
60044
60250
|
PAGE = "limit=200";
|
|
60045
60251
|
linkage = (type, id) => ({ type, id });
|
|
@@ -60076,7 +60282,7 @@ var init_iap_api = __esm({
|
|
|
60076
60282
|
return (await this.client.list(`/v1/inAppPurchaseVersions/${enc(versionId)}/localizations?${PAGE}`)).map(toLocalization);
|
|
60077
60283
|
}
|
|
60078
60284
|
async listPricePoints(resourceId, territory) {
|
|
60079
|
-
const points = await this.client.list(`/v2/inAppPurchases/${enc(resourceId)}/pricePoints?filter[territory]=${enc(territory)}&${
|
|
60285
|
+
const points = await this.client.list(`/v2/inAppPurchases/${enc(resourceId)}/pricePoints?filter[territory]=${enc(territory)}&${PRICE_POINT_PAGE}`);
|
|
60080
60286
|
return points.map((p) => ({ id: p.id, customerPrice: attr(p, "customerPrice") }));
|
|
60081
60287
|
}
|
|
60082
60288
|
async readPriceSchedule(resourceId) {
|
|
@@ -60105,7 +60311,7 @@ var init_iap_api = __esm({
|
|
|
60105
60311
|
const attributes = {
|
|
60106
60312
|
name: product.referenceName,
|
|
60107
60313
|
productId: product.productId,
|
|
60108
|
-
inAppPurchaseType:
|
|
60314
|
+
inAppPurchaseType: appleProductType(product.type),
|
|
60109
60315
|
familySharable: false,
|
|
60110
60316
|
...product.reviewNote ? { reviewNote: product.reviewNote } : {}
|
|
60111
60317
|
};
|
|
@@ -60189,11 +60395,13 @@ function readLocalizations(reader, item, path2) {
|
|
|
60189
60395
|
const localizations = reader.list(item, "localizations", path2).map((value, i) => {
|
|
60190
60396
|
const at2 = `${path2}.localizations[${i}]`;
|
|
60191
60397
|
const obj3 = reader.object(value, at2, LOCALIZATION_KEYS);
|
|
60192
|
-
|
|
60193
|
-
|
|
60194
|
-
|
|
60195
|
-
|
|
60196
|
-
|
|
60398
|
+
const name = reader.text(obj3, "name", at2);
|
|
60399
|
+
const length = [...name].length;
|
|
60400
|
+
const { min, max } = APPLE_LIMITS.displayName;
|
|
60401
|
+
if (name && (length < min || length > max)) reader.problems.push(`${at2}.name must be ${min}\u2013${max} characters (got ${length})`);
|
|
60402
|
+
const description = reader.text(obj3, "description", at2);
|
|
60403
|
+
reader.maxLength(description, `${at2}.description`, APPLE_LIMITS.description);
|
|
60404
|
+
return { locale: reader.text(obj3, "locale", at2, LOCALE), name, description };
|
|
60197
60405
|
});
|
|
60198
60406
|
reader.duplicates(localizations.map((l) => l.locale), `${path2}.localizations`, "locale");
|
|
60199
60407
|
return localizations;
|
|
@@ -60201,10 +60409,12 @@ function readLocalizations(reader, item, path2) {
|
|
|
60201
60409
|
function readPrice(reader, item, path2) {
|
|
60202
60410
|
const at2 = `${path2}.price`;
|
|
60203
60411
|
const obj3 = reader.object(item.price, at2, PRICE_KEYS);
|
|
60412
|
+
const rule = obj3.rule === void 0 ? void 0 : reader.oneOf(obj3, "rule", at2, PRICE_RULES, "exact matches Apple's price exactly; atLeast takes the next Apple price point up");
|
|
60204
60413
|
return {
|
|
60205
60414
|
baseTerritory: reader.text(obj3, "baseTerritory", at2, TERRITORY),
|
|
60206
60415
|
currency: reader.text(obj3, "currency", at2, CURRENCY),
|
|
60207
|
-
amount: reader.text(obj3, "amount", at2, AMOUNT)
|
|
60416
|
+
amount: reader.text(obj3, "amount", at2, AMOUNT),
|
|
60417
|
+
...rule ? { rule } : {}
|
|
60208
60418
|
};
|
|
60209
60419
|
}
|
|
60210
60420
|
function readTerritories(reader, item, path2, baseTerritory) {
|
|
@@ -60216,34 +60426,66 @@ function readTerritories(reader, item, path2, baseTerritory) {
|
|
|
60216
60426
|
}
|
|
60217
60427
|
return territories;
|
|
60218
60428
|
}
|
|
60429
|
+
function readWixEntity(reader, value, at2) {
|
|
60430
|
+
const obj3 = reader.object(value, at2, WIX_KEYS);
|
|
60431
|
+
return { entityType: reader.text(obj3, "entityType", at2), entityId: reader.text(obj3, "entityId", at2) };
|
|
60432
|
+
}
|
|
60433
|
+
function readUnlocks(reader, obj3, path2) {
|
|
60434
|
+
return reader.list(obj3, "unlocks", path2).map((value, i) => {
|
|
60435
|
+
const at2 = `${path2}.unlocks[${i}]`;
|
|
60436
|
+
const unlock = reader.object(value, at2, UNLOCK_KEYS);
|
|
60437
|
+
return { entityType: reader.text(unlock, "entityType", at2), entityId: reader.text(unlock, "entityId", at2), entitlement: reader.text(unlock, "entitlement", at2) };
|
|
60438
|
+
});
|
|
60439
|
+
}
|
|
60440
|
+
function readSubject(reader, obj3, path2, type) {
|
|
60441
|
+
if (obj3.wix === void 0 === (obj3.unlocks === void 0)) {
|
|
60442
|
+
reader.problems.push(`${path2} must name exactly one of "wix" or "unlocks"`);
|
|
60443
|
+
return {};
|
|
60444
|
+
}
|
|
60445
|
+
if (obj3.wix !== void 0) return { wix: readWixEntity(reader, obj3.wix, `${path2}.wix`) };
|
|
60446
|
+
if (type !== "consumable") reader.problems.push(`${path2}: only a consumable may carry "unlocks" \u2014 a non-consumable sells exactly one Wix item`);
|
|
60447
|
+
return { unlocks: readUnlocks(reader, obj3, path2) };
|
|
60448
|
+
}
|
|
60449
|
+
function limitedText(reader, obj3, key2, path2, max, rule) {
|
|
60450
|
+
const value = reader.text(obj3, key2, path2, rule);
|
|
60451
|
+
reader.maxLength(value, childPath(path2, key2), max);
|
|
60452
|
+
return value;
|
|
60453
|
+
}
|
|
60219
60454
|
function readItem(reader, value, index) {
|
|
60220
60455
|
const path2 = `items[${index}]`;
|
|
60221
60456
|
const obj3 = reader.object(value, path2, ITEM_KEYS);
|
|
60222
|
-
const
|
|
60457
|
+
const type = reader.oneOf(obj3, "type", path2, PRODUCT_TYPES, "subscriptions are not supported by this version");
|
|
60223
60458
|
const price = readPrice(reader, obj3, path2);
|
|
60224
60459
|
const reviewNote = reader.optionalText(obj3, "reviewNote", path2);
|
|
60460
|
+
if (reviewNote) reader.maxLength(reviewNote, `${path2}.reviewNote`, APPLE_LIMITS.reviewNote);
|
|
60225
60461
|
return {
|
|
60226
|
-
|
|
60462
|
+
...readSubject(reader, obj3, path2, type),
|
|
60227
60463
|
classification: reader.literal(obj3, "classification", path2, "digital", "physical goods and unclassified products are never registered as in-app purchases"),
|
|
60228
60464
|
entitlement: reader.text(obj3, "entitlement", path2),
|
|
60229
|
-
productId: reader
|
|
60230
|
-
type
|
|
60231
|
-
referenceName: reader
|
|
60465
|
+
productId: limitedText(reader, obj3, "productId", path2, APPLE_LIMITS.productId, PRODUCT_ID),
|
|
60466
|
+
type,
|
|
60467
|
+
referenceName: limitedText(reader, obj3, "referenceName", path2, APPLE_LIMITS.referenceName),
|
|
60232
60468
|
...reviewNote === void 0 ? {} : { reviewNote },
|
|
60233
60469
|
localizations: readLocalizations(reader, obj3, path2),
|
|
60234
60470
|
price,
|
|
60235
60471
|
territories: readTerritories(reader, obj3, path2, price.baseTerritory)
|
|
60236
60472
|
};
|
|
60237
60473
|
}
|
|
60474
|
+
function wixEntitiesOf(item) {
|
|
60475
|
+
return item.unlocks ?? (item.wix ? [item.wix] : []);
|
|
60476
|
+
}
|
|
60238
60477
|
function reportDuplicateMappings(reader, items) {
|
|
60239
60478
|
reader.duplicates(items.map((i) => i.productId), "items", "productId");
|
|
60240
|
-
reader.duplicates(items.
|
|
60241
|
-
reader.duplicates(items.
|
|
60479
|
+
reader.duplicates(items.flatMap((i) => wixEntitiesOf(i).map((e) => `${e.entityType}:${e.entityId}`)), "items", "Wix entity");
|
|
60480
|
+
reader.duplicates(items.flatMap((i) => [i.entitlement, ...(i.unlocks ?? []).map((u) => u.entitlement)]), "items", "entitlement");
|
|
60242
60481
|
reader.duplicates(items.map((i) => i.referenceName), "items", "referenceName");
|
|
60243
60482
|
}
|
|
60483
|
+
function looksLikeCredential(text8) {
|
|
60484
|
+
return CREDENTIAL_SHAPES.some((shape) => shape.test(text8));
|
|
60485
|
+
}
|
|
60244
60486
|
function reportCredentialMaterial(reader, value, path2) {
|
|
60245
60487
|
if (typeof value === "string") {
|
|
60246
|
-
if (
|
|
60488
|
+
if (looksLikeCredential(value)) {
|
|
60247
60489
|
reader.problems.push(`${path2} looks like credential material \u2014 keys and tokens never belong in a manifest`);
|
|
60248
60490
|
}
|
|
60249
60491
|
} else if (Array.isArray(value)) {
|
|
@@ -60286,7 +60528,7 @@ function sha256Digest(value) {
|
|
|
60286
60528
|
function manifestDigest(manifest) {
|
|
60287
60529
|
return sha256Digest(manifest);
|
|
60288
60530
|
}
|
|
60289
|
-
var IapManifestError, APPLE_APP_ID, TEAM_ID, BUNDLE_ID, PRODUCT_ID, TERRITORY, CURRENCY, AMOUNT, LOCALE, CREDENTIAL_SHAPES, MANIFEST_KEYS, TARGET_KEYS, ITEM_KEYS, WIX_KEYS, LOCALIZATION_KEYS, PRICE_KEYS, childPath, ManifestReader;
|
|
60531
|
+
var IapManifestError, APPLE_APP_ID, TEAM_ID, BUNDLE_ID, PRODUCT_ID, TERRITORY, CURRENCY, AMOUNT, LOCALE, CREDENTIAL_SHAPES, MANIFEST_KEYS, TARGET_KEYS, ITEM_KEYS, WIX_KEYS, UNLOCK_KEYS, LOCALIZATION_KEYS, PRICE_KEYS, PRODUCT_TYPES, PRICE_RULES, APPLE_LIMITS, childPath, ManifestReader;
|
|
60290
60532
|
var init_iap_manifest = __esm({
|
|
60291
60533
|
"../store-release/iap-manifest.ts"() {
|
|
60292
60534
|
IapManifestError = class extends Error {
|
|
@@ -60300,7 +60542,7 @@ var init_iap_manifest = __esm({
|
|
|
60300
60542
|
APPLE_APP_ID = { pattern: /^\d+$/, hint: "the numeric App Store Connect app id" };
|
|
60301
60543
|
TEAM_ID = { pattern: /^[A-Z0-9]{10}$/, hint: "a 10-character Apple team id" };
|
|
60302
60544
|
BUNDLE_ID = { pattern: /^[A-Za-z0-9.-]+$/, hint: "a bundle id" };
|
|
60303
|
-
PRODUCT_ID = { pattern: /^[A-Za-z0-9._]+$/, hint: "
|
|
60545
|
+
PRODUCT_ID = { pattern: /^[A-Za-z0-9._-]+$/, hint: "letters, digits, hyphens, periods and underscores only" };
|
|
60304
60546
|
TERRITORY = { pattern: /^[A-Z]{3}$/, hint: "an ISO 3166-1 alpha-3 territory code such as USA" };
|
|
60305
60547
|
CURRENCY = { pattern: /^[A-Z]{3}$/, hint: "an ISO 4217 currency code such as USD" };
|
|
60306
60548
|
AMOUNT = { pattern: /^\d+(\.\d+)?$/, hint: 'an exact decimal string such as "0.99"' };
|
|
@@ -60314,6 +60556,7 @@ var init_iap_manifest = __esm({
|
|
|
60314
60556
|
TARGET_KEYS = ["himiApp", "wixSiteId", "appleAppId", "bundleId", "teamId", "profile"];
|
|
60315
60557
|
ITEM_KEYS = [
|
|
60316
60558
|
"wix",
|
|
60559
|
+
"unlocks",
|
|
60317
60560
|
"classification",
|
|
60318
60561
|
"entitlement",
|
|
60319
60562
|
"productId",
|
|
@@ -60325,8 +60568,12 @@ var init_iap_manifest = __esm({
|
|
|
60325
60568
|
"territories"
|
|
60326
60569
|
];
|
|
60327
60570
|
WIX_KEYS = ["entityType", "entityId"];
|
|
60571
|
+
UNLOCK_KEYS = ["entityType", "entityId", "entitlement"];
|
|
60328
60572
|
LOCALIZATION_KEYS = ["locale", "name", "description"];
|
|
60329
|
-
PRICE_KEYS = ["baseTerritory", "currency", "amount"];
|
|
60573
|
+
PRICE_KEYS = ["baseTerritory", "currency", "amount", "rule"];
|
|
60574
|
+
PRODUCT_TYPES = ["nonConsumable", "consumable"];
|
|
60575
|
+
PRICE_RULES = ["exact", "atLeast"];
|
|
60576
|
+
APPLE_LIMITS = { displayName: { min: 2, max: 30 }, description: 45, referenceName: 64, productId: 100, reviewNote: 4e3 };
|
|
60330
60577
|
childPath = (path2, key2) => path2 ? `${path2}.${key2}` : key2;
|
|
60331
60578
|
ManifestReader = class {
|
|
60332
60579
|
problems = [];
|
|
@@ -60366,6 +60613,15 @@ var init_iap_manifest = __esm({
|
|
|
60366
60613
|
if (obj3[key2] !== expected) this.problems.push(`${childPath(path2, key2)} must be ${JSON.stringify(expected)} \u2014 ${why}`);
|
|
60367
60614
|
return expected;
|
|
60368
60615
|
}
|
|
60616
|
+
oneOf(obj3, key2, path2, allowed2, why) {
|
|
60617
|
+
const value = obj3[key2];
|
|
60618
|
+
if (allowed2.includes(value)) return value;
|
|
60619
|
+
this.problems.push(`${childPath(path2, key2)} must be ${allowed2.map((a) => JSON.stringify(a)).join(" or ")} \u2014 ${why}`);
|
|
60620
|
+
return allowed2[0];
|
|
60621
|
+
}
|
|
60622
|
+
maxLength(value, at2, max) {
|
|
60623
|
+
if ([...value].length > max) this.problems.push(`${at2} must be at most ${max} characters (got ${[...value].length})`);
|
|
60624
|
+
}
|
|
60369
60625
|
duplicates(values, path2, what) {
|
|
60370
60626
|
const seen = /* @__PURE__ */ new Set();
|
|
60371
60627
|
for (const value of values) {
|
|
@@ -60377,6 +60633,84 @@ var init_iap_manifest = __esm({
|
|
|
60377
60633
|
}
|
|
60378
60634
|
});
|
|
60379
60635
|
|
|
60636
|
+
// ../store-release/iap-pricing.ts
|
|
60637
|
+
function normalizeAmount(amount) {
|
|
60638
|
+
const [whole, fraction = ""] = amount.trim().split(".");
|
|
60639
|
+
const integer = whole.replace(/^0+(?=\d)/, "");
|
|
60640
|
+
const decimals = fraction.replace(/0+$/, "");
|
|
60641
|
+
return decimals ? `${integer}.${decimals}` : integer;
|
|
60642
|
+
}
|
|
60643
|
+
function compareAmounts(a, b) {
|
|
60644
|
+
const [aWhole, aFraction = ""] = normalizeAmount(a).split(".");
|
|
60645
|
+
const [bWhole, bFraction = ""] = normalizeAmount(b).split(".");
|
|
60646
|
+
if (aWhole.length !== bWhole.length) return aWhole.length - bWhole.length;
|
|
60647
|
+
if (aWhole !== bWhole) return aWhole < bWhole ? -1 : 1;
|
|
60648
|
+
const width = Math.max(aFraction.length, bFraction.length);
|
|
60649
|
+
const [x, y] = [aFraction.padEnd(width, "0"), bFraction.padEnd(width, "0")];
|
|
60650
|
+
return x === y ? 0 : x < y ? -1 : 1;
|
|
60651
|
+
}
|
|
60652
|
+
function pickPoint(points, price) {
|
|
60653
|
+
if (price.rule !== "atLeast") return points.find((p) => compareAmounts(p.customerPrice, price.amount) === 0);
|
|
60654
|
+
return points.filter((p) => compareAmounts(p.customerPrice, price.amount) >= 0).sort((a, b) => compareAmounts(a.customerPrice, b.customerPrice))[0];
|
|
60655
|
+
}
|
|
60656
|
+
function nearestPrices(amounts, amount) {
|
|
60657
|
+
const distance = (a) => Math.abs(Number(a) - Number(amount));
|
|
60658
|
+
return [...amounts].sort((a, b) => distance(a) - distance(b)).slice(0, 2).sort(compareAmounts);
|
|
60659
|
+
}
|
|
60660
|
+
function describeRequestedPrice(price, target) {
|
|
60661
|
+
const where = `${price.currency} in ${price.baseTerritory}`;
|
|
60662
|
+
if (price.rule !== "atLeast") return `${price.amount} ${where}`;
|
|
60663
|
+
return target ? `${target} ${where} (the lowest point at or above ${price.amount})` : `the lowest point at or above ${price.amount} ${where}`;
|
|
60664
|
+
}
|
|
60665
|
+
function noPricePointMessage(price, amounts) {
|
|
60666
|
+
if (price.rule === "atLeast") {
|
|
60667
|
+
const highest = [...amounts].sort((a, b) => compareAmounts(b, a))[0] ?? "none";
|
|
60668
|
+
return `no Apple price point at or above ${price.amount} ${price.currency} in ${price.baseTerritory} (highest: ${highest}) \u2014 lower the manifest's amount and make a new plan.`;
|
|
60669
|
+
}
|
|
60670
|
+
return `no Apple price point of exactly ${price.amount} ${price.currency} in ${price.baseTerritory} (nearest: ${nearestPrices(amounts, price.amount).join(", ") || "none"}) \u2014 Apple sells only fixed price points; change the manifest's amount (or set price.rule "atLeast") and make a new plan. Nothing is rounded here.`;
|
|
60671
|
+
}
|
|
60672
|
+
var asPoints, PriceGrid;
|
|
60673
|
+
var init_iap_pricing = __esm({
|
|
60674
|
+
"../store-release/iap-pricing.ts"() {
|
|
60675
|
+
asPoints = (amounts) => amounts.map((customerPrice) => ({ customerPrice }));
|
|
60676
|
+
PriceGrid = class {
|
|
60677
|
+
constructor(api2) {
|
|
60678
|
+
this.api = api2;
|
|
60679
|
+
}
|
|
60680
|
+
amounts = /* @__PURE__ */ new Map();
|
|
60681
|
+
async pointsFor(resourceId, territory) {
|
|
60682
|
+
const points = await this.api.listPricePoints(resourceId, territory);
|
|
60683
|
+
this.amounts.set(territory, points.map((p) => p.customerPrice));
|
|
60684
|
+
return points;
|
|
60685
|
+
}
|
|
60686
|
+
/** The amounts Apple offers in a territory, read through `viaResourceId` at most once per run. */
|
|
60687
|
+
async amountsIn(territory, viaResourceId) {
|
|
60688
|
+
const cached3 = this.amounts.get(territory);
|
|
60689
|
+
if (cached3 || !viaResourceId) return cached3;
|
|
60690
|
+
return (await this.pointsFor(viaResourceId, territory)).map((p) => p.customerPrice);
|
|
60691
|
+
}
|
|
60692
|
+
/** The Apple amount a price resolves to; undefined for atLeast until some product can show the grid. */
|
|
60693
|
+
async targetAmount(price, viaResourceId) {
|
|
60694
|
+
if (price.rule !== "atLeast") return price.amount;
|
|
60695
|
+
const amounts = await this.amountsIn(price.baseTerritory, viaResourceId);
|
|
60696
|
+
return amounts && pickPoint(asPoints(amounts), price)?.customerPrice;
|
|
60697
|
+
}
|
|
60698
|
+
/** Plan time: does Apple offer a point for this price? Answered from the cached grid. */
|
|
60699
|
+
async check(price, viaResourceId) {
|
|
60700
|
+
const amounts = await this.amountsIn(price.baseTerritory, viaResourceId) ?? [];
|
|
60701
|
+
const amount = pickPoint(asPoints(amounts), price)?.customerPrice;
|
|
60702
|
+
return amount ? { amount } : { failure: noPricePointMessage(price, amounts) };
|
|
60703
|
+
}
|
|
60704
|
+
/** Apply time: the point to write. Point ids are per product, so this lists the product's own. */
|
|
60705
|
+
async resolve(resourceId, price) {
|
|
60706
|
+
const points = await this.pointsFor(resourceId, price.baseTerritory);
|
|
60707
|
+
const point = pickPoint(points, price);
|
|
60708
|
+
return point ? { point } : { failure: noPricePointMessage(price, points.map((p) => p.customerPrice)) };
|
|
60709
|
+
}
|
|
60710
|
+
};
|
|
60711
|
+
}
|
|
60712
|
+
});
|
|
60713
|
+
|
|
60380
60714
|
// ../store-release/iap-result.ts
|
|
60381
60715
|
function remainingRequirements(observed) {
|
|
60382
60716
|
if (!observed) return ["the product does not exist in App Store Connect yet"];
|
|
@@ -60389,8 +60723,8 @@ function mappingFor(target, item, appleResourceId) {
|
|
|
60389
60723
|
wixSiteId: target.wixSiteId,
|
|
60390
60724
|
appleAppId: target.appleAppId,
|
|
60391
60725
|
bundleId: target.bundleId,
|
|
60392
|
-
wixEntityType: item.wix.entityType,
|
|
60393
|
-
|
|
60726
|
+
...item.wix ? { wixEntityType: item.wix.entityType, wixEntityId: item.wix.entityId } : {},
|
|
60727
|
+
...item.unlocks ? { unlocks: item.unlocks } : {},
|
|
60394
60728
|
entitlement: item.entitlement,
|
|
60395
60729
|
appleProductId: item.productId,
|
|
60396
60730
|
...appleResourceId ? { appleResourceId } : {},
|
|
@@ -60418,7 +60752,7 @@ function itemResult(input) {
|
|
|
60418
60752
|
...observed?.availability ? { observed: observed.availability.territories, availableInNewTerritories: observed.availability.availableInNewTerritories } : {}
|
|
60419
60753
|
},
|
|
60420
60754
|
localizations: { requested: item.localizations.map((l) => l.locale), observed: observed?.localizations.map((l) => l.locale) ?? [] },
|
|
60421
|
-
remainingRequirements: remainingRequirements(observed),
|
|
60755
|
+
remainingRequirements: [...input.requirements ?? [], ...remainingRequirements(observed)],
|
|
60422
60756
|
lastReadAt: input.readAt.toISOString()
|
|
60423
60757
|
}
|
|
60424
60758
|
};
|
|
@@ -60456,11 +60790,10 @@ var init_iap_result = __esm({
|
|
|
60456
60790
|
});
|
|
60457
60791
|
|
|
60458
60792
|
// ../store-release/iap-plan.ts
|
|
60459
|
-
function
|
|
60460
|
-
|
|
60461
|
-
const
|
|
60462
|
-
|
|
60463
|
-
return decimals ? `${integer}.${decimals}` : integer;
|
|
60793
|
+
function priceNote(price, target) {
|
|
60794
|
+
if (price.rule !== "atLeast") return CONDITIONAL_PRICE_NOTE;
|
|
60795
|
+
const where = `${price.currency} in ${price.baseTerritory}`;
|
|
60796
|
+
return target ? `Apple price ${target} ${where} \u2014 the lowest price point at or above ${price.amount}` : `the lowest Apple price point at or above ${price.amount} ${where} is chosen once the product exists`;
|
|
60464
60797
|
}
|
|
60465
60798
|
function describeOp(op) {
|
|
60466
60799
|
return "locale" in op ? `${op.kind} ${op.locale}` : op.kind;
|
|
@@ -60561,8 +60894,8 @@ async function observeProduct(api2, product) {
|
|
|
60561
60894
|
...availability ? { availability } : {}
|
|
60562
60895
|
};
|
|
60563
60896
|
}
|
|
60564
|
-
function priceMatches(observed, requested) {
|
|
60565
|
-
return observed !== void 0 && observed.baseTerritory === requested.baseTerritory && observed.customerPrices.length === 1 &&
|
|
60897
|
+
function priceMatches(observed, requested, target) {
|
|
60898
|
+
return target !== void 0 && observed !== void 0 && observed.baseTerritory === requested.baseTerritory && observed.customerPrices.length === 1 && compareAmounts(observed.customerPrices[0], target) === 0;
|
|
60566
60899
|
}
|
|
60567
60900
|
function availabilityMatches(observed, territories) {
|
|
60568
60901
|
return observed !== void 0 && !observed.availableInNewTerritories && sameSet(observed.territories, territories);
|
|
@@ -60574,12 +60907,11 @@ function localizationDiffs(item, observed) {
|
|
|
60574
60907
|
return found.name === wanted.name && found.description === wanted.description ? [] : [`localization ${wanted.locale} differs`];
|
|
60575
60908
|
});
|
|
60576
60909
|
}
|
|
60577
|
-
function priceDiff(item, observed) {
|
|
60578
|
-
if (priceMatches(observed.price, item.price)) return [];
|
|
60910
|
+
function priceDiff(item, observed, target) {
|
|
60911
|
+
if (priceMatches(observed.price, item.price, target)) return [];
|
|
60579
60912
|
if (!observed.price) return ["no price is set"];
|
|
60580
60913
|
const prices = observed.price.customerPrices.join("/") || "none";
|
|
60581
|
-
|
|
60582
|
-
return [`price is ${prices} in ${observed.price.baseTerritory}; the manifest requests ${requested}`];
|
|
60914
|
+
return [`price is ${prices} in ${observed.price.baseTerritory}; the manifest requests ${describeRequestedPrice(item.price, target)}`];
|
|
60583
60915
|
}
|
|
60584
60916
|
function availabilityDiff(item, observed) {
|
|
60585
60917
|
if (availabilityMatches(observed.availability, item.territories)) return [];
|
|
@@ -60587,8 +60919,8 @@ function availabilityDiff(item, observed) {
|
|
|
60587
60919
|
const auto = observed.availability.availableInNewTerritories ? " (plus new territories automatically)" : "";
|
|
60588
60920
|
return [`available in ${observed.availability.territories.join(", ")}${auto}; the manifest requests ${item.territories.join(", ")}`];
|
|
60589
60921
|
}
|
|
60590
|
-
function metadataDiffs(item, observed) {
|
|
60591
|
-
return [...localizationDiffs(item, observed), ...priceDiff(item, observed), ...availabilityDiff(item, observed)];
|
|
60922
|
+
function metadataDiffs(item, observed, target = exactTarget(item.price)) {
|
|
60923
|
+
return [...localizationDiffs(item, observed), ...priceDiff(item, observed, target), ...availabilityDiff(item, observed)];
|
|
60592
60924
|
}
|
|
60593
60925
|
function isLocked(observed) {
|
|
60594
60926
|
return !EDITABLE_PRODUCT_STATES.has(observed.state) || observed.versions.some((v) => LOCKED_VERSION_STATES.has(v.state));
|
|
@@ -60596,8 +60928,9 @@ function isLocked(observed) {
|
|
|
60596
60928
|
function itemPlan(item, decision, fields = {}) {
|
|
60597
60929
|
return { productId: item.productId, decision, ops: [], conflicts: [], notes: [], ...fields };
|
|
60598
60930
|
}
|
|
60599
|
-
function creationPlan(item) {
|
|
60931
|
+
function creationPlan(item, target) {
|
|
60600
60932
|
return itemPlan(item, "create", {
|
|
60933
|
+
...target ? { applePrice: target, targetPrice: target } : {},
|
|
60601
60934
|
ops: [
|
|
60602
60935
|
{ kind: "createProduct" },
|
|
60603
60936
|
{ kind: "createVersion" },
|
|
@@ -60605,17 +60938,20 @@ function creationPlan(item) {
|
|
|
60605
60938
|
{ kind: "setPrice" },
|
|
60606
60939
|
{ kind: "setAvailability" }
|
|
60607
60940
|
],
|
|
60608
|
-
notes: [
|
|
60941
|
+
notes: [priceNote(item.price, target)]
|
|
60609
60942
|
});
|
|
60610
60943
|
}
|
|
60611
60944
|
function identityConflicts(item, observed) {
|
|
60612
60945
|
const conflicts = [];
|
|
60613
|
-
if (observed.type !==
|
|
60946
|
+
if (observed.type !== appleProductType(item.type)) {
|
|
60947
|
+
const wanted = item.type === "consumable" ? "a consumable" : "a non-consumable";
|
|
60948
|
+
conflicts.push(`Apple has ${item.productId} as ${observed.type}; product ids and types are immutable, so it cannot become ${wanted}`);
|
|
60949
|
+
}
|
|
60614
60950
|
if (observed.referenceName !== item.referenceName) conflicts.push(`Apple's reference name is "${observed.referenceName}"; the manifest says "${item.referenceName}"`);
|
|
60615
60951
|
return conflicts;
|
|
60616
60952
|
}
|
|
60617
|
-
function lockedPlan(item, observed) {
|
|
60618
|
-
const diffs = metadataDiffs(item, observed);
|
|
60953
|
+
function lockedPlan(item, observed, target) {
|
|
60954
|
+
const diffs = metadataDiffs(item, observed, target);
|
|
60619
60955
|
if (!diffs.length) return itemPlan(item, "reuse", { observed });
|
|
60620
60956
|
const conflict = `${item.productId} is ${observed.state} and this command never changes a live or in-review product: ${diffs.join("; ")}`;
|
|
60621
60957
|
return itemPlan(item, "conflict", { observed, conflicts: [conflict] });
|
|
@@ -60631,58 +60967,54 @@ function localizationOps(item, observed) {
|
|
|
60631
60967
|
return found.name === wanted.name && found.description === wanted.description ? [] : [{ kind: "updateLocalization", locale: wanted.locale }];
|
|
60632
60968
|
});
|
|
60633
60969
|
}
|
|
60634
|
-
async function
|
|
60635
|
-
const points = await api2.listPricePoints(resourceId, price.baseTerritory);
|
|
60636
|
-
const wanted = normalizeAmount(price.amount);
|
|
60637
|
-
const point = points.find((p) => normalizeAmount(p.customerPrice) === wanted);
|
|
60638
|
-
const distance = (p) => Math.abs(Number(p.customerPrice) - Number(price.amount));
|
|
60639
|
-
const nearest = [...points].sort((a, b) => distance(a) - distance(b)).slice(0, 2).map((p) => p.customerPrice).sort((a, b) => Number(a) - Number(b));
|
|
60640
|
-
return { point, nearest };
|
|
60641
|
-
}
|
|
60642
|
-
function noExactPriceMessage(price, nearest) {
|
|
60643
|
-
return `no Apple price point of exactly ${price.amount} ${price.currency} in ${price.baseTerritory} (nearest: ${nearest.join(", ") || "none"}) \u2014 Apple sells only fixed price points; change the manifest's amount and make a new plan. Nothing is rounded here.`;
|
|
60644
|
-
}
|
|
60645
|
-
async function priceOps(api2, item, observed) {
|
|
60970
|
+
async function priceOps(pricing, item, observed, target) {
|
|
60646
60971
|
if (observed.price) {
|
|
60647
|
-
|
|
60972
|
+
const matches = priceMatches(observed.price, item.price, target);
|
|
60973
|
+
return { ops: [], conflicts: matches ? [] : [`${priceDiff(item, observed, target)[0]} \u2014 an existing price is never changed here`] };
|
|
60648
60974
|
}
|
|
60649
|
-
const {
|
|
60650
|
-
return
|
|
60975
|
+
const { amount, failure } = await pricing.check(item.price, observed.resourceId);
|
|
60976
|
+
return amount ? { ops: [{ kind: "setPrice" }], conflicts: [], applePrice: amount } : { ops: [], conflicts: [failure ?? "no Apple price point"] };
|
|
60651
60977
|
}
|
|
60652
60978
|
function availabilityOps(item, observed) {
|
|
60653
60979
|
if (!observed.availability) return { ops: [{ kind: "setAvailability" }], conflicts: [] };
|
|
60654
60980
|
const conflicts = availabilityMatches(observed.availability, item.territories) ? [] : [`${availabilityDiff(item, observed)[0]} \u2014 existing availability is never changed here`];
|
|
60655
60981
|
return { ops: [], conflicts };
|
|
60656
60982
|
}
|
|
60657
|
-
async function editablePlan(
|
|
60658
|
-
const price = await priceOps(
|
|
60983
|
+
async function editablePlan(pricing, item, observed, target) {
|
|
60984
|
+
const price = await priceOps(pricing, item, observed, target);
|
|
60659
60985
|
const availability = availabilityOps(item, observed);
|
|
60660
60986
|
const conflicts = [...price.conflicts, ...availability.conflicts];
|
|
60661
60987
|
if (conflicts.length) return itemPlan(item, "conflict", { observed, conflicts });
|
|
60662
60988
|
const ops = [...localizationOps(item, observed), ...price.ops, ...availability.ops];
|
|
60663
|
-
return itemPlan(item, ops.length ? "update" : "reuse", { observed, ops });
|
|
60989
|
+
return itemPlan(item, ops.length ? "update" : "reuse", { observed, ops, ...price.applePrice ? { applePrice: price.applePrice } : {} });
|
|
60664
60990
|
}
|
|
60665
|
-
async function planForProduct(api2, item, product) {
|
|
60991
|
+
async function planForProduct(api2, item, product, pricing) {
|
|
60666
60992
|
const observed = await observeProduct(api2, product);
|
|
60993
|
+
const target = await pricing.targetAmount(item.price, product.id);
|
|
60667
60994
|
const conflicts = identityConflicts(item, observed);
|
|
60668
|
-
|
|
60669
|
-
return
|
|
60995
|
+
const decided = conflicts.length ? itemPlan(item, "conflict", { observed, conflicts }) : isLocked(observed) ? lockedPlan(item, observed, target) : await editablePlan(pricing, item, observed, target);
|
|
60996
|
+
return target ? { ...decided, targetPrice: target } : decided;
|
|
60670
60997
|
}
|
|
60671
|
-
async function planItem(api2, item, products) {
|
|
60998
|
+
async function planItem(pricing, api2, item, products) {
|
|
60672
60999
|
const nameClash = products.find((p) => p.referenceName === item.referenceName && p.productId !== item.productId);
|
|
60673
61000
|
if (nameClash) return itemPlan(item, "conflict", { conflicts: [`reference name "${item.referenceName}" is already used by ${nameClash.productId}`] });
|
|
60674
61001
|
const match = products.find((p) => p.productId === item.productId);
|
|
60675
|
-
|
|
61002
|
+
if (match) return planForProduct(api2, item, match, pricing);
|
|
61003
|
+
return creationPlan(item, await pricing.targetAmount(item.price, products[0]?.id));
|
|
60676
61004
|
}
|
|
60677
|
-
async function
|
|
61005
|
+
async function planItemsAt(context, manifest, indices = manifest.items.map((_, i) => i)) {
|
|
61006
|
+
const pricing = context.pricing ?? new PriceGrid(context.api);
|
|
60678
61007
|
return withAccessContext(context.identity, manifest.target, async () => {
|
|
60679
61008
|
await verifyTarget(context.api, manifest, context.identity);
|
|
60680
61009
|
const products = await discoverProducts(context, manifest);
|
|
60681
|
-
const plans =
|
|
60682
|
-
for (const
|
|
61010
|
+
const plans = /* @__PURE__ */ new Map();
|
|
61011
|
+
for (const i of indices) plans.set(i, await planItem(pricing, context.api, manifest.items[i], products));
|
|
60683
61012
|
return plans;
|
|
60684
61013
|
});
|
|
60685
61014
|
}
|
|
61015
|
+
async function planItems(context, manifest) {
|
|
61016
|
+
return [...(await planItemsAt(context, manifest)).values()];
|
|
61017
|
+
}
|
|
60686
61018
|
function sealPlan(unsealed) {
|
|
60687
61019
|
const normalized = JSON.parse(JSON.stringify(unsealed));
|
|
60688
61020
|
return { ...normalized, planDigest: sha256Digest(normalized) };
|
|
@@ -60724,26 +61056,32 @@ function verifyPlan2(doc) {
|
|
|
60724
61056
|
return plan;
|
|
60725
61057
|
}
|
|
60726
61058
|
async function statusIap(manifest, context) {
|
|
60727
|
-
const
|
|
61059
|
+
const pricing = context.pricing ?? new PriceGrid(context.api);
|
|
61060
|
+
const plans = await planItems({ ...context, pricing }, manifest);
|
|
60728
61061
|
const readAt = context.now();
|
|
60729
|
-
const items =
|
|
60730
|
-
|
|
61062
|
+
const items = [];
|
|
61063
|
+
for (const [i, p] of plans.entries()) {
|
|
61064
|
+
const item = manifest.items[i];
|
|
61065
|
+
const target = p.observed ? await pricing.targetAmount(item.price, p.observed.resourceId) : void 0;
|
|
61066
|
+
items.push(itemResult({
|
|
60731
61067
|
target: manifest.target,
|
|
60732
|
-
item
|
|
61068
|
+
item,
|
|
60733
61069
|
observed: p.observed,
|
|
60734
61070
|
outcome: STATUS_OUTCOMES[p.decision],
|
|
60735
61071
|
conflicts: p.conflicts,
|
|
60736
|
-
unmet: p.observed ? metadataDiffs(
|
|
61072
|
+
unmet: p.observed ? metadataDiffs(item, p.observed, target) : [],
|
|
60737
61073
|
readAt
|
|
60738
|
-
})
|
|
60739
|
-
|
|
61074
|
+
}));
|
|
61075
|
+
}
|
|
60740
61076
|
return iapResult({ target: manifest.target, identity: context.identity, items, generatedAt: readAt });
|
|
60741
61077
|
}
|
|
60742
|
-
var IapProvisionError, EDITABLE_VERSION_STATE,
|
|
61078
|
+
var IapProvisionError, EDITABLE_VERSION_STATE, EDITABLE_PRODUCT_STATES, LOCKED_VERSION_STATES, OP_KINDS, LOCALE_OPS, CONDITIONAL_PRICE_NOTE, STATUS_OUTCOMES, isoDay, exactTarget, planError;
|
|
60743
61079
|
var init_iap_plan = __esm({
|
|
60744
61080
|
"../store-release/iap-plan.ts"() {
|
|
61081
|
+
init_iap_api();
|
|
60745
61082
|
init_iap_client();
|
|
60746
61083
|
init_iap_manifest();
|
|
61084
|
+
init_iap_pricing();
|
|
60747
61085
|
init_iap_result();
|
|
60748
61086
|
IapProvisionError = class extends Error {
|
|
60749
61087
|
constructor(reason, message) {
|
|
@@ -60753,7 +61091,6 @@ var init_iap_plan = __esm({
|
|
|
60753
61091
|
}
|
|
60754
61092
|
};
|
|
60755
61093
|
EDITABLE_VERSION_STATE = "PREPARE_FOR_SUBMISSION";
|
|
60756
|
-
APPLE_NON_CONSUMABLE = "NON_CONSUMABLE";
|
|
60757
61094
|
EDITABLE_PRODUCT_STATES = /* @__PURE__ */ new Set(["MISSING_METADATA", "READY_TO_SUBMIT", "DEVELOPER_ACTION_NEEDED", "REJECTED"]);
|
|
60758
61095
|
LOCKED_VERSION_STATES = /* @__PURE__ */ new Set(["READY_FOR_REVIEW", "WAITING_FOR_REVIEW", "IN_REVIEW", "ACCEPTED", "APPROVED"]);
|
|
60759
61096
|
OP_KINDS = /* @__PURE__ */ new Set(["createProduct", "createVersion", "createLocalization", "updateLocalization", "setPrice", "setAvailability"]);
|
|
@@ -60761,6 +61098,7 @@ var init_iap_plan = __esm({
|
|
|
60761
61098
|
CONDITIONAL_PRICE_NOTE = "the exact price point can only be looked up after the product exists \u2014 apply stops with a partial result if Apple has no point of exactly the requested amount";
|
|
60762
61099
|
STATUS_OUTCOMES = { create: "absent", update: "incomplete", reuse: "reused", conflict: "conflict" };
|
|
60763
61100
|
isoDay = (date) => date.toISOString().slice(0, 10);
|
|
61101
|
+
exactTarget = (price) => price.rule === "atLeast" ? void 0 : price.amount;
|
|
60764
61102
|
planError = (message) => new IapProvisionError("plan", message);
|
|
60765
61103
|
}
|
|
60766
61104
|
});
|
|
@@ -60783,7 +61121,7 @@ function driftOf(planned, live2) {
|
|
|
60783
61121
|
return unplanned.length ? `${live2.productId} now needs ${unplanned.map(describeOp).join(", ")}, which the plan did not list` : void 0;
|
|
60784
61122
|
}
|
|
60785
61123
|
function assertNoDrift(plan, live2) {
|
|
60786
|
-
const drift =
|
|
61124
|
+
const drift = [...live2].map(([i, current]) => driftOf(plan.items[i], current)).filter((d) => d !== void 0);
|
|
60787
61125
|
if (!drift.length) return;
|
|
60788
61126
|
throw new IapProvisionError("drift", `App Store Connect changed since the plan was made \u2014 nothing was written:
|
|
60789
61127
|
- ${drift.join("\n - ")}
|
|
@@ -60834,12 +61172,13 @@ async function findLocalization(api2, versionId, locale, matchText) {
|
|
|
60834
61172
|
if (!found || matchText && (found.name !== matchText.name || found.description !== matchText.description)) return void 0;
|
|
60835
61173
|
return found;
|
|
60836
61174
|
}
|
|
60837
|
-
function operationRunners(
|
|
61175
|
+
function operationRunners(context, manifest) {
|
|
61176
|
+
const { api: api2, sleep, pricing } = context;
|
|
60838
61177
|
const appId2 = manifest.target.appleAppId;
|
|
60839
61178
|
return {
|
|
60840
61179
|
createProduct: async (run2) => {
|
|
60841
61180
|
const { item } = run2;
|
|
60842
|
-
const newProduct = { productId: item.productId, referenceName: item.referenceName, ...item.reviewNote ? { reviewNote: item.reviewNote } : {} };
|
|
61181
|
+
const newProduct = { productId: item.productId, referenceName: item.referenceName, type: item.type, ...item.reviewNote ? { reviewNote: item.reviewNote } : {} };
|
|
60843
61182
|
const { value, state } = await ensure({ write: () => api2.createProduct(appId2, newProduct), find: () => findProduct(api2, appId2, item.productId) }, sleep);
|
|
60844
61183
|
run2.resourceId = value.id;
|
|
60845
61184
|
return { state, resourceId: value.id };
|
|
@@ -60867,9 +61206,9 @@ function operationRunners(api2, manifest, sleep) {
|
|
|
60867
61206
|
setPrice: async (run2) => {
|
|
60868
61207
|
const resourceId = required(run2.resourceId, "the product id");
|
|
60869
61208
|
const { price } = run2.item;
|
|
60870
|
-
const { point,
|
|
60871
|
-
if (!point) throw new UnresolvedWriteError(
|
|
60872
|
-
const matches = async () => priceMatches(await readObservedPrice(api2, resourceId), price) ? true : void 0;
|
|
61209
|
+
const { point, failure } = await pricing.resolve(resourceId, price);
|
|
61210
|
+
if (!point) throw new UnresolvedWriteError(failure ?? "no Apple price point");
|
|
61211
|
+
const matches = async () => priceMatches(await readObservedPrice(api2, resourceId), price, point.customerPrice) ? true : void 0;
|
|
60873
61212
|
const { state } = await ensure({ write: async () => (await api2.createPriceSchedule(resourceId, price.baseTerritory, point.id), true), find: matches }, sleep);
|
|
60874
61213
|
return { state };
|
|
60875
61214
|
},
|
|
@@ -60902,10 +61241,10 @@ async function runStep(runner2, run2, op, context, manifest) {
|
|
|
60902
61241
|
context.journal.record({ productId: run2.item.productId, ...run2.resourceId ? { productResourceId: run2.resourceId } : {}, ...entry, at: context.now().toISOString() });
|
|
60903
61242
|
return step;
|
|
60904
61243
|
}
|
|
60905
|
-
async function resumeFromApple(run2, remaining,
|
|
60906
|
-
const product = await
|
|
61244
|
+
async function resumeFromApple(run2, remaining, context) {
|
|
61245
|
+
const product = await context.api.readProduct(required(run2.resourceId, "the product id"));
|
|
60907
61246
|
if (!product) return { reason: "the product exists but cannot be read back yet \u2014 rerun apply later" };
|
|
60908
|
-
const replanned = await planForProduct(
|
|
61247
|
+
const replanned = await planForProduct(context.api, run2.item, product, context.pricing);
|
|
60909
61248
|
if (replanned.decision === "conflict") return { reason: `the product already existed: ${replanned.conflicts.join("; ")}` };
|
|
60910
61249
|
const unplanned = replanned.ops.filter((op) => !remaining.some((r) => sameOp(r, op)));
|
|
60911
61250
|
if (unplanned.length) return { reason: `the product already existed and needs ${unplanned.map(describeOp).join(", ")}, which the plan did not list \u2014 make a new plan` };
|
|
@@ -60914,7 +61253,7 @@ async function resumeFromApple(run2, remaining, api2) {
|
|
|
60914
61253
|
return { ops: replanned.ops };
|
|
60915
61254
|
}
|
|
60916
61255
|
async function runOps(run2, ops, context, manifest) {
|
|
60917
|
-
const runners = operationRunners(context
|
|
61256
|
+
const runners = operationRunners(context, manifest);
|
|
60918
61257
|
const steps = [];
|
|
60919
61258
|
let queue = [...ops];
|
|
60920
61259
|
for (let op = queue.shift(); op; op = queue.shift()) {
|
|
@@ -60922,7 +61261,7 @@ async function runOps(run2, ops, context, manifest) {
|
|
|
60922
61261
|
steps.push(step);
|
|
60923
61262
|
if (step.state === "failed") return [...steps, ...queue.map((rest) => stepFor(rest, "notRun"))];
|
|
60924
61263
|
if (op.kind === "createProduct" && step.state === "reconciled") {
|
|
60925
|
-
const resumed = await resumeFromApple(run2, queue, context
|
|
61264
|
+
const resumed = await resumeFromApple(run2, queue, context).catch((e) => ({ reason: `could not re-read the product: ${explainFailure(e, context, manifest)}` }));
|
|
60926
61265
|
if ("reason" in resumed) return [...steps, ...queue.map((rest, i) => stepFor(rest, "notRun", i === 0 ? { detail: resumed.reason } : {}))];
|
|
60927
61266
|
queue = resumed.ops;
|
|
60928
61267
|
}
|
|
@@ -60945,10 +61284,17 @@ async function readBack(run2, context, manifest) {
|
|
|
60945
61284
|
return { failure: explainFailure(e, context, manifest) };
|
|
60946
61285
|
}
|
|
60947
61286
|
}
|
|
61287
|
+
async function unmetFor(item, observed, context) {
|
|
61288
|
+
return observed ? metadataDiffs(item, observed, await context.pricing.targetAmount(item.price, observed.resourceId)) : [];
|
|
61289
|
+
}
|
|
61290
|
+
async function knownUnmetFor(item, planned, context) {
|
|
61291
|
+
if (!planned.observed) return [];
|
|
61292
|
+
return metadataDiffs(item, planned.observed, planned.targetPrice ?? await context.pricing.targetAmount(item.price));
|
|
61293
|
+
}
|
|
60948
61294
|
async function applyItem(item, live2, context, manifest) {
|
|
60949
61295
|
const base = { target: manifest.target, item, readAt: context.now() };
|
|
60950
61296
|
if (live2.decision === "conflict") {
|
|
60951
|
-
const unmet2 =
|
|
61297
|
+
const unmet2 = await knownUnmetFor(item, live2, context);
|
|
60952
61298
|
return itemResult({ ...base, observed: live2.observed, outcome: "conflict", conflicts: live2.conflicts, unmet: unmet2 });
|
|
60953
61299
|
}
|
|
60954
61300
|
const run2 = {
|
|
@@ -60959,29 +61305,55 @@ async function applyItem(item, live2, context, manifest) {
|
|
|
60959
61305
|
};
|
|
60960
61306
|
const steps = await runOps(run2, live2.ops, context, manifest);
|
|
60961
61307
|
const { observed, failure } = await readBack(run2, context, manifest);
|
|
60962
|
-
const unmet = failure ? [`could not read the product back: ${failure}`] :
|
|
61308
|
+
const unmet = failure ? [`could not read the product back: ${failure}`] : await unmetFor(item, observed, context);
|
|
60963
61309
|
const outcome = outcomeOf(live2.ops, steps, unmet);
|
|
60964
61310
|
return itemResult({ ...base, readAt: context.now(), observed, outcome, steps, unmet, appleResourceId: run2.resourceId });
|
|
60965
61311
|
}
|
|
60966
|
-
function
|
|
60967
|
-
|
|
61312
|
+
function deferredResult(item, live2, context, manifest) {
|
|
61313
|
+
const steps = live2.ops.map((op) => stepFor(op, "notRun"));
|
|
61314
|
+
return itemResult({ target: manifest.target, item, observed: live2.observed, outcome: "deferred", steps, requirements: [DEFERRED_REQUIREMENT], readAt: context.now() });
|
|
60968
61315
|
}
|
|
60969
|
-
|
|
61316
|
+
function needsRefresh(planned) {
|
|
61317
|
+
return planned.decision !== "conflict" && planned.ops.length > 0;
|
|
61318
|
+
}
|
|
61319
|
+
function plannedResult(item, planned, plan, context, manifest) {
|
|
61320
|
+
if (planned.decision === "conflict") return applyItem(item, planned, context, manifest);
|
|
61321
|
+
return itemResult({ target: manifest.target, item, observed: planned.observed, outcome: "reused", readAt: new Date(plan.createdAt) });
|
|
61322
|
+
}
|
|
61323
|
+
async function applyIap(planDoc, applyContext) {
|
|
60970
61324
|
const plan = verifyPlan2(planDoc);
|
|
60971
|
-
assertSameAccount(plan.identity,
|
|
61325
|
+
assertSameAccount(plan.identity, applyContext.identity);
|
|
61326
|
+
const context = { ...applyContext, pricing: applyContext.pricing ?? new PriceGrid(applyContext.api) };
|
|
60972
61327
|
const manifest = plan.manifest;
|
|
60973
|
-
const
|
|
61328
|
+
const refresh = plan.items.flatMap((planned, i) => needsRefresh(planned) ? [i] : []);
|
|
61329
|
+
const live2 = await planItemsAt(context, manifest, refresh);
|
|
60974
61330
|
assertNoDrift(plan, live2);
|
|
60975
61331
|
const items = [];
|
|
60976
|
-
|
|
61332
|
+
let created = 0;
|
|
61333
|
+
for (const [i, item] of manifest.items.entries()) {
|
|
61334
|
+
const itemPlan2 = live2.get(i);
|
|
61335
|
+
if (!itemPlan2) {
|
|
61336
|
+
items.push(await plannedResult(item, plan.items[i], plan, context, manifest));
|
|
61337
|
+
continue;
|
|
61338
|
+
}
|
|
61339
|
+
const needsCreate = itemPlan2.ops.some((op) => op.kind === "createProduct");
|
|
61340
|
+
if (needsCreate && context.createLimit !== void 0 && created >= context.createLimit) {
|
|
61341
|
+
items.push(deferredResult(item, itemPlan2, context, manifest));
|
|
61342
|
+
continue;
|
|
61343
|
+
}
|
|
61344
|
+
if (needsCreate) created++;
|
|
61345
|
+
items.push(await applyItem(item, itemPlan2, context, manifest));
|
|
61346
|
+
}
|
|
60977
61347
|
return iapResult({ target: manifest.target, identity: context.identity, items, generatedAt: context.now() });
|
|
60978
61348
|
}
|
|
60979
|
-
var RECONCILE_READS, RECONCILE_BASE_WAIT_MS, AMBIGUOUS_WRITE_KINDS, UnresolvedWriteError, required;
|
|
61349
|
+
var DEFERRED_REQUIREMENT, RECONCILE_READS, RECONCILE_BASE_WAIT_MS, AMBIGUOUS_WRITE_KINDS, UnresolvedWriteError, required;
|
|
60980
61350
|
var init_iap_apply = __esm({
|
|
60981
61351
|
"../store-release/iap-apply.ts"() {
|
|
60982
61352
|
init_iap_client();
|
|
60983
61353
|
init_iap_plan();
|
|
61354
|
+
init_iap_pricing();
|
|
60984
61355
|
init_iap_result();
|
|
61356
|
+
DEFERRED_REQUIREMENT = "deferred by --limit \u2014 rerun apply to continue; nothing was written for this product in this run";
|
|
60985
61357
|
RECONCILE_READS = 4;
|
|
60986
61358
|
RECONCILE_BASE_WAIT_MS = 1e3;
|
|
60987
61359
|
AMBIGUOUS_WRITE_KINDS = /* @__PURE__ */ new Set(["uncertain", "rateLimited", "conflict"]);
|
|
@@ -61182,6 +61554,182 @@ var init_iap_credentials = __esm({
|
|
|
61182
61554
|
}
|
|
61183
61555
|
});
|
|
61184
61556
|
|
|
61557
|
+
// ../store-release/iap-draft.ts
|
|
61558
|
+
import { createHash as createHash33 } from "node:crypto";
|
|
61559
|
+
function fit(text8, max) {
|
|
61560
|
+
const chars = [...text8];
|
|
61561
|
+
return chars.length <= max ? text8 : `${chars.slice(0, max - 1).join("").trimEnd()}${ELLIPSIS}`;
|
|
61562
|
+
}
|
|
61563
|
+
function plainText2(html) {
|
|
61564
|
+
return html.replace(/<[^>]*>/g, " ").replace(/&(amp|nbsp|lt|gt|quot|#39);/g, (entity) => ENTITIES[entity]).replace(/\s+/g, " ").trim();
|
|
61565
|
+
}
|
|
61566
|
+
function displayName(name) {
|
|
61567
|
+
const plain2 = plainText2(name);
|
|
61568
|
+
return fit([...plain2].length >= APPLE_LIMITS.displayName.min ? plain2 : `${plain2} item`, APPLE_LIMITS.displayName.max);
|
|
61569
|
+
}
|
|
61570
|
+
function boundedProductId(bundleId, slug, tail, hashSource) {
|
|
61571
|
+
const readable = `${bundleId}.${slug}.${tail}`;
|
|
61572
|
+
if (tail && readable.length <= APPLE_LIMITS.productId) return readable;
|
|
61573
|
+
const suffix = `.${slug}.${sha(hashSource).slice(0, 24)}`;
|
|
61574
|
+
return `${bundleId.slice(0, APPLE_LIMITS.productId - suffix.length)}${suffix}`;
|
|
61575
|
+
}
|
|
61576
|
+
function productIdFor(bundleId, slug, wixId) {
|
|
61577
|
+
return boundedProductId(bundleId, slug, wixId.toLowerCase().replace(/[^a-z0-9]/g, ""), wixId);
|
|
61578
|
+
}
|
|
61579
|
+
function applePointFor(amount, options) {
|
|
61580
|
+
if (!options.applePrices) return void 0;
|
|
61581
|
+
const price = { baseTerritory: options.baseTerritory, currency: options.currency, amount, rule: "atLeast" };
|
|
61582
|
+
return pickPoint(options.applePrices.map((customerPrice) => ({ customerPrice })), price)?.customerPrice;
|
|
61583
|
+
}
|
|
61584
|
+
function highestApplePrice(options) {
|
|
61585
|
+
return [...options.applePrices ?? []].sort((a, b) => Number(b) - Number(a))[0] ?? "none";
|
|
61586
|
+
}
|
|
61587
|
+
function skipReason(item, options) {
|
|
61588
|
+
if (item.hidden) return "hidden in the Wix catalog (unpublished, archived or not visible)";
|
|
61589
|
+
if (item.kind !== "digital") return SKIP_REASONS[item.kind];
|
|
61590
|
+
if (!item.price || normalizeAmount(item.price.amount) === "0") return "free \u2014 there is nothing to sell";
|
|
61591
|
+
if (item.price.currency !== options.currency) {
|
|
61592
|
+
return `priced in ${item.price.currency}; the base territory ${options.baseTerritory} prices in ${options.currency}`;
|
|
61593
|
+
}
|
|
61594
|
+
if (options.applePrices && !applePointFor(item.price.amount, options)) {
|
|
61595
|
+
return `priced above Apple's highest price point (${highestApplePrice(options)} ${options.currency}) in ${options.baseTerritory}`;
|
|
61596
|
+
}
|
|
61597
|
+
if ([item.name, item.description ?? ""].some(looksLikeCredential)) return "its name or description looks like a credential \u2014 fix the text in Wix";
|
|
61598
|
+
return void 0;
|
|
61599
|
+
}
|
|
61600
|
+
function priceFor(amount, options) {
|
|
61601
|
+
return { baseTerritory: options.baseTerritory, currency: options.currency, amount: normalizeAmount(amount), rule: "atLeast" };
|
|
61602
|
+
}
|
|
61603
|
+
function perItemProduct(item, options, names) {
|
|
61604
|
+
const plainDescription = item.description ? plainText2(item.description) : "";
|
|
61605
|
+
const name = nameOf(item);
|
|
61606
|
+
return {
|
|
61607
|
+
wix: { entityType: item.source, entityId: item.id },
|
|
61608
|
+
classification: "digital",
|
|
61609
|
+
entitlement: entityKey(item.source, item.id),
|
|
61610
|
+
productId: productIdFor(options.target.bundleId, SOURCE_SLUGS[item.source], item.id),
|
|
61611
|
+
type: "nonConsumable",
|
|
61612
|
+
referenceName: names.claim(name, item.id),
|
|
61613
|
+
localizations: [{ locale: options.locale, name: displayName(name), description: fit(plainDescription || `Unlock ${name}`, APPLE_LIMITS.description) }],
|
|
61614
|
+
price: priceFor(item.price.amount, options),
|
|
61615
|
+
territories: options.territories
|
|
61616
|
+
};
|
|
61617
|
+
}
|
|
61618
|
+
function tierKey(amount, currency) {
|
|
61619
|
+
return `${currency.toLowerCase()}_${normalizeAmount(amount).replace(".", "_")}`;
|
|
61620
|
+
}
|
|
61621
|
+
function unlockFor(item) {
|
|
61622
|
+
return { entityType: item.source, entityId: item.id, entitlement: entityKey(item.source, item.id) };
|
|
61623
|
+
}
|
|
61624
|
+
function tierPrice(amount, applePoint, options) {
|
|
61625
|
+
if (!applePoint) return priceFor(amount, options);
|
|
61626
|
+
return { baseTerritory: options.baseTerritory, currency: options.currency, amount: normalizeAmount(applePoint) };
|
|
61627
|
+
}
|
|
61628
|
+
function newTier(key2, price, options, names) {
|
|
61629
|
+
const label2 = `${price.amount} ${options.currency}`;
|
|
61630
|
+
return {
|
|
61631
|
+
unlocks: [],
|
|
61632
|
+
classification: "digital",
|
|
61633
|
+
entitlement: `tier:${key2}`,
|
|
61634
|
+
productId: tierProductId(options, key2),
|
|
61635
|
+
type: "consumable",
|
|
61636
|
+
referenceName: names.claim(`Credit ${label2}`, key2),
|
|
61637
|
+
localizations: [{ locale: options.locale, name: displayName(`Unlock (${label2})`), description: TIER_DESCRIPTION }],
|
|
61638
|
+
price,
|
|
61639
|
+
territories: options.territories
|
|
61640
|
+
};
|
|
61641
|
+
}
|
|
61642
|
+
function draftTiers(items, options, names, existingItems) {
|
|
61643
|
+
const byProductId = new Map(existingItems.map((i) => [i.productId, i]));
|
|
61644
|
+
const created = [];
|
|
61645
|
+
for (const item of items) {
|
|
61646
|
+
const applePoint = applePointFor(item.price.amount, options);
|
|
61647
|
+
const price = tierPrice(item.price.amount, applePoint, options);
|
|
61648
|
+
const key2 = tierKey(price.amount, options.currency);
|
|
61649
|
+
const productId = tierProductId(options, key2);
|
|
61650
|
+
let tier = byProductId.get(productId);
|
|
61651
|
+
if (!tier) {
|
|
61652
|
+
tier = newTier(key2, price, options, names);
|
|
61653
|
+
byProductId.set(productId, tier);
|
|
61654
|
+
created.push(tier);
|
|
61655
|
+
}
|
|
61656
|
+
tier.unlocks = [...tier.unlocks ?? [], unlockFor(item)];
|
|
61657
|
+
}
|
|
61658
|
+
return created;
|
|
61659
|
+
}
|
|
61660
|
+
function draftManifest(catalog, options) {
|
|
61661
|
+
const existingItems = structuredClone(options.existing?.items ?? []);
|
|
61662
|
+
const mapped = new Set(existingItems.flatMap((i) => wixEntitiesOf(i).map((e) => entityKey(e.entityType, e.entityId))));
|
|
61663
|
+
const names = new ReferenceNames(existingItems);
|
|
61664
|
+
const skipped = [];
|
|
61665
|
+
const toDraft = [];
|
|
61666
|
+
let kept = 0;
|
|
61667
|
+
for (const item of catalog) {
|
|
61668
|
+
if (mapped.has(entityKey(item.source, item.id))) kept++;
|
|
61669
|
+
else {
|
|
61670
|
+
const reason = skipReason(item, options);
|
|
61671
|
+
if (reason) skipped.push({ source: item.source, id: item.id, name: item.name, reason });
|
|
61672
|
+
else toDraft.push(item);
|
|
61673
|
+
}
|
|
61674
|
+
}
|
|
61675
|
+
const drafted = options.mode === "tiers" ? draftTiers(toDraft, options, names, existingItems) : toDraft.map((item) => perItemProduct(item, options, names));
|
|
61676
|
+
const items = [...existingItems, ...drafted];
|
|
61677
|
+
const newlyMapped = toDraft.length;
|
|
61678
|
+
if (!items.length) return { skipped, kept, newlyMapped };
|
|
61679
|
+
const manifest = { manifestVersion: 1, target: options.target, items };
|
|
61680
|
+
return { manifest: parseIapManifest(JSON.stringify(manifest), "the drafted manifest"), skipped, kept, newlyMapped };
|
|
61681
|
+
}
|
|
61682
|
+
var SOURCE_SLUGS, SKIP_REASONS, TIER_DESCRIPTION, ELLIPSIS, entityKey, sha, ENTITIES, MAX_SUFFIX, ReferenceNames, nameOf, tierProductId;
|
|
61683
|
+
var init_iap_draft = __esm({
|
|
61684
|
+
"../store-release/iap-draft.ts"() {
|
|
61685
|
+
init_iap_manifest();
|
|
61686
|
+
init_iap_pricing();
|
|
61687
|
+
SOURCE_SLUGS = { storesProduct: "product", onlineProgram: "program", pricingPlan: "plan" };
|
|
61688
|
+
SKIP_REASONS = {
|
|
61689
|
+
physical: "physical goods are never sold through in-app purchase",
|
|
61690
|
+
subscription: "a recurring plan needs an App Store subscription, which this version does not register",
|
|
61691
|
+
unknown: "the catalog does not say whether it is digital"
|
|
61692
|
+
};
|
|
61693
|
+
TIER_DESCRIPTION = "Unlocks one item in the app";
|
|
61694
|
+
ELLIPSIS = "\u2026";
|
|
61695
|
+
entityKey = (entityType, entityId) => `${entityType}:${entityId}`;
|
|
61696
|
+
sha = (text8) => createHash33("sha256").update(text8).digest("hex");
|
|
61697
|
+
ENTITIES = { "&": "&", " ": " ", "<": "<", ">": ">", """: '"', "'": "'" };
|
|
61698
|
+
MAX_SUFFIX = 24;
|
|
61699
|
+
ReferenceNames = class {
|
|
61700
|
+
taken = /* @__PURE__ */ new Set();
|
|
61701
|
+
constructor(existing) {
|
|
61702
|
+
for (const item of existing) this.taken.add(item.referenceName);
|
|
61703
|
+
}
|
|
61704
|
+
/** The name itself, else name · id prefix, lengthening the prefix (then a hash, then counting) until unused. */
|
|
61705
|
+
claim(name, disambiguator) {
|
|
61706
|
+
const plain2 = plainText2(name);
|
|
61707
|
+
for (const suffix of this.suffixes(disambiguator)) {
|
|
61708
|
+
const room = Math.max(1, APPLE_LIMITS.referenceName - suffix.length - 3);
|
|
61709
|
+
const candidate = suffix ? `${fit(plain2, room)} \xB7 ${suffix}` : fit(plain2, APPLE_LIMITS.referenceName);
|
|
61710
|
+
if (!this.taken.has(candidate)) {
|
|
61711
|
+
this.taken.add(candidate);
|
|
61712
|
+
return candidate;
|
|
61713
|
+
}
|
|
61714
|
+
}
|
|
61715
|
+
throw new Error(`could not find a unique reference name for "${plain2}"`);
|
|
61716
|
+
}
|
|
61717
|
+
/** Suffixes stay short (at most MAX_SUFFIX characters) so the name keeps room within Apple's 64. */
|
|
61718
|
+
*suffixes(disambiguator) {
|
|
61719
|
+
yield "";
|
|
61720
|
+
const compact = disambiguator.replace(/[^A-Za-z0-9]/g, "");
|
|
61721
|
+
for (let length = 8; length < Math.min(compact.length, MAX_SUFFIX); length += 4) yield compact.slice(0, length);
|
|
61722
|
+
if (compact.length <= MAX_SUFFIX) yield compact;
|
|
61723
|
+
const hash = sha(disambiguator).slice(0, 12);
|
|
61724
|
+
yield hash;
|
|
61725
|
+
for (let n = 2; n < 1e4; n++) yield `${hash}-${n}`;
|
|
61726
|
+
}
|
|
61727
|
+
};
|
|
61728
|
+
nameOf = (item) => plainText2(item.name) || item.id;
|
|
61729
|
+
tierProductId = (options, key2) => boundedProductId(options.target.bundleId, "tier", key2, key2);
|
|
61730
|
+
}
|
|
61731
|
+
});
|
|
61732
|
+
|
|
61185
61733
|
// src/base44-analyze/redact.ts
|
|
61186
61734
|
function isSecretKey(key2) {
|
|
61187
61735
|
const words = key2.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
@@ -61265,6 +61813,161 @@ var init_redact = __esm({
|
|
|
61265
61813
|
}
|
|
61266
61814
|
});
|
|
61267
61815
|
|
|
61816
|
+
// src/store-iap-catalog.ts
|
|
61817
|
+
function amountText(value) {
|
|
61818
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
61819
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
61820
|
+
}
|
|
61821
|
+
function priceOf(amount, currency) {
|
|
61822
|
+
const text8 = amountText(amount);
|
|
61823
|
+
return text8 && typeof currency === "string" && currency ? { amount: text8, currency } : void 0;
|
|
61824
|
+
}
|
|
61825
|
+
function catalogItem(fields) {
|
|
61826
|
+
return Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0 && v !== "" && v !== false));
|
|
61827
|
+
}
|
|
61828
|
+
function productKind(productType) {
|
|
61829
|
+
const type = String(productType ?? "").toLowerCase();
|
|
61830
|
+
return type === "digital" || type === "physical" ? type : "unknown";
|
|
61831
|
+
}
|
|
61832
|
+
async function allByCursor(wix, path2, key2, extra = {}) {
|
|
61833
|
+
const rows = [];
|
|
61834
|
+
let cursor;
|
|
61835
|
+
for (let page = 0; page < MAX_PAGES2; page++) {
|
|
61836
|
+
const cursorPaging = cursor ? { limit: PAGE2, cursor } : { limit: PAGE2 };
|
|
61837
|
+
const res = await withRetry(path2, () => wix("POST", path2, { ...extra, query: { cursorPaging } }));
|
|
61838
|
+
rows.push(...res[key2] ?? []);
|
|
61839
|
+
cursor = res.pagingMetadata?.cursors?.next;
|
|
61840
|
+
if (!cursor) return rows;
|
|
61841
|
+
}
|
|
61842
|
+
throw new Error(`${path2} had more than ${MAX_PAGES2} pages \u2014 refusing to draft from a partial catalog`);
|
|
61843
|
+
}
|
|
61844
|
+
async function allByOffset(fetchPage, label2) {
|
|
61845
|
+
const rows = [];
|
|
61846
|
+
for (let page = 0; page < MAX_PAGES2; page++) {
|
|
61847
|
+
const { rows: batch, total } = await withRetry(label2, () => fetchPage(rows.length));
|
|
61848
|
+
rows.push(...batch);
|
|
61849
|
+
const done = batch.length === 0 || (total !== void 0 ? rows.length >= total : batch.length < PAGE2);
|
|
61850
|
+
if (done) return rows;
|
|
61851
|
+
}
|
|
61852
|
+
throw new Error(`${label2} had more than ${MAX_PAGES2} pages \u2014 refusing to draft from a partial catalog`);
|
|
61853
|
+
}
|
|
61854
|
+
function fromV3Product(p) {
|
|
61855
|
+
return catalogItem({
|
|
61856
|
+
source: "storesProduct",
|
|
61857
|
+
id: p.id,
|
|
61858
|
+
name: p.name || p.id,
|
|
61859
|
+
description: p.plainDescription,
|
|
61860
|
+
kind: productKind(p.productType),
|
|
61861
|
+
price: priceOf(p.actualPriceRange?.minValue?.amount, p.currency),
|
|
61862
|
+
hidden: p.visible === false || void 0
|
|
61863
|
+
});
|
|
61864
|
+
}
|
|
61865
|
+
function fromV1Product(p) {
|
|
61866
|
+
return catalogItem({
|
|
61867
|
+
source: "storesProduct",
|
|
61868
|
+
id: p.id,
|
|
61869
|
+
name: p.name || p.id,
|
|
61870
|
+
description: typeof p.description === "string" ? p.description : void 0,
|
|
61871
|
+
kind: productKind(p.productType),
|
|
61872
|
+
price: priceOf(p.priceData?.price ?? p.price?.price, p.priceData?.currency ?? p.price?.currency),
|
|
61873
|
+
hidden: p.visible === false || void 0
|
|
61874
|
+
});
|
|
61875
|
+
}
|
|
61876
|
+
async function readStores(wix, notes) {
|
|
61877
|
+
try {
|
|
61878
|
+
return (await allByCursor(wix, "/stores/v3/products/query", "products", { fields: ["CURRENCY", "PLAIN_DESCRIPTION"] })).map(fromV3Product);
|
|
61879
|
+
} catch (e) {
|
|
61880
|
+
if (isNotInstalled2(e)) return notInstalled(notes, "Wix Stores");
|
|
61881
|
+
if (!isWrongCatalogVersion(e)) throw e;
|
|
61882
|
+
}
|
|
61883
|
+
notes.push("Wix Stores uses Catalog V1 on this site; read through the V1 product query.");
|
|
61884
|
+
try {
|
|
61885
|
+
const rows = await allByOffset(async (offset) => {
|
|
61886
|
+
const res = await wix("POST", "/stores/v1/products/query", { query: { paging: { limit: PAGE2, offset } } });
|
|
61887
|
+
return { rows: res.products ?? [], total: res.totalResults };
|
|
61888
|
+
}, "/stores/v1/products/query");
|
|
61889
|
+
return rows.map(fromV1Product);
|
|
61890
|
+
} catch (e) {
|
|
61891
|
+
if (isNotInstalled2(e)) return notInstalled(notes, "Wix Stores");
|
|
61892
|
+
throw e;
|
|
61893
|
+
}
|
|
61894
|
+
}
|
|
61895
|
+
function notInstalled(notes, vertical) {
|
|
61896
|
+
notes.push(`${vertical} is not installed on this site.`);
|
|
61897
|
+
return [];
|
|
61898
|
+
}
|
|
61899
|
+
function fromProgram(p) {
|
|
61900
|
+
return catalogItem({
|
|
61901
|
+
source: "onlineProgram",
|
|
61902
|
+
id: p.id,
|
|
61903
|
+
name: p.description?.title || p.id,
|
|
61904
|
+
description: p.description?.details,
|
|
61905
|
+
kind: "digital",
|
|
61906
|
+
price: priceOf(p.price?.value, p.price?.currency),
|
|
61907
|
+
hidden: p.status && p.status !== "PUBLISHED" || void 0
|
|
61908
|
+
});
|
|
61909
|
+
}
|
|
61910
|
+
async function readPrograms(wix, notes) {
|
|
61911
|
+
let listed;
|
|
61912
|
+
try {
|
|
61913
|
+
listed = await allByCursor(wix, "/online-programs/v3/programs/query", "programs");
|
|
61914
|
+
} catch (e) {
|
|
61915
|
+
if (isNotInstalled2(e)) return notInstalled(notes, "Online Programs");
|
|
61916
|
+
throw e;
|
|
61917
|
+
}
|
|
61918
|
+
const full = await mapLimit(
|
|
61919
|
+
listed,
|
|
61920
|
+
PROGRAM_READ_CONCURRENCY,
|
|
61921
|
+
(p) => withRetry(`program ${p.id}`, () => wix("GET", `/online-programs/v3/programs/${encodeURIComponent(p.id)}`))
|
|
61922
|
+
);
|
|
61923
|
+
return full.map((res) => fromProgram(res.program ?? {}));
|
|
61924
|
+
}
|
|
61925
|
+
function planKind(pricing) {
|
|
61926
|
+
if (pricing?.singlePaymentUnlimited) return "digital";
|
|
61927
|
+
return pricing?.subscription || pricing?.singlePaymentForDuration ? "subscription" : "unknown";
|
|
61928
|
+
}
|
|
61929
|
+
function fromPlan(p) {
|
|
61930
|
+
return catalogItem({
|
|
61931
|
+
source: "pricingPlan",
|
|
61932
|
+
id: p.id,
|
|
61933
|
+
name: p.name || p.id,
|
|
61934
|
+
description: p.description,
|
|
61935
|
+
kind: planKind(p.pricing),
|
|
61936
|
+
price: priceOf(p.pricing?.price?.value, p.pricing?.price?.currency),
|
|
61937
|
+
hidden: p.archived === true || p.public === false || void 0
|
|
61938
|
+
});
|
|
61939
|
+
}
|
|
61940
|
+
async function readPlans(wix, notes) {
|
|
61941
|
+
try {
|
|
61942
|
+
const rows = await allByOffset(async (offset) => {
|
|
61943
|
+
const res = await wix("GET", `/pricing-plans/v2/plans?limit=${PAGE2}&offset=${offset}`);
|
|
61944
|
+
return { rows: res.plans ?? [], total: res.pagingMetadata?.total };
|
|
61945
|
+
}, "/pricing-plans/v2/plans");
|
|
61946
|
+
return rows.map(fromPlan);
|
|
61947
|
+
} catch (e) {
|
|
61948
|
+
if (isNotInstalled2(e)) return notInstalled(notes, "Pricing Plans");
|
|
61949
|
+
throw e;
|
|
61950
|
+
}
|
|
61951
|
+
}
|
|
61952
|
+
async function readWixCatalog(wix) {
|
|
61953
|
+
const notes = [];
|
|
61954
|
+
const items = [...await readStores(wix, notes), ...await readPrograms(wix, notes), ...await readPlans(wix, notes)];
|
|
61955
|
+
notes.push(NEVER_DRAFTED_NOTE);
|
|
61956
|
+
return { items, notes };
|
|
61957
|
+
}
|
|
61958
|
+
var PAGE2, MAX_PAGES2, PROGRAM_READ_CONCURRENCY, NEVER_DRAFTED_NOTE, isNotInstalled2, isWrongCatalogVersion;
|
|
61959
|
+
var init_store_iap_catalog = __esm({
|
|
61960
|
+
"src/store-iap-catalog.ts"() {
|
|
61961
|
+
init_wix_owner();
|
|
61962
|
+
PAGE2 = 100;
|
|
61963
|
+
MAX_PAGES2 = 500;
|
|
61964
|
+
PROGRAM_READ_CONCURRENCY = 4;
|
|
61965
|
+
NEVER_DRAFTED_NOTE = "Bookings services and events are real-world services, which Apple does not allow as in-app purchases \u2014 they are never drafted.";
|
|
61966
|
+
isNotInstalled2 = (e) => e instanceof WixApiError && e.isAppNotInstalled;
|
|
61967
|
+
isWrongCatalogVersion = (e) => e instanceof WixApiError && e.status === 428 && /CATALOG_V1/.test(e.message);
|
|
61968
|
+
}
|
|
61969
|
+
});
|
|
61970
|
+
|
|
61268
61971
|
// src/store-iap.ts
|
|
61269
61972
|
var store_iap_exports = {};
|
|
61270
61973
|
__export(store_iap_exports, {
|
|
@@ -61275,7 +61978,7 @@ __export(store_iap_exports, {
|
|
|
61275
61978
|
runStoreIap: () => runStoreIap
|
|
61276
61979
|
});
|
|
61277
61980
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
61278
|
-
import { mkdirSync as mkdirSync32, readFileSync as readFileSync77, rmSync as rmSync16, writeFileSync as writeFileSync26 } from "node:fs";
|
|
61981
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync32, readFileSync as readFileSync77, rmSync as rmSync16, writeFileSync as writeFileSync26 } from "node:fs";
|
|
61279
61982
|
import { homedir as homedir7, hostname } from "node:os";
|
|
61280
61983
|
import { join as join65 } from "node:path";
|
|
61281
61984
|
function scrubDeep(value, scrub) {
|
|
@@ -61346,9 +62049,27 @@ function printItemPlan(out2, item) {
|
|
|
61346
62049
|
for (const note of item.notes) out2.line(` note: ${note}`);
|
|
61347
62050
|
for (const conflict of item.conflicts) out2.line(` \u2717 conflict: ${conflict}`);
|
|
61348
62051
|
}
|
|
62052
|
+
function countsLine(values, order) {
|
|
62053
|
+
return order.map((key2) => [key2, values.filter((v) => v === key2).length]).filter(([, n]) => n > 0).map(([key2, n]) => `${key2} ${n}`).join(" \xB7 ");
|
|
62054
|
+
}
|
|
62055
|
+
function priceIncreases(plan) {
|
|
62056
|
+
return plan.items.flatMap((item, i) => {
|
|
62057
|
+
const requested = plan.manifest.items[i].price;
|
|
62058
|
+
if (!item.applePrice || normalizeAmount(item.applePrice) === normalizeAmount(requested.amount)) return [];
|
|
62059
|
+
return [`${item.productId} ${requested.amount} \u2192 ${item.applePrice} ${requested.currency}`];
|
|
62060
|
+
});
|
|
62061
|
+
}
|
|
61349
62062
|
function printPlanItems(out2, plan) {
|
|
61350
62063
|
printHeader(out2, plan.identity, plan.target);
|
|
61351
|
-
|
|
62064
|
+
if (plan.items.length <= SUMMARY_THRESHOLD) {
|
|
62065
|
+
for (const item of plan.items) printItemPlan(out2, item);
|
|
62066
|
+
} else {
|
|
62067
|
+
out2.line(` ${plan.items.length} products: ${countsLine(plan.items.map((i) => i.decision), DECISION_ORDER)}`);
|
|
62068
|
+
const increases = priceIncreases(plan);
|
|
62069
|
+
if (increases.length) out2.line(` price: ${increases.length} take a higher Apple price point than requested, e.g. ${increases.slice(0, 3).join(" \xB7 ")}`);
|
|
62070
|
+
for (const item of plan.items.filter((i) => i.decision === "conflict").slice(0, SUMMARY_DETAIL_LIMIT)) printItemPlan(out2, item);
|
|
62071
|
+
out2.line(" (every product is listed in the plan file)");
|
|
62072
|
+
}
|
|
61352
62073
|
out2.line();
|
|
61353
62074
|
}
|
|
61354
62075
|
function printPlan(out2, plan, planPath) {
|
|
@@ -61384,14 +62105,22 @@ function printItemResult(out2, item) {
|
|
|
61384
62105
|
}
|
|
61385
62106
|
function printResult(out2, result, resultPath) {
|
|
61386
62107
|
printHeader(out2, result.identity, result.target);
|
|
61387
|
-
|
|
62108
|
+
if (result.items.length <= SUMMARY_THRESHOLD) {
|
|
62109
|
+
for (const item of result.items) printItemResult(out2, item);
|
|
62110
|
+
} else {
|
|
62111
|
+
out2.line(` ${result.items.length} products: ${countsLine(result.items.map((i) => i.provisioning.outcome), OUTCOME_ORDER)}`);
|
|
62112
|
+
const unfinished = result.items.filter((i) => !COMPLETE.has(i.provisioning.outcome));
|
|
62113
|
+
for (const item of unfinished.slice(0, SUMMARY_DETAIL_LIMIT)) printItemResult(out2, item);
|
|
62114
|
+
if (unfinished.length > SUMMARY_DETAIL_LIMIT) out2.line(` \u2026and ${unfinished.length - SUMMARY_DETAIL_LIMIT} more not complete (see the result file)`);
|
|
62115
|
+
}
|
|
61388
62116
|
out2.line();
|
|
61389
62117
|
if (resultPath) out2.line(` result written to ${resultPath}`);
|
|
61390
62118
|
out2.line(" backend sync: not configured \xB7 checkout: not active \u2014 the result is a mapping candidate, not an activation.");
|
|
61391
62119
|
}
|
|
61392
|
-
function applyQuestion(plan) {
|
|
62120
|
+
function applyQuestion(plan, limit) {
|
|
61393
62121
|
const operations = plan.items.reduce((n, item) => n + item.ops.length, 0);
|
|
61394
|
-
|
|
62122
|
+
const cap = limit === void 0 ? "" : `, creating at most ${limit} new product(s) in this run`;
|
|
62123
|
+
return ` As profile "${plan.identity.profile}" (team ${plan.identity.team}), apply ${operations} planned operation(s) to App Store Connect app ${plan.target.appleAppId} (${plan.target.bundleId})${cap}?`;
|
|
61395
62124
|
}
|
|
61396
62125
|
async function planCommand(flags, deps, out2) {
|
|
61397
62126
|
const manifestPath = requireFlag(flags, "manifest", "plan");
|
|
@@ -61406,10 +62135,17 @@ async function planCommand(flags, deps, out2) {
|
|
|
61406
62135
|
else printPlan(out2, plan, planPath);
|
|
61407
62136
|
return plan.items.some((i) => i.decision === "conflict") ? 1 : 0;
|
|
61408
62137
|
}
|
|
61409
|
-
|
|
62138
|
+
function createLimit(flags) {
|
|
62139
|
+
const value = textFlag(flags, "limit");
|
|
62140
|
+
if (value === void 0) return void 0;
|
|
62141
|
+
if (!/^[1-9]\d*$/.test(value)) throw new UsageError(`--limit must be a positive whole number (got ${JSON.stringify(value)})`);
|
|
62142
|
+
return Number(value);
|
|
62143
|
+
}
|
|
62144
|
+
async function applyWithLock(deps, plan, session, limit) {
|
|
61410
62145
|
const release = deps.acquireLock(plan.target.appleAppId);
|
|
61411
62146
|
try {
|
|
61412
|
-
|
|
62147
|
+
const context = { api: session.api, identity: session.identity, now: deps.now, sleep: deps.sleep, journal: session.journal };
|
|
62148
|
+
return await applyIap(plan, limit === void 0 ? context : { ...context, createLimit: limit });
|
|
61413
62149
|
} finally {
|
|
61414
62150
|
release();
|
|
61415
62151
|
}
|
|
@@ -61418,6 +62154,7 @@ async function applyCommand(flags, deps, out2) {
|
|
|
61418
62154
|
const planPath = requireFlag(flags, "plan", "apply");
|
|
61419
62155
|
const profile = requireFlag(flags, "profile", "apply");
|
|
61420
62156
|
const resultPath = requireFlag(flags, "out", "apply");
|
|
62157
|
+
const limit = createLimit(flags);
|
|
61421
62158
|
const confirmed = flags.yes === true;
|
|
61422
62159
|
const canAsk = out2.interactive && !out2.json && deps.canPrompt();
|
|
61423
62160
|
if (!confirmed && !canAsk) {
|
|
@@ -61429,11 +62166,11 @@ async function applyCommand(flags, deps, out2) {
|
|
|
61429
62166
|
const plan = readPlan(deps, planPath);
|
|
61430
62167
|
const session = openSession(deps, out2, plan.target, profile);
|
|
61431
62168
|
if (!out2.json) printApplyPreview(out2, plan, planPath);
|
|
61432
|
-
if (!confirmed && !await deps.confirm(applyQuestion(plan))) {
|
|
62169
|
+
if (!confirmed && !await deps.confirm(applyQuestion(plan, limit))) {
|
|
61433
62170
|
out2.line(" aborted \u2014 nothing was changed in App Store Connect.");
|
|
61434
62171
|
return 1;
|
|
61435
62172
|
}
|
|
61436
|
-
const result = await applyWithLock(deps, plan, session);
|
|
62173
|
+
const result = await applyWithLock(deps, plan, session, limit);
|
|
61437
62174
|
writeJson(deps, out2, resultPath, result);
|
|
61438
62175
|
if (out2.json) out2.print({ ok: result.complete, result: resultPath, items: result.items });
|
|
61439
62176
|
else printResult(out2, result, resultPath);
|
|
@@ -61451,6 +62188,104 @@ async function statusCommand(flags, deps, out2) {
|
|
|
61451
62188
|
else printResult(out2, result, resultPath);
|
|
61452
62189
|
return result.complete ? 0 : 1;
|
|
61453
62190
|
}
|
|
62191
|
+
function draftMode(flags) {
|
|
62192
|
+
const value = textFlag(flags, "mode") ?? "per-item";
|
|
62193
|
+
const mode = DRAFT_MODES[value];
|
|
62194
|
+
if (!mode) throw new UsageError(`--mode must be per-item or tiers (got ${JSON.stringify(value)})`);
|
|
62195
|
+
return mode;
|
|
62196
|
+
}
|
|
62197
|
+
function territoriesFlag(flags, base) {
|
|
62198
|
+
const territories = (textFlag(flags, "territories") ?? base).split(",").map((t) => t.trim().toUpperCase()).filter(Boolean);
|
|
62199
|
+
const bad2 = territories.find((t) => !TERRITORY_CODE.test(t));
|
|
62200
|
+
if (bad2) throw new UsageError(`--territories must be ISO 3166-1 alpha-3 codes such as USA (got ${JSON.stringify(bad2)})`);
|
|
62201
|
+
const duplicate = territories.find((t, i) => territories.indexOf(t) !== i);
|
|
62202
|
+
if (duplicate) throw new UsageError(`--territories: duplicate territory ${duplicate}`);
|
|
62203
|
+
if (!territories.includes(base)) throw new UsageError(`--territories must include the base territory ${base}`);
|
|
62204
|
+
return territories;
|
|
62205
|
+
}
|
|
62206
|
+
function localeFlag(flags) {
|
|
62207
|
+
const locale = textFlag(flags, "locale") ?? "en-US";
|
|
62208
|
+
if (!LOCALE_CODE.test(locale)) throw new UsageError(`--locale must be a locale such as en-US (got ${JSON.stringify(locale)})`);
|
|
62209
|
+
return locale;
|
|
62210
|
+
}
|
|
62211
|
+
function baseTerritoryFlag(flags) {
|
|
62212
|
+
const base = requireFlag(flags, "base-territory", "draft").toUpperCase();
|
|
62213
|
+
if (!TERRITORY_CODE.test(base)) throw new UsageError(`--base-territory must be an ISO 3166-1 alpha-3 code such as USA (got ${JSON.stringify(base)})`);
|
|
62214
|
+
return base;
|
|
62215
|
+
}
|
|
62216
|
+
async function applePriceGrid(session, target, baseTerritory) {
|
|
62217
|
+
return withAccessContext(session.identity, target, async () => {
|
|
62218
|
+
const [anyProduct] = await session.api.listProducts(target.appleAppId);
|
|
62219
|
+
return anyProduct ? new PriceGrid(session.api).amountsIn(baseTerritory, anyProduct.id) : void 0;
|
|
62220
|
+
});
|
|
62221
|
+
}
|
|
62222
|
+
function readMergeManifest(deps, path2, target) {
|
|
62223
|
+
if (!path2) return void 0;
|
|
62224
|
+
const existing = readManifest2(deps, path2);
|
|
62225
|
+
if (existing.target.wixSiteId !== target.wixSiteId) {
|
|
62226
|
+
throw new Error(`${path2} is for Wix site ${existing.target.wixSiteId}, not ${target.wixSiteId} \u2014 refusing to carry another site's mappings into this one.`);
|
|
62227
|
+
}
|
|
62228
|
+
if (existing.target.appleAppId !== target.appleAppId || existing.target.bundleId !== target.bundleId) {
|
|
62229
|
+
throw new Error(`${path2} is for app ${existing.target.appleAppId} (${existing.target.bundleId}), not ${target.appleAppId} (${target.bundleId}) \u2014 refusing to merge another app's mapping.`);
|
|
62230
|
+
}
|
|
62231
|
+
return existing;
|
|
62232
|
+
}
|
|
62233
|
+
async function baseCurrency(session, target, baseTerritory) {
|
|
62234
|
+
const territories = await withAccessContext(session.identity, target, () => session.api.listTerritories());
|
|
62235
|
+
const currency = territories.find((t) => t.id === baseTerritory)?.currency;
|
|
62236
|
+
if (!currency) throw new Error(`App Store Connect has no territory ${baseTerritory} \u2014 pass an ISO 3166-1 alpha-3 code such as USA or DEU.`);
|
|
62237
|
+
return currency;
|
|
62238
|
+
}
|
|
62239
|
+
function skippedSummary(draft) {
|
|
62240
|
+
const counts = /* @__PURE__ */ new Map();
|
|
62241
|
+
for (const s of draft.skipped) counts.set(s.reason, (counts.get(s.reason) ?? 0) + 1);
|
|
62242
|
+
return [...counts].map(([reason, n]) => `${reason} \xD7${n}`).join(" \xB7 ");
|
|
62243
|
+
}
|
|
62244
|
+
function printDraft(out2, catalog, draft, args) {
|
|
62245
|
+
out2.line(` ${pad("catalog")}${catalog.items.length} Wix item(s) read`);
|
|
62246
|
+
for (const note of args.notes) out2.line(` ${pad("")}${note}`);
|
|
62247
|
+
const kept = args.mergePath ? ` \xB7 kept ${draft.kept} from ${args.mergePath}` : "";
|
|
62248
|
+
out2.line(` ${pad("drafted")}${args.drafted} new product(s) for ${draft.newlyMapped} Wix item(s)${kept}`);
|
|
62249
|
+
if (draft.skipped.length) out2.line(` ${pad("skipped")}${draft.skipped.length} \u2014 ${skippedSummary(draft)}`);
|
|
62250
|
+
out2.line();
|
|
62251
|
+
if (!draft.manifest) {
|
|
62252
|
+
out2.line(" nothing in the catalog can be sold through in-app purchase \u2014 no manifest written.");
|
|
62253
|
+
return;
|
|
62254
|
+
}
|
|
62255
|
+
out2.line(` manifest written to ${args.outPath} \u2014 review or trim it if you like; nothing was changed in App Store Connect.`);
|
|
62256
|
+
out2.line(` next: himi store iap plan --manifest ${args.outPath} --profile ${args.profile} --out ./iap-plan.json`);
|
|
62257
|
+
}
|
|
62258
|
+
async function draftCommand(flags, deps, out2) {
|
|
62259
|
+
const site = requireFlag(flags, "site", "draft");
|
|
62260
|
+
const profile = requireFlag(flags, "profile", "draft");
|
|
62261
|
+
const appleAppId = requireFlag(flags, "apple-app", "draft");
|
|
62262
|
+
const bundleId = requireFlag(flags, "bundle", "draft");
|
|
62263
|
+
const baseTerritory = baseTerritoryFlag(flags);
|
|
62264
|
+
const outPath = requireFlag(flags, "out", "draft");
|
|
62265
|
+
const mode = draftMode(flags);
|
|
62266
|
+
const territories = territoriesFlag(flags, baseTerritory);
|
|
62267
|
+
const locale = localeFlag(flags);
|
|
62268
|
+
const mergePath = textFlag(flags, "merge");
|
|
62269
|
+
const target = { himiApp: textFlag(flags, "himi-app") ?? bundleId, wixSiteId: site, appleAppId, bundleId, teamId: deps.profileTeam(profile), profile };
|
|
62270
|
+
const existing = readMergeManifest(deps, mergePath, target);
|
|
62271
|
+
const session = openSession(deps, out2, target, profile);
|
|
62272
|
+
const currency = await baseCurrency(session, target, baseTerritory);
|
|
62273
|
+
const applePrices = await applePriceGrid(session, target, baseTerritory);
|
|
62274
|
+
const catalog = await readWixCatalog(deps.wixTransport(site));
|
|
62275
|
+
const notes = mode === "tiers" && !applePrices ? [...catalog.notes, GRID_UNAVAILABLE_NOTE] : catalog.notes;
|
|
62276
|
+
const draft = draftManifest(catalog.items, { target, mode, baseTerritory, currency, territories, locale, existing, ...applePrices ? { applePrices } : {} });
|
|
62277
|
+
const drafted = (draft.manifest?.items.length ?? 0) - (existing?.items.length ?? 0);
|
|
62278
|
+
if (draft.manifest) writeJson(deps, out2, outPath, draft.manifest);
|
|
62279
|
+
const report3 = { ok: draft.manifest !== void 0, manifest: draft.manifest ? outPath : null, mode: textFlag(flags, "mode") ?? "per-item", drafted, newlyMapped: draft.newlyMapped, kept: draft.kept, skipped: draft.skipped, notes };
|
|
62280
|
+
const reportPath = textFlag(flags, "report");
|
|
62281
|
+
if (reportPath) writeJson(deps, out2, reportPath, report3);
|
|
62282
|
+
if (out2.json) out2.print(report3);
|
|
62283
|
+
else {
|
|
62284
|
+
printHeader(out2, session.identity, target);
|
|
62285
|
+
printDraft(out2, catalog, draft, { drafted, outPath, profile, mergePath, notes });
|
|
62286
|
+
}
|
|
62287
|
+
return draft.manifest ? 0 : 1;
|
|
62288
|
+
}
|
|
61454
62289
|
async function runStoreIap(pos, flags, io, deps) {
|
|
61455
62290
|
const out2 = new CommandOutput(io, flags.json === true);
|
|
61456
62291
|
const sub = pos[2];
|
|
@@ -61463,18 +62298,10 @@ async function runStoreIap(pos, flags, io, deps) {
|
|
|
61463
62298
|
return failFrom(e, out2);
|
|
61464
62299
|
}
|
|
61465
62300
|
}
|
|
61466
|
-
function readJournalEntries(path2) {
|
|
61467
|
-
try {
|
|
61468
|
-
const doc = JSON.parse(readFileSync77(path2, "utf8"));
|
|
61469
|
-
return Array.isArray(doc?.entries) ? doc.entries : [];
|
|
61470
|
-
} catch {
|
|
61471
|
-
return [];
|
|
61472
|
-
}
|
|
61473
|
-
}
|
|
61474
62301
|
function fileJournal(path2) {
|
|
61475
62302
|
return {
|
|
61476
|
-
record: (entry) =>
|
|
61477
|
-
|
|
62303
|
+
record: (entry) => appendFileSync2(path2, `${JSON.stringify(entry)}
|
|
62304
|
+
`, { mode: 384 })
|
|
61478
62305
|
};
|
|
61479
62306
|
}
|
|
61480
62307
|
function readLockSummary(path2) {
|
|
@@ -61527,26 +62354,35 @@ function defaultStoreIapDeps() {
|
|
|
61527
62354
|
writeFileAtomic,
|
|
61528
62355
|
resolveCredentials: ({ profileFlag, target }) => resolveIapCredentials({ profileFlag, target }),
|
|
61529
62356
|
createApi: (key2) => createIapApi(createAscClient({ key: key2 })),
|
|
61530
|
-
openJournal: (appleAppId) => fileJournal(join65(stateDir(appleAppId), "journal.
|
|
62357
|
+
openJournal: (appleAppId) => fileJournal(join65(stateDir(appleAppId), "journal.jsonl")),
|
|
61531
62358
|
acquireLock: (appleAppId) => acquireApplyLock(stateDir(appleAppId), appleAppId),
|
|
61532
62359
|
canPrompt: () => process.stdin.isTTY === true,
|
|
62360
|
+
wixTransport: (siteId) => ownerTransport(siteId),
|
|
62361
|
+
profileTeam: (profile) => resolveStoreCredentials({ app: profile, cfg: {}, profileFlag: profile, env: {} }).team,
|
|
61533
62362
|
confirm: confirmOnTerminal,
|
|
61534
62363
|
now: () => /* @__PURE__ */ new Date(),
|
|
61535
62364
|
sleep: (ms) => new Promise((resolve56) => setTimeout(resolve56, ms))
|
|
61536
62365
|
};
|
|
61537
62366
|
}
|
|
61538
|
-
var STORE_IAP_USAGE, UsageError, CommandOutput, pad, COMPLETE, COMMANDS;
|
|
62367
|
+
var STORE_IAP_USAGE, SUMMARY_THRESHOLD, SUMMARY_DETAIL_LIMIT, UsageError, CommandOutput, pad, DECISION_ORDER, OUTCOME_ORDER, COMPLETE, DRAFT_MODES, TERRITORY_CODE, LOCALE_CODE, GRID_UNAVAILABLE_NOTE, COMMANDS;
|
|
61539
62368
|
var init_store_iap = __esm({
|
|
61540
62369
|
"src/store-iap.ts"() {
|
|
61541
62370
|
init_iap_api();
|
|
61542
62371
|
init_iap_apply();
|
|
61543
62372
|
init_iap_client();
|
|
62373
|
+
init_credential_profile();
|
|
61544
62374
|
init_iap_credentials();
|
|
62375
|
+
init_iap_draft();
|
|
61545
62376
|
init_iap_manifest();
|
|
62377
|
+
init_iap_pricing();
|
|
61546
62378
|
init_iap_plan();
|
|
61547
62379
|
init_redact();
|
|
61548
62380
|
init_progress();
|
|
61549
|
-
|
|
62381
|
+
init_store_iap_catalog();
|
|
62382
|
+
init_wix_owner();
|
|
62383
|
+
STORE_IAP_USAGE = "himi store iap draft|plan|apply|status \u2014 draft --site <msid> --profile <name> --apple-app <id> --bundle <id> --base-territory <ISO3> --out <manifest.json> [--territories <a,b>] [--locale <l>] [--mode per-item|tiers] [--merge <manifest.json>] [--report <file>] \xB7 plan --manifest <file> --profile <name> --out <plan.json> \xB7 apply --plan <plan.json> --profile <name> --out <result.json> [--yes] [--limit <n>] \xB7 status --manifest <file> --profile <name> [--out <file>]";
|
|
62384
|
+
SUMMARY_THRESHOLD = 50;
|
|
62385
|
+
SUMMARY_DETAIL_LIMIT = 20;
|
|
61550
62386
|
UsageError = class extends Error {
|
|
61551
62387
|
};
|
|
61552
62388
|
CommandOutput = class {
|
|
@@ -61585,8 +62421,15 @@ var init_store_iap = __esm({
|
|
|
61585
62421
|
}
|
|
61586
62422
|
};
|
|
61587
62423
|
pad = (label2, width = 12) => label2.padEnd(width);
|
|
62424
|
+
DECISION_ORDER = ["create", "update", "reuse", "conflict"];
|
|
62425
|
+
OUTCOME_ORDER = ["created", "updated", "reused", "conflict", "partial", "failed", "deferred", "absent", "incomplete"];
|
|
61588
62426
|
COMPLETE = /* @__PURE__ */ new Set(["created", "updated", "reused"]);
|
|
62427
|
+
DRAFT_MODES = { "per-item": "perItem", tiers: "tiers" };
|
|
62428
|
+
TERRITORY_CODE = /^[A-Z]{3}$/;
|
|
62429
|
+
LOCALE_CODE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
62430
|
+
GRID_UNAVAILABLE_NOTE = "the app has no in-app purchase yet to read Apple's price grid through, so tiers are keyed by Wix price \u2014 prices that resolve to the same Apple point get separate tiers; redraft after the first product exists to merge them";
|
|
61589
62431
|
COMMANDS = {
|
|
62432
|
+
draft: draftCommand,
|
|
61590
62433
|
plan: planCommand,
|
|
61591
62434
|
apply: applyCommand,
|
|
61592
62435
|
status: statusCommand
|
|
@@ -61971,7 +62814,7 @@ __export(preflight_app_exports, {
|
|
|
61971
62814
|
profileAccount: () => profileAccount
|
|
61972
62815
|
});
|
|
61973
62816
|
import { readFileSync as readFileSync78, existsSync as existsSync67, readdirSync as readdirSync31, statSync as statSync18 } from "node:fs";
|
|
61974
|
-
import { createHash as
|
|
62817
|
+
import { createHash as createHash34 } from "node:crypto";
|
|
61975
62818
|
import { join as join66 } from "node:path";
|
|
61976
62819
|
function analyze(projectYml, storeConfig, icon, account) {
|
|
61977
62820
|
const f = [];
|
|
@@ -62260,7 +63103,7 @@ function placeholderHashes2() {
|
|
|
62260
63103
|
const p = join66(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
|
|
62261
63104
|
try {
|
|
62262
63105
|
if (!statSync18(p).isFile()) continue;
|
|
62263
|
-
return [LEGACY_PLACEHOLDER_SHA2562,
|
|
63106
|
+
return [LEGACY_PLACEHOLDER_SHA2562, createHash34("sha256").update(readFileSync78(p)).digest("hex")];
|
|
62264
63107
|
} catch {
|
|
62265
63108
|
}
|
|
62266
63109
|
}
|
|
@@ -62297,7 +63140,7 @@ async function appIconState(iosDir) {
|
|
|
62297
63140
|
try {
|
|
62298
63141
|
if (!statSync18(p).isFile()) continue;
|
|
62299
63142
|
const bytes2 = readFileSync78(p);
|
|
62300
|
-
const hash =
|
|
63143
|
+
const hash = createHash34("sha256").update(bytes2).digest("hex");
|
|
62301
63144
|
const pixels = await pixelIssues(bytes2);
|
|
62302
63145
|
return {
|
|
62303
63146
|
exists: true,
|
|
@@ -62870,7 +63713,7 @@ var init_site_member = __esm({
|
|
|
62870
63713
|
});
|
|
62871
63714
|
|
|
62872
63715
|
// src/notify-ping-all.ts
|
|
62873
|
-
import { createHash as
|
|
63716
|
+
import { createHash as createHash35 } from "node:crypto";
|
|
62874
63717
|
async function listApprovedMembers(input) {
|
|
62875
63718
|
const first = await scanMembers(input);
|
|
62876
63719
|
const second = await scanMembers(input);
|
|
@@ -62924,7 +63767,7 @@ async function scanMembers(input) {
|
|
|
62924
63767
|
}
|
|
62925
63768
|
}
|
|
62926
63769
|
function campaignRequestId(campaignId, memberId) {
|
|
62927
|
-
const bytes2 =
|
|
63770
|
+
const bytes2 = createHash35("sha256").update(JSON.stringify(["himi-push-campaign", campaignId, memberId])).digest().subarray(0, 16);
|
|
62928
63771
|
bytes2[6] = bytes2[6] & 15 | 80;
|
|
62929
63772
|
bytes2[8] = bytes2[8] & 63 | 128;
|
|
62930
63773
|
const hex2 = bytes2.toString("hex");
|
|
@@ -64084,7 +64927,7 @@ var init_copy_clean = __esm({
|
|
|
64084
64927
|
|
|
64085
64928
|
// src/site-analyze/published-html.ts
|
|
64086
64929
|
function textFromHtml(fragment) {
|
|
64087
|
-
return fragment.replace(/<[^>]*>/g, " ").replace(/&(amp|lt|gt|quot|#39|apos|nbsp);/g, (e) =>
|
|
64930
|
+
return fragment.replace(/<[^>]*>/g, " ").replace(/&(amp|lt|gt|quot|#39|apos|nbsp);/g, (e) => ENTITIES2[e] ?? " ").replace(/&#x?[0-9a-f]+;/gi, " ");
|
|
64088
64931
|
}
|
|
64089
64932
|
function unescapeJsonString(s) {
|
|
64090
64933
|
return s.replace(/\\"/g, '"').replace(/\\n/g, " ").replace(/\\\\/g, "\\");
|
|
@@ -64223,7 +65066,7 @@ function appsFromDefIds(ids) {
|
|
|
64223
65066
|
return name ? { id, name } : { id };
|
|
64224
65067
|
});
|
|
64225
65068
|
}
|
|
64226
|
-
var GRID_APP_ID_RE, MAIN_PAGE_RE, APP_DEF_RE, PAGE_ENTRY_RE, OG_IMAGE_RE, ICON_RE, HTML_LANG_RE, META_DESC_RE, OG_TITLE_RE, OG_DESC_RE, HEADING_RE, SKIP_SLUG, POPUP, WELL_KNOWN_WIX_APPS,
|
|
65069
|
+
var GRID_APP_ID_RE, MAIN_PAGE_RE, APP_DEF_RE, PAGE_ENTRY_RE, OG_IMAGE_RE, ICON_RE, HTML_LANG_RE, META_DESC_RE, OG_TITLE_RE, OG_DESC_RE, HEADING_RE, SKIP_SLUG, POPUP, WELL_KNOWN_WIX_APPS, ENTITIES2;
|
|
64227
65070
|
var init_published_html = __esm({
|
|
64228
65071
|
"src/site-analyze/published-html.ts"() {
|
|
64229
65072
|
init_rest();
|
|
@@ -64254,7 +65097,7 @@ var init_published_html = __esm({
|
|
|
64254
65097
|
"14cc59bc-f0b7-15b8-e1c7-89ce41d0e0c9": "Wix Bookings Service Pages",
|
|
64255
65098
|
"215238eb-22a5-4c36-9e7b-e7c08025e04e": "Wix Forms"
|
|
64256
65099
|
};
|
|
64257
|
-
|
|
65100
|
+
ENTITIES2 = {
|
|
64258
65101
|
"&": "&",
|
|
64259
65102
|
"<": "<",
|
|
64260
65103
|
">": ">",
|
|
@@ -73098,10 +73941,10 @@ async function installVerticals(msid, wanted, token, fetchImpl = fetch, timeoutM
|
|
|
73098
73941
|
body = text8 ? JSON.parse(text8) : {};
|
|
73099
73942
|
} catch {
|
|
73100
73943
|
}
|
|
73101
|
-
const
|
|
73944
|
+
const nameOf2 = (appDefId) => wanted.find((w) => w.appDefId === appDefId)?.vertical ?? appDefId;
|
|
73102
73945
|
const out2 = { installed: [], errors: [], needsSetup: [] };
|
|
73103
73946
|
for (const [appDefId, info2] of Object.entries(body.installedApps ?? {})) {
|
|
73104
|
-
const vertical =
|
|
73947
|
+
const vertical = nameOf2(appDefId);
|
|
73105
73948
|
out2.installed.push({ vertical, appDefId, version: info2?.version });
|
|
73106
73949
|
const actions = (info2?.requiredActions ?? []).filter((a) => a?.actionType || a?.link);
|
|
73107
73950
|
for (const a of actions) {
|
|
@@ -73114,7 +73957,7 @@ async function installVerticals(msid, wanted, token, fetchImpl = fetch, timeoutM
|
|
|
73114
73957
|
}
|
|
73115
73958
|
}
|
|
73116
73959
|
for (const [appDefId, err] of Object.entries(body.installErrors ?? {})) {
|
|
73117
|
-
out2.errors.push({ vertical:
|
|
73960
|
+
out2.errors.push({ vertical: nameOf2(appDefId), appDefId, message: err?.message ?? "unknown" });
|
|
73118
73961
|
}
|
|
73119
73962
|
return out2;
|
|
73120
73963
|
}
|
|
@@ -73981,7 +74824,7 @@ var init_context = __esm({
|
|
|
73981
74824
|
import { mkdtempSync as mkdtempSync5, readFileSync as readFileSync88, mkdirSync as mkdirSync39, writeFileSync as writeFileSync35, existsSync as existsSync72, rmSync as rmSync20, readdirSync as readdirSync32, statSync as statSync20, lstatSync as lstatSync16, accessSync, constants as constants3, renameSync as renameSync9, cpSync as cpSync3 } from "node:fs";
|
|
73982
74825
|
import { execFile as execFile2, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn7 } from "node:child_process";
|
|
73983
74826
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
73984
|
-
import { createHash as
|
|
74827
|
+
import { createHash as createHash36, randomUUID as randomUUID8 } from "node:crypto";
|
|
73985
74828
|
import { tmpdir as tmpdir6, homedir as homedir10, hostname as hostname2 } from "node:os";
|
|
73986
74829
|
import { join as join73, resolve as resolve55, relative as relative14, basename as basename17, dirname as dirname40 } from "node:path";
|
|
73987
74830
|
import { fileURLToPath as fileURLToPath26 } from "node:url";
|
|
@@ -74550,11 +75393,11 @@ async function loadReleaseFromDir(app, dir) {
|
|
|
74550
75393
|
const bundles = {};
|
|
74551
75394
|
const seenSha = /* @__PURE__ */ new Set();
|
|
74552
75395
|
for (const meta of Object.values(manifest.bundles ?? {})) {
|
|
74553
|
-
const
|
|
74554
|
-
if (!
|
|
74555
|
-
seenSha.add(
|
|
74556
|
-
const b = await store.getBundle(app,
|
|
74557
|
-
if (b) bundles[
|
|
75396
|
+
const sha2 = meta?.sha256;
|
|
75397
|
+
if (!sha2 || seenSha.has(sha2)) continue;
|
|
75398
|
+
seenSha.add(sha2);
|
|
75399
|
+
const b = await store.getBundle(app, sha2);
|
|
75400
|
+
if (b) bundles[sha2] = asBuffer(b).toString("base64");
|
|
74558
75401
|
}
|
|
74559
75402
|
const tokensBytes = await store.getTokens(app);
|
|
74560
75403
|
const tokens2 = tokensBytes ? utf8Decode(tokensBytes) : void 0;
|
|
@@ -76621,16 +77464,28 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
76621
77464
|
team (guideline 4.2.6), declared per-app as
|
|
76622
77465
|
ios.credentialProfile in store.config.yaml and resolved
|
|
76623
77466
|
from ~/.himalaya/store-credentials/<name>.json.
|
|
77467
|
+
himi store iap draft --site <msid> --profile <name> --apple-app <id> --bundle <id> --base-territory <ISO3>
|
|
77468
|
+
--out <manifest.json> [--territories <a,b>] [--locale <l>] [--mode per-item|tiers]
|
|
77469
|
+
[--merge <manifest.json>] [--report <file>] [--json]
|
|
77470
|
+
READ-ONLY. Read the site's whole Wix catalog (Stores, Online
|
|
77471
|
+
Programs, Pricing Plans) and write the manifest for you:
|
|
77472
|
+
digital, priced items only (physical, recurring, free and
|
|
77473
|
+
hidden ones are skipped with a reason), stable product ids,
|
|
77474
|
+
prices at the next Apple price point up. --mode tiers makes
|
|
77475
|
+
one consumable per price instead. --merge keeps an earlier
|
|
77476
|
+
manifest's mappings (their product ids are permanent).
|
|
76624
77477
|
himi store iap plan --manifest <file> --profile <name> --out <plan.json> [--json]
|
|
76625
77478
|
READ-ONLY. Verify the owner's App Store Connect app and
|
|
76626
77479
|
decide which selected digital products (one-time
|
|
76627
77480
|
non-consumables) to register; writes a reviewable plan.
|
|
76628
|
-
himi store iap apply --plan <plan.json> --profile <name> --out <result.json> [--yes] [--json]
|
|
77481
|
+
himi store iap apply --plan <plan.json> --profile <name> --out <result.json> [--yes] [--limit <n>] [--json]
|
|
76629
77482
|
create the planned products with draft metadata, an
|
|
76630
77483
|
exact price and explicit territories. Re-reads Apple
|
|
76631
77484
|
first, never edits a live product, never duplicates on
|
|
76632
77485
|
a rerun; prompts unless --yes. Writes the Wix\u2192Apple
|
|
76633
77486
|
mapping \u2014 backend sync not configured, checkout off.
|
|
77487
|
+
--limit <n> creates at most n new products per run (big
|
|
77488
|
+
catalogs go in batches; a rerun continues).
|
|
76634
77489
|
himi store iap status --manifest <file> --profile <name> [--out <file>] [--json]
|
|
76635
77490
|
READ-ONLY: what App Store Connect has for each product.
|
|
76636
77491
|
No build, signing or repo checkout. Guide: himi docs iap-products
|
|
@@ -76916,7 +77771,7 @@ var COMMANDS2 = [
|
|
|
76916
77771
|
{ name: "visibility", summary: "read (no value) or set whether the public registry (/v1/apps + the Himi Player picker) LISTS the app. `public` lists it for everyone; `unlisted` hides it from listings while direct reads by app id \u2014 deep links, previews \u2014 keep working. App from --app or the content dir. Setting it needs no republish.", required: "[public|unlisted] [--app <a>] [--content <dir>] (--control-token)" },
|
|
76917
77772
|
{ name: "dist", summary: "build an INSTALLABLE QA binary (signed IPA / APK) on the Wix Job Service Mac pool and return an install link \u2014 the serve plane brokers the job, the pool builds it. WAITS for the build by default (~4 min) and prints the install link; `--no-wait` returns a jobId immediately instead. Omitting `--release` builds whatever is LIVE for the app. A named draft is refused unless `--allow-draft` makes that exceptional build explicit. `status <jobId>` / `watch <jobId>` / `list --app <a>` / `cancel <jobId>` inspect and stop builds (`watch` reports up front whether the job mints its own signing identity or borrows one, so a doomed iOS build is cancellable in seconds rather than after the archive). Off-repo, name a target \u2014 `--target wix` for a self-serve Wix login or `--target bo` for the internal employee path (the in-repo default is a local dev plane that isn't reachable off-repo); self-serve callers may build their OWN apps under a tighter budget (2 in flight / 5 per 24h), while a manage-only owner-role credential is refused. `--shell` picks the native host to sign into (branded-lite | owner-lite | brandy | player | demo-app); `brandy` is the site shell \u2014 the customer's own website as native-framed tabs. `--signing` defaults to `wix-managed` \u2014 the Wix Apple account mints a per-app signing identity for the build, so the caller provisions no Apple credentials of their own; `--signing byo --site <id>` reuses an existing customer ad-hoc profile and certificate for iOS; requires an owned site and a completed personal signing binding. `--android-signing` is the Android counterpart and a SEPARATE axis (a keystore, not an Apple account): `qa-keystore` (default \u2014 the repo's committed QA key, identical on every pool machine, so a build installs over the previous one), `wix-keystore` (the Wix key the pool machine carries; the build FAILS naming the paths searched if it carries none, rather than quietly signing with something else), or `debug` (the old per-machine key \u2014 QA must uninstall any previous build first). NOT the normal way to ship: screens, Tier-5 bundles, tokens and app-config all go over the air with `himi push` in seconds \u2014 a binary trip is only needed for a NATIVE delta (a new SDUI component, a new declared capability/entitlement, a new bundle id, or a minBinaryVersion floor bump). Refuses to build an app whose launcher icon is missing, a placeholder, or one a store would reject \u2014 the one thing a binary carries that no later OTA push can fix; `--force` builds anyway", required: "[--app <a>] [--platform ios,android] [--release <id>] [--allow-draft] [--target <t>] [--shell branded-lite|owner-lite|brandy|player|demo-app] [--signing wix-managed|byo] [--site <id>] [--android-signing qa-keystore|wix-keystore|debug] [--borrow-ios-id <bundleId>] [--no-wait] [--force] | status <jobId> [--target <t>] | watch <jobId> [--target <t>] [--interval <ms>] | list --app <a> [--target <t>] | cancel <jobId> [--target <t>]" },
|
|
76918
77773
|
{ name: "testflight", summary: 'build, sign and upload an app to TestFlight (internal testers \u2014 no Beta App Review), then WAIT until App Store Connect finishes processing it. Sugar for `store upload --track testflight`. Prompts first: a build number is consumed permanently on a real Apple account and cannot be reused \u2014 `--yes` for CI, `--dry-run` to see the plan without touching anything. The wait is the real check, not a nicety: fastlane prints "Successfully uploaded" for a binary App Store Connect then rejects in processing, and a rejected binary leaves NO build record at all (the reason is emailed). Public App Store review is deliberately NOT automatable here \u2014 `--track appstore` is refused; use /submit-ios-app when you mean to go public', required: "<app> [--track testflight|internal] [--profile <name>] [--yes] [--dry-run] [--skip-build] [--no-wait] [--json]" },
|
|
76919
|
-
{ name: "store", summary: "the App Store Connect group behind `testflight`. `preflight <app>` checks readiness offline (icon, entitlements, encryption flag, version, store.config ids, listing + review record); `upload <app>` is what `testflight` runs; `status <app>` reads live build + processing state. `--profile` selects WHICH App Store Connect account: an app built for a Wix customer ships under the CUSTOMER's Apple team (Apple guideline 4.2.6 forbids an app-generation service submitting on a client's behalf), declared per-app as ios.credentialProfile in store.config.yaml and resolved from ~/.himalaya/store-credentials/<name>.json \u2014 the .p8 itself never enters the repo. `iap plan|apply|status` registers an app owner's
|
|
77774
|
+
{ name: "store", summary: "the App Store Connect group behind `testflight`. `preflight <app>` checks readiness offline (icon, entitlements, encryption flag, version, store.config ids, listing + review record); `upload <app>` is what `testflight` runs; `status <app>` reads live build + processing state. `--profile` selects WHICH App Store Connect account: an app built for a Wix customer ships under the CUSTOMER's Apple team (Apple guideline 4.2.6 forbids an app-generation service submitting on a client's behalf), declared per-app as ios.credentialProfile in store.config.yaml and resolved from ~/.himalaya/store-credentials/<name>.json \u2014 the .p8 itself never enters the repo. `iap draft|plan|apply|status` registers an app owner's digital products as in-app purchases (non-consumables, or consumable price tiers): draft reads the Wix catalog and writes the manifest, plan and status only read, apply is the one mutation boundary, and none of them builds, signs or needs a checkout (`himi docs iap-products`)", required: "preflight <app> [--profile <n>] | upload <app> [--track <t>] [--profile <n>] [--yes] [--dry-run] | status <app> [--profile <n>] | iap draft --site <msid> --profile <n> --apple-app <id> --bundle <id> --base-territory <t> --out <f> [--territories <a,b>] [--locale <l>] [--mode per-item|tiers] [--merge <f>] [--report <f>] [--json] | iap plan --manifest <f> --profile <n> --out <f> [--json] | iap apply --plan <f> --profile <n> --out <f> [--yes] [--limit <n>] [--json] | iap status --manifest <f> --profile <n> [--out <f>] [--json]" },
|
|
76920
77775
|
{ name: "guide", summary: "print the Himalaya overview + headless dev loop + available topics (offline)", required: "[--json]" },
|
|
76921
77776
|
{ name: "docs", summary: "print an authoring guide; no topic = list them (offline)", required: "[<topic>] [--json]" },
|
|
76922
77777
|
{ name: "components", summary: "print the SDUI component vocabulary + props; a name = that component (offline)", required: "[<Name>] [--json]" },
|
|
@@ -78141,7 +78996,7 @@ function templateNameError(name, from) {
|
|
|
78141
78996
|
const composed2 = TEMPLATE_APP_PREFIX + name;
|
|
78142
78997
|
return isValidAppId(composed2) ? null : `template name "${name}" (${from}) makes app id "${composed2}", which is over the serve plane's app-id length limit; pass a shorter --name`;
|
|
78143
78998
|
}
|
|
78144
|
-
function materializeForPreview(appDir3, slug,
|
|
78999
|
+
function materializeForPreview(appDir3, slug, displayName2) {
|
|
78145
79000
|
if (!isTemplateSourceTree(appDir3)) return appDir3;
|
|
78146
79001
|
const slugError = templateNameError(slug, "preview slug");
|
|
78147
79002
|
if (slugError) throw Object.assign(new Error(slugError), { exitCode: 2 });
|
|
@@ -78173,7 +79028,7 @@ function materializeForPreview(appDir3, slug, displayName) {
|
|
|
78173
79028
|
rmSync20(dest, { recursive: true, force: true });
|
|
78174
79029
|
mkdirSync39(dest, { recursive: true });
|
|
78175
79030
|
try {
|
|
78176
|
-
materializeSource(forBuild,
|
|
79031
|
+
materializeSource(forBuild, displayName2, dest, slug);
|
|
78177
79032
|
} catch (e) {
|
|
78178
79033
|
rmSync20(dest, { recursive: true, force: true });
|
|
78179
79034
|
throw e;
|
|
@@ -79363,7 +80218,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
79363
80218
|
try {
|
|
79364
80219
|
const bytes2 = readFileSync88(iconPath);
|
|
79365
80220
|
payload.icon = bytes2.toString("base64");
|
|
79366
|
-
iconHash =
|
|
80221
|
+
iconHash = createHash36("sha256").update(bytes2).digest("hex");
|
|
79367
80222
|
} catch {
|
|
79368
80223
|
}
|
|
79369
80224
|
}
|
|
@@ -81212,7 +82067,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
81212
82067
|
let localSourceHash;
|
|
81213
82068
|
try {
|
|
81214
82069
|
const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
|
|
81215
|
-
localSourceHash =
|
|
82070
|
+
localSourceHash = createHash36("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
|
|
81216
82071
|
} catch {
|
|
81217
82072
|
}
|
|
81218
82073
|
return runReleasePatch({
|
|
@@ -81604,9 +82459,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
81604
82459
|
return 2;
|
|
81605
82460
|
}
|
|
81606
82461
|
const { packRelease: packRelease2, packReleaseToDir: packReleaseToDir2 } = await Promise.resolve().then(() => (init_pack(), pack_exports));
|
|
81607
|
-
const
|
|
82462
|
+
const displayName2 = str9(flags.name);
|
|
81608
82463
|
const launchMode = flags.snapshot === true ? "snapshot" : "live";
|
|
81609
|
-
const packOpts = { createdBy: `himi-cli ${cliVersion()}`, launchMode, ...
|
|
82464
|
+
const packOpts = { createdBy: `himi-cli ${cliVersion()}`, launchMode, ...displayName2 ? { displayName: displayName2 } : {} };
|
|
81610
82465
|
const writePack = (root, appName2, out2, source) => format === "dir" ? packReleaseToDir2(root, appName2, out2, packOpts) : packRelease2(root, appName2, out2, { ...packOpts, ...source ? { source } : {} });
|
|
81611
82466
|
const releaseDir = str9(flags["release-dir"]);
|
|
81612
82467
|
if (releaseDir) {
|
|
@@ -82858,7 +83713,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82858
83713
|
});
|
|
82859
83714
|
}
|
|
82860
83715
|
const bytes2 = readFileSync88(file);
|
|
82861
|
-
const
|
|
83716
|
+
const sha2 = createHash36("sha256").update(bytes2).digest("hex");
|
|
82862
83717
|
const forScreens = (str9(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
82863
83718
|
if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
|
|
82864
83719
|
io.error(JSON.stringify({ ok: false, error: "no_rebind_takes_no_value", message: "--no-rebind is a switch and takes no value; pass it bare.", usage: PATCH_USAGE }));
|
|
@@ -82884,7 +83739,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82884
83739
|
ok: false,
|
|
82885
83740
|
error: "orphan_bundle",
|
|
82886
83741
|
app,
|
|
82887
|
-
sha256:
|
|
83742
|
+
sha256: sha2,
|
|
82888
83743
|
message: "publishing bundle bytes does not repoint any screen at them: the patch adds the bundle to the manifest and leaves every screen's `bundle` field where it was, so this patch would land bytes that nothing renders.",
|
|
82889
83744
|
hint: "name the screens to repoint: --for-screen cart,orders. Pass --no-rebind if you really mean to upload bytes nothing references yet.",
|
|
82890
83745
|
alternative: "`himi push --only bundle:<name>` infers this from the package and drags the referencing screens in for you.",
|
|
@@ -82893,9 +83748,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82893
83748
|
return 2;
|
|
82894
83749
|
}
|
|
82895
83750
|
const patch = {
|
|
82896
|
-
bundles: { [
|
|
83751
|
+
bundles: { [sha2]: bytes2.toString("base64") }
|
|
82897
83752
|
};
|
|
82898
|
-
const touched = [`bundle:${
|
|
83753
|
+
const touched = [`bundle:${sha2.slice(0, 12)}`];
|
|
82899
83754
|
const rebound = {};
|
|
82900
83755
|
const bundleBase = await fetchReleaseManifest(client2, app, fromRelease);
|
|
82901
83756
|
if ("error" in bundleBase) {
|
|
@@ -82928,8 +83783,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82928
83783
|
}, null, 2));
|
|
82929
83784
|
return 1;
|
|
82930
83785
|
}
|
|
82931
|
-
patch.screens[id] = { descriptor: got.text, bundle:
|
|
82932
|
-
rebound[id] =
|
|
83786
|
+
patch.screens[id] = { descriptor: got.text, bundle: sha2 };
|
|
83787
|
+
rebound[id] = sha2;
|
|
82933
83788
|
touched.push(`screen:${id}`);
|
|
82934
83789
|
}
|
|
82935
83790
|
}
|
|
@@ -82937,7 +83792,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82937
83792
|
...common,
|
|
82938
83793
|
fromRelease: baseId,
|
|
82939
83794
|
plan: { patch, touched, included: [], rebound },
|
|
82940
|
-
extra: { kind, file: { path: file, bytes: bytes2.length, sha256:
|
|
83795
|
+
extra: { kind, file: { path: file, bytes: bytes2.length, sha256: sha2 } }
|
|
82941
83796
|
});
|
|
82942
83797
|
}
|
|
82943
83798
|
// OPERATIONAL FLAGS (RFC 0013) — the surface the admin portal's FlagsPanel used to own.
|