@wix/himalaya-cli 0.1154.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,16 +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"() {
|
|
6768
6786
|
"use strict";
|
|
6769
6787
|
SYSTEM_FONT_FAMILY = "$system";
|
|
6770
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
|
+
};
|
|
6771
6813
|
}
|
|
6772
6814
|
});
|
|
6773
6815
|
|
|
@@ -7861,6 +7903,47 @@ var init_validator = __esm({
|
|
|
7861
7903
|
}
|
|
7862
7904
|
});
|
|
7863
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
|
+
|
|
7864
7947
|
// ../../core/dev-server/src/l10n-strings.ts
|
|
7865
7948
|
function isUserVisibleCopy(s) {
|
|
7866
7949
|
if (typeof s !== "string" || s.length === 0 || s.length > 200) return false;
|
|
@@ -10245,6 +10328,15 @@ function renderedWixMediaReferences(descriptor) {
|
|
|
10245
10328
|
regions.forEach((region) => collectWixMediaReferences(region.value, region.path, found, false, false, "", region.atRoot));
|
|
10246
10329
|
return found;
|
|
10247
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
|
+
}
|
|
10248
10340
|
function validateScreenDescriptor(screenId, descriptor, opts = {}) {
|
|
10249
10341
|
const issues = [];
|
|
10250
10342
|
const strict = opts.strict ?? false;
|
|
@@ -10288,10 +10380,11 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
|
|
|
10288
10380
|
indexIssue(at2, issue2);
|
|
10289
10381
|
}
|
|
10290
10382
|
}
|
|
10383
|
+
const tokenRegistry = opts.tokens ? buildTokenRegistry(opts.tokens) : void 0;
|
|
10291
10384
|
for (const t of v.tokenViolations) {
|
|
10292
10385
|
if (t.name.startsWith("$")) continue;
|
|
10293
10386
|
if (pathCrossesOpaqueAuthorJson(t.path)) continue;
|
|
10294
|
-
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) });
|
|
10295
10388
|
}
|
|
10296
10389
|
for (const u of v.unknownComponents) {
|
|
10297
10390
|
if (pathCrossesOpaqueAuthorJson(u.path)) continue;
|
|
@@ -10835,6 +10928,8 @@ var init_validate_descriptor_core = __esm({
|
|
|
10835
10928
|
"../../core/dev-server/src/validate-descriptor.core.ts"() {
|
|
10836
10929
|
init_validator();
|
|
10837
10930
|
init_validator();
|
|
10931
|
+
init_tokens();
|
|
10932
|
+
init_token_suggest();
|
|
10838
10933
|
init_l10n_strings();
|
|
10839
10934
|
init_a11y_contract();
|
|
10840
10935
|
init_bindings();
|
|
@@ -14387,8 +14482,8 @@ function rgbToHex([r, g, b]) {
|
|
|
14387
14482
|
async function normalizeSourceIcon(input, opts) {
|
|
14388
14483
|
const sharp = await sharpOrThrow();
|
|
14389
14484
|
const size = opts.size ?? 1024;
|
|
14390
|
-
const
|
|
14391
|
-
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));
|
|
14392
14487
|
const pad3 = Math.round((size - inner) / 2);
|
|
14393
14488
|
let trimmed;
|
|
14394
14489
|
try {
|
|
@@ -18282,7 +18377,7 @@ function agentCovers(agent, cli) {
|
|
|
18282
18377
|
const gap = Math.abs(Date.parse(agent.ts) - Date.parse(cli.ts));
|
|
18283
18378
|
if (!(gap <= AGENT_WINS_WITHIN_MS)) return false;
|
|
18284
18379
|
if (cli.step && agent.step === cli.step) return true;
|
|
18285
|
-
return [...shasOf(cli.images)].some((
|
|
18380
|
+
return [...shasOf(cli.images)].some((sha2) => agentShas.has(sha2));
|
|
18286
18381
|
}
|
|
18287
18382
|
function visibleEntries(entries) {
|
|
18288
18383
|
return entries.filter((e) => !isCliEntry(e) || !entries.some((a) => agentCovers(a, e)));
|
|
@@ -34589,7 +34684,8 @@ var init_renderNode = __esm({
|
|
|
34589
34684
|
if (v.startsWith("$item")) {
|
|
34590
34685
|
const key2 = v.replace(/^\$item\.?/, "");
|
|
34591
34686
|
const resolved2 = ctx.itemScope ? key2 ? ctx.itemScope[key2] : ctx.itemScope : void 0;
|
|
34592
|
-
|
|
34687
|
+
if (resolved2 != null) return asTextL10n(resolved2, ctx);
|
|
34688
|
+
return ctx.itemScope ? null : bindingPlaceholder(v, ctx);
|
|
34593
34689
|
}
|
|
34594
34690
|
if (ctx.provider) return /* @__PURE__ */ jsx8(Bound, { ctx, binding: v, render: (val) => asTextL10n(val, ctx) });
|
|
34595
34691
|
return bindingPlaceholder(v, ctx);
|
|
@@ -40514,9 +40610,9 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
|
|
|
40514
40610
|
evidence: { runId: `dev:${state.startedAt}:${screenId}`, ts: (/* @__PURE__ */ new Date()).toISOString() }
|
|
40515
40611
|
});
|
|
40516
40612
|
}
|
|
40517
|
-
async function fontFaceBytesBySha(_appDir,
|
|
40613
|
+
async function fontFaceBytesBySha(_appDir, sha2) {
|
|
40518
40614
|
try {
|
|
40519
|
-
return await catalogFaceBytes(
|
|
40615
|
+
return await catalogFaceBytes(sha2);
|
|
40520
40616
|
} catch {
|
|
40521
40617
|
}
|
|
40522
40618
|
return void 0;
|
|
@@ -42877,13 +42973,13 @@ function createDevServer(opts = {}) {
|
|
|
42877
42973
|
return handleAppIcon(req, res, state);
|
|
42878
42974
|
}
|
|
42879
42975
|
if (url.split("?")[0].startsWith("/v1/asset/") && req.method === "GET") {
|
|
42880
|
-
const
|
|
42976
|
+
const sha2 = url.split("?")[0].slice("/v1/asset/".length);
|
|
42881
42977
|
const q = new URLSearchParams(url.split("?")[1] ?? "");
|
|
42882
42978
|
const assetApp = q.get("app") ?? state.appName;
|
|
42883
42979
|
const assetDir = state.app && assetApp === state.app.name ? state.app.dir : resolveAppDir(assetApp);
|
|
42884
|
-
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;
|
|
42885
42981
|
if (!hit && assetDir) {
|
|
42886
|
-
const fontBytes = await fontFaceBytesBySha(assetDir,
|
|
42982
|
+
const fontBytes = await fontFaceBytesBySha(assetDir, sha2);
|
|
42887
42983
|
if (fontBytes) {
|
|
42888
42984
|
res.writeHead(200, {
|
|
42889
42985
|
"content-type": "font/ttf",
|
|
@@ -42901,7 +42997,7 @@ function createDevServer(opts = {}) {
|
|
|
42901
42997
|
if (serve) return await proxyToServe(serve, req, res);
|
|
42902
42998
|
}
|
|
42903
42999
|
res.writeHead(404, { "content-type": "application/json" });
|
|
42904
|
-
res.end(JSON.stringify({ error: "asset_not_found", app: assetApp, sha }));
|
|
43000
|
+
res.end(JSON.stringify({ error: "asset_not_found", app: assetApp, sha: sha2 }));
|
|
42905
43001
|
return;
|
|
42906
43002
|
}
|
|
42907
43003
|
const bytes2 = readFileSync44(hit.path);
|
|
@@ -43998,6 +44094,7 @@ __export(screen_crawl_exports, {
|
|
|
43998
44094
|
isUnservedRequest: () => isUnservedRequest,
|
|
43999
44095
|
layeredTransport: () => layeredTransport,
|
|
44000
44096
|
mapBundleFrame: () => mapBundleFrame,
|
|
44097
|
+
missingItemFields: () => missingItemFields,
|
|
44001
44098
|
netTransport: () => netTransport,
|
|
44002
44099
|
parseWorkerAction: () => parseWorkerAction,
|
|
44003
44100
|
recordScreenTraffic: () => recordScreenTraffic,
|
|
@@ -44614,6 +44711,96 @@ function renderedNameGaps(descriptor, state) {
|
|
|
44614
44711
|
}
|
|
44615
44712
|
return [...found.values()];
|
|
44616
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
|
+
}
|
|
44617
44804
|
function collectObservations(descriptorOrPayload, state) {
|
|
44618
44805
|
const out2 = [];
|
|
44619
44806
|
const walk2 = (node) => {
|
|
@@ -45112,6 +45299,17 @@ async function crawlScreenInner(opts) {
|
|
|
45112
45299
|
});
|
|
45113
45300
|
}
|
|
45114
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
|
+
}
|
|
45115
45313
|
for (const site of sites) {
|
|
45116
45314
|
if (parseWorkerAction(site.actionId)) continue;
|
|
45117
45315
|
const before = { ...site.params ?? {}, ...site.args ?? {} };
|
|
@@ -45735,7 +45933,7 @@ async function crawlScreenRelations(opts) {
|
|
|
45735
45933
|
const sites = actionSitesForDescriptor(descriptor).filter(
|
|
45736
45934
|
(s) => parseWorkerAction(s.actionId) && !opts.skipAction?.(s.actionId, s.componentId)
|
|
45737
45935
|
);
|
|
45738
|
-
const
|
|
45936
|
+
const nameOf2 = (s) => parseWorkerAction(s.actionId).action;
|
|
45739
45937
|
const serviceOf = (s) => parseWorkerAction(s.actionId).service;
|
|
45740
45938
|
const boot = () => bootScreenWorker({
|
|
45741
45939
|
bundleJs,
|
|
@@ -45793,7 +45991,7 @@ async function crawlScreenRelations(opts) {
|
|
|
45793
45991
|
)
|
|
45794
45992
|
];
|
|
45795
45993
|
const trig = (s) => ({ tap: s.componentId, key: s.key, action: s.actionId });
|
|
45796
|
-
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")) {
|
|
45797
45995
|
const after = await replay([s]);
|
|
45798
45996
|
if (!after) continue;
|
|
45799
45997
|
checked++;
|
|
@@ -45806,12 +46004,12 @@ async function crawlScreenRelations(opts) {
|
|
|
45806
46004
|
invariant: "refresh-not-idempotent",
|
|
45807
46005
|
severity: "warning",
|
|
45808
46006
|
axis: "relation",
|
|
45809
|
-
message: `${
|
|
46007
|
+
message: `${nameOf2(s)} from a fresh boot settles on a different state than the boot itself, under identical data: ${summarizeDiff(baseSig, sig)}`,
|
|
45810
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"
|
|
45811
46009
|
});
|
|
45812
46010
|
}
|
|
45813
46011
|
}
|
|
45814
|
-
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")) {
|
|
45815
46013
|
const target = listPathOf(s.itemsPath);
|
|
45816
46014
|
const after = target ? await replay([s]) : null;
|
|
45817
46015
|
if (!after) continue;
|
|
@@ -45827,13 +46025,13 @@ async function crawlScreenRelations(opts) {
|
|
|
45827
46025
|
axis: "relation",
|
|
45828
46026
|
at: p,
|
|
45829
46027
|
actual: `${baseLens[p]} \u2192 ${n}`,
|
|
45830
|
-
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`,
|
|
45831
46029
|
hint: "append the new page to the existing rows (and de-duplicate by id); the user scrolled to see MORE"
|
|
45832
46030
|
});
|
|
45833
46031
|
}
|
|
45834
46032
|
}
|
|
45835
46033
|
}
|
|
45836
|
-
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 }))) {
|
|
45837
46035
|
if (screenListPaths.length !== 1) continue;
|
|
45838
46036
|
const after = await replay([s]);
|
|
45839
46037
|
if (!after) continue;
|
|
@@ -45849,18 +46047,18 @@ async function crawlScreenRelations(opts) {
|
|
|
45849
46047
|
axis: "relation",
|
|
45850
46048
|
at: p,
|
|
45851
46049
|
actual: `${baseLens[p]} \u2192 ${n}`,
|
|
45852
|
-
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`,
|
|
45853
46051
|
hint: "filter from the FULL set you keep aside, and assign the narrowed result \u2014 not a concatenation"
|
|
45854
46052
|
});
|
|
45855
46053
|
}
|
|
45856
46054
|
}
|
|
45857
46055
|
}
|
|
45858
|
-
const declaredNames = sites.map(
|
|
46056
|
+
const declaredNames = sites.map(nameOf2);
|
|
45859
46057
|
for (const a of sites) {
|
|
45860
|
-
const partner = inversePartner(
|
|
46058
|
+
const partner = inversePartner(nameOf2(a), declaredNames);
|
|
45861
46059
|
if (!partner) continue;
|
|
45862
|
-
if (isUndoAction(
|
|
45863
|
-
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);
|
|
45864
46062
|
if (!b) continue;
|
|
45865
46063
|
const undoIsRowBound = isRowBound(b);
|
|
45866
46064
|
const after = await replay([a, b], (i, bootState, prev) => {
|
|
@@ -45880,15 +46078,15 @@ async function crawlScreenRelations(opts) {
|
|
|
45880
46078
|
invariant: "inverse-pair-diverges",
|
|
45881
46079
|
severity: "warning",
|
|
45882
46080
|
axis: "relation",
|
|
45883
|
-
message: `${
|
|
45884
|
-
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`
|
|
45885
46083
|
});
|
|
45886
46084
|
}
|
|
45887
46085
|
}
|
|
45888
46086
|
return { findings: dedupe(findings), checked, byKind };
|
|
45889
46087
|
});
|
|
45890
46088
|
}
|
|
45891
|
-
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;
|
|
45892
46090
|
var init_screen_crawl = __esm({
|
|
45893
46091
|
"../../core/dev-server/src/screen-crawl.ts"() {
|
|
45894
46092
|
init_validate_descriptor_core();
|
|
@@ -45947,6 +46145,7 @@ var init_screen_crawl = __esm({
|
|
|
45947
46145
|
NAME_KEYS = new Set(ACCESSIBLE_NAME_KEYS);
|
|
45948
46146
|
A11Y_ROWS_PER_LIST = 20;
|
|
45949
46147
|
A11Y_LIST_DEPTH = 3;
|
|
46148
|
+
ITEM_PATH_RE = /\$item\.([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/g;
|
|
45950
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?"';
|
|
45951
46150
|
UNBINDABLE_ROW = Symbol("unbindable-row");
|
|
45952
46151
|
}
|
|
@@ -46497,10 +46696,10 @@ function hydrationServices(tier5Dir, bundleName, input = {}) {
|
|
|
46497
46696
|
const js = readBuiltBundle(tier5Dir, bundleName);
|
|
46498
46697
|
return js ? servicesFromBundleJs(js, input) : null;
|
|
46499
46698
|
}
|
|
46500
|
-
function releaseIdFor(
|
|
46501
|
-
if (
|
|
46699
|
+
function releaseIdFor(sha2, content) {
|
|
46700
|
+
if (sha2 === "dev") return contentReleaseId(content);
|
|
46502
46701
|
const stored = JSON.parse(JSON.stringify(content));
|
|
46503
|
-
return `${
|
|
46702
|
+
return `${sha2}-${releaseContentId(stored).slice(0, 12)}`;
|
|
46504
46703
|
}
|
|
46505
46704
|
function rebindReleaseId(manifest) {
|
|
46506
46705
|
const { releaseId: _releaseId, gitSha: gitSha2, shellSha: _shellSha, ...content } = manifest;
|
|
@@ -47402,12 +47601,12 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
|
|
|
47402
47601
|
// never re-fetch, and the "replace an asset without a store release" claim would be false.
|
|
47403
47602
|
...Object.keys(assets).length ? { assets } : {}
|
|
47404
47603
|
};
|
|
47405
|
-
const
|
|
47604
|
+
const sha2 = gitSha();
|
|
47406
47605
|
const buildable = shellSha();
|
|
47407
47606
|
const manifest = {
|
|
47408
|
-
releaseId: releaseIdFor(
|
|
47409
|
-
gitSha:
|
|
47410
|
-
...buildable && buildable !==
|
|
47607
|
+
releaseId: releaseIdFor(sha2, content),
|
|
47608
|
+
gitSha: sha2,
|
|
47609
|
+
...buildable && buildable !== sha2 ? { shellSha: buildable } : {},
|
|
47411
47610
|
...content
|
|
47412
47611
|
};
|
|
47413
47612
|
await store.putManifest(app, manifest);
|
|
@@ -47490,8 +47689,8 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
|
|
|
47490
47689
|
return { ...schedule, bundleHash: target.bundleHash };
|
|
47491
47690
|
}) } : {}
|
|
47492
47691
|
};
|
|
47493
|
-
const
|
|
47494
|
-
const manifest = { releaseId: contentReleaseId(content), gitSha:
|
|
47692
|
+
const sha2 = gitSha();
|
|
47693
|
+
const manifest = { releaseId: contentReleaseId(content), gitSha: sha2, ...content };
|
|
47495
47694
|
await store.putManifest(app, manifest);
|
|
47496
47695
|
return { manifest, failedToBuild: [], missingBundles: [], auth: {}, tierCapViolations: [] };
|
|
47497
47696
|
}
|
|
@@ -48469,14 +48668,14 @@ function observeShots(dir, memory, now) {
|
|
|
48469
48668
|
const archived = archiveFile(dir, memory, join43(shotsDir, f));
|
|
48470
48669
|
if (!archived) continue;
|
|
48471
48670
|
seen.add(id);
|
|
48472
|
-
const { sha, ext } = archived;
|
|
48671
|
+
const { sha: sha2, ext } = archived;
|
|
48473
48672
|
const prev = memory.shots[id];
|
|
48474
48673
|
if (!prev) {
|
|
48475
|
-
memory.shots[id] = { sha, ext, firstSeen: now, changedAt: now, versions: 1, present: true };
|
|
48476
|
-
memory.timeline.push({ ts: now, id, sha, ext });
|
|
48477
|
-
} else if (prev.sha !==
|
|
48478
|
-
memory.shots[id] = { ...prev, sha, ext, changedAt: now, versions: prev.versions + 1, present: true };
|
|
48479
|
-
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 });
|
|
48480
48679
|
} else if (!prev.present) {
|
|
48481
48680
|
memory.shots[id] = { ...prev, present: true };
|
|
48482
48681
|
}
|
|
@@ -48959,12 +49158,12 @@ function planFromPayload(payload, base, selectors) {
|
|
|
48959
49158
|
if (wantBundles.size) {
|
|
48960
49159
|
patch.bundles = {};
|
|
48961
49160
|
for (const name of wantBundles) {
|
|
48962
|
-
const
|
|
48963
|
-
const b64 =
|
|
48964
|
-
if (!
|
|
49161
|
+
const sha2 = local.bundles?.[name]?.sha256;
|
|
49162
|
+
const b64 = sha2 ? payload.bundles?.[sha2] : void 0;
|
|
49163
|
+
if (!sha2 || !b64) {
|
|
48965
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 } };
|
|
48966
49165
|
}
|
|
48967
|
-
patch.bundles[
|
|
49166
|
+
patch.bundles[sha2] = b64;
|
|
48968
49167
|
touched.push(`bundle:${name}`);
|
|
48969
49168
|
}
|
|
48970
49169
|
}
|
|
@@ -48987,13 +49186,13 @@ function planFromPayload(payload, base, selectors) {
|
|
|
48987
49186
|
return { ok: false, error: { error: "selector_matched_nothing", selector: `screen:${id}`, message: `screen "${id}" has no built descriptor in this release.`, screens: localScreens } };
|
|
48988
49187
|
}
|
|
48989
49188
|
const localKey = local.screens?.[id]?.bundle ?? null;
|
|
48990
|
-
const
|
|
48991
|
-
const carriedHere =
|
|
49189
|
+
const sha2 = resolveBundleSha(local, localKey);
|
|
49190
|
+
const carriedHere = sha2 ? patch.bundles?.[sha2] !== void 0 : false;
|
|
48992
49191
|
const baseKeyForSha = (s) => {
|
|
48993
49192
|
for (const [k, e] of Object.entries(base.bundles ?? {})) if (e?.sha256 === s) return k;
|
|
48994
49193
|
return null;
|
|
48995
49194
|
};
|
|
48996
|
-
const baseKey =
|
|
49195
|
+
const baseKey = sha2 ? baseKeyForSha(sha2) : null;
|
|
48997
49196
|
if (localKey !== null && !carriedHere && !baseKey) {
|
|
48998
49197
|
return {
|
|
48999
49198
|
ok: false,
|
|
@@ -49005,7 +49204,7 @@ function planFromPayload(payload, base, selectors) {
|
|
|
49005
49204
|
}
|
|
49006
49205
|
};
|
|
49007
49206
|
}
|
|
49008
|
-
const bundleRef = carriedHere ?
|
|
49207
|
+
const bundleRef = carriedHere ? sha2 : baseKey;
|
|
49009
49208
|
patch.screens[id] = { descriptor, bundle: bundleRef };
|
|
49010
49209
|
if (bundleRef && bundleRef !== (base.screens?.[id]?.bundle ?? null)) rebound[id] = bundleRef;
|
|
49011
49210
|
touched.push(`screen:${id}`);
|
|
@@ -50994,7 +51193,7 @@ function shotStateFor(dir, image) {
|
|
|
50994
51193
|
}
|
|
50995
51194
|
}
|
|
50996
51195
|
function recordedShots(dir, screenId) {
|
|
50997
|
-
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);
|
|
50998
51197
|
}
|
|
50999
51198
|
function offlineArtifact(state) {
|
|
51000
51199
|
if (!state || state.state === "content") return false;
|
|
@@ -51268,7 +51467,7 @@ var init_studio_reel = __esm({
|
|
|
51268
51467
|
};
|
|
51269
51468
|
isObj3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
51270
51469
|
str4 = (v) => typeof v === "string" && v ? v : null;
|
|
51271
|
-
historyName = (
|
|
51470
|
+
historyName = (sha2, ext) => typeof sha2 === "string" && typeof ext === "string" && HISTORY_NAME.test(`${sha2}.${ext}`) ? `${sha2}.${ext}` : null;
|
|
51272
51471
|
cited = (p) => [
|
|
51273
51472
|
...p.frames.map((f) => f.file),
|
|
51274
51473
|
...p.finals.flatMap((f) => f.file ? [f.file] : []),
|
|
@@ -51517,10 +51716,10 @@ function stillBuilding(dir, image, now) {
|
|
|
51517
51716
|
if (!entries.length) return void 0;
|
|
51518
51717
|
const times = entries.map((e) => Date.parse(e.ts)).filter(Number.isFinite);
|
|
51519
51718
|
if (!times.length || !(now.getTime() - Math.max(...times) > STILL_BUILDING_AFTER_MS)) return void 0;
|
|
51520
|
-
const
|
|
51719
|
+
const sha2 = sha256Of2(image);
|
|
51521
51720
|
const latest = entries[entries.length - 1];
|
|
51522
|
-
if (Array.isArray(latest.images) && latest.images.some((i) => !!i && typeof i === "object" && i.sha256 ===
|
|
51523
|
-
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 });
|
|
51524
51723
|
} catch {
|
|
51525
51724
|
return void 0;
|
|
51526
51725
|
}
|
|
@@ -51807,10 +52006,10 @@ function homeShotForPostcard(dir, opts = {}) {
|
|
|
51807
52006
|
}
|
|
51808
52007
|
if (opts.repeatOk) return pick2;
|
|
51809
52008
|
try {
|
|
51810
|
-
const
|
|
52009
|
+
const sha2 = sha256Of2(pick2.image);
|
|
51811
52010
|
const entries = readProgress(dir, { all: true }).entries;
|
|
51812
52011
|
const latest = entries[entries.length - 1];
|
|
51813
|
-
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);
|
|
51814
52013
|
if (shown2) return { skipped: "the latest postcard already shows this home picture" };
|
|
51815
52014
|
} catch {
|
|
51816
52015
|
return { skipped: "the home shot could not be read" };
|
|
@@ -58603,16 +58802,16 @@ ${releaseId}`;
|
|
|
58603
58802
|
if (assetMatch) {
|
|
58604
58803
|
if (!app) return json4(400, { error: "missing_app" });
|
|
58605
58804
|
if (!store.getAsset) return json4(501, { error: "assets_unsupported" });
|
|
58606
|
-
const
|
|
58607
|
-
if (!isSafeSegment(
|
|
58805
|
+
const sha2 = assetMatch[1];
|
|
58806
|
+
if (!isSafeSegment(sha2)) return json4(400, { error: "bad_asset" });
|
|
58608
58807
|
const m = pin ? await pinnedManifest(app, pin) : await manifest(app);
|
|
58609
|
-
const record2 = Object.values(m?.assets ?? {}).find((a) => a.sha256 ===
|
|
58808
|
+
const record2 = Object.values(m?.assets ?? {}).find((a) => a.sha256 === sha2);
|
|
58610
58809
|
if (!record2) return json4(404, { error: "asset_not_found", app });
|
|
58611
|
-
const etag = strongEtagForSha256(
|
|
58810
|
+
const etag = strongEtagForSha256(sha2);
|
|
58612
58811
|
if (request.headers.get("if-none-match") === etag) {
|
|
58613
58812
|
return new Response(null, { status: 304, headers: { etag, "cache-control": BUNDLE_CACHE_CONTROL } });
|
|
58614
58813
|
}
|
|
58615
|
-
const bytes2 = await store.getAsset(app,
|
|
58814
|
+
const bytes2 = await store.getAsset(app, sha2);
|
|
58616
58815
|
if (!bytes2) return json4(404, { error: "asset_not_found", app });
|
|
58617
58816
|
return new Response(bytes2, {
|
|
58618
58817
|
status: 200,
|
|
@@ -59189,21 +59388,21 @@ var init_preview_assets_generated = __esm({
|
|
|
59189
59388
|
"../../packages/serve/src/preview-assets.generated.ts"() {
|
|
59190
59389
|
init_preview_assets_format();
|
|
59191
59390
|
PREVIEW_ASSET_MANIFEST = [
|
|
59192
|
-
{ key: "assets/index-
|
|
59193
|
-
{ key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset:
|
|
59194
|
-
{ key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset:
|
|
59195
|
-
{ key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset:
|
|
59196
|
-
{ key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
59197
|
-
{ key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset:
|
|
59198
|
-
{ key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset:
|
|
59199
|
-
{ key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
59200
|
-
{ key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset:
|
|
59201
|
-
{ key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset:
|
|
59202
|
-
{ key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset:
|
|
59203
|
-
{ key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset:
|
|
59204
|
-
{ key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset:
|
|
59205
|
-
{ key: "index.html", contentType: "text/html; charset=utf-8", offset:
|
|
59206
|
-
{ 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" }
|
|
59207
59406
|
];
|
|
59208
59407
|
}
|
|
59209
59408
|
});
|
|
@@ -59826,7 +60025,7 @@ var init_iap_client = __esm({
|
|
|
59826
60025
|
"../store-release/iap-client.ts"() {
|
|
59827
60026
|
init_register_bundle();
|
|
59828
60027
|
ASC_ORIGIN = "https://api.appstoreconnect.apple.com";
|
|
59829
|
-
MAX_PAGES =
|
|
60028
|
+
MAX_PAGES = 100;
|
|
59830
60029
|
MAX_READ_ATTEMPTS = 4;
|
|
59831
60030
|
REQUEST_TIMEOUT_MS = 3e4;
|
|
59832
60031
|
MAX_RETRY_WAIT_MS = 6e4;
|
|
@@ -59985,6 +60184,9 @@ var init_iap_client = __esm({
|
|
|
59985
60184
|
});
|
|
59986
60185
|
|
|
59987
60186
|
// ../store-release/iap-api.ts
|
|
60187
|
+
function appleProductType(type) {
|
|
60188
|
+
return APPLE_PRODUCT_TYPES[type];
|
|
60189
|
+
}
|
|
59988
60190
|
function single(doc) {
|
|
59989
60191
|
return doc.data && !Array.isArray(doc.data) ? doc.data : void 0;
|
|
59990
60192
|
}
|
|
@@ -60038,10 +60240,12 @@ async function readOptional(read) {
|
|
|
60038
60240
|
function createIapApi(client2) {
|
|
60039
60241
|
return new AppStoreConnectIapApi(client2);
|
|
60040
60242
|
}
|
|
60041
|
-
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;
|
|
60042
60244
|
var init_iap_api = __esm({
|
|
60043
60245
|
"../store-release/iap-api.ts"() {
|
|
60044
60246
|
init_iap_client();
|
|
60247
|
+
APPLE_PRODUCT_TYPES = { nonConsumable: "NON_CONSUMABLE", consumable: "CONSUMABLE" };
|
|
60248
|
+
PRICE_POINT_PAGE = "limit=8000";
|
|
60045
60249
|
INITIAL_PRICE_REF = "${initial-price}";
|
|
60046
60250
|
PAGE = "limit=200";
|
|
60047
60251
|
linkage = (type, id) => ({ type, id });
|
|
@@ -60078,7 +60282,7 @@ var init_iap_api = __esm({
|
|
|
60078
60282
|
return (await this.client.list(`/v1/inAppPurchaseVersions/${enc(versionId)}/localizations?${PAGE}`)).map(toLocalization);
|
|
60079
60283
|
}
|
|
60080
60284
|
async listPricePoints(resourceId, territory) {
|
|
60081
|
-
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}`);
|
|
60082
60286
|
return points.map((p) => ({ id: p.id, customerPrice: attr(p, "customerPrice") }));
|
|
60083
60287
|
}
|
|
60084
60288
|
async readPriceSchedule(resourceId) {
|
|
@@ -60107,7 +60311,7 @@ var init_iap_api = __esm({
|
|
|
60107
60311
|
const attributes = {
|
|
60108
60312
|
name: product.referenceName,
|
|
60109
60313
|
productId: product.productId,
|
|
60110
|
-
inAppPurchaseType:
|
|
60314
|
+
inAppPurchaseType: appleProductType(product.type),
|
|
60111
60315
|
familySharable: false,
|
|
60112
60316
|
...product.reviewNote ? { reviewNote: product.reviewNote } : {}
|
|
60113
60317
|
};
|
|
@@ -60191,11 +60395,13 @@ function readLocalizations(reader, item, path2) {
|
|
|
60191
60395
|
const localizations = reader.list(item, "localizations", path2).map((value, i) => {
|
|
60192
60396
|
const at2 = `${path2}.localizations[${i}]`;
|
|
60193
60397
|
const obj3 = reader.object(value, at2, LOCALIZATION_KEYS);
|
|
60194
|
-
|
|
60195
|
-
|
|
60196
|
-
|
|
60197
|
-
|
|
60198
|
-
|
|
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 };
|
|
60199
60405
|
});
|
|
60200
60406
|
reader.duplicates(localizations.map((l) => l.locale), `${path2}.localizations`, "locale");
|
|
60201
60407
|
return localizations;
|
|
@@ -60203,10 +60409,12 @@ function readLocalizations(reader, item, path2) {
|
|
|
60203
60409
|
function readPrice(reader, item, path2) {
|
|
60204
60410
|
const at2 = `${path2}.price`;
|
|
60205
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");
|
|
60206
60413
|
return {
|
|
60207
60414
|
baseTerritory: reader.text(obj3, "baseTerritory", at2, TERRITORY),
|
|
60208
60415
|
currency: reader.text(obj3, "currency", at2, CURRENCY),
|
|
60209
|
-
amount: reader.text(obj3, "amount", at2, AMOUNT)
|
|
60416
|
+
amount: reader.text(obj3, "amount", at2, AMOUNT),
|
|
60417
|
+
...rule ? { rule } : {}
|
|
60210
60418
|
};
|
|
60211
60419
|
}
|
|
60212
60420
|
function readTerritories(reader, item, path2, baseTerritory) {
|
|
@@ -60218,34 +60426,66 @@ function readTerritories(reader, item, path2, baseTerritory) {
|
|
|
60218
60426
|
}
|
|
60219
60427
|
return territories;
|
|
60220
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
|
+
}
|
|
60221
60454
|
function readItem(reader, value, index) {
|
|
60222
60455
|
const path2 = `items[${index}]`;
|
|
60223
60456
|
const obj3 = reader.object(value, path2, ITEM_KEYS);
|
|
60224
|
-
const
|
|
60457
|
+
const type = reader.oneOf(obj3, "type", path2, PRODUCT_TYPES, "subscriptions are not supported by this version");
|
|
60225
60458
|
const price = readPrice(reader, obj3, path2);
|
|
60226
60459
|
const reviewNote = reader.optionalText(obj3, "reviewNote", path2);
|
|
60460
|
+
if (reviewNote) reader.maxLength(reviewNote, `${path2}.reviewNote`, APPLE_LIMITS.reviewNote);
|
|
60227
60461
|
return {
|
|
60228
|
-
|
|
60462
|
+
...readSubject(reader, obj3, path2, type),
|
|
60229
60463
|
classification: reader.literal(obj3, "classification", path2, "digital", "physical goods and unclassified products are never registered as in-app purchases"),
|
|
60230
60464
|
entitlement: reader.text(obj3, "entitlement", path2),
|
|
60231
|
-
productId: reader
|
|
60232
|
-
type
|
|
60233
|
-
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),
|
|
60234
60468
|
...reviewNote === void 0 ? {} : { reviewNote },
|
|
60235
60469
|
localizations: readLocalizations(reader, obj3, path2),
|
|
60236
60470
|
price,
|
|
60237
60471
|
territories: readTerritories(reader, obj3, path2, price.baseTerritory)
|
|
60238
60472
|
};
|
|
60239
60473
|
}
|
|
60474
|
+
function wixEntitiesOf(item) {
|
|
60475
|
+
return item.unlocks ?? (item.wix ? [item.wix] : []);
|
|
60476
|
+
}
|
|
60240
60477
|
function reportDuplicateMappings(reader, items) {
|
|
60241
60478
|
reader.duplicates(items.map((i) => i.productId), "items", "productId");
|
|
60242
|
-
reader.duplicates(items.
|
|
60243
|
-
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");
|
|
60244
60481
|
reader.duplicates(items.map((i) => i.referenceName), "items", "referenceName");
|
|
60245
60482
|
}
|
|
60483
|
+
function looksLikeCredential(text8) {
|
|
60484
|
+
return CREDENTIAL_SHAPES.some((shape) => shape.test(text8));
|
|
60485
|
+
}
|
|
60246
60486
|
function reportCredentialMaterial(reader, value, path2) {
|
|
60247
60487
|
if (typeof value === "string") {
|
|
60248
|
-
if (
|
|
60488
|
+
if (looksLikeCredential(value)) {
|
|
60249
60489
|
reader.problems.push(`${path2} looks like credential material \u2014 keys and tokens never belong in a manifest`);
|
|
60250
60490
|
}
|
|
60251
60491
|
} else if (Array.isArray(value)) {
|
|
@@ -60288,7 +60528,7 @@ function sha256Digest(value) {
|
|
|
60288
60528
|
function manifestDigest(manifest) {
|
|
60289
60529
|
return sha256Digest(manifest);
|
|
60290
60530
|
}
|
|
60291
|
-
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;
|
|
60292
60532
|
var init_iap_manifest = __esm({
|
|
60293
60533
|
"../store-release/iap-manifest.ts"() {
|
|
60294
60534
|
IapManifestError = class extends Error {
|
|
@@ -60302,7 +60542,7 @@ var init_iap_manifest = __esm({
|
|
|
60302
60542
|
APPLE_APP_ID = { pattern: /^\d+$/, hint: "the numeric App Store Connect app id" };
|
|
60303
60543
|
TEAM_ID = { pattern: /^[A-Z0-9]{10}$/, hint: "a 10-character Apple team id" };
|
|
60304
60544
|
BUNDLE_ID = { pattern: /^[A-Za-z0-9.-]+$/, hint: "a bundle id" };
|
|
60305
|
-
PRODUCT_ID = { pattern: /^[A-Za-z0-9._]+$/, hint: "
|
|
60545
|
+
PRODUCT_ID = { pattern: /^[A-Za-z0-9._-]+$/, hint: "letters, digits, hyphens, periods and underscores only" };
|
|
60306
60546
|
TERRITORY = { pattern: /^[A-Z]{3}$/, hint: "an ISO 3166-1 alpha-3 territory code such as USA" };
|
|
60307
60547
|
CURRENCY = { pattern: /^[A-Z]{3}$/, hint: "an ISO 4217 currency code such as USD" };
|
|
60308
60548
|
AMOUNT = { pattern: /^\d+(\.\d+)?$/, hint: 'an exact decimal string such as "0.99"' };
|
|
@@ -60316,6 +60556,7 @@ var init_iap_manifest = __esm({
|
|
|
60316
60556
|
TARGET_KEYS = ["himiApp", "wixSiteId", "appleAppId", "bundleId", "teamId", "profile"];
|
|
60317
60557
|
ITEM_KEYS = [
|
|
60318
60558
|
"wix",
|
|
60559
|
+
"unlocks",
|
|
60319
60560
|
"classification",
|
|
60320
60561
|
"entitlement",
|
|
60321
60562
|
"productId",
|
|
@@ -60327,8 +60568,12 @@ var init_iap_manifest = __esm({
|
|
|
60327
60568
|
"territories"
|
|
60328
60569
|
];
|
|
60329
60570
|
WIX_KEYS = ["entityType", "entityId"];
|
|
60571
|
+
UNLOCK_KEYS = ["entityType", "entityId", "entitlement"];
|
|
60330
60572
|
LOCALIZATION_KEYS = ["locale", "name", "description"];
|
|
60331
|
-
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 };
|
|
60332
60577
|
childPath = (path2, key2) => path2 ? `${path2}.${key2}` : key2;
|
|
60333
60578
|
ManifestReader = class {
|
|
60334
60579
|
problems = [];
|
|
@@ -60368,6 +60613,15 @@ var init_iap_manifest = __esm({
|
|
|
60368
60613
|
if (obj3[key2] !== expected) this.problems.push(`${childPath(path2, key2)} must be ${JSON.stringify(expected)} \u2014 ${why}`);
|
|
60369
60614
|
return expected;
|
|
60370
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
|
+
}
|
|
60371
60625
|
duplicates(values, path2, what) {
|
|
60372
60626
|
const seen = /* @__PURE__ */ new Set();
|
|
60373
60627
|
for (const value of values) {
|
|
@@ -60379,6 +60633,84 @@ var init_iap_manifest = __esm({
|
|
|
60379
60633
|
}
|
|
60380
60634
|
});
|
|
60381
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
|
+
|
|
60382
60714
|
// ../store-release/iap-result.ts
|
|
60383
60715
|
function remainingRequirements(observed) {
|
|
60384
60716
|
if (!observed) return ["the product does not exist in App Store Connect yet"];
|
|
@@ -60391,8 +60723,8 @@ function mappingFor(target, item, appleResourceId) {
|
|
|
60391
60723
|
wixSiteId: target.wixSiteId,
|
|
60392
60724
|
appleAppId: target.appleAppId,
|
|
60393
60725
|
bundleId: target.bundleId,
|
|
60394
|
-
wixEntityType: item.wix.entityType,
|
|
60395
|
-
|
|
60726
|
+
...item.wix ? { wixEntityType: item.wix.entityType, wixEntityId: item.wix.entityId } : {},
|
|
60727
|
+
...item.unlocks ? { unlocks: item.unlocks } : {},
|
|
60396
60728
|
entitlement: item.entitlement,
|
|
60397
60729
|
appleProductId: item.productId,
|
|
60398
60730
|
...appleResourceId ? { appleResourceId } : {},
|
|
@@ -60420,7 +60752,7 @@ function itemResult(input) {
|
|
|
60420
60752
|
...observed?.availability ? { observed: observed.availability.territories, availableInNewTerritories: observed.availability.availableInNewTerritories } : {}
|
|
60421
60753
|
},
|
|
60422
60754
|
localizations: { requested: item.localizations.map((l) => l.locale), observed: observed?.localizations.map((l) => l.locale) ?? [] },
|
|
60423
|
-
remainingRequirements: remainingRequirements(observed),
|
|
60755
|
+
remainingRequirements: [...input.requirements ?? [], ...remainingRequirements(observed)],
|
|
60424
60756
|
lastReadAt: input.readAt.toISOString()
|
|
60425
60757
|
}
|
|
60426
60758
|
};
|
|
@@ -60458,11 +60790,10 @@ var init_iap_result = __esm({
|
|
|
60458
60790
|
});
|
|
60459
60791
|
|
|
60460
60792
|
// ../store-release/iap-plan.ts
|
|
60461
|
-
function
|
|
60462
|
-
|
|
60463
|
-
const
|
|
60464
|
-
|
|
60465
|
-
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`;
|
|
60466
60797
|
}
|
|
60467
60798
|
function describeOp(op) {
|
|
60468
60799
|
return "locale" in op ? `${op.kind} ${op.locale}` : op.kind;
|
|
@@ -60563,8 +60894,8 @@ async function observeProduct(api2, product) {
|
|
|
60563
60894
|
...availability ? { availability } : {}
|
|
60564
60895
|
};
|
|
60565
60896
|
}
|
|
60566
|
-
function priceMatches(observed, requested) {
|
|
60567
|
-
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;
|
|
60568
60899
|
}
|
|
60569
60900
|
function availabilityMatches(observed, territories) {
|
|
60570
60901
|
return observed !== void 0 && !observed.availableInNewTerritories && sameSet(observed.territories, territories);
|
|
@@ -60576,12 +60907,11 @@ function localizationDiffs(item, observed) {
|
|
|
60576
60907
|
return found.name === wanted.name && found.description === wanted.description ? [] : [`localization ${wanted.locale} differs`];
|
|
60577
60908
|
});
|
|
60578
60909
|
}
|
|
60579
|
-
function priceDiff(item, observed) {
|
|
60580
|
-
if (priceMatches(observed.price, item.price)) return [];
|
|
60910
|
+
function priceDiff(item, observed, target) {
|
|
60911
|
+
if (priceMatches(observed.price, item.price, target)) return [];
|
|
60581
60912
|
if (!observed.price) return ["no price is set"];
|
|
60582
60913
|
const prices = observed.price.customerPrices.join("/") || "none";
|
|
60583
|
-
|
|
60584
|
-
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)}`];
|
|
60585
60915
|
}
|
|
60586
60916
|
function availabilityDiff(item, observed) {
|
|
60587
60917
|
if (availabilityMatches(observed.availability, item.territories)) return [];
|
|
@@ -60589,8 +60919,8 @@ function availabilityDiff(item, observed) {
|
|
|
60589
60919
|
const auto = observed.availability.availableInNewTerritories ? " (plus new territories automatically)" : "";
|
|
60590
60920
|
return [`available in ${observed.availability.territories.join(", ")}${auto}; the manifest requests ${item.territories.join(", ")}`];
|
|
60591
60921
|
}
|
|
60592
|
-
function metadataDiffs(item, observed) {
|
|
60593
|
-
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)];
|
|
60594
60924
|
}
|
|
60595
60925
|
function isLocked(observed) {
|
|
60596
60926
|
return !EDITABLE_PRODUCT_STATES.has(observed.state) || observed.versions.some((v) => LOCKED_VERSION_STATES.has(v.state));
|
|
@@ -60598,8 +60928,9 @@ function isLocked(observed) {
|
|
|
60598
60928
|
function itemPlan(item, decision, fields = {}) {
|
|
60599
60929
|
return { productId: item.productId, decision, ops: [], conflicts: [], notes: [], ...fields };
|
|
60600
60930
|
}
|
|
60601
|
-
function creationPlan(item) {
|
|
60931
|
+
function creationPlan(item, target) {
|
|
60602
60932
|
return itemPlan(item, "create", {
|
|
60933
|
+
...target ? { applePrice: target, targetPrice: target } : {},
|
|
60603
60934
|
ops: [
|
|
60604
60935
|
{ kind: "createProduct" },
|
|
60605
60936
|
{ kind: "createVersion" },
|
|
@@ -60607,17 +60938,20 @@ function creationPlan(item) {
|
|
|
60607
60938
|
{ kind: "setPrice" },
|
|
60608
60939
|
{ kind: "setAvailability" }
|
|
60609
60940
|
],
|
|
60610
|
-
notes: [
|
|
60941
|
+
notes: [priceNote(item.price, target)]
|
|
60611
60942
|
});
|
|
60612
60943
|
}
|
|
60613
60944
|
function identityConflicts(item, observed) {
|
|
60614
60945
|
const conflicts = [];
|
|
60615
|
-
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
|
+
}
|
|
60616
60950
|
if (observed.referenceName !== item.referenceName) conflicts.push(`Apple's reference name is "${observed.referenceName}"; the manifest says "${item.referenceName}"`);
|
|
60617
60951
|
return conflicts;
|
|
60618
60952
|
}
|
|
60619
|
-
function lockedPlan(item, observed) {
|
|
60620
|
-
const diffs = metadataDiffs(item, observed);
|
|
60953
|
+
function lockedPlan(item, observed, target) {
|
|
60954
|
+
const diffs = metadataDiffs(item, observed, target);
|
|
60621
60955
|
if (!diffs.length) return itemPlan(item, "reuse", { observed });
|
|
60622
60956
|
const conflict = `${item.productId} is ${observed.state} and this command never changes a live or in-review product: ${diffs.join("; ")}`;
|
|
60623
60957
|
return itemPlan(item, "conflict", { observed, conflicts: [conflict] });
|
|
@@ -60633,58 +60967,54 @@ function localizationOps(item, observed) {
|
|
|
60633
60967
|
return found.name === wanted.name && found.description === wanted.description ? [] : [{ kind: "updateLocalization", locale: wanted.locale }];
|
|
60634
60968
|
});
|
|
60635
60969
|
}
|
|
60636
|
-
async function
|
|
60637
|
-
const points = await api2.listPricePoints(resourceId, price.baseTerritory);
|
|
60638
|
-
const wanted = normalizeAmount(price.amount);
|
|
60639
|
-
const point = points.find((p) => normalizeAmount(p.customerPrice) === wanted);
|
|
60640
|
-
const distance = (p) => Math.abs(Number(p.customerPrice) - Number(price.amount));
|
|
60641
|
-
const nearest = [...points].sort((a, b) => distance(a) - distance(b)).slice(0, 2).map((p) => p.customerPrice).sort((a, b) => Number(a) - Number(b));
|
|
60642
|
-
return { point, nearest };
|
|
60643
|
-
}
|
|
60644
|
-
function noExactPriceMessage(price, nearest) {
|
|
60645
|
-
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.`;
|
|
60646
|
-
}
|
|
60647
|
-
async function priceOps(api2, item, observed) {
|
|
60970
|
+
async function priceOps(pricing, item, observed, target) {
|
|
60648
60971
|
if (observed.price) {
|
|
60649
|
-
|
|
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`] };
|
|
60650
60974
|
}
|
|
60651
|
-
const {
|
|
60652
|
-
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"] };
|
|
60653
60977
|
}
|
|
60654
60978
|
function availabilityOps(item, observed) {
|
|
60655
60979
|
if (!observed.availability) return { ops: [{ kind: "setAvailability" }], conflicts: [] };
|
|
60656
60980
|
const conflicts = availabilityMatches(observed.availability, item.territories) ? [] : [`${availabilityDiff(item, observed)[0]} \u2014 existing availability is never changed here`];
|
|
60657
60981
|
return { ops: [], conflicts };
|
|
60658
60982
|
}
|
|
60659
|
-
async function editablePlan(
|
|
60660
|
-
const price = await priceOps(
|
|
60983
|
+
async function editablePlan(pricing, item, observed, target) {
|
|
60984
|
+
const price = await priceOps(pricing, item, observed, target);
|
|
60661
60985
|
const availability = availabilityOps(item, observed);
|
|
60662
60986
|
const conflicts = [...price.conflicts, ...availability.conflicts];
|
|
60663
60987
|
if (conflicts.length) return itemPlan(item, "conflict", { observed, conflicts });
|
|
60664
60988
|
const ops = [...localizationOps(item, observed), ...price.ops, ...availability.ops];
|
|
60665
|
-
return itemPlan(item, ops.length ? "update" : "reuse", { observed, ops });
|
|
60989
|
+
return itemPlan(item, ops.length ? "update" : "reuse", { observed, ops, ...price.applePrice ? { applePrice: price.applePrice } : {} });
|
|
60666
60990
|
}
|
|
60667
|
-
async function planForProduct(api2, item, product) {
|
|
60991
|
+
async function planForProduct(api2, item, product, pricing) {
|
|
60668
60992
|
const observed = await observeProduct(api2, product);
|
|
60993
|
+
const target = await pricing.targetAmount(item.price, product.id);
|
|
60669
60994
|
const conflicts = identityConflicts(item, observed);
|
|
60670
|
-
|
|
60671
|
-
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;
|
|
60672
60997
|
}
|
|
60673
|
-
async function planItem(api2, item, products) {
|
|
60998
|
+
async function planItem(pricing, api2, item, products) {
|
|
60674
60999
|
const nameClash = products.find((p) => p.referenceName === item.referenceName && p.productId !== item.productId);
|
|
60675
61000
|
if (nameClash) return itemPlan(item, "conflict", { conflicts: [`reference name "${item.referenceName}" is already used by ${nameClash.productId}`] });
|
|
60676
61001
|
const match = products.find((p) => p.productId === item.productId);
|
|
60677
|
-
|
|
61002
|
+
if (match) return planForProduct(api2, item, match, pricing);
|
|
61003
|
+
return creationPlan(item, await pricing.targetAmount(item.price, products[0]?.id));
|
|
60678
61004
|
}
|
|
60679
|
-
async function
|
|
61005
|
+
async function planItemsAt(context, manifest, indices = manifest.items.map((_, i) => i)) {
|
|
61006
|
+
const pricing = context.pricing ?? new PriceGrid(context.api);
|
|
60680
61007
|
return withAccessContext(context.identity, manifest.target, async () => {
|
|
60681
61008
|
await verifyTarget(context.api, manifest, context.identity);
|
|
60682
61009
|
const products = await discoverProducts(context, manifest);
|
|
60683
|
-
const plans =
|
|
60684
|
-
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));
|
|
60685
61012
|
return plans;
|
|
60686
61013
|
});
|
|
60687
61014
|
}
|
|
61015
|
+
async function planItems(context, manifest) {
|
|
61016
|
+
return [...(await planItemsAt(context, manifest)).values()];
|
|
61017
|
+
}
|
|
60688
61018
|
function sealPlan(unsealed) {
|
|
60689
61019
|
const normalized = JSON.parse(JSON.stringify(unsealed));
|
|
60690
61020
|
return { ...normalized, planDigest: sha256Digest(normalized) };
|
|
@@ -60726,26 +61056,32 @@ function verifyPlan2(doc) {
|
|
|
60726
61056
|
return plan;
|
|
60727
61057
|
}
|
|
60728
61058
|
async function statusIap(manifest, context) {
|
|
60729
|
-
const
|
|
61059
|
+
const pricing = context.pricing ?? new PriceGrid(context.api);
|
|
61060
|
+
const plans = await planItems({ ...context, pricing }, manifest);
|
|
60730
61061
|
const readAt = context.now();
|
|
60731
|
-
const items =
|
|
60732
|
-
|
|
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({
|
|
60733
61067
|
target: manifest.target,
|
|
60734
|
-
item
|
|
61068
|
+
item,
|
|
60735
61069
|
observed: p.observed,
|
|
60736
61070
|
outcome: STATUS_OUTCOMES[p.decision],
|
|
60737
61071
|
conflicts: p.conflicts,
|
|
60738
|
-
unmet: p.observed ? metadataDiffs(
|
|
61072
|
+
unmet: p.observed ? metadataDiffs(item, p.observed, target) : [],
|
|
60739
61073
|
readAt
|
|
60740
|
-
})
|
|
60741
|
-
|
|
61074
|
+
}));
|
|
61075
|
+
}
|
|
60742
61076
|
return iapResult({ target: manifest.target, identity: context.identity, items, generatedAt: readAt });
|
|
60743
61077
|
}
|
|
60744
|
-
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;
|
|
60745
61079
|
var init_iap_plan = __esm({
|
|
60746
61080
|
"../store-release/iap-plan.ts"() {
|
|
61081
|
+
init_iap_api();
|
|
60747
61082
|
init_iap_client();
|
|
60748
61083
|
init_iap_manifest();
|
|
61084
|
+
init_iap_pricing();
|
|
60749
61085
|
init_iap_result();
|
|
60750
61086
|
IapProvisionError = class extends Error {
|
|
60751
61087
|
constructor(reason, message) {
|
|
@@ -60755,7 +61091,6 @@ var init_iap_plan = __esm({
|
|
|
60755
61091
|
}
|
|
60756
61092
|
};
|
|
60757
61093
|
EDITABLE_VERSION_STATE = "PREPARE_FOR_SUBMISSION";
|
|
60758
|
-
APPLE_NON_CONSUMABLE = "NON_CONSUMABLE";
|
|
60759
61094
|
EDITABLE_PRODUCT_STATES = /* @__PURE__ */ new Set(["MISSING_METADATA", "READY_TO_SUBMIT", "DEVELOPER_ACTION_NEEDED", "REJECTED"]);
|
|
60760
61095
|
LOCKED_VERSION_STATES = /* @__PURE__ */ new Set(["READY_FOR_REVIEW", "WAITING_FOR_REVIEW", "IN_REVIEW", "ACCEPTED", "APPROVED"]);
|
|
60761
61096
|
OP_KINDS = /* @__PURE__ */ new Set(["createProduct", "createVersion", "createLocalization", "updateLocalization", "setPrice", "setAvailability"]);
|
|
@@ -60763,6 +61098,7 @@ var init_iap_plan = __esm({
|
|
|
60763
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";
|
|
60764
61099
|
STATUS_OUTCOMES = { create: "absent", update: "incomplete", reuse: "reused", conflict: "conflict" };
|
|
60765
61100
|
isoDay = (date) => date.toISOString().slice(0, 10);
|
|
61101
|
+
exactTarget = (price) => price.rule === "atLeast" ? void 0 : price.amount;
|
|
60766
61102
|
planError = (message) => new IapProvisionError("plan", message);
|
|
60767
61103
|
}
|
|
60768
61104
|
});
|
|
@@ -60785,7 +61121,7 @@ function driftOf(planned, live2) {
|
|
|
60785
61121
|
return unplanned.length ? `${live2.productId} now needs ${unplanned.map(describeOp).join(", ")}, which the plan did not list` : void 0;
|
|
60786
61122
|
}
|
|
60787
61123
|
function assertNoDrift(plan, live2) {
|
|
60788
|
-
const drift =
|
|
61124
|
+
const drift = [...live2].map(([i, current]) => driftOf(plan.items[i], current)).filter((d) => d !== void 0);
|
|
60789
61125
|
if (!drift.length) return;
|
|
60790
61126
|
throw new IapProvisionError("drift", `App Store Connect changed since the plan was made \u2014 nothing was written:
|
|
60791
61127
|
- ${drift.join("\n - ")}
|
|
@@ -60836,12 +61172,13 @@ async function findLocalization(api2, versionId, locale, matchText) {
|
|
|
60836
61172
|
if (!found || matchText && (found.name !== matchText.name || found.description !== matchText.description)) return void 0;
|
|
60837
61173
|
return found;
|
|
60838
61174
|
}
|
|
60839
|
-
function operationRunners(
|
|
61175
|
+
function operationRunners(context, manifest) {
|
|
61176
|
+
const { api: api2, sleep, pricing } = context;
|
|
60840
61177
|
const appId2 = manifest.target.appleAppId;
|
|
60841
61178
|
return {
|
|
60842
61179
|
createProduct: async (run2) => {
|
|
60843
61180
|
const { item } = run2;
|
|
60844
|
-
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 } : {} };
|
|
60845
61182
|
const { value, state } = await ensure({ write: () => api2.createProduct(appId2, newProduct), find: () => findProduct(api2, appId2, item.productId) }, sleep);
|
|
60846
61183
|
run2.resourceId = value.id;
|
|
60847
61184
|
return { state, resourceId: value.id };
|
|
@@ -60869,9 +61206,9 @@ function operationRunners(api2, manifest, sleep) {
|
|
|
60869
61206
|
setPrice: async (run2) => {
|
|
60870
61207
|
const resourceId = required(run2.resourceId, "the product id");
|
|
60871
61208
|
const { price } = run2.item;
|
|
60872
|
-
const { point,
|
|
60873
|
-
if (!point) throw new UnresolvedWriteError(
|
|
60874
|
-
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;
|
|
60875
61212
|
const { state } = await ensure({ write: async () => (await api2.createPriceSchedule(resourceId, price.baseTerritory, point.id), true), find: matches }, sleep);
|
|
60876
61213
|
return { state };
|
|
60877
61214
|
},
|
|
@@ -60904,10 +61241,10 @@ async function runStep(runner2, run2, op, context, manifest) {
|
|
|
60904
61241
|
context.journal.record({ productId: run2.item.productId, ...run2.resourceId ? { productResourceId: run2.resourceId } : {}, ...entry, at: context.now().toISOString() });
|
|
60905
61242
|
return step;
|
|
60906
61243
|
}
|
|
60907
|
-
async function resumeFromApple(run2, remaining,
|
|
60908
|
-
const product = await
|
|
61244
|
+
async function resumeFromApple(run2, remaining, context) {
|
|
61245
|
+
const product = await context.api.readProduct(required(run2.resourceId, "the product id"));
|
|
60909
61246
|
if (!product) return { reason: "the product exists but cannot be read back yet \u2014 rerun apply later" };
|
|
60910
|
-
const replanned = await planForProduct(
|
|
61247
|
+
const replanned = await planForProduct(context.api, run2.item, product, context.pricing);
|
|
60911
61248
|
if (replanned.decision === "conflict") return { reason: `the product already existed: ${replanned.conflicts.join("; ")}` };
|
|
60912
61249
|
const unplanned = replanned.ops.filter((op) => !remaining.some((r) => sameOp(r, op)));
|
|
60913
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` };
|
|
@@ -60916,7 +61253,7 @@ async function resumeFromApple(run2, remaining, api2) {
|
|
|
60916
61253
|
return { ops: replanned.ops };
|
|
60917
61254
|
}
|
|
60918
61255
|
async function runOps(run2, ops, context, manifest) {
|
|
60919
|
-
const runners = operationRunners(context
|
|
61256
|
+
const runners = operationRunners(context, manifest);
|
|
60920
61257
|
const steps = [];
|
|
60921
61258
|
let queue = [...ops];
|
|
60922
61259
|
for (let op = queue.shift(); op; op = queue.shift()) {
|
|
@@ -60924,7 +61261,7 @@ async function runOps(run2, ops, context, manifest) {
|
|
|
60924
61261
|
steps.push(step);
|
|
60925
61262
|
if (step.state === "failed") return [...steps, ...queue.map((rest) => stepFor(rest, "notRun"))];
|
|
60926
61263
|
if (op.kind === "createProduct" && step.state === "reconciled") {
|
|
60927
|
-
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)}` }));
|
|
60928
61265
|
if ("reason" in resumed) return [...steps, ...queue.map((rest, i) => stepFor(rest, "notRun", i === 0 ? { detail: resumed.reason } : {}))];
|
|
60929
61266
|
queue = resumed.ops;
|
|
60930
61267
|
}
|
|
@@ -60947,10 +61284,17 @@ async function readBack(run2, context, manifest) {
|
|
|
60947
61284
|
return { failure: explainFailure(e, context, manifest) };
|
|
60948
61285
|
}
|
|
60949
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
|
+
}
|
|
60950
61294
|
async function applyItem(item, live2, context, manifest) {
|
|
60951
61295
|
const base = { target: manifest.target, item, readAt: context.now() };
|
|
60952
61296
|
if (live2.decision === "conflict") {
|
|
60953
|
-
const unmet2 =
|
|
61297
|
+
const unmet2 = await knownUnmetFor(item, live2, context);
|
|
60954
61298
|
return itemResult({ ...base, observed: live2.observed, outcome: "conflict", conflicts: live2.conflicts, unmet: unmet2 });
|
|
60955
61299
|
}
|
|
60956
61300
|
const run2 = {
|
|
@@ -60961,29 +61305,55 @@ async function applyItem(item, live2, context, manifest) {
|
|
|
60961
61305
|
};
|
|
60962
61306
|
const steps = await runOps(run2, live2.ops, context, manifest);
|
|
60963
61307
|
const { observed, failure } = await readBack(run2, context, manifest);
|
|
60964
|
-
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);
|
|
60965
61309
|
const outcome = outcomeOf(live2.ops, steps, unmet);
|
|
60966
61310
|
return itemResult({ ...base, readAt: context.now(), observed, outcome, steps, unmet, appleResourceId: run2.resourceId });
|
|
60967
61311
|
}
|
|
60968
|
-
function
|
|
60969
|
-
|
|
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() });
|
|
61315
|
+
}
|
|
61316
|
+
function needsRefresh(planned) {
|
|
61317
|
+
return planned.decision !== "conflict" && planned.ops.length > 0;
|
|
60970
61318
|
}
|
|
60971
|
-
|
|
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) {
|
|
60972
61324
|
const plan = verifyPlan2(planDoc);
|
|
60973
|
-
assertSameAccount(plan.identity,
|
|
61325
|
+
assertSameAccount(plan.identity, applyContext.identity);
|
|
61326
|
+
const context = { ...applyContext, pricing: applyContext.pricing ?? new PriceGrid(applyContext.api) };
|
|
60974
61327
|
const manifest = plan.manifest;
|
|
60975
|
-
const
|
|
61328
|
+
const refresh = plan.items.flatMap((planned, i) => needsRefresh(planned) ? [i] : []);
|
|
61329
|
+
const live2 = await planItemsAt(context, manifest, refresh);
|
|
60976
61330
|
assertNoDrift(plan, live2);
|
|
60977
61331
|
const items = [];
|
|
60978
|
-
|
|
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
|
+
}
|
|
60979
61347
|
return iapResult({ target: manifest.target, identity: context.identity, items, generatedAt: context.now() });
|
|
60980
61348
|
}
|
|
60981
|
-
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;
|
|
60982
61350
|
var init_iap_apply = __esm({
|
|
60983
61351
|
"../store-release/iap-apply.ts"() {
|
|
60984
61352
|
init_iap_client();
|
|
60985
61353
|
init_iap_plan();
|
|
61354
|
+
init_iap_pricing();
|
|
60986
61355
|
init_iap_result();
|
|
61356
|
+
DEFERRED_REQUIREMENT = "deferred by --limit \u2014 rerun apply to continue; nothing was written for this product in this run";
|
|
60987
61357
|
RECONCILE_READS = 4;
|
|
60988
61358
|
RECONCILE_BASE_WAIT_MS = 1e3;
|
|
60989
61359
|
AMBIGUOUS_WRITE_KINDS = /* @__PURE__ */ new Set(["uncertain", "rateLimited", "conflict"]);
|
|
@@ -61184,6 +61554,182 @@ var init_iap_credentials = __esm({
|
|
|
61184
61554
|
}
|
|
61185
61555
|
});
|
|
61186
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
|
+
|
|
61187
61733
|
// src/base44-analyze/redact.ts
|
|
61188
61734
|
function isSecretKey(key2) {
|
|
61189
61735
|
const words = key2.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
@@ -61267,6 +61813,161 @@ var init_redact = __esm({
|
|
|
61267
61813
|
}
|
|
61268
61814
|
});
|
|
61269
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
|
+
|
|
61270
61971
|
// src/store-iap.ts
|
|
61271
61972
|
var store_iap_exports = {};
|
|
61272
61973
|
__export(store_iap_exports, {
|
|
@@ -61277,7 +61978,7 @@ __export(store_iap_exports, {
|
|
|
61277
61978
|
runStoreIap: () => runStoreIap
|
|
61278
61979
|
});
|
|
61279
61980
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
61280
|
-
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";
|
|
61281
61982
|
import { homedir as homedir7, hostname } from "node:os";
|
|
61282
61983
|
import { join as join65 } from "node:path";
|
|
61283
61984
|
function scrubDeep(value, scrub) {
|
|
@@ -61348,9 +62049,27 @@ function printItemPlan(out2, item) {
|
|
|
61348
62049
|
for (const note of item.notes) out2.line(` note: ${note}`);
|
|
61349
62050
|
for (const conflict of item.conflicts) out2.line(` \u2717 conflict: ${conflict}`);
|
|
61350
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
|
+
}
|
|
61351
62062
|
function printPlanItems(out2, plan) {
|
|
61352
62063
|
printHeader(out2, plan.identity, plan.target);
|
|
61353
|
-
|
|
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
|
+
}
|
|
61354
62073
|
out2.line();
|
|
61355
62074
|
}
|
|
61356
62075
|
function printPlan(out2, plan, planPath) {
|
|
@@ -61386,14 +62105,22 @@ function printItemResult(out2, item) {
|
|
|
61386
62105
|
}
|
|
61387
62106
|
function printResult(out2, result, resultPath) {
|
|
61388
62107
|
printHeader(out2, result.identity, result.target);
|
|
61389
|
-
|
|
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
|
+
}
|
|
61390
62116
|
out2.line();
|
|
61391
62117
|
if (resultPath) out2.line(` result written to ${resultPath}`);
|
|
61392
62118
|
out2.line(" backend sync: not configured \xB7 checkout: not active \u2014 the result is a mapping candidate, not an activation.");
|
|
61393
62119
|
}
|
|
61394
|
-
function applyQuestion(plan) {
|
|
62120
|
+
function applyQuestion(plan, limit) {
|
|
61395
62121
|
const operations = plan.items.reduce((n, item) => n + item.ops.length, 0);
|
|
61396
|
-
|
|
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}?`;
|
|
61397
62124
|
}
|
|
61398
62125
|
async function planCommand(flags, deps, out2) {
|
|
61399
62126
|
const manifestPath = requireFlag(flags, "manifest", "plan");
|
|
@@ -61408,10 +62135,17 @@ async function planCommand(flags, deps, out2) {
|
|
|
61408
62135
|
else printPlan(out2, plan, planPath);
|
|
61409
62136
|
return plan.items.some((i) => i.decision === "conflict") ? 1 : 0;
|
|
61410
62137
|
}
|
|
61411
|
-
|
|
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) {
|
|
61412
62145
|
const release = deps.acquireLock(plan.target.appleAppId);
|
|
61413
62146
|
try {
|
|
61414
|
-
|
|
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 });
|
|
61415
62149
|
} finally {
|
|
61416
62150
|
release();
|
|
61417
62151
|
}
|
|
@@ -61420,6 +62154,7 @@ async function applyCommand(flags, deps, out2) {
|
|
|
61420
62154
|
const planPath = requireFlag(flags, "plan", "apply");
|
|
61421
62155
|
const profile = requireFlag(flags, "profile", "apply");
|
|
61422
62156
|
const resultPath = requireFlag(flags, "out", "apply");
|
|
62157
|
+
const limit = createLimit(flags);
|
|
61423
62158
|
const confirmed = flags.yes === true;
|
|
61424
62159
|
const canAsk = out2.interactive && !out2.json && deps.canPrompt();
|
|
61425
62160
|
if (!confirmed && !canAsk) {
|
|
@@ -61431,11 +62166,11 @@ async function applyCommand(flags, deps, out2) {
|
|
|
61431
62166
|
const plan = readPlan(deps, planPath);
|
|
61432
62167
|
const session = openSession(deps, out2, plan.target, profile);
|
|
61433
62168
|
if (!out2.json) printApplyPreview(out2, plan, planPath);
|
|
61434
|
-
if (!confirmed && !await deps.confirm(applyQuestion(plan))) {
|
|
62169
|
+
if (!confirmed && !await deps.confirm(applyQuestion(plan, limit))) {
|
|
61435
62170
|
out2.line(" aborted \u2014 nothing was changed in App Store Connect.");
|
|
61436
62171
|
return 1;
|
|
61437
62172
|
}
|
|
61438
|
-
const result = await applyWithLock(deps, plan, session);
|
|
62173
|
+
const result = await applyWithLock(deps, plan, session, limit);
|
|
61439
62174
|
writeJson(deps, out2, resultPath, result);
|
|
61440
62175
|
if (out2.json) out2.print({ ok: result.complete, result: resultPath, items: result.items });
|
|
61441
62176
|
else printResult(out2, result, resultPath);
|
|
@@ -61453,6 +62188,104 @@ async function statusCommand(flags, deps, out2) {
|
|
|
61453
62188
|
else printResult(out2, result, resultPath);
|
|
61454
62189
|
return result.complete ? 0 : 1;
|
|
61455
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
|
+
}
|
|
61456
62289
|
async function runStoreIap(pos, flags, io, deps) {
|
|
61457
62290
|
const out2 = new CommandOutput(io, flags.json === true);
|
|
61458
62291
|
const sub = pos[2];
|
|
@@ -61465,18 +62298,10 @@ async function runStoreIap(pos, flags, io, deps) {
|
|
|
61465
62298
|
return failFrom(e, out2);
|
|
61466
62299
|
}
|
|
61467
62300
|
}
|
|
61468
|
-
function readJournalEntries(path2) {
|
|
61469
|
-
try {
|
|
61470
|
-
const doc = JSON.parse(readFileSync77(path2, "utf8"));
|
|
61471
|
-
return Array.isArray(doc?.entries) ? doc.entries : [];
|
|
61472
|
-
} catch {
|
|
61473
|
-
return [];
|
|
61474
|
-
}
|
|
61475
|
-
}
|
|
61476
62301
|
function fileJournal(path2) {
|
|
61477
62302
|
return {
|
|
61478
|
-
record: (entry) =>
|
|
61479
|
-
|
|
62303
|
+
record: (entry) => appendFileSync2(path2, `${JSON.stringify(entry)}
|
|
62304
|
+
`, { mode: 384 })
|
|
61480
62305
|
};
|
|
61481
62306
|
}
|
|
61482
62307
|
function readLockSummary(path2) {
|
|
@@ -61529,26 +62354,35 @@ function defaultStoreIapDeps() {
|
|
|
61529
62354
|
writeFileAtomic,
|
|
61530
62355
|
resolveCredentials: ({ profileFlag, target }) => resolveIapCredentials({ profileFlag, target }),
|
|
61531
62356
|
createApi: (key2) => createIapApi(createAscClient({ key: key2 })),
|
|
61532
|
-
openJournal: (appleAppId) => fileJournal(join65(stateDir(appleAppId), "journal.
|
|
62357
|
+
openJournal: (appleAppId) => fileJournal(join65(stateDir(appleAppId), "journal.jsonl")),
|
|
61533
62358
|
acquireLock: (appleAppId) => acquireApplyLock(stateDir(appleAppId), appleAppId),
|
|
61534
62359
|
canPrompt: () => process.stdin.isTTY === true,
|
|
62360
|
+
wixTransport: (siteId) => ownerTransport(siteId),
|
|
62361
|
+
profileTeam: (profile) => resolveStoreCredentials({ app: profile, cfg: {}, profileFlag: profile, env: {} }).team,
|
|
61535
62362
|
confirm: confirmOnTerminal,
|
|
61536
62363
|
now: () => /* @__PURE__ */ new Date(),
|
|
61537
62364
|
sleep: (ms) => new Promise((resolve56) => setTimeout(resolve56, ms))
|
|
61538
62365
|
};
|
|
61539
62366
|
}
|
|
61540
|
-
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;
|
|
61541
62368
|
var init_store_iap = __esm({
|
|
61542
62369
|
"src/store-iap.ts"() {
|
|
61543
62370
|
init_iap_api();
|
|
61544
62371
|
init_iap_apply();
|
|
61545
62372
|
init_iap_client();
|
|
62373
|
+
init_credential_profile();
|
|
61546
62374
|
init_iap_credentials();
|
|
62375
|
+
init_iap_draft();
|
|
61547
62376
|
init_iap_manifest();
|
|
62377
|
+
init_iap_pricing();
|
|
61548
62378
|
init_iap_plan();
|
|
61549
62379
|
init_redact();
|
|
61550
62380
|
init_progress();
|
|
61551
|
-
|
|
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;
|
|
61552
62386
|
UsageError = class extends Error {
|
|
61553
62387
|
};
|
|
61554
62388
|
CommandOutput = class {
|
|
@@ -61587,8 +62421,15 @@ var init_store_iap = __esm({
|
|
|
61587
62421
|
}
|
|
61588
62422
|
};
|
|
61589
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"];
|
|
61590
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";
|
|
61591
62431
|
COMMANDS = {
|
|
62432
|
+
draft: draftCommand,
|
|
61592
62433
|
plan: planCommand,
|
|
61593
62434
|
apply: applyCommand,
|
|
61594
62435
|
status: statusCommand
|
|
@@ -61973,7 +62814,7 @@ __export(preflight_app_exports, {
|
|
|
61973
62814
|
profileAccount: () => profileAccount
|
|
61974
62815
|
});
|
|
61975
62816
|
import { readFileSync as readFileSync78, existsSync as existsSync67, readdirSync as readdirSync31, statSync as statSync18 } from "node:fs";
|
|
61976
|
-
import { createHash as
|
|
62817
|
+
import { createHash as createHash34 } from "node:crypto";
|
|
61977
62818
|
import { join as join66 } from "node:path";
|
|
61978
62819
|
function analyze(projectYml, storeConfig, icon, account) {
|
|
61979
62820
|
const f = [];
|
|
@@ -62262,7 +63103,7 @@ function placeholderHashes2() {
|
|
|
62262
63103
|
const p = join66(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
|
|
62263
63104
|
try {
|
|
62264
63105
|
if (!statSync18(p).isFile()) continue;
|
|
62265
|
-
return [LEGACY_PLACEHOLDER_SHA2562,
|
|
63106
|
+
return [LEGACY_PLACEHOLDER_SHA2562, createHash34("sha256").update(readFileSync78(p)).digest("hex")];
|
|
62266
63107
|
} catch {
|
|
62267
63108
|
}
|
|
62268
63109
|
}
|
|
@@ -62299,7 +63140,7 @@ async function appIconState(iosDir) {
|
|
|
62299
63140
|
try {
|
|
62300
63141
|
if (!statSync18(p).isFile()) continue;
|
|
62301
63142
|
const bytes2 = readFileSync78(p);
|
|
62302
|
-
const hash =
|
|
63143
|
+
const hash = createHash34("sha256").update(bytes2).digest("hex");
|
|
62303
63144
|
const pixels = await pixelIssues(bytes2);
|
|
62304
63145
|
return {
|
|
62305
63146
|
exists: true,
|
|
@@ -62872,7 +63713,7 @@ var init_site_member = __esm({
|
|
|
62872
63713
|
});
|
|
62873
63714
|
|
|
62874
63715
|
// src/notify-ping-all.ts
|
|
62875
|
-
import { createHash as
|
|
63716
|
+
import { createHash as createHash35 } from "node:crypto";
|
|
62876
63717
|
async function listApprovedMembers(input) {
|
|
62877
63718
|
const first = await scanMembers(input);
|
|
62878
63719
|
const second = await scanMembers(input);
|
|
@@ -62926,7 +63767,7 @@ async function scanMembers(input) {
|
|
|
62926
63767
|
}
|
|
62927
63768
|
}
|
|
62928
63769
|
function campaignRequestId(campaignId, memberId) {
|
|
62929
|
-
const bytes2 =
|
|
63770
|
+
const bytes2 = createHash35("sha256").update(JSON.stringify(["himi-push-campaign", campaignId, memberId])).digest().subarray(0, 16);
|
|
62930
63771
|
bytes2[6] = bytes2[6] & 15 | 80;
|
|
62931
63772
|
bytes2[8] = bytes2[8] & 63 | 128;
|
|
62932
63773
|
const hex2 = bytes2.toString("hex");
|
|
@@ -64086,7 +64927,7 @@ var init_copy_clean = __esm({
|
|
|
64086
64927
|
|
|
64087
64928
|
// src/site-analyze/published-html.ts
|
|
64088
64929
|
function textFromHtml(fragment) {
|
|
64089
|
-
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, " ");
|
|
64090
64931
|
}
|
|
64091
64932
|
function unescapeJsonString(s) {
|
|
64092
64933
|
return s.replace(/\\"/g, '"').replace(/\\n/g, " ").replace(/\\\\/g, "\\");
|
|
@@ -64225,7 +65066,7 @@ function appsFromDefIds(ids) {
|
|
|
64225
65066
|
return name ? { id, name } : { id };
|
|
64226
65067
|
});
|
|
64227
65068
|
}
|
|
64228
|
-
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;
|
|
64229
65070
|
var init_published_html = __esm({
|
|
64230
65071
|
"src/site-analyze/published-html.ts"() {
|
|
64231
65072
|
init_rest();
|
|
@@ -64256,7 +65097,7 @@ var init_published_html = __esm({
|
|
|
64256
65097
|
"14cc59bc-f0b7-15b8-e1c7-89ce41d0e0c9": "Wix Bookings Service Pages",
|
|
64257
65098
|
"215238eb-22a5-4c36-9e7b-e7c08025e04e": "Wix Forms"
|
|
64258
65099
|
};
|
|
64259
|
-
|
|
65100
|
+
ENTITIES2 = {
|
|
64260
65101
|
"&": "&",
|
|
64261
65102
|
"<": "<",
|
|
64262
65103
|
">": ">",
|
|
@@ -73100,10 +73941,10 @@ async function installVerticals(msid, wanted, token, fetchImpl = fetch, timeoutM
|
|
|
73100
73941
|
body = text8 ? JSON.parse(text8) : {};
|
|
73101
73942
|
} catch {
|
|
73102
73943
|
}
|
|
73103
|
-
const
|
|
73944
|
+
const nameOf2 = (appDefId) => wanted.find((w) => w.appDefId === appDefId)?.vertical ?? appDefId;
|
|
73104
73945
|
const out2 = { installed: [], errors: [], needsSetup: [] };
|
|
73105
73946
|
for (const [appDefId, info2] of Object.entries(body.installedApps ?? {})) {
|
|
73106
|
-
const vertical =
|
|
73947
|
+
const vertical = nameOf2(appDefId);
|
|
73107
73948
|
out2.installed.push({ vertical, appDefId, version: info2?.version });
|
|
73108
73949
|
const actions = (info2?.requiredActions ?? []).filter((a) => a?.actionType || a?.link);
|
|
73109
73950
|
for (const a of actions) {
|
|
@@ -73116,7 +73957,7 @@ async function installVerticals(msid, wanted, token, fetchImpl = fetch, timeoutM
|
|
|
73116
73957
|
}
|
|
73117
73958
|
}
|
|
73118
73959
|
for (const [appDefId, err] of Object.entries(body.installErrors ?? {})) {
|
|
73119
|
-
out2.errors.push({ vertical:
|
|
73960
|
+
out2.errors.push({ vertical: nameOf2(appDefId), appDefId, message: err?.message ?? "unknown" });
|
|
73120
73961
|
}
|
|
73121
73962
|
return out2;
|
|
73122
73963
|
}
|
|
@@ -73983,7 +74824,7 @@ var init_context = __esm({
|
|
|
73983
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";
|
|
73984
74825
|
import { execFile as execFile2, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn7 } from "node:child_process";
|
|
73985
74826
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
73986
|
-
import { createHash as
|
|
74827
|
+
import { createHash as createHash36, randomUUID as randomUUID8 } from "node:crypto";
|
|
73987
74828
|
import { tmpdir as tmpdir6, homedir as homedir10, hostname as hostname2 } from "node:os";
|
|
73988
74829
|
import { join as join73, resolve as resolve55, relative as relative14, basename as basename17, dirname as dirname40 } from "node:path";
|
|
73989
74830
|
import { fileURLToPath as fileURLToPath26 } from "node:url";
|
|
@@ -74552,11 +75393,11 @@ async function loadReleaseFromDir(app, dir) {
|
|
|
74552
75393
|
const bundles = {};
|
|
74553
75394
|
const seenSha = /* @__PURE__ */ new Set();
|
|
74554
75395
|
for (const meta of Object.values(manifest.bundles ?? {})) {
|
|
74555
|
-
const
|
|
74556
|
-
if (!
|
|
74557
|
-
seenSha.add(
|
|
74558
|
-
const b = await store.getBundle(app,
|
|
74559
|
-
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");
|
|
74560
75401
|
}
|
|
74561
75402
|
const tokensBytes = await store.getTokens(app);
|
|
74562
75403
|
const tokens2 = tokensBytes ? utf8Decode(tokensBytes) : void 0;
|
|
@@ -76623,16 +77464,28 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
|
|
|
76623
77464
|
team (guideline 4.2.6), declared per-app as
|
|
76624
77465
|
ios.credentialProfile in store.config.yaml and resolved
|
|
76625
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).
|
|
76626
77477
|
himi store iap plan --manifest <file> --profile <name> --out <plan.json> [--json]
|
|
76627
77478
|
READ-ONLY. Verify the owner's App Store Connect app and
|
|
76628
77479
|
decide which selected digital products (one-time
|
|
76629
77480
|
non-consumables) to register; writes a reviewable plan.
|
|
76630
|
-
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]
|
|
76631
77482
|
create the planned products with draft metadata, an
|
|
76632
77483
|
exact price and explicit territories. Re-reads Apple
|
|
76633
77484
|
first, never edits a live product, never duplicates on
|
|
76634
77485
|
a rerun; prompts unless --yes. Writes the Wix\u2192Apple
|
|
76635
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).
|
|
76636
77489
|
himi store iap status --manifest <file> --profile <name> [--out <file>] [--json]
|
|
76637
77490
|
READ-ONLY: what App Store Connect has for each product.
|
|
76638
77491
|
No build, signing or repo checkout. Guide: himi docs iap-products
|
|
@@ -76918,7 +77771,7 @@ var COMMANDS2 = [
|
|
|
76918
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)" },
|
|
76919
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>]" },
|
|
76920
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]" },
|
|
76921
|
-
{ 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]" },
|
|
76922
77775
|
{ name: "guide", summary: "print the Himalaya overview + headless dev loop + available topics (offline)", required: "[--json]" },
|
|
76923
77776
|
{ name: "docs", summary: "print an authoring guide; no topic = list them (offline)", required: "[<topic>] [--json]" },
|
|
76924
77777
|
{ name: "components", summary: "print the SDUI component vocabulary + props; a name = that component (offline)", required: "[<Name>] [--json]" },
|
|
@@ -78143,7 +78996,7 @@ function templateNameError(name, from) {
|
|
|
78143
78996
|
const composed2 = TEMPLATE_APP_PREFIX + name;
|
|
78144
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`;
|
|
78145
78998
|
}
|
|
78146
|
-
function materializeForPreview(appDir3, slug,
|
|
78999
|
+
function materializeForPreview(appDir3, slug, displayName2) {
|
|
78147
79000
|
if (!isTemplateSourceTree(appDir3)) return appDir3;
|
|
78148
79001
|
const slugError = templateNameError(slug, "preview slug");
|
|
78149
79002
|
if (slugError) throw Object.assign(new Error(slugError), { exitCode: 2 });
|
|
@@ -78175,7 +79028,7 @@ function materializeForPreview(appDir3, slug, displayName) {
|
|
|
78175
79028
|
rmSync20(dest, { recursive: true, force: true });
|
|
78176
79029
|
mkdirSync39(dest, { recursive: true });
|
|
78177
79030
|
try {
|
|
78178
|
-
materializeSource(forBuild,
|
|
79031
|
+
materializeSource(forBuild, displayName2, dest, slug);
|
|
78179
79032
|
} catch (e) {
|
|
78180
79033
|
rmSync20(dest, { recursive: true, force: true });
|
|
78181
79034
|
throw e;
|
|
@@ -79365,7 +80218,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
|
|
|
79365
80218
|
try {
|
|
79366
80219
|
const bytes2 = readFileSync88(iconPath);
|
|
79367
80220
|
payload.icon = bytes2.toString("base64");
|
|
79368
|
-
iconHash =
|
|
80221
|
+
iconHash = createHash36("sha256").update(bytes2).digest("hex");
|
|
79369
80222
|
} catch {
|
|
79370
80223
|
}
|
|
79371
80224
|
}
|
|
@@ -81214,7 +82067,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
81214
82067
|
let localSourceHash;
|
|
81215
82068
|
try {
|
|
81216
82069
|
const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
|
|
81217
|
-
localSourceHash =
|
|
82070
|
+
localSourceHash = createHash36("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
|
|
81218
82071
|
} catch {
|
|
81219
82072
|
}
|
|
81220
82073
|
return runReleasePatch({
|
|
@@ -81606,9 +82459,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
81606
82459
|
return 2;
|
|
81607
82460
|
}
|
|
81608
82461
|
const { packRelease: packRelease2, packReleaseToDir: packReleaseToDir2 } = await Promise.resolve().then(() => (init_pack(), pack_exports));
|
|
81609
|
-
const
|
|
82462
|
+
const displayName2 = str9(flags.name);
|
|
81610
82463
|
const launchMode = flags.snapshot === true ? "snapshot" : "live";
|
|
81611
|
-
const packOpts = { createdBy: `himi-cli ${cliVersion()}`, launchMode, ...
|
|
82464
|
+
const packOpts = { createdBy: `himi-cli ${cliVersion()}`, launchMode, ...displayName2 ? { displayName: displayName2 } : {} };
|
|
81612
82465
|
const writePack = (root, appName2, out2, source) => format === "dir" ? packReleaseToDir2(root, appName2, out2, packOpts) : packRelease2(root, appName2, out2, { ...packOpts, ...source ? { source } : {} });
|
|
81613
82466
|
const releaseDir = str9(flags["release-dir"]);
|
|
81614
82467
|
if (releaseDir) {
|
|
@@ -82860,7 +83713,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82860
83713
|
});
|
|
82861
83714
|
}
|
|
82862
83715
|
const bytes2 = readFileSync88(file);
|
|
82863
|
-
const
|
|
83716
|
+
const sha2 = createHash36("sha256").update(bytes2).digest("hex");
|
|
82864
83717
|
const forScreens = (str9(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
82865
83718
|
if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
|
|
82866
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 }));
|
|
@@ -82886,7 +83739,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82886
83739
|
ok: false,
|
|
82887
83740
|
error: "orphan_bundle",
|
|
82888
83741
|
app,
|
|
82889
|
-
sha256:
|
|
83742
|
+
sha256: sha2,
|
|
82890
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.",
|
|
82891
83744
|
hint: "name the screens to repoint: --for-screen cart,orders. Pass --no-rebind if you really mean to upload bytes nothing references yet.",
|
|
82892
83745
|
alternative: "`himi push --only bundle:<name>` infers this from the package and drags the referencing screens in for you.",
|
|
@@ -82895,9 +83748,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82895
83748
|
return 2;
|
|
82896
83749
|
}
|
|
82897
83750
|
const patch = {
|
|
82898
|
-
bundles: { [
|
|
83751
|
+
bundles: { [sha2]: bytes2.toString("base64") }
|
|
82899
83752
|
};
|
|
82900
|
-
const touched = [`bundle:${
|
|
83753
|
+
const touched = [`bundle:${sha2.slice(0, 12)}`];
|
|
82901
83754
|
const rebound = {};
|
|
82902
83755
|
const bundleBase = await fetchReleaseManifest(client2, app, fromRelease);
|
|
82903
83756
|
if ("error" in bundleBase) {
|
|
@@ -82930,8 +83783,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82930
83783
|
}, null, 2));
|
|
82931
83784
|
return 1;
|
|
82932
83785
|
}
|
|
82933
|
-
patch.screens[id] = { descriptor: got.text, bundle:
|
|
82934
|
-
rebound[id] =
|
|
83786
|
+
patch.screens[id] = { descriptor: got.text, bundle: sha2 };
|
|
83787
|
+
rebound[id] = sha2;
|
|
82935
83788
|
touched.push(`screen:${id}`);
|
|
82936
83789
|
}
|
|
82937
83790
|
}
|
|
@@ -82939,7 +83792,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
|
|
|
82939
83792
|
...common,
|
|
82940
83793
|
fromRelease: baseId,
|
|
82941
83794
|
plan: { patch, touched, included: [], rebound },
|
|
82942
|
-
extra: { kind, file: { path: file, bytes: bytes2.length, sha256:
|
|
83795
|
+
extra: { kind, file: { path: file, bytes: bytes2.length, sha256: sha2 } }
|
|
82943
83796
|
});
|
|
82944
83797
|
}
|
|
82945
83798
|
// OPERATIONAL FLAGS (RFC 0013) — the surface the admin portal's FlagsPanel used to own.
|