@wix/himalaya-cli 0.1058.0 → 0.1060.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -2699,7 +2699,7 @@ function isAttestationV2(value) {
2699
2699
  const a = value;
2700
2700
  const coverage = a.coverage;
2701
2701
  const report2 = a.report;
2702
- return a.attestation === 2 && typeof a.runId === "string" && ["himi-test", "himi-render", "himi-shots"].includes(a.tool) && [a.app, a.contentDir, a.gitDiffHash, a.gitUntrackedHash, a.releaseId, a.releaseHash, a.selfHash].every((item) => typeof item === "string" && item.length > 0) && stringRecord(a.descriptorHashes) && stringRecord(a.screenshots) && !!coverage && [coverage.screens, coverage.actionsDispatched, coverage.uncoveredFns, coverage.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && typeof report2?.ok === "boolean" && !!report2.counts && [report2.counts.errors, report2.counts.warnings, report2.counts.gaps].every((count) => typeof count === "number" && Number.isFinite(count) && count >= 0) && (a.prev === null || typeof a.prev === "string");
2702
+ return a.attestation === 2 && typeof a.runId === "string" && ["himi-test", "himi-render", "himi-shots"].includes(a.tool) && [a.app, a.contentDir, a.gitDiffHash, a.gitUntrackedHash, a.releaseId, a.releaseHash, a.selfHash].every((item) => typeof item === "string" && item.length > 0) && stringRecord(a.descriptorHashes) && stringRecord(a.screenshots) && !!coverage && [coverage.screens, coverage.actionsDispatched, coverage.uncoveredFns, coverage.gaps].every((count2) => typeof count2 === "number" && Number.isFinite(count2) && count2 >= 0) && typeof report2?.ok === "boolean" && !!report2.counts && [report2.counts.errors, report2.counts.warnings, report2.counts.gaps].every((count2) => typeof count2 === "number" && Number.isFinite(count2) && count2 >= 0) && (a.prev === null || typeof a.prev === "string");
2703
2703
  }
2704
2704
  function readLatestAttestation(contentDir2) {
2705
2705
  try {
@@ -6684,7 +6684,6 @@ function resolveTranslation(value, catalog, sourceCatalog) {
6684
6684
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
6685
6685
  var init_bindings = __esm({
6686
6686
  "../../core/ts/src/bindings.ts"() {
6687
- "use strict";
6688
6687
  BINDING_SIGILS = ["service", "flag", "item", "t"];
6689
6688
  BINDING_SIGIL_RE = new RegExp(
6690
6689
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -12327,8 +12326,8 @@ function canonicalHex(value) {
12327
12326
  function tokenColorProblems(colors) {
12328
12327
  const problems = [];
12329
12328
  for (const [name, value] of Object.entries(colors ?? {})) {
12330
- const leaves = typeof value === "object" && value !== null && !Array.isArray(value) ? Object.entries(value).map(([variant, raw]) => [`${name}.${variant}`, raw]) : [[name, value]];
12331
- for (const [path2, raw] of leaves) {
12329
+ const leaves2 = typeof value === "object" && value !== null && !Array.isArray(value) ? Object.entries(value).map(([variant, raw]) => [`${name}.${variant}`, raw]) : [[name, value]];
12330
+ for (const [path2, raw] of leaves2) {
12332
12331
  if (typeof raw !== "string") {
12333
12332
  problems.push({ path: path2, value: JSON.stringify(raw) ?? String(raw) });
12334
12333
  continue;
@@ -15100,6 +15099,20 @@ async function tabRootScreens(dir) {
15100
15099
  (nav?.tabBar?.items ?? []).map((i) => i?.screenId).filter((s) => typeof s === "string" && !!s)
15101
15100
  );
15102
15101
  }
15102
+ async function navEntryScreens(dir, homeScreenId) {
15103
+ const nav = (await loadContentConfigQuietly(dir))?.navigation;
15104
+ const out = /* @__PURE__ */ new Set();
15105
+ if (homeScreenId) out.add(homeScreenId);
15106
+ const walk2 = (v, depth) => {
15107
+ if (depth > 6 || typeof v !== "object" || v === null) return;
15108
+ for (const [k, x] of Object.entries(v)) {
15109
+ if ((k === "screenId" || k === "contentScreenId") && typeof x === "string" && x) out.add(x);
15110
+ else walk2(x, depth + 1);
15111
+ }
15112
+ };
15113
+ for (const chrome of ["tabBar", "drawer", "sidebar", "topBarDefault"]) walk2(nav?.[chrome], 0);
15114
+ return out;
15115
+ }
15103
15116
  async function localDbEntities(dir) {
15104
15117
  const schema = (await loadContentConfigQuietly(dir))?.dataSchema;
15105
15118
  const names = (schema?.entities ?? []).map((e) => e?.name).filter((n) => typeof n === "string" && n.length > 0);
@@ -16835,9 +16848,9 @@ function hostFromProject(project) {
16835
16848
  const blocks = project.split(/(?=^ [A-Za-z0-9_]+:\s*$)/m);
16836
16849
  const host = blocks.find((block) => /^ type: application\s*$/m.test(block));
16837
16850
  if (!host) throw new Error("Live Activity generation requires an iOS application target");
16838
- const scalar3 = (key2, indent) => new RegExp(`^ {${indent}}${key2}:\\s*(.+)$`, "m").exec(host)?.[1]?.trim().replace(/^['"]|['"]$/g, "");
16851
+ const scalar4 = (key2, indent) => new RegExp(`^ {${indent}}${key2}:\\s*(.+)$`, "m").exec(host)?.[1]?.trim().replace(/^['"]|['"]$/g, "");
16839
16852
  const name = /^ ([A-Za-z0-9_]+):/m.exec(host)?.[1];
16840
- const bundleId = scalar3("PRODUCT_BUNDLE_IDENTIFIER", 8) ?? scalar3("bundleId", 4);
16853
+ const bundleId = scalar4("PRODUCT_BUNDLE_IDENTIFIER", 8) ?? scalar4("bundleId", 4);
16841
16854
  const scheme = /CFBundleURLSchemes:\s*\n\s*-\s*(.+)/.exec(host)?.[1]?.trim().replace(/^['"]|['"]$/g, "");
16842
16855
  if (!name || !bundleId || !scheme) throw new Error("Live Activity generation requires host name, bundleId and URL scheme");
16843
16856
  const configs = /^ configFiles:\s*\n((?: .+\n)+)/m.exec(host)?.[1]?.trimEnd().split("\n").map((line) => line.trim()) ?? [];
@@ -16846,9 +16859,9 @@ function hostFromProject(project) {
16846
16859
  bundleId,
16847
16860
  scheme,
16848
16861
  configs,
16849
- displayName: scalar3("CFBundleDisplayName", 8) ?? name,
16850
- version: scalar3("CFBundleShortVersionString", 8) ?? scalar3("MARKETING_VERSION", 8) ?? "1.0",
16851
- build: scalar3("CFBundleVersion", 8) ?? scalar3("CURRENT_PROJECT_VERSION", 8) ?? "1"
16862
+ displayName: scalar4("CFBundleDisplayName", 8) ?? name,
16863
+ version: scalar4("CFBundleShortVersionString", 8) ?? scalar4("MARKETING_VERSION", 8) ?? "1.0",
16864
+ build: scalar4("CFBundleVersion", 8) ?? scalar4("CURRENT_PROJECT_VERSION", 8) ?? "1"
16852
16865
  };
16853
16866
  }
16854
16867
  function liveActivityPlan(project, enabled, deferredActions = false) {
@@ -18942,8 +18955,8 @@ var init_store2 = __esm({
18942
18955
  `);
18943
18956
  }
18944
18957
  seedNotesIfEmpty() {
18945
- const count = this.db.prepare("SELECT COUNT(*) as c FROM notes").get();
18946
- if (count.c > 0) return;
18958
+ const count2 = this.db.prepare("SELECT COUNT(*) as c FROM notes").get();
18959
+ if (count2.c > 0) return;
18947
18960
  const seedNotes = [
18948
18961
  {
18949
18962
  id: "n_001",
@@ -18983,8 +18996,8 @@ var init_store2 = __esm({
18983
18996
  }
18984
18997
  }
18985
18998
  seedProductsIfEmpty() {
18986
- const count = this.db.prepare("SELECT COUNT(*) as c FROM products").get();
18987
- if (count.c > 0) return;
18999
+ const count2 = this.db.prepare("SELECT COUNT(*) as c FROM products").get();
19000
+ if (count2.c > 0) return;
18988
19001
  const now = Math.floor(Date.now() / 1e3);
18989
19002
  const seed2 = [
18990
19003
  { id: "p_001", sku: "MNT-CAP-01", title: "Summit cap", description: "Wool-blend beanie. Charcoal. One size, packs flat.", price_cents: 2900, currency: "USD", category: "apparel", inventory: 28, status: "active" },
@@ -20561,6 +20574,23 @@ function forEachChildComponent(node, cb) {
20561
20574
  };
20562
20575
  visit(node.body.props);
20563
20576
  }
20577
+ function visitComponents(root, cb) {
20578
+ const visit = (value) => {
20579
+ if (isNode(value)) {
20580
+ cb(value);
20581
+ visit(value.body.props);
20582
+ return;
20583
+ }
20584
+ if (Array.isArray(value)) {
20585
+ for (const entry of value) visit(entry);
20586
+ return;
20587
+ }
20588
+ if (typeof value === "object" && value !== null) {
20589
+ for (const entry of Object.values(value)) visit(entry);
20590
+ }
20591
+ };
20592
+ visit(root);
20593
+ }
20564
20594
  function assignStableIds(descriptor) {
20565
20595
  const used = /* @__PURE__ */ new Set();
20566
20596
  const uniquify = (base) => {
@@ -22812,7 +22842,6 @@ function shouldRevealError(args) {
22812
22842
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
22813
22843
  var init_validation = __esm({
22814
22844
  "../../core/ts/src/validation.ts"() {
22815
- "use strict";
22816
22845
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
22817
22846
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
22818
22847
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -26173,8 +26202,8 @@ function componentShape(value) {
26173
26202
  if (!isRecord(value) || typeof value.id !== "string" || !isRecord(value.body)) return false;
26174
26203
  return typeof value.body.package === "string" && typeof value.body.type === "string" && isRecord(value.body.props);
26175
26204
  }
26176
- function rotateRight(value, count) {
26177
- return value >>> count | value << 32 - count;
26205
+ function rotateRight(value, count2) {
26206
+ return value >>> count2 | value << 32 - count2;
26178
26207
  }
26179
26208
  function workerSubtreeSha256(raw) {
26180
26209
  const input = new TextEncoder().encode(raw);
@@ -26413,7 +26442,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
26413
26442
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
26414
26443
  var init_worker_subtree = __esm({
26415
26444
  "../../core/ts/src/worker-subtree.ts"() {
26416
- "use strict";
26417
26445
  init_worker_subtree_safe_types_generated();
26418
26446
  WORKER_SUBTREE_CEILINGS = Object.freeze({
26419
26447
  maxEncodedBytes: 65536,
@@ -27276,6 +27304,11 @@ function withExtras(name, render) {
27276
27304
  Wrapped.displayName = name;
27277
27305
  return Wrapped;
27278
27306
  }
27307
+ function sheetTabStops(root) {
27308
+ const all = Array.from(root.querySelectorAll(NATIVELY_TABBABLE)).filter((el) => el.tabIndex >= 0 && !el.disabled && !el.closest('[inert], [hidden], [aria-hidden="true"]'));
27309
+ const ordered = all.filter((el) => el.tabIndex > 0).sort((a, b) => a.tabIndex - b.tabIndex);
27310
+ return [...ordered, ...all.filter((el) => el.tabIndex === 0)];
27311
+ }
27279
27312
  function chartNumbers(raw) {
27280
27313
  if (!Array.isArray(raw)) return [];
27281
27314
  return raw.map((v) => v && typeof v === "object" && !Array.isArray(v) ? v.value : v).map(Number).filter((n) => Number.isFinite(n));
@@ -27335,14 +27368,14 @@ function dataTableCells(row, columns) {
27335
27368
  if (Array.isArray(o.cells)) return columns.map((_, i) => o.cells[i] == null ? "" : String(o.cells[i]));
27336
27369
  return columns.map((c) => c.key && o[c.key] != null ? String(o[c.key]) : "");
27337
27370
  }
27338
- function resampleWaveform(samples, count) {
27371
+ function resampleWaveform(samples, count2) {
27339
27372
  const clean2 = samples.map((n) => Number.isFinite(n) ? Math.min(Math.max(n, 0), 1) : 0);
27340
- if (count <= 0 || clean2.length === 0) return [];
27341
- if (clean2.length <= count) return clean2;
27373
+ if (count2 <= 0 || clean2.length === 0) return [];
27374
+ if (clean2.length <= count2) return clean2;
27342
27375
  const out = [];
27343
- for (let i = 0; i < count; i++) {
27344
- const lo = Math.floor(i * clean2.length / count);
27345
- const hi = Math.min(Math.max(lo + 1, Math.floor((i + 1) * clean2.length / count)), clean2.length);
27376
+ for (let i = 0; i < count2; i++) {
27377
+ const lo = Math.floor(i * clean2.length / count2);
27378
+ const hi = Math.min(Math.max(lo + 1, Math.floor((i + 1) * clean2.length / count2)), clean2.length);
27346
27379
  let sum = 0;
27347
27380
  for (let j = lo; j < hi; j++) sum += clean2[j];
27348
27381
  out.push(sum / (hi - lo));
@@ -28539,10 +28572,10 @@ function renderInner(node, ctx) {
28539
28572
  case "Agenda":
28540
28573
  return /* @__PURE__ */ jsx6(AgendaNode, { props, ctx, wrap });
28541
28574
  case "PageControl": {
28542
- const count = props.count ?? 0;
28575
+ const count2 = props.count ?? 0;
28543
28576
  const cur = props.currentIndex ?? 0;
28544
28577
  const accent = C("accentPrimary", "#116DFF");
28545
- return /* @__PURE__ */ jsx6("div", { style: { display: "inline-flex", alignItems: "center", gap: 8, ...wrap }, children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsx6("span", { style: { width: i === cur ? 22 : 9, height: 9, borderRadius: 999, background: i === cur ? accent : C("textTertiary", "#aab2bd"), transition: "width .2s" } }, i)) });
28578
+ return /* @__PURE__ */ jsx6("div", { style: { display: "inline-flex", alignItems: "center", gap: 8, ...wrap }, children: Array.from({ length: count2 }).map((_, i) => /* @__PURE__ */ jsx6("span", { style: { width: i === cur ? 22 : 9, height: 9, borderRadius: 999, background: i === cur ? accent : C("textTertiary", "#aab2bd"), transition: "width .2s" } }, i)) });
28546
28579
  }
28547
28580
  // ───────────── pickers & inputs (RFC 0033) ─────────────
28548
28581
  case "WheelPicker":
@@ -28574,45 +28607,11 @@ function renderInner(node, ctx) {
28574
28607
  return /* @__PURE__ */ jsx6(PdfViewNode, { props, ctx, wrap });
28575
28608
  case "MediaPlayer":
28576
28609
  return /* @__PURE__ */ jsx6(MediaPlayerView, { nodeId: node.id, props, ctx, wrap });
28577
- // Motion grammar (Batch 4) — Lottie/Rive vector animation. The container is
28578
- // tagged + sized here; lottie-web mounts the animation browser-side via
28579
- // ensureLottieRuntime (dynamic import). Reduced motion → static last frame.
28580
- case "Lottie": {
28581
- ensureLottieRuntime();
28582
- const lh = typeof props.height === "number" && props.height > 0 ? props.height : 160;
28583
- return /* @__PURE__ */ jsx6(
28584
- "div",
28585
- {
28586
- "data-himalaya-lottie": props.source ? String(props.source) : "",
28587
- "data-himalaya-lottie-loop": props.loop === false ? "0" : "1",
28588
- "data-himalaya-lottie-autoplay": props.autoplay === false ? "0" : "1",
28589
- "data-himalaya-lottie-speed": typeof props.speed === "number" ? String(props.speed) : "1",
28590
- "data-himalaya-lottie-reduce": ctx.reduceMotion() ? "1" : "0",
28591
- role: "img",
28592
- "aria-label": "animation",
28593
- style: { width: "100%", height: lh, display: "block", ...wrap }
28594
- }
28595
- );
28596
- }
28597
- // Motion grammar (Phase-2) — Rive. @rive-app/canvas mounts to the <canvas>
28598
- // browser-side via ensureRiveRuntime (dynamic import). Reduced motion → no autoplay.
28599
- case "Rive": {
28600
- ensureRiveRuntime();
28601
- const rh = typeof props.height === "number" && props.height > 0 ? props.height : 160;
28602
- return /* @__PURE__ */ jsx6(
28603
- "canvas",
28604
- {
28605
- "data-himalaya-rive": props.source ? String(props.source) : "",
28606
- "data-himalaya-rive-artboard": props.artboard ? String(props.artboard) : "",
28607
- "data-himalaya-rive-sm": props.stateMachine ? String(props.stateMachine) : "",
28608
- "data-himalaya-rive-autoplay": props.autoplay === false ? "0" : "1",
28609
- "data-himalaya-rive-reduce": ctx.reduceMotion() ? "1" : "0",
28610
- role: "img",
28611
- "aria-label": "animation",
28612
- style: { width: "100%", height: rh, display: "block", ...wrap }
28613
- }
28614
- );
28615
- }
28610
+ // Motion grammar (Batch 4 / Phase-2) — Lottie and Rive; see LottieNode / RiveNode.
28611
+ case "Lottie":
28612
+ return /* @__PURE__ */ jsx6(LottieNode, { props, ctx, wrap });
28613
+ case "Rive":
28614
+ return /* @__PURE__ */ jsx6(RiveNode, { props, ctx, wrap });
28616
28615
  // ───────────── forms ─────────────
28617
28616
  case "Form":
28618
28617
  return /* @__PURE__ */ jsx6(FormView, { props, ctx, wrap });
@@ -28681,9 +28680,9 @@ function renderInner(node, ctx) {
28681
28680
  case "WorkerSubtree":
28682
28681
  return /* @__PURE__ */ jsx6(WorkerSubtreeNode, { props, ctx, wrap, nodeId: node.id });
28683
28682
  case "Skeleton": {
28684
- const count = typeof props.count === "number" ? props.count : 1;
28685
- if (count <= 1) return /* @__PURE__ */ jsx6("div", { style: { width: props.width ?? "100%", height: props.height ?? 14, borderRadius: props.cornerRadius ?? 6, background: C("surfaceTertiary", "#f0f0f3"), flex: "none", ...wrap } });
28686
- return /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexDirection: "column", gap: 10, width: "100%", ...wrap }, children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsx6("div", { style: { height: props.height ?? 22, width: `${100 - i * 8}%`, borderRadius: props.cornerRadius ?? 8, background: C("surfaceTertiary", "#f0f0f3") } }, i)) });
28683
+ const count2 = typeof props.count === "number" ? props.count : 1;
28684
+ if (count2 <= 1) return /* @__PURE__ */ jsx6("div", { style: { width: props.width ?? "100%", height: props.height ?? 14, borderRadius: props.cornerRadius ?? 6, background: C("surfaceTertiary", "#f0f0f3"), flex: "none", ...wrap } });
28685
+ return /* @__PURE__ */ jsx6("div", { style: { display: "flex", flexDirection: "column", gap: 10, width: "100%", ...wrap }, children: Array.from({ length: count2 }).map((_, i) => /* @__PURE__ */ jsx6("div", { style: { height: props.height ?? 22, width: `${100 - i * 8}%`, borderRadius: props.cornerRadius ?? 8, background: C("surfaceTertiary", "#f0f0f3") } }, i)) });
28687
28686
  }
28688
28687
  // ───────────── search & filter ─────────────
28689
28688
  case "SearchField":
@@ -28762,7 +28761,7 @@ function renderInner(node, ctx) {
28762
28761
  }
28763
28762
  }
28764
28763
  }
28765
- var useIsomorphicLayoutEffect, ResponsiveGrid, SPLIT_EXPANDED_MIN_WIDTH, SPLIT_COLUMN_GAP, ResponsiveSplitView, ABSOLUTE_URL_RE, appearKeyframesInjected, pressStylesInjected, FOCUS_RING_CSS, focusStylesInjected, HLIST_CHILD_CSS, hListStylesInjected, listAnimateRuntimeInstalled, transitionKeyframesInjected, countUpRuntimeInjected, MEASURE_PREFIX, measureRuntimeInjected, revealRuntimeInjected, vectorAnimStylesInjected, scrollProgressRuntimeInjected, confettiRuntimeInjected, lottieRuntimeInjected, riveRuntimeInjected, gradientKeyframesInjected, parallaxRuntimeInjected, spinKeyframesInjected, marqueeKeyframesInjected, alignItems, zJustifyItems, hAlignItems, bodyFont, kids, rowKids, staggeredKids, asTextL10n, asBoundText, useBoundString, InterpolatedTextView, txt, selectFragmentCase, fragmentValueHash, fragmentSlotContent, FragmentSlotNode, WorkerSubtreeNode, resolveItemDeep2, resolveSwipeActions, resolveModBindings, resolvedComponentId, directServiceBindings, ReactiveDirectScalars, HiddenGate, KEYBOARD_INPUT_MODES, AUTOCAPITALIZE_VALUES, ENTER_KEY_HINTS, AUTOGROW_CEILING, EditableField, MenuNode, LoadMoreSentinel, FieldErrorNode, FormView, MediaSlotProvider, MEDIA_ITEM_BINDABLE, MediaPlayerView, VideoNode, PdfViewNode, WebViewNode, InteractivePicker, InteractiveDatePicker, InteractiveSearchField, InteractiveStepper, SYNTHETIC_TAP_ROLE, SYNTHETIC_TABINDEX, INFERRED_DECORATIVE, SELF_RENDERED_LEAF, ROLE_WHEN_NAMED, AUTHORED_ROLE, PENDING_RANGE, RANGE_ROLES, INTERACTIVE_ROLES, ALWAYS_INTERACTIVE_COMPONENT_TYPES, controlNode, ToggleNode, SliderNode, SelectionControlNode, TabsNode, SegmentedControlNode, DisclosureGroupNode, GridNode, SplitViewNode, TextNode, ImageNode, AvatarNode, IconNode, ButtonNode, OtpInputNode, ProgressBarNode, ActivityIndicatorNode, BottomSheetNode, ChipsInputNode, ChipNode, TableNode, GaugeRing, GaugeNode, RATING_METRICS, ratingMetrics, RatingNode, AvatarStripNode, CHART_STROKE_CYCLE, chartStrokeAt, chartBarCenter, ChartNode, donutSliceOpacity, DonutChartNode, LegendMarker, LegendNode, cellAlign, DataTableNode, voiceNoteState, voiceNoteDelivery, VOICE_NOTE_DEFAULT_BARS, clampBarCount, VoiceNoteNode, TimelineNode, WheelPickerNode, ColorPickerNode, FilterChipsNode, TILE_PX, MAX_MERCATOR_LAT, DEFAULT_TILE_MAX_ZOOM, CIRCLE_SEGMENTS, METRES_PER_DEG_LAT, MARKER_HIT_RADIUS, SHAPE_HIT_RADIUS, MapNode, CollapsingHeaderNode, UnsupportedOnSurface, CALENDAR_DATE_ONLY_RE, CAL_DEFAULT_MAX_DOTS, CAL_AGENDA_INITIAL_DAYS, CAL_AGENDA_PAGE_DAYS, CAL_DEFAULT_SNAP_MINUTES, CAL_UNASSIGNED_LANE_ID, MonthCalendarNode, TimeGridNode, AgendaNode, ARIA_ROLE, initialFocusScheduled, initialFocusSeen, PRESENTED_SCOPE, presentedOpeners, presentedObserver, A11yHintDescription, WebRenderer;
28764
+ var useIsomorphicLayoutEffect, ResponsiveGrid, SPLIT_EXPANDED_MIN_WIDTH, SPLIT_COLUMN_GAP, ResponsiveSplitView, ABSOLUTE_URL_RE, appearKeyframesInjected, pressStylesInjected, FOCUS_RING_CSS, focusStylesInjected, HLIST_CHILD_CSS, hListStylesInjected, listAnimateRuntimeInstalled, transitionKeyframesInjected, countUpRuntimeInjected, MEASURE_PREFIX, measureRuntimeInjected, revealRuntimeInjected, vectorAnimStylesInjected, scrollProgressRuntimeInjected, confettiRuntimeInjected, lottieRuntimeInjected, riveRuntimeInjected, gradientKeyframesInjected, parallaxRuntimeInjected, spinKeyframesInjected, marqueeKeyframesInjected, alignItems, zJustifyItems, hAlignItems, bodyFont, kids, rowKids, staggeredKids, asTextL10n, asBoundText, useBoundString, InterpolatedTextView, txt, selectFragmentCase, fragmentValueHash, fragmentSlotContent, FragmentSlotNode, WorkerSubtreeNode, resolveItemDeep2, resolveSwipeActions, resolveModBindings, resolvedComponentId, directServiceBindings, ReactiveDirectScalars, HiddenGate, KEYBOARD_INPUT_MODES, AUTOCAPITALIZE_VALUES, ENTER_KEY_HINTS, AUTOGROW_CEILING, EditableField, MenuNode, LoadMoreSentinel, FieldErrorNode, FormView, MediaSlotProvider, MEDIA_ITEM_BINDABLE, MediaPlayerView, VideoNode, PdfViewNode, WebViewNode, InteractivePicker, InteractiveDatePicker, InteractiveSearchField, InteractiveStepper, SYNTHETIC_TAP_ROLE, SYNTHETIC_TABINDEX, INFERRED_DECORATIVE, SELF_RENDERED_LEAF, ROLE_WHEN_NAMED, AUTHORED_ROLE, PENDING_RANGE, RANGE_ROLES, INTERACTIVE_ROLES, ALWAYS_INTERACTIVE_COMPONENT_TYPES, controlNode, ToggleNode, SliderNode, SelectionControlNode, TabsNode, SegmentedControlNode, DisclosureGroupNode, GridNode, SplitViewNode, TextNode, ImageNode, ANIMATION_LEAF, LottieNode, RiveNode, AvatarNode, IconNode, ButtonNode, OtpInputNode, ProgressBarNode, ActivityIndicatorNode, NATIVELY_TABBABLE, BottomSheetNode, ChipsInputNode, ChipNode, TableNode, GaugeRing, GaugeNode, RATING_METRICS, ratingMetrics, RatingNode, AvatarStripNode, CHART_STROKE_CYCLE, chartStrokeAt, chartBarCenter, ChartNode, donutSliceOpacity, DonutChartNode, LegendMarker, LegendNode, cellAlign, DataTableNode, voiceNoteState, voiceNoteDelivery, VOICE_NOTE_DEFAULT_BARS, clampBarCount, VoiceNoteNode, TimelineNode, WheelPickerNode, ColorPickerNode, FilterChipsNode, TILE_PX, MAX_MERCATOR_LAT, DEFAULT_TILE_MAX_ZOOM, CIRCLE_SEGMENTS, METRES_PER_DEG_LAT, MARKER_HIT_RADIUS, SHAPE_HIT_RADIUS, MapNode, CollapsingHeaderNode, UnsupportedOnSurface, CALENDAR_DATE_ONLY_RE, CAL_DEFAULT_MAX_DOTS, CAL_AGENDA_INITIAL_DAYS, CAL_AGENDA_PAGE_DAYS, CAL_DEFAULT_SNAP_MINUTES, CAL_UNASSIGNED_LANE_ID, MonthCalendarNode, TimeGridNode, AgendaNode, ARIA_ROLE, initialFocusScheduled, initialFocusSeen, PRESENTED_SCOPE, presentedOpeners, presentedObserver, A11yHintDescription, WebRenderer;
28766
28765
  var init_renderNode = __esm({
28767
28766
  "../../core/web-renderer/src/renderNode.tsx"() {
28768
28767
  "use strict";
@@ -30524,6 +30523,39 @@ var init_renderNode = __esm({
30524
30523
  }
30525
30524
  return /* @__PURE__ */ jsx6("div", { style: { width: w, height: boxHeight, aspectRatio: ar, borderRadius: r, background: ph ?? (hasSource ? `linear-gradient(135deg, ${C("surfaceTertiary", "#e6e6e6")}, ${C("surfaceSecondary", "#f2f2f2")})` : "transparent"), color: C("textSecondary", "#999"), display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "ui-monospace, monospace", fontSize: 11, flex: "none", ...wrap }, children: banner || !hasSource ? "" : "\u2022" });
30526
30525
  });
30526
+ ANIMATION_LEAF = { "aria-hidden": "true", [INFERRED_DECORATIVE]: true, [SELF_RENDERED_LEAF]: true, [ROLE_WHEN_NAMED]: "img" };
30527
+ LottieNode = withExtras("LottieNode", ({ props, ctx, wrap }) => {
30528
+ ensureLottieRuntime();
30529
+ const lh = typeof props.height === "number" && props.height > 0 ? props.height : 160;
30530
+ return /* @__PURE__ */ jsx6(
30531
+ "div",
30532
+ {
30533
+ "data-himalaya-lottie": props.source ? String(props.source) : "",
30534
+ "data-himalaya-lottie-loop": props.loop === false ? "0" : "1",
30535
+ "data-himalaya-lottie-autoplay": props.autoplay === false ? "0" : "1",
30536
+ "data-himalaya-lottie-speed": typeof props.speed === "number" ? String(props.speed) : "1",
30537
+ "data-himalaya-lottie-reduce": ctx.reduceMotion() ? "1" : "0",
30538
+ ...ANIMATION_LEAF,
30539
+ style: { width: "100%", height: lh, display: "block", ...wrap }
30540
+ }
30541
+ );
30542
+ });
30543
+ RiveNode = withExtras("RiveNode", ({ props, ctx, wrap }) => {
30544
+ ensureRiveRuntime();
30545
+ const rh = typeof props.height === "number" && props.height > 0 ? props.height : 160;
30546
+ return /* @__PURE__ */ jsx6(
30547
+ "canvas",
30548
+ {
30549
+ "data-himalaya-rive": props.source ? String(props.source) : "",
30550
+ "data-himalaya-rive-artboard": props.artboard ? String(props.artboard) : "",
30551
+ "data-himalaya-rive-sm": props.stateMachine ? String(props.stateMachine) : "",
30552
+ "data-himalaya-rive-autoplay": props.autoplay === false ? "0" : "1",
30553
+ "data-himalaya-rive-reduce": ctx.reduceMotion() ? "1" : "0",
30554
+ ...ANIMATION_LEAF,
30555
+ style: { width: "100%", height: rh, display: "block", ...wrap }
30556
+ }
30557
+ );
30558
+ });
30527
30559
  AvatarNode = withExtras("AvatarNode", ({ props, ctx, wrap }) => {
30528
30560
  const C = ctx.color;
30529
30561
  const boundUrl = useResolvedScalar(isBinding(props.imageUrl) ? props.imageUrl : void 0, ctx);
@@ -30703,16 +30735,53 @@ var init_renderNode = __esm({
30703
30735
  ensureSpinKeyframes();
30704
30736
  return /* @__PURE__ */ jsx6("div", { role: "progressbar", style: { width: size, height: size, borderRadius: "50%", border: `${thick}px solid ${track}`, borderTopColor: tint, boxSizing: "border-box", flex: "none", animation: ctx.empty ? void 0 : "himalaya-spin 0.8s linear infinite", ...wrap } });
30705
30737
  });
30738
+ NATIVELY_TABBABLE = 'a[href], area[href], button, input:not([type="hidden"]), select, textarea, iframe, object, embed, audio[controls], video[controls], summary, [contenteditable]:not([contenteditable="false"]), [tabindex]';
30706
30739
  BottomSheetNode = withExtras("BottomSheetNode", ({ props, ctx, wrap }) => {
30707
30740
  const sheetTitle = useBoundString(props.title, ctx);
30708
30741
  const C = ctx.color;
30709
30742
  const bound = useResolvedScalar(props.presentedPath, ctx);
30710
30743
  const gated = props.presentedPath != null || props.presented != null;
30711
30744
  const presented = props.presentedPath != null ? bound : props.presented;
30712
- if (gated && presented !== true && presented !== "true") return null;
30745
+ const shown2 = !gated || presented === true || presented === "true";
30746
+ const modal = ariaModal(props.modal, true) === "true";
30747
+ const dialogRef = React5.useRef(null);
30748
+ useIsomorphicLayoutEffect(() => {
30749
+ if (!shown2 || !modal) return;
30750
+ const dialog = dialogRef.current;
30751
+ const active = dialog?.ownerDocument.activeElement;
30752
+ if (dialog && active instanceof HTMLElement && active !== dialog.ownerDocument.body && !dialog.contains(active)) {
30753
+ restoreFocusOnDismiss(dialog, active);
30754
+ }
30755
+ const layer = pushModalLayer(() => dialogRef.current);
30756
+ const enter = setTimeout(() => {
30757
+ const d = dialogRef.current;
30758
+ if (!d || d.contains(d.ownerDocument.activeElement)) return;
30759
+ if (d.querySelector("[data-himalaya-initial-focus]")) return;
30760
+ d.focus();
30761
+ }, 0);
30762
+ return () => {
30763
+ clearTimeout(enter);
30764
+ layer.release();
30765
+ };
30766
+ }, [shown2, modal]);
30767
+ const trapTab = (e) => {
30768
+ if (!modal || e.key !== "Tab" || e.defaultPrevented || e.nativeEvent.defaultPrevented) return;
30769
+ const d = dialogRef.current;
30770
+ if (!d) return;
30771
+ e.preventDefault();
30772
+ const stops = sheetTabStops(d);
30773
+ if (!stops.length) {
30774
+ d.focus();
30775
+ return;
30776
+ }
30777
+ const at = stops.indexOf(d.ownerDocument.activeElement);
30778
+ const next = e.shiftKey ? at <= 0 ? stops.length - 1 : at - 1 : at === -1 || at === stops.length - 1 ? 0 : at + 1;
30779
+ stops[next].focus();
30780
+ };
30781
+ if (!shown2) return null;
30713
30782
  const grabber = props.showGrabber !== false;
30714
30783
  const sheet = idiomControls(ctx.idiom).sheet;
30715
- return /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,.35)", zIndex: 50, display: "flex", flexDirection: "column", justifyContent: "flex-end", ...wrap }, children: /* @__PURE__ */ jsxs3("div", { role: "dialog", "aria-modal": ariaModal(props.modal, true), "aria-label": props.title != null ? sheetTitle : "Sheet", style: { width: "100%", background: C("surfacePrimary", "#fff"), borderRadius: `${sheet.cornerRadius}px ${sheet.cornerRadius}px 0 0`, boxShadow: "0 -4px 24px rgba(0,0,0,.12)", overflow: "hidden" }, children: [
30784
+ return /* @__PURE__ */ jsx6("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,.35)", zIndex: 50, display: "flex", flexDirection: "column", justifyContent: "flex-end", ...wrap }, children: /* @__PURE__ */ jsxs3("div", { ref: dialogRef, role: "dialog", "aria-modal": modal ? "true" : void 0, tabIndex: modal ? -1 : void 0, onKeyDown: modal ? trapTab : void 0, "aria-label": props.title != null ? sheetTitle : "Sheet", style: { width: "100%", background: C("surfacePrimary", "#fff"), borderRadius: `${sheet.cornerRadius}px ${sheet.cornerRadius}px 0 0`, boxShadow: "0 -4px 24px rgba(0,0,0,.12)", overflow: "hidden" }, children: [
30716
30785
  grabber && /* @__PURE__ */ jsx6("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0 4px" }, children: /* @__PURE__ */ jsx6("div", { style: { width: sheet.grabberWidth, height: sheet.grabberHeight, borderRadius: 999, background: C("surfaceTertiary", "#d8dce2") } }) }),
30717
30786
  /* @__PURE__ */ jsx6("div", { style: { padding: "8px 16px 16px" }, children: props.content ? renderNode(props.content, ctx) : null })
30718
30787
  ] }) });
@@ -30841,8 +30910,8 @@ var init_renderNode = __esm({
30841
30910
  const overflow = props.overflowCount || Math.max(0, avatars.length - maxVisible);
30842
30911
  const shift = Math.round(size * (1 - (props.overlapRatio ?? 0.67)));
30843
30912
  const caption = useBoundString(props.caption, ctx).trim();
30844
- const count = shown2.length + overflow;
30845
- const countText = count > 0 ? `${count} ${count === 1 ? "person" : "people"}` : "";
30913
+ const count2 = shown2.length + overflow;
30914
+ const countText = count2 > 0 ? `${count2} ${count2 === 1 ? "person" : "people"}` : "";
30846
30915
  const groupName = [caption, countText].filter(Boolean).join(", ");
30847
30916
  return /* @__PURE__ */ jsxs3("div", { role: "group", "aria-label": groupName || void 0, "data-a11y-value": countText || void 0, ...{ [SELF_RENDERED_LEAF]: true }, style: { width: "100%", ...wrap }, children: [
30848
30917
  /* @__PURE__ */ jsxs3("div", { style: { display: "flex" }, children: [
@@ -31157,7 +31226,7 @@ var init_renderNode = __esm({
31157
31226
  const W = 100, H = 100;
31158
31227
  const slot = bars.length > 0 ? W / bars.length : W;
31159
31228
  const barW = Math.max(0.6, slot * 0.58);
31160
- const cut = progress * W;
31229
+ const cut2 = progress * W;
31161
31230
  const rates = Array.isArray(props.rates) ? props.rates.map(Number).filter((n) => n > 0) : [];
31162
31231
  const [pickedRate, setPickedRate] = React5.useState(null);
31163
31232
  React5.useEffect(() => {
@@ -31268,7 +31337,7 @@ var init_renderNode = __esm({
31268
31337
  height: bh,
31269
31338
  rx: barW / 2,
31270
31339
  fill: accent,
31271
- opacity: cx <= cut ? isPlayable ? 1 : 0.45 : isPlayable ? 0.28 : 0.16
31340
+ opacity: cx <= cut2 ? isPlayable ? 1 : 0.45 : isPlayable ? 0.28 : 0.16
31272
31341
  },
31273
31342
  i
31274
31343
  );
@@ -36184,7 +36253,7 @@ async function handleWebWix(req, res, state) {
36184
36253
  const session = webWixSession(req, res, state);
36185
36254
  const auth = resolveWebWixAuth(state.app);
36186
36255
  const path2 = new URL(req.url ?? "", "http://localhost").pathname;
36187
- const readJson5 = async () => {
36256
+ const readJson6 = async () => {
36188
36257
  const chunks = [];
36189
36258
  for await (const c of req) chunks.push(c);
36190
36259
  const t = Buffer.concat(chunks).toString("utf8");
@@ -36201,7 +36270,7 @@ async function handleWebWix(req, res, state) {
36201
36270
  res.end(JSON.stringify(obj3));
36202
36271
  };
36203
36272
  if (path2 === "/v1/__webwix/login" && req.method === "POST") {
36204
- const b = await readJson5();
36273
+ const b = await readJson6();
36205
36274
  try {
36206
36275
  const { memberToken, visitorToken } = await memberDirectLogin(
36207
36276
  auth.clientId,
@@ -36218,7 +36287,7 @@ async function handleWebWix(req, res, state) {
36218
36287
  }
36219
36288
  }
36220
36289
  if (path2 === "/v1/__webwix/call" && req.method === "POST") {
36221
- const b = await readJson5();
36290
+ const b = await readJson6();
36222
36291
  const ownerRung = b.ownerMsid !== void 0 || b.checkOwnerSession === true;
36223
36292
  const msid = String(b.ownerMsid ?? "");
36224
36293
  const cookie = req.headers.cookie ?? "";
@@ -36366,7 +36435,7 @@ async function handleWebWix(req, res, state) {
36366
36435
  });
36367
36436
  }
36368
36437
  if (path2 === "/v1/__webwix/register" && req.method === "POST") {
36369
- const b = await readJson5();
36438
+ const b = await readJson6();
36370
36439
  try {
36371
36440
  const { memberToken, visitorToken } = await memberDirectRegister(
36372
36441
  auth.clientId,
@@ -39365,10 +39434,10 @@ function effectSince(w, mark, action, componentId) {
39365
39434
  }
39366
39435
  function resolveSiteRows(itemsPath, valueOf) {
39367
39436
  if (!itemsPath || itemsPath === UNTRACKED_ITEMS) return void 0;
39368
- const cut = itemsPath.indexOf(NESTED_ITEMS_SEP);
39369
- if (cut < 0) return valueOf(itemsPath);
39370
- const outer = valueOf(itemsPath.slice(0, cut));
39371
- const field = itemsPath.slice(cut + NESTED_ITEMS_SEP.length);
39437
+ const cut2 = itemsPath.indexOf(NESTED_ITEMS_SEP);
39438
+ if (cut2 < 0) return valueOf(itemsPath);
39439
+ const outer = valueOf(itemsPath.slice(0, cut2));
39440
+ const field = itemsPath.slice(cut2 + NESTED_ITEMS_SEP.length);
39372
39441
  const first = Array.isArray(outer) ? outer[0] : void 0;
39373
39442
  return first && typeof first === "object" ? first[field] : void 0;
39374
39443
  }
@@ -39523,6 +39592,7 @@ async function crawlScreenInner(opts) {
39523
39592
  const findings = [];
39524
39593
  const actionsSkipped = [];
39525
39594
  const navEdges = [];
39595
+ const navUnfollowed = /* @__PURE__ */ new Set();
39526
39596
  const unobservedActions = [];
39527
39597
  const effects = [];
39528
39598
  let observations;
@@ -39545,7 +39615,10 @@ async function crawlScreenInner(opts) {
39545
39615
  return pathValue(row0, path2);
39546
39616
  };
39547
39617
  const target = resolveItem(merged.screenId);
39548
- if (typeof target !== "string" || !target || target.includes("$")) continue;
39618
+ if (typeof target !== "string" || !target || target.includes("$")) {
39619
+ navUnfollowed.add(`tap:${site.componentId ?? JSON.stringify(site.params ?? {})}`);
39620
+ continue;
39621
+ }
39549
39622
  const params = {};
39550
39623
  for (const [k, v] of Object.entries(merged)) {
39551
39624
  if (k === "screenId" || k === "presentation") continue;
@@ -39558,7 +39631,10 @@ async function crawlScreenInner(opts) {
39558
39631
  };
39559
39632
  const drainWorkerEdges = (w, via) => {
39560
39633
  for (const d of w.navDirectives.splice(0)) {
39561
- if (!d.screenId || d.screenId.includes("$")) continue;
39634
+ if (!d.screenId || d.screenId.includes("$")) {
39635
+ navUnfollowed.add(`${via}:${d.screenId ?? ""}`);
39636
+ continue;
39637
+ }
39562
39638
  const params = {};
39563
39639
  for (const [k, v] of Object.entries(d.args)) if (!v.includes("$")) params[k] = v;
39564
39640
  navEdges.push({ target: d.screenId, params, via });
@@ -39939,7 +40015,7 @@ async function crawlScreenInner(opts) {
39939
40015
  const edges = dedupeEdges(navEdges);
39940
40016
  if (linkableScreenIds?.size && !opts.capabilitySuccess) {
39941
40017
  const linkable = linkableScreenIds;
39942
- const resolvesToLinkable = (target) => {
40018
+ const resolvesToLinkable2 = (target) => {
39943
40019
  if (linkable.has(target)) return true;
39944
40020
  for (const base of linkable) {
39945
40021
  if (target.length > base.length + 1 && target.startsWith(base)) {
@@ -39953,7 +40029,7 @@ async function crawlScreenInner(opts) {
39953
40029
  for (const e of edges) {
39954
40030
  if (e.target === screenId) continue;
39955
40031
  if (reported.has(e.target)) continue;
39956
- if (knownScreenIds.has(e.target) || resolvesToLinkable(e.target)) continue;
40032
+ if (knownScreenIds.has(e.target) || resolvesToLinkable2(e.target)) continue;
39957
40033
  reported.add(e.target);
39958
40034
  findings.push({
39959
40035
  screen: screenId,
@@ -39974,6 +40050,7 @@ async function crawlScreenInner(opts) {
39974
40050
  actionsExercised: exercised,
39975
40051
  actionsSkipped,
39976
40052
  navEdges: edges,
40053
+ ...navUnfollowed.size ? { navUnfollowed: navUnfollowed.size } : {},
39977
40054
  ...effects.length ? { effects } : {},
39978
40055
  ...observations ? { observations } : {},
39979
40056
  ...unobservedActions.length ? { unobservedActions } : {},
@@ -42029,6 +42106,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
42029
42106
  ...collect2?.lint ? { lint, workers } : {},
42030
42107
  ...collect2?.render ? { renders } : {},
42031
42108
  ...collect2?.descriptors ? { descriptors: baked, linkableScreenIds: [...linkable] } : {},
42109
+ ...collect2?.descriptors && typeof preloaded.meta.homeScreenId === "string" && preloaded.meta.homeScreenId ? { homeScreenId: preloaded.meta.homeScreenId } : {},
42032
42110
  ...renderTokens !== void 0 ? { tokens: renderTokens } : {},
42033
42111
  ...preloaded.meta.surfaces ? { surfaceNav: preloaded.meta.surfaces } : {},
42034
42112
  auth: (() => {
@@ -42294,6 +42372,27 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
42294
42372
  .lant{font-weight:600}
42295
42373
  .lanu{font-size:12px;word-break:break-all}
42296
42374
  .lanh{color:var(--muted);font-size:12px;margin-top:4px}
42375
+ .mbox{overflow-x:auto}
42376
+ .msum{color:var(--muted);font-size:12px;margin-bottom:8px}
42377
+ .mnode rect{fill:var(--card);stroke:var(--line);stroke-width:1.2}
42378
+ .mnode text{font-size:11px;fill:var(--ink)}
42379
+ .mnode.ok rect{fill:var(--okbg);stroke:var(--ok)}
42380
+ .mnode.failed rect{fill:var(--badbg);stroke:var(--bad)}
42381
+ .mnode.pending rect{stroke-dasharray:4 3}
42382
+ .mnode.skipped text{fill:var(--muted)}
42383
+ .mnode.entry rect{stroke-width:2.4}
42384
+ .mnode.unreached rect{fill:var(--warnbg);stroke:var(--warn);stroke-dasharray:4 3}
42385
+ .medge{fill:none;stroke:var(--faint);stroke-width:1.2}
42386
+ .mbroken{font-size:11px;fill:var(--bad)}
42387
+ .mrow{font-size:11px;fill:var(--warn)}
42388
+ .mfollow{font-size:9px;fill:var(--warn)}
42389
+ .mnode.unfollowed rect{stroke:var(--warn)}
42390
+ .changes{margin-top:12px;padding:10px 12px;border:1px solid var(--line);border-radius:12px;font-size:12px}
42391
+ .changes .cht{font-weight:600;margin-bottom:4px}
42392
+ .changes ul{margin:0;padding-left:16px}
42393
+ .changes li{margin:2px 0;word-break:break-word}
42394
+ .changes .sw{display:inline-block;width:10px;height:10px;border-radius:3px;margin-left:6px;vertical-align:middle;border:1px solid var(--line)}
42395
+ .changes .chm{color:var(--muted);margin-top:2px}
42297
42396
  .film{display:flex;gap:6px;overflow-x:auto;margin-top:12px;padding-bottom:4px}.film img{width:48px;aspect-ratio:390/844;object-fit:cover;object-position:top;border-radius:8px;border:1px solid var(--line);cursor:zoom-in}
42298
42397
  .chips{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px}.chip{font-size:12px;padding:5px 10px;border-radius:999px;background:var(--card);border:1px solid var(--line);color:var(--muted)}
42299
42398
  .chip.ok{background:var(--okbg);color:var(--ok);border-color:transparent}.chip.warn{background:var(--warnbg);color:var(--warn);border-color:transparent}.chip.bad{background:var(--badbg);color:var(--bad);border-color:transparent}
@@ -42351,6 +42450,7 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
42351
42450
  <div class="card section"><h2>Screens</h2><div class="wall" id="wall"></div></div>
42352
42451
  <div class="card section" id="brandcard"><h2>Brand</h2><div class="brand" id="brand"></div></div>
42353
42452
  <div class="card section" id="navcard"><h2>Navigation the test crawl followed</h2><div class="nav" id="nav"></div></div>
42453
+ <div class="card section" id="appmap" hidden><h2>App map</h2><div class="msum" id="msum"></div><div class="mbox" id="mapbox"></div></div>
42354
42454
  <div class="card section"><h2>Postcards</h2><ul class="log" id="log"></ul></div>
42355
42455
  </div></div></div>
42356
42456
  <div id="lb"><img alt=""></div>
@@ -42362,6 +42462,48 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
42362
42462
  var cur=null, lastSeen=Date.now();
42363
42463
  function $(id){return document.getElementById(id)}
42364
42464
  function el(tag,cls,txt){var e=document.createElement(tag);if(cls)e.className=cls;if(txt!=null)e.textContent=txt;return e}
42465
+ /* #3342 \u2014 the app map. Layers are the distance from an entry (tab roots, or the first screen), the
42466
+ screens nothing leads to sit in their own last row, a link to a screen that does not exist is a red
42467
+ stub naming it. Built with DOM calls: a screen id is SVG text, never markup. Past 60 screens only
42468
+ the ones worth looking at are drawn (unreached, failed) and the summary carries the rest. */
42469
+ function drawMap(m){var card=$("appmap"),box=$("mapbox");clear(box);if(!m){card.hidden=true;return}card.hidden=false;
42470
+ var un=m.screens.filter(function(x){return !x.reached});
42471
+ /* "Nothing leads here" only when the crawl saw every link: a link built from list data (a feed's rows,
42472
+ a home grid) has a target it cannot resolve, and one of those may well open the screen. */
42473
+ var gone=!m.done?" not reached yet \xB7 ":m.unfollowed?" not seen linked \xB7 ":" unreached \xB7 ";
42474
+ $("msum").textContent=m.screens.length+" screens \xB7 "+un.length+gone+m.broken.length+" broken link"+(m.broken.length===1?"":"s")+(m.unfollowed?" \xB7 "+m.unfollowed+" link"+(m.unfollowed===1?"":"s")+" the crawl could not follow":"")+(m.edgesDropped?" \xB7 "+m.edgesDropped+" more link"+(m.edgesDropped===1?"":"s")+" not drawn":"")+(m.done?"":" \xB7 crawling");
42475
+ var show=m.screens.length>60?m.screens.filter(function(x){return !x.reached||x.status==="failed"}):m.screens,on={};show.forEach(function(x){on[x.id]=1});
42476
+ var out={},depth={},q=[];m.edges.forEach(function(e){if(on[e.from]&&on[e.to])(out[e.from]=out[e.from]||[]).push(e.to)});
42477
+ show.forEach(function(x){if(x.entry){depth[x.id]=0;q.push(x.id)}});
42478
+ while(q.length){var c=q.shift();(out[c]||[]).forEach(function(t){if(depth[t]==null){depth[t]=depth[c]+1;q.push(t)}})}
42479
+ var maxd=0;show.forEach(function(x){if(depth[x.id]>maxd)maxd=depth[x.id]});
42480
+ var rows=[];show.forEach(function(x){var r=!x.reached?maxd+2:depth[x.id]!=null?depth[x.id]:maxd+1;(rows[r]=rows[r]||[]).push(x)});
42481
+ /* Nodes are as wide as the longest id drawn needs (up to 300px): an id cut to its first letters
42482
+ cannot be told from its template siblings, which differ only at the end. Past that, the middle
42483
+ goes and both ends stay; the full id is always in the node's title. */
42484
+ var CW=6.5,longest=0;show.forEach(function(x){longest=Math.max(longest,x.id.length)});
42485
+ var W=Math.round(Math.min(300,Math.max(124,longest*CW+20))),H=28,HG=12,VG=36,P=10,ns="http://www.w3.org/2000/svg",pos={},y=P,width=0,unRow=maxd+2;
42486
+ function lab(id){var k=Math.floor((W-20)/CW);if(id.length<=k)return id;var a=Math.ceil((k-1)/2),b=Math.floor((k-1)/2);return id.slice(0,a)+"\u2026"+id.slice(id.length-b)}
42487
+ var svg=document.createElementNS(ns,"svg"),edgesG=document.createElementNS(ns,"g"),nodesG=document.createElementNS(ns,"g");svg.appendChild(edgesG);svg.appendChild(nodesG);
42488
+ function sv(tag,attrs,txt){var e=document.createElementNS(ns,tag);for(var k in attrs)e.setAttribute(k,String(attrs[k]));if(txt!=null)e.textContent=txt;return e}
42489
+ for(var r=0;r<rows.length;r++){var row=rows[r];if(!row)continue;
42490
+ if(r===unRow){nodesG.appendChild(sv("text",{x:P,y:y+10,"class":"mrow"},!m.done?"Not reached yet \u2014 no link to these seen so far":m.unfollowed?"Not seen linked \u2014 "+m.unfollowed+" link"+(m.unfollowed===1?"":"s")+" built from list data could not be followed":"Unreached \u2014 nothing leads here"));y+=16}
42491
+ row.forEach(function(x,i){var nx=P+i*(W+HG);pos[x.id]={x:nx,y:y};width=Math.max(width,nx+W+P);
42492
+ var g=sv("g",{"class":"mnode "+x.status+(x.entry?" entry":"")+(x.reached?"":" unreached")+(x.unfollowed?" unfollowed":""),"data-id":x.id});
42493
+ g.appendChild(sv("rect",{x:nx,y:y,width:W,height:H,rx:7}));g.appendChild(sv("text",{x:nx+W/2,y:y+H/2+4,"text-anchor":"middle"},lab(x.id)));
42494
+ g.appendChild(sv("title",{},x.id+" \xB7 "+x.status+(x.entry?" \xB7 entry":"")+(x.reached?"":" \xB7 unreached")+(x.unfollowed?" \xB7 "+x.unfollowed+" links the crawl could not follow":"")));
42495
+ if(x.unfollowed)g.appendChild(sv("text",{x:nx+W-6,y:y+10,"text-anchor":"end","class":"mfollow"},"+"+x.unfollowed+"?"));nodesG.appendChild(g)});
42496
+ y+=H+VG}
42497
+ m.edges.forEach(function(e){var a=pos[e.from],b=pos[e.to];if(!a||!b)return;var x1=a.x+W/2,y1=a.y+H,x2=b.x+W/2,y2=b.y,my=(y1+y2)/2;
42498
+ edgesG.appendChild(sv("path",{"class":"medge",d:"M"+x1+" "+y1+" C"+x1+" "+my+" "+x2+" "+my+" "+x2+" "+y2}))});
42499
+ var by=y;m.broken.forEach(function(e,i){var a=pos[e.from];if(!a)return;var bx=a.x+W+4,yy=a.y+H/2+4+i%2*10;
42500
+ nodesG.appendChild(sv("text",{x:bx,y:yy,"class":"mbroken"},"\u2192 missing: "+e.to));width=Math.max(width,bx+170)});
42501
+ svg.setAttribute("width",String(Math.max(width,200)));svg.setAttribute("height",String(Math.max(by,40)));svg.setAttribute("role","img");svg.setAttribute("aria-label","App map: "+$("msum").textContent);box.appendChild(svg)}
42502
+ /* #3342 \u2014 what the last rebuild changed, one line each, already text (studio.ts formats it); a hex
42503
+ token gets old/new swatches, painted through style, never markup. */
42504
+ function changesCard(b){var d=el("div","changes");d.appendChild(el("div","cht","What changed \xB7 "+hhmm(b.at)));var ul=document.createElement("ul");
42505
+ b.changes.lines.forEach(function(l){var li=el("li",null,l.text);if(l.from){var a=el("span","sw");a.style.background=l.from;li.appendChild(a)}if(l.to){var z=el("span","sw");z.style.background=l.to;li.appendChild(z)}ul.appendChild(li)});
42506
+ d.appendChild(ul);if(b.changes.more)d.appendChild(el("div","chm","+"+b.changes.more+" more"));return d}
42365
42507
  /* #3250 \u2014 a --lan run's way onto the owner's phone. The QR arrives as rows of 1/0 modules and is drawn
42366
42508
  as ONE path of unit squares: built with DOM calls, so nothing in the URL can become markup. */
42367
42509
  function lanCard(lan){var d=el("div","lan");d.appendChild(el("div","lant","Open on your phone"));
@@ -42397,7 +42539,7 @@ function render(s){
42397
42539
  if(pip){paintStrip(s)}else{
42398
42540
  var kept=s.live?focus.querySelector("iframe"):null;if(kept&&(kept.getAttribute("src")!==s.live.url||kept.getAttribute("data-run")!==s.live.runId))kept=null;
42399
42541
  if(kept){while(focus.lastChild&&focus.lastChild!==kept.parentNode)focus.removeChild(focus.lastChild)}else clear(focus);
42400
- if(s.live){$("focuslbl").textContent="Live preview";if(!kept){var f=el("div","phone vpf");var fr=document.createElement("iframe");fr.className="vp";fr.setAttribute("width","390");fr.setAttribute("height","844");fr.setAttribute("data-run",s.live.runId);fr.src=s.live.url;fr.title="Live preview";f.appendChild(fr);focus.appendChild(f)}fit();if(s.live.buildError){var be=el("div","chip bad",null);be.style.display="block";be.style.marginTop="8px";be.textContent="Last edit did not build, still showing the last good version: "+s.live.buildError;focus.appendChild(be)}focus.appendChild(el("div","cap",(s.live.watching?"Repaints as the app is edited \xB7 ":"The build this run started with; himi run --watch repaints on every edit \xB7 ")+s.live.url));if(s.live.lan)focus.appendChild(lanCard(s.live.lan))}
42542
+ if(s.live){$("focuslbl").textContent="Live preview";if(!kept){var f=el("div","phone vpf");var fr=document.createElement("iframe");fr.className="vp";fr.setAttribute("width","390");fr.setAttribute("height","844");fr.setAttribute("data-run",s.live.runId);fr.src=s.live.url;fr.title="Live preview";f.appendChild(fr);focus.appendChild(f)}fit();if(s.live.buildError){var be=el("div","chip bad",null);be.style.display="block";be.style.marginTop="8px";be.textContent="Last edit did not build, still showing the last good version: "+s.live.buildError;focus.appendChild(be)}focus.appendChild(el("div","cap",(s.live.watching?"Repaints as the app is edited \xB7 ":"The build this run started with; himi run --watch repaints on every edit \xB7 ")+s.live.url));if(s.live.lan)focus.appendChild(lanCard(s.live.lan));if(s.live.lastBuild&&s.live.lastBuild.changes)focus.appendChild(changesCard(s.live.lastBuild))}
42401
42543
  else if(s.focus&&s.focus.frames.length){var fl=s.focus.frames;$("focuslbl").textContent="Latest \xB7 "+s.focus.id;var p=el("div","phone");p.appendChild(img(fl[fl.length-1].img));focus.appendChild(p);focus.appendChild(el("div","cap",fl.length+(fl.length===1?" version":" versions")+" so far"))}
42402
42544
  else{$("focuslbl").textContent="Latest";focus.appendChild(el("div","empty","The first render appears here."))}
42403
42545
  if(s.focus&&s.focus.frames.length>1)s.focus.frames.forEach(function(fm){film.appendChild(img(fm.img))});}}
@@ -42415,7 +42557,7 @@ function render(s){
42415
42557
  if(s.brand.site){var si=img(s.brand.site,"site");brand.appendChild(si)}
42416
42558
  if(s.brand.site&&s.focus&&s.focus.frames.length){brand.appendChild(el("span","sub","\u2192"));var pf=el("div","shot");pf.style.width="90px";var ff=el("div","frame");ff.appendChild(img(s.focus.frames[s.focus.frames.length-1].img));pf.appendChild(ff);brand.appendChild(pf)}
42417
42559
  if(s.app.palette.length){var sw=el("div","sw");s.app.palette.forEach(function(c){var d=el("span");d.style.background=c.hex;d.title=c.name+" "+c.hex;sw.appendChild(d)});brand.appendChild(sw)}
42418
- var nav=$("nav");clear(nav);$("navcard").style.display=s.nav.length?"":"none";s.nav.forEach(function(e){nav.appendChild(el("span",null,e.from+" \u2192 "+e.to))});
42560
+ var nav=$("nav");clear(nav);$("navcard").style.display=s.nav.length&&!s.map?"":"none";drawMap(s.map);s.nav.forEach(function(e){nav.appendChild(el("span",null,e.from+" \u2192 "+e.to))});
42419
42561
  var log=$("log");clear(log);if(!s.journal.length)log.appendChild(el("li","empty","Postcards the agent journals appear here."));
42420
42562
  s.journal.forEach(function(j){var li=el("li");li.appendChild(el("div","t",hhmm(j.ts)));var body=el("div");body.appendChild(el("div",null,j.message));if(j.step)body.appendChild(el("div","s","step "+j.stepNumber+" \xB7 "+j.step));
42421
42563
  if(j.images.length){var row=el("div","imgs");j.images.forEach(function(u){row.appendChild(img(u))});body.appendChild(row)}li.appendChild(body);log.appendChild(li)});
@@ -42535,7 +42677,7 @@ function favicon(n,bad){try{var c=document.createElement("canvas");c.width=c.hei
42535
42677
  function burst(){var c=$("fx");c.setAttribute("data-bursts",String((+c.getAttribute("data-bursts")||0)+1));if(reduced())return;var x=c.getContext&&c.getContext("2d");if(!x)return;var W=c.width=innerWidth,H=c.height=innerHeight,cols=["#1f7a4d","#3b6fd8","#e0a030","#d4537e",accentNow()],P=[];for(var i=0;i<120;i++)P.push({x:W/2,y:H*.35,vx:(Math.random()-.5)*14,vy:-Math.random()*12-4,c:cols[i%cols.length],r:Math.random()*6.28});var t0=now();(function f(){x.clearRect(0,0,W,H);P.forEach(function(p){p.x+=p.vx;p.y+=p.vy;p.vy+=.35;p.r+=.2;x.save();x.translate(p.x,p.y);x.rotate(p.r);x.fillStyle=p.c;x.fillRect(-4,-2,8,4);x.restore()});if(now()-t0<2200)raf(f);else x.clearRect(0,0,W,H)})()}
42536
42678
  function countTo(e,a,b){if(!e)return;if(reduced()){e.textContent=String(b);return}var t0=now();(function f(){var k=Math.min(1,(now()-t0)/900);e.textContent=String(Math.round(a+(b-a)*k));if(k<1)raf(f)})()}
42537
42679
  function snap(s){var sc={},acc=null;s.screens.forEach(function(x){sc[x.id]=x.img});s.app.palette.forEach(function(p){if(p.name==="accentPrimary")acc=p.hex});return{screens:sc,step:s.current?s.current.stepNumber:0,top:s.journal[0]?s.journal[0].ts:null,build:s.live&&s.live.lastBuild?s.live.lastBuild.at:s.pastBuild?s.pastBuild.at:null,run:s.live?s.live.runId:s.pastBuild?s.pastBuild.run:null,push:s.health.push?s.health.push.releaseId:null,gaps:s.health.test?s.health.test.gaps:null,accent:acc,reveal:s.reveal?s.reveal.at:null}}
42538
- function buildLine(b){if(!b.ok)return "Build failed \xB7 still showing the last good version";var what=b.screens.length?b.screens.slice(0,3).join(", ")+(b.screens.length>3?" +"+(b.screens.length-3):""):b.tokens?"design tokens":b.config?"app config":"no visible change";return "Rebuilt"+(b.ms!=null?" in "+(b.ms/1000).toFixed(1)+" s":"")+" \xB7 "+what}
42680
+ function buildLine(b){if(!b.ok)return "Build failed \xB7 still showing the last good version";var what=b.headline?b.headline:b.screens.length?b.screens.slice(0,3).join(", ")+(b.screens.length>3?" +"+(b.screens.length-3):""):b.tokens?"design tokens":b.config?"app config":"no visible change";return "Rebuilt"+(b.ms!=null?" in "+(b.ms/1000).toFixed(1)+" s":"")+" \xB7 "+what}
42539
42681
  function effects(s){var is=snap(s),was=seen;seen=is;if(is.accent)document.documentElement.style.setProperty("--accent",is.accent);
42540
42682
  /* The phone (kept across renders) and the Now line outlive a render: clear what they played last. */
42541
42683
  var stage=rewinding?$("still"):$("focus"),ph=stage.querySelector(".phone"),nm=$("nowmsg");if(ph)ph.classList.remove("pulse","bad");nm.classList.remove("slidein");if(!was)return;
@@ -42598,9 +42740,174 @@ var init_studio_page = __esm({
42598
42740
  }
42599
42741
  });
42600
42742
 
42601
- // src/studio-timeline.ts
42602
- import { existsSync as existsSync40, readFileSync as readFileSync43 } from "node:fs";
42743
+ // src/build-changes.ts
42744
+ var build_changes_exports = {};
42745
+ __export(build_changes_exports, {
42746
+ boardLines: () => boardLines,
42747
+ describeChanges: () => describeChanges,
42748
+ diffDescriptors: () => diffDescriptors,
42749
+ diffTokens: () => diffTokens,
42750
+ headline: () => headline
42751
+ });
42752
+ import { existsSync as existsSync40, readdirSync as readdirSync22, readFileSync as readFileSync43 } from "node:fs";
42603
42753
  import { join as join38 } from "node:path";
42754
+ function componentsOf(descriptor) {
42755
+ const out = /* @__PURE__ */ new Map();
42756
+ visitComponents(descriptor, (n) => {
42757
+ if (typeof n.id !== "string" || out.has(n.id)) return;
42758
+ const props = typeof n.body.props === "object" && n.body.props !== null ? n.body.props : {};
42759
+ out.set(n.id, { type: n.body.type, props });
42760
+ });
42761
+ return out;
42762
+ }
42763
+ function diffDescriptors(screen, before, after) {
42764
+ const a = componentsOf(before);
42765
+ const b = componentsOf(after);
42766
+ const props = [];
42767
+ const added = [];
42768
+ for (const [id, now] of b) {
42769
+ const was = a.get(id);
42770
+ if (!was) {
42771
+ added.push({ kind: "added", screen, node: id, type: now.type });
42772
+ continue;
42773
+ }
42774
+ const keys = /* @__PURE__ */ new Set([...Object.keys(was.props), ...Object.keys(now.props)]);
42775
+ for (const key2 of keys) {
42776
+ const x = was.props[key2];
42777
+ const y = now.props[key2];
42778
+ if (!scalar2(x) && !scalar2(y)) continue;
42779
+ if (x === y) continue;
42780
+ props.push({
42781
+ kind: TEXT_PROPS.has(key2) ? "text" : "prop",
42782
+ screen,
42783
+ node: id,
42784
+ path: key2,
42785
+ ...scalar2(x) ? { from: cut(String(x)) } : {},
42786
+ ...scalar2(y) ? { to: cut(String(y)) } : {}
42787
+ });
42788
+ }
42789
+ }
42790
+ const removed = [];
42791
+ for (const [id, was] of a) if (!b.has(id)) removed.push({ kind: "removed", screen, node: id, type: was.type });
42792
+ return [...props.filter((c) => c.kind === "text"), ...added, ...removed, ...props.filter((c) => c.kind === "prop")];
42793
+ }
42794
+ function leaves(value, prefix, out) {
42795
+ if (scalar2(value)) {
42796
+ out.set(prefix, String(value));
42797
+ return;
42798
+ }
42799
+ if (typeof value !== "object" || value === null) return;
42800
+ for (const [k, v] of Object.entries(value)) leaves(v, prefix ? `${prefix}.${k}` : k, out);
42801
+ }
42802
+ function diffTokens(before, after) {
42803
+ const a = /* @__PURE__ */ new Map();
42804
+ const b = /* @__PURE__ */ new Map();
42805
+ leaves(before, "", a);
42806
+ leaves(after, "", b);
42807
+ const paths = [.../* @__PURE__ */ new Set([...a.keys(), ...b.keys()])].sort();
42808
+ const out = [];
42809
+ for (const path2 of paths) {
42810
+ const x = a.get(path2);
42811
+ const y = b.get(path2);
42812
+ if (x === y) continue;
42813
+ out.push({ kind: "token", path: path2, ...x !== void 0 ? { from: cut(x) } : {}, ...y !== void 0 ? { to: cut(y) } : {} });
42814
+ }
42815
+ return out;
42816
+ }
42817
+ function describeChanges(oldTree, newTree, diff, opts = {}) {
42818
+ if (!existsSync40(oldTree) || !existsSync40(newTree)) return { items: [], more: 0 };
42819
+ const all = [];
42820
+ const before = screensIn(oldTree);
42821
+ const after = screensIn(newTree);
42822
+ for (const id of diff.screens) {
42823
+ if (!after.has(id) || !before.has(id)) continue;
42824
+ const was = readJson2(join38(oldTree, "screens", `${id}.json`));
42825
+ const now = readJson2(join38(newTree, "screens", `${id}.json`));
42826
+ const changes = diffDescriptors(id, was, now);
42827
+ if (changes.length) all.push(...changes);
42828
+ else all.push({ kind: JSON.stringify(was) === JSON.stringify(now) ? "logic" : "descriptor", screen: id });
42829
+ }
42830
+ if (diff.tokens) all.push(...diffTokens(readJson2(join38(oldTree, "tokens.json")), readJson2(join38(newTree, "tokens.json"))));
42831
+ for (const id of [...after].sort()) if (!before.has(id)) all.push({ kind: "screen-added", screen: id });
42832
+ for (const id of [...before].sort()) if (!after.has(id)) all.push({ kind: "screen-removed", screen: id });
42833
+ const ranked = all.map((c, i) => ({ c, i })).sort((x, y) => RANK[x.c.kind] - RANK[y.c.kind] || x.i - y.i).map((x) => x.c);
42834
+ const max = opts.max ?? MAX_ITEMS;
42835
+ return { items: ranked.slice(0, max), more: Math.max(0, ranked.length - max) };
42836
+ }
42837
+ function headline(changes) {
42838
+ const c = changes.items[0];
42839
+ return c ? lineOf(c) : null;
42840
+ }
42841
+ function boardLines(changes) {
42842
+ const lines = [];
42843
+ for (const c of changes.items) {
42844
+ const text5 = lineOf(c);
42845
+ const same2 = lines.find((l) => l.text === text5);
42846
+ if (same2) {
42847
+ same2.n++;
42848
+ continue;
42849
+ }
42850
+ lines.push({
42851
+ text: text5,
42852
+ n: 1,
42853
+ ...c.kind === "token" && c.from && HEX.test(c.from) ? { from: c.from } : {},
42854
+ ...c.kind === "token" && c.to && HEX.test(c.to) ? { to: c.to } : {}
42855
+ });
42856
+ }
42857
+ return { lines: lines.map(({ n, ...l }) => n > 1 ? { ...l, text: `${l.text} (\xD7${n})` } : l), more: changes.more };
42858
+ }
42859
+ function lineOf(c) {
42860
+ switch (c.kind) {
42861
+ case "text":
42862
+ case "prop":
42863
+ return `${c.screen}: \u201C${c.from ?? ""}\u201D \u2192 \u201C${c.to ?? ""}\u201D`;
42864
+ case "added":
42865
+ return `${c.screen}: + ${c.type}`;
42866
+ case "removed":
42867
+ return `${c.screen}: \u2212 ${c.type}`;
42868
+ case "token":
42869
+ return `${c.path} ${c.from ?? "(new)"} \u2192 ${c.to ?? "(removed)"}`;
42870
+ case "descriptor":
42871
+ return `${c.screen}: descriptor changed`;
42872
+ case "logic":
42873
+ return `${c.screen}: logic changed`;
42874
+ case "screen-added":
42875
+ return `new screen: ${c.screen}`;
42876
+ case "screen-removed":
42877
+ return `screen removed: ${c.screen}`;
42878
+ }
42879
+ }
42880
+ var TEXT_PROPS, RANK, MAX_TEXT, MAX_ITEMS, cut, scalar2, readJson2, screensIn, HEX;
42881
+ var init_build_changes = __esm({
42882
+ "src/build-changes.ts"() {
42883
+ init_stable_ids();
42884
+ TEXT_PROPS = /* @__PURE__ */ new Set(["text", "template", "title", "subtitle", "label", "placeholder", "caption", "message", "value", "hint", "description"]);
42885
+ RANK = { text: 0, added: 1, removed: 1, token: 2, prop: 3, "screen-added": 4, "screen-removed": 4, descriptor: 5, logic: 5 };
42886
+ MAX_TEXT = 60;
42887
+ MAX_ITEMS = 12;
42888
+ cut = (s) => s.length > MAX_TEXT ? `${s.slice(0, MAX_TEXT - 1)}\u2026` : s;
42889
+ scalar2 = (v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean";
42890
+ readJson2 = (path2) => {
42891
+ try {
42892
+ return JSON.parse(readFileSync43(path2, "utf8"));
42893
+ } catch {
42894
+ return void 0;
42895
+ }
42896
+ };
42897
+ screensIn = (tree) => {
42898
+ try {
42899
+ return new Set(readdirSync22(join38(tree, "screens")).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5)));
42900
+ } catch {
42901
+ return /* @__PURE__ */ new Set();
42902
+ }
42903
+ };
42904
+ HEX = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
42905
+ }
42906
+ });
42907
+
42908
+ // src/studio-timeline.ts
42909
+ import { existsSync as existsSync41, readFileSync as readFileSync44 } from "node:fs";
42910
+ import { join as join39 } from "node:path";
42604
42911
  function stateOf(view) {
42605
42912
  const j = view.journal[0];
42606
42913
  const h = view.health;
@@ -42667,10 +42974,10 @@ function validFrame(f) {
42667
42974
  return true;
42668
42975
  }
42669
42976
  function readTimeline(dir) {
42670
- const file = join38(dir, TIMELINE_FILE);
42671
- if (!existsSync40(file)) return [];
42977
+ const file = join39(dir, TIMELINE_FILE);
42978
+ if (!existsSync41(file)) return [];
42672
42979
  const out = [];
42673
- for (const line of readFileSync43(file, "utf8").split("\n")) {
42980
+ for (const line of readFileSync44(file, "utf8").split("\n")) {
42674
42981
  if (!line.trim()) continue;
42675
42982
  try {
42676
42983
  const f = JSON.parse(line);
@@ -42687,22 +42994,23 @@ function trim(frames) {
42687
42994
  return frames.slice(-MAX_FRAMES);
42688
42995
  }
42689
42996
  function writeTimelineJs(dir, frames) {
42690
- writeFileAtomic(join38(dir, TIMELINE_JS), `window.__himiTimeline && window.__himiTimeline(${scriptJson(frames)});
42997
+ writeFileAtomic(join39(dir, TIMELINE_JS), `window.__himiTimeline && window.__himiTimeline(${scriptJson(frames)});
42691
42998
  `);
42692
42999
  }
42693
43000
  function appendFrame(dir, view, reveal = null) {
42694
43001
  const frames = readTimeline(dir);
42695
43002
  const last = frames[frames.length - 1];
42696
43003
  if (last && last.v === view.v && !reveal) {
42697
- if (!existsSync40(join38(dir, TIMELINE_JS))) writeTimelineJs(dir, frames);
43004
+ if (!existsSync41(join39(dir, TIMELINE_JS))) writeTimelineJs(dir, frames);
42698
43005
  return frames.length;
42699
43006
  }
42700
43007
  let prev = null;
42701
43008
  for (const f of frames) prev = fold(prev, f);
42702
43009
  const frame = frameFor(stateOf(view), prev, view.generatedAt, view.v, frames.length % KEYFRAME_EVERY === 0);
43010
+ if (!frame.key && !reveal && Object.keys(frame).every((k) => k === "ts" || k === "v")) return frames.length;
42703
43011
  if (reveal) frame.reveal = reveal;
42704
43012
  const kept = trim([...frames, frame]);
42705
- writeFileAtomic(join38(dir, TIMELINE_FILE), kept.map((f) => JSON.stringify(f)).join("\n") + "\n");
43013
+ writeFileAtomic(join39(dir, TIMELINE_FILE), kept.map((f) => JSON.stringify(f)).join("\n") + "\n");
42706
43014
  writeTimelineJs(dir, kept);
42707
43015
  return kept.length;
42708
43016
  }
@@ -42724,8 +43032,8 @@ var init_studio_timeline = __esm({
42724
43032
  "src/studio-timeline.ts"() {
42725
43033
  init_progress();
42726
43034
  init_studio_page();
42727
- TIMELINE_FILE = join38(".himi", "studio", "timeline.jsonl");
42728
- TIMELINE_JS = join38(".himi", "studio", "timeline.js");
43035
+ TIMELINE_FILE = join39(".himi", "studio", "timeline.jsonl");
43036
+ TIMELINE_JS = join39(".himi", "studio", "timeline.js");
42729
43037
  MAX_FRAMES = 2e3;
42730
43038
  KEYFRAME_EVERY = 50;
42731
43039
  same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
@@ -42748,6 +43056,7 @@ var init_studio_timeline = __esm({
42748
43056
  var studio_exports = {};
42749
43057
  __export(studio_exports, {
42750
43058
  EDGES_RECORD: () => EDGES_RECORD,
43059
+ MAP_RECORD: () => MAP_RECORD,
42751
43060
  PUSH_RECORD: () => PUSH_RECORD,
42752
43061
  RUN_RECORD: () => RUN_RECORD,
42753
43062
  STUDIO_DIR: () => STUDIO_DIR,
@@ -42762,8 +43071,8 @@ __export(studio_exports, {
42762
43071
  writeStudioRecord: () => writeStudioRecord
42763
43072
  });
42764
43073
  import { createHash as createHash21 } from "node:crypto";
42765
- import { existsSync as existsSync41, readdirSync as readdirSync22, readFileSync as readFileSync44, realpathSync as realpathSync5, statSync as statSync8 } from "node:fs";
42766
- import { basename as basename10, extname as extname3, join as join39, relative as relative7, resolve as resolve33, sep as sep9 } from "node:path";
43074
+ import { existsSync as existsSync42, readdirSync as readdirSync23, readFileSync as readFileSync45, realpathSync as realpathSync5, statSync as statSync8 } from "node:fs";
43075
+ import { basename as basename10, extname as extname3, join as join40, relative as relative7, resolve as resolve33, sep as sep9 } from "node:path";
42767
43076
  import { pathToFileURL as pathToFileURL7 } from "node:url";
42768
43077
  function realInside(dir, abs) {
42769
43078
  try {
@@ -42788,12 +43097,12 @@ function archiveFile(dir, memory, abs) {
42788
43097
  if (size === 0) return null;
42789
43098
  const key2 = relative7(dir, abs);
42790
43099
  const hit = memory.files?.[key2];
42791
- if (hit && hit.size === size && hit.mtimeMs === mtimeMs && hit.ino === ino && existsSync41(join39(dir, HISTORY_DIR, `${hit.sha}.${hit.ext}`))) {
43100
+ if (hit && hit.size === size && hit.mtimeMs === mtimeMs && hit.ino === ino && existsSync42(join40(dir, HISTORY_DIR, `${hit.sha}.${hit.ext}`))) {
42792
43101
  return { sha: hit.sha, ext: hit.ext, file: `${hit.sha}.${hit.ext}` };
42793
43102
  }
42794
43103
  let archived;
42795
43104
  try {
42796
- archived = archiveBytes(dir, readFileSync44(real));
43105
+ archived = archiveBytes(dir, readFileSync45(real));
42797
43106
  } catch {
42798
43107
  return null;
42799
43108
  }
@@ -42802,17 +43111,17 @@ function archiveFile(dir, memory, abs) {
42802
43111
  return { sha: archived.sha256, ext: archived.ext, file: `${archived.sha256}.${archived.ext}` };
42803
43112
  }
42804
43113
  function loadMemory(dir, now) {
42805
- const raw = obj(readJson2(join39(dir, MEMORY_FILE)));
43114
+ const raw = obj(readJson3(join40(dir, MEMORY_FILE)));
42806
43115
  if (raw && raw.version === 1 && obj(raw.shots) && Array.isArray(raw.timeline)) return raw;
42807
43116
  return { version: 1, createdAt: now, shots: {}, timeline: [] };
42808
43117
  }
42809
43118
  function observeShots(dir, memory, now) {
42810
- const shotsDir = join39(dir, ".himi", "shots");
43119
+ const shotsDir = join40(dir, ".himi", "shots");
42811
43120
  const seen = /* @__PURE__ */ new Set();
42812
- const files = existsSync41(shotsDir) ? readdirSync22(shotsDir).filter((f) => /\.(png|jpe?g)$/i.test(f)).sort() : [];
43121
+ const files = existsSync42(shotsDir) ? readdirSync23(shotsDir).filter((f) => /\.(png|jpe?g)$/i.test(f)).sort() : [];
42813
43122
  for (const f of files) {
42814
43123
  const id = basename10(f, extname3(f));
42815
- const archived = archiveFile(dir, memory, join39(shotsDir, f));
43124
+ const archived = archiveFile(dir, memory, join40(shotsDir, f));
42816
43125
  if (!archived) continue;
42817
43126
  seen.add(id);
42818
43127
  const { sha, ext } = archived;
@@ -42827,14 +43136,14 @@ function observeShots(dir, memory, now) {
42827
43136
  memory.shots[id] = { ...prev, present: true };
42828
43137
  }
42829
43138
  }
42830
- if (existsSync41(shotsDir)) {
43139
+ if (existsSync42(shotsDir)) {
42831
43140
  for (const [id, shot] of Object.entries(memory.shots)) if (!seen.has(id) && shot.present) memory.shots[id] = { ...shot, present: false };
42832
43141
  }
42833
43142
  if (memory.timeline.length > MAX_TIMELINE) memory.timeline = memory.timeline.slice(-MAX_TIMELINE);
42834
43143
  return memory;
42835
43144
  }
42836
43145
  function palette(dir) {
42837
- const colors = obj(obj(readJson2(join39(dir, "tokens.json")))?.colors);
43146
+ const colors = obj(obj(readJson3(join40(dir, "tokens.json")))?.colors);
42838
43147
  if (!colors) return [];
42839
43148
  const pick2 = (key2) => {
42840
43149
  const v = colors[key2];
@@ -42844,7 +43153,7 @@ function palette(dir) {
42844
43153
  return ["accentPrimary", "accentSecondary", "accentSurface", "surfacePrimary", "textPrimary"].map(pick2).filter((x) => !!x);
42845
43154
  }
42846
43155
  function appIdentity(dir) {
42847
- const manifest = obj(readJson2(join39(dir, "himalaya.content.json")));
43156
+ const manifest = obj(readJson3(join40(dir, "himalaya.content.json")));
42848
43157
  const name = text(manifest?.name) ?? basename10(resolve33(dir));
42849
43158
  const icon = text(manifest?.icon)?.trim();
42850
43159
  const iconPath = icon ? resolve33(dir, icon) : null;
@@ -42852,12 +43161,12 @@ function appIdentity(dir) {
42852
43161
  return { name, iconFile: real && statSync8(real).isFile() ? iconPath : null };
42853
43162
  }
42854
43163
  function testRuns(dir) {
42855
- const att = join39(dir, ".himi", "attestations");
42856
- if (!existsSync41(att)) return [];
43164
+ const att = join40(dir, ".himi", "attestations");
43165
+ if (!existsSync42(att)) return [];
42857
43166
  const runs = [];
42858
- for (const f of readdirSync22(att)) {
43167
+ for (const f of readdirSync23(att)) {
42859
43168
  if (!f.endsWith(".json") || f === "latest.json") continue;
42860
- const r = obj(readJson2(join39(att, f)));
43169
+ const r = obj(readJson3(join40(att, f)));
42861
43170
  if (!r || r.tool !== "himi-test" || typeof r.runId !== "string") continue;
42862
43171
  const report2 = obj(r.report);
42863
43172
  const coverage = obj(r.coverage);
@@ -42873,20 +43182,20 @@ function testRuns(dir) {
42873
43182
  return runs.sort((a, b) => a.runId.localeCompare(b.runId));
42874
43183
  }
42875
43184
  function siteHome(dir) {
42876
- for (const pages of [join39(dir, ".himi", "business-book", "evidence", "pages"), join39(dir, "evidence", "pages")]) {
42877
- if (!existsSync41(pages)) continue;
42878
- const home = readdirSync22(pages).filter((f) => /^home.*\.(png|jpe?g)$/i.test(f)).sort()[0];
42879
- if (home) return join39(pages, home);
43185
+ for (const pages of [join40(dir, ".himi", "business-book", "evidence", "pages"), join40(dir, "evidence", "pages")]) {
43186
+ if (!existsSync42(pages)) continue;
43187
+ const home = readdirSync23(pages).filter((f) => /^home.*\.(png|jpe?g)$/i.test(f)).sort()[0];
43188
+ if (home) return join40(pages, home);
42880
43189
  }
42881
43190
  return null;
42882
43191
  }
42883
43192
  function uxStatuses(dir) {
42884
- const platforms = obj(obj(readJson2(join39(dir, ".himi", "ux-audit.json")))?.platforms);
43193
+ const platforms = obj(obj(readJson3(join40(dir, ".himi", "ux-audit.json")))?.platforms);
42885
43194
  if (!platforms) return [];
42886
43195
  return Object.entries(platforms).map(([platform, rec]) => ({ platform, status: text(obj(rec)?.status) ?? "" })).filter((p) => p.status);
42887
43196
  }
42888
43197
  function liveRun(dir) {
42889
- const rec = obj(readJson2(join39(dir, RUN_RECORD)));
43198
+ const rec = obj(readJson3(join40(dir, RUN_RECORD)));
42890
43199
  const url = text(rec?.url);
42891
43200
  const pid = rec?.pid;
42892
43201
  if (!url || !/^https?:\/\//i.test(url) || typeof pid !== "number") return null;
@@ -42907,7 +43216,8 @@ function liveRun(dir) {
42907
43216
  ms: typeof build2.ms === "number" && Number.isFinite(build2.ms) ? Math.max(0, Math.round(build2.ms)) : null,
42908
43217
  screens: (Array.isArray(build2.screens) ? build2.screens : []).filter((x) => typeof x === "string").slice(0, 20),
42909
43218
  tokens: build2.tokens === true,
42910
- config: build2.config === true
43219
+ config: build2.config === true,
43220
+ ...changesOf(build2.changes)
42911
43221
  } : null,
42912
43222
  lan: lanOf(rec?.lanUrl)
42913
43223
  };
@@ -42915,6 +43225,47 @@ function liveRun(dir) {
42915
43225
  return null;
42916
43226
  }
42917
43227
  }
43228
+ function mapOf(value) {
43229
+ const raw = obj(value);
43230
+ if (!raw || !Array.isArray(raw.screens)) return null;
43231
+ const screens = [];
43232
+ const ids = /* @__PURE__ */ new Set();
43233
+ for (const it of raw.screens.slice(0, 300)) {
43234
+ const o = obj(it);
43235
+ const id = typeof o?.id === "string" ? o.id.slice(0, 120) : null;
43236
+ if (!o || !id || ids.has(id) || typeof o.status !== "string" || !MAP_STATUSES.has(o.status)) continue;
43237
+ ids.add(id);
43238
+ screens.push({ id, entry: o.entry === true, reached: o.reached === true, status: o.status, unfollowed: count(o.unfollowed) });
43239
+ }
43240
+ if (!screens.length) return null;
43241
+ const link = (it, needTo) => {
43242
+ const o = obj(it);
43243
+ const from = typeof o?.from === "string" ? o.from.slice(0, 120) : null;
43244
+ const to = typeof o?.to === "string" ? o.to.slice(0, 120) : null;
43245
+ if (!from || !to || !ids.has(from) || needTo && !ids.has(to)) return null;
43246
+ return { from, to, via: typeof o?.via === "string" ? o.via.slice(0, 120) : "" };
43247
+ };
43248
+ const edges = (Array.isArray(raw.edges) ? raw.edges.slice(0, 600) : []).map((e) => link(e, true)).filter((e) => !!e);
43249
+ const broken = (Array.isArray(raw.broken) ? raw.broken.slice(0, 100) : []).map((e) => link(e, false)).filter((e) => !!e);
43250
+ return { screens, edges, broken, unfollowed: count(raw.unfollowed), edgesDropped: count(raw.edgesDropped), done: raw.done === true };
43251
+ }
43252
+ function changesOf(value) {
43253
+ const raw = obj(value);
43254
+ const items = [];
43255
+ for (const it of Array.isArray(raw?.items) ? raw.items.slice(0, 12) : []) {
43256
+ const o = obj(it);
43257
+ if (!o || typeof o.kind !== "string" || !CHANGE_KINDS.has(o.kind)) continue;
43258
+ const c = { kind: o.kind };
43259
+ for (const k of ["screen", "node", "type", "path", "from", "to"]) {
43260
+ if (typeof o[k] === "string") c[k] = o[k].slice(0, 80);
43261
+ }
43262
+ items.push(c);
43263
+ }
43264
+ if (!items.length) return { headline: null, changes: null };
43265
+ const more = typeof raw?.more === "number" && Number.isFinite(raw.more) ? Math.max(0, Math.round(raw.more)) : 0;
43266
+ const changes = { items, more };
43267
+ return { headline: headline(changes), changes: boardLines(changes) };
43268
+ }
42918
43269
  function lanOf(value) {
42919
43270
  const raw = text(value);
42920
43271
  if (!raw) return null;
@@ -42954,9 +43305,9 @@ function buildView(dir, memory, now) {
42954
43305
  const runs = testRuns(dir);
42955
43306
  const last = runs[runs.length - 1];
42956
43307
  const prev = runs[runs.length - 2];
42957
- const validate3 = obj(readJson2(join39(dir, VALIDATE_RECORD)));
42958
- const push = obj(readJson2(join39(dir, PUSH_RECORD)));
42959
- const edges = readJson2(join39(dir, EDGES_RECORD));
43308
+ const validate3 = obj(readJson3(join40(dir, VALIDATE_RECORD)));
43309
+ const push = obj(readJson3(join40(dir, PUSH_RECORD)));
43310
+ const edges = readJson3(join40(dir, EDGES_RECORD));
42960
43311
  const nav = [];
42961
43312
  if (Array.isArray(edges)) {
42962
43313
  const seen = /* @__PURE__ */ new Set();
@@ -42994,6 +43345,7 @@ function buildView(dir, memory, now) {
42994
43345
  },
42995
43346
  brand: { site: siteImg },
42996
43347
  nav,
43348
+ map: mapOf(readJson3(join40(dir, MAP_RECORD))),
42997
43349
  live: liveRun(dir),
42998
43350
  timelineFrames: 0,
42999
43351
  reveal: memory.reveal ?? null
@@ -43027,15 +43379,15 @@ function writeBoard(dir, now) {
43027
43379
  } catch {
43028
43380
  view.timelineFrames = 0;
43029
43381
  }
43030
- writeAtomic(join39(dir, MEMORY_FILE), JSON.stringify(memory));
43031
- writeAtomic(join39(dir, STATE_FILE), `window.__himiStudio && window.__himiStudio(${scriptJson(view)});
43382
+ writeAtomic(join40(dir, MEMORY_FILE), JSON.stringify(memory));
43383
+ writeAtomic(join40(dir, STATE_FILE), `window.__himiStudio && window.__himiStudio(${scriptJson(view)});
43032
43384
  `);
43033
- writeAtomic(join39(dir, STUDIO_PAGE), renderStudioHtml(view));
43385
+ writeAtomic(join40(dir, STUDIO_PAGE), renderStudioHtml(view));
43034
43386
  return view;
43035
43387
  }
43036
43388
  function refreshStudio(dir, now = /* @__PURE__ */ new Date()) {
43037
43389
  try {
43038
- if (!existsSync41(join39(dir, STUDIO_PAGE))) return { refreshed: false };
43390
+ if (!existsSync42(join40(dir, STUDIO_PAGE))) return { refreshed: false };
43039
43391
  return { refreshed: true, view: writeBoard(dir, now.toISOString()) };
43040
43392
  } catch {
43041
43393
  return { refreshed: false };
@@ -43043,39 +43395,41 @@ function refreshStudio(dir, now = /* @__PURE__ */ new Date()) {
43043
43395
  }
43044
43396
  function openStudio(dir, now = /* @__PURE__ */ new Date()) {
43045
43397
  const view = writeBoard(dir, now.toISOString());
43046
- const page = join39(dir, STUDIO_PAGE);
43398
+ const page = join40(dir, STUDIO_PAGE);
43047
43399
  return { page, url: pathToFileURL7(page).href, view };
43048
43400
  }
43049
43401
  function writeStudioRecord(dir, record2, value) {
43050
43402
  try {
43051
43403
  if (!realDirIn(dir, ".himi")) return;
43052
- writeAtomic(join39(dir, record2), `${JSON.stringify(value, null, 2)}
43404
+ writeAtomic(join40(dir, record2), `${JSON.stringify(value, null, 2)}
43053
43405
  `);
43054
43406
  } catch {
43055
43407
  }
43056
43408
  }
43057
- var STUDIO_DIR, STUDIO_PAGE, STATE_FILE, MEMORY_FILE, VALIDATE_RECORD, PUSH_RECORD, EDGES_RECORD, RUN_RECORD, MAX_TIMELINE, MAX_JOURNAL, MAX_NAV_EDGES, readJson2, obj, num, text, historyUrl, runTime;
43409
+ var STUDIO_DIR, STUDIO_PAGE, STATE_FILE, MEMORY_FILE, VALIDATE_RECORD, PUSH_RECORD, EDGES_RECORD, MAP_RECORD, RUN_RECORD, MAX_TIMELINE, MAX_JOURNAL, MAX_NAV_EDGES, readJson3, obj, num, text, historyUrl, runTime, MAP_STATUSES, count, CHANGE_KINDS;
43058
43410
  var init_studio = __esm({
43059
43411
  "src/studio.ts"() {
43060
43412
  init_studio_page();
43061
43413
  init_qr();
43414
+ init_build_changes();
43062
43415
  init_studio_timeline();
43063
43416
  init_progress();
43064
43417
  init_studio_page();
43065
- STUDIO_DIR = join39(".himi", "studio");
43066
- STUDIO_PAGE = join39(STUDIO_DIR, "index.html");
43067
- STATE_FILE = join39(STUDIO_DIR, "state.js");
43068
- MEMORY_FILE = join39(STUDIO_DIR, "memory.json");
43069
- VALIDATE_RECORD = join39(".himi", "validate.json");
43070
- PUSH_RECORD = join39(".himi", "last-push.json");
43071
- EDGES_RECORD = join39(".himi", "test-edges.json");
43072
- RUN_RECORD = join39(".himi", "run.json");
43418
+ STUDIO_DIR = join40(".himi", "studio");
43419
+ STUDIO_PAGE = join40(STUDIO_DIR, "index.html");
43420
+ STATE_FILE = join40(STUDIO_DIR, "state.js");
43421
+ MEMORY_FILE = join40(STUDIO_DIR, "memory.json");
43422
+ VALIDATE_RECORD = join40(".himi", "validate.json");
43423
+ PUSH_RECORD = join40(".himi", "last-push.json");
43424
+ EDGES_RECORD = join40(".himi", "test-edges.json");
43425
+ MAP_RECORD = join40(".himi", "test-map.json");
43426
+ RUN_RECORD = join40(".himi", "run.json");
43073
43427
  MAX_TIMELINE = 500;
43074
43428
  MAX_JOURNAL = 80;
43075
43429
  MAX_NAV_EDGES = 40;
43076
- readJson2 = (file) => {
43430
+ readJson3 = (file) => {
43077
43431
  try {
43078
- return JSON.parse(readFileSync44(file, "utf8"));
43432
+ return JSON.parse(readFileSync45(file, "utf8"));
43079
43433
  } catch {
43080
43434
  return null;
43081
43435
  }
@@ -43085,6 +43439,9 @@ var init_studio = __esm({
43085
43439
  text = (v) => typeof v === "string" && v ? v : null;
43086
43440
  historyUrl = (fileName) => `../history/${fileName}`;
43087
43441
  runTime = (runId) => runId.replace(/-[0-9a-f]+$/i, "");
43442
+ MAP_STATUSES = /* @__PURE__ */ new Set(["ok", "failed", "pending", "skipped"]);
43443
+ count = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? Math.min(Math.round(v), 1e5) : 0;
43444
+ CHANGE_KINDS = /* @__PURE__ */ new Set(["text", "prop", "added", "removed", "token", "descriptor", "logic", "screen-added", "screen-removed"]);
43088
43445
  }
43089
43446
  });
43090
43447
 
@@ -43374,16 +43731,16 @@ function regressionDiff(base, live2, touched) {
43374
43731
  rows.push({ item: `screen:${id}`, live: resolveBundleSha(live2, l.bundle), base: resolveBundleSha(base, b.bundle), effect: `${id} rebinds to its ${base.releaseId} worker bundle` });
43375
43732
  }
43376
43733
  }
43377
- const scalar3 = (key2, item, what) => {
43734
+ const scalar4 = (key2, item, what) => {
43378
43735
  if (moves.has(item)) return;
43379
43736
  const l = live2[key2] ?? null;
43380
43737
  const b = base[key2] ?? null;
43381
43738
  if (l !== b) rows.push({ item, live: l, base: b, effect: `${what} reverts to ${base.releaseId}` });
43382
43739
  };
43383
- scalar3("tokensHash", "tokens", "design tokens");
43384
- scalar3("appConfigHash", "app-config", "app-config");
43385
- scalar3("iconHash", "icon", "the launcher icon");
43386
- scalar3("minBinaryVersion", "minBinaryVersion", "the minimum binary version");
43740
+ scalar4("tokensHash", "tokens", "design tokens");
43741
+ scalar4("appConfigHash", "app-config", "app-config");
43742
+ scalar4("iconHash", "icon", "the launcher icon");
43743
+ scalar4("minBinaryVersion", "minBinaryVersion", "the minimum binary version");
43387
43744
  for (const name of /* @__PURE__ */ new Set([...Object.keys(live2.assets ?? {}), ...Object.keys(base.assets ?? {})])) {
43388
43745
  const l = live2.assets?.[name]?.sha256 ?? null;
43389
43746
  const b = base.assets?.[name]?.sha256 ?? null;
@@ -43563,11 +43920,11 @@ __export(captcha_exports, {
43563
43920
  captchaContentInput: () => captchaContentInput,
43564
43921
  runCaptcha: () => runCaptcha
43565
43922
  });
43566
- import { readFileSync as readFileSync45 } from "node:fs";
43923
+ import { readFileSync as readFileSync46 } from "node:fs";
43567
43924
  import { resolve as resolve34 } from "node:path";
43568
43925
  function readObject(path2) {
43569
43926
  try {
43570
- return JSON.parse(readFileSync45(path2, "utf8"));
43927
+ return JSON.parse(readFileSync46(path2, "utf8"));
43571
43928
  } catch {
43572
43929
  return {};
43573
43930
  }
@@ -43711,11 +44068,11 @@ var init_push_setup_apple_credentials = __esm({
43711
44068
  });
43712
44069
 
43713
44070
  // src/push-onboarding.ts
43714
- import { existsSync as existsSync42, mkdirSync as mkdirSync19, readFileSync as readFileSync46, statSync as statSync9, writeFileSync as writeFileSync15, chmodSync as chmodSync2 } from "node:fs";
43715
- import { basename as basename11, dirname as dirname24, join as join40, resolve as resolve35 } from "node:path";
43716
- function readJson3(path2) {
44071
+ import { existsSync as existsSync43, mkdirSync as mkdirSync19, readFileSync as readFileSync47, statSync as statSync9, writeFileSync as writeFileSync15, chmodSync as chmodSync2 } from "node:fs";
44072
+ import { basename as basename11, dirname as dirname24, join as join41, resolve as resolve35 } from "node:path";
44073
+ function readJson4(path2) {
43717
44074
  try {
43718
- const parsed = JSON.parse(readFileSync46(path2, "utf8"));
44075
+ const parsed = JSON.parse(readFileSync47(path2, "utf8"));
43719
44076
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
43720
44077
  } catch {
43721
44078
  return {};
@@ -43723,9 +44080,9 @@ function readJson3(path2) {
43723
44080
  }
43724
44081
  function pushInputsFromContent(dir) {
43725
44082
  const root = resolve35(dir);
43726
- if (!existsSync42(join40(root, "himalaya.content.json"))) return { initialized: false };
43727
- const content = readJson3(join40(root, "himalaya.content.json"));
43728
- const provisioning = readJson3(join40(root, "provisioning.json"));
44083
+ if (!existsSync43(join41(root, "himalaya.content.json"))) return { initialized: false };
44084
+ const content = readJson4(join41(root, "himalaya.content.json"));
44085
+ const provisioning = readJson4(join41(root, "provisioning.json"));
43729
44086
  const native = provisioning.native ?? {};
43730
44087
  return {
43731
44088
  initialized: true,
@@ -43800,7 +44157,7 @@ function readKeyFile(path2) {
43800
44157
  }
43801
44158
  let body;
43802
44159
  try {
43803
- body = readFileSync46(path2, "utf8");
44160
+ body = readFileSync47(path2, "utf8");
43804
44161
  } catch {
43805
44162
  return { ok: false, code: "key_file_unreadable", reason: "cannot read the selected key file; check its path and file permissions" };
43806
44163
  }
@@ -43854,13 +44211,13 @@ function buildPushSetupBody(i, keys) {
43854
44211
  };
43855
44212
  }
43856
44213
  function readPushOnboarding(dir) {
43857
- const path2 = join40(resolve35(dir), PUSH_ONBOARDING_PATH);
43858
- if (!existsSync42(path2)) return { version: 1 };
43859
- const parsed = readJson3(path2);
44214
+ const path2 = join41(resolve35(dir), PUSH_ONBOARDING_PATH);
44215
+ if (!existsSync43(path2)) return { version: 1 };
44216
+ const parsed = readJson4(path2);
43860
44217
  return { version: 1, ...parsed };
43861
44218
  }
43862
44219
  function writeFile(dir, file) {
43863
- const path2 = join40(resolve35(dir), PUSH_ONBOARDING_PATH);
44220
+ const path2 = join41(resolve35(dir), PUSH_ONBOARDING_PATH);
43864
44221
  mkdirSync19(dirname24(path2), { recursive: true, mode: 448 });
43865
44222
  writeFileSync15(path2, JSON.stringify(file, null, 2) + "\n", { mode: 384 });
43866
44223
  try {
@@ -43889,8 +44246,8 @@ function recordPushDeferral(dir, d) {
43889
44246
  return push;
43890
44247
  }
43891
44248
  function clearPushDeferral(dir) {
43892
- const path2 = join40(resolve35(dir), PUSH_ONBOARDING_PATH);
43893
- if (!existsSync42(path2)) return;
44249
+ const path2 = join41(resolve35(dir), PUSH_ONBOARDING_PATH);
44250
+ if (!existsSync43(path2)) return;
43894
44251
  const { push: _dropped, ...rest } = readPushOnboarding(dir);
43895
44252
  writeFile(dir, { ...rest, version: 1 });
43896
44253
  }
@@ -44661,8 +45018,8 @@ __export(template_trim_exports, {
44661
45018
  routeMatcher: () => routeMatcher,
44662
45019
  runTemplateTrim: () => runTemplateTrim
44663
45020
  });
44664
- import { cpSync, existsSync as existsSync43, mkdirSync as mkdirSync20, readdirSync as readdirSync23, readFileSync as readFileSync47, renameSync as renameSync5, rmSync as rmSync8, statSync as statSync10, writeFileSync as writeFileSync16 } from "node:fs";
44665
- import { dirname as dirname25, join as join41 } from "node:path";
45021
+ import { cpSync, existsSync as existsSync44, mkdirSync as mkdirSync20, readdirSync as readdirSync24, readFileSync as readFileSync48, renameSync as renameSync5, rmSync as rmSync8, statSync as statSync10, writeFileSync as writeFileSync16 } from "node:fs";
45022
+ import { dirname as dirname25, join as join42 } from "node:path";
44666
45023
  function routeMatcher(pattern) {
44667
45024
  const detail = /^\^([A-Za-z0-9_]+)\(\?:\[_\/\]\(\.\+\)\)\?\$$/.exec(pattern);
44668
45025
  if (detail) {
@@ -44777,8 +45134,8 @@ function parseApp(dev, config) {
44777
45134
  while (!dev.startsWith("})", pos)) {
44778
45135
  ENTRY.lastIndex = pos;
44779
45136
  const m = ENTRY.exec(dev);
44780
- if (!m) throw new TrimRefusal(`dev/index.ts line ${lineOf(dev, pos)}: expected a \`"<screenId>": {\` entry of the SCREENS map`);
44781
- const id = jsonString(m[1], `dev/index.ts line ${lineOf(dev, pos)}: the SCREENS key`);
45137
+ if (!m) throw new TrimRefusal(`dev/index.ts line ${lineOf2(dev, pos)}: expected a \`"<screenId>": {\` entry of the SCREENS map`);
45138
+ const id = jsonString(m[1], `dev/index.ts line ${lineOf2(dev, pos)}: the SCREENS key`);
44782
45139
  const open = pos + m[0].length - 1;
44783
45140
  const close = objectEnd(dev, open);
44784
45141
  if (close < 0 || dev.slice(close, close + 2) !== ",\n") {
@@ -44807,9 +45164,9 @@ function parseApp(dev, config) {
44807
45164
  if (dev.startsWith("];", pos) || dev.startsWith("\n];", pos)) break;
44808
45165
  TENTRY.lastIndex = pos;
44809
45166
  const m = TENTRY.exec(dev);
44810
- if (!m) throw new TrimRefusal(`dev/index.ts line ${lineOf(dev, pos)}: expected a \`{ pattern: \u2026, display: \u2026, template: {\u2026} },\` entry of TEMPLATED`);
44811
- const pattern = jsonString(m[1], `dev/index.ts line ${lineOf(dev, pos)}: the TEMPLATED pattern`);
44812
- const display = jsonString(m[2], `dev/index.ts line ${lineOf(dev, pos)}: the TEMPLATED display`);
45167
+ if (!m) throw new TrimRefusal(`dev/index.ts line ${lineOf2(dev, pos)}: expected a \`{ pattern: \u2026, display: \u2026, template: {\u2026} },\` entry of TEMPLATED`);
45168
+ const pattern = jsonString(m[1], `dev/index.ts line ${lineOf2(dev, pos)}: the TEMPLATED pattern`);
45169
+ const display = jsonString(m[2], `dev/index.ts line ${lineOf2(dev, pos)}: the TEMPLATED display`);
44813
45170
  const lineEnd = dev.indexOf("\n", pos);
44814
45171
  const tk = dev.indexOf("template: {", pos);
44815
45172
  if (tk < 0 || tk > lineEnd) throw new TrimRefusal(`dev/index.ts: the templated route ${display} has no \`template: {\` on its first line`);
@@ -45281,7 +45638,7 @@ function planTrim(src, keepIds, links) {
45281
45638
  const homeModules = links === "drop" ? homeModulesPass(app, kept, edited) : void 0;
45282
45639
  let dev = "";
45283
45640
  let cursor = 0;
45284
- const cut = (from, to, replacement = "") => {
45641
+ const cut2 = (from, to, replacement = "") => {
45285
45642
  dev += app.dev.slice(cursor, from) + replacement;
45286
45643
  cursor = to;
45287
45644
  };
@@ -45290,15 +45647,15 @@ function planTrim(src, keepIds, links) {
45290
45647
  for (const r of app.routes) {
45291
45648
  if (templatedEmptied && r.kind === "templated") continue;
45292
45649
  if (!kept.has(r)) {
45293
- cut(r.lineStart, r.end);
45650
+ cut2(r.lineStart, r.end);
45294
45651
  continue;
45295
45652
  }
45296
45653
  const block = edited.get(r);
45297
45654
  let text5 = block ? serializeAt(block, app.dev, r.open) : void 0;
45298
45655
  if (r === app.home && homeModules?.text !== void 0) text5 = homeModules.text;
45299
- if (text5 !== void 0) cut(r.open, r.close, text5);
45656
+ if (text5 !== void 0) cut2(r.open, r.close, text5);
45300
45657
  }
45301
- if (templatedEmptied) cut(templatedEmptied[0], templatedEmptied[1], "\n");
45658
+ if (templatedEmptied) cut2(templatedEmptied[0], templatedEmptied[1], "\n");
45302
45659
  dev += app.dev.slice(cursor);
45303
45660
  let config = app.config;
45304
45661
  const edits = [];
@@ -45356,20 +45713,20 @@ function homeModulesPass(app, kept, edited) {
45356
45713
  return { text: patched.src, dropped: patched.droppedModules };
45357
45714
  }
45358
45715
  function listWorkers(dir) {
45359
- const root = join41(dir, "tier5-src");
45360
- if (!existsSync43(root)) return { dirs: [], files: {} };
45361
- const dirs = readdirSync23(root).filter((n) => statSync10(join41(root, n)).isDirectory()).sort();
45716
+ const root = join42(dir, "tier5-src");
45717
+ if (!existsSync44(root)) return { dirs: [], files: {} };
45718
+ const dirs = readdirSync24(root).filter((n) => statSync10(join42(root, n)).isDirectory()).sort();
45362
45719
  const files = {};
45363
45720
  const walk2 = (abs, rel) => {
45364
- for (const name of readdirSync23(abs)) {
45721
+ for (const name of readdirSync24(abs)) {
45365
45722
  if (name === "node_modules") continue;
45366
- const p = join41(abs, name);
45723
+ const p = join42(abs, name);
45367
45724
  const r = `${rel}/${name}`;
45368
45725
  if (statSync10(p).isDirectory()) walk2(p, r);
45369
- else if (/\.(?:[cm]?[jt]sx?|json)$/.test(name)) files[r] = readFileSync47(p, "utf8");
45726
+ else if (/\.(?:[cm]?[jt]sx?|json)$/.test(name)) files[r] = readFileSync48(p, "utf8");
45370
45727
  }
45371
45728
  };
45372
- for (const d of dirs) walk2(join41(root, d), `tier5-src/${d}`);
45729
+ for (const d of dirs) walk2(join42(root, d), `tier5-src/${d}`);
45373
45730
  return { dirs, files };
45374
45731
  }
45375
45732
  function csv(v) {
@@ -45392,16 +45749,16 @@ async function runTemplateTrim(req, deps) {
45392
45749
  links = req.links;
45393
45750
  }
45394
45751
  const dir = req.dir;
45395
- const devPath = join41(dir, "dev/index.ts");
45396
- const configPath = join41(dir, "config.ts");
45397
- if (!existsSync43(devPath) || !existsSync43(configPath)) {
45752
+ const devPath = join42(dir, "dev/index.ts");
45753
+ const configPath = join42(dir, "config.ts");
45754
+ if (!existsSync44(devPath) || !existsSync44(configPath)) {
45398
45755
  return refuse3(new TrimRefusal(`${dir} has no dev/index.ts + config.ts \u2014 point --content at an app scaffolded with \`himi init --template\``));
45399
45756
  }
45400
45757
  if (isTemplateSourceTree(dir)) {
45401
45758
  return refuse3(new TrimRefusal(`${dir} is a template source tree (its identity is still __HIMI_APP_*__ sentinels) \u2014 trim the app \`himi init --template\` scaffolds from it, not the template itself`));
45402
45759
  }
45403
- const dev = readFileSync47(devPath, "utf8");
45404
- const config = readFileSync47(configPath, "utf8");
45760
+ const dev = readFileSync48(devPath, "utf8");
45761
+ const config = readFileSync48(configPath, "utf8");
45405
45762
  const workers = listWorkers(dir);
45406
45763
  let plan;
45407
45764
  try {
@@ -45411,23 +45768,23 @@ async function runTemplateTrim(req, deps) {
45411
45768
  throw e;
45412
45769
  }
45413
45770
  if (req.dryRun) return { code: 0, result: { ok: true, dryRun: true, ...plan.report, validate: null } };
45414
- const hadHimi = existsSync43(join41(dir, ".himi"));
45415
- const backupRel = join41(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
45416
- const backup = join41(dir, backupRel);
45771
+ const hadHimi = existsSync44(join42(dir, ".himi"));
45772
+ const backupRel = join42(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
45773
+ const backup = join42(dir, backupRel);
45417
45774
  const originals = [[devPath, "dev/index.ts"], [configPath, "config.ts"]];
45418
45775
  const moved = [];
45419
45776
  const restore = () => {
45420
45777
  for (const [path2, rel] of originals) {
45421
- const original = readFileSync47(join41(backup, rel));
45422
- if (!existsSync43(path2) || !readFileSync47(path2).equals(original)) writeFileSync16(path2, original);
45778
+ const original = readFileSync48(join42(backup, rel));
45779
+ if (!existsSync44(path2) || !readFileSync48(path2).equals(original)) writeFileSync16(path2, original);
45423
45780
  }
45424
- for (const name of [...moved].reverse()) renameSync5(join41(backup, "tier5-src", name), join41(dir, "tier5-src", name));
45781
+ for (const name of [...moved].reverse()) renameSync5(join42(backup, "tier5-src", name), join42(dir, "tier5-src", name));
45425
45782
  moved.length = 0;
45426
45783
  };
45427
45784
  const apply = () => {
45428
45785
  const moveDir = deps.moveDir ?? renameSync5;
45429
45786
  for (const name of plan.removedWorkers) {
45430
- moveDir(join41(dir, "tier5-src", name), join41(backup, "tier5-src", name));
45787
+ moveDir(join42(dir, "tier5-src", name), join42(backup, "tier5-src", name));
45431
45788
  moved.push(name);
45432
45789
  }
45433
45790
  if (plan.dev !== dev) writeFileSync16(devPath, plan.dev);
@@ -45435,10 +45792,10 @@ async function runTemplateTrim(req, deps) {
45435
45792
  };
45436
45793
  const dropBackup = () => {
45437
45794
  rmSync8(backup, { recursive: true, force: true });
45438
- for (const d of [dirname25(backup), join41(dir, ".himi")]) {
45439
- if (d === join41(dir, ".himi") && hadHimi) break;
45795
+ for (const d of [dirname25(backup), join42(dir, ".himi")]) {
45796
+ if (d === join42(dir, ".himi") && hadHimi) break;
45440
45797
  try {
45441
- if (!readdirSync23(d).length) rmSync8(d, { recursive: true });
45798
+ if (!readdirSync24(d).length) rmSync8(d, { recursive: true });
45442
45799
  } catch {
45443
45800
  }
45444
45801
  }
@@ -45462,9 +45819,9 @@ async function runTemplateTrim(req, deps) {
45462
45819
  return { code: 1, result: { ok: false, error: `${error} (${message}); every file was restored`, rolledBack: true, ...base(validate4) } };
45463
45820
  };
45464
45821
  try {
45465
- mkdirSync20(join41(backup, "dev"), { recursive: true });
45466
- for (const [path2, rel] of originals) cpSync(path2, join41(backup, rel));
45467
- if (plan.removedWorkers.length) mkdirSync20(join41(backup, "tier5-src"), { recursive: true });
45822
+ mkdirSync20(join42(backup, "dev"), { recursive: true });
45823
+ for (const [path2, rel] of originals) cpSync(path2, join42(backup, rel));
45824
+ if (plan.removedWorkers.length) mkdirSync20(join42(backup, "tier5-src"), { recursive: true });
45468
45825
  } catch (e) {
45469
45826
  dropBackup();
45470
45827
  return { code: 1, result: { ok: false, error: `could not back up the app before trimming it (${e.message}); nothing was changed`, rolledBack: true, ...base() } };
@@ -45477,7 +45834,7 @@ async function runTemplateTrim(req, deps) {
45477
45834
  let failure;
45478
45835
  let validate3;
45479
45836
  try {
45480
- const written = parseApp(readFileSync47(devPath, "utf8"), readFileSync47(configPath, "utf8"));
45837
+ const written = parseApp(readFileSync48(devPath, "utf8"), readFileSync48(configPath, "utf8"));
45481
45838
  const leftovers = leftoverNavRefs(written.nav.value, plan.removedIds);
45482
45839
  if (leftovers.length) failure = { error: "navigation still names screens this trim removed", leftovers };
45483
45840
  else {
@@ -45581,7 +45938,7 @@ function summarize3(errors) {
45581
45938
  const shown2 = errors.slice(0, 5).map(one).join("; ");
45582
45939
  return errors.length > 5 ? `${shown2}; and ${errors.length - 5} more` : shown2;
45583
45940
  }
45584
- var TrimRefusal, LIT, ATOM, QUANT, CAP, SAFE_ROUTE_PATTERN, lineOf, isStatic, entryLabel, isNode3, asItems, NAV_SCREEN_KEYS, sorted, refuse3, plural;
45941
+ var TrimRefusal, LIT, ATOM, QUANT, CAP, SAFE_ROUTE_PATTERN, lineOf2, isStatic, entryLabel, isNode3, asItems, NAV_SCREEN_KEYS, sorted, refuse3, plural;
45585
45942
  var init_template_trim = __esm({
45586
45943
  "src/template-trim.ts"() {
45587
45944
  init_validate_descriptor();
@@ -45601,7 +45958,7 @@ var init_template_trim = __esm({
45601
45958
  SAFE_ROUTE_PATTERN = new RegExp(
45602
45959
  String.raw`^\^${LIT}(?:${CAP}${LIT}|\(\?:(?:${LIT}|\[(?:[^\]\\[]|\\.)+\])${CAP}\)\?)?\$$`
45603
45960
  );
45604
- lineOf = (src, at) => src.slice(0, at).split("\n").length;
45961
+ lineOf2 = (src, at) => src.slice(0, at).split("\n").length;
45605
45962
  isStatic = (id) => typeof id === "string" && id !== "" && !id.startsWith("$");
45606
45963
  entryLabel = (entry) => {
45607
45964
  const id = entry?.id;
@@ -46150,10 +46507,10 @@ function parseZipEntries(buf) {
46150
46507
  }
46151
46508
  }
46152
46509
  if (eocd < 0) throw new Error("not a zip archive (no end-of-central-directory record)");
46153
- const count = u16(eocd + 10);
46510
+ const count2 = u16(eocd + 10);
46154
46511
  let p = u32(eocd + 16);
46155
46512
  const out = [];
46156
- for (let i = 0; i < count; i++) {
46513
+ for (let i = 0; i < count2; i++) {
46157
46514
  if (p + 46 > buf.length || u32(p) !== ZIP_CENTRAL_SIG) throw new Error("corrupt zip central directory");
46158
46515
  const method = u16(p + 10);
46159
46516
  const crc = u32(p + 16);
@@ -46245,8 +46602,8 @@ __export(pack_exports, {
46245
46602
  });
46246
46603
  import { createHash as createHash22 } from "node:crypto";
46247
46604
  import { deflateRawSync, inflateRawSync } from "node:zlib";
46248
- import { existsSync as existsSync44, mkdirSync as mkdirSync21, readFileSync as readFileSync48, readdirSync as readdirSync24, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
46249
- import { dirname as dirname26, join as join42, resolve as resolve37 } from "node:path";
46605
+ import { existsSync as existsSync45, mkdirSync as mkdirSync21, readFileSync as readFileSync49, readdirSync as readdirSync25, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
46606
+ import { dirname as dirname26, join as join43, resolve as resolve37 } from "node:path";
46250
46607
  function writeZip(entries) {
46251
46608
  const sorted2 = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
46252
46609
  const total = sorted2.reduce((n, e) => n + e.bytes.length, 0);
@@ -46340,13 +46697,13 @@ function inflateEntry(buf, entry) {
46340
46697
  return bytes2;
46341
46698
  }
46342
46699
  function readIf(path2) {
46343
- return existsSync44(path2) && statSync11(path2).isFile() ? readFileSync48(path2) : null;
46700
+ return existsSync45(path2) && statSync11(path2).isFile() ? readFileSync49(path2) : null;
46344
46701
  }
46345
46702
  function listDir(dir) {
46346
- return existsSync44(dir) ? readdirSync24(dir).filter((f) => statSync11(join42(dir, f)).isFile()) : [];
46703
+ return existsSync45(dir) ? readdirSync25(dir).filter((f) => statSync11(join43(dir, f)).isFile()) : [];
46347
46704
  }
46348
46705
  function gatherReleaseEntries(appDir2) {
46349
- const manifestPath = join42(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
46706
+ const manifestPath = join43(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
46350
46707
  const manifestBytes = readIf(manifestPath);
46351
46708
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
46352
46709
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -46370,7 +46727,7 @@ function gatherReleaseEntries(appDir2) {
46370
46727
  }
46371
46728
  const missing = [];
46372
46729
  for (const rel of wanted) {
46373
- const bytes2 = readIf(join42(appDir2, rel));
46730
+ const bytes2 = readIf(join43(appDir2, rel));
46374
46731
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
46375
46732
  else missing.push(rel);
46376
46733
  }
@@ -46381,12 +46738,12 @@ function gatherReleaseEntries(appDir2) {
46381
46738
  );
46382
46739
  }
46383
46740
  for (const name of SINGLETONS) {
46384
- const bytes2 = readIf(join42(appDir2, name));
46741
+ const bytes2 = readIf(join43(appDir2, name));
46385
46742
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
46386
46743
  }
46387
46744
  const skipped = [];
46388
46745
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
46389
- for (const f of listDir(join42(appDir2, sub))) {
46746
+ for (const f of listDir(join43(appDir2, sub))) {
46390
46747
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
46391
46748
  }
46392
46749
  }
@@ -46419,18 +46776,18 @@ function buildEntries(appDir2, app, opts) {
46419
46776
  return { all, header, manifest, skipped };
46420
46777
  }
46421
46778
  function packRelease(releaseRoot, app, out, opts = {}) {
46422
- const appDir2 = join42(resolve37(releaseRoot), app);
46779
+ const appDir2 = join43(resolve37(releaseRoot), app);
46423
46780
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
46424
46781
  const buf = writeZip(all);
46425
- const isDir2 = existsSync44(out) && statSync11(out).isDirectory();
46426
- const outFile = isDir2 ? join42(resolve37(out), packageFileName(app, header.releaseId)) : resolve37(out);
46782
+ const isDir2 = existsSync45(out) && statSync11(out).isDirectory();
46783
+ const outFile = isDir2 ? join43(resolve37(out), packageFileName(app, header.releaseId)) : resolve37(out);
46427
46784
  mkdirSync21(dirname26(outFile), { recursive: true });
46428
46785
  writeFileSync17(outFile, buf);
46429
46786
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
46430
46787
  }
46431
46788
  function resolvePackageDirDest(outDir, app, releaseId) {
46432
46789
  const packagedApp = (dir) => {
46433
- const bytes2 = readIf(join42(dir, HIMI_PACKAGE_HEADER_PATH));
46790
+ const bytes2 = readIf(join43(dir, HIMI_PACKAGE_HEADER_PATH));
46434
46791
  if (!bytes2) return void 0;
46435
46792
  try {
46436
46793
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -46439,15 +46796,15 @@ function resolvePackageDirDest(outDir, app, releaseId) {
46439
46796
  }
46440
46797
  };
46441
46798
  const replaceable = (dir) => {
46442
- if (!existsSync44(dir)) return true;
46799
+ if (!existsSync45(dir)) return true;
46443
46800
  if (!statSync11(dir).isDirectory()) return false;
46444
- return readdirSync24(dir).length === 0 || packagedApp(dir) === app;
46801
+ return readdirSync25(dir).length === 0 || packagedApp(dir) === app;
46445
46802
  };
46446
- if (existsSync44(outDir) && !statSync11(outDir).isDirectory()) {
46803
+ if (existsSync45(outDir) && !statSync11(outDir).isDirectory()) {
46447
46804
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
46448
46805
  }
46449
46806
  if (replaceable(outDir)) return outDir;
46450
- const child = join42(outDir, packageDirName(app, releaseId));
46807
+ const child = join43(outDir, packageDirName(app, releaseId));
46451
46808
  if (!replaceable(child)) {
46452
46809
  throw new Error(
46453
46810
  `refusing to replace ${child}: it is not empty and is not a ${app} package (no ${HIMI_PACKAGE_HEADER_PATH}). Pass --out to a new or empty directory.`
@@ -46461,13 +46818,13 @@ function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
46461
46818
  "a package carrying the app's source has no --format dir form: readers that predate the source entry drop it before hashing a directory and refuse the package as tampered. Pack it as a .himi archive."
46462
46819
  );
46463
46820
  }
46464
- const appDir2 = join42(resolve37(releaseRoot), app);
46821
+ const appDir2 = join43(resolve37(releaseRoot), app);
46465
46822
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
46466
46823
  const dest = resolvePackageDirDest(resolve37(outDir), app, header.releaseId);
46467
46824
  rmSync9(dest, { recursive: true, force: true });
46468
46825
  let bytes2 = 0;
46469
46826
  for (const entry of all) {
46470
- const target = join42(dest, entry.path);
46827
+ const target = join43(dest, entry.path);
46471
46828
  mkdirSync21(dirname26(target), { recursive: true });
46472
46829
  writeFileSync17(target, entry.bytes);
46473
46830
  bytes2 += entry.bytes.length;
@@ -46490,21 +46847,21 @@ function openPackageBuffer(buf) {
46490
46847
  return openFromEntries(readZip(buf), { requireHeader: true });
46491
46848
  }
46492
46849
  function openPackageFile(file) {
46493
- return openPackageBuffer(readFileSync48(file));
46850
+ return openPackageBuffer(readFileSync49(file));
46494
46851
  }
46495
46852
  function openPackageDir(dir) {
46496
46853
  const root = resolve37(dir);
46497
46854
  const entries = /* @__PURE__ */ new Map();
46498
46855
  const walk2 = (rel) => {
46499
- const abs = rel ? join42(root, rel) : root;
46500
- for (const name of readdirSync24(abs)) {
46856
+ const abs = rel ? join43(root, rel) : root;
46857
+ for (const name of readdirSync25(abs)) {
46501
46858
  const childRel = rel ? `${rel}/${name}` : name;
46502
- const childAbs = join42(abs, name);
46859
+ const childAbs = join43(abs, name);
46503
46860
  if (statSync11(childAbs).isDirectory()) walk2(childRel);
46504
- else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync48(childAbs));
46861
+ else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync49(childAbs));
46505
46862
  }
46506
46863
  };
46507
- if (!existsSync44(root)) throw new Error(`no such package directory: ${root}`);
46864
+ if (!existsSync45(root)) throw new Error(`no such package directory: ${root}`);
46508
46865
  walk2("");
46509
46866
  const manifestBytes = entries.get(HIMI_PACKAGE_MANIFEST_PATH);
46510
46867
  if (manifestBytes) {
@@ -46546,10 +46903,10 @@ function unpackTo(buf, dest, appFallback) {
46546
46903
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
46547
46904
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
46548
46905
  const root = resolve37(dest);
46549
- const appDir2 = join42(root, app);
46906
+ const appDir2 = join43(root, app);
46550
46907
  rmSync9(appDir2, { recursive: true, force: true });
46551
46908
  for (const [rel, bytes2] of entries) {
46552
- const target = join42(appDir2, rel);
46909
+ const target = join43(appDir2, rel);
46553
46910
  mkdirSync21(dirname26(target), { recursive: true });
46554
46911
  writeFileSync17(target, bytes2);
46555
46912
  }
@@ -46585,20 +46942,20 @@ __export(app_source_exports, {
46585
46942
  writeConflicts: () => writeConflicts,
46586
46943
  zipAppSource: () => zipAppSource
46587
46944
  });
46588
- import { readdirSync as readdirSync25, lstatSync as lstatSync8, readFileSync as readFileSync49, mkdirSync as mkdirSync22, writeFileSync as writeFileSync18, existsSync as existsSync45, rmSync as rmSync10, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
46589
- import { join as join43, resolve as resolve38, sep as sep10 } from "node:path";
46945
+ import { readdirSync as readdirSync26, lstatSync as lstatSync8, readFileSync as readFileSync50, mkdirSync as mkdirSync22, writeFileSync as writeFileSync18, existsSync as existsSync46, rmSync as rmSync10, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
46946
+ import { join as join44, resolve as resolve38, sep as sep10 } from "node:path";
46590
46947
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
46591
46948
  function walkSourceTree(dir, file, skip) {
46592
46949
  const walk2 = (relDir) => {
46593
- for (const name of readdirSync25(join43(dir, relDir))) {
46594
- const rel = relDir ? join43(relDir, name) : name;
46595
- const st = lstatSync8(join43(dir, rel));
46950
+ for (const name of readdirSync26(join44(dir, relDir))) {
46951
+ const rel = relDir ? join44(relDir, name) : name;
46952
+ const st = lstatSync8(join44(dir, rel));
46596
46953
  if (st.isSymbolicLink()) {
46597
46954
  skip(rel);
46598
46955
  continue;
46599
46956
  }
46600
46957
  if (st.isDirectory()) {
46601
- if (isDeniedSourceDir(name, relDir) || existsSync45(join43(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
46958
+ if (isDeniedSourceDir(name, relDir) || existsSync46(join44(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
46602
46959
  else walk2(rel);
46603
46960
  continue;
46604
46961
  }
@@ -46616,7 +46973,7 @@ function collectAppSource(dir) {
46616
46973
  const skipped = [];
46617
46974
  walkSourceTree(
46618
46975
  dir,
46619
- (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync49(join43(dir, hostRel)) }),
46976
+ (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync50(join44(dir, hostRel)) }),
46620
46977
  (hostRel) => skipped.push(hostRel)
46621
46978
  );
46622
46979
  entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -46670,13 +47027,13 @@ function writeAppSource(entries, destDir) {
46670
47027
  const segments = entry.path.split("/");
46671
47028
  let dir = root;
46672
47029
  for (const segment of segments.slice(0, -1)) {
46673
- dir = join43(dir, segment);
47030
+ dir = join44(dir, segment);
46674
47031
  const kind = pathKind(dir);
46675
47032
  if (kind?.isSymbolicLink()) unlinkSync3(dir);
46676
47033
  else if (kind) continue;
46677
47034
  mkdirSync22(dir);
46678
47035
  }
46679
- const target = join43(root, ...segments);
47036
+ const target = join44(root, ...segments);
46680
47037
  if (pathKind(target)?.isSymbolicLink()) unlinkSync3(target);
46681
47038
  writeFileSync18(target, entry.bytes);
46682
47039
  written.push(entry.path);
@@ -46684,7 +47041,7 @@ function writeAppSource(entries, destDir) {
46684
47041
  return written;
46685
47042
  }
46686
47043
  function staleSourcePaths(entries, destDir) {
46687
- if (!existsSync45(destDir)) return [];
47044
+ if (!existsSync46(destDir)) return [];
46688
47045
  const archived = new Set(entries.map((e) => e.path));
46689
47046
  const stale = [];
46690
47047
  walkSourceTree(destDir, (archivePath) => {
@@ -46711,7 +47068,7 @@ function writeConflicts(entries, destDir) {
46711
47068
  let dir = root;
46712
47069
  let stop = false;
46713
47070
  for (const [i, segment] of segments.slice(0, -1).entries()) {
46714
- dir = join43(dir, segment);
47071
+ dir = join44(dir, segment);
46715
47072
  const kind2 = pathKind(dir);
46716
47073
  if (!kind2 || kind2.isSymbolicLink()) {
46717
47074
  stop = true;
@@ -46723,7 +47080,7 @@ function writeConflicts(entries, destDir) {
46723
47080
  break;
46724
47081
  }
46725
47082
  if (stop) continue;
46726
- const kind = pathKind(join43(root, ...segments));
47083
+ const kind = pathKind(join44(root, ...segments));
46727
47084
  if (kind && !kind.isSymbolicLink() && kind.isDirectory()) {
46728
47085
  say(entry.path, "is a directory here, but the release has a file at that path");
46729
47086
  }
@@ -46733,22 +47090,22 @@ function writeConflicts(entries, destDir) {
46733
47090
  function removeStaleSource(entries, destDir) {
46734
47091
  const root = resolve38(destDir);
46735
47092
  const removed = staleSourcePaths(entries, root);
46736
- for (const rel of removed) rmSync10(join43(root, ...rel.split("/")), { force: true });
47093
+ for (const rel of removed) rmSync10(join44(root, ...rel.split("/")), { force: true });
46737
47094
  const needed = /* @__PURE__ */ new Set();
46738
47095
  for (const entry of entries) {
46739
47096
  if (isDeniedSourcePath(entry.path)) continue;
46740
47097
  const segments = entry.path.split("/");
46741
47098
  for (let i = 1; i < segments.length; i++) needed.add(segments.slice(0, i).join("/"));
46742
47099
  }
46743
- if (existsSync45(root)) pruneEmptyDirs(root, "", needed, removed);
47100
+ if (existsSync46(root)) pruneEmptyDirs(root, "", needed, removed);
46744
47101
  return removed.sort();
46745
47102
  }
46746
47103
  function pruneEmptyDirs(root, rel, needed, removed) {
46747
47104
  let empty = true;
46748
- for (const name of readdirSync25(rel ? join43(root, rel) : root)) {
46749
- const childRel = rel ? join43(rel, name) : name;
47105
+ for (const name of readdirSync26(rel ? join44(root, rel) : root)) {
47106
+ const childRel = rel ? join44(rel, name) : name;
46750
47107
  const posix4 = childRel.split(sep10).join("/");
46751
- const kind = lstatSync8(join43(root, childRel));
47108
+ const kind = lstatSync8(join44(root, childRel));
46752
47109
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name, rel)) {
46753
47110
  empty = false;
46754
47111
  continue;
@@ -46758,7 +47115,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
46758
47115
  empty = false;
46759
47116
  continue;
46760
47117
  }
46761
- rmdirSync2(join43(root, childRel));
47118
+ rmdirSync2(join44(root, childRel));
46762
47119
  removed.push(`${posix4}/`);
46763
47120
  }
46764
47121
  return empty;
@@ -46813,13 +47170,13 @@ __export(eject_exports, {
46813
47170
  EJECTABLE: () => EJECTABLE,
46814
47171
  ejectModule: () => ejectModule
46815
47172
  });
46816
- import { existsSync as existsSync46, mkdirSync as mkdirSync23, readFileSync as readFileSync50, writeFileSync as writeFileSync19 } from "node:fs";
46817
- import { dirname as dirname27, join as join44, relative as relative8, resolve as resolve39, sep as sep11 } from "node:path";
47173
+ import { existsSync as existsSync47, mkdirSync as mkdirSync23, readFileSync as readFileSync51, writeFileSync as writeFileSync19 } from "node:fs";
47174
+ import { dirname as dirname27, join as join45, relative as relative8, resolve as resolve39, sep as sep11 } from "node:path";
46818
47175
  function locate(repoRootOrCwd, mod) {
46819
47176
  const inRepo = resolve39(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
46820
- if (existsSync46(inRepo)) return { path: inRepo, form: "source" };
47177
+ if (existsSync47(inRepo)) return { path: inRepo, form: "source" };
46821
47178
  const dist = resolve39(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
46822
- if (existsSync46(dist)) return { path: dist, form: "bundle" };
47179
+ if (existsSync47(dist)) return { path: dist, form: "bundle" };
46823
47180
  return null;
46824
47181
  }
46825
47182
  function specifiersFor(mod) {
@@ -46843,7 +47200,7 @@ function ejectModule(opts) {
46843
47200
  };
46844
47201
  }
46845
47202
  const ext = found.form === "source" ? "ts" : "mjs";
46846
- const target = join44(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
47203
+ const target = join45(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
46847
47204
  mkdirSync23(dirname27(target), { recursive: true });
46848
47205
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
46849
47206
  //
@@ -46857,15 +47214,15 @@ function ejectModule(opts) {
46857
47214
  // gate still applies. Ejecting buys layout and flow, never the floor.
46858
47215
 
46859
47216
  `;
46860
- const normalized = readFileSync50(found.path, "utf8").replace(
47217
+ const normalized = readFileSync51(found.path, "utf8").replace(
46861
47218
  /(["'])@himalaya\/([a-z0-9-]+(?:\/[a-z0-9-]+)?)\1/g,
46862
47219
  (_m, q, sub) => `${q}@wix/himalaya/${sub}${q}`
46863
47220
  );
46864
47221
  writeFileSync19(target, banner + normalized);
46865
47222
  const rewired = [];
46866
47223
  for (const f of files) {
46867
- if (!existsSync46(f)) continue;
46868
- const before = readFileSync50(f, "utf8");
47224
+ if (!existsSync47(f)) continue;
47225
+ const before = readFileSync51(f, "utf8");
46869
47226
  let after = before;
46870
47227
  let rel = relative8(dirname27(f), target).split(sep11).join("/").replace(/\.tsx?$/, ".js");
46871
47228
  if (!rel.startsWith(".")) rel = `./${rel}`;
@@ -46897,9 +47254,96 @@ var init_eject = __esm({
46897
47254
  }
46898
47255
  });
46899
47256
 
47257
+ // src/app-map.ts
47258
+ function buildAppMap(input) {
47259
+ const declared = [...new Set(input.declared)].slice(0, MAX_SCREENS);
47260
+ const known = new Set(declared);
47261
+ let entries = [...new Set(input.entries)].filter((id) => known.has(id));
47262
+ if (!entries.length && declared.length) entries = [declared[0]];
47263
+ const entrySet = new Set(entries);
47264
+ const all = [];
47265
+ const broken = [];
47266
+ const seen = /* @__PURE__ */ new Set();
47267
+ const crawled = /* @__PURE__ */ new Map();
47268
+ const unfollowed = /* @__PURE__ */ new Map();
47269
+ for (const r of input.results) {
47270
+ crawled.set(r.screen, (crawled.get(r.screen) ?? false) || r.failed);
47271
+ if (r.navUnfollowed) unfollowed.set(r.screen, Math.max(unfollowed.get(r.screen) ?? 0, r.navUnfollowed));
47272
+ for (const e of r.navEdges ?? []) {
47273
+ const to = known.has(e.target) ? e.target : baseOf(e.target, input.linkable, known);
47274
+ if (!to) {
47275
+ const key3 = `${r.screen}>!${e.target}`;
47276
+ if (input.linkable?.size && !resolvesToLinkable(e.target, input.linkable) && !seen.has(key3)) {
47277
+ seen.add(key3);
47278
+ broken.push({ from: r.screen, to: e.target, via: e.via });
47279
+ }
47280
+ continue;
47281
+ }
47282
+ const key2 = `${r.screen}>${to}`;
47283
+ if (seen.has(key2)) continue;
47284
+ seen.add(key2);
47285
+ all.push({ from: r.screen, to, via: e.via });
47286
+ }
47287
+ }
47288
+ const reached = new Set(entries);
47289
+ const out = /* @__PURE__ */ new Map();
47290
+ for (const e of all) {
47291
+ const next = out.get(e.from);
47292
+ if (next) next.push(e.to);
47293
+ else out.set(e.from, [e.to]);
47294
+ }
47295
+ for (const queue = [...entries]; queue.length; ) {
47296
+ for (const next of out.get(queue.shift()) ?? []) {
47297
+ if (reached.has(next)) continue;
47298
+ reached.add(next);
47299
+ queue.push(next);
47300
+ }
47301
+ }
47302
+ const screens = declared.map((id) => ({
47303
+ id,
47304
+ entry: entrySet.has(id),
47305
+ reached: reached.has(id),
47306
+ status: crawled.has(id) ? crawled.get(id) ? "failed" : "ok" : input.done ? "skipped" : "pending",
47307
+ unfollowed: unfollowed.get(id) ?? 0
47308
+ }));
47309
+ const totalUnfollowed = [...unfollowed.values()].reduce((a, b) => a + b, 0);
47310
+ return {
47311
+ screens,
47312
+ edges: all.slice(0, MAX_EDGES),
47313
+ broken,
47314
+ unreached: declared.filter((id) => !reached.has(id)),
47315
+ unfollowed: totalUnfollowed,
47316
+ edgesDropped: Math.max(0, all.length - MAX_EDGES),
47317
+ done: input.done
47318
+ };
47319
+ }
47320
+ function resolvesToLinkable(target, linkable) {
47321
+ if (linkable.has(target)) return true;
47322
+ for (const base of linkable) {
47323
+ if (target.length > base.length + 1 && target.startsWith(base) && (target[base.length] === "_" || target[base.length] === "/")) return true;
47324
+ }
47325
+ return false;
47326
+ }
47327
+ function baseOf(target, linkable, known) {
47328
+ let best = null;
47329
+ for (const base of linkable ?? []) {
47330
+ if (!known.has(base) || target.length <= base.length + 1 || !target.startsWith(base)) continue;
47331
+ const sep15 = target[base.length];
47332
+ if ((sep15 === "_" || sep15 === "/") && (!best || base.length > best.length)) best = base;
47333
+ }
47334
+ return best;
47335
+ }
47336
+ var MAX_SCREENS, MAX_EDGES;
47337
+ var init_app_map = __esm({
47338
+ "src/app-map.ts"() {
47339
+ MAX_SCREENS = 300;
47340
+ MAX_EDGES = 600;
47341
+ }
47342
+ });
47343
+
46900
47344
  // src/wix-writes.ts
46901
- import { existsSync as existsSync47, readFileSync as readFileSync51 } from "node:fs";
46902
- import { join as join45 } from "node:path";
47345
+ import { existsSync as existsSync48, readFileSync as readFileSync52 } from "node:fs";
47346
+ import { join as join46 } from "node:path";
46903
47347
  function isProvenRead(method, path2) {
46904
47348
  if (/[#\x00-\x1F\x7F]/.test(path2)) return false;
46905
47349
  const verb = method.toUpperCase();
@@ -46980,11 +47424,11 @@ function parseDataAccess(text5, from) {
46980
47424
  }
46981
47425
  function readDataAccess(dir) {
46982
47426
  for (const name of ["SPEC.md", "MOBILE-UX.md"]) {
46983
- const p = join45(dir, name);
46984
- if (!existsSync47(p)) continue;
47427
+ const p = join46(dir, name);
47428
+ if (!existsSync48(p)) continue;
46985
47429
  let text5;
46986
47430
  try {
46987
- text5 = readFileSync51(p, "utf8");
47431
+ text5 = readFileSync52(p, "utf8");
46988
47432
  } catch {
46989
47433
  continue;
46990
47434
  }
@@ -47091,14 +47535,14 @@ __export(test_exports, {
47091
47535
  scopeDescriptors: () => scopeDescriptors,
47092
47536
  writeRecordedMocks: () => writeRecordedMocks
47093
47537
  });
47094
- import { existsSync as existsSync48, mkdirSync as mkdirSync24, readFileSync as readFileSync52, writeFileSync as writeFileSync20 } from "node:fs";
47095
- import { join as join46, resolve as resolve40 } from "node:path";
47538
+ import { existsSync as existsSync49, mkdirSync as mkdirSync24, readFileSync as readFileSync53, writeFileSync as writeFileSync20 } from "node:fs";
47539
+ import { join as join47, resolve as resolve40 } from "node:path";
47096
47540
  function readTestConfig(dir) {
47097
47541
  const p = resolve40(dir, TEST_CONFIG_FILE);
47098
- if (!existsSync48(p)) return {};
47542
+ if (!existsSync49(p)) return {};
47099
47543
  let parsed;
47100
47544
  try {
47101
- parsed = JSON.parse(readFileSync52(p, "utf8"));
47545
+ parsed = JSON.parse(readFileSync53(p, "utf8"));
47102
47546
  } catch (e) {
47103
47547
  throw new Error(`${TEST_CONFIG_FILE} is not valid JSON \u2014 ${e.message}`);
47104
47548
  }
@@ -47145,7 +47589,7 @@ function readTestConfig(dir) {
47145
47589
  function readNetRules(path2) {
47146
47590
  let parsed;
47147
47591
  try {
47148
- parsed = JSON.parse(readFileSync52(path2, "utf8"));
47592
+ parsed = JSON.parse(readFileSync53(path2, "utf8"));
47149
47593
  } catch (e) {
47150
47594
  throw new Error(`--net ${path2} could not be read as JSON \u2014 ${e.message}`);
47151
47595
  }
@@ -47166,7 +47610,7 @@ function readNetRules(path2) {
47166
47610
  function defaultNetRules(dir) {
47167
47611
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
47168
47612
  const p = resolve40(dir, rel);
47169
- if (existsSync48(p)) {
47613
+ if (existsSync49(p)) {
47170
47614
  const rules = readNetRules(p);
47171
47615
  if (rules.length) return { rules, from: rel };
47172
47616
  }
@@ -47182,13 +47626,13 @@ function readCannedResponses() {
47182
47626
  ];
47183
47627
  for (const url of candidates) {
47184
47628
  try {
47185
- const parsed = JSON.parse(readFileSync52(url, "utf8"));
47629
+ const parsed = JSON.parse(readFileSync53(url, "utf8"));
47186
47630
  const byKey = {};
47187
47631
  for (const [k, v] of Object.entries(parsed)) {
47188
47632
  if (!k.startsWith("//")) byKey[k] = v;
47189
47633
  }
47190
- const count = Object.keys(byKey).length;
47191
- if (count) return { byKey, count, from: url.pathname.includes("web-renderer") ? "in-repo fixture" : "bundled fixture" };
47634
+ const count2 = Object.keys(byKey).length;
47635
+ if (count2) return { byKey, count: count2, from: url.pathname.includes("web-renderer") ? "in-repo fixture" : "bundled fixture" };
47192
47636
  } catch {
47193
47637
  }
47194
47638
  }
@@ -47272,8 +47716,8 @@ function writeRecordedMocks(opts) {
47272
47716
  mkdir: (p) => mkdirSync24(p, { recursive: true }),
47273
47717
  write: (p, s) => writeFileSync20(p, s)
47274
47718
  };
47275
- fs.mkdir(join46(opts.dir, "dev"));
47276
- fs.write(join46(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
47719
+ fs.mkdir(join47(opts.dir, "dev"));
47720
+ fs.write(join47(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
47277
47721
  return { ok: true, rules, to: "dev/net-mocks.json" };
47278
47722
  }
47279
47723
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -47310,6 +47754,15 @@ async function runShotsPass(opts) {
47310
47754
  }
47311
47755
  return { dir: opts.outDir, written };
47312
47756
  }
47757
+ function withCovered(rows, coveredBy) {
47758
+ const byScreen = new Map(rows.map((r) => [r.screen, r]));
47759
+ const out = [...rows];
47760
+ for (const [instance, representative] of coveredBy) {
47761
+ const r = byScreen.get(representative);
47762
+ if (r && !byScreen.has(instance)) out.push({ ...r, screen: instance, navEdges: [], navUnfollowed: 0 });
47763
+ }
47764
+ return out;
47765
+ }
47313
47766
  async function runCrawl(opts) {
47314
47767
  const { descriptors, deps } = opts;
47315
47768
  const localDbSchema = await localDbEntities(opts.dir);
@@ -47320,6 +47773,7 @@ async function runCrawl(opts) {
47320
47773
  const notCrawled = descriptors.filter((d) => (!only || only.has(d.screenId)) && invalid.has(d.screenId)).map((d) => ({ screen: d.screenId, why: "descriptor fails `himi validate` \u2014 fix that first, then this screen is crawled too" }));
47321
47774
  const requested = descriptors.filter((d) => (!only || only.has(d.screenId)) && !invalid.has(d.screenId));
47322
47775
  const collapsed = [];
47776
+ const coveredBy = /* @__PURE__ */ new Map();
47323
47777
  let targets = requested;
47324
47778
  if (!opts.allScreens) {
47325
47779
  const groups = /* @__PURE__ */ new Map();
@@ -47334,6 +47788,7 @@ async function runCrawl(opts) {
47334
47788
  const rep = list.find((d) => Object.keys(d.descriptor?.input ?? {}).length > 0) ?? list[0];
47335
47789
  targets.push(rep);
47336
47790
  if (list.length > 1) collapsed.push({ representative: rep.screenId, instances: list.length });
47791
+ for (const d of list) if (d.screenId !== rep.screenId) coveredBy.set(d.screenId, rep.screenId);
47337
47792
  }
47338
47793
  }
47339
47794
  const net = opts.offline ? null : opts.netPath ? { rules: readNetRules(resolve40(opts.netPath)), from: opts.netPath } : defaultNetRules(opts.dir);
@@ -47379,6 +47834,12 @@ async function runCrawl(opts) {
47379
47834
  const cov = baked.bundleJs && deps.startWorkerCoverage ? await deps.startWorkerCoverage() : null;
47380
47835
  const r = await deps.crawlScreen(args);
47381
47836
  results.push(r);
47837
+ if (opts.onScreen) {
47838
+ try {
47839
+ opts.onScreen(buildAppMap({ declared: [...knownScreenIds], entries: opts.mapEntries ?? opts.tabRoots ?? [], ...opts.linkableScreenIds?.size ? { linkable: opts.linkableScreenIds } : {}, results: withCovered(results.map(mapResult), coveredBy), done: false }));
47840
+ } catch {
47841
+ }
47842
+ }
47382
47843
  writes.push(...writesFromEffects(baked.screenId, r.effects));
47383
47844
  for (const journey of opts.config?.journeys?.filter((candidate) => candidate.screen === baked.screenId) ?? []) {
47384
47845
  if (!deps.crawlScreenJourney) {
@@ -47587,6 +48048,8 @@ async function runCrawl(opts) {
47587
48048
  ...bundleOf.get(r.screen)?.bundleMapWhy ? { sourcemap: bundleOf.get(r.screen).bundleMapWhy } : {}
47588
48049
  })),
47589
48050
  ...edgesFollowed.length ? { edges: edgesFollowed } : {},
48051
+ // Final statuses come from the whole run's failures (hostile, deep, journeys), not one pass.
48052
+ navGraph: buildAppMap({ declared: [...knownScreenIds], entries: opts.mapEntries ?? opts.tabRoots ?? [], ...opts.linkableScreenIds?.size ? { linkable: opts.linkableScreenIds } : {}, results: withCovered(results.map((r) => ({ ...mapResult(r), failed: failedScreens.has(r.screen) })), coveredBy), done: true }),
47590
48053
  ...edgesCapped.length ? { edgesCapped } : {},
47591
48054
  ...retired.length ? { retired } : {},
47592
48055
  ...opts.deep && opts.deep >= 2 ? { deep: { depth: opts.deep, ...deepTotals } } : {},
@@ -47739,13 +48202,15 @@ function formatReport(r) {
47739
48202
  }
47740
48203
  return out.join("\n");
47741
48204
  }
47742
- var TEST_CONFIG_FILE, triggerLabel;
48205
+ var TEST_CONFIG_FILE, mapResult, triggerLabel;
47743
48206
  var init_test = __esm({
47744
48207
  "src/test.ts"() {
48208
+ init_app_map();
47745
48209
  init_wix_gateway_rules();
47746
48210
  init_crawl_layers();
47747
48211
  init_wix_writes();
47748
48212
  TEST_CONFIG_FILE = "himi-test.json";
48213
+ mapResult = (r) => ({ screen: r.screen, navEdges: r.navEdges ?? [], navUnfollowed: r.navUnfollowed ?? 0, failed: (r.findings ?? []).some((x) => x.severity === "error") });
47749
48214
  triggerLabel = (f) => (f.trigger === "boot" ? "on appear" : `${f.trigger.key} ${f.trigger.tap ?? "(no id)"} \u2192 ${f.trigger.action}`) + (f.axis ? ` [${f.axis}]` : "");
47750
48215
  }
47751
48216
  });
@@ -47755,14 +48220,14 @@ var mobile_ux_lint_exports = {};
47755
48220
  __export(mobile_ux_lint_exports, {
47756
48221
  mobileUxIssues: () => mobileUxIssues
47757
48222
  });
47758
- import { existsSync as existsSync49, readFileSync as readFileSync53 } from "node:fs";
47759
- import { join as join47 } from "node:path";
48223
+ import { existsSync as existsSync50, readFileSync as readFileSync54 } from "node:fs";
48224
+ import { join as join48 } from "node:path";
47760
48225
  function mobileUxIssues(dir, strict) {
47761
- const file = join47(dir, "MOBILE-UX.md");
47762
- if (!existsSync49(file)) return [];
48226
+ const file = join48(dir, "MOBILE-UX.md");
48227
+ if (!existsSync50(file)) return [];
47763
48228
  let text5;
47764
48229
  try {
47765
- text5 = readFileSync53(file, "utf8");
48230
+ text5 = readFileSync54(file, "utf8");
47766
48231
  } catch {
47767
48232
  return [];
47768
48233
  }
@@ -49437,8 +49902,8 @@ __export(functions_exports, {
49437
49902
  resolveFunctionsTarget: () => resolveFunctionsTarget,
49438
49903
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
49439
49904
  });
49440
- import { readFileSync as readFileSync54, existsSync as existsSync50 } from "node:fs";
49441
- import { join as join48, resolve as resolve41 } from "node:path";
49905
+ import { readFileSync as readFileSync55, existsSync as existsSync51 } from "node:fs";
49906
+ import { join as join49, resolve as resolve41 } from "node:path";
49442
49907
  function resolveFunctionsTarget(input) {
49443
49908
  const explicitUrl = input.functionsUrl?.trim();
49444
49909
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -49457,9 +49922,9 @@ async function loadFunctionsApp(dir) {
49457
49922
  }
49458
49923
  const sources = {};
49459
49924
  for (const definition of config.functions.functions) {
49460
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join48(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync50);
49925
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join49(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync51);
49461
49926
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
49462
- sources[definition.name] = readFileSync54(source, "utf8");
49927
+ sources[definition.name] = readFileSync55(source, "utf8");
49463
49928
  }
49464
49929
  const data = config.cms || config.data ? mergeDataResources(config) : void 0;
49465
49930
  const dataErrors = validateDataResources(data, loaded.name).filter((issue2) => issue2.severity === "error");
@@ -49619,10 +50084,10 @@ __export(studio_reel_exports, {
49619
50084
  utcDay: () => utcDay
49620
50085
  });
49621
50086
  import { createHash as createHash25 } from "node:crypto";
49622
- import { lstatSync as lstatSync9, readFileSync as readFileSync55 } from "node:fs";
49623
- import { basename as basename12, join as join49 } from "node:path";
50087
+ import { lstatSync as lstatSync9, readFileSync as readFileSync56 } from "node:fs";
50088
+ import { basename as basename12, join as join50 } from "node:path";
49624
50089
  function loadMemory2(dir) {
49625
- const m = readJson4(join49(dir, ".himi", "studio", "memory.json"));
50090
+ const m = readJson5(join50(dir, ".himi", "studio", "memory.json"));
49626
50091
  if (!isObj2(m) || m.version !== 1 || !isObj2(m.shots) || !Array.isArray(m.timeline)) return null;
49627
50092
  const shots = [];
49628
50093
  for (const [id, s] of Object.entries(m.shots)) {
@@ -49659,8 +50124,8 @@ function planReel(dir) {
49659
50124
  const memory = loadMemory2(dir);
49660
50125
  const { entries } = readProgress(dir);
49661
50126
  const summary = summarizeProgress(entries);
49662
- const manifest = readJson4(join49(dir, "himalaya.content.json"));
49663
- const tokens = readJson4(join49(dir, "tokens.json"));
50127
+ const manifest = readJson5(join50(dir, "himalaya.content.json"));
50128
+ const tokens = readJson5(join50(dir, "tokens.json"));
49664
50129
  const accentToken = tokens?.colors?.accentPrimary;
49665
50130
  const accentRaw = typeof accentToken === "string" ? accentToken : accentToken?.light;
49666
50131
  const accent = typeof accentRaw === "string" && ACCENT.test(accentRaw) ? accentRaw : DEFAULT_ACCENT;
@@ -49702,10 +50167,10 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
49702
50167
  const verified = (f) => {
49703
50168
  if (!HISTORY_NAME.test(f)) return null;
49704
50169
  try {
49705
- const path2 = join49(dir, HISTORY_DIR, f);
50170
+ const path2 = join50(dir, HISTORY_DIR, f);
49706
50171
  const st = lstatSync9(path2);
49707
50172
  if (!st.isFile() || st.nlink !== 1) return null;
49708
- const bytes2 = readFileSync55(path2);
50173
+ const bytes2 = readFileSync56(path2);
49709
50174
  return createHash25("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
49710
50175
  } catch {
49711
50176
  return null;
@@ -49778,7 +50243,7 @@ function sinceStart(start, ts2) {
49778
50243
  }
49779
50244
  function renderReel(plan, images) {
49780
50245
  const img = (f, alt, cls = "") => f && images.has(f) ? `<img class="${cls}" src="${images.get(f)}" alt="${escapeHtml(alt)}">` : "";
49781
- const count = (n) => escapeHtml(String(Number.isSafeInteger(n) && n >= 0 ? n : 0));
50246
+ const count2 = (n) => escapeHtml(String(Number.isSafeInteger(n) && n >= 0 ? n : 0));
49782
50247
  const accent = ACCENT.test(plan.accent) ? plan.accent : DEFAULT_ACCENT;
49783
50248
  const clock = (ts2) => sinceStart(plan.startedAt, ts2);
49784
50249
  const frames = plan.frames.filter((f) => images.has(f.file));
@@ -49804,14 +50269,14 @@ figcaption{font-size:12px;color:var(--muted);margin-top:6px;word-break:break-all
49804
50269
  footer{margin-top:48px;font-size:12px;color:var(--muted)}
49805
50270
  </style></head><body><div class="wrap">
49806
50271
  <h1>${escapeHtml(plan.app)}</h1><div class="muted">The making of \u2014 every version of the app as it was built${plan.startedAt ? `, ${escapeHtml(utcDay(plan.startedAt))}` : ""}</div>
49807
- <div class="stats"><div><b>${duration(plan.stats.elapsedMs)}</b><span class="muted">from first picture to last</span></div><div><b>${count(plan.stats.screens)}</b><span class="muted">screens</span></div><div><b>${count(plan.stats.versions)}</b><span class="muted">versions rendered</span></div><div><b>${count(plan.stats.postcards)}</b><span class="muted">postcards</span></div></div>
50272
+ <div class="stats"><div><b>${duration(plan.stats.elapsedMs)}</b><span class="muted">from first picture to last</span></div><div><b>${count2(plan.stats.screens)}</b><span class="muted">screens</span></div><div><b>${count2(plan.stats.versions)}</b><span class="muted">versions rendered</span></div><div><b>${count2(plan.stats.postcards)}</b><span class="muted">postcards</span></div></div>
49808
50273
  <h2>${escapeHtml(plan.focusId ?? "The app")}, version by version</h2>
49809
50274
  ${flip}
49810
50275
  <h2>Where it ended up</h2>
49811
50276
  <div class="grid">${plan.finals.map((f) => `<figure>${img(f.file, f.id)}<figcaption>${escapeHtml(f.id)}</figcaption></figure>`).join("")}</div>
49812
50277
  <h2>Postcards</h2>
49813
50278
  <ul class="log">${plan.postcards.map((c) => `<li><div class="t">${clock(c.ts)}</div><div><div>${escapeHtml(c.message)}</div>${c.step ? `<div class="s">step ${JOURNEY_STEPS.indexOf(c.step) + 1} \xB7 ${escapeHtml(c.step)}</div>` : ""}${c.files.length ? `<div class="imgs">${c.files.map((f) => img(f, c.message)).join("")}</div>` : ""}</div></li>`).join("") || `<li class="muted">No postcards were journaled.</li>`}</ul>
49814
- <footer>Built with himi.${plan.dropped ? ` ${count(plan.dropped)} pictures were left out to keep this file small.` : ""}</footer>
50279
+ <footer>Built with himi.${plan.dropped ? ` ${count2(plan.dropped)} pictures were left out to keep this file small.` : ""}</footer>
49815
50280
  </div>
49816
50281
  <script>
49817
50282
  (function(){var F=${data};var imgs=document.querySelectorAll(".phone img");if(imgs.length<2)return;var i=0;
@@ -49825,7 +50290,7 @@ function buildReel(dir, budget = REEL_BUDGET_BYTES) {
49825
50290
  const { plan, images } = inlineImages(dir, planReel(dir), budget);
49826
50291
  return { html: renderReel(plan, images), plan };
49827
50292
  }
49828
- var REEL_BUDGET_BYTES, MAX_FLIP_FRAMES, DEFAULT_ACCENT, ACCENT, HISTORY_NAME, MIME2, mimeOf, readJson4, isObj2, str, historyName, cited;
50293
+ var REEL_BUDGET_BYTES, MAX_FLIP_FRAMES, DEFAULT_ACCENT, ACCENT, HISTORY_NAME, MIME2, mimeOf, readJson5, isObj2, str, historyName, cited;
49829
50294
  var init_studio_reel = __esm({
49830
50295
  "src/studio-reel.ts"() {
49831
50296
  init_progress();
@@ -49837,9 +50302,9 @@ var init_studio_reel = __esm({
49837
50302
  HISTORY_NAME = /^[0-9a-f]{64}\.(png|jpg|gif|webp)$/;
49838
50303
  MIME2 = { png: "image/png", jpg: "image/jpeg", gif: "image/gif", webp: "image/webp" };
49839
50304
  mimeOf = (file) => MIME2[file.slice(65)] ?? "image/png";
49840
- readJson4 = (file) => {
50305
+ readJson5 = (file) => {
49841
50306
  try {
49842
- return JSON.parse(readFileSync55(file, "utf8"));
50307
+ return JSON.parse(readFileSync56(file, "utf8"));
49843
50308
  } catch {
49844
50309
  return null;
49845
50310
  }
@@ -49870,9 +50335,9 @@ __export(native_decode_exports, {
49870
50335
  runNativeDecode: () => runNativeDecode
49871
50336
  });
49872
50337
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
49873
- import { existsSync as existsSync51, mkdtempSync as mkdtempSync2, readFileSync as readFileSync56, rmSync as rmSync11, writeFileSync as writeFileSync21 } from "node:fs";
50338
+ import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, readFileSync as readFileSync57, rmSync as rmSync11, writeFileSync as writeFileSync21 } from "node:fs";
49874
50339
  import { tmpdir as tmpdir3 } from "node:os";
49875
- import { join as join50 } from "node:path";
50340
+ import { join as join51 } from "node:path";
49876
50341
  import { fileURLToPath as fileURLToPath20 } from "node:url";
49877
50342
  function nativeDecodePackageDir() {
49878
50343
  return fileURLToPath20(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -49882,8 +50347,8 @@ function nativeDecodeAndroidHarnessDir() {
49882
50347
  }
49883
50348
  function nativeOracleShipped() {
49884
50349
  return {
49885
- ios: existsSync51(join50(nativeDecodePackageDir(), "Package.swift")),
49886
- android: existsSync51(join50(nativeDecodeAndroidHarnessDir(), "gradlew"))
50350
+ ios: existsSync52(join51(nativeDecodePackageDir(), "Package.swift")),
50351
+ android: existsSync52(join51(nativeDecodeAndroidHarnessDir(), "gradlew"))
49887
50352
  };
49888
50353
  }
49889
50354
  function detectNativeDecode(options = {}) {
@@ -49894,7 +50359,7 @@ function detectNativeDecode(options = {}) {
49894
50359
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
49895
50360
  }
49896
50361
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
49897
- const packageAvailable = options.packageAvailable ?? existsSync51(join50(packageDir, "Package.swift"));
50362
+ const packageAvailable = options.packageAvailable ?? existsSync52(join51(packageDir, "Package.swift"));
49898
50363
  if (!packageAvailable) {
49899
50364
  return {
49900
50365
  platform: "ios",
@@ -49906,7 +50371,7 @@ function detectNativeDecode(options = {}) {
49906
50371
  }
49907
50372
  function localPropertiesSdk(harnessDir) {
49908
50373
  try {
49909
- const match = readFileSync56(join50(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
50374
+ const match = readFileSync57(join51(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
49910
50375
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
49911
50376
  } catch {
49912
50377
  return null;
@@ -49914,8 +50379,8 @@ function localPropertiesSdk(harnessDir) {
49914
50379
  }
49915
50380
  function detectAndroidNativeDecode(options = {}) {
49916
50381
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
49917
- const gradlew = join50(harnessDir, "gradlew");
49918
- const gradlewAvailable = options.gradlewAvailable ?? existsSync51(gradlew);
50382
+ const gradlew = join51(harnessDir, "gradlew");
50383
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync52(gradlew);
49919
50384
  if (!gradlewAvailable) {
49920
50385
  return {
49921
50386
  platform: "android",
@@ -49925,7 +50390,7 @@ function detectAndroidNativeDecode(options = {}) {
49925
50390
  }
49926
50391
  const androidHome = options.androidHome ?? process.env.ANDROID_HOME ?? "";
49927
50392
  const sdk = androidHome.trim() || (options.localPropertiesSdk !== void 0 ? options.localPropertiesSdk : localPropertiesSdk(harnessDir));
49928
- const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync51(sdk));
50393
+ const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync52(sdk));
49929
50394
  if (!sdkAvailable) {
49930
50395
  return {
49931
50396
  platform: "android",
@@ -49997,14 +50462,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
49997
50462
  return verdicts;
49998
50463
  }
49999
50464
  function runNativeDecode(options) {
50000
- const descriptorDir = mkdtempSync2(join50(tmpdir3(), "himi-native-decode-"));
50465
+ const descriptorDir = mkdtempSync2(join51(tmpdir3(), "himi-native-decode-"));
50001
50466
  const startedAt = Date.now();
50002
50467
  try {
50003
50468
  for (const descriptor of options.descriptors) {
50004
50469
  if (typeof descriptor.descriptorJson !== "string") {
50005
50470
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
50006
50471
  }
50007
- writeFileSync21(join50(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50472
+ writeFileSync21(join51(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50008
50473
  }
50009
50474
  options.progress?.("building cached Swift oracle");
50010
50475
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -50016,7 +50481,7 @@ function runNativeDecode(options) {
50016
50481
  encoding: "utf8"
50017
50482
  }).trim();
50018
50483
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
50019
- const stdout = execFileSync5(join50(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
50484
+ const stdout = execFileSync5(join51(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
50020
50485
  stdio: ["ignore", "pipe", "pipe"],
50021
50486
  encoding: "utf8"
50022
50487
  });
@@ -50034,15 +50499,15 @@ function runNativeDecode(options) {
50034
50499
  }
50035
50500
  }
50036
50501
  function runAndroidNativeDecode(options) {
50037
- const descriptorDir = mkdtempSync2(join50(tmpdir3(), "himi-native-decode-android-"));
50038
- const outputPath = join50(descriptorDir, "verdicts.ndjson");
50502
+ const descriptorDir = mkdtempSync2(join51(tmpdir3(), "himi-native-decode-android-"));
50503
+ const outputPath = join51(descriptorDir, "verdicts.ndjson");
50039
50504
  const startedAt = Date.now();
50040
50505
  try {
50041
50506
  for (const descriptor of options.descriptors) {
50042
50507
  if (typeof descriptor.descriptorJson !== "string") {
50043
50508
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
50044
50509
  }
50045
- writeFileSync21(join50(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50510
+ writeFileSync21(join51(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50046
50511
  }
50047
50512
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
50048
50513
  execFileSync5(options.gradlew, [
@@ -50057,7 +50522,7 @@ function runAndroidNativeDecode(options) {
50057
50522
  stdio: ["ignore", "ignore", "pipe"],
50058
50523
  encoding: "utf8"
50059
50524
  });
50060
- const stdout = readFileSync56(outputPath, "utf8");
50525
+ const stdout = readFileSync57(outputPath, "utf8");
50061
50526
  return {
50062
50527
  platform: "android",
50063
50528
  verdicts: parseNativeDecodeOutput(stdout, options.descriptors.map((descriptor) => descriptor.screenId), "android"),
@@ -50127,30 +50592,30 @@ __export(browser_authoring_exports, {
50127
50592
  });
50128
50593
  import { createServer as createServer3 } from "node:http";
50129
50594
  import { createHash as createHash26, randomBytes as randomBytes4 } from "node:crypto";
50130
- import { existsSync as existsSync52, readdirSync as readdirSync26, readFileSync as readFileSync57, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
50131
- import { join as join51, posix as posix2, relative as relative9, sep as sep12 } from "node:path";
50595
+ import { existsSync as existsSync53, readdirSync as readdirSync27, readFileSync as readFileSync58, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
50596
+ import { join as join52, posix as posix2, relative as relative9, sep as sep12 } from "node:path";
50132
50597
  function humanMs(ms) {
50133
50598
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
50134
50599
  }
50135
50600
  function collectWorkerSources(tier5SrcDir, bundleName) {
50136
- const root = join51(tier5SrcDir, bundleName);
50601
+ const root = join52(tier5SrcDir, bundleName);
50137
50602
  const files = {};
50138
50603
  const visitedDirs = /* @__PURE__ */ new Set();
50139
50604
  const walk2 = (abs, rel) => {
50140
50605
  const real = realpathSync6(abs);
50141
50606
  if (visitedDirs.has(real)) return;
50142
50607
  visitedDirs.add(real);
50143
- for (const name of readdirSync26(abs).sort()) {
50144
- const childAbs = join51(abs, name);
50608
+ for (const name of readdirSync27(abs).sort()) {
50609
+ const childAbs = join52(abs, name);
50145
50610
  const childRel = posix2.join(rel, name);
50146
50611
  if (statSync12(childAbs).isDirectory()) {
50147
50612
  walk2(childAbs, childRel);
50148
50613
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
50149
- files[childRel] = readFileSync57(childAbs, "utf8");
50614
+ files[childRel] = readFileSync58(childAbs, "utf8");
50150
50615
  }
50151
50616
  }
50152
50617
  };
50153
- if (!existsSync52(root)) return null;
50618
+ if (!existsSync53(root)) return null;
50154
50619
  walk2(root, `/tier5-src/${bundleName}`);
50155
50620
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p) => p in files);
50156
50621
  if (!entry) return null;
@@ -50167,20 +50632,20 @@ function collectRelativeSiblings(files, tier5SrcDir) {
50167
50632
  const spec = m[1];
50168
50633
  const virtual = posix2.normalize(posix2.join(dir, spec));
50169
50634
  if (!virtual.startsWith("/tier5-src/")) continue;
50170
- const onDisk = join51(tier5SrcDir, virtual.slice("/tier5-src/".length));
50635
+ const onDisk = join52(tier5SrcDir, virtual.slice("/tier5-src/".length));
50171
50636
  const candidates = [
50172
50637
  onDisk,
50173
50638
  `${onDisk}.ts`,
50174
50639
  `${onDisk}.js`,
50175
50640
  onDisk.replace(/\.js$/, ".ts"),
50176
- join51(onDisk, "index.ts"),
50177
- join51(onDisk, "index.js")
50641
+ join52(onDisk, "index.ts"),
50642
+ join52(onDisk, "index.js")
50178
50643
  ];
50179
50644
  for (const abs of candidates) {
50180
- if (!existsSync52(abs) || statSync12(abs).isDirectory()) continue;
50645
+ if (!existsSync53(abs) || statSync12(abs).isDirectory()) continue;
50181
50646
  const key2 = `/tier5-src/${relative9(tier5SrcDir, abs).split(sep12).join(posix2.sep)}`;
50182
50647
  if (key2 in files) break;
50183
- files[key2] = readFileSync57(abs, "utf8");
50648
+ files[key2] = readFileSync58(abs, "utf8");
50184
50649
  queue.push(key2);
50185
50650
  break;
50186
50651
  }
@@ -50215,7 +50680,7 @@ function collectSdkModules(files, resolveSubpath) {
50215
50680
  function sdkSubpathReader(resolve48) {
50216
50681
  return (sub) => {
50217
50682
  try {
50218
- return readFileSync57(resolve48(`@wix/himalaya/${sub}`), "utf8");
50683
+ return readFileSync58(resolve48(`@wix/himalaya/${sub}`), "utf8");
50219
50684
  } catch {
50220
50685
  return null;
50221
50686
  }
@@ -50387,8 +50852,8 @@ __export(coverage_exports, {
50387
50852
  suppressedNativeKeys: () => suppressedNativeKeys,
50388
50853
  trendAndStore: () => trendAndStore
50389
50854
  });
50390
- import { existsSync as existsSync53, mkdirSync as mkdirSync25, readFileSync as readFileSync58, writeFileSync as writeFileSync22 } from "node:fs";
50391
- import { dirname as dirname28, join as join52 } from "node:path";
50855
+ import { existsSync as existsSync54, mkdirSync as mkdirSync25, readFileSync as readFileSync59, writeFileSync as writeFileSync22 } from "node:fs";
50856
+ import { dirname as dirname28, join as join53 } from "node:path";
50392
50857
  function suppressedNativeKeys(oracle) {
50393
50858
  return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
50394
50859
  }
@@ -50568,11 +51033,11 @@ function assembleLedger(r, options = {}) {
50568
51033
  return out;
50569
51034
  }
50570
51035
  function trendAndStore(dir, entries, suppressed) {
50571
- const p = join52(dir, LEDGER_FILE);
51036
+ const p = join53(dir, LEDGER_FILE);
50572
51037
  let previous = null;
50573
51038
  try {
50574
- if (existsSync53(p)) {
50575
- const parsed = JSON.parse(readFileSync58(p, "utf8"));
51039
+ if (existsSync54(p)) {
51040
+ const parsed = JSON.parse(readFileSync59(p, "utf8"));
50576
51041
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k) => typeof k === "string");
50577
51042
  }
50578
51043
  } catch {
@@ -50611,11 +51076,11 @@ function formatLedger(entries, trend) {
50611
51076
  return out.join("\n");
50612
51077
  }
50613
51078
  function diffAndStoreObservations(dir, current) {
50614
- const p = join52(dir, OBS_FILE);
51079
+ const p = join53(dir, OBS_FILE);
50615
51080
  let previous = null;
50616
51081
  try {
50617
- if (existsSync53(p)) {
50618
- const parsed = JSON.parse(readFileSync58(p, "utf8"));
51082
+ if (existsSync54(p)) {
51083
+ const parsed = JSON.parse(readFileSync59(p, "utf8"));
50619
51084
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
50620
51085
  (v) => Array.isArray(v) && v.every((l) => typeof l === "string")
50621
51086
  )) {
@@ -50664,8 +51129,8 @@ var LEDGER_FILE, OBS_FILE;
50664
51129
  var init_coverage = __esm({
50665
51130
  "src/coverage.ts"() {
50666
51131
  init_native_decode();
50667
- LEDGER_FILE = join52(".himi", "test-ledger.json");
50668
- OBS_FILE = join52(".himi", "test-observations.json");
51132
+ LEDGER_FILE = join53(".himi", "test-ledger.json");
51133
+ OBS_FILE = join53(".himi", "test-observations.json");
50669
51134
  }
50670
51135
  });
50671
51136
 
@@ -50676,16 +51141,16 @@ __export(design_kits_exports, {
50676
51141
  kitsDir: () => kitsDir,
50677
51142
  listKits: () => listKits2
50678
51143
  });
50679
- import { existsSync as existsSync54, readFileSync as readFileSync59, readdirSync as readdirSync27 } from "node:fs";
50680
- import { join as join53, basename as basename13 } from "node:path";
51144
+ import { existsSync as existsSync55, readFileSync as readFileSync60, readdirSync as readdirSync28 } from "node:fs";
51145
+ import { join as join54, basename as basename13 } from "node:path";
50681
51146
  import { fileURLToPath as fileURLToPath21 } from "node:url";
50682
51147
  function kitsDir() {
50683
- return KIT_DIR_CANDIDATES.find((p) => existsSync54(p));
51148
+ return KIT_DIR_CANDIDATES.find((p) => existsSync55(p));
50684
51149
  }
50685
51150
  function listKits2() {
50686
51151
  const dir = kitsDir();
50687
51152
  if (!dir) return [];
50688
- return readdirSync27(dir).filter((f) => f.endsWith(".json")).map((f) => basename13(f, ".json")).sort();
51153
+ return readdirSync28(dir).filter((f) => f.endsWith(".json")).map((f) => basename13(f, ".json")).sort();
50689
51154
  }
50690
51155
  function kitReport(name) {
50691
51156
  const dir = kitsDir();
@@ -50704,7 +51169,7 @@ In a published @wix/himalaya-cli this is a packaging bug \u2014 the kits should
50704
51169
  if (!kits.includes(name)) {
50705
51170
  return { ok: false, kit: name, kits, problems: [`no design kit named "${name}". Available: ${kits.join(", ")}`] };
50706
51171
  }
50707
- const raw = readFileSync59(join53(dir, `${name}.json`), "utf8");
51172
+ const raw = readFileSync60(join54(dir, `${name}.json`), "utf8");
50708
51173
  let parsed;
50709
51174
  try {
50710
51175
  parsed = JSON.parse(raw);
@@ -50728,9 +51193,9 @@ var init_design_kits = __esm({
50728
51193
  "src/design-kits.ts"() {
50729
51194
  KIT_DIR_CANDIDATES = [
50730
51195
  // in-repo: tools/himi-cli/src/design-kits.ts → <repo>/stdlib/design-kits/
50731
- join53(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
51196
+ join54(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
50732
51197
  // published CLI: <pkg>/dist/cli.mjs → <pkg>/stdlib/design-kits/
50733
- join53(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
51198
+ join54(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
50734
51199
  ];
50735
51200
  }
50736
51201
  });
@@ -50744,11 +51209,11 @@ __export(assets_exports, {
50744
51209
  removeAssetPayload: () => removeAssetPayload,
50745
51210
  verifyReport: () => verifyReport
50746
51211
  });
50747
- import { readFileSync as readFileSync60 } from "node:fs";
51212
+ import { readFileSync as readFileSync61 } from "node:fs";
50748
51213
  import { basename as basename14 } from "node:path";
50749
51214
  function declaredAssets(dir) {
50750
51215
  return collectAssets(dir).map((a) => {
50751
- const bytes2 = readFileSync60(a.path);
51216
+ const bytes2 = readFileSync61(a.path);
50752
51217
  return {
50753
51218
  name: a.name,
50754
51219
  sha256: assetSha256(bytes2),
@@ -50825,7 +51290,7 @@ function verifyReport(dir, app, manifest) {
50825
51290
  };
50826
51291
  }
50827
51292
  function addAssetPayload(manifest, filePath, opts) {
50828
- const bytes2 = readFileSync60(filePath);
51293
+ const bytes2 = readFileSync61(filePath);
50829
51294
  const name = opts.name ?? basename14(filePath);
50830
51295
  const sha2565 = assetSha256(bytes2);
50831
51296
  const record2 = {
@@ -52468,21 +52933,21 @@ var init_preview_assets_generated = __esm({
52468
52933
  "../../packages/serve/src/preview-assets.generated.ts"() {
52469
52934
  init_preview_assets_format();
52470
52935
  PREVIEW_ASSET_MANIFEST = [
52471
- { key: "assets/index-zgMUMszy.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1058954, sha256: "f7d0a27ec7c0241f1ab3bbda5a83b3d0195f95c0cefbda2125289ce966b6f2da" },
52472
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1058962, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
52473
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1063572, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
52474
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1552156, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
52475
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1643080, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
52476
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1734064, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
52477
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1825044, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
52478
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1915916, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
52479
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2006896, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
52480
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2115532, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
52481
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2224616, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
52482
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2333664, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
52483
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2441364, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
52484
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2529472, length: 4947, sha256: "b2015f4f6ddcb285a96284836207d8e60d0bf4de727b072c4a3b48e919f5067a" },
52485
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2534419, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
52936
+ { key: "assets/index-BU6QijMM.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1060352, sha256: "f608f6d0e14dafe6c495e5df15d09d7512e0a8d87732feea4f4e8e36de7917e4" },
52937
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1060360, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
52938
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1064970, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
52939
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1553554, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
52940
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1644478, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
52941
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1735462, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
52942
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1826442, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
52943
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1917314, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
52944
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2008294, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
52945
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2116930, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
52946
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2226014, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
52947
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2335062, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
52948
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2442762, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
52949
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2530870, length: 4947, sha256: "c760d7592d0a40904bb13edaec8e8991f79fc2eb732c5dfee54fea36e6dceecb" },
52950
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2535817, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
52486
52951
  ];
52487
52952
  }
52488
52953
  });
@@ -52500,9 +52965,9 @@ __export(run_exports, {
52500
52965
  runPackage: () => runPackage,
52501
52966
  withoutProdServe: () => withoutProdServe
52502
52967
  });
52503
- import { cpSync as cpSync2, existsSync as existsSync55, mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, readFileSync as readFileSync61, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
52968
+ import { cpSync as cpSync2, existsSync as existsSync56, mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, readFileSync as readFileSync62, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
52504
52969
  import { networkInterfaces as osNetworkInterfaces, tmpdir as tmpdir4 } from "node:os";
52505
- import { dirname as dirname29, join as join54 } from "node:path";
52970
+ import { dirname as dirname29, join as join55 } from "node:path";
52506
52971
  import { fileURLToPath as fileURLToPath22 } from "node:url";
52507
52972
  function makePackageRunHandler(opts) {
52508
52973
  const store = new FileReleaseStore(opts.releaseRoot);
@@ -52530,9 +52995,9 @@ async function fetchBytes(url) {
52530
52995
  }
52531
52996
  async function resolvePackage(source, workDir) {
52532
52997
  const isUrl2 = /^https?:\/\//i.test(source);
52533
- if (!isUrl2 && existsSync55(source) && statSync13(source).isDirectory()) {
52534
- const appDirManifest = join54(source, "release-manifest.json");
52535
- if (existsSync55(appDirManifest)) {
52998
+ if (!isUrl2 && existsSync56(source) && statSync13(source).isDirectory()) {
52999
+ const appDirManifest = join55(source, "release-manifest.json");
53000
+ if (existsSync56(appDirManifest)) {
52536
53001
  const opened2 = openPackageDir(source);
52537
53002
  if (!opened2.validation.ok) {
52538
53003
  throw new Error(`package is not valid:
@@ -52541,9 +53006,9 @@ async function resolvePackage(source, workDir) {
52541
53006
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
52542
53007
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
52543
53008
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
52544
- const root2 = workDir ?? mkdtempSync3(join54(tmpdir4(), "himi-run-"));
52545
- const target = join54(root2, app2);
52546
- if (join54(source) !== target) {
53009
+ const root2 = workDir ?? mkdtempSync3(join55(tmpdir4(), "himi-run-"));
53010
+ const target = join55(root2, app2);
53011
+ if (join55(source) !== target) {
52547
53012
  mkdirSync26(root2, { recursive: true });
52548
53013
  rmSync12(target, { recursive: true, force: true });
52549
53014
  cpSync2(source, target, { recursive: true });
@@ -52560,8 +53025,8 @@ async function resolvePackage(source, workDir) {
52560
53025
  }
52561
53026
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
52562
53027
  }
52563
- const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync61(source);
52564
- const root = workDir ?? mkdtempSync3(join54(tmpdir4(), "himi-run-"));
53028
+ const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync62(source);
53029
+ const root = workDir ?? mkdtempSync3(join55(tmpdir4(), "himi-run-"));
52565
53030
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
52566
53031
  const opened = openPackageDir(appDir2);
52567
53032
  if (!opened.validation.ok) {
@@ -52584,14 +53049,14 @@ async function loadPreviewAssets() {
52584
53049
  const mod = await Promise.resolve().then(() => (init_preview_assets_generated(), preview_assets_generated_exports));
52585
53050
  const here = dirname29(fileURLToPath22(import.meta.url));
52586
53051
  const candidates = [
52587
- join54(here, "preview-assets.generated.bin"),
53052
+ join55(here, "preview-assets.generated.bin"),
52588
53053
  // published: copied next to the bundled CLI
52589
- join54(here, "../../../packages/serve/src/preview-assets.generated.bin")
53054
+ join55(here, "../../../packages/serve/src/preview-assets.generated.bin")
52590
53055
  // in-repo checkout
52591
53056
  ];
52592
53057
  for (const path2 of candidates) {
52593
- if (!existsSync55(path2)) continue;
52594
- const blob = readFileSync61(path2);
53058
+ if (!existsSync56(path2)) continue;
53059
+ const blob = readFileSync62(path2);
52595
53060
  return mod.assemblePreviewAssetsFromBlob(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
52596
53061
  }
52597
53062
  return /* @__PURE__ */ new Map();
@@ -52738,9 +53203,9 @@ __export(run_watch_exports, {
52738
53203
  watchedContentDir: () => watchedContentDir,
52739
53204
  writeRunRecord: () => writeRunRecord
52740
53205
  });
52741
- import { closeSync as closeSync2, constants, existsSync as existsSync56, fstatSync, lstatSync as lstatSync10, statSync as statSync14, mkdirSync as mkdirSync27, openSync as openSync2, readFileSync as readFileSync62, realpathSync as realpathSync7, renameSync as renameSync6, rmSync as rmSync13, writeFileSync as writeFileSync23, watch as fsWatch } from "node:fs";
53206
+ import { closeSync as closeSync2, constants, existsSync as existsSync57, fstatSync, lstatSync as lstatSync10, statSync as statSync14, mkdirSync as mkdirSync27, openSync as openSync2, readFileSync as readFileSync63, realpathSync as realpathSync7, renameSync as renameSync6, rmSync as rmSync13, writeFileSync as writeFileSync23, watch as fsWatch } from "node:fs";
52742
53207
  import { randomUUID as randomUUID3 } from "node:crypto";
52743
- import { extname as extname4, join as join55, resolve as resolve42, sep as sep13 } from "node:path";
53208
+ import { extname as extname4, join as join56, resolve as resolve42, sep as sep13 } from "node:path";
52744
53209
  function diffReleases(prev, next) {
52745
53210
  const bundles = Object.keys(next.bundles).filter((b) => prev.bundles[b]?.sha256 !== next.bundles[b].sha256).sort();
52746
53211
  const changedBundles = new Set(bundles);
@@ -52781,7 +53246,7 @@ function watchContent(dir, onChange, opts = {}) {
52781
53246
  let timer = null;
52782
53247
  const pending = /* @__PURE__ */ new Set();
52783
53248
  const watcher2 = watch2(dir, { recursive: true }, (_event, file) => {
52784
- if (!file || ignoredChange(file, (rel) => isDir(join55(dir, rel)))) return;
53249
+ if (!file || ignoredChange(file, (rel) => isDir(join56(dir, rel)))) return;
52785
53250
  pending.add(file);
52786
53251
  if (timer) clearTimeout(timer);
52787
53252
  timer = setTimeout(() => {
@@ -52802,7 +53267,7 @@ function watchedContentDir(source) {
52802
53267
  if (!source || /^https?:\/\//i.test(source)) return null;
52803
53268
  const dir = resolve42(source);
52804
53269
  try {
52805
- return statSync14(dir).isDirectory() && existsSync56(join55(dir, "himalaya.content.json")) ? dir : null;
53270
+ return statSync14(dir).isDirectory() && existsSync57(join56(dir, "himalaya.content.json")) ? dir : null;
52806
53271
  } catch {
52807
53272
  return null;
52808
53273
  }
@@ -52859,7 +53324,7 @@ function serialize(task) {
52859
53324
  });
52860
53325
  }
52861
53326
  function himiDir(dir) {
52862
- const root = join55(dir, ".himi");
53327
+ const root = join56(dir, ".himi");
52863
53328
  try {
52864
53329
  return lstatSync10(root).isDirectory() ? root : null;
52865
53330
  } catch (e) {
@@ -52871,11 +53336,11 @@ function himiDir(dir) {
52871
53336
  function writeRunRecord(dir, record2) {
52872
53337
  const root = himiDir(dir);
52873
53338
  if (!root) return false;
52874
- const tmp = join55(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
53339
+ const tmp = join56(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
52875
53340
  try {
52876
53341
  writeFileSync23(tmp, `${JSON.stringify(record2, null, 2)}
52877
53342
  `, { flag: "wx" });
52878
- renameSync6(tmp, join55(root, "run.json"));
53343
+ renameSync6(tmp, join56(root, "run.json"));
52879
53344
  } catch (e) {
52880
53345
  rmSync13(tmp, { force: true });
52881
53346
  throw e;
@@ -52883,7 +53348,7 @@ function writeRunRecord(dir, record2) {
52883
53348
  return true;
52884
53349
  }
52885
53350
  function removeRunRecord(dir, owner) {
52886
- const root = join55(dir, ".himi");
53351
+ const root = join56(dir, ".himi");
52887
53352
  try {
52888
53353
  if (!lstatSync10(root).isDirectory()) return;
52889
53354
  } catch {
@@ -52898,16 +53363,16 @@ function removeRunRecord(dir, owner) {
52898
53363
  }
52899
53364
  if (typeof pid === "number" && pid !== owner) return;
52900
53365
  }
52901
- rmSync13(join55(root, "run.json"), { force: true });
53366
+ rmSync13(join56(root, "run.json"), { force: true });
52902
53367
  }
52903
53368
  function boardFile(dir, pathname) {
52904
53369
  let rel = null;
52905
- if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join55("studio", "index.html");
52906
- else if (pathname === "/__himi/studio/state.js") rel = join55("studio", "state.js");
52907
- else if (pathname === "/__himi/studio/timeline.js") rel = join55("studio", "timeline.js");
53370
+ if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join56("studio", "index.html");
53371
+ else if (pathname === "/__himi/studio/state.js") rel = join56("studio", "state.js");
53372
+ else if (pathname === "/__himi/studio/timeline.js") rel = join56("studio", "timeline.js");
52908
53373
  else {
52909
53374
  const m = /^\/__himi\/history\/([0-9a-f]{64}\.(png|jpg|gif|webp))$/.exec(pathname);
52910
- if (m) rel = join55("history", m[1]);
53375
+ if (m) rel = join56("history", m[1]);
52911
53376
  }
52912
53377
  if (!rel) return null;
52913
53378
  const bytes2 = readConfined(resolve42(dir, ".himi"), rel);
@@ -52923,7 +53388,7 @@ function readConfined(root, rel) {
52923
53388
  const parts = rel.split(sep13);
52924
53389
  let at = root;
52925
53390
  for (let i = 0; i < parts.length; i++) {
52926
- at = join55(at, parts[i]);
53391
+ at = join56(at, parts[i]);
52927
53392
  const st = lstatSync10(at);
52928
53393
  if (st.isSymbolicLink() || !(i === parts.length - 1 ? st.isFile() : st.isDirectory())) return null;
52929
53394
  }
@@ -52932,7 +53397,7 @@ function readConfined(root, rel) {
52932
53397
  const fd = openSync2(at, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
52933
53398
  try {
52934
53399
  if (!fstatSync(fd).isFile()) return null;
52935
- return readFileSync62(fd);
53400
+ return readFileSync63(fd);
52936
53401
  } finally {
52937
53402
  closeSync2(fd);
52938
53403
  }
@@ -52984,7 +53449,7 @@ __export(run_browser_exports, {
52984
53449
  runInBrowser: () => runInBrowser
52985
53450
  });
52986
53451
  import { createServer as createServer5 } from "node:http";
52987
- import { existsSync as existsSync57, readFileSync as readFileSync63 } from "node:fs";
53452
+ import { existsSync as existsSync58, readFileSync as readFileSync64 } from "node:fs";
52988
53453
  import { spawnSync as spawnSync3 } from "node:child_process";
52989
53454
  import { dirname as dirname30, resolve as resolve43 } from "node:path";
52990
53455
  import { fileURLToPath as fileURLToPath23 } from "node:url";
@@ -52992,7 +53457,7 @@ function stagedDir() {
52992
53457
  return process.env.HIMI_BROWSER_PLANE_OUT ? resolve43(process.env.HIMI_BROWSER_PLANE_OUT) : resolve43(REPO, "packages", "serve", "dist", "browser-plane");
52993
53458
  }
52994
53459
  function missingStaged() {
52995
- return [...SERVED].filter((f) => !existsSync57(resolve43(stagedDir(), f))).sort();
53460
+ return [...SERVED].filter((f) => !existsSync58(resolve43(stagedDir(), f))).sort();
52996
53461
  }
52997
53462
  function ensureBrowserPlaneStaged() {
52998
53463
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
@@ -53077,7 +53542,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
53077
53542
  }
53078
53543
  const name = path2 === "/" || path2 === "/index.html" ? "host.html" : path2.slice(1);
53079
53544
  if (SERVED.has(name)) {
53080
- if (!existsSync57(resolve43(stagedDir(), name))) {
53545
+ if (!existsSync58(resolve43(stagedDir(), name))) {
53081
53546
  const restaged = ensureBrowserPlaneStaged();
53082
53547
  if (!restaged.ok) {
53083
53548
  res.writeHead(503, { "content-type": "application/json" });
@@ -53087,7 +53552,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
53087
53552
  }
53088
53553
  let bytes2;
53089
53554
  try {
53090
- bytes2 = readFileSync63(resolve43(stagedDir(), name));
53555
+ bytes2 = readFileSync64(resolve43(stagedDir(), name));
53091
53556
  } catch (e) {
53092
53557
  res.writeHead(503, { "content-type": "application/json" });
53093
53558
  res.end(JSON.stringify({ error: "browser_plane_unstaged", file: name, detail: String(e) }));
@@ -53548,7 +54013,7 @@ __export(preflight_exports, {
53548
54013
  parseStoreConfig: () => parseStoreConfig,
53549
54014
  requireEnv: () => requireEnv
53550
54015
  });
53551
- import { existsSync as existsSync58, readFileSync as readFileSync64 } from "node:fs";
54016
+ import { existsSync as existsSync59, readFileSync as readFileSync65 } from "node:fs";
53552
54017
  import { isAbsolute as isAbsolute3, relative as relative10, resolve as resolve44 } from "node:path";
53553
54018
  function isInsideRepo(p) {
53554
54019
  const rel = relative10(REPO_ROOT3, resolve44(p));
@@ -53572,7 +54037,7 @@ function strip2(line) {
53572
54037
  const hash = line.indexOf("#");
53573
54038
  return (hash >= 0 ? line.slice(0, hash) : line).replace(/\s+$/, "");
53574
54039
  }
53575
- function scalar2(v) {
54040
+ function scalar3(v) {
53576
54041
  const t = v.trim();
53577
54042
  if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
53578
54043
  return t.slice(1, -1);
@@ -53595,20 +54060,20 @@ function parseStoreConfig(text5) {
53595
54060
  cfg[key2] = section2;
53596
54061
  } else {
53597
54062
  section2 = null;
53598
- cfg[key2] = scalar2(rest);
54063
+ cfg[key2] = scalar3(rest);
53599
54064
  }
53600
54065
  } else if (section2) {
53601
- section2[key2] = scalar2(rest);
54066
+ section2[key2] = scalar3(rest);
53602
54067
  }
53603
54068
  }
53604
54069
  return cfg;
53605
54070
  }
53606
54071
  function loadStoreConfig(app) {
53607
54072
  const p = storeConfigPath(app);
53608
- if (!existsSync58(p)) {
54073
+ if (!existsSync59(p)) {
53609
54074
  throw new Error(`No store config at ${p}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
53610
54075
  }
53611
- return parseStoreConfig(readFileSync64(p, "utf8"));
54076
+ return parseStoreConfig(readFileSync65(p, "utf8"));
53612
54077
  }
53613
54078
  var init_preflight = __esm({
53614
54079
  "../store-release/preflight.ts"() {
@@ -53627,19 +54092,19 @@ __export(credential_profile_exports, {
53627
54092
  profilePath: () => profilePath,
53628
54093
  resolveStoreCredentials: () => resolveStoreCredentials
53629
54094
  });
53630
- import { existsSync as existsSync59, readFileSync as readFileSync65 } from "node:fs";
54095
+ import { existsSync as existsSync60, readFileSync as readFileSync66 } from "node:fs";
53631
54096
  import { homedir as homedir5 } from "node:os";
53632
- import { join as join56 } from "node:path";
54097
+ import { join as join57 } from "node:path";
53633
54098
  function credentialsDir(home = homedir5()) {
53634
- return join56(home, ".himalaya", "store-credentials");
54099
+ return join57(home, ".himalaya", "store-credentials");
53635
54100
  }
53636
54101
  function profilePath(name, home = homedir5()) {
53637
54102
  if (!PROFILE_NAME.test(name)) {
53638
54103
  throw new Error(
53639
- `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join56(credentialsDir(home), "<name>.json")}; it is never a path.`
54104
+ `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join57(credentialsDir(home), "<name>.json")}; it is never a path.`
53640
54105
  );
53641
54106
  }
53642
- return join56(credentialsDir(home), `${name}.json`);
54107
+ return join57(credentialsDir(home), `${name}.json`);
53643
54108
  }
53644
54109
  function parseProfile(text5, label2) {
53645
54110
  let doc;
@@ -53670,8 +54135,8 @@ function resolveStoreCredentials(opts) {
53670
54135
  const { app, cfg } = opts;
53671
54136
  const env = opts.env ?? process.env;
53672
54137
  const home = opts.home ?? homedir5();
53673
- const read = opts.readProfile ?? ((p) => readFileSync65(p, "utf8"));
53674
- const exists = opts.profileExists ?? existsSync59;
54138
+ const read = opts.readProfile ?? ((p) => readFileSync66(p, "utf8"));
54139
+ const exists = opts.profileExists ?? existsSync60;
53675
54140
  const ios = cfg.ios ?? {};
53676
54141
  const flagName = opts.profileFlag?.trim();
53677
54142
  const named = namedProfile(cfg, opts.profileFlag);
@@ -53751,9 +54216,9 @@ __export(preflight_app_exports, {
53751
54216
  preflightApp: () => preflightApp,
53752
54217
  profileAccount: () => profileAccount
53753
54218
  });
53754
- import { readFileSync as readFileSync66, existsSync as existsSync60, readdirSync as readdirSync28, statSync as statSync15 } from "node:fs";
54219
+ import { readFileSync as readFileSync67, existsSync as existsSync61, readdirSync as readdirSync29, statSync as statSync15 } from "node:fs";
53755
54220
  import { createHash as createHash27 } from "node:crypto";
53756
- import { join as join57 } from "node:path";
54221
+ import { join as join58 } from "node:path";
53757
54222
  function analyze(projectYml, storeConfig, icon, account) {
53758
54223
  const f = [];
53759
54224
  const has = (re, s) => re.test(s);
@@ -53817,13 +54282,13 @@ function profileAccount(app, storeConfig, opts = {}) {
53817
54282
  }
53818
54283
  }
53819
54284
  async function preflightApp(app, opts = {}) {
53820
- const projectYml = existsSync60(iosProjectYml(app)) ? readFileSync66(iosProjectYml(app), "utf8") : "";
53821
- const storeConfig = existsSync60(storeConfigPath(app)) ? readFileSync66(storeConfigPath(app), "utf8") : "";
54285
+ const projectYml = existsSync61(iosProjectYml(app)) ? readFileSync67(iosProjectYml(app), "utf8") : "";
54286
+ const storeConfig = existsSync61(storeConfigPath(app)) ? readFileSync67(storeConfigPath(app), "utf8") : "";
53822
54287
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
53823
54288
  const findings = analyze(
53824
54289
  projectYml,
53825
54290
  storeConfig,
53826
- await appIconState(join57(appDir(app), "ios")),
54291
+ await appIconState(join58(appDir(app), "ios")),
53827
54292
  profileAccount(app, storeConfig, opts)
53828
54293
  );
53829
54294
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
@@ -53852,18 +54317,18 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
53852
54317
  return f;
53853
54318
  }
53854
54319
  function appLevelMetadataFiles(app, locale = "en-US") {
53855
- const root = join57(storeDir(app), "metadata");
54320
+ const root = join58(storeDir(app), "metadata");
53856
54321
  const out = /* @__PURE__ */ new Set();
53857
- if (existsSync60(root)) {
53858
- for (const n of readdirSync28(root)) if (n.endsWith(".txt")) out.add(n);
54322
+ if (existsSync61(root)) {
54323
+ for (const n of readdirSync29(root)) if (n.endsWith(".txt")) out.add(n);
53859
54324
  }
53860
- if (existsSync60(join57(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
54325
+ if (existsSync61(join58(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
53861
54326
  return out;
53862
54327
  }
53863
54328
  function reviewInfoFiles(app) {
53864
- const dir = join57(storeDir(app), "review_information");
53865
- if (!existsSync60(dir)) return /* @__PURE__ */ new Set();
53866
- return new Set(readdirSync28(dir).filter((n) => n.endsWith(".txt")));
54329
+ const dir = join58(storeDir(app), "review_information");
54330
+ if (!existsSync61(dir)) return /* @__PURE__ */ new Set();
54331
+ return new Set(readdirSync29(dir).filter((n) => n.endsWith(".txt")));
53867
54332
  }
53868
54333
  function isValidAppStoreSize(w, h) {
53869
54334
  return APP_STORE_SIZES.some(([a, b]) => w === a && h === b || w === b && h === a);
@@ -53972,12 +54437,12 @@ function analyzePlayListing(assets) {
53972
54437
  return f;
53973
54438
  }
53974
54439
  function playListingAssets(app, locale = "en-US") {
53975
- const dir = join57(storeDir(app), "play", locale, "images");
54440
+ const dir = join58(storeDir(app), "play", locale, "images");
53976
54441
  const read = (names) => {
53977
54442
  for (const name of names) {
53978
- const p = join57(dir, name);
54443
+ const p = join58(dir, name);
53979
54444
  try {
53980
- if (statSync15(p).isFile()) return { name, bytes: readFileSync66(p) };
54445
+ if (statSync15(p).isFile()) return { name, bytes: readFileSync67(p) };
53981
54446
  } catch {
53982
54447
  }
53983
54448
  }
@@ -54008,26 +54473,26 @@ function analyzeListing(metaFiles, shots) {
54008
54473
  return f;
54009
54474
  }
54010
54475
  function metadataFilesPresent(app, locale = "en-US") {
54011
- const dir = join57(storeDir(app), "metadata", locale);
54012
- if (!existsSync60(dir)) return /* @__PURE__ */ new Set();
54013
- return new Set(readdirSync28(dir).filter((n) => n.endsWith(".txt")));
54476
+ const dir = join58(storeDir(app), "metadata", locale);
54477
+ if (!existsSync61(dir)) return /* @__PURE__ */ new Set();
54478
+ return new Set(readdirSync29(dir).filter((n) => n.endsWith(".txt")));
54014
54479
  }
54015
54480
  function listScreenshots(app, locale = "en-US") {
54016
54481
  const out = [];
54017
- const flat = join57(storeDir(app), "screenshots", locale);
54018
- if (existsSync60(flat)) {
54019
- for (const png of readdirSync28(flat).filter((n) => n.endsWith(".png"))) {
54020
- const sz = pngSize(readFileSync66(join57(flat, png)));
54482
+ const flat = join58(storeDir(app), "screenshots", locale);
54483
+ if (existsSync61(flat)) {
54484
+ for (const png of readdirSync29(flat).filter((n) => n.endsWith(".png"))) {
54485
+ const sz = pngSize(readFileSync67(join58(flat, png)));
54021
54486
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
54022
54487
  }
54023
54488
  }
54024
- const legacy = join57(storeDir(app), "metadata", locale, "screenshots");
54025
- if (existsSync60(legacy)) {
54026
- for (const deviceDir of readdirSync28(legacy)) {
54027
- const d = join57(legacy, deviceDir);
54489
+ const legacy = join58(storeDir(app), "metadata", locale, "screenshots");
54490
+ if (existsSync61(legacy)) {
54491
+ for (const deviceDir of readdirSync29(legacy)) {
54492
+ const d = join58(legacy, deviceDir);
54028
54493
  if (!statSync15(d).isDirectory()) continue;
54029
- for (const png of readdirSync28(d).filter((n) => n.endsWith(".png"))) {
54030
- const sz = pngSize(readFileSync66(join57(d, png)));
54494
+ for (const png of readdirSync29(d).filter((n) => n.endsWith(".png"))) {
54495
+ const sz = pngSize(readFileSync67(join58(d, png)));
54031
54496
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
54032
54497
  }
54033
54498
  }
@@ -54035,13 +54500,13 @@ function listScreenshots(app, locale = "en-US") {
54035
54500
  return out;
54036
54501
  }
54037
54502
  function placeholderHashes2() {
54038
- const templateIos = join57(appDir("_template"), "ios");
54039
- if (!existsSync60(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
54040
- for (const target of readdirSync28(templateIos)) {
54041
- const p = join57(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54503
+ const templateIos = join58(appDir("_template"), "ios");
54504
+ if (!existsSync61(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
54505
+ for (const target of readdirSync29(templateIos)) {
54506
+ const p = join58(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54042
54507
  try {
54043
54508
  if (!statSync15(p).isFile()) continue;
54044
- return [LEGACY_PLACEHOLDER_SHA2562, createHash27("sha256").update(readFileSync66(p)).digest("hex")];
54509
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash27("sha256").update(readFileSync67(p)).digest("hex")];
54045
54510
  } catch {
54046
54511
  }
54047
54512
  }
@@ -54072,12 +54537,12 @@ async function pixelIssues(bytes2) {
54072
54537
  }
54073
54538
  }
54074
54539
  async function appIconState(iosDir) {
54075
- if (!existsSync60(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
54076
- for (const target of readdirSync28(iosDir)) {
54077
- const p = join57(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54540
+ if (!existsSync61(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
54541
+ for (const target of readdirSync29(iosDir)) {
54542
+ const p = join58(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54078
54543
  try {
54079
54544
  if (!statSync15(p).isFile()) continue;
54080
- const bytes2 = readFileSync66(p);
54545
+ const bytes2 = readFileSync67(p);
54081
54546
  const hash = createHash27("sha256").update(bytes2).digest("hex");
54082
54547
  const pixels = await pixelIssues(bytes2);
54083
54548
  return {
@@ -54149,7 +54614,7 @@ __export(version_exports, {
54149
54614
  setAndroidVersion: () => setAndroidVersion,
54150
54615
  setIosVersion: () => setIosVersion
54151
54616
  });
54152
- import { readFileSync as readFileSync67, writeFileSync as writeFileSync24 } from "node:fs";
54617
+ import { readFileSync as readFileSync68, writeFileSync as writeFileSync24 } from "node:fs";
54153
54618
  function readIosVersion(projectYml) {
54154
54619
  const marketing = matchScalar(projectYml, "CFBundleShortVersionString");
54155
54620
  const build2 = matchScalar(projectYml, "CFBundleVersion");
@@ -54207,8 +54672,8 @@ function setAndroidVersion(gradle, v) {
54207
54672
  function bumpVersion(app, opts) {
54208
54673
  const iosPath = iosProjectYml(app);
54209
54674
  const gradlePath = androidGradle(app);
54210
- const iosYml = readFileSync67(iosPath, "utf8");
54211
- const gradle = readFileSync67(gradlePath, "utf8");
54675
+ const iosYml = readFileSync68(iosPath, "utf8");
54676
+ const gradle = readFileSync68(gradlePath, "utf8");
54212
54677
  const current = readIosVersion(iosYml);
54213
54678
  const next = {
54214
54679
  marketing: opts.marketing ?? current.marketing,
@@ -54225,7 +54690,7 @@ var init_version = __esm({
54225
54690
  });
54226
54691
 
54227
54692
  // ../store-release/register-bundle.ts
54228
- import { readFileSync as readFileSync68 } from "node:fs";
54693
+ import { readFileSync as readFileSync69 } from "node:fs";
54229
54694
  import { createPrivateKey as createPrivateKey2, sign } from "node:crypto";
54230
54695
  function b64url2(b) {
54231
54696
  return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -54243,7 +54708,7 @@ function bundleIdBody(identifier, name, platform = "IOS") {
54243
54708
  async function registerBundleId(opts) {
54244
54709
  const env = requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
54245
54710
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
54246
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync68(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
54711
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync69(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
54247
54712
  const res = await fetch(`${ASC_API}/v1/bundleIds`, {
54248
54713
  method: "POST",
54249
54714
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
@@ -54297,7 +54762,7 @@ __export(asc_status_exports, {
54297
54762
  ascStatus: () => ascStatus,
54298
54763
  buildVerdict: () => buildVerdict
54299
54764
  });
54300
- import { readFileSync as readFileSync69 } from "node:fs";
54765
+ import { readFileSync as readFileSync70 } from "node:fs";
54301
54766
  function buildVerdict(builds) {
54302
54767
  if (!builds.length) {
54303
54768
  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).";
@@ -54311,7 +54776,7 @@ function buildVerdict(builds) {
54311
54776
  function client(creds) {
54312
54777
  const env = creds ?? requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
54313
54778
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
54314
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync69(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
54779
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync70(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
54315
54780
  return async (path2) => {
54316
54781
  const r = await fetch(`${ASC}${path2}`, { headers: { Authorization: `Bearer ${jwt}` } });
54317
54782
  return { status: r.status, json: await r.json().catch(() => ({})) };
@@ -54471,7 +54936,7 @@ __export(secrets_exports, {
54471
54936
  runRemoteSecretsCommand: () => runRemoteSecretsCommand,
54472
54937
  runSecretsCommand: () => runSecretsCommand
54473
54938
  });
54474
- import { readFileSync as readFileSync70 } from "node:fs";
54939
+ import { readFileSync as readFileSync71 } from "node:fs";
54475
54940
  function environment(value) {
54476
54941
  if (value === "preview" || value === "production") return value;
54477
54942
  throw new Error("--environment must be preview or production");
@@ -54502,7 +54967,7 @@ async function runSecretsCommand(operation, flags, io, options = {}) {
54502
54967
  io.log(JSON.stringify(await vault.list({ app, environment: env })));
54503
54968
  return 0;
54504
54969
  }
54505
- const value = options.readValue ? options.readValue() : readFileSync70(0, "utf8").replace(/\r?\n$/, "");
54970
+ const value = options.readValue ? options.readValue() : readFileSync71(0, "utf8").replace(/\r?\n$/, "");
54506
54971
  if (operation === "set") await vault.set({ app, environment: env, name, value });
54507
54972
  else if (operation === "rotate") await vault.rotate({ app, environment: env, name, value });
54508
54973
  else throw new Error(`unknown secrets operation ${operation}`);
@@ -54531,7 +54996,7 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
54531
54996
  }
54532
54997
  let body;
54533
54998
  if (operation !== "list") {
54534
- const value = options.readValue ? options.readValue() : readFileSync70(0, "utf8").replace(/\r?\n$/, "");
54999
+ const value = options.readValue ? options.readValue() : readFileSync71(0, "utf8").replace(/\r?\n$/, "");
54535
55000
  body = JSON.stringify({ value });
54536
55001
  }
54537
55002
  const response = await (options.fetchImpl ?? fetch)(url, { method: operation === "list" ? "GET" : operation === "set" ? "PUT" : "POST", headers: headers2, body });
@@ -54567,15 +55032,15 @@ __export(notify_exports, {
54567
55032
  runNotify: () => runNotify,
54568
55033
  saveAdminSession: () => saveAdminSession
54569
55034
  });
54570
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync28, readFileSync as readFileSync71, writeFileSync as writeFileSync25 } from "node:fs";
55035
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync28, readFileSync as readFileSync72, writeFileSync as writeFileSync25 } from "node:fs";
54571
55036
  import { homedir as homedir6 } from "node:os";
54572
- import { dirname as dirname32, join as join58 } from "node:path";
55037
+ import { dirname as dirname32, join as join59 } from "node:path";
54573
55038
  function defaultSessionsPath() {
54574
- return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join58(homedir6(), ".himalaya", "admin-sessions.json");
55039
+ return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join59(homedir6(), ".himalaya", "admin-sessions.json");
54575
55040
  }
54576
55041
  function loadSessionsFile(path2 = defaultSessionsPath()) {
54577
55042
  try {
54578
- return JSON.parse(readFileSync71(path2, "utf8"));
55043
+ return JSON.parse(readFileSync72(path2, "utf8"));
54579
55044
  } catch {
54580
55045
  return {};
54581
55046
  }
@@ -54850,11 +55315,11 @@ function resolveWixUrl(path2) {
54850
55315
  if (/^https?:\/\//i.test(path2)) return path2;
54851
55316
  return `${WIX_API3}${path2}`;
54852
55317
  }
54853
- function classifyHttp(status, count) {
55318
+ function classifyHttp(status, count2) {
54854
55319
  if (status === 428) return "not-installed";
54855
55320
  if (status === 404) return "wrong-path";
54856
55321
  if (status && status >= 400) return "error";
54857
- return count > 0 ? "ok" : "empty";
55322
+ return count2 > 0 ? "ok" : "empty";
54858
55323
  }
54859
55324
  function makeTransport(fetchImpl, auth, timeoutMs = DEFAULT_TIMEOUT_MS4) {
54860
55325
  return async (method, path2, body) => {
@@ -55097,9 +55562,9 @@ function planDetail(p) {
55097
55562
  const pricing = p.pricing && typeof p.pricing === "object" ? p.pricing : void 0;
55098
55563
  const sub = pricing?.subscription && typeof pricing.subscription === "object" ? pricing.subscription : void 0;
55099
55564
  const cycleDur = sub?.cycleDuration && typeof sub.cycleDuration === "object" ? sub.cycleDuration : void 0;
55100
- const count = cycleDur?.count;
55565
+ const count2 = cycleDur?.count;
55101
55566
  const unit = str4(cycleDur?.unit ?? cycleDur?.period).toLowerCase();
55102
- const cycle = typeof count === "number" && unit ? `${count} ${unit}` : str4(sub?.cycle ?? pricing?.cycle ?? p.period ?? p.billingPeriod);
55567
+ const cycle = typeof count2 === "number" && unit ? `${count2} ${unit}` : str4(sub?.cycle ?? pricing?.cycle ?? p.period ?? p.billingPeriod);
55103
55568
  const perks = asArr(p.perks ?? p.benefits).map((x) => str4(x) || str4(x.name)).filter(Boolean);
55104
55569
  const bits = [cycle, perks.slice(0, 4).join("; ")].filter(Boolean);
55105
55570
  return bits.length ? bits.join(" \xB7 ") : void 0;
@@ -55740,9 +56205,9 @@ function deriveTailoring(ctx, overrides = {}, revision = "site-derived") {
55740
56205
  }
55741
56206
  function bounded(s, max) {
55742
56207
  if (s.length <= max) return s;
55743
- const cut = s.slice(0, max - 1);
55744
- const space = cut.lastIndexOf(" ");
55745
- return `${(space > max * 0.6 ? cut.slice(0, space) : cut).trimEnd()}\u2026`;
56208
+ const cut2 = s.slice(0, max - 1);
56209
+ const space = cut2.lastIndexOf(" ");
56210
+ return `${(space > max * 0.6 ? cut2.slice(0, space) : cut2).trimEnd()}\u2026`;
55746
56211
  }
55747
56212
  function deriveStaticFaq(faq) {
55748
56213
  if (!Array.isArray(faq)) return [];
@@ -57440,11 +57905,11 @@ function editorText(fields, probes) {
57440
57905
  function stripLead(name) {
57441
57906
  const [left = "", ...rest] = name.split(/\s+&\s+/);
57442
57907
  const words = left.split(/\s+/);
57443
- let cut = 0;
57908
+ let cut2 = 0;
57444
57909
  words.forEach((w, i) => {
57445
- if (LEAD_STOPWORDS.has(w.toLowerCase())) cut = i + 1;
57910
+ if (LEAD_STOPWORDS.has(w.toLowerCase())) cut2 = i + 1;
57446
57911
  });
57447
- const kept = words.slice(cut).join(" ");
57912
+ const kept = words.slice(cut2).join(" ");
57448
57913
  return kept ? [kept, ...rest].join(" & ") : "";
57449
57914
  }
57450
57915
  function nameMismatches(identity, look, fields) {
@@ -57495,13 +57960,13 @@ function duplicateMedia(look, probes) {
57495
57960
  const k = mediaKey(u);
57496
57961
  counts.set(k, (counts.get(k) ?? 0) + 1);
57497
57962
  }
57498
- for (const [k, count] of counts) {
57499
- if (count < DUPLICATE_MEDIA_MIN || out.length >= CAP3.perKind) continue;
57963
+ for (const [k, count2] of counts) {
57964
+ if (count2 < DUPLICATE_MEDIA_MIN || out.length >= CAP3.perKind) continue;
57500
57965
  out.push({
57501
57966
  kind: "duplicate-media",
57502
57967
  value: k.slice(0, 80),
57503
57968
  where: [where],
57504
- note: `the same image is used ${count} times here \u2014 template filler, not ${count} distinct photos; do not present it as the business's gallery`
57969
+ note: `the same image is used ${count2} times here \u2014 template filler, not ${count2} distinct photos; do not present it as the business's gallery`
57505
57970
  });
57506
57971
  }
57507
57972
  };
@@ -58344,7 +58809,7 @@ var init_brand_font = __esm({
58344
58809
 
58345
58810
  // src/site-analyze/write-artifacts.ts
58346
58811
  import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
58347
- import { join as join59 } from "node:path";
58812
+ import { join as join60 } from "node:path";
58348
58813
  function attachVeloSources(book, sources, put) {
58349
58814
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
58350
58815
  for (const f of book.velo.files) {
@@ -58499,12 +58964,12 @@ async function downloadMedia(book, fetchImpl, put) {
58499
58964
  }
58500
58965
  async function writeBusinessBook(result, opts) {
58501
58966
  const out = opts.outDir;
58502
- mkdirSync29(join59(out, "evidence/pages"), { recursive: true });
58503
- mkdirSync29(join59(out, "media/catalog"), { recursive: true });
58967
+ mkdirSync29(join60(out, "evidence/pages"), { recursive: true });
58968
+ mkdirSync29(join60(out, "media/catalog"), { recursive: true });
58504
58969
  const files = [];
58505
58970
  const put = (rel, body) => {
58506
- const p = join59(out, rel);
58507
- mkdirSync29(join59(p, ".."), { recursive: true });
58971
+ const p = join60(out, rel);
58972
+ mkdirSync29(join60(p, ".."), { recursive: true });
58508
58973
  writeFileSync26(p, body);
58509
58974
  files.push(rel);
58510
58975
  };
@@ -58598,8 +59063,8 @@ var init_customer_build = __esm({
58598
59063
  });
58599
59064
 
58600
59065
  // src/site-analyze/apply.ts
58601
- import { existsSync as existsSync61, readFileSync as readFileSync72, writeFileSync as writeFileSync27, copyFileSync as copyFileSync3, mkdirSync as mkdirSync30, realpathSync as realpathSync8, statSync as statSync16 } from "node:fs";
58602
- import { dirname as dirname33, isAbsolute as isAbsolute4, join as join60, resolve as resolve45, sep as sep14 } from "node:path";
59066
+ import { existsSync as existsSync62, readFileSync as readFileSync73, writeFileSync as writeFileSync27, copyFileSync as copyFileSync3, mkdirSync as mkdirSync30, realpathSync as realpathSync8, statSync as statSync16 } from "node:fs";
59067
+ import { dirname as dirname33, isAbsolute as isAbsolute4, join as join61, resolve as resolve45, sep as sep14 } from "node:path";
58603
59068
  import { spawnSync as spawnSync4 } from "node:child_process";
58604
59069
  function unpairedRedirectNote(overlay) {
58605
59070
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -58614,7 +59079,7 @@ function committedShellProvisioning(path2) {
58614
59079
  function findRepoRoot(cwd) {
58615
59080
  let dir = cwd;
58616
59081
  for (let i = 0; i < 8; i++) {
58617
- if (SHELL_NAMES2.some((name) => existsSync61(join60(dir, shellAppDir(name), "package.json")))) return dir;
59082
+ if (SHELL_NAMES2.some((name) => existsSync62(join61(dir, shellAppDir(name), "package.json")))) return dir;
58618
59083
  const next = dirname33(dir);
58619
59084
  if (next === dir) break;
58620
59085
  dir = next;
@@ -58683,11 +59148,11 @@ function mergeCustomerOwned(committed, overlay) {
58683
59148
  }
58684
59149
  function buildMergedContract(options) {
58685
59150
  const { appDir: appDir2, packageDir, overlay } = options;
58686
- const committedPath = join60(appDir2, "provisioning.json");
58687
- if (!existsSync61(committedPath)) {
59151
+ const committedPath = join61(appDir2, "provisioning.json");
59152
+ if (!existsSync62(committedPath)) {
58688
59153
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
58689
59154
  }
58690
- const committed = JSON.parse(readFileSync72(committedPath, "utf8"));
59155
+ const committed = JSON.parse(readFileSync73(committedPath, "utf8"));
58691
59156
  const merged = mergeCustomerOwned(committed, overlay);
58692
59157
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
58693
59158
  const branding = merged.branding ?? {};
@@ -58699,8 +59164,8 @@ function buildMergedContract(options) {
58699
59164
  };
58700
59165
  const stage = (fromRel, toRel) => {
58701
59166
  const from = contained(packageDir, fromRel);
58702
- const to = contained(join60(appDir2, STAGED), toRel.slice(STAGED.length + 1));
58703
- if (!from || !to || !existsSync61(from)) return void 0;
59167
+ const to = contained(join61(appDir2, STAGED), toRel.slice(STAGED.length + 1));
59168
+ if (!from || !to || !existsSync62(from)) return void 0;
58704
59169
  let real;
58705
59170
  let realRoot;
58706
59171
  try {
@@ -58722,11 +59187,11 @@ function buildMergedContract(options) {
58722
59187
  };
58723
59188
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
58724
59189
  if (icon) branding.appIconPath = icon;
58725
- const facesPath = join60(packageDir, "media/fonts/faces.json");
58726
- if (existsSync61(facesPath)) {
59190
+ const facesPath = join61(packageDir, "media/fonts/faces.json");
59191
+ if (existsSync62(facesPath)) {
58727
59192
  let faces = [];
58728
59193
  try {
58729
- const parsed = JSON.parse(readFileSync72(facesPath, "utf8"));
59194
+ const parsed = JSON.parse(readFileSync73(facesPath, "utf8"));
58730
59195
  faces = Array.isArray(parsed) ? parsed : [];
58731
59196
  } catch {
58732
59197
  faces = [];
@@ -58747,9 +59212,9 @@ function buildMergedContract(options) {
58747
59212
  merged.branding = branding;
58748
59213
  const body = `${JSON.stringify(merged, null, 2)}
58749
59214
  `;
58750
- const path2 = join60(packageDir, "provisioning.contract.json");
59215
+ const path2 = join61(packageDir, "provisioning.contract.json");
58751
59216
  writeFileSync27(path2, body);
58752
- const localPath = join60(appDir2, LOCAL_CONTRACT_REL);
59217
+ const localPath = join61(appDir2, LOCAL_CONTRACT_REL);
58753
59218
  mkdirSync30(dirname33(localPath), { recursive: true });
58754
59219
  writeFileSync27(localPath, body);
58755
59220
  staged.push(localPath);
@@ -58784,10 +59249,10 @@ function catalogTypography(overlay, template) {
58784
59249
  function staticFaqFor(overlay, overlayPath) {
58785
59250
  const own3 = overlay.faq;
58786
59251
  if (own3 !== void 0) return deriveStaticFaq(own3);
58787
- const bookPath = join60(dirname33(resolve45(overlayPath)), "book.json");
58788
- if (!existsSync61(bookPath)) return void 0;
59252
+ const bookPath = join61(dirname33(resolve45(overlayPath)), "book.json");
59253
+ if (!existsSync62(bookPath)) return void 0;
58789
59254
  try {
58790
- const book = JSON.parse(readFileSync72(bookPath, "utf8"));
59255
+ const book = JSON.parse(readFileSync73(bookPath, "utf8"));
58791
59256
  const copy = book?.copy;
58792
59257
  if (!copy || typeof copy !== "object" || copy.unavailable) return void 0;
58793
59258
  const pairs = deriveStaticFaq(copy.faq);
@@ -58801,8 +59266,8 @@ async function applyOverlays(input) {
58801
59266
  const cwd = resolve45(input.cwd);
58802
59267
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
58803
59268
  assertKnownShell(shell);
58804
- const overlay = JSON.parse(readFileSync72(input.overlayPath, "utf8"));
58805
- const tokens = JSON.parse(readFileSync72(input.tokensPath, "utf8"));
59269
+ const overlay = JSON.parse(readFileSync73(input.overlayPath, "utf8"));
59270
+ const tokens = JSON.parse(readFileSync73(input.tokensPath, "utf8"));
58806
59271
  const faq = staticFaqFor(overlay, input.overlayPath);
58807
59272
  if (faq !== void 0) overlay.faq = faq;
58808
59273
  const wrote = [];
@@ -58814,7 +59279,7 @@ async function applyOverlays(input) {
58814
59279
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
58815
59280
  );
58816
59281
  }
58817
- const appDir2 = join60(repoRoot2, shellAppDir(shell));
59282
+ const appDir2 = join61(repoRoot2, shellAppDir(shell));
58818
59283
  const packageDir = dirname33(resolve45(input.overlayPath));
58819
59284
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
58820
59285
  wrote.push(...staged);
@@ -58836,11 +59301,11 @@ async function applyOverlays(input) {
58836
59301
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
58837
59302
  };
58838
59303
  }
58839
- const pkgProv = join60(cwd, "provisioning.json");
59304
+ const pkgProv = join61(cwd, "provisioning.json");
58840
59305
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
58841
59306
  let packageFaq = faq;
58842
- if (existsSync61(pkgProv)) {
58843
- const cur = JSON.parse(readFileSync72(pkgProv, "utf8"));
59307
+ if (existsSync62(pkgProv)) {
59308
+ const cur = JSON.parse(readFileSync73(pkgProv, "utf8"));
58844
59309
  const merged = deepMerge4(cur, overlay);
58845
59310
  packageFaq = merged.faq;
58846
59311
  writeFileSync27(pkgProv, JSON.stringify(merged, null, 2) + "\n");
@@ -58849,9 +59314,9 @@ async function applyOverlays(input) {
58849
59314
  writeFileSync27(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
58850
59315
  wrote.push(pkgProv);
58851
59316
  }
58852
- const pkgTokens = join60(cwd, "tokens.json");
58853
- if (existsSync61(pkgTokens) && (tokens.colors || tokens.typography)) {
58854
- const cur = JSON.parse(readFileSync72(pkgTokens, "utf8"));
59317
+ const pkgTokens = join61(cwd, "tokens.json");
59318
+ if (existsSync62(pkgTokens) && (tokens.colors || tokens.typography)) {
59319
+ const cur = JSON.parse(readFileSync73(pkgTokens, "utf8"));
58855
59320
  let tokensIn = tokens;
58856
59321
  if (tokens.typography) {
58857
59322
  const fonts = catalogTypography(tokens.typography, cur.typography);
@@ -58876,9 +59341,9 @@ async function applyOverlays(input) {
58876
59341
  wrote.push(pkgTokens);
58877
59342
  }
58878
59343
  const homeAction = homeRefreshActionId(shell);
58879
- const homeSrc = join60(cwd, "dev/index.ts");
58880
- if (homeAction && existsSync61(homeSrc)) {
58881
- const prev = readFileSync72(homeSrc, "utf8");
59344
+ const homeSrc = join61(cwd, "dev/index.ts");
59345
+ if (homeAction && existsSync62(homeSrc)) {
59346
+ const prev = readFileSync73(homeSrc, "utf8");
58882
59347
  const patched = patchBrandParams(prev, overlay, homeAction);
58883
59348
  let next = patched.src;
58884
59349
  if (!patched.foreign && faq?.length && prev.includes('"tier5.himiBrandedFaq.load"') && !prev.includes('"staticFaq"')) {
@@ -58909,13 +59374,13 @@ async function applyOverlays(input) {
58909
59374
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
58910
59375
  }
58911
59376
  }
58912
- if (!input.iconPath || !existsSync61(input.iconPath)) {
59377
+ if (!input.iconPath || !existsSync62(input.iconPath)) {
58913
59378
  const book = input.iconPath ? dirname33(dirname33(resolve45(input.iconPath))) : void 0;
58914
59379
  notes.push(
58915
59380
  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"
58916
59381
  );
58917
59382
  }
58918
- if (input.iconPath && existsSync61(input.iconPath)) {
59383
+ if (input.iconPath && existsSync62(input.iconPath)) {
58919
59384
  try {
58920
59385
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
58921
59386
  wrote.push(...installed.wrote);
@@ -63102,7 +63567,7 @@ __export(cli_exports, {
63102
63567
  runSiteAnalyze: () => runSiteAnalyze,
63103
63568
  runSiteFunctionCli: () => runSiteFunctionCli
63104
63569
  });
63105
- import { readFileSync as readFileSync73 } from "node:fs";
63570
+ import { readFileSync as readFileSync74 } from "node:fs";
63106
63571
  import { resolve as resolve46 } from "node:path";
63107
63572
  function assetFetchFor(_opts) {
63108
63573
  return pinnedFetch2;
@@ -63201,7 +63666,7 @@ async function runSiteFunctionCli(opts) {
63201
63666
  let source;
63202
63667
  if (opts.sourcePath) {
63203
63668
  const p = resolve46(opts.cwd ?? process.cwd(), opts.sourcePath);
63204
- source = readFileSync73(p, "utf8");
63669
+ source = readFileSync74(p, "utf8");
63205
63670
  }
63206
63671
  let body;
63207
63672
  if (opts.bodyRaw != null) {
@@ -63460,7 +63925,7 @@ __export(site_create_exports, {
63460
63925
  runSiteList: () => runSiteList,
63461
63926
  writeProvisioningBinding: () => writeProvisioningBinding
63462
63927
  });
63463
- import { existsSync as existsSync62, readFileSync as readFileSync74, writeFileSync as writeFileSync28 } from "node:fs";
63928
+ import { existsSync as existsSync63, readFileSync as readFileSync75, writeFileSync as writeFileSync28 } from "node:fs";
63464
63929
  import { resolve as resolvePath2 } from "node:path";
63465
63930
  function oauthClientName(projectName) {
63466
63931
  return `${projectName} client`;
@@ -63609,7 +64074,7 @@ async function runSiteCreate(opts) {
63609
64074
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
63610
64075
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
63611
64076
  }
63612
- function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync62, readFileSync: readFileSync74, writeFileSync: writeFileSync28 }) {
64077
+ function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync63, readFileSync: readFileSync75, writeFileSync: writeFileSync28 }) {
63613
64078
  const msid = binding2.siteId;
63614
64079
  const path2 = resolvePath2(target);
63615
64080
  const committed = committedShellProvisioning(path2);
@@ -64282,11 +64747,11 @@ var init_context = __esm({
64282
64747
  });
64283
64748
 
64284
64749
  // src/cli.ts
64285
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync75, mkdirSync as mkdirSync31, writeFileSync as writeFileSync29, existsSync as existsSync63, rmSync as rmSync14, readdirSync as readdirSync29, statSync as statSync17, lstatSync as lstatSync11, accessSync, constants as constants2, renameSync as renameSync7, cpSync as cpSync3 } from "node:fs";
64750
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync76, mkdirSync as mkdirSync31, writeFileSync as writeFileSync29, existsSync as existsSync64, rmSync as rmSync14, readdirSync as readdirSync30, statSync as statSync17, lstatSync as lstatSync11, accessSync, constants as constants2, renameSync as renameSync7, cpSync as cpSync3 } from "node:fs";
64286
64751
  import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
64287
64752
  import { createHash as createHash28, randomUUID as randomUUID5 } from "node:crypto";
64288
64753
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
64289
- import { join as join61, resolve as resolve47, relative as relative11, basename as basename15, dirname as dirname34 } from "node:path";
64754
+ import { join as join62, resolve as resolve47, relative as relative11, basename as basename15, dirname as dirname34 } from "node:path";
64290
64755
  import { fileURLToPath as fileURLToPath24 } from "node:url";
64291
64756
 
64292
64757
  // ../serve-cli/src/client.ts
@@ -65487,14 +65952,14 @@ async function seedCms(opts) {
65487
65952
  report2.ok = false;
65488
65953
  continue;
65489
65954
  }
65490
- const count = await countItems(call, id);
65491
- if (count === null && !force) {
65955
+ const count2 = await countItems(call, id);
65956
+ if (count2 === null && !force) {
65492
65957
  report2.skipped.push({ collection: id, reason: "could not read the current row count, so seeding might bury existing content. Re-run, or pass --force if you are sure." });
65493
65958
  report2.ok = false;
65494
65959
  continue;
65495
65960
  }
65496
- if (count !== null && count > 0 && !force) {
65497
- report2.skipped.push({ collection: id, reason: `already has ${count} items \u2014 seeding an already-populated collection needs --force so real content is never buried under sample rows` });
65961
+ if (count2 !== null && count2 > 0 && !force) {
65962
+ report2.skipped.push({ collection: id, reason: `already has ${count2} items \u2014 seeding an already-populated collection needs --force so real content is never buried under sample rows` });
65498
65963
  continue;
65499
65964
  }
65500
65965
  if (!dryRun) {
@@ -65585,7 +66050,7 @@ function placeholder(type, key2, i) {
65585
66050
  }
65586
66051
  }
65587
66052
  async function writeSeedRows(opts) {
65588
- const { app, cms, dir, count = 8 } = opts;
66053
+ const { app, cms, dir, count: count2 = 8 } = opts;
65589
66054
  const file = join8(dir, "cms-seed.json");
65590
66055
  const owned = (cms?.collections ?? []).filter((s) => s.id?.startsWith(namespacePrefix(app)));
65591
66056
  if (!owned.length) {
@@ -65616,10 +66081,10 @@ async function writeSeedRows(opts) {
65616
66081
  const collections = {};
65617
66082
  for (const spec of owned) {
65618
66083
  existing[spec.id] = Array.from(
65619
- { length: count },
66084
+ { length: count2 },
65620
66085
  (_, i) => Object.fromEntries(spec.fields.map((f) => [f.key, placeholder(f.type, f.displayName || f.key, i)]))
65621
66086
  );
65622
- collections[spec.id] = count;
66087
+ collections[spec.id] = count2;
65623
66088
  }
65624
66089
  writeFileSync6(file, `${JSON.stringify(existing, null, 2)}
65625
66090
  `);
@@ -65634,7 +66099,7 @@ ${s.fields.map((f) => ` ${f.key} (${f.type})`).join("\n")}`
65634
66099
  schema,
65635
66100
  ``,
65636
66101
  `Rules:`,
65637
- ` - Keep the exact same JSON shape: one key per collection id, an array of ${count} row objects.`,
66102
+ ` - Keep the exact same JSON shape: one key per collection id, an array of ${count2} row objects.`,
65638
66103
  ` - Use ONLY the field keys listed above, and keep each value's JSON type (TEXT\u2192string, NUMBER\u2192number, BOOLEAN\u2192boolean, DATE/DATETIME\u2192ISO string, IMAGE/VIDEO/AUDIO/DOCUMENT\u2192an https URL).`,
65639
66104
  ` - Make the content specific and plausible for this app's domain \u2014 real-sounding names, sensible prices, varied categories. No "Lorem ipsum", no "Sample item 1".`,
65640
66105
  ` - Leave any collection key already in the file that is not listed above untouched.`,
@@ -66402,8 +66867,8 @@ var str6 = (v) => typeof v === "string" ? v : void 0;
66402
66867
  var num2 = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
66403
66868
  function embeddedTemplateCatalog() {
66404
66869
  return availableTemplates().map((name) => {
66405
- const path2 = join61(templateSourceDir(name), TEMPLATE_META_FILE);
66406
- const meta = JSON.parse(readFileSync75(path2, "utf8"));
66870
+ const path2 = join62(templateSourceDir(name), TEMPLATE_META_FILE);
66871
+ const meta = JSON.parse(readFileSync76(path2, "utf8"));
66407
66872
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
66408
66873
  throw new Error(`${path2} must be { title: string, description: string, tags: string[], category?: string }`);
66409
66874
  }
@@ -67082,7 +67547,7 @@ var COMMANDS = [
67082
67547
  ];
67083
67548
  function cliVersion() {
67084
67549
  try {
67085
- return JSON.parse(readFileSync75(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
67550
+ return JSON.parse(readFileSync76(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
67086
67551
  } catch {
67087
67552
  return "unknown";
67088
67553
  }
@@ -67200,7 +67665,7 @@ function ageInDays(publishedAt, now = Date.now()) {
67200
67665
  }
67201
67666
  function packageVersion(path2) {
67202
67667
  try {
67203
- const version2 = JSON.parse(readFileSync75(path2, "utf8")).version;
67668
+ const version2 = JSON.parse(readFileSync76(path2, "utf8")).version;
67204
67669
  return typeof version2 === "string" && version2 ? version2 : void 0;
67205
67670
  } catch {
67206
67671
  return void 0;
@@ -67209,7 +67674,7 @@ function packageVersion(path2) {
67209
67674
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
67210
67675
  let dir = resolve47(fromDir);
67211
67676
  for (; ; ) {
67212
- const version2 = packageVersion(join61(dir, "node_modules", "@wix", "himalaya", "package.json"));
67677
+ const version2 = packageVersion(join62(dir, "node_modules", "@wix", "himalaya", "package.json"));
67213
67678
  if (version2) return { version: version2, source: "local" };
67214
67679
  const parent = dirname34(dir);
67215
67680
  if (parent === dir) break;
@@ -67219,7 +67684,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
67219
67684
  if (bundled) return { version: bundled, source: "bundled" };
67220
67685
  try {
67221
67686
  const root = execFileSync7("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
67222
- const version2 = root ? packageVersion(join61(root, "@wix", "himalaya", "package.json")) : void 0;
67687
+ const version2 = root ? packageVersion(join62(root, "@wix", "himalaya", "package.json")) : void 0;
67223
67688
  return version2 ? { version: version2, source: "global" } : {};
67224
67689
  } catch {
67225
67690
  return {};
@@ -67266,11 +67731,11 @@ var UPDATE_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1e3;
67266
67731
  var UPDATE_SNOOZE_MS = 24 * 60 * 60 * 1e3;
67267
67732
  var UPDATE_REEXEC_ENV = "HIMI_UPDATED_ONCE";
67268
67733
  function updateCachePath(env = process.env) {
67269
- return env.HIMI_UPDATE_CACHE_FILE ?? join61(homedir7(), ".himalaya", "update-check.json");
67734
+ return env.HIMI_UPDATE_CACHE_FILE ?? join62(homedir7(), ".himalaya", "update-check.json");
67270
67735
  }
67271
67736
  function readUpdateCache(path2) {
67272
67737
  try {
67273
- const parsed = JSON.parse(readFileSync75(path2, "utf8"));
67738
+ const parsed = JSON.parse(readFileSync76(path2, "utf8"));
67274
67739
  return parsed && typeof parsed === "object" ? parsed : {};
67275
67740
  } catch {
67276
67741
  return {};
@@ -67338,12 +67803,12 @@ function createUpdateRunner(spawn7 = spawnSync5) {
67338
67803
  },
67339
67804
  installedVersion: () => {
67340
67805
  const root = globalNodeModulesRoot();
67341
- return root ? packageVersion(join61(root, "@wix", "himalaya-cli", "package.json")) : void 0;
67806
+ return root ? packageVersion(join62(root, "@wix", "himalaya-cli", "package.json")) : void 0;
67342
67807
  },
67343
67808
  reexec: (argv) => {
67344
67809
  const root = globalNodeModulesRoot();
67345
- const entry = root ? join61(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
67346
- if (!entry || !existsSync63(entry)) return void 0;
67810
+ const entry = root ? join62(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
67811
+ if (!entry || !existsSync64(entry)) return void 0;
67347
67812
  const result = spawnSync5(process.execPath, [entry, ...argv], {
67348
67813
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
67349
67814
  stdio: "inherit",
@@ -67390,7 +67855,7 @@ function staleUpdateLockFile(cachePath, now) {
67390
67855
  for (const path2 of [`${cachePath}.lock.break`, `${cachePath}.lock`]) {
67391
67856
  let content;
67392
67857
  try {
67393
- content = readFileSync75(path2, "utf8");
67858
+ content = readFileSync76(path2, "utf8");
67394
67859
  } catch {
67395
67860
  continue;
67396
67861
  }
@@ -67413,7 +67878,7 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
67413
67878
  const mine = lockStamp(now);
67414
67879
  const readLock = (path2) => {
67415
67880
  try {
67416
- return readFileSync75(path2, "utf8");
67881
+ return readFileSync76(path2, "utf8");
67417
67882
  } catch {
67418
67883
  return void 0;
67419
67884
  }
@@ -67997,7 +68462,7 @@ function executableOnPath(name, env = process.env) {
67997
68462
  const suffixes = process.platform === "win32" ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
67998
68463
  for (const dir of entries) {
67999
68464
  for (const suffix of suffixes) {
68000
- const candidate = join61(dir, `${name}${suffix}`);
68465
+ const candidate = join62(dir, `${name}${suffix}`);
68001
68466
  try {
68002
68467
  if (!statSync17(candidate).isFile()) continue;
68003
68468
  if (process.platform !== "win32") accessSync(candidate, constants2.X_OK);
@@ -68106,7 +68571,7 @@ async function nativeConfigAdvisories(app, configSource) {
68106
68571
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
68107
68572
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
68108
68573
  const iosYmlPath = iosProjectYml2(app);
68109
- const iosYml = existsSync63(iosYmlPath) ? readFileSync75(iosYmlPath, "utf8") : "";
68574
+ const iosYml = existsSync64(iosYmlPath) ? readFileSync76(iosYmlPath, "utf8") : "";
68110
68575
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
68111
68576
  if (!optedIn) {
68112
68577
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -68120,7 +68585,7 @@ async function nativeConfigAdvisories(app, configSource) {
68120
68585
  }
68121
68586
  function readConfigSource(dir) {
68122
68587
  try {
68123
- return readFileSync75(join61(dir, "config.ts"), "utf8");
68588
+ return readFileSync76(join62(dir, "config.ts"), "utf8");
68124
68589
  } catch {
68125
68590
  return null;
68126
68591
  }
@@ -68156,7 +68621,7 @@ function keptPlan(kept) {
68156
68621
  }
68157
68622
  async function initFromRemoteTemplate(name, parentDir, template, c) {
68158
68623
  const dir = resolve47(parentDir, name);
68159
- if (existsSync63(join61(dir, "himalaya.content.json"))) {
68624
+ if (existsSync64(join62(dir, "himalaya.content.json"))) {
68160
68625
  throw new Error(`content package already exists at ${dir}`);
68161
68626
  }
68162
68627
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -68199,11 +68664,11 @@ function symlinkDestinationIssue(dest) {
68199
68664
  function restoreDestinationIssue(dest, force) {
68200
68665
  const link = symlinkDestinationIssue(dest);
68201
68666
  if (link) return link;
68202
- if (!existsSync63(dest)) return null;
68667
+ if (!existsSync64(dest)) return null;
68203
68668
  if (!statSync17(dest).isDirectory()) {
68204
68669
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
68205
68670
  }
68206
- if (readdirSync29(dest).length > 0 && !force) {
68671
+ if (readdirSync30(dest).length > 0 && !force) {
68207
68672
  return {
68208
68673
  error: `${dest} already exists and is not empty`,
68209
68674
  hint: "pass --force to write into it anyway, or --out/--name to land somewhere else"
@@ -68214,7 +68679,7 @@ function restoreDestinationIssue(dest, force) {
68214
68679
  function isHimalayaMonorepoCheckout(start) {
68215
68680
  let dir = resolve47(start);
68216
68681
  for (; ; ) {
68217
- if (existsSync63(join61(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync63(join61(dir, "core", "serve", "src", "server.ts"))) return true;
68682
+ if (existsSync64(join62(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync64(join62(dir, "core", "serve", "src", "server.ts"))) return true;
68218
68683
  const parent = dirname34(dir);
68219
68684
  if (parent === dir) return false;
68220
68685
  dir = parent;
@@ -68275,7 +68740,7 @@ function materializeForPreview(appDir2, slug, displayName) {
68275
68740
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
68276
68741
  );
68277
68742
  const repoRoot2 = resolve47(fileURLToPath24(new URL("../../..", import.meta.url)));
68278
- const root = join61(repoRoot2, "node_modules", ".cache", "himi-template-preview");
68743
+ const root = join62(repoRoot2, "node_modules", ".cache", "himi-template-preview");
68279
68744
  const stillRunning = (pid) => {
68280
68745
  try {
68281
68746
  process.kill(pid, 0);
@@ -68285,16 +68750,16 @@ function materializeForPreview(appDir2, slug, displayName) {
68285
68750
  }
68286
68751
  };
68287
68752
  try {
68288
- for (const entry of readdirSync29(root, { withFileTypes: true })) {
68753
+ for (const entry of readdirSync30(root, { withFileTypes: true })) {
68289
68754
  if (!entry.isDirectory()) continue;
68290
68755
  const pid = Number(entry.name.startsWith(`${slug}-`) ? entry.name.slice(slug.length + 1) : NaN);
68291
68756
  if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && !stillRunning(pid)) {
68292
- rmSync14(join61(root, entry.name), { recursive: true, force: true });
68757
+ rmSync14(join62(root, entry.name), { recursive: true, force: true });
68293
68758
  }
68294
68759
  }
68295
68760
  } catch {
68296
68761
  }
68297
- const dest = join61(root, `${slug}-${process.pid}`);
68762
+ const dest = join62(root, `${slug}-${process.pid}`);
68298
68763
  rmSync14(dest, { recursive: true, force: true });
68299
68764
  mkdirSync31(dest, { recursive: true });
68300
68765
  try {
@@ -68307,8 +68772,8 @@ function materializeForPreview(appDir2, slug, displayName) {
68307
68772
  }
68308
68773
  async function appId(dir) {
68309
68774
  try {
68310
- const { readFileSync: readFileSync76 } = await import("node:fs");
68311
- const m = JSON.parse(readFileSync76(join61(dir, "himalaya.content.json"), "utf8"));
68775
+ const { readFileSync: readFileSync77 } = await import("node:fs");
68776
+ const m = JSON.parse(readFileSync77(join62(dir, "himalaya.content.json"), "utf8"));
68312
68777
  if (m.name) return m.name;
68313
68778
  } catch {
68314
68779
  }
@@ -68316,8 +68781,8 @@ async function appId(dir) {
68316
68781
  }
68317
68782
  async function contentVisibility(dir) {
68318
68783
  try {
68319
- const { readFileSync: readFileSync76 } = await import("node:fs");
68320
- const m = JSON.parse(readFileSync76(join61(dir, "himalaya.content.json"), "utf8"));
68784
+ const { readFileSync: readFileSync77 } = await import("node:fs");
68785
+ const m = JSON.parse(readFileSync77(join62(dir, "himalaya.content.json"), "utf8"));
68321
68786
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
68322
68787
  } catch {
68323
68788
  }
@@ -68541,7 +69006,7 @@ Seed your coding agent with \`himi agent-init\`. Guides: ${idx.portalUrl}`);
68541
69006
  ...res.removed.length ? { removed: res.removed, note: "older copies at the app root, moved to .himi/legacy-agent-files/ (a backup, in case one became app code) \u2014 the current ones live in .agents/skills/himalaya/" } : {},
68542
69007
  // In a git repo the refresh moves the diff identity an earlier `himi test` attested (that
68543
69008
  // hash is shared with the repo gates and deliberately exempts nothing but its own metadata).
68544
- ...existsSync63(join61(dir, ".himi", "attestations", "latest.json")) ? { next: "if `himi push` then refuses the attestation as stale, re-run `himi test` \u2014 in a git checkout this refresh changed the tree it covered" } : {},
69009
+ ...existsSync64(join62(dir, ".himi", "attestations", "latest.json")) ? { next: "if `himi push` then refuses the attestation as stale, re-run `himi test` \u2014 in a git checkout this refresh changed the tree it covered" } : {},
68545
69010
  ...res.skipped.length ? { skipped: res.skipped } : {},
68546
69011
  portalUrl: idx.portalUrl
68547
69012
  });
@@ -68564,7 +69029,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
68564
69029
  try {
68565
69030
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68566
69031
  const bres = await buildContentForValidation2(dir, { resolution });
68567
- out = mkdtempSync4(join61(tmpdir5(), "himi-ux-record-"));
69032
+ out = mkdtempSync4(join62(tmpdir5(), "himi-ux-record-"));
68568
69033
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68569
69034
  const outcome = await buildReleaseFromContent2(dir, out, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {}, collect: { lint: true } });
68570
69035
  const releaseId = outcome.manifest.releaseId;
@@ -68731,7 +69196,7 @@ async function producedWidgetFamilies(dir) {
68731
69196
  }
68732
69197
  const outDir = tier5Dir.bundleOutDir(dir);
68733
69198
  try {
68734
- readdirSync29(outDir);
69199
+ readdirSync30(outDir);
68735
69200
  } catch {
68736
69201
  return { families: void 0, source: "unbuilt" };
68737
69202
  }
@@ -68742,7 +69207,7 @@ async function producedWidgetFamilies(dir) {
68742
69207
  let js;
68743
69208
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
68744
69209
  try {
68745
- js = readFileSync75(join61(outDir, candidate), "utf8");
69210
+ js = readFileSync76(join62(outDir, candidate), "utf8");
68746
69211
  break;
68747
69212
  } catch {
68748
69213
  }
@@ -68773,12 +69238,27 @@ async function producedWidgetFamilies(dir) {
68773
69238
  }
68774
69239
  return { families, source: "dist/bundles" };
68775
69240
  }
68776
- var boardOpen = (dir) => existsSync63(join61(dir, ".himi", "studio", "index.html"));
69241
+ var boardOpen = (dir) => existsSync64(join62(dir, ".himi", "studio", "index.html"));
68777
69242
  var BOARD_REFRESHERS = /* @__PURE__ */ new Set(["render", "test", "validate", "build", "push", "icon", "site", "ux", "progress"]);
68778
69243
  async function leaveForBoard(dir, record2, value) {
68779
69244
  if (!boardOpen(dir)) return;
68780
- const { writeStudioRecord: writeStudioRecord2, VALIDATE_RECORD: VALIDATE_RECORD2, PUSH_RECORD: PUSH_RECORD2, EDGES_RECORD: EDGES_RECORD2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
68781
- writeStudioRecord2(dir, { validate: VALIDATE_RECORD2, push: PUSH_RECORD2, edges: EDGES_RECORD2 }[record2], value);
69245
+ const { writeStudioRecord: writeStudioRecord2, VALIDATE_RECORD: VALIDATE_RECORD2, PUSH_RECORD: PUSH_RECORD2, EDGES_RECORD: EDGES_RECORD2, MAP_RECORD: MAP_RECORD2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
69246
+ writeStudioRecord2(dir, { validate: VALIDATE_RECORD2, push: PUSH_RECORD2, edges: EDGES_RECORD2, map: MAP_RECORD2 }[record2], value);
69247
+ }
69248
+ async function liveMapFor(dir) {
69249
+ if (!boardOpen(dir)) return void 0;
69250
+ const { writeStudioRecord: writeStudioRecord2, refreshStudio: refreshStudio2, MAP_RECORD: MAP_RECORD2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
69251
+ let last = 0;
69252
+ return (map) => {
69253
+ const now = Date.now();
69254
+ if (now - last < 1e3) return;
69255
+ last = now;
69256
+ try {
69257
+ writeStudioRecord2(dir, MAP_RECORD2, map);
69258
+ refreshStudio2(dir);
69259
+ } catch {
69260
+ }
69261
+ };
68782
69262
  }
68783
69263
  async function run(argv, io = defaultIO, deps = {}) {
68784
69264
  const code = await runCommand(argv, io, deps);
@@ -69053,7 +69533,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69053
69533
  const owed = openApprovals2(readApprovals2(dir).events).length;
69054
69534
  next.push(`carried the planning journal into the app (${carried.join(", ")}${owed ? `; ${owed} approval${owed === 1 ? "" : "s"} still owed` : ""}) \u2014 \`himi progress --brief\` there shows it`);
69055
69535
  }
69056
- const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f) => existsSync63(join61(parentDir, f)));
69536
+ const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f) => existsSync64(join62(parentDir, f)));
69057
69537
  if (planInParent.length) {
69058
69538
  next.push(`${planInParent.join(" and ")} in ${parentDir} ${planInParent.length > 1 ? "are" : "is"} not the app's \u2014 if that is the approved plan, move it over the template's boilerplate in ${dir}`);
69059
69539
  }
@@ -69180,7 +69660,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69180
69660
  const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
69181
69661
  let meta;
69182
69662
  try {
69183
- meta = JSON.parse(readFileSync75(join61(sourceDir, TEMPLATE_META_FILE), "utf8"));
69663
+ meta = JSON.parse(readFileSync76(join62(sourceDir, TEMPLATE_META_FILE), "utf8"));
69184
69664
  } catch {
69185
69665
  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>)` }));
69186
69666
  return 2;
@@ -69190,7 +69670,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69190
69670
  return 2;
69191
69671
  }
69192
69672
  try {
69193
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync75(join61(sourceDir, "tokens.json"), "utf8")));
69673
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync76(join62(sourceDir, "tokens.json"), "utf8")));
69194
69674
  meta = { ...appearance, ...meta };
69195
69675
  } catch {
69196
69676
  }
@@ -69212,7 +69692,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69212
69692
  const force = flags.force === true;
69213
69693
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
69214
69694
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
69215
- const out = mkdtempSync4(join61(tmpdir5(), "himi-template-"));
69695
+ const out = mkdtempSync4(join62(tmpdir5(), "himi-template-"));
69216
69696
  publishScratch.push(out);
69217
69697
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
69218
69698
  const outcome = await buildReleaseFromContent2(buildDir, out, {
@@ -69266,7 +69746,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69266
69746
  let iconHash;
69267
69747
  if (iconPath) {
69268
69748
  try {
69269
- const bytes2 = readFileSync75(iconPath);
69749
+ const bytes2 = readFileSync76(iconPath);
69270
69750
  payload.icon = bytes2.toString("base64");
69271
69751
  iconHash = createHash28("sha256").update(bytes2).digest("hex");
69272
69752
  } catch {
@@ -69472,12 +69952,12 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69472
69952
  print({ ok: false, error: "eject needs a module", hint: `one of: ${EJECTABLE2.join(", ")}` });
69473
69953
  return 2;
69474
69954
  }
69475
- const { readdirSync: readdirSync30, existsSync: fileExists } = await import("node:fs");
69955
+ const { readdirSync: readdirSync31, existsSync: fileExists } = await import("node:fs");
69476
69956
  const { join: pathJoin } = await import("node:path");
69477
69957
  const targets = [];
69478
69958
  const t5 = pathJoin(dir, "tier5-src");
69479
69959
  if (fileExists(t5)) {
69480
- for (const d of readdirSync30(t5, { withFileTypes: true })) {
69960
+ for (const d of readdirSync31(t5, { withFileTypes: true })) {
69481
69961
  if (d.isDirectory()) targets.push(pathJoin(t5, d.name, "index.ts"));
69482
69962
  }
69483
69963
  }
@@ -69509,7 +69989,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69509
69989
  const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
69510
69990
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
69511
69991
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
69512
- const out = mkdtempSync4(join61(tmpdir5(), "himi-validate-"));
69992
+ const out = mkdtempSync4(join62(tmpdir5(), "himi-validate-"));
69513
69993
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
69514
69994
  const outcome = await buildReleaseFromContent2(dir, out, {
69515
69995
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -69552,7 +70032,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69552
70032
  const path2 = dpxSessionTokenPath();
69553
70033
  let raw;
69554
70034
  try {
69555
- raw = readFileSync75(path2, "utf8");
70035
+ raw = readFileSync76(path2, "utf8");
69556
70036
  } catch {
69557
70037
  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`);
69558
70038
  }
@@ -69672,13 +70152,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69672
70152
  case "studio": {
69673
70153
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
69674
70154
  const dir = contentDir(flags);
69675
- if (!existsSync63(join61(dir, "himalaya.content.json"))) {
70155
+ if (!existsSync64(join62(dir, "himalaya.content.json"))) {
69676
70156
  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/" }));
69677
70157
  return 2;
69678
70158
  }
69679
70159
  if (pos[1] === "reel") {
69680
70160
  const { buildReel: buildReel2 } = await Promise.resolve().then(() => (init_studio_reel(), studio_reel_exports));
69681
- const out = resolve47(str6(flags.out) ?? join61(dir, ".himi", "making-of.html"));
70161
+ const out = resolve47(str6(flags.out) ?? join62(dir, ".himi", "making-of.html"));
69682
70162
  const { html, plan } = buildReel2(dir);
69683
70163
  mkdirSync31(dirname34(out), { recursive: true });
69684
70164
  writeFileSync29(out, html);
@@ -69745,7 +70225,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69745
70225
  } = await Promise.resolve().then(() => (init_progress(), progress_exports));
69746
70226
  const dir = contentDir(flags);
69747
70227
  const journal = journalPath2(dir);
69748
- const isContentPackage = existsSync63(join61(dir, "himalaya.content.json"));
70228
+ const isContentPackage = existsSync64(join62(dir, "himalaya.content.json"));
69749
70229
  const hint = isContentPackage ? {} : { hint: "no himalaya.content.json here \u2014 run from the app directory (or pass --content <dir>) so the build board finds this journal" };
69750
70230
  const message = pos.slice(1).join(" ").trim();
69751
70231
  const approvalFlags = ["approve", "done", "drop"].filter((f) => flags[f] !== void 0);
@@ -69817,8 +70297,8 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69817
70297
  try {
69818
70298
  const entry = appendProgress2(dir, { message, ...step ? { step } : {}, images });
69819
70299
  const notified = flags.notify === true ? await notifyDesktop2(`himi \xB7 ${appLabel2(dir) ?? "your app"}`, message) : void 0;
69820
- const { count } = summarizeProgress2(readProgress2(dir).entries);
69821
- print({ ok: true, entry, journal, count, ...notified === void 0 ? {} : { notified }, ...hint });
70300
+ const { count: count2 } = summarizeProgress2(readProgress2(dir).entries);
70301
+ print({ ok: true, entry, journal, count: count2, ...notified === void 0 ? {} : { notified }, ...hint });
69822
70302
  return 0;
69823
70303
  } catch (e) {
69824
70304
  if (e instanceof ProgressError2) {
@@ -69881,7 +70361,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69881
70361
  case "test": {
69882
70362
  const dir = contentDir(flags);
69883
70363
  const structuredOutput = flags.json === true || io.isTTY !== true;
69884
- if (existsSync63(join61(dir, "functions"))) {
70364
+ if (existsSync64(join62(dir, "functions"))) {
69885
70365
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
69886
70366
  const loaded = await loadFunctionsApp2(dir);
69887
70367
  const results = [];
@@ -69968,7 +70448,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69968
70448
  }
69969
70449
  }
69970
70450
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
69971
- const out = mkdtempSync4(join61(tmpdir5(), "himi-test-"));
70451
+ const out = mkdtempSync4(join62(tmpdir5(), "himi-test-"));
69972
70452
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
69973
70453
  const outcome = await buildReleaseFromContent2(dir, out, {
69974
70454
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -70013,7 +70493,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70013
70493
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
70014
70494
  return 2;
70015
70495
  }
70016
- const sources = collectWorkerSources2(join61(dir, "tier5-src"), screen.bundleName);
70496
+ const sources = collectWorkerSources2(join62(dir, "tier5-src"), screen.bundleName);
70017
70497
  if (!sources) {
70018
70498
  io.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
70019
70499
  return 2;
@@ -70114,6 +70594,8 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70114
70594
  const cmsSeed = offline ? null : await cmsSeedRows(dir, app);
70115
70595
  const canned = offline ? null : readCannedResponses2();
70116
70596
  const tabRoots = await tabRootScreens(dir);
70597
+ const onScreen = await liveMapFor(dir);
70598
+ const mapEntries = await navEntryScreens(dir, outcome.homeScreenId);
70117
70599
  let result;
70118
70600
  try {
70119
70601
  result = await runCrawl2({
@@ -70130,6 +70612,8 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70130
70612
  ...cmsSeed ? { cmsSeed } : {},
70131
70613
  ...canned ? { canned } : {},
70132
70614
  ...tabRoots.size ? { tabRoots } : {},
70615
+ ...onScreen ? { onScreen } : {},
70616
+ ...mapEntries.size ? { mapEntries } : {},
70133
70617
  // The app's exact linkable set, straight off the build outcome (#2512) — arms the
70134
70618
  // nav-target check. Omitted when the build reported none, so the crawl stays silent
70135
70619
  // rather than judging every target against an empty set.
@@ -70174,7 +70658,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70174
70658
  const sp = await runShotsPass2({
70175
70659
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
70176
70660
  tokens: outcome.tokens,
70177
- outDir: join61(dir, ".himi", "shots"),
70661
+ outDir: join62(dir, ".himi", "shots"),
70178
70662
  width: 390,
70179
70663
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
70180
70664
  ...budgetMs ? { budgetMs } : {},
@@ -70212,11 +70696,11 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70212
70696
  releaseId: result.releaseId,
70213
70697
  releasePayload: release,
70214
70698
  descriptors: attestedDescriptors,
70215
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join61(dir, ".himi", "shots", name)])),
70699
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join62(dir, ".himi", "shots", name)])),
70216
70700
  coverage: {
70217
70701
  screens: crawledScreens.size,
70218
70702
  actionsDispatched: result.actions.exercised,
70219
- uncoveredFns: (result.crawled ?? []).reduce((count, screen) => count + (screen.uncoveredFunctions?.length ?? 0), 0),
70703
+ uncoveredFns: (result.crawled ?? []).reduce((count2, screen) => count2 + (screen.uncoveredFunctions?.length ?? 0), 0),
70220
70704
  gaps: ledger.length
70221
70705
  },
70222
70706
  report: {
@@ -70233,6 +70717,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70233
70717
  }));
70234
70718
  } else io.log(formatReport2(result) + formatDelta2(delta) + formatLedger2(ledger, trend));
70235
70719
  await leaveForBoard(dir, "edges", result.edges ?? []);
70720
+ if (result.navGraph) await leaveForBoard(dir, "map", result.navGraph);
70236
70721
  return result.ok ? 0 : 1;
70237
70722
  }
70238
70723
  case "render": {
@@ -70302,7 +70787,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70302
70787
  return 2;
70303
70788
  }
70304
70789
  try {
70305
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync75(stateArg.replace(/^@/, ""), "utf8");
70790
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync76(stateArg.replace(/^@/, ""), "utf8");
70306
70791
  injectedState = JSON.parse(raw);
70307
70792
  } catch (e) {
70308
70793
  io.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e.message}` }));
@@ -70317,7 +70802,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70317
70802
  return 2;
70318
70803
  }
70319
70804
  try {
70320
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync75(inputArg.replace(/^@/, ""), "utf8");
70805
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync76(inputArg.replace(/^@/, ""), "utf8");
70321
70806
  const parsed = JSON.parse(raw);
70322
70807
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
70323
70808
  injectedInput = parsed;
@@ -70366,7 +70851,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70366
70851
  }
70367
70852
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
70368
70853
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
70369
- const out = mkdtempSync4(join61(tmpdir5(), "himi-render-"));
70854
+ const out = mkdtempSync4(join62(tmpdir5(), "himi-render-"));
70370
70855
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
70371
70856
  const outcome = await buildReleaseFromContent2(dir, out, {
70372
70857
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -70380,7 +70865,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70380
70865
  const diagnostics = (outcome.lint ?? []).find((s) => s.screenId === screenId)?.issues ?? [];
70381
70866
  let pngFile;
70382
70867
  let attestationPngFile;
70383
- const shotPath = join61(dir, ".himi", "shots", `${screenId}.png`);
70868
+ const shotPath = join62(dir, ".himi", "shots", `${screenId}.png`);
70384
70869
  const pngTarget = typeof flags.png === "string" ? str6(flags.png) : flags.png === true ? shotPath : void 0;
70385
70870
  if (pngTarget && r.pngBase64) {
70386
70871
  pngFile = writePngFile(pngTarget, r.pngBase64);
@@ -70716,7 +71201,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70716
71201
  io.error(JSON.stringify(linked));
70717
71202
  return 1;
70718
71203
  }
70719
- if (existsSync63(dest)) {
71204
+ if (existsSync64(dest)) {
70720
71205
  if (!statSync17(dest).isDirectory()) {
70721
71206
  io.error(JSON.stringify({
70722
71207
  error: `${dest} exists and is not a directory`,
@@ -70724,12 +71209,12 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70724
71209
  }));
70725
71210
  return 1;
70726
71211
  }
70727
- const occupied = readdirSync29(dest).length > 0;
71212
+ const occupied = readdirSync30(dest).length > 0;
70728
71213
  if (occupied && !force) {
70729
71214
  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" }));
70730
71215
  return 1;
70731
71216
  }
70732
- if (replace && occupied && !existsSync63(join61(dest, "himalaya.content.json"))) {
71217
+ if (replace && occupied && !existsSync64(join62(dest, "himalaya.content.json"))) {
70733
71218
  io.error(JSON.stringify({
70734
71219
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
70735
71220
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -70870,7 +71355,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
70870
71355
  const force = flags.force === true;
70871
71356
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
70872
71357
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
70873
- const out = mkdtempSync4(join61(tmpdir5(), "himi-push-"));
71358
+ const out = mkdtempSync4(join62(tmpdir5(), "himi-push-"));
70874
71359
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
70875
71360
  const outcome = await buildReleaseFromContent2(dir, out, {
70876
71361
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -71391,7 +71876,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71391
71876
  }
71392
71877
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
71393
71878
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
71394
- const built = mkdtempSync4(join61(tmpdir5(), "himi-pack-"));
71879
+ const built = mkdtempSync4(join62(tmpdir5(), "himi-pack-"));
71395
71880
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
71396
71881
  const outcome = await buildReleaseFromContent2(dir, built, {
71397
71882
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -71596,7 +72081,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71596
72081
  }));
71597
72082
  return 1;
71598
72083
  }
71599
- const archive = readFileSync75(resolve47(file));
72084
+ const archive = readFileSync76(resolve47(file));
71600
72085
  const result = await client2().createPackageShare({
71601
72086
  app: opened.header.app,
71602
72087
  releaseId: opened.header.releaseId,
@@ -71656,11 +72141,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71656
72141
  const app = await appId(dir2);
71657
72142
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
71658
72143
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
71659
- temp = mkdtempSync4(join61(tmpdir5(), "himi-run-build-"));
72144
+ temp = mkdtempSync4(join62(tmpdir5(), "himi-run-build-"));
71660
72145
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
71661
72146
  const initial = await buildReleaseFromContent2(dir2, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir2) } : {} });
71662
72147
  servedMissing = initial.missingBundles;
71663
- source = join61(temp, app);
72148
+ source = join62(temp, app);
71664
72149
  }
71665
72150
  const chromeArg = str6(flags.chrome) ?? "preview";
71666
72151
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -71696,7 +72181,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71696
72181
  if (!recordKit) return;
71697
72182
  startedAt = (/* @__PURE__ */ new Date()).toISOString();
71698
72183
  const failure = record2(served2, { ok: true, at: startedAt });
71699
- if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join61(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
72184
+ if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join62(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
71700
72185
  await refreshBoard();
71701
72186
  };
71702
72187
  const retract = async () => {
@@ -71727,7 +72212,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71727
72212
  }
71728
72213
  const watchKit = watch2 ? await Promise.resolve().then(() => (init_run_watch(), run_watch_exports)) : null;
71729
72214
  const bus = watchKit ? new watchKit.ReloadBus() : void 0;
71730
- if (watch2) served = mkdtempSync4(join61(tmpdir5(), "himi-run-served-"));
72215
+ if (watch2) served = mkdtempSync4(join62(tmpdir5(), "himi-run-served-"));
71731
72216
  const running = await runPackage2(source, {
71732
72217
  port,
71733
72218
  chrome: chromeArg,
@@ -71749,8 +72234,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71749
72234
  await announce(running);
71750
72235
  if (watchKit && bus) {
71751
72236
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
71752
- const live2 = join61(running.releaseRoot, running.app);
71753
- const manifestOf = (tree) => JSON.parse(readFileSync75(join61(tree, "release-manifest.json"), "utf8"));
72237
+ const live2 = join62(running.releaseRoot, running.app);
72238
+ const manifestOf = (tree) => JSON.parse(readFileSync76(join62(tree, "release-manifest.json"), "utf8"));
71754
72239
  let current = manifestOf(live2);
71755
72240
  const failed2 = async (message) => {
71756
72241
  if (stopping) return;
@@ -71761,7 +72246,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71761
72246
  };
71762
72247
  rebuild = watchKit.serialize(async () => {
71763
72248
  const t0 = Date.now();
71764
- const next = mkdtempSync4(join61(tmpdir5(), "himi-run-build-"));
72249
+ const next = mkdtempSync4(join62(tmpdir5(), "himi-run-build-"));
71765
72250
  try {
71766
72251
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
71767
72252
  if (bres.compileErrors.length) {
@@ -71778,22 +72263,24 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71778
72263
  return;
71779
72264
  }
71780
72265
  if (stopping) return;
71781
- const fresh = manifestOf(join61(next, running.app));
72266
+ const fresh = manifestOf(join62(next, running.app));
71782
72267
  const diff = watchKit.diffReleases(current, fresh);
72268
+ const { describeChanges: describeChanges2, headline: headline2 } = await Promise.resolve().then(() => (init_build_changes(), build_changes_exports));
72269
+ const changes = describeChanges2(live2, join62(next, running.app), diff);
71783
72270
  const retired = `${live2}.retired-${Date.now()}`;
71784
72271
  renameSync7(live2, retired);
71785
72272
  try {
71786
- renameSync7(join61(next, running.app), live2);
72273
+ renameSync7(join62(next, running.app), live2);
71787
72274
  } catch {
71788
- cpSync3(join61(next, running.app), live2, { recursive: true });
72275
+ cpSync3(join62(next, running.app), live2, { recursive: true });
71789
72276
  }
71790
72277
  rmSync14(retired, { recursive: true, force: true });
71791
72278
  current = fresh;
71792
72279
  servedMissing = outcome.missingBundles;
71793
72280
  for (const [event, data] of watchKit.reloadEvents(diff)) bus.publish(event, data);
71794
- record2(running, { ok: true, at: (/* @__PURE__ */ new Date()).toISOString(), ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config });
72281
+ record2(running, { ok: true, at: (/* @__PURE__ */ new Date()).toISOString(), ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config, changes });
71795
72282
  await refreshBoard();
71796
- io.error(JSON.stringify({ watch: "rebuilt", ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config, tabs: bus.listeners }));
72283
+ io.error(JSON.stringify({ watch: "rebuilt", ms: Date.now() - t0, screens: diff.screens, tokens: diff.tokens, config: diff.config, ...changes.items.length ? { changed: headline2(changes), changes } : {}, tabs: bus.listeners }));
71797
72284
  } catch (e) {
71798
72285
  await failed2(e.message.split("\n")[0]);
71799
72286
  } finally {
@@ -72395,7 +72882,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72395
72882
  io.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
72396
72883
  return 2;
72397
72884
  }
72398
- if (!existsSync63(file)) {
72885
+ if (!existsSync64(file)) {
72399
72886
  io.error(JSON.stringify({ error: `no such file: ${file}` }));
72400
72887
  return 2;
72401
72888
  }
@@ -72451,7 +72938,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72451
72938
  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."
72452
72939
  };
72453
72940
  if (kind === "tokens" || kind === "app-config") {
72454
- const body = readFileSync75(file, "utf8");
72941
+ const body = readFileSync76(file, "utf8");
72455
72942
  const patch2 = kind === "tokens" ? { tokens: body } : { appConfig: body };
72456
72943
  return runReleasePatch({
72457
72944
  ...common,
@@ -72466,7 +72953,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72466
72953
  io.error(JSON.stringify({ error: "patch screen needs a <screenId>", usage: PATCH_USAGE }));
72467
72954
  return 2;
72468
72955
  }
72469
- const descriptor = readFileSync75(file, "utf8");
72956
+ const descriptor = readFileSync76(file, "utf8");
72470
72957
  try {
72471
72958
  JSON.parse(descriptor);
72472
72959
  } catch (e) {
@@ -72506,7 +72993,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72506
72993
  extra: { kind, screen: id, file: { path: file, bytes: Buffer.byteLength(descriptor, "utf8") } }
72507
72994
  });
72508
72995
  }
72509
- const bytes2 = readFileSync75(file);
72996
+ const bytes2 = readFileSync76(file);
72510
72997
  const sha = createHash28("sha256").update(bytes2).digest("hex");
72511
72998
  const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
72512
72999
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
@@ -72650,7 +73137,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72650
73137
  }
72651
73138
  let def;
72652
73139
  try {
72653
- const parsed = JSON.parse(arg.startsWith("@") ? readFileSync75(arg.slice(1), "utf8") : arg);
73140
+ const parsed = JSON.parse(arg.startsWith("@") ? readFileSync76(arg.slice(1), "utf8") : arg);
72654
73141
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
72655
73142
  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}`] }));
72656
73143
  return 2;
@@ -73114,7 +73601,7 @@ ${label2}`);
73114
73601
  const platform = str6(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
73115
73602
  {
73116
73603
  const distDir = contentDir(flags);
73117
- if (existsSync63(join61(distDir, "himalaya.content.json"))) {
73604
+ if (existsSync64(join62(distDir, "himalaya.content.json"))) {
73118
73605
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
73119
73606
  const errors = issues.filter((i) => i.severity === "error");
73120
73607
  for (const i of issues.filter((i2) => i2.severity === "warning")) {
@@ -73383,7 +73870,7 @@ ${label2}`);
73383
73870
  const dpxPath = dpxSessionTokenPath();
73384
73871
  let dpx;
73385
73872
  try {
73386
- dpx = readFileSync75(dpxPath, "utf8").trim();
73873
+ dpx = readFileSync76(dpxPath, "utf8").trim();
73387
73874
  } catch {
73388
73875
  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` }));
73389
73876
  return 1;
@@ -73791,7 +74278,7 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
73791
74278
  const path2 = dpxSessionTokenPath();
73792
74279
  let raw;
73793
74280
  try {
73794
- raw = readFileSync75(path2, "utf8");
74281
+ raw = readFileSync76(path2, "utf8");
73795
74282
  } catch {
73796
74283
  io.error(JSON.stringify({ error: `no DPX BO session at ${path2}` }));
73797
74284
  return 1;