@wix/himalaya-cli 0.855.0 → 0.857.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
@@ -832,19 +832,120 @@ var init_source_deny = __esm({
832
832
  });
833
833
 
834
834
  // src/mobile-ux-contract.ts
835
- function mobileUxMarkerFindings(mobile) {
835
+ function mobileUxDocumentBody(mobile) {
836
+ const comments = /<!--[\s\S]*?(?:-->|$)/g;
837
+ let out = "";
838
+ let last = 0;
839
+ let blankBefore = true;
840
+ for (const match of mobile.matchAll(comments)) {
841
+ const offset = match.index ?? 0;
842
+ const between = mobile.slice(last, offset);
843
+ const newline = between.lastIndexOf("\n");
844
+ if (newline >= 0) blankBefore = between.slice(newline + 1).trim() === "";
845
+ else blankBefore = blankBefore && between.trim() === "";
846
+ out += between;
847
+ const ownsTheLine = offset === 0 || mobile[offset - 1] === "\n";
848
+ const precedesHeading = blankBefore && mobile.startsWith("#", offset + match[0].length);
849
+ out += ownsTheLine || precedesHeading ? "\n" : " ";
850
+ last = offset + match[0].length;
851
+ }
852
+ return out + mobile.slice(last);
853
+ }
854
+ function mobileUxSection(text2, heading) {
855
+ const match = new RegExp(`(?:^|\\n)##[ \\t]+${heading}\\b[^\\n]*([\\s\\S]*?)(?=\\n##[ \\t]|$)`, "i").exec(text2);
856
+ return match ? match[1] : null;
857
+ }
858
+ function sectionTables(markdown) {
859
+ const lines = markdown.split("\n").map((line) => line.trim());
860
+ const tables = [];
861
+ for (let i = 1; i < lines.length; i++) {
862
+ if (!DELIMITER_ROW.test(lines[i])) continue;
863
+ const header = lines[i - 1];
864
+ if (!header.includes("|")) continue;
865
+ const table = { header: splitCells(header), data: [] };
866
+ let j = i + 1;
867
+ for (; j < lines.length; j++) {
868
+ const line = lines[j];
869
+ if (!line.includes("|")) break;
870
+ if (DELIMITER_ROW.test(line)) continue;
871
+ const cells = splitCells(line);
872
+ if (cells.every((cell) => cell === "")) continue;
873
+ table.data.push(cells);
874
+ }
875
+ tables.push(table);
876
+ i = j;
877
+ }
878
+ return tables;
879
+ }
880
+ function namesOneCta(cell) {
881
+ if (!cell) return false;
882
+ return !/[,;]|\s\/\s/.test(cell);
883
+ }
884
+ function declaredStates(text2) {
885
+ const contract = section(text2, "Screen contract");
886
+ if (!contract) return [];
887
+ const states = [];
888
+ for (const bullet of contract.matchAll(/^[ \t]*[-*][ \t]*[^:\n]*\bstates?[ \t]*:[ \t]*(\S.*)$/gim)) {
889
+ const items = bullet[1].split(/[,;/]/).map((item) => item.trim().replace(/[.:]+$/, "")).filter(Boolean);
890
+ if (!items.length || items.some((item) => !/^[\w][\w.\-+]*$/.test(item))) continue;
891
+ states.push(...items);
892
+ }
893
+ return [...new Set(states.map((state) => state.toLowerCase()))];
894
+ }
895
+ function primaryCtaStructureGap(text2) {
896
+ const acceptance = section(text2, "Implementation acceptance");
897
+ if (acceptance === null) return "There is no `## Implementation acceptance` section to read it from.";
898
+ const tables = sectionTables(acceptance).filter((table) => table.data.length > 0);
899
+ if (!tables.length) return "Its acceptance table has no rows.";
900
+ const ctaTables = tables.map((table) => ({ table, cta: table.header.findIndex((cell) => /primary\s*(?:cta|call[- ]?to[- ]?action|action)/i.test(cell)) })).filter((entry) => entry.cta >= 0);
901
+ if (!ctaTables.length) return "Its acceptance table has no `Primary CTA` column.";
902
+ const unanswered = ctaTables.flatMap(({ table, cta }) => table.data.filter((row) => !namesOneCta(row[cta] ?? "")));
903
+ if (unanswered.length) {
904
+ return `${unanswered.length} acceptance row(s) do not name exactly one primary CTA (first: ${JSON.stringify(unanswered[0][0] ?? "")}).`;
905
+ }
906
+ const data = ctaTables.flatMap(({ table }) => table.data);
907
+ const flat = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
908
+ const rowText = data.map((row) => flat(row.join(" ")));
909
+ const missing = declaredStates(text2).filter((state) => !rowText.some((row) => row.includes(flat(state))));
910
+ if (missing.length) return `The acceptance table has no row for the declared state(s): ${missing.join(", ")}.`;
911
+ return null;
912
+ }
913
+ function hasUnterminatedComment(mobile) {
914
+ let i = 0;
915
+ for (; ; ) {
916
+ const open = mobile.indexOf("<!--", i);
917
+ if (open < 0) return false;
918
+ const close = mobile.indexOf("-->", open + 4);
919
+ if (close < 0) return true;
920
+ i = close + 3;
921
+ }
922
+ }
923
+ function mobileUxMarkerFindings(mobile, renderedWidths = []) {
924
+ const text2 = mobileUxDocumentBody(mobile);
836
925
  const findings = [];
837
- if (!/^## Implementation acceptance\b/im.test(mobile)) {
838
- findings.push({ code: "missing-acceptance-matrix", message: "MOBILE-UX.md must contain the exact heading `## Implementation acceptance`." });
926
+ if (!/^## Implementation acceptance\b/im.test(text2)) {
927
+ findings.push({ code: "missing-acceptance-matrix", message: "MOBILE-UX.md must contain the heading `## Implementation acceptance` at the start of a line. Case is not checked, but use this spelling: other tooling addresses the document by it." });
839
928
  }
840
- if (!/^## Evidence\b/im.test(mobile)) {
841
- findings.push({ code: "missing-evidence-section", message: "MOBILE-UX.md must contain the exact heading `## Evidence`." });
929
+ if (!/^## Evidence\b/im.test(text2)) {
930
+ findings.push({ code: "missing-evidence-section", message: "MOBILE-UX.md must contain the heading `## Evidence` at the start of a line. Case is not checked, but use this spelling: other tooling addresses the document by it." });
842
931
  }
843
- if (!/one primary CTA per state/i.test(mobile)) {
844
- findings.push({ code: "missing-primary-cta-rule", message: "MOBILE-UX.md must include the exact phrase `one primary CTA per state`." });
932
+ if (!/one primary CTA per state/i.test(text2)) {
933
+ const gap = primaryCtaStructureGap(text2);
934
+ if (gap) {
935
+ findings.push({
936
+ code: "missing-primary-cta-rule",
937
+ message: "MOBILE-UX.md must establish one primary CTA per state: give `## Implementation acceptance` a `Primary CTA` column with a row per declared state and exactly one CTA in each cell, or keep the phrase `one primary CTA per state`. " + gap
938
+ });
939
+ }
845
940
  }
846
- if (!hasWidthEvidence(mobile)) {
847
- findings.push({ code: "missing-width-evidence", message: widthEvidenceMessage(mobile) });
941
+ if (!hasWidthEvidence(text2, renderedWidths)) {
942
+ findings.push({ code: "missing-width-evidence", message: widthEvidenceMessage(text2, renderedWidths) });
943
+ }
944
+ if (findings.length && hasUnterminatedComment(mobile)) {
945
+ return [{
946
+ code: "unterminated-html-comment",
947
+ message: "MOBILE-UX.md has an HTML comment opened with `<!--` and never closed with `-->`, so everything after it is commented out \u2014 in this check and in any renderer. Close the comment and re-run: the headings and phrases below it are not being read."
948
+ }];
848
949
  }
849
950
  return findings;
850
951
  }
@@ -886,13 +987,33 @@ function closestWidthPair(mentions) {
886
987
  }
887
988
  return best;
888
989
  }
889
- function hasWidthEvidence(mobile) {
990
+ function recordedWidthSpan(widths) {
991
+ const compact = widths.find((width) => inRange(width) && width <= COMPACT_MAX);
992
+ const wider = widths.find((width) => inRange(width) && width >= WIDER_MIN);
993
+ return compact !== void 0 && wider !== void 0 ? { compact, wider } : null;
994
+ }
995
+ function hasWidthEvidence(mobile, renderedWidths) {
996
+ if (recordedWidthSpan(renderedWidths)) return true;
890
997
  if (WIDTH_LITERAL.test(mobile)) return true;
891
998
  const pair = closestWidthPair(widthMentions(mobile));
892
999
  return pair !== null && pair.gap <= WIDTH_WINDOW;
893
1000
  }
894
- function widthEvidenceMessage(mobile) {
895
- const base = `MOBILE-UX.md has no width evidence. Record the widths you actually rendered \u2014 one at most ${COMPACT_MAX}px (compact) and one at least ${WIDER_MIN}px (medium or expanded), within ~${WIDTH_WINDOW} characters of each other so they read as one statement. A number counts only where it is spelled as a width on itself: a unit (\`320px\`, \`320 pt\`, \`320 dp\`, \`320 pixels\`, \`320 wide\`) or a screenshot filename (\`home-320.png\`). For example: \`Rendered 320px and 834px (home-320.png, home-834.png)\`. The scaffold's \`compact (320/360) \u2026 expanded\` sentence is still accepted as-is.`;
1001
+ function recordedRenderNote(renderedWidths) {
1002
+ if (renderedWidths.length === 0) return "";
1003
+ const list = (values) => {
1004
+ const unique = [...new Set(values)].sort((a, b) => a - b);
1005
+ const shown2 = unique.slice(0, 6).map((width) => `${width}px`).join(", ");
1006
+ return unique.length > 6 ? `${shown2}, \u2026` : shown2;
1007
+ };
1008
+ const plausible = renderedWidths.filter(inRange);
1009
+ const held = `The render record in \`${UX_EVIDENCE_PATH}\` holds`;
1010
+ if (plausible.length === 0) {
1011
+ return ` ${held} ${list(renderedWidths)}, and none of those is a plausible viewport (${PLAUSIBLE_WIDTH.min}-${PLAUSIBLE_WIDTH.max}px), so the record proves nothing about a size class.`;
1012
+ }
1013
+ return ` ${held} ${list(plausible)} \u2014 ` + (plausible.some((width) => width <= COMPACT_MAX) ? `compact only. Render the same screen again at ${WIDER_MIN}px or wider \u2014 \`himi render <screenId> --device ipad\`.` : `wider than compact only. Render the same screen again at ${COMPACT_MAX}px or less \u2014 \`himi render <screenId> --device iphone\`.`);
1014
+ }
1015
+ function widthEvidenceMessage(mobile, renderedWidths) {
1016
+ const base = `MOBILE-UX.md has no width evidence. The shortest way to supply it is to RENDER: \`himi render <screenId> --content <dir> --device iphone\` and again \`--device ipad\` (or any two \`--width\`s spanning the bands below) write the widths they rendered into \`${UX_EVIDENCE_PATH}\`, and a recorded compact render plus a wider one satisfy this marker with no prose at all. Otherwise write the widths you actually rendered into this document \u2014 one at most ${COMPACT_MAX}px (compact) and one at least ${WIDER_MIN}px (medium or expanded), within ~${WIDTH_WINDOW} characters of each other so they read as one statement. A number counts only where it is spelled as a width on itself: a unit (\`320px\`, \`320 pt\`, \`320 dp\`, \`320 pixels\`, \`320 wide\`) or a screenshot filename (\`home-320.png\`). For example: \`Rendered 320px and 834px (home-320.png, home-834.png)\`. The scaffold's \`compact (320/360) \u2026 expanded\` sentence is still accepted as-is.` + recordedRenderNote(renderedWidths);
896
1017
  if (/compact \(320\/360\)/i.test(mobile)) {
897
1018
  return base + " `compact (320/360)` is present, but no `expanded` follows it within ~400 characters \u2014 keep them in the same sentence.";
898
1019
  }
@@ -911,9 +1032,10 @@ function widthEvidenceMessage(mobile) {
911
1032
  }
912
1033
  return base;
913
1034
  }
914
- var SPEC_TEMPLATE, MOBILE_UX_TEMPLATE, WIDTH_LITERAL, COMPACT_MAX, WIDER_MIN, PLAUSIBLE_WIDTH, YEAR, inRange, looksLikeYear, UNIT_ATTACHED, UNIT_WORD_ATTACHED, WIDTH_WINDOW, EDGE_WINDOW, FILENAME_AFTER, ANTI_CUE_BEFORE, PAIR_HEIGHT_BEFORE, ANTI_CUE_AFTER, isCompact, isWider;
1035
+ var UX_EVIDENCE_PATH, SPEC_TEMPLATE, MOBILE_UX_MARKER_NOTE, MOBILE_UX_TEMPLATE, section, DELIMITER_ROW, splitCells, WIDTH_LITERAL, COMPACT_MAX, WIDER_MIN, PLAUSIBLE_WIDTH, YEAR, inRange, looksLikeYear, UNIT_ATTACHED, UNIT_WORD_ATTACHED, WIDTH_WINDOW, EDGE_WINDOW, FILENAME_AFTER, ANTI_CUE_BEFORE, PAIR_HEIGHT_BEFORE, ANTI_CUE_AFTER, isCompact, isWider;
915
1036
  var init_mobile_ux_contract = __esm({
916
1037
  "src/mobile-ux-contract.ts"() {
1038
+ UX_EVIDENCE_PATH = ".himi/ux-audit.json";
917
1039
  SPEC_TEMPLATE = `# App specification
918
1040
 
919
1041
  ## User and outcome
@@ -938,8 +1060,37 @@ var init_mobile_ux_contract = __esm({
938
1060
  - Required: iOS, Android
939
1061
  - Optional: web, desktop, watch, widgets
940
1062
  `;
1063
+ MOBILE_UX_MARKER_NOTE = `<!-- LOAD-BEARING \u2014 read this before you rewrite the file.
1064
+
1065
+ \`himi validate\` warns, and \`himi validate --strict\` / \`himi ux audit --strict\` REFUSE, a
1066
+ MOBILE-UX.md that satisfies none of the forms below. Everything else here is yours to
1067
+ replace freely. This comment is not itself an answer: HTML comments are stripped before the
1068
+ check runs, so keeping it satisfies nothing.
1069
+
1070
+ 1. The headings \`## Implementation acceptance\` and \`## Evidence\`, each at the start of
1071
+ a line. Case is not checked; use these spellings anyway, because other tooling
1072
+ addresses this document by them.
1073
+ 2. One primary CTA per state \u2014 EITHER the phrase \`one primary CTA per state\`, OR a
1074
+ \`Primary CTA\` column in the acceptance table below, with a row for every state you
1075
+ declare under \`## Screen contract\` and exactly one CTA named in each row's cell.
1076
+ 3. Width evidence \u2014 EITHER a RECORDED RENDER, which is the shortest way and needs no prose
1077
+ at all: \`himi render <screenId> --device iphone\` and again \`--device ipad\` each append
1078
+ the width they rendered to \`.himi/ux-audit.json\`, and one below 600px plus one at 600px
1079
+ or wider satisfies this on their own. OR the scaffold's own sentence,
1080
+ \`compact (320/360)\` followed by
1081
+ \`expanded\`, OR two widths you actually rendered, EACH SPELLED AS A WIDTH ON ITSELF:
1082
+ one below 600px and one at 600px or wider, within about 400 characters of each other.
1083
+ A number is spelled as a width when it carries a unit (\`320px\`, \`834 pt\`,
1084
+ \`320 pixels\`, \`320 wide\`) or sits in a screenshot filename (\`home-320.png\`,
1085
+ \`home-390x844.png\`). Nothing is read from the words around the number, so a BARE
1086
+ figure never counts however you label it \u2014 \`Widths: 320, 390, 834\` and
1087
+ \`compact (320) and expanded (834)\` both name no width. One unit per number fixes it.
1088
+
1089
+ Full contract: \`himi docs mobile-ux\` -> "Strict audit contract". -->`;
941
1090
  MOBILE_UX_TEMPLATE = `# Mobile UX
942
1091
 
1092
+ ${MOBILE_UX_MARKER_NOTE}
1093
+
943
1094
  ## Flows
944
1095
  ### <Task>
945
1096
  1. Entry:
@@ -982,6 +1133,9 @@ in preview fixtures or labelled, capability-limited fallback states\u2014not ord
982
1133
  > Keep this document safe to publish with a release: do not include credentials, personal data,
983
1134
  > internal URLs, private research, or unreviewed notes. Read \`himi docs mobile-ux\` before filling it in.
984
1135
  `;
1136
+ section = mobileUxSection;
1137
+ DELIMITER_ROW = /^\|?\s*:?-+:?\s*(?:\|\s*:?-+:?\s*)+\|?$/;
1138
+ splitCells = (line) => line.replace(/^\|/, "").replace(/\|\s*$/, "").split("|").map((cell) => cell.trim());
985
1139
  WIDTH_LITERAL = /compact \(320\/360\)[\s\S]{0,400}?expanded/i;
986
1140
  COMPACT_MAX = 599;
987
1141
  WIDER_MIN = 600;
@@ -2083,7 +2237,7 @@ async function mintToken(args, label2, cacheKey, spawnImpl) {
2083
2237
  tokenCache.set(cacheKey, fromEnv);
2084
2238
  return fromEnv;
2085
2239
  }
2086
- const token = await new Promise((resolve41, reject) => {
2240
+ const token = await new Promise((resolve42, reject) => {
2087
2241
  let child;
2088
2242
  try {
2089
2243
  child = spawnImpl("wix", [...args]);
@@ -2112,12 +2266,12 @@ async function mintToken(args, label2, cacheKey, spawnImpl) {
2112
2266
  child.stdout?.on("data", (d) => {
2113
2267
  out += String(d);
2114
2268
  const m = out.match(TOKEN_RE);
2115
- if (m) done(() => resolve41(m[0]));
2269
+ if (m) done(() => resolve42(m[0]));
2116
2270
  });
2117
2271
  child.stderr?.on("data", (d) => {
2118
2272
  out += String(d);
2119
2273
  const m = out.match(TOKEN_RE);
2120
- if (m) done(() => resolve41(m[0]));
2274
+ if (m) done(() => resolve42(m[0]));
2121
2275
  });
2122
2276
  child.on("error", (e) => done(() => reject(new Error(mintHelp(label2, e.message)))));
2123
2277
  child.on("exit", (code) => done(() => reject(new Error(
@@ -2298,8 +2452,8 @@ function committedProvisioningPath(name) {
2298
2452
  function committedShellFor(path) {
2299
2453
  const normalized = path.replace(/\\/g, "/");
2300
2454
  return SHELL_NAMES2.find((name) => {
2301
- const relative7 = committedProvisioningPath(name);
2302
- return normalized === relative7 || normalized.endsWith(`/${relative7}`);
2455
+ const relative8 = committedProvisioningPath(name);
2456
+ return normalized === relative8 || normalized.endsWith(`/${relative8}`);
2303
2457
  });
2304
2458
  }
2305
2459
  function isBrandable(repoRoot2, name) {
@@ -4729,7 +4883,6 @@ function resolveTranslation(value, catalog, sourceCatalog) {
4729
4883
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
4730
4884
  var init_bindings = __esm({
4731
4885
  "../../core/ts/src/bindings.ts"() {
4732
- "use strict";
4733
4886
  BINDING_SIGILS = ["service", "flag", "item", "t"];
4734
4887
  BINDING_SIGIL_RE = new RegExp(
4735
4888
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -6606,7 +6759,7 @@ function validateWorkerAction(action, context) {
6606
6759
  }
6607
6760
  }
6608
6761
  if (context.actionCatalogWithheld) {
6609
- return actionIssue(action, "warning", `worker actionId "${action.actionId}" was not cross-checked: this surface has precomputed service state but no precomputed action catalog`);
6762
+ return actionIssue(action, "warning", `worker actionId "${action.actionId}" was not cross-checked: this surface has precomputed service state but no precomputed action catalog. This is a property of the surface doing the checking, not of your app \u2014 there is nothing to fix in the content package; re-run the check where the bundle can be inspected (\`himi validate\` / \`himi test\` locally) to have it cross-checked`);
6610
6763
  }
6611
6764
  const reason = context.bundleInspectionError ? `the bound bundle could not be inspected: ${context.bundleInspectionError}` : "no inspectable logic bundle is bound";
6612
6765
  return actionIssue(action, "error", `worker actionId "${action.actionId}" cannot resolve because ${reason}`);
@@ -9003,8 +9156,8 @@ Lifetime used`) : new QuickJSUseAfterFree("Lifetime not alive");
9003
9156
  this.dispose = () => {
9004
9157
  this.handle.alive && this.handle.dispose(), this.disposeResolvers();
9005
9158
  };
9006
- this.context = args.context, this.owner = args.context.runtime, this.handle = args.promiseHandle, this.settled = new Promise((resolve41) => {
9007
- this.onSettled = resolve41;
9159
+ this.context = args.context, this.owner = args.context.runtime, this.handle = args.promiseHandle, this.settled = new Promise((resolve42) => {
9160
+ this.onSettled = resolve42;
9008
9161
  }), this.resolveHandle = args.resolveHandle, this.rejectHandle = args.rejectHandle;
9009
9162
  }
9010
9163
  get alive() {
@@ -9368,12 +9521,12 @@ Lifetime used`) : new QuickJSUseAfterFree("Lifetime not alive");
9368
9521
  let vmPromise = scope.manage(this.getProp(this.global, "Promise")), vmPromiseResolve = scope.manage(this.getProp(vmPromise, "resolve"));
9369
9522
  return this.callFunction(vmPromiseResolve, vmPromise, promiseLikeHandle);
9370
9523
  });
9371
- return vmResolveResult.error ? Promise.resolve(vmResolveResult) : new Promise((resolve41) => {
9524
+ return vmResolveResult.error ? Promise.resolve(vmResolveResult) : new Promise((resolve42) => {
9372
9525
  Scope.withScope((scope) => {
9373
9526
  let resolveHandle = scope.manage(this.newFunction("resolve", (value) => {
9374
- resolve41(this.success(value && value.dup()));
9527
+ resolve42(this.success(value && value.dup()));
9375
9528
  })), rejectHandle = scope.manage(this.newFunction("reject", (error) => {
9376
- resolve41(this.fail(error && error.dup()));
9529
+ resolve42(this.fail(error && error.dup()));
9377
9530
  })), promiseHandle = scope.manage(vmResolveResult.value), promiseThenHandle = scope.manage(this.getProp(promiseHandle, "then"));
9378
9531
  this.callFunction(promiseThenHandle, promiseHandle, resolveHandle, rejectHandle).unwrap().dispose();
9379
9532
  });
@@ -10219,13 +10372,13 @@ async function QuickJSRaw(moduleArg = {}) {
10219
10372
  }
10220
10373
  readAsync = async (url) => {
10221
10374
  if (isFileURI(url)) {
10222
- return new Promise((resolve41, reject) => {
10375
+ return new Promise((resolve42, reject) => {
10223
10376
  var xhr = new XMLHttpRequest();
10224
10377
  xhr.open("GET", url, true);
10225
10378
  xhr.responseType = "arraybuffer";
10226
10379
  xhr.onload = () => {
10227
10380
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
10228
- resolve41(xhr.response);
10381
+ resolve42(xhr.response);
10229
10382
  return;
10230
10383
  }
10231
10384
  reject(xhr.status);
@@ -10513,10 +10666,10 @@ async function QuickJSRaw(moduleArg = {}) {
10513
10666
  }
10514
10667
  var info = getWasmImports();
10515
10668
  if (Module["instantiateWasm"]) {
10516
- return new Promise((resolve41, reject) => {
10669
+ return new Promise((resolve42, reject) => {
10517
10670
  try {
10518
10671
  Module["instantiateWasm"](info, (inst, mod) => {
10519
- resolve41(receiveInstance(inst, mod));
10672
+ resolve42(receiveInstance(inst, mod));
10520
10673
  });
10521
10674
  } catch (e) {
10522
10675
  err(`Module.instantiateWasm callback failed with error: ${e}`);
@@ -10689,15 +10842,15 @@ async function QuickJSRaw(moduleArg = {}) {
10689
10842
  return parts;
10690
10843
  },
10691
10844
  normalize: (path) => {
10692
- var isAbsolute4 = PATH.isAbs(path), trailingSlash = path.slice(-1) === "/";
10693
- path = PATH.normalizeArray(path.split("/").filter((p) => !!p), !isAbsolute4).join("/");
10694
- if (!path && !isAbsolute4) {
10845
+ var isAbsolute5 = PATH.isAbs(path), trailingSlash = path.slice(-1) === "/";
10846
+ path = PATH.normalizeArray(path.split("/").filter((p) => !!p), !isAbsolute5).join("/");
10847
+ if (!path && !isAbsolute5) {
10695
10848
  path = ".";
10696
10849
  }
10697
10850
  if (path && trailingSlash) {
10698
10851
  path += "/";
10699
10852
  }
10700
- return (isAbsolute4 ? "/" : "") + path;
10853
+ return (isAbsolute5 ? "/" : "") + path;
10701
10854
  },
10702
10855
  dirname: (path) => {
10703
10856
  var result = PATH.splitPath(path), root = result[0], dir = result[1];
@@ -12163,12 +12316,12 @@ async function QuickJSRaw(moduleArg = {}) {
12163
12316
  throw new FS.ErrnoError(75);
12164
12317
  }
12165
12318
  var old_node = FS.lookupNode(old_dir, old_name);
12166
- var relative7 = PATH_FS.relative(old_path, new_dirname);
12167
- if (relative7.charAt(0) !== ".") {
12319
+ var relative8 = PATH_FS.relative(old_path, new_dirname);
12320
+ if (relative8.charAt(0) !== ".") {
12168
12321
  throw new FS.ErrnoError(28);
12169
12322
  }
12170
- relative7 = PATH_FS.relative(new_path, old_dirname);
12171
- if (relative7.charAt(0) !== ".") {
12323
+ relative8 = PATH_FS.relative(new_path, old_dirname);
12324
+ if (relative8.charAt(0) !== ".") {
12172
12325
  throw new FS.ErrnoError(55);
12173
12326
  }
12174
12327
  var new_node;
@@ -14577,8 +14730,8 @@ async function QuickJSRaw(moduleArg = {}) {
14577
14730
  if (runtimeInitialized) {
14578
14731
  moduleRtn = Module;
14579
14732
  } else {
14580
- moduleRtn = new Promise((resolve41, reject) => {
14581
- readyPromiseResolve = resolve41;
14733
+ moduleRtn = new Promise((resolve42, reject) => {
14734
+ readyPromiseResolve = resolve42;
14582
14735
  readyPromiseReject = reject;
14583
14736
  });
14584
14737
  }
@@ -15442,13 +15595,13 @@ async function QuickJSRaw3(moduleArg = {}) {
15442
15595
  }
15443
15596
  readAsync = async (url) => {
15444
15597
  if (isFileURI(url)) {
15445
- return new Promise((resolve41, reject) => {
15598
+ return new Promise((resolve42, reject) => {
15446
15599
  var xhr = new XMLHttpRequest();
15447
15600
  xhr.open("GET", url, true);
15448
15601
  xhr.responseType = "arraybuffer";
15449
15602
  xhr.onload = () => {
15450
15603
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
15451
- resolve41(xhr.response);
15604
+ resolve42(xhr.response);
15452
15605
  return;
15453
15606
  }
15454
15607
  reject(xhr.status);
@@ -15715,10 +15868,10 @@ async function QuickJSRaw3(moduleArg = {}) {
15715
15868
  }
15716
15869
  var info = getWasmImports();
15717
15870
  if (Module["instantiateWasm"]) {
15718
- return new Promise((resolve41, reject) => {
15871
+ return new Promise((resolve42, reject) => {
15719
15872
  try {
15720
15873
  Module["instantiateWasm"](info, (inst, mod) => {
15721
- resolve41(receiveInstance(inst, mod));
15874
+ resolve42(receiveInstance(inst, mod));
15722
15875
  });
15723
15876
  } catch (e) {
15724
15877
  err(`Module.instantiateWasm callback failed with error: ${e}`);
@@ -16146,8 +16299,8 @@ async function QuickJSRaw3(moduleArg = {}) {
16146
16299
  }, whenDone() {
16147
16300
  assert(Asyncify.currData, "Tried to wait for an async operation when none is in progress.");
16148
16301
  assert(!Asyncify.asyncPromiseHandlers, "Cannot have multiple async operations in flight at once");
16149
- return new Promise((resolve41, reject) => {
16150
- Asyncify.asyncPromiseHandlers = { resolve: resolve41, reject };
16302
+ return new Promise((resolve42, reject) => {
16303
+ Asyncify.asyncPromiseHandlers = { resolve: resolve42, reject };
16151
16304
  });
16152
16305
  }, allocateData() {
16153
16306
  var ptr = _malloc(12 + Asyncify.StackSize);
@@ -16775,8 +16928,8 @@ async function QuickJSRaw3(moduleArg = {}) {
16775
16928
  if (runtimeInitialized) {
16776
16929
  moduleRtn = Module;
16777
16930
  } else {
16778
- moduleRtn = new Promise((resolve41, reject) => {
16779
- readyPromiseResolve = resolve41;
16931
+ moduleRtn = new Promise((resolve42, reject) => {
16932
+ readyPromiseResolve = resolve42;
16780
16933
  readyPromiseReject = reject;
16781
16934
  });
16782
16935
  }
@@ -20469,6 +20622,101 @@ var init_wix_gateway_rules = __esm({
20469
20622
  }
20470
20623
  });
20471
20624
 
20625
+ // src/ux-evidence.ts
20626
+ import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "node:fs";
20627
+ import { dirname as dirname15, isAbsolute as isAbsolute2, join as join25, relative as relative3, resolve as resolve15 } from "node:path";
20628
+ function readText(path) {
20629
+ try {
20630
+ return existsSync21(path) ? readFileSync23(path, "utf8") : null;
20631
+ } catch {
20632
+ return null;
20633
+ }
20634
+ }
20635
+ function parseEvidence(path) {
20636
+ const raw = readText(path);
20637
+ if (raw === null) return { evidence: null };
20638
+ try {
20639
+ const value = JSON.parse(raw);
20640
+ if (value.version !== 1 || !value.platforms || typeof value.platforms !== "object") {
20641
+ return { evidence: null, error: "must be an object with version: 1 and a platforms object" };
20642
+ }
20643
+ return { evidence: value };
20644
+ } catch (error) {
20645
+ return { evidence: null, error: `is not valid JSON: ${error.message}` };
20646
+ }
20647
+ }
20648
+ function uxEvidencePath(content) {
20649
+ return join25(resolve15(content), UX_EVIDENCE_PATH);
20650
+ }
20651
+ function isRenderEntry(value) {
20652
+ if (!value || typeof value !== "object") return false;
20653
+ const entry = value;
20654
+ return typeof entry.screenId === "string" && entry.screenId.length > 0 && typeof entry.width === "number" && Number.isFinite(entry.width) && entry.width > 0 && typeof entry.recordedAt === "string" && entry.recordedAt.length > 0;
20655
+ }
20656
+ function renderEvidenceEntries(evidence) {
20657
+ const raw = evidence?.renders;
20658
+ return Array.isArray(raw) ? raw.filter(isRenderEntry) : [];
20659
+ }
20660
+ function recordedRenderWidths(content) {
20661
+ const { evidence } = parseEvidence(uxEvidencePath(content));
20662
+ return renderEvidenceEntries(evidence).map((entry) => entry.width);
20663
+ }
20664
+ function writeEvidence(path, evidence) {
20665
+ mkdirSync10(dirname15(path), { recursive: true });
20666
+ writeFileSync9(path, JSON.stringify(evidence, null, 2) + "\n");
20667
+ }
20668
+ function recordedFilePath(dir, file) {
20669
+ const rel = relative3(dir, resolve15(file));
20670
+ return rel && !rel.startsWith("..") && !isAbsolute2(rel) ? rel : file;
20671
+ }
20672
+ function recordRenderWidth(content, entry) {
20673
+ try {
20674
+ if (!entry.screenId || !Number.isFinite(entry.width) || entry.width <= 0) return null;
20675
+ const dir = resolve15(content);
20676
+ const path = join25(dir, UX_EVIDENCE_PATH);
20677
+ const parsed = parseEvidence(path);
20678
+ if (parsed.error) return null;
20679
+ const evidence = parsed.evidence ?? { version: 1, platforms: {} };
20680
+ const width = entry.width;
20681
+ const written = {
20682
+ screenId: entry.screenId,
20683
+ width,
20684
+ ...entry.device ? { device: entry.device } : {},
20685
+ ...entry.file ? { file: recordedFilePath(dir, entry.file) } : {},
20686
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString()
20687
+ };
20688
+ const renders = renderEvidenceEntries(evidence);
20689
+ const at = renders.findIndex((existing) => existing.screenId === written.screenId && existing.width === width);
20690
+ if (at >= 0) renders[at] = written;
20691
+ else renders.push(written);
20692
+ evidence.renders = renders;
20693
+ writeEvidence(path, evidence);
20694
+ return written;
20695
+ } catch {
20696
+ return null;
20697
+ }
20698
+ }
20699
+ function recordNativeAudit(content, platform, status, checks, reason) {
20700
+ const normalizedChecks = status === "unavailable" ? [] : [...new Set(checks.map((check) => check.trim()).filter(Boolean))];
20701
+ if (status !== "unavailable" && !normalizedChecks.length) {
20702
+ throw new Error("--checks needs at least one comma-separated check (for example: back,keyboard,voiceover)");
20703
+ }
20704
+ if (status === "unavailable" && !reason?.trim()) throw new Error("--reason is required when --status unavailable");
20705
+ const dir = resolve15(content);
20706
+ const path = join25(dir, UX_EVIDENCE_PATH);
20707
+ const parsed = parseEvidence(path);
20708
+ if (parsed.error) throw new Error(`${UX_EVIDENCE_PATH} ${parsed.error}`);
20709
+ const evidence = parsed.evidence ?? { version: 1, platforms: {} };
20710
+ evidence.platforms[platform] = { status, checks: normalizedChecks, ...reason?.trim() ? { reason: reason.trim() } : {}, recordedAt: (/* @__PURE__ */ new Date()).toISOString() };
20711
+ writeEvidence(path, evidence);
20712
+ return evidence;
20713
+ }
20714
+ var init_ux_evidence = __esm({
20715
+ "src/ux-evidence.ts"() {
20716
+ init_mobile_ux_contract();
20717
+ }
20718
+ });
20719
+
20472
20720
  // ../../core/sdk/capability-catalog/src/types.ts
20473
20721
  var init_types = __esm({
20474
20722
  "../../core/sdk/capability-catalog/src/types.ts"() {
@@ -21195,9 +21443,9 @@ var init_src2 = __esm({
21195
21443
  });
21196
21444
 
21197
21445
  // ../app-icons/plan.ts
21198
- import { existsSync as existsSync23, readFileSync as readFileSync25, readdirSync as readdirSync12 } from "node:fs";
21446
+ import { existsSync as existsSync24, readFileSync as readFileSync26, readdirSync as readdirSync12 } from "node:fs";
21199
21447
  import { createHash as createHash7 } from "node:crypto";
21200
- import { dirname as dirname16, join as join27, resolve as resolve17 } from "node:path";
21448
+ import { dirname as dirname17, join as join28, resolve as resolve18 } from "node:path";
21201
21449
  function stripComments(src) {
21202
21450
  let out = "";
21203
21451
  let i = 0;
@@ -21326,15 +21574,15 @@ function rotateHue(hex2, deg) {
21326
21574
  }
21327
21575
  function resolveTokensPath(app, configTs) {
21328
21576
  const literal = /^ {2}designTokensPath\s*:\s*["']([^"']+)["']/m.exec(stripComments(configTs))?.[1];
21329
- if (literal) return { path: resolve17(appDir(app), literal), source: "declared" };
21330
- return { path: join27(appDir(app), "tokens.json"), source: "assumed" };
21577
+ if (literal) return { path: resolve18(appDir(app), literal), source: "declared" };
21578
+ return { path: join28(appDir(app), "tokens.json"), source: "assumed" };
21331
21579
  }
21332
21580
  function resolveToken(app, token, tokensPathOverride) {
21333
- const tokensPath = tokensPathOverride ?? join27(appDir(app), "tokens.json");
21334
- if (!existsSync23(tokensPath)) {
21581
+ const tokensPath = tokensPathOverride ?? join28(appDir(app), "tokens.json");
21582
+ if (!existsSync24(tokensPath)) {
21335
21583
  throw new Error(`app-icons: ${app} has no tokens.json at ${tokensPath} (needed to resolve "${token}").`);
21336
21584
  }
21337
- const tokens = JSON.parse(readFileSync25(tokensPath, "utf8"));
21585
+ const tokens = JSON.parse(readFileSync26(tokensPath, "utf8"));
21338
21586
  const value = tokens.colors?.[token];
21339
21587
  if (value === void 0) {
21340
21588
  throw new Error(`app-icons: ${app} references icon colour token "${token}" but tokens.json has no such color.`);
@@ -21375,8 +21623,8 @@ function iconSpec(app, configTs, id, tokensPathOverride) {
21375
21623
  assertIconContract(app, cfg);
21376
21624
  const tokens = tokensPathOverride ?? resolveTokensPath(app, configTs).path;
21377
21625
  if (cfg.source) {
21378
- const abs = resolve17(appDir(app), cfg.source);
21379
- if (!existsSync23(abs)) {
21626
+ const abs = resolve18(appDir(app), cfg.source);
21627
+ if (!existsSync24(abs)) {
21380
21628
  throw new Error(`app-icons: ${app} declares icon.source "${cfg.source}" but ${abs} does not exist.`);
21381
21629
  }
21382
21630
  return {
@@ -21413,17 +21661,17 @@ function iconSpec(app, configTs, id, tokensPathOverride) {
21413
21661
  };
21414
21662
  }
21415
21663
  function iosAssetCatalog2(app) {
21416
- return assetCatalogIn(join27(appDir(app), "ios"));
21664
+ return assetCatalogIn(join28(appDir(app), "ios"));
21417
21665
  }
21418
21666
  function assetCatalogIn(platformDir) {
21419
- if (!existsSync23(platformDir)) return void 0;
21667
+ if (!existsSync24(platformDir)) return void 0;
21420
21668
  const children = readdirSync12(platformDir).filter((c) => !c.startsWith("build")).sort();
21421
- const catalogs = children.map((c) => join27(platformDir, c, "Assets.xcassets")).filter(existsSync23);
21669
+ const catalogs = children.map((c) => join28(platformDir, c, "Assets.xcassets")).filter(existsSync24);
21422
21670
  if (catalogs.length === 1) return catalogs[0];
21423
21671
  if (catalogs.length > 1) return void 0;
21424
21672
  const targets = [];
21425
21673
  for (const child of children) {
21426
- const dir = join27(platformDir, child);
21674
+ const dir = join28(platformDir, child);
21427
21675
  let entries;
21428
21676
  try {
21429
21677
  entries = readdirSync12(dir);
@@ -21431,29 +21679,29 @@ function assetCatalogIn(platformDir) {
21431
21679
  continue;
21432
21680
  }
21433
21681
  if (entries.includes("Info.plist") || entries.some((e) => e.endsWith(".swift"))) {
21434
- targets.push(join27(dir, "Assets.xcassets"));
21682
+ targets.push(join28(dir, "Assets.xcassets"));
21435
21683
  }
21436
21684
  }
21437
21685
  return targets.length === 1 ? targets[0] : void 0;
21438
21686
  }
21439
21687
  function androidResDir2(manifestPath) {
21440
- return join27(dirname16(manifestPath), "res");
21688
+ return join28(dirname17(manifestPath), "res");
21441
21689
  }
21442
21690
  function androidManifestPath(app) {
21443
- const androidDir = join27(appDir(app), "android");
21444
- if (!existsSync23(androidDir)) return void 0;
21445
- const preferred = join27(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
21446
- if (existsSync23(preferred)) return preferred;
21447
- const candidates = readdirSync12(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join27(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync23);
21691
+ const androidDir = join28(appDir(app), "android");
21692
+ if (!existsSync24(androidDir)) return void 0;
21693
+ const preferred = join28(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
21694
+ if (existsSync24(preferred)) return preferred;
21695
+ const candidates = readdirSync12(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join28(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync24);
21448
21696
  return candidates.length === 1 ? candidates[0] : void 0;
21449
21697
  }
21450
21698
  function androidOptedIn2(manifestPath) {
21451
- if (!manifestPath || !existsSync23(manifestPath)) return false;
21452
- return readFileSync25(manifestPath, "utf8").includes("@mipmap/ic_launcher");
21699
+ if (!manifestPath || !existsSync24(manifestPath)) return false;
21700
+ return readFileSync26(manifestPath, "utf8").includes("@mipmap/ic_launcher");
21453
21701
  }
21454
21702
  function templateIconSha256() {
21455
21703
  const cat = iosAssetCatalog2("_template");
21456
- return cat ? fileSha256(join27(cat, "AppIcon.appiconset", "icon_1024.png")) : null;
21704
+ return cat ? fileSha256(join28(cat, "AppIcon.appiconset", "icon_1024.png")) : null;
21457
21705
  }
21458
21706
  function placeholderHashes() {
21459
21707
  const current = templateIconSha256();
@@ -21461,7 +21709,7 @@ function placeholderHashes() {
21461
21709
  }
21462
21710
  function fileSha256(path) {
21463
21711
  try {
21464
- return createHash7("sha256").update(readFileSync25(path)).digest("hex");
21712
+ return createHash7("sha256").update(readFileSync26(path)).digest("hex");
21465
21713
  } catch {
21466
21714
  return null;
21467
21715
  }
@@ -21472,8 +21720,8 @@ function generationAllowed(app, configTs, opts = {}) {
21472
21720
  return { allowed: true, reason: "declares an icon block" };
21473
21721
  }
21474
21722
  const iosCat = iosAssetCatalog2(app);
21475
- const iosIcon = iosCat ? join27(iosCat, "AppIcon.appiconset", "icon_1024.png") : void 0;
21476
- if (!iosIcon || !existsSync23(iosIcon)) return { allowed: true, reason: "no icon yet" };
21723
+ const iosIcon = iosCat ? join28(iosCat, "AppIcon.appiconset", "icon_1024.png") : void 0;
21724
+ if (!iosIcon || !existsSync24(iosIcon)) return { allowed: true, reason: "no icon yet" };
21477
21725
  const hash = fileSha256(iosIcon);
21478
21726
  const placeholders = opts.placeholderHashes ?? placeholderHashes();
21479
21727
  if (app === "_template") return { allowed: true, reason: "the template itself" };
@@ -21497,8 +21745,8 @@ var init_plan2 = __esm({
21497
21745
  });
21498
21746
 
21499
21747
  // ../native-config/icon.ts
21500
- import { existsSync as existsSync24, readFileSync as readFileSync26 } from "node:fs";
21501
- import { join as join28 } from "node:path";
21748
+ import { existsSync as existsSync25, readFileSync as readFileSync27 } from "node:fs";
21749
+ import { join as join29 } from "node:path";
21502
21750
  function androidAdaptiveIconXml() {
21503
21751
  return [
21504
21752
  '<?xml version="1.0" encoding="utf-8"?>',
@@ -21525,8 +21773,8 @@ function androidIconArtifacts(app, configTs) {
21525
21773
  if (!androidOptedIn2(manifest)) return [];
21526
21774
  if (!generationAllowed(app, configTs).allowed) return [];
21527
21775
  const res = androidResDir2(manifest);
21528
- const foreground = join28(res, "mipmap-mdpi", "ic_launcher_foreground.png");
21529
- if (!existsSync24(foreground)) {
21776
+ const foreground = join29(res, "mipmap-mdpi", "ic_launcher_foreground.png");
21777
+ if (!existsSync25(foreground)) {
21530
21778
  throw new Error(
21531
21779
  `native-config icon: ${app} opts into @mipmap/ic_launcher but has no mipmap-*/ic_launcher_foreground.png. Run \`npm run app-icons -- ${app}\` first \u2014 the XML emitted here points at those rasters.`
21532
21780
  );
@@ -21535,11 +21783,11 @@ function androidIconArtifacts(app, configTs) {
21535
21783
  const spec = iconSpec(app, configTs, id, resolveTokensPath(app, configTs).path);
21536
21784
  return [
21537
21785
  ...ANDROID_ADAPTIVE_FILES.map((name) => ({
21538
- path: join28(res, "mipmap-anydpi-v26", name),
21786
+ path: join29(res, "mipmap-anydpi-v26", name),
21539
21787
  content: androidAdaptiveIconXml()
21540
21788
  })),
21541
21789
  {
21542
- path: join28(res, "values", ANDROID_ICON_BACKGROUND_FILE),
21790
+ path: join29(res, "values", ANDROID_ICON_BACKGROUND_FILE),
21543
21791
  content: androidIconBackgroundXml(spec.background)
21544
21792
  }
21545
21793
  ];
@@ -21773,8 +22021,8 @@ __export(gen_native_config_exports, {
21773
22021
  tvInfoPlan: () => tvInfoPlan,
21774
22022
  watchInfoPlan: () => watchInfoPlan
21775
22023
  });
21776
- import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync27, readdirSync as readdirSync13, rmSync as rmSync3, writeFileSync as writeFileSync11 } from "node:fs";
21777
- import { dirname as dirname17, join as join29 } from "node:path";
22024
+ import { existsSync as existsSync26, mkdirSync as mkdirSync12, readFileSync as readFileSync28, readdirSync as readdirSync13, rmSync as rmSync3, writeFileSync as writeFileSync11 } from "node:fs";
22025
+ import { dirname as dirname18, join as join30 } from "node:path";
21778
22026
  function parseStringArray(configTs, field) {
21779
22027
  const m = new RegExp(`${field}:\\s*\\[([^\\]]*)\\]`).exec(configTs);
21780
22028
  if (!m) return [];
@@ -21846,9 +22094,9 @@ function buildSet(capabilities, envelope = []) {
21846
22094
  return [...new Set([...capabilities, ...envelope].map(catalogKey))].sort();
21847
22095
  }
21848
22096
  function loadOverrides(app) {
21849
- const p = join29(appDir(app), "native-config.overrides.json");
21850
- if (!existsSync25(p)) return {};
21851
- return JSON.parse(readFileSync27(p, "utf8"));
22097
+ const p = join30(appDir(app), "native-config.overrides.json");
22098
+ if (!existsSync26(p)) return {};
22099
+ return JSON.parse(readFileSync28(p, "utf8"));
21852
22100
  }
21853
22101
  function needsQuote(s) {
21854
22102
  if (s === "") return true;
@@ -21903,19 +22151,19 @@ function endText(kind, label2, indent) {
21903
22151
  return kind === "yaml" ? `${indent}# <<< ${label2} <<<` : `${indent}<!-- <<< ${label2} <<< -->`;
21904
22152
  }
21905
22153
  function hostAppliesDeferredWidgetActions(app) {
21906
- const root = join29(appDir(app), "ios");
21907
- if (!existsSync25(root)) return false;
22154
+ const root = join30(appDir(app), "ios");
22155
+ if (!existsSync26(root)) return false;
21908
22156
  const stack = [root];
21909
22157
  while (stack.length) {
21910
22158
  const dir = stack.pop();
21911
22159
  for (const entry of readdirSync13(dir, { withFileTypes: true })) {
21912
22160
  if (entry.name.startsWith(".") || entry.name === "GeneratedLiveActivity") continue;
21913
- const path = join29(dir, entry.name);
22161
+ const path = join30(dir, entry.name);
21914
22162
  if (entry.isDirectory()) {
21915
22163
  if (entry.name.endsWith(".xcodeproj") || entry.name === "build") continue;
21916
22164
  stack.push(path);
21917
22165
  } else if (entry.name.endsWith(".swift")) {
21918
- if (readFileSync27(path, "utf8").includes("WidgetActionRelay.drain(")) return true;
22166
+ if (readFileSync28(path, "utf8").includes("WidgetActionRelay.drain(")) return true;
21919
22167
  }
21920
22168
  }
21921
22169
  }
@@ -21937,18 +22185,18 @@ function injectBlock(text2, kind, label2, render) {
21937
22185
  return [...lines.slice(0, beginIdx), ...block, ...lines.slice(endIdx + 1)].join("\n");
21938
22186
  }
21939
22187
  function androidManifestPath2(app) {
21940
- const androidDir = join29(appDir(app), "android");
21941
- const preferred = join29(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
21942
- if (existsSync25(preferred)) return preferred;
21943
- const candidates = existsSync25(androidDir) ? readdirSync13(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join29(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync25) : [];
22188
+ const androidDir = join30(appDir(app), "android");
22189
+ const preferred = join30(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
22190
+ if (existsSync26(preferred)) return preferred;
22191
+ const candidates = existsSync26(androidDir) ? readdirSync13(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join30(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync26) : [];
21944
22192
  return candidates.length === 1 ? candidates[0] : preferred;
21945
22193
  }
21946
22194
  function automotiveAppDescPath(app) {
21947
- return join29(dirname17(androidManifestPath2(app)), "res", "xml", "automotive_app_desc.xml");
22195
+ return join30(dirname18(androidManifestPath2(app)), "res", "xml", "automotive_app_desc.xml");
21948
22196
  }
21949
22197
  function applyToFile(path, kind, label2, render, check) {
21950
- if (!existsSync25(path)) return { path, outcome: "skipped" };
21951
- const before = readFileSync27(path, "utf8");
22198
+ if (!existsSync26(path)) return { path, outcome: "skipped" };
22199
+ const before = readFileSync28(path, "utf8");
21952
22200
  if (!hasMarkers(before, kind, label2)) return { path, outcome: "skipped" };
21953
22201
  const after = injectBlock(before, kind, label2, render);
21954
22202
  if (after === before) return { path, outcome: "unchanged" };
@@ -21957,15 +22205,15 @@ function applyToFile(path, kind, label2, render, check) {
21957
22205
  return { path, outcome: "written" };
21958
22206
  }
21959
22207
  function applyWholeFile(path, content, check) {
21960
- const current = existsSync25(path) ? readFileSync27(path, "utf8") : null;
22208
+ const current = existsSync26(path) ? readFileSync28(path, "utf8") : null;
21961
22209
  if (current === content) return { path, outcome: "unchanged" };
21962
22210
  if (check) return { path, outcome: "drift" };
21963
- mkdirSync12(dirname17(path), { recursive: true });
22211
+ mkdirSync12(dirname18(path), { recursive: true });
21964
22212
  writeFileSync11(path, content);
21965
22213
  return { path, outcome: "written" };
21966
22214
  }
21967
22215
  function removeWholeFile(path, check) {
21968
- if (!existsSync25(path)) return { path, outcome: "skipped" };
22216
+ if (!existsSync26(path)) return { path, outcome: "skipped" };
21969
22217
  if (check) return { path, outcome: "drift" };
21970
22218
  rmSync3(path);
21971
22219
  return { path, outcome: "removed" };
@@ -21973,7 +22221,7 @@ function removeWholeFile(path, check) {
21973
22221
  function genNativeConfig(app, opts = {}) {
21974
22222
  const check = opts.check ?? false;
21975
22223
  const configPath = appConfigTs(app);
21976
- const configTs = existsSync25(configPath) ? readFileSync27(configPath, "utf8") : "";
22224
+ const configTs = existsSync26(configPath) ? readFileSync28(configPath, "utf8") : "";
21977
22225
  const capabilities = parseStringArray(configTs, "capabilities");
21978
22226
  const envelope = parseStringArray(configTs, "capabilityEnvelope");
21979
22227
  const ids = buildSet(capabilities, envelope);
@@ -21989,7 +22237,7 @@ function genNativeConfig(app, opts = {}) {
21989
22237
  const carBlock = applyToFile(carManifestPath, "xml", LABEL_CAR, (i) => renderCarManifestXml(carCategory, i), check);
21990
22238
  assertCarBlockLanded(app, carCategory, carManifestPath, carBlock.outcome, LABEL_CAR);
21991
22239
  const iosYml = iosProjectYml(app);
21992
- const project = existsSync25(iosYml) ? readFileSync27(iosYml, "utf8") : "";
22240
+ const project = existsSync26(iosYml) ? readFileSync28(iosYml, "utf8") : "";
21993
22241
  const targetManaged = hasMarkers(project, "yaml", LABEL_LIVE_TARGET);
21994
22242
  const embedManaged = hasMarkers(project, "yaml", LABEL_LIVE_EMBED);
21995
22243
  if (targetManaged !== embedManaged) throw new Error(`${app}: Live Activity target and embedding markers must be installed together`);
@@ -22043,12 +22291,12 @@ function genNativeConfig(app, opts = {}) {
22043
22291
  const plan = live2;
22044
22292
  files.push(applyToFile(iosYml, "yaml", LABEL_LIVE_TARGET, (i) => plan.target.map((line) => i + line), check));
22045
22293
  files.push(applyToFile(iosYml, "yaml", LABEL_LIVE_EMBED, (i) => plan.embed.map((line) => i + line), check));
22046
- const source = join29(dirname17(iosYml), LIVE_SOURCE_PATH);
22294
+ const source = join30(dirname18(iosYml), LIVE_SOURCE_PATH);
22047
22295
  files.push(plan.source ? applyWholeFile(source, plan.source, check) : removeWholeFile(source, check));
22048
- const entitlements = join29(dirname17(iosYml), LIVE_ENTITLEMENTS_PATH);
22296
+ const entitlements = join30(dirname18(iosYml), LIVE_ENTITLEMENTS_PATH);
22049
22297
  files.push(plan.entitlements ? applyWholeFile(entitlements, plan.entitlements, check) : removeWholeFile(entitlements, check));
22050
22298
  }
22051
- const iosSplashManaged = existsSync25(iosYml) && hasMarkers(readFileSync27(iosYml, "utf8"), "yaml", LABEL_IOS_SPLASH);
22299
+ const iosSplashManaged = existsSync26(iosYml) && hasMarkers(readFileSync28(iosYml, "utf8"), "yaml", LABEL_IOS_SPLASH);
22052
22300
  const manifest = androidManifestPath2(app);
22053
22301
  const splash = splashPlan(app, configTs, manifest, iosSplashManaged);
22054
22302
  for (const artifact of splash.artifacts) files.push(applyWholeFile(artifact.path, artifact.content, check));
@@ -22136,8 +22384,8 @@ var init_app_module = __esm({
22136
22384
  });
22137
22385
 
22138
22386
  // ../../core/dev-server/src/loader.ts
22139
- import { resolve as resolve18, basename as basename7, dirname as dirname18 } from "node:path";
22140
- import { existsSync as existsSync26, readdirSync as readdirSync14, readFileSync as readFileSync28, mkdirSync as mkdirSync13 } from "node:fs";
22387
+ import { resolve as resolve19, basename as basename7, dirname as dirname19 } from "node:path";
22388
+ import { existsSync as existsSync27, readdirSync as readdirSync14, readFileSync as readFileSync29, mkdirSync as mkdirSync13 } from "node:fs";
22141
22389
  import { pathToFileURL as pathToFileURL5, fileURLToPath as fileURLToPath11 } from "node:url";
22142
22390
  function resolveWixWebAuthConfig(app) {
22143
22391
  const auth = app?.meta.auth;
@@ -22166,17 +22414,17 @@ function resolveWixClientId(app) {
22166
22414
  }
22167
22415
  function resolveDevEntry(name) {
22168
22416
  for (const base of APP_ROOTS2) {
22169
- const dir = resolve18(ROOT2, base, name);
22170
- const entry = resolve18(dir, "dev/index.ts");
22171
- if (existsSync26(entry)) return { entry, dir };
22417
+ const dir = resolve19(ROOT2, base, name);
22418
+ const entry = resolve19(dir, "dev/index.ts");
22419
+ if (existsSync27(entry)) return { entry, dir };
22172
22420
  }
22173
22421
  return null;
22174
22422
  }
22175
22423
  async function devModuleUrl(entry, dir) {
22176
22424
  const hasTypeScriptLoader = process.execArgv.some((arg) => arg.includes("tsx"));
22177
22425
  if (!isOffRepo() && hasTypeScriptLoader) return pathToFileURL5(entry).href;
22178
- const out = resolve18(dir, "dist/.himi/dev.mjs");
22179
- mkdirSync13(dirname18(out), { recursive: true });
22426
+ const out = resolve19(dir, "dist/.himi/dev.mjs");
22427
+ mkdirSync13(dirname19(out), { recursive: true });
22180
22428
  await buildAppEntry(entry, out, {
22181
22429
  // Plain Node must resolve the published-style umbrella entry points for
22182
22430
  // both standalone and in-repo CLI content. In-repo workspace packages
@@ -22198,7 +22446,7 @@ async function importDevModule(name, entry, dir) {
22198
22446
  }
22199
22447
  function readPlatformsJson(dir) {
22200
22448
  try {
22201
- const raw = JSON.parse(readFileSync28(resolve18(dir, "platforms.json"), "utf8"));
22449
+ const raw = JSON.parse(readFileSync29(resolve19(dir, "platforms.json"), "utf8"));
22202
22450
  return Object.fromEntries(
22203
22451
  Object.entries(raw).filter(([k, v]) => VALID_PLATFORM_KEYS2.has(k) && typeof v === "boolean")
22204
22452
  );
@@ -22207,17 +22455,17 @@ function readPlatformsJson(dir) {
22207
22455
  }
22208
22456
  }
22209
22457
  async function loadContent(dir) {
22210
- const absDir = resolve18(dir);
22211
- const entry = resolve18(absDir, "dev/index.ts");
22212
- if (!existsSync26(entry)) {
22458
+ const absDir = resolve19(dir);
22459
+ const entry = resolve19(absDir, "dev/index.ts");
22460
+ if (!existsSync27(entry)) {
22213
22461
  throw new Error(`No content package at "${absDir}": expected ${entry}.`);
22214
22462
  }
22215
22463
  let name = basename7(absDir);
22216
- const manifestPath = resolve18(absDir, "himalaya.content.json");
22217
- if (existsSync26(manifestPath)) {
22464
+ const manifestPath = resolve19(absDir, "himalaya.content.json");
22465
+ if (existsSync27(manifestPath)) {
22218
22466
  let manifest;
22219
22467
  try {
22220
- manifest = JSON.parse(readFileSync28(manifestPath, "utf8"));
22468
+ manifest = JSON.parse(readFileSync29(manifestPath, "utf8"));
22221
22469
  } catch (e) {
22222
22470
  throw new Error(`Invalid himalaya.content.json at ${manifestPath}: ${e.message}`);
22223
22471
  }
@@ -22243,11 +22491,11 @@ function listAvailableApps() {
22243
22491
  const out = [];
22244
22492
  const seen = /* @__PURE__ */ new Set();
22245
22493
  for (const base of APP_ROOTS2) {
22246
- const baseDir = resolve18(ROOT2, base);
22247
- if (!existsSync26(baseDir)) continue;
22494
+ const baseDir = resolve19(ROOT2, base);
22495
+ if (!existsSync27(baseDir)) continue;
22248
22496
  for (const name of readdirSync14(baseDir)) {
22249
22497
  if (name.startsWith("_") || name.startsWith(".") || seen.has(name)) continue;
22250
- if (existsSync26(resolve18(baseDir, name, "dev/index.ts"))) {
22498
+ if (existsSync27(resolve19(baseDir, name, "dev/index.ts"))) {
22251
22499
  seen.add(name);
22252
22500
  out.push({ name, root: base, kind: base === "test-apps" ? "test" : "app" });
22253
22501
  }
@@ -22261,7 +22509,7 @@ var init_loader = __esm({
22261
22509
  init_app_module();
22262
22510
  init_preview_surfaces();
22263
22511
  init_build_lib();
22264
- ROOT2 = resolve18(fileURLToPath11(new URL("../../..", import.meta.url)));
22512
+ ROOT2 = resolve19(fileURLToPath11(new URL("../../..", import.meta.url)));
22265
22513
  APP_ROOTS2 = ["apps", "test-apps"];
22266
22514
  VALID_PLATFORM_KEYS2 = /* @__PURE__ */ new Set([
22267
22515
  ...PREVIEW_SURFACES.map((s) => s.platformKey).filter((k) => k !== null),
@@ -22306,8 +22554,8 @@ var init_build_sha = __esm({
22306
22554
 
22307
22555
  // ../../core/dev-server/src/app-assets.ts
22308
22556
  import { createHash as createHash8 } from "node:crypto";
22309
- import { existsSync as existsSync27, lstatSync as lstatSync3, readFileSync as readFileSync29, readdirSync as readdirSync15, realpathSync as realpathSync3, statSync as statSync6 } from "node:fs";
22310
- import { join as join30, resolve as resolve19, sep as sep5 } from "node:path";
22557
+ import { existsSync as existsSync28, lstatSync as lstatSync3, readFileSync as readFileSync30, readdirSync as readdirSync15, realpathSync as realpathSync3, statSync as statSync5 } from "node:fs";
22558
+ import { join as join31, resolve as resolve20, sep as sep5 } from "node:path";
22311
22559
  function contentTypeFor(name) {
22312
22560
  const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
22313
22561
  return CONTENT_TYPES[ext] ?? "application/octet-stream";
@@ -22321,19 +22569,19 @@ function assetSha256(bytes2) {
22321
22569
  function findAssetsDir(appDir2) {
22322
22570
  let declared;
22323
22571
  try {
22324
- declared = JSON.parse(readFileSync29(join30(appDir2, "himalaya.content.json"), "utf8")).assets;
22572
+ declared = JSON.parse(readFileSync30(join31(appDir2, "himalaya.content.json"), "utf8")).assets;
22325
22573
  } catch {
22326
22574
  declared = void 0;
22327
22575
  }
22328
22576
  const rel = typeof declared === "string" && declared ? declared : "assets";
22329
- const abs = resolve19(appDir2, rel);
22330
- const base = resolve19(appDir2);
22577
+ const abs = resolve20(appDir2, rel);
22578
+ const base = resolve20(appDir2);
22331
22579
  if (abs !== base && !abs.startsWith(base + sep5)) return null;
22332
- return existsSync27(abs) && statSync6(abs).isDirectory() ? abs : null;
22580
+ return existsSync28(abs) && statSync5(abs).isDirectory() ? abs : null;
22333
22581
  }
22334
22582
  function readOtaOnlyPatterns(appDir2) {
22335
22583
  try {
22336
- const raw = JSON.parse(readFileSync29(join30(appDir2, "assets.json"), "utf8"));
22584
+ const raw = JSON.parse(readFileSync30(join31(appDir2, "assets.json"), "utf8"));
22337
22585
  return Array.isArray(raw.otaOnly) ? raw.otaOnly.filter((p) => typeof p === "string") : [];
22338
22586
  } catch {
22339
22587
  return [];
@@ -22351,7 +22599,7 @@ function collectAssets(appDir2) {
22351
22599
  const walk2 = (abs, prefix) => {
22352
22600
  for (const entry of readdirSync15(abs, { withFileTypes: true })) {
22353
22601
  if (entry.name.startsWith(".")) continue;
22354
- const child = join30(abs, entry.name);
22602
+ const child = join31(abs, entry.name);
22355
22603
  const name = prefix ? `${prefix}/${entry.name}` : entry.name;
22356
22604
  if (entry.isDirectory()) {
22357
22605
  walk2(child, name);
@@ -22360,7 +22608,7 @@ function collectAssets(appDir2) {
22360
22608
  if (!entry.isFile()) continue;
22361
22609
  const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
22362
22610
  const baked = !OTA_ONLY_BY_DEFAULT.has(ext) && !otaOnly.some((p) => matchesPattern(name, p));
22363
- out.push({ name, path: child, bytes: statSync6(child).size, contentType: contentTypeFor(name), baked });
22611
+ out.push({ name, path: child, bytes: statSync5(child).size, contentType: contentTypeFor(name), baked });
22364
22612
  }
22365
22613
  };
22366
22614
  walk2(dir, "");
@@ -22369,7 +22617,7 @@ function collectAssets(appDir2) {
22369
22617
  function collectBrandFonts(appDir2) {
22370
22618
  let faces;
22371
22619
  try {
22372
- faces = JSON.parse(readFileSync29(join30(appDir2, "media/fonts/faces.json"), "utf8"));
22620
+ faces = JSON.parse(readFileSync30(join31(appDir2, "media/fonts/faces.json"), "utf8"));
22373
22621
  } catch {
22374
22622
  return [];
22375
22623
  }
@@ -22382,7 +22630,7 @@ function collectBrandFonts(appDir2) {
22382
22630
  const rel = typeof raw?.file === "string" ? raw.file : "";
22383
22631
  if (!family || !postscriptName || !rel) continue;
22384
22632
  if (rel.startsWith("/") || rel.split("/").includes("..")) continue;
22385
- const path = join30(appDir2, rel);
22633
+ const path = join31(appDir2, rel);
22386
22634
  try {
22387
22635
  if (!lstatSync3(path).isFile()) continue;
22388
22636
  const root = realpathSync3(appDir2);
@@ -22418,7 +22666,7 @@ function resolveAssetForBuild(appDir2, name, serverBaseUrl, appId2) {
22418
22666
  if (!hit) return null;
22419
22667
  let bytes2;
22420
22668
  try {
22421
- bytes2 = readFileSync29(hit.path);
22669
+ bytes2 = readFileSync30(hit.path);
22422
22670
  } catch {
22423
22671
  return null;
22424
22672
  }
@@ -22492,7 +22740,7 @@ async function injectRequest(listener, request) {
22492
22740
  req.headers.host ??= url.host;
22493
22741
  if (body && body.length > 0) req.push(body);
22494
22742
  req.push(null);
22495
- return await new Promise((resolve41, reject) => {
22743
+ return await new Promise((resolve42, reject) => {
22496
22744
  const streamed = () => {
22497
22745
  queueMicrotask(() => {
22498
22746
  req.emit("close");
@@ -22508,7 +22756,7 @@ async function injectRequest(listener, request) {
22508
22756
  else headers2.set(name, value);
22509
22757
  }
22510
22758
  const bodyless = out.status === 204 || out.status === 304 || request.method === "HEAD";
22511
- resolve41(new Response(bodyless ? null : out.body, { status: out.status, headers: headers2 }));
22759
+ resolve42(new Response(bodyless ? null : out.body, { status: out.status, headers: headers2 }));
22512
22760
  }, streamed);
22513
22761
  res.on("inject-error", reject);
22514
22762
  try {
@@ -22611,25 +22859,25 @@ var init_inject = __esm({
22611
22859
 
22612
22860
  // ../../core/dev-server/src/store.ts
22613
22861
  import { mkdirSync as mkdirSync14 } from "node:fs";
22614
- import { dirname as dirname19, resolve as resolve20 } from "node:path";
22862
+ import { dirname as dirname20, resolve as resolve21 } from "node:path";
22615
22863
  import { fileURLToPath as fileURLToPath12 } from "node:url";
22616
22864
  import { createRequire } from "node:module";
22617
22865
  var nodeRequire, ROOT3, Store;
22618
22866
  var init_store2 = __esm({
22619
22867
  "../../core/dev-server/src/store.ts"() {
22620
22868
  nodeRequire = createRequire(import.meta.url);
22621
- ROOT3 = resolve20(fileURLToPath12(new URL("../../..", import.meta.url)));
22869
+ ROOT3 = resolve21(fileURLToPath12(new URL("../../..", import.meta.url)));
22622
22870
  Store = class {
22623
22871
  _db;
22624
22872
  dbPath;
22625
22873
  constructor(appName) {
22626
- const dataRoot = process.env.HIMALAYA_DEV_STORE_DIR ? resolve20(process.env.HIMALAYA_DEV_STORE_DIR) : resolve20(ROOT3, "core/dev-server/data");
22627
- this.dbPath = resolve20(dataRoot, `${appName}.sqlite`);
22874
+ const dataRoot = process.env.HIMALAYA_DEV_STORE_DIR ? resolve21(process.env.HIMALAYA_DEV_STORE_DIR) : resolve21(ROOT3, "core/dev-server/data");
22875
+ this.dbPath = resolve21(dataRoot, `${appName}.sqlite`);
22628
22876
  }
22629
22877
  /** The node:sqlite handle — opened, bootstrapped, and seeded on first access. */
22630
22878
  get db() {
22631
22879
  if (this._db) return this._db;
22632
- mkdirSync14(dirname19(this.dbPath), { recursive: true });
22880
+ mkdirSync14(dirname20(this.dbPath), { recursive: true });
22633
22881
  const { DatabaseSync } = nodeRequire("node:sqlite");
22634
22882
  this._db = new DatabaseSync(this.dbPath, { timeout: 5e3 });
22635
22883
  this.bootstrap();
@@ -22968,15 +23216,15 @@ var init_store2 = __esm({
22968
23216
  });
22969
23217
 
22970
23218
  // ../../core/dev-server/src/derive-transport-capability.ts
22971
- import { readFileSync as readFileSync30 } from "node:fs";
22972
- import { resolve as resolve21 } from "node:path";
23219
+ import { readFileSync as readFileSync31 } from "node:fs";
23220
+ import { resolve as resolve22 } from "node:path";
22973
23221
  function bundleRidesWixTransport(bundleJs) {
22974
23222
  return /himalaya\.wixApi|wixApi\.call/.test(bundleJs);
22975
23223
  }
22976
23224
  function readFirst(roots, file) {
22977
23225
  for (const root of typeof roots === "string" ? [roots] : roots) {
22978
23226
  try {
22979
- return readFileSync30(resolve21(root, file), "utf8");
23227
+ return readFileSync31(resolve22(root, file), "utf8");
22980
23228
  } catch {
22981
23229
  }
22982
23230
  }
@@ -23144,7 +23392,7 @@ var init_wix_member_login = __esm({
23144
23392
  });
23145
23393
 
23146
23394
  // ../../core/dev-server/src/wix-gateway.ts
23147
- import { readFileSync as readFileSync31, existsSync as existsSync28 } from "node:fs";
23395
+ import { readFileSync as readFileSync32, existsSync as existsSync29 } from "node:fs";
23148
23396
  function redactJsonValue(v) {
23149
23397
  if (Array.isArray(v)) return v.map(redactJsonValue);
23150
23398
  if (v && typeof v === "object") {
@@ -23183,8 +23431,8 @@ function resolveNetMocks(fileMocks, appDeclared) {
23183
23431
  }
23184
23432
  function readNetMocksFile(path) {
23185
23433
  try {
23186
- if (!existsSync28(path)) return null;
23187
- const raw = JSON.parse(readFileSync31(path, "utf-8"));
23434
+ if (!existsSync29(path)) return null;
23435
+ const raw = JSON.parse(readFileSync32(path, "utf-8"));
23188
23436
  if (Array.isArray(raw)) return wixMockRuleErrors(raw).length ? null : { rules: raw };
23189
23437
  if (raw && typeof raw === "object") {
23190
23438
  const o = raw;
@@ -23984,13 +24232,13 @@ var init_l10n_catalog = __esm({
23984
24232
  });
23985
24233
 
23986
24234
  // ../../core/dev-server/src/l10n.ts
23987
- import { readFileSync as readFileSync32, existsSync as existsSync29 } from "node:fs";
23988
- import { resolve as resolve22 } from "node:path";
24235
+ import { readFileSync as readFileSync33, existsSync as existsSync30 } from "node:fs";
24236
+ import { resolve as resolve23 } from "node:path";
23989
24237
  import { fileURLToPath as fileURLToPath13 } from "node:url";
23990
24238
  function loadCatalog2(tag) {
23991
24239
  if (catalogCache.has(tag)) return catalogCache.get(tag);
23992
- const path = resolve22(ROOT4, `tools/l10n/catalog.${tag}.json`);
23993
- const map = existsSync29(path) ? sourceMap(parseCatalog(readFileSync32(path, "utf8"))) : /* @__PURE__ */ new Map();
24240
+ const path = resolve23(ROOT4, `tools/l10n/catalog.${tag}.json`);
24241
+ const map = existsSync30(path) ? sourceMap(parseCatalog(readFileSync33(path, "utf8"))) : /* @__PURE__ */ new Map();
23994
24242
  catalogCache.set(tag, map);
23995
24243
  return map;
23996
24244
  }
@@ -23999,8 +24247,8 @@ function pickLocale(acceptLanguage) {
23999
24247
  const tags = acceptLanguage.split(",").map((s) => s.split(";")[0].trim().toLowerCase()).map((s) => s.split("-")[0]).filter((s) => s.length > 0);
24000
24248
  for (const tag of tags) {
24001
24249
  if (tag === "en") continue;
24002
- const path = resolve22(ROOT4, `tools/l10n/catalog.${tag}.json`);
24003
- if (existsSync29(path)) return tag;
24250
+ const path = resolve23(ROOT4, `tools/l10n/catalog.${tag}.json`);
24251
+ if (existsSync30(path)) return tag;
24004
24252
  }
24005
24253
  return null;
24006
24254
  }
@@ -24056,7 +24304,7 @@ var init_l10n = __esm({
24056
24304
  "../../core/dev-server/src/l10n.ts"() {
24057
24305
  init_l10n_strings();
24058
24306
  init_l10n_catalog();
24059
- ROOT4 = resolve22(fileURLToPath13(new URL("../../..", import.meta.url)));
24307
+ ROOT4 = resolve23(fileURLToPath13(new URL("../../..", import.meta.url)));
24060
24308
  catalogCache = /* @__PURE__ */ new Map();
24061
24309
  }
24062
24310
  });
@@ -24120,13 +24368,13 @@ var init_stable_ids = __esm({
24120
24368
  // ../../core/dev-server/src/preview.ts
24121
24369
  import { spawn as spawn2 } from "node:child_process";
24122
24370
  import { createHash as createHash11 } from "node:crypto";
24123
- import { existsSync as existsSync30, createReadStream, statSync as statSync7, readFileSync as readFileSync33, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
24371
+ import { existsSync as existsSync31, createReadStream, statSync as statSync6, readFileSync as readFileSync34, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
24124
24372
  import { homedir as homedir5, tmpdir as tmpdir2 } from "node:os";
24125
- import { resolve as resolvePath, dirname as dirname20 } from "node:path";
24373
+ import { resolve as resolvePath, dirname as dirname21 } from "node:path";
24126
24374
  import { fileURLToPath as fileURLToPath14 } from "node:url";
24127
24375
  function firstExisting(paths) {
24128
24376
  for (const p of paths) {
24129
- if (p && existsSync30(p)) return p;
24377
+ if (p && existsSync31(p)) return p;
24130
24378
  }
24131
24379
  return null;
24132
24380
  }
@@ -24135,7 +24383,7 @@ function macosWinlistBin() {
24135
24383
  }
24136
24384
  function macosWinlistSourceHash() {
24137
24385
  try {
24138
- return createHash11("sha1").update(readFileSync33(MACOS_WINLIST)).digest("hex").slice(0, 12);
24386
+ return createHash11("sha1").update(readFileSync34(MACOS_WINLIST)).digest("hex").slice(0, 12);
24139
24387
  } catch {
24140
24388
  return "missing";
24141
24389
  }
@@ -24165,7 +24413,7 @@ function isHimalayaAppWindow(w) {
24165
24413
  }
24166
24414
  async function macosWinlistCommand() {
24167
24415
  const bin = macosWinlistBin();
24168
- if (existsSync30(bin)) return compiledWinlist(bin);
24416
+ if (existsSync31(bin)) return compiledWinlist(bin);
24169
24417
  if (macosWinlistCompileFailed) return interpretWinlist();
24170
24418
  if (!macosWinlistInFlight) {
24171
24419
  macosWinlistInFlight = buildMacosWinlist(bin).finally(() => {
@@ -24193,7 +24441,7 @@ async function buildMacosWinlist(bin) {
24193
24441
  unlinkSync2(staging);
24194
24442
  } catch {
24195
24443
  }
24196
- return existsSync30(bin) ? compiledWinlist(bin) : interpretWinlist();
24444
+ return existsSync31(bin) ? compiledWinlist(bin) : interpretWinlist();
24197
24445
  }
24198
24446
  return compiledWinlist(bin);
24199
24447
  }
@@ -24280,7 +24528,7 @@ async function captureFrame(params, format = "png") {
24280
24528
  const tmp = resolvePath(tmpdir2(), `himalaya-macos-preview-${windowId}-${process.pid}-${++macosCaptureSeq}.png`);
24281
24529
  try {
24282
24530
  await runner.capture(SCREENCAPTURE_BIN, ["-o", "-x", `-l${windowId}`, tmp]);
24283
- return { buf: readFileSync33(tmp), mime: "image/png" };
24531
+ return { buf: readFileSync34(tmp), mime: "image/png" };
24284
24532
  } finally {
24285
24533
  try {
24286
24534
  unlinkSync2(tmp);
@@ -24793,12 +25041,12 @@ function finalizeRecording(key2, rec) {
24793
25041
  if (rec.finalize) return rec.finalize;
24794
25042
  rec.finalize = (async () => {
24795
25043
  clearTimeout(rec.autoStop);
24796
- await new Promise((resolve41) => {
25044
+ await new Promise((resolve42) => {
24797
25045
  let done = false;
24798
25046
  const finish2 = () => {
24799
25047
  if (!done) {
24800
25048
  done = true;
24801
- resolve41();
25049
+ resolve42();
24802
25050
  }
24803
25051
  };
24804
25052
  rec.child.once("close", finish2);
@@ -24818,7 +25066,7 @@ function finalizeRecording(key2, rec) {
24818
25066
  } catch {
24819
25067
  }
24820
25068
  }
24821
- const sizeBytes = existsSync30(rec.localPath) ? statSync7(rec.localPath).size : 0;
25069
+ const sizeBytes = existsSync31(rec.localPath) ? statSync6(rec.localPath).size : 0;
24822
25070
  clips.set(rec.id, { path: rec.localPath, mime: rec.mime });
24823
25071
  recordings.delete(key2);
24824
25072
  return { sizeBytes };
@@ -24890,12 +25138,12 @@ async function handlePreviewClip(res, url) {
24890
25138
  const q = new URLSearchParams(url.split("?")[1] ?? "");
24891
25139
  const id = q.get("id") ?? "";
24892
25140
  const clip = clips.get(id);
24893
- if (!clip || !existsSync30(clip.path)) {
25141
+ if (!clip || !existsSync31(clip.path)) {
24894
25142
  res.statusCode = 404;
24895
25143
  res.setHeader("content-type", "application/json");
24896
25144
  return void res.end(JSON.stringify({ error: "clip_not_found", id }));
24897
25145
  }
24898
- const size = statSync7(clip.path).size;
25146
+ const size = statSync6(clip.path).size;
24899
25147
  res.statusCode = 200;
24900
25148
  res.setHeader("content-type", clip.mime);
24901
25149
  res.setHeader("content-length", String(size));
@@ -24909,7 +25157,7 @@ var init_preview = __esm({
24909
25157
  DEVICE_LIST_TIMEOUT_MS = 5e3;
24910
25158
  defaultRunner2 = {
24911
25159
  capture(cmd, args, timeoutMs = DEFAULT_CAPTURE_TIMEOUT_MS) {
24912
- return new Promise((resolve41, reject) => {
25160
+ return new Promise((resolve42, reject) => {
24913
25161
  const child = spawn2(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
24914
25162
  const out = [];
24915
25163
  const err = [];
@@ -24931,7 +25179,7 @@ var init_preview = __esm({
24931
25179
  child.stderr.on("data", (b) => err.push(b));
24932
25180
  child.on("error", (e) => settle(reject, e));
24933
25181
  child.on("close", (code) => {
24934
- if (code === 0) settle(resolve41, Buffer.concat(out));
25182
+ if (code === 0) settle(resolve42, Buffer.concat(out));
24935
25183
  else settle(reject, new Error(`${cmd} exited ${code}: ${Buffer.concat(err).toString("utf8")}`));
24936
25184
  });
24937
25185
  });
@@ -24951,7 +25199,7 @@ var init_preview = __esm({
24951
25199
  SWIFT_BIN = process.env.HIMALAYA_SWIFT_PATH ?? "swift";
24952
25200
  SWIFTC_BIN = process.env.HIMALAYA_SWIFTC_PATH ?? "swiftc";
24953
25201
  SCREENCAPTURE_BIN = process.env.HIMALAYA_SCREENCAPTURE_PATH ?? "screencapture";
24954
- MACOS_WINLIST = resolvePath(dirname20(fileURLToPath14(import.meta.url)), "../scripts/macos-winshot.swift");
25202
+ MACOS_WINLIST = resolvePath(dirname21(fileURLToPath14(import.meta.url)), "../scripts/macos-winshot.swift");
24955
25203
  MACOS_WINLIST_BUILD_TIMEOUT_MS = 12e4;
24956
25204
  MACOS_WINLIST_INTERPRET_TIMEOUT_MS = 3e4;
24957
25205
  HIMALAYA_MAC_BUNDLE_ID = /^(com\.himalaya\.|com\.wix\.himi\.|dev\.himalaya\.)/i;
@@ -25008,8 +25256,8 @@ var init_tier4_integrity = __esm({
25008
25256
  });
25009
25257
 
25010
25258
  // ../../core/dev-server/src/tier4-verifier.ts
25011
- import { readFileSync as readFileSync34, existsSync as existsSync31 } from "node:fs";
25012
- import { resolve as resolve23 } from "node:path";
25259
+ import { readFileSync as readFileSync35, existsSync as existsSync32 } from "node:fs";
25260
+ import { resolve as resolve24 } from "node:path";
25013
25261
  var TierFourVerifier;
25014
25262
  var init_tier4_verifier = __esm({
25015
25263
  "../../core/dev-server/src/tier4-verifier.ts"() {
@@ -25025,8 +25273,8 @@ var init_tier4_verifier = __esm({
25025
25273
  async verifyServedFile(bundleId, relPath, providedBytes) {
25026
25274
  const expected = this.expectedSri(bundleId, relPath);
25027
25275
  if (!expected) return { ok: true };
25028
- const path = resolve23(this.tier4Root, bundleId, relPath);
25029
- const bytes2 = providedBytes ?? (existsSync31(path) ? readFileSync34(path) : null);
25276
+ const path = resolve24(this.tier4Root, bundleId, relPath);
25277
+ const bytes2 = providedBytes ?? (existsSync32(path) ? readFileSync35(path) : null);
25030
25278
  if (!bytes2) return { ok: false, reason: "file not found" };
25031
25279
  if (computeSri(bytes2) !== expected) return { ok: false, reason: "sri mismatch" };
25032
25280
  return { ok: true };
@@ -25034,9 +25282,9 @@ var init_tier4_verifier = __esm({
25034
25282
  bundle(bundleId) {
25035
25283
  let cached3 = this.cache.get(bundleId);
25036
25284
  if (cached3) return cached3;
25037
- const manifestPath = resolve23(this.tier4Root, bundleId, "manifest.json");
25038
- if (!existsSync31(manifestPath)) return void 0;
25039
- const manifest = JSON.parse(readFileSync34(manifestPath, "utf8"));
25285
+ const manifestPath = resolve24(this.tier4Root, bundleId, "manifest.json");
25286
+ if (!existsSync32(manifestPath)) return void 0;
25287
+ const manifest = JSON.parse(readFileSync35(manifestPath, "utf8"));
25040
25288
  cached3 = { integrity: parseManifestIntegrity(manifest) };
25041
25289
  this.cache.set(bundleId, cached3);
25042
25290
  return cached3;
@@ -35616,7 +35864,7 @@ async function stabilizePngPage(page, scheme) {
35616
35864
  const browser = globalThis;
35617
35865
  browser.document.documentElement.style.colorScheme = selected;
35618
35866
  await browser.document.fonts?.ready;
35619
- await new Promise((resolve41) => browser.requestAnimationFrame(() => browser.requestAnimationFrame(() => resolve41())));
35867
+ await new Promise((resolve42) => browser.requestAnimationFrame(() => browser.requestAnimationFrame(() => resolve42())));
35620
35868
  }, scheme);
35621
35869
  }
35622
35870
  async function renderPngBatch(items) {
@@ -36560,8 +36808,8 @@ var init_src5 = __esm({
36560
36808
  });
36561
36809
 
36562
36810
  // ../../core/dev-server/src/flags/index.ts
36563
- import { readFileSync as readFileSync35 } from "node:fs";
36564
- import { resolve as resolve24 } from "node:path";
36811
+ import { readFileSync as readFileSync36 } from "node:fs";
36812
+ import { resolve as resolve25 } from "node:path";
36565
36813
  function selectProvider(config, store) {
36566
36814
  const local = new LocalFlagProvider(store, () => config?.overrides);
36567
36815
  if (!config || config.provider === "local") return local;
@@ -36656,7 +36904,7 @@ var init_flags2 = __esm({
36656
36904
  if (now - this.lastFetch < this.ttlMs) return;
36657
36905
  this.lastFetch = now;
36658
36906
  try {
36659
- const defs = JSON.parse(readFileSync35(resolve24(appDir2, "flags.json"), "utf8"));
36907
+ const defs = JSON.parse(readFileSync36(resolve25(appDir2, "flags.json"), "utf8"));
36660
36908
  if (Array.isArray(defs) && defs.length > 0) store.replaceAll(defs);
36661
36909
  } catch {
36662
36910
  }
@@ -36706,19 +36954,19 @@ var init_motion_flags = __esm({
36706
36954
  });
36707
36955
 
36708
36956
  // ../../core/dev-server/src/kits.ts
36709
- import { readdirSync as readdirSync16, readFileSync as readFileSync36 } from "node:fs";
36710
- import { resolve as resolve25, join as join31 } from "node:path";
36957
+ import { readdirSync as readdirSync16, readFileSync as readFileSync37 } from "node:fs";
36958
+ import { resolve as resolve26, join as join32 } from "node:path";
36711
36959
  import { fileURLToPath as fileURLToPath15 } from "node:url";
36712
36960
  function listKits() {
36713
36961
  return readdirSync16(CATALOG).filter((f) => f.endsWith(".json")).map((f) => {
36714
- const { tokens, ...meta } = JSON.parse(readFileSync36(join31(CATALOG, f), "utf8"));
36962
+ const { tokens, ...meta } = JSON.parse(readFileSync37(join32(CATALOG, f), "utf8"));
36715
36963
  return meta;
36716
36964
  });
36717
36965
  }
36718
36966
  function readKit(id) {
36719
36967
  if (!/^[a-z0-9-]+$/.test(id)) return null;
36720
36968
  try {
36721
- return JSON.parse(readFileSync36(join31(CATALOG, `${id}.json`), "utf8"));
36969
+ return JSON.parse(readFileSync37(join32(CATALOG, `${id}.json`), "utf8"));
36722
36970
  } catch {
36723
36971
  return null;
36724
36972
  }
@@ -36726,8 +36974,8 @@ function readKit(id) {
36726
36974
  var ROOT5, CATALOG;
36727
36975
  var init_kits = __esm({
36728
36976
  "../../core/dev-server/src/kits.ts"() {
36729
- ROOT5 = resolve25(fileURLToPath15(new URL("../../..", import.meta.url)));
36730
- CATALOG = join31(ROOT5, "stdlib/design-kits");
36977
+ ROOT5 = resolve26(fileURLToPath15(new URL("../../..", import.meta.url)));
36978
+ CATALOG = join32(ROOT5, "stdlib/design-kits");
36731
36979
  }
36732
36980
  });
36733
36981
 
@@ -37095,12 +37343,12 @@ __export(validate_descriptor_exports, {
37095
37343
  validateScreenDescriptor: () => validateScreenDescriptor,
37096
37344
  workerSubtreeAllowed: () => workerSubtreeAllowed
37097
37345
  });
37098
- import { readFileSync as readFileSync37, existsSync as existsSync32, readdirSync as readdirSync17 } from "node:fs";
37099
- import { resolve as resolve26 } from "node:path";
37346
+ import { readFileSync as readFileSync38, existsSync as existsSync33, readdirSync as readdirSync17 } from "node:fs";
37347
+ import { resolve as resolve27 } from "node:path";
37100
37348
  import { runInNewContext as runInNewContext2 } from "node:vm";
37101
37349
  async function loadAllowedComponents(appDir2) {
37102
- const modPath = resolve26(appDir2, "components.ts");
37103
- if (!existsSync32(modPath)) return void 0;
37350
+ const modPath = resolve27(appDir2, "components.ts");
37351
+ if (!existsSync33(modPath)) return void 0;
37104
37352
  try {
37105
37353
  const { pathToFileURL: pathToFileURL8 } = await import("node:url");
37106
37354
  const mod = await import(pathToFileURL8(modPath).href);
@@ -37112,8 +37360,8 @@ async function loadAllowedComponents(appDir2) {
37112
37360
  return void 0;
37113
37361
  }
37114
37362
  function readBuiltBundle(tier5Root, bundleName) {
37115
- const p = resolve26(tier5Root, `${bundleName}.bundle.js`);
37116
- return existsSync32(p) ? readFileSync37(p, "utf8") : void 0;
37363
+ const p = resolve27(tier5Root, `${bundleName}.bundle.js`);
37364
+ return existsSync33(p) ? readFileSync38(p, "utf8") : void 0;
37117
37365
  }
37118
37366
  function bundleNameFromUrl(url) {
37119
37367
  if (typeof url !== "string") return null;
@@ -37140,11 +37388,11 @@ function appWorkerActionsFor(config, tier5Root) {
37140
37388
  }
37141
37389
  }
37142
37390
  function readBuiltBundleMap(tier5Root, bundleName) {
37143
- const p = resolve26(tier5Root, `${bundleName}.bundle.js.map`);
37144
- return existsSync32(p) ? readFileSync37(p, "utf8") : void 0;
37391
+ const p = resolve27(tier5Root, `${bundleName}.bundle.js.map`);
37392
+ return existsSync33(p) ? readFileSync38(p, "utf8") : void 0;
37145
37393
  }
37146
37394
  function nativeSourceFiles(directory, extension, buildDirMaxDepth, depth = 1) {
37147
- if (!existsSync32(directory)) return [];
37395
+ if (!existsSync33(directory)) return [];
37148
37396
  let entries;
37149
37397
  try {
37150
37398
  entries = readdirSync17(directory, { withFileTypes: true });
@@ -37155,7 +37403,7 @@ function nativeSourceFiles(directory, extension, buildDirMaxDepth, depth = 1) {
37155
37403
  return entries.flatMap((entry) => {
37156
37404
  if (NATIVE_SCRATCH_DIRS.has(entry.name)) return [];
37157
37405
  if (entry.name === BUILD_OUTPUT_DIR && depth <= buildDirMaxDepth) return [];
37158
- const path = resolve26(directory, entry.name);
37406
+ const path = resolve27(directory, entry.name);
37159
37407
  if (entry.isDirectory()) return nativeSourceFiles(path, extension, buildDirMaxDepth, depth + 1);
37160
37408
  return entry.name.endsWith(extension) ? [path] : [];
37161
37409
  });
@@ -37164,12 +37412,12 @@ function registeredActions(source) {
37164
37412
  return [...source.matchAll(/ActionDispatcher(?:\.shared)?\s*\.\s*register\s*\(\s*"([A-Za-z0-9_.]+)"/g)].map((match) => match[1]);
37165
37413
  }
37166
37414
  function discoverNativeActions(appDir2) {
37167
- const roots = NATIVE_SOURCE_ROOTS.filter(({ directory }) => existsSync32(resolve26(appDir2, directory)));
37168
- const sources = roots.flatMap(({ directory, extension, buildDirMaxDepth }) => nativeSourceFiles(resolve26(appDir2, directory), extension, buildDirMaxDepth));
37415
+ const roots = NATIVE_SOURCE_ROOTS.filter(({ directory }) => existsSync33(resolve27(appDir2, directory)));
37416
+ const sources = roots.flatMap(({ directory, extension, buildDirMaxDepth }) => nativeSourceFiles(resolve27(appDir2, directory), extension, buildDirMaxDepth));
37169
37417
  const registered = sources.flatMap((source) => {
37170
37418
  let text2;
37171
37419
  try {
37172
- text2 = readFileSync37(source, "utf8");
37420
+ text2 = readFileSync38(source, "utf8");
37173
37421
  } catch (err) {
37174
37422
  if (!vanished(err)) throw err;
37175
37423
  return [];
@@ -37285,8 +37533,8 @@ __export(config_exports, {
37285
37533
  serializeRolloutYaml: () => serializeRolloutYaml,
37286
37534
  validateOverlayPatch: () => validateOverlayPatch
37287
37535
  });
37288
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync12 } from "node:fs";
37289
- import { dirname as dirname21, join as join32, resolve as resolve27 } from "node:path";
37536
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync39, writeFileSync as writeFileSync12 } from "node:fs";
37537
+ import { dirname as dirname22, join as join33, resolve as resolve28 } from "node:path";
37290
37538
  import { fileURLToPath as fileURLToPath16 } from "node:url";
37291
37539
  function strip(line) {
37292
37540
  const h = line.indexOf("#");
@@ -37351,14 +37599,14 @@ function parseRevocationsYaml(text2) {
37351
37599
  function appConfigPath(app, file, root = REPO_ROOT6) {
37352
37600
  assertAppId(app, "appConfigPath");
37353
37601
  for (const base of ["apps", "test-apps"]) {
37354
- const p = join32(root, base, app, file);
37602
+ const p = join33(root, base, app, file);
37355
37603
  try {
37356
- readFileSync38(p);
37604
+ readFileSync39(p);
37357
37605
  return p;
37358
37606
  } catch {
37359
37607
  }
37360
37608
  }
37361
- return join32(root, "apps", app, file);
37609
+ return join33(root, "apps", app, file);
37362
37610
  }
37363
37611
  function serializeRolloutYaml(cfg) {
37364
37612
  const lines = [`app: ${cfg.app}`, "rings:"];
@@ -37393,14 +37641,14 @@ function validateOverlayPatch(patch) {
37393
37641
  }
37394
37642
  function readJsonAt(app, file, root) {
37395
37643
  try {
37396
- return JSON.parse(readFileSync38(appConfigPath(app, file, root), "utf8"));
37644
+ return JSON.parse(readFileSync39(appConfigPath(app, file, root), "utf8"));
37397
37645
  } catch {
37398
37646
  return null;
37399
37647
  }
37400
37648
  }
37401
37649
  function writeJsonAt(app, file, root, value) {
37402
37650
  const path = appConfigPath(app, file, root);
37403
- mkdirSync15(dirname21(path), { recursive: true });
37651
+ mkdirSync15(dirname22(path), { recursive: true });
37404
37652
  writeFileSync12(path, JSON.stringify(value, null, 2) + "\n");
37405
37653
  return path;
37406
37654
  }
@@ -37423,14 +37671,14 @@ function saveLocalFlags(app, defs, root = REPO_ROOT6) {
37423
37671
  }
37424
37672
  function loadRollout(app) {
37425
37673
  try {
37426
- return parseRolloutYaml(readFileSync38(appConfigPath(app, "rollout.yaml"), "utf8"));
37674
+ return parseRolloutYaml(readFileSync39(appConfigPath(app, "rollout.yaml"), "utf8"));
37427
37675
  } catch {
37428
37676
  return null;
37429
37677
  }
37430
37678
  }
37431
37679
  function loadRevocations(app) {
37432
37680
  try {
37433
- return parseRevocationsYaml(readFileSync38(appConfigPath(app, "revocations.yaml"), "utf8")).revoked;
37681
+ return parseRevocationsYaml(readFileSync39(appConfigPath(app, "revocations.yaml"), "utf8")).revoked;
37434
37682
  } catch {
37435
37683
  return [];
37436
37684
  }
@@ -37440,7 +37688,7 @@ var init_config2 = __esm({
37440
37688
  "../../core/serve-control/src/config.ts"() {
37441
37689
  init_errors();
37442
37690
  init_contract();
37443
- REPO_ROOT6 = resolve27(dirname21(fileURLToPath16(import.meta.url)), "../../..");
37691
+ REPO_ROOT6 = resolve28(dirname22(fileURLToPath16(import.meta.url)), "../../..");
37444
37692
  OPERATIONAL_OVERLAY_KEYS = /* @__PURE__ */ new Set([
37445
37693
  "wixCachePolicy",
37446
37694
  "telemetryConfig",
@@ -37501,8 +37749,8 @@ var init_config2 = __esm({
37501
37749
  // ../../core/dev-server/src/index.ts
37502
37750
  import { createServer as createServer2 } from "node:http";
37503
37751
  import { readFile as readFile2, stat } from "node:fs/promises";
37504
- import { readFileSync as readFileSync39, existsSync as existsSync33, watch, mkdirSync as mkdirSync16, rmSync as rmSync4 } from "node:fs";
37505
- import { extname, resolve as resolve28, normalize as normalize3, sep as sep6, join as join33 } from "node:path";
37752
+ import { readFileSync as readFileSync40, existsSync as existsSync34, watch, mkdirSync as mkdirSync16, rmSync as rmSync4 } from "node:fs";
37753
+ import { extname, resolve as resolve29, normalize as normalize3, sep as sep6, join as join34 } from "node:path";
37506
37754
  import { createHash as createHash15, randomUUID } from "node:crypto";
37507
37755
  import { fileURLToPath as fileURLToPath17, pathToFileURL as pathToFileURL6 } from "node:url";
37508
37756
  function deepMerge2(base, patch) {
@@ -37551,8 +37799,8 @@ function makeEtag(body) {
37551
37799
  function readAppTokens(state) {
37552
37800
  if (!state.app) return null;
37553
37801
  try {
37554
- const tokenPath = state.app.module.designTokensPath ?? resolve28(state.app.dir, state.app.module.config.designTokensPath ?? "tokens.json");
37555
- const parsed = JSON.parse(readFileSync39(tokenPath, "utf8"));
37802
+ const tokenPath = state.app.module.designTokensPath ?? resolve29(state.app.dir, state.app.module.config.designTokensPath ?? "tokens.json");
37803
+ const parsed = JSON.parse(readFileSync40(tokenPath, "utf8"));
37556
37804
  delete parsed._meta;
37557
37805
  return parsed;
37558
37806
  } catch {
@@ -37564,7 +37812,7 @@ function effectiveAppConfigOverride(state) {
37564
37812
  if (state.app) {
37565
37813
  try {
37566
37814
  gitOverlay = JSON.parse(
37567
- readFileSync39(resolve28(state.app.dir, "app-config.overlay.json"), "utf8")
37815
+ readFileSync40(resolve29(state.app.dir, "app-config.overlay.json"), "utf8")
37568
37816
  );
37569
37817
  } catch {
37570
37818
  }
@@ -37620,8 +37868,8 @@ function handleL10nRequest(req, res, state) {
37620
37868
  if (!state.app || app !== state.app.name) {
37621
37869
  return sendJson(res, 404, { error: "unknown_app", app }, cors);
37622
37870
  }
37623
- const path = resolve28(state.app.dir, "l10n", `${tag}.json`);
37624
- if (!existsSync33(path)) {
37871
+ const path = resolve29(state.app.dir, "l10n", `${tag}.json`);
37872
+ if (!existsSync34(path)) {
37625
37873
  res.writeHead(204, cors);
37626
37874
  res.end();
37627
37875
  return;
@@ -37630,7 +37878,7 @@ function handleL10nRequest(req, res, state) {
37630
37878
  let messages;
37631
37879
  try {
37632
37880
  messages = Object.fromEntries(
37633
- keyMap(parseCatalog(readFileSync39(path, "utf8")), { sourceFallback: tag === sourceLocale })
37881
+ keyMap(parseCatalog(readFileSync40(path, "utf8")), { sourceFallback: tag === sourceLocale })
37634
37882
  );
37635
37883
  } catch {
37636
37884
  return sendJson(res, 500, { error: "catalog_unreadable", app, tag }, cors);
@@ -37677,8 +37925,8 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
37677
37925
  }
37678
37926
  function servedFontFaces(appDir2, tokens) {
37679
37927
  try {
37680
- const policyPath = join33(appDir2, "fonts.json");
37681
- const policy = existsSync33(policyPath) ? JSON.parse(readFileSync39(policyPath, "utf8")) : {};
37928
+ const policyPath = join34(appDir2, "fonts.json");
37929
+ const policy = existsSync34(policyPath) ? JSON.parse(readFileSync40(policyPath, "utf8")) : {};
37682
37930
  const plan = planFonts(tokens, policy);
37683
37931
  if (plan.families.length === 0) return void 0;
37684
37932
  return servedProjection(plan);
@@ -37693,7 +37941,7 @@ function fontFaceBytesBySha(_appDir, sha) {
37693
37941
  for (const face of family.faces) {
37694
37942
  if (face.sha256 !== sha) continue;
37695
37943
  const src = faceSource(face.file, face.sha256);
37696
- if (src) return readFileSync39(src);
37944
+ if (src) return readFileSync40(src);
37697
37945
  }
37698
37946
  }
37699
37947
  } catch {
@@ -38040,7 +38288,7 @@ async function buildDescriptorForScreenId(state, screenId, query = new URLSearch
38040
38288
  function bundleIntegritySync(name, dir) {
38041
38289
  for (const root of dir ? [dir, TIER5_ROOT] : [TIER5_ROOT]) {
38042
38290
  try {
38043
- const bytes2 = readFileSync39(resolve28(root, `${name}.bundle.js`));
38291
+ const bytes2 = readFileSync40(resolve29(root, `${name}.bundle.js`));
38044
38292
  return "sha256-" + createHash15("sha256").update(bytes2).digest("base64");
38045
38293
  } catch {
38046
38294
  }
@@ -38290,7 +38538,7 @@ function startBundleArtifactWatcher(state) {
38290
38538
  }
38291
38539
  }
38292
38540
  async function rebuildBundleSource(name) {
38293
- const lib = await import(pathToFileURL6(resolve28(TIER5_SRC_ROOT, "build-lib.mjs")).href);
38541
+ const lib = await import(pathToFileURL6(resolve29(TIER5_SRC_ROOT, "build-lib.mjs")).href);
38294
38542
  await lib.buildBundle(name);
38295
38543
  }
38296
38544
  function startBundleSourceWatcher(state) {
@@ -38345,11 +38593,11 @@ function applyTopBarDefault(descriptor, config) {
38345
38593
  async function reloadAppModule(state, opts) {
38346
38594
  const current = state.app;
38347
38595
  if (!current) return { ok: false, error: "no_app" };
38348
- const entry = resolve28(current.dir, "dev/index.ts");
38596
+ const entry = resolve29(current.dir, "dev/index.ts");
38349
38597
  mkdirSync16(HMR_TMP_DIR, { recursive: true });
38350
- const outfile = resolve28(HMR_TMP_DIR, `${current.name}-${++appReloadSeq}.mjs`);
38598
+ const outfile = resolve29(HMR_TMP_DIR, `${current.name}-${++appReloadSeq}.mjs`);
38351
38599
  try {
38352
- const lib = await import(pathToFileURL6(resolve28(TIER5_SRC_ROOT, "build-lib.mjs")).href);
38600
+ const lib = await import(pathToFileURL6(resolve29(TIER5_SRC_ROOT, "build-lib.mjs")).href);
38353
38601
  await lib.buildAppEntry(entry, outfile, { confineTo: current.dir });
38354
38602
  const mod = await import(pathToFileURL6(outfile).href);
38355
38603
  const next = mod.default;
@@ -38622,7 +38870,7 @@ async function handleWebWix(req, res, state) {
38622
38870
  }
38623
38871
  if (match && (match.rule.kind ?? "mock") === "mock") {
38624
38872
  const mocked = match.rule.response;
38625
- if (mocked.delayMs) await new Promise((resolve41) => setTimeout(resolve41, mocked.delayMs));
38873
+ if (mocked.delayMs) await new Promise((resolve42) => setTimeout(resolve42, mocked.delayMs));
38626
38874
  const responseBody = mocked.template ? renderTemplate(mocked.body, match.captures) : mocked.body;
38627
38875
  recordWixTraffic(state.wix, state.appName, state.adminUrl, {
38628
38876
  ts: started,
@@ -39113,7 +39361,7 @@ async function handleAdminPlayground(req, res, state) {
39113
39361
  const name = decodeURIComponent(sampleMatch[1]);
39114
39362
  if (!/^[a-zA-Z0-9._-]+$/.test(name)) return sendJson(res, 400, { error: "bad_name" });
39115
39363
  try {
39116
- const body = await readFile2(resolve28(SAMPLES_ROOT, name), "utf-8");
39364
+ const body = await readFile2(resolve29(SAMPLES_ROOT, name), "utf-8");
39117
39365
  return sendJson(res, 200, JSON.parse(body));
39118
39366
  } catch {
39119
39367
  return sendJson(res, 404, { error: "sample_not_found", name });
@@ -39383,9 +39631,9 @@ async function readBundleFile(name, dir) {
39383
39631
  if (!/^[A-Za-z0-9_.\-]+$/.test(name)) return null;
39384
39632
  const roots = dir ? [dir, TIER5_ROOT] : [TIER5_ROOT];
39385
39633
  const candidates = roots.flatMap((root) => [
39386
- resolve28(root, `${name}.bundle.js`),
39387
- resolve28(root, `${name}.js`),
39388
- resolve28(root, name)
39634
+ resolve29(root, `${name}.bundle.js`),
39635
+ resolve29(root, `${name}.js`),
39636
+ resolve29(root, name)
39389
39637
  ]);
39390
39638
  for (const path of candidates) {
39391
39639
  if (!roots.some((root) => path.startsWith(root))) continue;
@@ -39536,14 +39784,14 @@ async function handleWidgetClearPreview(res, state, widgetId) {
39536
39784
  }
39537
39785
  function readActionHandlerManifest(app) {
39538
39786
  if (!/^[A-Za-z0-9_.\-]+$/.test(app)) return { handlers: [] };
39539
- const manifestPath = resolve28(ACTION_HANDLERS_ROOT, `${app}.manifest.json`);
39540
- const bundlePath = resolve28(ACTION_HANDLERS_ROOT, `${app}.handlers.js`);
39787
+ const manifestPath = resolve29(ACTION_HANDLERS_ROOT, `${app}.manifest.json`);
39788
+ const bundlePath = resolve29(ACTION_HANDLERS_ROOT, `${app}.handlers.js`);
39541
39789
  if (!manifestPath.startsWith(ACTION_HANDLERS_ROOT) || !bundlePath.startsWith(ACTION_HANDLERS_ROOT)) return { handlers: [] };
39542
39790
  let decls;
39543
39791
  let bytes2;
39544
39792
  try {
39545
- decls = JSON.parse(readFileSync39(manifestPath, "utf8"));
39546
- bytes2 = readFileSync39(bundlePath);
39793
+ decls = JSON.parse(readFileSync40(manifestPath, "utf8"));
39794
+ bytes2 = readFileSync40(bundlePath);
39547
39795
  } catch {
39548
39796
  return { handlers: [] };
39549
39797
  }
@@ -39556,7 +39804,7 @@ function handleActionHandlersManifest(res, app) {
39556
39804
  }
39557
39805
  async function handleActionHandlerBundleGet(res, app) {
39558
39806
  if (!/^[A-Za-z0-9_.\-]+$/.test(app)) return sendJson(res, 404, { error: "not_found" });
39559
- const path = resolve28(ACTION_HANDLERS_ROOT, `${app}.handlers.js`);
39807
+ const path = resolve29(ACTION_HANDLERS_ROOT, `${app}.handlers.js`);
39560
39808
  if (!path.startsWith(ACTION_HANDLERS_ROOT)) return sendJson(res, 404, { error: "not_found" });
39561
39809
  try {
39562
39810
  const bytes2 = await readFile2(path);
@@ -39573,9 +39821,9 @@ async function handleActionHandlerBundleGet(res, app) {
39573
39821
  }
39574
39822
  async function serveStatic(req, res, root, prefix) {
39575
39823
  const requested = decodeURIComponent((req.url ?? "").split("?")[0]);
39576
- const relative7 = requested.replace(prefix, "").replace(/^\//, "");
39577
- const safe = normalize3(relative7).replace(/^(\.\.[\/\\])+/, "");
39578
- const fullPath = resolve28(root, safe);
39824
+ const relative8 = requested.replace(prefix, "").replace(/^\//, "");
39825
+ const safe = normalize3(relative8).replace(/^(\.\.[\/\\])+/, "");
39826
+ const fullPath = resolve29(root, safe);
39579
39827
  if (!fullPath.startsWith(root)) {
39580
39828
  return send(res, 403, "forbidden");
39581
39829
  }
@@ -39781,7 +40029,7 @@ function createDevServer(opts = {}) {
39781
40029
  };
39782
40030
  const netMocksDisabled = opts.netMocks === false;
39783
40031
  const explicitNetMocksPath = typeof opts.netMocks === "string" ? opts.netMocks : process.env.HIMALAYA_NET_MOCKS;
39784
- const netMocksPath = netMocksDisabled ? void 0 : explicitNetMocksPath ?? (state.app?.meta.devGateway?.enabled === false ? void 0 : state.app ? resolve28(state.app.dir, "dev/net-mocks.json") : void 0);
40032
+ const netMocksPath = netMocksDisabled ? void 0 : explicitNetMocksPath ?? (state.app?.meta.devGateway?.enabled === false ? void 0 : state.app ? resolve29(state.app.dir, "dev/net-mocks.json") : void 0);
39785
40033
  const resolved2 = netMocksDisabled ? null : resolveNetMocks(
39786
40034
  netMocksPath ? readNetMocksFile(netMocksPath) : null,
39787
40035
  state.app?.meta.devGateway
@@ -39841,7 +40089,7 @@ function createDevServer(opts = {}) {
39841
40089
  const q = new URLSearchParams(url.split("?")[1] ?? "");
39842
40090
  const assetApp = q.get("app") ?? state.appName;
39843
40091
  const assetDir = state.app && assetApp === state.app.name ? state.app.dir : resolveAppDir(assetApp);
39844
- const hit = assetDir ? collectAssets(assetDir).find((a) => assetSha256(readFileSync39(a.path)) === sha) : void 0;
40092
+ const hit = assetDir ? collectAssets(assetDir).find((a) => assetSha256(readFileSync40(a.path)) === sha) : void 0;
39845
40093
  if (!hit && assetDir) {
39846
40094
  const fontBytes = fontFaceBytesBySha(assetDir, sha);
39847
40095
  if (fontBytes) {
@@ -39864,7 +40112,7 @@ function createDevServer(opts = {}) {
39864
40112
  res.end(JSON.stringify({ error: "asset_not_found", app: assetApp, sha }));
39865
40113
  return;
39866
40114
  }
39867
- const bytes2 = readFileSync39(hit.path);
40115
+ const bytes2 = readFileSync40(hit.path);
39868
40116
  res.writeHead(200, {
39869
40117
  "content-type": hit.contentType,
39870
40118
  "content-length": String(bytes2.length),
@@ -39950,8 +40198,8 @@ function createDevServer(opts = {}) {
39950
40198
  }
39951
40199
  if (url === "/v1/schema/screen-descriptor.json" && req.method === "GET") {
39952
40200
  try {
39953
- const schemaPath = resolve28(ROOT6, "core/schema/generated/screen-descriptor.schema.json");
39954
- const body = readFileSync39(schemaPath, "utf8");
40201
+ const schemaPath = resolve29(ROOT6, "core/schema/generated/screen-descriptor.schema.json");
40202
+ const body = readFileSync40(schemaPath, "utf8");
39955
40203
  return send(res, 200, body, {
39956
40204
  "content-type": "application/schema+json; charset=utf-8",
39957
40205
  "cache-control": "no-store",
@@ -39985,7 +40233,7 @@ function createDevServer(opts = {}) {
39985
40233
  try {
39986
40234
  const { parseRevocationsYaml: parseRevocationsYaml2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
39987
40235
  revoked = parseRevocationsYaml2(
39988
- readFileSync39(resolve28(state.app.dir, "revocations.yaml"), "utf8")
40236
+ readFileSync40(resolve29(state.app.dir, "revocations.yaml"), "utf8")
39989
40237
  ).revoked;
39990
40238
  } catch {
39991
40239
  }
@@ -40217,18 +40465,18 @@ var init_src6 = __esm({
40217
40465
  init_src5();
40218
40466
  init_kits();
40219
40467
  init_widgets();
40220
- ROOT6 = resolve28(fileURLToPath17(new URL("../../..", import.meta.url)));
40221
- TIER3_ROOT = resolve28(ROOT6, "core/dev-server/web");
40222
- TIER4_ROOT = resolve28(ROOT6, "core/dev-server/tier4-bundles");
40468
+ ROOT6 = resolve29(fileURLToPath17(new URL("../../..", import.meta.url)));
40469
+ TIER3_ROOT = resolve29(ROOT6, "core/dev-server/web");
40470
+ TIER4_ROOT = resolve29(ROOT6, "core/dev-server/tier4-bundles");
40223
40471
  tier4Verifier = new TierFourVerifier(TIER4_ROOT);
40224
- TIER5_ROOT = resolve28(ROOT6, "core/dev-server/tier5-bundles");
40225
- ACTION_HANDLERS_ROOT = resolve28(ROOT6, "core/dev-server/action-handlers");
40226
- SAMPLES_ROOT = resolve28(ROOT6, "core/dev-server/sdui-samples");
40472
+ TIER5_ROOT = resolve29(ROOT6, "core/dev-server/tier5-bundles");
40473
+ ACTION_HANDLERS_ROOT = resolve29(ROOT6, "core/dev-server/action-handlers");
40474
+ SAMPLES_ROOT = resolve29(ROOT6, "core/dev-server/sdui-samples");
40227
40475
  TELEMETRY_BUFFER_CAP = 1e3;
40228
40476
  overrideHistorySeq = 0;
40229
40477
  CACHE_CONTROL = "max-age=600, stale-while-revalidate=86400";
40230
- TIER5_SRC_ROOT = resolve28(ROOT6, "core/dev-server/tier5-bundles-src");
40231
- HMR_TMP_DIR = resolve28(ROOT6, "node_modules/.cache/himalaya-hmr");
40478
+ TIER5_SRC_ROOT = resolve29(ROOT6, "core/dev-server/tier5-bundles-src");
40479
+ HMR_TMP_DIR = resolve29(ROOT6, "node_modules/.cache/himalaya-hmr");
40232
40480
  appReloadSeq = 0;
40233
40481
  WEBWIX_CLIENT_ID = process.env.DEMO_APP_WIX_CLIENT_ID ?? "4f58d461-c1f5-4a5d-a467-62cc2329e4dd";
40234
40482
  WEBWIX_REDIRECT = "himalaya-demo-app://oauth/callback";
@@ -40593,12 +40841,12 @@ var init_query = __esm({
40593
40841
  });
40594
40842
 
40595
40843
  // ../../core/dev-server/src/crawl-input.ts
40596
- function resolved(v, resolve41) {
40597
- if (typeof v === "string") return v.startsWith("$service:") ? resolve41(v) : v;
40844
+ function resolved(v, resolve42) {
40845
+ if (typeof v === "string") return v.startsWith("$service:") ? resolve42(v) : v;
40598
40846
  if (Array.isArray(v)) {
40599
40847
  const out = v.flatMap((el) => {
40600
40848
  if (typeof el !== "string" || !el.startsWith("$service:")) return [el];
40601
- const r = resolve41(el);
40849
+ const r = resolve42(el);
40602
40850
  return Array.isArray(r) ? r : [r];
40603
40851
  });
40604
40852
  return out;
@@ -40612,9 +40860,9 @@ function hasRowBinding(props) {
40612
40860
  return itemToken(v) || Array.isArray(v) && v.some(itemToken);
40613
40861
  });
40614
40862
  }
40615
- function declaredOptionIds(props, resolve41) {
40863
+ function declaredOptionIds(props, resolve42) {
40616
40864
  const literal = props.options ?? props.tabs ?? props.chips;
40617
- const bound = props.optionsPath !== void 0 ? resolved(props.optionsPath, resolve41) : void 0;
40865
+ const bound = props.optionsPath !== void 0 ? resolved(props.optionsPath, resolve42) : void 0;
40618
40866
  const raw = Array.isArray(bound) ? bound : literal;
40619
40867
  if (!Array.isArray(raw)) return [];
40620
40868
  const out = [];
@@ -40657,24 +40905,24 @@ function seedTokens(state, limit = 48) {
40657
40905
  walk2(state, 0);
40658
40906
  return out;
40659
40907
  }
40660
- function synthesizeInput(control, resolve41, tokens) {
40908
+ function synthesizeInput(control, resolve42, tokens) {
40661
40909
  const id = `${control.type}.${control.callback}`;
40662
40910
  const row = SYNTHESIZED_CONTROLS[id];
40663
40911
  if (!row) return null;
40664
40912
  const props = control.props;
40665
40913
  if (hasRowBinding(props)) return null;
40666
40914
  if (row.from === "declared-options") {
40667
- const ids = declaredOptionIds(props, resolve41);
40915
+ const ids = declaredOptionIds(props, resolve42);
40668
40916
  if (!ids.length) return null;
40669
40917
  if (row.encoding === "string-array") {
40670
- const currentRaw2 = resolved(props.selectedIds, resolve41);
40918
+ const currentRaw2 = resolved(props.selectedIds, resolve42);
40671
40919
  const current3 = (Array.isArray(currentRaw2) ? currentRaw2 : []).filter((x) => typeof x === "string");
40672
40920
  const pick4 = ids.find((o) => !current3.includes(o));
40673
40921
  if (pick4 === void 0) return null;
40674
40922
  const value = props.multiSelect === true ? [...current3, pick4] : [pick4];
40675
40923
  return { key: row.key, value, from: row.from, current: current3, control: id };
40676
40924
  }
40677
- const currentRaw = resolved(props.selectedId, resolve41);
40925
+ const currentRaw = resolved(props.selectedId, resolve42);
40678
40926
  const current2 = typeof currentRaw === "string" ? currentRaw : void 0;
40679
40927
  const pick3 = ids.find((o) => o !== current2);
40680
40928
  if (pick3 === void 0) return null;
@@ -40682,7 +40930,7 @@ function synthesizeInput(control, resolve41, tokens) {
40682
40930
  }
40683
40931
  if (!acceptsWords(props) || !acceptsWords(control.wrapper ?? {})) return null;
40684
40932
  const binding2 = typeof props.valuePath === "string" ? props.valuePath : typeof props.value === "string" && props.value.startsWith("$service:") ? props.value : void 0;
40685
- const bound = binding2 === void 0 ? void 0 : resolve41(binding2);
40933
+ const bound = binding2 === void 0 ? void 0 : resolve42(binding2);
40686
40934
  const literal = typeof props.value === "string" && !props.value.startsWith("$") ? props.value : void 0;
40687
40935
  const current = typeof bound === "string" ? bound : typeof bound === "number" ? String(bound) : literal ?? "";
40688
40936
  const max = typeof props.maxLength === "number" && props.maxLength > 0 ? props.maxLength : void 0;
@@ -42899,8 +43147,8 @@ __export(build_exports2, {
42899
43147
  withStoreNavBundle: () => withStoreNavBundle
42900
43148
  });
42901
43149
  import { createHash as createHash18 } from "node:crypto";
42902
- import { readFileSync as readFileSync40, readdirSync as readdirSync18, existsSync as existsSync34 } from "node:fs";
42903
- import { dirname as dirname22, join as join34, resolve as resolve29 } from "node:path";
43150
+ import { readFileSync as readFileSync41, readdirSync as readdirSync18, existsSync as existsSync35 } from "node:fs";
43151
+ import { dirname as dirname23, join as join35, resolve as resolve30 } from "node:path";
42904
43152
  import { fileURLToPath as fileURLToPath18 } from "node:url";
42905
43153
  import { createRequire as createRequire2 } from "node:module";
42906
43154
  import { execFileSync as execFileSync4 } from "node:child_process";
@@ -42922,7 +43170,7 @@ function bundleMapFor(tier5Dir, bundleName) {
42922
43170
  if (!bundleName) return { bundleMap: null };
42923
43171
  const built = readBuiltBundleMap(tier5Dir, bundleName);
42924
43172
  if (built) return { bundleMap: built };
42925
- if (resolve29(tier5Dir) !== TIER5_DIR) return { bundleMap: null };
43173
+ if (resolve30(tier5Dir) !== TIER5_DIR) return { bundleMap: null };
42926
43174
  let r;
42927
43175
  try {
42928
43176
  const { sourcemapForCommittedBundleSync } = requireFromHere(SOURCEMAP_ON_DEMAND_PATH);
@@ -42938,10 +43186,10 @@ function l10nLintOptions(locale, appDir2) {
42938
43186
  const tags = (locale.translations ?? []).filter((t) => t !== locale.default);
42939
43187
  const catalog = /* @__PURE__ */ new Set();
42940
43188
  for (const tag of tags) {
42941
- const path = join34(appDir2, "l10n", `${tag}.json`);
42942
- if (!existsSync34(path)) continue;
43189
+ const path = join35(appDir2, "l10n", `${tag}.json`);
43190
+ if (!existsSync35(path)) continue;
42943
43191
  try {
42944
- const parsed = JSON.parse(readFileSync40(path, "utf8"));
43192
+ const parsed = JSON.parse(readFileSync41(path, "utf8"));
42945
43193
  for (const m of Object.values(parsed.messages ?? {})) {
42946
43194
  if (typeof m?.source === "string" && typeof m.translation === "string" && m.translation.length > 0) {
42947
43195
  catalog.add(m.source);
@@ -43106,7 +43354,7 @@ function shellSha(root = REPO_ROOT7) {
43106
43354
  function isMonorepoCheckout(dir) {
43107
43355
  try {
43108
43356
  const top = execFileSync4("git", ["rev-parse", "--show-toplevel"], { cwd: dir, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
43109
- return top.length > 0 && existsSync34(join34(top, "core", "schema", "core.proto"));
43357
+ return top.length > 0 && existsSync35(join35(top, "core", "schema", "core.proto"));
43110
43358
  } catch {
43111
43359
  return false;
43112
43360
  }
@@ -43239,8 +43487,8 @@ async function buildReleaseFromContent(contentDir2, dest, opts = {}) {
43239
43487
  }
43240
43488
  function readTokensForLint(preloaded) {
43241
43489
  try {
43242
- const tokenPath = preloaded.module.designTokensPath ?? resolve29(preloaded.dir, preloaded.module.config.designTokensPath ?? "tokens.json");
43243
- const parsed = JSON.parse(readFileSync40(tokenPath, "utf8"));
43490
+ const tokenPath = preloaded.module.designTokensPath ?? resolve30(preloaded.dir, preloaded.module.config.designTokensPath ?? "tokens.json");
43491
+ const parsed = JSON.parse(readFileSync41(tokenPath, "utf8"));
43244
43492
  delete parsed._meta;
43245
43493
  return parsed;
43246
43494
  } catch {
@@ -43377,7 +43625,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
43377
43625
  if (addrCache.has(name)) return addrCache.get(name);
43378
43626
  let bytes2;
43379
43627
  try {
43380
- bytes2 = readFileSync40(join34(tier5Dir, `${name}.bundle.js`));
43628
+ bytes2 = readFileSync41(join35(tier5Dir, `${name}.bundle.js`));
43381
43629
  } catch {
43382
43630
  return null;
43383
43631
  }
@@ -43398,9 +43646,9 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
43398
43646
  const MAX_AUTHORING_DOC_BYTES = 128 * 1024;
43399
43647
  if (store.putDoc) {
43400
43648
  for (const [name, file] of [["spec", "SPEC.md"], ["mobile-ux", "MOBILE-UX.md"]]) {
43401
- const path = join34(preloaded.dir, file);
43402
- if (!existsSync34(path)) continue;
43403
- const bytes2 = readFileSync40(path);
43649
+ const path = join35(preloaded.dir, file);
43650
+ if (!existsSync35(path)) continue;
43651
+ const bytes2 = readFileSync41(path);
43404
43652
  if (bytes2.length === 0 || bytes2.length > MAX_AUTHORING_DOC_BYTES) {
43405
43653
  throw new Error(`${file} must be non-empty and at most ${MAX_AUTHORING_DOC_BYTES} bytes to ship with a release.`);
43406
43654
  }
@@ -43415,7 +43663,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
43415
43663
  const iconPath = resolveAppDirIcon(preloaded.dir);
43416
43664
  if (iconPath) {
43417
43665
  try {
43418
- const bytes2 = readFileSync40(iconPath);
43666
+ const bytes2 = readFileSync41(iconPath);
43419
43667
  await store.putIcon(app, bytes2);
43420
43668
  iconHash = sha2564(bytes2);
43421
43669
  } catch {
@@ -43437,7 +43685,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
43437
43685
  }
43438
43686
  const seenAssetSha = /* @__PURE__ */ new Set();
43439
43687
  for (const a of collected) {
43440
- const bytes2 = readFileSync40(a.path);
43688
+ const bytes2 = readFileSync41(a.path);
43441
43689
  const hash = sha2564(bytes2);
43442
43690
  if (!seenAssetSha.has(hash)) {
43443
43691
  await store.putAsset(app, hash, bytes2, a.contentType);
@@ -43458,7 +43706,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
43458
43706
  const canEmitFonts = hasVendoredFloor();
43459
43707
  try {
43460
43708
  const appTokens = readTokensForLint(preloaded) ?? {};
43461
- const policy = existsSync34(join34(preloaded.dir, "fonts.json")) ? JSON.parse(readFileSync40(join34(preloaded.dir, "fonts.json"), "utf8")) : {};
43709
+ const policy = existsSync35(join35(preloaded.dir, "fonts.json")) ? JSON.parse(readFileSync41(join35(preloaded.dir, "fonts.json"), "utf8")) : {};
43462
43710
  fontPlan = planFonts(appTokens, policy);
43463
43711
  } catch (err) {
43464
43712
  throw new Error(
@@ -43474,7 +43722,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
43474
43722
  `[release-build] ${family.name} face ${face.postScriptName} has no local bytes (sha ${face.sha256.slice(0, 12)}\u2026), so the release would declare a face the asset route cannot serve. Run \`npm run fonts:catalog\` to warm the cache before publishing.`
43475
43723
  );
43476
43724
  }
43477
- const bytes2 = readFileSync40(src);
43725
+ const bytes2 = readFileSync41(src);
43478
43726
  const hash = sha2564(bytes2);
43479
43727
  if (hash !== face.sha256) {
43480
43728
  throw new Error(
@@ -43498,7 +43746,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
43498
43746
  if (store.putAsset) {
43499
43747
  for (const face of collectBrandFonts(preloaded.dir)) {
43500
43748
  try {
43501
- const bytes2 = readFileSync40(face.path);
43749
+ const bytes2 = readFileSync41(face.path);
43502
43750
  if (bytes2.length === 0) continue;
43503
43751
  const hash = sha2564(bytes2);
43504
43752
  await store.putAsset(app, hash, bytes2, "font/ttf");
@@ -43920,10 +44168,10 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
43920
44168
  await store.reset(app);
43921
44169
  const sources = {};
43922
44170
  for (const definition of config.functions.functions) {
43923
- const candidates = [".ts", ".js", ".mts", ".mjs"].map((extension) => join34(preloaded.dir, "functions", `${definition.name}${extension}`));
43924
- const source = candidates.find((candidate) => existsSync34(candidate));
44171
+ const candidates = [".ts", ".js", ".mts", ".mjs"].map((extension) => join35(preloaded.dir, "functions", `${definition.name}${extension}`));
44172
+ const source = candidates.find((candidate) => existsSync35(candidate));
43925
44173
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
43926
- sources[definition.name] = readFileSync40(source, "utf8");
44174
+ sources[definition.name] = readFileSync41(source, "utf8");
43927
44175
  }
43928
44176
  const built = await buildFunctions(config.functions.functions, sources);
43929
44177
  const bundles = {};
@@ -43951,7 +44199,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
43951
44199
  return { manifest, failedToBuild: [], missingBundles: [], auth: {} };
43952
44200
  }
43953
44201
  function listBuildableApps() {
43954
- const appsDir = join34(REPO_ROOT7, "apps");
44202
+ const appsDir = join35(REPO_ROOT7, "apps");
43955
44203
  return readdirSync18(appsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("_")).map((d) => d.name);
43956
44204
  }
43957
44205
  var BAKE_OFFLINE_BASE, KNOWN_ICON_NAMES, KNOWN_ICONS, REPO_ROOT7, TIER5_DIR, requireFromHere, SOURCEMAP_ON_DEMAND_PATH, BAKED_SERVER_BASE_URL;
@@ -43982,10 +44230,10 @@ var init_build3 = __esm({
43982
44230
  BAKE_OFFLINE_BASE = "http://himalaya-bake.invalid";
43983
44231
  KNOWN_ICON_NAMES = [...ICON_NAMES].sort();
43984
44232
  KNOWN_ICONS = new Set(KNOWN_ICON_NAMES);
43985
- REPO_ROOT7 = resolve29(dirname22(fileURLToPath18(import.meta.url)), "../..");
43986
- TIER5_DIR = join34(REPO_ROOT7, "core/dev-server/tier5-bundles");
44233
+ REPO_ROOT7 = resolve30(dirname23(fileURLToPath18(import.meta.url)), "../..");
44234
+ TIER5_DIR = join35(REPO_ROOT7, "core/dev-server/tier5-bundles");
43987
44235
  requireFromHere = createRequire2(import.meta.url);
43988
- SOURCEMAP_ON_DEMAND_PATH = join34(REPO_ROOT7, "core/dev-server/tier5-bundles-src/sourcemap-on-demand.mjs");
44236
+ SOURCEMAP_ON_DEMAND_PATH = join35(REPO_ROOT7, "core/dev-server/tier5-bundles-src/sourcemap-on-demand.mjs");
43989
44237
  BAKED_SERVER_BASE_URL = "http://baked.himalaya.invalid";
43990
44238
  }
43991
44239
  });
@@ -44162,18 +44410,18 @@ __export(captcha_exports, {
44162
44410
  captchaContentInput: () => captchaContentInput,
44163
44411
  runCaptcha: () => runCaptcha
44164
44412
  });
44165
- import { readFileSync as readFileSync41 } from "node:fs";
44166
- import { resolve as resolve30 } from "node:path";
44413
+ import { readFileSync as readFileSync42 } from "node:fs";
44414
+ import { resolve as resolve31 } from "node:path";
44167
44415
  function readObject(path) {
44168
44416
  try {
44169
- return JSON.parse(readFileSync41(path, "utf8"));
44417
+ return JSON.parse(readFileSync42(path, "utf8"));
44170
44418
  } catch {
44171
44419
  return {};
44172
44420
  }
44173
44421
  }
44174
44422
  function captchaContentInput(dir) {
44175
- const content = readObject(resolve30(dir, "himalaya.content.json"));
44176
- const provisioning = readObject(resolve30(dir, "provisioning.json"));
44423
+ const content = readObject(resolve31(dir, "himalaya.content.json"));
44424
+ const provisioning = readObject(resolve31(dir, "provisioning.json"));
44177
44425
  return {
44178
44426
  app: text(content.name),
44179
44427
  siteId: text(provisioning.siteId),
@@ -44825,8 +45073,8 @@ __export(pack_exports, {
44825
45073
  });
44826
45074
  import { createHash as createHash19 } from "node:crypto";
44827
45075
  import { deflateRawSync, inflateRawSync } from "node:zlib";
44828
- import { existsSync as existsSync35, mkdirSync as mkdirSync17, readFileSync as readFileSync42, readdirSync as readdirSync19, rmSync as rmSync5, statSync as statSync8, writeFileSync as writeFileSync13 } from "node:fs";
44829
- import { dirname as dirname23, join as join35, resolve as resolve31 } from "node:path";
45076
+ import { existsSync as existsSync36, mkdirSync as mkdirSync17, readFileSync as readFileSync43, readdirSync as readdirSync19, rmSync as rmSync5, statSync as statSync7, writeFileSync as writeFileSync13 } from "node:fs";
45077
+ import { dirname as dirname24, join as join36, resolve as resolve32 } from "node:path";
44830
45078
  function writeZip(entries) {
44831
45079
  const sorted = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
44832
45080
  const total = sorted.reduce((n, e) => n + e.bytes.length, 0);
@@ -44920,13 +45168,13 @@ function inflateEntry(buf, entry) {
44920
45168
  return bytes2;
44921
45169
  }
44922
45170
  function readIf(path) {
44923
- return existsSync35(path) && statSync8(path).isFile() ? readFileSync42(path) : null;
45171
+ return existsSync36(path) && statSync7(path).isFile() ? readFileSync43(path) : null;
44924
45172
  }
44925
45173
  function listDir(dir) {
44926
- return existsSync35(dir) ? readdirSync19(dir).filter((f) => statSync8(join35(dir, f)).isFile()) : [];
45174
+ return existsSync36(dir) ? readdirSync19(dir).filter((f) => statSync7(join36(dir, f)).isFile()) : [];
44927
45175
  }
44928
45176
  function gatherReleaseEntries(appDir2) {
44929
- const manifestPath = join35(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
45177
+ const manifestPath = join36(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
44930
45178
  const manifestBytes = readIf(manifestPath);
44931
45179
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
44932
45180
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -44950,7 +45198,7 @@ function gatherReleaseEntries(appDir2) {
44950
45198
  }
44951
45199
  const missing = [];
44952
45200
  for (const rel of wanted) {
44953
- const bytes2 = readIf(join35(appDir2, rel));
45201
+ const bytes2 = readIf(join36(appDir2, rel));
44954
45202
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
44955
45203
  else missing.push(rel);
44956
45204
  }
@@ -44961,12 +45209,12 @@ function gatherReleaseEntries(appDir2) {
44961
45209
  );
44962
45210
  }
44963
45211
  for (const name of SINGLETONS) {
44964
- const bytes2 = readIf(join35(appDir2, name));
45212
+ const bytes2 = readIf(join36(appDir2, name));
44965
45213
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
44966
45214
  }
44967
45215
  const skipped = [];
44968
45216
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
44969
- for (const f of listDir(join35(appDir2, sub))) {
45217
+ for (const f of listDir(join36(appDir2, sub))) {
44970
45218
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
44971
45219
  }
44972
45220
  }
@@ -44999,18 +45247,18 @@ function buildEntries(appDir2, app, opts) {
44999
45247
  return { all, header, manifest, skipped };
45000
45248
  }
45001
45249
  function packRelease(releaseRoot, app, out, opts = {}) {
45002
- const appDir2 = join35(resolve31(releaseRoot), app);
45250
+ const appDir2 = join36(resolve32(releaseRoot), app);
45003
45251
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
45004
45252
  const buf = writeZip(all);
45005
- const isDir = existsSync35(out) && statSync8(out).isDirectory();
45006
- const outFile = isDir ? join35(resolve31(out), packageFileName(app, header.releaseId)) : resolve31(out);
45007
- mkdirSync17(dirname23(outFile), { recursive: true });
45253
+ const isDir = existsSync36(out) && statSync7(out).isDirectory();
45254
+ const outFile = isDir ? join36(resolve32(out), packageFileName(app, header.releaseId)) : resolve32(out);
45255
+ mkdirSync17(dirname24(outFile), { recursive: true });
45008
45256
  writeFileSync13(outFile, buf);
45009
45257
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
45010
45258
  }
45011
45259
  function resolvePackageDirDest(outDir, app, releaseId) {
45012
45260
  const packagedApp = (dir) => {
45013
- const bytes2 = readIf(join35(dir, HIMI_PACKAGE_HEADER_PATH));
45261
+ const bytes2 = readIf(join36(dir, HIMI_PACKAGE_HEADER_PATH));
45014
45262
  if (!bytes2) return void 0;
45015
45263
  try {
45016
45264
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -45019,15 +45267,15 @@ function resolvePackageDirDest(outDir, app, releaseId) {
45019
45267
  }
45020
45268
  };
45021
45269
  const replaceable = (dir) => {
45022
- if (!existsSync35(dir)) return true;
45023
- if (!statSync8(dir).isDirectory()) return false;
45270
+ if (!existsSync36(dir)) return true;
45271
+ if (!statSync7(dir).isDirectory()) return false;
45024
45272
  return readdirSync19(dir).length === 0 || packagedApp(dir) === app;
45025
45273
  };
45026
- if (existsSync35(outDir) && !statSync8(outDir).isDirectory()) {
45274
+ if (existsSync36(outDir) && !statSync7(outDir).isDirectory()) {
45027
45275
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
45028
45276
  }
45029
45277
  if (replaceable(outDir)) return outDir;
45030
- const child = join35(outDir, packageDirName(app, releaseId));
45278
+ const child = join36(outDir, packageDirName(app, releaseId));
45031
45279
  if (!replaceable(child)) {
45032
45280
  throw new Error(
45033
45281
  `refusing to replace ${child}: it is not empty and is not a ${app} package (no ${HIMI_PACKAGE_HEADER_PATH}). Pass --out to a new or empty directory.`
@@ -45041,14 +45289,14 @@ function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
45041
45289
  "a package carrying the app's source has no --format dir form: readers that predate the source entry drop it before hashing a directory and refuse the package as tampered. Pack it as a .himi archive."
45042
45290
  );
45043
45291
  }
45044
- const appDir2 = join35(resolve31(releaseRoot), app);
45292
+ const appDir2 = join36(resolve32(releaseRoot), app);
45045
45293
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
45046
- const dest = resolvePackageDirDest(resolve31(outDir), app, header.releaseId);
45294
+ const dest = resolvePackageDirDest(resolve32(outDir), app, header.releaseId);
45047
45295
  rmSync5(dest, { recursive: true, force: true });
45048
45296
  let bytes2 = 0;
45049
45297
  for (const entry of all) {
45050
- const target = join35(dest, entry.path);
45051
- mkdirSync17(dirname23(target), { recursive: true });
45298
+ const target = join36(dest, entry.path);
45299
+ mkdirSync17(dirname24(target), { recursive: true });
45052
45300
  writeFileSync13(target, entry.bytes);
45053
45301
  bytes2 += entry.bytes.length;
45054
45302
  }
@@ -45070,21 +45318,21 @@ function openPackageBuffer(buf) {
45070
45318
  return openFromEntries(readZip(buf), { requireHeader: true });
45071
45319
  }
45072
45320
  function openPackageFile(file) {
45073
- return openPackageBuffer(readFileSync42(file));
45321
+ return openPackageBuffer(readFileSync43(file));
45074
45322
  }
45075
45323
  function openPackageDir(dir) {
45076
- const root = resolve31(dir);
45324
+ const root = resolve32(dir);
45077
45325
  const entries = /* @__PURE__ */ new Map();
45078
45326
  const walk2 = (rel) => {
45079
- const abs = rel ? join35(root, rel) : root;
45327
+ const abs = rel ? join36(root, rel) : root;
45080
45328
  for (const name of readdirSync19(abs)) {
45081
45329
  const childRel = rel ? `${rel}/${name}` : name;
45082
- const childAbs = join35(abs, name);
45083
- if (statSync8(childAbs).isDirectory()) walk2(childRel);
45084
- else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync42(childAbs));
45330
+ const childAbs = join36(abs, name);
45331
+ if (statSync7(childAbs).isDirectory()) walk2(childRel);
45332
+ else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync43(childAbs));
45085
45333
  }
45086
45334
  };
45087
- if (!existsSync35(root)) throw new Error(`no such package directory: ${root}`);
45335
+ if (!existsSync36(root)) throw new Error(`no such package directory: ${root}`);
45088
45336
  walk2("");
45089
45337
  const manifestBytes = entries.get(HIMI_PACKAGE_MANIFEST_PATH);
45090
45338
  if (manifestBytes) {
@@ -45125,12 +45373,12 @@ function unpackTo(buf, dest, appFallback) {
45125
45373
  const app = header.app ?? manifest?.app ?? appFallback;
45126
45374
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
45127
45375
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
45128
- const root = resolve31(dest);
45129
- const appDir2 = join35(root, app);
45376
+ const root = resolve32(dest);
45377
+ const appDir2 = join36(root, app);
45130
45378
  rmSync5(appDir2, { recursive: true, force: true });
45131
45379
  for (const [rel, bytes2] of entries) {
45132
- const target = join35(appDir2, rel);
45133
- mkdirSync17(dirname23(target), { recursive: true });
45380
+ const target = join36(appDir2, rel);
45381
+ mkdirSync17(dirname24(target), { recursive: true });
45134
45382
  writeFileSync13(target, bytes2);
45135
45383
  }
45136
45384
  return { root, appDir: appDir2, app };
@@ -45164,19 +45412,19 @@ __export(app_source_exports, {
45164
45412
  writeConflicts: () => writeConflicts,
45165
45413
  zipAppSource: () => zipAppSource
45166
45414
  });
45167
- import { readdirSync as readdirSync20, lstatSync as lstatSync4, readFileSync as readFileSync43, mkdirSync as mkdirSync18, writeFileSync as writeFileSync14, existsSync as existsSync36, rmSync as rmSync6, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
45168
- import { join as join36, resolve as resolve32, sep as sep7 } from "node:path";
45415
+ import { readdirSync as readdirSync20, lstatSync as lstatSync4, readFileSync as readFileSync44, mkdirSync as mkdirSync18, writeFileSync as writeFileSync14, existsSync as existsSync37, rmSync as rmSync6, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
45416
+ import { join as join37, resolve as resolve33, sep as sep7 } from "node:path";
45169
45417
  function walkSourceTree(dir, file, skip) {
45170
45418
  const walk2 = (relDir) => {
45171
- for (const name of readdirSync20(join36(dir, relDir))) {
45172
- const rel = relDir ? join36(relDir, name) : name;
45173
- const st = lstatSync4(join36(dir, rel));
45419
+ for (const name of readdirSync20(join37(dir, relDir))) {
45420
+ const rel = relDir ? join37(relDir, name) : name;
45421
+ const st = lstatSync4(join37(dir, rel));
45174
45422
  if (st.isSymbolicLink()) {
45175
45423
  skip(rel);
45176
45424
  continue;
45177
45425
  }
45178
45426
  if (st.isDirectory()) {
45179
- if (isDeniedSourceDir(name) || existsSync36(join36(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
45427
+ if (isDeniedSourceDir(name) || existsSync37(join37(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
45180
45428
  else walk2(rel);
45181
45429
  continue;
45182
45430
  }
@@ -45194,7 +45442,7 @@ function collectAppSource(dir) {
45194
45442
  const skipped = [];
45195
45443
  walkSourceTree(
45196
45444
  dir,
45197
- (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync43(join36(dir, hostRel)) }),
45445
+ (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync44(join37(dir, hostRel)) }),
45198
45446
  (hostRel) => skipped.push(hostRel)
45199
45447
  );
45200
45448
  entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -45224,7 +45472,7 @@ function pathKind(p) {
45224
45472
  }
45225
45473
  }
45226
45474
  function writeAppSource(entries, destDir) {
45227
- const root = resolve32(destDir);
45475
+ const root = resolve33(destDir);
45228
45476
  if (pathKind(root)?.isSymbolicLink()) {
45229
45477
  throw new Error(
45230
45478
  `refusing to restore into ${root}: the destination itself is a symlink, so every file would be written outside it. Remove it, or pass --out/--name somewhere else.`
@@ -45237,13 +45485,13 @@ function writeAppSource(entries, destDir) {
45237
45485
  const segments = entry.path.split("/");
45238
45486
  let dir = root;
45239
45487
  for (const segment of segments.slice(0, -1)) {
45240
- dir = join36(dir, segment);
45488
+ dir = join37(dir, segment);
45241
45489
  const kind = pathKind(dir);
45242
45490
  if (kind?.isSymbolicLink()) unlinkSync3(dir);
45243
45491
  else if (kind) continue;
45244
45492
  mkdirSync18(dir);
45245
45493
  }
45246
- const target = join36(root, ...segments);
45494
+ const target = join37(root, ...segments);
45247
45495
  if (pathKind(target)?.isSymbolicLink()) unlinkSync3(target);
45248
45496
  writeFileSync14(target, entry.bytes);
45249
45497
  written.push(entry.path);
@@ -45251,7 +45499,7 @@ function writeAppSource(entries, destDir) {
45251
45499
  return written;
45252
45500
  }
45253
45501
  function staleSourcePaths(entries, destDir) {
45254
- if (!existsSync36(destDir)) return [];
45502
+ if (!existsSync37(destDir)) return [];
45255
45503
  const archived = new Set(entries.map((e) => e.path));
45256
45504
  const stale = [];
45257
45505
  walkSourceTree(destDir, (archivePath) => {
@@ -45264,7 +45512,7 @@ function deniedArchivePaths(entries) {
45264
45512
  return entries.filter((e) => isDeniedSourcePath(e.path)).map((e) => e.path).sort();
45265
45513
  }
45266
45514
  function writeConflicts(entries, destDir) {
45267
- const root = resolve32(destDir);
45515
+ const root = resolve33(destDir);
45268
45516
  const conflicts = [];
45269
45517
  const seen = /* @__PURE__ */ new Set();
45270
45518
  const say = (rel, what) => {
@@ -45278,7 +45526,7 @@ function writeConflicts(entries, destDir) {
45278
45526
  let dir = root;
45279
45527
  let stop = false;
45280
45528
  for (const [i, segment] of segments.slice(0, -1).entries()) {
45281
- dir = join36(dir, segment);
45529
+ dir = join37(dir, segment);
45282
45530
  const kind2 = pathKind(dir);
45283
45531
  if (!kind2 || kind2.isSymbolicLink()) {
45284
45532
  stop = true;
@@ -45290,7 +45538,7 @@ function writeConflicts(entries, destDir) {
45290
45538
  break;
45291
45539
  }
45292
45540
  if (stop) continue;
45293
- const kind = pathKind(join36(root, ...segments));
45541
+ const kind = pathKind(join37(root, ...segments));
45294
45542
  if (kind && !kind.isSymbolicLink() && kind.isDirectory()) {
45295
45543
  say(entry.path, "is a directory here, but the release has a file at that path");
45296
45544
  }
@@ -45298,24 +45546,24 @@ function writeConflicts(entries, destDir) {
45298
45546
  return conflicts;
45299
45547
  }
45300
45548
  function removeStaleSource(entries, destDir) {
45301
- const root = resolve32(destDir);
45549
+ const root = resolve33(destDir);
45302
45550
  const removed = staleSourcePaths(entries, root);
45303
- for (const rel of removed) rmSync6(join36(root, ...rel.split("/")), { force: true });
45551
+ for (const rel of removed) rmSync6(join37(root, ...rel.split("/")), { force: true });
45304
45552
  const needed = /* @__PURE__ */ new Set();
45305
45553
  for (const entry of entries) {
45306
45554
  if (isDeniedSourcePath(entry.path)) continue;
45307
45555
  const segments = entry.path.split("/");
45308
45556
  for (let i = 1; i < segments.length; i++) needed.add(segments.slice(0, i).join("/"));
45309
45557
  }
45310
- if (existsSync36(root)) pruneEmptyDirs(root, "", needed, removed);
45558
+ if (existsSync37(root)) pruneEmptyDirs(root, "", needed, removed);
45311
45559
  return removed.sort();
45312
45560
  }
45313
45561
  function pruneEmptyDirs(root, rel, needed, removed) {
45314
45562
  let empty = true;
45315
- for (const name of readdirSync20(rel ? join36(root, rel) : root)) {
45316
- const childRel = rel ? join36(rel, name) : name;
45563
+ for (const name of readdirSync20(rel ? join37(root, rel) : root)) {
45564
+ const childRel = rel ? join37(rel, name) : name;
45317
45565
  const posix3 = childRel.split(sep7).join("/");
45318
- const kind = lstatSync4(join36(root, childRel));
45566
+ const kind = lstatSync4(join37(root, childRel));
45319
45567
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name)) {
45320
45568
  empty = false;
45321
45569
  continue;
@@ -45325,7 +45573,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
45325
45573
  empty = false;
45326
45574
  continue;
45327
45575
  }
45328
- rmdirSync(join36(root, childRel));
45576
+ rmdirSync(join37(root, childRel));
45329
45577
  removed.push(`${posix3}/`);
45330
45578
  }
45331
45579
  return empty;
@@ -45353,13 +45601,13 @@ __export(eject_exports, {
45353
45601
  EJECTABLE: () => EJECTABLE,
45354
45602
  ejectModule: () => ejectModule
45355
45603
  });
45356
- import { existsSync as existsSync37, mkdirSync as mkdirSync19, readFileSync as readFileSync44, writeFileSync as writeFileSync15 } from "node:fs";
45357
- import { dirname as dirname24, join as join37, relative as relative4, resolve as resolve33, sep as sep8 } from "node:path";
45604
+ import { existsSync as existsSync38, mkdirSync as mkdirSync19, readFileSync as readFileSync45, writeFileSync as writeFileSync15 } from "node:fs";
45605
+ import { dirname as dirname25, join as join38, relative as relative5, resolve as resolve34, sep as sep8 } from "node:path";
45358
45606
  function locate(repoRootOrCwd, mod) {
45359
- const inRepo = resolve33(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
45360
- if (existsSync37(inRepo)) return { path: inRepo, form: "source" };
45361
- const dist = resolve33(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
45362
- if (existsSync37(dist)) return { path: dist, form: "bundle" };
45607
+ const inRepo = resolve34(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
45608
+ if (existsSync38(inRepo)) return { path: inRepo, form: "source" };
45609
+ const dist = resolve34(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
45610
+ if (existsSync38(dist)) return { path: dist, form: "bundle" };
45363
45611
  return null;
45364
45612
  }
45365
45613
  function specifiersFor(mod) {
@@ -45383,8 +45631,8 @@ function ejectModule(opts) {
45383
45631
  };
45384
45632
  }
45385
45633
  const ext = found.form === "source" ? "ts" : "mjs";
45386
- const target = join37(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
45387
- mkdirSync19(dirname24(target), { recursive: true });
45634
+ const target = join38(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
45635
+ mkdirSync19(dirname25(target), { recursive: true });
45388
45636
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
45389
45637
  //
45390
45638
  // Central fixes to @wix/himalaya/${mod} NO LONGER REACH THIS APP \u2014 including any
@@ -45397,17 +45645,17 @@ function ejectModule(opts) {
45397
45645
  // gate still applies. Ejecting buys layout and flow, never the floor.
45398
45646
 
45399
45647
  `;
45400
- const normalized = readFileSync44(found.path, "utf8").replace(
45648
+ const normalized = readFileSync45(found.path, "utf8").replace(
45401
45649
  /(["'])@himalaya\/([a-z0-9-]+(?:\/[a-z0-9-]+)?)\1/g,
45402
45650
  (_m, q, sub) => `${q}@wix/himalaya/${sub}${q}`
45403
45651
  );
45404
45652
  writeFileSync15(target, banner + normalized);
45405
45653
  const rewired = [];
45406
45654
  for (const f of files) {
45407
- if (!existsSync37(f)) continue;
45408
- const before = readFileSync44(f, "utf8");
45655
+ if (!existsSync38(f)) continue;
45656
+ const before = readFileSync45(f, "utf8");
45409
45657
  let after = before;
45410
- let rel = relative4(dirname24(f), target).split(sep8).join("/").replace(/\.tsx?$/, ".js");
45658
+ let rel = relative5(dirname25(f), target).split(sep8).join("/").replace(/\.tsx?$/, ".js");
45411
45659
  if (!rel.startsWith(".")) rel = `./${rel}`;
45412
45660
  for (const spec of specifiersFor(mod)) {
45413
45661
  const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -45453,14 +45701,14 @@ __export(test_exports, {
45453
45701
  scopeDescriptors: () => scopeDescriptors,
45454
45702
  writeRecordedMocks: () => writeRecordedMocks
45455
45703
  });
45456
- import { existsSync as existsSync38, mkdirSync as mkdirSync20, readFileSync as readFileSync45, writeFileSync as writeFileSync16 } from "node:fs";
45457
- import { join as join38, resolve as resolve34 } from "node:path";
45704
+ import { existsSync as existsSync39, mkdirSync as mkdirSync20, readFileSync as readFileSync46, writeFileSync as writeFileSync16 } from "node:fs";
45705
+ import { join as join39, resolve as resolve35 } from "node:path";
45458
45706
  function readTestConfig(dir) {
45459
- const p = resolve34(dir, TEST_CONFIG_FILE);
45460
- if (!existsSync38(p)) return {};
45707
+ const p = resolve35(dir, TEST_CONFIG_FILE);
45708
+ if (!existsSync39(p)) return {};
45461
45709
  let parsed;
45462
45710
  try {
45463
- parsed = JSON.parse(readFileSync45(p, "utf8"));
45711
+ parsed = JSON.parse(readFileSync46(p, "utf8"));
45464
45712
  } catch (e) {
45465
45713
  throw new Error(`${TEST_CONFIG_FILE} is not valid JSON \u2014 ${e.message}`);
45466
45714
  }
@@ -45503,7 +45751,7 @@ function readTestConfig(dir) {
45503
45751
  function readNetRules(path) {
45504
45752
  let parsed;
45505
45753
  try {
45506
- parsed = JSON.parse(readFileSync45(path, "utf8"));
45754
+ parsed = JSON.parse(readFileSync46(path, "utf8"));
45507
45755
  } catch (e) {
45508
45756
  throw new Error(`--net ${path} could not be read as JSON \u2014 ${e.message}`);
45509
45757
  }
@@ -45523,8 +45771,8 @@ function readNetRules(path) {
45523
45771
  }
45524
45772
  function defaultNetRules(dir) {
45525
45773
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
45526
- const p = resolve34(dir, rel);
45527
- if (existsSync38(p)) {
45774
+ const p = resolve35(dir, rel);
45775
+ if (existsSync39(p)) {
45528
45776
  const rules = readNetRules(p);
45529
45777
  if (rules.length) return { rules, from: rel };
45530
45778
  }
@@ -45540,7 +45788,7 @@ function readCannedResponses() {
45540
45788
  ];
45541
45789
  for (const url of candidates) {
45542
45790
  try {
45543
- const parsed = JSON.parse(readFileSync45(url, "utf8"));
45791
+ const parsed = JSON.parse(readFileSync46(url, "utf8"));
45544
45792
  const byKey = {};
45545
45793
  for (const [k, v] of Object.entries(parsed)) {
45546
45794
  if (!k.startsWith("//")) byKey[k] = v;
@@ -45632,8 +45880,8 @@ function writeRecordedMocks(opts) {
45632
45880
  mkdir: (p) => mkdirSync20(p, { recursive: true }),
45633
45881
  write: (p, s) => writeFileSync16(p, s)
45634
45882
  };
45635
- fs.mkdir(join38(opts.dir, "dev"));
45636
- fs.write(join38(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
45883
+ fs.mkdir(join39(opts.dir, "dev"));
45884
+ fs.write(join39(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
45637
45885
  return { ok: true, rules, to: "dev/net-mocks.json" };
45638
45886
  }
45639
45887
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -45696,7 +45944,7 @@ async function runCrawl(opts) {
45696
45944
  if (list.length > 1) collapsed.push({ representative: rep.screenId, instances: list.length });
45697
45945
  }
45698
45946
  }
45699
- const net = opts.offline ? null : opts.netPath ? { rules: readNetRules(resolve34(opts.netPath)), from: opts.netPath } : defaultNetRules(opts.dir);
45947
+ const net = opts.offline ? null : opts.netPath ? { rules: readNetRules(resolve35(opts.netPath)), from: opts.netPath } : defaultNetRules(opts.dir);
45700
45948
  const suppressions = opts.config?.skip ?? [];
45701
45949
  const suppressed = [];
45702
45950
  const matchSuppression = (screen, actionId, componentId, invariant) => suppressions.find((s) => (s.screen === void 0 || s.screen === screen) && (s.action === void 0 || s.action === actionId) && (s.component === void 0 || s.component === componentId) && (s.invariant === void 0 || s.invariant === invariant));
@@ -46090,19 +46338,19 @@ var mobile_ux_lint_exports = {};
46090
46338
  __export(mobile_ux_lint_exports, {
46091
46339
  mobileUxIssues: () => mobileUxIssues
46092
46340
  });
46093
- import { existsSync as existsSync39, readFileSync as readFileSync46 } from "node:fs";
46094
- import { join as join39 } from "node:path";
46341
+ import { existsSync as existsSync40, readFileSync as readFileSync47 } from "node:fs";
46342
+ import { join as join40 } from "node:path";
46095
46343
  function mobileUxIssues(dir, strict) {
46096
- const file = join39(dir, "MOBILE-UX.md");
46097
- if (!existsSync39(file)) return [];
46344
+ const file = join40(dir, "MOBILE-UX.md");
46345
+ if (!existsSync40(file)) return [];
46098
46346
  let text2;
46099
46347
  try {
46100
- text2 = readFileSync46(file, "utf8");
46348
+ text2 = readFileSync47(file, "utf8");
46101
46349
  } catch {
46102
46350
  return [];
46103
46351
  }
46104
46352
  if (!text2) return [];
46105
- return mobileUxMarkerFindings(text2).map((finding) => ({
46353
+ return mobileUxMarkerFindings(text2, recordedRenderWidths(dir)).map((finding) => ({
46106
46354
  screen: null,
46107
46355
  pass: "mobile-ux",
46108
46356
  ruleCode: finding.code,
@@ -46118,6 +46366,7 @@ function mobileUxIssues(dir, strict) {
46118
46366
  var init_mobile_ux_lint = __esm({
46119
46367
  "src/mobile-ux-lint.ts"() {
46120
46368
  init_mobile_ux_contract();
46369
+ init_ux_evidence();
46121
46370
  }
46122
46371
  });
46123
46372
 
@@ -46300,9 +46549,9 @@ function ipv6Private(address) {
46300
46549
  const value = address.toLowerCase().replace(/^\[|\]$/g, "");
46301
46550
  const sections = value.split("::");
46302
46551
  if (sections.length > 2) return true;
46303
- const parse = (section) => {
46304
- if (!section) return [];
46305
- const parts = section.split(":");
46552
+ const parse = (section2) => {
46553
+ if (!section2) return [];
46554
+ const parts = section2.split(":");
46306
46555
  const last = parts.at(-1) ?? "";
46307
46556
  if (!last.includes(".")) return parts.map((part) => Number.parseInt(part, 16));
46308
46557
  parts.pop();
@@ -46376,7 +46625,7 @@ async function pinnedFetch(url, init, addresses, maxResponseBytes) {
46376
46625
  const headers2 = Object.fromEntries(new Headers(init.headers).entries());
46377
46626
  const request = url.protocol === "https:" ? httpsRequest : httpRequest;
46378
46627
  const hostname = url.hostname.replace(/^\[|\]$/g, "");
46379
- return await new Promise((resolve41, reject) => {
46628
+ return await new Promise((resolve42, reject) => {
46380
46629
  let settled = false;
46381
46630
  const finish2 = (fn) => {
46382
46631
  if (settled) return;
@@ -46413,7 +46662,7 @@ async function pinnedFetch(url, init, addresses, maxResponseBytes) {
46413
46662
  }
46414
46663
  const responseHeaders = new Headers();
46415
46664
  for (const [key2, value] of Object.entries(response.headers)) if (value !== void 0) responseHeaders.set(key2, Array.isArray(value) ? value.join(", ") : value);
46416
- resolve41(new Response(bytes2, { status: response.statusCode, statusText: response.statusMessage, headers: responseHeaders }));
46665
+ resolve42(new Response(bytes2, { status: response.statusCode, statusText: response.statusMessage, headers: responseHeaders }));
46417
46666
  }));
46418
46667
  });
46419
46668
  req.once("error", (error) => finish2(() => reject(error)));
@@ -46713,7 +46962,7 @@ function createLocalTestSandbox() {
46713
46962
  }
46714
46963
  })();
46715
46964
  `;
46716
- return await new Promise((resolve41, reject) => {
46965
+ return await new Promise((resolve42, reject) => {
46717
46966
  const { port1, port2 } = new MessageChannel();
46718
46967
  const serializableContext = {
46719
46968
  auth: context.auth,
@@ -46789,7 +47038,7 @@ function createLocalTestSandbox() {
46789
47038
  timer = setTimeout(() => finish2(() => reject(new SandboxError("timeout", `execution exceeded ${limits.timeoutMs}ms`))), Math.max(1, limits.timeoutMs));
46790
47039
  return;
46791
47040
  }
46792
- if (message.ok) finish2(() => resolve41({ value: message.value, durationMs: Date.now() - started }));
47041
+ if (message.ok) finish2(() => resolve42({ value: message.value, durationMs: Date.now() - started }));
46793
47042
  else finish2(() => reject(new SandboxError("execution_failed", message.message ?? "handler failed")));
46794
47043
  });
46795
47044
  worker.once("error", (error) => finish2(() => reject(new SandboxError("execution_failed", error.message))));
@@ -47756,8 +48005,8 @@ __export(functions_exports, {
47756
48005
  resolveFunctionsTarget: () => resolveFunctionsTarget,
47757
48006
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
47758
48007
  });
47759
- import { readFileSync as readFileSync47, existsSync as existsSync40 } from "node:fs";
47760
- import { join as join40, resolve as resolve35 } from "node:path";
48008
+ import { readFileSync as readFileSync48, existsSync as existsSync41 } from "node:fs";
48009
+ import { join as join41, resolve as resolve36 } from "node:path";
47761
48010
  function resolveFunctionsTarget(input) {
47762
48011
  const explicitUrl = input.functionsUrl?.trim();
47763
48012
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -47769,16 +48018,16 @@ function resolveFunctionsTarget(input) {
47769
48018
  return { mode: "local" };
47770
48019
  }
47771
48020
  async function loadFunctionsApp(dir) {
47772
- const loaded = await loadContent(resolve35(dir));
48021
+ const loaded = await loadContent(resolve36(dir));
47773
48022
  const config = loaded.module.config;
47774
48023
  if (config.kind !== "functions" || !Array.isArray(config.functions?.functions)) {
47775
48024
  throw new Error(`content package "${dir}" is not a functions-only app`);
47776
48025
  }
47777
48026
  const sources = {};
47778
48027
  for (const definition of config.functions.functions) {
47779
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join40(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync40);
48028
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join41(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync41);
47780
48029
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
47781
- sources[definition.name] = readFileSync47(source, "utf8");
48030
+ sources[definition.name] = readFileSync48(source, "utf8");
47782
48031
  }
47783
48032
  const data = config.cms || config.data ? mergeDataResources(config) : void 0;
47784
48033
  const dataErrors = validateDataResources(data, loaded.name).filter((issue2) => issue2.severity === "error");
@@ -47927,14 +48176,15 @@ __export(native_decode_exports, {
47927
48176
  nativeDecodeAndroidHarnessDir: () => nativeDecodeAndroidHarnessDir,
47928
48177
  nativeDecodePackageDir: () => nativeDecodePackageDir,
47929
48178
  nativeDecodePlatforms: () => nativeDecodePlatforms,
48179
+ nativeOracleShipped: () => nativeOracleShipped,
47930
48180
  parseNativeDecodeOutput: () => parseNativeDecodeOutput,
47931
48181
  runAndroidNativeDecode: () => runAndroidNativeDecode,
47932
48182
  runNativeDecode: () => runNativeDecode
47933
48183
  });
47934
48184
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
47935
- import { existsSync as existsSync41, mkdtempSync as mkdtempSync2, readFileSync as readFileSync48, rmSync as rmSync7, writeFileSync as writeFileSync17 } from "node:fs";
48185
+ import { existsSync as existsSync42, mkdtempSync as mkdtempSync2, readFileSync as readFileSync49, rmSync as rmSync7, writeFileSync as writeFileSync17 } from "node:fs";
47936
48186
  import { tmpdir as tmpdir3 } from "node:os";
47937
- import { join as join41 } from "node:path";
48187
+ import { join as join42 } from "node:path";
47938
48188
  import { fileURLToPath as fileURLToPath19 } from "node:url";
47939
48189
  function nativeDecodePackageDir() {
47940
48190
  return fileURLToPath19(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -47942,6 +48192,12 @@ function nativeDecodePackageDir() {
47942
48192
  function nativeDecodeAndroidHarnessDir() {
47943
48193
  return fileURLToPath19(new URL("../../../tools/wix-sdk-android-tests/", import.meta.url));
47944
48194
  }
48195
+ function nativeOracleShipped() {
48196
+ return {
48197
+ ios: existsSync42(join42(nativeDecodePackageDir(), "Package.swift")),
48198
+ android: existsSync42(join42(nativeDecodeAndroidHarnessDir(), "gradlew"))
48199
+ };
48200
+ }
47945
48201
  function detectNativeDecode(options = {}) {
47946
48202
  const hostPlatform = options.hostPlatform ?? process.platform;
47947
48203
  const packageDir = options.packageDir ?? nativeDecodePackageDir();
@@ -47950,7 +48206,7 @@ function detectNativeDecode(options = {}) {
47950
48206
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
47951
48207
  }
47952
48208
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
47953
- const packageAvailable = options.packageAvailable ?? existsSync41(join41(packageDir, "Package.swift"));
48209
+ const packageAvailable = options.packageAvailable ?? existsSync42(join42(packageDir, "Package.swift"));
47954
48210
  if (!packageAvailable) {
47955
48211
  return {
47956
48212
  platform: "ios",
@@ -47962,7 +48218,7 @@ function detectNativeDecode(options = {}) {
47962
48218
  }
47963
48219
  function localPropertiesSdk(harnessDir) {
47964
48220
  try {
47965
- const match = readFileSync48(join41(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
48221
+ const match = readFileSync49(join42(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
47966
48222
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
47967
48223
  } catch {
47968
48224
  return null;
@@ -47970,8 +48226,8 @@ function localPropertiesSdk(harnessDir) {
47970
48226
  }
47971
48227
  function detectAndroidNativeDecode(options = {}) {
47972
48228
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
47973
- const gradlew = join41(harnessDir, "gradlew");
47974
- const gradlewAvailable = options.gradlewAvailable ?? existsSync41(gradlew);
48229
+ const gradlew = join42(harnessDir, "gradlew");
48230
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync42(gradlew);
47975
48231
  if (!gradlewAvailable) {
47976
48232
  return {
47977
48233
  platform: "android",
@@ -47981,7 +48237,7 @@ function detectAndroidNativeDecode(options = {}) {
47981
48237
  }
47982
48238
  const androidHome = options.androidHome ?? process.env.ANDROID_HOME ?? "";
47983
48239
  const sdk = androidHome.trim() || (options.localPropertiesSdk !== void 0 ? options.localPropertiesSdk : localPropertiesSdk(harnessDir));
47984
- const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync41(sdk));
48240
+ const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync42(sdk));
47985
48241
  if (!sdkAvailable) {
47986
48242
  return {
47987
48243
  platform: "android",
@@ -48053,14 +48309,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
48053
48309
  return verdicts;
48054
48310
  }
48055
48311
  function runNativeDecode(options) {
48056
- const descriptorDir = mkdtempSync2(join41(tmpdir3(), "himi-native-decode-"));
48312
+ const descriptorDir = mkdtempSync2(join42(tmpdir3(), "himi-native-decode-"));
48057
48313
  const startedAt = Date.now();
48058
48314
  try {
48059
48315
  for (const descriptor of options.descriptors) {
48060
48316
  if (typeof descriptor.descriptorJson !== "string") {
48061
48317
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
48062
48318
  }
48063
- writeFileSync17(join41(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
48319
+ writeFileSync17(join42(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
48064
48320
  }
48065
48321
  options.progress?.("building cached Swift oracle");
48066
48322
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -48072,7 +48328,7 @@ function runNativeDecode(options) {
48072
48328
  encoding: "utf8"
48073
48329
  }).trim();
48074
48330
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
48075
- const stdout = execFileSync5(join41(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
48331
+ const stdout = execFileSync5(join42(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
48076
48332
  stdio: ["ignore", "pipe", "pipe"],
48077
48333
  encoding: "utf8"
48078
48334
  });
@@ -48090,15 +48346,15 @@ function runNativeDecode(options) {
48090
48346
  }
48091
48347
  }
48092
48348
  function runAndroidNativeDecode(options) {
48093
- const descriptorDir = mkdtempSync2(join41(tmpdir3(), "himi-native-decode-android-"));
48094
- const outputPath = join41(descriptorDir, "verdicts.ndjson");
48349
+ const descriptorDir = mkdtempSync2(join42(tmpdir3(), "himi-native-decode-android-"));
48350
+ const outputPath = join42(descriptorDir, "verdicts.ndjson");
48095
48351
  const startedAt = Date.now();
48096
48352
  try {
48097
48353
  for (const descriptor of options.descriptors) {
48098
48354
  if (typeof descriptor.descriptorJson !== "string") {
48099
48355
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
48100
48356
  }
48101
- writeFileSync17(join41(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
48357
+ writeFileSync17(join42(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
48102
48358
  }
48103
48359
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
48104
48360
  execFileSync5(options.gradlew, [
@@ -48113,7 +48369,7 @@ function runAndroidNativeDecode(options) {
48113
48369
  stdio: ["ignore", "ignore", "pipe"],
48114
48370
  encoding: "utf8"
48115
48371
  });
48116
- const stdout = readFileSync48(outputPath, "utf8");
48372
+ const stdout = readFileSync49(outputPath, "utf8");
48117
48373
  return {
48118
48374
  platform: "android",
48119
48375
  verdicts: parseNativeDecodeOutput(stdout, options.descriptors.map((descriptor) => descriptor.screenId), "android"),
@@ -48184,13 +48440,13 @@ __export(browser_authoring_exports, {
48184
48440
  import { createServer as createServer3 } from "node:http";
48185
48441
  import { createHash as createHash22, randomBytes as randomBytes5 } from "node:crypto";
48186
48442
  import { execFile as execFile2 } from "node:child_process";
48187
- import { existsSync as existsSync42, readdirSync as readdirSync21, readFileSync as readFileSync49, realpathSync as realpathSync4, statSync as statSync9 } from "node:fs";
48188
- import { join as join42, posix, relative as relative5, sep as sep9 } from "node:path";
48443
+ import { existsSync as existsSync43, readdirSync as readdirSync21, readFileSync as readFileSync50, realpathSync as realpathSync4, statSync as statSync8 } from "node:fs";
48444
+ import { join as join43, posix, relative as relative6, sep as sep9 } from "node:path";
48189
48445
  function humanMs(ms) {
48190
48446
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
48191
48447
  }
48192
48448
  function collectWorkerSources(tier5SrcDir, bundleName) {
48193
- const root = join42(tier5SrcDir, bundleName);
48449
+ const root = join43(tier5SrcDir, bundleName);
48194
48450
  const files = {};
48195
48451
  const visitedDirs = /* @__PURE__ */ new Set();
48196
48452
  const walk2 = (abs, rel) => {
@@ -48198,16 +48454,16 @@ function collectWorkerSources(tier5SrcDir, bundleName) {
48198
48454
  if (visitedDirs.has(real)) return;
48199
48455
  visitedDirs.add(real);
48200
48456
  for (const name of readdirSync21(abs).sort()) {
48201
- const childAbs = join42(abs, name);
48457
+ const childAbs = join43(abs, name);
48202
48458
  const childRel = posix.join(rel, name);
48203
- if (statSync9(childAbs).isDirectory()) {
48459
+ if (statSync8(childAbs).isDirectory()) {
48204
48460
  walk2(childAbs, childRel);
48205
48461
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
48206
- files[childRel] = readFileSync49(childAbs, "utf8");
48462
+ files[childRel] = readFileSync50(childAbs, "utf8");
48207
48463
  }
48208
48464
  }
48209
48465
  };
48210
- if (!existsSync42(root)) return null;
48466
+ if (!existsSync43(root)) return null;
48211
48467
  walk2(root, `/tier5-src/${bundleName}`);
48212
48468
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p) => p in files);
48213
48469
  if (!entry) return null;
@@ -48224,20 +48480,20 @@ function collectRelativeSiblings(files, tier5SrcDir) {
48224
48480
  const spec = m[1];
48225
48481
  const virtual = posix.normalize(posix.join(dir, spec));
48226
48482
  if (!virtual.startsWith("/tier5-src/")) continue;
48227
- const onDisk = join42(tier5SrcDir, virtual.slice("/tier5-src/".length));
48483
+ const onDisk = join43(tier5SrcDir, virtual.slice("/tier5-src/".length));
48228
48484
  const candidates = [
48229
48485
  onDisk,
48230
48486
  `${onDisk}.ts`,
48231
48487
  `${onDisk}.js`,
48232
48488
  onDisk.replace(/\.js$/, ".ts"),
48233
- join42(onDisk, "index.ts"),
48234
- join42(onDisk, "index.js")
48489
+ join43(onDisk, "index.ts"),
48490
+ join43(onDisk, "index.js")
48235
48491
  ];
48236
48492
  for (const abs of candidates) {
48237
- if (!existsSync42(abs) || statSync9(abs).isDirectory()) continue;
48238
- const key2 = `/tier5-src/${relative5(tier5SrcDir, abs).split(sep9).join(posix.sep)}`;
48493
+ if (!existsSync43(abs) || statSync8(abs).isDirectory()) continue;
48494
+ const key2 = `/tier5-src/${relative6(tier5SrcDir, abs).split(sep9).join(posix.sep)}`;
48239
48495
  if (key2 in files) break;
48240
- files[key2] = readFileSync49(abs, "utf8");
48496
+ files[key2] = readFileSync50(abs, "utf8");
48241
48497
  queue.push(key2);
48242
48498
  break;
48243
48499
  }
@@ -48269,10 +48525,10 @@ function collectSdkModules(files, resolveSubpath) {
48269
48525
  for (const source of Object.values(files)) visit(source);
48270
48526
  return { modules, unsupported };
48271
48527
  }
48272
- function sdkSubpathReader(resolve41) {
48528
+ function sdkSubpathReader(resolve42) {
48273
48529
  return (sub) => {
48274
48530
  try {
48275
- return readFileSync49(resolve41(`@wix/himalaya/${sub}`), "utf8");
48531
+ return readFileSync50(resolve42(`@wix/himalaya/${sub}`), "utf8");
48276
48532
  } catch {
48277
48533
  return null;
48278
48534
  }
@@ -48353,7 +48609,7 @@ async function runBrowserAuthoring(opts) {
48353
48609
  const token = mintToken2(payload);
48354
48610
  const unusable = await probeAuthoringPage(opts.authoringBase, opts.fetchImpl, opts.probeTimeoutMs);
48355
48611
  if (unusable) throw new Error(unusable);
48356
- return await new Promise((resolve41, reject) => {
48612
+ return await new Promise((resolve42, reject) => {
48357
48613
  let settled = false;
48358
48614
  let url = "";
48359
48615
  const server = createServer3();
@@ -48405,7 +48661,7 @@ async function runBrowserAuthoring(opts) {
48405
48661
  }
48406
48662
  res.writeHead(200, { "content-type": "text/plain" }).end("ok");
48407
48663
  const report = body.result;
48408
- finish2(() => resolve41({ report, url }));
48664
+ finish2(() => resolve42({ report, url }));
48409
48665
  })();
48410
48666
  });
48411
48667
  server.on("error", (e) => finish2(() => reject(e)));
@@ -48443,11 +48699,15 @@ __export(coverage_exports, {
48443
48699
  diffAndStoreObservations: () => diffAndStoreObservations,
48444
48700
  formatDelta: () => formatDelta,
48445
48701
  formatLedger: () => formatLedger,
48702
+ suppressedNativeKeys: () => suppressedNativeKeys,
48446
48703
  trendAndStore: () => trendAndStore
48447
48704
  });
48448
- import { existsSync as existsSync43, mkdirSync as mkdirSync21, readFileSync as readFileSync50, writeFileSync as writeFileSync18 } from "node:fs";
48449
- import { dirname as dirname25, join as join43 } from "node:path";
48450
- function assembleLedger(r) {
48705
+ import { existsSync as existsSync44, mkdirSync as mkdirSync21, readFileSync as readFileSync51, writeFileSync as writeFileSync18 } from "node:fs";
48706
+ import { dirname as dirname26, join as join44 } from "node:path";
48707
+ function suppressedNativeKeys(oracle) {
48708
+ return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
48709
+ }
48710
+ function assembleLedger(r, options = {}) {
48451
48711
  const out = [];
48452
48712
  for (const c of r.crawled ?? []) {
48453
48713
  const noRow = (c.actionsSkipped ?? []).filter((s) => s.why === "no-row-value");
@@ -48569,8 +48829,9 @@ function assembleLedger(r) {
48569
48829
  remedy: "npx playwright install chromium (~170MB, one time) \u2014 then every himi test writes a PNG per screen to .himi/shots/"
48570
48830
  });
48571
48831
  }
48832
+ const oracle = options.nativeOracle ?? nativeOracleShipped();
48572
48833
  const nativeIos = r.nativeDecode?.ios;
48573
- if (!nativeIos || "unavailable" in nativeIos) {
48834
+ if (oracle.ios && (!nativeIos || "unavailable" in nativeIos)) {
48574
48835
  const reason = nativeIos && "unavailable" in nativeIos ? ` (${nativeIos.reason})` : "";
48575
48836
  out.push({
48576
48837
  key: "native-decode|ios",
@@ -48581,7 +48842,7 @@ function assembleLedger(r) {
48581
48842
  });
48582
48843
  }
48583
48844
  const nativeAndroid = r.nativeDecode?.android;
48584
- if (!nativeAndroid || "unavailable" in nativeAndroid) {
48845
+ if (oracle.android && (!nativeAndroid || "unavailable" in nativeAndroid)) {
48585
48846
  const reason = nativeAndroid && "unavailable" in nativeAndroid ? ` (${nativeAndroid.reason})` : "";
48586
48847
  out.push({
48587
48848
  key: "native-decode|android",
@@ -48611,30 +48872,32 @@ function assembleLedger(r) {
48611
48872
  }
48612
48873
  return out;
48613
48874
  }
48614
- function trendAndStore(dir, entries) {
48615
- const p = join43(dir, LEDGER_FILE);
48875
+ function trendAndStore(dir, entries, suppressed) {
48876
+ const p = join44(dir, LEDGER_FILE);
48616
48877
  let previous = null;
48617
48878
  try {
48618
- if (existsSync43(p)) {
48619
- const parsed = JSON.parse(readFileSync50(p, "utf8"));
48879
+ if (existsSync44(p)) {
48880
+ const parsed = JSON.parse(readFileSync51(p, "utf8"));
48620
48881
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k) => typeof k === "string");
48621
48882
  }
48622
48883
  } catch {
48623
48884
  }
48624
48885
  const current = entries.map((e) => e.key);
48886
+ const stored = [.../* @__PURE__ */ new Set([...current, ...(previous ?? []).filter((key2) => (suppressed ?? []).includes(key2))])];
48625
48887
  try {
48626
- mkdirSync21(dirname25(p), { recursive: true });
48627
- writeFileSync18(p, JSON.stringify({ keys: current, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
48888
+ mkdirSync21(dirname26(p), { recursive: true });
48889
+ writeFileSync18(p, JSON.stringify({ keys: stored, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
48628
48890
  } catch {
48629
48891
  return null;
48630
48892
  }
48631
48893
  if (previous === null) return { opened: 0, closed: 0 };
48632
- const prev = new Set(previous);
48894
+ const baseline = previous.filter((key2) => !(suppressed ?? []).includes(key2));
48895
+ const prev = new Set(baseline);
48633
48896
  const cur = new Set(current);
48634
48897
  return {
48635
48898
  opened: current.filter((k) => !prev.has(k)).length,
48636
- closed: previous.filter((k) => !cur.has(k)).length,
48637
- previousTotal: previous.length
48899
+ closed: baseline.filter((k) => !cur.has(k)).length,
48900
+ previousTotal: baseline.length
48638
48901
  };
48639
48902
  }
48640
48903
  function formatLedger(entries, trend) {
@@ -48653,11 +48916,11 @@ function formatLedger(entries, trend) {
48653
48916
  return out.join("\n");
48654
48917
  }
48655
48918
  function diffAndStoreObservations(dir, current) {
48656
- const p = join43(dir, OBS_FILE);
48919
+ const p = join44(dir, OBS_FILE);
48657
48920
  let previous = null;
48658
48921
  try {
48659
- if (existsSync43(p)) {
48660
- const parsed = JSON.parse(readFileSync50(p, "utf8"));
48922
+ if (existsSync44(p)) {
48923
+ const parsed = JSON.parse(readFileSync51(p, "utf8"));
48661
48924
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
48662
48925
  (v) => Array.isArray(v) && v.every((l) => typeof l === "string")
48663
48926
  )) {
@@ -48667,7 +48930,7 @@ function diffAndStoreObservations(dir, current) {
48667
48930
  } catch {
48668
48931
  }
48669
48932
  try {
48670
- mkdirSync21(dirname25(p), { recursive: true });
48933
+ mkdirSync21(dirname26(p), { recursive: true });
48671
48934
  writeFileSync18(p, JSON.stringify({ screens: current, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
48672
48935
  } catch {
48673
48936
  return null;
@@ -48705,8 +48968,9 @@ function formatDelta(delta) {
48705
48968
  var LEDGER_FILE, OBS_FILE;
48706
48969
  var init_coverage = __esm({
48707
48970
  "src/coverage.ts"() {
48708
- LEDGER_FILE = join43(".himi", "test-ledger.json");
48709
- OBS_FILE = join43(".himi", "test-observations.json");
48971
+ init_native_decode();
48972
+ LEDGER_FILE = join44(".himi", "test-ledger.json");
48973
+ OBS_FILE = join44(".himi", "test-observations.json");
48710
48974
  }
48711
48975
  });
48712
48976
 
@@ -48719,11 +48983,11 @@ __export(assets_exports, {
48719
48983
  removeAssetPayload: () => removeAssetPayload,
48720
48984
  verifyReport: () => verifyReport
48721
48985
  });
48722
- import { readFileSync as readFileSync51 } from "node:fs";
48986
+ import { readFileSync as readFileSync52 } from "node:fs";
48723
48987
  import { basename as basename8 } from "node:path";
48724
48988
  function declaredAssets(dir) {
48725
48989
  return collectAssets(dir).map((a) => {
48726
- const bytes2 = readFileSync51(a.path);
48990
+ const bytes2 = readFileSync52(a.path);
48727
48991
  return {
48728
48992
  name: a.name,
48729
48993
  sha256: assetSha256(bytes2),
@@ -48799,7 +49063,7 @@ function verifyReport(dir, app, manifest) {
48799
49063
  };
48800
49064
  }
48801
49065
  function addAssetPayload(manifest, filePath, opts) {
48802
- const bytes2 = readFileSync51(filePath);
49066
+ const bytes2 = readFileSync52(filePath);
48803
49067
  const name = opts.name ?? basename8(filePath);
48804
49068
  const sha2565 = assetSha256(bytes2);
48805
49069
  const record2 = {
@@ -50213,9 +50477,9 @@ __export(run_exports, {
50213
50477
  runPackage: () => runPackage,
50214
50478
  withoutProdServe: () => withoutProdServe
50215
50479
  });
50216
- import { cpSync, existsSync as existsSync44, mkdirSync as mkdirSync22, mkdtempSync as mkdtempSync3, readFileSync as readFileSync52, rmSync as rmSync8, statSync as statSync10 } from "node:fs";
50480
+ import { cpSync, existsSync as existsSync45, mkdirSync as mkdirSync22, mkdtempSync as mkdtempSync3, readFileSync as readFileSync53, rmSync as rmSync8, statSync as statSync9 } from "node:fs";
50217
50481
  import { tmpdir as tmpdir4 } from "node:os";
50218
- import { join as join44 } from "node:path";
50482
+ import { join as join45 } from "node:path";
50219
50483
  import { spawn as spawn3 } from "node:child_process";
50220
50484
  function makePackageRunHandler(opts) {
50221
50485
  return makeRunHandler({
@@ -50233,9 +50497,9 @@ async function fetchBytes(url) {
50233
50497
  }
50234
50498
  async function resolvePackage(source, workDir) {
50235
50499
  const isUrl2 = /^https?:\/\//i.test(source);
50236
- if (!isUrl2 && existsSync44(source) && statSync10(source).isDirectory()) {
50237
- const appDirManifest = join44(source, "release-manifest.json");
50238
- if (existsSync44(appDirManifest)) {
50500
+ if (!isUrl2 && existsSync45(source) && statSync9(source).isDirectory()) {
50501
+ const appDirManifest = join45(source, "release-manifest.json");
50502
+ if (existsSync45(appDirManifest)) {
50239
50503
  const opened2 = openPackageDir(source);
50240
50504
  if (!opened2.validation.ok) {
50241
50505
  throw new Error(`package is not valid:
@@ -50244,9 +50508,9 @@ async function resolvePackage(source, workDir) {
50244
50508
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
50245
50509
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
50246
50510
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
50247
- const root2 = workDir ?? mkdtempSync3(join44(tmpdir4(), "himi-run-"));
50248
- const target = join44(root2, app2);
50249
- if (join44(source) !== target) {
50511
+ const root2 = workDir ?? mkdtempSync3(join45(tmpdir4(), "himi-run-"));
50512
+ const target = join45(root2, app2);
50513
+ if (join45(source) !== target) {
50250
50514
  mkdirSync22(root2, { recursive: true });
50251
50515
  rmSync8(target, { recursive: true, force: true });
50252
50516
  cpSync(source, target, { recursive: true });
@@ -50263,8 +50527,8 @@ async function resolvePackage(source, workDir) {
50263
50527
  }
50264
50528
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
50265
50529
  }
50266
- const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync52(source);
50267
- const root = workDir ?? mkdtempSync3(join44(tmpdir4(), "himi-run-"));
50530
+ const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync53(source);
50531
+ const root = workDir ?? mkdtempSync3(join45(tmpdir4(), "himi-run-"));
50268
50532
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
50269
50533
  const opened = openPackageDir(appDir2);
50270
50534
  if (!opened.validation.ok) {
@@ -50364,19 +50628,19 @@ __export(run_browser_exports, {
50364
50628
  runInBrowser: () => runInBrowser
50365
50629
  });
50366
50630
  import { createServer as createServer5 } from "node:http";
50367
- import { existsSync as existsSync45, readFileSync as readFileSync53 } from "node:fs";
50631
+ import { existsSync as existsSync46, readFileSync as readFileSync54 } from "node:fs";
50368
50632
  import { spawnSync as spawnSync3, execFile as execFile3 } from "node:child_process";
50369
- import { dirname as dirname26, resolve as resolve36 } from "node:path";
50633
+ import { dirname as dirname27, resolve as resolve37 } from "node:path";
50370
50634
  import { fileURLToPath as fileURLToPath20 } from "node:url";
50371
50635
  function stagedDir() {
50372
- return process.env.HIMI_BROWSER_PLANE_OUT ? resolve36(process.env.HIMI_BROWSER_PLANE_OUT) : resolve36(REPO, "packages", "serve", "dist", "browser-plane");
50636
+ return process.env.HIMI_BROWSER_PLANE_OUT ? resolve37(process.env.HIMI_BROWSER_PLANE_OUT) : resolve37(REPO, "packages", "serve", "dist", "browser-plane");
50373
50637
  }
50374
50638
  function missingStaged() {
50375
- return [...SERVED].filter((f) => !existsSync45(resolve36(stagedDir(), f))).sort();
50639
+ return [...SERVED].filter((f) => !existsSync46(resolve37(stagedDir(), f))).sort();
50376
50640
  }
50377
50641
  function ensureBrowserPlaneStaged() {
50378
50642
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
50379
- const r = spawnSync3(process.execPath, [resolve36(REPO, "core/serve/scripts/stage-browser-plane.mjs")], {
50643
+ const r = spawnSync3(process.execPath, [resolve37(REPO, "core/serve/scripts/stage-browser-plane.mjs")], {
50380
50644
  cwd: REPO,
50381
50645
  encoding: "utf8"
50382
50646
  });
@@ -50462,7 +50726,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
50462
50726
  }
50463
50727
  const name = path === "/" || path === "/index.html" ? "host.html" : path.slice(1);
50464
50728
  if (SERVED.has(name)) {
50465
- if (!existsSync45(resolve36(stagedDir(), name))) {
50729
+ if (!existsSync46(resolve37(stagedDir(), name))) {
50466
50730
  const restaged = ensureBrowserPlaneStaged();
50467
50731
  if (!restaged.ok) {
50468
50732
  res.writeHead(503, { "content-type": "application/json" });
@@ -50472,7 +50736,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
50472
50736
  }
50473
50737
  let bytes2;
50474
50738
  try {
50475
- bytes2 = readFileSync53(resolve36(stagedDir(), name));
50739
+ bytes2 = readFileSync54(resolve37(stagedDir(), name));
50476
50740
  } catch (e) {
50477
50741
  res.writeHead(503, { "content-type": "application/json" });
50478
50742
  res.end(JSON.stringify({ error: "browser_plane_unstaged", file: name, detail: String(e) }));
@@ -50515,8 +50779,8 @@ var HERE, REPO, SERVED;
50515
50779
  var init_run_browser = __esm({
50516
50780
  "src/run-browser.ts"() {
50517
50781
  init_file_store();
50518
- HERE = dirname26(fileURLToPath20(import.meta.url));
50519
- REPO = resolve36(HERE, "..", "..", "..");
50782
+ HERE = dirname27(fileURLToPath20(import.meta.url));
50783
+ REPO = resolve37(HERE, "..", "..", "..");
50520
50784
  SERVED = /* @__PURE__ */ new Set(["host.html", "sw.js", "package-reader.js"]);
50521
50785
  }
50522
50786
  });
@@ -50890,11 +51154,11 @@ __export(preflight_exports, {
50890
51154
  parseStoreConfig: () => parseStoreConfig,
50891
51155
  requireEnv: () => requireEnv
50892
51156
  });
50893
- import { existsSync as existsSync46, readFileSync as readFileSync54 } from "node:fs";
50894
- import { isAbsolute as isAbsolute2, relative as relative6, resolve as resolve37 } from "node:path";
51157
+ import { existsSync as existsSync47, readFileSync as readFileSync55 } from "node:fs";
51158
+ import { isAbsolute as isAbsolute3, relative as relative7, resolve as resolve38 } from "node:path";
50895
51159
  function isInsideRepo(p) {
50896
- const rel = relative6(REPO_ROOT3, resolve37(p));
50897
- return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
51160
+ const rel = relative7(REPO_ROOT3, resolve38(p));
51161
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute3(rel);
50898
51162
  }
50899
51163
  function assertCredentialOutsideRepo(label2, p) {
50900
51164
  if (isInsideRepo(p)) {
@@ -50923,7 +51187,7 @@ function scalar2(v) {
50923
51187
  }
50924
51188
  function parseStoreConfig(text2) {
50925
51189
  const cfg = {};
50926
- let section = null;
51190
+ let section2 = null;
50927
51191
  for (const raw of text2.split("\n")) {
50928
51192
  const line = strip2(raw);
50929
51193
  if (!line.trim()) continue;
@@ -50933,24 +51197,24 @@ function parseStoreConfig(text2) {
50933
51197
  const [, key2, rest] = kv;
50934
51198
  if (!indented) {
50935
51199
  if (rest.trim() === "") {
50936
- section = {};
50937
- cfg[key2] = section;
51200
+ section2 = {};
51201
+ cfg[key2] = section2;
50938
51202
  } else {
50939
- section = null;
51203
+ section2 = null;
50940
51204
  cfg[key2] = scalar2(rest);
50941
51205
  }
50942
- } else if (section) {
50943
- section[key2] = scalar2(rest);
51206
+ } else if (section2) {
51207
+ section2[key2] = scalar2(rest);
50944
51208
  }
50945
51209
  }
50946
51210
  return cfg;
50947
51211
  }
50948
51212
  function loadStoreConfig(app) {
50949
51213
  const p = storeConfigPath(app);
50950
- if (!existsSync46(p)) {
51214
+ if (!existsSync47(p)) {
50951
51215
  throw new Error(`No store config at ${p}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
50952
51216
  }
50953
- return parseStoreConfig(readFileSync54(p, "utf8"));
51217
+ return parseStoreConfig(readFileSync55(p, "utf8"));
50954
51218
  }
50955
51219
  var init_preflight = __esm({
50956
51220
  "../store-release/preflight.ts"() {
@@ -50973,9 +51237,9 @@ __export(preflight_app_exports, {
50973
51237
  pngSize: () => pngSize,
50974
51238
  preflightApp: () => preflightApp
50975
51239
  });
50976
- import { readFileSync as readFileSync55, existsSync as existsSync47, readdirSync as readdirSync22, statSync as statSync11 } from "node:fs";
51240
+ import { readFileSync as readFileSync56, existsSync as existsSync48, readdirSync as readdirSync22, statSync as statSync10 } from "node:fs";
50977
51241
  import { createHash as createHash23 } from "node:crypto";
50978
- import { join as join45 } from "node:path";
51242
+ import { join as join46 } from "node:path";
50979
51243
  function analyze(projectYml, storeConfig, icon) {
50980
51244
  const f = [];
50981
51245
  const has = (re, s) => re.test(s);
@@ -51023,10 +51287,10 @@ function analyze(projectYml, storeConfig, icon) {
51023
51287
  return f;
51024
51288
  }
51025
51289
  async function preflightApp(app) {
51026
- const projectYml = existsSync47(iosProjectYml(app)) ? readFileSync55(iosProjectYml(app), "utf8") : "";
51027
- const storeConfig = existsSync47(storeConfigPath(app)) ? readFileSync55(storeConfigPath(app), "utf8") : "";
51290
+ const projectYml = existsSync48(iosProjectYml(app)) ? readFileSync56(iosProjectYml(app), "utf8") : "";
51291
+ const storeConfig = existsSync48(storeConfigPath(app)) ? readFileSync56(storeConfigPath(app), "utf8") : "";
51028
51292
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
51029
- const findings = analyze(projectYml, storeConfig, await appIconState(join45(appDir(app), "ios")));
51293
+ const findings = analyze(projectYml, storeConfig, await appIconState(join46(appDir(app), "ios")));
51030
51294
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
51031
51295
  findings.push(
51032
51296
  ...analyzePlayListing(playListingAssets(app)).map((finding) => ({ ...finding, level: "warn" }))
@@ -51053,17 +51317,17 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
51053
51317
  return f;
51054
51318
  }
51055
51319
  function appLevelMetadataFiles(app, locale = "en-US") {
51056
- const root = join45(storeDir(app), "metadata");
51320
+ const root = join46(storeDir(app), "metadata");
51057
51321
  const out = /* @__PURE__ */ new Set();
51058
- if (existsSync47(root)) {
51322
+ if (existsSync48(root)) {
51059
51323
  for (const n of readdirSync22(root)) if (n.endsWith(".txt")) out.add(n);
51060
51324
  }
51061
- if (existsSync47(join45(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
51325
+ if (existsSync48(join46(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
51062
51326
  return out;
51063
51327
  }
51064
51328
  function reviewInfoFiles(app) {
51065
- const dir = join45(storeDir(app), "review_information");
51066
- if (!existsSync47(dir)) return /* @__PURE__ */ new Set();
51329
+ const dir = join46(storeDir(app), "review_information");
51330
+ if (!existsSync48(dir)) return /* @__PURE__ */ new Set();
51067
51331
  return new Set(readdirSync22(dir).filter((n) => n.endsWith(".txt")));
51068
51332
  }
51069
51333
  function isValidAppStoreSize(w, h) {
@@ -51173,12 +51437,12 @@ function analyzePlayListing(assets) {
51173
51437
  return f;
51174
51438
  }
51175
51439
  function playListingAssets(app, locale = "en-US") {
51176
- const dir = join45(storeDir(app), "play", locale, "images");
51440
+ const dir = join46(storeDir(app), "play", locale, "images");
51177
51441
  const read = (names) => {
51178
51442
  for (const name of names) {
51179
- const p = join45(dir, name);
51443
+ const p = join46(dir, name);
51180
51444
  try {
51181
- if (statSync11(p).isFile()) return { name, bytes: readFileSync55(p) };
51445
+ if (statSync10(p).isFile()) return { name, bytes: readFileSync56(p) };
51182
51446
  } catch {
51183
51447
  }
51184
51448
  }
@@ -51209,26 +51473,26 @@ function analyzeListing(metaFiles, shots) {
51209
51473
  return f;
51210
51474
  }
51211
51475
  function metadataFilesPresent(app, locale = "en-US") {
51212
- const dir = join45(storeDir(app), "metadata", locale);
51213
- if (!existsSync47(dir)) return /* @__PURE__ */ new Set();
51476
+ const dir = join46(storeDir(app), "metadata", locale);
51477
+ if (!existsSync48(dir)) return /* @__PURE__ */ new Set();
51214
51478
  return new Set(readdirSync22(dir).filter((n) => n.endsWith(".txt")));
51215
51479
  }
51216
51480
  function listScreenshots(app, locale = "en-US") {
51217
51481
  const out = [];
51218
- const flat = join45(storeDir(app), "screenshots", locale);
51219
- if (existsSync47(flat)) {
51482
+ const flat = join46(storeDir(app), "screenshots", locale);
51483
+ if (existsSync48(flat)) {
51220
51484
  for (const png of readdirSync22(flat).filter((n) => n.endsWith(".png"))) {
51221
- const sz = pngSize(readFileSync55(join45(flat, png)));
51485
+ const sz = pngSize(readFileSync56(join46(flat, png)));
51222
51486
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
51223
51487
  }
51224
51488
  }
51225
- const legacy = join45(storeDir(app), "metadata", locale, "screenshots");
51226
- if (existsSync47(legacy)) {
51489
+ const legacy = join46(storeDir(app), "metadata", locale, "screenshots");
51490
+ if (existsSync48(legacy)) {
51227
51491
  for (const deviceDir of readdirSync22(legacy)) {
51228
- const d = join45(legacy, deviceDir);
51229
- if (!statSync11(d).isDirectory()) continue;
51492
+ const d = join46(legacy, deviceDir);
51493
+ if (!statSync10(d).isDirectory()) continue;
51230
51494
  for (const png of readdirSync22(d).filter((n) => n.endsWith(".png"))) {
51231
- const sz = pngSize(readFileSync55(join45(d, png)));
51495
+ const sz = pngSize(readFileSync56(join46(d, png)));
51232
51496
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
51233
51497
  }
51234
51498
  }
@@ -51236,13 +51500,13 @@ function listScreenshots(app, locale = "en-US") {
51236
51500
  return out;
51237
51501
  }
51238
51502
  function placeholderHashes2() {
51239
- const templateIos = join45(appDir("_template"), "ios");
51240
- if (!existsSync47(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
51503
+ const templateIos = join46(appDir("_template"), "ios");
51504
+ if (!existsSync48(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
51241
51505
  for (const target of readdirSync22(templateIos)) {
51242
- const p = join45(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51506
+ const p = join46(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51243
51507
  try {
51244
- if (!statSync11(p).isFile()) continue;
51245
- return [LEGACY_PLACEHOLDER_SHA2562, createHash23("sha256").update(readFileSync55(p)).digest("hex")];
51508
+ if (!statSync10(p).isFile()) continue;
51509
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash23("sha256").update(readFileSync56(p)).digest("hex")];
51246
51510
  } catch {
51247
51511
  }
51248
51512
  }
@@ -51273,12 +51537,12 @@ async function pixelIssues(bytes2) {
51273
51537
  }
51274
51538
  }
51275
51539
  async function appIconState(iosDir) {
51276
- if (!existsSync47(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
51540
+ if (!existsSync48(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
51277
51541
  for (const target of readdirSync22(iosDir)) {
51278
- const p = join45(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51542
+ const p = join46(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51279
51543
  try {
51280
- if (!statSync11(p).isFile()) continue;
51281
- const bytes2 = readFileSync55(p);
51544
+ if (!statSync10(p).isFile()) continue;
51545
+ const bytes2 = readFileSync56(p);
51282
51546
  const hash = createHash23("sha256").update(bytes2).digest("hex");
51283
51547
  const pixels = await pixelIssues(bytes2);
51284
51548
  return {
@@ -51348,14 +51612,14 @@ __export(credential_profile_exports, {
51348
51612
  profilePath: () => profilePath,
51349
51613
  resolveStoreCredentials: () => resolveStoreCredentials
51350
51614
  });
51351
- import { existsSync as existsSync48, readFileSync as readFileSync56 } from "node:fs";
51615
+ import { existsSync as existsSync49, readFileSync as readFileSync57 } from "node:fs";
51352
51616
  import { homedir as homedir6 } from "node:os";
51353
- import { join as join46 } from "node:path";
51617
+ import { join as join47 } from "node:path";
51354
51618
  function credentialsDir(home = homedir6()) {
51355
- return join46(home, ".himalaya", "store-credentials");
51619
+ return join47(home, ".himalaya", "store-credentials");
51356
51620
  }
51357
51621
  function profilePath(name, home = homedir6()) {
51358
- return join46(credentialsDir(home), `${name}.json`);
51622
+ return join47(credentialsDir(home), `${name}.json`);
51359
51623
  }
51360
51624
  function parseProfile(text2, label2) {
51361
51625
  let doc;
@@ -51382,8 +51646,8 @@ function resolveStoreCredentials(opts) {
51382
51646
  const { app, cfg } = opts;
51383
51647
  const env = opts.env ?? process.env;
51384
51648
  const home = opts.home ?? homedir6();
51385
- const read = opts.readProfile ?? ((p) => readFileSync56(p, "utf8"));
51386
- const exists = opts.profileExists ?? existsSync48;
51649
+ const read = opts.readProfile ?? ((p) => readFileSync57(p, "utf8"));
51650
+ const exists = opts.profileExists ?? existsSync49;
51387
51651
  const ios = cfg.ios ?? {};
51388
51652
  const flagName = opts.profileFlag?.trim();
51389
51653
  const named = flagName || ios.credentialProfile?.trim();
@@ -51457,7 +51721,7 @@ __export(version_exports, {
51457
51721
  setAndroidVersion: () => setAndroidVersion,
51458
51722
  setIosVersion: () => setIosVersion
51459
51723
  });
51460
- import { readFileSync as readFileSync57, writeFileSync as writeFileSync19 } from "node:fs";
51724
+ import { readFileSync as readFileSync58, writeFileSync as writeFileSync19 } from "node:fs";
51461
51725
  function readIosVersion(projectYml) {
51462
51726
  const marketing = matchScalar(projectYml, "CFBundleShortVersionString");
51463
51727
  const build2 = matchScalar(projectYml, "CFBundleVersion");
@@ -51515,8 +51779,8 @@ function setAndroidVersion(gradle, v) {
51515
51779
  function bumpVersion(app, opts) {
51516
51780
  const iosPath = iosProjectYml(app);
51517
51781
  const gradlePath = androidGradle(app);
51518
- const iosYml = readFileSync57(iosPath, "utf8");
51519
- const gradle = readFileSync57(gradlePath, "utf8");
51782
+ const iosYml = readFileSync58(iosPath, "utf8");
51783
+ const gradle = readFileSync58(gradlePath, "utf8");
51520
51784
  const current = readIosVersion(iosYml);
51521
51785
  const next = {
51522
51786
  marketing: opts.marketing ?? current.marketing,
@@ -51533,7 +51797,7 @@ var init_version = __esm({
51533
51797
  });
51534
51798
 
51535
51799
  // ../store-release/register-bundle.ts
51536
- import { readFileSync as readFileSync58 } from "node:fs";
51800
+ import { readFileSync as readFileSync59 } from "node:fs";
51537
51801
  import { createPrivateKey, sign } from "node:crypto";
51538
51802
  function b64url2(b) {
51539
51803
  return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -51551,7 +51815,7 @@ function bundleIdBody(identifier, name, platform = "IOS") {
51551
51815
  async function registerBundleId(opts) {
51552
51816
  const env = requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
51553
51817
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
51554
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync58(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51818
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync59(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51555
51819
  const res = await fetch(`${ASC_API}/v1/bundleIds`, {
51556
51820
  method: "POST",
51557
51821
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
@@ -51605,7 +51869,7 @@ __export(asc_status_exports, {
51605
51869
  ascStatus: () => ascStatus,
51606
51870
  buildVerdict: () => buildVerdict
51607
51871
  });
51608
- import { readFileSync as readFileSync59 } from "node:fs";
51872
+ import { readFileSync as readFileSync60 } from "node:fs";
51609
51873
  function buildVerdict(builds) {
51610
51874
  if (!builds.length) {
51611
51875
  return "NO builds registered \u2014 if you uploaded recently and it's been >15 min, the binary was REJECTED IN PROCESSING (reason is email-only; run validate-binary.ts / scan-purpose-strings.ts to reproduce it).";
@@ -51619,7 +51883,7 @@ function buildVerdict(builds) {
51619
51883
  function client2(creds) {
51620
51884
  const env = creds ?? requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
51621
51885
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
51622
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync59(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51886
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync60(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51623
51887
  return async (path) => {
51624
51888
  const r = await fetch(`${ASC}${path}`, { headers: { Authorization: `Bearer ${jwt}` } });
51625
51889
  return { status: r.status, json: await r.json().catch(() => ({})) };
@@ -53635,11 +53899,11 @@ function suggestKeys(args) {
53635
53899
  if (rule.tab && allowed2(rule) && rule.re.test(label2)) set(rule.tab, label2);
53636
53900
  }
53637
53901
  }
53638
- for (const section of sections) {
53902
+ for (const section2 of sections) {
53639
53903
  for (const rule of KEY_RULES) {
53640
- if (!allowed2(rule) || !rule.re.test(section.heading)) continue;
53641
- set(rule.title, section.heading);
53642
- if (section.body) set(rule.subtitle, section.body);
53904
+ if (!allowed2(rule) || !rule.re.test(section2.heading)) continue;
53905
+ set(rule.title, section2.heading);
53906
+ if (section2.body) set(rule.subtitle, section2.body);
53643
53907
  }
53644
53908
  }
53645
53909
  if (tagline) set("home.onboardingBody", tagline.text);
@@ -53672,12 +53936,12 @@ function buildCopyChapter(args) {
53672
53936
  pageUrl: page.url
53673
53937
  }))
53674
53938
  );
53675
- for (const section of candidates) {
53939
+ for (const section2 of candidates) {
53676
53940
  if (faq.length >= CAP.faq) break;
53677
- if (!section.heading || !section.body || !/\?\s*$/.test(section.heading)) continue;
53678
- if (about && section.body === about) continue;
53679
- if (faq.some((f) => f.q.toLowerCase() === section.heading.toLowerCase())) continue;
53680
- faq.push({ q: section.heading, a: section.body, pageUrl: section.pageUrl });
53941
+ if (!section2.heading || !section2.body || !/\?\s*$/.test(section2.heading)) continue;
53942
+ if (about && section2.body === about) continue;
53943
+ if (faq.some((f) => f.q.toLowerCase() === section2.heading.toLowerCase())) continue;
53944
+ faq.push({ q: section2.heading, a: section2.body, pageUrl: section2.pageUrl });
53681
53945
  }
53682
53946
  }
53683
53947
  const testimonials = dedupe2(pages.flatMap((p) => p.testimonials ?? []), CAP.testimonials);
@@ -54709,10 +54973,10 @@ function isFetchableFontUrl(raw) {
54709
54973
  return true;
54710
54974
  }
54711
54975
  function resolveAll(hostname) {
54712
- return new Promise((resolve41, reject) => {
54976
+ return new Promise((resolve42, reject) => {
54713
54977
  dnsLookup(hostname, { all: true, verbatim: true }, (err, addresses) => {
54714
54978
  if (err) reject(err);
54715
- else resolve41(addresses);
54979
+ else resolve42(addresses);
54716
54980
  });
54717
54981
  });
54718
54982
  }
@@ -54720,13 +54984,13 @@ function withAbort(start, signal, what) {
54720
54984
  if (signal?.aborted) return Promise.reject(new Error(`${what} aborted`));
54721
54985
  const work = start();
54722
54986
  if (!signal) return work;
54723
- return new Promise((resolve41, reject) => {
54987
+ return new Promise((resolve42, reject) => {
54724
54988
  const onAbort = () => reject(new Error(`${what} aborted`));
54725
54989
  signal.addEventListener("abort", onAbort, { once: true });
54726
- work.then(resolve41, reject).finally(() => signal.removeEventListener("abort", onAbort));
54990
+ work.then(resolve42, reject).finally(() => signal.removeEventListener("abort", onAbort));
54727
54991
  });
54728
54992
  }
54729
- async function pinAddress(url, resolve41 = resolveAll, signal) {
54993
+ async function pinAddress(url, resolve42 = resolveAll, signal) {
54730
54994
  const host = url.hostname.replace(/^\[|\]$/g, "");
54731
54995
  if (ipv4Octets(host)) {
54732
54996
  if (isBlockedAddress(host)) throw new Error(`${host} is a blocked address`);
@@ -54736,7 +55000,7 @@ async function pinAddress(url, resolve41 = resolveAll, signal) {
54736
55000
  if (isBlockedAddress(host)) throw new Error(`${host} is a blocked address`);
54737
55001
  return { address: host, family: 6 };
54738
55002
  }
54739
- const addresses = await withAbort(() => resolve41(host), signal, `${host} lookup`);
55003
+ const addresses = await withAbort(() => resolve42(host), signal, `${host} lookup`);
54740
55004
  if (!addresses.length) throw new Error(`${host} resolved to no address`);
54741
55005
  const blocked = addresses.filter((a) => isBlockedAddress(a.address)).map((a) => a.address);
54742
55006
  if (blocked.length) throw new Error(`${host} resolves to a blocked address (${blocked.join(", ")})`);
@@ -54759,7 +55023,7 @@ function decodedBody(res, hasBody) {
54759
55023
  return out;
54760
55024
  }
54761
55025
  function requestOnce(url, pinned, init) {
54762
- return new Promise((resolve41, reject) => {
55026
+ return new Promise((resolve42, reject) => {
54763
55027
  const headers2 = { host: url.host, "accept-encoding": "identity" };
54764
55028
  new Headers(init.headers ?? {}).forEach((value, key2) => {
54765
55029
  if (key2.toLowerCase() !== "host") headers2[key2] = value;
@@ -54793,7 +55057,7 @@ function requestOnce(url, pinned, init) {
54793
55057
  reject(err);
54794
55058
  return;
54795
55059
  }
54796
- resolve41(new Response(body, { status, statusText: res.statusMessage ?? "", headers: out }));
55060
+ resolve42(new Response(body, { status, statusText: res.statusMessage ?? "", headers: out }));
54797
55061
  }
54798
55062
  );
54799
55063
  req.on("error", reject);
@@ -54902,7 +55166,7 @@ var init_brand_font = __esm({
54902
55166
 
54903
55167
  // src/site-analyze/write-artifacts.ts
54904
55168
  import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync20 } from "node:fs";
54905
- import { join as join47 } from "node:path";
55169
+ import { join as join48 } from "node:path";
54906
55170
  function attachVeloSources(book, sources, put) {
54907
55171
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
54908
55172
  for (const f of book.velo.files) {
@@ -55057,12 +55321,12 @@ async function downloadMedia(book, fetchImpl, put) {
55057
55321
  }
55058
55322
  async function writeBusinessBook(result, opts) {
55059
55323
  const out = opts.outDir;
55060
- mkdirSync23(join47(out, "evidence/pages"), { recursive: true });
55061
- mkdirSync23(join47(out, "media/catalog"), { recursive: true });
55324
+ mkdirSync23(join48(out, "evidence/pages"), { recursive: true });
55325
+ mkdirSync23(join48(out, "media/catalog"), { recursive: true });
55062
55326
  const files = [];
55063
55327
  const put = (rel, body) => {
55064
- const p = join47(out, rel);
55065
- mkdirSync23(join47(p, ".."), { recursive: true });
55328
+ const p = join48(out, rel);
55329
+ mkdirSync23(join48(p, ".."), { recursive: true });
55066
55330
  writeFileSync20(p, body);
55067
55331
  files.push(rel);
55068
55332
  };
@@ -55393,15 +55657,15 @@ var init_home_descriptor = __esm({
55393
55657
  });
55394
55658
 
55395
55659
  // src/site-analyze/apply.ts
55396
- import { existsSync as existsSync49, readFileSync as readFileSync60, writeFileSync as writeFileSync21, copyFileSync as copyFileSync3, mkdirSync as mkdirSync24, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
55397
- import { dirname as dirname27, isAbsolute as isAbsolute3, join as join48, resolve as resolve38, sep as sep10 } from "node:path";
55660
+ import { existsSync as existsSync50, readFileSync as readFileSync61, writeFileSync as writeFileSync21, copyFileSync as copyFileSync3, mkdirSync as mkdirSync24, realpathSync as realpathSync5, statSync as statSync11 } from "node:fs";
55661
+ import { dirname as dirname28, isAbsolute as isAbsolute4, join as join49, resolve as resolve39, sep as sep10 } from "node:path";
55398
55662
  import { spawnSync as spawnSync4 } from "node:child_process";
55399
55663
  function unpairedRedirectNote(overlay) {
55400
55664
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
55401
55665
  return `no OAuth redirect paired with client ${overlay.wix.clientId} \u2014 the app (and prepare:customer) will refuse to start until one is set. Register it with \`himi site client ensure --site ${overlay.siteId?.trim() || "<msid>"} --redirect-uri <uri> --yes\`, then re-run analyze with \`--redirect-uri <uri>\` (or export HIMI_BRANDED_LITE_WIX_REDIRECT_URI)`;
55402
55666
  }
55403
55667
  function posix2(p) {
55404
- return resolve38(p).replace(/\\/g, "/");
55668
+ return resolve39(p).replace(/\\/g, "/");
55405
55669
  }
55406
55670
  function committedShellProvisioning(path) {
55407
55671
  return committedShellFor(posix2(path));
@@ -55409,8 +55673,8 @@ function committedShellProvisioning(path) {
55409
55673
  function findRepoRoot(cwd) {
55410
55674
  let dir = cwd;
55411
55675
  for (let i = 0; i < 8; i++) {
55412
- if (SHELL_NAMES2.some((name) => existsSync49(join48(dir, shellAppDir(name), "package.json")))) return dir;
55413
- const next = dirname27(dir);
55676
+ if (SHELL_NAMES2.some((name) => existsSync50(join49(dir, shellAppDir(name), "package.json")))) return dir;
55677
+ const next = dirname28(dir);
55414
55678
  if (next === dir) break;
55415
55679
  dir = next;
55416
55680
  }
@@ -55435,7 +55699,7 @@ function assertNotCommitted(path, forbidden) {
55435
55699
  `refusing to overwrite committed ${committedProvisioningPath(committed)} \u2014 use prepare:customer --contract`
55436
55700
  );
55437
55701
  }
55438
- if (forbidden && resolve38(path) === resolve38(forbidden)) {
55702
+ if (forbidden && resolve39(path) === resolve39(forbidden)) {
55439
55703
  const named = committedShellProvisioning(forbidden);
55440
55704
  throw new Error(
55441
55705
  `refusing to overwrite committed ${named ? committedProvisioningPath(named) : forbidden} \u2014 pass a content-package provisioning.json or use prepare:customer --contract`
@@ -55477,24 +55741,24 @@ function mergeCustomerOwned(committed, overlay) {
55477
55741
  }
55478
55742
  function buildMergedContract(options) {
55479
55743
  const { appDir: appDir2, packageDir, overlay } = options;
55480
- const committedPath = join48(appDir2, "provisioning.json");
55481
- if (!existsSync49(committedPath)) {
55744
+ const committedPath = join49(appDir2, "provisioning.json");
55745
+ if (!existsSync50(committedPath)) {
55482
55746
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
55483
55747
  }
55484
- const committed = JSON.parse(readFileSync60(committedPath, "utf8"));
55748
+ const committed = JSON.parse(readFileSync61(committedPath, "utf8"));
55485
55749
  const merged = mergeCustomerOwned(committed, overlay);
55486
55750
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
55487
55751
  const branding = merged.branding ?? {};
55488
55752
  const staged = [];
55489
55753
  const contained = (root, rel) => {
55490
- if (isAbsolute3(rel)) return void 0;
55491
- const full = resolve38(root, rel);
55492
- return full.startsWith(resolve38(root) + sep10) ? full : void 0;
55754
+ if (isAbsolute4(rel)) return void 0;
55755
+ const full = resolve39(root, rel);
55756
+ return full.startsWith(resolve39(root) + sep10) ? full : void 0;
55493
55757
  };
55494
55758
  const stage = (fromRel, toRel) => {
55495
55759
  const from = contained(packageDir, fromRel);
55496
- const to = contained(join48(appDir2, STAGED), toRel.slice(STAGED.length + 1));
55497
- if (!from || !to || !existsSync49(from)) return void 0;
55760
+ const to = contained(join49(appDir2, STAGED), toRel.slice(STAGED.length + 1));
55761
+ if (!from || !to || !existsSync50(from)) return void 0;
55498
55762
  let real;
55499
55763
  let realRoot;
55500
55764
  try {
@@ -55505,8 +55769,8 @@ function buildMergedContract(options) {
55505
55769
  }
55506
55770
  if (!real.startsWith(realRoot + sep10)) return void 0;
55507
55771
  try {
55508
- if (!statSync12(real).isFile()) return void 0;
55509
- mkdirSync24(dirname27(to), { recursive: true });
55772
+ if (!statSync11(real).isFile()) return void 0;
55773
+ mkdirSync24(dirname28(to), { recursive: true });
55510
55774
  copyFileSync3(real, to);
55511
55775
  } catch {
55512
55776
  return void 0;
@@ -55516,11 +55780,11 @@ function buildMergedContract(options) {
55516
55780
  };
55517
55781
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
55518
55782
  if (icon) branding.appIconPath = icon;
55519
- const facesPath = join48(packageDir, "media/fonts/faces.json");
55520
- if (existsSync49(facesPath)) {
55783
+ const facesPath = join49(packageDir, "media/fonts/faces.json");
55784
+ if (existsSync50(facesPath)) {
55521
55785
  let faces = [];
55522
55786
  try {
55523
- const parsed = JSON.parse(readFileSync60(facesPath, "utf8"));
55787
+ const parsed = JSON.parse(readFileSync61(facesPath, "utf8"));
55524
55788
  faces = Array.isArray(parsed) ? parsed : [];
55525
55789
  } catch {
55526
55790
  faces = [];
@@ -55541,20 +55805,20 @@ function buildMergedContract(options) {
55541
55805
  merged.branding = branding;
55542
55806
  const body = `${JSON.stringify(merged, null, 2)}
55543
55807
  `;
55544
- const path = join48(packageDir, "provisioning.contract.json");
55808
+ const path = join49(packageDir, "provisioning.contract.json");
55545
55809
  writeFileSync21(path, body);
55546
- const localPath = join48(appDir2, LOCAL_CONTRACT_REL);
55547
- mkdirSync24(dirname27(localPath), { recursive: true });
55810
+ const localPath = join49(appDir2, LOCAL_CONTRACT_REL);
55811
+ mkdirSync24(dirname28(localPath), { recursive: true });
55548
55812
  writeFileSync21(localPath, body);
55549
55813
  staged.push(localPath);
55550
55814
  return { path, staged };
55551
55815
  }
55552
55816
  async function applyOverlays(input) {
55553
- const cwd = resolve38(input.cwd);
55817
+ const cwd = resolve39(input.cwd);
55554
55818
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
55555
55819
  assertKnownShell(shell);
55556
- const overlay = JSON.parse(readFileSync60(input.overlayPath, "utf8"));
55557
- const tokens = JSON.parse(readFileSync60(input.tokensPath, "utf8"));
55820
+ const overlay = JSON.parse(readFileSync61(input.overlayPath, "utf8"));
55821
+ const tokens = JSON.parse(readFileSync61(input.tokensPath, "utf8"));
55558
55822
  const wrote = [];
55559
55823
  const notes = [];
55560
55824
  const repoRoot2 = findRepoRoot(cwd);
@@ -55564,8 +55828,8 @@ async function applyOverlays(input) {
55564
55828
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
55565
55829
  );
55566
55830
  }
55567
- const appDir2 = join48(repoRoot2, shellAppDir(shell));
55568
- const packageDir = dirname27(resolve38(input.overlayPath));
55831
+ const appDir2 = join49(repoRoot2, shellAppDir(shell));
55832
+ const packageDir = dirname28(resolve39(input.overlayPath));
55569
55833
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
55570
55834
  wrote.push(...staged);
55571
55835
  const r = spawnSync4("npm", ["run", "prepare:customer", "--", "--contract", contract], {
@@ -55586,10 +55850,10 @@ async function applyOverlays(input) {
55586
55850
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
55587
55851
  };
55588
55852
  }
55589
- const pkgProv = join48(cwd, "provisioning.json");
55853
+ const pkgProv = join49(cwd, "provisioning.json");
55590
55854
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
55591
- if (existsSync49(pkgProv)) {
55592
- const cur = JSON.parse(readFileSync60(pkgProv, "utf8"));
55855
+ if (existsSync50(pkgProv)) {
55856
+ const cur = JSON.parse(readFileSync61(pkgProv, "utf8"));
55593
55857
  const merged = deepMerge3(cur, overlay);
55594
55858
  writeFileSync21(pkgProv, JSON.stringify(merged, null, 2) + "\n");
55595
55859
  wrote.push(pkgProv);
@@ -55597,17 +55861,17 @@ async function applyOverlays(input) {
55597
55861
  writeFileSync21(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
55598
55862
  wrote.push(pkgProv);
55599
55863
  }
55600
- const pkgTokens = join48(cwd, "tokens.json");
55601
- if (existsSync49(pkgTokens) && (tokens.colors || tokens.typography)) {
55602
- const cur = JSON.parse(readFileSync60(pkgTokens, "utf8"));
55864
+ const pkgTokens = join49(cwd, "tokens.json");
55865
+ if (existsSync50(pkgTokens) && (tokens.colors || tokens.typography)) {
55866
+ const cur = JSON.parse(readFileSync61(pkgTokens, "utf8"));
55603
55867
  const merged = deepMerge3(cur, tokens);
55604
55868
  writeFileSync21(pkgTokens, JSON.stringify(merged, null, 2) + "\n");
55605
55869
  wrote.push(pkgTokens);
55606
55870
  }
55607
55871
  const homeAction = homeRefreshActionId(shell);
55608
- const homeSrc = join48(cwd, "dev/index.ts");
55609
- if (homeAction && existsSync49(homeSrc)) {
55610
- const prev = readFileSync60(homeSrc, "utf8");
55872
+ const homeSrc = join49(cwd, "dev/index.ts");
55873
+ if (homeAction && existsSync50(homeSrc)) {
55874
+ const prev = readFileSync61(homeSrc, "utf8");
55611
55875
  const patched = patchBrandParams(prev, overlay, homeAction);
55612
55876
  let next = patched.src;
55613
55877
  if (!patched.foreign && overlay.installedVerticals?.length) {
@@ -55627,13 +55891,13 @@ async function applyOverlays(input) {
55627
55891
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
55628
55892
  }
55629
55893
  }
55630
- if (!input.iconPath || !existsSync49(input.iconPath)) {
55631
- const book = input.iconPath ? dirname27(dirname27(resolve38(input.iconPath))) : void 0;
55894
+ if (!input.iconPath || !existsSync50(input.iconPath)) {
55895
+ const book = input.iconPath ? dirname28(dirname28(resolve39(input.iconPath))) : void 0;
55632
55896
  notes.push(
55633
55897
  book ? `no icon installed \u2014 ${siteLogoAbsence(book).hint}` : "no icon installed and no business book to say why \u2014 `himi icon generate` draws one from the accent, or `himi icon set <file.png>` installs the customer's own mark"
55634
55898
  );
55635
55899
  }
55636
- if (input.iconPath && existsSync49(input.iconPath)) {
55900
+ if (input.iconPath && existsSync50(input.iconPath)) {
55637
55901
  try {
55638
55902
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
55639
55903
  wrote.push(...installed.wrote);
@@ -56767,13 +57031,13 @@ __export(cli_exports, {
56767
57031
  runSiteAnalyze: () => runSiteAnalyze,
56768
57032
  runSiteFunctionCli: () => runSiteFunctionCli
56769
57033
  });
56770
- import { readFileSync as readFileSync61 } from "node:fs";
56771
- import { resolve as resolve39 } from "node:path";
57034
+ import { readFileSync as readFileSync62 } from "node:fs";
57035
+ import { resolve as resolve40 } from "node:path";
56772
57036
  async function runSiteAnalyze(opts) {
56773
57037
  const fetchImpl = opts.fetchImpl ?? fetch;
56774
57038
  const assetFetch = opts.fetchImpl ?? pinnedFetch2;
56775
57039
  const cwd = opts.cwd ?? process.cwd();
56776
- const outDir = resolve39(cwd, opts.outDir ?? ".himi/business-book");
57040
+ const outDir = resolve40(cwd, opts.outDir ?? ".himi/business-book");
56777
57041
  let ownerAuth;
56778
57042
  let msid;
56779
57043
  try {
@@ -56810,9 +57074,9 @@ async function runSiteAnalyze(opts) {
56810
57074
  if (opts.apply) {
56811
57075
  applied = await applyOverlays({
56812
57076
  cwd,
56813
- overlayPath: resolve39(outDir, "provisioning.overlay.json"),
56814
- tokensPath: resolve39(outDir, "tokens.overlay.json"),
56815
- iconPath: resolve39(outDir, "media/icon.png"),
57077
+ overlayPath: resolve40(outDir, "provisioning.overlay.json"),
57078
+ tokensPath: resolve40(outDir, "tokens.overlay.json"),
57079
+ iconPath: resolve40(outDir, "media/icon.png"),
56816
57080
  shell: opts.shell
56817
57081
  });
56818
57082
  const note = noteWithWarnings(applied.note, warnings);
@@ -56848,8 +57112,8 @@ async function runSiteFunctionCli(opts) {
56848
57112
  const token = opts.token ?? await siteToken(msid);
56849
57113
  let source;
56850
57114
  if (opts.sourcePath) {
56851
- const p = resolve39(opts.cwd ?? process.cwd(), opts.sourcePath);
56852
- source = readFileSync61(p, "utf8");
57115
+ const p = resolve40(opts.cwd ?? process.cwd(), opts.sourcePath);
57116
+ source = readFileSync62(p, "utf8");
56853
57117
  }
56854
57118
  let body;
56855
57119
  if (opts.bodyRaw != null) {
@@ -56900,7 +57164,7 @@ async function accountToken2(spawnImpl = spawn4) {
56900
57164
  cached2 = fromEnv;
56901
57165
  return fromEnv;
56902
57166
  }
56903
- const token = await new Promise((resolve41, reject) => {
57167
+ const token = await new Promise((resolve42, reject) => {
56904
57168
  let child;
56905
57169
  try {
56906
57170
  child = spawnImpl("wix", ["token"]);
@@ -56929,7 +57193,7 @@ async function accountToken2(spawnImpl = spawn4) {
56929
57193
  const scan = (d) => {
56930
57194
  out += String(d);
56931
57195
  const m = out.match(TOKEN_RE2);
56932
- if (m) done(() => resolve41(m[0]));
57196
+ if (m) done(() => resolve42(m[0]));
56933
57197
  };
56934
57198
  child.stdout?.on("data", scan);
56935
57199
  child.stderr?.on("data", scan);
@@ -57106,7 +57370,7 @@ __export(site_create_exports, {
57106
57370
  runSiteList: () => runSiteList,
57107
57371
  writeProvisioningBinding: () => writeProvisioningBinding
57108
57372
  });
57109
- import { existsSync as existsSync50, readFileSync as readFileSync62, writeFileSync as writeFileSync22 } from "node:fs";
57373
+ import { existsSync as existsSync51, readFileSync as readFileSync63, writeFileSync as writeFileSync22 } from "node:fs";
57110
57374
  import { resolve as resolvePath2 } from "node:path";
57111
57375
  function oauthClientName(projectName) {
57112
57376
  return `${projectName} client`;
@@ -57255,7 +57519,7 @@ async function runSiteCreate(opts) {
57255
57519
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
57256
57520
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
57257
57521
  }
57258
- function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync50, readFileSync: readFileSync62, writeFileSync: writeFileSync22 }) {
57522
+ function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync51, readFileSync: readFileSync63, writeFileSync: writeFileSync22 }) {
57259
57523
  const msid = binding2.siteId;
57260
57524
  const path = resolvePath2(target);
57261
57525
  const committed = committedShellProvisioning(path);
@@ -57589,11 +57853,11 @@ var init_site_oauth_client = __esm({
57589
57853
  });
57590
57854
 
57591
57855
  // src/cli.ts
57592
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync63, mkdirSync as mkdirSync25, writeFileSync as writeFileSync23, existsSync as existsSync51, rmSync as rmSync9, readdirSync as readdirSync23, statSync as statSync13, lstatSync as lstatSync5 } from "node:fs";
57856
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync64, mkdirSync as mkdirSync25, writeFileSync as writeFileSync23, existsSync as existsSync52, rmSync as rmSync9, readdirSync as readdirSync23, statSync as statSync12, lstatSync as lstatSync5 } from "node:fs";
57593
57857
  import { execFileSync as execFileSync6 } from "node:child_process";
57594
57858
  import { createHash as createHash24 } from "node:crypto";
57595
57859
  import { tmpdir as tmpdir5 } from "node:os";
57596
- import { join as join49, resolve as resolve40, basename as basename9, dirname as dirname28 } from "node:path";
57860
+ import { join as join50, resolve as resolve41, basename as basename9, dirname as dirname29 } from "node:path";
57597
57861
  import { fileURLToPath as fileURLToPath21 } from "node:url";
57598
57862
 
57599
57863
  // ../serve-cli/src/client.ts
@@ -59109,7 +59373,7 @@ async function browserLogin(opts) {
59109
59373
  const log = opts.log ?? (() => {
59110
59374
  });
59111
59375
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
59112
- return await new Promise((resolve41, reject) => {
59376
+ return await new Promise((resolve42, reject) => {
59113
59377
  let settled = false;
59114
59378
  let loginUrl = "";
59115
59379
  const server = createServer();
@@ -59173,7 +59437,7 @@ async function browserLogin(opts) {
59173
59437
  return;
59174
59438
  }
59175
59439
  res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", connection: "close" }).end(CLOSE_PAGE);
59176
- finish2(() => resolve41({ token, loginUrl }));
59440
+ finish2(() => resolve42({ token, loginUrl }));
59177
59441
  });
59178
59442
  });
59179
59443
  server.on("error", (err) => finish2(() => reject(err)));
@@ -60583,17 +60847,18 @@ init_crawl_layers();
60583
60847
  // src/ux-audit.ts
60584
60848
  init_wix_gateway_rules();
60585
60849
  init_mobile_ux_contract();
60586
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync23, readdirSync as readdirSync11, writeFileSync as writeFileSync9 } from "node:fs";
60587
- import { join as join25, relative as relative3, resolve as resolve15 } from "node:path";
60588
- var EVIDENCE_RELATIVE_PATH = ".himi/ux-audit.json";
60850
+ init_ux_evidence();
60851
+ init_ux_evidence();
60852
+ import { existsSync as existsSync22, readFileSync as readFileSync24, readdirSync as readdirSync11 } from "node:fs";
60853
+ import { join as join26, relative as relative4, resolve as resolve16 } from "node:path";
60589
60854
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".json"]);
60590
60855
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".himi", "coverage"]);
60591
60856
  function issue(issues, strict, code, message, path) {
60592
60857
  issues.push({ code, message, ...path ? { path } : {}, severity: strict ? "error" : "warning" });
60593
60858
  }
60594
- function readText(path) {
60859
+ function readText2(path) {
60595
60860
  try {
60596
- return existsSync21(path) ? readFileSync23(path, "utf8") : null;
60861
+ return existsSync22(path) ? readFileSync24(path, "utf8") : null;
60597
60862
  } catch {
60598
60863
  return null;
60599
60864
  }
@@ -60602,32 +60867,19 @@ function walkSource(dir, root = dir) {
60602
60867
  const found = [];
60603
60868
  for (const entry of readdirSync11(dir, { withFileTypes: true })) {
60604
60869
  if (entry.isDirectory()) {
60605
- if (!IGNORED_DIRECTORIES.has(entry.name)) found.push(...walkSource(join25(dir, entry.name), root));
60870
+ if (!IGNORED_DIRECTORIES.has(entry.name)) found.push(...walkSource(join26(dir, entry.name), root));
60606
60871
  continue;
60607
60872
  }
60608
60873
  const ext = entry.name.slice(entry.name.lastIndexOf("."));
60609
60874
  if (!SOURCE_EXTENSIONS.has(ext)) continue;
60610
- const absolute = join25(dir, entry.name);
60611
- const text2 = readText(absolute);
60612
- if (text2 !== null) found.push({ path: relative3(root, absolute), text: text2 });
60875
+ const absolute = join26(dir, entry.name);
60876
+ const text2 = readText2(absolute);
60877
+ if (text2 !== null) found.push({ path: relative4(root, absolute), text: text2 });
60613
60878
  }
60614
60879
  return found;
60615
60880
  }
60616
- function parseEvidence(path) {
60617
- const raw = readText(path);
60618
- if (raw === null) return { evidence: null };
60619
- try {
60620
- const value = JSON.parse(raw);
60621
- if (value.version !== 1 || !value.platforms || typeof value.platforms !== "object") {
60622
- return { evidence: null, error: "must be an object with version: 1 and a platforms object" };
60623
- }
60624
- return { evidence: value };
60625
- } catch (error) {
60626
- return { evidence: null, error: `is not valid JSON: ${error.message}` };
60627
- }
60628
- }
60629
60881
  function blankMatrixRows(text2) {
60630
- const acceptance = text2.match(/## Implementation acceptance\s*([\s\S]*?)(?=\n##\s|$)/i)?.[1] ?? "";
60882
+ const acceptance = mobileUxSection(mobileUxDocumentBody(text2), "Implementation acceptance") ?? "";
60631
60883
  return /\|\s*(?:Ready|Empty|Add\s*\/\s*edit|Saved\s*\/\s*completed|Error\s*\/\s*recovery)\s*\|\s*\|\s*\|\s*\|\s*\|/i.test(acceptance);
60632
60884
  }
60633
60885
  function usesAppNetwork(source) {
@@ -60635,40 +60887,41 @@ function usesAppNetwork(source) {
60635
60887
  return source.some(({ text: text2 }) => /\bwixApi\b|(?:from|import)\s*["']@(?:himalaya|wix\/himalaya)\/wix-[^"']+["']/.test(code(text2)));
60636
60888
  }
60637
60889
  function auditUx(content, strict = false) {
60638
- const dir = resolve15(content);
60890
+ const dir = resolve16(content);
60639
60891
  const issues = [];
60640
- const evidencePath = join25(dir, EVIDENCE_RELATIVE_PATH);
60641
- const spec = readText(join25(dir, "SPEC.md"));
60642
- const mobile = readText(join25(dir, "MOBILE-UX.md"));
60892
+ const evidencePath = join26(dir, UX_EVIDENCE_PATH);
60893
+ const parsed = parseEvidence(evidencePath);
60894
+ const renderedWidths = renderEvidenceEntries(parsed.evidence).map((entry) => entry.width);
60895
+ const spec = readText2(join26(dir, "SPEC.md"));
60896
+ const mobile = readText2(join26(dir, "MOBILE-UX.md"));
60643
60897
  if (!spec) issue(issues, strict, "missing-spec", "SPEC.md is missing; define the customer task before calling this UX-ready.", "SPEC.md");
60644
60898
  if (!mobile) {
60645
60899
  issue(issues, strict, "missing-mobile-ux", "MOBILE-UX.md is missing; add the mobile state and platform contract.", "MOBILE-UX.md");
60646
60900
  } else {
60647
- for (const finding of mobileUxMarkerFindings(mobile)) issue(issues, strict, finding.code, finding.message, "MOBILE-UX.md");
60901
+ for (const finding of mobileUxMarkerFindings(mobile, renderedWidths)) issue(issues, strict, finding.code, finding.message, "MOBILE-UX.md");
60648
60902
  if (blankMatrixRows(mobile)) issue(issues, strict, "blank-acceptance-matrix", "The seeded acceptance rows are still blank; replace them with this app's flows and visible results.", "MOBILE-UX.md");
60649
60903
  }
60650
- const parsed = parseEvidence(evidencePath);
60651
60904
  if (parsed.error) {
60652
- issues.push({ code: "invalid-native-evidence", message: `${EVIDENCE_RELATIVE_PATH} ${parsed.error}.`, path: EVIDENCE_RELATIVE_PATH, severity: "error" });
60905
+ issues.push({ code: "invalid-native-evidence", message: `${UX_EVIDENCE_PATH} ${parsed.error}.`, path: UX_EVIDENCE_PATH, severity: "error" });
60653
60906
  }
60654
60907
  for (const platform of ["ios", "android"]) {
60655
60908
  const record2 = parsed.evidence?.platforms[platform];
60656
60909
  if (!record2) {
60657
- issue(issues, strict, `missing-${platform}-audit`, `No ${platform} native audit is recorded. Use \`himi ux record --platform ${platform} \u2026\` after driving the installed app.`, EVIDENCE_RELATIVE_PATH);
60910
+ issue(issues, strict, `missing-${platform}-audit`, `No ${platform} native audit is recorded. Use \`himi ux record --platform ${platform} \u2026\` after driving the installed app.`, UX_EVIDENCE_PATH);
60658
60911
  continue;
60659
60912
  }
60660
60913
  if (!["passed", "failed", "unavailable"].includes(record2.status)) {
60661
- issues.push({ code: `invalid-${platform}-status`, message: `${platform} audit has an invalid status.`, path: EVIDENCE_RELATIVE_PATH, severity: "error" });
60914
+ issues.push({ code: `invalid-${platform}-status`, message: `${platform} audit has an invalid status.`, path: UX_EVIDENCE_PATH, severity: "error" });
60662
60915
  } else if (record2.status === "failed") {
60663
- issues.push({ code: `${platform}-audit-failed`, message: `${platform} native audit is recorded as failed; fix it or record a justified unavailable platform.`, path: EVIDENCE_RELATIVE_PATH, severity: "error" });
60916
+ issues.push({ code: `${platform}-audit-failed`, message: `${platform} native audit is recorded as failed; fix it or record a justified unavailable platform.`, path: UX_EVIDENCE_PATH, severity: "error" });
60664
60917
  }
60665
60918
  const malformedChecks = record2.checks !== void 0 && !Array.isArray(record2.checks);
60666
60919
  if (malformedChecks || record2.status !== "unavailable" && (record2.checks ?? []).length === 0) {
60667
60920
  const message = malformedChecks ? `${platform} audit has a malformed checks list; it must be an array.` : `${platform} audit needs the checks that were driven.`;
60668
- issues.push({ code: `missing-${platform}-checks`, message, path: EVIDENCE_RELATIVE_PATH, severity: "error" });
60921
+ issues.push({ code: `missing-${platform}-checks`, message, path: UX_EVIDENCE_PATH, severity: "error" });
60669
60922
  }
60670
60923
  if (record2.status === "unavailable" && !record2.reason?.trim()) {
60671
- issues.push({ code: `missing-${platform}-reason`, message: `${platform} is unavailable but has no reason.`, path: EVIDENCE_RELATIVE_PATH, severity: "error" });
60924
+ issues.push({ code: `missing-${platform}-reason`, message: `${platform} is unavailable but has no reason.`, path: UX_EVIDENCE_PATH, severity: "error" });
60672
60925
  }
60673
60926
  }
60674
60927
  const source = walkSource(dir);
@@ -60680,12 +60933,12 @@ function auditUx(content, strict = false) {
60680
60933
  const preview = matches(previewControl);
60681
60934
  if (preview) issue(issues, strict, "possible-preview-control", "Possible customer-visible test/demo state control found. Keep scenario controls out of shipping UI, or document a capability-limited fallback.", preview.path);
60682
60935
  if (usesAppNetwork(source)) {
60683
- const mocksPath = join25(dir, "dev", "net-mocks.json");
60684
- if (!existsSync21(mocksPath)) {
60936
+ const mocksPath = join26(dir, "dev", "net-mocks.json");
60937
+ if (!existsSync22(mocksPath)) {
60685
60938
  issue(issues, strict, "missing-app-mocks", "This package uses a Wix/network client but has no app-specific dev/net-mocks.json. Add fixtures for dynamic, entitlement, or recovery flows.", "dev/net-mocks.json");
60686
60939
  } else {
60687
60940
  try {
60688
- const parsed2 = JSON.parse(readFileSync23(mocksPath, "utf8"));
60941
+ const parsed2 = JSON.parse(readFileSync24(mocksPath, "utf8"));
60689
60942
  const errors2 = Array.isArray(parsed2?.rules) ? wixMockRuleErrors(parsed2.rules) : ['missing top-level "rules" array'];
60690
60943
  if (errors2.length) issue(issues, strict, "invalid-app-mocks", `dev/net-mocks.json cannot serve the flows it claims to mock: ${errors2.join("; ")}`, "dev/net-mocks.json");
60691
60944
  } catch (error) {
@@ -60695,49 +60948,33 @@ function auditUx(content, strict = false) {
60695
60948
  }
60696
60949
  const errors = issues.filter((item) => item.severity === "error");
60697
60950
  const warnings = issues.filter((item) => item.severity === "warning");
60698
- return { ok: errors.length === 0, strict, content: dir, evidencePath: EVIDENCE_RELATIVE_PATH, errors, warnings, counts: { errors: errors.length, warnings: warnings.length } };
60699
- }
60700
- function recordNativeAudit(content, platform, status, checks, reason) {
60701
- const normalizedChecks = status === "unavailable" ? [] : [...new Set(checks.map((check) => check.trim()).filter(Boolean))];
60702
- if (status !== "unavailable" && !normalizedChecks.length) {
60703
- throw new Error("--checks needs at least one comma-separated check (for example: back,keyboard,voiceover)");
60704
- }
60705
- if (status === "unavailable" && !reason?.trim()) throw new Error("--reason is required when --status unavailable");
60706
- const dir = resolve15(content);
60707
- const path = join25(dir, EVIDENCE_RELATIVE_PATH);
60708
- const parsed = parseEvidence(path);
60709
- if (parsed.error) throw new Error(`${EVIDENCE_RELATIVE_PATH} ${parsed.error}`);
60710
- const evidence = parsed.evidence ?? { version: 1, platforms: {} };
60711
- evidence.platforms[platform] = { status, checks: normalizedChecks, ...reason?.trim() ? { reason: reason.trim() } : {}, recordedAt: (/* @__PURE__ */ new Date()).toISOString() };
60712
- mkdirSync10(join25(dir, ".himi"), { recursive: true });
60713
- writeFileSync9(path, JSON.stringify(evidence, null, 2) + "\n");
60714
- return evidence;
60951
+ return { ok: errors.length === 0, strict, content: dir, evidencePath: UX_EVIDENCE_PATH, errors, warnings, counts: { errors: errors.length, warnings: warnings.length } };
60715
60952
  }
60716
60953
 
60717
60954
  // src/guides.ts
60718
60955
  init_mobile_ux_contract();
60719
- import { readFileSync as readFileSync24, existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10, copyFileSync as copyFileSync2 } from "node:fs";
60956
+ import { readFileSync as readFileSync25, existsSync as existsSync23, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10, copyFileSync as copyFileSync2 } from "node:fs";
60720
60957
  import { fileURLToPath as fileURLToPath10 } from "node:url";
60721
- import { dirname as dirname15, join as join26, resolve as resolve16 } from "node:path";
60722
- var GUIDES_DIR = resolve16(dirname15(fileURLToPath10(import.meta.url)), "../guides");
60958
+ import { dirname as dirname16, join as join27, resolve as resolve17 } from "node:path";
60959
+ var GUIDES_DIR = resolve17(dirname16(fileURLToPath10(import.meta.url)), "../guides");
60723
60960
  function guidesAvailable() {
60724
- return existsSync22(join26(GUIDES_DIR, "index.json"));
60961
+ return existsSync23(join27(GUIDES_DIR, "index.json"));
60725
60962
  }
60726
60963
  function loadIndex() {
60727
- return JSON.parse(readFileSync24(join26(GUIDES_DIR, "index.json"), "utf8"));
60964
+ return JSON.parse(readFileSync25(join27(GUIDES_DIR, "index.json"), "utf8"));
60728
60965
  }
60729
60966
  function readGuide(id) {
60730
60967
  const meta = loadIndex().guides.find((g) => g.id === id);
60731
60968
  if (!meta) return null;
60732
- return readFileSync24(join26(GUIDES_DIR, meta.file), "utf8");
60969
+ return readFileSync25(join27(GUIDES_DIR, meta.file), "utf8");
60733
60970
  }
60734
60971
  function loadComponentReference() {
60735
60972
  const idx = loadIndex();
60736
- return JSON.parse(readFileSync24(join26(GUIDES_DIR, idx.componentReferenceFile), "utf8"));
60973
+ return JSON.parse(readFileSync25(join27(GUIDES_DIR, idx.componentReferenceFile), "utf8"));
60737
60974
  }
60738
60975
  function loadSdkReference() {
60739
60976
  const idx = loadIndex();
60740
- return JSON.parse(readFileSync24(join26(GUIDES_DIR, idx.sdkReferenceFile), "utf8"));
60977
+ return JSON.parse(readFileSync25(join27(GUIDES_DIR, idx.sdkReferenceFile), "utf8"));
60741
60978
  }
60742
60979
  function buildAgentPrimer() {
60743
60980
  const idx = loadIndex();
@@ -60846,19 +61083,19 @@ function writeAgentContext(targetDir) {
60846
61083
  const idx = loadIndex();
60847
61084
  const files = [];
60848
61085
  const write2 = (rel, body) => {
60849
- const p = join26(targetDir, rel);
60850
- mkdirSync11(dirname15(p), { recursive: true });
61086
+ const p = join27(targetDir, rel);
61087
+ mkdirSync11(dirname16(p), { recursive: true });
60851
61088
  writeFileSync10(p, body);
60852
61089
  files.push(rel);
60853
61090
  };
60854
61091
  const copy = (srcRel, destRel) => {
60855
- const p = join26(targetDir, destRel);
60856
- mkdirSync11(dirname15(p), { recursive: true });
60857
- copyFileSync2(join26(GUIDES_DIR, srcRel), p);
61092
+ const p = join27(targetDir, destRel);
61093
+ mkdirSync11(dirname16(p), { recursive: true });
61094
+ copyFileSync2(join27(GUIDES_DIR, srcRel), p);
60858
61095
  files.push(destRel);
60859
61096
  };
60860
61097
  const writeIfMissing = (rel, body) => {
60861
- if (existsSync22(join26(targetDir, rel))) return;
61098
+ if (existsSync23(join27(targetDir, rel))) return;
60862
61099
  write2(rel, body);
60863
61100
  };
60864
61101
  write2("HIMALAYA.md", buildAgentPrimer());
@@ -60919,8 +61156,8 @@ var str5 = (v) => typeof v === "string" ? v : void 0;
60919
61156
  var num = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
60920
61157
  function embeddedTemplateCatalog() {
60921
61158
  return availableTemplates().map((name) => {
60922
- const path = join49(templateSourceDir(name), TEMPLATE_META_FILE);
60923
- const meta = JSON.parse(readFileSync63(path, "utf8"));
61159
+ const path = join50(templateSourceDir(name), TEMPLATE_META_FILE);
61160
+ const meta = JSON.parse(readFileSync64(path, "utf8"));
60924
61161
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
60925
61162
  throw new Error(`${path} must be { title: string, description: string, tags: string[], category?: string }`);
60926
61163
  }
@@ -61377,7 +61614,7 @@ var COMMANDS = [
61377
61614
  { name: "functions", summary: "inspect/invoke server functions and operate durable jobs or schedules", required: "list|describe|openapi|invoke | jobs list|get|cancel|retry|dlq | schedule describe|invoke [--app <a>] [--environment preview|production] [--release <id>]" },
61378
61615
  { name: "eject", summary: "take ownership of a shared flow module (auth|checkout|screens): copies its source into tier5-src/_ejected/ and repoints this package's imports at the copy. The ladder's escape hatch \u2014 for when the factory options cannot express what you need. AFTER EJECTING, CENTRAL FIXES NO LONGER REACH THIS APP, including future corrections to the fail-closed entitlement and confirm-the-session rules. The floor is unaffected: credentials still go only through the native auth actions and money only through the cashier rail.", required: "<auth|checkout|screens> [--content <dir>]" },
61379
61616
  { name: "validate", summary: "lint the content package (no push): fixed-contract TypeScript checking for tier5-src workers + schema/proto + token refs + component allowlist + $service bindings + strict-enum foot-guns + capability decls + worker boot dry-run + build/compile failures. Type issues are advisory warnings normally; --strict promotes them and hygiene warnings to errors. Exit 1 on any error. Off-repo, the CLI supplies its compiler and SDK declarations.", required: "[--content <dir>] [--strict]" },
61380
- { name: "test", summary: "DERIVED behavioral crawl \u2014 no test files to write. Boots each screen's real worker, replays its onAppear, then dispatches every action site the descriptor already declares (each from a fresh boot) and checks a narrow set of unambiguous invariants: no uncaught throw, never stuck on `loading`, no `$service:` token or NaN/undefined/[object Object] reaching visible text, no binding aimed at state the worker never defines, no nav param silently dropped, no dead nav target. Catches what validate/render cannot \u2014 a WRONG REDUCER passes both with ok:true. Runs the worker in a DEVICE-PARITY sandbox (no setTimeout/setInterval, as on JSC/WebView) so a bundle that would die on device fails here. Crawls on HERMETIC DATA by default \u2014 zero network: the app's own dev/net-mocks.json first (--net <file> overrides), then the config's CMS `seed:` rows, then a canned fixture of frozen real vertical responses shipped with the CLI (exercises code paths, not API-contract truth) \u2014 so the success/mapping path runs, not just the failure UI. Two boot-only HOSTILE passes (every list empty; every request 500) then check the failure/empty day \u2014 corpus-proven, so their findings are errors. NAV EDGES are followed too: every himi.open site ($item params resolved against row 0 of its list, $service against worker state) and worker _nav directive is harvested, and each baked target is booted WITH the params the source sends \u2014 detail screens finally run their content arm; edge findings are errors tagged edge:<source>. --native runs both real native decoders over the exact baked JSON bytes; --native ios or --native android selects one. It is explicit and needs the selected local toolchain. Default runs name missing native verdicts in the coverage ledger. --offline strips all data layers (the old reject-all axis); --no-hostile skips the hostile passes. --record (EXPLICIT, never automatic) refreshes dev/net-mocks.json from live reads first: it boots each screen (load + root onAppear ONLY \u2014 nothing the app's own launch would not fire; the tap/action crawl never sees the live transport, and only proven reads \u2014 GET, or read-shaped POST (/query, /search, /list*, \u2026) \u2014 ever reach the wire), freezes the round-trips, then crawls hermetically on the fresh fixture; record against a QA site, not production. Nodes it cannot judge headless are named, never silently passed. Exit 1 on any error. The CLI carries the authoring runtime off-repo; no per-app SDK install is required. --browser runs the whole loop (build \u2192 extract \u2192 validate \u2192 render \u2192 test) in YOUR browser instead of here, so the source never reaches a pod: it opens one screen's worker + descriptor in a tab, blocks on a loopback callback, and prints the report it posts back. One screen per tab \u2014 name it. --no-open prints the URL rather than launching a browser (SSH); --timeout <ms> bounds the wait, which matters because the page has no execution deadline of its own and an infinite loop in the worker hangs the tab.", required: "[<screenId>\u2026] [--content <dir>] [--net <file>] [--budget <ms>] [--deep [n]] [--record] [--native [ios|android]] [--no-shots] [--offline] [--no-hostile] [--all-screens] [--browser] [--no-open] [--timeout <ms>] [--json]" },
61617
+ { name: "test", summary: "DERIVED behavioral crawl \u2014 no test files to write. Boots each screen's real worker, replays its onAppear, then dispatches every action site the descriptor already declares (each from a fresh boot) and checks a narrow set of unambiguous invariants: no uncaught throw, never stuck on `loading`, no `$service:` token or NaN/undefined/[object Object] reaching visible text, no binding aimed at state the worker never defines, no nav param silently dropped, no dead nav target. Catches what validate/render cannot \u2014 a WRONG REDUCER passes both with ok:true. Runs the worker in a DEVICE-PARITY sandbox (no setTimeout/setInterval, as on JSC/WebView) so a bundle that would die on device fails here. Crawls on HERMETIC DATA by default \u2014 zero network: the app's own dev/net-mocks.json first (--net <file> overrides), then the config's CMS `seed:` rows, then a canned fixture of frozen real vertical responses shipped with the CLI (exercises code paths, not API-contract truth) \u2014 so the success/mapping path runs, not just the failure UI. Two boot-only HOSTILE passes (every list empty; every request 500) then check the failure/empty day \u2014 corpus-proven, so their findings are errors. NAV EDGES are followed too: every himi.open site ($item params resolved against row 0 of its list, $service against worker state) and worker _nav directive is harvested, and each baked target is booted WITH the params the source sends \u2014 detail screens finally run their content arm; edge findings are errors tagged edge:<source>. --native runs both real native decoders over the exact baked JSON bytes; --native ios or --native android selects one. It is explicit, runs only from a Himalaya SOURCE CHECKOUT (the decoder oracles are that tree's own HimalayaCore Swift package and Gradle harness, which a published install does not ship), and needs the selected local toolchain. From a source checkout, default runs name missing native verdicts in the coverage ledger; elsewhere the ledger stays silent about them rather than offering a remedy you cannot run. --offline strips all data layers (the old reject-all axis); --no-hostile skips the hostile passes. --record (EXPLICIT, never automatic) refreshes dev/net-mocks.json from live reads first: it boots each screen (load + root onAppear ONLY \u2014 nothing the app's own launch would not fire; the tap/action crawl never sees the live transport, and only proven reads \u2014 GET, or read-shaped POST (/query, /search, /list*, \u2026) \u2014 ever reach the wire), freezes the round-trips, then crawls hermetically on the fresh fixture; record against a QA site, not production. Nodes it cannot judge headless are named, never silently passed. Exit 1 on any error. The CLI carries the authoring runtime off-repo; no per-app SDK install is required. --browser runs the whole loop (build \u2192 extract \u2192 validate \u2192 render \u2192 test) in YOUR browser instead of here, so the source never reaches a pod: it opens one screen's worker + descriptor in a tab, blocks on a loopback callback, and prints the report it posts back. One screen per tab \u2014 name it. --no-open prints the URL rather than launching a browser (SSH); --timeout <ms> bounds the wait, which matters because the page has no execution deadline of its own and an infinite loop in the worker hangs the tab.", required: "[<screenId>\u2026] [--content <dir>] [--net <file>] [--budget <ms>] [--deep [n]] [--record] [--native [ios|android]] [--no-shots] [--offline] [--no-hostile] [--all-screens] [--browser] [--no-open] [--timeout <ms>] [--json]" },
61381
61618
  { name: "ux", summary: "audit or record mobile UX evidence. `audit` checks the public-safe contract, seeded blank acceptance rows, placeholder source copy, app-specific mocks, and iOS/Android audit records. It warns while existing templates migrate; `--strict` promotes missing evidence. `record` stores checks performed on an installed iOS/Android build: `--checks` is required for passed|failed and empty for unavailable, which drove nothing and needs a `--reason` instead.", required: "audit [--content <dir>] [--strict] [--json] | record --content <dir> --platform ios|android --status passed|failed|unavailable [--checks back,keyboard,...] [--reason <text>]" },
61382
61619
  { name: "render", summary: "headless-render a screen with the web renderer: render-tree + diagnostics, optional --png <file>, --scheme dark, --tree. Local by default (content dir). Pass --remote --app --release to hit GET /v1/preview/\u2026/render on the serve plane after a push. Boots the worker so $service bindings resolve; --static raw bindings; --live real Wix fetches; --state injects worker state; --input supplies pre-boot navigation params; --width/--device for PNG viewport; --rtl for dir=rtl + $env:isRTL.", required: "<screenId> [--content <dir>] [--scheme dark] [--png <file>] [--tree] [--static] [--live] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--remote --app --release]" },
61383
61620
  { name: "cms", summary: "inspect, provision, and seed the app's CMS (Wix Data) collections, declared as a `cms` block in config.ts. `query` is READ-ONLY \u2014 by default it fetches ONLY the physical ids `config.ts` declares (item counts + schema drift) and reports `siteScanned: false`; it never lists the site, so an UNDECLARED `himi_<app>_` collection this app owns is not reached without `--all`. `--all` SCANS the site \u2014 every collection it lists is labeled `owned`/`adopted`/`foreign`, and it describes every foreign one \u2014 id, non-system field keys + types, and the site's read role \u2014 so an app with no `cms` block at all can DISCOVER a collection to adopt (`--all` is the one case where a missing `cms` block is not 'nothing to do'). Foreign collections are described up to a cap and are never item-counted (`itemCount: null`), because a row count is one query each; `foreign.count` is how many the scan LISTED \u2014 NOT a site census, since the list endpoint is paged and is not followed \u2014 and `foreign.truncated` says how many of those went undescribed, while `unlistedCollections` reports what the listing itself left out (from Wix's own paging total). The scan is OWNER-scoped \u2014 it needs --site <msid> (or WIX_SITE_ID) plus owner auth via `wix token`; a visitor token can read a public collection's ITEMS but can never enumerate collections, so there is no tokenless discovery path. `create` reconciles declared collections additively and idempotently, and REFUSES any id outside the app's `himi_<app>_` namespace, so it is safe against a production site. It reports declared-vs-site `permissionDrift` but never writes permissions unless asked with --apply-permissions, so a field reconcile can't silently revert access control changed in the dashboard. `seed` inserts the git-tracked `seed:` rows into EMPTY owned collections (--force to add to a non-empty one; --ai writes placeholder rows plus a refinement prompt for you to review). Adopted collections (declared by `wixId`) are read-only and never created, updated, or seeded. Site from --site <msid> or WIX_SITE_ID; owner auth via `wix token`.", required: "query|create|seed [--site <msid>] [--content <dir>] [--all] [--dry-run] [--force] [--allow-destructive] [--apply-permissions] [--only <id>] [--ai] [--count <n>]" },
@@ -61423,7 +61660,7 @@ var COMMANDS = [
61423
61660
  ];
61424
61661
  function cliVersion() {
61425
61662
  try {
61426
- return JSON.parse(readFileSync63(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
61663
+ return JSON.parse(readFileSync64(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
61427
61664
  } catch {
61428
61665
  return "unknown";
61429
61666
  }
@@ -61439,18 +61676,18 @@ function compareVersions2(a, b) {
61439
61676
  }
61440
61677
  function packageVersion(path) {
61441
61678
  try {
61442
- const version = JSON.parse(readFileSync63(path, "utf8")).version;
61679
+ const version = JSON.parse(readFileSync64(path, "utf8")).version;
61443
61680
  return typeof version === "string" && version ? version : void 0;
61444
61681
  } catch {
61445
61682
  return void 0;
61446
61683
  }
61447
61684
  }
61448
61685
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
61449
- let dir = resolve40(fromDir);
61686
+ let dir = resolve41(fromDir);
61450
61687
  for (; ; ) {
61451
- const version = packageVersion(join49(dir, "node_modules", "@wix", "himalaya", "package.json"));
61688
+ const version = packageVersion(join50(dir, "node_modules", "@wix", "himalaya", "package.json"));
61452
61689
  if (version) return { version, source: "local" };
61453
- const parent = dirname28(dir);
61690
+ const parent = dirname29(dir);
61454
61691
  if (parent === dir) break;
61455
61692
  dir = parent;
61456
61693
  }
@@ -61458,7 +61695,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
61458
61695
  if (bundled) return { version: bundled, source: "bundled" };
61459
61696
  try {
61460
61697
  const root = execFileSync6("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
61461
- const version = root ? packageVersion(join49(root, "@wix", "himalaya", "package.json")) : void 0;
61698
+ const version = root ? packageVersion(join50(root, "@wix", "himalaya", "package.json")) : void 0;
61462
61699
  return version ? { version, source: "global" } : {};
61463
61700
  } catch {
61464
61701
  return {};
@@ -61557,6 +61794,40 @@ function previewLogsUrl(serveBase, app, release) {
61557
61794
  function previewTelemetryUrl(serveBase, app, release) {
61558
61795
  return `${serveBase.replace(/\/$/, "")}/v1/telemetry/events?app=${encodeURIComponent(app)}&release=${encodeURIComponent(release)}`;
61559
61796
  }
61797
+ function diagnoseBody(body) {
61798
+ const findings = body.findings;
61799
+ if (!Array.isArray(findings)) return body;
61800
+ const messageOf2 = (entry) => {
61801
+ const message = entry && typeof entry === "object" ? entry.message : void 0;
61802
+ return typeof message === "string" ? message : void 0;
61803
+ };
61804
+ const shown2 = new Set(findings.map(messageOf2).filter((message) => message !== void 0));
61805
+ const folded = (value) => Array.isArray(value) && value.length > 0 && value.every((entry) => {
61806
+ const message = messageOf2(entry);
61807
+ return message !== void 0 && shown2.has(message);
61808
+ });
61809
+ const out = {};
61810
+ const emptied = [];
61811
+ for (const [key2, value] of Object.entries(body)) {
61812
+ const fold = (key2 === "issues" || key2 === "failures" || key2 === "warnings") && folded(value);
61813
+ if (fold) emptied.push(key2);
61814
+ out[key2] = fold ? [] : value;
61815
+ }
61816
+ if (emptied.length) out.foldedIntoFindings = emptied;
61817
+ return out;
61818
+ }
61819
+ var PREVIEW_TOKEN_NOTE = "the preview link carries a bearer with a limited lifetime, and this call has no usable expiry for it \u2014 the plane stated none, or the one it stated is unreadable, already past, or beyond the hour this CLI will reason about. Run the `previewRenew` command printed beside this note \u2014 from the same plane and credentials as this call (any `--serve-url`/`--control-url`/token flags you passed) \u2014 for a fresh one.";
61820
+ function previewTokenNote(expiresAt, now = Date.now()) {
61821
+ const at = expiresAt ? Date.parse(expiresAt) : NaN;
61822
+ if (!Number.isFinite(at)) return PREVIEW_TOKEN_NOTE;
61823
+ const seconds = (at - now) / 1e3;
61824
+ if (seconds <= 0 || seconds > 3600) return PREVIEW_TOKEN_NOTE;
61825
+ const minutes = Math.max(1, Math.round(seconds / 60));
61826
+ return `the preview link carries a bearer that expires at ${expiresAt} \u2014 about ${minutes} minute${minutes === 1 ? "" : "s"} from now; run the \`previewRenew\` command printed beside this note \u2014 from the same plane and credentials as this call (any \`--serve-url\`/\`--control-url\`/token flags you passed) \u2014 for a fresh one.`;
61827
+ }
61828
+ function previewRenewCommand(app, release, target, scope = {}) {
61829
+ return `himi preview --app ${app} --release ${release}` + (scope.screen ? ` --screen ${scope.screen}` : "") + (scope.surface ? ` --surface ${scope.surface}` : "") + (target !== "local" ? ` --target ${target}` : "");
61830
+ }
61560
61831
  function previewPlayerLink(app, release, screenId) {
61561
61832
  return buildPlayerPreviewUrl({ app, releaseId: release, screenId, source: "cli" });
61562
61833
  }
@@ -61838,7 +62109,7 @@ async function nativeConfigAdvisories(app, configSource) {
61838
62109
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
61839
62110
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
61840
62111
  const iosYmlPath = iosProjectYml2(app);
61841
- const iosYml = existsSync51(iosYmlPath) ? readFileSync63(iosYmlPath, "utf8") : "";
62112
+ const iosYml = existsSync52(iosYmlPath) ? readFileSync64(iosYmlPath, "utf8") : "";
61842
62113
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
61843
62114
  if (!optedIn) {
61844
62115
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -61852,19 +62123,19 @@ async function nativeConfigAdvisories(app, configSource) {
61852
62123
  }
61853
62124
  function readConfigSource(dir) {
61854
62125
  try {
61855
- return readFileSync63(join49(dir, "config.ts"), "utf8");
62126
+ return readFileSync64(join50(dir, "config.ts"), "utf8");
61856
62127
  } catch {
61857
62128
  return null;
61858
62129
  }
61859
62130
  }
61860
62131
  function writePngFile(path, base64) {
61861
- mkdirSync25(dirname28(path), { recursive: true });
62132
+ mkdirSync25(dirname29(path), { recursive: true });
61862
62133
  writeFileSync23(path, Buffer.from(base64, "base64"));
61863
62134
  return path;
61864
62135
  }
61865
62136
  async function initFromRemoteTemplate(name, parentDir, template, c) {
61866
- const dir = resolve40(parentDir, name);
61867
- if (existsSync51(join49(dir, "himalaya.content.json"))) {
62137
+ const dir = resolve41(parentDir, name);
62138
+ if (existsSync52(join50(dir, "himalaya.content.json"))) {
61868
62139
  throw new Error(`content package already exists at ${dir}`);
61869
62140
  }
61870
62141
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -61876,7 +62147,7 @@ async function initFromRemoteTemplate(name, parentDir, template, c) {
61876
62147
  return finishScaffold(materializeSource(archive.files, name, dir), template);
61877
62148
  }
61878
62149
  function contentDir(flags) {
61879
- return resolve40(str5(flags.content) ?? process.cwd());
62150
+ return resolve41(str5(flags.content) ?? process.cwd());
61880
62151
  }
61881
62152
  function renameRestoredSource(restored, declaredApp, renamed) {
61882
62153
  const text2 = {};
@@ -61907,8 +62178,8 @@ function symlinkDestinationIssue(dest) {
61907
62178
  function restoreDestinationIssue(dest, force) {
61908
62179
  const link = symlinkDestinationIssue(dest);
61909
62180
  if (link) return link;
61910
- if (!existsSync51(dest)) return null;
61911
- if (!statSync13(dest).isDirectory()) {
62181
+ if (!existsSync52(dest)) return null;
62182
+ if (!statSync12(dest).isDirectory()) {
61912
62183
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
61913
62184
  }
61914
62185
  if (readdirSync23(dest).length > 0 && !force) {
@@ -61920,10 +62191,10 @@ function restoreDestinationIssue(dest, force) {
61920
62191
  return null;
61921
62192
  }
61922
62193
  function isHimalayaMonorepoCheckout(start) {
61923
- let dir = resolve40(start);
62194
+ let dir = resolve41(start);
61924
62195
  for (; ; ) {
61925
- if (existsSync51(join49(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync51(join49(dir, "core", "serve", "src", "server.ts"))) return true;
61926
- const parent = dirname28(dir);
62196
+ if (existsSync52(join50(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync52(join50(dir, "core", "serve", "src", "server.ts"))) return true;
62197
+ const parent = dirname29(dir);
61927
62198
  if (parent === dir) return false;
61928
62199
  dir = parent;
61929
62200
  }
@@ -61953,8 +62224,8 @@ function materializeForPreview(appDir2, slug, displayName) {
61953
62224
  const forBuild = Object.fromEntries(
61954
62225
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
61955
62226
  );
61956
- const repoRoot2 = resolve40(fileURLToPath21(new URL("../../..", import.meta.url)));
61957
- const dest = join49(repoRoot2, "node_modules", ".cache", "himi-template-preview", slug);
62227
+ const repoRoot2 = resolve41(fileURLToPath21(new URL("../../..", import.meta.url)));
62228
+ const dest = join50(repoRoot2, "node_modules", ".cache", "himi-template-preview", slug);
61958
62229
  rmSync9(dest, { recursive: true, force: true });
61959
62230
  mkdirSync25(dest, { recursive: true });
61960
62231
  materializeSource(forBuild, displayName, dest, slug);
@@ -61962,8 +62233,8 @@ function materializeForPreview(appDir2, slug, displayName) {
61962
62233
  }
61963
62234
  async function appId(dir) {
61964
62235
  try {
61965
- const { readFileSync: readFileSync64 } = await import("node:fs");
61966
- const m = JSON.parse(readFileSync64(join49(dir, "himalaya.content.json"), "utf8"));
62236
+ const { readFileSync: readFileSync65 } = await import("node:fs");
62237
+ const m = JSON.parse(readFileSync65(join50(dir, "himalaya.content.json"), "utf8"));
61967
62238
  if (m.name) return m.name;
61968
62239
  } catch {
61969
62240
  }
@@ -61971,8 +62242,8 @@ async function appId(dir) {
61971
62242
  }
61972
62243
  async function contentVisibility(dir) {
61973
62244
  try {
61974
- const { readFileSync: readFileSync64 } = await import("node:fs");
61975
- const m = JSON.parse(readFileSync64(join49(dir, "himalaya.content.json"), "utf8"));
62245
+ const { readFileSync: readFileSync65 } = await import("node:fs");
62246
+ const m = JSON.parse(readFileSync65(join50(dir, "himalaya.content.json"), "utf8"));
61976
62247
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
61977
62248
  } catch {
61978
62249
  }
@@ -62173,7 +62444,7 @@ Seed your coding agent with \`himi agent-init\`. Portal: ${idx.portalUrl}`);
62173
62444
  return 0;
62174
62445
  }
62175
62446
  case "agent-init": {
62176
- const dir = resolve40(str5(flags.dir) ?? process.cwd());
62447
+ const dir = resolve41(str5(flags.dir) ?? process.cwd());
62177
62448
  const res = writeAgentContext(dir);
62178
62449
  print({ ok: true, dir: res.dir, files: res.files, portalUrl: idx.portalUrl });
62179
62450
  return 0;
@@ -62259,7 +62530,7 @@ async function producedWidgetFamilies(dir) {
62259
62530
  let js;
62260
62531
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
62261
62532
  try {
62262
- js = readFileSync63(join49(outDir, candidate), "utf8");
62533
+ js = readFileSync64(join50(outDir, candidate), "utf8");
62263
62534
  break;
62264
62535
  } catch {
62265
62536
  }
@@ -62349,7 +62620,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62349
62620
  return 2;
62350
62621
  }
62351
62622
  const template = str5(flags.template);
62352
- const parentDir = resolve40(str5(flags.dir) ?? process.cwd());
62623
+ const parentDir = resolve41(str5(flags.dir) ?? process.cwd());
62353
62624
  if (template && flags.remote === true) {
62354
62625
  try {
62355
62626
  const res2 = await initFromRemoteTemplate(name, parentDir, template, client3());
@@ -62437,11 +62708,11 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62437
62708
  return 0;
62438
62709
  }
62439
62710
  if (sub === "publish") {
62440
- const appDir2 = resolve40(str5(flags.dir) ?? process.cwd());
62441
- const sourceDir = resolve40(str5(flags["source-dir"]) ?? appDir2);
62711
+ const appDir2 = resolve41(str5(flags.dir) ?? process.cwd());
62712
+ const sourceDir = resolve41(str5(flags["source-dir"]) ?? appDir2);
62442
62713
  let meta;
62443
62714
  try {
62444
- meta = JSON.parse(readFileSync63(join49(sourceDir, TEMPLATE_META_FILE), "utf8"));
62715
+ meta = JSON.parse(readFileSync64(join50(sourceDir, TEMPLATE_META_FILE), "utf8"));
62445
62716
  } catch {
62446
62717
  io.error(JSON.stringify({ error: `no ${TEMPLATE_META_FILE} in ${sourceDir} \u2014 a template must declare { title, description, tags } to be published`, hint: `add ${TEMPLATE_META_FILE} next to himalaya.content.json (or pass --source-dir <dir>)` }));
62447
62718
  return 2;
@@ -62451,7 +62722,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62451
62722
  return 2;
62452
62723
  }
62453
62724
  try {
62454
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync63(join49(sourceDir, "tokens.json"), "utf8")));
62725
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync64(join50(sourceDir, "tokens.json"), "utf8")));
62455
62726
  meta = { ...appearance, ...meta };
62456
62727
  } catch {
62457
62728
  }
@@ -62463,13 +62734,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62463
62734
  const force = flags.force === true;
62464
62735
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
62465
62736
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
62466
- const out = mkdtempSync4(join49(tmpdir5(), "himi-template-"));
62737
+ const out = mkdtempSync4(join50(tmpdir5(), "himi-template-"));
62467
62738
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
62468
62739
  const outcome = await buildReleaseFromContent2(buildDir, out, {
62469
62740
  ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
62470
62741
  collect: { lint: true, strict }
62471
62742
  });
62472
- const scaffoldSourceFindings = resolve40(sourceDir) === resolve40(appDir2) ? [] : [
62743
+ const scaffoldSourceFindings = resolve41(sourceDir) === resolve41(appDir2) ? [] : [
62473
62744
  ...tokenColorIssues(sourceDir),
62474
62745
  ...surfaceIssues(sourceDir, void 0, strict),
62475
62746
  ...fontIssues(sourceDir, strict),
@@ -62512,11 +62783,11 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62512
62783
  }));
62513
62784
  return 1;
62514
62785
  }
62515
- const iconPath = str5(flags.icon) ? resolve40(str5(flags.icon)) : templateIconPath(name);
62786
+ const iconPath = str5(flags.icon) ? resolve41(str5(flags.icon)) : templateIconPath(name);
62516
62787
  let iconHash;
62517
62788
  if (iconPath) {
62518
62789
  try {
62519
- const bytes2 = readFileSync63(iconPath);
62790
+ const bytes2 = readFileSync64(iconPath);
62520
62791
  payload.icon = bytes2.toString("base64");
62521
62792
  iconHash = createHash24("sha256").update(bytes2).digest("hex");
62522
62793
  } catch {
@@ -62715,7 +62986,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62715
62986
  const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
62716
62987
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
62717
62988
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
62718
- const out = mkdtempSync4(join49(tmpdir5(), "himi-validate-"));
62989
+ const out = mkdtempSync4(join50(tmpdir5(), "himi-validate-"));
62719
62990
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
62720
62991
  const outcome = await buildReleaseFromContent2(dir, out, {
62721
62992
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -62752,7 +63023,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62752
63023
  const path = dpxSessionTokenPath();
62753
63024
  let raw;
62754
63025
  try {
62755
- raw = readFileSync63(path, "utf8");
63026
+ raw = readFileSync64(path, "utf8");
62756
63027
  } catch {
62757
63028
  throw new Error(`no DPX BO session at ${path}; run dpx auth login, write dpx auth token to that path, then himi login --dpx --target bo`);
62758
63029
  }
@@ -62904,7 +63175,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62904
63175
  case "test": {
62905
63176
  const dir = contentDir(flags);
62906
63177
  const structuredOutput = flags.json === true || io.isTTY !== true;
62907
- if (existsSync51(join49(dir, "functions"))) {
63178
+ if (existsSync52(join50(dir, "functions"))) {
62908
63179
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
62909
63180
  const loaded = await loadFunctionsApp2(dir);
62910
63181
  const results = [];
@@ -62991,7 +63262,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62991
63262
  }
62992
63263
  }
62993
63264
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
62994
- const out = mkdtempSync4(join49(tmpdir5(), "himi-test-"));
63265
+ const out = mkdtempSync4(join50(tmpdir5(), "himi-test-"));
62995
63266
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
62996
63267
  const outcome = await buildReleaseFromContent2(dir, out, {
62997
63268
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -63036,7 +63307,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63036
63307
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
63037
63308
  return 2;
63038
63309
  }
63039
- const sources = collectWorkerSources2(join49(dir, "tier5-src"), screen.bundleName);
63310
+ const sources = collectWorkerSources2(join50(dir, "tier5-src"), screen.bundleName);
63040
63311
  if (!sources) {
63041
63312
  io.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
63042
63313
  return 2;
@@ -63197,7 +63468,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63197
63468
  const sp = await runShotsPass2({
63198
63469
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
63199
63470
  tokens: outcome.tokens,
63200
- outDir: join49(dir, ".himi", "shots"),
63471
+ outDir: join50(dir, ".himi", "shots"),
63201
63472
  width: 390,
63202
63473
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
63203
63474
  ...budgetMs ? { budgetMs } : {},
@@ -63214,9 +63485,11 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63214
63485
  result.shots = { ...sp.written.length ? { dir: sp.dir } : {}, written: sp.written.length, ...sp.unavailable ? { unavailable: true } : {} };
63215
63486
  }
63216
63487
  }
63217
- const { assembleLedger: assembleLedger2, trendAndStore: trendAndStore2, formatLedger: formatLedger2, diffAndStoreObservations: diffAndStoreObservations2, formatDelta: formatDelta2 } = await Promise.resolve().then(() => (init_coverage(), coverage_exports));
63218
- const ledger = assembleLedger2(result);
63219
- const trend = trendAndStore2(dir, ledger);
63488
+ const { assembleLedger: assembleLedger2, trendAndStore: trendAndStore2, formatLedger: formatLedger2, diffAndStoreObservations: diffAndStoreObservations2, formatDelta: formatDelta2, suppressedNativeKeys: suppressedNativeKeys2 } = await Promise.resolve().then(() => (init_coverage(), coverage_exports));
63489
+ const { nativeOracleShipped: nativeOracleShipped2 } = await Promise.resolve().then(() => (init_native_decode(), native_decode_exports));
63490
+ const nativeOracle = nativeOracleShipped2();
63491
+ const ledger = assembleLedger2(result, { nativeOracle });
63492
+ const trend = trendAndStore2(dir, ledger, suppressedNativeKeys2(nativeOracle));
63220
63493
  const delta = diffAndStoreObservations2(dir, result.observations ?? {});
63221
63494
  const legacy = { ...result, ledger, ...trend ? { trend } : {}, ...delta ? { delta } : {}, ...recorded ? { recorded } : {} };
63222
63495
  const release = await loadReleaseFromDir(app, out);
@@ -63233,7 +63506,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63233
63506
  releaseId: result.releaseId,
63234
63507
  releasePayload: release,
63235
63508
  descriptors: attestedDescriptors,
63236
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join49(dir, ".himi", "shots", name)])),
63509
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join50(dir, ".himi", "shots", name)])),
63237
63510
  coverage: {
63238
63511
  screens: crawledScreens.size,
63239
63512
  actionsDispatched: result.actions.exercised,
@@ -63322,7 +63595,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63322
63595
  return 2;
63323
63596
  }
63324
63597
  try {
63325
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync63(stateArg.replace(/^@/, ""), "utf8");
63598
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync64(stateArg.replace(/^@/, ""), "utf8");
63326
63599
  injectedState = JSON.parse(raw);
63327
63600
  } catch (e) {
63328
63601
  io.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e.message}` }));
@@ -63337,7 +63610,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63337
63610
  return 2;
63338
63611
  }
63339
63612
  try {
63340
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync63(inputArg.replace(/^@/, ""), "utf8");
63613
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync64(inputArg.replace(/^@/, ""), "utf8");
63341
63614
  const parsed = JSON.parse(raw);
63342
63615
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
63343
63616
  injectedInput = parsed;
@@ -63370,7 +63643,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63370
63643
  return 2;
63371
63644
  }
63372
63645
  } else if (typeof flags.width === "string" && flags.width) {
63373
- renderWidth = Number(flags.width);
63646
+ renderWidth = Math.round(Number(flags.width));
63374
63647
  if (!Number.isFinite(renderWidth) || renderWidth <= 0) {
63375
63648
  io.error(JSON.stringify({ error: `--width must be a positive number of pixels (got "${str5(flags.width)}")` }));
63376
63649
  return 2;
@@ -63386,7 +63659,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63386
63659
  }
63387
63660
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
63388
63661
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
63389
- const out = mkdtempSync4(join49(tmpdir5(), "himi-render-"));
63662
+ const out = mkdtempSync4(join50(tmpdir5(), "himi-render-"));
63390
63663
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
63391
63664
  const outcome = await buildReleaseFromContent2(dir, out, {
63392
63665
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -63402,7 +63675,15 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63402
63675
  let attestationPngFile;
63403
63676
  if (typeof flags.png === "string" && r.pngBase64) {
63404
63677
  pngFile = writePngFile(str5(flags.png), r.pngBase64);
63405
- attestationPngFile = writePngFile(join49(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
63678
+ attestationPngFile = writePngFile(join50(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
63679
+ }
63680
+ if (renderWidth !== void 0 && r.renderable) {
63681
+ recordRenderWidth(dir, {
63682
+ screenId,
63683
+ width: renderWidth,
63684
+ ...deviceName ? { device: deviceName } : {},
63685
+ ...pngFile ? { file: pngFile } : {}
63686
+ });
63406
63687
  }
63407
63688
  const legacyRender = {
63408
63689
  ok: r.renderable,
@@ -63707,7 +63988,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
63707
63988
  return 2;
63708
63989
  }
63709
63990
  const renamed = str5(flags.name);
63710
- const dest = resolve40(str5(flags.out) ?? process.cwd(), renamed ?? wanted);
63991
+ const dest = resolve41(str5(flags.out) ?? process.cwd(), renamed ?? wanted);
63711
63992
  const replace = flags.replace === true;
63712
63993
  const force = replace || flags.force === true;
63713
63994
  const linked = symlinkDestinationIssue(dest);
@@ -63715,8 +63996,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
63715
63996
  io.error(JSON.stringify(linked));
63716
63997
  return 1;
63717
63998
  }
63718
- if (existsSync51(dest)) {
63719
- if (!statSync13(dest).isDirectory()) {
63999
+ if (existsSync52(dest)) {
64000
+ if (!statSync12(dest).isDirectory()) {
63720
64001
  io.error(JSON.stringify({
63721
64002
  error: `${dest} exists and is not a directory`,
63722
64003
  hint: "pass --out/--name to land somewhere else, or remove the file"
@@ -63728,7 +64009,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
63728
64009
  io.error(JSON.stringify({ error: `${dest} already exists and is not empty`, hint: "pass --force to write into it anyway (merging, and naming what it keeps), --replace to restore exactly what was archived, or --out/--name to land somewhere else" }));
63729
64010
  return 1;
63730
64011
  }
63731
- if (replace && occupied && !existsSync51(join49(dest, "himalaya.content.json"))) {
64012
+ if (replace && occupied && !existsSync52(join50(dest, "himalaya.content.json"))) {
63732
64013
  io.error(JSON.stringify({
63733
64014
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
63734
64015
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -63822,7 +64103,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
63822
64103
  const force = flags.force === true;
63823
64104
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
63824
64105
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
63825
- const out = mkdtempSync4(join49(tmpdir5(), "himi-push-"));
64106
+ const out = mkdtempSync4(join50(tmpdir5(), "himi-push-"));
63826
64107
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
63827
64108
  const outcome = await buildReleaseFromContent2(dir, out, {
63828
64109
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -64048,7 +64329,16 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64048
64329
  ...loopbackWarning ? { warning: loopbackWarning } : {},
64049
64330
  ...report.issues ? { issues: report.issues } : {},
64050
64331
  previewWeb: base ? previewWebUrl(base, app, release, void 0, void 0, previewToken) : null,
64051
- ...previewToken ? { previewTokenExpiresAt: previewTokenExpiresAt ?? null } : {},
64332
+ // A timestamp alone leaves the reader to work out both how long they have and how to
64333
+ // get another one. Print the duration and the re-mint command beside it (#2502 item 3).
64334
+ // No `mintedHere` guard is needed here, unlike `himi preview`: this bearer can only
64335
+ // have come from the publish response two hundred lines up, so THIS call minted it by
64336
+ // construction. `push` has no `--preview-token` to paste one in with.
64337
+ ...previewToken ? {
64338
+ previewTokenExpiresAt: previewTokenExpiresAt ?? null,
64339
+ previewRenew: previewRenewCommand(app, release, targetName()),
64340
+ previewTokenNote: previewTokenNote(previewTokenExpiresAt)
64341
+ } : {},
64052
64342
  // RFC 0051 — the surfaces THIS RELEASE targets, read from the manifest the
64053
64343
  // push just published (never a re-read of the local file: review found the
64054
64344
  // two can disagree — e.g. an older CLI whose local parser drops a key the
@@ -64132,7 +64422,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64132
64422
  return 2;
64133
64423
  }
64134
64424
  try {
64135
- const res = writePack(resolve40(releaseDir), only, resolve40(str5(flags.out) ?? process.cwd()));
64425
+ const res = writePack(resolve41(releaseDir), only, resolve41(str5(flags.out) ?? process.cwd()));
64136
64426
  print({
64137
64427
  ok: true,
64138
64428
  app: res.app,
@@ -64181,7 +64471,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64181
64471
  }
64182
64472
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
64183
64473
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
64184
- const built = mkdtempSync4(join49(tmpdir5(), "himi-pack-"));
64474
+ const built = mkdtempSync4(join50(tmpdir5(), "himi-pack-"));
64185
64475
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
64186
64476
  const outcome = await buildReleaseFromContent2(dir, built, {
64187
64477
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -64203,7 +64493,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64203
64493
  return 1;
64204
64494
  }
64205
64495
  try {
64206
- const res = writePack(built, app, resolve40(str5(flags.out) ?? dir), sourceArchive);
64496
+ const res = writePack(built, app, resolve41(str5(flags.out) ?? dir), sourceArchive);
64207
64497
  print({
64208
64498
  ok: true,
64209
64499
  app: res.app,
@@ -64248,7 +64538,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64248
64538
  const { HIMI_PACKAGE_SOURCE_PATH: HIMI_PACKAGE_SOURCE_PATH2, isSafeAppId: isSafeAppId2, packageContents: packageContents2 } = await Promise.resolve().then(() => (init_himi_package(), himi_package_exports));
64249
64539
  let opened;
64250
64540
  try {
64251
- opened = openPackageFile2(resolve40(file));
64541
+ opened = openPackageFile2(resolve41(file));
64252
64542
  } catch (e) {
64253
64543
  io.error(JSON.stringify({ error: e.message }));
64254
64544
  return 1;
@@ -64261,7 +64551,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64261
64551
  io.error(JSON.stringify({
64262
64552
  ok: false,
64263
64553
  error: "app_source_not_in_package",
64264
- detail: `${resolve40(file)} is a release package: it carries built output (screens, bundles, tokens) and no source`,
64554
+ detail: `${resolve41(file)} is a release package: it carries built output (screens, bundles, tokens) and no source`,
64265
64555
  hint: "re-pack it from the content package with `himi pack --dev`, or restore from the serve plane with `himi pull <app>`"
64266
64556
  }));
64267
64557
  return 1;
@@ -64284,7 +64574,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64284
64574
  return 1;
64285
64575
  }
64286
64576
  }
64287
- const dest = resolve40(str5(flags.out) ?? process.cwd(), renamed ?? opened.header.app);
64577
+ const dest = resolve41(str5(flags.out) ?? process.cwd(), renamed ?? opened.header.app);
64288
64578
  const issue2 = restoreDestinationIssue(dest, flags.force === true);
64289
64579
  if (issue2) {
64290
64580
  io.error(JSON.stringify(issue2));
@@ -64369,7 +64659,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64369
64659
  }
64370
64660
  try {
64371
64661
  const { openPackageFile: openPackageFile2 } = await Promise.resolve().then(() => (init_pack(), pack_exports));
64372
- const opened = openPackageFile2(resolve40(file));
64662
+ const opened = openPackageFile2(resolve41(file));
64373
64663
  if (!opened.validation.ok || !opened.header) {
64374
64664
  io.error(JSON.stringify({ error: "refusing to share an invalid package", issues: opened.validation.errors }));
64375
64665
  return 1;
@@ -64379,12 +64669,12 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64379
64669
  io.error(JSON.stringify({
64380
64670
  ok: false,
64381
64671
  error: "refusing to share a package that carries the app's source",
64382
- detail: `${resolve40(file)} was packed with --dev, and a share link is public and unauthenticated \u2014 anyone with the URL could \`himi import\` your codebase`,
64672
+ detail: `${resolve41(file)} was packed with --dev, and a share link is public and unauthenticated \u2014 anyone with the URL could \`himi import\` your codebase`,
64383
64673
  hint: "share a release package instead (`himi pack` with no --dev), or hand the dev package over directly"
64384
64674
  }));
64385
64675
  return 1;
64386
64676
  }
64387
- const archive = readFileSync63(resolve40(file));
64677
+ const archive = readFileSync64(resolve41(file));
64388
64678
  const result = await client3().createPackageShare({
64389
64679
  app: opened.header.app,
64390
64680
  releaseId: opened.header.releaseId,
@@ -64421,10 +64711,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64421
64711
  const app = await appId(dir);
64422
64712
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
64423
64713
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
64424
- temp = mkdtempSync4(join49(tmpdir5(), "himi-run-build-"));
64714
+ temp = mkdtempSync4(join50(tmpdir5(), "himi-run-build-"));
64425
64715
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
64426
64716
  await buildReleaseFromContent2(dir, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {} });
64427
- source = join49(temp, app);
64717
+ source = join50(temp, app);
64428
64718
  }
64429
64719
  const chromeArg = str5(flags.chrome) ?? "preview";
64430
64720
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -64449,9 +64739,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64449
64739
  open: flags["no-open"] !== true
64450
64740
  });
64451
64741
  print({ ok: true, app: run2.app, url: run2.url, port: run2.port, plane: "browser" });
64452
- await new Promise((resolve41) => {
64742
+ await new Promise((resolve42) => {
64453
64743
  const stop = () => {
64454
- void run2.close().finally(() => resolve41());
64744
+ void run2.close().finally(() => resolve42());
64455
64745
  };
64456
64746
  process.once("SIGINT", stop);
64457
64747
  process.once("SIGTERM", stop);
@@ -64509,6 +64799,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64509
64799
  }
64510
64800
  let previewToken = str5(flags["preview-token"]);
64511
64801
  let previewTokenExpiresAt;
64802
+ let mintedHere = false;
64803
+ let mintFailure;
64512
64804
  if (!previewToken) {
64513
64805
  const target = resolveTarget({
64514
64806
  target: str5(flags.target),
@@ -64521,15 +64813,24 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64521
64813
  telemetryToken: str5(flags["telemetry-token"]),
64522
64814
  controlToken: str5(flags["control-token"])
64523
64815
  });
64524
- if (target.controlToken) {
64816
+ if (!target.controlToken) {
64817
+ mintFailure = `this target has no stored control credential, so none was requested \u2014 run \`himi login --target ${targetName()}\`, or pass --control-token`;
64818
+ } else {
64525
64819
  try {
64526
64820
  const minted = await new ServeCliClient(target).previewToken(app, release);
64527
- if (minted.ok && minted.json && typeof minted.json === "object") {
64821
+ if (!minted.ok) {
64822
+ mintFailure = `the plane refused the mint request (HTTP ${minted.status}). Your credential reached it, so this is the plane's answer, not your session`;
64823
+ } else if (!minted.json || typeof minted.json !== "object") {
64824
+ mintFailure = "the plane accepted the mint request but its answer was not a JSON object, so no token could be read. Your credential is fine";
64825
+ } else {
64528
64826
  const body = minted.json;
64529
64827
  previewToken = typeof body.previewToken === "string" ? body.previewToken : void 0;
64530
64828
  previewTokenExpiresAt = typeof body.previewTokenExpiresAt === "string" ? body.previewTokenExpiresAt : void 0;
64829
+ mintedHere = previewToken !== void 0;
64830
+ if (!mintedHere) mintFailure = "the plane answered the mint request without a token \u2014 HIMALAYA_PREVIEW_TOKEN_SECRET may be unconfigured there. Your credential is fine";
64531
64831
  }
64532
- } catch {
64832
+ } catch (e) {
64833
+ mintFailure = `the mint request could not be sent: ${e.message}. Your credential was never judged`;
64533
64834
  }
64534
64835
  }
64535
64836
  }
@@ -64540,7 +64841,22 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64540
64841
  ...screen ? { screen } : {},
64541
64842
  ...surface ? { surface } : {},
64542
64843
  previewWeb: base ? previewWebUrl(base, app, release, screen, surface, previewToken) : null,
64543
- ...previewTokenExpiresAt ? { previewTokenExpiresAt } : {},
64844
+ // Gated on the MINT, like the two fields below: a plane that returns an expiry but no
64845
+ // token would otherwise print a lifetime for a bearer the same envelope says does not
64846
+ // exist.
64847
+ ...mintedHere && previewTokenExpiresAt ? { previewTokenExpiresAt } : {},
64848
+ // Same as the push envelope: the expiry alone leaves the reader to work out both how
64849
+ // long they have and how to get another link. `himi preview` re-mints, so the renew is
64850
+ // this very command — say so rather than making them find it again (#2502 item 3).
64851
+ //
64852
+ // Gated on the MINT, not on the expiry: a plane that mints a bearer but returns no
64853
+ // `previewTokenExpiresAt` would otherwise withhold the guidance in exactly the case
64854
+ // where the reader has no timestamp to reason from — while a bearer the caller pasted
64855
+ // in with `--preview-token` is one this command cannot promise to reissue.
64856
+ ...mintedHere ? {
64857
+ previewRenew: previewRenewCommand(app, release, targetName(), { screen, surface }),
64858
+ previewTokenNote: previewTokenNote(previewTokenExpiresAt)
64859
+ } : previewToken || !mintFailure ? {} : { previewTokenNote: `no preview bearer was minted \u2014 ${mintFailure}. The link above carries no \`pvt\`, so it opens a LIVE release and will not open a draft.` },
64544
64860
  previewPlayerDebug: previewPlayerDebugLink(app, release, screen, previewToken),
64545
64861
  context: base ? previewContextUrl(base, app, release, screen) : null,
64546
64862
  diagnose: base && screen ? previewDiagnoseUrl(base, app, release, screen) : null,
@@ -64718,8 +65034,16 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64718
65034
  if (!r.ok) throw Object.assign(new Error("crashes_read_failed"), { response: r });
64719
65035
  out.crashes = r.json?.crashes ?? [];
64720
65036
  }
64721
- if ((out.events?.length ?? 0) === 0 && (out.logs?.length ?? 0) === 0 && !includeSummary && !includeCrashes) {
64722
- out.note = "no telemetry yet \u2014 open the web preview or Himi Player link for this release, then retry or use --follow";
65037
+ const eventCount = out.events?.length ?? 0;
65038
+ const logCount = out.logs?.length ?? 0;
65039
+ const narrowed = !!(level || str5(flags.screen) || str5(flags.screenId) || str5(flags.type) || str5(flags.event) || flags["crashes-only"] === true || flags.crashesOnly === true || num(flags.limit) !== void 0 || eventSince !== void 0 || logSince !== void 0);
65040
+ const wholeChannels = includeEvents && includeLogs && !narrowed;
65041
+ if (wholeChannels && eventCount === 0 && logCount === 0 && !includeSummary && !includeCrashes) {
65042
+ out.note = "no events and no worker console output on this release \u2014 open the web preview or Himi Player link for it, then retry or use --follow";
65043
+ } else if (wholeChannels && eventCount > 0 && logCount === 0) {
65044
+ out.note = "events arrived but the worker console channel is empty \u2014 commonly because the worker never called `console.*`, though an empty channel can also mean the lines aged out or never made it through ingestion. What the CLI can say is that this channel returned nothing, not that the app was never opened";
65045
+ } else if (narrowed && eventCount === 0 && logCount === 0 && !includeSummary && !includeCrashes) {
65046
+ out.note = "nothing matched this query. Drop --screen/--type/--level/--since/--limit/--crashes-only to see the whole event and console channels for this release";
64723
65047
  }
64724
65048
  return out;
64725
65049
  };
@@ -64800,7 +65124,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64800
65124
  if (r.status !== 422) return printServeFailure(r);
64801
65125
  }
64802
65126
  const body = r.json ?? {};
64803
- print(body);
65127
+ print(diagnoseBody(body));
64804
65128
  return body.ok === false ? 1 : 0;
64805
65129
  }
64806
65130
  case "deeplinks": {
@@ -64856,8 +65180,12 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64856
65180
  print({ ok: false, status: r.status, body: r.json ?? r.text });
64857
65181
  return 1;
64858
65182
  }
64859
- const logs = byLevel(r.json?.logs ?? []);
64860
- print({ ok: true, logs, ...logs.length === 0 ? { note: "no logs yet \u2014 a worker emits telemetry only once someone opens the app/preview on a device or web with a session. Boot-time console.log is visible via `himi validate` / `himi render`." } : {} });
65183
+ const rawLogs = r.json?.logs ?? [];
65184
+ const logs = byLevel(rawLogs);
65185
+ const narrowed = !!(level || str5(flags.screen) || str5(flags.screenId) || num2(flags.since) !== void 0 || num2(flags.limit) !== void 0);
65186
+ const scope = release ? "this release" : "this app";
65187
+ const note = logs.length > 0 ? void 0 : narrowed ? `no worker \`console.*\` lines matched this query${level && rawLogs.length ? ` (${rawLogs.length} line(s) were hidden by --level ${level})` : ""}. Drop --level/--screen/--since/--limit to see the whole console channel for ${scope}; the telemetry EVENT stream is a separate channel, read with \`himi telemetry\`.` : `no worker \`console.*\` output on ${scope}. This is the console channel \u2014 distinct from the telemetry EVENT stream, which \`himi telemetry --app <app> --release <id>\` reads: ${scope} can carry many events and no console lines \u2014 commonly because the worker never logged any, though an empty channel can also mean the lines aged out or never made it through ingestion. Boot-time \`console.log\` is visible via \`himi validate\` / \`himi render\`.`;
65188
+ print({ ok: true, logs, ...note ? { note } : {} });
64861
65189
  return 0;
64862
65190
  }
64863
65191
  const interval = num2(flags.interval) ?? 2e3;
@@ -65242,7 +65570,7 @@ ${label2}`);
65242
65570
  const platform = str5(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
65243
65571
  {
65244
65572
  const distDir = contentDir(flags);
65245
- if (existsSync51(join49(distDir, "himalaya.content.json"))) {
65573
+ if (existsSync52(join50(distDir, "himalaya.content.json"))) {
65246
65574
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
65247
65575
  const errors = issues.filter((i) => i.severity === "error");
65248
65576
  for (const i of issues.filter((i2) => i2.severity === "warning")) {
@@ -65512,7 +65840,7 @@ ${label2}`);
65512
65840
  const dpxPath = dpxSessionTokenPath();
65513
65841
  let dpx;
65514
65842
  try {
65515
- dpx = readFileSync63(dpxPath, "utf8").trim();
65843
+ dpx = readFileSync64(dpxPath, "utf8").trim();
65516
65844
  } catch {
65517
65845
  io.error(JSON.stringify({ error: `no dpx session at ${dpxPath} \u2014 run \`dpx auth login\` then \`dpx auth token > ${dpxPath}\` (dpx 2.x keeps its own state in ~/.dpx/auth.json and does NOT write the legacy session file), or omit --dpx` }));
65518
65846
  return 1;
@@ -65799,7 +66127,7 @@ ${report.ok ? "OK \u2014 data + serve plane reachable." : typeof report.hint ===
65799
66127
  const path = dpxSessionTokenPath();
65800
66128
  let raw;
65801
66129
  try {
65802
- raw = readFileSync63(path, "utf8");
66130
+ raw = readFileSync64(path, "utf8");
65803
66131
  } catch {
65804
66132
  io.error(JSON.stringify({ error: `no DPX BO session at ${path}` }));
65805
66133
  return 1;
@@ -66139,12 +66467,14 @@ export {
66139
66467
  ConfigLoadError,
66140
66468
  PLACEHOLDER_CLIENT_ID,
66141
66469
  PLACEHOLDER_SITE_URL,
66470
+ PREVIEW_TOKEN_NOTE,
66142
66471
  PUBLIC_WIX_API_BASE,
66143
66472
  autoClaimSlug,
66144
66473
  buildDoctorReport,
66145
66474
  compareVersions2 as compareVersions,
66146
66475
  decodeControlTokenClaims,
66147
66476
  defaultTargetForCommand,
66477
+ diagnoseBody,
66148
66478
  doctorVersionReport,
66149
66479
  foreignPlaneError,
66150
66480
  formatSdkVersionLine,
@@ -66167,8 +66497,10 @@ export {
66167
66497
  previewPlayerDebugLink,
66168
66498
  previewPlayerLink,
66169
66499
  previewRenderUrl,
66500
+ previewRenewCommand,
66170
66501
  previewScreensUrl,
66171
66502
  previewTelemetryUrl,
66503
+ previewTokenNote,
66172
66504
  previewWebUrl,
66173
66505
  probeControlToken,
66174
66506
  probeReachable,