@wix/himalaya-cli 0.1037.0 → 0.1038.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.
Files changed (75) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/genie-chat/src/index.d.ts +119 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-genie-chat.mjs +51 -1
  3. package/dist/cli.mjs +864 -400
  4. package/dist/preview-assets.generated.bin +0 -0
  5. package/guides/headless-loop.md +7 -0
  6. package/guides/himi-test.md +66 -1
  7. package/guides/sdk-reference.json +74 -2
  8. package/guides/trim-a-template.md +5 -0
  9. package/package.json +2 -2
  10. package/templates/beauty-salon-owner/dev/index.ts +2148 -1262
  11. package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +338 -26
  12. package/templates/beauty-salon-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  13. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  14. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  15. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  16. package/templates/community-club-owner/dev/index.ts +2151 -1265
  17. package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +338 -26
  18. package/templates/community-club-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  19. package/templates/community-club-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  20. package/templates/community-club-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  21. package/templates/community-club-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  22. package/templates/events-meetups-owner/dev/index.ts +2151 -1265
  23. package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +338 -26
  24. package/templates/events-meetups-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  25. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  26. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  27. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  28. package/templates/gym-fitness-owner/dev/index.ts +2148 -1262
  29. package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +338 -26
  30. package/templates/gym-fitness-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  31. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  32. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  33. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  34. package/templates/nonprofit-cause-owner/dev/index.ts +2148 -1262
  35. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +338 -26
  36. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  37. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  38. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  39. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  40. package/templates/owner-lite/dev/index.ts +2145 -1259
  41. package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +338 -26
  42. package/templates/owner-lite/tier5-src/_shared/aria-mentions.ts +202 -0
  43. package/templates/owner-lite/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  44. package/templates/owner-lite/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  45. package/templates/owner-lite/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  46. package/templates/podcast-creator-owner/dev/index.ts +2151 -1265
  47. package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +338 -26
  48. package/templates/podcast-creator-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  49. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  50. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  51. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  52. package/templates/portfolio-creative-owner/dev/index.ts +2148 -1262
  53. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +338 -26
  54. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  55. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  56. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  57. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  58. package/templates/restaurant-owner/dev/index.ts +2148 -1262
  59. package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +338 -26
  60. package/templates/restaurant-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  61. package/templates/restaurant-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  62. package/templates/restaurant-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  63. package/templates/restaurant-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  64. package/templates/retail-store-owner/dev/index.ts +2151 -1265
  65. package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +338 -26
  66. package/templates/retail-store-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  67. package/templates/retail-store-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  68. package/templates/retail-store-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  69. package/templates/retail-store-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
  70. package/templates/yoga-studio-owner/dev/index.ts +2148 -1262
  71. package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +338 -26
  72. package/templates/yoga-studio-owner/tier5-src/_shared/aria-mentions.ts +202 -0
  73. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-aria/index.ts +29 -0
  74. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-ask/index.ts +6 -0
  75. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-manage/index.ts +64 -6
package/dist/cli.mjs CHANGED
@@ -1123,6 +1123,14 @@ var init_mobile_ux_contract = __esm({
1123
1123
  | Screen | Job | Primary action | Data | Notes |
1124
1124
  | --- | --- | --- | --- | --- |
1125
1125
 
1126
+ ## Data access
1127
+ <!-- \`himi test\` checks every write to Wix it sees against this table. One row per screen id (and
1128
+ role); \`Wix writes\` is \`none\`, or the approved writes named by action (\`bookSelected\`),
1129
+ endpoint (\`POST /bookings/v2/bookings\`), capability (\`registerDirect\`) or kind (\`payment\`).
1130
+ \`himi docs himi-test\` -> "Writes to Wix". -->
1131
+ | Screen | Role | Reads | Wix writes | Prohibited |
1132
+ | --- | --- | --- | --- | --- |
1133
+
1126
1134
  ## Platform envelope
1127
1135
  - Required: iOS, Android
1128
1136
  - Optional: web, desktop, watch, widgets
@@ -1528,7 +1536,9 @@ export default defineService("home", {
1528
1536
  surfaceTertiary: { light: "#eef1f4", dark: "#1c2128" },
1529
1537
  textPrimary: { light: "#1f2328", dark: "#e6edf3" },
1530
1538
  textSecondary: { light: "#57606a", dark: "#9198a1" },
1531
- textOnAccent: { light: "#ffffff", dark: "#ffffff" },
1539
+ // Dark ink on the dark arm: white on #388bfd is 3.34:1 and fails AA, which the tokens
1540
+ // pass now warns about (#3275); #0d1117 on it is 5.66:1.
1541
+ textOnAccent: { light: "#ffffff", dark: "#0d1117" },
1532
1542
  accentPrimary: { light: "#0969da", dark: "#388bfd" },
1533
1543
  border: { light: "#d0d7de", dark: "#30363d" },
1534
1544
  danger: { light: "#cf222e", dark: "#f85149" },
@@ -11088,6 +11098,74 @@ var init_token_colors = __esm({
11088
11098
  }
11089
11099
  });
11090
11100
 
11101
+ // ../../core/ts/src/idiom.ts
11102
+ function authored(overlays, name) {
11103
+ return typeof name === "string" && overlays !== void 0 && Object.hasOwn(overlays, name) && overlays[name] !== null && typeof overlays[name] === "object" && !Array.isArray(overlays[name]);
11104
+ }
11105
+ function selectIdiomOverlay(doc, ctx = {}) {
11106
+ const policy = doc.idiomPolicy;
11107
+ const overlays = doc.idiom;
11108
+ const isRecord3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
11109
+ if (!isRecord3(policy) || !isRecord3(overlays)) return void 0;
11110
+ const kind = policy.policy ?? "follow-surface";
11111
+ if (kind === "none") return void 0;
11112
+ if (kind === "pinned") return authored(overlays, policy.pin) ? policy.pin : void 0;
11113
+ if (kind !== "follow-surface") return void 0;
11114
+ const surfaces = policy.surfaces;
11115
+ const candidates = [
11116
+ ctx.surface !== void 0 && isRecord3(surfaces) && Object.hasOwn(surfaces, ctx.surface) ? surfaces[ctx.surface] : void 0,
11117
+ ...ctx.native ?? [],
11118
+ policy.fallback
11119
+ ];
11120
+ return candidates.find((c) => authored(overlays, c));
11121
+ }
11122
+ function isPlainObject(v) {
11123
+ return v !== null && typeof v === "object" && !Array.isArray(v);
11124
+ }
11125
+ function deepMerge(base, overlay) {
11126
+ if (!isPlainObject(overlay) || !isPlainObject(base)) return overlay;
11127
+ const out = { ...base };
11128
+ for (const [key2, value] of Object.entries(overlay)) {
11129
+ if (UNSAFE_KEYS.has(key2)) continue;
11130
+ out[key2] = Object.hasOwn(base, key2) ? deepMerge(base[key2], value) : value;
11131
+ }
11132
+ return out;
11133
+ }
11134
+ function mergeIdiomOverlay(base, overlay) {
11135
+ const merged = deepMerge(base, overlay);
11136
+ const baseColors = base.colors;
11137
+ const overlayColors = overlay.colors;
11138
+ if (isPlainObject(baseColors) && isPlainObject(overlayColors)) {
11139
+ const colors = { ...merged.colors };
11140
+ for (const [name, value] of Object.entries(overlayColors)) {
11141
+ if (UNSAFE_KEYS.has(name)) continue;
11142
+ const was = Object.hasOwn(baseColors, name) ? baseColors[name] : void 0;
11143
+ if (typeof was === "string" && isPlainObject(value)) colors[name] = { light: was, ...value };
11144
+ }
11145
+ merged.colors = colors;
11146
+ }
11147
+ return merged;
11148
+ }
11149
+ function resolveIdiom(tokens, ctx = {}) {
11150
+ const doc = tokens;
11151
+ const name = selectIdiomOverlay(doc, ctx);
11152
+ if (name === void 0) return tokens;
11153
+ const merged = mergeIdiomOverlay(tokens, doc.idiom[name]);
11154
+ const { idiomPolicy: _applied, ...final } = merged;
11155
+ return final;
11156
+ }
11157
+ var IDIOM_CONTEXTS, UNSAFE_KEYS;
11158
+ var init_idiom = __esm({
11159
+ "../../core/ts/src/idiom.ts"() {
11160
+ IDIOM_CONTEXTS = {
11161
+ ios: { native: ["ios"] },
11162
+ android: { native: ["material"] },
11163
+ mac: { native: ["desktop", "ios"], surface: "mac" }
11164
+ };
11165
+ UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
11166
+ }
11167
+ });
11168
+
11091
11169
  // src/token-lint.ts
11092
11170
  import { existsSync as existsSync13, readFileSync as readFileSync14 } from "node:fs";
11093
11171
  import { join as join16 } from "node:path";
@@ -11178,11 +11256,109 @@ function tokenColorIssues(dir, tokensPathOverride) {
11178
11256
  `${tokensPath}: colors.${problem.path} is ${problem.value} \u2014 the native runtimes parse only #RRGGBB or #RRGGBBAA` + (problem.suggestion ? `. Write ${problem.suggestion}.` : `, and this value names no colour that can be converted for you. Write the hex it should be.`)
11179
11257
  );
11180
11258
  }
11259
+ const base = contrastFindings(colors);
11260
+ for (const f of base) warn(`${tokensPath}: ${f.render(f.schemes)}`);
11261
+ if (idiom !== null && typeof idiom === "object" && !Array.isArray(idiom)) {
11262
+ const seen = new Set(base.flatMap((f) => f.schemes.map((s) => `${f.key}|${s}`)));
11263
+ for (const [overlay, layer] of Object.entries(idiom)) {
11264
+ const layerColors = layer?.colors;
11265
+ if (layerColors === null || typeof layerColors !== "object" || Array.isArray(layerColors)) continue;
11266
+ const merged = mergeIdiomOverlay({ colors }, { colors: layerColors }).colors;
11267
+ for (const f of contrastFindings(merged)) {
11268
+ const added = f.schemes.filter((s) => !seen.has(`${f.key}|${s}`));
11269
+ if (added.length) warn(`${tokensPath}: under the idiom.${overlay} overlay, ${f.render(added)}`);
11270
+ }
11271
+ }
11272
+ }
11181
11273
  return issues;
11182
11274
  }
11275
+ function schemeValue(token, scheme) {
11276
+ let v = token;
11277
+ if (token !== null && typeof token === "object" && !Array.isArray(token)) {
11278
+ const o = token;
11279
+ v = scheme === "highContrast" ? o.highContrast ?? o.dark ?? o.light : scheme === "dark" ? o.dark ?? o.light : o.light;
11280
+ }
11281
+ return typeof v === "string" && isCanonicalHex(v) ? v.trim().toUpperCase() : void 0;
11282
+ }
11283
+ function paintedOver(fg, bg) {
11284
+ if (bg.length === 9 && bg.slice(7) !== "FF") return void 0;
11285
+ if (fg.length === 9) return mixHex(fg.slice(0, 7), bg.slice(0, 7), parseInt(fg.slice(7), 16) / 255);
11286
+ return fg;
11287
+ }
11288
+ function sameHueForContrast(fg, bg, target = AA_TEXT) {
11289
+ const base = fg.slice(0, 7);
11290
+ const opaqueBg = bg.slice(0, 7);
11291
+ const pole = contrastRatio("#000000", opaqueBg) >= contrastRatio("#FFFFFF", opaqueBg) ? "#000000" : "#FFFFFF";
11292
+ for (let step = 0; step <= 100; step++) {
11293
+ const candidate = mixHex(base, pole, 1 - step / 100);
11294
+ if (contrastRatio(candidate, opaqueBg) >= target) return candidate;
11295
+ }
11296
+ return void 0;
11297
+ }
11298
+ function contrastFindings(colors) {
11299
+ const declared = /* @__PURE__ */ new Set(["light"]);
11300
+ for (const v of Object.values(colors)) {
11301
+ if (v === null || typeof v !== "object" || Array.isArray(v)) continue;
11302
+ for (const s of SCHEMES) if (s in v) declared.add(s);
11303
+ }
11304
+ const findings = /* @__PURE__ */ new Map();
11305
+ for (const pair of CONTRAST_PAIRS) {
11306
+ const fgName = pair.fg;
11307
+ if (colors[fgName] === void 0 || colors[pair.bg] === void 0) continue;
11308
+ for (const scheme of SCHEMES) {
11309
+ if (!declared.has(scheme)) continue;
11310
+ const fg = schemeValue(colors[fgName], scheme);
11311
+ const bg = schemeValue(colors[pair.bg], scheme);
11312
+ if (!fg || !bg) continue;
11313
+ const painted = paintedOver(fg, bg);
11314
+ if (!painted) continue;
11315
+ const opaqueBg = bg.slice(0, 7);
11316
+ const ratio = contrastRatio(painted, opaqueBg);
11317
+ if (ratio >= AA_TEXT) continue;
11318
+ const key2 = `${fgName}|${pair.bg}|${fg}|${bg}`;
11319
+ const same2 = findings.get(key2);
11320
+ if (same2) {
11321
+ same2.schemes.push(scheme);
11322
+ continue;
11323
+ }
11324
+ const darker = contrastRatio("#000000", opaqueBg) >= contrastRatio("#FFFFFF", opaqueBg);
11325
+ const ink = darker ? "#000000" : "#FFFFFF";
11326
+ const suggestion = pair.suggest === "same-hue" ? sameHueForContrast(painted, opaqueBg) : pair.suggest === "ink" && contrastRatio(ink, opaqueBg) >= AA_TEXT ? ink : void 0;
11327
+ const fix = !suggestion ? ` Pick a ${fgName} that clears ${AA_TEXT}:1 against ${pair.bg}.` : pair.suggest === "same-hue" ? ` A ${darker ? "darker" : "lighter"} ${fgName} of the same hue that clears it is ${suggestion} (${contrastRatio(suggestion, opaqueBg).toFixed(2)}:1).` : ` ${suggestion} clears it (${contrastRatio(suggestion, opaqueBg).toFixed(2)}:1).`;
11328
+ findings.set(key2, {
11329
+ key: key2,
11330
+ schemes: [scheme],
11331
+ render: (schemes) => `colors.${fgName} (${fg}) on colors.${pair.bg} (${bg}) is ${ratio.toFixed(2)}:1 in ${schemes.join(" / ")} \u2014 below the WCAG AA ${AA_TEXT}:1 for text. ${fgName} is ${pair.use}, so that text is hard to read.${fix}`
11332
+ });
11333
+ }
11334
+ }
11335
+ return [...findings.values()];
11336
+ }
11337
+ var AA_TEXT, CONTRAST_PAIRS, SCHEMES, channels, toHex, mixHex;
11183
11338
  var init_token_lint = __esm({
11184
11339
  "src/token-lint.ts"() {
11185
11340
  init_token_colors();
11341
+ init_idiom();
11342
+ AA_TEXT = 4.5;
11343
+ CONTRAST_PAIRS = [
11344
+ {
11345
+ fg: "accentPrimary",
11346
+ bg: "surfacePrimary",
11347
+ use: "the text colour of secondary, plain and link buttons, selected tab labels and inline action text",
11348
+ suggest: "same-hue"
11349
+ },
11350
+ { fg: "accentText", bg: "surfacePrimary", use: "the dedicated colour for accent-coloured text", suggest: "same-hue" },
11351
+ { fg: "textOnAccent", bg: "accentPrimary", use: "the label on filled (primary) buttons and selected chips", suggest: "ink" },
11352
+ { fg: "textPrimary", bg: "surfacePrimary", use: "body and title text" },
11353
+ { fg: "textSecondary", bg: "surfacePrimary", use: "secondary text (subtitles, captions, metadata)" }
11354
+ ];
11355
+ SCHEMES = ["light", "dark", "highContrast"];
11356
+ channels = (hex2) => [1, 3, 5].map((i) => parseInt(hex2.slice(i, i + 2), 16));
11357
+ toHex = (rgb) => `#${rgb.map((c) => Math.round(Math.min(255, Math.max(0, c))).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
11358
+ mixHex = (a, b, w) => {
11359
+ const [ca, cb] = [channels(a), channels(b)];
11360
+ return toHex(ca.map((c, i) => c * w + cb[i] * (1 - w)));
11361
+ };
11186
11362
  }
11187
11363
  });
11188
11364
 
@@ -11841,8 +12017,8 @@ async function sharpOrThrow() {
11841
12017
  async function inspectIconPixels(png) {
11842
12018
  const sharp = await sharpOrThrow();
11843
12019
  const { data, info } = await sharp(png).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
11844
- const { width, height, channels: channels2 } = info;
11845
- if (channels2 !== 4) throw new Error(`app-icons: expected RGBA after ensureAlpha, got ${channels2} channels`);
12020
+ const { width, height, channels: channels3 } = info;
12021
+ if (channels3 !== 4) throw new Error(`app-icons: expected RGBA after ensureAlpha, got ${channels3} channels`);
11846
12022
  let minAlpha = 255;
11847
12023
  let transparent = 0;
11848
12024
  for (let i = 3; i < data.length; i += 4) {
@@ -12691,75 +12867,6 @@ var init_face_source = __esm({
12691
12867
  }
12692
12868
  });
12693
12869
 
12694
- // ../../core/ts/src/idiom.ts
12695
- function authored(overlays, name) {
12696
- return typeof name === "string" && overlays !== void 0 && Object.hasOwn(overlays, name) && overlays[name] !== null && typeof overlays[name] === "object" && !Array.isArray(overlays[name]);
12697
- }
12698
- function selectIdiomOverlay(doc, ctx = {}) {
12699
- const policy = doc.idiomPolicy;
12700
- const overlays = doc.idiom;
12701
- const isRecord3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
12702
- if (!isRecord3(policy) || !isRecord3(overlays)) return void 0;
12703
- const kind = policy.policy ?? "follow-surface";
12704
- if (kind === "none") return void 0;
12705
- if (kind === "pinned") return authored(overlays, policy.pin) ? policy.pin : void 0;
12706
- if (kind !== "follow-surface") return void 0;
12707
- const surfaces = policy.surfaces;
12708
- const candidates = [
12709
- ctx.surface !== void 0 && isRecord3(surfaces) && Object.hasOwn(surfaces, ctx.surface) ? surfaces[ctx.surface] : void 0,
12710
- ...ctx.native ?? [],
12711
- policy.fallback
12712
- ];
12713
- return candidates.find((c) => authored(overlays, c));
12714
- }
12715
- function isPlainObject(v) {
12716
- return v !== null && typeof v === "object" && !Array.isArray(v);
12717
- }
12718
- function deepMerge(base, overlay) {
12719
- if (!isPlainObject(overlay) || !isPlainObject(base)) return overlay;
12720
- const out = { ...base };
12721
- for (const [key2, value] of Object.entries(overlay)) {
12722
- if (UNSAFE_KEYS.has(key2)) continue;
12723
- out[key2] = Object.hasOwn(base, key2) ? deepMerge(base[key2], value) : value;
12724
- }
12725
- return out;
12726
- }
12727
- function mergeIdiomOverlay(base, overlay) {
12728
- const merged = deepMerge(base, overlay);
12729
- const baseColors = base.colors;
12730
- const overlayColors = overlay.colors;
12731
- if (isPlainObject(baseColors) && isPlainObject(overlayColors)) {
12732
- const colors = { ...merged.colors };
12733
- for (const [name, value] of Object.entries(overlayColors)) {
12734
- if (UNSAFE_KEYS.has(name)) continue;
12735
- const was = Object.hasOwn(baseColors, name) ? baseColors[name] : void 0;
12736
- if (typeof was === "string" && isPlainObject(value)) colors[name] = { light: was, ...value };
12737
- }
12738
- merged.colors = colors;
12739
- }
12740
- return merged;
12741
- }
12742
- function resolveIdiom(tokens, ctx = {}) {
12743
- const doc = tokens;
12744
- const name = selectIdiomOverlay(doc, ctx);
12745
- if (name === void 0) return tokens;
12746
- const merged = mergeIdiomOverlay(tokens, doc.idiom[name]);
12747
- const { idiomPolicy: _applied, ...final } = merged;
12748
- return final;
12749
- }
12750
- var IDIOM_CONTEXTS, UNSAFE_KEYS;
12751
- var init_idiom = __esm({
12752
- "../../core/ts/src/idiom.ts"() {
12753
- "use strict";
12754
- IDIOM_CONTEXTS = {
12755
- ios: { native: ["ios"] },
12756
- android: { native: ["material"] },
12757
- mac: { native: ["desktop", "ios"], surface: "mac" }
12758
- };
12759
- UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
12760
- }
12761
- });
12762
-
12763
12870
  // ../../core/web-renderer/src/idiom-controls.ts
12764
12871
  function pickRole(colors, role) {
12765
12872
  if (typeof role === "string") return role;
@@ -20137,7 +20244,6 @@ function shouldRevealError(args) {
20137
20244
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
20138
20245
  var init_validation = __esm({
20139
20246
  "../../core/ts/src/validation.ts"() {
20140
- "use strict";
20141
20247
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
20142
20248
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
20143
20249
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -23724,7 +23830,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
23724
23830
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
23725
23831
  var init_worker_subtree = __esm({
23726
23832
  "../../core/ts/src/worker-subtree.ts"() {
23727
- "use strict";
23728
23833
  init_worker_subtree_safe_types_generated();
23729
23834
  WORKER_SUBTREE_CEILINGS = Object.freeze({
23730
23835
  maxEncodedBytes: 65536,
@@ -36627,6 +36732,8 @@ async function bootScreenWorker(opts) {
36627
36732
  ...opts.inputOverride ?? {}
36628
36733
  };
36629
36734
  const appear = opts.skipAppear ? null : rootOnAppear(opts.descriptor);
36735
+ const hostCalls = [];
36736
+ const host = hostDispatch(opts.localDbSchema, declaredCapabilitiesOf(opts.descriptor), opts.capabilitySuccess === true, opts.clock ?? CRAWL_CLOCK);
36630
36737
  const w = await bootBundleWorker(opts.bundleJs, {
36631
36738
  wixCall: opts.wixCall,
36632
36739
  input,
@@ -36635,7 +36742,10 @@ async function bootScreenWorker(opts) {
36635
36742
  // while the worker believed it was 2026, so a modelled session carried a timestamp fifty-six
36636
36743
  // years before the "now" the app computes against — inconsistent rendered dates, and any
36637
36744
  // time-dependent branch taking the wrong arm.
36638
- dispatch: hostDispatch(opts.localDbSchema, declaredCapabilitiesOf(opts.descriptor), opts.capabilitySuccess === true, opts.clock ?? CRAWL_CLOCK),
36745
+ dispatch: (action, params) => {
36746
+ hostCalls.push(String(action));
36747
+ return host(action, params);
36748
+ },
36639
36749
  deviceParity: true,
36640
36750
  clock: opts.clock ?? CRAWL_CLOCK,
36641
36751
  seed: opts.seed ?? 1,
@@ -36658,7 +36768,12 @@ async function bootScreenWorker(opts) {
36658
36768
  }
36659
36769
  for (let i = 0; i < 20; i++) await yieldMacrotask();
36660
36770
  const { state, defects } = snapshot(w);
36661
- return { ...w, state: Object.keys(state).length ? state : w.services, defects, ...deadLifecycle ? { deadLifecycle } : {} };
36771
+ return { ...w, hostCalls, state: Object.keys(state).length ? state : w.services, defects, ...deadLifecycle ? { deadLifecycle } : {} };
36772
+ }
36773
+ function effectSince(w, mark, action, componentId) {
36774
+ const wix = w.calls.slice(mark.wix);
36775
+ const host = w.hostCalls.slice(mark.host);
36776
+ return wix.length || host.length ? { action, componentId, wix, host } : null;
36662
36777
  }
36663
36778
  function resolveSiteRows(itemsPath, valueOf) {
36664
36779
  if (!itemsPath || itemsPath === UNTRACKED_ITEMS) return void 0;
@@ -36719,7 +36834,10 @@ async function crawlScreenCapabilitySuccess(opts) {
36719
36834
  const r = await crawlScreenInner({ ...opts, capabilitySuccess: true });
36720
36835
  return {
36721
36836
  findings: r.findings.map((f) => ({ ...f, axis: f.axis ? `capability:success+${f.axis}` : "capability:success" })),
36722
- modelled: modelled.length
36837
+ modelled: modelled.length,
36838
+ // The success arm is where a paid flow goes on past `isAvailable` to `proceedToPayment`, so
36839
+ // its effects are the write inventory's too — unlike its counts, they cannot double anything.
36840
+ ...r.effects?.length ? { effects: r.effects } : {}
36723
36841
  };
36724
36842
  });
36725
36843
  }
@@ -36759,6 +36877,9 @@ async function crawlScreenJourney(opts) {
36759
36877
  });
36760
36878
  const sites = actionSitesForDescriptor(opts.descriptor);
36761
36879
  let stepsExercised = 0;
36880
+ const effects = [];
36881
+ const bootEffect = effectSince(worker, { wix: 0, host: 0 }, "boot", null);
36882
+ if (bootEffect) effects.push(bootEffect);
36762
36883
  for (const step of opts.journey.steps) {
36763
36884
  const declared = sites.find((site2) => site2.actionId === step.action && (step.component === void 0 || site2.componentId === step.component));
36764
36885
  if (!declared) {
@@ -36781,6 +36902,7 @@ async function crawlScreenJourney(opts) {
36781
36902
  fail2(`journey "${opts.journey.name}" names unavailable worker action ${step.action}`, step);
36782
36903
  continue;
36783
36904
  }
36905
+ const mark = { wix: worker.calls.length, host: worker.hostCalls.length };
36784
36906
  try {
36785
36907
  await handle.dispatch(parsed.action, ...workerArgs({ params: { ...site.params ?? {}, ...site.args ?? {}, ...step.params ?? {} } }));
36786
36908
  for (let i = 0; i < 8; i++) await yieldMacrotask();
@@ -36788,6 +36910,8 @@ async function crawlScreenJourney(opts) {
36788
36910
  } catch (error) {
36789
36911
  fail2(`journey "${opts.journey.name}" step ${step.action} threw: ${errText(error)}`, step);
36790
36912
  }
36913
+ const stepEffect = effectSince(worker, mark, site.actionId, site.componentId);
36914
+ if (stepEffect) effects.push(stepEffect);
36791
36915
  }
36792
36916
  const settled = snapshot(worker);
36793
36917
  if (worker.errors.length) {
@@ -36797,7 +36921,7 @@ async function crawlScreenJourney(opts) {
36797
36921
  for (const expected of opts.journey.expectText) {
36798
36922
  if (!values.includes(expected)) fail2(`journey "${opts.journey.name}" never rendered its expected visible result: ${JSON.stringify(expected)}`);
36799
36923
  }
36800
- return { screen: opts.screenId, journey: opts.journey.name, stepsExercised, findings };
36924
+ return { screen: opts.screenId, journey: opts.journey.name, stepsExercised, findings, ...effects.length ? { effects } : {} };
36801
36925
  });
36802
36926
  }
36803
36927
  async function crawlScreenInner(opts) {
@@ -36812,6 +36936,7 @@ async function crawlScreenInner(opts) {
36812
36936
  const actionsSkipped = [];
36813
36937
  const navEdges = [];
36814
36938
  const unobservedActions = [];
36939
+ const effects = [];
36815
36940
  let observations;
36816
36941
  let seedWords = [];
36817
36942
  const harvestStaticEdges = (state) => {
@@ -37095,6 +37220,8 @@ async function crawlScreenInner(opts) {
37095
37220
  harvestStaticEdges(first.state);
37096
37221
  drainWorkerEdges(first, "worker:boot");
37097
37222
  drainPollDirectives(first, "boot");
37223
+ const bootEffect = effectSince(first, { wix: 0, host: 0 }, "boot", null);
37224
+ if (bootEffect) effects.push(bootEffect);
37098
37225
  if (/\bwixApi\b/.test(bundleJs) && first.calls.length === 0) {
37099
37226
  const inputGated = /himalaya\??\.input\b|\binput\s*[.?]/.test(bundleJs);
37100
37227
  findings.push({
@@ -37180,6 +37307,7 @@ async function crawlScreenInner(opts) {
37180
37307
  const dispatched = typed ? { ...effective, args: { ...effective.args ?? {}, [typed.key]: typed.value } } : effective;
37181
37308
  const before = JSON.stringify(preState);
37182
37309
  const callsBefore = w.calls.length;
37310
+ const hostBefore = w.hostCalls.length;
37183
37311
  const errors = [];
37184
37312
  try {
37185
37313
  await handle.dispatch(parsed.action, ...workerArgs(dispatched));
@@ -37192,6 +37320,8 @@ async function crawlScreenInner(opts) {
37192
37320
  const navCountBefore = navEdges.length;
37193
37321
  drainWorkerEdges(w, `worker:${site.actionId}`);
37194
37322
  drainPollDirectives(w, trigger);
37323
+ const tapEffect = effectSince(w, { wix: callsBefore, host: hostBefore }, site.actionId, site.componentId);
37324
+ if (tapEffect) effects.push(tapEffect);
37195
37325
  if (!errors.length && navEdges.length === navCountBefore && w.calls.length === callsBefore && JSON.stringify(state) === before) {
37196
37326
  if (typed) {
37197
37327
  findings.push({
@@ -37256,6 +37386,7 @@ async function crawlScreenInner(opts) {
37256
37386
  actionsExercised: exercised,
37257
37387
  actionsSkipped,
37258
37388
  navEdges: edges,
37389
+ ...effects.length ? { effects } : {},
37259
37390
  ...observations ? { observations } : {},
37260
37391
  ...unobservedActions.length ? { unobservedActions } : {},
37261
37392
  findings: dedupe(findings),
@@ -37474,6 +37605,7 @@ async function crawlScreenDeep(opts, deep) {
37474
37605
  const queue = [[]];
37475
37606
  let dispatches = 0;
37476
37607
  let capped = false;
37608
+ const effects = [];
37477
37609
  const boot = () => bootScreenWorker({
37478
37610
  bundleJs,
37479
37611
  descriptor,
@@ -37494,6 +37626,13 @@ async function crawlScreenDeep(opts, deep) {
37494
37626
  }
37495
37627
  const path2 = queue.shift();
37496
37628
  const w = await boot();
37629
+ let pending = null;
37630
+ const flushStep = () => {
37631
+ if (!pending) return;
37632
+ const e = effectSince(w, pending.mark, pending.site.actionId, pending.site.componentId);
37633
+ if (e) effects.push(e);
37634
+ pending = null;
37635
+ };
37497
37636
  try {
37498
37637
  let threwAt = null;
37499
37638
  for (const [step, siteIdx] of path2.entries()) {
@@ -37515,6 +37654,8 @@ async function crawlScreenDeep(opts, deep) {
37515
37654
  break;
37516
37655
  }
37517
37656
  dispatches += 1;
37657
+ flushStep();
37658
+ pending = { mark: { wix: w.calls.length, host: w.hostCalls.length }, site };
37518
37659
  try {
37519
37660
  await handle.dispatch(parsed.action, ...workerArgs(effective));
37520
37661
  } catch (e) {
@@ -37535,8 +37676,12 @@ async function crawlScreenDeep(opts, deep) {
37535
37676
  break;
37536
37677
  }
37537
37678
  }
37538
- if (threwAt) continue;
37679
+ if (threwAt) {
37680
+ flushStep();
37681
+ continue;
37682
+ }
37539
37683
  for (let i = 0; i < 20; i++) await yieldMacrotask();
37684
+ flushStep();
37540
37685
  const { state, defects } = snapshot(w);
37541
37686
  const hash = deepStateSignature(state, defects);
37542
37687
  if (visited.has(hash)) continue;
@@ -37581,7 +37726,7 @@ async function crawlScreenDeep(opts, deep) {
37581
37726
  w.dispose();
37582
37727
  }
37583
37728
  }
37584
- return { findings: dedupe(findings), nodesVisited: visited.size, dispatches, capped };
37729
+ return { findings: dedupe(findings), nodesVisited: visited.size, dispatches, capped, ...effects.length ? { effects } : {} };
37585
37730
  });
37586
37731
  }
37587
37732
  function effectiveSiteForRow(site, row) {
@@ -40896,11 +41041,11 @@ function notCarriedDiff(local, base) {
40896
41041
  const CARRIED = /* @__PURE__ */ new Set(["screens", "bundles", "tokensHash", "appConfigHash"]);
40897
41042
  const PROVENANCE = /* @__PURE__ */ new Set(["releaseId", "gitSha", "shellSha", "label", "notes", "publishedAt", "publishedBy", "attestation", "forcedPastErrors", "app", "appSourceHash"]);
40898
41043
  const REPORTED_IN_DETAIL = /* @__PURE__ */ new Set(["assets", "minBinaryVersion", "iconHash"]);
40899
- const comparable = (key2, value) => key2 === "widgets" ? stableWidgets(value) : value;
41044
+ const comparable2 = (key2, value) => key2 === "widgets" ? stableWidgets(value) : value;
40900
41045
  for (const key2 of /* @__PURE__ */ new Set([...Object.keys(local), ...Object.keys(base)])) {
40901
41046
  if (CARRIED.has(key2) || PROVENANCE.has(key2) || REPORTED_IN_DETAIL.has(key2)) continue;
40902
- const l = comparable(key2, local[key2] ?? null);
40903
- const b = comparable(key2, base[key2] ?? null);
41047
+ const l = comparable2(key2, local[key2] ?? null);
41048
+ const b = comparable2(key2, base[key2] ?? null);
40904
41049
  const ls = JSON.stringify(l) ?? "null";
40905
41050
  const bs = JSON.stringify(b) ?? "null";
40906
41051
  if (ls === bs) continue;
@@ -42067,6 +42212,7 @@ var init_home_descriptor = __esm({
42067
42212
  var template_trim_exports = {};
42068
42213
  __export(template_trim_exports, {
42069
42214
  TrimRefusal: () => TrimRefusal,
42215
+ compareErrors: () => compareErrors,
42070
42216
  leftoverNavRefs: () => leftoverNavRefs,
42071
42217
  linearSafePattern: () => linearSafePattern,
42072
42218
  parseApp: () => parseApp,
@@ -42835,6 +42981,16 @@ async function runTemplateTrim(req, deps) {
42835
42981
  if (!existsSync42(path2) || !readFileSync46(path2).equals(original)) writeFileSync15(path2, original);
42836
42982
  }
42837
42983
  for (const name of [...moved].reverse()) renameSync4(join39(backup, "tier5-src", name), join39(dir, "tier5-src", name));
42984
+ moved.length = 0;
42985
+ };
42986
+ const apply = () => {
42987
+ const moveDir = deps.moveDir ?? renameSync4;
42988
+ for (const name of plan.removedWorkers) {
42989
+ moveDir(join39(dir, "tier5-src", name), join39(backup, "tier5-src", name));
42990
+ moved.push(name);
42991
+ }
42992
+ if (plan.dev !== dev) writeFileSync15(devPath, plan.dev);
42993
+ if (plan.config !== config) writeFileSync15(configPath, plan.config);
42838
42994
  };
42839
42995
  const dropBackup = () => {
42840
42996
  rmSync8(backup, { recursive: true, force: true });
@@ -42873,13 +43029,7 @@ async function runTemplateTrim(req, deps) {
42873
43029
  return { code: 1, result: { ok: false, error: `could not back up the app before trimming it (${e.message}); nothing was changed`, rolledBack: true, ...base() } };
42874
43030
  }
42875
43031
  try {
42876
- const moveDir = deps.moveDir ?? renameSync4;
42877
- for (const name of plan.removedWorkers) {
42878
- moveDir(join39(dir, "tier5-src", name), join39(backup, "tier5-src", name));
42879
- moved.push(name);
42880
- }
42881
- if (plan.dev !== dev) writeFileSync15(devPath, plan.dev);
42882
- if (plan.config !== config) writeFileSync15(configPath, plan.config);
43032
+ apply();
42883
43033
  } catch (e) {
42884
43034
  return abort("the trim failed while writing", e);
42885
43035
  }
@@ -42896,9 +43046,53 @@ async function runTemplateTrim(req, deps) {
42896
43046
  } catch (e) {
42897
43047
  return abort("the trim could not be checked", e, validate3);
42898
43048
  }
43049
+ let compared;
43050
+ if (failure && validate3 && validate3.code !== 0 && comparable(validate3)) {
43051
+ try {
43052
+ restore();
43053
+ } catch (e) {
43054
+ return abort(String(failure.error), e, validate3);
43055
+ }
43056
+ let original;
43057
+ try {
43058
+ original = await deps.validate(dir, { fresh: true });
43059
+ } catch {
43060
+ }
43061
+ if (original && comparable(original)) {
43062
+ compared = compareErrors(validate3.errors ?? [], original.code === 0 ? [] : original.errors ?? []);
43063
+ }
43064
+ const clean2 = compared !== void 0 && compared.introduced.length === 0;
43065
+ if (clean2 || req.force) {
43066
+ try {
43067
+ apply();
43068
+ } catch (e) {
43069
+ return abort("the trim failed while re-applying it after validating the untrimmed app", e, validate3);
43070
+ }
43071
+ }
43072
+ if (clean2) failure = void 0;
43073
+ else if (compared) {
43074
+ failure = {
43075
+ ...failure,
43076
+ error: `himi validate --strict failed on the trimmed app \u2014 the trim introduced ${plural(compared.introduced.length, "error")} the untrimmed app does not have: ${summarize3(compared.introduced)}`,
43077
+ introducedErrors: compared.introduced,
43078
+ preExistingErrors: compared.preExisting
43079
+ };
43080
+ }
43081
+ }
42899
43082
  if (!failure) {
42900
43083
  dropBackup();
42901
- return { code: 0, result: { ok: true, ...base(validate3) } };
43084
+ const preExisting = compared?.preExisting ?? [];
43085
+ return {
43086
+ code: 0,
43087
+ result: {
43088
+ ok: true,
43089
+ ...base(validate3),
43090
+ ...preExisting.length ? {
43091
+ preExistingErrors: preExisting,
43092
+ note: `trimmed; ${plural(preExisting.length, "error")} ${preExisting.length === 1 ? "was" : "were"} already present before trim: ${summarize3(preExisting)}`
43093
+ } : {}
43094
+ }
43095
+ };
42902
43096
  }
42903
43097
  if (req.force) {
42904
43098
  return {
@@ -42917,7 +43111,36 @@ async function runTemplateTrim(req, deps) {
42917
43111
  result: { ok: false, ...failure, rolledBack: true, hint: "nothing changed \u2014 fix what validate reports (or keep more screens) and trim again, or pass --force to keep the trimmed tree anyway", ...base(validate3) }
42918
43112
  };
42919
43113
  }
42920
- var TrimRefusal, LIT, ATOM, QUANT, CAP, SAFE_ROUTE_PATTERN, lineOf, isStatic, entryLabel, isNode3, asItems, NAV_SCREEN_KEYS, sorted, refuse3;
43114
+ function comparable(v) {
43115
+ if (v.code === 0) return true;
43116
+ if (!v.errors?.length) return false;
43117
+ return v.counts === void 0 || v.counts.errors === v.errors.length;
43118
+ }
43119
+ function compareErrors(trimmed, original) {
43120
+ const left = /* @__PURE__ */ new Map();
43121
+ for (const e of original) left.set(e.id, (left.get(e.id) ?? 0) + 1);
43122
+ const out = { preExisting: [], introduced: [] };
43123
+ for (const e of trimmed) {
43124
+ const n = left.get(e.id) ?? 0;
43125
+ if (n > 0) {
43126
+ left.set(e.id, n - 1);
43127
+ out.preExisting.push(e);
43128
+ } else out.introduced.push(e);
43129
+ }
43130
+ return out;
43131
+ }
43132
+ function summarize3(errors) {
43133
+ const one = (e) => {
43134
+ const rule = e.pass ? e.ruleCode && e.ruleCode !== e.pass ? `${e.pass}/${e.ruleCode}` : e.pass : e.ruleCode;
43135
+ const place = e.screen ? `on ${e.screen}` : e.path ? `in ${e.path}` : void 0;
43136
+ const where = [rule, place].filter(Boolean).join(" ");
43137
+ const msg = e.message.length > 160 ? `${e.message.slice(0, 157)}...` : e.message;
43138
+ return where ? `${where}: ${msg}` : msg;
43139
+ };
43140
+ const shown2 = errors.slice(0, 5).map(one).join("; ");
43141
+ return errors.length > 5 ? `${shown2}; and ${errors.length - 5} more` : shown2;
43142
+ }
43143
+ var TrimRefusal, LIT, ATOM, QUANT, CAP, SAFE_ROUTE_PATTERN, lineOf, isStatic, entryLabel, isNode3, asItems, NAV_SCREEN_KEYS, sorted, refuse3, plural;
42921
43144
  var init_template_trim = __esm({
42922
43145
  "src/template-trim.ts"() {
42923
43146
  init_validate_descriptor();
@@ -42951,6 +43174,7 @@ var init_template_trim = __esm({
42951
43174
  NAV_SCREEN_KEYS = /* @__PURE__ */ new Set(["screenId", "parentScreenId", "contentScreenId", "badgeScreenId"]);
42952
43175
  sorted = (xs) => [...new Set(xs)].sort();
42953
43176
  refuse3 = (e) => ({ code: 2, result: { ok: false, error: e.message, ...e.detail } });
43177
+ plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
42954
43178
  }
42955
43179
  });
42956
43180
 
@@ -44232,6 +44456,184 @@ var init_eject = __esm({
44232
44456
  }
44233
44457
  });
44234
44458
 
44459
+ // src/wix-writes.ts
44460
+ import { existsSync as existsSync46, readFileSync as readFileSync50 } from "node:fs";
44461
+ import { join as join43 } from "node:path";
44462
+ function isProvenRead(method, path2) {
44463
+ if (/[#\x00-\x1F\x7F]/.test(path2)) return false;
44464
+ const verb = method.toUpperCase();
44465
+ if (verb === "GET") return true;
44466
+ const bare = path2.split("?")[0].replace(/\/+$/, "");
44467
+ return verb === "POST" && /\/(query|search|list[^/]*|get[^/]*|count|aggregate|facets|connections)$/i.test(bare);
44468
+ }
44469
+ function nativeWriteOf(verb) {
44470
+ const exact = NATIVE_WRITES[verb];
44471
+ if (exact) return { canonical: verb, ...exact };
44472
+ const member = MEMBER_VERB.exec(verb);
44473
+ if (member) return { canonical: `himi.wix.${member[1]}`, ...NATIVE_WRITES[`himi.wix.${member[1]}`] };
44474
+ return null;
44475
+ }
44476
+ function writesFromEffects(screen, effects) {
44477
+ const out = [];
44478
+ for (const e of effects ?? []) {
44479
+ const base = { screen, action: e.action, ...e.componentId ? { component: e.componentId } : {} };
44480
+ for (const call of e.wix) {
44481
+ const sp = call.indexOf(" ");
44482
+ const method = (sp < 0 ? call : call.slice(0, sp)).toUpperCase();
44483
+ const path2 = sp < 0 ? "" : call.slice(sp + 1);
44484
+ if (isProvenRead(method, path2)) continue;
44485
+ out.push({ ...base, via: "wix", method, path: path2.split("?")[0] });
44486
+ }
44487
+ for (const verb of e.host) {
44488
+ const native = nativeWriteOf(verb);
44489
+ if (native) out.push({ ...base, via: "native", capability: verb, kind: native.kind, what: native.what });
44490
+ }
44491
+ }
44492
+ return dedupeWrites(out);
44493
+ }
44494
+ function dedupeWrites(list) {
44495
+ const seen = /* @__PURE__ */ new Set();
44496
+ const out = [];
44497
+ for (const w of list) {
44498
+ const k = writeKey(w);
44499
+ if (seen.has(k)) continue;
44500
+ seen.add(k);
44501
+ out.push(w);
44502
+ }
44503
+ return out;
44504
+ }
44505
+ function unreachedNativeWrites(screen, bundleJs, observed) {
44506
+ if (!bundleJs) return [];
44507
+ const seen = new Set(observed.filter((w) => w.screen === screen && w.via === "native").map((w) => nativeWriteOf(w.capability)?.canonical));
44508
+ const out = [];
44509
+ for (const [verb, native] of Object.entries(NATIVE_WRITES)) {
44510
+ if (seen.has(verb)) continue;
44511
+ if (!BUNDLE_TEXT[verb].test(bundleJs)) continue;
44512
+ out.push({ screen, action: "unreached", via: "native", capability: verb, kind: native.kind, what: native.what });
44513
+ }
44514
+ return out;
44515
+ }
44516
+ function parseDataAccess(text5, from) {
44517
+ const body = text5.replace(/<!--[\s\S]*?(?:-->|$)/g, "");
44518
+ const heading = new RegExp(`(?:^|\\n)##[ \\t]+${DATA_ACCESS_HEADING}\\b[^\\n]*([\\s\\S]*?)(?=\\n##[ \\t]|$)`, "i").exec(body);
44519
+ if (!heading) return null;
44520
+ const lines = heading[1].split("\n").map((l) => l.trim()).filter((l) => l.startsWith("|"));
44521
+ if (lines.length < 2) return null;
44522
+ const cells = (line) => line.replace(/^\|/, "").replace(/\|\s*$/, "").split("|").map((c) => c.trim());
44523
+ const header = cells(lines[0]).map((c) => unquote(c).toLowerCase());
44524
+ const screenCol = header.findIndex((h) => /^screens?$/.test(h));
44525
+ let writesCol = header.findIndex((h) => /^wix writes?$/.test(h));
44526
+ if (writesCol < 0) writesCol = header.findIndex((h) => /^writes?$/.test(h));
44527
+ if (screenCol < 0 || writesCol < 0) return null;
44528
+ const rows = [];
44529
+ for (const line of lines.slice(1)) {
44530
+ if (/^\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?$/.test(line)) continue;
44531
+ const row = cells(line);
44532
+ const screen = unquote(row[screenCol] ?? "");
44533
+ if (!screen) continue;
44534
+ const cell = unquote(row[writesCol] ?? "");
44535
+ const entries = cell.split(/[,;]/).map((e) => e.trim()).filter((e) => e && !NONE.test(e));
44536
+ rows.push({ screen, allowed: NONE.test(cell) || !entries.length ? null : entries });
44537
+ }
44538
+ return rows.length ? { from, rows } : null;
44539
+ }
44540
+ function readDataAccess(dir) {
44541
+ for (const name of ["SPEC.md", "MOBILE-UX.md"]) {
44542
+ const p = join43(dir, name);
44543
+ if (!existsSync46(p)) continue;
44544
+ let text5;
44545
+ try {
44546
+ text5 = readFileSync50(p, "utf8");
44547
+ } catch {
44548
+ continue;
44549
+ }
44550
+ const plan = parseDataAccess(text5, name);
44551
+ if (plan) return plan;
44552
+ }
44553
+ return null;
44554
+ }
44555
+ function entryApproves(entry, w) {
44556
+ const e = entry.trim();
44557
+ const lower = e.toLowerCase();
44558
+ const actionTail = w.action.split(".").pop() ?? w.action;
44559
+ if (e === w.action || e === actionTail) return true;
44560
+ if (w.via === "native") {
44561
+ const tail = w.capability.split(".").pop();
44562
+ if (e === w.capability || e === tail || e === nativeWriteOf(w.capability)?.canonical) return true;
44563
+ if (w.kind === "payment" && /^payments?$/.test(lower)) return true;
44564
+ if (w.kind === "member-signup" && /^(sign-?up|register|registration|member[- ]signup)$/.test(lower)) return true;
44565
+ return false;
44566
+ }
44567
+ const m = /^(?:(GET|POST|PUT|PATCH|DELETE)\s+)?(\/\S*|https?:\/\/\S+)$/i.exec(e);
44568
+ if (!m) return false;
44569
+ if (m[1] && m[1].toUpperCase() !== w.method) return false;
44570
+ return pathPattern(m[2]).test(w.path);
44571
+ }
44572
+ function pathPattern(p) {
44573
+ const body = p.replace(/\/+$/, "").split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join("[^/]+");
44574
+ return new RegExp(`^${body}/?$`);
44575
+ }
44576
+ function unapprovedWriteFindings(writes, plan) {
44577
+ if (!plan) return [];
44578
+ const byScreen = /* @__PURE__ */ new Map();
44579
+ for (const row of plan.rows) {
44580
+ const list = byScreen.get(row.screen) ?? [];
44581
+ list.push(row);
44582
+ byScreen.set(row.screen, list);
44583
+ }
44584
+ const findings = [];
44585
+ for (const w of writes) {
44586
+ const rows = byScreen.get(w.screen) ?? byScreen.get("*");
44587
+ const unreached = w.action === "unreached";
44588
+ const trigger = w.action === "boot" || unreached ? "boot" : { tap: w.component ?? null, key: "write", action: w.action };
44589
+ const does = unreached ? `can write to Wix (${writeLabel(w)} \u2014 its worker bundle calls it, on a path this crawl did not reach)` : `writes to Wix (${writeLabel(w)})`;
44590
+ const hint = `Before publishing, reconcile this with the owner-approved plan: remove the action (or trim the screen with \`himi template trim\`) if the owner did not approve it, or name the write in ${plan.from}'s \`## ${DATA_ACCESS_HEADING}\` table \u2014 ` + (unreached ? "" : `its action (\`${w.action.split(".").pop()}\`), `) + (w.via === "wix" ? `its endpoint (\`${w.method} ${w.path}\`)` : `its capability (\`${w.capability.split(".").pop()}\`)`) + ` \u2014 once they have.`;
44591
+ if (!rows) {
44592
+ findings.push({
44593
+ screen: w.screen,
44594
+ trigger,
44595
+ invariant: "write-not-approved",
44596
+ severity: "warning",
44597
+ message: `${does}, and ${plan.from}'s data-access table has no row for this screen`,
44598
+ hint
44599
+ });
44600
+ continue;
44601
+ }
44602
+ const allowed2 = rows.flatMap((r) => r.allowed ?? []);
44603
+ if (allowed2.some((entry) => entryApproves(entry, w))) continue;
44604
+ findings.push({
44605
+ screen: w.screen,
44606
+ trigger,
44607
+ invariant: "write-not-approved",
44608
+ severity: "warning",
44609
+ message: allowed2.length ? `${does}, which ${plan.from}'s data-access table does not name for this screen (approved: ${allowed2.join(", ")})` : `${does}, but ${plan.from}'s data-access table approves no writes on this screen`,
44610
+ hint
44611
+ });
44612
+ }
44613
+ return findings;
44614
+ }
44615
+ var NATIVE_WRITES, MEMBER_VERB, BUNDLE_TEXT, writeKey, writeLabel, DATA_ACCESS_HEADING, NONE, unquote;
44616
+ var init_wix_writes = __esm({
44617
+ "src/wix-writes.ts"() {
44618
+ NATIVE_WRITES = {
44619
+ "himi.wix.registerDirect": { kind: "member-signup", what: "creates a site member" },
44620
+ "himi.wix.resetPassword": { kind: "member-account", what: "sends a site member a password-reset email" },
44621
+ "capability.wix_cashier.proceedToPayment": { kind: "payment", what: "charges the customer through the Wix cashier" }
44622
+ };
44623
+ MEMBER_VERB = /^[a-z_]+\.wix\.(registerDirect|resetPassword)$/;
44624
+ BUNDLE_TEXT = {
44625
+ "himi.wix.registerDirect": /["'`][a-z_]+\.wix\.registerDirect["'`]|\}(?:\.wix)?\.registerDirect`/,
44626
+ "himi.wix.resetPassword": /["'`][a-z_]+\.wix\.resetPassword["'`]|\}(?:\.wix)?\.resetPassword`/,
44627
+ "capability.wix_cashier.proceedToPayment": /capability\.wix_cashier\.proceedToPayment["'`]/
44628
+ };
44629
+ writeKey = (w) => [w.screen, w.action, w.via, w.method ?? "", w.path ?? "", w.capability ?? ""].join("|");
44630
+ writeLabel = (w) => w.via === "wix" ? `${w.method} ${w.path}` : `${w.capability} (${w.what})`;
44631
+ DATA_ACCESS_HEADING = "Data access";
44632
+ NONE = /^(|-+|—|–|none|no|nothing|n\/a|na|read[- ]only|reads? only|✗|×)$/i;
44633
+ unquote = (cell) => cell.replace(/`/g, "").trim();
44634
+ }
44635
+ });
44636
+
44235
44637
  // src/test.ts
44236
44638
  var test_exports = {};
44237
44639
  __export(test_exports, {
@@ -44248,14 +44650,14 @@ __export(test_exports, {
44248
44650
  scopeDescriptors: () => scopeDescriptors,
44249
44651
  writeRecordedMocks: () => writeRecordedMocks
44250
44652
  });
44251
- import { existsSync as existsSync46, mkdirSync as mkdirSync23, readFileSync as readFileSync50, writeFileSync as writeFileSync19 } from "node:fs";
44252
- import { join as join43, resolve as resolve40 } from "node:path";
44653
+ import { existsSync as existsSync47, mkdirSync as mkdirSync23, readFileSync as readFileSync51, writeFileSync as writeFileSync19 } from "node:fs";
44654
+ import { join as join44, resolve as resolve40 } from "node:path";
44253
44655
  function readTestConfig(dir) {
44254
44656
  const p = resolve40(dir, TEST_CONFIG_FILE);
44255
- if (!existsSync46(p)) return {};
44657
+ if (!existsSync47(p)) return {};
44256
44658
  let parsed;
44257
44659
  try {
44258
- parsed = JSON.parse(readFileSync50(p, "utf8"));
44660
+ parsed = JSON.parse(readFileSync51(p, "utf8"));
44259
44661
  } catch (e) {
44260
44662
  throw new Error(`${TEST_CONFIG_FILE} is not valid JSON \u2014 ${e.message}`);
44261
44663
  }
@@ -44288,6 +44690,10 @@ function readTestConfig(dir) {
44288
44690
  if (step.component !== void 0 && typeof step.component !== "string") throw new Error(`${TEST_CONFIG_FILE}: journeys[${i}].steps[${stepIndex}].component must be a string`);
44289
44691
  if (step.params !== void 0 && (!step.params || typeof step.params !== "object" || Array.isArray(step.params))) throw new Error(`${TEST_CONFIG_FILE}: journeys[${i}].steps[${stepIndex}].params must be an object`);
44290
44692
  });
44693
+ const input = journey.input;
44694
+ if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input) || Object.values(input).some((v) => typeof v !== "string"))) {
44695
+ throw new Error(`${TEST_CONFIG_FILE}: journeys[${i}].input must be an object of string values \u2014 the screen input a nav edge would send, e.g. { "id": "svc-0001" }`);
44696
+ }
44291
44697
  if (!Array.isArray(journey.expectText) || !journey.expectText.length || journey.expectText.some((text5) => typeof text5 !== "string" || !text5.trim())) {
44292
44698
  throw new Error(`${TEST_CONFIG_FILE}: journeys[${i}] needs non-empty expectText values that prove a visible result`);
44293
44699
  }
@@ -44298,7 +44704,7 @@ function readTestConfig(dir) {
44298
44704
  function readNetRules(path2) {
44299
44705
  let parsed;
44300
44706
  try {
44301
- parsed = JSON.parse(readFileSync50(path2, "utf8"));
44707
+ parsed = JSON.parse(readFileSync51(path2, "utf8"));
44302
44708
  } catch (e) {
44303
44709
  throw new Error(`--net ${path2} could not be read as JSON \u2014 ${e.message}`);
44304
44710
  }
@@ -44319,7 +44725,7 @@ function readNetRules(path2) {
44319
44725
  function defaultNetRules(dir) {
44320
44726
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
44321
44727
  const p = resolve40(dir, rel);
44322
- if (existsSync46(p)) {
44728
+ if (existsSync47(p)) {
44323
44729
  const rules = readNetRules(p);
44324
44730
  if (rules.length) return { rules, from: rel };
44325
44731
  }
@@ -44335,7 +44741,7 @@ function readCannedResponses() {
44335
44741
  ];
44336
44742
  for (const url of candidates) {
44337
44743
  try {
44338
- const parsed = JSON.parse(readFileSync50(url, "utf8"));
44744
+ const parsed = JSON.parse(readFileSync51(url, "utf8"));
44339
44745
  const byKey = {};
44340
44746
  for (const [k, v] of Object.entries(parsed)) {
44341
44747
  if (!k.startsWith("//")) byKey[k] = v;
@@ -44357,9 +44763,7 @@ function makeRecordingWixCall(send2, token, app) {
44357
44763
  refused.push(`${verb} ${path2}`);
44358
44764
  throw new Error(`--record refused ${verb} ${path2}: the path carries a fragment or control character \u2014 not a provable read`);
44359
44765
  }
44360
- const bare = path2.split("?")[0].replace(/\/+$/, "");
44361
- const readShaped = verb === "GET" || verb === "POST" && /\/(query|search|list[^/]*|get[^/]*|count|aggregate|facets|connections)$/i.test(bare);
44362
- if (!readShaped) {
44766
+ if (!isProvenRead(verb, path2)) {
44363
44767
  refused.push(`${verb} ${path2}`);
44364
44768
  throw new Error(`--record refused ${verb} ${path2}: recording sends only proven reads (GET, or POST ending in /query, /search, /list*, /get*, /count, /aggregate, /facets, /connections) \u2014 if this IS a read, add its rule to dev/net-mocks.json by hand`);
44365
44769
  }
@@ -44427,8 +44831,8 @@ function writeRecordedMocks(opts) {
44427
44831
  mkdir: (p) => mkdirSync23(p, { recursive: true }),
44428
44832
  write: (p, s) => writeFileSync19(p, s)
44429
44833
  };
44430
- fs.mkdir(join43(opts.dir, "dev"));
44431
- fs.write(join43(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
44834
+ fs.mkdir(join44(opts.dir, "dev"));
44835
+ fs.write(join44(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
44432
44836
  return { ok: true, rules, to: "dev/net-mocks.json" };
44433
44837
  }
44434
44838
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -44496,6 +44900,7 @@ async function runCrawl(opts) {
44496
44900
  const suppressed = [];
44497
44901
  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));
44498
44902
  const results = [];
44903
+ const writes = [];
44499
44904
  const uncoveredByScreen = /* @__PURE__ */ new Map();
44500
44905
  const coverageFns = /* @__PURE__ */ new Map();
44501
44906
  const bundleOf = new Map(descriptors.map((d) => [d.screenId, d]));
@@ -44533,18 +44938,21 @@ async function runCrawl(opts) {
44533
44938
  const cov = baked.bundleJs && deps.startWorkerCoverage ? await deps.startWorkerCoverage() : null;
44534
44939
  const r = await deps.crawlScreen(args);
44535
44940
  results.push(r);
44941
+ writes.push(...writesFromEffects(baked.screenId, r.effects));
44536
44942
  for (const journey of opts.config?.journeys?.filter((candidate) => candidate.screen === baked.screenId) ?? []) {
44537
44943
  if (!deps.crawlScreenJourney) {
44538
44944
  journeyFindings.push({ screen: baked.screenId, trigger: "boot", invariant: "journey-failed", severity: "error", message: `journey "${journey.name}" cannot run because this CLI's crawl engine is too old`, hint: "Upgrade @wix/himalaya-cli so typed journeys run instead of being silently skipped." });
44539
44945
  journeyResults.push({ name: journey.name, screen: baked.screenId, stepsExercised: 0, ok: false });
44540
44946
  continue;
44541
44947
  }
44542
- const jr = await deps.crawlScreenJourney({ ...args, journey });
44948
+ const jr = await deps.crawlScreenJourney({ ...args, ...journey.input ? { inputOverride: journey.input } : {}, journey });
44949
+ writes.push(...writesFromEffects(baked.screenId, jr.effects));
44543
44950
  journeyFindings.push(...jr.findings);
44544
44951
  journeyResults.push({ name: journey.name, screen: baked.screenId, stepsExercised: jr.stepsExercised, ok: jr.findings.length === 0 });
44545
44952
  }
44546
44953
  if (opts.deep && opts.deep >= 2 && deps.crawlScreenDeep && baked.bundleJs) {
44547
44954
  const dr = await deps.crawlScreenDeep(args, { depth: opts.deep });
44955
+ writes.push(...writesFromEffects(baked.screenId, dr.effects));
44548
44956
  deepTotals.nodesVisited += dr.nodesVisited;
44549
44957
  deepTotals.dispatches += dr.dispatches;
44550
44958
  deepTotals.capped = deepTotals.capped || dr.capped;
@@ -44584,6 +44992,7 @@ async function runCrawl(opts) {
44584
44992
  }
44585
44993
  if (deps.crawlScreenCapabilitySuccess && baked.bundleJs) {
44586
44994
  const cr = await deps.crawlScreenCapabilitySuccess(args);
44995
+ writes.push(...writesFromEffects(baked.screenId, cr.effects));
44587
44996
  capabilityModelled += cr.modelled;
44588
44997
  const keyOfC = deps.findingKey ?? ((f) => `${f.invariant}|${f.at ?? ""}|${f.screen}|${f.message}`);
44589
44998
  const seen = new Set([...r.findings, ...relationFindings].map(keyOfC));
@@ -44660,6 +45069,7 @@ async function runCrawl(opts) {
44660
45069
  list.push(...fns);
44661
45070
  coverageFns.set(target, list);
44662
45071
  }
45072
+ writes.push(...writesFromEffects(target, er.effects));
44663
45073
  edgesFollowed.push({ target, from: shape.from, params: shape.params, via: shape.via });
44664
45074
  if (!er.findings.some((f) => f.invariant === "no-request-issued") && !opts.tabRoots?.has(target) && !fetchProven.has(target)) {
44665
45075
  fetchProven.set(target, { from: shape.from, params: shape.params });
@@ -44684,7 +45094,11 @@ async function runCrawl(opts) {
44684
45094
  if (uncovered.length) uncoveredByScreen.set(screen, uncovered);
44685
45095
  }
44686
45096
  }
44687
- const all = [...results.flatMap((r) => r.findings), ...hostileFindings, ...deepFindings, ...journeyFindings, ...edgeFindings, ...relationFindings].filter((f) => {
45097
+ const inventory = dedupeWrites(writes);
45098
+ const unreachedWrites = targets.flatMap((d) => unreachedNativeWrites(d.screenId, d.bundleJs, inventory));
45099
+ const plan = readDataAccess(opts.dir);
45100
+ const writeFindings = unapprovedWriteFindings([...inventory, ...unreachedWrites], plan);
45101
+ const all = [...results.flatMap((r) => r.findings), ...hostileFindings, ...deepFindings, ...journeyFindings, ...edgeFindings, ...relationFindings, ...writeFindings].filter((f) => {
44688
45102
  if (f.invariant === "no-request-issued" && !f.axis && fetchProven.has(f.screen)) {
44689
45103
  const by = fetchProven.get(f.screen);
44690
45104
  if (!retired.some((x) => x.screen === f.screen)) retired.push({ screen: f.screen, from: by.from, params: by.params });
@@ -44738,6 +45152,9 @@ async function runCrawl(opts) {
44738
45152
  ...deps.crawlScreenRelations ? { relations: { checked: relationsChecked, byKind: relationsByKind } } : {},
44739
45153
  ...deps.crawlScreenCapabilitySuccess ? { capabilities: { modelled: capabilityModelled } } : {},
44740
45154
  ...journeyResults.length ? { journeys: journeyResults } : {},
45155
+ writes: inventory,
45156
+ ...unreachedWrites.length ? { unreachedWrites } : {},
45157
+ ...plan ? { writesPlan: { from: plan.from, rows: plan.rows.length } } : {},
44741
45158
  ...results.some((r) => r.observations) ? { observations: Object.fromEntries(results.filter((r) => r.observations).map((r) => [r.screen, r.observations])) } : {}
44742
45159
  };
44743
45160
  }
@@ -44823,6 +45240,17 @@ function formatReport(r) {
44823
45240
  }
44824
45241
  out.push("");
44825
45242
  }
45243
+ if (r.writes?.length || r.unreachedWrites?.length) {
45244
+ out.push(` Writes to Wix (${r.writes.length}) \u2014 every write the crawl saw a worker attempt (none reached Wix). A write behind a form or a selection shows only under --deep or a journey:`);
45245
+ const byScreenW = /* @__PURE__ */ new Map();
45246
+ for (const w of r.writes) byScreenW.set(w.screen, [...byScreenW.get(w.screen) ?? [], w]);
45247
+ for (const [screen, list] of [...byScreenW].sort(([a], [b]) => a.localeCompare(b))) {
45248
+ for (const w of list) out.push(` ${screen} ${w.action === "boot" ? "on appear" : w.action} ${writeLabel(w)}`);
45249
+ }
45250
+ for (const w of r.unreachedWrites ?? []) out.push(` ${w.screen} (not reached \u2014 in its worker bundle) ${writeLabel(w)}`);
45251
+ out.push(r.writesPlan ? ` checked against ${r.writesPlan.from}'s \`## ${DATA_ACCESS_HEADING}\` table (${r.writesPlan.rows} row${r.writesPlan.rows === 1 ? "" : "s"}) \u2014 unapproved writes are write-not-approved warnings above` : ` no \`## ${DATA_ACCESS_HEADING}\` table (Screen + Wix writes columns) in SPEC.md or MOBILE-UX.md, so nothing checked these against the approved plan \u2014 \`himi docs himi-test\` \u2192 "Writes to Wix"`);
45252
+ out.push("");
45253
+ }
44826
45254
  if (r.shots?.written && r.shots.dir) {
44827
45255
  out.push(` ${r.shots.written} screenshot${r.shots.written === 1 ? "" : "s"} \u2192 ${r.shots.dir}/`);
44828
45256
  }
@@ -44875,6 +45303,7 @@ var init_test = __esm({
44875
45303
  "src/test.ts"() {
44876
45304
  init_wix_gateway_rules();
44877
45305
  init_crawl_layers();
45306
+ init_wix_writes();
44878
45307
  TEST_CONFIG_FILE = "himi-test.json";
44879
45308
  triggerLabel = (f) => (f.trigger === "boot" ? "on appear" : `${f.trigger.key} ${f.trigger.tap ?? "(no id)"} \u2192 ${f.trigger.action}`) + (f.axis ? ` [${f.axis}]` : "");
44880
45309
  }
@@ -44885,14 +45314,14 @@ var mobile_ux_lint_exports = {};
44885
45314
  __export(mobile_ux_lint_exports, {
44886
45315
  mobileUxIssues: () => mobileUxIssues
44887
45316
  });
44888
- import { existsSync as existsSync47, readFileSync as readFileSync51 } from "node:fs";
44889
- import { join as join44 } from "node:path";
45317
+ import { existsSync as existsSync48, readFileSync as readFileSync52 } from "node:fs";
45318
+ import { join as join45 } from "node:path";
44890
45319
  function mobileUxIssues(dir, strict) {
44891
- const file = join44(dir, "MOBILE-UX.md");
44892
- if (!existsSync47(file)) return [];
45320
+ const file = join45(dir, "MOBILE-UX.md");
45321
+ if (!existsSync48(file)) return [];
44893
45322
  let text5;
44894
45323
  try {
44895
- text5 = readFileSync51(file, "utf8");
45324
+ text5 = readFileSync52(file, "utf8");
44896
45325
  } catch {
44897
45326
  return [];
44898
45327
  }
@@ -46567,8 +46996,8 @@ __export(functions_exports, {
46567
46996
  resolveFunctionsTarget: () => resolveFunctionsTarget,
46568
46997
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
46569
46998
  });
46570
- import { readFileSync as readFileSync52, existsSync as existsSync48 } from "node:fs";
46571
- import { join as join45, resolve as resolve41 } from "node:path";
46999
+ import { readFileSync as readFileSync53, existsSync as existsSync49 } from "node:fs";
47000
+ import { join as join46, resolve as resolve41 } from "node:path";
46572
47001
  function resolveFunctionsTarget(input) {
46573
47002
  const explicitUrl = input.functionsUrl?.trim();
46574
47003
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -46587,9 +47016,9 @@ async function loadFunctionsApp(dir) {
46587
47016
  }
46588
47017
  const sources = {};
46589
47018
  for (const definition of config.functions.functions) {
46590
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join45(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync48);
47019
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join46(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync49);
46591
47020
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
46592
- sources[definition.name] = readFileSync52(source, "utf8");
47021
+ sources[definition.name] = readFileSync53(source, "utf8");
46593
47022
  }
46594
47023
  const data = config.cms || config.data ? mergeDataResources(config) : void 0;
46595
47024
  const dataErrors = validateDataResources(data, loaded.name).filter((issue2) => issue2.severity === "error");
@@ -46741,10 +47170,10 @@ __export(studio_reel_exports, {
46741
47170
  utcDay: () => utcDay
46742
47171
  });
46743
47172
  import { createHash as createHash24 } from "node:crypto";
46744
- import { lstatSync as lstatSync7, readFileSync as readFileSync53 } from "node:fs";
46745
- import { basename as basename11, join as join46 } from "node:path";
47173
+ import { lstatSync as lstatSync7, readFileSync as readFileSync54 } from "node:fs";
47174
+ import { basename as basename11, join as join47 } from "node:path";
46746
47175
  function loadMemory2(dir) {
46747
- const m = readJson4(join46(dir, ".himi", "studio", "memory.json"));
47176
+ const m = readJson4(join47(dir, ".himi", "studio", "memory.json"));
46748
47177
  if (!isObj2(m) || m.version !== 1 || !isObj2(m.shots) || !Array.isArray(m.timeline)) return null;
46749
47178
  const shots = [];
46750
47179
  for (const [id, s] of Object.entries(m.shots)) {
@@ -46781,8 +47210,8 @@ function planReel(dir) {
46781
47210
  const memory = loadMemory2(dir);
46782
47211
  const { entries } = readProgress(dir);
46783
47212
  const summary = summarizeProgress(entries);
46784
- const manifest = readJson4(join46(dir, "himalaya.content.json"));
46785
- const tokens = readJson4(join46(dir, "tokens.json"));
47213
+ const manifest = readJson4(join47(dir, "himalaya.content.json"));
47214
+ const tokens = readJson4(join47(dir, "tokens.json"));
46786
47215
  const accentToken = tokens?.colors?.accentPrimary;
46787
47216
  const accentRaw = typeof accentToken === "string" ? accentToken : accentToken?.light;
46788
47217
  const accent = typeof accentRaw === "string" && ACCENT.test(accentRaw) ? accentRaw : DEFAULT_ACCENT;
@@ -46824,10 +47253,10 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
46824
47253
  const verified = (f) => {
46825
47254
  if (!HISTORY_NAME.test(f)) return null;
46826
47255
  try {
46827
- const path2 = join46(dir, HISTORY_DIR, f);
47256
+ const path2 = join47(dir, HISTORY_DIR, f);
46828
47257
  const st = lstatSync7(path2);
46829
47258
  if (!st.isFile() || st.nlink !== 1) return null;
46830
- const bytes2 = readFileSync53(path2);
47259
+ const bytes2 = readFileSync54(path2);
46831
47260
  return createHash24("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
46832
47261
  } catch {
46833
47262
  return null;
@@ -46961,7 +47390,7 @@ var init_studio_reel = __esm({
46961
47390
  mimeOf = (file) => MIME2[file.slice(65)] ?? "image/png";
46962
47391
  readJson4 = (file) => {
46963
47392
  try {
46964
- return JSON.parse(readFileSync53(file, "utf8"));
47393
+ return JSON.parse(readFileSync54(file, "utf8"));
46965
47394
  } catch {
46966
47395
  return null;
46967
47396
  }
@@ -46992,9 +47421,9 @@ __export(native_decode_exports, {
46992
47421
  runNativeDecode: () => runNativeDecode
46993
47422
  });
46994
47423
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
46995
- import { existsSync as existsSync49, mkdtempSync as mkdtempSync2, readFileSync as readFileSync54, rmSync as rmSync11, writeFileSync as writeFileSync20 } from "node:fs";
47424
+ import { existsSync as existsSync50, mkdtempSync as mkdtempSync2, readFileSync as readFileSync55, rmSync as rmSync11, writeFileSync as writeFileSync20 } from "node:fs";
46996
47425
  import { tmpdir as tmpdir3 } from "node:os";
46997
- import { join as join47 } from "node:path";
47426
+ import { join as join48 } from "node:path";
46998
47427
  import { fileURLToPath as fileURLToPath20 } from "node:url";
46999
47428
  function nativeDecodePackageDir() {
47000
47429
  return fileURLToPath20(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -47004,8 +47433,8 @@ function nativeDecodeAndroidHarnessDir() {
47004
47433
  }
47005
47434
  function nativeOracleShipped() {
47006
47435
  return {
47007
- ios: existsSync49(join47(nativeDecodePackageDir(), "Package.swift")),
47008
- android: existsSync49(join47(nativeDecodeAndroidHarnessDir(), "gradlew"))
47436
+ ios: existsSync50(join48(nativeDecodePackageDir(), "Package.swift")),
47437
+ android: existsSync50(join48(nativeDecodeAndroidHarnessDir(), "gradlew"))
47009
47438
  };
47010
47439
  }
47011
47440
  function detectNativeDecode(options = {}) {
@@ -47016,7 +47445,7 @@ function detectNativeDecode(options = {}) {
47016
47445
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
47017
47446
  }
47018
47447
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
47019
- const packageAvailable = options.packageAvailable ?? existsSync49(join47(packageDir, "Package.swift"));
47448
+ const packageAvailable = options.packageAvailable ?? existsSync50(join48(packageDir, "Package.swift"));
47020
47449
  if (!packageAvailable) {
47021
47450
  return {
47022
47451
  platform: "ios",
@@ -47028,7 +47457,7 @@ function detectNativeDecode(options = {}) {
47028
47457
  }
47029
47458
  function localPropertiesSdk(harnessDir) {
47030
47459
  try {
47031
- const match = readFileSync54(join47(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
47460
+ const match = readFileSync55(join48(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
47032
47461
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
47033
47462
  } catch {
47034
47463
  return null;
@@ -47036,8 +47465,8 @@ function localPropertiesSdk(harnessDir) {
47036
47465
  }
47037
47466
  function detectAndroidNativeDecode(options = {}) {
47038
47467
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
47039
- const gradlew = join47(harnessDir, "gradlew");
47040
- const gradlewAvailable = options.gradlewAvailable ?? existsSync49(gradlew);
47468
+ const gradlew = join48(harnessDir, "gradlew");
47469
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync50(gradlew);
47041
47470
  if (!gradlewAvailable) {
47042
47471
  return {
47043
47472
  platform: "android",
@@ -47047,7 +47476,7 @@ function detectAndroidNativeDecode(options = {}) {
47047
47476
  }
47048
47477
  const androidHome = options.androidHome ?? process.env.ANDROID_HOME ?? "";
47049
47478
  const sdk = androidHome.trim() || (options.localPropertiesSdk !== void 0 ? options.localPropertiesSdk : localPropertiesSdk(harnessDir));
47050
- const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync49(sdk));
47479
+ const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync50(sdk));
47051
47480
  if (!sdkAvailable) {
47052
47481
  return {
47053
47482
  platform: "android",
@@ -47119,14 +47548,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
47119
47548
  return verdicts;
47120
47549
  }
47121
47550
  function runNativeDecode(options) {
47122
- const descriptorDir = mkdtempSync2(join47(tmpdir3(), "himi-native-decode-"));
47551
+ const descriptorDir = mkdtempSync2(join48(tmpdir3(), "himi-native-decode-"));
47123
47552
  const startedAt = Date.now();
47124
47553
  try {
47125
47554
  for (const descriptor of options.descriptors) {
47126
47555
  if (typeof descriptor.descriptorJson !== "string") {
47127
47556
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
47128
47557
  }
47129
- writeFileSync20(join47(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
47558
+ writeFileSync20(join48(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
47130
47559
  }
47131
47560
  options.progress?.("building cached Swift oracle");
47132
47561
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -47138,7 +47567,7 @@ function runNativeDecode(options) {
47138
47567
  encoding: "utf8"
47139
47568
  }).trim();
47140
47569
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
47141
- const stdout = execFileSync5(join47(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
47570
+ const stdout = execFileSync5(join48(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
47142
47571
  stdio: ["ignore", "pipe", "pipe"],
47143
47572
  encoding: "utf8"
47144
47573
  });
@@ -47156,15 +47585,15 @@ function runNativeDecode(options) {
47156
47585
  }
47157
47586
  }
47158
47587
  function runAndroidNativeDecode(options) {
47159
- const descriptorDir = mkdtempSync2(join47(tmpdir3(), "himi-native-decode-android-"));
47160
- const outputPath = join47(descriptorDir, "verdicts.ndjson");
47588
+ const descriptorDir = mkdtempSync2(join48(tmpdir3(), "himi-native-decode-android-"));
47589
+ const outputPath = join48(descriptorDir, "verdicts.ndjson");
47161
47590
  const startedAt = Date.now();
47162
47591
  try {
47163
47592
  for (const descriptor of options.descriptors) {
47164
47593
  if (typeof descriptor.descriptorJson !== "string") {
47165
47594
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
47166
47595
  }
47167
- writeFileSync20(join47(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
47596
+ writeFileSync20(join48(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
47168
47597
  }
47169
47598
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
47170
47599
  execFileSync5(options.gradlew, [
@@ -47179,7 +47608,7 @@ function runAndroidNativeDecode(options) {
47179
47608
  stdio: ["ignore", "ignore", "pipe"],
47180
47609
  encoding: "utf8"
47181
47610
  });
47182
- const stdout = readFileSync54(outputPath, "utf8");
47611
+ const stdout = readFileSync55(outputPath, "utf8");
47183
47612
  return {
47184
47613
  platform: "android",
47185
47614
  verdicts: parseNativeDecodeOutput(stdout, options.descriptors.map((descriptor) => descriptor.screenId), "android"),
@@ -47249,13 +47678,13 @@ __export(browser_authoring_exports, {
47249
47678
  });
47250
47679
  import { createServer as createServer3 } from "node:http";
47251
47680
  import { createHash as createHash25, randomBytes as randomBytes4 } from "node:crypto";
47252
- import { existsSync as existsSync50, readdirSync as readdirSync24, readFileSync as readFileSync55, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
47253
- import { join as join48, posix, relative as relative8, sep as sep12 } from "node:path";
47681
+ import { existsSync as existsSync51, readdirSync as readdirSync24, readFileSync as readFileSync56, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
47682
+ import { join as join49, posix, relative as relative8, sep as sep12 } from "node:path";
47254
47683
  function humanMs(ms) {
47255
47684
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
47256
47685
  }
47257
47686
  function collectWorkerSources(tier5SrcDir, bundleName) {
47258
- const root = join48(tier5SrcDir, bundleName);
47687
+ const root = join49(tier5SrcDir, bundleName);
47259
47688
  const files = {};
47260
47689
  const visitedDirs = /* @__PURE__ */ new Set();
47261
47690
  const walk2 = (abs, rel) => {
@@ -47263,16 +47692,16 @@ function collectWorkerSources(tier5SrcDir, bundleName) {
47263
47692
  if (visitedDirs.has(real)) return;
47264
47693
  visitedDirs.add(real);
47265
47694
  for (const name of readdirSync24(abs).sort()) {
47266
- const childAbs = join48(abs, name);
47695
+ const childAbs = join49(abs, name);
47267
47696
  const childRel = posix.join(rel, name);
47268
47697
  if (statSync12(childAbs).isDirectory()) {
47269
47698
  walk2(childAbs, childRel);
47270
47699
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
47271
- files[childRel] = readFileSync55(childAbs, "utf8");
47700
+ files[childRel] = readFileSync56(childAbs, "utf8");
47272
47701
  }
47273
47702
  }
47274
47703
  };
47275
- if (!existsSync50(root)) return null;
47704
+ if (!existsSync51(root)) return null;
47276
47705
  walk2(root, `/tier5-src/${bundleName}`);
47277
47706
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p) => p in files);
47278
47707
  if (!entry) return null;
@@ -47289,20 +47718,20 @@ function collectRelativeSiblings(files, tier5SrcDir) {
47289
47718
  const spec = m[1];
47290
47719
  const virtual = posix.normalize(posix.join(dir, spec));
47291
47720
  if (!virtual.startsWith("/tier5-src/")) continue;
47292
- const onDisk = join48(tier5SrcDir, virtual.slice("/tier5-src/".length));
47721
+ const onDisk = join49(tier5SrcDir, virtual.slice("/tier5-src/".length));
47293
47722
  const candidates = [
47294
47723
  onDisk,
47295
47724
  `${onDisk}.ts`,
47296
47725
  `${onDisk}.js`,
47297
47726
  onDisk.replace(/\.js$/, ".ts"),
47298
- join48(onDisk, "index.ts"),
47299
- join48(onDisk, "index.js")
47727
+ join49(onDisk, "index.ts"),
47728
+ join49(onDisk, "index.js")
47300
47729
  ];
47301
47730
  for (const abs of candidates) {
47302
- if (!existsSync50(abs) || statSync12(abs).isDirectory()) continue;
47731
+ if (!existsSync51(abs) || statSync12(abs).isDirectory()) continue;
47303
47732
  const key2 = `/tier5-src/${relative8(tier5SrcDir, abs).split(sep12).join(posix.sep)}`;
47304
47733
  if (key2 in files) break;
47305
- files[key2] = readFileSync55(abs, "utf8");
47734
+ files[key2] = readFileSync56(abs, "utf8");
47306
47735
  queue.push(key2);
47307
47736
  break;
47308
47737
  }
@@ -47337,7 +47766,7 @@ function collectSdkModules(files, resolveSubpath) {
47337
47766
  function sdkSubpathReader(resolve48) {
47338
47767
  return (sub) => {
47339
47768
  try {
47340
- return readFileSync55(resolve48(`@wix/himalaya/${sub}`), "utf8");
47769
+ return readFileSync56(resolve48(`@wix/himalaya/${sub}`), "utf8");
47341
47770
  } catch {
47342
47771
  return null;
47343
47772
  }
@@ -47509,8 +47938,8 @@ __export(coverage_exports, {
47509
47938
  suppressedNativeKeys: () => suppressedNativeKeys,
47510
47939
  trendAndStore: () => trendAndStore
47511
47940
  });
47512
- import { existsSync as existsSync51, mkdirSync as mkdirSync24, readFileSync as readFileSync56, writeFileSync as writeFileSync21 } from "node:fs";
47513
- import { dirname as dirname26, join as join49 } from "node:path";
47941
+ import { existsSync as existsSync52, mkdirSync as mkdirSync24, readFileSync as readFileSync57, writeFileSync as writeFileSync21 } from "node:fs";
47942
+ import { dirname as dirname26, join as join50 } from "node:path";
47514
47943
  function suppressedNativeKeys(oracle) {
47515
47944
  return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
47516
47945
  }
@@ -47690,11 +48119,11 @@ function assembleLedger(r, options = {}) {
47690
48119
  return out;
47691
48120
  }
47692
48121
  function trendAndStore(dir, entries, suppressed) {
47693
- const p = join49(dir, LEDGER_FILE);
48122
+ const p = join50(dir, LEDGER_FILE);
47694
48123
  let previous = null;
47695
48124
  try {
47696
- if (existsSync51(p)) {
47697
- const parsed = JSON.parse(readFileSync56(p, "utf8"));
48125
+ if (existsSync52(p)) {
48126
+ const parsed = JSON.parse(readFileSync57(p, "utf8"));
47698
48127
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k) => typeof k === "string");
47699
48128
  }
47700
48129
  } catch {
@@ -47733,11 +48162,11 @@ function formatLedger(entries, trend) {
47733
48162
  return out.join("\n");
47734
48163
  }
47735
48164
  function diffAndStoreObservations(dir, current) {
47736
- const p = join49(dir, OBS_FILE);
48165
+ const p = join50(dir, OBS_FILE);
47737
48166
  let previous = null;
47738
48167
  try {
47739
- if (existsSync51(p)) {
47740
- const parsed = JSON.parse(readFileSync56(p, "utf8"));
48168
+ if (existsSync52(p)) {
48169
+ const parsed = JSON.parse(readFileSync57(p, "utf8"));
47741
48170
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
47742
48171
  (v) => Array.isArray(v) && v.every((l) => typeof l === "string")
47743
48172
  )) {
@@ -47786,8 +48215,8 @@ var LEDGER_FILE, OBS_FILE;
47786
48215
  var init_coverage = __esm({
47787
48216
  "src/coverage.ts"() {
47788
48217
  init_native_decode();
47789
- LEDGER_FILE = join49(".himi", "test-ledger.json");
47790
- OBS_FILE = join49(".himi", "test-observations.json");
48218
+ LEDGER_FILE = join50(".himi", "test-ledger.json");
48219
+ OBS_FILE = join50(".himi", "test-observations.json");
47791
48220
  }
47792
48221
  });
47793
48222
 
@@ -47798,11 +48227,11 @@ __export(design_kits_exports, {
47798
48227
  kitsDir: () => kitsDir,
47799
48228
  listKits: () => listKits2
47800
48229
  });
47801
- import { existsSync as existsSync52, readFileSync as readFileSync57, readdirSync as readdirSync25 } from "node:fs";
47802
- import { join as join50, basename as basename12 } from "node:path";
48230
+ import { existsSync as existsSync53, readFileSync as readFileSync58, readdirSync as readdirSync25 } from "node:fs";
48231
+ import { join as join51, basename as basename12 } from "node:path";
47803
48232
  import { fileURLToPath as fileURLToPath21 } from "node:url";
47804
48233
  function kitsDir() {
47805
- return KIT_DIR_CANDIDATES.find((p) => existsSync52(p));
48234
+ return KIT_DIR_CANDIDATES.find((p) => existsSync53(p));
47806
48235
  }
47807
48236
  function listKits2() {
47808
48237
  const dir = kitsDir();
@@ -47826,7 +48255,7 @@ In a published @wix/himalaya-cli this is a packaging bug \u2014 the kits should
47826
48255
  if (!kits.includes(name)) {
47827
48256
  return { ok: false, kit: name, kits, problems: [`no design kit named "${name}". Available: ${kits.join(", ")}`] };
47828
48257
  }
47829
- const raw = readFileSync57(join50(dir, `${name}.json`), "utf8");
48258
+ const raw = readFileSync58(join51(dir, `${name}.json`), "utf8");
47830
48259
  let parsed;
47831
48260
  try {
47832
48261
  parsed = JSON.parse(raw);
@@ -47850,9 +48279,9 @@ var init_design_kits = __esm({
47850
48279
  "src/design-kits.ts"() {
47851
48280
  KIT_DIR_CANDIDATES = [
47852
48281
  // in-repo: tools/himi-cli/src/design-kits.ts → <repo>/stdlib/design-kits/
47853
- join50(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
48282
+ join51(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
47854
48283
  // published CLI: <pkg>/dist/cli.mjs → <pkg>/stdlib/design-kits/
47855
- join50(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
48284
+ join51(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
47856
48285
  ];
47857
48286
  }
47858
48287
  });
@@ -47866,11 +48295,11 @@ __export(assets_exports, {
47866
48295
  removeAssetPayload: () => removeAssetPayload,
47867
48296
  verifyReport: () => verifyReport
47868
48297
  });
47869
- import { readFileSync as readFileSync58 } from "node:fs";
48298
+ import { readFileSync as readFileSync59 } from "node:fs";
47870
48299
  import { basename as basename13 } from "node:path";
47871
48300
  function declaredAssets(dir) {
47872
48301
  return collectAssets(dir).map((a) => {
47873
- const bytes2 = readFileSync58(a.path);
48302
+ const bytes2 = readFileSync59(a.path);
47874
48303
  return {
47875
48304
  name: a.name,
47876
48305
  sha256: assetSha256(bytes2),
@@ -47947,7 +48376,7 @@ function verifyReport(dir, app, manifest) {
47947
48376
  };
47948
48377
  }
47949
48378
  function addAssetPayload(manifest, filePath, opts) {
47950
- const bytes2 = readFileSync58(filePath);
48379
+ const bytes2 = readFileSync59(filePath);
47951
48380
  const name = opts.name ?? basename13(filePath);
47952
48381
  const sha2565 = assetSha256(bytes2);
47953
48382
  const record2 = {
@@ -49587,21 +50016,21 @@ var init_preview_assets_generated = __esm({
49587
50016
  "../../packages/serve/src/preview-assets.generated.ts"() {
49588
50017
  init_preview_assets_format();
49589
50018
  PREVIEW_ASSET_MANIFEST = [
49590
- { key: "assets/index-3uQs-pBH.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1050432, sha256: "928dfeb0c4f367f39a34c92f32a7194f483918a869912cf33ad05bc8d6f8a483" },
49591
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1050440, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
49592
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1055050, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
49593
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1543634, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
49594
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1634558, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
49595
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1725542, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
49596
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1816522, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
49597
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1907394, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
49598
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1998374, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
49599
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2107010, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
49600
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2216094, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
49601
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2325142, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
49602
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2432842, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
49603
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2520950, length: 4947, sha256: "d5b6c88c0e67711667b4df9d04e74e4a77af7131bd8082754447cbfd69a173a8" },
49604
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2525897, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
50019
+ { key: "assets/index-BJFiKWYO.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1051133, sha256: "99b754e0231cb82ad2cafbca64ba3cd3bdc74aa53d8351f6a57828034c3a3f44" },
50020
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1051141, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
50021
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1055751, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
50022
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1544335, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
50023
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1635259, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
50024
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1726243, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
50025
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1817223, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
50026
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1908095, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
50027
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1999075, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
50028
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2107711, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
50029
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2216795, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
50030
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2325843, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
50031
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2433543, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
50032
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2521651, length: 4947, sha256: "1c4dfb3d465449d6a73677293df29d93b1c236b46602b553a58ef6e89c52de11" },
50033
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2526598, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
49605
50034
  ];
49606
50035
  }
49607
50036
  });
@@ -49617,9 +50046,9 @@ __export(run_exports, {
49617
50046
  runPackage: () => runPackage,
49618
50047
  withoutProdServe: () => withoutProdServe
49619
50048
  });
49620
- import { cpSync as cpSync2, existsSync as existsSync53, mkdirSync as mkdirSync25, mkdtempSync as mkdtempSync3, readFileSync as readFileSync59, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
50049
+ import { cpSync as cpSync2, existsSync as existsSync54, mkdirSync as mkdirSync25, mkdtempSync as mkdtempSync3, readFileSync as readFileSync60, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
49621
50050
  import { tmpdir as tmpdir4 } from "node:os";
49622
- import { dirname as dirname27, join as join51 } from "node:path";
50051
+ import { dirname as dirname27, join as join52 } from "node:path";
49623
50052
  import { fileURLToPath as fileURLToPath22 } from "node:url";
49624
50053
  function makePackageRunHandler(opts) {
49625
50054
  const store = new FileReleaseStore(opts.releaseRoot);
@@ -49647,9 +50076,9 @@ async function fetchBytes(url) {
49647
50076
  }
49648
50077
  async function resolvePackage(source, workDir) {
49649
50078
  const isUrl2 = /^https?:\/\//i.test(source);
49650
- if (!isUrl2 && existsSync53(source) && statSync13(source).isDirectory()) {
49651
- const appDirManifest = join51(source, "release-manifest.json");
49652
- if (existsSync53(appDirManifest)) {
50079
+ if (!isUrl2 && existsSync54(source) && statSync13(source).isDirectory()) {
50080
+ const appDirManifest = join52(source, "release-manifest.json");
50081
+ if (existsSync54(appDirManifest)) {
49653
50082
  const opened2 = openPackageDir(source);
49654
50083
  if (!opened2.validation.ok) {
49655
50084
  throw new Error(`package is not valid:
@@ -49658,9 +50087,9 @@ async function resolvePackage(source, workDir) {
49658
50087
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
49659
50088
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
49660
50089
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
49661
- const root2 = workDir ?? mkdtempSync3(join51(tmpdir4(), "himi-run-"));
49662
- const target = join51(root2, app2);
49663
- if (join51(source) !== target) {
50090
+ const root2 = workDir ?? mkdtempSync3(join52(tmpdir4(), "himi-run-"));
50091
+ const target = join52(root2, app2);
50092
+ if (join52(source) !== target) {
49664
50093
  mkdirSync25(root2, { recursive: true });
49665
50094
  rmSync12(target, { recursive: true, force: true });
49666
50095
  cpSync2(source, target, { recursive: true });
@@ -49677,8 +50106,8 @@ async function resolvePackage(source, workDir) {
49677
50106
  }
49678
50107
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
49679
50108
  }
49680
- const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync59(source);
49681
- const root = workDir ?? mkdtempSync3(join51(tmpdir4(), "himi-run-"));
50109
+ const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync60(source);
50110
+ const root = workDir ?? mkdtempSync3(join52(tmpdir4(), "himi-run-"));
49682
50111
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
49683
50112
  const opened = openPackageDir(appDir2);
49684
50113
  if (!opened.validation.ok) {
@@ -49701,14 +50130,14 @@ async function loadPreviewAssets() {
49701
50130
  const mod = await Promise.resolve().then(() => (init_preview_assets_generated(), preview_assets_generated_exports));
49702
50131
  const here = dirname27(fileURLToPath22(import.meta.url));
49703
50132
  const candidates = [
49704
- join51(here, "preview-assets.generated.bin"),
50133
+ join52(here, "preview-assets.generated.bin"),
49705
50134
  // published: copied next to the bundled CLI
49706
- join51(here, "../../../packages/serve/src/preview-assets.generated.bin")
50135
+ join52(here, "../../../packages/serve/src/preview-assets.generated.bin")
49707
50136
  // in-repo checkout
49708
50137
  ];
49709
50138
  for (const path2 of candidates) {
49710
- if (!existsSync53(path2)) continue;
49711
- const blob = readFileSync59(path2);
50139
+ if (!existsSync54(path2)) continue;
50140
+ const blob = readFileSync60(path2);
49712
50141
  return mod.assemblePreviewAssetsFromBlob(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
49713
50142
  }
49714
50143
  return /* @__PURE__ */ new Map();
@@ -49805,9 +50234,9 @@ __export(run_watch_exports, {
49805
50234
  watchedContentDir: () => watchedContentDir,
49806
50235
  writeRunRecord: () => writeRunRecord
49807
50236
  });
49808
- import { closeSync as closeSync2, constants, existsSync as existsSync54, fstatSync, lstatSync as lstatSync8, statSync as statSync14, mkdirSync as mkdirSync26, openSync as openSync2, readFileSync as readFileSync60, realpathSync as realpathSync6, renameSync as renameSync5, rmSync as rmSync13, writeFileSync as writeFileSync22, watch as fsWatch } from "node:fs";
50237
+ import { closeSync as closeSync2, constants, existsSync as existsSync55, fstatSync, lstatSync as lstatSync8, statSync as statSync14, mkdirSync as mkdirSync26, openSync as openSync2, readFileSync as readFileSync61, realpathSync as realpathSync6, renameSync as renameSync5, rmSync as rmSync13, writeFileSync as writeFileSync22, watch as fsWatch } from "node:fs";
49809
50238
  import { randomUUID as randomUUID3 } from "node:crypto";
49810
- import { extname as extname4, join as join52, resolve as resolve42, sep as sep13 } from "node:path";
50239
+ import { extname as extname4, join as join53, resolve as resolve42, sep as sep13 } from "node:path";
49811
50240
  function diffReleases(prev, next) {
49812
50241
  const bundles = Object.keys(next.bundles).filter((b) => prev.bundles[b]?.sha256 !== next.bundles[b].sha256).sort();
49813
50242
  const changedBundles = new Set(bundles);
@@ -49847,7 +50276,7 @@ function watchContent(dir, onChange, opts = {}) {
49847
50276
  let timer = null;
49848
50277
  const pending = /* @__PURE__ */ new Set();
49849
50278
  const watcher2 = watch2(dir, { recursive: true }, (_event, file) => {
49850
- if (!file || ignoredChange(file, (rel) => isDir(join52(dir, rel)))) return;
50279
+ if (!file || ignoredChange(file, (rel) => isDir(join53(dir, rel)))) return;
49851
50280
  pending.add(file);
49852
50281
  if (timer) clearTimeout(timer);
49853
50282
  timer = setTimeout(() => {
@@ -49868,7 +50297,7 @@ function watchedContentDir(source) {
49868
50297
  if (!source || /^https?:\/\//i.test(source)) return null;
49869
50298
  const dir = resolve42(source);
49870
50299
  try {
49871
- return statSync14(dir).isDirectory() && existsSync54(join52(dir, "himalaya.content.json")) ? dir : null;
50300
+ return statSync14(dir).isDirectory() && existsSync55(join53(dir, "himalaya.content.json")) ? dir : null;
49872
50301
  } catch {
49873
50302
  return null;
49874
50303
  }
@@ -49925,7 +50354,7 @@ function serialize(task) {
49925
50354
  });
49926
50355
  }
49927
50356
  function himiDir(dir) {
49928
- const root = join52(dir, ".himi");
50357
+ const root = join53(dir, ".himi");
49929
50358
  try {
49930
50359
  return lstatSync8(root).isDirectory() ? root : null;
49931
50360
  } catch (e) {
@@ -49937,11 +50366,11 @@ function himiDir(dir) {
49937
50366
  function writeRunRecord(dir, record2) {
49938
50367
  const root = himiDir(dir);
49939
50368
  if (!root) return false;
49940
- const tmp = join52(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
50369
+ const tmp = join53(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
49941
50370
  try {
49942
50371
  writeFileSync22(tmp, `${JSON.stringify(record2, null, 2)}
49943
50372
  `, { flag: "wx" });
49944
- renameSync5(tmp, join52(root, "run.json"));
50373
+ renameSync5(tmp, join53(root, "run.json"));
49945
50374
  } catch (e) {
49946
50375
  rmSync13(tmp, { force: true });
49947
50376
  throw e;
@@ -49949,7 +50378,7 @@ function writeRunRecord(dir, record2) {
49949
50378
  return true;
49950
50379
  }
49951
50380
  function removeRunRecord(dir, owner) {
49952
- const root = join52(dir, ".himi");
50381
+ const root = join53(dir, ".himi");
49953
50382
  try {
49954
50383
  if (!lstatSync8(root).isDirectory()) return;
49955
50384
  } catch {
@@ -49964,16 +50393,16 @@ function removeRunRecord(dir, owner) {
49964
50393
  }
49965
50394
  if (typeof pid === "number" && pid !== owner) return;
49966
50395
  }
49967
- rmSync13(join52(root, "run.json"), { force: true });
50396
+ rmSync13(join53(root, "run.json"), { force: true });
49968
50397
  }
49969
50398
  function boardFile(dir, pathname) {
49970
50399
  let rel = null;
49971
- if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join52("studio", "index.html");
49972
- else if (pathname === "/__himi/studio/state.js") rel = join52("studio", "state.js");
49973
- else if (pathname === "/__himi/studio/timeline.js") rel = join52("studio", "timeline.js");
50400
+ if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join53("studio", "index.html");
50401
+ else if (pathname === "/__himi/studio/state.js") rel = join53("studio", "state.js");
50402
+ else if (pathname === "/__himi/studio/timeline.js") rel = join53("studio", "timeline.js");
49974
50403
  else {
49975
50404
  const m = /^\/__himi\/history\/([0-9a-f]{64}\.(png|jpg|gif|webp))$/.exec(pathname);
49976
- if (m) rel = join52("history", m[1]);
50405
+ if (m) rel = join53("history", m[1]);
49977
50406
  }
49978
50407
  if (!rel) return null;
49979
50408
  const bytes2 = readConfined(resolve42(dir, ".himi"), rel);
@@ -49989,7 +50418,7 @@ function readConfined(root, rel) {
49989
50418
  const parts = rel.split(sep13);
49990
50419
  let at = root;
49991
50420
  for (let i = 0; i < parts.length; i++) {
49992
- at = join52(at, parts[i]);
50421
+ at = join53(at, parts[i]);
49993
50422
  const st = lstatSync8(at);
49994
50423
  if (st.isSymbolicLink() || !(i === parts.length - 1 ? st.isFile() : st.isDirectory())) return null;
49995
50424
  }
@@ -49998,7 +50427,7 @@ function readConfined(root, rel) {
49998
50427
  const fd = openSync2(at, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
49999
50428
  try {
50000
50429
  if (!fstatSync(fd).isFile()) return null;
50001
- return readFileSync60(fd);
50430
+ return readFileSync61(fd);
50002
50431
  } finally {
50003
50432
  closeSync2(fd);
50004
50433
  }
@@ -50049,7 +50478,7 @@ __export(run_browser_exports, {
50049
50478
  runInBrowser: () => runInBrowser
50050
50479
  });
50051
50480
  import { createServer as createServer5 } from "node:http";
50052
- import { existsSync as existsSync55, readFileSync as readFileSync61 } from "node:fs";
50481
+ import { existsSync as existsSync56, readFileSync as readFileSync62 } from "node:fs";
50053
50482
  import { spawnSync as spawnSync3 } from "node:child_process";
50054
50483
  import { dirname as dirname28, resolve as resolve43 } from "node:path";
50055
50484
  import { fileURLToPath as fileURLToPath23 } from "node:url";
@@ -50057,7 +50486,7 @@ function stagedDir() {
50057
50486
  return process.env.HIMI_BROWSER_PLANE_OUT ? resolve43(process.env.HIMI_BROWSER_PLANE_OUT) : resolve43(REPO, "packages", "serve", "dist", "browser-plane");
50058
50487
  }
50059
50488
  function missingStaged() {
50060
- return [...SERVED].filter((f) => !existsSync55(resolve43(stagedDir(), f))).sort();
50489
+ return [...SERVED].filter((f) => !existsSync56(resolve43(stagedDir(), f))).sort();
50061
50490
  }
50062
50491
  function ensureBrowserPlaneStaged() {
50063
50492
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
@@ -50142,7 +50571,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
50142
50571
  }
50143
50572
  const name = path2 === "/" || path2 === "/index.html" ? "host.html" : path2.slice(1);
50144
50573
  if (SERVED.has(name)) {
50145
- if (!existsSync55(resolve43(stagedDir(), name))) {
50574
+ if (!existsSync56(resolve43(stagedDir(), name))) {
50146
50575
  const restaged = ensureBrowserPlaneStaged();
50147
50576
  if (!restaged.ok) {
50148
50577
  res.writeHead(503, { "content-type": "application/json" });
@@ -50152,7 +50581,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
50152
50581
  }
50153
50582
  let bytes2;
50154
50583
  try {
50155
- bytes2 = readFileSync61(resolve43(stagedDir(), name));
50584
+ bytes2 = readFileSync62(resolve43(stagedDir(), name));
50156
50585
  } catch (e) {
50157
50586
  res.writeHead(503, { "content-type": "application/json" });
50158
50587
  res.end(JSON.stringify({ error: "browser_plane_unstaged", file: name, detail: String(e) }));
@@ -50613,7 +51042,7 @@ __export(preflight_exports, {
50613
51042
  parseStoreConfig: () => parseStoreConfig,
50614
51043
  requireEnv: () => requireEnv
50615
51044
  });
50616
- import { existsSync as existsSync56, readFileSync as readFileSync62 } from "node:fs";
51045
+ import { existsSync as existsSync57, readFileSync as readFileSync63 } from "node:fs";
50617
51046
  import { isAbsolute as isAbsolute3, relative as relative9, resolve as resolve44 } from "node:path";
50618
51047
  function isInsideRepo(p) {
50619
51048
  const rel = relative9(REPO_ROOT3, resolve44(p));
@@ -50670,10 +51099,10 @@ function parseStoreConfig(text5) {
50670
51099
  }
50671
51100
  function loadStoreConfig(app) {
50672
51101
  const p = storeConfigPath(app);
50673
- if (!existsSync56(p)) {
51102
+ if (!existsSync57(p)) {
50674
51103
  throw new Error(`No store config at ${p}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
50675
51104
  }
50676
- return parseStoreConfig(readFileSync62(p, "utf8"));
51105
+ return parseStoreConfig(readFileSync63(p, "utf8"));
50677
51106
  }
50678
51107
  var init_preflight = __esm({
50679
51108
  "../store-release/preflight.ts"() {
@@ -50692,19 +51121,19 @@ __export(credential_profile_exports, {
50692
51121
  profilePath: () => profilePath,
50693
51122
  resolveStoreCredentials: () => resolveStoreCredentials
50694
51123
  });
50695
- import { existsSync as existsSync57, readFileSync as readFileSync63 } from "node:fs";
51124
+ import { existsSync as existsSync58, readFileSync as readFileSync64 } from "node:fs";
50696
51125
  import { homedir as homedir5 } from "node:os";
50697
- import { join as join53 } from "node:path";
51126
+ import { join as join54 } from "node:path";
50698
51127
  function credentialsDir(home = homedir5()) {
50699
- return join53(home, ".himalaya", "store-credentials");
51128
+ return join54(home, ".himalaya", "store-credentials");
50700
51129
  }
50701
51130
  function profilePath(name, home = homedir5()) {
50702
51131
  if (!PROFILE_NAME.test(name)) {
50703
51132
  throw new Error(
50704
- `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join53(credentialsDir(home), "<name>.json")}; it is never a path.`
51133
+ `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join54(credentialsDir(home), "<name>.json")}; it is never a path.`
50705
51134
  );
50706
51135
  }
50707
- return join53(credentialsDir(home), `${name}.json`);
51136
+ return join54(credentialsDir(home), `${name}.json`);
50708
51137
  }
50709
51138
  function parseProfile(text5, label2) {
50710
51139
  let doc;
@@ -50735,8 +51164,8 @@ function resolveStoreCredentials(opts) {
50735
51164
  const { app, cfg } = opts;
50736
51165
  const env = opts.env ?? process.env;
50737
51166
  const home = opts.home ?? homedir5();
50738
- const read = opts.readProfile ?? ((p) => readFileSync63(p, "utf8"));
50739
- const exists = opts.profileExists ?? existsSync57;
51167
+ const read = opts.readProfile ?? ((p) => readFileSync64(p, "utf8"));
51168
+ const exists = opts.profileExists ?? existsSync58;
50740
51169
  const ios = cfg.ios ?? {};
50741
51170
  const flagName = opts.profileFlag?.trim();
50742
51171
  const named = namedProfile(cfg, opts.profileFlag);
@@ -50816,9 +51245,9 @@ __export(preflight_app_exports, {
50816
51245
  preflightApp: () => preflightApp,
50817
51246
  profileAccount: () => profileAccount
50818
51247
  });
50819
- import { readFileSync as readFileSync64, existsSync as existsSync58, readdirSync as readdirSync26, statSync as statSync15 } from "node:fs";
51248
+ import { readFileSync as readFileSync65, existsSync as existsSync59, readdirSync as readdirSync26, statSync as statSync15 } from "node:fs";
50820
51249
  import { createHash as createHash26 } from "node:crypto";
50821
- import { join as join54 } from "node:path";
51250
+ import { join as join55 } from "node:path";
50822
51251
  function analyze(projectYml, storeConfig, icon, account) {
50823
51252
  const f = [];
50824
51253
  const has = (re, s) => re.test(s);
@@ -50882,13 +51311,13 @@ function profileAccount(app, storeConfig, opts = {}) {
50882
51311
  }
50883
51312
  }
50884
51313
  async function preflightApp(app, opts = {}) {
50885
- const projectYml = existsSync58(iosProjectYml(app)) ? readFileSync64(iosProjectYml(app), "utf8") : "";
50886
- const storeConfig = existsSync58(storeConfigPath(app)) ? readFileSync64(storeConfigPath(app), "utf8") : "";
51314
+ const projectYml = existsSync59(iosProjectYml(app)) ? readFileSync65(iosProjectYml(app), "utf8") : "";
51315
+ const storeConfig = existsSync59(storeConfigPath(app)) ? readFileSync65(storeConfigPath(app), "utf8") : "";
50887
51316
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
50888
51317
  const findings = analyze(
50889
51318
  projectYml,
50890
51319
  storeConfig,
50891
- await appIconState(join54(appDir(app), "ios")),
51320
+ await appIconState(join55(appDir(app), "ios")),
50892
51321
  profileAccount(app, storeConfig, opts)
50893
51322
  );
50894
51323
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
@@ -50917,17 +51346,17 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
50917
51346
  return f;
50918
51347
  }
50919
51348
  function appLevelMetadataFiles(app, locale = "en-US") {
50920
- const root = join54(storeDir(app), "metadata");
51349
+ const root = join55(storeDir(app), "metadata");
50921
51350
  const out = /* @__PURE__ */ new Set();
50922
- if (existsSync58(root)) {
51351
+ if (existsSync59(root)) {
50923
51352
  for (const n of readdirSync26(root)) if (n.endsWith(".txt")) out.add(n);
50924
51353
  }
50925
- if (existsSync58(join54(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
51354
+ if (existsSync59(join55(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
50926
51355
  return out;
50927
51356
  }
50928
51357
  function reviewInfoFiles(app) {
50929
- const dir = join54(storeDir(app), "review_information");
50930
- if (!existsSync58(dir)) return /* @__PURE__ */ new Set();
51358
+ const dir = join55(storeDir(app), "review_information");
51359
+ if (!existsSync59(dir)) return /* @__PURE__ */ new Set();
50931
51360
  return new Set(readdirSync26(dir).filter((n) => n.endsWith(".txt")));
50932
51361
  }
50933
51362
  function isValidAppStoreSize(w, h) {
@@ -51037,12 +51466,12 @@ function analyzePlayListing(assets) {
51037
51466
  return f;
51038
51467
  }
51039
51468
  function playListingAssets(app, locale = "en-US") {
51040
- const dir = join54(storeDir(app), "play", locale, "images");
51469
+ const dir = join55(storeDir(app), "play", locale, "images");
51041
51470
  const read = (names) => {
51042
51471
  for (const name of names) {
51043
- const p = join54(dir, name);
51472
+ const p = join55(dir, name);
51044
51473
  try {
51045
- if (statSync15(p).isFile()) return { name, bytes: readFileSync64(p) };
51474
+ if (statSync15(p).isFile()) return { name, bytes: readFileSync65(p) };
51046
51475
  } catch {
51047
51476
  }
51048
51477
  }
@@ -51073,26 +51502,26 @@ function analyzeListing(metaFiles, shots) {
51073
51502
  return f;
51074
51503
  }
51075
51504
  function metadataFilesPresent(app, locale = "en-US") {
51076
- const dir = join54(storeDir(app), "metadata", locale);
51077
- if (!existsSync58(dir)) return /* @__PURE__ */ new Set();
51505
+ const dir = join55(storeDir(app), "metadata", locale);
51506
+ if (!existsSync59(dir)) return /* @__PURE__ */ new Set();
51078
51507
  return new Set(readdirSync26(dir).filter((n) => n.endsWith(".txt")));
51079
51508
  }
51080
51509
  function listScreenshots(app, locale = "en-US") {
51081
51510
  const out = [];
51082
- const flat = join54(storeDir(app), "screenshots", locale);
51083
- if (existsSync58(flat)) {
51511
+ const flat = join55(storeDir(app), "screenshots", locale);
51512
+ if (existsSync59(flat)) {
51084
51513
  for (const png of readdirSync26(flat).filter((n) => n.endsWith(".png"))) {
51085
- const sz = pngSize(readFileSync64(join54(flat, png)));
51514
+ const sz = pngSize(readFileSync65(join55(flat, png)));
51086
51515
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
51087
51516
  }
51088
51517
  }
51089
- const legacy = join54(storeDir(app), "metadata", locale, "screenshots");
51090
- if (existsSync58(legacy)) {
51518
+ const legacy = join55(storeDir(app), "metadata", locale, "screenshots");
51519
+ if (existsSync59(legacy)) {
51091
51520
  for (const deviceDir of readdirSync26(legacy)) {
51092
- const d = join54(legacy, deviceDir);
51521
+ const d = join55(legacy, deviceDir);
51093
51522
  if (!statSync15(d).isDirectory()) continue;
51094
51523
  for (const png of readdirSync26(d).filter((n) => n.endsWith(".png"))) {
51095
- const sz = pngSize(readFileSync64(join54(d, png)));
51524
+ const sz = pngSize(readFileSync65(join55(d, png)));
51096
51525
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
51097
51526
  }
51098
51527
  }
@@ -51100,13 +51529,13 @@ function listScreenshots(app, locale = "en-US") {
51100
51529
  return out;
51101
51530
  }
51102
51531
  function placeholderHashes2() {
51103
- const templateIos = join54(appDir("_template"), "ios");
51104
- if (!existsSync58(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
51532
+ const templateIos = join55(appDir("_template"), "ios");
51533
+ if (!existsSync59(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
51105
51534
  for (const target of readdirSync26(templateIos)) {
51106
- const p = join54(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51535
+ const p = join55(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51107
51536
  try {
51108
51537
  if (!statSync15(p).isFile()) continue;
51109
- return [LEGACY_PLACEHOLDER_SHA2562, createHash26("sha256").update(readFileSync64(p)).digest("hex")];
51538
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash26("sha256").update(readFileSync65(p)).digest("hex")];
51110
51539
  } catch {
51111
51540
  }
51112
51541
  }
@@ -51137,12 +51566,12 @@ async function pixelIssues(bytes2) {
51137
51566
  }
51138
51567
  }
51139
51568
  async function appIconState(iosDir) {
51140
- if (!existsSync58(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
51569
+ if (!existsSync59(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
51141
51570
  for (const target of readdirSync26(iosDir)) {
51142
- const p = join54(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51571
+ const p = join55(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
51143
51572
  try {
51144
51573
  if (!statSync15(p).isFile()) continue;
51145
- const bytes2 = readFileSync64(p);
51574
+ const bytes2 = readFileSync65(p);
51146
51575
  const hash = createHash26("sha256").update(bytes2).digest("hex");
51147
51576
  const pixels = await pixelIssues(bytes2);
51148
51577
  return {
@@ -51214,7 +51643,7 @@ __export(version_exports, {
51214
51643
  setAndroidVersion: () => setAndroidVersion,
51215
51644
  setIosVersion: () => setIosVersion
51216
51645
  });
51217
- import { readFileSync as readFileSync65, writeFileSync as writeFileSync23 } from "node:fs";
51646
+ import { readFileSync as readFileSync66, writeFileSync as writeFileSync23 } from "node:fs";
51218
51647
  function readIosVersion(projectYml) {
51219
51648
  const marketing = matchScalar(projectYml, "CFBundleShortVersionString");
51220
51649
  const build2 = matchScalar(projectYml, "CFBundleVersion");
@@ -51272,8 +51701,8 @@ function setAndroidVersion(gradle, v) {
51272
51701
  function bumpVersion(app, opts) {
51273
51702
  const iosPath = iosProjectYml(app);
51274
51703
  const gradlePath = androidGradle(app);
51275
- const iosYml = readFileSync65(iosPath, "utf8");
51276
- const gradle = readFileSync65(gradlePath, "utf8");
51704
+ const iosYml = readFileSync66(iosPath, "utf8");
51705
+ const gradle = readFileSync66(gradlePath, "utf8");
51277
51706
  const current = readIosVersion(iosYml);
51278
51707
  const next = {
51279
51708
  marketing: opts.marketing ?? current.marketing,
@@ -51290,7 +51719,7 @@ var init_version = __esm({
51290
51719
  });
51291
51720
 
51292
51721
  // ../store-release/register-bundle.ts
51293
- import { readFileSync as readFileSync66 } from "node:fs";
51722
+ import { readFileSync as readFileSync67 } from "node:fs";
51294
51723
  import { createPrivateKey as createPrivateKey2, sign } from "node:crypto";
51295
51724
  function b64url2(b) {
51296
51725
  return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -51308,7 +51737,7 @@ function bundleIdBody(identifier, name, platform = "IOS") {
51308
51737
  async function registerBundleId(opts) {
51309
51738
  const env = requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
51310
51739
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
51311
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync66(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51740
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync67(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51312
51741
  const res = await fetch(`${ASC_API}/v1/bundleIds`, {
51313
51742
  method: "POST",
51314
51743
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
@@ -51362,7 +51791,7 @@ __export(asc_status_exports, {
51362
51791
  ascStatus: () => ascStatus,
51363
51792
  buildVerdict: () => buildVerdict
51364
51793
  });
51365
- import { readFileSync as readFileSync67 } from "node:fs";
51794
+ import { readFileSync as readFileSync68 } from "node:fs";
51366
51795
  function buildVerdict(builds) {
51367
51796
  if (!builds.length) {
51368
51797
  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).";
@@ -51376,7 +51805,7 @@ function buildVerdict(builds) {
51376
51805
  function client(creds) {
51377
51806
  const env = creds ?? requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
51378
51807
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
51379
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync67(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51808
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync68(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
51380
51809
  return async (path2) => {
51381
51810
  const r = await fetch(`${ASC}${path2}`, { headers: { Authorization: `Bearer ${jwt}` } });
51382
51811
  return { status: r.status, json: await r.json().catch(() => ({})) };
@@ -51536,7 +51965,7 @@ __export(secrets_exports, {
51536
51965
  runRemoteSecretsCommand: () => runRemoteSecretsCommand,
51537
51966
  runSecretsCommand: () => runSecretsCommand
51538
51967
  });
51539
- import { readFileSync as readFileSync68 } from "node:fs";
51968
+ import { readFileSync as readFileSync69 } from "node:fs";
51540
51969
  function environment(value) {
51541
51970
  if (value === "preview" || value === "production") return value;
51542
51971
  throw new Error("--environment must be preview or production");
@@ -51567,7 +51996,7 @@ async function runSecretsCommand(operation, flags, io, options = {}) {
51567
51996
  io.log(JSON.stringify(await vault.list({ app, environment: env })));
51568
51997
  return 0;
51569
51998
  }
51570
- const value = options.readValue ? options.readValue() : readFileSync68(0, "utf8").replace(/\r?\n$/, "");
51999
+ const value = options.readValue ? options.readValue() : readFileSync69(0, "utf8").replace(/\r?\n$/, "");
51571
52000
  if (operation === "set") await vault.set({ app, environment: env, name, value });
51572
52001
  else if (operation === "rotate") await vault.rotate({ app, environment: env, name, value });
51573
52002
  else throw new Error(`unknown secrets operation ${operation}`);
@@ -51596,7 +52025,7 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
51596
52025
  }
51597
52026
  let body;
51598
52027
  if (operation !== "list") {
51599
- const value = options.readValue ? options.readValue() : readFileSync68(0, "utf8").replace(/\r?\n$/, "");
52028
+ const value = options.readValue ? options.readValue() : readFileSync69(0, "utf8").replace(/\r?\n$/, "");
51600
52029
  body = JSON.stringify({ value });
51601
52030
  }
51602
52031
  const response = await (options.fetchImpl ?? fetch)(url, { method: operation === "list" ? "GET" : operation === "set" ? "PUT" : "POST", headers: headers2, body });
@@ -51632,15 +52061,15 @@ __export(notify_exports, {
51632
52061
  runNotify: () => runNotify,
51633
52062
  saveAdminSession: () => saveAdminSession
51634
52063
  });
51635
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync27, readFileSync as readFileSync69, writeFileSync as writeFileSync24 } from "node:fs";
52064
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync27, readFileSync as readFileSync70, writeFileSync as writeFileSync24 } from "node:fs";
51636
52065
  import { homedir as homedir6 } from "node:os";
51637
- import { dirname as dirname30, join as join55 } from "node:path";
52066
+ import { dirname as dirname30, join as join56 } from "node:path";
51638
52067
  function defaultSessionsPath() {
51639
- return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join55(homedir6(), ".himalaya", "admin-sessions.json");
52068
+ return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join56(homedir6(), ".himalaya", "admin-sessions.json");
51640
52069
  }
51641
52070
  function loadSessionsFile(path2 = defaultSessionsPath()) {
51642
52071
  try {
51643
- return JSON.parse(readFileSync69(path2, "utf8"));
52072
+ return JSON.parse(readFileSync70(path2, "utf8"));
51644
52073
  } catch {
51645
52074
  return {};
51646
52075
  }
@@ -53018,7 +53447,7 @@ var init_traits = __esm({
53018
53447
  });
53019
53448
 
53020
53449
  // src/site-analyze/tokens.ts
53021
- function channels(css) {
53450
+ function channels2(css) {
53022
53451
  const canonical3 = canonicalHex(css);
53023
53452
  if (!canonical3) return null;
53024
53453
  return [
@@ -53028,11 +53457,11 @@ function channels(css) {
53028
53457
  ];
53029
53458
  }
53030
53459
  function isNearWhite(css) {
53031
- const c = channels(css);
53460
+ const c = channels2(css);
53032
53461
  return !!c && c[0] >= 240 && c[1] >= 240 && c[2] >= 240;
53033
53462
  }
53034
53463
  function isNearBlack(css) {
53035
- const c = channels(css);
53464
+ const c = channels2(css);
53036
53465
  return !!c && c[0] <= 80 && c[1] <= 80 && c[2] <= 80;
53037
53466
  }
53038
53467
  function usableCssColor(v) {
@@ -53171,12 +53600,12 @@ var init_tokens3 = __esm({
53171
53600
  function channel2(hex2, offset) {
53172
53601
  return Number.parseInt(hex2.slice(offset, offset + 2), 16);
53173
53602
  }
53174
- function toHex(r, g, b) {
53603
+ function toHex2(r, g, b) {
53175
53604
  return `#${[r, g, b].map((value) => Math.round(value).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
53176
53605
  }
53177
53606
  function mix2(color, background, colorWeight) {
53178
53607
  const values = [1, 3, 5].map((offset) => channel2(color, offset) * colorWeight + channel2(background, offset) * (1 - colorWeight));
53179
- return toHex(values[0], values[1], values[2]);
53608
+ return toHex2(values[0], values[1], values[2]);
53180
53609
  }
53181
53610
  function relativeLuminance2(color) {
53182
53611
  const linear = [1, 3, 5].map((offset) => {
@@ -55516,7 +55945,7 @@ var init_brand_font = __esm({
55516
55945
 
55517
55946
  // src/site-analyze/write-artifacts.ts
55518
55947
  import { mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
55519
- import { join as join56 } from "node:path";
55948
+ import { join as join57 } from "node:path";
55520
55949
  function attachVeloSources(book, sources, put) {
55521
55950
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
55522
55951
  for (const f of book.velo.files) {
@@ -55671,12 +56100,12 @@ async function downloadMedia(book, fetchImpl, put) {
55671
56100
  }
55672
56101
  async function writeBusinessBook(result, opts) {
55673
56102
  const out = opts.outDir;
55674
- mkdirSync28(join56(out, "evidence/pages"), { recursive: true });
55675
- mkdirSync28(join56(out, "media/catalog"), { recursive: true });
56103
+ mkdirSync28(join57(out, "evidence/pages"), { recursive: true });
56104
+ mkdirSync28(join57(out, "media/catalog"), { recursive: true });
55676
56105
  const files = [];
55677
56106
  const put = (rel, body) => {
55678
- const p = join56(out, rel);
55679
- mkdirSync28(join56(p, ".."), { recursive: true });
56107
+ const p = join57(out, rel);
56108
+ mkdirSync28(join57(p, ".."), { recursive: true });
55680
56109
  writeFileSync25(p, body);
55681
56110
  files.push(rel);
55682
56111
  };
@@ -55770,8 +56199,8 @@ var init_customer_build = __esm({
55770
56199
  });
55771
56200
 
55772
56201
  // src/site-analyze/apply.ts
55773
- import { existsSync as existsSync59, readFileSync as readFileSync70, writeFileSync as writeFileSync26, copyFileSync as copyFileSync3, mkdirSync as mkdirSync29, realpathSync as realpathSync7, statSync as statSync16 } from "node:fs";
55774
- import { dirname as dirname31, isAbsolute as isAbsolute4, join as join57, resolve as resolve45, sep as sep14 } from "node:path";
56202
+ import { existsSync as existsSync60, readFileSync as readFileSync71, writeFileSync as writeFileSync26, copyFileSync as copyFileSync3, mkdirSync as mkdirSync29, realpathSync as realpathSync7, statSync as statSync16 } from "node:fs";
56203
+ import { dirname as dirname31, isAbsolute as isAbsolute4, join as join58, resolve as resolve45, sep as sep14 } from "node:path";
55775
56204
  import { spawnSync as spawnSync4 } from "node:child_process";
55776
56205
  function unpairedRedirectNote(overlay) {
55777
56206
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -55786,7 +56215,7 @@ function committedShellProvisioning(path2) {
55786
56215
  function findRepoRoot(cwd) {
55787
56216
  let dir = cwd;
55788
56217
  for (let i = 0; i < 8; i++) {
55789
- if (SHELL_NAMES2.some((name) => existsSync59(join57(dir, shellAppDir(name), "package.json")))) return dir;
56218
+ if (SHELL_NAMES2.some((name) => existsSync60(join58(dir, shellAppDir(name), "package.json")))) return dir;
55790
56219
  const next = dirname31(dir);
55791
56220
  if (next === dir) break;
55792
56221
  dir = next;
@@ -55854,11 +56283,11 @@ function mergeCustomerOwned(committed, overlay) {
55854
56283
  }
55855
56284
  function buildMergedContract(options) {
55856
56285
  const { appDir: appDir2, packageDir, overlay } = options;
55857
- const committedPath = join57(appDir2, "provisioning.json");
55858
- if (!existsSync59(committedPath)) {
56286
+ const committedPath = join58(appDir2, "provisioning.json");
56287
+ if (!existsSync60(committedPath)) {
55859
56288
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
55860
56289
  }
55861
- const committed = JSON.parse(readFileSync70(committedPath, "utf8"));
56290
+ const committed = JSON.parse(readFileSync71(committedPath, "utf8"));
55862
56291
  const merged = mergeCustomerOwned(committed, overlay);
55863
56292
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
55864
56293
  const branding = merged.branding ?? {};
@@ -55870,8 +56299,8 @@ function buildMergedContract(options) {
55870
56299
  };
55871
56300
  const stage = (fromRel, toRel) => {
55872
56301
  const from = contained(packageDir, fromRel);
55873
- const to = contained(join57(appDir2, STAGED), toRel.slice(STAGED.length + 1));
55874
- if (!from || !to || !existsSync59(from)) return void 0;
56302
+ const to = contained(join58(appDir2, STAGED), toRel.slice(STAGED.length + 1));
56303
+ if (!from || !to || !existsSync60(from)) return void 0;
55875
56304
  let real;
55876
56305
  let realRoot;
55877
56306
  try {
@@ -55893,11 +56322,11 @@ function buildMergedContract(options) {
55893
56322
  };
55894
56323
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
55895
56324
  if (icon) branding.appIconPath = icon;
55896
- const facesPath = join57(packageDir, "media/fonts/faces.json");
55897
- if (existsSync59(facesPath)) {
56325
+ const facesPath = join58(packageDir, "media/fonts/faces.json");
56326
+ if (existsSync60(facesPath)) {
55898
56327
  let faces = [];
55899
56328
  try {
55900
- const parsed = JSON.parse(readFileSync70(facesPath, "utf8"));
56329
+ const parsed = JSON.parse(readFileSync71(facesPath, "utf8"));
55901
56330
  faces = Array.isArray(parsed) ? parsed : [];
55902
56331
  } catch {
55903
56332
  faces = [];
@@ -55918,9 +56347,9 @@ function buildMergedContract(options) {
55918
56347
  merged.branding = branding;
55919
56348
  const body = `${JSON.stringify(merged, null, 2)}
55920
56349
  `;
55921
- const path2 = join57(packageDir, "provisioning.contract.json");
56350
+ const path2 = join58(packageDir, "provisioning.contract.json");
55922
56351
  writeFileSync26(path2, body);
55923
- const localPath = join57(appDir2, LOCAL_CONTRACT_REL);
56352
+ const localPath = join58(appDir2, LOCAL_CONTRACT_REL);
55924
56353
  mkdirSync29(dirname31(localPath), { recursive: true });
55925
56354
  writeFileSync26(localPath, body);
55926
56355
  staged.push(localPath);
@@ -55930,8 +56359,8 @@ async function applyOverlays(input) {
55930
56359
  const cwd = resolve45(input.cwd);
55931
56360
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
55932
56361
  assertKnownShell(shell);
55933
- const overlay = JSON.parse(readFileSync70(input.overlayPath, "utf8"));
55934
- const tokens = JSON.parse(readFileSync70(input.tokensPath, "utf8"));
56362
+ const overlay = JSON.parse(readFileSync71(input.overlayPath, "utf8"));
56363
+ const tokens = JSON.parse(readFileSync71(input.tokensPath, "utf8"));
55935
56364
  const wrote = [];
55936
56365
  const notes = [];
55937
56366
  const repoRoot2 = findRepoRoot(cwd);
@@ -55941,7 +56370,7 @@ async function applyOverlays(input) {
55941
56370
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
55942
56371
  );
55943
56372
  }
55944
- const appDir2 = join57(repoRoot2, shellAppDir(shell));
56373
+ const appDir2 = join58(repoRoot2, shellAppDir(shell));
55945
56374
  const packageDir = dirname31(resolve45(input.overlayPath));
55946
56375
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
55947
56376
  wrote.push(...staged);
@@ -55963,10 +56392,10 @@ async function applyOverlays(input) {
55963
56392
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
55964
56393
  };
55965
56394
  }
55966
- const pkgProv = join57(cwd, "provisioning.json");
56395
+ const pkgProv = join58(cwd, "provisioning.json");
55967
56396
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
55968
- if (existsSync59(pkgProv)) {
55969
- const cur = JSON.parse(readFileSync70(pkgProv, "utf8"));
56397
+ if (existsSync60(pkgProv)) {
56398
+ const cur = JSON.parse(readFileSync71(pkgProv, "utf8"));
55970
56399
  const merged = deepMerge4(cur, overlay);
55971
56400
  writeFileSync26(pkgProv, JSON.stringify(merged, null, 2) + "\n");
55972
56401
  wrote.push(pkgProv);
@@ -55974,9 +56403,9 @@ async function applyOverlays(input) {
55974
56403
  writeFileSync26(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
55975
56404
  wrote.push(pkgProv);
55976
56405
  }
55977
- const pkgTokens = join57(cwd, "tokens.json");
55978
- if (existsSync59(pkgTokens) && (tokens.colors || tokens.typography)) {
55979
- const cur = JSON.parse(readFileSync70(pkgTokens, "utf8"));
56406
+ const pkgTokens = join58(cwd, "tokens.json");
56407
+ if (existsSync60(pkgTokens) && (tokens.colors || tokens.typography)) {
56408
+ const cur = JSON.parse(readFileSync71(pkgTokens, "utf8"));
55980
56409
  const merged = deepMerge4(cur, tokens);
55981
56410
  const mergedColors = merged.colors;
55982
56411
  if (mergedColors && typeof mergedColors === "object" && !Array.isArray(mergedColors)) {
@@ -55995,9 +56424,9 @@ async function applyOverlays(input) {
55995
56424
  wrote.push(pkgTokens);
55996
56425
  }
55997
56426
  const homeAction = homeRefreshActionId(shell);
55998
- const homeSrc = join57(cwd, "dev/index.ts");
55999
- if (homeAction && existsSync59(homeSrc)) {
56000
- const prev = readFileSync70(homeSrc, "utf8");
56427
+ const homeSrc = join58(cwd, "dev/index.ts");
56428
+ if (homeAction && existsSync60(homeSrc)) {
56429
+ const prev = readFileSync71(homeSrc, "utf8");
56001
56430
  const patched = patchBrandParams(prev, overlay, homeAction);
56002
56431
  let next = patched.src;
56003
56432
  if (!patched.foreign && overlay.installedVerticals?.length) {
@@ -56020,13 +56449,13 @@ async function applyOverlays(input) {
56020
56449
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
56021
56450
  }
56022
56451
  }
56023
- if (!input.iconPath || !existsSync59(input.iconPath)) {
56452
+ if (!input.iconPath || !existsSync60(input.iconPath)) {
56024
56453
  const book = input.iconPath ? dirname31(dirname31(resolve45(input.iconPath))) : void 0;
56025
56454
  notes.push(
56026
56455
  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"
56027
56456
  );
56028
56457
  }
56029
- if (input.iconPath && existsSync59(input.iconPath)) {
56458
+ if (input.iconPath && existsSync60(input.iconPath)) {
56030
56459
  try {
56031
56460
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
56032
56461
  wrote.push(...installed.wrote);
@@ -60201,7 +60630,7 @@ __export(cli_exports, {
60201
60630
  runSiteAnalyze: () => runSiteAnalyze,
60202
60631
  runSiteFunctionCli: () => runSiteFunctionCli
60203
60632
  });
60204
- import { readFileSync as readFileSync71 } from "node:fs";
60633
+ import { readFileSync as readFileSync72 } from "node:fs";
60205
60634
  import { resolve as resolve46 } from "node:path";
60206
60635
  function assetFetchFor(_opts) {
60207
60636
  return pinnedFetch2;
@@ -60300,7 +60729,7 @@ async function runSiteFunctionCli(opts) {
60300
60729
  let source;
60301
60730
  if (opts.sourcePath) {
60302
60731
  const p = resolve46(opts.cwd ?? process.cwd(), opts.sourcePath);
60303
- source = readFileSync71(p, "utf8");
60732
+ source = readFileSync72(p, "utf8");
60304
60733
  }
60305
60734
  let body;
60306
60735
  if (opts.bodyRaw != null) {
@@ -60559,7 +60988,7 @@ __export(site_create_exports, {
60559
60988
  runSiteList: () => runSiteList,
60560
60989
  writeProvisioningBinding: () => writeProvisioningBinding
60561
60990
  });
60562
- import { existsSync as existsSync60, readFileSync as readFileSync72, writeFileSync as writeFileSync27 } from "node:fs";
60991
+ import { existsSync as existsSync61, readFileSync as readFileSync73, writeFileSync as writeFileSync27 } from "node:fs";
60563
60992
  import { resolve as resolvePath2 } from "node:path";
60564
60993
  function oauthClientName(projectName) {
60565
60994
  return `${projectName} client`;
@@ -60708,7 +61137,7 @@ async function runSiteCreate(opts) {
60708
61137
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
60709
61138
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
60710
61139
  }
60711
- function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync60, readFileSync: readFileSync72, writeFileSync: writeFileSync27 }) {
61140
+ function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync61, readFileSync: readFileSync73, writeFileSync: writeFileSync27 }) {
60712
61141
  const msid = binding2.siteId;
60713
61142
  const path2 = resolvePath2(target);
60714
61143
  const committed = committedShellProvisioning(path2);
@@ -61381,11 +61810,11 @@ var init_context = __esm({
61381
61810
  });
61382
61811
 
61383
61812
  // src/cli.ts
61384
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync73, mkdirSync as mkdirSync30, writeFileSync as writeFileSync28, existsSync as existsSync61, rmSync as rmSync14, readdirSync as readdirSync27, statSync as statSync17, lstatSync as lstatSync9, accessSync, constants as constants2, renameSync as renameSync6, cpSync as cpSync3 } from "node:fs";
61813
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync74, mkdirSync as mkdirSync30, writeFileSync as writeFileSync28, existsSync as existsSync62, rmSync as rmSync14, readdirSync as readdirSync27, statSync as statSync17, lstatSync as lstatSync9, accessSync, constants as constants2, renameSync as renameSync6, cpSync as cpSync3 } from "node:fs";
61385
61814
  import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
61386
61815
  import { createHash as createHash27, randomUUID as randomUUID5 } from "node:crypto";
61387
61816
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
61388
- import { join as join58, resolve as resolve47, relative as relative10, basename as basename14, dirname as dirname32 } from "node:path";
61817
+ import { join as join59, resolve as resolve47, relative as relative10, basename as basename14, dirname as dirname32 } from "node:path";
61389
61818
  import { fileURLToPath as fileURLToPath24 } from "node:url";
61390
61819
 
61391
61820
  // ../serve-cli/src/client.ts
@@ -64678,8 +65107,8 @@ var str6 = (v) => typeof v === "string" ? v : void 0;
64678
65107
  var num2 = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
64679
65108
  function embeddedTemplateCatalog() {
64680
65109
  return availableTemplates().map((name) => {
64681
- const path2 = join58(templateSourceDir(name), TEMPLATE_META_FILE);
64682
- const meta = JSON.parse(readFileSync73(path2, "utf8"));
65110
+ const path2 = join59(templateSourceDir(name), TEMPLATE_META_FILE);
65111
+ const meta = JSON.parse(readFileSync74(path2, "utf8"));
64683
65112
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
64684
65113
  throw new Error(`${path2} must be { title: string, description: string, tags: string[], category?: string }`);
64685
65114
  }
@@ -65351,7 +65780,7 @@ var COMMANDS = [
65351
65780
  ];
65352
65781
  function cliVersion() {
65353
65782
  try {
65354
- return JSON.parse(readFileSync73(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
65783
+ return JSON.parse(readFileSync74(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
65355
65784
  } catch {
65356
65785
  return "unknown";
65357
65786
  }
@@ -65469,7 +65898,7 @@ function ageInDays(publishedAt, now = Date.now()) {
65469
65898
  }
65470
65899
  function packageVersion(path2) {
65471
65900
  try {
65472
- const version2 = JSON.parse(readFileSync73(path2, "utf8")).version;
65901
+ const version2 = JSON.parse(readFileSync74(path2, "utf8")).version;
65473
65902
  return typeof version2 === "string" && version2 ? version2 : void 0;
65474
65903
  } catch {
65475
65904
  return void 0;
@@ -65478,7 +65907,7 @@ function packageVersion(path2) {
65478
65907
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
65479
65908
  let dir = resolve47(fromDir);
65480
65909
  for (; ; ) {
65481
- const version2 = packageVersion(join58(dir, "node_modules", "@wix", "himalaya", "package.json"));
65910
+ const version2 = packageVersion(join59(dir, "node_modules", "@wix", "himalaya", "package.json"));
65482
65911
  if (version2) return { version: version2, source: "local" };
65483
65912
  const parent = dirname32(dir);
65484
65913
  if (parent === dir) break;
@@ -65488,7 +65917,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
65488
65917
  if (bundled) return { version: bundled, source: "bundled" };
65489
65918
  try {
65490
65919
  const root = execFileSync7("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
65491
- const version2 = root ? packageVersion(join58(root, "@wix", "himalaya", "package.json")) : void 0;
65920
+ const version2 = root ? packageVersion(join59(root, "@wix", "himalaya", "package.json")) : void 0;
65492
65921
  return version2 ? { version: version2, source: "global" } : {};
65493
65922
  } catch {
65494
65923
  return {};
@@ -65535,11 +65964,11 @@ var UPDATE_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1e3;
65535
65964
  var UPDATE_SNOOZE_MS = 24 * 60 * 60 * 1e3;
65536
65965
  var UPDATE_REEXEC_ENV = "HIMI_UPDATED_ONCE";
65537
65966
  function updateCachePath(env = process.env) {
65538
- return env.HIMI_UPDATE_CACHE_FILE ?? join58(homedir7(), ".himalaya", "update-check.json");
65967
+ return env.HIMI_UPDATE_CACHE_FILE ?? join59(homedir7(), ".himalaya", "update-check.json");
65539
65968
  }
65540
65969
  function readUpdateCache(path2) {
65541
65970
  try {
65542
- const parsed = JSON.parse(readFileSync73(path2, "utf8"));
65971
+ const parsed = JSON.parse(readFileSync74(path2, "utf8"));
65543
65972
  return parsed && typeof parsed === "object" ? parsed : {};
65544
65973
  } catch {
65545
65974
  return {};
@@ -65607,12 +66036,12 @@ function createUpdateRunner(spawn7 = spawnSync5) {
65607
66036
  },
65608
66037
  installedVersion: () => {
65609
66038
  const root = globalNodeModulesRoot();
65610
- return root ? packageVersion(join58(root, "@wix", "himalaya-cli", "package.json")) : void 0;
66039
+ return root ? packageVersion(join59(root, "@wix", "himalaya-cli", "package.json")) : void 0;
65611
66040
  },
65612
66041
  reexec: (argv) => {
65613
66042
  const root = globalNodeModulesRoot();
65614
- const entry = root ? join58(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
65615
- if (!entry || !existsSync61(entry)) return void 0;
66043
+ const entry = root ? join59(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
66044
+ if (!entry || !existsSync62(entry)) return void 0;
65616
66045
  const result = spawnSync5(process.execPath, [entry, ...argv], {
65617
66046
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
65618
66047
  stdio: "inherit",
@@ -65659,7 +66088,7 @@ function staleUpdateLockFile(cachePath, now) {
65659
66088
  for (const path2 of [`${cachePath}.lock.break`, `${cachePath}.lock`]) {
65660
66089
  let content;
65661
66090
  try {
65662
- content = readFileSync73(path2, "utf8");
66091
+ content = readFileSync74(path2, "utf8");
65663
66092
  } catch {
65664
66093
  continue;
65665
66094
  }
@@ -65682,7 +66111,7 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
65682
66111
  const mine = lockStamp(now);
65683
66112
  const readLock = (path2) => {
65684
66113
  try {
65685
- return readFileSync73(path2, "utf8");
66114
+ return readFileSync74(path2, "utf8");
65686
66115
  } catch {
65687
66116
  return void 0;
65688
66117
  }
@@ -65793,6 +66222,52 @@ function loaderArgs(execArgv) {
65793
66222
  return out;
65794
66223
  }
65795
66224
  var spawnUpdateRefresh = createUpdateRefreshSpawner();
66225
+ function createTrimValidator(deps = {}, spawnFn = spawn6) {
66226
+ const outcome = (code, report2, said) => {
66227
+ let counts;
66228
+ let errors;
66229
+ try {
66230
+ const parsed = JSON.parse(report2);
66231
+ counts = parsed.counts;
66232
+ if (Array.isArray(parsed.findings)) {
66233
+ const text5 = (v) => typeof v === "string" && v ? v : void 0;
66234
+ errors = parsed.findings.filter((f) => f && f.severity === "error" && typeof f.id === "string").map((f) => ({
66235
+ id: f.id,
66236
+ message: text5(f.message) ?? "",
66237
+ ...text5(f.pass) ? { pass: text5(f.pass) } : {},
66238
+ ...text5(f.screen) ? { screen: text5(f.screen) } : {},
66239
+ ...text5(f.path) ? { path: text5(f.path) } : {},
66240
+ ...text5(f.ruleCode) ? { ruleCode: text5(f.ruleCode) } : {}
66241
+ }));
66242
+ }
66243
+ } catch {
66244
+ }
66245
+ return { code, counts, ...errors ? { errors } : {}, ...code !== 0 ? { tail: said.slice(-4e3) } : {} };
66246
+ };
66247
+ return async (dir, opts = {}) => {
66248
+ const argv = ["validate", "--content", resolve47(dir), "--strict", "--skip-version-check"];
66249
+ if (!opts.fresh) {
66250
+ const out = [];
66251
+ const err = [];
66252
+ const code2 = await run(argv, { log: (l) => out.push(l), error: (l) => err.push(l) }, deps);
66253
+ return outcome(code2, out[out.length - 1] ?? "", [...out, ...err].join("\n"));
66254
+ }
66255
+ const { code, stdout, stderr } = await new Promise((done) => {
66256
+ const chunks = { out: [], err: [] };
66257
+ const child = spawnFn(process.execPath, [...loaderArgs(process.execArgv), fileURLToPath24(import.meta.url), ...argv], {
66258
+ cwd: process.cwd(),
66259
+ env: process.env,
66260
+ stdio: ["ignore", "pipe", "pipe"]
66261
+ });
66262
+ child.stdout?.on("data", (b) => chunks.out.push(b));
66263
+ child.stderr?.on("data", (b) => chunks.err.push(b));
66264
+ child.on("error", (e) => done({ code: 2, stdout: "", stderr: String(e) }));
66265
+ child.on("close", (c) => done({ code: c ?? 1, stdout: Buffer.concat(chunks.out).toString("utf8"), stderr: Buffer.concat(chunks.err).toString("utf8") }));
66266
+ });
66267
+ return outcome(code, stdout.trim(), `${stdout}
66268
+ ${stderr}`);
66269
+ };
66270
+ }
65796
66271
  async function maybeSelfUpdate(argv, io, opts = {}) {
65797
66272
  const env = opts.env ?? process.env;
65798
66273
  const running = opts.running ?? cliVersion();
@@ -66220,7 +66695,7 @@ function executableOnPath(name, env = process.env) {
66220
66695
  const suffixes = process.platform === "win32" ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
66221
66696
  for (const dir of entries) {
66222
66697
  for (const suffix of suffixes) {
66223
- const candidate = join58(dir, `${name}${suffix}`);
66698
+ const candidate = join59(dir, `${name}${suffix}`);
66224
66699
  try {
66225
66700
  if (!statSync17(candidate).isFile()) continue;
66226
66701
  if (process.platform !== "win32") accessSync(candidate, constants2.X_OK);
@@ -66329,7 +66804,7 @@ async function nativeConfigAdvisories(app, configSource) {
66329
66804
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
66330
66805
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
66331
66806
  const iosYmlPath = iosProjectYml2(app);
66332
- const iosYml = existsSync61(iosYmlPath) ? readFileSync73(iosYmlPath, "utf8") : "";
66807
+ const iosYml = existsSync62(iosYmlPath) ? readFileSync74(iosYmlPath, "utf8") : "";
66333
66808
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
66334
66809
  if (!optedIn) {
66335
66810
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -66343,7 +66818,7 @@ async function nativeConfigAdvisories(app, configSource) {
66343
66818
  }
66344
66819
  function readConfigSource(dir) {
66345
66820
  try {
66346
- return readFileSync73(join58(dir, "config.ts"), "utf8");
66821
+ return readFileSync74(join59(dir, "config.ts"), "utf8");
66347
66822
  } catch {
66348
66823
  return null;
66349
66824
  }
@@ -66359,7 +66834,7 @@ function keptPlan(kept) {
66359
66834
  }
66360
66835
  async function initFromRemoteTemplate(name, parentDir, template, c) {
66361
66836
  const dir = resolve47(parentDir, name);
66362
- if (existsSync61(join58(dir, "himalaya.content.json"))) {
66837
+ if (existsSync62(join59(dir, "himalaya.content.json"))) {
66363
66838
  throw new Error(`content package already exists at ${dir}`);
66364
66839
  }
66365
66840
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -66402,7 +66877,7 @@ function symlinkDestinationIssue(dest) {
66402
66877
  function restoreDestinationIssue(dest, force) {
66403
66878
  const link = symlinkDestinationIssue(dest);
66404
66879
  if (link) return link;
66405
- if (!existsSync61(dest)) return null;
66880
+ if (!existsSync62(dest)) return null;
66406
66881
  if (!statSync17(dest).isDirectory()) {
66407
66882
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
66408
66883
  }
@@ -66417,7 +66892,7 @@ function restoreDestinationIssue(dest, force) {
66417
66892
  function isHimalayaMonorepoCheckout(start) {
66418
66893
  let dir = resolve47(start);
66419
66894
  for (; ; ) {
66420
- if (existsSync61(join58(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync61(join58(dir, "core", "serve", "src", "server.ts"))) return true;
66895
+ if (existsSync62(join59(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync62(join59(dir, "core", "serve", "src", "server.ts"))) return true;
66421
66896
  const parent = dirname32(dir);
66422
66897
  if (parent === dir) return false;
66423
66898
  dir = parent;
@@ -66460,7 +66935,7 @@ function materializeForPreview(appDir2, slug, displayName) {
66460
66935
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
66461
66936
  );
66462
66937
  const repoRoot2 = resolve47(fileURLToPath24(new URL("../../..", import.meta.url)));
66463
- const root = join58(repoRoot2, "node_modules", ".cache", "himi-template-preview");
66938
+ const root = join59(repoRoot2, "node_modules", ".cache", "himi-template-preview");
66464
66939
  const stillRunning = (pid) => {
66465
66940
  try {
66466
66941
  process.kill(pid, 0);
@@ -66474,12 +66949,12 @@ function materializeForPreview(appDir2, slug, displayName) {
66474
66949
  if (!entry.isDirectory()) continue;
66475
66950
  const pid = Number(entry.name.startsWith(`${slug}-`) ? entry.name.slice(slug.length + 1) : NaN);
66476
66951
  if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && !stillRunning(pid)) {
66477
- rmSync14(join58(root, entry.name), { recursive: true, force: true });
66952
+ rmSync14(join59(root, entry.name), { recursive: true, force: true });
66478
66953
  }
66479
66954
  }
66480
66955
  } catch {
66481
66956
  }
66482
- const dest = join58(root, `${slug}-${process.pid}`);
66957
+ const dest = join59(root, `${slug}-${process.pid}`);
66483
66958
  rmSync14(dest, { recursive: true, force: true });
66484
66959
  mkdirSync30(dest, { recursive: true });
66485
66960
  materializeSource(forBuild, displayName, dest, slug);
@@ -66487,8 +66962,8 @@ function materializeForPreview(appDir2, slug, displayName) {
66487
66962
  }
66488
66963
  async function appId(dir) {
66489
66964
  try {
66490
- const { readFileSync: readFileSync74 } = await import("node:fs");
66491
- const m = JSON.parse(readFileSync74(join58(dir, "himalaya.content.json"), "utf8"));
66965
+ const { readFileSync: readFileSync75 } = await import("node:fs");
66966
+ const m = JSON.parse(readFileSync75(join59(dir, "himalaya.content.json"), "utf8"));
66492
66967
  if (m.name) return m.name;
66493
66968
  } catch {
66494
66969
  }
@@ -66496,8 +66971,8 @@ async function appId(dir) {
66496
66971
  }
66497
66972
  async function contentVisibility(dir) {
66498
66973
  try {
66499
- const { readFileSync: readFileSync74 } = await import("node:fs");
66500
- const m = JSON.parse(readFileSync74(join58(dir, "himalaya.content.json"), "utf8"));
66974
+ const { readFileSync: readFileSync75 } = await import("node:fs");
66975
+ const m = JSON.parse(readFileSync75(join59(dir, "himalaya.content.json"), "utf8"));
66501
66976
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
66502
66977
  } catch {
66503
66978
  }
@@ -66721,7 +67196,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
66721
67196
  try {
66722
67197
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
66723
67198
  const bres = await buildContentForValidation2(dir, { resolution });
66724
- out = mkdtempSync4(join58(tmpdir5(), "himi-ux-record-"));
67199
+ out = mkdtempSync4(join59(tmpdir5(), "himi-ux-record-"));
66725
67200
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
66726
67201
  const outcome = await buildReleaseFromContent2(dir, out, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {}, collect: { lint: true } });
66727
67202
  const releaseId = outcome.manifest.releaseId;
@@ -66899,7 +67374,7 @@ async function producedWidgetFamilies(dir) {
66899
67374
  let js;
66900
67375
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
66901
67376
  try {
66902
- js = readFileSync73(join58(outDir, candidate), "utf8");
67377
+ js = readFileSync74(join59(outDir, candidate), "utf8");
66903
67378
  break;
66904
67379
  } catch {
66905
67380
  }
@@ -66930,7 +67405,7 @@ async function producedWidgetFamilies(dir) {
66930
67405
  }
66931
67406
  return { families, source: "dist/bundles" };
66932
67407
  }
66933
- var boardOpen = (dir) => existsSync61(join58(dir, ".himi", "studio", "index.html"));
67408
+ var boardOpen = (dir) => existsSync62(join59(dir, ".himi", "studio", "index.html"));
66934
67409
  var BOARD_REFRESHERS = /* @__PURE__ */ new Set(["render", "test", "validate", "build", "push", "icon", "site", "ux", "progress"]);
66935
67410
  async function leaveForBoard(dir, record2, value) {
66936
67411
  if (!boardOpen(dir)) return;
@@ -67295,19 +67770,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67295
67770
  const { runTemplateTrim: runTemplateTrim2 } = await Promise.resolve().then(() => (init_template_trim(), template_trim_exports));
67296
67771
  const outcome = await runTemplateTrim2(
67297
67772
  { dir: contentDir(flags), keep: flags.keep, links: flags.links, dryRun: flags["dry-run"] === true, force: flags.force === true },
67298
- {
67299
- validate: async (dir) => {
67300
- const out = [];
67301
- const err = [];
67302
- const code = await run(["validate", "--content", dir, "--strict"], { log: (l) => out.push(l), error: (l) => err.push(l) }, deps);
67303
- let counts;
67304
- try {
67305
- counts = JSON.parse(out[out.length - 1] ?? "").counts;
67306
- } catch {
67307
- }
67308
- return { code, counts, ...code !== 0 ? { tail: [...out, ...err].join("\n").slice(-4e3) } : {} };
67309
- }
67310
- }
67773
+ { validate: createTrimValidator(deps) }
67311
67774
  );
67312
67775
  if (outcome.code === 2) io.error(JSON.stringify(outcome.result));
67313
67776
  else print(outcome.result);
@@ -67318,7 +67781,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67318
67781
  const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
67319
67782
  let meta;
67320
67783
  try {
67321
- meta = JSON.parse(readFileSync73(join58(sourceDir, TEMPLATE_META_FILE), "utf8"));
67784
+ meta = JSON.parse(readFileSync74(join59(sourceDir, TEMPLATE_META_FILE), "utf8"));
67322
67785
  } catch {
67323
67786
  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>)` }));
67324
67787
  return 2;
@@ -67328,7 +67791,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67328
67791
  return 2;
67329
67792
  }
67330
67793
  try {
67331
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync73(join58(sourceDir, "tokens.json"), "utf8")));
67794
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync74(join59(sourceDir, "tokens.json"), "utf8")));
67332
67795
  meta = { ...appearance, ...meta };
67333
67796
  } catch {
67334
67797
  }
@@ -67340,7 +67803,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67340
67803
  const force = flags.force === true;
67341
67804
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
67342
67805
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
67343
- const out = mkdtempSync4(join58(tmpdir5(), "himi-template-"));
67806
+ const out = mkdtempSync4(join59(tmpdir5(), "himi-template-"));
67344
67807
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
67345
67808
  const outcome = await buildReleaseFromContent2(buildDir, out, {
67346
67809
  ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
@@ -67393,7 +67856,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67393
67856
  let iconHash;
67394
67857
  if (iconPath) {
67395
67858
  try {
67396
- const bytes2 = readFileSync73(iconPath);
67859
+ const bytes2 = readFileSync74(iconPath);
67397
67860
  payload.icon = bytes2.toString("base64");
67398
67861
  iconHash = createHash27("sha256").update(bytes2).digest("hex");
67399
67862
  } catch {
@@ -67626,7 +68089,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67626
68089
  const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
67627
68090
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
67628
68091
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
67629
- const out = mkdtempSync4(join58(tmpdir5(), "himi-validate-"));
68092
+ const out = mkdtempSync4(join59(tmpdir5(), "himi-validate-"));
67630
68093
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
67631
68094
  const outcome = await buildReleaseFromContent2(dir, out, {
67632
68095
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -67669,7 +68132,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67669
68132
  const path2 = dpxSessionTokenPath();
67670
68133
  let raw;
67671
68134
  try {
67672
- raw = readFileSync73(path2, "utf8");
68135
+ raw = readFileSync74(path2, "utf8");
67673
68136
  } catch {
67674
68137
  throw new Error(`no DPX BO session at ${path2}; run dpx auth login, write dpx auth token to that path, then himi login --dpx --target bo`);
67675
68138
  }
@@ -67789,13 +68252,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67789
68252
  case "studio": {
67790
68253
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
67791
68254
  const dir = contentDir(flags);
67792
- if (!existsSync61(join58(dir, "himalaya.content.json"))) {
68255
+ if (!existsSync62(join59(dir, "himalaya.content.json"))) {
67793
68256
  io.error(JSON.stringify({ ok: false, error: "not_a_content_package", dir, hint: "run from the app directory (or pass --content <dir>) \u2014 the board reads that package's .himi/" }));
67794
68257
  return 2;
67795
68258
  }
67796
68259
  if (pos[1] === "reel") {
67797
68260
  const { buildReel: buildReel2 } = await Promise.resolve().then(() => (init_studio_reel(), studio_reel_exports));
67798
- const out = resolve47(str6(flags.out) ?? join58(dir, ".himi", "making-of.html"));
68261
+ const out = resolve47(str6(flags.out) ?? join59(dir, ".himi", "making-of.html"));
67799
68262
  const { html, plan } = buildReel2(dir);
67800
68263
  mkdirSync30(dirname32(out), { recursive: true });
67801
68264
  writeFileSync28(out, html);
@@ -67845,7 +68308,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67845
68308
  const { appendProgress: appendProgress2, readProgress: readProgress2, summarizeProgress: summarizeProgress2, parseStep: parseStep2, notifyDesktop: notifyDesktop2, appLabel: appLabel2, journalPath: journalPath2, ProgressError: ProgressError2, JOURNEY_STEPS: JOURNEY_STEPS2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
67846
68309
  const dir = contentDir(flags);
67847
68310
  const journal = journalPath2(dir);
67848
- const hint = existsSync61(join58(dir, "himalaya.content.json")) ? {} : { hint: "no himalaya.content.json here \u2014 run from the app directory (or pass --content <dir>) so the build board finds this journal" };
68311
+ const hint = existsSync62(join59(dir, "himalaya.content.json")) ? {} : { hint: "no himalaya.content.json here \u2014 run from the app directory (or pass --content <dir>) so the build board finds this journal" };
67849
68312
  const message = pos.slice(1).join(" ").trim();
67850
68313
  if (!message) {
67851
68314
  if (flags.step !== void 0 || flags.image !== void 0 || flags.notify === true) {
@@ -67937,7 +68400,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
67937
68400
  case "test": {
67938
68401
  const dir = contentDir(flags);
67939
68402
  const structuredOutput = flags.json === true || io.isTTY !== true;
67940
- if (existsSync61(join58(dir, "functions"))) {
68403
+ if (existsSync62(join59(dir, "functions"))) {
67941
68404
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
67942
68405
  const loaded = await loadFunctionsApp2(dir);
67943
68406
  const results = [];
@@ -68024,7 +68487,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68024
68487
  }
68025
68488
  }
68026
68489
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
68027
- const out = mkdtempSync4(join58(tmpdir5(), "himi-test-"));
68490
+ const out = mkdtempSync4(join59(tmpdir5(), "himi-test-"));
68028
68491
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68029
68492
  const outcome = await buildReleaseFromContent2(dir, out, {
68030
68493
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -68069,7 +68532,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68069
68532
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
68070
68533
  return 2;
68071
68534
  }
68072
- const sources = collectWorkerSources2(join58(dir, "tier5-src"), screen.bundleName);
68535
+ const sources = collectWorkerSources2(join59(dir, "tier5-src"), screen.bundleName);
68073
68536
  if (!sources) {
68074
68537
  io.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
68075
68538
  return 2;
@@ -68230,7 +68693,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68230
68693
  const sp = await runShotsPass2({
68231
68694
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
68232
68695
  tokens: outcome.tokens,
68233
- outDir: join58(dir, ".himi", "shots"),
68696
+ outDir: join59(dir, ".himi", "shots"),
68234
68697
  width: 390,
68235
68698
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
68236
68699
  ...budgetMs ? { budgetMs } : {},
@@ -68268,7 +68731,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68268
68731
  releaseId: result.releaseId,
68269
68732
  releasePayload: release,
68270
68733
  descriptors: attestedDescriptors,
68271
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join58(dir, ".himi", "shots", name)])),
68734
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join59(dir, ".himi", "shots", name)])),
68272
68735
  coverage: {
68273
68736
  screens: crawledScreens.size,
68274
68737
  actionsDispatched: result.actions.exercised,
@@ -68358,7 +68821,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68358
68821
  return 2;
68359
68822
  }
68360
68823
  try {
68361
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync73(stateArg.replace(/^@/, ""), "utf8");
68824
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync74(stateArg.replace(/^@/, ""), "utf8");
68362
68825
  injectedState = JSON.parse(raw);
68363
68826
  } catch (e) {
68364
68827
  io.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e.message}` }));
@@ -68373,7 +68836,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68373
68836
  return 2;
68374
68837
  }
68375
68838
  try {
68376
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync73(inputArg.replace(/^@/, ""), "utf8");
68839
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync74(inputArg.replace(/^@/, ""), "utf8");
68377
68840
  const parsed = JSON.parse(raw);
68378
68841
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
68379
68842
  injectedInput = parsed;
@@ -68422,7 +68885,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68422
68885
  }
68423
68886
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68424
68887
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
68425
- const out = mkdtempSync4(join58(tmpdir5(), "himi-render-"));
68888
+ const out = mkdtempSync4(join59(tmpdir5(), "himi-render-"));
68426
68889
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68427
68890
  const outcome = await buildReleaseFromContent2(dir, out, {
68428
68891
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -68436,7 +68899,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68436
68899
  const diagnostics = (outcome.lint ?? []).find((s) => s.screenId === screenId)?.issues ?? [];
68437
68900
  let pngFile;
68438
68901
  let attestationPngFile;
68439
- const shotPath = join58(dir, ".himi", "shots", `${screenId}.png`);
68902
+ const shotPath = join59(dir, ".himi", "shots", `${screenId}.png`);
68440
68903
  const pngTarget = typeof flags.png === "string" ? str6(flags.png) : flags.png === true ? shotPath : void 0;
68441
68904
  if (pngTarget && r.pngBase64) {
68442
68905
  pngFile = writePngFile(pngTarget, r.pngBase64);
@@ -68566,7 +69029,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68566
69029
  installIcon: installIcon2,
68567
69030
  siteBookIcon: siteBookIcon2,
68568
69031
  siteLogoAbsence: siteLogoAbsence2,
68569
- summarize: summarize3
69032
+ summarize: summarize4
68570
69033
  } = await Promise.resolve().then(() => (init_icon(), icon_exports));
68571
69034
  const requireVerified = sub === "check";
68572
69035
  const report2 = async (status, extra = {}) => {
@@ -68591,11 +69054,11 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68591
69054
  }))
68592
69055
  ];
68593
69056
  if (flags.json === true) {
68594
- print({ ok: !blocksRelease2(status, { requireVerified }), ...extra, ...status, findings, summary: summarize3(status) });
69057
+ print({ ok: !blocksRelease2(status, { requireVerified }), ...extra, ...status, findings, summary: summarize4(status) });
68595
69058
  return blocksRelease2(status, { requireVerified }) ? 1 : 0;
68596
69059
  }
68597
69060
  for (const [k, v] of Object.entries(extra)) io.log(`${k}: ${Array.isArray(v) ? v.join(", ") : String(v)}`);
68598
- io.log(`launcher icon: ${summarize3(status)}`);
69061
+ io.log(`launcher icon: ${summarize4(status)}`);
68599
69062
  if (status.path) io.log(` file: ${status.path}${status.header ? ` (${status.header.width}x${status.header.height})` : ""}`);
68600
69063
  for (const f of findings) io.log(` ${f.level === "error" ? "\u2717" : "\u26A0"} ${f.message}`);
68601
69064
  if (status.verdict === "ok" || status.verdict === "unverified") {
@@ -68772,7 +69235,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68772
69235
  io.error(JSON.stringify(linked));
68773
69236
  return 1;
68774
69237
  }
68775
- if (existsSync61(dest)) {
69238
+ if (existsSync62(dest)) {
68776
69239
  if (!statSync17(dest).isDirectory()) {
68777
69240
  io.error(JSON.stringify({
68778
69241
  error: `${dest} exists and is not a directory`,
@@ -68785,7 +69248,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68785
69248
  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" }));
68786
69249
  return 1;
68787
69250
  }
68788
- if (replace && occupied && !existsSync61(join58(dest, "himalaya.content.json"))) {
69251
+ if (replace && occupied && !existsSync62(join59(dest, "himalaya.content.json"))) {
68789
69252
  io.error(JSON.stringify({
68790
69253
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
68791
69254
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -68924,7 +69387,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
68924
69387
  const force = flags.force === true;
68925
69388
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68926
69389
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
68927
- const out = mkdtempSync4(join58(tmpdir5(), "himi-push-"));
69390
+ const out = mkdtempSync4(join59(tmpdir5(), "himi-push-"));
68928
69391
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68929
69392
  const outcome = await buildReleaseFromContent2(dir, out, {
68930
69393
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -69445,7 +69908,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69445
69908
  }
69446
69909
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
69447
69910
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
69448
- const built = mkdtempSync4(join58(tmpdir5(), "himi-pack-"));
69911
+ const built = mkdtempSync4(join59(tmpdir5(), "himi-pack-"));
69449
69912
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
69450
69913
  const outcome = await buildReleaseFromContent2(dir, built, {
69451
69914
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -69648,7 +70111,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69648
70111
  }));
69649
70112
  return 1;
69650
70113
  }
69651
- const archive = readFileSync73(resolve47(file));
70114
+ const archive = readFileSync74(resolve47(file));
69652
70115
  const result = await client2().createPackageShare({
69653
70116
  app: opened.header.app,
69654
70117
  releaseId: opened.header.releaseId,
@@ -69702,11 +70165,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69702
70165
  const app = await appId(dir2);
69703
70166
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
69704
70167
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
69705
- temp = mkdtempSync4(join58(tmpdir5(), "himi-run-build-"));
70168
+ temp = mkdtempSync4(join59(tmpdir5(), "himi-run-build-"));
69706
70169
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
69707
70170
  const initial = await buildReleaseFromContent2(dir2, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir2) } : {} });
69708
70171
  servedMissing = initial.missingBundles;
69709
- source = join58(temp, app);
70172
+ source = join59(temp, app);
69710
70173
  }
69711
70174
  const chromeArg = str6(flags.chrome) ?? "preview";
69712
70175
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -69742,7 +70205,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69742
70205
  if (!recordKit) return;
69743
70206
  startedAt = (/* @__PURE__ */ new Date()).toISOString();
69744
70207
  const failure = record2(served2, { ok: true, at: startedAt });
69745
- if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join58(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
70208
+ if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join59(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
69746
70209
  await refreshBoard();
69747
70210
  };
69748
70211
  const retract = async () => {
@@ -69773,7 +70236,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69773
70236
  }
69774
70237
  const watchKit = watch2 ? await Promise.resolve().then(() => (init_run_watch(), run_watch_exports)) : null;
69775
70238
  const bus = watchKit ? new watchKit.ReloadBus() : void 0;
69776
- if (watch2) served = mkdtempSync4(join58(tmpdir5(), "himi-run-served-"));
70239
+ if (watch2) served = mkdtempSync4(join59(tmpdir5(), "himi-run-served-"));
69777
70240
  const running = await runPackage2(source, {
69778
70241
  port,
69779
70242
  chrome: chromeArg,
@@ -69794,8 +70257,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69794
70257
  await announce(running);
69795
70258
  if (watchKit && bus) {
69796
70259
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
69797
- const live2 = join58(running.releaseRoot, running.app);
69798
- const manifestOf = (tree) => JSON.parse(readFileSync73(join58(tree, "release-manifest.json"), "utf8"));
70260
+ const live2 = join59(running.releaseRoot, running.app);
70261
+ const manifestOf = (tree) => JSON.parse(readFileSync74(join59(tree, "release-manifest.json"), "utf8"));
69799
70262
  let current = manifestOf(live2);
69800
70263
  const failed2 = async (message) => {
69801
70264
  if (stopping) return;
@@ -69806,7 +70269,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69806
70269
  };
69807
70270
  rebuild = watchKit.serialize(async () => {
69808
70271
  const t0 = Date.now();
69809
- const next = mkdtempSync4(join58(tmpdir5(), "himi-run-build-"));
70272
+ const next = mkdtempSync4(join59(tmpdir5(), "himi-run-build-"));
69810
70273
  try {
69811
70274
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
69812
70275
  if (bres.compileErrors.length) {
@@ -69823,14 +70286,14 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
69823
70286
  return;
69824
70287
  }
69825
70288
  if (stopping) return;
69826
- const fresh = manifestOf(join58(next, running.app));
70289
+ const fresh = manifestOf(join59(next, running.app));
69827
70290
  const diff = watchKit.diffReleases(current, fresh);
69828
70291
  const retired = `${live2}.retired-${Date.now()}`;
69829
70292
  renameSync6(live2, retired);
69830
70293
  try {
69831
- renameSync6(join58(next, running.app), live2);
70294
+ renameSync6(join59(next, running.app), live2);
69832
70295
  } catch {
69833
- cpSync3(join58(next, running.app), live2, { recursive: true });
70296
+ cpSync3(join59(next, running.app), live2, { recursive: true });
69834
70297
  }
69835
70298
  rmSync14(retired, { recursive: true, force: true });
69836
70299
  current = fresh;
@@ -70428,7 +70891,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70428
70891
  io.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
70429
70892
  return 2;
70430
70893
  }
70431
- if (!existsSync61(file)) {
70894
+ if (!existsSync62(file)) {
70432
70895
  io.error(JSON.stringify({ error: `no such file: ${file}` }));
70433
70896
  return 2;
70434
70897
  }
@@ -70484,7 +70947,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70484
70947
  lintNote: "`himi patch` sends a raw file. Nothing here built or linted your app \u2014 `himi push --only <selector>` is the same edit with the full lint gate in front of it."
70485
70948
  };
70486
70949
  if (kind === "tokens" || kind === "app-config") {
70487
- const body = readFileSync73(file, "utf8");
70950
+ const body = readFileSync74(file, "utf8");
70488
70951
  const patch2 = kind === "tokens" ? { tokens: body } : { appConfig: body };
70489
70952
  return runReleasePatch({
70490
70953
  ...common,
@@ -70499,7 +70962,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70499
70962
  io.error(JSON.stringify({ error: "patch screen needs a <screenId>", usage: PATCH_USAGE }));
70500
70963
  return 2;
70501
70964
  }
70502
- const descriptor = readFileSync73(file, "utf8");
70965
+ const descriptor = readFileSync74(file, "utf8");
70503
70966
  try {
70504
70967
  JSON.parse(descriptor);
70505
70968
  } catch (e) {
@@ -70539,7 +71002,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70539
71002
  extra: { kind, screen: id, file: { path: file, bytes: Buffer.byteLength(descriptor, "utf8") } }
70540
71003
  });
70541
71004
  }
70542
- const bytes2 = readFileSync73(file);
71005
+ const bytes2 = readFileSync74(file);
70543
71006
  const sha = createHash27("sha256").update(bytes2).digest("hex");
70544
71007
  const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
70545
71008
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
@@ -70683,7 +71146,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70683
71146
  }
70684
71147
  let def;
70685
71148
  try {
70686
- const parsed = JSON.parse(arg.startsWith("@") ? readFileSync73(arg.slice(1), "utf8") : arg);
71149
+ const parsed = JSON.parse(arg.startsWith("@") ? readFileSync74(arg.slice(1), "utf8") : arg);
70687
71150
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
70688
71151
  io.error(JSON.stringify({ error: "invalid_definition", problems: [`--definition must be a JSON object, got ${Array.isArray(parsed) ? "an array" : parsed === null ? "null" : typeof parsed}`] }));
70689
71152
  return 2;
@@ -71147,7 +71610,7 @@ ${label2}`);
71147
71610
  const platform = str6(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
71148
71611
  {
71149
71612
  const distDir = contentDir(flags);
71150
- if (existsSync61(join58(distDir, "himalaya.content.json"))) {
71613
+ if (existsSync62(join59(distDir, "himalaya.content.json"))) {
71151
71614
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
71152
71615
  const errors = issues.filter((i) => i.severity === "error");
71153
71616
  for (const i of issues.filter((i2) => i2.severity === "warning")) {
@@ -71416,7 +71879,7 @@ ${label2}`);
71416
71879
  const dpxPath = dpxSessionTokenPath();
71417
71880
  let dpx;
71418
71881
  try {
71419
- dpx = readFileSync73(dpxPath, "utf8").trim();
71882
+ dpx = readFileSync74(dpxPath, "utf8").trim();
71420
71883
  } catch {
71421
71884
  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` }));
71422
71885
  return 1;
@@ -71818,7 +72281,7 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
71818
72281
  const path2 = dpxSessionTokenPath();
71819
72282
  let raw;
71820
72283
  try {
71821
- raw = readFileSync73(path2, "utf8");
72284
+ raw = readFileSync74(path2, "utf8");
71822
72285
  } catch {
71823
72286
  io.error(JSON.stringify({ error: `no DPX BO session at ${path2}` }));
71824
72287
  return 1;
@@ -72207,6 +72670,7 @@ export {
72207
72670
  buildDoctorReport,
72208
72671
  compareVersions2 as compareVersions,
72209
72672
  createScopedRegistryResolver,
72673
+ createTrimValidator,
72210
72674
  createUpdateRefreshSpawner,
72211
72675
  createUpdateRunner,
72212
72676
  credentialKind,