@wix/himalaya-cli 0.1007.0 → 0.1008.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
@@ -2990,14 +2990,14 @@ function confinementMatcher(contentRoot, extraRoots = []) {
2990
2990
  linked = [];
2991
2991
  const scanned = /* @__PURE__ */ new Set();
2992
2992
  const queue = [...roots];
2993
- const CAP3 = 4096;
2993
+ const CAP4 = 4096;
2994
2994
  const take = (abs) => {
2995
2995
  const target = realpathOrSelf(abs);
2996
- if (scanned.has(target) || linked.length >= CAP3) return;
2996
+ if (scanned.has(target) || linked.length >= CAP4) return;
2997
2997
  linked.push(target);
2998
2998
  queue.push(target);
2999
2999
  };
3000
- while (queue.length && linked.length < CAP3) {
3000
+ while (queue.length && linked.length < CAP4) {
3001
3001
  const r = queue.shift();
3002
3002
  for (const nm of [r, join12(r, "node_modules")]) {
3003
3003
  if (scanned.has(nm)) continue;
@@ -3061,7 +3061,7 @@ function confinementPlugin(contentRoot) {
3061
3061
  }
3062
3062
  function assertConfinedTsconfig(entry, contentRoot, allows) {
3063
3063
  const seen = /* @__PURE__ */ new Set();
3064
- const refuse3 = (p, why) => {
3064
+ const refuse4 = (p, why) => {
3065
3065
  throw new Error(
3066
3066
  `refused to read ${p}: ${why}. A tsconfig may extend a file from its own package or an installed dependency \u2014 nothing else.`
3067
3067
  );
@@ -3127,7 +3127,7 @@ function assertConfinedTsconfig(entry, contentRoot, allows) {
3127
3127
  try {
3128
3128
  manifest = JSON.parse(stripJsonc(readFileSync10(pj, "utf8")));
3129
3129
  } catch {
3130
- refuse3(pj, "its package.json could not be parsed, so its `tsconfig` field cannot be checked");
3130
+ refuse4(pj, "its package.json could not be parsed, so its `tsconfig` field cannot be checked");
3131
3131
  }
3132
3132
  const field = manifest?.tsconfig;
3133
3133
  if (typeof field === "string" && field) {
@@ -3143,7 +3143,7 @@ function assertConfinedTsconfig(entry, contentRoot, allows) {
3143
3143
  const real = realpathOrSelf(file);
3144
3144
  if (seen.has(real)) return;
3145
3145
  seen.add(real);
3146
- if (!allows(real)) refuse3(file, "it is outside this content package");
3146
+ if (!allows(real)) refuse4(file, "it is outside this content package");
3147
3147
  let raw;
3148
3148
  try {
3149
3149
  raw = readFileSync10(real, "utf8");
@@ -3154,7 +3154,7 @@ function assertConfinedTsconfig(entry, contentRoot, allows) {
3154
3154
  try {
3155
3155
  parsed = JSON.parse(stripJsonc(raw));
3156
3156
  } catch {
3157
- refuse3(file, "its tsconfig could not be parsed, so its `extends` cannot be checked");
3157
+ refuse4(file, "its tsconfig could not be parsed, so its `extends` cannot be checked");
3158
3158
  }
3159
3159
  const ext = parsed?.extends;
3160
3160
  for (const one of Array.isArray(ext) ? ext : ext ? [ext] : []) {
@@ -5318,6 +5318,7 @@ function walk(node, registry, path2, out) {
5318
5318
  var SYSTEM_FONT_FAMILY;
5319
5319
  var init_tokens = __esm({
5320
5320
  "../../core/ts/src/tokens.ts"() {
5321
+ "use strict";
5321
5322
  SYSTEM_FONT_FAMILY = "$system";
5322
5323
  }
5323
5324
  });
@@ -5343,6 +5344,7 @@ function resolveTranslation(value, catalog, sourceCatalog) {
5343
5344
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
5344
5345
  var init_bindings = __esm({
5345
5346
  "../../core/ts/src/bindings.ts"() {
5347
+ "use strict";
5346
5348
  BINDING_SIGILS = ["service", "flag", "item", "t"];
5347
5349
  BINDING_SIGIL_RE = new RegExp(
5348
5350
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -8581,6 +8583,7 @@ var init_validate_descriptor_core = __esm({
8581
8583
  action: "any",
8582
8584
  cornerRadius: "number",
8583
8585
  disabled: "boolean",
8586
+ disabledPath: "string",
8584
8587
  fullWidth: "boolean",
8585
8588
  iconOnly: "boolean",
8586
8589
  leadingIcon: "string",
@@ -12015,11 +12018,11 @@ function facesFor(family, weights, catalog = loadCatalog()) {
12015
12018
  }
12016
12019
  function matchWeightCss(available, desired) {
12017
12020
  if (available.length === 0) return void 0;
12018
- const sorted = [...available].sort((a, b) => a - b);
12019
- const upFrom = (n) => sorted.find((w) => w >= n);
12020
- const downFrom = (n) => [...sorted].reverse().find((w) => w <= n);
12021
+ const sorted2 = [...available].sort((a, b) => a - b);
12022
+ const upFrom = (n) => sorted2.find((w) => w >= n);
12023
+ const downFrom = (n) => [...sorted2].reverse().find((w) => w <= n);
12021
12024
  if (desired >= 400 && desired <= 500) {
12022
- const within = sorted.find((w) => w >= desired && w <= 500);
12025
+ const within = sorted2.find((w) => w >= desired && w <= 500);
12023
12026
  return within ?? downFrom(desired) ?? upFrom(desired);
12024
12027
  }
12025
12028
  if (desired < 400) return downFrom(desired) ?? upFrom(desired);
@@ -19465,6 +19468,7 @@ function shouldRevealError(args) {
19465
19468
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
19466
19469
  var init_validation = __esm({
19467
19470
  "../../core/ts/src/validation.ts"() {
19471
+ "use strict";
19468
19472
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
19469
19473
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
19470
19474
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -22889,6 +22893,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
22889
22893
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
22890
22894
  var init_worker_subtree = __esm({
22891
22895
  "../../core/ts/src/worker-subtree.ts"() {
22896
+ "use strict";
22892
22897
  init_worker_subtree_safe_types_generated();
22893
22898
  WORKER_SUBTREE_CEILINGS = Object.freeze({
22894
22899
  maxEncodedBytes: 65536,
@@ -24036,7 +24041,7 @@ function daysForMode(anchor, mode, firstWeekday) {
24036
24041
  return Array.from({ length: n }, (_, i) => new Date(anchor.getFullYear(), anchor.getMonth(), anchor.getDate() + i));
24037
24042
  }
24038
24043
  function packOverlaps(items) {
24039
- const sorted = [...items].sort((a, b) => a.startMin - b.startMin || a.endMin - b.endMin);
24044
+ const sorted2 = [...items].sort((a, b) => a.startMin - b.startMin || a.endMin - b.endMin);
24040
24045
  const result = [];
24041
24046
  let cluster = [];
24042
24047
  let clusterEnd = -Infinity;
@@ -24057,7 +24062,7 @@ function packOverlaps(items) {
24057
24062
  cluster = [];
24058
24063
  clusterEnd = -Infinity;
24059
24064
  };
24060
- for (const item of sorted) {
24065
+ for (const item of sorted2) {
24061
24066
  if (cluster.length && item.startMin >= clusterEnd) flush();
24062
24067
  cluster.push(item);
24063
24068
  clusterEnd = Math.max(clusterEnd, item.endMin);
@@ -26707,7 +26712,8 @@ var init_renderNode = __esm({
26707
26712
  const primary = buttonStyle === "primary" || destructive;
26708
26713
  const outlined = buttonStyle === "secondary";
26709
26714
  const accent = destructive ? C("danger", "#E62214") : C("accentPrimary", "#116DFF");
26710
- const disabled = props.disabled === true || props.loading === true;
26715
+ const boundDisabled = useResolvedScalar(props.disabledPath, ctx);
26716
+ const disabled = props.disabled === true || props.loading === true || hiddenBool(boundDisabled);
26711
26717
  const onClick = ctx.onAction && props.action && !disabled ? (e) => {
26712
26718
  e?.stopPropagation?.();
26713
26719
  ctx.onAction(resolveItemDeep2(props.action, ctx));
@@ -30546,9 +30552,9 @@ function freezeCaptured(def, widgetId, opts = {}) {
30546
30552
  }
30547
30553
  const raw = def.timeline.frames;
30548
30554
  if (Array.isArray(raw) && raw.length) {
30549
- const sorted = [...raw].filter((f) => f && typeof f.startsAt === "number" && f.component).sort((a, b) => a.startsAt - b.startsAt).slice(0, MAX_TIMELINE_FRAMES);
30550
- if (sorted.length) {
30551
- frames = sorted.map((f) => ({ startsAt: f.startsAt, payload: f.component }));
30555
+ const sorted2 = [...raw].filter((f) => f && typeof f.startsAt === "number" && f.component).sort((a, b) => a.startsAt - b.startsAt).slice(0, MAX_TIMELINE_FRAMES);
30556
+ if (sorted2.length) {
30557
+ frames = sorted2.map((f) => ({ startsAt: f.startsAt, payload: f.component }));
30552
30558
  payload = frames[0].payload;
30553
30559
  }
30554
30560
  }
@@ -38552,16 +38558,16 @@ function pushSetupBodyBytes(body) {
38552
38558
  return Buffer.byteLength(JSON.stringify(body), "utf8");
38553
38559
  }
38554
38560
  async function pushSetupRequest(options) {
38555
- const refuse3 = (error) => ({ ok: false, status: 0, body: null, error });
38556
- if (!options.target.controlToken) return refuse3("control_token_required");
38557
- if (!options.wixToken) return refuse3("wix_account_token_required");
38558
- if (credentialHostRefusal(options.target.control)) return refuse3("insecure_target");
38559
- if (options.previewTag !== void 0 && !PREVIEW_TAG.test(options.previewTag)) return refuse3("invalid_preview_tag");
38561
+ const refuse4 = (error) => ({ ok: false, status: 0, body: null, error });
38562
+ if (!options.target.controlToken) return refuse4("control_token_required");
38563
+ if (!options.wixToken) return refuse4("wix_account_token_required");
38564
+ if (credentialHostRefusal(options.target.control)) return refuse4("insecure_target");
38565
+ if (options.previewTag !== void 0 && !PREVIEW_TAG.test(options.previewTag)) return refuse4("invalid_preview_tag");
38560
38566
  const setup = options.operation === "setup";
38561
- if (setup && !options.body) return refuse3("push_setup_body_required");
38562
- if (!setup && (!options.query?.app || !options.query?.site)) return refuse3("push_setup_query_required");
38567
+ if (setup && !options.body) return refuse4("push_setup_body_required");
38568
+ if (!setup && (!options.query?.app || !options.query?.site)) return refuse4("push_setup_query_required");
38563
38569
  if (setup && pushSetupBodyBytes(options.body) > MAX_PUSH_SETUP_BODY_BYTES) {
38564
- return refuse3("push_setup_body_too_large");
38570
+ return refuse4("push_setup_body_too_large");
38565
38571
  }
38566
38572
  const base = options.target.control.replace(/\/+$/, "");
38567
38573
  let path2 = "/v1/admin/push/setup";
@@ -39143,6 +39149,1285 @@ var init_notify_setup = __esm({
39143
39149
  }
39144
39150
  });
39145
39151
 
39152
+ // src/site-analyze/types.ts
39153
+ function isRedirectUri(value) {
39154
+ return /^[a-z][a-z0-9+.-]*:\/\/\S*$/i.test(value);
39155
+ }
39156
+ function assertRedirectUri(value) {
39157
+ const trimmed = value.trim();
39158
+ if (!isRedirectUri(trimmed)) {
39159
+ throw new Error(
39160
+ `--redirect-uri must be a URI with a scheme (e.g. myapp://oauth/callback), got: ${JSON.stringify(value)}. It must also be REGISTERED on --client-id; register one with \`himi site client ensure --site <msid> --redirect-uri <uri> --yes\`.`
39161
+ );
39162
+ }
39163
+ return trimmed;
39164
+ }
39165
+ var BOOK_SCHEMA_VERSION, KNOWN_VERTICALS;
39166
+ var init_types2 = __esm({
39167
+ "src/site-analyze/types.ts"() {
39168
+ BOOK_SCHEMA_VERSION = 2;
39169
+ KNOWN_VERTICALS = [
39170
+ "store",
39171
+ "bookings",
39172
+ "restaurants",
39173
+ "programs",
39174
+ "plans",
39175
+ "feed",
39176
+ "members",
39177
+ "forum",
39178
+ "coupons",
39179
+ "notifications",
39180
+ "loyalty",
39181
+ "reviews",
39182
+ "portfolio",
39183
+ "reservations",
39184
+ "invoices",
39185
+ "badges",
39186
+ "notifsettings",
39187
+ "events",
39188
+ "blog",
39189
+ "groups",
39190
+ "cms",
39191
+ "donations",
39192
+ "giftCards",
39193
+ "videos",
39194
+ "podcasts",
39195
+ "forms",
39196
+ "galleries",
39197
+ "inbox"
39198
+ ];
39199
+ }
39200
+ });
39201
+
39202
+ // src/site-analyze/home-descriptor.ts
39203
+ function objectEnd(src, open) {
39204
+ let depth = 0;
39205
+ let inString = false;
39206
+ for (let i = open; i < src.length; i += 1) {
39207
+ const c = src[i];
39208
+ if (inString) {
39209
+ if (c === "\\") i += 1;
39210
+ else if (c === '"') inString = false;
39211
+ continue;
39212
+ }
39213
+ if (c === '"') inString = true;
39214
+ else if (c === "{") depth += 1;
39215
+ else if (c === "}") {
39216
+ depth -= 1;
39217
+ if (depth === 0) return i + 1;
39218
+ }
39219
+ }
39220
+ return -1;
39221
+ }
39222
+ function hasScreen(src, screenId) {
39223
+ const id = escapeRe2(screenId);
39224
+ return new RegExp(`"${id}": \\{\\s*"screenId": "${id}"`).test(src);
39225
+ }
39226
+ function findNode(root, pred) {
39227
+ if (Array.isArray(root)) {
39228
+ for (const child of root) {
39229
+ const hit = findNode(child, pred);
39230
+ if (hit) return hit;
39231
+ }
39232
+ return void 0;
39233
+ }
39234
+ if (!root || typeof root !== "object") return void 0;
39235
+ const node = root;
39236
+ if (node.body && typeof node.body === "object" && pred(node)) return node;
39237
+ for (const value of Object.values(root)) {
39238
+ const hit = findNode(value, pred);
39239
+ if (hit) return hit;
39240
+ }
39241
+ return void 0;
39242
+ }
39243
+ function cloneCell(template, prefix, vertical, card) {
39244
+ const cell = structuredClone(template);
39245
+ const accent = findNode(cell, (n) => n.body?.type === "Icon" && textProps(n)?.tint === "accentPrimary");
39246
+ const title = findNode(cell, (n) => n.body?.type === "Text" && textProps(n)?.style === "bodyEmphasis");
39247
+ const body = findNode(cell, (n) => n.body?.type === "Text" && textProps(n)?.style === "footnote");
39248
+ if (!accent || !title || !body || !cell.modifiers) return void 0;
39249
+ const onTap = cell.modifiers.onTap;
39250
+ if (!onTap?.actionId) return void 0;
39251
+ textProps(accent).name = card.icon;
39252
+ textProps(title).text = card.title;
39253
+ textProps(body).text = card.body;
39254
+ cell.modifiers.onTap = { actionId: onTap.actionId, params: { screenId: card.screenId } };
39255
+ cell.id = `${prefix}.${vertical}`;
39256
+ cell.modifiers.accessibilityLabel = card.title;
39257
+ return cell;
39258
+ }
39259
+ function lineIndent(src, open) {
39260
+ return /^[ \t]*/.exec(src.slice(src.lastIndexOf("\n", open) + 1, open))[0];
39261
+ }
39262
+ function serializeAt(node, src, open) {
39263
+ const indent = lineIndent(src, open);
39264
+ return JSON.stringify(node, null, 2).split("\n").map((line, i) => i === 0 ? line : indent + line).join("\n");
39265
+ }
39266
+ function patchGrids(src, installed, patch) {
39267
+ const OPEN = /"id": "([A-Za-z_0-9]+(?:\.[A-Za-z_0-9]+)*\.verticals)"/g;
39268
+ let out = "";
39269
+ let cursor = 0;
39270
+ for (let m = OPEN.exec(src); m; m = OPEN.exec(src)) {
39271
+ const open = src.lastIndexOf("{", m.index);
39272
+ if (open < cursor) continue;
39273
+ const close = objectEnd(src, open);
39274
+ if (close < 0) continue;
39275
+ const blob = src.slice(open, close);
39276
+ let node;
39277
+ try {
39278
+ node = JSON.parse(blob);
39279
+ } catch {
39280
+ continue;
39281
+ }
39282
+ const items = node.body?.type === "Grid" ? node.body.props?.items : void 0;
39283
+ if (!Array.isArray(items) || !items.length) continue;
39284
+ const prefix = m[1].slice(0, -".verticals".length);
39285
+ const baked = /* @__PURE__ */ new Map();
39286
+ const foreignCells = [];
39287
+ for (const item of items) {
39288
+ const id = item.id;
39289
+ const legacy = item.modifiers?.accessibilityLabel;
39290
+ const claim = typeof id === "string" && id ? id : typeof legacy === "string" ? legacy : "";
39291
+ const vertical = claim.startsWith(`${prefix}.`) ? claim.slice(prefix.length + 1) : "";
39292
+ if (isKnown(vertical)) baked.set(vertical, item);
39293
+ else if (!GRID_VERTICAL_LABELS.has(vertical)) foreignCells.push(item);
39294
+ }
39295
+ const wanted = [];
39296
+ for (const vertical of EXPLORE_ORDER) {
39297
+ if (!installed.has(vertical)) continue;
39298
+ if (baked.has(vertical)) {
39299
+ wanted.push(vertical);
39300
+ continue;
39301
+ }
39302
+ const card = EXPLORE_CARDS[vertical];
39303
+ if (!hasScreen(src, card.screenId)) {
39304
+ patch.unreachable.push(vertical);
39305
+ continue;
39306
+ }
39307
+ wanted.push(vertical);
39308
+ }
39309
+ const shape = baked.values().next().value ?? items[0];
39310
+ const next = [...foreignCells];
39311
+ const cells = [];
39312
+ for (const vertical of wanted) {
39313
+ if (next.length >= EXPLORE_MAX_CELLS) break;
39314
+ const bakedCell = baked.get(vertical);
39315
+ if (bakedCell) {
39316
+ next.push(bakedCell);
39317
+ cells.push(vertical);
39318
+ continue;
39319
+ }
39320
+ const made = cloneCell(shape, prefix, vertical, EXPLORE_CARDS[vertical]);
39321
+ if (!made) {
39322
+ patch.unreachable.push(vertical);
39323
+ continue;
39324
+ }
39325
+ next.push(made);
39326
+ cells.push(vertical);
39327
+ patch.added.push(vertical);
39328
+ }
39329
+ if (!cells.length) continue;
39330
+ patch.cells = cells;
39331
+ node.body.props.items = next;
39332
+ out += src.slice(cursor, open) + serializeAt(node, src, open);
39333
+ cursor = close;
39334
+ OPEN.lastIndex = close;
39335
+ }
39336
+ return out + src.slice(cursor);
39337
+ }
39338
+ function patchHomeModules(src, verticals) {
39339
+ const patch = { src, cells: [], added: [], droppedModules: [], droppedCtas: [], unreachable: [] };
39340
+ const next = patchModules(src, new Set(verticals.filter(isKnown)), patch);
39341
+ return { src: next, droppedModules: patch.droppedModules };
39342
+ }
39343
+ function patchModules(src, installed, patch) {
39344
+ return src.replace(/"homeModules": "((?:[^"\\]|\\.)*)"/g, (whole, encoded2) => {
39345
+ let modules;
39346
+ try {
39347
+ const parsed = JSON.parse(JSON.parse(`"${encoded2}"`));
39348
+ if (!Array.isArray(parsed)) return whole;
39349
+ modules = parsed;
39350
+ } catch {
39351
+ return whole;
39352
+ }
39353
+ const kept = modules.filter((module) => {
39354
+ const source = module?.dataSource;
39355
+ if (typeof source !== "string") return true;
39356
+ const needs = MODULE_SOURCE_VERTICALS[source];
39357
+ return !needs || needs.some((vertical) => installed.has(vertical));
39358
+ });
39359
+ if (!kept.length || kept.length === modules.length) return whole;
39360
+ for (const module of modules) {
39361
+ if (!kept.includes(module) && typeof module?.id === "string") patch.droppedModules.push(module.id);
39362
+ }
39363
+ return `"homeModules": ${JSON.stringify(JSON.stringify(kept))}`;
39364
+ });
39365
+ }
39366
+ function homeScreenSpan(src, homeActionId) {
39367
+ const OPEN = screenDeclarations();
39368
+ for (let m = OPEN.exec(src); m; m = OPEN.exec(src)) {
39369
+ const open = src.indexOf("{", m.index);
39370
+ const close = objectEnd(src, open);
39371
+ if (close < 0) continue;
39372
+ if (src.slice(open, close).includes(`"actionId": "${homeActionId}"`)) return { open, close };
39373
+ OPEN.lastIndex = close;
39374
+ }
39375
+ return void 0;
39376
+ }
39377
+ function isDeadStoreCta(node) {
39378
+ if (node.body?.type !== "Button") return false;
39379
+ const action = node.body.props?.action;
39380
+ const screenId = action?.params?.screenId;
39381
+ return typeof screenId === "string" && isStoreScreen(screenId);
39382
+ }
39383
+ function pruneTree(root, isDead) {
39384
+ const dropped = [];
39385
+ if (!isNode2(root)) return { emptied: false, dropped };
39386
+ return { emptied: !pruneNode(root, dropped, isDead), dropped };
39387
+ }
39388
+ function pruneNode(node, dropped, isDead = isDeadStoreCta) {
39389
+ const props = node.body?.props;
39390
+ if (!props) return true;
39391
+ const lists = props.itemsPath ? ["children"] : ["children", "items"];
39392
+ for (const key2 of lists) {
39393
+ const list = props[key2];
39394
+ if (!Array.isArray(list) || !list.length) continue;
39395
+ const kept = list.filter((child) => {
39396
+ if (child && typeof child === "object" && isDead(child)) {
39397
+ dropped.push(typeof child.id === "string" && child.id ? child.id : "(unnamed CTA)");
39398
+ return false;
39399
+ }
39400
+ if (!isNode2(child)) return true;
39401
+ return pruneNode(child, dropped, isDead);
39402
+ });
39403
+ if (kept.length === list.length) continue;
39404
+ if (!kept.length) return false;
39405
+ props[key2] = kept;
39406
+ }
39407
+ for (const [key2, child] of Object.entries(props)) {
39408
+ if (lists.includes(key2)) continue;
39409
+ pruneWithin(child, dropped, isDead);
39410
+ }
39411
+ return true;
39412
+ }
39413
+ function pruneWithin(value, dropped, isDead) {
39414
+ if (Array.isArray(value)) {
39415
+ for (const child of value) pruneWithin(child, dropped, isDead);
39416
+ return;
39417
+ }
39418
+ if (!value || typeof value !== "object") return;
39419
+ if (isNode2(value)) {
39420
+ pruneNode(value, dropped, isDead);
39421
+ return;
39422
+ }
39423
+ for (const child of Object.values(value)) pruneWithin(child, dropped, isDead);
39424
+ }
39425
+ function patchStoreCtas(src, verticals, homeActionId) {
39426
+ if (verticals.includes("store")) return { src, dropped: [] };
39427
+ const span = homeScreenSpan(src, homeActionId);
39428
+ if (!span) return { src, dropped: [] };
39429
+ let screen;
39430
+ try {
39431
+ screen = JSON.parse(src.slice(span.open, span.close));
39432
+ } catch {
39433
+ return { src, dropped: [] };
39434
+ }
39435
+ if (!isNode2(screen.payload)) return { src, dropped: [] };
39436
+ const dropped = [];
39437
+ if (!pruneNode(screen.payload, dropped) || !dropped.length) return { src, dropped: [] };
39438
+ return {
39439
+ src: src.slice(0, span.open) + serializeAt(screen, src, span.open) + src.slice(span.close),
39440
+ dropped
39441
+ };
39442
+ }
39443
+ function patchHomeForVerticals(src, verticals, homeActionId) {
39444
+ const installed = new Set(verticals.filter(isKnown));
39445
+ const patch = { src, cells: [], added: [], droppedModules: [], droppedCtas: [], unreachable: [] };
39446
+ if (!installed.size) return patch;
39447
+ const ctas = patchStoreCtas(patchModules(patchGrids(src, installed, patch), installed, patch), [...installed], homeActionId);
39448
+ patch.src = ctas.src;
39449
+ patch.droppedCtas = ctas.dropped;
39450
+ return patch;
39451
+ }
39452
+ var EXPLORE_CARDS, EXPLORE_ORDER, EXPLORE_MAX_CELLS, MODULE_SOURCE_VERTICALS, isKnown, GRID_VERTICAL_LABELS, escapeRe2, textProps, STORE_SCREENS, isStoreScreen, screenDeclarations, isNode2;
39453
+ var init_home_descriptor = __esm({
39454
+ "src/site-analyze/home-descriptor.ts"() {
39455
+ init_types2();
39456
+ EXPLORE_CARDS = {
39457
+ store: { icon: "bag", screenId: "himi_shop", title: "Shop", body: "Browse products and manage your cart." },
39458
+ bookings: { icon: "calendar", screenId: "himi_services", title: "Book", body: "Find a service and choose a time." },
39459
+ restaurants: { icon: "grid", screenId: "himi_menu", title: "Food", body: "Browse the menu and reserve a table." },
39460
+ events: { icon: "calendar", screenId: "himi_events", title: "Events", body: "Discover events and RSVP." },
39461
+ plans: { icon: "star", screenId: "himi_plans", title: "Plans", body: "Compare membership plans and join." },
39462
+ blog: { icon: "newspaper", screenId: "himi_blog", title: "Blog", body: "Read, save and discuss the latest posts." },
39463
+ groups: { icon: "people", screenId: "himi_groups", title: "Groups", body: "Meet members and join the conversation." },
39464
+ portfolio: { icon: "photo", screenId: "himi_portfolio", title: "Portfolio", body: "A showcase of our work." },
39465
+ galleries: { icon: "image", screenId: "himi_galleries", title: "Galleries", body: "Browse our photo galleries." },
39466
+ videos: { icon: "play", screenId: "himi_videos", title: "Videos", body: "Watch our video channels." },
39467
+ podcasts: { icon: "microphone", screenId: "himi_podcasts", title: "Podcast", body: "Listen to our latest episodes." },
39468
+ programs: { icon: "layers", screenId: "himi_programs", title: "Programs", body: "Follow structured programs at your own pace." },
39469
+ reservations: { icon: "clock", screenId: "himi_reservations", title: "Reservations", body: "Your table reservations." },
39470
+ donations: { icon: "heart", screenId: "himi_donations", title: "Donate", body: "Support the causes we care about." },
39471
+ giftCards: { icon: "tag", screenId: "himi_gift_cards", title: "Gift Cards", body: "Your gift cards and balances." },
39472
+ coupons: { icon: "tag", screenId: "himi_coupons", title: "Offers", body: "Deals and coupons available to you." },
39473
+ loyalty: { icon: "star", screenId: "himi_loyalty", title: "Rewards", body: "Earn points and redeem them for rewards." },
39474
+ forms: { icon: "list", screenId: "himi_forms", title: "Forms", body: "Fill out our forms." },
39475
+ cms: { icon: "grid", screenId: "himi_cms", title: "Content", body: "Explore custom content published by this business." },
39476
+ members: { icon: "people", screenId: "himi_members", title: "Members", body: "Meet the people in this community." },
39477
+ forum: { icon: "chat", screenId: "himi_forum", title: "Forum", body: "Join the discussion with the community." },
39478
+ feed: { icon: "chat", screenId: "himi_feed", title: "Feed", body: "Share updates and react to the latest posts." },
39479
+ reviews: { icon: "star_fill", screenId: "himi_reviews", title: "Reviews", body: "What customers are saying." },
39480
+ inbox: { icon: "chat", screenId: "himi_inbox", title: "Inbox", body: "Your messages with us." },
39481
+ invoices: { icon: "doc", screenId: "himi_invoices", title: "Invoices", body: "Your billing history." },
39482
+ badges: { icon: "star", screenId: "himi_badges", title: "Badges", body: "Achievements you can earn." },
39483
+ notifications: { icon: "bell", screenId: "himi_notifications", title: "Notifications", body: "Your activity and updates." },
39484
+ notifsettings: { icon: "settings", screenId: "himi_notif_settings", title: "Notification Settings", body: "Choose what you hear about." }
39485
+ };
39486
+ EXPLORE_ORDER = [
39487
+ "store",
39488
+ "bookings",
39489
+ "restaurants",
39490
+ "events",
39491
+ "plans",
39492
+ "blog",
39493
+ "groups",
39494
+ "portfolio",
39495
+ "galleries",
39496
+ "videos",
39497
+ "podcasts",
39498
+ "programs",
39499
+ "reservations",
39500
+ "donations",
39501
+ "giftCards",
39502
+ "coupons",
39503
+ "loyalty",
39504
+ "forms",
39505
+ "cms",
39506
+ "members",
39507
+ "forum",
39508
+ "feed",
39509
+ "reviews",
39510
+ "inbox",
39511
+ "invoices",
39512
+ "badges",
39513
+ "notifications",
39514
+ "notifsettings"
39515
+ ];
39516
+ EXPLORE_MAX_CELLS = 8;
39517
+ MODULE_SOURCE_VERTICALS = {
39518
+ "member.bookings": ["bookings"],
39519
+ "member.bookings.schedule": ["bookings"],
39520
+ "device.saved_items": ["store", "blog"]
39521
+ };
39522
+ isKnown = (v) => KNOWN_VERTICALS.includes(v);
39523
+ GRID_VERTICAL_LABELS = /* @__PURE__ */ new Set([...KNOWN_VERTICALS, "faq"]);
39524
+ escapeRe2 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
39525
+ textProps = (n) => n?.body?.props;
39526
+ STORE_SCREENS = /* @__PURE__ */ new Set([
39527
+ "himi_shop",
39528
+ "himi_saved_products",
39529
+ "himi_collections",
39530
+ "himi_collection",
39531
+ "himi_cart",
39532
+ "himi_orders"
39533
+ ]);
39534
+ isStoreScreen = (id) => STORE_SCREENS.has(id) || /^himi_(?:product|order)[_/]/.test(id);
39535
+ screenDeclarations = () => /"([A-Za-z_0-9]+)": \{\s*"screenId": "\1"/g;
39536
+ isNode2 = (v) => !!v && typeof v === "object" && !!v.body && typeof v.body === "object";
39537
+ }
39538
+ });
39539
+
39540
+ // src/template-trim.ts
39541
+ var template_trim_exports = {};
39542
+ __export(template_trim_exports, {
39543
+ TrimRefusal: () => TrimRefusal,
39544
+ leftoverNavRefs: () => leftoverNavRefs,
39545
+ linearSafePattern: () => linearSafePattern,
39546
+ parseApp: () => parseApp,
39547
+ planTrim: () => planTrim,
39548
+ routeMatcher: () => routeMatcher,
39549
+ runTemplateTrim: () => runTemplateTrim
39550
+ });
39551
+ import { cpSync, existsSync as existsSync39, mkdirSync as mkdirSync18, readdirSync as readdirSync20, readFileSync as readFileSync43, renameSync as renameSync3, rmSync as rmSync6, statSync as statSync8, writeFileSync as writeFileSync14 } from "node:fs";
39552
+ import { dirname as dirname23, join as join36 } from "node:path";
39553
+ function routeMatcher(pattern) {
39554
+ const detail = /^\^([A-Za-z0-9_]+)\(\?:\[_\/\]\(\.\+\)\)\?\$$/.exec(pattern);
39555
+ if (detail) {
39556
+ const base = detail[1];
39557
+ return {
39558
+ // `.` never matches a newline, so neither does this.
39559
+ test: (id) => id === base || id.length > base.length + 1 && id.startsWith(base) && (id[base.length] === "_" || id[base.length] === "/") && !id.includes("\n", base.length + 1)
39560
+ };
39561
+ }
39562
+ const suffixed = /^\^([A-Za-z0-9_]+)\(\[A-Za-z0-9\]\{1,32\}\)\$$/.exec(pattern);
39563
+ if (suffixed) {
39564
+ const prefix = suffixed[1];
39565
+ return {
39566
+ test: (id) => {
39567
+ if (!id.startsWith(prefix)) return false;
39568
+ const rest = id.slice(prefix.length);
39569
+ return rest.length >= 1 && rest.length <= 32 && /^[A-Za-z0-9]+$/.test(rest);
39570
+ }
39571
+ };
39572
+ }
39573
+ if (!linearSafePattern(pattern)) return null;
39574
+ let re;
39575
+ try {
39576
+ re = new RegExp(pattern);
39577
+ } catch {
39578
+ return null;
39579
+ }
39580
+ return re;
39581
+ }
39582
+ function linearSafePattern(pattern) {
39583
+ return pattern.length <= 256 && SAFE_ROUTE_PATTERN.test(pattern);
39584
+ }
39585
+ function bundlesOf(block) {
39586
+ const out = /* @__PURE__ */ new Set();
39587
+ const walk2 = (v) => {
39588
+ if (Array.isArray(v)) return v.forEach(walk2);
39589
+ if (!v || typeof v !== "object") return;
39590
+ const lb = v.logicBundle;
39591
+ const name = bundleNameFromUrl(lb?.url);
39592
+ if (name) out.add(name);
39593
+ for (const child of Object.values(v)) walk2(child);
39594
+ };
39595
+ walk2(block);
39596
+ return [...out];
39597
+ }
39598
+ function parseJson(text4, what) {
39599
+ try {
39600
+ const value = JSON.parse(text4);
39601
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("not an object");
39602
+ return value;
39603
+ } catch (e) {
39604
+ throw new TrimRefusal(
39605
+ `${what} is not a plain JSON object literal (${e.message}) \u2014 trim only rewrites the shape \`himi init\` writes; edit that block back to JSON or trim by hand (\`himi docs trim-a-template\`)`
39606
+ );
39607
+ }
39608
+ }
39609
+ function jsonString(literal, what) {
39610
+ try {
39611
+ return JSON.parse(literal);
39612
+ } catch (e) {
39613
+ throw new TrimRefusal(`${what} ${literal} is not a valid JSON string (${e.message}) \u2014 trim reads the shape \`himi init\` writes; fix the literal or trim by hand (\`himi docs trim-a-template\`)`);
39614
+ }
39615
+ }
39616
+ function bracketEnd(src, open) {
39617
+ let depth = 0;
39618
+ let inString = false;
39619
+ for (let i = open; i < src.length; i += 1) {
39620
+ const c = src[i];
39621
+ if (inString) {
39622
+ if (c === "\\") i += 1;
39623
+ else if (c === '"') inString = false;
39624
+ continue;
39625
+ }
39626
+ if (c === '"') inString = true;
39627
+ else if (c === "[" || c === "{") depth += 1;
39628
+ else if (c === "]" || c === "}") {
39629
+ depth -= 1;
39630
+ if (depth === 0) return i + 1;
39631
+ }
39632
+ }
39633
+ return -1;
39634
+ }
39635
+ function parseSubtree(config) {
39636
+ const KEY = /^[ \t]*["']?workerSubtreeScreens["']?[ \t]*:[ \t]*/gm;
39637
+ const hits = [...config.matchAll(KEY)];
39638
+ if (!hits.length) return void 0;
39639
+ if (hits.length > 1) throw new TrimRefusal("config.ts declares workerSubtreeScreens more than once \u2014 trim cannot tell which one the app uses; keep one and trim again");
39640
+ const start = hits[0].index + hits[0][0].length;
39641
+ const unreadable = 'config.ts: workerSubtreeScreens is not a JSON array literal of screen ids \u2014 trim will not guess at it; write it as `["id", \u2026]` or trim by hand';
39642
+ if (config[start] !== "[") throw new TrimRefusal(unreadable);
39643
+ const end = bracketEnd(config, start);
39644
+ if (end < 0) throw new TrimRefusal(unreadable);
39645
+ let value;
39646
+ try {
39647
+ value = JSON.parse(config.slice(start, end));
39648
+ } catch {
39649
+ throw new TrimRefusal(unreadable);
39650
+ }
39651
+ if (!Array.isArray(value)) throw new TrimRefusal(unreadable);
39652
+ return { start, end, value };
39653
+ }
39654
+ function parseApp(dev, config) {
39655
+ const SCREENS_ANCHOR = "const SCREENS = runtimeSetting({\n";
39656
+ const at = dev.indexOf(SCREENS_ANCHOR);
39657
+ if (at < 0) {
39658
+ throw new TrimRefusal("dev/index.ts has no `const SCREENS = runtimeSetting({` \u2014 trim reads the SCREENS map in the shape `himi init` writes, and this file is not in it");
39659
+ }
39660
+ const routes = [];
39661
+ const exact = /* @__PURE__ */ new Map();
39662
+ let pos = at + SCREENS_ANCHOR.length;
39663
+ const ENTRY = / ("(?:[^"\\\n]|\\.)*"): \{/y;
39664
+ while (!dev.startsWith("})", pos)) {
39665
+ ENTRY.lastIndex = pos;
39666
+ const m = ENTRY.exec(dev);
39667
+ if (!m) throw new TrimRefusal(`dev/index.ts line ${lineOf(dev, pos)}: expected a \`"<screenId>": {\` entry of the SCREENS map`);
39668
+ const id = jsonString(m[1], `dev/index.ts line ${lineOf(dev, pos)}: the SCREENS key`);
39669
+ const open = pos + m[0].length - 1;
39670
+ const close = objectEnd(dev, open);
39671
+ if (close < 0 || dev.slice(close, close + 2) !== ",\n") {
39672
+ throw new TrimRefusal(`dev/index.ts: the SCREENS entry "${id}" does not end in \`},\` on its own line`);
39673
+ }
39674
+ const decl = screenDeclarations();
39675
+ decl.lastIndex = pos;
39676
+ const d = decl.exec(dev);
39677
+ if (!d || d.index !== pos + 2 || d[1] !== id) {
39678
+ throw new TrimRefusal(`dev/index.ts: the SCREENS entry "${id}" does not open with its own \`"screenId": "${id}"\``);
39679
+ }
39680
+ const block = parseJson(dev.slice(open, close), `the screen "${id}" in dev/index.ts`);
39681
+ const route = { kind: "screen", label: id, lineStart: pos, end: close + 2, open, close, block, bundles: bundlesOf(block) };
39682
+ routes.push(route);
39683
+ exact.set(id, route);
39684
+ pos = close + 2;
39685
+ }
39686
+ const tAt = dev.indexOf("const TEMPLATED");
39687
+ const tOpen = tAt < 0 ? -1 : dev.indexOf("= [\n", tAt);
39688
+ if (tOpen < 0) throw new TrimRefusal("dev/index.ts has no `const TEMPLATED \u2026 = [` \u2014 trim reads the templated routes in the shape `himi init` writes");
39689
+ pos = tOpen + "= [\n".length;
39690
+ const templatedStart = pos;
39691
+ const templated = [];
39692
+ const TENTRY = / \{ pattern: ("(?:[^"\\\n]|\\.)*"), display: ("(?:[^"\\\n]|\\.)*"),/y;
39693
+ for (; ; ) {
39694
+ if (dev.startsWith("];", pos) || dev.startsWith("\n];", pos)) break;
39695
+ TENTRY.lastIndex = pos;
39696
+ const m = TENTRY.exec(dev);
39697
+ if (!m) throw new TrimRefusal(`dev/index.ts line ${lineOf(dev, pos)}: expected a \`{ pattern: \u2026, display: \u2026, template: {\u2026} },\` entry of TEMPLATED`);
39698
+ const pattern = jsonString(m[1], `dev/index.ts line ${lineOf(dev, pos)}: the TEMPLATED pattern`);
39699
+ const display = jsonString(m[2], `dev/index.ts line ${lineOf(dev, pos)}: the TEMPLATED display`);
39700
+ const lineEnd = dev.indexOf("\n", pos);
39701
+ const tk = dev.indexOf("template: {", pos);
39702
+ if (tk < 0 || tk > lineEnd) throw new TrimRefusal(`dev/index.ts: the templated route ${display} has no \`template: {\` on its first line`);
39703
+ const open = tk + "template: ".length;
39704
+ const close = objectEnd(dev, open);
39705
+ if (close < 0 || dev.slice(close, close + 4) !== " },\n") {
39706
+ throw new TrimRefusal(`dev/index.ts: the templated route ${display} does not end in \`} },\``);
39707
+ }
39708
+ const re = routeMatcher(pattern);
39709
+ if (!re) {
39710
+ throw new TrimRefusal(`dev/index.ts: the templated route ${display} has a pattern trim does not recognise (${JSON.stringify(pattern)}); trim compiles a route pattern only when it is anchored and cannot backtrack exponentially (no repeated group whose body repeats or alternates, no backreferences or lookarounds)`);
39711
+ }
39712
+ const block = parseJson(dev.slice(open, close), `the templated route ${display} in dev/index.ts`);
39713
+ const label2 = display.replace(/[[{].*$/, "").replace(/[_/]+$/, "") || display;
39714
+ const route = { kind: "templated", label: label2, lineStart: pos, end: close + 4, open, close, block, re, bundles: bundlesOf(block) };
39715
+ routes.push(route);
39716
+ templated.push(route);
39717
+ pos = close + 4;
39718
+ }
39719
+ const templatedRegion = [templatedStart, pos];
39720
+ const hm = /\n {2}homeScreenId: ("(?:[^"\\\n]|\\.)*"),\n/.exec(dev);
39721
+ if (!hm) throw new TrimRefusal('dev/index.ts has no `homeScreenId: "\u2026",` in its default export');
39722
+ const homeId = jsonString(hm[1], "dev/index.ts: homeScreenId");
39723
+ const home = exact.get(homeId);
39724
+ if (!home) throw new TrimRefusal(`dev/index.ts: homeScreenId "${homeId}" is not a screen in the SCREENS map`);
39725
+ const NAV_ANCHOR = "\n navigation: {";
39726
+ const nAt = config.indexOf(NAV_ANCHOR);
39727
+ if (nAt < 0) throw new TrimRefusal("config.ts has no `navigation: {` \u2014 trim rewrites navigation in the shape `himi init` writes");
39728
+ const nOpen = nAt + NAV_ANCHOR.length - 1;
39729
+ const nClose = objectEnd(config, nOpen);
39730
+ if (nClose < 0) throw new TrimRefusal("config.ts: the navigation object never closes");
39731
+ const navValue = parseJson(config.slice(nOpen, nClose), "config.ts navigation");
39732
+ const subtree = parseSubtree(config);
39733
+ return { dev, config, home, routes, exact, templated, templatedRegion, nav: { open: nOpen, close: nClose, value: navValue }, subtree };
39734
+ }
39735
+ function resolveIn(app, id) {
39736
+ return app.exact.get(id) ?? app.templated.find((r) => r.re.test(id));
39737
+ }
39738
+ function openTarget(action) {
39739
+ const a = action;
39740
+ return a?.actionId === "himi.open" && isStatic(a.params?.screenId) ? a.params.screenId : void 0;
39741
+ }
39742
+ function linksOf(route) {
39743
+ const out = [];
39744
+ const walk2 = (v, owner) => {
39745
+ if (Array.isArray(v)) return v.forEach((c) => walk2(c, owner));
39746
+ if (!v || typeof v !== "object") return;
39747
+ const here = typeof v.id === "string" ? entryLabel(v) : owner;
39748
+ const to = openTarget(v);
39749
+ if (to) out.push({ from: route, node: here, to });
39750
+ for (const child of Object.values(v)) walk2(child, here);
39751
+ };
39752
+ walk2(route.block, "(screen)");
39753
+ return out;
39754
+ }
39755
+ function tapTarget(entry) {
39756
+ const e = entry;
39757
+ for (const a of [e.action, e.onTap, e.modifiers?.onTap, e.body?.props?.action, e.body?.props?.onTap]) {
39758
+ const to = openTarget(a);
39759
+ if (to) return to;
39760
+ }
39761
+ return void 0;
39762
+ }
39763
+ function rowTemplateTarget(entry, removed) {
39764
+ if (!isNode3(entry)) return void 0;
39765
+ const props = entry.body.props;
39766
+ if (!props?.itemsPath || !Array.isArray(props.items)) return void 0;
39767
+ for (const row of props.items) {
39768
+ const to = tapTarget(row);
39769
+ if (to && removed(to)) return to;
39770
+ }
39771
+ return void 0;
39772
+ }
39773
+ function emptyDeadCases(root, isDead) {
39774
+ const out = [];
39775
+ const walk2 = (v) => {
39776
+ if (Array.isArray(v)) return v.forEach(walk2);
39777
+ if (!v || typeof v !== "object") return;
39778
+ for (const child of Object.values(v)) walk2(child);
39779
+ if (!isNode3(v) || v.body.type !== "FragmentSlot" || !Array.isArray(v.body.props?.cases)) return;
39780
+ for (const c of v.body.props.cases) {
39781
+ if (!isNode3(c.content) || !pruneTree(c.content, isDead).emptied) continue;
39782
+ const props = c.content.body.props ?? {};
39783
+ for (const key2 of props.itemsPath ? ["children"] : ["children", "items"]) {
39784
+ if (Array.isArray(props[key2]) && props[key2].length) props[key2] = [];
39785
+ }
39786
+ out.push({ id: String(c.id ?? ""), byModuleId: v.body.props.casePath === "$item.id" });
39787
+ }
39788
+ };
39789
+ walk2(root);
39790
+ return out;
39791
+ }
39792
+ function dropModules(block, ids) {
39793
+ const dropped = [];
39794
+ const walk2 = (v) => {
39795
+ if (Array.isArray(v)) return v.forEach(walk2);
39796
+ if (!v || typeof v !== "object") return;
39797
+ const rec = v;
39798
+ if (typeof rec.homeModules === "string") {
39799
+ try {
39800
+ const modules = JSON.parse(rec.homeModules);
39801
+ const kept = Array.isArray(modules) ? modules.filter((m) => !(typeof m?.id === "string" && ids.has(m.id))) : void 0;
39802
+ if (kept && kept.length && kept.length < modules.length) {
39803
+ for (const m of modules) if (!kept.includes(m)) dropped.push(String(m.id));
39804
+ rec.homeModules = JSON.stringify(kept);
39805
+ }
39806
+ } catch {
39807
+ }
39808
+ }
39809
+ for (const child of Object.values(rec)) walk2(child);
39810
+ };
39811
+ walk2(block);
39812
+ return dropped;
39813
+ }
39814
+ function pruneChrome(items, removed, ledger) {
39815
+ const gone = (item) => typeof item.screenId === "string" && removed(item.screenId);
39816
+ const groups = [{ rows: [] }];
39817
+ for (const item of items) {
39818
+ if (item.role === "section") groups.push({ header: item, rows: [] });
39819
+ else groups[groups.length - 1].rows.push(item);
39820
+ }
39821
+ const out = [];
39822
+ for (const g of groups) {
39823
+ const navigable = g.rows.filter((r) => typeof r.screenId === "string");
39824
+ const keptRows = g.rows.filter((r) => !gone(r));
39825
+ const emptied = !!g.header && navigable.length > 0 && !keptRows.some((r) => typeof r.screenId === "string");
39826
+ if (g.header && !emptied) out.push(g.header);
39827
+ for (const r of emptied ? [] : keptRows) out.push(r);
39828
+ }
39829
+ const keptSet = new Set(out);
39830
+ for (const item of items) if (!keptSet.has(item)) ledger.push(typeof item.id === "string" ? item.id : String(item.screenId ?? "(unnamed)"));
39831
+ return out;
39832
+ }
39833
+ function rewriteNav(nav, removed, homeId) {
39834
+ const next = structuredClone(nav);
39835
+ const report2 = { linkable: [], tabs: [], drawer: [], sidebar: [], deepLinkPatterns: [], topBarActions: [], headerActions: [], surfaceDestinations: [], appIconBadgeSources: [], reparented: [] };
39836
+ const tabItems = asItems(next.tabBar);
39837
+ const hadTabs = !!tabItems?.length;
39838
+ if (tabItems) next.tabBar.items = pruneChrome(tabItems, removed, report2.tabs);
39839
+ const drawerItems = asItems(next.drawer);
39840
+ if (drawerItems) next.drawer.items = pruneChrome(drawerItems, removed, report2.drawer);
39841
+ const sidebarItems = asItems(next.sidebar);
39842
+ if (sidebarItems) next.sidebar.items = pruneChrome(sidebarItems, removed, report2.sidebar);
39843
+ if (hadTabs && !asItems(next.tabBar).length) {
39844
+ throw new TrimRefusal(`no tab would remain \u2014 every tabBar item opens a screen this trim removes (${report2.tabs.join(", ")}); keep at least one tab's screen`, { removedTabs: report2.tabs });
39845
+ }
39846
+ const remainingRoots = [...asItems(next.tabBar) ?? [], ...asItems(next.sidebar) ?? []].filter((i) => i.role !== "section");
39847
+ const remainingRootIds = new Set(remainingRoots.map((i) => i.id).filter((id) => typeof id === "string"));
39848
+ const removedRootIds = /* @__PURE__ */ new Set([...report2.tabs, ...report2.sidebar]);
39849
+ if (Array.isArray(next.deepLinkPatterns)) {
39850
+ next.deepLinkPatterns = next.deepLinkPatterns.filter((p) => {
39851
+ const id = p?.params?.screenId ?? p?.screenId;
39852
+ if (typeof id === "string" && removed(id)) {
39853
+ report2.deepLinkPatterns.push(String(p.pattern ?? id));
39854
+ return false;
39855
+ }
39856
+ return true;
39857
+ });
39858
+ }
39859
+ for (const key2 of ["drawer", "sidebar"]) {
39860
+ const actions = next[key2]?.header?.actions;
39861
+ if (!Array.isArray(actions)) continue;
39862
+ next[key2].header.actions = actions.filter((a) => {
39863
+ if (!(typeof a?.screenId === "string" && removed(a.screenId))) return true;
39864
+ report2.headerActions.push(`${key2}:${String(a.icon ?? a.screenId)}`);
39865
+ return false;
39866
+ });
39867
+ }
39868
+ const topActions = next.topBarDefault?.actions;
39869
+ if (Array.isArray(topActions)) {
39870
+ next.topBarDefault.actions = topActions.filter((a) => {
39871
+ const to = openTarget(a?.onTap) ?? openTarget(a?.action);
39872
+ if (to && removed(to)) {
39873
+ report2.topBarActions.push(entryLabel(a));
39874
+ return false;
39875
+ }
39876
+ return true;
39877
+ });
39878
+ }
39879
+ if (next.surfaces && typeof next.surfaces === "object") {
39880
+ for (const [surface, cfg] of Object.entries(next.surfaces)) {
39881
+ if (!cfg || typeof cfg !== "object") continue;
39882
+ const goneDest = (id) => typeof id === "string" && !remainingRootIds.has(id) && (removedRootIds.has(id) || removed(id));
39883
+ const unmapped = /* @__PURE__ */ new Set();
39884
+ if (cfg.screenIds && typeof cfg.screenIds === "object") {
39885
+ for (const [dest, screenId] of Object.entries(cfg.screenIds)) {
39886
+ if (goneDest(dest) || typeof screenId === "string" && removed(screenId)) {
39887
+ delete cfg.screenIds[dest];
39888
+ report2.surfaceDestinations.push(`${surface}:${dest}`);
39889
+ if (!remainingRootIds.has(dest)) unmapped.add(dest);
39890
+ }
39891
+ }
39892
+ }
39893
+ if (Array.isArray(cfg.destinations)) {
39894
+ cfg.destinations = cfg.destinations.filter((id) => {
39895
+ const gone = goneDest(id) || typeof id === "string" && unmapped.has(id);
39896
+ if (!gone) return true;
39897
+ const entry = `${surface}:${String(id)}`;
39898
+ if (!report2.surfaceDestinations.includes(entry)) report2.surfaceDestinations.push(entry);
39899
+ return false;
39900
+ });
39901
+ }
39902
+ }
39903
+ }
39904
+ if (Array.isArray(next.appIconBadgeSources)) {
39905
+ const goneActions = new Set(report2.topBarActions);
39906
+ const idsOf = (items) => new Set((items ?? []).map((i) => i.id).filter((x) => typeof x === "string"));
39907
+ const removedTabs = new Set(report2.tabs);
39908
+ const removedDrawer = new Set(report2.drawer);
39909
+ const remainingTabIds = idsOf(asItems(next.tabBar));
39910
+ const remainingDrawerIds = idsOf(asItems(next.drawer));
39911
+ next.appIconBadgeSources = next.appIconBadgeSources.filter((s) => {
39912
+ if (typeof s !== "string") return true;
39913
+ const [kind, id] = [s.slice(0, s.indexOf(":")), s.slice(s.indexOf(":") + 1)];
39914
+ const gone = kind === "topBarAction" ? goneActions.has(id) : kind === "tab" ? removedTabs.has(id) && !remainingTabIds.has(id) : kind === "drawer" ? removedDrawer.has(id) && !remainingDrawerIds.has(id) : false;
39915
+ if (gone) report2.appIconBadgeSources.push(s);
39916
+ return !gone;
39917
+ });
39918
+ }
39919
+ if (Array.isArray(next.linkableScreens)) {
39920
+ const original = next.linkableScreens;
39921
+ const byId = /* @__PURE__ */ new Map();
39922
+ for (const l of original) if (typeof l.screenId === "string") byId.set(l.screenId, l);
39923
+ const kept = original.filter((l) => {
39924
+ if (typeof l.screenId === "string" && removed(l.screenId)) {
39925
+ report2.linkable.push(l.screenId);
39926
+ return false;
39927
+ }
39928
+ return true;
39929
+ });
39930
+ const keptById = /* @__PURE__ */ new Map();
39931
+ for (const l of kept) if (typeof l.screenId === "string") keptById.set(l.screenId, l);
39932
+ const newParent = (l) => {
39933
+ let p = typeof l.parentScreenId === "string" ? l.parentScreenId : "";
39934
+ const seen = /* @__PURE__ */ new Set();
39935
+ while (p && removed(p) && !seen.has(p)) {
39936
+ seen.add(p);
39937
+ const up = byId.get(p)?.parentScreenId;
39938
+ p = typeof up === "string" ? up : "";
39939
+ }
39940
+ return p;
39941
+ };
39942
+ const tabFor = (screenId) => {
39943
+ const own2 = remainingRoots.find((t) => t.screenId === screenId);
39944
+ return typeof own2?.id === "string" ? own2.id : void 0;
39945
+ };
39946
+ const homeTab = tabFor(homeId) ?? (typeof remainingRoots[0]?.id === "string" ? remainingRoots[0].id : "");
39947
+ const finalTab = /* @__PURE__ */ new Map();
39948
+ const resolveTab = (l, depth = 0) => {
39949
+ if (finalTab.has(l)) return finalTab.get(l);
39950
+ const current = typeof l.tabId === "string" ? l.tabId : "";
39951
+ let tab = current;
39952
+ if (current && removedRootIds.has(current) && !remainingRootIds.has(current)) {
39953
+ const parent = newParent(l);
39954
+ const pl = parent ? keptById.get(parent) : void 0;
39955
+ tab = pl && pl !== l && depth < 64 ? resolveTab(pl, depth + 1) : tabFor(String(l.screenId)) ?? homeTab;
39956
+ }
39957
+ finalTab.set(l, tab);
39958
+ return tab;
39959
+ };
39960
+ for (const l of kept) {
39961
+ const fromParent = typeof l.parentScreenId === "string" ? l.parentScreenId : "";
39962
+ const fromTab = typeof l.tabId === "string" ? l.tabId : "";
39963
+ const toParent = newParent(l);
39964
+ const toTab = resolveTab(l);
39965
+ if (toParent === fromParent && toTab === fromTab) continue;
39966
+ const entry = { screenId: String(l.screenId) };
39967
+ if (toParent !== fromParent) entry.parentScreenId = { from: fromParent, to: toParent };
39968
+ if (toTab !== fromTab) entry.tabId = { from: fromTab, to: toTab };
39969
+ report2.reparented.push(entry);
39970
+ }
39971
+ for (const l of kept) {
39972
+ if (typeof l.parentScreenId === "string") l.parentScreenId = newParent(l);
39973
+ if (typeof l.tabId === "string") l.tabId = resolveTab(l);
39974
+ }
39975
+ next.linkableScreens = kept;
39976
+ }
39977
+ return { nav: next, report: report2 };
39978
+ }
39979
+ function leftoverNavRefs(nav, removedIds) {
39980
+ const gone = new Set(removedIds);
39981
+ const out = [];
39982
+ const walk2 = (v, path2) => {
39983
+ if (Array.isArray(v)) return v.forEach((c, i) => walk2(c, `${path2}[${i}]`));
39984
+ if (!v || typeof v !== "object") return;
39985
+ for (const [k, child] of Object.entries(v)) {
39986
+ const here = path2 ? `${path2}.${k}` : k;
39987
+ if (NAV_SCREEN_KEYS.has(k) && typeof child === "string") {
39988
+ if (gone.has(child)) out.push(`${here}=${child}`);
39989
+ } else if (k === "screenIds" && child && typeof child === "object" && !Array.isArray(child)) {
39990
+ for (const [dest, id] of Object.entries(child)) if (typeof id === "string" && gone.has(id)) out.push(`${here}.${dest}=${id}`);
39991
+ } else walk2(child, here);
39992
+ }
39993
+ };
39994
+ walk2(nav, "");
39995
+ return out;
39996
+ }
39997
+ function navPinnedScreens(nav) {
39998
+ const out = [];
39999
+ const add = (v) => {
40000
+ if (isStatic(v)) out.push(v);
40001
+ };
40002
+ for (const key2 of ["drawer", "sidebar"]) {
40003
+ const block = nav[key2];
40004
+ if (!block || typeof block !== "object") continue;
40005
+ add(block.contentScreenId);
40006
+ add(block.header?.screenId);
40007
+ add(block.footer?.screenId);
40008
+ add(block.footer?.badgeScreenId);
40009
+ }
40010
+ return out;
40011
+ }
40012
+ function planTrim(src, keepIds, links) {
40013
+ const app = parseApp(src.dev, src.config);
40014
+ const kept = /* @__PURE__ */ new Set([app.home]);
40015
+ const unknown = [];
40016
+ for (const id of keepIds) {
40017
+ const hits = [];
40018
+ const ex = app.exact.get(id);
40019
+ if (ex) hits.push(ex);
40020
+ const base = app.templated.find((r) => r.label === id);
40021
+ if (base) hits.push(base);
40022
+ if (!hits.length) {
40023
+ const served = app.templated.find((r) => r.re.test(id));
40024
+ if (served) hits.push(served);
40025
+ }
40026
+ if (!hits.length) unknown.push(id);
40027
+ for (const r of hits) kept.add(r);
40028
+ }
40029
+ if (unknown.length) {
40030
+ throw new TrimRefusal(`unknown screen id${unknown.length > 1 ? "s" : ""} ${unknown.join(", ")} \u2014 --keep takes the ids this app serves`, {
40031
+ unknown,
40032
+ validIds: sorted(app.routes.map((r) => r.label))
40033
+ });
40034
+ }
40035
+ const explicit = new Set([...kept].filter((r) => r !== app.home || keepIds.includes(app.home.label)));
40036
+ const implicit = explicit.has(app.home) ? [] : [app.home];
40037
+ for (const id of navPinnedScreens(app.nav.value)) {
40038
+ const route = resolveIn(app, id);
40039
+ if (!route || kept.has(route)) continue;
40040
+ kept.add(route);
40041
+ implicit.push(route);
40042
+ }
40043
+ const follow = (seed2) => {
40044
+ const set = new Set(seed2);
40045
+ const added = [];
40046
+ const queue = app.routes.filter((r) => set.has(r));
40047
+ while (queue.length) {
40048
+ const r = queue.shift();
40049
+ for (const link of linksOf(r)) {
40050
+ const target = resolveIn(app, link.to);
40051
+ if (!target || set.has(target)) continue;
40052
+ set.add(target);
40053
+ added.push({ screen: r.label, node: link.node, to: target.label });
40054
+ queue.push(target);
40055
+ }
40056
+ }
40057
+ return { set, added };
40058
+ };
40059
+ let addedByLinks;
40060
+ if (links === "follow") {
40061
+ const closed = follow(kept);
40062
+ for (const r of closed.set) kept.add(r);
40063
+ addedByLinks = closed.added;
40064
+ }
40065
+ const isKept = (r) => !r || kept.has(r);
40066
+ const removedRoutes = app.routes.filter((r) => !kept.has(r));
40067
+ const keptRoutes = app.routes.filter((r) => kept.has(r));
40068
+ const removedTarget = (id) => !isKept(resolveIn(app, id));
40069
+ const dropped = [];
40070
+ const unremovable = [];
40071
+ const emptied = [];
40072
+ const emptiedCases = [];
40073
+ const caseModules = [];
40074
+ const edited = /* @__PURE__ */ new Map();
40075
+ for (const r of keptRoutes) {
40076
+ if (!linksOf(r).some((l) => removedTarget(l.to))) continue;
40077
+ const block = structuredClone(r.block);
40078
+ const isDead = (entry) => {
40079
+ const own2 = tapTarget(entry);
40080
+ const to = own2 && removedTarget(own2) ? own2 : rowTemplateTarget(entry, removedTarget);
40081
+ if (!to) return false;
40082
+ dropped.push({ screen: r.label, node: entryLabel(entry), to });
40083
+ return true;
40084
+ };
40085
+ const topBar = block.topBar;
40086
+ if (topBar && Array.isArray(topBar.actions)) topBar.actions = topBar.actions.filter((a) => !isDead(a));
40087
+ const cases = emptyDeadCases(block.payload, isDead);
40088
+ for (const c of cases) emptiedCases.push({ screen: r.label, case: c.id });
40089
+ if (pruneTree(block.payload, isDead).emptied) emptied.push(r.label);
40090
+ const moduleIds = new Set(cases.filter((c) => c.byModuleId).map((c) => c.id));
40091
+ if (moduleIds.size) caseModules.push(...dropModules(block, moduleIds));
40092
+ for (const l of linksOf({ ...r, block })) if (removedTarget(l.to)) unremovable.push({ screen: r.label, to: l.to });
40093
+ edited.set(r, block);
40094
+ }
40095
+ const followWouldAdd = sorted([...follow(kept).set].filter((r) => !kept.has(r)).map((r) => r.label));
40096
+ if (dropped.length || unremovable.length) {
40097
+ if (!links) {
40098
+ throw new TrimRefusal(`kept screens link to screens this trim would remove \u2014 choose what happens to those links with --links`, {
40099
+ droppedLinks: dropped,
40100
+ ...emptiedCases.length ? { emptiedCases } : {},
40101
+ ...unremovable.length ? { unremovable } : {},
40102
+ ...emptied.length ? { dropWouldEmpty: emptied } : {},
40103
+ followWouldAdd,
40104
+ hint: `--links drop removes the ${dropped.length} tapping node(s) listed in droppedLinks; --links follow keeps ${followWouldAdd.join(", ")} as well`
40105
+ });
40106
+ }
40107
+ if (emptied.length) {
40108
+ throw new TrimRefusal(`dropping the links in ${emptied.map((id) => `"${id}"`).join(", ")} would empty the screen's root \u2014 nothing would be left on it; keep what it links to (--links follow) or add those ids to --keep`, {
40109
+ dropWouldEmpty: emptied
40110
+ });
40111
+ }
40112
+ if (unremovable.length) {
40113
+ throw new TrimRefusal(`"${unremovable[0].screen}" links to a removed screen from a position --links drop cannot remove (a screen root, or a slot that is not a child list) \u2014 keep ${sorted(unremovable.map((u) => u.to)).join(", ")}, or edit ${sorted(unremovable.map((u) => u.screen)).join(", ")} by hand first`, {
40114
+ unremovable
40115
+ });
40116
+ }
40117
+ }
40118
+ const removedIds = sorted(removedRoutes.filter((r) => {
40119
+ const res = resolveIn(app, r.label);
40120
+ return !(res && kept.has(res));
40121
+ }).map((r) => r.label));
40122
+ const removedIdSet = new Set(removedIds);
40123
+ const navRemoved = (id) => removedTarget(id) || removedIdSet.has(id);
40124
+ const { nav, report: navReport } = rewriteNav(app.nav.value, navRemoved, app.home.label);
40125
+ const subtreeRemoved = [];
40126
+ const subtreeNext = app.subtree?.value.filter((id) => {
40127
+ if (typeof id !== "string" || id.includes("*") || !app.exact.get(id) || isKept(app.exact.get(id))) return true;
40128
+ subtreeRemoved.push(id);
40129
+ return false;
40130
+ });
40131
+ const configBundles = new Set([...src.config.matchAll(/\/v1\/bundles\/([A-Za-z0-9_-]+)/g)].map((m) => m[1]));
40132
+ const keptWorkers = /* @__PURE__ */ new Set(["_shared", ...configBundles]);
40133
+ for (const r of keptRoutes) for (const b of r.bundles) keptWorkers.add(b);
40134
+ const importsOf = (dir) => {
40135
+ const out = [];
40136
+ for (const [rel, text4] of Object.entries(src.workerFiles)) {
40137
+ if (!rel.startsWith(`tier5-src/${dir}/`)) continue;
40138
+ for (const m of text4.matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']\.\.\/([A-Za-z0-9_.-]+)(?:\/|["'])/g)) out.push(m[1]);
40139
+ }
40140
+ return out;
40141
+ };
40142
+ const pending = [...keptWorkers];
40143
+ while (pending.length) {
40144
+ for (const dep of importsOf(pending.pop())) {
40145
+ if (!keptWorkers.has(dep)) {
40146
+ keptWorkers.add(dep);
40147
+ pending.push(dep);
40148
+ }
40149
+ }
40150
+ }
40151
+ const dirs = new Set(src.workerDirs);
40152
+ const removedWorkers = sorted(removedRoutes.flatMap((r) => r.bundles).filter((b) => dirs.has(b) && !keptWorkers.has(b)));
40153
+ const workerRefs = [];
40154
+ const literal = /(["'`])([A-Za-z_][A-Za-z0-9_]*[_/]?)(?=\1|\$\{)/g;
40155
+ for (const rel of Object.keys(src.workerFiles).sort()) {
40156
+ const dir = rel.split("/")[1];
40157
+ if (!keptWorkers.has(dir) || !dirs.has(dir)) continue;
40158
+ const lines = src.workerFiles[rel].split("\n");
40159
+ lines.forEach((line, i) => {
40160
+ for (const m of line.matchAll(literal)) {
40161
+ const token = m[2];
40162
+ const prefix = /[_/]$/.test(token);
40163
+ const route = prefix ? app.templated.find((r) => r.re.test(`${token}x`)) : resolveIn(app, token);
40164
+ if (route && !kept.has(route)) workerRefs.push(`${rel}:${i + 1} \u2192 ${route.label}`);
40165
+ }
40166
+ });
40167
+ }
40168
+ const homeModules = links === "drop" ? homeModulesPass(app, kept, edited) : void 0;
40169
+ let dev = "";
40170
+ let cursor = 0;
40171
+ const cut = (from, to, replacement = "") => {
40172
+ dev += app.dev.slice(cursor, from) + replacement;
40173
+ cursor = to;
40174
+ };
40175
+ const emptyList = (region, list) => list.length > 0 && list.every((r) => !kept.has(r)) ? region : void 0;
40176
+ const templatedEmptied = emptyList(app.templatedRegion, app.templated);
40177
+ for (const r of app.routes) {
40178
+ if (templatedEmptied && r.kind === "templated") continue;
40179
+ if (!kept.has(r)) {
40180
+ cut(r.lineStart, r.end);
40181
+ continue;
40182
+ }
40183
+ const block = edited.get(r);
40184
+ let text4 = block ? serializeAt(block, app.dev, r.open) : void 0;
40185
+ if (r === app.home && homeModules?.text !== void 0) text4 = homeModules.text;
40186
+ if (text4 !== void 0) cut(r.open, r.close, text4);
40187
+ }
40188
+ if (templatedEmptied) cut(templatedEmptied[0], templatedEmptied[1], "\n");
40189
+ dev += app.dev.slice(cursor);
40190
+ let config = app.config;
40191
+ const edits = [];
40192
+ if (JSON.stringify(nav) !== JSON.stringify(app.nav.value)) {
40193
+ edits.push({ start: app.nav.open, end: app.nav.close, text: serializeAt(nav, app.config, app.nav.open) });
40194
+ }
40195
+ if (app.subtree && subtreeRemoved.length) {
40196
+ const wrapped = app.config.slice(app.subtree.start, app.subtree.end).includes("\n");
40197
+ edits.push({ start: app.subtree.start, end: app.subtree.end, text: wrapped ? serializeAt(subtreeNext, app.config, app.subtree.start) : JSON.stringify(subtreeNext) });
40198
+ }
40199
+ for (const e of edits.sort((a, b) => b.start - a.start)) config = config.slice(0, e.start) + e.text + config.slice(e.end);
40200
+ const keptExact = keptRoutes.filter((r) => r.kind === "screen");
40201
+ const keptTemplated = keptRoutes.filter((r) => r.kind === "templated");
40202
+ const report2 = {
40203
+ ...links ? { links } : {},
40204
+ kept: { screens: sorted(keptExact.map((r) => r.label)), templated: sorted(keptTemplated.map((r) => r.label)) },
40205
+ implicit: implicit.map((r) => r.label),
40206
+ removed: {
40207
+ screens: sorted(removedRoutes.filter((r) => r.kind === "screen").map((r) => r.label)),
40208
+ templated: sorted(removedRoutes.filter((r) => r.kind === "templated").map((r) => r.label)),
40209
+ workers: removedWorkers,
40210
+ linkable: navReport.linkable,
40211
+ tabs: navReport.tabs,
40212
+ drawer: navReport.drawer,
40213
+ sidebar: navReport.sidebar,
40214
+ deepLinkPatterns: navReport.deepLinkPatterns,
40215
+ topBarActions: navReport.topBarActions,
40216
+ headerActions: navReport.headerActions,
40217
+ surfaceDestinations: navReport.surfaceDestinations,
40218
+ appIconBadgeSources: navReport.appIconBadgeSources,
40219
+ workerSubtreeScreens: subtreeRemoved
40220
+ },
40221
+ reparented: navReport.reparented,
40222
+ ...addedByLinks ? { addedByLinks } : {},
40223
+ ...links === "drop" ? { droppedLinks: dropped } : {},
40224
+ ...links === "drop" && emptiedCases.length ? { emptiedCases } : {},
40225
+ ...links === "drop" && (caseModules.length || homeModules?.dropped.length) ? { home: { droppedModules: [...caseModules, ...homeModules?.dropped ?? []] } } : {},
40226
+ warnings: {
40227
+ workerRefs,
40228
+ ...workerRefs.length ? { hint: `kept worker code still names ${sorted(workerRefs.map((w) => w.slice(w.lastIndexOf("\u2192 ") + 2))).join(", ")} \u2014 those taps are built at runtime and will open nothing; add the ids to --keep if they should keep working` } : {}
40229
+ },
40230
+ bytes: { before: Buffer.byteLength(app.dev), after: Buffer.byteLength(dev) }
40231
+ };
40232
+ return { dev, config, removedWorkers, removedIds, report: report2 };
40233
+ }
40234
+ function homeModulesPass(app, kept, edited) {
40235
+ const verticals = Object.entries(EXPLORE_CARDS).filter(([, card]) => app.exact.has(card.screenId));
40236
+ const removedVertical = verticals.some(([, card]) => !kept.has(app.exact.get(card.screenId)));
40237
+ if (!removedVertical) return { dropped: [] };
40238
+ const block = edited.get(app.home);
40239
+ const text4 = block ? serializeAt(block, app.dev, app.home.open) : app.dev.slice(app.home.open, app.home.close);
40240
+ const installed = verticals.filter(([, card]) => kept.has(app.exact.get(card.screenId))).map(([v]) => v);
40241
+ const patched = patchHomeModules(text4, installed);
40242
+ if (!patched.droppedModules.length) return { dropped: [] };
40243
+ return { text: patched.src, dropped: patched.droppedModules };
40244
+ }
40245
+ function listWorkers(dir) {
40246
+ const root = join36(dir, "tier5-src");
40247
+ if (!existsSync39(root)) return { dirs: [], files: {} };
40248
+ const dirs = readdirSync20(root).filter((n) => statSync8(join36(root, n)).isDirectory()).sort();
40249
+ const files = {};
40250
+ const walk2 = (abs, rel) => {
40251
+ for (const name of readdirSync20(abs)) {
40252
+ if (name === "node_modules") continue;
40253
+ const p = join36(abs, name);
40254
+ const r = `${rel}/${name}`;
40255
+ if (statSync8(p).isDirectory()) walk2(p, r);
40256
+ else if (/\.(?:[cm]?[jt]sx?|json)$/.test(name)) files[r] = readFileSync43(p, "utf8");
40257
+ }
40258
+ };
40259
+ for (const d of dirs) walk2(join36(root, d), `tier5-src/${d}`);
40260
+ return { dirs, files };
40261
+ }
40262
+ function csv(v) {
40263
+ if (v === void 0 || v === true || v === false) return void 0;
40264
+ const parts = (Array.isArray(v) ? v : [v]).flatMap((s) => s.split(",")).map((s) => s.trim()).filter(Boolean);
40265
+ return parts.length ? [...new Set(parts)] : void 0;
40266
+ }
40267
+ async function runTemplateTrim(req, deps) {
40268
+ const keep = csv(req.keep);
40269
+ if (!keep) {
40270
+ return refuse3(new TrimRefusal("--keep is required \u2014 the screen ids to keep, comma-separated (the home screen is always kept)", {
40271
+ usage: "himi template trim --keep <id,id,\u2026> [--content <dir>] [--links follow|drop] [--dry-run] [--force]"
40272
+ }));
40273
+ }
40274
+ let links;
40275
+ if (req.links !== void 0) {
40276
+ if (req.links !== "follow" && req.links !== "drop") {
40277
+ return refuse3(new TrimRefusal(`--links takes follow or drop (got ${JSON.stringify(req.links)})`));
40278
+ }
40279
+ links = req.links;
40280
+ }
40281
+ const dir = req.dir;
40282
+ const devPath = join36(dir, "dev/index.ts");
40283
+ const configPath = join36(dir, "config.ts");
40284
+ if (!existsSync39(devPath) || !existsSync39(configPath)) {
40285
+ return refuse3(new TrimRefusal(`${dir} has no dev/index.ts + config.ts \u2014 point --content at an app scaffolded with \`himi init --template\``));
40286
+ }
40287
+ if (isTemplateSourceTree(dir)) {
40288
+ 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`));
40289
+ }
40290
+ const dev = readFileSync43(devPath, "utf8");
40291
+ const config = readFileSync43(configPath, "utf8");
40292
+ const workers = listWorkers(dir);
40293
+ let plan;
40294
+ try {
40295
+ plan = planTrim({ dev, config, workerDirs: workers.dirs, workerFiles: workers.files }, keep, links);
40296
+ } catch (e) {
40297
+ if (e instanceof TrimRefusal) return refuse3(e);
40298
+ throw e;
40299
+ }
40300
+ if (req.dryRun) return { code: 0, result: { ok: true, dryRun: true, ...plan.report, validate: null } };
40301
+ const hadHimi = existsSync39(join36(dir, ".himi"));
40302
+ const backupRel = join36(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
40303
+ const backup = join36(dir, backupRel);
40304
+ const originals = [[devPath, "dev/index.ts"], [configPath, "config.ts"]];
40305
+ const moved = [];
40306
+ const restore = () => {
40307
+ for (const [path2, rel] of originals) {
40308
+ const original = readFileSync43(join36(backup, rel));
40309
+ if (!existsSync39(path2) || !readFileSync43(path2).equals(original)) writeFileSync14(path2, original);
40310
+ }
40311
+ for (const name of [...moved].reverse()) renameSync3(join36(backup, "tier5-src", name), join36(dir, "tier5-src", name));
40312
+ };
40313
+ const dropBackup = () => {
40314
+ rmSync6(backup, { recursive: true, force: true });
40315
+ for (const d of [dirname23(backup), join36(dir, ".himi")]) {
40316
+ if (d === join36(dir, ".himi") && hadHimi) break;
40317
+ try {
40318
+ if (!readdirSync20(d).length) rmSync6(d, { recursive: true });
40319
+ } catch {
40320
+ }
40321
+ }
40322
+ };
40323
+ const base = (validate4) => ({
40324
+ dryRun: false,
40325
+ ...plan.report,
40326
+ validate: validate4 ? { counts: validate4.counts ?? null, ...validate4.tail ? { tail: validate4.tail } : {} } : null
40327
+ });
40328
+ const abort = (error, cause, validate4) => {
40329
+ const message = cause?.message ?? String(cause);
40330
+ try {
40331
+ restore();
40332
+ } catch (e) {
40333
+ return {
40334
+ code: 1,
40335
+ result: { ok: false, error: `${error} (${message}), and restoring the originals failed too (${e.message})`, rolledBack: false, backup: backupRel, hint: `the originals are in ${backupRel} \u2014 copy dev/index.ts, config.ts and tier5-src/* back by hand`, ...base(validate4) }
40336
+ };
40337
+ }
40338
+ dropBackup();
40339
+ return { code: 1, result: { ok: false, error: `${error} (${message}); every file was restored`, rolledBack: true, ...base(validate4) } };
40340
+ };
40341
+ try {
40342
+ mkdirSync18(join36(backup, "dev"), { recursive: true });
40343
+ for (const [path2, rel] of originals) cpSync(path2, join36(backup, rel));
40344
+ if (plan.removedWorkers.length) mkdirSync18(join36(backup, "tier5-src"), { recursive: true });
40345
+ } catch (e) {
40346
+ dropBackup();
40347
+ return { code: 1, result: { ok: false, error: `could not back up the app before trimming it (${e.message}); nothing was changed`, rolledBack: true, ...base() } };
40348
+ }
40349
+ try {
40350
+ const moveDir = deps.moveDir ?? renameSync3;
40351
+ for (const name of plan.removedWorkers) {
40352
+ moveDir(join36(dir, "tier5-src", name), join36(backup, "tier5-src", name));
40353
+ moved.push(name);
40354
+ }
40355
+ if (plan.dev !== dev) writeFileSync14(devPath, plan.dev);
40356
+ if (plan.config !== config) writeFileSync14(configPath, plan.config);
40357
+ } catch (e) {
40358
+ return abort("the trim failed while writing", e);
40359
+ }
40360
+ let failure;
40361
+ let validate3;
40362
+ try {
40363
+ const written = parseApp(readFileSync43(devPath, "utf8"), readFileSync43(configPath, "utf8"));
40364
+ const leftovers = leftoverNavRefs(written.nav.value, plan.removedIds);
40365
+ if (leftovers.length) failure = { error: "navigation still names screens this trim removed", leftovers };
40366
+ else {
40367
+ validate3 = await deps.validate(dir);
40368
+ if (validate3.code !== 0) failure = { error: "himi validate --strict failed on the trimmed app" };
40369
+ }
40370
+ } catch (e) {
40371
+ return abort("the trim could not be checked", e, validate3);
40372
+ }
40373
+ if (!failure) {
40374
+ dropBackup();
40375
+ return { code: 0, result: { ok: true, ...base(validate3) } };
40376
+ }
40377
+ if (req.force) {
40378
+ return {
40379
+ code: 1,
40380
+ result: { ok: false, ...failure, rolledBack: false, backup: backupRel, hint: `--force kept the trimmed tree; the originals are in ${backupRel}`, ...base(validate3) }
40381
+ };
40382
+ }
40383
+ try {
40384
+ restore();
40385
+ } catch (e) {
40386
+ return abort(String(failure.error), e, validate3);
40387
+ }
40388
+ dropBackup();
40389
+ return {
40390
+ code: 1,
40391
+ result: { ok: false, ...failure, rolledBack: true, hint: "nothing changed \u2014 fix what validate reports (or keep more screens) and trim again, or pass --force to keep the trimmed tree anyway", ...base(validate3) }
40392
+ };
40393
+ }
40394
+ var TrimRefusal, LIT, ATOM, QUANT, CAP, SAFE_ROUTE_PATTERN, lineOf, isStatic, entryLabel, isNode3, asItems, NAV_SCREEN_KEYS, sorted, refuse3;
40395
+ var init_template_trim = __esm({
40396
+ "src/template-trim.ts"() {
40397
+ init_validate_descriptor();
40398
+ init_scaffold();
40399
+ init_home_descriptor();
40400
+ TrimRefusal = class extends Error {
40401
+ detail;
40402
+ constructor(message, detail = {}) {
40403
+ super(message);
40404
+ this.detail = detail;
40405
+ }
40406
+ };
40407
+ LIT = String.raw`(?:[A-Za-z0-9_/.:-]|\\[./_:-])*`;
40408
+ ATOM = String.raw`(?:\[(?:[^\]\\[]|\\.)+\]|\\[dw]|\.)`;
40409
+ QUANT = String.raw`(?:[+*]|\{\d+(?:,\d*)?\})`;
40410
+ CAP = String.raw`\(${ATOM}${QUANT}\)`;
40411
+ SAFE_ROUTE_PATTERN = new RegExp(
40412
+ String.raw`^\^${LIT}(?:${CAP}${LIT}|\(\?:(?:${LIT}|\[(?:[^\]\\[]|\\.)+\])${CAP}\)\?)?\$$`
40413
+ );
40414
+ lineOf = (src, at) => src.slice(0, at).split("\n").length;
40415
+ isStatic = (id) => typeof id === "string" && id !== "" && !id.startsWith("$");
40416
+ entryLabel = (entry) => {
40417
+ const id = entry?.id;
40418
+ return typeof id === "string" && id ? id : "(unnamed)";
40419
+ };
40420
+ isNode3 = (v) => !!v && typeof v === "object" && !!v.body && typeof v.body === "object";
40421
+ asItems = (block) => {
40422
+ const items = block?.items;
40423
+ return Array.isArray(items) ? items : void 0;
40424
+ };
40425
+ NAV_SCREEN_KEYS = /* @__PURE__ */ new Set(["screenId", "parentScreenId", "contentScreenId", "badgeScreenId"]);
40426
+ sorted = (xs) => [...new Set(xs)].sort();
40427
+ refuse3 = (e) => ({ code: 2, result: { ok: false, error: e.message, ...e.detail } });
40428
+ }
40429
+ });
40430
+
39146
40431
  // src/nav-lint.ts
39147
40432
  var nav_lint_exports = {};
39148
40433
  __export(nav_lint_exports, {
@@ -39436,7 +40721,7 @@ function packagePathFor(pathname) {
39436
40721
  }
39437
40722
  return UNSUPPORTED;
39438
40723
  }
39439
- function parseJson(read, path2, errors) {
40724
+ function parseJson2(read, path2, errors) {
39440
40725
  const bytes2 = read(path2);
39441
40726
  if (!bytes2) return null;
39442
40727
  try {
@@ -39449,7 +40734,7 @@ function parseJson(read, path2, errors) {
39449
40734
  function validatePackage(read, opts = {}) {
39450
40735
  const errors = [];
39451
40736
  const warnings = [];
39452
- const header = parseJson(read, HIMI_PACKAGE_HEADER_PATH, errors);
40737
+ const header = parseJson2(read, HIMI_PACKAGE_HEADER_PATH, errors);
39453
40738
  if (!header && opts.requireHeader) {
39454
40739
  errors.push(`${HIMI_PACKAGE_HEADER_PATH} is missing \u2014 not a .himi package`);
39455
40740
  }
@@ -39477,7 +40762,7 @@ function validatePackage(read, opts = {}) {
39477
40762
  errors.push(`${HIMI_PACKAGE_SOURCE_PATH} is present in a package whose header declares no source`);
39478
40763
  }
39479
40764
  }
39480
- const manifest = parseJson(read, HIMI_PACKAGE_MANIFEST_PATH, errors);
40765
+ const manifest = parseJson2(read, HIMI_PACKAGE_MANIFEST_PATH, errors);
39481
40766
  if (!manifest) {
39482
40767
  if (!errors.some((e) => e.startsWith(HIMI_PACKAGE_MANIFEST_PATH))) {
39483
40768
  errors.push(`${HIMI_PACKAGE_MANIFEST_PATH} is missing`);
@@ -39769,13 +41054,13 @@ __export(pack_exports, {
39769
41054
  });
39770
41055
  import { createHash as createHash19 } from "node:crypto";
39771
41056
  import { deflateRawSync, inflateRawSync } from "node:zlib";
39772
- import { existsSync as existsSync39, mkdirSync as mkdirSync18, readFileSync as readFileSync43, readdirSync as readdirSync20, rmSync as rmSync6, statSync as statSync8, writeFileSync as writeFileSync14 } from "node:fs";
39773
- import { dirname as dirname23, join as join36, resolve as resolve35 } from "node:path";
41057
+ import { existsSync as existsSync40, mkdirSync as mkdirSync19, readFileSync as readFileSync44, readdirSync as readdirSync21, rmSync as rmSync7, statSync as statSync9, writeFileSync as writeFileSync15 } from "node:fs";
41058
+ import { dirname as dirname24, join as join37, resolve as resolve35 } from "node:path";
39774
41059
  function writeZip(entries) {
39775
- const sorted = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
39776
- const total = sorted.reduce((n, e) => n + e.bytes.length, 0);
41060
+ const sorted2 = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
41061
+ const total = sorted2.reduce((n, e) => n + e.bytes.length, 0);
39777
41062
  if (total > ZIP32_MAX) {
39778
- const biggest = sorted.reduce((a, b) => a.bytes.length >= b.bytes.length ? a : b);
41063
+ const biggest = sorted2.reduce((a, b) => a.bytes.length >= b.bytes.length ? a : b);
39779
41064
  throw new Error(
39780
41065
  `package content is ${(total / 1e9).toFixed(1)} GB, over the 4 GB limit of the package format (no zip64 support); the largest entry is ${biggest.path} at ${(biggest.bytes.length / 1e9).toFixed(1)} GB`
39781
41066
  );
@@ -39783,7 +41068,7 @@ function writeZip(entries) {
39783
41068
  const locals = [];
39784
41069
  const centrals = [];
39785
41070
  let offset = 0;
39786
- for (const entry of sorted) {
41071
+ for (const entry of sorted2) {
39787
41072
  if (!isSafeEntryName(entry.path)) throw new Error(`unsafe package entry name: ${JSON.stringify(entry.path)}`);
39788
41073
  const name = Buffer.from(entry.path, "utf8");
39789
41074
  const crc = zipCrc32(entry.bytes);
@@ -39830,8 +41115,8 @@ function writeZip(entries) {
39830
41115
  eocd.writeUInt32LE(EOCD_SIG, 0);
39831
41116
  eocd.writeUInt16LE(0, 4);
39832
41117
  eocd.writeUInt16LE(0, 6);
39833
- eocd.writeUInt16LE(sorted.length, 8);
39834
- eocd.writeUInt16LE(sorted.length, 10);
41118
+ eocd.writeUInt16LE(sorted2.length, 8);
41119
+ eocd.writeUInt16LE(sorted2.length, 10);
39835
41120
  eocd.writeUInt32LE(centralBuf.length, 12);
39836
41121
  eocd.writeUInt32LE(offset, 16);
39837
41122
  eocd.writeUInt16LE(0, 20);
@@ -39864,13 +41149,13 @@ function inflateEntry(buf, entry) {
39864
41149
  return bytes2;
39865
41150
  }
39866
41151
  function readIf(path2) {
39867
- return existsSync39(path2) && statSync8(path2).isFile() ? readFileSync43(path2) : null;
41152
+ return existsSync40(path2) && statSync9(path2).isFile() ? readFileSync44(path2) : null;
39868
41153
  }
39869
41154
  function listDir(dir) {
39870
- return existsSync39(dir) ? readdirSync20(dir).filter((f) => statSync8(join36(dir, f)).isFile()) : [];
41155
+ return existsSync40(dir) ? readdirSync21(dir).filter((f) => statSync9(join37(dir, f)).isFile()) : [];
39871
41156
  }
39872
41157
  function gatherReleaseEntries(appDir2) {
39873
- const manifestPath = join36(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
41158
+ const manifestPath = join37(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
39874
41159
  const manifestBytes = readIf(manifestPath);
39875
41160
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
39876
41161
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -39894,7 +41179,7 @@ function gatherReleaseEntries(appDir2) {
39894
41179
  }
39895
41180
  const missing = [];
39896
41181
  for (const rel of wanted) {
39897
- const bytes2 = readIf(join36(appDir2, rel));
41182
+ const bytes2 = readIf(join37(appDir2, rel));
39898
41183
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
39899
41184
  else missing.push(rel);
39900
41185
  }
@@ -39905,12 +41190,12 @@ function gatherReleaseEntries(appDir2) {
39905
41190
  );
39906
41191
  }
39907
41192
  for (const name of SINGLETONS) {
39908
- const bytes2 = readIf(join36(appDir2, name));
41193
+ const bytes2 = readIf(join37(appDir2, name));
39909
41194
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
39910
41195
  }
39911
41196
  const skipped = [];
39912
41197
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
39913
- for (const f of listDir(join36(appDir2, sub))) {
41198
+ for (const f of listDir(join37(appDir2, sub))) {
39914
41199
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
39915
41200
  }
39916
41201
  }
@@ -39943,18 +41228,18 @@ function buildEntries(appDir2, app, opts) {
39943
41228
  return { all, header, manifest, skipped };
39944
41229
  }
39945
41230
  function packRelease(releaseRoot, app, out, opts = {}) {
39946
- const appDir2 = join36(resolve35(releaseRoot), app);
41231
+ const appDir2 = join37(resolve35(releaseRoot), app);
39947
41232
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
39948
41233
  const buf = writeZip(all);
39949
- const isDir = existsSync39(out) && statSync8(out).isDirectory();
39950
- const outFile = isDir ? join36(resolve35(out), packageFileName(app, header.releaseId)) : resolve35(out);
39951
- mkdirSync18(dirname23(outFile), { recursive: true });
39952
- writeFileSync14(outFile, buf);
41234
+ const isDir = existsSync40(out) && statSync9(out).isDirectory();
41235
+ const outFile = isDir ? join37(resolve35(out), packageFileName(app, header.releaseId)) : resolve35(out);
41236
+ mkdirSync19(dirname24(outFile), { recursive: true });
41237
+ writeFileSync15(outFile, buf);
39953
41238
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
39954
41239
  }
39955
41240
  function resolvePackageDirDest(outDir, app, releaseId) {
39956
41241
  const packagedApp = (dir) => {
39957
- const bytes2 = readIf(join36(dir, HIMI_PACKAGE_HEADER_PATH));
41242
+ const bytes2 = readIf(join37(dir, HIMI_PACKAGE_HEADER_PATH));
39958
41243
  if (!bytes2) return void 0;
39959
41244
  try {
39960
41245
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -39963,15 +41248,15 @@ function resolvePackageDirDest(outDir, app, releaseId) {
39963
41248
  }
39964
41249
  };
39965
41250
  const replaceable = (dir) => {
39966
- if (!existsSync39(dir)) return true;
39967
- if (!statSync8(dir).isDirectory()) return false;
39968
- return readdirSync20(dir).length === 0 || packagedApp(dir) === app;
41251
+ if (!existsSync40(dir)) return true;
41252
+ if (!statSync9(dir).isDirectory()) return false;
41253
+ return readdirSync21(dir).length === 0 || packagedApp(dir) === app;
39969
41254
  };
39970
- if (existsSync39(outDir) && !statSync8(outDir).isDirectory()) {
41255
+ if (existsSync40(outDir) && !statSync9(outDir).isDirectory()) {
39971
41256
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
39972
41257
  }
39973
41258
  if (replaceable(outDir)) return outDir;
39974
- const child = join36(outDir, packageDirName(app, releaseId));
41259
+ const child = join37(outDir, packageDirName(app, releaseId));
39975
41260
  if (!replaceable(child)) {
39976
41261
  throw new Error(
39977
41262
  `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.`
@@ -39985,15 +41270,15 @@ function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
39985
41270
  "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."
39986
41271
  );
39987
41272
  }
39988
- const appDir2 = join36(resolve35(releaseRoot), app);
41273
+ const appDir2 = join37(resolve35(releaseRoot), app);
39989
41274
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
39990
41275
  const dest = resolvePackageDirDest(resolve35(outDir), app, header.releaseId);
39991
- rmSync6(dest, { recursive: true, force: true });
41276
+ rmSync7(dest, { recursive: true, force: true });
39992
41277
  let bytes2 = 0;
39993
41278
  for (const entry of all) {
39994
- const target = join36(dest, entry.path);
39995
- mkdirSync18(dirname23(target), { recursive: true });
39996
- writeFileSync14(target, entry.bytes);
41279
+ const target = join37(dest, entry.path);
41280
+ mkdirSync19(dirname24(target), { recursive: true });
41281
+ writeFileSync15(target, entry.bytes);
39997
41282
  bytes2 += entry.bytes.length;
39998
41283
  }
39999
41284
  return { app, releaseId: header.releaseId, header, out: dest, bytes: bytes2, entryCount: all.length, skipped };
@@ -40014,21 +41299,21 @@ function openPackageBuffer(buf) {
40014
41299
  return openFromEntries(readZip(buf), { requireHeader: true });
40015
41300
  }
40016
41301
  function openPackageFile(file) {
40017
- return openPackageBuffer(readFileSync43(file));
41302
+ return openPackageBuffer(readFileSync44(file));
40018
41303
  }
40019
41304
  function openPackageDir(dir) {
40020
41305
  const root = resolve35(dir);
40021
41306
  const entries = /* @__PURE__ */ new Map();
40022
41307
  const walk2 = (rel) => {
40023
- const abs = rel ? join36(root, rel) : root;
40024
- for (const name of readdirSync20(abs)) {
41308
+ const abs = rel ? join37(root, rel) : root;
41309
+ for (const name of readdirSync21(abs)) {
40025
41310
  const childRel = rel ? `${rel}/${name}` : name;
40026
- const childAbs = join36(abs, name);
40027
- if (statSync8(childAbs).isDirectory()) walk2(childRel);
40028
- else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync43(childAbs));
41311
+ const childAbs = join37(abs, name);
41312
+ if (statSync9(childAbs).isDirectory()) walk2(childRel);
41313
+ else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync44(childAbs));
40029
41314
  }
40030
41315
  };
40031
- if (!existsSync39(root)) throw new Error(`no such package directory: ${root}`);
41316
+ if (!existsSync40(root)) throw new Error(`no such package directory: ${root}`);
40032
41317
  walk2("");
40033
41318
  const manifestBytes = entries.get(HIMI_PACKAGE_MANIFEST_PATH);
40034
41319
  if (manifestBytes) {
@@ -40070,12 +41355,12 @@ function unpackTo(buf, dest, appFallback) {
40070
41355
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
40071
41356
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
40072
41357
  const root = resolve35(dest);
40073
- const appDir2 = join36(root, app);
40074
- rmSync6(appDir2, { recursive: true, force: true });
41358
+ const appDir2 = join37(root, app);
41359
+ rmSync7(appDir2, { recursive: true, force: true });
40075
41360
  for (const [rel, bytes2] of entries) {
40076
- const target = join36(appDir2, rel);
40077
- mkdirSync18(dirname23(target), { recursive: true });
40078
- writeFileSync14(target, bytes2);
41361
+ const target = join37(appDir2, rel);
41362
+ mkdirSync19(dirname24(target), { recursive: true });
41363
+ writeFileSync15(target, bytes2);
40079
41364
  }
40080
41365
  return { root, appDir: appDir2, app };
40081
41366
  }
@@ -40109,20 +41394,20 @@ __export(app_source_exports, {
40109
41394
  writeConflicts: () => writeConflicts,
40110
41395
  zipAppSource: () => zipAppSource
40111
41396
  });
40112
- import { readdirSync as readdirSync21, lstatSync as lstatSync5, readFileSync as readFileSync44, mkdirSync as mkdirSync19, writeFileSync as writeFileSync15, existsSync as existsSync40, rmSync as rmSync7, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
40113
- import { join as join37, resolve as resolve36, sep as sep8 } from "node:path";
41397
+ import { readdirSync as readdirSync22, lstatSync as lstatSync5, readFileSync as readFileSync45, mkdirSync as mkdirSync20, writeFileSync as writeFileSync16, existsSync as existsSync41, rmSync as rmSync8, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
41398
+ import { join as join38, resolve as resolve36, sep as sep8 } from "node:path";
40114
41399
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
40115
41400
  function walkSourceTree(dir, file, skip) {
40116
41401
  const walk2 = (relDir) => {
40117
- for (const name of readdirSync21(join37(dir, relDir))) {
40118
- const rel = relDir ? join37(relDir, name) : name;
40119
- const st = lstatSync5(join37(dir, rel));
41402
+ for (const name of readdirSync22(join38(dir, relDir))) {
41403
+ const rel = relDir ? join38(relDir, name) : name;
41404
+ const st = lstatSync5(join38(dir, rel));
40120
41405
  if (st.isSymbolicLink()) {
40121
41406
  skip(rel);
40122
41407
  continue;
40123
41408
  }
40124
41409
  if (st.isDirectory()) {
40125
- if (isDeniedSourceDir(name, relDir) || existsSync40(join37(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
41410
+ if (isDeniedSourceDir(name, relDir) || existsSync41(join38(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
40126
41411
  else walk2(rel);
40127
41412
  continue;
40128
41413
  }
@@ -40140,7 +41425,7 @@ function collectAppSource(dir) {
40140
41425
  const skipped = [];
40141
41426
  walkSourceTree(
40142
41427
  dir,
40143
- (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync44(join37(dir, hostRel)) }),
41428
+ (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync45(join38(dir, hostRel)) }),
40144
41429
  (hostRel) => skipped.push(hostRel)
40145
41430
  );
40146
41431
  entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -40187,28 +41472,28 @@ function writeAppSource(entries, destDir) {
40187
41472
  `refusing to restore into ${root}: the destination itself is a symlink, so every file would be written outside it. Remove it, or pass --out/--name somewhere else.`
40188
41473
  );
40189
41474
  }
40190
- mkdirSync19(root, { recursive: true });
41475
+ mkdirSync20(root, { recursive: true });
40191
41476
  const written = [];
40192
41477
  for (const entry of entries) {
40193
41478
  if (isDeniedSourcePath(entry.path)) continue;
40194
41479
  const segments = entry.path.split("/");
40195
41480
  let dir = root;
40196
41481
  for (const segment of segments.slice(0, -1)) {
40197
- dir = join37(dir, segment);
41482
+ dir = join38(dir, segment);
40198
41483
  const kind = pathKind(dir);
40199
41484
  if (kind?.isSymbolicLink()) unlinkSync3(dir);
40200
41485
  else if (kind) continue;
40201
- mkdirSync19(dir);
41486
+ mkdirSync20(dir);
40202
41487
  }
40203
- const target = join37(root, ...segments);
41488
+ const target = join38(root, ...segments);
40204
41489
  if (pathKind(target)?.isSymbolicLink()) unlinkSync3(target);
40205
- writeFileSync15(target, entry.bytes);
41490
+ writeFileSync16(target, entry.bytes);
40206
41491
  written.push(entry.path);
40207
41492
  }
40208
41493
  return written;
40209
41494
  }
40210
41495
  function staleSourcePaths(entries, destDir) {
40211
- if (!existsSync40(destDir)) return [];
41496
+ if (!existsSync41(destDir)) return [];
40212
41497
  const archived = new Set(entries.map((e) => e.path));
40213
41498
  const stale = [];
40214
41499
  walkSourceTree(destDir, (archivePath) => {
@@ -40235,7 +41520,7 @@ function writeConflicts(entries, destDir) {
40235
41520
  let dir = root;
40236
41521
  let stop = false;
40237
41522
  for (const [i, segment] of segments.slice(0, -1).entries()) {
40238
- dir = join37(dir, segment);
41523
+ dir = join38(dir, segment);
40239
41524
  const kind2 = pathKind(dir);
40240
41525
  if (!kind2 || kind2.isSymbolicLink()) {
40241
41526
  stop = true;
@@ -40247,7 +41532,7 @@ function writeConflicts(entries, destDir) {
40247
41532
  break;
40248
41533
  }
40249
41534
  if (stop) continue;
40250
- const kind = pathKind(join37(root, ...segments));
41535
+ const kind = pathKind(join38(root, ...segments));
40251
41536
  if (kind && !kind.isSymbolicLink() && kind.isDirectory()) {
40252
41537
  say(entry.path, "is a directory here, but the release has a file at that path");
40253
41538
  }
@@ -40257,22 +41542,22 @@ function writeConflicts(entries, destDir) {
40257
41542
  function removeStaleSource(entries, destDir) {
40258
41543
  const root = resolve36(destDir);
40259
41544
  const removed = staleSourcePaths(entries, root);
40260
- for (const rel of removed) rmSync7(join37(root, ...rel.split("/")), { force: true });
41545
+ for (const rel of removed) rmSync8(join38(root, ...rel.split("/")), { force: true });
40261
41546
  const needed = /* @__PURE__ */ new Set();
40262
41547
  for (const entry of entries) {
40263
41548
  if (isDeniedSourcePath(entry.path)) continue;
40264
41549
  const segments = entry.path.split("/");
40265
41550
  for (let i = 1; i < segments.length; i++) needed.add(segments.slice(0, i).join("/"));
40266
41551
  }
40267
- if (existsSync40(root)) pruneEmptyDirs(root, "", needed, removed);
41552
+ if (existsSync41(root)) pruneEmptyDirs(root, "", needed, removed);
40268
41553
  return removed.sort();
40269
41554
  }
40270
41555
  function pruneEmptyDirs(root, rel, needed, removed) {
40271
41556
  let empty = true;
40272
- for (const name of readdirSync21(rel ? join37(root, rel) : root)) {
40273
- const childRel = rel ? join37(rel, name) : name;
41557
+ for (const name of readdirSync22(rel ? join38(root, rel) : root)) {
41558
+ const childRel = rel ? join38(rel, name) : name;
40274
41559
  const posix3 = childRel.split(sep8).join("/");
40275
- const kind = lstatSync5(join37(root, childRel));
41560
+ const kind = lstatSync5(join38(root, childRel));
40276
41561
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name, rel)) {
40277
41562
  empty = false;
40278
41563
  continue;
@@ -40282,7 +41567,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
40282
41567
  empty = false;
40283
41568
  continue;
40284
41569
  }
40285
- rmdirSync(join37(root, childRel));
41570
+ rmdirSync(join38(root, childRel));
40286
41571
  removed.push(`${posix3}/`);
40287
41572
  }
40288
41573
  return empty;
@@ -40337,13 +41622,13 @@ __export(eject_exports, {
40337
41622
  EJECTABLE: () => EJECTABLE,
40338
41623
  ejectModule: () => ejectModule
40339
41624
  });
40340
- import { existsSync as existsSync41, mkdirSync as mkdirSync20, readFileSync as readFileSync45, writeFileSync as writeFileSync16 } from "node:fs";
40341
- import { dirname as dirname24, join as join38, relative as relative5, resolve as resolve37, sep as sep9 } from "node:path";
41625
+ import { existsSync as existsSync42, mkdirSync as mkdirSync21, readFileSync as readFileSync46, writeFileSync as writeFileSync17 } from "node:fs";
41626
+ import { dirname as dirname25, join as join39, relative as relative5, resolve as resolve37, sep as sep9 } from "node:path";
40342
41627
  function locate(repoRootOrCwd, mod) {
40343
41628
  const inRepo = resolve37(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
40344
- if (existsSync41(inRepo)) return { path: inRepo, form: "source" };
41629
+ if (existsSync42(inRepo)) return { path: inRepo, form: "source" };
40345
41630
  const dist = resolve37(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
40346
- if (existsSync41(dist)) return { path: dist, form: "bundle" };
41631
+ if (existsSync42(dist)) return { path: dist, form: "bundle" };
40347
41632
  return null;
40348
41633
  }
40349
41634
  function specifiersFor(mod) {
@@ -40367,8 +41652,8 @@ function ejectModule(opts) {
40367
41652
  };
40368
41653
  }
40369
41654
  const ext = found.form === "source" ? "ts" : "mjs";
40370
- const target = join38(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
40371
- mkdirSync20(dirname24(target), { recursive: true });
41655
+ const target = join39(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
41656
+ mkdirSync21(dirname25(target), { recursive: true });
40372
41657
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
40373
41658
  //
40374
41659
  // Central fixes to @wix/himalaya/${mod} NO LONGER REACH THIS APP \u2014 including any
@@ -40381,17 +41666,17 @@ function ejectModule(opts) {
40381
41666
  // gate still applies. Ejecting buys layout and flow, never the floor.
40382
41667
 
40383
41668
  `;
40384
- const normalized = readFileSync45(found.path, "utf8").replace(
41669
+ const normalized = readFileSync46(found.path, "utf8").replace(
40385
41670
  /(["'])@himalaya\/([a-z0-9-]+(?:\/[a-z0-9-]+)?)\1/g,
40386
41671
  (_m, q, sub) => `${q}@wix/himalaya/${sub}${q}`
40387
41672
  );
40388
- writeFileSync16(target, banner + normalized);
41673
+ writeFileSync17(target, banner + normalized);
40389
41674
  const rewired = [];
40390
41675
  for (const f of files) {
40391
- if (!existsSync41(f)) continue;
40392
- const before = readFileSync45(f, "utf8");
41676
+ if (!existsSync42(f)) continue;
41677
+ const before = readFileSync46(f, "utf8");
40393
41678
  let after = before;
40394
- let rel = relative5(dirname24(f), target).split(sep9).join("/").replace(/\.tsx?$/, ".js");
41679
+ let rel = relative5(dirname25(f), target).split(sep9).join("/").replace(/\.tsx?$/, ".js");
40395
41680
  if (!rel.startsWith(".")) rel = `./${rel}`;
40396
41681
  for (const spec of specifiersFor(mod)) {
40397
41682
  const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -40401,7 +41686,7 @@ function ejectModule(opts) {
40401
41686
  );
40402
41687
  }
40403
41688
  if (after !== before) {
40404
- writeFileSync16(f, after);
41689
+ writeFileSync17(f, after);
40405
41690
  rewired.push(f);
40406
41691
  }
40407
41692
  }
@@ -40437,14 +41722,14 @@ __export(test_exports, {
40437
41722
  scopeDescriptors: () => scopeDescriptors,
40438
41723
  writeRecordedMocks: () => writeRecordedMocks
40439
41724
  });
40440
- import { existsSync as existsSync42, mkdirSync as mkdirSync21, readFileSync as readFileSync46, writeFileSync as writeFileSync17 } from "node:fs";
40441
- import { join as join39, resolve as resolve38 } from "node:path";
41725
+ import { existsSync as existsSync43, mkdirSync as mkdirSync22, readFileSync as readFileSync47, writeFileSync as writeFileSync18 } from "node:fs";
41726
+ import { join as join40, resolve as resolve38 } from "node:path";
40442
41727
  function readTestConfig(dir) {
40443
41728
  const p = resolve38(dir, TEST_CONFIG_FILE);
40444
- if (!existsSync42(p)) return {};
41729
+ if (!existsSync43(p)) return {};
40445
41730
  let parsed;
40446
41731
  try {
40447
- parsed = JSON.parse(readFileSync46(p, "utf8"));
41732
+ parsed = JSON.parse(readFileSync47(p, "utf8"));
40448
41733
  } catch (e) {
40449
41734
  throw new Error(`${TEST_CONFIG_FILE} is not valid JSON \u2014 ${e.message}`);
40450
41735
  }
@@ -40487,7 +41772,7 @@ function readTestConfig(dir) {
40487
41772
  function readNetRules(path2) {
40488
41773
  let parsed;
40489
41774
  try {
40490
- parsed = JSON.parse(readFileSync46(path2, "utf8"));
41775
+ parsed = JSON.parse(readFileSync47(path2, "utf8"));
40491
41776
  } catch (e) {
40492
41777
  throw new Error(`--net ${path2} could not be read as JSON \u2014 ${e.message}`);
40493
41778
  }
@@ -40508,7 +41793,7 @@ function readNetRules(path2) {
40508
41793
  function defaultNetRules(dir) {
40509
41794
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
40510
41795
  const p = resolve38(dir, rel);
40511
- if (existsSync42(p)) {
41796
+ if (existsSync43(p)) {
40512
41797
  const rules = readNetRules(p);
40513
41798
  if (rules.length) return { rules, from: rel };
40514
41799
  }
@@ -40524,7 +41809,7 @@ function readCannedResponses() {
40524
41809
  ];
40525
41810
  for (const url of candidates) {
40526
41811
  try {
40527
- const parsed = JSON.parse(readFileSync46(url, "utf8"));
41812
+ const parsed = JSON.parse(readFileSync47(url, "utf8"));
40528
41813
  const byKey = {};
40529
41814
  for (const [k, v] of Object.entries(parsed)) {
40530
41815
  if (!k.startsWith("//")) byKey[k] = v;
@@ -40613,11 +41898,11 @@ function writeRecordedMocks(opts) {
40613
41898
  };
40614
41899
  }
40615
41900
  const fs = opts.io ?? {
40616
- mkdir: (p) => mkdirSync21(p, { recursive: true }),
40617
- write: (p, s) => writeFileSync17(p, s)
41901
+ mkdir: (p) => mkdirSync22(p, { recursive: true }),
41902
+ write: (p, s) => writeFileSync18(p, s)
40618
41903
  };
40619
- fs.mkdir(join39(opts.dir, "dev"));
40620
- fs.write(join39(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
41904
+ fs.mkdir(join40(opts.dir, "dev"));
41905
+ fs.write(join40(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
40621
41906
  return { ok: true, rules, to: "dev/net-mocks.json" };
40622
41907
  }
40623
41908
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -41074,14 +42359,14 @@ var mobile_ux_lint_exports = {};
41074
42359
  __export(mobile_ux_lint_exports, {
41075
42360
  mobileUxIssues: () => mobileUxIssues
41076
42361
  });
41077
- import { existsSync as existsSync43, readFileSync as readFileSync47 } from "node:fs";
41078
- import { join as join40 } from "node:path";
42362
+ import { existsSync as existsSync44, readFileSync as readFileSync48 } from "node:fs";
42363
+ import { join as join41 } from "node:path";
41079
42364
  function mobileUxIssues(dir, strict) {
41080
- const file = join40(dir, "MOBILE-UX.md");
41081
- if (!existsSync43(file)) return [];
42365
+ const file = join41(dir, "MOBILE-UX.md");
42366
+ if (!existsSync44(file)) return [];
41082
42367
  let text4;
41083
42368
  try {
41084
- text4 = readFileSync47(file, "utf8");
42369
+ text4 = readFileSync48(file, "utf8");
41085
42370
  } catch {
41086
42371
  return [];
41087
42372
  }
@@ -42756,8 +44041,8 @@ __export(functions_exports, {
42756
44041
  resolveFunctionsTarget: () => resolveFunctionsTarget,
42757
44042
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
42758
44043
  });
42759
- import { readFileSync as readFileSync48, existsSync as existsSync44 } from "node:fs";
42760
- import { join as join41, resolve as resolve39 } from "node:path";
44044
+ import { readFileSync as readFileSync49, existsSync as existsSync45 } from "node:fs";
44045
+ import { join as join42, resolve as resolve39 } from "node:path";
42761
44046
  function resolveFunctionsTarget(input) {
42762
44047
  const explicitUrl = input.functionsUrl?.trim();
42763
44048
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -42776,9 +44061,9 @@ async function loadFunctionsApp(dir) {
42776
44061
  }
42777
44062
  const sources = {};
42778
44063
  for (const definition of config.functions.functions) {
42779
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join41(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync44);
44064
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join42(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync45);
42780
44065
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
42781
- sources[definition.name] = readFileSync48(source, "utf8");
44066
+ sources[definition.name] = readFileSync49(source, "utf8");
42782
44067
  }
42783
44068
  const data = config.cms || config.data ? mergeDataResources(config) : void 0;
42784
44069
  const dataErrors = validateDataResources(data, loaded.name).filter((issue2) => issue2.severity === "error");
@@ -42933,9 +44218,9 @@ __export(native_decode_exports, {
42933
44218
  runNativeDecode: () => runNativeDecode
42934
44219
  });
42935
44220
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
42936
- import { existsSync as existsSync45, mkdtempSync as mkdtempSync2, readFileSync as readFileSync49, rmSync as rmSync8, writeFileSync as writeFileSync18 } from "node:fs";
44221
+ import { existsSync as existsSync46, mkdtempSync as mkdtempSync2, readFileSync as readFileSync50, rmSync as rmSync9, writeFileSync as writeFileSync19 } from "node:fs";
42937
44222
  import { tmpdir as tmpdir3 } from "node:os";
42938
- import { join as join42 } from "node:path";
44223
+ import { join as join43 } from "node:path";
42939
44224
  import { fileURLToPath as fileURLToPath20 } from "node:url";
42940
44225
  function nativeDecodePackageDir() {
42941
44226
  return fileURLToPath20(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -42945,8 +44230,8 @@ function nativeDecodeAndroidHarnessDir() {
42945
44230
  }
42946
44231
  function nativeOracleShipped() {
42947
44232
  return {
42948
- ios: existsSync45(join42(nativeDecodePackageDir(), "Package.swift")),
42949
- android: existsSync45(join42(nativeDecodeAndroidHarnessDir(), "gradlew"))
44233
+ ios: existsSync46(join43(nativeDecodePackageDir(), "Package.swift")),
44234
+ android: existsSync46(join43(nativeDecodeAndroidHarnessDir(), "gradlew"))
42950
44235
  };
42951
44236
  }
42952
44237
  function detectNativeDecode(options = {}) {
@@ -42957,7 +44242,7 @@ function detectNativeDecode(options = {}) {
42957
44242
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
42958
44243
  }
42959
44244
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
42960
- const packageAvailable = options.packageAvailable ?? existsSync45(join42(packageDir, "Package.swift"));
44245
+ const packageAvailable = options.packageAvailable ?? existsSync46(join43(packageDir, "Package.swift"));
42961
44246
  if (!packageAvailable) {
42962
44247
  return {
42963
44248
  platform: "ios",
@@ -42969,7 +44254,7 @@ function detectNativeDecode(options = {}) {
42969
44254
  }
42970
44255
  function localPropertiesSdk(harnessDir) {
42971
44256
  try {
42972
- const match = readFileSync49(join42(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
44257
+ const match = readFileSync50(join43(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
42973
44258
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
42974
44259
  } catch {
42975
44260
  return null;
@@ -42977,8 +44262,8 @@ function localPropertiesSdk(harnessDir) {
42977
44262
  }
42978
44263
  function detectAndroidNativeDecode(options = {}) {
42979
44264
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
42980
- const gradlew = join42(harnessDir, "gradlew");
42981
- const gradlewAvailable = options.gradlewAvailable ?? existsSync45(gradlew);
44265
+ const gradlew = join43(harnessDir, "gradlew");
44266
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync46(gradlew);
42982
44267
  if (!gradlewAvailable) {
42983
44268
  return {
42984
44269
  platform: "android",
@@ -42988,7 +44273,7 @@ function detectAndroidNativeDecode(options = {}) {
42988
44273
  }
42989
44274
  const androidHome = options.androidHome ?? process.env.ANDROID_HOME ?? "";
42990
44275
  const sdk = androidHome.trim() || (options.localPropertiesSdk !== void 0 ? options.localPropertiesSdk : localPropertiesSdk(harnessDir));
42991
- const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync45(sdk));
44276
+ const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync46(sdk));
42992
44277
  if (!sdkAvailable) {
42993
44278
  return {
42994
44279
  platform: "android",
@@ -43060,14 +44345,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
43060
44345
  return verdicts;
43061
44346
  }
43062
44347
  function runNativeDecode(options) {
43063
- const descriptorDir = mkdtempSync2(join42(tmpdir3(), "himi-native-decode-"));
44348
+ const descriptorDir = mkdtempSync2(join43(tmpdir3(), "himi-native-decode-"));
43064
44349
  const startedAt = Date.now();
43065
44350
  try {
43066
44351
  for (const descriptor of options.descriptors) {
43067
44352
  if (typeof descriptor.descriptorJson !== "string") {
43068
44353
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
43069
44354
  }
43070
- writeFileSync18(join42(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
44355
+ writeFileSync19(join43(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
43071
44356
  }
43072
44357
  options.progress?.("building cached Swift oracle");
43073
44358
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -43079,7 +44364,7 @@ function runNativeDecode(options) {
43079
44364
  encoding: "utf8"
43080
44365
  }).trim();
43081
44366
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
43082
- const stdout = execFileSync5(join42(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
44367
+ const stdout = execFileSync5(join43(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
43083
44368
  stdio: ["ignore", "pipe", "pipe"],
43084
44369
  encoding: "utf8"
43085
44370
  });
@@ -43093,19 +44378,19 @@ function runNativeDecode(options) {
43093
44378
  const stderr = failure.stderr ? ` \u2014 ${String(failure.stderr).trim().slice(0, 2e3)}` : "";
43094
44379
  throw new Error(`iOS decoder oracle failed: ${failure.message}${stderr}`);
43095
44380
  } finally {
43096
- rmSync8(descriptorDir, { recursive: true, force: true });
44381
+ rmSync9(descriptorDir, { recursive: true, force: true });
43097
44382
  }
43098
44383
  }
43099
44384
  function runAndroidNativeDecode(options) {
43100
- const descriptorDir = mkdtempSync2(join42(tmpdir3(), "himi-native-decode-android-"));
43101
- const outputPath = join42(descriptorDir, "verdicts.ndjson");
44385
+ const descriptorDir = mkdtempSync2(join43(tmpdir3(), "himi-native-decode-android-"));
44386
+ const outputPath = join43(descriptorDir, "verdicts.ndjson");
43102
44387
  const startedAt = Date.now();
43103
44388
  try {
43104
44389
  for (const descriptor of options.descriptors) {
43105
44390
  if (typeof descriptor.descriptorJson !== "string") {
43106
44391
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
43107
44392
  }
43108
- writeFileSync18(join42(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
44393
+ writeFileSync19(join43(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
43109
44394
  }
43110
44395
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
43111
44396
  execFileSync5(options.gradlew, [
@@ -43120,7 +44405,7 @@ function runAndroidNativeDecode(options) {
43120
44405
  stdio: ["ignore", "ignore", "pipe"],
43121
44406
  encoding: "utf8"
43122
44407
  });
43123
- const stdout = readFileSync49(outputPath, "utf8");
44408
+ const stdout = readFileSync50(outputPath, "utf8");
43124
44409
  return {
43125
44410
  platform: "android",
43126
44411
  verdicts: parseNativeDecodeOutput(stdout, options.descriptors.map((descriptor) => descriptor.screenId), "android"),
@@ -43131,7 +44416,7 @@ function runAndroidNativeDecode(options) {
43131
44416
  const stderr = failure.stderr ? ` \u2014 ${String(failure.stderr).trim().slice(0, 2e3)}` : "";
43132
44417
  throw new Error(`Android decoder oracle failed: ${failure.message}${stderr}`);
43133
44418
  } finally {
43134
- rmSync8(descriptorDir, { recursive: true, force: true });
44419
+ rmSync9(descriptorDir, { recursive: true, force: true });
43135
44420
  }
43136
44421
  }
43137
44422
  function fieldAt(path2) {
@@ -43191,30 +44476,30 @@ __export(browser_authoring_exports, {
43191
44476
  import { createServer as createServer3 } from "node:http";
43192
44477
  import { createHash as createHash22, randomBytes as randomBytes4 } from "node:crypto";
43193
44478
  import { execFile as execFile2 } from "node:child_process";
43194
- import { existsSync as existsSync46, readdirSync as readdirSync22, readFileSync as readFileSync50, realpathSync as realpathSync4, statSync as statSync9 } from "node:fs";
43195
- import { join as join43, posix, relative as relative6, sep as sep10 } from "node:path";
44479
+ import { existsSync as existsSync47, readdirSync as readdirSync23, readFileSync as readFileSync51, realpathSync as realpathSync4, statSync as statSync10 } from "node:fs";
44480
+ import { join as join44, posix, relative as relative6, sep as sep10 } from "node:path";
43196
44481
  function humanMs(ms) {
43197
44482
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
43198
44483
  }
43199
44484
  function collectWorkerSources(tier5SrcDir, bundleName) {
43200
- const root = join43(tier5SrcDir, bundleName);
44485
+ const root = join44(tier5SrcDir, bundleName);
43201
44486
  const files = {};
43202
44487
  const visitedDirs = /* @__PURE__ */ new Set();
43203
44488
  const walk2 = (abs, rel) => {
43204
44489
  const real = realpathSync4(abs);
43205
44490
  if (visitedDirs.has(real)) return;
43206
44491
  visitedDirs.add(real);
43207
- for (const name of readdirSync22(abs).sort()) {
43208
- const childAbs = join43(abs, name);
44492
+ for (const name of readdirSync23(abs).sort()) {
44493
+ const childAbs = join44(abs, name);
43209
44494
  const childRel = posix.join(rel, name);
43210
- if (statSync9(childAbs).isDirectory()) {
44495
+ if (statSync10(childAbs).isDirectory()) {
43211
44496
  walk2(childAbs, childRel);
43212
44497
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
43213
- files[childRel] = readFileSync50(childAbs, "utf8");
44498
+ files[childRel] = readFileSync51(childAbs, "utf8");
43214
44499
  }
43215
44500
  }
43216
44501
  };
43217
- if (!existsSync46(root)) return null;
44502
+ if (!existsSync47(root)) return null;
43218
44503
  walk2(root, `/tier5-src/${bundleName}`);
43219
44504
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p) => p in files);
43220
44505
  if (!entry) return null;
@@ -43231,20 +44516,20 @@ function collectRelativeSiblings(files, tier5SrcDir) {
43231
44516
  const spec = m[1];
43232
44517
  const virtual = posix.normalize(posix.join(dir, spec));
43233
44518
  if (!virtual.startsWith("/tier5-src/")) continue;
43234
- const onDisk = join43(tier5SrcDir, virtual.slice("/tier5-src/".length));
44519
+ const onDisk = join44(tier5SrcDir, virtual.slice("/tier5-src/".length));
43235
44520
  const candidates = [
43236
44521
  onDisk,
43237
44522
  `${onDisk}.ts`,
43238
44523
  `${onDisk}.js`,
43239
44524
  onDisk.replace(/\.js$/, ".ts"),
43240
- join43(onDisk, "index.ts"),
43241
- join43(onDisk, "index.js")
44525
+ join44(onDisk, "index.ts"),
44526
+ join44(onDisk, "index.js")
43242
44527
  ];
43243
44528
  for (const abs of candidates) {
43244
- if (!existsSync46(abs) || statSync9(abs).isDirectory()) continue;
44529
+ if (!existsSync47(abs) || statSync10(abs).isDirectory()) continue;
43245
44530
  const key2 = `/tier5-src/${relative6(tier5SrcDir, abs).split(sep10).join(posix.sep)}`;
43246
44531
  if (key2 in files) break;
43247
- files[key2] = readFileSync50(abs, "utf8");
44532
+ files[key2] = readFileSync51(abs, "utf8");
43248
44533
  queue.push(key2);
43249
44534
  break;
43250
44535
  }
@@ -43279,7 +44564,7 @@ function collectSdkModules(files, resolveSubpath) {
43279
44564
  function sdkSubpathReader(resolve45) {
43280
44565
  return (sub) => {
43281
44566
  try {
43282
- return readFileSync50(resolve45(`@wix/himalaya/${sub}`), "utf8");
44567
+ return readFileSync51(resolve45(`@wix/himalaya/${sub}`), "utf8");
43283
44568
  } catch {
43284
44569
  return null;
43285
44570
  }
@@ -43453,8 +44738,8 @@ __export(coverage_exports, {
43453
44738
  suppressedNativeKeys: () => suppressedNativeKeys,
43454
44739
  trendAndStore: () => trendAndStore
43455
44740
  });
43456
- import { existsSync as existsSync47, mkdirSync as mkdirSync22, readFileSync as readFileSync51, writeFileSync as writeFileSync19 } from "node:fs";
43457
- import { dirname as dirname25, join as join44 } from "node:path";
44741
+ import { existsSync as existsSync48, mkdirSync as mkdirSync23, readFileSync as readFileSync52, writeFileSync as writeFileSync20 } from "node:fs";
44742
+ import { dirname as dirname26, join as join45 } from "node:path";
43458
44743
  function suppressedNativeKeys(oracle) {
43459
44744
  return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
43460
44745
  }
@@ -43624,11 +44909,11 @@ function assembleLedger(r, options = {}) {
43624
44909
  return out;
43625
44910
  }
43626
44911
  function trendAndStore(dir, entries, suppressed) {
43627
- const p = join44(dir, LEDGER_FILE);
44912
+ const p = join45(dir, LEDGER_FILE);
43628
44913
  let previous = null;
43629
44914
  try {
43630
- if (existsSync47(p)) {
43631
- const parsed = JSON.parse(readFileSync51(p, "utf8"));
44915
+ if (existsSync48(p)) {
44916
+ const parsed = JSON.parse(readFileSync52(p, "utf8"));
43632
44917
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k) => typeof k === "string");
43633
44918
  }
43634
44919
  } catch {
@@ -43636,8 +44921,8 @@ function trendAndStore(dir, entries, suppressed) {
43636
44921
  const current = entries.map((e) => e.key);
43637
44922
  const stored = [.../* @__PURE__ */ new Set([...current, ...(previous ?? []).filter((key2) => (suppressed ?? []).includes(key2))])];
43638
44923
  try {
43639
- mkdirSync22(dirname25(p), { recursive: true });
43640
- writeFileSync19(p, JSON.stringify({ keys: stored, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
44924
+ mkdirSync23(dirname26(p), { recursive: true });
44925
+ writeFileSync20(p, JSON.stringify({ keys: stored, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
43641
44926
  } catch {
43642
44927
  return null;
43643
44928
  }
@@ -43667,11 +44952,11 @@ function formatLedger(entries, trend) {
43667
44952
  return out.join("\n");
43668
44953
  }
43669
44954
  function diffAndStoreObservations(dir, current) {
43670
- const p = join44(dir, OBS_FILE);
44955
+ const p = join45(dir, OBS_FILE);
43671
44956
  let previous = null;
43672
44957
  try {
43673
- if (existsSync47(p)) {
43674
- const parsed = JSON.parse(readFileSync51(p, "utf8"));
44958
+ if (existsSync48(p)) {
44959
+ const parsed = JSON.parse(readFileSync52(p, "utf8"));
43675
44960
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
43676
44961
  (v) => Array.isArray(v) && v.every((l) => typeof l === "string")
43677
44962
  )) {
@@ -43681,8 +44966,8 @@ function diffAndStoreObservations(dir, current) {
43681
44966
  } catch {
43682
44967
  }
43683
44968
  try {
43684
- mkdirSync22(dirname25(p), { recursive: true });
43685
- writeFileSync19(p, JSON.stringify({ screens: current, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
44969
+ mkdirSync23(dirname26(p), { recursive: true });
44970
+ writeFileSync20(p, JSON.stringify({ screens: current, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
43686
44971
  } catch {
43687
44972
  return null;
43688
44973
  }
@@ -43720,8 +45005,8 @@ var LEDGER_FILE, OBS_FILE;
43720
45005
  var init_coverage = __esm({
43721
45006
  "src/coverage.ts"() {
43722
45007
  init_native_decode();
43723
- LEDGER_FILE = join44(".himi", "test-ledger.json");
43724
- OBS_FILE = join44(".himi", "test-observations.json");
45008
+ LEDGER_FILE = join45(".himi", "test-ledger.json");
45009
+ OBS_FILE = join45(".himi", "test-observations.json");
43725
45010
  }
43726
45011
  });
43727
45012
 
@@ -43732,16 +45017,16 @@ __export(design_kits_exports, {
43732
45017
  kitsDir: () => kitsDir,
43733
45018
  listKits: () => listKits2
43734
45019
  });
43735
- import { existsSync as existsSync48, readFileSync as readFileSync52, readdirSync as readdirSync23 } from "node:fs";
43736
- import { join as join45, basename as basename10 } from "node:path";
45020
+ import { existsSync as existsSync49, readFileSync as readFileSync53, readdirSync as readdirSync24 } from "node:fs";
45021
+ import { join as join46, basename as basename10 } from "node:path";
43737
45022
  import { fileURLToPath as fileURLToPath21 } from "node:url";
43738
45023
  function kitsDir() {
43739
- return KIT_DIR_CANDIDATES.find((p) => existsSync48(p));
45024
+ return KIT_DIR_CANDIDATES.find((p) => existsSync49(p));
43740
45025
  }
43741
45026
  function listKits2() {
43742
45027
  const dir = kitsDir();
43743
45028
  if (!dir) return [];
43744
- return readdirSync23(dir).filter((f) => f.endsWith(".json")).map((f) => basename10(f, ".json")).sort();
45029
+ return readdirSync24(dir).filter((f) => f.endsWith(".json")).map((f) => basename10(f, ".json")).sort();
43745
45030
  }
43746
45031
  function kitReport(name) {
43747
45032
  const dir = kitsDir();
@@ -43760,7 +45045,7 @@ In a published @wix/himalaya-cli this is a packaging bug \u2014 the kits should
43760
45045
  if (!kits.includes(name)) {
43761
45046
  return { ok: false, kit: name, kits, problems: [`no design kit named "${name}". Available: ${kits.join(", ")}`] };
43762
45047
  }
43763
- const raw = readFileSync52(join45(dir, `${name}.json`), "utf8");
45048
+ const raw = readFileSync53(join46(dir, `${name}.json`), "utf8");
43764
45049
  let parsed;
43765
45050
  try {
43766
45051
  parsed = JSON.parse(raw);
@@ -43784,9 +45069,9 @@ var init_design_kits = __esm({
43784
45069
  "src/design-kits.ts"() {
43785
45070
  KIT_DIR_CANDIDATES = [
43786
45071
  // in-repo: tools/himi-cli/src/design-kits.ts → <repo>/stdlib/design-kits/
43787
- join45(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
45072
+ join46(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
43788
45073
  // published CLI: <pkg>/dist/cli.mjs → <pkg>/stdlib/design-kits/
43789
- join45(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
45074
+ join46(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
43790
45075
  ];
43791
45076
  }
43792
45077
  });
@@ -43800,11 +45085,11 @@ __export(assets_exports, {
43800
45085
  removeAssetPayload: () => removeAssetPayload,
43801
45086
  verifyReport: () => verifyReport
43802
45087
  });
43803
- import { readFileSync as readFileSync53 } from "node:fs";
45088
+ import { readFileSync as readFileSync54 } from "node:fs";
43804
45089
  import { basename as basename11 } from "node:path";
43805
45090
  function declaredAssets(dir) {
43806
45091
  return collectAssets(dir).map((a) => {
43807
- const bytes2 = readFileSync53(a.path);
45092
+ const bytes2 = readFileSync54(a.path);
43808
45093
  return {
43809
45094
  name: a.name,
43810
45095
  sha256: assetSha256(bytes2),
@@ -43881,7 +45166,7 @@ function verifyReport(dir, app, manifest) {
43881
45166
  };
43882
45167
  }
43883
45168
  function addAssetPayload(manifest, filePath, opts) {
43884
- const bytes2 = readFileSync53(filePath);
45169
+ const bytes2 = readFileSync54(filePath);
43885
45170
  const name = opts.name ?? basename11(filePath);
43886
45171
  const sha2565 = assetSha256(bytes2);
43887
45172
  const record2 = {
@@ -45357,19 +46642,19 @@ var init_preview_assets_generated = __esm({
45357
46642
  "../../packages/serve/src/preview-assets.generated.ts"() {
45358
46643
  init_preview_assets_format();
45359
46644
  PREVIEW_ASSET_MANIFEST = [
45360
- { key: "assets/index-BpZXVPIV.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1012137, sha256: "8d4244200f95528ecbe4b96cad3de8c238d257b4010a578191223445a5fa3c29" },
45361
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1012145, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
45362
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1103069, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
45363
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1194053, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
45364
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1285033, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
45365
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1375905, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
45366
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1466885, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
45367
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 1575521, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
45368
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 1684605, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
45369
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 1793653, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
45370
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 1901353, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
45371
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 1989461, length: 4448, sha256: "95a85e0c6551f6ce184bac040d790ba35ade87d66869827925898126e2398a40" },
45372
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 1993909, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
46645
+ { key: "assets/index-BKft9wm1.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1012166, sha256: "3a87519baabf67cecf163d20ea1eaaeba288fdeab2fc00cc3d22f09b6a29a194" },
46646
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1012174, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
46647
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1103098, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
46648
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1194082, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
46649
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1285062, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
46650
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1375934, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
46651
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1466914, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
46652
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 1575550, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
46653
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 1684634, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
46654
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 1793682, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
46655
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 1901382, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
46656
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 1989490, length: 4448, sha256: "71627ade6c0e47a8956b4841c1f6d644fee50a9a4ca9e6d9106b589e84891bd7" },
46657
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 1993938, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
45373
46658
  ];
45374
46659
  }
45375
46660
  });
@@ -45385,9 +46670,9 @@ __export(run_exports, {
45385
46670
  runPackage: () => runPackage,
45386
46671
  withoutProdServe: () => withoutProdServe
45387
46672
  });
45388
- import { cpSync, existsSync as existsSync49, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync3, readFileSync as readFileSync54, rmSync as rmSync9, statSync as statSync10 } from "node:fs";
46673
+ import { cpSync as cpSync2, existsSync as existsSync50, mkdirSync as mkdirSync24, mkdtempSync as mkdtempSync3, readFileSync as readFileSync55, rmSync as rmSync10, statSync as statSync11 } from "node:fs";
45389
46674
  import { tmpdir as tmpdir4 } from "node:os";
45390
- import { dirname as dirname26, join as join46 } from "node:path";
46675
+ import { dirname as dirname27, join as join47 } from "node:path";
45391
46676
  import { fileURLToPath as fileURLToPath22 } from "node:url";
45392
46677
  import { spawn as spawn3 } from "node:child_process";
45393
46678
  function makePackageRunHandler(opts) {
@@ -45406,9 +46691,9 @@ async function fetchBytes(url) {
45406
46691
  }
45407
46692
  async function resolvePackage(source, workDir) {
45408
46693
  const isUrl2 = /^https?:\/\//i.test(source);
45409
- if (!isUrl2 && existsSync49(source) && statSync10(source).isDirectory()) {
45410
- const appDirManifest = join46(source, "release-manifest.json");
45411
- if (existsSync49(appDirManifest)) {
46694
+ if (!isUrl2 && existsSync50(source) && statSync11(source).isDirectory()) {
46695
+ const appDirManifest = join47(source, "release-manifest.json");
46696
+ if (existsSync50(appDirManifest)) {
45412
46697
  const opened2 = openPackageDir(source);
45413
46698
  if (!opened2.validation.ok) {
45414
46699
  throw new Error(`package is not valid:
@@ -45417,12 +46702,12 @@ async function resolvePackage(source, workDir) {
45417
46702
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
45418
46703
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
45419
46704
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
45420
- const root2 = workDir ?? mkdtempSync3(join46(tmpdir4(), "himi-run-"));
45421
- const target = join46(root2, app2);
45422
- if (join46(source) !== target) {
45423
- mkdirSync23(root2, { recursive: true });
45424
- rmSync9(target, { recursive: true, force: true });
45425
- cpSync(source, target, { recursive: true });
46705
+ const root2 = workDir ?? mkdtempSync3(join47(tmpdir4(), "himi-run-"));
46706
+ const target = join47(root2, app2);
46707
+ if (join47(source) !== target) {
46708
+ mkdirSync24(root2, { recursive: true });
46709
+ rmSync10(target, { recursive: true, force: true });
46710
+ cpSync2(source, target, { recursive: true });
45426
46711
  }
45427
46712
  return {
45428
46713
  releaseRoot: root2,
@@ -45436,12 +46721,12 @@ async function resolvePackage(source, workDir) {
45436
46721
  }
45437
46722
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
45438
46723
  }
45439
- const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync54(source);
45440
- const root = workDir ?? mkdtempSync3(join46(tmpdir4(), "himi-run-"));
46724
+ const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync55(source);
46725
+ const root = workDir ?? mkdtempSync3(join47(tmpdir4(), "himi-run-"));
45441
46726
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
45442
46727
  const opened = openPackageDir(appDir2);
45443
46728
  if (!opened.validation.ok) {
45444
- rmSync9(workDir ? appDir2 : root, { recursive: true, force: true });
46729
+ rmSync10(workDir ? appDir2 : root, { recursive: true, force: true });
45445
46730
  throw new Error(`package is not valid:
45446
46731
  ${opened.validation.errors.join("\n ")}`);
45447
46732
  }
@@ -45458,16 +46743,16 @@ async function resolvePackage(source, workDir) {
45458
46743
  async function loadPreviewAssets() {
45459
46744
  try {
45460
46745
  const mod = await Promise.resolve().then(() => (init_preview_assets_generated(), preview_assets_generated_exports));
45461
- const here = dirname26(fileURLToPath22(import.meta.url));
46746
+ const here = dirname27(fileURLToPath22(import.meta.url));
45462
46747
  const candidates = [
45463
- join46(here, "preview-assets.generated.bin"),
46748
+ join47(here, "preview-assets.generated.bin"),
45464
46749
  // published: copied next to the bundled CLI
45465
- join46(here, "../../../packages/serve/src/preview-assets.generated.bin")
46750
+ join47(here, "../../../packages/serve/src/preview-assets.generated.bin")
45466
46751
  // in-repo checkout
45467
46752
  ];
45468
46753
  for (const path2 of candidates) {
45469
- if (!existsSync49(path2)) continue;
45470
- const blob = readFileSync54(path2);
46754
+ if (!existsSync50(path2)) continue;
46755
+ const blob = readFileSync55(path2);
45471
46756
  return mod.assemblePreviewAssetsFromBlob(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
45472
46757
  }
45473
46758
  return /* @__PURE__ */ new Map();
@@ -45492,7 +46777,7 @@ async function runPackage(source, opts = {}) {
45492
46777
  const resolved2 = await resolvePackage(source, opts.workDir);
45493
46778
  const assets = opts.assets ?? (opts.spa ? /* @__PURE__ */ new Map() : await loadPreviewAssets());
45494
46779
  if (!opts.spa && assets.size === 0) {
45495
- if (resolved2.temp) rmSync9(resolved2.temp, { recursive: true, force: true });
46780
+ if (resolved2.temp) rmSync10(resolved2.temp, { recursive: true, force: true });
45496
46781
  throw new Error(NO_SPA_MESSAGE);
45497
46782
  }
45498
46783
  const handler = makePackageRunHandler({
@@ -45522,7 +46807,7 @@ async function runPackage(source, opts = {}) {
45522
46807
  warnings: resolved2.warnings,
45523
46808
  close: () => new Promise((done) => {
45524
46809
  server.close(() => {
45525
- if (resolved2.temp) rmSync9(resolved2.temp, { recursive: true, force: true });
46810
+ if (resolved2.temp) rmSync10(resolved2.temp, { recursive: true, force: true });
45526
46811
  done();
45527
46812
  });
45528
46813
  })
@@ -45549,15 +46834,15 @@ __export(run_browser_exports, {
45549
46834
  runInBrowser: () => runInBrowser
45550
46835
  });
45551
46836
  import { createServer as createServer5 } from "node:http";
45552
- import { existsSync as existsSync50, readFileSync as readFileSync55 } from "node:fs";
46837
+ import { existsSync as existsSync51, readFileSync as readFileSync56 } from "node:fs";
45553
46838
  import { spawnSync as spawnSync3, execFile as execFile3 } from "node:child_process";
45554
- import { dirname as dirname27, resolve as resolve40 } from "node:path";
46839
+ import { dirname as dirname28, resolve as resolve40 } from "node:path";
45555
46840
  import { fileURLToPath as fileURLToPath23 } from "node:url";
45556
46841
  function stagedDir() {
45557
46842
  return process.env.HIMI_BROWSER_PLANE_OUT ? resolve40(process.env.HIMI_BROWSER_PLANE_OUT) : resolve40(REPO, "packages", "serve", "dist", "browser-plane");
45558
46843
  }
45559
46844
  function missingStaged() {
45560
- return [...SERVED].filter((f) => !existsSync50(resolve40(stagedDir(), f))).sort();
46845
+ return [...SERVED].filter((f) => !existsSync51(resolve40(stagedDir(), f))).sort();
45561
46846
  }
45562
46847
  function ensureBrowserPlaneStaged() {
45563
46848
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
@@ -45647,7 +46932,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
45647
46932
  }
45648
46933
  const name = path2 === "/" || path2 === "/index.html" ? "host.html" : path2.slice(1);
45649
46934
  if (SERVED.has(name)) {
45650
- if (!existsSync50(resolve40(stagedDir(), name))) {
46935
+ if (!existsSync51(resolve40(stagedDir(), name))) {
45651
46936
  const restaged = ensureBrowserPlaneStaged();
45652
46937
  if (!restaged.ok) {
45653
46938
  res.writeHead(503, { "content-type": "application/json" });
@@ -45657,7 +46942,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
45657
46942
  }
45658
46943
  let bytes2;
45659
46944
  try {
45660
- bytes2 = readFileSync55(resolve40(stagedDir(), name));
46945
+ bytes2 = readFileSync56(resolve40(stagedDir(), name));
45661
46946
  } catch (e) {
45662
46947
  res.writeHead(503, { "content-type": "application/json" });
45663
46948
  res.end(JSON.stringify({ error: "browser_plane_unstaged", file: name, detail: String(e) }));
@@ -45700,7 +46985,7 @@ var HERE, REPO, SERVED;
45700
46985
  var init_run_browser = __esm({
45701
46986
  "src/run-browser.ts"() {
45702
46987
  init_file_store();
45703
- HERE = dirname27(fileURLToPath23(import.meta.url));
46988
+ HERE = dirname28(fileURLToPath23(import.meta.url));
45704
46989
  REPO = resolve40(HERE, "..", "..", "..");
45705
46990
  SERVED = /* @__PURE__ */ new Set(["host.html", "sw.js", "package-reader.js"]);
45706
46991
  }
@@ -46120,7 +47405,7 @@ __export(preflight_exports, {
46120
47405
  parseStoreConfig: () => parseStoreConfig,
46121
47406
  requireEnv: () => requireEnv
46122
47407
  });
46123
- import { existsSync as existsSync51, readFileSync as readFileSync56 } from "node:fs";
47408
+ import { existsSync as existsSync52, readFileSync as readFileSync57 } from "node:fs";
46124
47409
  import { isAbsolute as isAbsolute3, relative as relative7, resolve as resolve41 } from "node:path";
46125
47410
  function isInsideRepo(p) {
46126
47411
  const rel = relative7(REPO_ROOT3, resolve41(p));
@@ -46177,10 +47462,10 @@ function parseStoreConfig(text4) {
46177
47462
  }
46178
47463
  function loadStoreConfig(app) {
46179
47464
  const p = storeConfigPath(app);
46180
- if (!existsSync51(p)) {
47465
+ if (!existsSync52(p)) {
46181
47466
  throw new Error(`No store config at ${p}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
46182
47467
  }
46183
- return parseStoreConfig(readFileSync56(p, "utf8"));
47468
+ return parseStoreConfig(readFileSync57(p, "utf8"));
46184
47469
  }
46185
47470
  var init_preflight = __esm({
46186
47471
  "../store-release/preflight.ts"() {
@@ -46199,19 +47484,19 @@ __export(credential_profile_exports, {
46199
47484
  profilePath: () => profilePath,
46200
47485
  resolveStoreCredentials: () => resolveStoreCredentials
46201
47486
  });
46202
- import { existsSync as existsSync52, readFileSync as readFileSync57 } from "node:fs";
47487
+ import { existsSync as existsSync53, readFileSync as readFileSync58 } from "node:fs";
46203
47488
  import { homedir as homedir5 } from "node:os";
46204
- import { join as join47 } from "node:path";
47489
+ import { join as join48 } from "node:path";
46205
47490
  function credentialsDir(home = homedir5()) {
46206
- return join47(home, ".himalaya", "store-credentials");
47491
+ return join48(home, ".himalaya", "store-credentials");
46207
47492
  }
46208
47493
  function profilePath(name, home = homedir5()) {
46209
47494
  if (!PROFILE_NAME.test(name)) {
46210
47495
  throw new Error(
46211
- `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join47(credentialsDir(home), "<name>.json")}; it is never a path.`
47496
+ `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join48(credentialsDir(home), "<name>.json")}; it is never a path.`
46212
47497
  );
46213
47498
  }
46214
- return join47(credentialsDir(home), `${name}.json`);
47499
+ return join48(credentialsDir(home), `${name}.json`);
46215
47500
  }
46216
47501
  function parseProfile(text4, label2) {
46217
47502
  let doc;
@@ -46242,8 +47527,8 @@ function resolveStoreCredentials(opts) {
46242
47527
  const { app, cfg } = opts;
46243
47528
  const env = opts.env ?? process.env;
46244
47529
  const home = opts.home ?? homedir5();
46245
- const read = opts.readProfile ?? ((p) => readFileSync57(p, "utf8"));
46246
- const exists = opts.profileExists ?? existsSync52;
47530
+ const read = opts.readProfile ?? ((p) => readFileSync58(p, "utf8"));
47531
+ const exists = opts.profileExists ?? existsSync53;
46247
47532
  const ios = cfg.ios ?? {};
46248
47533
  const flagName = opts.profileFlag?.trim();
46249
47534
  const named = namedProfile(cfg, opts.profileFlag);
@@ -46323,9 +47608,9 @@ __export(preflight_app_exports, {
46323
47608
  preflightApp: () => preflightApp,
46324
47609
  profileAccount: () => profileAccount
46325
47610
  });
46326
- import { readFileSync as readFileSync58, existsSync as existsSync53, readdirSync as readdirSync24, statSync as statSync11 } from "node:fs";
47611
+ import { readFileSync as readFileSync59, existsSync as existsSync54, readdirSync as readdirSync25, statSync as statSync12 } from "node:fs";
46327
47612
  import { createHash as createHash23 } from "node:crypto";
46328
- import { join as join48 } from "node:path";
47613
+ import { join as join49 } from "node:path";
46329
47614
  function analyze(projectYml, storeConfig, icon, account) {
46330
47615
  const f = [];
46331
47616
  const has = (re, s) => re.test(s);
@@ -46389,13 +47674,13 @@ function profileAccount(app, storeConfig, opts = {}) {
46389
47674
  }
46390
47675
  }
46391
47676
  async function preflightApp(app, opts = {}) {
46392
- const projectYml = existsSync53(iosProjectYml(app)) ? readFileSync58(iosProjectYml(app), "utf8") : "";
46393
- const storeConfig = existsSync53(storeConfigPath(app)) ? readFileSync58(storeConfigPath(app), "utf8") : "";
47677
+ const projectYml = existsSync54(iosProjectYml(app)) ? readFileSync59(iosProjectYml(app), "utf8") : "";
47678
+ const storeConfig = existsSync54(storeConfigPath(app)) ? readFileSync59(storeConfigPath(app), "utf8") : "";
46394
47679
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
46395
47680
  const findings = analyze(
46396
47681
  projectYml,
46397
47682
  storeConfig,
46398
- await appIconState(join48(appDir(app), "ios")),
47683
+ await appIconState(join49(appDir(app), "ios")),
46399
47684
  profileAccount(app, storeConfig, opts)
46400
47685
  );
46401
47686
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
@@ -46424,18 +47709,18 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
46424
47709
  return f;
46425
47710
  }
46426
47711
  function appLevelMetadataFiles(app, locale = "en-US") {
46427
- const root = join48(storeDir(app), "metadata");
47712
+ const root = join49(storeDir(app), "metadata");
46428
47713
  const out = /* @__PURE__ */ new Set();
46429
- if (existsSync53(root)) {
46430
- for (const n of readdirSync24(root)) if (n.endsWith(".txt")) out.add(n);
47714
+ if (existsSync54(root)) {
47715
+ for (const n of readdirSync25(root)) if (n.endsWith(".txt")) out.add(n);
46431
47716
  }
46432
- if (existsSync53(join48(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
47717
+ if (existsSync54(join49(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
46433
47718
  return out;
46434
47719
  }
46435
47720
  function reviewInfoFiles(app) {
46436
- const dir = join48(storeDir(app), "review_information");
46437
- if (!existsSync53(dir)) return /* @__PURE__ */ new Set();
46438
- return new Set(readdirSync24(dir).filter((n) => n.endsWith(".txt")));
47721
+ const dir = join49(storeDir(app), "review_information");
47722
+ if (!existsSync54(dir)) return /* @__PURE__ */ new Set();
47723
+ return new Set(readdirSync25(dir).filter((n) => n.endsWith(".txt")));
46439
47724
  }
46440
47725
  function isValidAppStoreSize(w, h) {
46441
47726
  return APP_STORE_SIZES.some(([a, b]) => w === a && h === b || w === b && h === a);
@@ -46544,12 +47829,12 @@ function analyzePlayListing(assets) {
46544
47829
  return f;
46545
47830
  }
46546
47831
  function playListingAssets(app, locale = "en-US") {
46547
- const dir = join48(storeDir(app), "play", locale, "images");
47832
+ const dir = join49(storeDir(app), "play", locale, "images");
46548
47833
  const read = (names) => {
46549
47834
  for (const name of names) {
46550
- const p = join48(dir, name);
47835
+ const p = join49(dir, name);
46551
47836
  try {
46552
- if (statSync11(p).isFile()) return { name, bytes: readFileSync58(p) };
47837
+ if (statSync12(p).isFile()) return { name, bytes: readFileSync59(p) };
46553
47838
  } catch {
46554
47839
  }
46555
47840
  }
@@ -46580,26 +47865,26 @@ function analyzeListing(metaFiles, shots) {
46580
47865
  return f;
46581
47866
  }
46582
47867
  function metadataFilesPresent(app, locale = "en-US") {
46583
- const dir = join48(storeDir(app), "metadata", locale);
46584
- if (!existsSync53(dir)) return /* @__PURE__ */ new Set();
46585
- return new Set(readdirSync24(dir).filter((n) => n.endsWith(".txt")));
47868
+ const dir = join49(storeDir(app), "metadata", locale);
47869
+ if (!existsSync54(dir)) return /* @__PURE__ */ new Set();
47870
+ return new Set(readdirSync25(dir).filter((n) => n.endsWith(".txt")));
46586
47871
  }
46587
47872
  function listScreenshots(app, locale = "en-US") {
46588
47873
  const out = [];
46589
- const flat = join48(storeDir(app), "screenshots", locale);
46590
- if (existsSync53(flat)) {
46591
- for (const png of readdirSync24(flat).filter((n) => n.endsWith(".png"))) {
46592
- const sz = pngSize(readFileSync58(join48(flat, png)));
47874
+ const flat = join49(storeDir(app), "screenshots", locale);
47875
+ if (existsSync54(flat)) {
47876
+ for (const png of readdirSync25(flat).filter((n) => n.endsWith(".png"))) {
47877
+ const sz = pngSize(readFileSync59(join49(flat, png)));
46593
47878
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
46594
47879
  }
46595
47880
  }
46596
- const legacy = join48(storeDir(app), "metadata", locale, "screenshots");
46597
- if (existsSync53(legacy)) {
46598
- for (const deviceDir of readdirSync24(legacy)) {
46599
- const d = join48(legacy, deviceDir);
46600
- if (!statSync11(d).isDirectory()) continue;
46601
- for (const png of readdirSync24(d).filter((n) => n.endsWith(".png"))) {
46602
- const sz = pngSize(readFileSync58(join48(d, png)));
47881
+ const legacy = join49(storeDir(app), "metadata", locale, "screenshots");
47882
+ if (existsSync54(legacy)) {
47883
+ for (const deviceDir of readdirSync25(legacy)) {
47884
+ const d = join49(legacy, deviceDir);
47885
+ if (!statSync12(d).isDirectory()) continue;
47886
+ for (const png of readdirSync25(d).filter((n) => n.endsWith(".png"))) {
47887
+ const sz = pngSize(readFileSync59(join49(d, png)));
46603
47888
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
46604
47889
  }
46605
47890
  }
@@ -46607,13 +47892,13 @@ function listScreenshots(app, locale = "en-US") {
46607
47892
  return out;
46608
47893
  }
46609
47894
  function placeholderHashes2() {
46610
- const templateIos = join48(appDir("_template"), "ios");
46611
- if (!existsSync53(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
46612
- for (const target of readdirSync24(templateIos)) {
46613
- const p = join48(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
47895
+ const templateIos = join49(appDir("_template"), "ios");
47896
+ if (!existsSync54(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
47897
+ for (const target of readdirSync25(templateIos)) {
47898
+ const p = join49(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
46614
47899
  try {
46615
- if (!statSync11(p).isFile()) continue;
46616
- return [LEGACY_PLACEHOLDER_SHA2562, createHash23("sha256").update(readFileSync58(p)).digest("hex")];
47900
+ if (!statSync12(p).isFile()) continue;
47901
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash23("sha256").update(readFileSync59(p)).digest("hex")];
46617
47902
  } catch {
46618
47903
  }
46619
47904
  }
@@ -46644,12 +47929,12 @@ async function pixelIssues(bytes2) {
46644
47929
  }
46645
47930
  }
46646
47931
  async function appIconState(iosDir) {
46647
- if (!existsSync53(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
46648
- for (const target of readdirSync24(iosDir)) {
46649
- const p = join48(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
47932
+ if (!existsSync54(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
47933
+ for (const target of readdirSync25(iosDir)) {
47934
+ const p = join49(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
46650
47935
  try {
46651
- if (!statSync11(p).isFile()) continue;
46652
- const bytes2 = readFileSync58(p);
47936
+ if (!statSync12(p).isFile()) continue;
47937
+ const bytes2 = readFileSync59(p);
46653
47938
  const hash = createHash23("sha256").update(bytes2).digest("hex");
46654
47939
  const pixels = await pixelIssues(bytes2);
46655
47940
  return {
@@ -46721,7 +48006,7 @@ __export(version_exports, {
46721
48006
  setAndroidVersion: () => setAndroidVersion,
46722
48007
  setIosVersion: () => setIosVersion
46723
48008
  });
46724
- import { readFileSync as readFileSync59, writeFileSync as writeFileSync20 } from "node:fs";
48009
+ import { readFileSync as readFileSync60, writeFileSync as writeFileSync21 } from "node:fs";
46725
48010
  function readIosVersion(projectYml) {
46726
48011
  const marketing = matchScalar(projectYml, "CFBundleShortVersionString");
46727
48012
  const build2 = matchScalar(projectYml, "CFBundleVersion");
@@ -46779,15 +48064,15 @@ function setAndroidVersion(gradle, v) {
46779
48064
  function bumpVersion(app, opts) {
46780
48065
  const iosPath = iosProjectYml(app);
46781
48066
  const gradlePath = androidGradle(app);
46782
- const iosYml = readFileSync59(iosPath, "utf8");
46783
- const gradle = readFileSync59(gradlePath, "utf8");
48067
+ const iosYml = readFileSync60(iosPath, "utf8");
48068
+ const gradle = readFileSync60(gradlePath, "utf8");
46784
48069
  const current = readIosVersion(iosYml);
46785
48070
  const next = {
46786
48071
  marketing: opts.marketing ?? current.marketing,
46787
48072
  build: opts.build === "increment" || opts.build === void 0 ? current.build + (opts.build === "increment" ? 1 : 0) : opts.build
46788
48073
  };
46789
- writeFileSync20(iosPath, setIosVersion(iosYml, next));
46790
- writeFileSync20(gradlePath, setAndroidVersion(gradle, next));
48074
+ writeFileSync21(iosPath, setIosVersion(iosYml, next));
48075
+ writeFileSync21(gradlePath, setAndroidVersion(gradle, next));
46791
48076
  return next;
46792
48077
  }
46793
48078
  var init_version = __esm({
@@ -46797,7 +48082,7 @@ var init_version = __esm({
46797
48082
  });
46798
48083
 
46799
48084
  // ../store-release/register-bundle.ts
46800
- import { readFileSync as readFileSync60 } from "node:fs";
48085
+ import { readFileSync as readFileSync61 } from "node:fs";
46801
48086
  import { createPrivateKey as createPrivateKey2, sign } from "node:crypto";
46802
48087
  function b64url2(b) {
46803
48088
  return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -46815,7 +48100,7 @@ function bundleIdBody(identifier, name, platform = "IOS") {
46815
48100
  async function registerBundleId(opts) {
46816
48101
  const env = requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
46817
48102
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
46818
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync60(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
48103
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync61(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
46819
48104
  const res = await fetch(`${ASC_API}/v1/bundleIds`, {
46820
48105
  method: "POST",
46821
48106
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
@@ -46869,7 +48154,7 @@ __export(asc_status_exports, {
46869
48154
  ascStatus: () => ascStatus,
46870
48155
  buildVerdict: () => buildVerdict
46871
48156
  });
46872
- import { readFileSync as readFileSync61 } from "node:fs";
48157
+ import { readFileSync as readFileSync62 } from "node:fs";
46873
48158
  function buildVerdict(builds) {
46874
48159
  if (!builds.length) {
46875
48160
  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).";
@@ -46883,7 +48168,7 @@ function buildVerdict(builds) {
46883
48168
  function client(creds) {
46884
48169
  const env = creds ?? requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
46885
48170
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
46886
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync61(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
48171
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync62(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
46887
48172
  return async (path2) => {
46888
48173
  const r = await fetch(`${ASC}${path2}`, { headers: { Authorization: `Bearer ${jwt}` } });
46889
48174
  return { status: r.status, json: await r.json().catch(() => ({})) };
@@ -46932,7 +48217,7 @@ var init_asc_status = __esm({
46932
48217
  // ../../core/functions-secrets/src/vault.ts
46933
48218
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
46934
48219
  import { mkdir, readFile as readFile2, rename, writeFile as writeFile2 } from "node:fs/promises";
46935
- import { dirname as dirname28 } from "node:path";
48220
+ import { dirname as dirname29 } from "node:path";
46936
48221
  function createJsonSecretStore(filePath) {
46937
48222
  const read = async () => {
46938
48223
  try {
@@ -46945,7 +48230,7 @@ function createJsonSecretStore(filePath) {
46945
48230
  }
46946
48231
  };
46947
48232
  const write2 = async (records) => {
46948
- await mkdir(dirname28(filePath), { recursive: true });
48233
+ await mkdir(dirname29(filePath), { recursive: true });
46949
48234
  const temporary = `${filePath}.${process.pid}.tmp`;
46950
48235
  await writeFile2(temporary, `${JSON.stringify(records, null, 2)}
46951
48236
  `, { mode: 384 });
@@ -47043,7 +48328,7 @@ __export(secrets_exports, {
47043
48328
  runRemoteSecretsCommand: () => runRemoteSecretsCommand,
47044
48329
  runSecretsCommand: () => runSecretsCommand
47045
48330
  });
47046
- import { readFileSync as readFileSync62 } from "node:fs";
48331
+ import { readFileSync as readFileSync63 } from "node:fs";
47047
48332
  function environment(value) {
47048
48333
  if (value === "preview" || value === "production") return value;
47049
48334
  throw new Error("--environment must be preview or production");
@@ -47074,7 +48359,7 @@ async function runSecretsCommand(operation, flags, io, options = {}) {
47074
48359
  io.log(JSON.stringify(await vault.list({ app, environment: env })));
47075
48360
  return 0;
47076
48361
  }
47077
- const value = options.readValue ? options.readValue() : readFileSync62(0, "utf8").replace(/\r?\n$/, "");
48362
+ const value = options.readValue ? options.readValue() : readFileSync63(0, "utf8").replace(/\r?\n$/, "");
47078
48363
  if (operation === "set") await vault.set({ app, environment: env, name, value });
47079
48364
  else if (operation === "rotate") await vault.rotate({ app, environment: env, name, value });
47080
48365
  else throw new Error(`unknown secrets operation ${operation}`);
@@ -47103,7 +48388,7 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
47103
48388
  }
47104
48389
  let body;
47105
48390
  if (operation !== "list") {
47106
- const value = options.readValue ? options.readValue() : readFileSync62(0, "utf8").replace(/\r?\n$/, "");
48391
+ const value = options.readValue ? options.readValue() : readFileSync63(0, "utf8").replace(/\r?\n$/, "");
47107
48392
  body = JSON.stringify({ value });
47108
48393
  }
47109
48394
  const response = await (options.fetchImpl ?? fetch)(url, { method: operation === "list" ? "GET" : operation === "set" ? "PUT" : "POST", headers: headers2, body });
@@ -47139,15 +48424,15 @@ __export(notify_exports, {
47139
48424
  runNotify: () => runNotify,
47140
48425
  saveAdminSession: () => saveAdminSession
47141
48426
  });
47142
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync24, readFileSync as readFileSync63, writeFileSync as writeFileSync21 } from "node:fs";
48427
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync25, readFileSync as readFileSync64, writeFileSync as writeFileSync22 } from "node:fs";
47143
48428
  import { homedir as homedir6 } from "node:os";
47144
- import { dirname as dirname29, join as join49 } from "node:path";
48429
+ import { dirname as dirname30, join as join50 } from "node:path";
47145
48430
  function defaultSessionsPath() {
47146
- return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join49(homedir6(), ".himalaya", "admin-sessions.json");
48431
+ return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join50(homedir6(), ".himalaya", "admin-sessions.json");
47147
48432
  }
47148
48433
  function loadSessionsFile(path2 = defaultSessionsPath()) {
47149
48434
  try {
47150
- return JSON.parse(readFileSync63(path2, "utf8"));
48435
+ return JSON.parse(readFileSync64(path2, "utf8"));
47151
48436
  } catch {
47152
48437
  return {};
47153
48438
  }
@@ -47155,10 +48440,10 @@ function loadSessionsFile(path2 = defaultSessionsPath()) {
47155
48440
  function saveAdminSession(adminUrl, session, path2 = defaultSessionsPath()) {
47156
48441
  const file = loadSessionsFile(path2);
47157
48442
  file.sessions = { ...file.sessions, [adminUrl]: session };
47158
- mkdirSync24(dirname29(path2), { recursive: true, mode: 448 });
47159
- writeFileSync21(path2, JSON.stringify(file, null, 2) + "\n", { mode: 384 });
48443
+ mkdirSync25(dirname30(path2), { recursive: true, mode: 448 });
48444
+ writeFileSync22(path2, JSON.stringify(file, null, 2) + "\n", { mode: 384 });
47160
48445
  try {
47161
- chmodSync3(dirname29(path2), 448);
48446
+ chmodSync3(dirname30(path2), 448);
47162
48447
  chmodSync3(path2, 384);
47163
48448
  } catch {
47164
48449
  }
@@ -47417,56 +48702,6 @@ var init_notify = __esm({
47417
48702
  }
47418
48703
  });
47419
48704
 
47420
- // src/site-analyze/types.ts
47421
- function isRedirectUri(value) {
47422
- return /^[a-z][a-z0-9+.-]*:\/\/\S*$/i.test(value);
47423
- }
47424
- function assertRedirectUri(value) {
47425
- const trimmed = value.trim();
47426
- if (!isRedirectUri(trimmed)) {
47427
- throw new Error(
47428
- `--redirect-uri must be a URI with a scheme (e.g. myapp://oauth/callback), got: ${JSON.stringify(value)}. It must also be REGISTERED on --client-id; register one with \`himi site client ensure --site <msid> --redirect-uri <uri> --yes\`.`
47429
- );
47430
- }
47431
- return trimmed;
47432
- }
47433
- var BOOK_SCHEMA_VERSION, KNOWN_VERTICALS;
47434
- var init_types2 = __esm({
47435
- "src/site-analyze/types.ts"() {
47436
- BOOK_SCHEMA_VERSION = 2;
47437
- KNOWN_VERTICALS = [
47438
- "store",
47439
- "bookings",
47440
- "restaurants",
47441
- "programs",
47442
- "plans",
47443
- "feed",
47444
- "members",
47445
- "forum",
47446
- "coupons",
47447
- "notifications",
47448
- "loyalty",
47449
- "reviews",
47450
- "portfolio",
47451
- "reservations",
47452
- "invoices",
47453
- "badges",
47454
- "notifsettings",
47455
- "events",
47456
- "blog",
47457
- "groups",
47458
- "cms",
47459
- "donations",
47460
- "giftCards",
47461
- "videos",
47462
- "podcasts",
47463
- "forms",
47464
- "galleries",
47465
- "inbox"
47466
- ];
47467
- }
47468
- });
47469
-
47470
48705
  // src/site-analyze/rest.ts
47471
48706
  function resolveWixUrl(path2) {
47472
48707
  if (/^https?:\/\//i.test(path2)) return path2;
@@ -49372,30 +50607,30 @@ function sectionsFrom(pages) {
49372
50607
  const raw = page.sections?.length ? page.sections : page.headings.map((heading) => ({ heading, body: void 0 }));
49373
50608
  let fromThisPage = 0;
49374
50609
  for (const s of raw) {
49375
- if (fromThisPage >= CAP.sectionsPerPage) break;
49376
- const heading = copyOrDrop(s.heading, CAP.heading);
50610
+ if (fromThisPage >= CAP2.sectionsPerPage) break;
50611
+ const heading = copyOrDrop(s.heading, CAP2.heading);
49377
50612
  if (!heading) continue;
49378
50613
  const key2 = heading.toLowerCase();
49379
50614
  if (seen.has(key2)) continue;
49380
50615
  seen.add(key2);
49381
- const body = copyOrDrop(s.body, CAP.body);
50616
+ const body = copyOrDrop(s.body, CAP2.body);
49382
50617
  out.push({ heading, ...body ? { body } : {}, pageUrl: page.url, role: page.role });
49383
50618
  fromThisPage++;
49384
- if (out.length >= CAP.sections) return out;
50619
+ if (out.length >= CAP2.sections) return out;
49385
50620
  }
49386
50621
  }
49387
50622
  return out;
49388
50623
  }
49389
50624
  function taglineFrom(identity, pages, published) {
49390
- const owned = copyOrDrop(identity.description, CAP.tagline);
50625
+ const owned = copyOrDrop(identity.description, CAP2.tagline);
49391
50626
  if (owned && identity.source === "owner-api") return { text: owned, source: "site-properties" };
49392
50627
  const home = pages.find((p) => p.role === "home") ?? pages[0];
49393
- const og = copyOrDrop(home?.meta?.ogDescription ?? published?.meta?.ogDescription, CAP.tagline);
50628
+ const og = copyOrDrop(home?.meta?.ogDescription ?? published?.meta?.ogDescription, CAP2.tagline);
49394
50629
  if (og) return { text: og, source: "og" };
49395
- const meta = copyOrDrop(home?.meta?.description ?? published?.meta?.description, CAP.tagline);
50630
+ const meta = copyOrDrop(home?.meta?.description ?? published?.meta?.description, CAP2.tagline);
49396
50631
  if (meta) return { text: meta, source: "meta" };
49397
50632
  if (owned) return { text: owned, source: "hero" };
49398
- const hero = copyOrDrop(home?.heroCopy, CAP.tagline);
50633
+ const hero = copyOrDrop(home?.heroCopy, CAP2.tagline);
49399
50634
  return hero ? { text: hero, source: "hero" } : null;
49400
50635
  }
49401
50636
  function suggestKeys(args) {
@@ -49404,9 +50639,9 @@ function suggestKeys(args) {
49404
50639
  const installed = new Set(installedVerticals);
49405
50640
  const set = (key2, value) => {
49406
50641
  if (!key2 || out[key2] || !value) return;
49407
- if (Object.keys(out).length >= CAP.keys) return;
50642
+ if (Object.keys(out).length >= CAP2.keys) return;
49408
50643
  if (key2.length > 120 || !/^[A-Za-z0-9._:-]+$/.test(key2)) return;
49409
- out[key2] = value.slice(0, CAP.value);
50644
+ out[key2] = value.slice(0, CAP2.value);
49410
50645
  };
49411
50646
  const allowed2 = (rule) => !rule.vertical || installed.has(rule.vertical);
49412
50647
  for (const label2 of nav) {
@@ -49428,42 +50663,42 @@ function buildCopyChapter(args) {
49428
50663
  const { look, probes, identity, installedVerticals, published } = args;
49429
50664
  const pages = pagesOf(look);
49430
50665
  const sections = sectionsFrom(pages);
49431
- const nav = dedupe2(look.navLabels.map((n) => copyOrDrop(n, CAP.heading)), CAP.nav);
50666
+ const nav = dedupe2(look.navLabels.map((n) => copyOrDrop(n, CAP2.heading)), CAP2.nav);
49432
50667
  const tagline = taglineFrom(identity, pages, published);
49433
50668
  const ctas = dedupe2(
49434
50669
  pages.flatMap((p) => p.ctaLabels?.length ? p.ctaLabels : p.layout.ctas),
49435
- CAP.ctas
50670
+ CAP2.ctas
49436
50671
  );
49437
50672
  const faq = [];
49438
50673
  for (const page of pages) {
49439
50674
  for (const pair of page.faq ?? []) {
49440
- if (faq.length >= CAP.faq) break;
50675
+ if (faq.length >= CAP2.faq) break;
49441
50676
  if (faq.some((f) => f.q.toLowerCase() === pair.q.toLowerCase())) continue;
49442
50677
  faq.push({ ...pair, pageUrl: page.url });
49443
50678
  }
49444
50679
  }
49445
50680
  const about = pages.map((p) => p.about).find(Boolean) ?? sections.find((s) => ABOUT_RULE.re.test(s.heading))?.body ?? null;
49446
- if (faq.length < CAP.faq) {
50681
+ if (faq.length < CAP2.faq) {
49447
50682
  const candidates = copyPageOrder(pages).flatMap(
49448
50683
  (page) => (page.sections ?? []).map((sec) => ({
49449
- heading: copyOrDrop(sec.heading, CAP.heading),
49450
- body: copyOrDrop(sec.body, CAP.body),
50684
+ heading: copyOrDrop(sec.heading, CAP2.heading),
50685
+ body: copyOrDrop(sec.body, CAP2.body),
49451
50686
  pageUrl: page.url
49452
50687
  }))
49453
50688
  );
49454
50689
  for (const section2 of candidates) {
49455
- if (faq.length >= CAP.faq) break;
50690
+ if (faq.length >= CAP2.faq) break;
49456
50691
  if (!section2.heading || !section2.body || !/\?\s*$/.test(section2.heading)) continue;
49457
50692
  if (about && section2.body === about) continue;
49458
50693
  if (faq.some((f) => f.q.toLowerCase() === section2.heading.toLowerCase())) continue;
49459
50694
  faq.push({ q: section2.heading, a: section2.body, pageUrl: section2.pageUrl });
49460
50695
  }
49461
50696
  }
49462
- const testimonials = dedupe2(pages.flatMap((p) => p.testimonials ?? []), CAP.testimonials);
50697
+ const testimonials = dedupe2(pages.flatMap((p) => p.testimonials ?? []), CAP2.testimonials);
49463
50698
  const blogExcerpts = [];
49464
50699
  for (const it of probes.find((p) => p.vertical === "blog")?.items ?? []) {
49465
- if (blogExcerpts.length >= CAP.blogExcerpts) break;
49466
- const title = copyOrDrop(it.title, CAP.heading);
50700
+ if (blogExcerpts.length >= CAP2.blogExcerpts) break;
50701
+ const title = copyOrDrop(it.title, CAP2.heading);
49467
50702
  if (!title) continue;
49468
50703
  blogExcerpts.push({ title, ...it.description ? { excerpt: it.description } : {} });
49469
50704
  }
@@ -49471,8 +50706,8 @@ function buildCopyChapter(args) {
49471
50706
  for (const probe of probes) {
49472
50707
  if (probe.vertical === "members") continue;
49473
50708
  for (const item of probe.items) {
49474
- if (catalog.length >= CAP.catalog) break;
49475
- const title = copyOrDrop(item.title, CAP.heading);
50709
+ if (catalog.length >= CAP2.catalog) break;
50710
+ const title = copyOrDrop(item.title, CAP2.heading);
49476
50711
  if (!title) continue;
49477
50712
  catalog.push({
49478
50713
  vertical: probe.vertical,
@@ -49484,7 +50719,7 @@ function buildCopyChapter(args) {
49484
50719
  if (!sections.length) {
49485
50720
  for (const heading of published?.headings ?? []) {
49486
50721
  sections.push({ heading, pageUrl: identity.url.primary ?? "", role: "home" });
49487
- if (sections.length >= CAP.sections) break;
50722
+ if (sections.length >= CAP2.sections) break;
49488
50723
  }
49489
50724
  }
49490
50725
  const empty = !tagline && !sections.length && !nav.length && !ctas.length && !catalog.length && !faq.length && !testimonials.length && !about && !blogExcerpts.length;
@@ -49505,11 +50740,11 @@ function buildCopyChapter(args) {
49505
50740
  suggestedCopyKeys: suggestKeys({ nav, sections, tagline, installedVerticals })
49506
50741
  };
49507
50742
  }
49508
- var CAP, SYSTEM_PAGE_RE, ROLE_PRIORITY, KEY_RULES, ABOUT_RULE;
50743
+ var CAP2, SYSTEM_PAGE_RE, ROLE_PRIORITY, KEY_RULES, ABOUT_RULE;
49509
50744
  var init_copy = __esm({
49510
50745
  "src/site-analyze/copy.ts"() {
49511
50746
  init_copy_clean();
49512
- CAP = {
50747
+ CAP2 = {
49513
50748
  sections: 40,
49514
50749
  sectionsPerPage: 8,
49515
50750
  ctas: 24,
@@ -50732,8 +51967,8 @@ var init_brand_font = __esm({
50732
51967
  });
50733
51968
 
50734
51969
  // src/site-analyze/write-artifacts.ts
50735
- import { mkdirSync as mkdirSync25, writeFileSync as writeFileSync22 } from "node:fs";
50736
- import { join as join50 } from "node:path";
51970
+ import { mkdirSync as mkdirSync26, writeFileSync as writeFileSync23 } from "node:fs";
51971
+ import { join as join51 } from "node:path";
50737
51972
  function attachVeloSources(book, sources, put) {
50738
51973
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
50739
51974
  for (const f of book.velo.files) {
@@ -50888,13 +52123,13 @@ async function downloadMedia(book, fetchImpl, put) {
50888
52123
  }
50889
52124
  async function writeBusinessBook(result, opts) {
50890
52125
  const out = opts.outDir;
50891
- mkdirSync25(join50(out, "evidence/pages"), { recursive: true });
50892
- mkdirSync25(join50(out, "media/catalog"), { recursive: true });
52126
+ mkdirSync26(join51(out, "evidence/pages"), { recursive: true });
52127
+ mkdirSync26(join51(out, "media/catalog"), { recursive: true });
50893
52128
  const files = [];
50894
52129
  const put = (rel, body) => {
50895
- const p = join50(out, rel);
50896
- mkdirSync25(join50(p, ".."), { recursive: true });
50897
- writeFileSync22(p, body);
52130
+ const p = join51(out, rel);
52131
+ mkdirSync26(join51(p, ".."), { recursive: true });
52132
+ writeFileSync23(p, body);
50898
52133
  files.push(rel);
50899
52134
  };
50900
52135
  const book = result.book;
@@ -50986,336 +52221,9 @@ var init_customer_build = __esm({
50986
52221
  }
50987
52222
  });
50988
52223
 
50989
- // src/site-analyze/home-descriptor.ts
50990
- function objectEnd(src, open) {
50991
- let depth = 0;
50992
- let inString = false;
50993
- for (let i = open; i < src.length; i += 1) {
50994
- const c = src[i];
50995
- if (inString) {
50996
- if (c === "\\") i += 1;
50997
- else if (c === '"') inString = false;
50998
- continue;
50999
- }
51000
- if (c === '"') inString = true;
51001
- else if (c === "{") depth += 1;
51002
- else if (c === "}") {
51003
- depth -= 1;
51004
- if (depth === 0) return i + 1;
51005
- }
51006
- }
51007
- return -1;
51008
- }
51009
- function hasScreen(src, screenId) {
51010
- const id = escapeRe2(screenId);
51011
- return new RegExp(`"${id}": \\{\\s*"screenId": "${id}"`).test(src);
51012
- }
51013
- function findNode(root, pred) {
51014
- if (Array.isArray(root)) {
51015
- for (const child of root) {
51016
- const hit = findNode(child, pred);
51017
- if (hit) return hit;
51018
- }
51019
- return void 0;
51020
- }
51021
- if (!root || typeof root !== "object") return void 0;
51022
- const node = root;
51023
- if (node.body && typeof node.body === "object" && pred(node)) return node;
51024
- for (const value of Object.values(root)) {
51025
- const hit = findNode(value, pred);
51026
- if (hit) return hit;
51027
- }
51028
- return void 0;
51029
- }
51030
- function cloneCell(template, prefix, vertical, card) {
51031
- const cell = structuredClone(template);
51032
- const accent = findNode(cell, (n) => n.body?.type === "Icon" && textProps(n)?.tint === "accentPrimary");
51033
- const title = findNode(cell, (n) => n.body?.type === "Text" && textProps(n)?.style === "bodyEmphasis");
51034
- const body = findNode(cell, (n) => n.body?.type === "Text" && textProps(n)?.style === "footnote");
51035
- if (!accent || !title || !body || !cell.modifiers) return void 0;
51036
- const onTap = cell.modifiers.onTap;
51037
- if (!onTap?.actionId) return void 0;
51038
- textProps(accent).name = card.icon;
51039
- textProps(title).text = card.title;
51040
- textProps(body).text = card.body;
51041
- cell.modifiers.onTap = { actionId: onTap.actionId, params: { screenId: card.screenId } };
51042
- cell.id = `${prefix}.${vertical}`;
51043
- cell.modifiers.accessibilityLabel = card.title;
51044
- return cell;
51045
- }
51046
- function lineIndent(src, open) {
51047
- return /^[ \t]*/.exec(src.slice(src.lastIndexOf("\n", open) + 1, open))[0];
51048
- }
51049
- function serializeAt(node, src, open) {
51050
- const indent = lineIndent(src, open);
51051
- return JSON.stringify(node, null, 2).split("\n").map((line, i) => i === 0 ? line : indent + line).join("\n");
51052
- }
51053
- function patchGrids(src, installed, patch) {
51054
- const OPEN = /"id": "([A-Za-z_0-9]+(?:\.[A-Za-z_0-9]+)*\.verticals)"/g;
51055
- let out = "";
51056
- let cursor = 0;
51057
- for (let m = OPEN.exec(src); m; m = OPEN.exec(src)) {
51058
- const open = src.lastIndexOf("{", m.index);
51059
- if (open < cursor) continue;
51060
- const close = objectEnd(src, open);
51061
- if (close < 0) continue;
51062
- const blob = src.slice(open, close);
51063
- let node;
51064
- try {
51065
- node = JSON.parse(blob);
51066
- } catch {
51067
- continue;
51068
- }
51069
- const items = node.body?.type === "Grid" ? node.body.props?.items : void 0;
51070
- if (!Array.isArray(items) || !items.length) continue;
51071
- const prefix = m[1].slice(0, -".verticals".length);
51072
- const baked = /* @__PURE__ */ new Map();
51073
- const foreignCells = [];
51074
- for (const item of items) {
51075
- const id = item.id;
51076
- const legacy = item.modifiers?.accessibilityLabel;
51077
- const claim = typeof id === "string" && id ? id : typeof legacy === "string" ? legacy : "";
51078
- const vertical = claim.startsWith(`${prefix}.`) ? claim.slice(prefix.length + 1) : "";
51079
- if (isKnown(vertical)) baked.set(vertical, item);
51080
- else if (!GRID_VERTICAL_LABELS.has(vertical)) foreignCells.push(item);
51081
- }
51082
- const wanted = [];
51083
- for (const vertical of EXPLORE_ORDER) {
51084
- if (!installed.has(vertical)) continue;
51085
- if (baked.has(vertical)) {
51086
- wanted.push(vertical);
51087
- continue;
51088
- }
51089
- const card = EXPLORE_CARDS[vertical];
51090
- if (!hasScreen(src, card.screenId)) {
51091
- patch.unreachable.push(vertical);
51092
- continue;
51093
- }
51094
- wanted.push(vertical);
51095
- }
51096
- const shape = baked.values().next().value ?? items[0];
51097
- const next = [...foreignCells];
51098
- const cells = [];
51099
- for (const vertical of wanted) {
51100
- if (next.length >= EXPLORE_MAX_CELLS) break;
51101
- const bakedCell = baked.get(vertical);
51102
- if (bakedCell) {
51103
- next.push(bakedCell);
51104
- cells.push(vertical);
51105
- continue;
51106
- }
51107
- const made = cloneCell(shape, prefix, vertical, EXPLORE_CARDS[vertical]);
51108
- if (!made) {
51109
- patch.unreachable.push(vertical);
51110
- continue;
51111
- }
51112
- next.push(made);
51113
- cells.push(vertical);
51114
- patch.added.push(vertical);
51115
- }
51116
- if (!cells.length) continue;
51117
- patch.cells = cells;
51118
- node.body.props.items = next;
51119
- out += src.slice(cursor, open) + serializeAt(node, src, open);
51120
- cursor = close;
51121
- OPEN.lastIndex = close;
51122
- }
51123
- return out + src.slice(cursor);
51124
- }
51125
- function patchModules(src, installed, patch) {
51126
- return src.replace(/"homeModules": "((?:[^"\\]|\\.)*)"/g, (whole, encoded2) => {
51127
- let modules;
51128
- try {
51129
- const parsed = JSON.parse(JSON.parse(`"${encoded2}"`));
51130
- if (!Array.isArray(parsed)) return whole;
51131
- modules = parsed;
51132
- } catch {
51133
- return whole;
51134
- }
51135
- const kept = modules.filter((module) => {
51136
- const source = module?.dataSource;
51137
- if (typeof source !== "string") return true;
51138
- const needs = MODULE_SOURCE_VERTICALS[source];
51139
- return !needs || needs.some((vertical) => installed.has(vertical));
51140
- });
51141
- if (!kept.length || kept.length === modules.length) return whole;
51142
- for (const module of modules) {
51143
- if (!kept.includes(module) && typeof module?.id === "string") patch.droppedModules.push(module.id);
51144
- }
51145
- return `"homeModules": ${JSON.stringify(JSON.stringify(kept))}`;
51146
- });
51147
- }
51148
- function homeScreenSpan(src, homeActionId) {
51149
- const OPEN = /"([A-Za-z_0-9]+)": \{\s*"screenId": "\1"/g;
51150
- for (let m = OPEN.exec(src); m; m = OPEN.exec(src)) {
51151
- const open = src.indexOf("{", m.index);
51152
- const close = objectEnd(src, open);
51153
- if (close < 0) continue;
51154
- if (src.slice(open, close).includes(`"actionId": "${homeActionId}"`)) return { open, close };
51155
- OPEN.lastIndex = close;
51156
- }
51157
- return void 0;
51158
- }
51159
- function isDeadStoreCta(node) {
51160
- if (node.body?.type !== "Button") return false;
51161
- const action = node.body.props?.action;
51162
- const screenId = action?.params?.screenId;
51163
- return typeof screenId === "string" && isStoreScreen(screenId);
51164
- }
51165
- function pruneNode(node, dropped) {
51166
- const props = node.body?.props;
51167
- if (!props) return true;
51168
- const lists = props.itemsPath ? ["children"] : ["children", "items"];
51169
- for (const key2 of lists) {
51170
- const list = props[key2];
51171
- if (!Array.isArray(list) || !list.length) continue;
51172
- const kept = list.filter((child) => {
51173
- if (!isNode2(child)) return true;
51174
- if (isDeadStoreCta(child)) {
51175
- dropped.push(typeof child.id === "string" && child.id ? child.id : "(unnamed CTA)");
51176
- return false;
51177
- }
51178
- return pruneNode(child, dropped);
51179
- });
51180
- if (kept.length === list.length) continue;
51181
- if (!kept.length) return false;
51182
- props[key2] = kept;
51183
- }
51184
- for (const [key2, child] of Object.entries(props)) {
51185
- if (lists.includes(key2)) continue;
51186
- pruneWithin(child, dropped);
51187
- }
51188
- return true;
51189
- }
51190
- function pruneWithin(value, dropped) {
51191
- if (Array.isArray(value)) {
51192
- for (const child of value) pruneWithin(child, dropped);
51193
- return;
51194
- }
51195
- if (!value || typeof value !== "object") return;
51196
- if (isNode2(value)) {
51197
- pruneNode(value, dropped);
51198
- return;
51199
- }
51200
- for (const child of Object.values(value)) pruneWithin(child, dropped);
51201
- }
51202
- function patchStoreCtas(src, verticals, homeActionId) {
51203
- if (verticals.includes("store")) return { src, dropped: [] };
51204
- const span = homeScreenSpan(src, homeActionId);
51205
- if (!span) return { src, dropped: [] };
51206
- let screen;
51207
- try {
51208
- screen = JSON.parse(src.slice(span.open, span.close));
51209
- } catch {
51210
- return { src, dropped: [] };
51211
- }
51212
- if (!isNode2(screen.payload)) return { src, dropped: [] };
51213
- const dropped = [];
51214
- if (!pruneNode(screen.payload, dropped) || !dropped.length) return { src, dropped: [] };
51215
- return {
51216
- src: src.slice(0, span.open) + serializeAt(screen, src, span.open) + src.slice(span.close),
51217
- dropped
51218
- };
51219
- }
51220
- function patchHomeForVerticals(src, verticals, homeActionId) {
51221
- const installed = new Set(verticals.filter(isKnown));
51222
- const patch = { src, cells: [], added: [], droppedModules: [], droppedCtas: [], unreachable: [] };
51223
- if (!installed.size) return patch;
51224
- const ctas = patchStoreCtas(patchModules(patchGrids(src, installed, patch), installed, patch), [...installed], homeActionId);
51225
- patch.src = ctas.src;
51226
- patch.droppedCtas = ctas.dropped;
51227
- return patch;
51228
- }
51229
- var EXPLORE_CARDS, EXPLORE_ORDER, EXPLORE_MAX_CELLS, MODULE_SOURCE_VERTICALS, isKnown, GRID_VERTICAL_LABELS, escapeRe2, textProps, STORE_SCREENS, isStoreScreen, isNode2;
51230
- var init_home_descriptor = __esm({
51231
- "src/site-analyze/home-descriptor.ts"() {
51232
- init_types2();
51233
- EXPLORE_CARDS = {
51234
- store: { icon: "bag", screenId: "himi_shop", title: "Shop", body: "Browse products and manage your cart." },
51235
- bookings: { icon: "calendar", screenId: "himi_services", title: "Book", body: "Find a service and choose a time." },
51236
- restaurants: { icon: "grid", screenId: "himi_menu", title: "Food", body: "Browse the menu and reserve a table." },
51237
- events: { icon: "calendar", screenId: "himi_events", title: "Events", body: "Discover events and RSVP." },
51238
- plans: { icon: "star", screenId: "himi_plans", title: "Plans", body: "Compare membership plans and join." },
51239
- blog: { icon: "newspaper", screenId: "himi_blog", title: "Blog", body: "Read, save and discuss the latest posts." },
51240
- groups: { icon: "people", screenId: "himi_groups", title: "Groups", body: "Meet members and join the conversation." },
51241
- portfolio: { icon: "photo", screenId: "himi_portfolio", title: "Portfolio", body: "A showcase of our work." },
51242
- galleries: { icon: "image", screenId: "himi_galleries", title: "Galleries", body: "Browse our photo galleries." },
51243
- videos: { icon: "play", screenId: "himi_videos", title: "Videos", body: "Watch our video channels." },
51244
- podcasts: { icon: "microphone", screenId: "himi_podcasts", title: "Podcast", body: "Listen to our latest episodes." },
51245
- programs: { icon: "layers", screenId: "himi_programs", title: "Programs", body: "Follow structured programs at your own pace." },
51246
- reservations: { icon: "clock", screenId: "himi_reservations", title: "Reservations", body: "Your table reservations." },
51247
- donations: { icon: "heart", screenId: "himi_donations", title: "Donate", body: "Support the causes we care about." },
51248
- giftCards: { icon: "tag", screenId: "himi_gift_cards", title: "Gift Cards", body: "Your gift cards and balances." },
51249
- coupons: { icon: "tag", screenId: "himi_coupons", title: "Offers", body: "Deals and coupons available to you." },
51250
- loyalty: { icon: "star", screenId: "himi_loyalty", title: "Rewards", body: "Earn points and redeem them for rewards." },
51251
- forms: { icon: "list", screenId: "himi_forms", title: "Forms", body: "Fill out our forms." },
51252
- cms: { icon: "grid", screenId: "himi_cms", title: "Content", body: "Explore custom content published by this business." },
51253
- members: { icon: "people", screenId: "himi_members", title: "Members", body: "Meet the people in this community." },
51254
- forum: { icon: "chat", screenId: "himi_forum", title: "Forum", body: "Join the discussion with the community." },
51255
- feed: { icon: "chat", screenId: "himi_feed", title: "Feed", body: "Share updates and react to the latest posts." },
51256
- reviews: { icon: "star_fill", screenId: "himi_reviews", title: "Reviews", body: "What customers are saying." },
51257
- inbox: { icon: "chat", screenId: "himi_inbox", title: "Inbox", body: "Your messages with us." },
51258
- invoices: { icon: "doc", screenId: "himi_invoices", title: "Invoices", body: "Your billing history." },
51259
- badges: { icon: "star", screenId: "himi_badges", title: "Badges", body: "Achievements you can earn." },
51260
- notifications: { icon: "bell", screenId: "himi_notifications", title: "Notifications", body: "Your activity and updates." },
51261
- notifsettings: { icon: "settings", screenId: "himi_notif_settings", title: "Notification Settings", body: "Choose what you hear about." }
51262
- };
51263
- EXPLORE_ORDER = [
51264
- "store",
51265
- "bookings",
51266
- "restaurants",
51267
- "events",
51268
- "plans",
51269
- "blog",
51270
- "groups",
51271
- "portfolio",
51272
- "galleries",
51273
- "videos",
51274
- "podcasts",
51275
- "programs",
51276
- "reservations",
51277
- "donations",
51278
- "giftCards",
51279
- "coupons",
51280
- "loyalty",
51281
- "forms",
51282
- "cms",
51283
- "members",
51284
- "forum",
51285
- "feed",
51286
- "reviews",
51287
- "inbox",
51288
- "invoices",
51289
- "badges",
51290
- "notifications",
51291
- "notifsettings"
51292
- ];
51293
- EXPLORE_MAX_CELLS = 8;
51294
- MODULE_SOURCE_VERTICALS = {
51295
- "member.bookings": ["bookings"],
51296
- "member.bookings.schedule": ["bookings"],
51297
- "device.saved_items": ["store", "blog"]
51298
- };
51299
- isKnown = (v) => KNOWN_VERTICALS.includes(v);
51300
- GRID_VERTICAL_LABELS = /* @__PURE__ */ new Set([...KNOWN_VERTICALS, "faq"]);
51301
- escapeRe2 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
51302
- textProps = (n) => n?.body?.props;
51303
- STORE_SCREENS = /* @__PURE__ */ new Set([
51304
- "himi_shop",
51305
- "himi_saved_products",
51306
- "himi_collections",
51307
- "himi_collection",
51308
- "himi_cart",
51309
- "himi_orders"
51310
- ]);
51311
- isStoreScreen = (id) => STORE_SCREENS.has(id) || /^himi_(?:product|order)[_/]/.test(id);
51312
- isNode2 = (v) => !!v && typeof v === "object" && !!v.body && typeof v.body === "object";
51313
- }
51314
- });
51315
-
51316
52224
  // src/site-analyze/apply.ts
51317
- import { existsSync as existsSync54, readFileSync as readFileSync64, writeFileSync as writeFileSync23, copyFileSync as copyFileSync3, mkdirSync as mkdirSync26, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
51318
- import { dirname as dirname30, isAbsolute as isAbsolute4, join as join51, resolve as resolve42, sep as sep11 } from "node:path";
52225
+ import { existsSync as existsSync55, readFileSync as readFileSync65, writeFileSync as writeFileSync24, copyFileSync as copyFileSync3, mkdirSync as mkdirSync27, realpathSync as realpathSync5, statSync as statSync13 } from "node:fs";
52226
+ import { dirname as dirname31, isAbsolute as isAbsolute4, join as join52, resolve as resolve42, sep as sep11 } from "node:path";
51319
52227
  import { spawnSync as spawnSync4 } from "node:child_process";
51320
52228
  function unpairedRedirectNote(overlay) {
51321
52229
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -51330,8 +52238,8 @@ function committedShellProvisioning(path2) {
51330
52238
  function findRepoRoot(cwd) {
51331
52239
  let dir = cwd;
51332
52240
  for (let i = 0; i < 8; i++) {
51333
- if (SHELL_NAMES2.some((name) => existsSync54(join51(dir, shellAppDir(name), "package.json")))) return dir;
51334
- const next = dirname30(dir);
52241
+ if (SHELL_NAMES2.some((name) => existsSync55(join52(dir, shellAppDir(name), "package.json")))) return dir;
52242
+ const next = dirname31(dir);
51335
52243
  if (next === dir) break;
51336
52244
  dir = next;
51337
52245
  }
@@ -51398,11 +52306,11 @@ function mergeCustomerOwned(committed, overlay) {
51398
52306
  }
51399
52307
  function buildMergedContract(options) {
51400
52308
  const { appDir: appDir2, packageDir, overlay } = options;
51401
- const committedPath = join51(appDir2, "provisioning.json");
51402
- if (!existsSync54(committedPath)) {
52309
+ const committedPath = join52(appDir2, "provisioning.json");
52310
+ if (!existsSync55(committedPath)) {
51403
52311
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
51404
52312
  }
51405
- const committed = JSON.parse(readFileSync64(committedPath, "utf8"));
52313
+ const committed = JSON.parse(readFileSync65(committedPath, "utf8"));
51406
52314
  const merged = mergeCustomerOwned(committed, overlay);
51407
52315
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
51408
52316
  const branding = merged.branding ?? {};
@@ -51414,8 +52322,8 @@ function buildMergedContract(options) {
51414
52322
  };
51415
52323
  const stage = (fromRel, toRel) => {
51416
52324
  const from = contained(packageDir, fromRel);
51417
- const to = contained(join51(appDir2, STAGED), toRel.slice(STAGED.length + 1));
51418
- if (!from || !to || !existsSync54(from)) return void 0;
52325
+ const to = contained(join52(appDir2, STAGED), toRel.slice(STAGED.length + 1));
52326
+ if (!from || !to || !existsSync55(from)) return void 0;
51419
52327
  let real;
51420
52328
  let realRoot;
51421
52329
  try {
@@ -51426,8 +52334,8 @@ function buildMergedContract(options) {
51426
52334
  }
51427
52335
  if (!real.startsWith(realRoot + sep11)) return void 0;
51428
52336
  try {
51429
- if (!statSync12(real).isFile()) return void 0;
51430
- mkdirSync26(dirname30(to), { recursive: true });
52337
+ if (!statSync13(real).isFile()) return void 0;
52338
+ mkdirSync27(dirname31(to), { recursive: true });
51431
52339
  copyFileSync3(real, to);
51432
52340
  } catch {
51433
52341
  return void 0;
@@ -51437,11 +52345,11 @@ function buildMergedContract(options) {
51437
52345
  };
51438
52346
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
51439
52347
  if (icon) branding.appIconPath = icon;
51440
- const facesPath = join51(packageDir, "media/fonts/faces.json");
51441
- if (existsSync54(facesPath)) {
52348
+ const facesPath = join52(packageDir, "media/fonts/faces.json");
52349
+ if (existsSync55(facesPath)) {
51442
52350
  let faces = [];
51443
52351
  try {
51444
- const parsed = JSON.parse(readFileSync64(facesPath, "utf8"));
52352
+ const parsed = JSON.parse(readFileSync65(facesPath, "utf8"));
51445
52353
  faces = Array.isArray(parsed) ? parsed : [];
51446
52354
  } catch {
51447
52355
  faces = [];
@@ -51462,11 +52370,11 @@ function buildMergedContract(options) {
51462
52370
  merged.branding = branding;
51463
52371
  const body = `${JSON.stringify(merged, null, 2)}
51464
52372
  `;
51465
- const path2 = join51(packageDir, "provisioning.contract.json");
51466
- writeFileSync23(path2, body);
51467
- const localPath = join51(appDir2, LOCAL_CONTRACT_REL);
51468
- mkdirSync26(dirname30(localPath), { recursive: true });
51469
- writeFileSync23(localPath, body);
52373
+ const path2 = join52(packageDir, "provisioning.contract.json");
52374
+ writeFileSync24(path2, body);
52375
+ const localPath = join52(appDir2, LOCAL_CONTRACT_REL);
52376
+ mkdirSync27(dirname31(localPath), { recursive: true });
52377
+ writeFileSync24(localPath, body);
51470
52378
  staged.push(localPath);
51471
52379
  return { path: path2, staged };
51472
52380
  }
@@ -51474,8 +52382,8 @@ async function applyOverlays(input) {
51474
52382
  const cwd = resolve42(input.cwd);
51475
52383
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
51476
52384
  assertKnownShell(shell);
51477
- const overlay = JSON.parse(readFileSync64(input.overlayPath, "utf8"));
51478
- const tokens = JSON.parse(readFileSync64(input.tokensPath, "utf8"));
52385
+ const overlay = JSON.parse(readFileSync65(input.overlayPath, "utf8"));
52386
+ const tokens = JSON.parse(readFileSync65(input.tokensPath, "utf8"));
51479
52387
  const wrote = [];
51480
52388
  const notes = [];
51481
52389
  const repoRoot2 = findRepoRoot(cwd);
@@ -51485,8 +52393,8 @@ async function applyOverlays(input) {
51485
52393
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
51486
52394
  );
51487
52395
  }
51488
- const appDir2 = join51(repoRoot2, shellAppDir(shell));
51489
- const packageDir = dirname30(resolve42(input.overlayPath));
52396
+ const appDir2 = join52(repoRoot2, shellAppDir(shell));
52397
+ const packageDir = dirname31(resolve42(input.overlayPath));
51490
52398
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
51491
52399
  wrote.push(...staged);
51492
52400
  const r = spawnSync4("npm", ["run", "prepare:customer", "--", "--contract", contract], {
@@ -51507,32 +52415,32 @@ async function applyOverlays(input) {
51507
52415
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
51508
52416
  };
51509
52417
  }
51510
- const pkgProv = join51(cwd, "provisioning.json");
52418
+ const pkgProv = join52(cwd, "provisioning.json");
51511
52419
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
51512
- if (existsSync54(pkgProv)) {
51513
- const cur = JSON.parse(readFileSync64(pkgProv, "utf8"));
52420
+ if (existsSync55(pkgProv)) {
52421
+ const cur = JSON.parse(readFileSync65(pkgProv, "utf8"));
51514
52422
  const merged = deepMerge4(cur, overlay);
51515
- writeFileSync23(pkgProv, JSON.stringify(merged, null, 2) + "\n");
52423
+ writeFileSync24(pkgProv, JSON.stringify(merged, null, 2) + "\n");
51516
52424
  wrote.push(pkgProv);
51517
52425
  } else {
51518
- writeFileSync23(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
52426
+ writeFileSync24(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
51519
52427
  wrote.push(pkgProv);
51520
52428
  }
51521
- const pkgTokens = join51(cwd, "tokens.json");
51522
- if (existsSync54(pkgTokens) && (tokens.colors || tokens.typography)) {
51523
- const cur = JSON.parse(readFileSync64(pkgTokens, "utf8"));
52429
+ const pkgTokens = join52(cwd, "tokens.json");
52430
+ if (existsSync55(pkgTokens) && (tokens.colors || tokens.typography)) {
52431
+ const cur = JSON.parse(readFileSync65(pkgTokens, "utf8"));
51524
52432
  const merged = deepMerge4(cur, tokens);
51525
52433
  const mergedColors = merged.colors;
51526
52434
  if (mergedColors && typeof mergedColors === "object" && !Array.isArray(mergedColors)) {
51527
52435
  merged.colors = alignAccentsToSurfaces(mergedColors, tokens.colors);
51528
52436
  }
51529
- writeFileSync23(pkgTokens, JSON.stringify(merged, null, 2) + "\n");
52437
+ writeFileSync24(pkgTokens, JSON.stringify(merged, null, 2) + "\n");
51530
52438
  wrote.push(pkgTokens);
51531
52439
  }
51532
52440
  const homeAction = homeRefreshActionId(shell);
51533
- const homeSrc = join51(cwd, "dev/index.ts");
51534
- if (homeAction && existsSync54(homeSrc)) {
51535
- const prev = readFileSync64(homeSrc, "utf8");
52441
+ const homeSrc = join52(cwd, "dev/index.ts");
52442
+ if (homeAction && existsSync55(homeSrc)) {
52443
+ const prev = readFileSync65(homeSrc, "utf8");
51536
52444
  const patched = patchBrandParams(prev, overlay, homeAction);
51537
52445
  let next = patched.src;
51538
52446
  if (!patched.foreign && overlay.installedVerticals?.length) {
@@ -51548,20 +52456,20 @@ async function applyOverlays(input) {
51548
52456
  }
51549
52457
  }
51550
52458
  if (next !== prev) {
51551
- writeFileSync23(homeSrc, next);
52459
+ writeFileSync24(homeSrc, next);
51552
52460
  wrote.push(homeSrc);
51553
52461
  }
51554
52462
  if (patched.unparsed) {
51555
52463
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
51556
52464
  }
51557
52465
  }
51558
- if (!input.iconPath || !existsSync54(input.iconPath)) {
51559
- const book = input.iconPath ? dirname30(dirname30(resolve42(input.iconPath))) : void 0;
52466
+ if (!input.iconPath || !existsSync55(input.iconPath)) {
52467
+ const book = input.iconPath ? dirname31(dirname31(resolve42(input.iconPath))) : void 0;
51560
52468
  notes.push(
51561
52469
  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"
51562
52470
  );
51563
52471
  }
51564
- if (input.iconPath && existsSync54(input.iconPath)) {
52472
+ if (input.iconPath && existsSync55(input.iconPath)) {
51565
52473
  try {
51566
52474
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
51567
52475
  wrote.push(...installed.wrote);
@@ -54545,8 +55453,8 @@ var init_dist = __esm({
54545
55453
  * @param jsonString - A JSON string or object representing the deserialized cookies.
54546
55454
  * @param store - The underlying store to persist the deserialized cookies into.
54547
55455
  */
54548
- static fromJSON(jsonString, store) {
54549
- return _CookieJar.deserializeSync(jsonString, store);
55456
+ static fromJSON(jsonString2, store) {
55457
+ return _CookieJar.deserializeSync(jsonString2, store);
54550
55458
  }
54551
55459
  };
54552
55460
  }
@@ -54575,29 +55483,29 @@ function ctasOf(texts) {
54575
55483
  function copyFromEval(evaled) {
54576
55484
  const sections = [];
54577
55485
  for (const raw of evaled.sections ?? []) {
54578
- const heading = copyOrDrop(raw?.heading, CAP2.heading);
55486
+ const heading = copyOrDrop(raw?.heading, CAP3.heading);
54579
55487
  if (!heading) continue;
54580
- const body = copyOrDrop(raw?.body, CAP2.body);
55488
+ const body = copyOrDrop(raw?.body, CAP3.body);
54581
55489
  sections.push(body ? { heading, body } : { heading });
54582
- if (sections.length >= CAP2.sections) break;
55490
+ if (sections.length >= CAP3.sections) break;
54583
55491
  }
54584
55492
  const faq = [];
54585
55493
  for (const raw of evaled.faq ?? []) {
54586
- const q = copyOrDrop(raw?.q, CAP2.q);
54587
- const a = copyOrDrop(raw?.a, CAP2.a);
55494
+ const q = copyOrDrop(raw?.q, CAP3.q);
55495
+ const a = copyOrDrop(raw?.a, CAP3.a);
54588
55496
  if (q && a && q !== a) faq.push({ q, a });
54589
- if (faq.length >= CAP2.faq) break;
55497
+ if (faq.length >= CAP3.faq) break;
54590
55498
  }
54591
- const ctaLabels = dedupe2((evaled.ctaLabels ?? []).map((c) => copyOrDrop(c, CAP2.cta)), CAP2.ctas);
55499
+ const ctaLabels = dedupe2((evaled.ctaLabels ?? []).map((c) => copyOrDrop(c, CAP3.cta)), CAP3.ctas);
54592
55500
  const testimonials = dedupe2(
54593
- (evaled.testimonials ?? []).map((t) => copyOrDrop(t, CAP2.testimonial)),
54594
- CAP2.testimonials
55501
+ (evaled.testimonials ?? []).map((t) => copyOrDrop(t, CAP3.testimonial)),
55502
+ CAP3.testimonials
54595
55503
  );
54596
- const about = copyOrDrop(evaled.about, CAP2.about);
55504
+ const about = copyOrDrop(evaled.about, CAP3.about);
54597
55505
  const meta = {};
54598
- const description = copyOrDrop(evaled.meta?.description, CAP2.meta);
54599
- const ogTitle = copyOrDrop(evaled.meta?.ogTitle, CAP2.meta);
54600
- const ogDescription = copyOrDrop(evaled.meta?.ogDescription, CAP2.meta);
55506
+ const description = copyOrDrop(evaled.meta?.description, CAP3.meta);
55507
+ const ogTitle = copyOrDrop(evaled.meta?.ogTitle, CAP3.meta);
55508
+ const ogDescription = copyOrDrop(evaled.meta?.ogDescription, CAP3.meta);
54601
55509
  if (description) meta.description = description;
54602
55510
  if (ogTitle) meta.ogTitle = ogTitle;
54603
55511
  if (ogDescription) meta.ogDescription = ogDescription;
@@ -54871,7 +55779,7 @@ async function createPlaywrightBrowser(load = () => import("playwright"), siteFe
54871
55779
  };
54872
55780
  return { port, screenshots, siteFetch, pin };
54873
55781
  }
54874
- var PLAYWRIGHT_HINT, PAGE_EVAL, CAP2, DOCUMENT_FETCH_TIMEOUT_MS, MAX_DOCUMENT_BYTES, SUBRESOURCE_PIN_TIMEOUT_MS, NON_NETWORK_SCHEMES, FORWARDED_REQUEST_HEADERS;
55782
+ var PLAYWRIGHT_HINT, PAGE_EVAL, CAP3, DOCUMENT_FETCH_TIMEOUT_MS, MAX_DOCUMENT_BYTES, SUBRESOURCE_PIN_TIMEOUT_MS, NON_NETWORK_SCHEMES, FORWARDED_REQUEST_HEADERS;
54875
55783
  var init_crawl_look = __esm({
54876
55784
  "src/site-analyze/crawl-look.ts"() {
54877
55785
  init_dist();
@@ -55066,7 +55974,7 @@ var init_crawl_look = __esm({
55066
55974
  } catch (e4) {}
55067
55975
  return { wst, nav, headings, hero, imgs, hrefs, buttons, html, htmlLang, velo, sections, ctaLabels, faq, testimonials, about, meta };
55068
55976
  })()`;
55069
- CAP2 = {
55977
+ CAP3 = {
55070
55978
  heading: 120,
55071
55979
  body: 300,
55072
55980
  cta: 80,
@@ -55715,7 +56623,7 @@ __export(cli_exports, {
55715
56623
  runSiteAnalyze: () => runSiteAnalyze,
55716
56624
  runSiteFunctionCli: () => runSiteFunctionCli
55717
56625
  });
55718
- import { readFileSync as readFileSync65 } from "node:fs";
56626
+ import { readFileSync as readFileSync66 } from "node:fs";
55719
56627
  import { resolve as resolve43 } from "node:path";
55720
56628
  function assetFetchFor(_opts) {
55721
56629
  return pinnedFetch2;
@@ -55802,7 +56710,7 @@ async function runSiteFunctionCli(opts) {
55802
56710
  let source;
55803
56711
  if (opts.sourcePath) {
55804
56712
  const p = resolve43(opts.cwd ?? process.cwd(), opts.sourcePath);
55805
- source = readFileSync65(p, "utf8");
56713
+ source = readFileSync66(p, "utf8");
55806
56714
  }
55807
56715
  let body;
55808
56716
  if (opts.bodyRaw != null) {
@@ -55956,9 +56864,9 @@ var init_wix_account = __esm({
55956
56864
  });
55957
56865
 
55958
56866
  // src/wix-verticals.ts
55959
- function resolveVerticals(csv) {
55960
- if (!csv) return [];
55961
- const names = csv.split(",").map((s) => s.trim()).filter(Boolean);
56867
+ function resolveVerticals(csv2) {
56868
+ if (!csv2) return [];
56869
+ const names = csv2.split(",").map((s) => s.trim()).filter(Boolean);
55962
56870
  const out = [];
55963
56871
  const seen = /* @__PURE__ */ new Set();
55964
56872
  for (const name of names) {
@@ -56059,7 +56967,7 @@ __export(site_create_exports, {
56059
56967
  runSiteList: () => runSiteList,
56060
56968
  writeProvisioningBinding: () => writeProvisioningBinding
56061
56969
  });
56062
- import { existsSync as existsSync55, readFileSync as readFileSync66, writeFileSync as writeFileSync24 } from "node:fs";
56970
+ import { existsSync as existsSync56, readFileSync as readFileSync67, writeFileSync as writeFileSync25 } from "node:fs";
56063
56971
  import { resolve as resolvePath2 } from "node:path";
56064
56972
  function oauthClientName(projectName) {
56065
56973
  return `${projectName} client`;
@@ -56208,7 +57116,7 @@ async function runSiteCreate(opts) {
56208
57116
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
56209
57117
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
56210
57118
  }
56211
- function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync55, readFileSync: readFileSync66, writeFileSync: writeFileSync24 }) {
57119
+ function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync56, readFileSync: readFileSync67, writeFileSync: writeFileSync25 }) {
56212
57120
  const msid = binding2.siteId;
56213
57121
  const path2 = resolvePath2(target);
56214
57122
  const committed = committedShellProvisioning(path2);
@@ -56881,11 +57789,11 @@ var init_context = __esm({
56881
57789
  });
56882
57790
 
56883
57791
  // src/cli.ts
56884
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync67, mkdirSync as mkdirSync27, writeFileSync as writeFileSync25, existsSync as existsSync56, rmSync as rmSync10, readdirSync as readdirSync25, statSync as statSync13, lstatSync as lstatSync6, accessSync, constants } from "node:fs";
57792
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync68, mkdirSync as mkdirSync28, writeFileSync as writeFileSync26, existsSync as existsSync57, rmSync as rmSync11, readdirSync as readdirSync26, statSync as statSync14, lstatSync as lstatSync6, accessSync, constants } from "node:fs";
56885
57793
  import { execFile as execFile4, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn5 } from "node:child_process";
56886
57794
  import { createHash as createHash24, randomUUID as randomUUID3 } from "node:crypto";
56887
57795
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
56888
- import { join as join52, resolve as resolve44, relative as relative8, basename as basename12, dirname as dirname31 } from "node:path";
57796
+ import { join as join53, resolve as resolve44, relative as relative8, basename as basename12, dirname as dirname32 } from "node:path";
56889
57797
  import { fileURLToPath as fileURLToPath24 } from "node:url";
56890
57798
 
56891
57799
  // ../serve-cli/src/client.ts
@@ -60163,8 +61071,8 @@ var str5 = (v) => typeof v === "string" ? v : void 0;
60163
61071
  var num = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
60164
61072
  function embeddedTemplateCatalog() {
60165
61073
  return availableTemplates().map((name) => {
60166
- const path2 = join52(templateSourceDir(name), TEMPLATE_META_FILE);
60167
- const meta = JSON.parse(readFileSync67(path2, "utf8"));
61074
+ const path2 = join53(templateSourceDir(name), TEMPLATE_META_FILE);
61075
+ const meta = JSON.parse(readFileSync68(path2, "utf8"));
60168
61076
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
60169
61077
  throw new Error(`${path2} must be { title: string, description: string, tags: string[], category?: string }`);
60170
61078
  }
@@ -60197,6 +61105,12 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
60197
61105
  himi template prompt <name> print the prompt that starts an app from that template
60198
61106
  himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted]
60199
61107
  publish an app AS a himi init --template on the serve plane
61108
+ himi template trim --keep <id,id,\u2026> [--content <dir>] [--links follow|drop] [--dry-run] [--force]
61109
+ cut a scaffolded template to the screens you keep (the home and
61110
+ any drawer body stay): removes the other screens, their navigation rows and
61111
+ unshared tier5-src workers, then proves it with validate --strict
61112
+ (rolled back on failure). Refuses when a kept screen links to a
61113
+ removed one \u2014 --links follow keeps those too, --links drop removes the taps
60200
61114
  himi build [--content <dir>] [--binary] compile Tier-5 workers with esbuild
60201
61115
  himi eject <auth|checkout|screens> [--content <dir>]
60202
61116
  the ladder's escape hatch \u2014 copy a shared flow module's
@@ -60752,7 +61666,7 @@ WHAT A PATCH DOES NOT CARRY \u2014 all reported in the output, never silent
60752
61666
  var COMMANDS = [
60753
61667
  { name: "captcha", summary: "create and store native CAPTCHA keys in Serve, or inspect setup status; customer setup verifies Wix site ownership; does not enable member CAPTCHA SDKs", required: "setup|status --app <app> [--content <dir>] [--site <msid>] [--ios-bundle-id <id>] [--android-package-name <id>] [--target <t>]" },
60754
61668
  { name: "init", summary: "scaffold a starter content package (OTA-only; no native shells). --template <t> starts from a full reference app (branded-lite = consumer storefront over a Wix site; owner-lite = owner CRUD/management) instead of the minimal counter; --templates lists bundled ones; --remote fetches the template off the serve plane (falls back to the bundled copy offline)", required: "<name> [--dir] [--template <t>] [--remote] | --templates" },
60755
- { name: "template", summary: "starter-template catalog + publishing. Every template is a SHOWCASE APP \u2014 a real published release you can open running before you start from it. `list` prints available templates with metadata (title/description/tags) plus each one's start prompt \u2014 pass --remote to include the serve-plane catalog so an agent can pick one; `prompt <name>` prints just that sentence for one template (the skill URL is the public door, or the plane named by --target/--serve-url); `publish` publishes an app AS a `himi init --template` (needs a himalaya.template.json; builds a previewable release + attaches the scaffold source; --source-dir overrides which tree becomes the source, --name sets the template name; bakes a launcher icon \u2014 --icon <png>, or the committed template-icons/<name>.png \u2014 and extracts accent/palette gallery meta from the scaffold's tokens.json; --visibility unlisted keeps it out of the /v1/templates catalog \u2014 templates default to public)", required: "list [--remote] | prompt <name> | publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--target <t>]" },
61669
+ { name: "template", summary: "starter-template catalog + publishing. Every template is a SHOWCASE APP \u2014 a real published release you can open running before you start from it. `list` prints available templates with metadata (title/description/tags) plus each one's start prompt \u2014 pass --remote to include the serve-plane catalog so an agent can pick one; `prompt <name>` prints just that sentence for one template (the skill URL is the public door, or the plane named by --target/--serve-url); `publish` publishes an app AS a `himi init --template` (needs a himalaya.template.json; builds a previewable release + attaches the scaffold source; --source-dir overrides which tree becomes the source, --name sets the template name; bakes a launcher icon \u2014 --icon <png>, or the committed template-icons/<name>.png \u2014 and extracts accent/palette gallery meta from the scaffold's tokens.json; --visibility unlisted keeps it out of the /v1/templates catalog \u2014 templates default to public); `trim --keep <ids>` cuts an app scaffolded from a template down to those screens (the home, and any screen the navigation pins such as a drawer body, always stays) \u2014 it deletes the other SCREENS/TEMPLATED entries whole, prunes navigation (linkableScreens, tabBar, drawer, deep links, surfaces) and drops tier5-src workers no kept screen uses, then runs validate --strict and rolls everything back if it fails (--force keeps it, --dry-run writes nothing). A kept screen that links to a removed one is refused unless --links follow (keep those too) or --links drop (remove the taps)", required: "list [--remote] | prompt <name> | publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--target <t>] | trim --keep <id,id,\u2026> [--content <dir>] [--links follow|drop] [--dry-run] [--force]" },
60756
61670
  { name: "build", summary: "compile tier5-src/<name>/index.ts -> dist/bundles/<name>.bundle.js with esbuild (--binary = resolve published @himalaya/*); semantic TypeScript checking belongs to himi validate", required: "[--content <dir>] [--binary]" },
60757
61671
  { name: "functions", summary: "inspect/invoke server functions and operate durable jobs or schedules", required: "list|describe|openapi|invoke | jobs list|get|cancel|retry|dlq | schedule describe|invoke [--app <a>] [--environment preview|production] [--release <id>]" },
60758
61672
  { name: "eject", summary: "take ownership of a shared flow module (auth|checkout|screens): copies its source into tier5-src/_ejected/ and repoints this package's imports at the copy. The ladder's escape hatch \u2014 for when the factory options cannot express what you need. AFTER EJECTING, CENTRAL FIXES NO LONGER REACH THIS APP, including future corrections to the fail-closed entitlement and confirm-the-session rules. The floor is unaffected: credentials still go only through the native auth actions and money only through the cashier rail.", required: "<auth|checkout|screens> [--content <dir>]" },
@@ -60807,7 +61721,7 @@ var COMMANDS = [
60807
61721
  ];
60808
61722
  function cliVersion() {
60809
61723
  try {
60810
- return JSON.parse(readFileSync67(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
61724
+ return JSON.parse(readFileSync68(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
60811
61725
  } catch {
60812
61726
  return "unknown";
60813
61727
  }
@@ -60925,7 +61839,7 @@ function ageInDays(publishedAt, now = Date.now()) {
60925
61839
  }
60926
61840
  function packageVersion(path2) {
60927
61841
  try {
60928
- const version2 = JSON.parse(readFileSync67(path2, "utf8")).version;
61842
+ const version2 = JSON.parse(readFileSync68(path2, "utf8")).version;
60929
61843
  return typeof version2 === "string" && version2 ? version2 : void 0;
60930
61844
  } catch {
60931
61845
  return void 0;
@@ -60934,9 +61848,9 @@ function packageVersion(path2) {
60934
61848
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
60935
61849
  let dir = resolve44(fromDir);
60936
61850
  for (; ; ) {
60937
- const version2 = packageVersion(join52(dir, "node_modules", "@wix", "himalaya", "package.json"));
61851
+ const version2 = packageVersion(join53(dir, "node_modules", "@wix", "himalaya", "package.json"));
60938
61852
  if (version2) return { version: version2, source: "local" };
60939
- const parent = dirname31(dir);
61853
+ const parent = dirname32(dir);
60940
61854
  if (parent === dir) break;
60941
61855
  dir = parent;
60942
61856
  }
@@ -60944,7 +61858,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
60944
61858
  if (bundled) return { version: bundled, source: "bundled" };
60945
61859
  try {
60946
61860
  const root = execFileSync7("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
60947
- const version2 = root ? packageVersion(join52(root, "@wix", "himalaya", "package.json")) : void 0;
61861
+ const version2 = root ? packageVersion(join53(root, "@wix", "himalaya", "package.json")) : void 0;
60948
61862
  return version2 ? { version: version2, source: "global" } : {};
60949
61863
  } catch {
60950
61864
  return {};
@@ -60991,11 +61905,11 @@ var UPDATE_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1e3;
60991
61905
  var UPDATE_SNOOZE_MS = 24 * 60 * 60 * 1e3;
60992
61906
  var UPDATE_REEXEC_ENV = "HIMI_UPDATED_ONCE";
60993
61907
  function updateCachePath(env = process.env) {
60994
- return env.HIMI_UPDATE_CACHE_FILE ?? join52(homedir7(), ".himalaya", "update-check.json");
61908
+ return env.HIMI_UPDATE_CACHE_FILE ?? join53(homedir7(), ".himalaya", "update-check.json");
60995
61909
  }
60996
61910
  function readUpdateCache(path2) {
60997
61911
  try {
60998
- const parsed = JSON.parse(readFileSync67(path2, "utf8"));
61912
+ const parsed = JSON.parse(readFileSync68(path2, "utf8"));
60999
61913
  return parsed && typeof parsed === "object" ? parsed : {};
61000
61914
  } catch {
61001
61915
  return {};
@@ -61003,8 +61917,8 @@ function readUpdateCache(path2) {
61003
61917
  }
61004
61918
  function writeUpdateCache(path2, cache) {
61005
61919
  try {
61006
- mkdirSync27(dirname31(path2), { recursive: true, mode: 448 });
61007
- writeFileSync25(path2, JSON.stringify(cache, null, 2) + "\n", { mode: 384 });
61920
+ mkdirSync28(dirname32(path2), { recursive: true, mode: 448 });
61921
+ writeFileSync26(path2, JSON.stringify(cache, null, 2) + "\n", { mode: 384 });
61008
61922
  } catch {
61009
61923
  }
61010
61924
  }
@@ -61063,12 +61977,12 @@ function createUpdateRunner(spawn6 = spawnSync5) {
61063
61977
  },
61064
61978
  installedVersion: () => {
61065
61979
  const root = globalNodeModulesRoot();
61066
- return root ? packageVersion(join52(root, "@wix", "himalaya-cli", "package.json")) : void 0;
61980
+ return root ? packageVersion(join53(root, "@wix", "himalaya-cli", "package.json")) : void 0;
61067
61981
  },
61068
61982
  reexec: (argv) => {
61069
61983
  const root = globalNodeModulesRoot();
61070
- const entry = root ? join52(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
61071
- if (!entry || !existsSync56(entry)) return void 0;
61984
+ const entry = root ? join53(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
61985
+ if (!entry || !existsSync57(entry)) return void 0;
61072
61986
  const result = spawnSync5(process.execPath, [entry, ...argv], {
61073
61987
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
61074
61988
  stdio: "inherit",
@@ -61115,7 +62029,7 @@ function staleUpdateLockFile(cachePath, now) {
61115
62029
  for (const path2 of [`${cachePath}.lock.break`, `${cachePath}.lock`]) {
61116
62030
  let content;
61117
62031
  try {
61118
- content = readFileSync67(path2, "utf8");
62032
+ content = readFileSync68(path2, "utf8");
61119
62033
  } catch {
61120
62034
  continue;
61121
62035
  }
@@ -61123,7 +62037,7 @@ function staleUpdateLockFile(cachePath, now) {
61123
62037
  let since = stamped;
61124
62038
  if (!Number.isFinite(since)) {
61125
62039
  try {
61126
- since = statSync13(path2).mtimeMs;
62040
+ since = statSync14(path2).mtimeMs;
61127
62041
  } catch {
61128
62042
  continue;
61129
62043
  }
@@ -61138,14 +62052,14 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
61138
62052
  const mine = lockStamp(now);
61139
62053
  const readLock = (path2) => {
61140
62054
  try {
61141
- return readFileSync67(path2, "utf8");
62055
+ return readFileSync68(path2, "utf8");
61142
62056
  } catch {
61143
62057
  return void 0;
61144
62058
  }
61145
62059
  };
61146
62060
  const tryCreate = (path2) => {
61147
62061
  try {
61148
- writeFileSync25(path2, mine, { flag: "wx", mode: 384 });
62062
+ writeFileSync26(path2, mine, { flag: "wx", mode: 384 });
61149
62063
  return true;
61150
62064
  } catch {
61151
62065
  return false;
@@ -61153,13 +62067,13 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
61153
62067
  };
61154
62068
  const removeIfMine = (path2) => {
61155
62069
  try {
61156
- if (readLock(path2) === mine) rmSync10(path2, { force: true });
62070
+ if (readLock(path2) === mine) rmSync11(path2, { force: true });
61157
62071
  } catch {
61158
62072
  }
61159
62073
  };
61160
62074
  const release = () => removeIfMine(lockPath);
61161
62075
  try {
61162
- mkdirSync27(dirname31(lockPath), { recursive: true, mode: 448 });
62076
+ mkdirSync28(dirname32(lockPath), { recursive: true, mode: 448 });
61163
62077
  } catch {
61164
62078
  return () => {
61165
62079
  };
@@ -61176,7 +62090,7 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
61176
62090
  hooks.underBreakLock?.();
61177
62091
  if (current === void 0) return tryCreate(lockPath) ? release : void 0;
61178
62092
  if (!isAbandoned(current)) return void 0;
61179
- rmSync10(lockPath, { force: true });
62093
+ rmSync11(lockPath, { force: true });
61180
62094
  return tryCreate(lockPath) ? release : void 0;
61181
62095
  } finally {
61182
62096
  removeIfMine(breakPath);
@@ -61676,9 +62590,9 @@ function executableOnPath(name, env = process.env) {
61676
62590
  const suffixes = process.platform === "win32" ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
61677
62591
  for (const dir of entries) {
61678
62592
  for (const suffix of suffixes) {
61679
- const candidate = join52(dir, `${name}${suffix}`);
62593
+ const candidate = join53(dir, `${name}${suffix}`);
61680
62594
  try {
61681
- if (!statSync13(candidate).isFile()) continue;
62595
+ if (!statSync14(candidate).isFile()) continue;
61682
62596
  if (process.platform !== "win32") accessSync(candidate, constants.X_OK);
61683
62597
  return true;
61684
62598
  } catch {
@@ -61785,7 +62699,7 @@ async function nativeConfigAdvisories(app, configSource) {
61785
62699
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
61786
62700
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
61787
62701
  const iosYmlPath = iosProjectYml2(app);
61788
- const iosYml = existsSync56(iosYmlPath) ? readFileSync67(iosYmlPath, "utf8") : "";
62702
+ const iosYml = existsSync57(iosYmlPath) ? readFileSync68(iosYmlPath, "utf8") : "";
61789
62703
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
61790
62704
  if (!optedIn) {
61791
62705
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -61799,19 +62713,19 @@ async function nativeConfigAdvisories(app, configSource) {
61799
62713
  }
61800
62714
  function readConfigSource(dir) {
61801
62715
  try {
61802
- return readFileSync67(join52(dir, "config.ts"), "utf8");
62716
+ return readFileSync68(join53(dir, "config.ts"), "utf8");
61803
62717
  } catch {
61804
62718
  return null;
61805
62719
  }
61806
62720
  }
61807
62721
  function writePngFile(path2, base64) {
61808
- mkdirSync27(dirname31(path2), { recursive: true });
61809
- writeFileSync25(path2, Buffer.from(base64, "base64"));
62722
+ mkdirSync28(dirname32(path2), { recursive: true });
62723
+ writeFileSync26(path2, Buffer.from(base64, "base64"));
61810
62724
  return path2;
61811
62725
  }
61812
62726
  async function initFromRemoteTemplate(name, parentDir, template, c) {
61813
62727
  const dir = resolve44(parentDir, name);
61814
- if (existsSync56(join52(dir, "himalaya.content.json"))) {
62728
+ if (existsSync57(join53(dir, "himalaya.content.json"))) {
61815
62729
  throw new Error(`content package already exists at ${dir}`);
61816
62730
  }
61817
62731
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -61854,11 +62768,11 @@ function symlinkDestinationIssue(dest) {
61854
62768
  function restoreDestinationIssue(dest, force) {
61855
62769
  const link = symlinkDestinationIssue(dest);
61856
62770
  if (link) return link;
61857
- if (!existsSync56(dest)) return null;
61858
- if (!statSync13(dest).isDirectory()) {
62771
+ if (!existsSync57(dest)) return null;
62772
+ if (!statSync14(dest).isDirectory()) {
61859
62773
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
61860
62774
  }
61861
- if (readdirSync25(dest).length > 0 && !force) {
62775
+ if (readdirSync26(dest).length > 0 && !force) {
61862
62776
  return {
61863
62777
  error: `${dest} already exists and is not empty`,
61864
62778
  hint: "pass --force to write into it anyway, or --out/--name to land somewhere else"
@@ -61869,8 +62783,8 @@ function restoreDestinationIssue(dest, force) {
61869
62783
  function isHimalayaMonorepoCheckout(start) {
61870
62784
  let dir = resolve44(start);
61871
62785
  for (; ; ) {
61872
- if (existsSync56(join52(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync56(join52(dir, "core", "serve", "src", "server.ts"))) return true;
61873
- const parent = dirname31(dir);
62786
+ if (existsSync57(join53(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync57(join53(dir, "core", "serve", "src", "server.ts"))) return true;
62787
+ const parent = dirname32(dir);
61874
62788
  if (parent === dir) return false;
61875
62789
  dir = parent;
61876
62790
  }
@@ -61912,7 +62826,7 @@ function materializeForPreview(appDir2, slug, displayName) {
61912
62826
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
61913
62827
  );
61914
62828
  const repoRoot2 = resolve44(fileURLToPath24(new URL("../../..", import.meta.url)));
61915
- const root = join52(repoRoot2, "node_modules", ".cache", "himi-template-preview");
62829
+ const root = join53(repoRoot2, "node_modules", ".cache", "himi-template-preview");
61916
62830
  const stillRunning = (pid) => {
61917
62831
  try {
61918
62832
  process.kill(pid, 0);
@@ -61922,25 +62836,25 @@ function materializeForPreview(appDir2, slug, displayName) {
61922
62836
  }
61923
62837
  };
61924
62838
  try {
61925
- for (const entry of readdirSync25(root, { withFileTypes: true })) {
62839
+ for (const entry of readdirSync26(root, { withFileTypes: true })) {
61926
62840
  if (!entry.isDirectory()) continue;
61927
62841
  const pid = Number(entry.name.startsWith(`${slug}-`) ? entry.name.slice(slug.length + 1) : NaN);
61928
62842
  if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && !stillRunning(pid)) {
61929
- rmSync10(join52(root, entry.name), { recursive: true, force: true });
62843
+ rmSync11(join53(root, entry.name), { recursive: true, force: true });
61930
62844
  }
61931
62845
  }
61932
62846
  } catch {
61933
62847
  }
61934
- const dest = join52(root, `${slug}-${process.pid}`);
61935
- rmSync10(dest, { recursive: true, force: true });
61936
- mkdirSync27(dest, { recursive: true });
62848
+ const dest = join53(root, `${slug}-${process.pid}`);
62849
+ rmSync11(dest, { recursive: true, force: true });
62850
+ mkdirSync28(dest, { recursive: true });
61937
62851
  materializeSource(forBuild, displayName, dest, slug);
61938
62852
  return dest;
61939
62853
  }
61940
62854
  async function appId(dir) {
61941
62855
  try {
61942
- const { readFileSync: readFileSync68 } = await import("node:fs");
61943
- const m = JSON.parse(readFileSync68(join52(dir, "himalaya.content.json"), "utf8"));
62856
+ const { readFileSync: readFileSync69 } = await import("node:fs");
62857
+ const m = JSON.parse(readFileSync69(join53(dir, "himalaya.content.json"), "utf8"));
61944
62858
  if (m.name) return m.name;
61945
62859
  } catch {
61946
62860
  }
@@ -61948,8 +62862,8 @@ async function appId(dir) {
61948
62862
  }
61949
62863
  async function contentVisibility(dir) {
61950
62864
  try {
61951
- const { readFileSync: readFileSync68 } = await import("node:fs");
61952
- const m = JSON.parse(readFileSync68(join52(dir, "himalaya.content.json"), "utf8"));
62865
+ const { readFileSync: readFileSync69 } = await import("node:fs");
62866
+ const m = JSON.parse(readFileSync69(join53(dir, "himalaya.content.json"), "utf8"));
61953
62867
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
61954
62868
  } catch {
61955
62869
  }
@@ -62173,7 +63087,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
62173
63087
  try {
62174
63088
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
62175
63089
  const bres = await buildContentForValidation2(dir, { resolution });
62176
- out = mkdtempSync4(join52(tmpdir5(), "himi-ux-record-"));
63090
+ out = mkdtempSync4(join53(tmpdir5(), "himi-ux-record-"));
62177
63091
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
62178
63092
  const outcome = await buildReleaseFromContent2(dir, out, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {}, collect: { lint: true } });
62179
63093
  const releaseId = outcome.manifest.releaseId;
@@ -62186,7 +63100,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
62186
63100
  return null;
62187
63101
  } finally {
62188
63102
  if (out !== void 0) try {
62189
- rmSync10(out, { recursive: true, force: true });
63103
+ rmSync11(out, { recursive: true, force: true });
62190
63104
  } catch {
62191
63105
  }
62192
63106
  }
@@ -62340,7 +63254,7 @@ async function producedWidgetFamilies(dir) {
62340
63254
  }
62341
63255
  const outDir = tier5Dir.bundleOutDir(dir);
62342
63256
  try {
62343
- readdirSync25(outDir);
63257
+ readdirSync26(outDir);
62344
63258
  } catch {
62345
63259
  return { families: void 0, source: "unbuilt" };
62346
63260
  }
@@ -62351,7 +63265,7 @@ async function producedWidgetFamilies(dir) {
62351
63265
  let js;
62352
63266
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
62353
63267
  try {
62354
- js = readFileSync67(join52(outDir, candidate), "utf8");
63268
+ js = readFileSync68(join53(outDir, candidate), "utf8");
62355
63269
  break;
62356
63270
  } catch {
62357
63271
  }
@@ -62721,12 +63635,34 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62721
63635
  print({ ok: true, templates: merged, remote: true });
62722
63636
  return 0;
62723
63637
  }
63638
+ if (sub === "trim") {
63639
+ const { runTemplateTrim: runTemplateTrim2 } = await Promise.resolve().then(() => (init_template_trim(), template_trim_exports));
63640
+ const outcome = await runTemplateTrim2(
63641
+ { dir: contentDir(flags), keep: flags.keep, links: flags.links, dryRun: flags["dry-run"] === true, force: flags.force === true },
63642
+ {
63643
+ validate: async (dir) => {
63644
+ const out = [];
63645
+ const err = [];
63646
+ const code = await run(["validate", "--content", dir, "--strict"], { log: (l) => out.push(l), error: (l) => err.push(l) }, deps);
63647
+ let counts;
63648
+ try {
63649
+ counts = JSON.parse(out[out.length - 1] ?? "").counts;
63650
+ } catch {
63651
+ }
63652
+ return { code, counts, ...code !== 0 ? { tail: [...out, ...err].join("\n").slice(-4e3) } : {} };
63653
+ }
63654
+ }
63655
+ );
63656
+ if (outcome.code === 2) io.error(JSON.stringify(outcome.result));
63657
+ else print(outcome.result);
63658
+ return outcome.code;
63659
+ }
62724
63660
  if (sub === "publish") {
62725
63661
  const appDir2 = resolve44(str5(flags.dir) ?? process.cwd());
62726
63662
  const sourceDir = resolve44(str5(flags["source-dir"]) ?? appDir2);
62727
63663
  let meta;
62728
63664
  try {
62729
- meta = JSON.parse(readFileSync67(join52(sourceDir, TEMPLATE_META_FILE), "utf8"));
63665
+ meta = JSON.parse(readFileSync68(join53(sourceDir, TEMPLATE_META_FILE), "utf8"));
62730
63666
  } catch {
62731
63667
  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>)` }));
62732
63668
  return 2;
@@ -62736,7 +63672,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62736
63672
  return 2;
62737
63673
  }
62738
63674
  try {
62739
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync67(join52(sourceDir, "tokens.json"), "utf8")));
63675
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync68(join53(sourceDir, "tokens.json"), "utf8")));
62740
63676
  meta = { ...appearance, ...meta };
62741
63677
  } catch {
62742
63678
  }
@@ -62748,7 +63684,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62748
63684
  const force = flags.force === true;
62749
63685
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
62750
63686
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
62751
- const out = mkdtempSync4(join52(tmpdir5(), "himi-template-"));
63687
+ const out = mkdtempSync4(join53(tmpdir5(), "himi-template-"));
62752
63688
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
62753
63689
  const outcome = await buildReleaseFromContent2(buildDir, out, {
62754
63690
  ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
@@ -62801,7 +63737,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62801
63737
  let iconHash;
62802
63738
  if (iconPath) {
62803
63739
  try {
62804
- const bytes2 = readFileSync67(iconPath);
63740
+ const bytes2 = readFileSync68(iconPath);
62805
63741
  payload.icon = bytes2.toString("base64");
62806
63742
  iconHash = createHash24("sha256").update(bytes2).digest("hex");
62807
63743
  } catch {
@@ -62970,7 +63906,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62970
63906
  });
62971
63907
  return 0;
62972
63908
  }
62973
- io.error(JSON.stringify({ error: "template needs a subcommand", usage: "himi template list [--remote] | himi template prompt <name> | himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--target <t>]" }));
63909
+ io.error(JSON.stringify({ error: "template needs a subcommand", usage: "himi template list [--remote] | himi template prompt <name> | himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--target <t>] | himi template trim --keep <id,id,\u2026> [--content <dir>] [--links follow|drop] [--dry-run] [--force]" }));
62974
63910
  return 2;
62975
63911
  }
62976
63912
  case "build": {
@@ -62997,12 +63933,12 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
62997
63933
  print({ ok: false, error: "eject needs a module", hint: `one of: ${EJECTABLE2.join(", ")}` });
62998
63934
  return 2;
62999
63935
  }
63000
- const { readdirSync: readdirSync26, existsSync: fileExists } = await import("node:fs");
63936
+ const { readdirSync: readdirSync27, existsSync: fileExists } = await import("node:fs");
63001
63937
  const { join: pathJoin } = await import("node:path");
63002
63938
  const targets = [];
63003
63939
  const t5 = pathJoin(dir, "tier5-src");
63004
63940
  if (fileExists(t5)) {
63005
- for (const d of readdirSync26(t5, { withFileTypes: true })) {
63941
+ for (const d of readdirSync27(t5, { withFileTypes: true })) {
63006
63942
  if (d.isDirectory()) targets.push(pathJoin(t5, d.name, "index.ts"));
63007
63943
  }
63008
63944
  }
@@ -63034,7 +63970,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63034
63970
  const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
63035
63971
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
63036
63972
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
63037
- const out = mkdtempSync4(join52(tmpdir5(), "himi-validate-"));
63973
+ const out = mkdtempSync4(join53(tmpdir5(), "himi-validate-"));
63038
63974
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
63039
63975
  const outcome = await buildReleaseFromContent2(dir, out, {
63040
63976
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -63071,7 +64007,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63071
64007
  const path2 = dpxSessionTokenPath();
63072
64008
  let raw;
63073
64009
  try {
63074
- raw = readFileSync67(path2, "utf8");
64010
+ raw = readFileSync68(path2, "utf8");
63075
64011
  } catch {
63076
64012
  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`);
63077
64013
  }
@@ -63241,7 +64177,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63241
64177
  case "test": {
63242
64178
  const dir = contentDir(flags);
63243
64179
  const structuredOutput = flags.json === true || io.isTTY !== true;
63244
- if (existsSync56(join52(dir, "functions"))) {
64180
+ if (existsSync57(join53(dir, "functions"))) {
63245
64181
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
63246
64182
  const loaded = await loadFunctionsApp2(dir);
63247
64183
  const results = [];
@@ -63328,7 +64264,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63328
64264
  }
63329
64265
  }
63330
64266
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
63331
- const out = mkdtempSync4(join52(tmpdir5(), "himi-test-"));
64267
+ const out = mkdtempSync4(join53(tmpdir5(), "himi-test-"));
63332
64268
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
63333
64269
  const outcome = await buildReleaseFromContent2(dir, out, {
63334
64270
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -63373,7 +64309,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63373
64309
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
63374
64310
  return 2;
63375
64311
  }
63376
- const sources = collectWorkerSources2(join52(dir, "tier5-src"), screen.bundleName);
64312
+ const sources = collectWorkerSources2(join53(dir, "tier5-src"), screen.bundleName);
63377
64313
  if (!sources) {
63378
64314
  io.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
63379
64315
  return 2;
@@ -63534,7 +64470,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63534
64470
  const sp = await runShotsPass2({
63535
64471
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
63536
64472
  tokens: outcome.tokens,
63537
- outDir: join52(dir, ".himi", "shots"),
64473
+ outDir: join53(dir, ".himi", "shots"),
63538
64474
  width: 390,
63539
64475
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
63540
64476
  ...budgetMs ? { budgetMs } : {},
@@ -63544,7 +64480,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63544
64480
  renderDescriptorWeb: renderDescriptorWeb2,
63545
64481
  renderPngBatch: renderPngBatch2,
63546
64482
  writePng: (path2, base64) => writePngFile(path2, base64),
63547
- clearDir: (d) => rmSync10(d, { recursive: true, force: true })
64483
+ clearDir: (d) => rmSync11(d, { recursive: true, force: true })
63548
64484
  }
63549
64485
  });
63550
64486
  writtenShots = sp.written;
@@ -63572,7 +64508,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63572
64508
  releaseId: result.releaseId,
63573
64509
  releasePayload: release,
63574
64510
  descriptors: attestedDescriptors,
63575
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join52(dir, ".himi", "shots", name)])),
64511
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join53(dir, ".himi", "shots", name)])),
63576
64512
  coverage: {
63577
64513
  screens: crawledScreens.size,
63578
64514
  actionsDispatched: result.actions.exercised,
@@ -63661,7 +64597,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63661
64597
  return 2;
63662
64598
  }
63663
64599
  try {
63664
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync67(stateArg.replace(/^@/, ""), "utf8");
64600
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync68(stateArg.replace(/^@/, ""), "utf8");
63665
64601
  injectedState = JSON.parse(raw);
63666
64602
  } catch (e) {
63667
64603
  io.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e.message}` }));
@@ -63676,7 +64612,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63676
64612
  return 2;
63677
64613
  }
63678
64614
  try {
63679
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync67(inputArg.replace(/^@/, ""), "utf8");
64615
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync68(inputArg.replace(/^@/, ""), "utf8");
63680
64616
  const parsed = JSON.parse(raw);
63681
64617
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
63682
64618
  injectedInput = parsed;
@@ -63725,7 +64661,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63725
64661
  }
63726
64662
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
63727
64663
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
63728
- const out = mkdtempSync4(join52(tmpdir5(), "himi-render-"));
64664
+ const out = mkdtempSync4(join53(tmpdir5(), "himi-render-"));
63729
64665
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
63730
64666
  const outcome = await buildReleaseFromContent2(dir, out, {
63731
64667
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -63741,7 +64677,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
63741
64677
  let attestationPngFile;
63742
64678
  if (typeof flags.png === "string" && r.pngBase64) {
63743
64679
  pngFile = writePngFile(str5(flags.png), r.pngBase64);
63744
- attestationPngFile = writePngFile(join52(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
64680
+ attestationPngFile = writePngFile(join53(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
63745
64681
  }
63746
64682
  if (renderWidth !== void 0 && r.renderable) {
63747
64683
  recordRenderWidth(dir, {
@@ -64073,20 +65009,20 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64073
65009
  io.error(JSON.stringify(linked));
64074
65010
  return 1;
64075
65011
  }
64076
- if (existsSync56(dest)) {
64077
- if (!statSync13(dest).isDirectory()) {
65012
+ if (existsSync57(dest)) {
65013
+ if (!statSync14(dest).isDirectory()) {
64078
65014
  io.error(JSON.stringify({
64079
65015
  error: `${dest} exists and is not a directory`,
64080
65016
  hint: "pass --out/--name to land somewhere else, or remove the file"
64081
65017
  }));
64082
65018
  return 1;
64083
65019
  }
64084
- const occupied = readdirSync25(dest).length > 0;
65020
+ const occupied = readdirSync26(dest).length > 0;
64085
65021
  if (occupied && !force) {
64086
65022
  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" }));
64087
65023
  return 1;
64088
65024
  }
64089
- if (replace && occupied && !existsSync56(join52(dest, "himalaya.content.json"))) {
65025
+ if (replace && occupied && !existsSync57(join53(dest, "himalaya.content.json"))) {
64090
65026
  io.error(JSON.stringify({
64091
65027
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
64092
65028
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -64225,7 +65161,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64225
65161
  const force = flags.force === true;
64226
65162
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
64227
65163
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
64228
- const out = mkdtempSync4(join52(tmpdir5(), "himi-push-"));
65164
+ const out = mkdtempSync4(join53(tmpdir5(), "himi-push-"));
64229
65165
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
64230
65166
  const outcome = await buildReleaseFromContent2(dir, out, {
64231
65167
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -64745,7 +65681,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64745
65681
  }
64746
65682
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
64747
65683
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
64748
- const built = mkdtempSync4(join52(tmpdir5(), "himi-pack-"));
65684
+ const built = mkdtempSync4(join53(tmpdir5(), "himi-pack-"));
64749
65685
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
64750
65686
  const outcome = await buildReleaseFromContent2(dir, built, {
64751
65687
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -64762,7 +65698,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64762
65698
  ...await navIssues2(dir, strict)
64763
65699
  ]);
64764
65700
  if (hasErrors && !force) {
64765
- rmSync10(built, { recursive: true, force: true });
65701
+ rmSync11(built, { recursive: true, force: true });
64766
65702
  print({ ...report2, packed: false, hint: "lint found errors \u2014 fix them, or re-run `himi pack --force` to pack anyway" });
64767
65703
  return 1;
64768
65704
  }
@@ -64798,7 +65734,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64798
65734
  io.error(JSON.stringify({ error: e.message }));
64799
65735
  return 1;
64800
65736
  } finally {
64801
- rmSync10(built, { recursive: true, force: true });
65737
+ rmSync11(built, { recursive: true, force: true });
64802
65738
  }
64803
65739
  }
64804
65740
  // ---- himi import — turn a dev .himi back into an editable content package ----
@@ -64948,7 +65884,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64948
65884
  }));
64949
65885
  return 1;
64950
65886
  }
64951
- const archive = readFileSync67(resolve44(file));
65887
+ const archive = readFileSync68(resolve44(file));
64952
65888
  const result = await client2().createPackageShare({
64953
65889
  app: opened.header.app,
64954
65890
  releaseId: opened.header.releaseId,
@@ -64985,10 +65921,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
64985
65921
  const app = await appId(dir);
64986
65922
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
64987
65923
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
64988
- temp = mkdtempSync4(join52(tmpdir5(), "himi-run-build-"));
65924
+ temp = mkdtempSync4(join53(tmpdir5(), "himi-run-build-"));
64989
65925
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
64990
65926
  await buildReleaseFromContent2(dir, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {} });
64991
- source = join52(temp, app);
65927
+ source = join53(temp, app);
64992
65928
  }
64993
65929
  const chromeArg = str5(flags.chrome) ?? "preview";
64994
65930
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -65051,7 +65987,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
65051
65987
  io.error(JSON.stringify({ error: e.message }));
65052
65988
  return 1;
65053
65989
  } finally {
65054
- if (temp) rmSync10(temp, { recursive: true, force: true });
65990
+ if (temp) rmSync11(temp, { recursive: true, force: true });
65055
65991
  }
65056
65992
  }
65057
65993
  case "preview": {
@@ -65592,7 +66528,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
65592
66528
  io.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
65593
66529
  return 2;
65594
66530
  }
65595
- if (!existsSync56(file)) {
66531
+ if (!existsSync57(file)) {
65596
66532
  io.error(JSON.stringify({ error: `no such file: ${file}` }));
65597
66533
  return 2;
65598
66534
  }
@@ -65648,7 +66584,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
65648
66584
  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."
65649
66585
  };
65650
66586
  if (kind === "tokens" || kind === "app-config") {
65651
- const body = readFileSync67(file, "utf8");
66587
+ const body = readFileSync68(file, "utf8");
65652
66588
  const patch2 = kind === "tokens" ? { tokens: body } : { appConfig: body };
65653
66589
  return runReleasePatch({
65654
66590
  ...common,
@@ -65663,7 +66599,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
65663
66599
  io.error(JSON.stringify({ error: "patch screen needs a <screenId>", usage: PATCH_USAGE }));
65664
66600
  return 2;
65665
66601
  }
65666
- const descriptor = readFileSync67(file, "utf8");
66602
+ const descriptor = readFileSync68(file, "utf8");
65667
66603
  try {
65668
66604
  JSON.parse(descriptor);
65669
66605
  } catch (e) {
@@ -65703,7 +66639,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
65703
66639
  extra: { kind, screen: id, file: { path: file, bytes: Buffer.byteLength(descriptor, "utf8") } }
65704
66640
  });
65705
66641
  }
65706
- const bytes2 = readFileSync67(file);
66642
+ const bytes2 = readFileSync68(file);
65707
66643
  const sha = createHash24("sha256").update(bytes2).digest("hex");
65708
66644
  const forScreens = (str5(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
65709
66645
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
@@ -65847,7 +66783,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
65847
66783
  }
65848
66784
  let def;
65849
66785
  try {
65850
- const parsed = JSON.parse(arg.startsWith("@") ? readFileSync67(arg.slice(1), "utf8") : arg);
66786
+ const parsed = JSON.parse(arg.startsWith("@") ? readFileSync68(arg.slice(1), "utf8") : arg);
65851
66787
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
65852
66788
  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}`] }));
65853
66789
  return 2;
@@ -66311,7 +67247,7 @@ ${label2}`);
66311
67247
  const platform = str5(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
66312
67248
  {
66313
67249
  const distDir = contentDir(flags);
66314
- if (existsSync56(join52(distDir, "himalaya.content.json"))) {
67250
+ if (existsSync57(join53(distDir, "himalaya.content.json"))) {
66315
67251
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
66316
67252
  const errors = issues.filter((i) => i.severity === "error");
66317
67253
  for (const i of issues.filter((i2) => i2.severity === "warning")) {
@@ -66580,7 +67516,7 @@ ${label2}`);
66580
67516
  const dpxPath = dpxSessionTokenPath();
66581
67517
  let dpx;
66582
67518
  try {
66583
- dpx = readFileSync67(dpxPath, "utf8").trim();
67519
+ dpx = readFileSync68(dpxPath, "utf8").trim();
66584
67520
  } catch {
66585
67521
  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` }));
66586
67522
  return 1;
@@ -66982,7 +67918,7 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
66982
67918
  const path2 = dpxSessionTokenPath();
66983
67919
  let raw;
66984
67920
  try {
66985
- raw = readFileSync67(path2, "utf8");
67921
+ raw = readFileSync68(path2, "utf8");
66986
67922
  } catch {
66987
67923
  io.error(JSON.stringify({ error: `no DPX BO session at ${path2}` }));
66988
67924
  return 1;