@wix/himalaya-cli 0.807.0 → 0.809.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
@@ -1014,7 +1014,7 @@ function sentinelizeIdentity(body, identity) {
1014
1014
  }
1015
1015
  if (identity.name) {
1016
1016
  const re = new RegExp(`\\b(${NAME_FIELDS.join("|")})(["']?\\s*[:=]\\s*)(["'])${escapeRe(identity.name)}\\3`, "g");
1017
- out = out.replace(re, (_m, key2, sep10, q) => `${key2}${sep10}${q}__HIMI_APP_NAME__${q}`);
1017
+ out = out.replace(re, (_m, key2, sep11, q) => `${key2}${sep11}${q}__HIMI_APP_NAME__${q}`);
1018
1018
  }
1019
1019
  return out;
1020
1020
  }
@@ -2496,8 +2496,8 @@ var init_splash = __esm({
2496
2496
 
2497
2497
  // ../../core/dev-server/tier5-bundles-src/build-lib.mjs
2498
2498
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL2 } from "node:url";
2499
- import { dirname as dirname8, resolve as resolve8 } from "node:path";
2500
- import { readdirSync as readdirSync6, existsSync as existsSync8, readFileSync as readFileSync10 } from "node:fs";
2499
+ import { dirname as dirname8, join as join12, resolve as resolve8, sep as sep2 } from "node:path";
2500
+ import { readdirSync as readdirSync6, existsSync as existsSync8, readFileSync as readFileSync10, realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
2501
2501
  async function loadCompiler() {
2502
2502
  try {
2503
2503
  const nativeCompiler = ["es", "build"].join("");
@@ -2530,6 +2530,259 @@ function isOffRepo() {
2530
2530
  if (process.env.HIMI_OFF_REPO === "1") return true;
2531
2531
  return !existsSync8(alias["@himalaya/state"]);
2532
2532
  }
2533
+ function buildConfinementRequired() {
2534
+ return _confinementRequired;
2535
+ }
2536
+ function realpathOrSelf(p) {
2537
+ try {
2538
+ return realpathSync2(resolve8(p));
2539
+ } catch {
2540
+ return resolve8(p);
2541
+ }
2542
+ }
2543
+ function isUnder(p, root) {
2544
+ return p === root || p.startsWith(root + sep2);
2545
+ }
2546
+ function confinementRoots(contentRoot) {
2547
+ const roots = /* @__PURE__ */ new Set();
2548
+ const add = (p) => {
2549
+ if (!p) return;
2550
+ roots.add(realpathOrSelf(p));
2551
+ roots.add(resolve8(p));
2552
+ };
2553
+ const real = realpathOrSelf(contentRoot);
2554
+ add(real);
2555
+ for (let d = real, up; ; d = up) {
2556
+ add(join12(d, "node_modules"));
2557
+ up = dirname8(d);
2558
+ if (up === d) break;
2559
+ }
2560
+ add(process.env.HIMI_AUTHORING_RUNTIME_DIR);
2561
+ add(process.env.HIMI_CLI_RUNTIME_DIR);
2562
+ if (!isOffRepo()) add(REPO_ROOT4);
2563
+ return [...roots];
2564
+ }
2565
+ function confinementMatcher(contentRoot, extraRoots = []) {
2566
+ const roots = [...confinementRoots(contentRoot), ...extraRoots.filter(Boolean).flatMap((r) => [realpathOrSelf(r), resolve8(r)])];
2567
+ let linked;
2568
+ const linkedRoots = () => {
2569
+ if (linked) return linked;
2570
+ linked = [];
2571
+ const scanned = /* @__PURE__ */ new Set();
2572
+ const queue = [...roots];
2573
+ const CAP3 = 4096;
2574
+ const take = (abs) => {
2575
+ const target = realpathOrSelf(abs);
2576
+ if (scanned.has(target) || linked.length >= CAP3) return;
2577
+ linked.push(target);
2578
+ queue.push(target);
2579
+ };
2580
+ while (queue.length && linked.length < CAP3) {
2581
+ const r = queue.shift();
2582
+ for (const nm of [r, join12(r, "node_modules")]) {
2583
+ if (scanned.has(nm)) continue;
2584
+ scanned.add(nm);
2585
+ let entries;
2586
+ try {
2587
+ entries = readdirSync6(nm, { withFileTypes: true });
2588
+ } catch {
2589
+ continue;
2590
+ }
2591
+ for (const e of entries) {
2592
+ const abs = join12(nm, e.name);
2593
+ if (e.isSymbolicLink()) take(abs);
2594
+ else if (e.isDirectory() && e.name.startsWith("@")) {
2595
+ let scoped;
2596
+ try {
2597
+ scoped = readdirSync6(abs, { withFileTypes: true });
2598
+ } catch {
2599
+ continue;
2600
+ }
2601
+ for (const sc of scoped) if (sc.isSymbolicLink()) take(join12(abs, sc.name));
2602
+ }
2603
+ }
2604
+ }
2605
+ }
2606
+ return linked;
2607
+ };
2608
+ return (p) => {
2609
+ const direct = resolve8(p);
2610
+ if (roots.some((r) => isUnder(direct, r))) return true;
2611
+ const real = realpathOrSelf(direct);
2612
+ if (real !== direct && roots.some((r) => isUnder(real, r))) return true;
2613
+ return linkedRoots().some((r) => isUnder(real, r) || isUnder(direct, r));
2614
+ };
2615
+ }
2616
+ function confinementPlugin(contentRoot) {
2617
+ const allows = confinementMatcher(contentRoot);
2618
+ return {
2619
+ name: "himalaya-confine-import-graph",
2620
+ setup(b) {
2621
+ b.onResolve({ filter: /^[./]/ }, (args) => {
2622
+ const base = args.path.startsWith("/") ? args.path : args.resolveDir ? resolve8(args.resolveDir, args.path) : void 0;
2623
+ if (!base || allows(base)) return void 0;
2624
+ return {
2625
+ errors: [{
2626
+ text: `refused to resolve ${args.path}: it is outside this content package. A worker or app config may import files from its own package, the Himalaya SDK, and its installed dependencies \u2014 nothing else.`
2627
+ }]
2628
+ };
2629
+ });
2630
+ b.onLoad({ filter: /.*/ }, (args) => {
2631
+ if (args.namespace && args.namespace !== "file") return void 0;
2632
+ if (allows(args.path)) return void 0;
2633
+ return {
2634
+ errors: [{
2635
+ text: `refused to read ${args.path}: it is outside this content package. A worker or app config may import files from its own package, the Himalaya SDK, and its installed dependencies \u2014 nothing else.`
2636
+ }]
2637
+ };
2638
+ });
2639
+ }
2640
+ };
2641
+ }
2642
+ function assertConfinedTsconfig(entry, contentRoot, allows) {
2643
+ const seen = /* @__PURE__ */ new Set();
2644
+ const refuse = (p, why) => {
2645
+ throw new Error(
2646
+ `refused to read ${p}: ${why}. A tsconfig may extend a file from its own package or an installed dependency \u2014 nothing else.`
2647
+ );
2648
+ };
2649
+ const stripJsonc = (text2) => {
2650
+ if (text2.charCodeAt(0) === 65279) text2 = text2.slice(1);
2651
+ let out = "";
2652
+ let inStr = false;
2653
+ let esc = false;
2654
+ for (let i = 0; i < text2.length; i++) {
2655
+ const c = text2[i];
2656
+ if (inStr) {
2657
+ out += c;
2658
+ if (esc) esc = false;
2659
+ else if (c === "\\") esc = true;
2660
+ else if (c === '"') inStr = false;
2661
+ continue;
2662
+ }
2663
+ if (c === '"') {
2664
+ inStr = true;
2665
+ out += c;
2666
+ continue;
2667
+ }
2668
+ if (c === "/" && text2[i + 1] === "/") {
2669
+ while (i < text2.length && text2[i] !== "\n") i++;
2670
+ continue;
2671
+ }
2672
+ if (c === "/" && text2[i + 1] === "*") {
2673
+ i += 2;
2674
+ while (i < text2.length && !(text2[i] === "*" && text2[i + 1] === "/")) i++;
2675
+ i++;
2676
+ continue;
2677
+ }
2678
+ if (c === "}" || c === "]") {
2679
+ const trimmed = out.replace(/\s+$/, "");
2680
+ if (trimmed.endsWith(",")) out = trimmed.slice(0, -1);
2681
+ }
2682
+ out += c;
2683
+ }
2684
+ return out;
2685
+ };
2686
+ const isFile = (p) => {
2687
+ try {
2688
+ return statSync2(p).isFile();
2689
+ } catch {
2690
+ return false;
2691
+ }
2692
+ };
2693
+ const asConfigFile = (t) => {
2694
+ if (isFile(t)) return t;
2695
+ const inDir = join12(t, "tsconfig.json");
2696
+ if (isFile(inDir)) return inDir;
2697
+ if (isFile(`${t}.json`)) return `${t}.json`;
2698
+ return t;
2699
+ };
2700
+ const resolveBare = (spec, fromDir) => {
2701
+ for (let d = fromDir, up; ; d = up) {
2702
+ const base = join12(d, "node_modules", spec);
2703
+ for (const cand of [base, `${base}.json`]) if (isFile(cand)) return cand;
2704
+ const pj = join12(base, "package.json");
2705
+ if (isFile(pj)) {
2706
+ let manifest;
2707
+ try {
2708
+ manifest = JSON.parse(stripJsonc(readFileSync10(pj, "utf8")));
2709
+ } catch {
2710
+ refuse(pj, "its package.json could not be parsed, so its `tsconfig` field cannot be checked");
2711
+ }
2712
+ const field = manifest?.tsconfig;
2713
+ if (typeof field === "string" && field) {
2714
+ return asConfigFile(field.startsWith("/") ? field : resolve8(base, field));
2715
+ }
2716
+ }
2717
+ if (isFile(join12(base, "tsconfig.json"))) return join12(base, "tsconfig.json");
2718
+ up = dirname8(d);
2719
+ if (up === d) return void 0;
2720
+ }
2721
+ };
2722
+ const follow = (file) => {
2723
+ const real = realpathOrSelf(file);
2724
+ if (seen.has(real)) return;
2725
+ seen.add(real);
2726
+ if (!allows(real)) refuse(file, "it is outside this content package");
2727
+ let raw;
2728
+ try {
2729
+ raw = readFileSync10(real, "utf8");
2730
+ } catch {
2731
+ return;
2732
+ }
2733
+ let parsed;
2734
+ try {
2735
+ parsed = JSON.parse(stripJsonc(raw));
2736
+ } catch {
2737
+ refuse(file, "its tsconfig could not be parsed, so its `extends` cannot be checked");
2738
+ }
2739
+ const ext = parsed?.extends;
2740
+ for (const one of Array.isArray(ext) ? ext : ext ? [ext] : []) {
2741
+ if (typeof one !== "string" || !one) continue;
2742
+ if (one.startsWith(".") || one.startsWith("/")) {
2743
+ const target = asConfigFile(one.startsWith("/") ? one : resolve8(dirname8(real), one));
2744
+ if (target) follow(target);
2745
+ } else {
2746
+ const target = resolveBare(one, dirname8(real));
2747
+ if (target) follow(target);
2748
+ }
2749
+ }
2750
+ };
2751
+ const scan = (dir) => {
2752
+ let entries;
2753
+ try {
2754
+ entries = readdirSync6(dir, { withFileTypes: true });
2755
+ } catch {
2756
+ return;
2757
+ }
2758
+ for (const e of entries) {
2759
+ const abs = join12(dir, e.name);
2760
+ if (e.isDirectory()) scan(abs);
2761
+ else if (e.name === "tsconfig.json" && e.isFile()) follow(abs);
2762
+ }
2763
+ };
2764
+ scan(realpathOrSelf(contentRoot));
2765
+ for (let d = dirname8(realpathOrSelf(entry)), up; ; d = up) {
2766
+ const cfg = join12(d, "tsconfig.json");
2767
+ if (existsSync8(cfg)) {
2768
+ follow(cfg);
2769
+ return;
2770
+ }
2771
+ up = dirname8(d);
2772
+ if (up === d) return;
2773
+ }
2774
+ }
2775
+ function confinementFor(contentRoot, where, entry) {
2776
+ if (!_confinementRequired) return void 0;
2777
+ if (!contentRoot) {
2778
+ throw new Error(
2779
+ `${where}: this process compiles untrusted uploads (requireBuildConfinement) but no content root was given, so the import graph would be unconfined. Pass \`confineTo: <content dir>\`.`
2780
+ );
2781
+ }
2782
+ const allows = confinementMatcher(contentRoot);
2783
+ if (entry) assertConfinedTsconfig(entry, contentRoot, allows);
2784
+ return confinementPlugin(contentRoot);
2785
+ }
2533
2786
  function umbrellaRewritePlugin({ external = false } = {}) {
2534
2787
  return {
2535
2788
  name: "himalaya-umbrella-rewrite",
@@ -2597,7 +2850,8 @@ function importMetaUrlPlugin() {
2597
2850
  };
2598
2851
  }
2599
2852
  async function buildBundleFromEntry(entry, outfile, opts = {}) {
2600
- const { resolution = "source", name = "bundle", absWorkingDir = REPO_ROOT4 } = opts;
2853
+ const { resolution = "source", name = "bundle", absWorkingDir = REPO_ROOT4, confineTo } = opts;
2854
+ const confine = confinementFor(confineTo, "buildBundleFromEntry", entry);
2601
2855
  if (!existsSync8(entry)) throw new Error(`no bundle entry: ${entry}`);
2602
2856
  const buildOpts = {
2603
2857
  entryPoints: [entry],
@@ -2620,11 +2874,13 @@ async function buildBundleFromEntry(entry, outfile, opts = {}) {
2620
2874
  else if (isOffRepo()) {
2621
2875
  buildOpts.plugins = process.env.HIMI_CLI_RUNTIME_DIR ? [cliRuntimeUmbrellaPlugin()] : [umbrellaRewritePlugin({ external: false })];
2622
2876
  }
2877
+ if (confine) buildOpts.plugins = [confine, ...buildOpts.plugins ?? []];
2623
2878
  await build(buildOpts);
2624
2879
  return outfile;
2625
2880
  }
2626
2881
  async function buildAppEntry(entry, outfile, opts = {}) {
2627
- const { umbrella = false, resolveTsExtensions = false, absWorkingDir = REPO_ROOT4 } = opts;
2882
+ const { umbrella = false, resolveTsExtensions = false, absWorkingDir = REPO_ROOT4, confineTo } = opts;
2883
+ const confine = confinementFor(confineTo, "buildAppEntry", entry);
2628
2884
  if (!existsSync8(entry)) throw new Error(`unknown app entry: ${entry}`);
2629
2885
  await build({
2630
2886
  entryPoints: [entry],
@@ -2648,6 +2904,9 @@ async function buildAppEntry(entry, outfile, opts = {}) {
2648
2904
  // -- a single global `define` here pinned the whole graph to the entry and broke
2649
2905
  // every app whose config.ts reads a file relative to itself.
2650
2906
  plugins: [
2907
+ // FIRST, and here it is load-bearing: `importMetaUrlPlugin` registers an onLoad and CLAIMS
2908
+ // every file containing `import.meta.url`, so a confinement behind it would never see them.
2909
+ ...confine ? [confine] : [],
2651
2910
  importMetaUrlPlugin(),
2652
2911
  ...resolveTsExtensions ? [tsExtResolvePlugin()] : [],
2653
2912
  ...process.env.HIMI_CLI_RUNTIME_DIR ? [cliRuntimeUmbrellaPlugin()] : [],
@@ -2656,7 +2915,7 @@ async function buildAppEntry(entry, outfile, opts = {}) {
2656
2915
  });
2657
2916
  return outfile;
2658
2917
  }
2659
- var _compilerP, __dirname, REPO_ROOT4, SDK, alias, sourceAliases, OUT_DIR, HERMES_SAFE_SUPPORTED, HERMES_UNSAFE_BUNDLES, APP_ENTRY_TARGET;
2918
+ var _compilerP, __dirname, REPO_ROOT4, SDK, alias, sourceAliases, OUT_DIR, HERMES_SAFE_SUPPORTED, HERMES_UNSAFE_BUNDLES, _confinementRequired, APP_ENTRY_TARGET;
2660
2919
  var init_build_lib = __esm({
2661
2920
  "../../core/dev-server/tier5-bundles-src/build-lib.mjs"() {
2662
2921
  "use strict";
@@ -2805,6 +3064,7 @@ var init_build_lib = __esm({
2805
3064
  // worker-safe wix-auth showcase pulls ~11 classes from stdlib/auth-providers.
2806
3065
  "demo-app-wix-platform"
2807
3066
  ]);
3067
+ _confinementRequired = false;
2808
3068
  APP_ENTRY_TARGET = ["node18"];
2809
3069
  }
2810
3070
  });
@@ -2829,7 +3089,7 @@ var init_tier5_dir = __esm({
2829
3089
 
2830
3090
  // src/tier5-typecheck.ts
2831
3091
  import { existsSync as existsSync9, readFileSync as readFileSync11 } from "node:fs";
2832
- import { relative as relative2, resolve as resolve10, sep as sep2 } from "node:path";
3092
+ import { relative as relative2, resolve as resolve10, sep as sep3 } from "node:path";
2833
3093
  import { pathToFileURL as pathToFileURL3 } from "node:url";
2834
3094
  async function loadTypeScript() {
2835
3095
  const runtime = process.env.HIMI_AUTHORING_RUNTIME_DIR;
@@ -2868,7 +3128,7 @@ function pathsFor(options) {
2868
3128
  return declarationPaths(options.umbrellaDir);
2869
3129
  }
2870
3130
  function relativeFile(contentDir2, fileName) {
2871
- return relative2(contentDir2, fileName).split(sep2).join("/");
3131
+ return relative2(contentDir2, fileName).split(sep3).join("/");
2872
3132
  }
2873
3133
  function belongsToContentWorkers(contentDir2, diagnostic) {
2874
3134
  if (!diagnostic.file) return true;
@@ -2890,6 +3150,19 @@ function toIssue(ts, contentDir2, diagnostic) {
2890
3150
  message: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
2891
3151
  };
2892
3152
  }
3153
+ function confinedHost(ts, contentDir2, options) {
3154
+ if (!buildConfinementRequired()) return void 0;
3155
+ const host = ts.createCompilerHost(options);
3156
+ const libDir = resolve10(ts.getDefaultLibFilePath(options), "..");
3157
+ const allows = confinementMatcher(contentDir2, [libDir]);
3158
+ const getSourceFile = host.getSourceFile.bind(host);
3159
+ const readFile3 = host.readFile.bind(host);
3160
+ const fileExists = host.fileExists.bind(host);
3161
+ host.getSourceFile = (fileName, ...rest) => allows(fileName) ? getSourceFile(fileName, ...rest) : void 0;
3162
+ host.readFile = (fileName) => allows(fileName) ? readFile3(fileName) : void 0;
3163
+ host.fileExists = (fileName) => allows(fileName) && fileExists(fileName);
3164
+ return host;
3165
+ }
2893
3166
  async function typecheckContentWorkers(contentDir2, options) {
2894
3167
  if (!existsSync9(resolve10(contentDir2, "tier5-src"))) return [];
2895
3168
  const ts = await compiler2();
@@ -2907,7 +3180,11 @@ async function typecheckContentWorkers(contentDir2, options) {
2907
3180
  };
2908
3181
  const parsed = ts.parseJsonConfigFileContent(config, ts.sys, contentDir2);
2909
3182
  if (parsed.fileNames.length === 0) return [];
2910
- const program = ts.createProgram({ rootNames: parsed.fileNames, options: parsed.options });
3183
+ const program = ts.createProgram({
3184
+ rootNames: parsed.fileNames,
3185
+ options: parsed.options,
3186
+ host: confinedHost(ts, contentDir2, parsed.options)
3187
+ });
2911
3188
  return [...parsed.errors, ...ts.getPreEmitDiagnostics(program)].filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error && belongsToContentWorkers(contentDir2, diagnostic)).map((diagnostic) => toIssue(ts, contentDir2, diagnostic));
2912
3189
  }
2913
3190
  var compilerPromise;
@@ -2936,31 +3213,31 @@ __export(build_exports, {
2936
3213
  umbrellaVersion: () => umbrellaVersion,
2937
3214
  umbrellaVersionIssue: () => umbrellaVersionIssue
2938
3215
  });
2939
- import { existsSync as existsSync10, readdirSync as readdirSync8, mkdirSync as mkdirSync6, statSync as statSync2, symlinkSync, readFileSync as readFileSync12 } from "node:fs";
2940
- import { resolve as resolve11, join as join12, dirname as dirname9 } from "node:path";
3216
+ import { existsSync as existsSync10, readdirSync as readdirSync8, mkdirSync as mkdirSync6, statSync as statSync3, symlinkSync, readFileSync as readFileSync12 } from "node:fs";
3217
+ import { resolve as resolve11, join as join13, dirname as dirname9 } from "node:path";
2941
3218
  import { fileURLToPath as fileURLToPath6 } from "node:url";
2942
3219
  import { execFileSync as execFileSync3 } from "node:child_process";
2943
3220
  function bundledRuntimeDir() {
2944
3221
  const configured = process.env.HIMI_CLI_RUNTIME_DIR;
2945
- if (configured && existsSync10(join12(configured, "node_modules", "@wix", "himalaya", "package.json"))) {
3222
+ if (configured && existsSync10(join13(configured, "node_modules", "@wix", "himalaya", "package.json"))) {
2946
3223
  return configured;
2947
3224
  }
2948
3225
  const candidate = resolve11(dirname9(fileURLToPath6(import.meta.url)), "authoring-runtime");
2949
- if (!existsSync10(join12(candidate, "node_modules", "@wix", "himalaya", "package.json"))) return null;
3226
+ if (!existsSync10(join13(candidate, "node_modules", "@wix", "himalaya", "package.json"))) return null;
2950
3227
  process.env.HIMI_CLI_RUNTIME_DIR = candidate;
2951
3228
  return candidate;
2952
3229
  }
2953
3230
  function bundledUmbrella() {
2954
3231
  const runtime = bundledRuntimeDir();
2955
- return runtime ? join12(runtime, "node_modules", "@wix", "himalaya") : null;
3232
+ return runtime ? join13(runtime, "node_modules", "@wix", "himalaya") : null;
2956
3233
  }
2957
3234
  function umbrellaDir(fromDir) {
2958
3235
  const bundled = bundledUmbrella();
2959
3236
  if (bundled) return bundled;
2960
3237
  let d = resolve11(fromDir);
2961
3238
  for (; ; ) {
2962
- const pkg = join12(d, "node_modules", "@wix", "himalaya");
2963
- if (existsSync10(join12(pkg, "package.json"))) return pkg;
3239
+ const pkg = join13(d, "node_modules", "@wix", "himalaya");
3240
+ if (existsSync10(join13(pkg, "package.json"))) return pkg;
2964
3241
  const parent = dirname9(d);
2965
3242
  if (parent === d) return null;
2966
3243
  d = parent;
@@ -2973,8 +3250,8 @@ function globalUmbrella() {
2973
3250
  try {
2974
3251
  const root = execFileSync3("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
2975
3252
  if (!root) return null;
2976
- const pkg = join12(root, "@wix", "himalaya");
2977
- return existsSync10(join12(pkg, "package.json")) ? pkg : null;
3253
+ const pkg = join13(root, "@wix", "himalaya");
3254
+ return existsSync10(join13(pkg, "package.json")) ? pkg : null;
2978
3255
  } catch {
2979
3256
  return null;
2980
3257
  }
@@ -2983,11 +3260,11 @@ function ensureUmbrella(contentDir2, globalPath = globalUmbrella()) {
2983
3260
  if (umbrellaInstalled(contentDir2)) return true;
2984
3261
  if (!globalPath) return false;
2985
3262
  try {
2986
- const scope = join12(contentDir2, "node_modules", "@wix");
3263
+ const scope = join13(contentDir2, "node_modules", "@wix");
2987
3264
  mkdirSync6(scope, { recursive: true });
2988
- const link = join12(scope, "himalaya");
3265
+ const link = join13(scope, "himalaya");
2989
3266
  if (!existsSync10(link)) symlinkSync(globalPath, link, "dir");
2990
- return existsSync10(join12(link, "package.json"));
3267
+ return existsSync10(join13(link, "package.json"));
2991
3268
  } catch {
2992
3269
  return false;
2993
3270
  }
@@ -3005,7 +3282,7 @@ function umbrellaVersion(fromDir) {
3005
3282
  const dir = umbrellaDir(fromDir);
3006
3283
  if (!dir) return null;
3007
3284
  try {
3008
- const v = JSON.parse(readFileSync12(join12(dir, "package.json"), "utf8")).version;
3285
+ const v = JSON.parse(readFileSync12(join13(dir, "package.json"), "utf8")).version;
3009
3286
  return typeof v === "string" && v ? v : null;
3010
3287
  } catch {
3011
3288
  return null;
@@ -3026,7 +3303,7 @@ function umbrellaDistIssue(contentDir2) {
3026
3303
  if (!dir) return null;
3027
3304
  let exportsMap;
3028
3305
  try {
3029
- exportsMap = JSON.parse(readFileSync12(join12(dir, "package.json"), "utf8")).exports;
3306
+ exportsMap = JSON.parse(readFileSync12(join13(dir, "package.json"), "utf8")).exports;
3030
3307
  } catch {
3031
3308
  return null;
3032
3309
  }
@@ -3080,14 +3357,19 @@ async function buildContentBundles(contentDir2, opts = {}) {
3080
3357
  if (!existsSync10(srcDir)) return { built, outDir, compileErrors, ...sdkVersionIssue ? { sdkVersionIssue } : {} };
3081
3358
  mkdirSync6(outDir, { recursive: true });
3082
3359
  for (const name of readdirSync8(srcDir).sort()) {
3083
- const entry = join12(srcDir, name, "index.ts");
3084
- if (!statSync2(join12(srcDir, name)).isDirectory() || !existsSync10(entry)) continue;
3360
+ const entry = join13(srcDir, name, "index.ts");
3361
+ if (!statSync3(join13(srcDir, name)).isDirectory() || !existsSync10(entry)) continue;
3085
3362
  try {
3086
- await buildBundleFromEntry(entry, join12(outDir, `${name}.bundle.js`), {
3363
+ await buildBundleFromEntry(entry, join13(outDir, `${name}.bundle.js`), {
3087
3364
  resolution,
3088
3365
  name,
3089
3366
  // Off-repo, resolve the umbrella from the content package's own node_modules.
3090
- ...offRepo ? { absWorkingDir: contentDir2 } : {}
3367
+ ...offRepo ? { absWorkingDir: contentDir2 } : {},
3368
+ // Bounds the import graph in a process that compiles untrusted uploads
3369
+ // (core/serve-authoring); inert everywhere else. Always passed, so the authoring guest
3370
+ // cannot reach this build without a root — build-lib throws rather than compile
3371
+ // unconfined.
3372
+ confineTo: contentDir2
3091
3373
  });
3092
3374
  built.push(name);
3093
3375
  } catch (err) {
@@ -3125,11 +3407,11 @@ __export(app_config_loader_exports, {
3125
3407
  loadAppConfigModule: () => loadAppConfigModule
3126
3408
  });
3127
3409
  import { existsSync as existsSync11, mkdirSync as mkdirSync7 } from "node:fs";
3128
- import { dirname as dirname10, join as join13, resolve as resolve12 } from "node:path";
3410
+ import { dirname as dirname10, join as join14, resolve as resolve12 } from "node:path";
3129
3411
  import { pathToFileURL as pathToFileURL4 } from "node:url";
3130
3412
  function configEntry(dir) {
3131
3413
  for (const f of ["config.ts", "config.js", "config.mjs"]) {
3132
- const p = join13(dir, f);
3414
+ const p = join14(dir, f);
3133
3415
  if (existsSync11(p)) return p;
3134
3416
  }
3135
3417
  return null;
@@ -3141,7 +3423,7 @@ async function buildConfigModuleUrl(entry, dir) {
3141
3423
  bundledRuntimeDir2();
3142
3424
  const out = resolve12(dir, "dist/.himi/config.mjs");
3143
3425
  mkdirSync7(dirname10(out), { recursive: true });
3144
- await buildAppEntry(entry, out, { umbrella: true, resolveTsExtensions: true, absWorkingDir: dir });
3426
+ await buildAppEntry(entry, out, { umbrella: true, resolveTsExtensions: true, absWorkingDir: dir, confineTo: dir });
3145
3427
  return pathToFileURL4(out).href;
3146
3428
  }
3147
3429
  function rememberBuild(key2, pending) {
@@ -3889,10 +4171,10 @@ var init_app_icon_rules = __esm({
3889
4171
 
3890
4172
  // ../../core/dev-server/src/app-icon.ts
3891
4173
  import { existsSync as existsSync14, readFileSync as readFileSync17, readdirSync as readdirSync9 } from "node:fs";
3892
- import { dirname as dirname13, join as join17, resolve as resolve13 } from "node:path";
4174
+ import { dirname as dirname13, join as join18, resolve as resolve13 } from "node:path";
3893
4175
  import { fileURLToPath as fileURLToPath7 } from "node:url";
3894
4176
  function findAppIconPath(appDir2) {
3895
- const iosDir = join17(appDir2, "ios");
4177
+ const iosDir = join18(appDir2, "ios");
3896
4178
  let children;
3897
4179
  try {
3898
4180
  children = readdirSync9(iosDir);
@@ -3900,7 +4182,7 @@ function findAppIconPath(appDir2) {
3900
4182
  return null;
3901
4183
  }
3902
4184
  for (const child of children) {
3903
- const p = join17(iosDir, child, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
4185
+ const p = join18(iosDir, child, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
3904
4186
  if (existsSync14(p)) return p;
3905
4187
  }
3906
4188
  return null;
@@ -3908,7 +4190,7 @@ function findAppIconPath(appDir2) {
3908
4190
  function findContentIconPath(appDir2) {
3909
4191
  let rel;
3910
4192
  try {
3911
- rel = JSON.parse(readFileSync17(join17(appDir2, "himalaya.content.json"), "utf8")).icon;
4193
+ rel = JSON.parse(readFileSync17(join18(appDir2, "himalaya.content.json"), "utf8")).icon;
3912
4194
  } catch {
3913
4195
  return null;
3914
4196
  }
@@ -3933,7 +4215,7 @@ function readIconFromDir(appDir2) {
3933
4215
  function resolveAppDir(appId2) {
3934
4216
  if (!/^[a-z0-9][a-z0-9._-]*$/i.test(appId2)) return null;
3935
4217
  for (const base of APP_ROOTS) {
3936
- const dir = join17(ROOT, base, appId2);
4218
+ const dir = join18(ROOT, base, appId2);
3937
4219
  if (existsSync14(dir)) return dir;
3938
4220
  }
3939
4221
  return null;
@@ -4226,11 +4508,11 @@ __export(icon_exports, {
4226
4508
  summarize: () => summarize
4227
4509
  });
4228
4510
  import { createHash as createHash5 } from "node:crypto";
4229
- import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync18, statSync as statSync3, writeFileSync as writeFileSync8 } from "node:fs";
4230
- import { basename as basename4, dirname as dirname14, join as join18, resolve as resolve14, sep as sep3 } from "node:path";
4511
+ import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync18, statSync as statSync4, writeFileSync as writeFileSync8 } from "node:fs";
4512
+ import { basename as basename4, dirname as dirname14, join as join19, resolve as resolve14, sep as sep4 } from "node:path";
4231
4513
  function readManifest(dir) {
4232
4514
  try {
4233
- return JSON.parse(readFileSync18(join18(dir, MANIFEST), "utf8"));
4515
+ return JSON.parse(readFileSync18(join19(dir, MANIFEST), "utf8"));
4234
4516
  } catch {
4235
4517
  return null;
4236
4518
  }
@@ -4244,7 +4526,7 @@ function declaredIconName(dir) {
4244
4526
  function iconDest(dir, name) {
4245
4527
  const base = resolve14(dir);
4246
4528
  const abs = resolve14(base, name);
4247
- if (!abs.startsWith(base + sep3)) {
4529
+ if (!abs.startsWith(base + sep4)) {
4248
4530
  throw new Error(
4249
4531
  `himi icon: himalaya.content.json declares "icon": "${name}", which resolves outside the package (${abs}). Declare a path inside it, e.g. "${DEFAULT_CONTENT_ICON}".`
4250
4532
  );
@@ -4254,9 +4536,9 @@ function iconDest(dir, name) {
4254
4536
  }
4255
4537
  function templateIconMatch(png) {
4256
4538
  for (const template of availableTemplates()) {
4257
- const candidate = join18(templateSourceDir(template), DEFAULT_CONTENT_ICON);
4539
+ const candidate = join19(templateSourceDir(template), DEFAULT_CONTENT_ICON);
4258
4540
  try {
4259
- if (statSync3(candidate).size !== png.length) continue;
4541
+ if (statSync4(candidate).size !== png.length) continue;
4260
4542
  if (readFileSync18(candidate).equals(png)) return template;
4261
4543
  } catch {
4262
4544
  }
@@ -4264,10 +4546,10 @@ function templateIconMatch(png) {
4264
4546
  return null;
4265
4547
  }
4266
4548
  function playState(dir) {
4267
- const p = join18(resolve14(dir), PLAY_LISTING_ICON);
4549
+ const p = join19(resolve14(dir), PLAY_LISTING_ICON);
4268
4550
  if (!existsSync15(p)) return null;
4269
4551
  try {
4270
- if (!statSync3(p).isFile()) return { path: p, issues: ["is a directory, not a PNG file"] };
4552
+ if (!statSync4(p).isFile()) return { path: p, issues: ["is a directory, not a PNG file"] };
4271
4553
  return { path: p, issues: playListingIconIssues(readFileSync18(p)) };
4272
4554
  } catch (err) {
4273
4555
  return { path: p, issues: [`could not be read (${err.message.split("\n")[0]})`] };
@@ -4294,7 +4576,7 @@ async function iconStatus(dir) {
4294
4576
  if (!path || !existsSync15(path)) return base;
4295
4577
  let png;
4296
4578
  try {
4297
- if (!statSync3(path).isFile()) {
4579
+ if (!statSync4(path).isFile()) {
4298
4580
  return {
4299
4581
  ...base,
4300
4582
  byteIssues: [`at ${path} is a directory, not a PNG file`],
@@ -4371,7 +4653,7 @@ function summarize(status) {
4371
4653
  }
4372
4654
  function backdropFromTokens(dir, fallback = "#1F2933") {
4373
4655
  try {
4374
- const tokens = JSON.parse(readFileSync18(join18(dir, "tokens.json"), "utf8"));
4656
+ const tokens = JSON.parse(readFileSync18(join19(dir, "tokens.json"), "utf8"));
4375
4657
  const accent = tokens.colors?.accentPrimary;
4376
4658
  const hex2 = typeof accent === "string" ? accent : accent?.light;
4377
4659
  return typeof hex2 === "string" && HEX_COLOR2.test(hex2) ? hex2 : fallback;
@@ -4430,15 +4712,15 @@ function declareIcon(dir, name) {
4430
4712
  if (k === "name") next.icon = name;
4431
4713
  }
4432
4714
  next.icon = name;
4433
- writeFileSync8(join18(dir, MANIFEST), JSON.stringify(next, null, 2) + "\n");
4434
- return [join18(dir, MANIFEST)];
4715
+ writeFileSync8(join19(dir, MANIFEST), JSON.stringify(next, null, 2) + "\n");
4716
+ return [join19(dir, MANIFEST)];
4435
4717
  }
4436
4718
  function siteBookIcon(bookDir) {
4437
- const p = join18(resolve14(bookDir), SITE_BOOK_ICON_REL);
4719
+ const p = join19(resolve14(bookDir), SITE_BOOK_ICON_REL);
4438
4720
  return existsSync15(p) ? p : null;
4439
4721
  }
4440
4722
  function siteLogoAbsence(bookDir) {
4441
- const bookJson = join18(resolve14(bookDir), "book.json");
4723
+ const bookJson = join19(resolve14(bookDir), "book.json");
4442
4724
  if (!existsSync15(bookJson)) {
4443
4725
  return { reason: "no-book", hint: `no business book at ${bookDir} \u2014 run \`himi site analyze --out <dir>\` first, or pass --book <dir>` };
4444
4726
  }
@@ -4520,7 +4802,7 @@ var init_icon = __esm({
4520
4802
  // ../fonts/catalog.ts
4521
4803
  import { existsSync as existsSync17, readFileSync as readFileSync19 } from "node:fs";
4522
4804
  import { fileURLToPath as fileURLToPath8 } from "node:url";
4523
- import { join as join20 } from "node:path";
4805
+ import { join as join21 } from "node:path";
4524
4806
  function loadCatalog() {
4525
4807
  if (cached) return cached;
4526
4808
  const path = CATALOG_CANDIDATES.find((p) => existsSync17(p));
@@ -4589,9 +4871,9 @@ var init_catalog = __esm({
4589
4871
  "../fonts/catalog.ts"() {
4590
4872
  CATALOG_CANDIDATES = [
4591
4873
  // in-repo: tools/fonts/catalog.ts → <repo>/stdlib/fonts/catalog.json
4592
- join20(fileURLToPath8(new URL("../..", import.meta.url)), "stdlib", "fonts", "catalog.json"),
4874
+ join21(fileURLToPath8(new URL("../..", import.meta.url)), "stdlib", "fonts", "catalog.json"),
4593
4875
  // published CLI: <pkg>/dist/cli.mjs → <pkg>/stdlib/fonts/catalog.json
4594
- join20(fileURLToPath8(new URL("..", import.meta.url)), "stdlib", "fonts", "catalog.json")
4876
+ join21(fileURLToPath8(new URL("..", import.meta.url)), "stdlib", "fonts", "catalog.json")
4595
4877
  ];
4596
4878
  cached = null;
4597
4879
  normalizeFamily = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -4696,17 +4978,17 @@ var init_plan_fonts = __esm({
4696
4978
  import { createHash as createHash6 } from "node:crypto";
4697
4979
  import { existsSync as existsSync18, readFileSync as readFileSync20 } from "node:fs";
4698
4980
  import { homedir as homedir4 } from "node:os";
4699
- import { basename as basename5, join as join21 } from "node:path";
4981
+ import { basename as basename5, join as join22 } from "node:path";
4700
4982
  import { fileURLToPath as fileURLToPath9 } from "node:url";
4701
4983
  function hasVendoredFloor() {
4702
4984
  return existsSync18(VENDORED_DIR);
4703
4985
  }
4704
4986
  function faceSource(file, sha2565) {
4705
- const vendored = join21(VENDORED_DIR, basename5(file));
4987
+ const vendored = join22(VENDORED_DIR, basename5(file));
4706
4988
  if (existsSync18(vendored) && createHash6("sha256").update(readFileSync20(vendored)).digest("hex") === sha2565) {
4707
4989
  return vendored;
4708
4990
  }
4709
- const cached3 = join21(CACHE_DIR, sha2565);
4991
+ const cached3 = join22(CACHE_DIR, sha2565);
4710
4992
  if (existsSync18(cached3)) return cached3;
4711
4993
  return null;
4712
4994
  }
@@ -4714,8 +4996,8 @@ var REPO_ROOT5, VENDORED_DIR, CACHE_DIR;
4714
4996
  var init_face_source = __esm({
4715
4997
  "../font-bake/face-source.ts"() {
4716
4998
  REPO_ROOT5 = fileURLToPath9(new URL("../..", import.meta.url));
4717
- VENDORED_DIR = process.env.HIMI_FONTS_VENDORED_DIR || join21(REPO_ROOT5, "stdlib", "fonts", "vendored", "wix-madefor");
4718
- CACHE_DIR = join21(process.env.XDG_CACHE_HOME || join21(homedir4(), ".cache"), "himi", "fonts");
4999
+ VENDORED_DIR = process.env.HIMI_FONTS_VENDORED_DIR || join22(REPO_ROOT5, "stdlib", "fonts", "vendored", "wix-madefor");
5000
+ CACHE_DIR = join22(process.env.XDG_CACHE_HOME || join22(homedir4(), ".cache"), "himi", "fonts");
4719
5001
  }
4720
5002
  });
4721
5003
 
@@ -4729,7 +5011,7 @@ __export(fonts_exports, {
4729
5011
  resolveFamilyName: () => resolveFamilyName
4730
5012
  });
4731
5013
  import { existsSync as existsSync19, readFileSync as readFileSync21 } from "node:fs";
4732
- import { join as join22 } from "node:path";
5014
+ import { join as join23 } from "node:path";
4733
5015
  function readJson(path, fallback) {
4734
5016
  try {
4735
5017
  return JSON.parse(readFileSync21(path, "utf8"));
@@ -4761,11 +5043,11 @@ function listReport(opts = {}) {
4761
5043
  return { ok: true, count: families.length, totalBytes, families };
4762
5044
  }
4763
5045
  function fontFindings(appDir2) {
4764
- const tokensPath = join22(appDir2, "tokens.json");
5046
+ const tokensPath = join23(appDir2, "tokens.json");
4765
5047
  if (!existsSync19(tokensPath)) return null;
4766
5048
  const catalog = loadCatalog();
4767
5049
  const tokens = readJson(tokensPath, {});
4768
- const policy = readJson(join22(appDir2, "fonts.json"), {});
5050
+ const policy = readJson(join23(appDir2, "fonts.json"), {});
4769
5051
  const plan = planFonts(tokens, policy, catalog);
4770
5052
  const findings = [];
4771
5053
  for (const family of plan.unresolved) {
@@ -4851,7 +5133,7 @@ function addReport(appDir2, family) {
4851
5133
  totalBytes: bytes2,
4852
5134
  problems: [],
4853
5135
  families: [{ family: resolved, license: fam?.license.id ?? "system", copyright: fam?.license.copyright, kb: Math.round(bytes2 / 1024) }],
4854
- hint: `add "${resolved}" to ${join22(appDir2, "fonts.json")} under "families" (or name it in tokens.json typography), then run gen.sh. Licence: ${fam?.license.id ?? "system"}${fam?.license.copyright ? ` \u2014 ${fam.license.copyright}` : ""}`
5136
+ hint: `add "${resolved}" to ${join23(appDir2, "fonts.json")} under "families" (or name it in tokens.json typography), then run gen.sh. Licence: ${fam?.license.id ?? "system"}${fam?.license.copyright ? ` \u2014 ${fam.license.copyright}` : ""}`
4855
5137
  };
4856
5138
  }
4857
5139
  var DEFAULT_ASSET_BUDGET_BYTES;
@@ -5773,7 +6055,7 @@ var init_src = __esm({
5773
6055
  // ../app-icons/plan.ts
5774
6056
  import { existsSync as existsSync23, readFileSync as readFileSync25, readdirSync as readdirSync12 } from "node:fs";
5775
6057
  import { createHash as createHash7 } from "node:crypto";
5776
- import { dirname as dirname16, join as join26, resolve as resolve17 } from "node:path";
6058
+ import { dirname as dirname16, join as join27, resolve as resolve17 } from "node:path";
5777
6059
  function stripComments(src) {
5778
6060
  let out = "";
5779
6061
  let i = 0;
@@ -5903,10 +6185,10 @@ function rotateHue(hex2, deg) {
5903
6185
  function resolveTokensPath(app, configTs) {
5904
6186
  const literal = /^ {2}designTokensPath\s*:\s*["']([^"']+)["']/m.exec(stripComments(configTs))?.[1];
5905
6187
  if (literal) return { path: resolve17(appDir(app), literal), source: "declared" };
5906
- return { path: join26(appDir(app), "tokens.json"), source: "assumed" };
6188
+ return { path: join27(appDir(app), "tokens.json"), source: "assumed" };
5907
6189
  }
5908
6190
  function resolveToken(app, token, tokensPathOverride) {
5909
- const tokensPath = tokensPathOverride ?? join26(appDir(app), "tokens.json");
6191
+ const tokensPath = tokensPathOverride ?? join27(appDir(app), "tokens.json");
5910
6192
  if (!existsSync23(tokensPath)) {
5911
6193
  throw new Error(`app-icons: ${app} has no tokens.json at ${tokensPath} (needed to resolve "${token}").`);
5912
6194
  }
@@ -5989,17 +6271,17 @@ function iconSpec(app, configTs, id, tokensPathOverride) {
5989
6271
  };
5990
6272
  }
5991
6273
  function iosAssetCatalog2(app) {
5992
- return assetCatalogIn(join26(appDir(app), "ios"));
6274
+ return assetCatalogIn(join27(appDir(app), "ios"));
5993
6275
  }
5994
6276
  function assetCatalogIn(platformDir) {
5995
6277
  if (!existsSync23(platformDir)) return void 0;
5996
6278
  const children = readdirSync12(platformDir).filter((c) => !c.startsWith("build")).sort();
5997
- const catalogs = children.map((c) => join26(platformDir, c, "Assets.xcassets")).filter(existsSync23);
6279
+ const catalogs = children.map((c) => join27(platformDir, c, "Assets.xcassets")).filter(existsSync23);
5998
6280
  if (catalogs.length === 1) return catalogs[0];
5999
6281
  if (catalogs.length > 1) return void 0;
6000
6282
  const targets = [];
6001
6283
  for (const child of children) {
6002
- const dir = join26(platformDir, child);
6284
+ const dir = join27(platformDir, child);
6003
6285
  let entries;
6004
6286
  try {
6005
6287
  entries = readdirSync12(dir);
@@ -6007,20 +6289,20 @@ function assetCatalogIn(platformDir) {
6007
6289
  continue;
6008
6290
  }
6009
6291
  if (entries.includes("Info.plist") || entries.some((e) => e.endsWith(".swift"))) {
6010
- targets.push(join26(dir, "Assets.xcassets"));
6292
+ targets.push(join27(dir, "Assets.xcassets"));
6011
6293
  }
6012
6294
  }
6013
6295
  return targets.length === 1 ? targets[0] : void 0;
6014
6296
  }
6015
6297
  function androidResDir2(manifestPath) {
6016
- return join26(dirname16(manifestPath), "res");
6298
+ return join27(dirname16(manifestPath), "res");
6017
6299
  }
6018
6300
  function androidManifestPath(app) {
6019
- const androidDir = join26(appDir(app), "android");
6301
+ const androidDir = join27(appDir(app), "android");
6020
6302
  if (!existsSync23(androidDir)) return void 0;
6021
- const preferred = join26(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
6303
+ const preferred = join27(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
6022
6304
  if (existsSync23(preferred)) return preferred;
6023
- const candidates = readdirSync12(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join26(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync23);
6305
+ const candidates = readdirSync12(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join27(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync23);
6024
6306
  return candidates.length === 1 ? candidates[0] : void 0;
6025
6307
  }
6026
6308
  function androidOptedIn2(manifestPath) {
@@ -6029,7 +6311,7 @@ function androidOptedIn2(manifestPath) {
6029
6311
  }
6030
6312
  function templateIconSha256() {
6031
6313
  const cat = iosAssetCatalog2("_template");
6032
- return cat ? fileSha256(join26(cat, "AppIcon.appiconset", "icon_1024.png")) : null;
6314
+ return cat ? fileSha256(join27(cat, "AppIcon.appiconset", "icon_1024.png")) : null;
6033
6315
  }
6034
6316
  function placeholderHashes() {
6035
6317
  const current = templateIconSha256();
@@ -6048,7 +6330,7 @@ function generationAllowed(app, configTs, opts = {}) {
6048
6330
  return { allowed: true, reason: "declares an icon block" };
6049
6331
  }
6050
6332
  const iosCat = iosAssetCatalog2(app);
6051
- const iosIcon = iosCat ? join26(iosCat, "AppIcon.appiconset", "icon_1024.png") : void 0;
6333
+ const iosIcon = iosCat ? join27(iosCat, "AppIcon.appiconset", "icon_1024.png") : void 0;
6052
6334
  if (!iosIcon || !existsSync23(iosIcon)) return { allowed: true, reason: "no icon yet" };
6053
6335
  const hash = fileSha256(iosIcon);
6054
6336
  const placeholders = opts.placeholderHashes ?? placeholderHashes();
@@ -6074,7 +6356,7 @@ var init_plan2 = __esm({
6074
6356
 
6075
6357
  // ../native-config/icon.ts
6076
6358
  import { existsSync as existsSync24, readFileSync as readFileSync26 } from "node:fs";
6077
- import { join as join27 } from "node:path";
6359
+ import { join as join28 } from "node:path";
6078
6360
  function androidAdaptiveIconXml() {
6079
6361
  return [
6080
6362
  '<?xml version="1.0" encoding="utf-8"?>',
@@ -6101,7 +6383,7 @@ function androidIconArtifacts(app, configTs) {
6101
6383
  if (!androidOptedIn2(manifest)) return [];
6102
6384
  if (!generationAllowed(app, configTs).allowed) return [];
6103
6385
  const res = androidResDir2(manifest);
6104
- const foreground = join27(res, "mipmap-mdpi", "ic_launcher_foreground.png");
6386
+ const foreground = join28(res, "mipmap-mdpi", "ic_launcher_foreground.png");
6105
6387
  if (!existsSync24(foreground)) {
6106
6388
  throw new Error(
6107
6389
  `native-config icon: ${app} opts into @mipmap/ic_launcher but has no mipmap-*/ic_launcher_foreground.png. Run \`npm run app-icons -- ${app}\` first \u2014 the XML emitted here points at those rasters.`
@@ -6111,11 +6393,11 @@ function androidIconArtifacts(app, configTs) {
6111
6393
  const spec = iconSpec(app, configTs, id, resolveTokensPath(app, configTs).path);
6112
6394
  return [
6113
6395
  ...ANDROID_ADAPTIVE_FILES.map((name) => ({
6114
- path: join27(res, "mipmap-anydpi-v26", name),
6396
+ path: join28(res, "mipmap-anydpi-v26", name),
6115
6397
  content: androidAdaptiveIconXml()
6116
6398
  })),
6117
6399
  {
6118
- path: join27(res, "values", ANDROID_ICON_BACKGROUND_FILE),
6400
+ path: join28(res, "values", ANDROID_ICON_BACKGROUND_FILE),
6119
6401
  content: androidIconBackgroundXml(spec.background)
6120
6402
  }
6121
6403
  ];
@@ -6350,7 +6632,7 @@ __export(gen_native_config_exports, {
6350
6632
  watchInfoPlan: () => watchInfoPlan
6351
6633
  });
6352
6634
  import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync27, readdirSync as readdirSync13, rmSync as rmSync3, writeFileSync as writeFileSync11 } from "node:fs";
6353
- import { dirname as dirname17, join as join28 } from "node:path";
6635
+ import { dirname as dirname17, join as join29 } from "node:path";
6354
6636
  function parseStringArray(configTs, field) {
6355
6637
  const m = new RegExp(`${field}:\\s*\\[([^\\]]*)\\]`).exec(configTs);
6356
6638
  if (!m) return [];
@@ -6422,7 +6704,7 @@ function buildSet(capabilities, envelope = []) {
6422
6704
  return [...new Set([...capabilities, ...envelope].map(catalogKey))].sort();
6423
6705
  }
6424
6706
  function loadOverrides(app) {
6425
- const p = join28(appDir(app), "native-config.overrides.json");
6707
+ const p = join29(appDir(app), "native-config.overrides.json");
6426
6708
  if (!existsSync25(p)) return {};
6427
6709
  return JSON.parse(readFileSync27(p, "utf8"));
6428
6710
  }
@@ -6479,14 +6761,14 @@ function endText(kind, label2, indent) {
6479
6761
  return kind === "yaml" ? `${indent}# <<< ${label2} <<<` : `${indent}<!-- <<< ${label2} <<< -->`;
6480
6762
  }
6481
6763
  function hostAppliesDeferredWidgetActions(app) {
6482
- const root = join28(appDir(app), "ios");
6764
+ const root = join29(appDir(app), "ios");
6483
6765
  if (!existsSync25(root)) return false;
6484
6766
  const stack = [root];
6485
6767
  while (stack.length) {
6486
6768
  const dir = stack.pop();
6487
6769
  for (const entry of readdirSync13(dir, { withFileTypes: true })) {
6488
6770
  if (entry.name.startsWith(".") || entry.name === "GeneratedLiveActivity") continue;
6489
- const path = join28(dir, entry.name);
6771
+ const path = join29(dir, entry.name);
6490
6772
  if (entry.isDirectory()) {
6491
6773
  if (entry.name.endsWith(".xcodeproj") || entry.name === "build") continue;
6492
6774
  stack.push(path);
@@ -6513,14 +6795,14 @@ function injectBlock(text2, kind, label2, render) {
6513
6795
  return [...lines.slice(0, beginIdx), ...block, ...lines.slice(endIdx + 1)].join("\n");
6514
6796
  }
6515
6797
  function androidManifestPath2(app) {
6516
- const androidDir = join28(appDir(app), "android");
6517
- const preferred = join28(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
6798
+ const androidDir = join29(appDir(app), "android");
6799
+ const preferred = join29(androidDir, `${app}-app`, "src", "main", "AndroidManifest.xml");
6518
6800
  if (existsSync25(preferred)) return preferred;
6519
- const candidates = existsSync25(androidDir) ? readdirSync13(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join28(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync25) : [];
6801
+ const candidates = existsSync25(androidDir) ? readdirSync13(androidDir, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.endsWith("-app")).map((d) => join29(androidDir, d.name, "src", "main", "AndroidManifest.xml")).filter(existsSync25) : [];
6520
6802
  return candidates.length === 1 ? candidates[0] : preferred;
6521
6803
  }
6522
6804
  function automotiveAppDescPath(app) {
6523
- return join28(dirname17(androidManifestPath2(app)), "res", "xml", "automotive_app_desc.xml");
6805
+ return join29(dirname17(androidManifestPath2(app)), "res", "xml", "automotive_app_desc.xml");
6524
6806
  }
6525
6807
  function applyToFile(path, kind, label2, render, check) {
6526
6808
  if (!existsSync25(path)) return { path, outcome: "skipped" };
@@ -6619,9 +6901,9 @@ function genNativeConfig(app, opts = {}) {
6619
6901
  const plan = live2;
6620
6902
  files.push(applyToFile(iosYml, "yaml", LABEL_LIVE_TARGET, (i) => plan.target.map((line) => i + line), check));
6621
6903
  files.push(applyToFile(iosYml, "yaml", LABEL_LIVE_EMBED, (i) => plan.embed.map((line) => i + line), check));
6622
- const source = join28(dirname17(iosYml), LIVE_SOURCE_PATH);
6904
+ const source = join29(dirname17(iosYml), LIVE_SOURCE_PATH);
6623
6905
  files.push(plan.source ? applyWholeFile(source, plan.source, check) : removeWholeFile(source, check));
6624
- const entitlements = join28(dirname17(iosYml), LIVE_ENTITLEMENTS_PATH);
6906
+ const entitlements = join29(dirname17(iosYml), LIVE_ENTITLEMENTS_PATH);
6625
6907
  files.push(plan.entitlements ? applyWholeFile(entitlements, plan.entitlements, check) : removeWholeFile(entitlements, check));
6626
6908
  }
6627
6909
  const iosSplashManaged = existsSync25(iosYml) && hasMarkers(readFileSync27(iosYml, "utf8"), "yaml", LABEL_IOS_SPLASH);
@@ -6759,7 +7041,8 @@ async function devModuleUrl(entry, dir) {
6759
7041
  // intentionally point at `.ts` sources, which are only loadable under tsx.
6760
7042
  umbrella: true,
6761
7043
  resolveTsExtensions: true,
6762
- absWorkingDir: dir
7044
+ absWorkingDir: dir,
7045
+ confineTo: dir
6763
7046
  });
6764
7047
  return pathToFileURL5(out).href;
6765
7048
  }
@@ -6881,8 +7164,8 @@ var init_build_sha = __esm({
6881
7164
 
6882
7165
  // ../../core/dev-server/src/app-assets.ts
6883
7166
  import { createHash as createHash8 } from "node:crypto";
6884
- import { existsSync as existsSync27, lstatSync as lstatSync3, readFileSync as readFileSync29, readdirSync as readdirSync15, realpathSync as realpathSync2, statSync as statSync5 } from "node:fs";
6885
- import { join as join29, resolve as resolve19, sep as sep4 } from "node:path";
7167
+ import { existsSync as existsSync27, lstatSync as lstatSync3, readFileSync as readFileSync29, readdirSync as readdirSync15, realpathSync as realpathSync3, statSync as statSync6 } from "node:fs";
7168
+ import { join as join30, resolve as resolve19, sep as sep5 } from "node:path";
6886
7169
  function contentTypeFor(name) {
6887
7170
  const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
6888
7171
  return CONTENT_TYPES[ext] ?? "application/octet-stream";
@@ -6896,19 +7179,19 @@ function assetSha256(bytes2) {
6896
7179
  function findAssetsDir(appDir2) {
6897
7180
  let declared;
6898
7181
  try {
6899
- declared = JSON.parse(readFileSync29(join29(appDir2, "himalaya.content.json"), "utf8")).assets;
7182
+ declared = JSON.parse(readFileSync29(join30(appDir2, "himalaya.content.json"), "utf8")).assets;
6900
7183
  } catch {
6901
7184
  declared = void 0;
6902
7185
  }
6903
7186
  const rel = typeof declared === "string" && declared ? declared : "assets";
6904
7187
  const abs = resolve19(appDir2, rel);
6905
7188
  const base = resolve19(appDir2);
6906
- if (abs !== base && !abs.startsWith(base + sep4)) return null;
6907
- return existsSync27(abs) && statSync5(abs).isDirectory() ? abs : null;
7189
+ if (abs !== base && !abs.startsWith(base + sep5)) return null;
7190
+ return existsSync27(abs) && statSync6(abs).isDirectory() ? abs : null;
6908
7191
  }
6909
7192
  function readOtaOnlyPatterns(appDir2) {
6910
7193
  try {
6911
- const raw = JSON.parse(readFileSync29(join29(appDir2, "assets.json"), "utf8"));
7194
+ const raw = JSON.parse(readFileSync29(join30(appDir2, "assets.json"), "utf8"));
6912
7195
  return Array.isArray(raw.otaOnly) ? raw.otaOnly.filter((p) => typeof p === "string") : [];
6913
7196
  } catch {
6914
7197
  return [];
@@ -6926,7 +7209,7 @@ function collectAssets(appDir2) {
6926
7209
  const walk2 = (abs, prefix) => {
6927
7210
  for (const entry of readdirSync15(abs, { withFileTypes: true })) {
6928
7211
  if (entry.name.startsWith(".")) continue;
6929
- const child = join29(abs, entry.name);
7212
+ const child = join30(abs, entry.name);
6930
7213
  const name = prefix ? `${prefix}/${entry.name}` : entry.name;
6931
7214
  if (entry.isDirectory()) {
6932
7215
  walk2(child, name);
@@ -6935,7 +7218,7 @@ function collectAssets(appDir2) {
6935
7218
  if (!entry.isFile()) continue;
6936
7219
  const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
6937
7220
  const baked = !OTA_ONLY_BY_DEFAULT.has(ext) && !otaOnly.some((p) => matchesPattern(name, p));
6938
- out.push({ name, path: child, bytes: statSync5(child).size, contentType: contentTypeFor(name), baked });
7221
+ out.push({ name, path: child, bytes: statSync6(child).size, contentType: contentTypeFor(name), baked });
6939
7222
  }
6940
7223
  };
6941
7224
  walk2(dir, "");
@@ -6944,7 +7227,7 @@ function collectAssets(appDir2) {
6944
7227
  function collectBrandFonts(appDir2) {
6945
7228
  let faces;
6946
7229
  try {
6947
- faces = JSON.parse(readFileSync29(join29(appDir2, "media/fonts/faces.json"), "utf8"));
7230
+ faces = JSON.parse(readFileSync29(join30(appDir2, "media/fonts/faces.json"), "utf8"));
6948
7231
  } catch {
6949
7232
  return [];
6950
7233
  }
@@ -6957,11 +7240,11 @@ function collectBrandFonts(appDir2) {
6957
7240
  const rel = typeof raw?.file === "string" ? raw.file : "";
6958
7241
  if (!family || !postscriptName || !rel) continue;
6959
7242
  if (rel.startsWith("/") || rel.split("/").includes("..")) continue;
6960
- const path = join29(appDir2, rel);
7243
+ const path = join30(appDir2, rel);
6961
7244
  try {
6962
7245
  if (!lstatSync3(path).isFile()) continue;
6963
- const root = realpathSync2(appDir2);
6964
- if (!realpathSync2(path).startsWith(root + sep4)) continue;
7246
+ const root = realpathSync3(appDir2);
7247
+ if (!realpathSync3(path).startsWith(root + sep5)) continue;
6965
7248
  } catch {
6966
7249
  continue;
6967
7250
  }
@@ -8782,7 +9065,7 @@ var init_stable_ids = __esm({
8782
9065
  // ../../core/dev-server/src/preview.ts
8783
9066
  import { spawn as spawn2 } from "node:child_process";
8784
9067
  import { createHash as createHash11 } from "node:crypto";
8785
- import { existsSync as existsSync30, createReadStream, statSync as statSync6, readFileSync as readFileSync33, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
9068
+ import { existsSync as existsSync30, createReadStream, statSync as statSync7, readFileSync as readFileSync33, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
8786
9069
  import { homedir as homedir5, tmpdir as tmpdir2 } from "node:os";
8787
9070
  import { resolve as resolvePath, dirname as dirname20 } from "node:path";
8788
9071
  import { fileURLToPath as fileURLToPath14 } from "node:url";
@@ -9480,7 +9763,7 @@ function finalizeRecording(key2, rec) {
9480
9763
  } catch {
9481
9764
  }
9482
9765
  }
9483
- const sizeBytes = existsSync30(rec.localPath) ? statSync6(rec.localPath).size : 0;
9766
+ const sizeBytes = existsSync30(rec.localPath) ? statSync7(rec.localPath).size : 0;
9484
9767
  clips.set(rec.id, { path: rec.localPath, mime: rec.mime });
9485
9768
  recordings.delete(key2);
9486
9769
  return { sizeBytes };
@@ -9557,7 +9840,7 @@ async function handlePreviewClip(res, url) {
9557
9840
  res.setHeader("content-type", "application/json");
9558
9841
  return void res.end(JSON.stringify({ error: "clip_not_found", id }));
9559
9842
  }
9560
- const size = statSync6(clip.path).size;
9843
+ const size = statSync7(clip.path).size;
9561
9844
  res.statusCode = 200;
9562
9845
  res.setHeader("content-type", clip.mime);
9563
9846
  res.setHeader("content-length", String(size));
@@ -11170,6 +11453,7 @@ function shouldRevealError(args) {
11170
11453
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
11171
11454
  var init_validation = __esm({
11172
11455
  "../../core/ts/src/validation.ts"() {
11456
+ "use strict";
11173
11457
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
11174
11458
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
11175
11459
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -14515,6 +14799,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
14515
14799
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
14516
14800
  var init_worker_subtree = __esm({
14517
14801
  "../../core/ts/src/worker-subtree.ts"() {
14802
+ "use strict";
14518
14803
  init_worker_subtree_safe_types_generated();
14519
14804
  WORKER_SUBTREE_CEILINGS = Object.freeze({
14520
14805
  maxEncodedBytes: 65536,
@@ -14734,8 +15019,8 @@ function ensureCountUpRuntime() {
14734
15019
  const neg = fixed.startsWith("-");
14735
15020
  const body = neg ? fixed.slice(1) : fixed;
14736
15021
  const [int, frac] = body.split(".");
14737
- const sep10 = int.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
14738
- return (neg ? "-" : "") + sep10 + (frac ? "." + frac : "");
15022
+ const sep11 = int.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
15023
+ return (neg ? "-" : "") + sep11 + (frac ? "." + frac : "");
14739
15024
  };
14740
15025
  const animateEl = (el) => {
14741
15026
  const node = el;
@@ -21912,18 +22197,18 @@ var init_motion_flags = __esm({
21912
22197
 
21913
22198
  // ../../core/dev-server/src/kits.ts
21914
22199
  import { readdirSync as readdirSync16, readFileSync as readFileSync36 } from "node:fs";
21915
- import { resolve as resolve25, join as join30 } from "node:path";
22200
+ import { resolve as resolve25, join as join31 } from "node:path";
21916
22201
  import { fileURLToPath as fileURLToPath15 } from "node:url";
21917
22202
  function listKits() {
21918
22203
  return readdirSync16(CATALOG).filter((f) => f.endsWith(".json")).map((f) => {
21919
- const { tokens, ...meta } = JSON.parse(readFileSync36(join30(CATALOG, f), "utf8"));
22204
+ const { tokens, ...meta } = JSON.parse(readFileSync36(join31(CATALOG, f), "utf8"));
21920
22205
  return meta;
21921
22206
  });
21922
22207
  }
21923
22208
  function readKit(id) {
21924
22209
  if (!/^[a-z0-9-]+$/.test(id)) return null;
21925
22210
  try {
21926
- return JSON.parse(readFileSync36(join30(CATALOG, `${id}.json`), "utf8"));
22211
+ return JSON.parse(readFileSync36(join31(CATALOG, `${id}.json`), "utf8"));
21927
22212
  } catch {
21928
22213
  return null;
21929
22214
  }
@@ -21932,7 +22217,7 @@ var ROOT5, CATALOG;
21932
22217
  var init_kits = __esm({
21933
22218
  "../../core/dev-server/src/kits.ts"() {
21934
22219
  ROOT5 = resolve25(fileURLToPath15(new URL("../../..", import.meta.url)));
21935
- CATALOG = join30(ROOT5, "stdlib/design-kits");
22220
+ CATALOG = join31(ROOT5, "stdlib/design-kits");
21936
22221
  }
21937
22222
  });
21938
22223
 
@@ -24135,7 +24420,7 @@ __export(config_exports, {
24135
24420
  validateOverlayPatch: () => validateOverlayPatch
24136
24421
  });
24137
24422
  import { mkdirSync as mkdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync12 } from "node:fs";
24138
- import { dirname as dirname21, join as join31, resolve as resolve27 } from "node:path";
24423
+ import { dirname as dirname21, join as join32, resolve as resolve27 } from "node:path";
24139
24424
  import { fileURLToPath as fileURLToPath16 } from "node:url";
24140
24425
  function strip(line) {
24141
24426
  const h = line.indexOf("#");
@@ -24200,14 +24485,14 @@ function parseRevocationsYaml(text2) {
24200
24485
  function appConfigPath(app, file, root = REPO_ROOT6) {
24201
24486
  assertAppId(app, "appConfigPath");
24202
24487
  for (const base of ["apps", "test-apps"]) {
24203
- const p = join31(root, base, app, file);
24488
+ const p = join32(root, base, app, file);
24204
24489
  try {
24205
24490
  readFileSync38(p);
24206
24491
  return p;
24207
24492
  } catch {
24208
24493
  }
24209
24494
  }
24210
- return join31(root, "apps", app, file);
24495
+ return join32(root, "apps", app, file);
24211
24496
  }
24212
24497
  function serializeRolloutYaml(cfg) {
24213
24498
  const lines = [`app: ${cfg.app}`, "rings:"];
@@ -24351,7 +24636,7 @@ var init_config2 = __esm({
24351
24636
  import { createServer as createServer2 } from "node:http";
24352
24637
  import { readFile as readFile2, stat } from "node:fs/promises";
24353
24638
  import { readFileSync as readFileSync39, existsSync as existsSync33, watch, mkdirSync as mkdirSync16, rmSync as rmSync4 } from "node:fs";
24354
- import { extname, resolve as resolve28, normalize as normalize3, sep as sep5, join as join32 } from "node:path";
24639
+ import { extname, resolve as resolve28, normalize as normalize3, sep as sep6, join as join33 } from "node:path";
24355
24640
  import { createHash as createHash15, randomUUID } from "node:crypto";
24356
24641
  import { fileURLToPath as fileURLToPath17, pathToFileURL as pathToFileURL6 } from "node:url";
24357
24642
  function deepMerge2(base, patch) {
@@ -24526,7 +24811,7 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
24526
24811
  }
24527
24812
  function servedFontFaces(appDir2, tokens) {
24528
24813
  try {
24529
- const policyPath = join32(appDir2, "fonts.json");
24814
+ const policyPath = join33(appDir2, "fonts.json");
24530
24815
  const policy = existsSync33(policyPath) ? JSON.parse(readFileSync39(policyPath, "utf8")) : {};
24531
24816
  const plan = planFonts(tokens, policy);
24532
24817
  if (plan.families.length === 0) return void 0;
@@ -25169,7 +25454,7 @@ function startBundleSourceWatcher(state) {
25169
25454
  try {
25170
25455
  const watcher = watch(TIER5_SRC_ROOT, { persistent: false, recursive: true }, (_event, filename) => {
25171
25456
  if (!filename || !/\.(ts|tsx)$/.test(filename)) return;
25172
- const name = filename.split(sep5)[0];
25457
+ const name = filename.split(sep6)[0];
25173
25458
  if (!name || name.startsWith(".")) return;
25174
25459
  pending.add(name);
25175
25460
  if (timer) clearTimeout(timer);
@@ -25199,7 +25484,7 @@ async function reloadAppModule(state, opts) {
25199
25484
  const outfile = resolve28(HMR_TMP_DIR, `${current.name}-${++appReloadSeq}.mjs`);
25200
25485
  try {
25201
25486
  const lib = await import(pathToFileURL6(resolve28(TIER5_SRC_ROOT, "build-lib.mjs")).href);
25202
- await lib.buildAppEntry(entry, outfile);
25487
+ await lib.buildAppEntry(entry, outfile, { confineTo: current.dir });
25203
25488
  const mod = await import(pathToFileURL6(outfile).href);
25204
25489
  const next = mod.default;
25205
25490
  if (!next || !next.config || typeof next.homeScreenId !== "string" || !next.screens || typeof next.screens.exact !== "object" || Object.keys(next.screens.exact).length === 0 || !Array.isArray(next.actions) || !next.seed) {
@@ -25284,7 +25569,7 @@ function startAppSourceWatcher(state) {
25284
25569
  try {
25285
25570
  const watcher = watch(appDir2, { persistent: false, recursive: true }, (_event, filename) => {
25286
25571
  if (!filename) return;
25287
- const rel = String(filename).split(sep5).join("/");
25572
+ const rel = String(filename).split(sep6).join("/");
25288
25573
  if (!classifyAppPath(rel)) return;
25289
25574
  pending.add(rel);
25290
25575
  if (timer) clearTimeout(timer);
@@ -27446,7 +27731,7 @@ __export(build_exports2, {
27446
27731
  });
27447
27732
  import { createHash as createHash18 } from "node:crypto";
27448
27733
  import { readFileSync as readFileSync40, readdirSync as readdirSync18, existsSync as existsSync34 } from "node:fs";
27449
- import { dirname as dirname22, join as join33, resolve as resolve29 } from "node:path";
27734
+ import { dirname as dirname22, join as join34, resolve as resolve29 } from "node:path";
27450
27735
  import { fileURLToPath as fileURLToPath18 } from "node:url";
27451
27736
  import { createRequire as createRequire2 } from "node:module";
27452
27737
  import { execFileSync as execFileSync4 } from "node:child_process";
@@ -27484,7 +27769,7 @@ function l10nLintOptions(locale, appDir2) {
27484
27769
  const tags = (locale.translations ?? []).filter((t) => t !== locale.default);
27485
27770
  const catalog = /* @__PURE__ */ new Set();
27486
27771
  for (const tag of tags) {
27487
- const path = join33(appDir2, "l10n", `${tag}.json`);
27772
+ const path = join34(appDir2, "l10n", `${tag}.json`);
27488
27773
  if (!existsSync34(path)) continue;
27489
27774
  try {
27490
27775
  const parsed = JSON.parse(readFileSync40(path, "utf8"));
@@ -27581,7 +27866,7 @@ function shellSha(root = REPO_ROOT7) {
27581
27866
  function isMonorepoCheckout(dir) {
27582
27867
  try {
27583
27868
  const top = execFileSync4("git", ["rev-parse", "--show-toplevel"], { cwd: dir, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
27584
- return top.length > 0 && existsSync34(join33(top, "core", "schema", "core.proto"));
27869
+ return top.length > 0 && existsSync34(join34(top, "core", "schema", "core.proto"));
27585
27870
  } catch {
27586
27871
  return false;
27587
27872
  }
@@ -27811,7 +28096,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
27811
28096
  if (addrCache.has(name)) return addrCache.get(name);
27812
28097
  let bytes2;
27813
28098
  try {
27814
- bytes2 = readFileSync40(join33(tier5Dir, `${name}.bundle.js`));
28099
+ bytes2 = readFileSync40(join34(tier5Dir, `${name}.bundle.js`));
27815
28100
  } catch {
27816
28101
  return null;
27817
28102
  }
@@ -27832,7 +28117,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
27832
28117
  const MAX_AUTHORING_DOC_BYTES = 128 * 1024;
27833
28118
  if (store.putDoc) {
27834
28119
  for (const [name, file] of [["spec", "SPEC.md"], ["mobile-ux", "MOBILE-UX.md"]]) {
27835
- const path = join33(preloaded.dir, file);
28120
+ const path = join34(preloaded.dir, file);
27836
28121
  if (!existsSync34(path)) continue;
27837
28122
  const bytes2 = readFileSync40(path);
27838
28123
  if (bytes2.length === 0 || bytes2.length > MAX_AUTHORING_DOC_BYTES) {
@@ -27892,7 +28177,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
27892
28177
  const canEmitFonts = hasVendoredFloor();
27893
28178
  try {
27894
28179
  const appTokens = readTokensForLint(preloaded) ?? {};
27895
- const policy = existsSync34(join33(preloaded.dir, "fonts.json")) ? JSON.parse(readFileSync40(join33(preloaded.dir, "fonts.json"), "utf8")) : {};
28180
+ const policy = existsSync34(join34(preloaded.dir, "fonts.json")) ? JSON.parse(readFileSync40(join34(preloaded.dir, "fonts.json"), "utf8")) : {};
27896
28181
  fontPlan = planFonts(appTokens, policy);
27897
28182
  } catch (err) {
27898
28183
  throw new Error(
@@ -28357,7 +28642,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
28357
28642
  await store.reset(app);
28358
28643
  const sources = {};
28359
28644
  for (const definition of config.functions.functions) {
28360
- const candidates = [".ts", ".js", ".mts", ".mjs"].map((extension) => join33(preloaded.dir, "functions", `${definition.name}${extension}`));
28645
+ const candidates = [".ts", ".js", ".mts", ".mjs"].map((extension) => join34(preloaded.dir, "functions", `${definition.name}${extension}`));
28361
28646
  const source = candidates.find((candidate) => existsSync34(candidate));
28362
28647
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
28363
28648
  sources[definition.name] = readFileSync40(source, "utf8");
@@ -28388,7 +28673,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
28388
28673
  return { manifest, failedToBuild: [], missingBundles: [], auth: {} };
28389
28674
  }
28390
28675
  function listBuildableApps() {
28391
- const appsDir = join33(REPO_ROOT7, "apps");
28676
+ const appsDir = join34(REPO_ROOT7, "apps");
28392
28677
  return readdirSync18(appsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("_")).map((d) => d.name);
28393
28678
  }
28394
28679
  var BAKE_OFFLINE_BASE, KNOWN_ICON_NAMES, KNOWN_ICONS, REPO_ROOT7, TIER5_DIR, requireFromHere, SOURCEMAP_ON_DEMAND_PATH, BAKED_SERVER_BASE_URL;
@@ -28419,9 +28704,9 @@ var init_build3 = __esm({
28419
28704
  KNOWN_ICON_NAMES = [...ICON_NAMES].sort();
28420
28705
  KNOWN_ICONS = new Set(KNOWN_ICON_NAMES);
28421
28706
  REPO_ROOT7 = resolve29(dirname22(fileURLToPath18(import.meta.url)), "../..");
28422
- TIER5_DIR = join33(REPO_ROOT7, "core/dev-server/tier5-bundles");
28707
+ TIER5_DIR = join34(REPO_ROOT7, "core/dev-server/tier5-bundles");
28423
28708
  requireFromHere = createRequire2(import.meta.url);
28424
- SOURCEMAP_ON_DEMAND_PATH = join33(REPO_ROOT7, "core/dev-server/tier5-bundles-src/sourcemap-on-demand.mjs");
28709
+ SOURCEMAP_ON_DEMAND_PATH = join34(REPO_ROOT7, "core/dev-server/tier5-bundles-src/sourcemap-on-demand.mjs");
28425
28710
  BAKED_SERVER_BASE_URL = "http://baked.himalaya.invalid";
28426
28711
  }
28427
28712
  });
@@ -28468,6 +28753,133 @@ var init_net_scenario = __esm({
28468
28753
  }
28469
28754
  });
28470
28755
 
28756
+ // ../../core/dev-server/src/crawl-relations.ts
28757
+ function inversePartner(action, declared) {
28758
+ if (/^toggle/i.test(action)) return null;
28759
+ const candidates = [];
28760
+ if (/^un[A-Z_a-z]/.test(action)) {
28761
+ candidates.push(lowerFirst(action.slice(2)), action.slice(2));
28762
+ } else {
28763
+ candidates.push("un" + action, "un" + upperFirst(action));
28764
+ }
28765
+ for (const c of candidates) if (c !== action && declared.includes(c)) return c;
28766
+ for (const [a, b] of PREFIX_PAIRS) {
28767
+ for (const [from, to] of [
28768
+ [a, b],
28769
+ [b, a]
28770
+ ]) {
28771
+ if (!action.toLowerCase().startsWith(from)) continue;
28772
+ const rest = action.slice(from.length);
28773
+ for (const c of [to + rest, to + upperFirst(rest), to]) {
28774
+ if (c !== action && declared.includes(c)) return c;
28775
+ }
28776
+ }
28777
+ }
28778
+ return null;
28779
+ }
28780
+ function isUndoAction(action) {
28781
+ if (/^toggle/i.test(action)) return false;
28782
+ if (/^un[A-Z]/.test(action) || /^un[a-z]/.test(action)) return true;
28783
+ return PREFIX_PAIRS.some(([, undo]) => action.toLowerCase().startsWith(undo));
28784
+ }
28785
+ function isOptionSelection(params) {
28786
+ return Object.keys(params).some((k) => /^(selected|tab|segment|mode|scope)/i.test(k) || /Id$/.test(k) || k === "id");
28787
+ }
28788
+ function stripVolatile(value, depth = 0) {
28789
+ if (depth > 8 || value === null || typeof value !== "object") return value;
28790
+ if (Array.isArray(value)) return value.map((v) => stripVolatile(v, depth + 1));
28791
+ const out = {};
28792
+ for (const k of Object.keys(value).sort()) {
28793
+ if (VOLATILE_KEY.test(k)) continue;
28794
+ out[k] = stripVolatile(value[k], depth + 1);
28795
+ }
28796
+ return out;
28797
+ }
28798
+ function relationalSignature(state) {
28799
+ return JSON.stringify(stripVolatile(state));
28800
+ }
28801
+ function listLengths(state, depth = 4) {
28802
+ const out = {};
28803
+ const walk2 = (node, path, left) => {
28804
+ if (left < 0 || node === null || typeof node !== "object") return;
28805
+ if (Array.isArray(node)) {
28806
+ out[path] = node.length;
28807
+ return;
28808
+ }
28809
+ for (const [k, v] of Object.entries(node)) {
28810
+ walk2(v, path ? `${path}.${k}` : k, left - 1);
28811
+ }
28812
+ };
28813
+ walk2(state, "", depth);
28814
+ return out;
28815
+ }
28816
+ function summarizeDiff(aSig, bSig, limit = 3) {
28817
+ let a;
28818
+ let b;
28819
+ try {
28820
+ a = JSON.parse(aSig);
28821
+ b = JSON.parse(bSig);
28822
+ } catch {
28823
+ return aSig === bSig ? "" : "states differ (unparseable signature)";
28824
+ }
28825
+ const diffs = [];
28826
+ const brief = (v) => {
28827
+ const t = JSON.stringify(v);
28828
+ if (t === void 0) return "undefined";
28829
+ return t.length > 80 ? `${t.slice(0, 77)}\u2026` : t;
28830
+ };
28831
+ const walk2 = (x, y, path) => {
28832
+ if (diffs.length >= limit) return;
28833
+ const bothObjects = x !== null && y !== null && typeof x === "object" && typeof y === "object" && !Array.isArray(x) && !Array.isArray(y);
28834
+ if (bothObjects) {
28835
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(x), ...Object.keys(y)])) {
28836
+ walk2(x[k], y[k], path ? `${path}.${k}` : k);
28837
+ }
28838
+ return;
28839
+ }
28840
+ if (Array.isArray(x) && Array.isArray(y)) {
28841
+ if (x.length !== y.length) {
28842
+ diffs.push(`${path || "state"}: ${x.length} \u2192 ${y.length} items`);
28843
+ return;
28844
+ }
28845
+ for (let i = 0; i < x.length && diffs.length < limit; i++) walk2(x[i], y[i], `${path}[${i}]`);
28846
+ return;
28847
+ }
28848
+ const xs = brief(x);
28849
+ const ys = brief(y);
28850
+ if (xs !== ys) diffs.push(`${path || "state"}: ${xs} \u2192 ${ys}`);
28851
+ };
28852
+ walk2(a, b, "");
28853
+ return diffs.slice(0, limit).join("; ");
28854
+ }
28855
+ var PREFIX_PAIRS, upperFirst, lowerFirst, REFRESH_ACTION, LOAD_MORE_ACTION, FILTER_ACTION, VOLATILE_KEY;
28856
+ var init_crawl_relations = __esm({
28857
+ "../../core/dev-server/src/crawl-relations.ts"() {
28858
+ PREFIX_PAIRS = [
28859
+ ["add", "remove"],
28860
+ ["add", "delete"],
28861
+ // NOT ["start", "stop"]. Measured 2026-09-16: it produced 2 warnings on unmutated, correct code
28862
+ // (voice-journal, `start → stop` and `startBackground → stop`, both leaving
28863
+ // `statusLabel: "Ready" → "Ready — recording stopped"`). start/stop is a LIFECYCLE pair, not an
28864
+ // algebraic inverse: stopping does not unmake the recording that was started, and an app is right
28865
+ // to say so. Only pairs where the undo genuinely restores the prior value belong here.
28866
+ ["open", "close"],
28867
+ ["show", "hide"],
28868
+ ["expand", "collapse"],
28869
+ ["select", "deselect"],
28870
+ ["increment", "decrement"],
28871
+ ["follow", "unfollow"],
28872
+ ["save", "unsave"]
28873
+ ];
28874
+ upperFirst = (s) => s ? s[0].toUpperCase() + s.slice(1) : s;
28875
+ lowerFirst = (s) => s ? s[0].toLowerCase() + s.slice(1) : s;
28876
+ REFRESH_ACTION = /^(refresh|reload|retry|sync|pullToRefresh)/i;
28877
+ LOAD_MORE_ACTION = /^(loadMore|nextPage|fetchMore)|^more$|More$|NextPage$/;
28878
+ FILTER_ACTION = /^(filter|search|query|sort)|(Filter|Search|Query|Sort)/;
28879
+ VOLATILE_KEY = /^_|^(id|uuid|guid|nonce|seed|token|timestamp|now|revision|etag)$|(Id|ID|Uuid|Guid|Nonce|Seed|Token|At|Timestamp|Ms|Revision|ETag)$/;
28880
+ }
28881
+ });
28882
+
28471
28883
  // ../../core/dev-server/src/screen-crawl.ts
28472
28884
  var screen_crawl_exports = {};
28473
28885
  __export(screen_crawl_exports, {
@@ -28480,6 +28892,7 @@ __export(screen_crawl_exports, {
28480
28892
  crawlScreenDeep: () => crawlScreenDeep,
28481
28893
  crawlScreenHostile: () => crawlScreenHostile,
28482
28894
  crawlScreenJourney: () => crawlScreenJourney,
28895
+ crawlScreenRelations: () => crawlScreenRelations,
28483
28896
  crawlSignature: () => crawlSignature,
28484
28897
  deepStateSignature: () => deepStateSignature,
28485
28898
  emptiedArrays: () => emptiedArrays,
@@ -28490,6 +28903,7 @@ __export(screen_crawl_exports, {
28490
28903
  parseWorkerAction: () => parseWorkerAction,
28491
28904
  recordScreenTraffic: () => recordScreenTraffic,
28492
28905
  rootOnAppear: () => rootOnAppear,
28906
+ snapshot: () => snapshot,
28493
28907
  structuralSignature: () => structuralSignature,
28494
28908
  unverifiableNodes: () => unverifiableNodes,
28495
28909
  workerArgs: () => workerArgs
@@ -29725,7 +30139,209 @@ async function crawlScreenDeep(opts, deep) {
29725
30139
  return { findings: dedupe(findings), nodesVisited: visited.size, dispatches, capped };
29726
30140
  });
29727
30141
  }
29728
- var ACTION_KEYS, UNTRACKED_ITEMS, NESTED_ITEMS_SEP, REJECT_ALL_MESSAGE, VLQ, WIX_DATA_QUERY, WIX_DATA_ANY, CAPABILITY_ACTION, BLANK_WHEN_BOUND;
30142
+ function effectiveSiteForRow(site, row) {
30143
+ const merged = { ...site.params ?? {}, ...site.args ?? {} };
30144
+ const substituted = {};
30145
+ for (const [k, v] of Object.entries(merged)) {
30146
+ substituted[k] = typeof v === "string" && v.startsWith("$item.") ? pathValue(row, v.slice("$item.".length)) : v === "$item" ? row : v;
30147
+ }
30148
+ return { ...site, params: substituted, args: void 0 };
30149
+ }
30150
+ function isRowBound(site) {
30151
+ const payload = { ...site.params ?? {}, ...site.args ?? {} };
30152
+ return Object.values(payload).some((v) => typeof v === "string" && (v === "$item" || v.startsWith("$item.")));
30153
+ }
30154
+ function collectItemsPaths(node, out = /* @__PURE__ */ new Set()) {
30155
+ if (!node || typeof node !== "object") return out;
30156
+ if (Array.isArray(node)) {
30157
+ for (const n of node) collectItemsPaths(n, out);
30158
+ return out;
30159
+ }
30160
+ const rec = node;
30161
+ const body = rec.body;
30162
+ if (body && typeof body === "object" && typeof body.type === "string" && body.props && typeof body.props === "object") {
30163
+ const itemsPath = body.props.itemsPath;
30164
+ if (typeof itemsPath === "string") out.add(itemsPath);
30165
+ }
30166
+ for (const v of Object.values(rec)) collectItemsPaths(v, out);
30167
+ return out;
30168
+ }
30169
+ function newlyAddedRow(before, after, itemsPath) {
30170
+ if (!itemsPath || itemsPath === UNTRACKED_ITEMS) return null;
30171
+ const rowsOf = (s) => {
30172
+ const provider = new FakeServiceProvider(s);
30173
+ const v = resolveSiteRows(itemsPath, (p) => provider.value(p));
30174
+ return Array.isArray(v) ? v.filter((r) => r && typeof r === "object") : [];
30175
+ };
30176
+ const seen = new Set(rowsOf(before).map((r) => relationalSignature(r)));
30177
+ const fresh = rowsOf(after).filter((r) => !seen.has(relationalSignature(r)));
30178
+ return fresh.length === 1 ? fresh[0] : null;
30179
+ }
30180
+ async function crawlScreenRelations(opts) {
30181
+ return withSwallowedRejections(async () => {
30182
+ const { screenId, descriptor, bundleJs } = opts;
30183
+ const findings = [];
30184
+ const byKind = { inverse: 0, refresh: 0, loadMore: 0, filter: 0 };
30185
+ let checked = 0;
30186
+ if (!bundleJs) return { findings, checked, byKind };
30187
+ const layered = layeredTransport({ rules: opts.rules, cmsSeed: opts.cmsSeed, canned: opts.canned });
30188
+ const wixCall = opts.transportWrap ? opts.transportWrap(layered.call) : layered.call;
30189
+ const sites = actionSitesForDescriptor(descriptor).filter(
30190
+ (s) => parseWorkerAction(s.actionId) && !opts.skipAction?.(s.actionId, s.componentId)
30191
+ );
30192
+ const nameOf = (s) => parseWorkerAction(s.actionId).action;
30193
+ const serviceOf = (s) => parseWorkerAction(s.actionId).service;
30194
+ const boot = () => bootScreenWorker({
30195
+ bundleJs,
30196
+ descriptor,
30197
+ wixCall,
30198
+ screenId,
30199
+ inputOverride: opts.inputOverride,
30200
+ budgetMs: opts.budgetMs,
30201
+ clock: opts.clock,
30202
+ seed: opts.seed,
30203
+ localDbSchema: opts.localDbSchema,
30204
+ ...opts.evaluator ? { evaluator: opts.evaluator } : {}
30205
+ });
30206
+ const replay = async (path, rowFor = () => null) => {
30207
+ const w = await boot();
30208
+ try {
30209
+ if (w.errors.length) return null;
30210
+ let prev = snapshot(w).state;
30211
+ const bootState = prev;
30212
+ for (const [i, site] of path.entries()) {
30213
+ const parsed = parseWorkerAction(site.actionId);
30214
+ const h = w.handles[parsed.service];
30215
+ if (!h || !h.actionNames().includes(parsed.action)) return null;
30216
+ const pinned = rowFor(i, bootState, prev);
30217
+ if (pinned === UNBINDABLE_ROW) return null;
30218
+ const eff = pinned ? effectiveSiteForRow(site, pinned) : effectiveSiteFor(site, prev);
30219
+ if (!eff) return null;
30220
+ try {
30221
+ await h.dispatch(parsed.action, ...workerArgs(eff));
30222
+ } catch {
30223
+ return null;
30224
+ }
30225
+ for (let k = 0; k < 20; k++) await yieldMacrotask();
30226
+ if (w.errors.length) return null;
30227
+ prev = snapshot(w).state;
30228
+ }
30229
+ return prev;
30230
+ } finally {
30231
+ w.dispose();
30232
+ }
30233
+ };
30234
+ const base = await replay([]);
30235
+ if (!base || Object.values(base).some(hasOwnError)) return { findings, checked, byKind };
30236
+ const baseSig = relationalSignature(base);
30237
+ const baseLens = listLengths(base);
30238
+ const listPathOf = (itemsPath) => {
30239
+ if (!itemsPath || itemsPath === UNTRACKED_ITEMS || itemsPath.includes("[]")) return null;
30240
+ const m = /^\$service:(.+)$/.exec(itemsPath);
30241
+ return m && baseLens[m[1]] !== void 0 ? m[1] : null;
30242
+ };
30243
+ const screenListPaths = [
30244
+ ...new Set(
30245
+ [...collectItemsPaths(descriptor)].map(listPathOf).filter((x) => x !== null)
30246
+ )
30247
+ ];
30248
+ const trig = (s) => ({ tap: s.componentId, key: s.key, action: s.actionId });
30249
+ for (const s of sites.filter((s2) => REFRESH_ACTION.test(nameOf(s2)) || s2.key === "onPullToRefresh")) {
30250
+ const after = await replay([s]);
30251
+ if (!after) continue;
30252
+ checked++;
30253
+ byKind.refresh++;
30254
+ const sig = relationalSignature(after);
30255
+ if (sig !== baseSig) {
30256
+ findings.push({
30257
+ screen: screenId,
30258
+ trigger: trig(s),
30259
+ invariant: "refresh-not-idempotent",
30260
+ severity: "warning",
30261
+ axis: "relation",
30262
+ message: `${nameOf(s)} from a fresh boot settles on a different state than the boot itself, under identical data: ${summarizeDiff(baseSig, sig)}`,
30263
+ hint: "a refresh that appends instead of replacing, or clears a flag the loader set \u2014 REPLACE collections on refresh; ids, *At and _counters are already ignored by this comparison"
30264
+ });
30265
+ }
30266
+ }
30267
+ for (const s of sites.filter((s2) => LOAD_MORE_ACTION.test(nameOf(s2)) || s2.key === "onLoadMore")) {
30268
+ const target = listPathOf(s.itemsPath);
30269
+ const after = target ? await replay([s]) : null;
30270
+ if (!after) continue;
30271
+ checked++;
30272
+ byKind.loadMore++;
30273
+ for (const [p, n] of Object.entries(listLengths(after))) {
30274
+ if (p === target && baseLens[p] !== void 0 && n < baseLens[p]) {
30275
+ findings.push({
30276
+ screen: screenId,
30277
+ trigger: trig(s),
30278
+ invariant: "list-shrank-on-load-more",
30279
+ severity: "warning",
30280
+ axis: "relation",
30281
+ at: p,
30282
+ actual: `${baseLens[p]} \u2192 ${n}`,
30283
+ message: `${nameOf(s)} shrank ${p} from ${baseLens[p]} to ${n} rows \u2014 a "load more" that replaces the page instead of appending it`,
30284
+ hint: "append the new page to the existing rows (and de-duplicate by id); the user scrolled to see MORE"
30285
+ });
30286
+ }
30287
+ }
30288
+ }
30289
+ for (const s of sites.filter((s2) => FILTER_ACTION.test(nameOf(s2)) && !isOptionSelection({ ...s2.params, ...s2.args }))) {
30290
+ if (screenListPaths.length !== 1) continue;
30291
+ const after = await replay([s]);
30292
+ if (!after) continue;
30293
+ checked++;
30294
+ byKind.filter++;
30295
+ for (const [p, n] of Object.entries(listLengths(after))) {
30296
+ if (screenListPaths.includes(p) && baseLens[p] !== void 0 && n > baseLens[p]) {
30297
+ findings.push({
30298
+ screen: screenId,
30299
+ trigger: trig(s),
30300
+ invariant: "list-grew-on-filter",
30301
+ severity: "warning",
30302
+ axis: "relation",
30303
+ at: p,
30304
+ actual: `${baseLens[p]} \u2192 ${n}`,
30305
+ message: `${nameOf(s)} grew ${p} from ${baseLens[p]} to ${n} rows \u2014 a filter/search/sort can narrow or reorder a list, never enlarge it`,
30306
+ hint: "filter from the FULL set you keep aside, and assign the narrowed result \u2014 not a concatenation"
30307
+ });
30308
+ }
30309
+ }
30310
+ }
30311
+ const declaredNames = sites.map(nameOf);
30312
+ for (const a of sites) {
30313
+ const partner = inversePartner(nameOf(a), declaredNames);
30314
+ if (!partner) continue;
30315
+ if (isUndoAction(nameOf(a))) continue;
30316
+ const b = sites.find((s) => nameOf(s) === partner && serviceOf(s) === serviceOf(a) && s !== a);
30317
+ if (!b) continue;
30318
+ const undoIsRowBound = isRowBound(b);
30319
+ const after = await replay([a, b], (i, bootState, prev) => {
30320
+ if (i !== 1) return null;
30321
+ const row = newlyAddedRow(bootState, prev, b.itemsPath);
30322
+ if (row) return row;
30323
+ return undoIsRowBound ? UNBINDABLE_ROW : null;
30324
+ });
30325
+ if (!after) continue;
30326
+ checked++;
30327
+ byKind.inverse++;
30328
+ const sig = relationalSignature(after);
30329
+ if (sig !== baseSig) {
30330
+ findings.push({
30331
+ screen: screenId,
30332
+ trigger: trig(b),
30333
+ invariant: "inverse-pair-diverges",
30334
+ severity: "warning",
30335
+ axis: "relation",
30336
+ message: `${nameOf(a)} \u2192 ${nameOf(b)} does not return to the boot state: ${summarizeDiff(baseSig, sig)}`,
30337
+ hint: `${nameOf(b)} should undo exactly what ${nameOf(a)} did \u2014 check which row it removes (by the id it was given) and every field ${nameOf(a)} touched; ids, *At and _counters are already ignored`
30338
+ });
30339
+ }
30340
+ }
30341
+ return { findings: dedupe(findings), checked, byKind };
30342
+ });
30343
+ }
30344
+ var ACTION_KEYS, UNTRACKED_ITEMS, NESTED_ITEMS_SEP, REJECT_ALL_MESSAGE, VLQ, WIX_DATA_QUERY, WIX_DATA_ANY, CAPABILITY_ACTION, BLANK_WHEN_BOUND, UNBINDABLE_ROW;
29729
30345
  var init_screen_crawl = __esm({
29730
30346
  "../../core/dev-server/src/screen-crawl.ts"() {
29731
30347
  init_validate_descriptor_core();
@@ -29735,6 +30351,7 @@ var init_screen_crawl = __esm({
29735
30351
  init_FakeServiceProvider();
29736
30352
  init_actionParams();
29737
30353
  init_implications();
30354
+ init_crawl_relations();
29738
30355
  ACTION_KEYS = [
29739
30356
  "action",
29740
30357
  "onTap",
@@ -29758,6 +30375,7 @@ var init_screen_crawl = __esm({
29758
30375
  Marquee: ["items", "itemsPath", "text"],
29759
30376
  MediaPlayer: ["items", "itemsPath"]
29760
30377
  };
30378
+ UNBINDABLE_ROW = Symbol("unbindable-row");
29761
30379
  }
29762
30380
  });
29763
30381
 
@@ -30313,8 +30931,8 @@ __export(pack_exports, {
30313
30931
  });
30314
30932
  import { createHash as createHash19 } from "node:crypto";
30315
30933
  import { deflateRawSync, inflateRawSync } from "node:zlib";
30316
- import { existsSync as existsSync35, mkdirSync as mkdirSync17, readFileSync as readFileSync42, readdirSync as readdirSync19, rmSync as rmSync5, statSync as statSync7, writeFileSync as writeFileSync13 } from "node:fs";
30317
- import { dirname as dirname23, join as join34, resolve as resolve31 } from "node:path";
30934
+ import { existsSync as existsSync35, mkdirSync as mkdirSync17, readFileSync as readFileSync42, readdirSync as readdirSync19, rmSync as rmSync5, statSync as statSync8, writeFileSync as writeFileSync13 } from "node:fs";
30935
+ import { dirname as dirname23, join as join35, resolve as resolve31 } from "node:path";
30318
30936
  function writeZip(entries) {
30319
30937
  const sorted = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
30320
30938
  const total = sorted.reduce((n, e) => n + e.bytes.length, 0);
@@ -30408,13 +31026,13 @@ function inflateEntry(buf, entry) {
30408
31026
  return bytes2;
30409
31027
  }
30410
31028
  function readIf(path) {
30411
- return existsSync35(path) && statSync7(path).isFile() ? readFileSync42(path) : null;
31029
+ return existsSync35(path) && statSync8(path).isFile() ? readFileSync42(path) : null;
30412
31030
  }
30413
31031
  function listDir(dir) {
30414
- return existsSync35(dir) ? readdirSync19(dir).filter((f) => statSync7(join34(dir, f)).isFile()) : [];
31032
+ return existsSync35(dir) ? readdirSync19(dir).filter((f) => statSync8(join35(dir, f)).isFile()) : [];
30415
31033
  }
30416
31034
  function gatherReleaseEntries(appDir2) {
30417
- const manifestPath = join34(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
31035
+ const manifestPath = join35(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
30418
31036
  const manifestBytes = readIf(manifestPath);
30419
31037
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
30420
31038
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -30438,7 +31056,7 @@ function gatherReleaseEntries(appDir2) {
30438
31056
  }
30439
31057
  const missing = [];
30440
31058
  for (const rel of wanted) {
30441
- const bytes2 = readIf(join34(appDir2, rel));
31059
+ const bytes2 = readIf(join35(appDir2, rel));
30442
31060
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
30443
31061
  else missing.push(rel);
30444
31062
  }
@@ -30449,12 +31067,12 @@ function gatherReleaseEntries(appDir2) {
30449
31067
  );
30450
31068
  }
30451
31069
  for (const name of SINGLETONS) {
30452
- const bytes2 = readIf(join34(appDir2, name));
31070
+ const bytes2 = readIf(join35(appDir2, name));
30453
31071
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
30454
31072
  }
30455
31073
  const skipped = [];
30456
31074
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
30457
- for (const f of listDir(join34(appDir2, sub))) {
31075
+ for (const f of listDir(join35(appDir2, sub))) {
30458
31076
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
30459
31077
  }
30460
31078
  }
@@ -30485,18 +31103,18 @@ function buildEntries(appDir2, app, opts) {
30485
31103
  return { all, header, manifest, skipped };
30486
31104
  }
30487
31105
  function packRelease(releaseRoot, app, out, opts = {}) {
30488
- const appDir2 = join34(resolve31(releaseRoot), app);
31106
+ const appDir2 = join35(resolve31(releaseRoot), app);
30489
31107
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
30490
31108
  const buf = writeZip(all);
30491
- const isDir = existsSync35(out) && statSync7(out).isDirectory();
30492
- const outFile = isDir ? join34(resolve31(out), packageFileName(app, header.releaseId)) : resolve31(out);
31109
+ const isDir = existsSync35(out) && statSync8(out).isDirectory();
31110
+ const outFile = isDir ? join35(resolve31(out), packageFileName(app, header.releaseId)) : resolve31(out);
30493
31111
  mkdirSync17(dirname23(outFile), { recursive: true });
30494
31112
  writeFileSync13(outFile, buf);
30495
31113
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
30496
31114
  }
30497
31115
  function resolvePackageDirDest(outDir, app, releaseId) {
30498
31116
  const packagedApp = (dir) => {
30499
- const bytes2 = readIf(join34(dir, HIMI_PACKAGE_HEADER_PATH));
31117
+ const bytes2 = readIf(join35(dir, HIMI_PACKAGE_HEADER_PATH));
30500
31118
  if (!bytes2) return void 0;
30501
31119
  try {
30502
31120
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -30506,14 +31124,14 @@ function resolvePackageDirDest(outDir, app, releaseId) {
30506
31124
  };
30507
31125
  const replaceable = (dir) => {
30508
31126
  if (!existsSync35(dir)) return true;
30509
- if (!statSync7(dir).isDirectory()) return false;
31127
+ if (!statSync8(dir).isDirectory()) return false;
30510
31128
  return readdirSync19(dir).length === 0 || packagedApp(dir) === app;
30511
31129
  };
30512
- if (existsSync35(outDir) && !statSync7(outDir).isDirectory()) {
31130
+ if (existsSync35(outDir) && !statSync8(outDir).isDirectory()) {
30513
31131
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
30514
31132
  }
30515
31133
  if (replaceable(outDir)) return outDir;
30516
- const child = join34(outDir, packageDirName(app, releaseId));
31134
+ const child = join35(outDir, packageDirName(app, releaseId));
30517
31135
  if (!replaceable(child)) {
30518
31136
  throw new Error(
30519
31137
  `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.`
@@ -30522,13 +31140,13 @@ function resolvePackageDirDest(outDir, app, releaseId) {
30522
31140
  return child;
30523
31141
  }
30524
31142
  function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
30525
- const appDir2 = join34(resolve31(releaseRoot), app);
31143
+ const appDir2 = join35(resolve31(releaseRoot), app);
30526
31144
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
30527
31145
  const dest = resolvePackageDirDest(resolve31(outDir), app, header.releaseId);
30528
31146
  rmSync5(dest, { recursive: true, force: true });
30529
31147
  let bytes2 = 0;
30530
31148
  for (const entry of all) {
30531
- const target = join34(dest, entry.path);
31149
+ const target = join35(dest, entry.path);
30532
31150
  mkdirSync17(dirname23(target), { recursive: true });
30533
31151
  writeFileSync13(target, entry.bytes);
30534
31152
  bytes2 += entry.bytes.length;
@@ -30557,11 +31175,11 @@ function openPackageDir(dir) {
30557
31175
  const root = resolve31(dir);
30558
31176
  const entries = /* @__PURE__ */ new Map();
30559
31177
  const walk2 = (rel) => {
30560
- const abs = rel ? join34(root, rel) : root;
31178
+ const abs = rel ? join35(root, rel) : root;
30561
31179
  for (const name of readdirSync19(abs)) {
30562
31180
  const childRel = rel ? `${rel}/${name}` : name;
30563
- const childAbs = join34(abs, name);
30564
- if (statSync7(childAbs).isDirectory()) walk2(childRel);
31181
+ const childAbs = join35(abs, name);
31182
+ if (statSync8(childAbs).isDirectory()) walk2(childRel);
30565
31183
  else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync42(childAbs));
30566
31184
  }
30567
31185
  };
@@ -30599,10 +31217,10 @@ function unpackTo(buf, dest, appFallback) {
30599
31217
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
30600
31218
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
30601
31219
  const root = resolve31(dest);
30602
- const appDir2 = join34(root, app);
31220
+ const appDir2 = join35(root, app);
30603
31221
  rmSync5(appDir2, { recursive: true, force: true });
30604
31222
  for (const [rel, bytes2] of entries) {
30605
- const target = join34(appDir2, rel);
31223
+ const target = join35(appDir2, rel);
30606
31224
  mkdirSync17(dirname23(target), { recursive: true });
30607
31225
  writeFileSync13(target, bytes2);
30608
31226
  }
@@ -30634,14 +31252,14 @@ __export(app_source_exports, {
30634
31252
  zipAppSource: () => zipAppSource
30635
31253
  });
30636
31254
  import { readdirSync as readdirSync20, lstatSync as lstatSync4, readFileSync as readFileSync43, mkdirSync as mkdirSync18, writeFileSync as writeFileSync14 } from "node:fs";
30637
- import { join as join35, dirname as dirname24, sep as sep6 } from "node:path";
31255
+ import { join as join36, dirname as dirname24, sep as sep7 } from "node:path";
30638
31256
  function collectAppSource(dir) {
30639
31257
  const entries = [];
30640
31258
  const skipped = [];
30641
31259
  const walk2 = (relDir) => {
30642
- for (const name of readdirSync20(join35(dir, relDir))) {
30643
- const rel = relDir ? join35(relDir, name) : name;
30644
- const st = lstatSync4(join35(dir, rel));
31260
+ for (const name of readdirSync20(join36(dir, relDir))) {
31261
+ const rel = relDir ? join36(relDir, name) : name;
31262
+ const st = lstatSync4(join36(dir, rel));
30645
31263
  if (st.isSymbolicLink()) {
30646
31264
  skipped.push(rel);
30647
31265
  continue;
@@ -30655,7 +31273,7 @@ function collectAppSource(dir) {
30655
31273
  skipped.push(rel);
30656
31274
  continue;
30657
31275
  }
30658
- entries.push({ path: rel.split(sep6).join("/"), bytes: readFileSync43(join35(dir, rel)) });
31276
+ entries.push({ path: rel.split(sep7).join("/"), bytes: readFileSync43(join36(dir, rel)) });
30659
31277
  }
30660
31278
  };
30661
31279
  walk2("");
@@ -30681,7 +31299,7 @@ function readAppSource(zip) {
30681
31299
  function writeAppSource(entries, destDir) {
30682
31300
  const written = [];
30683
31301
  for (const entry of entries) {
30684
- const target = join35(destDir, ...entry.path.split("/"));
31302
+ const target = join36(destDir, ...entry.path.split("/"));
30685
31303
  mkdirSync18(dirname24(target), { recursive: true });
30686
31304
  writeFileSync14(target, entry.bytes);
30687
31305
  written.push(entry.path);
@@ -30712,7 +31330,7 @@ __export(eject_exports, {
30712
31330
  ejectModule: () => ejectModule
30713
31331
  });
30714
31332
  import { existsSync as existsSync36, mkdirSync as mkdirSync19, readFileSync as readFileSync44, writeFileSync as writeFileSync15 } from "node:fs";
30715
- import { dirname as dirname25, join as join36, relative as relative4, resolve as resolve32, sep as sep7 } from "node:path";
31333
+ import { dirname as dirname25, join as join37, relative as relative4, resolve as resolve32, sep as sep8 } from "node:path";
30716
31334
  function locate(repoRootOrCwd, mod) {
30717
31335
  const inRepo = resolve32(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
30718
31336
  if (existsSync36(inRepo)) return { path: inRepo, form: "source" };
@@ -30741,7 +31359,7 @@ function ejectModule(opts) {
30741
31359
  };
30742
31360
  }
30743
31361
  const ext = found.form === "source" ? "ts" : "mjs";
30744
- const target = join36(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
31362
+ const target = join37(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
30745
31363
  mkdirSync19(dirname25(target), { recursive: true });
30746
31364
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
30747
31365
  //
@@ -30765,7 +31383,7 @@ function ejectModule(opts) {
30765
31383
  if (!existsSync36(f)) continue;
30766
31384
  const before = readFileSync44(f, "utf8");
30767
31385
  let after = before;
30768
- let rel = relative4(dirname25(f), target).split(sep7).join("/").replace(/\.tsx?$/, ".js");
31386
+ let rel = relative4(dirname25(f), target).split(sep8).join("/").replace(/\.tsx?$/, ".js");
30769
31387
  if (!rel.startsWith(".")) rel = `./${rel}`;
30770
31388
  for (const spec of specifiersFor(mod)) {
30771
31389
  const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -30812,7 +31430,7 @@ __export(test_exports, {
30812
31430
  writeRecordedMocks: () => writeRecordedMocks
30813
31431
  });
30814
31432
  import { existsSync as existsSync37, mkdirSync as mkdirSync20, readFileSync as readFileSync45, writeFileSync as writeFileSync16 } from "node:fs";
30815
- import { join as join37, resolve as resolve33 } from "node:path";
31433
+ import { join as join38, resolve as resolve33 } from "node:path";
30816
31434
  function readTestConfig(dir) {
30817
31435
  const p = resolve33(dir, TEST_CONFIG_FILE);
30818
31436
  if (!existsSync37(p)) return {};
@@ -30831,8 +31449,8 @@ function readTestConfig(dir) {
30831
31449
  if (typeof s.reason !== "string" || !s.reason.trim()) {
30832
31450
  throw new Error(`${TEST_CONFIG_FILE}: skip[${i}] needs a non-empty "reason" \u2014 suppressions have to stay auditable`);
30833
31451
  }
30834
- if (!s.screen && !s.action && !s.component) {
30835
- throw new Error(`${TEST_CONFIG_FILE}: skip[${i}] matches everything \u2014 give it a "screen", "action", or "component"`);
31452
+ if (!s.screen && !s.action && !s.component && !s.invariant) {
31453
+ throw new Error(`${TEST_CONFIG_FILE}: skip[${i}] matches everything \u2014 give it a "screen", "action", "component", or "invariant"`);
30836
31454
  }
30837
31455
  });
30838
31456
  }
@@ -30990,8 +31608,8 @@ function writeRecordedMocks(opts) {
30990
31608
  mkdir: (p) => mkdirSync20(p, { recursive: true }),
30991
31609
  write: (p, s) => writeFileSync16(p, s)
30992
31610
  };
30993
- fs.mkdir(join37(opts.dir, "dev"));
30994
- fs.write(join37(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
31611
+ fs.mkdir(join38(opts.dir, "dev"));
31612
+ fs.write(join38(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
30995
31613
  return { ok: true, rules, to: "dev/net-mocks.json" };
30996
31614
  }
30997
31615
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -31057,12 +31675,15 @@ async function runCrawl(opts) {
31057
31675
  const net = opts.offline ? null : opts.netPath ? { rules: readNetRules(resolve33(opts.netPath)), from: opts.netPath } : defaultNetRules(opts.dir);
31058
31676
  const suppressions = opts.config?.skip ?? [];
31059
31677
  const suppressed = [];
31060
- const matchSuppression = (screen, actionId, componentId) => suppressions.find((s) => (s.screen === void 0 || s.screen === screen) && (s.action === void 0 || s.action === actionId) && (s.component === void 0 || s.component === componentId));
31678
+ const matchSuppression = (screen, actionId, componentId, invariant) => suppressions.find((s) => (s.screen === void 0 || s.screen === screen) && (s.action === void 0 || s.action === actionId) && (s.component === void 0 || s.component === componentId) && (s.invariant === void 0 || s.invariant === invariant));
31061
31679
  const results = [];
31062
31680
  const uncoveredByScreen = /* @__PURE__ */ new Map();
31063
31681
  const coverageFns = /* @__PURE__ */ new Map();
31064
31682
  const bundleOf = new Map(descriptors.map((d) => [d.screenId, d]));
31065
31683
  const hostileFindings = [];
31684
+ const relationFindings = [];
31685
+ let relationsChecked = 0;
31686
+ const relationsByKind = { inverse: 0, refresh: 0, loadMore: 0, filter: 0 };
31066
31687
  const deepFindings = [];
31067
31688
  const journeyFindings = [];
31068
31689
  const journeyResults = [];
@@ -31132,14 +31753,22 @@ async function runCrawl(opts) {
31132
31753
  hostileFindings.push(...hs.filter((f) => !already.has(keyOf(f))));
31133
31754
  }
31134
31755
  }
31756
+ if (deps.crawlScreenRelations && baked.bundleJs) {
31757
+ const rr = await deps.crawlScreenRelations(args);
31758
+ relationsChecked += rr.checked;
31759
+ for (const [k, n] of Object.entries(rr.byKind ?? {})) relationsByKind[k] = (relationsByKind[k] ?? 0) + n;
31760
+ const keyOf = deps.findingKey ?? ((f) => `${f.invariant}|${f.at ?? ""}|${f.screen}|${f.message}`);
31761
+ const already = new Set(r.findings.map(keyOf));
31762
+ for (const f of rr.findings) if (!already.has(keyOf(f))) relationFindings.push(f);
31763
+ }
31135
31764
  await finishScreenCoverage();
31136
31765
  }
31137
31766
  const keep = (f) => {
31138
31767
  const actionId = typeof f.trigger === "object" ? f.trigger.action : "boot";
31139
31768
  const componentId = typeof f.trigger === "object" ? f.trigger.tap : null;
31140
- const s = matchSuppression(f.screen, actionId, componentId);
31141
- if (s && !suppressed.some((x) => x.screen === f.screen && x.action === actionId && x.component === (componentId ?? void 0))) {
31142
- suppressed.push({ screen: f.screen, action: actionId, ...componentId ? { component: componentId } : {}, reason: s.reason });
31769
+ const s = matchSuppression(f.screen, actionId, componentId, f.invariant);
31770
+ if (s && !suppressed.some((x) => x.screen === f.screen && x.action === actionId && x.component === (componentId ?? void 0) && x.invariant === s.invariant)) {
31771
+ suppressed.push({ screen: f.screen, action: actionId, ...componentId ? { component: componentId } : {}, ...s.invariant ? { invariant: s.invariant } : {}, reason: s.reason });
31143
31772
  }
31144
31773
  return !s;
31145
31774
  };
@@ -31220,7 +31849,7 @@ async function runCrawl(opts) {
31220
31849
  if (uncovered.length) uncoveredByScreen.set(screen, uncovered);
31221
31850
  }
31222
31851
  }
31223
- const all = [...results.flatMap((r) => r.findings), ...hostileFindings, ...deepFindings, ...journeyFindings, ...edgeFindings].filter((f) => {
31852
+ const all = [...results.flatMap((r) => r.findings), ...hostileFindings, ...deepFindings, ...journeyFindings, ...edgeFindings, ...relationFindings].filter((f) => {
31224
31853
  if (f.invariant === "no-request-issued" && !f.axis && fetchProven.has(f.screen)) {
31225
31854
  const by = fetchProven.get(f.screen);
31226
31855
  if (!retired.some((x) => x.screen === f.screen)) retired.push({ screen: f.screen, from: by.from, params: by.params });
@@ -31240,7 +31869,15 @@ async function runCrawl(opts) {
31240
31869
  ...blockedReasons.length ? { blockedReasons } : {},
31241
31870
  app: opts.app,
31242
31871
  releaseId: opts.releaseId,
31243
- transport: describeTransport(net, opts),
31872
+ // Whether the axis RAN, not whether it was injected. The engine is gated on `baked.bundleJs`,
31873
+ // so a run over static or bundle-less screens injects the dependency and replays nothing —
31874
+ // and the label then advertised "+ relations" beside `relations.checked: 0`. This is the
31875
+ // second time this line has overstated itself: it first claimed the axis unconditionally.
31876
+ transport: describeTransport(net, opts, {
31877
+ relations: relationsChecked > 0,
31878
+ hostile: opts.hostile !== false && Boolean(deps.crawlScreenHostile),
31879
+ deep: Boolean(opts.deep && opts.deep >= 2 && deps.crawlScreenDeep)
31880
+ }),
31244
31881
  screens: { total: targets.length, clean: targets.length - failedScreens.size, failed: failedScreens.size },
31245
31882
  actions: { exercised: results.reduce((n, r) => n + r.actionsExercised, 0) },
31246
31883
  counts: { errors: failures.length, warnings: warnings.length },
@@ -31263,22 +31900,22 @@ async function runCrawl(opts) {
31263
31900
  ...edgesCapped.length ? { edgesCapped } : {},
31264
31901
  ...retired.length ? { retired } : {},
31265
31902
  ...opts.deep && opts.deep >= 2 ? { deep: { depth: opts.deep, ...deepTotals } } : {},
31903
+ ...deps.crawlScreenRelations ? { relations: { checked: relationsChecked, byKind: relationsByKind } } : {},
31266
31904
  ...journeyResults.length ? { journeys: journeyResults } : {},
31267
31905
  ...results.some((r) => r.observations) ? { observations: Object.fromEntries(results.filter((r) => r.observations).map((r) => [r.screen, r.observations])) } : {}
31268
31906
  };
31269
31907
  }
31270
- function describeTransport(net, opts) {
31908
+ function describeTransport(net, opts, ran) {
31271
31909
  const layers = [];
31272
31910
  if (net) layers.push(`mocked (${net.from})`);
31273
31911
  const cmsCount = Object.keys(opts.cmsSeed ?? {}).length;
31274
31912
  if (cmsCount) layers.push(`cms (${cmsCount} seeded collection${cmsCount === 1 ? "" : "s"})`);
31275
31913
  if (opts.canned?.count) layers.push(`canned (${opts.canned.count} frozen endpoints, ${opts.canned.from} \u2014 exercises code paths, not API-contract truth)`);
31276
- if (!layers.length) {
31277
- return "offline (no net fixture \u2014 screens are crawled against a rejecting transport, which exercises their failure UI)";
31278
- }
31279
- const hostile = opts.hostile !== false ? " + hostile boot passes (empty-lists, all-500)" : "";
31280
- const deep = opts.deep && opts.deep >= 2 ? ` + deep chains (depth ${opts.deep})` : "";
31281
- return layers.join(" + ") + hostile + deep;
31914
+ const base = layers.length ? layers.join(" + ") : "offline (no net fixture \u2014 screens are crawled against a rejecting transport, which exercises their failure UI)";
31915
+ const hostile = ran.hostile ? " + hostile boot passes (empty-lists, all-500)" : "";
31916
+ const relations = ran.relations ? " + relations" : "";
31917
+ const deep = ran.deep ? ` + deep chains (depth ${opts.deep})` : "";
31918
+ return base + hostile + relations + deep;
31282
31919
  }
31283
31920
  function formatReport(r) {
31284
31921
  const out = [];
@@ -31413,9 +32050,9 @@ __export(mobile_ux_lint_exports, {
31413
32050
  mobileUxIssues: () => mobileUxIssues
31414
32051
  });
31415
32052
  import { existsSync as existsSync38, readFileSync as readFileSync46 } from "node:fs";
31416
- import { join as join38 } from "node:path";
32053
+ import { join as join39 } from "node:path";
31417
32054
  function mobileUxIssues(dir, strict) {
31418
- const file = join38(dir, "MOBILE-UX.md");
32055
+ const file = join39(dir, "MOBILE-UX.md");
31419
32056
  if (!existsSync38(file)) return [];
31420
32057
  let text2;
31421
32058
  try {
@@ -33079,7 +33716,7 @@ __export(functions_exports, {
33079
33716
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
33080
33717
  });
33081
33718
  import { readFileSync as readFileSync47, existsSync as existsSync39 } from "node:fs";
33082
- import { join as join39, resolve as resolve34 } from "node:path";
33719
+ import { join as join40, resolve as resolve34 } from "node:path";
33083
33720
  function resolveFunctionsTarget(input) {
33084
33721
  const explicitUrl = input.functionsUrl?.trim();
33085
33722
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -33098,7 +33735,7 @@ async function loadFunctionsApp(dir) {
33098
33735
  }
33099
33736
  const sources = {};
33100
33737
  for (const definition of config.functions.functions) {
33101
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join39(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync39);
33738
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join40(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync39);
33102
33739
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
33103
33740
  sources[definition.name] = readFileSync47(source, "utf8");
33104
33741
  }
@@ -33256,7 +33893,7 @@ __export(native_decode_exports, {
33256
33893
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
33257
33894
  import { existsSync as existsSync40, mkdtempSync as mkdtempSync2, readFileSync as readFileSync48, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
33258
33895
  import { tmpdir as tmpdir3 } from "node:os";
33259
- import { join as join40 } from "node:path";
33896
+ import { join as join41 } from "node:path";
33260
33897
  import { fileURLToPath as fileURLToPath19 } from "node:url";
33261
33898
  function nativeDecodePackageDir() {
33262
33899
  return fileURLToPath19(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -33272,7 +33909,7 @@ function detectNativeDecode(options = {}) {
33272
33909
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
33273
33910
  }
33274
33911
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
33275
- const packageAvailable = options.packageAvailable ?? existsSync40(join40(packageDir, "Package.swift"));
33912
+ const packageAvailable = options.packageAvailable ?? existsSync40(join41(packageDir, "Package.swift"));
33276
33913
  if (!packageAvailable) {
33277
33914
  return {
33278
33915
  platform: "ios",
@@ -33284,7 +33921,7 @@ function detectNativeDecode(options = {}) {
33284
33921
  }
33285
33922
  function localPropertiesSdk(harnessDir) {
33286
33923
  try {
33287
- const match = readFileSync48(join40(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
33924
+ const match = readFileSync48(join41(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
33288
33925
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
33289
33926
  } catch {
33290
33927
  return null;
@@ -33292,7 +33929,7 @@ function localPropertiesSdk(harnessDir) {
33292
33929
  }
33293
33930
  function detectAndroidNativeDecode(options = {}) {
33294
33931
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
33295
- const gradlew = join40(harnessDir, "gradlew");
33932
+ const gradlew = join41(harnessDir, "gradlew");
33296
33933
  const gradlewAvailable = options.gradlewAvailable ?? existsSync40(gradlew);
33297
33934
  if (!gradlewAvailable) {
33298
33935
  return {
@@ -33375,14 +34012,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
33375
34012
  return verdicts;
33376
34013
  }
33377
34014
  function runNativeDecode(options) {
33378
- const descriptorDir = mkdtempSync2(join40(tmpdir3(), "himi-native-decode-"));
34015
+ const descriptorDir = mkdtempSync2(join41(tmpdir3(), "himi-native-decode-"));
33379
34016
  const startedAt = Date.now();
33380
34017
  try {
33381
34018
  for (const descriptor of options.descriptors) {
33382
34019
  if (typeof descriptor.descriptorJson !== "string") {
33383
34020
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
33384
34021
  }
33385
- writeFileSync17(join40(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
34022
+ writeFileSync17(join41(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
33386
34023
  }
33387
34024
  options.progress?.("building cached Swift oracle");
33388
34025
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -33394,7 +34031,7 @@ function runNativeDecode(options) {
33394
34031
  encoding: "utf8"
33395
34032
  }).trim();
33396
34033
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
33397
- const stdout = execFileSync5(join40(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
34034
+ const stdout = execFileSync5(join41(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
33398
34035
  stdio: ["ignore", "pipe", "pipe"],
33399
34036
  encoding: "utf8"
33400
34037
  });
@@ -33412,15 +34049,15 @@ function runNativeDecode(options) {
33412
34049
  }
33413
34050
  }
33414
34051
  function runAndroidNativeDecode(options) {
33415
- const descriptorDir = mkdtempSync2(join40(tmpdir3(), "himi-native-decode-android-"));
33416
- const outputPath = join40(descriptorDir, "verdicts.ndjson");
34052
+ const descriptorDir = mkdtempSync2(join41(tmpdir3(), "himi-native-decode-android-"));
34053
+ const outputPath = join41(descriptorDir, "verdicts.ndjson");
33417
34054
  const startedAt = Date.now();
33418
34055
  try {
33419
34056
  for (const descriptor of options.descriptors) {
33420
34057
  if (typeof descriptor.descriptorJson !== "string") {
33421
34058
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
33422
34059
  }
33423
- writeFileSync17(join40(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
34060
+ writeFileSync17(join41(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
33424
34061
  }
33425
34062
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
33426
34063
  execFileSync5(options.gradlew, [
@@ -33506,23 +34143,23 @@ __export(browser_authoring_exports, {
33506
34143
  import { createServer as createServer3 } from "node:http";
33507
34144
  import { createHash as createHash22, randomBytes as randomBytes5 } from "node:crypto";
33508
34145
  import { execFile as execFile2 } from "node:child_process";
33509
- import { existsSync as existsSync41, readdirSync as readdirSync21, readFileSync as readFileSync49, realpathSync as realpathSync3, statSync as statSync8 } from "node:fs";
33510
- import { join as join41, posix, relative as relative5, sep as sep8 } from "node:path";
34146
+ import { existsSync as existsSync41, readdirSync as readdirSync21, readFileSync as readFileSync49, realpathSync as realpathSync4, statSync as statSync9 } from "node:fs";
34147
+ import { join as join42, posix, relative as relative5, sep as sep9 } from "node:path";
33511
34148
  function humanMs(ms) {
33512
34149
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
33513
34150
  }
33514
34151
  function collectWorkerSources(tier5SrcDir, bundleName) {
33515
- const root = join41(tier5SrcDir, bundleName);
34152
+ const root = join42(tier5SrcDir, bundleName);
33516
34153
  const files = {};
33517
34154
  const visitedDirs = /* @__PURE__ */ new Set();
33518
34155
  const walk2 = (abs, rel) => {
33519
- const real = realpathSync3(abs);
34156
+ const real = realpathSync4(abs);
33520
34157
  if (visitedDirs.has(real)) return;
33521
34158
  visitedDirs.add(real);
33522
34159
  for (const name of readdirSync21(abs).sort()) {
33523
- const childAbs = join41(abs, name);
34160
+ const childAbs = join42(abs, name);
33524
34161
  const childRel = posix.join(rel, name);
33525
- if (statSync8(childAbs).isDirectory()) {
34162
+ if (statSync9(childAbs).isDirectory()) {
33526
34163
  walk2(childAbs, childRel);
33527
34164
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
33528
34165
  files[childRel] = readFileSync49(childAbs, "utf8");
@@ -33546,18 +34183,18 @@ function collectRelativeSiblings(files, tier5SrcDir) {
33546
34183
  const spec = m[1];
33547
34184
  const virtual = posix.normalize(posix.join(dir, spec));
33548
34185
  if (!virtual.startsWith("/tier5-src/")) continue;
33549
- const onDisk = join41(tier5SrcDir, virtual.slice("/tier5-src/".length));
34186
+ const onDisk = join42(tier5SrcDir, virtual.slice("/tier5-src/".length));
33550
34187
  const candidates = [
33551
34188
  onDisk,
33552
34189
  `${onDisk}.ts`,
33553
34190
  `${onDisk}.js`,
33554
34191
  onDisk.replace(/\.js$/, ".ts"),
33555
- join41(onDisk, "index.ts"),
33556
- join41(onDisk, "index.js")
34192
+ join42(onDisk, "index.ts"),
34193
+ join42(onDisk, "index.js")
33557
34194
  ];
33558
34195
  for (const abs of candidates) {
33559
- if (!existsSync41(abs) || statSync8(abs).isDirectory()) continue;
33560
- const key2 = `/tier5-src/${relative5(tier5SrcDir, abs).split(sep8).join(posix.sep)}`;
34196
+ if (!existsSync41(abs) || statSync9(abs).isDirectory()) continue;
34197
+ const key2 = `/tier5-src/${relative5(tier5SrcDir, abs).split(sep9).join(posix.sep)}`;
33561
34198
  if (key2 in files) break;
33562
34199
  files[key2] = readFileSync49(abs, "utf8");
33563
34200
  queue.push(key2);
@@ -33768,7 +34405,7 @@ __export(coverage_exports, {
33768
34405
  trendAndStore: () => trendAndStore
33769
34406
  });
33770
34407
  import { existsSync as existsSync42, mkdirSync as mkdirSync21, readFileSync as readFileSync50, writeFileSync as writeFileSync18 } from "node:fs";
33771
- import { dirname as dirname26, join as join42 } from "node:path";
34408
+ import { dirname as dirname26, join as join43 } from "node:path";
33772
34409
  function assembleLedger(r) {
33773
34410
  const out = [];
33774
34411
  for (const c of r.crawled ?? []) {
@@ -33934,7 +34571,7 @@ function assembleLedger(r) {
33934
34571
  return out;
33935
34572
  }
33936
34573
  function trendAndStore(dir, entries) {
33937
- const p = join42(dir, LEDGER_FILE);
34574
+ const p = join43(dir, LEDGER_FILE);
33938
34575
  let previous = null;
33939
34576
  try {
33940
34577
  if (existsSync42(p)) {
@@ -33975,7 +34612,7 @@ function formatLedger(entries, trend) {
33975
34612
  return out.join("\n");
33976
34613
  }
33977
34614
  function diffAndStoreObservations(dir, current) {
33978
- const p = join42(dir, OBS_FILE);
34615
+ const p = join43(dir, OBS_FILE);
33979
34616
  let previous = null;
33980
34617
  try {
33981
34618
  if (existsSync42(p)) {
@@ -34027,8 +34664,8 @@ function formatDelta(delta) {
34027
34664
  var LEDGER_FILE, OBS_FILE;
34028
34665
  var init_coverage = __esm({
34029
34666
  "src/coverage.ts"() {
34030
- LEDGER_FILE = join42(".himi", "test-ledger.json");
34031
- OBS_FILE = join42(".himi", "test-observations.json");
34667
+ LEDGER_FILE = join43(".himi", "test-ledger.json");
34668
+ OBS_FILE = join43(".himi", "test-observations.json");
34032
34669
  }
34033
34670
  });
34034
34671
 
@@ -35232,9 +35869,9 @@ __export(run_exports, {
35232
35869
  runPackage: () => runPackage,
35233
35870
  withoutProdServe: () => withoutProdServe
35234
35871
  });
35235
- import { cpSync, existsSync as existsSync43, mkdirSync as mkdirSync22, mkdtempSync as mkdtempSync3, readFileSync as readFileSync52, rmSync as rmSync7, statSync as statSync9 } from "node:fs";
35872
+ import { cpSync, existsSync as existsSync43, mkdirSync as mkdirSync22, mkdtempSync as mkdtempSync3, readFileSync as readFileSync52, rmSync as rmSync7, statSync as statSync10 } from "node:fs";
35236
35873
  import { tmpdir as tmpdir4 } from "node:os";
35237
- import { join as join43 } from "node:path";
35874
+ import { join as join44 } from "node:path";
35238
35875
  import { spawn as spawn3 } from "node:child_process";
35239
35876
  function makePackageRunHandler(opts) {
35240
35877
  return makeRunHandler({
@@ -35252,8 +35889,8 @@ async function fetchBytes(url) {
35252
35889
  }
35253
35890
  async function resolvePackage(source, workDir) {
35254
35891
  const isUrl2 = /^https?:\/\//i.test(source);
35255
- if (!isUrl2 && existsSync43(source) && statSync9(source).isDirectory()) {
35256
- const appDirManifest = join43(source, "release-manifest.json");
35892
+ if (!isUrl2 && existsSync43(source) && statSync10(source).isDirectory()) {
35893
+ const appDirManifest = join44(source, "release-manifest.json");
35257
35894
  if (existsSync43(appDirManifest)) {
35258
35895
  const opened2 = openPackageDir(source);
35259
35896
  if (!opened2.validation.ok) {
@@ -35263,9 +35900,9 @@ async function resolvePackage(source, workDir) {
35263
35900
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
35264
35901
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
35265
35902
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
35266
- const root2 = workDir ?? mkdtempSync3(join43(tmpdir4(), "himi-run-"));
35267
- const target = join43(root2, app2);
35268
- if (join43(source) !== target) {
35903
+ const root2 = workDir ?? mkdtempSync3(join44(tmpdir4(), "himi-run-"));
35904
+ const target = join44(root2, app2);
35905
+ if (join44(source) !== target) {
35269
35906
  mkdirSync22(root2, { recursive: true });
35270
35907
  rmSync7(target, { recursive: true, force: true });
35271
35908
  cpSync(source, target, { recursive: true });
@@ -35283,7 +35920,7 @@ async function resolvePackage(source, workDir) {
35283
35920
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
35284
35921
  }
35285
35922
  const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync52(source);
35286
- const root = workDir ?? mkdtempSync3(join43(tmpdir4(), "himi-run-"));
35923
+ const root = workDir ?? mkdtempSync3(join44(tmpdir4(), "himi-run-"));
35287
35924
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
35288
35925
  const opened = openPackageDir(appDir2);
35289
35926
  if (!opened.validation.ok) {
@@ -35992,9 +36629,9 @@ __export(preflight_app_exports, {
35992
36629
  pngSize: () => pngSize,
35993
36630
  preflightApp: () => preflightApp
35994
36631
  });
35995
- import { readFileSync as readFileSync55, existsSync as existsSync46, readdirSync as readdirSync22, statSync as statSync10 } from "node:fs";
36632
+ import { readFileSync as readFileSync55, existsSync as existsSync46, readdirSync as readdirSync22, statSync as statSync11 } from "node:fs";
35996
36633
  import { createHash as createHash23 } from "node:crypto";
35997
- import { join as join44 } from "node:path";
36634
+ import { join as join45 } from "node:path";
35998
36635
  function analyze(projectYml, storeConfig, icon) {
35999
36636
  const f = [];
36000
36637
  const has = (re, s) => re.test(s);
@@ -36045,7 +36682,7 @@ async function preflightApp(app) {
36045
36682
  const projectYml = existsSync46(iosProjectYml(app)) ? readFileSync55(iosProjectYml(app), "utf8") : "";
36046
36683
  const storeConfig = existsSync46(storeConfigPath(app)) ? readFileSync55(storeConfigPath(app), "utf8") : "";
36047
36684
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
36048
- const findings = analyze(projectYml, storeConfig, await appIconState(join44(appDir(app), "ios")));
36685
+ const findings = analyze(projectYml, storeConfig, await appIconState(join45(appDir(app), "ios")));
36049
36686
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
36050
36687
  findings.push(
36051
36688
  ...analyzePlayListing(playListingAssets(app)).map((finding) => ({ ...finding, level: "warn" }))
@@ -36072,16 +36709,16 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
36072
36709
  return f;
36073
36710
  }
36074
36711
  function appLevelMetadataFiles(app, locale = "en-US") {
36075
- const root = join44(storeDir(app), "metadata");
36712
+ const root = join45(storeDir(app), "metadata");
36076
36713
  const out = /* @__PURE__ */ new Set();
36077
36714
  if (existsSync46(root)) {
36078
36715
  for (const n of readdirSync22(root)) if (n.endsWith(".txt")) out.add(n);
36079
36716
  }
36080
- if (existsSync46(join44(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
36717
+ if (existsSync46(join45(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
36081
36718
  return out;
36082
36719
  }
36083
36720
  function reviewInfoFiles(app) {
36084
- const dir = join44(storeDir(app), "review_information");
36721
+ const dir = join45(storeDir(app), "review_information");
36085
36722
  if (!existsSync46(dir)) return /* @__PURE__ */ new Set();
36086
36723
  return new Set(readdirSync22(dir).filter((n) => n.endsWith(".txt")));
36087
36724
  }
@@ -36192,12 +36829,12 @@ function analyzePlayListing(assets) {
36192
36829
  return f;
36193
36830
  }
36194
36831
  function playListingAssets(app, locale = "en-US") {
36195
- const dir = join44(storeDir(app), "play", locale, "images");
36832
+ const dir = join45(storeDir(app), "play", locale, "images");
36196
36833
  const read = (names) => {
36197
36834
  for (const name of names) {
36198
- const p = join44(dir, name);
36835
+ const p = join45(dir, name);
36199
36836
  try {
36200
- if (statSync10(p).isFile()) return { name, bytes: readFileSync55(p) };
36837
+ if (statSync11(p).isFile()) return { name, bytes: readFileSync55(p) };
36201
36838
  } catch {
36202
36839
  }
36203
36840
  }
@@ -36228,26 +36865,26 @@ function analyzeListing(metaFiles, shots) {
36228
36865
  return f;
36229
36866
  }
36230
36867
  function metadataFilesPresent(app, locale = "en-US") {
36231
- const dir = join44(storeDir(app), "metadata", locale);
36868
+ const dir = join45(storeDir(app), "metadata", locale);
36232
36869
  if (!existsSync46(dir)) return /* @__PURE__ */ new Set();
36233
36870
  return new Set(readdirSync22(dir).filter((n) => n.endsWith(".txt")));
36234
36871
  }
36235
36872
  function listScreenshots(app, locale = "en-US") {
36236
36873
  const out = [];
36237
- const flat = join44(storeDir(app), "screenshots", locale);
36874
+ const flat = join45(storeDir(app), "screenshots", locale);
36238
36875
  if (existsSync46(flat)) {
36239
36876
  for (const png of readdirSync22(flat).filter((n) => n.endsWith(".png"))) {
36240
- const sz = pngSize(readFileSync55(join44(flat, png)));
36877
+ const sz = pngSize(readFileSync55(join45(flat, png)));
36241
36878
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
36242
36879
  }
36243
36880
  }
36244
- const legacy = join44(storeDir(app), "metadata", locale, "screenshots");
36881
+ const legacy = join45(storeDir(app), "metadata", locale, "screenshots");
36245
36882
  if (existsSync46(legacy)) {
36246
36883
  for (const deviceDir of readdirSync22(legacy)) {
36247
- const d = join44(legacy, deviceDir);
36248
- if (!statSync10(d).isDirectory()) continue;
36884
+ const d = join45(legacy, deviceDir);
36885
+ if (!statSync11(d).isDirectory()) continue;
36249
36886
  for (const png of readdirSync22(d).filter((n) => n.endsWith(".png"))) {
36250
- const sz = pngSize(readFileSync55(join44(d, png)));
36887
+ const sz = pngSize(readFileSync55(join45(d, png)));
36251
36888
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
36252
36889
  }
36253
36890
  }
@@ -36255,12 +36892,12 @@ function listScreenshots(app, locale = "en-US") {
36255
36892
  return out;
36256
36893
  }
36257
36894
  function placeholderHashes2() {
36258
- const templateIos = join44(appDir("_template"), "ios");
36895
+ const templateIos = join45(appDir("_template"), "ios");
36259
36896
  if (!existsSync46(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
36260
36897
  for (const target of readdirSync22(templateIos)) {
36261
- const p = join44(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
36898
+ const p = join45(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
36262
36899
  try {
36263
- if (!statSync10(p).isFile()) continue;
36900
+ if (!statSync11(p).isFile()) continue;
36264
36901
  return [LEGACY_PLACEHOLDER_SHA2562, createHash23("sha256").update(readFileSync55(p)).digest("hex")];
36265
36902
  } catch {
36266
36903
  }
@@ -36294,9 +36931,9 @@ async function pixelIssues(bytes2) {
36294
36931
  async function appIconState(iosDir) {
36295
36932
  if (!existsSync46(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
36296
36933
  for (const target of readdirSync22(iosDir)) {
36297
- const p = join44(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
36934
+ const p = join45(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
36298
36935
  try {
36299
- if (!statSync10(p).isFile()) continue;
36936
+ if (!statSync11(p).isFile()) continue;
36300
36937
  const bytes2 = readFileSync55(p);
36301
36938
  const hash = createHash23("sha256").update(bytes2).digest("hex");
36302
36939
  const pixels = await pixelIssues(bytes2);
@@ -36369,12 +37006,12 @@ __export(credential_profile_exports, {
36369
37006
  });
36370
37007
  import { existsSync as existsSync47, readFileSync as readFileSync56 } from "node:fs";
36371
37008
  import { homedir as homedir6 } from "node:os";
36372
- import { join as join45 } from "node:path";
37009
+ import { join as join46 } from "node:path";
36373
37010
  function credentialsDir(home = homedir6()) {
36374
- return join45(home, ".himalaya", "store-credentials");
37011
+ return join46(home, ".himalaya", "store-credentials");
36375
37012
  }
36376
37013
  function profilePath(name, home = homedir6()) {
36377
- return join45(credentialsDir(home), `${name}.json`);
37014
+ return join46(credentialsDir(home), `${name}.json`);
36378
37015
  }
36379
37016
  function parseProfile(text2, label2) {
36380
37017
  let doc;
@@ -39887,7 +40524,7 @@ var init_brand_font = __esm({
39887
40524
 
39888
40525
  // src/site-analyze/write-artifacts.ts
39889
40526
  import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync20 } from "node:fs";
39890
- import { join as join46 } from "node:path";
40527
+ import { join as join47 } from "node:path";
39891
40528
  function attachVeloSources(book, sources, put) {
39892
40529
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
39893
40530
  for (const f of book.velo.files) {
@@ -40042,12 +40679,12 @@ async function downloadMedia(book, fetchImpl, put) {
40042
40679
  }
40043
40680
  async function writeBusinessBook(result, opts) {
40044
40681
  const out = opts.outDir;
40045
- mkdirSync23(join46(out, "evidence/pages"), { recursive: true });
40046
- mkdirSync23(join46(out, "media/catalog"), { recursive: true });
40682
+ mkdirSync23(join47(out, "evidence/pages"), { recursive: true });
40683
+ mkdirSync23(join47(out, "media/catalog"), { recursive: true });
40047
40684
  const files = [];
40048
40685
  const put = (rel, body) => {
40049
- const p = join46(out, rel);
40050
- mkdirSync23(join46(p, ".."), { recursive: true });
40686
+ const p = join47(out, rel);
40687
+ mkdirSync23(join47(p, ".."), { recursive: true });
40051
40688
  writeFileSync20(p, body);
40052
40689
  files.push(rel);
40053
40690
  };
@@ -40378,8 +41015,8 @@ var init_home_descriptor = __esm({
40378
41015
  });
40379
41016
 
40380
41017
  // src/site-analyze/apply.ts
40381
- import { existsSync as existsSync48, readFileSync as readFileSync60, writeFileSync as writeFileSync21, copyFileSync as copyFileSync3, mkdirSync as mkdirSync24, realpathSync as realpathSync4, statSync as statSync11 } from "node:fs";
40382
- import { dirname as dirname28, isAbsolute as isAbsolute3, join as join47, resolve as resolve37, sep as sep9 } from "node:path";
41018
+ import { existsSync as existsSync48, readFileSync as readFileSync60, writeFileSync as writeFileSync21, copyFileSync as copyFileSync3, mkdirSync as mkdirSync24, realpathSync as realpathSync5, statSync as statSync12 } from "node:fs";
41019
+ import { dirname as dirname28, isAbsolute as isAbsolute3, join as join48, resolve as resolve37, sep as sep10 } from "node:path";
40383
41020
  import { spawnSync as spawnSync4 } from "node:child_process";
40384
41021
  function unpairedRedirectNote(overlay) {
40385
41022
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -40394,7 +41031,7 @@ function committedShellProvisioning(path) {
40394
41031
  function findRepoRoot(cwd) {
40395
41032
  let dir = cwd;
40396
41033
  for (let i = 0; i < 8; i++) {
40397
- if (SHELL_NAMES2.some((name) => existsSync48(join47(dir, shellAppDir(name), "package.json")))) return dir;
41034
+ if (SHELL_NAMES2.some((name) => existsSync48(join48(dir, shellAppDir(name), "package.json")))) return dir;
40398
41035
  const next = dirname28(dir);
40399
41036
  if (next === dir) break;
40400
41037
  dir = next;
@@ -40462,7 +41099,7 @@ function mergeCustomerOwned(committed, overlay) {
40462
41099
  }
40463
41100
  function buildMergedContract(options) {
40464
41101
  const { appDir: appDir2, packageDir, overlay } = options;
40465
- const committedPath = join47(appDir2, "provisioning.json");
41102
+ const committedPath = join48(appDir2, "provisioning.json");
40466
41103
  if (!existsSync48(committedPath)) {
40467
41104
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
40468
41105
  }
@@ -40474,23 +41111,23 @@ function buildMergedContract(options) {
40474
41111
  const contained = (root, rel) => {
40475
41112
  if (isAbsolute3(rel)) return void 0;
40476
41113
  const full = resolve37(root, rel);
40477
- return full.startsWith(resolve37(root) + sep9) ? full : void 0;
41114
+ return full.startsWith(resolve37(root) + sep10) ? full : void 0;
40478
41115
  };
40479
41116
  const stage = (fromRel, toRel) => {
40480
41117
  const from = contained(packageDir, fromRel);
40481
- const to = contained(join47(appDir2, STAGED), toRel.slice(STAGED.length + 1));
41118
+ const to = contained(join48(appDir2, STAGED), toRel.slice(STAGED.length + 1));
40482
41119
  if (!from || !to || !existsSync48(from)) return void 0;
40483
41120
  let real;
40484
41121
  let realRoot;
40485
41122
  try {
40486
- real = realpathSync4(from);
40487
- realRoot = realpathSync4(packageDir);
41123
+ real = realpathSync5(from);
41124
+ realRoot = realpathSync5(packageDir);
40488
41125
  } catch {
40489
41126
  return void 0;
40490
41127
  }
40491
- if (!real.startsWith(realRoot + sep9)) return void 0;
41128
+ if (!real.startsWith(realRoot + sep10)) return void 0;
40492
41129
  try {
40493
- if (!statSync11(real).isFile()) return void 0;
41130
+ if (!statSync12(real).isFile()) return void 0;
40494
41131
  mkdirSync24(dirname28(to), { recursive: true });
40495
41132
  copyFileSync3(real, to);
40496
41133
  } catch {
@@ -40501,7 +41138,7 @@ function buildMergedContract(options) {
40501
41138
  };
40502
41139
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
40503
41140
  if (icon) branding.appIconPath = icon;
40504
- const facesPath = join47(packageDir, "media/fonts/faces.json");
41141
+ const facesPath = join48(packageDir, "media/fonts/faces.json");
40505
41142
  if (existsSync48(facesPath)) {
40506
41143
  let faces = [];
40507
41144
  try {
@@ -40526,9 +41163,9 @@ function buildMergedContract(options) {
40526
41163
  merged.branding = branding;
40527
41164
  const body = `${JSON.stringify(merged, null, 2)}
40528
41165
  `;
40529
- const path = join47(packageDir, "provisioning.contract.json");
41166
+ const path = join48(packageDir, "provisioning.contract.json");
40530
41167
  writeFileSync21(path, body);
40531
- const localPath = join47(appDir2, LOCAL_CONTRACT_REL);
41168
+ const localPath = join48(appDir2, LOCAL_CONTRACT_REL);
40532
41169
  mkdirSync24(dirname28(localPath), { recursive: true });
40533
41170
  writeFileSync21(localPath, body);
40534
41171
  staged.push(localPath);
@@ -40549,7 +41186,7 @@ async function applyOverlays(input) {
40549
41186
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
40550
41187
  );
40551
41188
  }
40552
- const appDir2 = join47(repoRoot2, shellAppDir(shell));
41189
+ const appDir2 = join48(repoRoot2, shellAppDir(shell));
40553
41190
  const packageDir = dirname28(resolve37(input.overlayPath));
40554
41191
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
40555
41192
  wrote.push(...staged);
@@ -40571,7 +41208,7 @@ async function applyOverlays(input) {
40571
41208
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
40572
41209
  };
40573
41210
  }
40574
- const pkgProv = join47(cwd, "provisioning.json");
41211
+ const pkgProv = join48(cwd, "provisioning.json");
40575
41212
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
40576
41213
  if (existsSync48(pkgProv)) {
40577
41214
  const cur = JSON.parse(readFileSync60(pkgProv, "utf8"));
@@ -40582,7 +41219,7 @@ async function applyOverlays(input) {
40582
41219
  writeFileSync21(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
40583
41220
  wrote.push(pkgProv);
40584
41221
  }
40585
- const pkgTokens = join47(cwd, "tokens.json");
41222
+ const pkgTokens = join48(cwd, "tokens.json");
40586
41223
  if (existsSync48(pkgTokens) && (tokens.colors || tokens.typography)) {
40587
41224
  const cur = JSON.parse(readFileSync60(pkgTokens, "utf8"));
40588
41225
  const merged = deepMerge3(cur, tokens);
@@ -40590,7 +41227,7 @@ async function applyOverlays(input) {
40590
41227
  wrote.push(pkgTokens);
40591
41228
  }
40592
41229
  const homeAction = homeRefreshActionId(shell);
40593
- const homeSrc = join47(cwd, "dev/index.ts");
41230
+ const homeSrc = join48(cwd, "dev/index.ts");
40594
41231
  if (homeAction && existsSync48(homeSrc)) {
40595
41232
  const prev = readFileSync60(homeSrc, "utf8");
40596
41233
  const patched = patchBrandParams(prev, overlay, homeAction);
@@ -42574,11 +43211,11 @@ var init_site_oauth_client = __esm({
42574
43211
  });
42575
43212
 
42576
43213
  // src/cli.ts
42577
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync63, mkdirSync as mkdirSync25, writeFileSync as writeFileSync23, existsSync as existsSync50, rmSync as rmSync8, readdirSync as readdirSync23, statSync as statSync12 } from "node:fs";
43214
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync63, mkdirSync as mkdirSync25, writeFileSync as writeFileSync23, existsSync as existsSync50, rmSync as rmSync8, readdirSync as readdirSync23, statSync as statSync13 } from "node:fs";
42578
43215
  import { execFileSync as execFileSync6 } from "node:child_process";
42579
43216
  import { createHash as createHash24 } from "node:crypto";
42580
43217
  import { tmpdir as tmpdir5 } from "node:os";
42581
- import { join as join48, resolve as resolve39, basename as basename8, dirname as dirname29 } from "node:path";
43218
+ import { join as join49, resolve as resolve39, basename as basename8, dirname as dirname29 } from "node:path";
42582
43219
  import { fileURLToPath as fileURLToPath21 } from "node:url";
42583
43220
 
42584
43221
  // ../serve-cli/src/client.ts
@@ -45086,10 +45723,10 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
45086
45723
  // src/notify.ts
45087
45724
  import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
45088
45725
  import { homedir as homedir3 } from "node:os";
45089
- import { dirname as dirname12, join as join14 } from "node:path";
45726
+ import { dirname as dirname12, join as join15 } from "node:path";
45090
45727
  var DEFAULT_ADMIN_URL = "http://127.0.0.1:8888";
45091
45728
  function defaultSessionsPath() {
45092
- return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join14(homedir3(), ".himalaya", "admin-sessions.json");
45729
+ return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join15(homedir3(), ".himalaya", "admin-sessions.json");
45093
45730
  }
45094
45731
  function loadSessionsFile(path = defaultSessionsPath()) {
45095
45732
  try {
@@ -45328,7 +45965,7 @@ NOTE: ${simulated} of those went to the booted iOS Simulator, NOT to the device
45328
45965
  init_car();
45329
45966
  init_preview_surfaces();
45330
45967
  import { existsSync as existsSync12, readFileSync as readFileSync15 } from "node:fs";
45331
- import { join as join15 } from "node:path";
45968
+ import { join as join16 } from "node:path";
45332
45969
  var VALID_PLATFORM_KEYS = /* @__PURE__ */ new Set([
45333
45970
  ...PREVIEW_SURFACES.map((s) => s.platformKey).filter((k) => k !== null),
45334
45971
  ...PREVIEW_EXEMPT_PLATFORM_KEYS
@@ -45339,7 +45976,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
45339
45976
  const error = (message) => issues.push({ screen: null, pass: "surfaces", severity: "error", message });
45340
45977
  let platforms = {};
45341
45978
  let envelopeUnreadable = false;
45342
- const p = join15(dir, "platforms.json");
45979
+ const p = join16(dir, "platforms.json");
45343
45980
  if (existsSync12(p)) {
45344
45981
  let raw;
45345
45982
  try {
@@ -45372,7 +46009,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
45372
46009
  });
45373
46010
  }
45374
46011
  if (envelopeUnreadable) return issues;
45375
- const configTs = join15(dir, "config.ts");
46012
+ const configTs = join16(dir, "config.ts");
45376
46013
  if (existsSync12(configTs)) {
45377
46014
  const declared = parseCarConfig(readFileSync15(configTs, "utf8")).category;
45378
46015
  const inEnvelope = platforms.androidauto === true;
@@ -45398,7 +46035,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
45398
46035
  }
45399
46036
  function readLocalPlatforms(dir) {
45400
46037
  try {
45401
- const raw = JSON.parse(readFileSync15(join15(dir, "platforms.json"), "utf8"));
46038
+ const raw = JSON.parse(readFileSync15(join16(dir, "platforms.json"), "utf8"));
45402
46039
  return Object.fromEntries(
45403
46040
  Object.entries(raw).filter(([k, v]) => VALID_PLATFORM_KEYS.has(k) && typeof v === "boolean")
45404
46041
  );
@@ -45410,10 +46047,10 @@ function readLocalPlatforms(dir) {
45410
46047
  // src/token-lint.ts
45411
46048
  init_token_colors();
45412
46049
  import { existsSync as existsSync13, readFileSync as readFileSync16 } from "node:fs";
45413
- import { join as join16 } from "node:path";
46050
+ import { join as join17 } from "node:path";
45414
46051
  function configuredTokensPath(dir) {
45415
46052
  for (const name of ["config.ts", "config.js", "himi.config.ts"]) {
45416
- const f = join16(dir, name);
46053
+ const f = join17(dir, name);
45417
46054
  if (!existsSync13(f)) continue;
45418
46055
  const src = readFileSync16(f, "utf8");
45419
46056
  const m = /designTokensPath\s*:\s*["'`]([^"'`]+)["'`]/.exec(src);
@@ -45433,7 +46070,7 @@ function tokenColorIssues(dir, tokensPathOverride) {
45433
46070
  );
45434
46071
  }
45435
46072
  const tokensPath = configured.path ?? "tokens.json";
45436
- const p = join16(dir, tokensPath);
46073
+ const p = join17(dir, tokensPath);
45437
46074
  if (!existsSync13(p)) {
45438
46075
  if (tokensPath !== "tokens.json") {
45439
46076
  warn(`config designTokensPath points at ${tokensPath}, which does not exist -- colour tokens were not checked`);
@@ -45481,9 +46118,9 @@ function tokenColorIssues(dir, tokensPathOverride) {
45481
46118
  // src/icon-lint.ts
45482
46119
  init_icon();
45483
46120
  import { existsSync as existsSync16 } from "node:fs";
45484
- import { join as join19 } from "node:path";
46121
+ import { join as join20 } from "node:path";
45485
46122
  async function iconIssues(dir, strict, opts = {}) {
45486
- if (!existsSync16(join19(dir, "himalaya.content.json"))) return [];
46123
+ if (!existsSync16(join20(dir, "himalaya.content.json"))) return [];
45487
46124
  const status = await iconStatus(dir);
45488
46125
  const issue2 = (severity, message) => ({
45489
46126
  screen: null,
@@ -45551,13 +46188,13 @@ function fontIssues(dir, strict) {
45551
46188
 
45552
46189
  // src/stores-api-lint.ts
45553
46190
  import { existsSync as existsSync20, readdirSync as readdirSync10, readFileSync as readFileSync22 } from "node:fs";
45554
- import { join as join23 } from "node:path";
46191
+ import { join as join24 } from "node:path";
45555
46192
  function storesV1Issues(contentDir2, strict) {
45556
- const roots = [join23(contentDir2, "tier5-src"), join23(contentDir2, "dev")].filter(existsSync20);
46193
+ const roots = [join24(contentDir2, "tier5-src"), join24(contentDir2, "dev")].filter(existsSync20);
45557
46194
  const files = [];
45558
46195
  const visit = (dir) => {
45559
46196
  for (const entry of readdirSync10(dir, { withFileTypes: true })) {
45560
- const path = join23(dir, entry.name);
46197
+ const path = join24(dir, entry.name);
45561
46198
  if (entry.isDirectory()) visit(path);
45562
46199
  else if (/\.(?:[cm]?[jt]sx?)$/i.test(entry.name)) files.push(path);
45563
46200
  }
@@ -45578,7 +46215,7 @@ init_crawl_layers();
45578
46215
  init_wix_gateway_rules();
45579
46216
  init_mobile_ux_contract();
45580
46217
  import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync23, readdirSync as readdirSync11, writeFileSync as writeFileSync9 } from "node:fs";
45581
- import { join as join24, relative as relative3, resolve as resolve15 } from "node:path";
46218
+ import { join as join25, relative as relative3, resolve as resolve15 } from "node:path";
45582
46219
  var EVIDENCE_RELATIVE_PATH = ".himi/ux-audit.json";
45583
46220
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".json"]);
45584
46221
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".himi", "coverage"]);
@@ -45596,12 +46233,12 @@ function walkSource(dir, root = dir) {
45596
46233
  const found = [];
45597
46234
  for (const entry of readdirSync11(dir, { withFileTypes: true })) {
45598
46235
  if (entry.isDirectory()) {
45599
- if (!IGNORED_DIRECTORIES.has(entry.name)) found.push(...walkSource(join24(dir, entry.name), root));
46236
+ if (!IGNORED_DIRECTORIES.has(entry.name)) found.push(...walkSource(join25(dir, entry.name), root));
45600
46237
  continue;
45601
46238
  }
45602
46239
  const ext = entry.name.slice(entry.name.lastIndexOf("."));
45603
46240
  if (!SOURCE_EXTENSIONS.has(ext)) continue;
45604
- const absolute = join24(dir, entry.name);
46241
+ const absolute = join25(dir, entry.name);
45605
46242
  const text2 = readText(absolute);
45606
46243
  if (text2 !== null) found.push({ path: relative3(root, absolute), text: text2 });
45607
46244
  }
@@ -45631,9 +46268,9 @@ function usesAppNetwork(source) {
45631
46268
  function auditUx(content, strict = false) {
45632
46269
  const dir = resolve15(content);
45633
46270
  const issues = [];
45634
- const evidencePath = join24(dir, EVIDENCE_RELATIVE_PATH);
45635
- const spec = readText(join24(dir, "SPEC.md"));
45636
- const mobile = readText(join24(dir, "MOBILE-UX.md"));
46271
+ const evidencePath = join25(dir, EVIDENCE_RELATIVE_PATH);
46272
+ const spec = readText(join25(dir, "SPEC.md"));
46273
+ const mobile = readText(join25(dir, "MOBILE-UX.md"));
45637
46274
  if (!spec) issue(issues, strict, "missing-spec", "SPEC.md is missing; define the customer task before calling this UX-ready.", "SPEC.md");
45638
46275
  if (!mobile) {
45639
46276
  issue(issues, strict, "missing-mobile-ux", "MOBILE-UX.md is missing; add the mobile state and platform contract.", "MOBILE-UX.md");
@@ -45672,7 +46309,7 @@ function auditUx(content, strict = false) {
45672
46309
  const preview = matches(previewControl);
45673
46310
  if (preview) issue(issues, strict, "possible-preview-control", "Possible customer-visible test/demo state control found. Keep scenario controls out of shipping UI, or document a capability-limited fallback.", preview.path);
45674
46311
  if (usesAppNetwork(source)) {
45675
- const mocksPath = join24(dir, "dev", "net-mocks.json");
46312
+ const mocksPath = join25(dir, "dev", "net-mocks.json");
45676
46313
  if (!existsSync21(mocksPath)) {
45677
46314
  issue(issues, strict, "missing-app-mocks", "This package uses a Wix/network client but has no app-specific dev/net-mocks.json. Add fixtures for dynamic, entitlement, or recovery flows.", "dev/net-mocks.json");
45678
46315
  } else {
@@ -45694,12 +46331,12 @@ function recordNativeAudit(content, platform, status, checks, reason) {
45694
46331
  if (!normalizedChecks.length) throw new Error("--checks needs at least one comma-separated check (for example: back,keyboard,voiceover)");
45695
46332
  if (status === "unavailable" && !reason?.trim()) throw new Error("--reason is required when --status unavailable");
45696
46333
  const dir = resolve15(content);
45697
- const path = join24(dir, EVIDENCE_RELATIVE_PATH);
46334
+ const path = join25(dir, EVIDENCE_RELATIVE_PATH);
45698
46335
  const parsed = parseEvidence(path);
45699
46336
  if (parsed.error) throw new Error(`${EVIDENCE_RELATIVE_PATH} ${parsed.error}`);
45700
46337
  const evidence = parsed.evidence ?? { version: 1, platforms: {} };
45701
46338
  evidence.platforms[platform] = { status, checks: normalizedChecks, ...reason?.trim() ? { reason: reason.trim() } : {}, recordedAt: (/* @__PURE__ */ new Date()).toISOString() };
45702
- mkdirSync10(join24(dir, ".himi"), { recursive: true });
46339
+ mkdirSync10(join25(dir, ".himi"), { recursive: true });
45703
46340
  writeFileSync9(path, JSON.stringify(evidence, null, 2) + "\n");
45704
46341
  return evidence;
45705
46342
  }
@@ -45708,26 +46345,26 @@ function recordNativeAudit(content, platform, status, checks, reason) {
45708
46345
  init_mobile_ux_contract();
45709
46346
  import { readFileSync as readFileSync24, existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10, copyFileSync as copyFileSync2 } from "node:fs";
45710
46347
  import { fileURLToPath as fileURLToPath10 } from "node:url";
45711
- import { dirname as dirname15, join as join25, resolve as resolve16 } from "node:path";
46348
+ import { dirname as dirname15, join as join26, resolve as resolve16 } from "node:path";
45712
46349
  var GUIDES_DIR = resolve16(dirname15(fileURLToPath10(import.meta.url)), "../guides");
45713
46350
  function guidesAvailable() {
45714
- return existsSync22(join25(GUIDES_DIR, "index.json"));
46351
+ return existsSync22(join26(GUIDES_DIR, "index.json"));
45715
46352
  }
45716
46353
  function loadIndex() {
45717
- return JSON.parse(readFileSync24(join25(GUIDES_DIR, "index.json"), "utf8"));
46354
+ return JSON.parse(readFileSync24(join26(GUIDES_DIR, "index.json"), "utf8"));
45718
46355
  }
45719
46356
  function readGuide(id) {
45720
46357
  const meta = loadIndex().guides.find((g) => g.id === id);
45721
46358
  if (!meta) return null;
45722
- return readFileSync24(join25(GUIDES_DIR, meta.file), "utf8");
46359
+ return readFileSync24(join26(GUIDES_DIR, meta.file), "utf8");
45723
46360
  }
45724
46361
  function loadComponentReference() {
45725
46362
  const idx = loadIndex();
45726
- return JSON.parse(readFileSync24(join25(GUIDES_DIR, idx.componentReferenceFile), "utf8"));
46363
+ return JSON.parse(readFileSync24(join26(GUIDES_DIR, idx.componentReferenceFile), "utf8"));
45727
46364
  }
45728
46365
  function loadSdkReference() {
45729
46366
  const idx = loadIndex();
45730
- return JSON.parse(readFileSync24(join25(GUIDES_DIR, idx.sdkReferenceFile), "utf8"));
46367
+ return JSON.parse(readFileSync24(join26(GUIDES_DIR, idx.sdkReferenceFile), "utf8"));
45731
46368
  }
45732
46369
  function buildAgentPrimer() {
45733
46370
  const idx = loadIndex();
@@ -45835,19 +46472,19 @@ function writeAgentContext(targetDir) {
45835
46472
  const idx = loadIndex();
45836
46473
  const files = [];
45837
46474
  const write2 = (rel, body) => {
45838
- const p = join25(targetDir, rel);
46475
+ const p = join26(targetDir, rel);
45839
46476
  mkdirSync11(dirname15(p), { recursive: true });
45840
46477
  writeFileSync10(p, body);
45841
46478
  files.push(rel);
45842
46479
  };
45843
46480
  const copy = (srcRel, destRel) => {
45844
- const p = join25(targetDir, destRel);
46481
+ const p = join26(targetDir, destRel);
45845
46482
  mkdirSync11(dirname15(p), { recursive: true });
45846
- copyFileSync2(join25(GUIDES_DIR, srcRel), p);
46483
+ copyFileSync2(join26(GUIDES_DIR, srcRel), p);
45847
46484
  files.push(destRel);
45848
46485
  };
45849
46486
  const writeIfMissing = (rel, body) => {
45850
- if (existsSync22(join25(targetDir, rel))) return;
46487
+ if (existsSync22(join26(targetDir, rel))) return;
45851
46488
  write2(rel, body);
45852
46489
  };
45853
46490
  write2("HIMALAYA.md", buildAgentPrimer());
@@ -45908,7 +46545,7 @@ var str5 = (v) => typeof v === "string" ? v : void 0;
45908
46545
  var num = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
45909
46546
  function embeddedTemplateCatalog() {
45910
46547
  return availableTemplates().map((name) => {
45911
- const path = join48(templateSourceDir(name), TEMPLATE_META_FILE);
46548
+ const path = join49(templateSourceDir(name), TEMPLATE_META_FILE);
45912
46549
  const meta = JSON.parse(readFileSync63(path, "utf8"));
45913
46550
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
45914
46551
  throw new Error(`${path} must be { title: string, description: string, tags: string[], category?: string }`);
@@ -46375,7 +47012,7 @@ function packageVersion(path) {
46375
47012
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
46376
47013
  let dir = resolve39(fromDir);
46377
47014
  for (; ; ) {
46378
- const version = packageVersion(join48(dir, "node_modules", "@wix", "himalaya", "package.json"));
47015
+ const version = packageVersion(join49(dir, "node_modules", "@wix", "himalaya", "package.json"));
46379
47016
  if (version) return { version, source: "local" };
46380
47017
  const parent = dirname29(dir);
46381
47018
  if (parent === dir) break;
@@ -46385,7 +47022,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
46385
47022
  if (bundled) return { version: bundled, source: "bundled" };
46386
47023
  try {
46387
47024
  const root = execFileSync6("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
46388
- const version = root ? packageVersion(join48(root, "@wix", "himalaya", "package.json")) : void 0;
47025
+ const version = root ? packageVersion(join49(root, "@wix", "himalaya", "package.json")) : void 0;
46389
47026
  return version ? { version, source: "global" } : {};
46390
47027
  } catch {
46391
47028
  return {};
@@ -46760,7 +47397,7 @@ async function nativeConfigAdvisories(app, configSource) {
46760
47397
  }
46761
47398
  function readConfigSource(dir) {
46762
47399
  try {
46763
- return readFileSync63(join48(dir, "config.ts"), "utf8");
47400
+ return readFileSync63(join49(dir, "config.ts"), "utf8");
46764
47401
  } catch {
46765
47402
  return null;
46766
47403
  }
@@ -46772,7 +47409,7 @@ function writePngFile(path, base64) {
46772
47409
  }
46773
47410
  async function initFromRemoteTemplate(name, parentDir, template, c) {
46774
47411
  const dir = resolve39(parentDir, name);
46775
- if (existsSync50(join48(dir, "himalaya.content.json"))) {
47412
+ if (existsSync50(join49(dir, "himalaya.content.json"))) {
46776
47413
  throw new Error(`content package already exists at ${dir}`);
46777
47414
  }
46778
47415
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -46789,7 +47426,7 @@ function contentDir(flags) {
46789
47426
  function isHimalayaMonorepoCheckout(start) {
46790
47427
  let dir = resolve39(start);
46791
47428
  for (; ; ) {
46792
- if (existsSync50(join48(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync50(join48(dir, "core", "serve", "src", "server.ts"))) return true;
47429
+ if (existsSync50(join49(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync50(join49(dir, "core", "serve", "src", "server.ts"))) return true;
46793
47430
  const parent = dirname29(dir);
46794
47431
  if (parent === dir) return false;
46795
47432
  dir = parent;
@@ -46820,7 +47457,7 @@ function materializeForPreview(appDir2, slug, displayName) {
46820
47457
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
46821
47458
  );
46822
47459
  const repoRoot2 = resolve39(fileURLToPath21(new URL("../../..", import.meta.url)));
46823
- const dest = join48(repoRoot2, "node_modules", ".cache", "himi-template-preview", slug);
47460
+ const dest = join49(repoRoot2, "node_modules", ".cache", "himi-template-preview", slug);
46824
47461
  rmSync8(dest, { recursive: true, force: true });
46825
47462
  mkdirSync25(dest, { recursive: true });
46826
47463
  materializeSource(forBuild, displayName, dest, slug);
@@ -46829,7 +47466,7 @@ function materializeForPreview(appDir2, slug, displayName) {
46829
47466
  async function appId(dir) {
46830
47467
  try {
46831
47468
  const { readFileSync: readFileSync64 } = await import("node:fs");
46832
- const m = JSON.parse(readFileSync64(join48(dir, "himalaya.content.json"), "utf8"));
47469
+ const m = JSON.parse(readFileSync64(join49(dir, "himalaya.content.json"), "utf8"));
46833
47470
  if (m.name) return m.name;
46834
47471
  } catch {
46835
47472
  }
@@ -46838,7 +47475,7 @@ async function appId(dir) {
46838
47475
  async function contentVisibility(dir) {
46839
47476
  try {
46840
47477
  const { readFileSync: readFileSync64 } = await import("node:fs");
46841
- const m = JSON.parse(readFileSync64(join48(dir, "himalaya.content.json"), "utf8"));
47478
+ const m = JSON.parse(readFileSync64(join49(dir, "himalaya.content.json"), "utf8"));
46842
47479
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
46843
47480
  } catch {
46844
47481
  }
@@ -47077,6 +47714,7 @@ async function loadAuthoring() {
47077
47714
  crawlScreen: crawl.crawlScreen,
47078
47715
  crawlScreenJourney: crawl.crawlScreenJourney,
47079
47716
  crawlScreenHostile: crawl.crawlScreenHostile,
47717
+ crawlScreenRelations: crawl.crawlScreenRelations,
47080
47718
  findingKey: crawl.findingKey,
47081
47719
  startWorkerCoverage: cov.startWorkerCoverage,
47082
47720
  uncoveredFunctions: cov.uncoveredFunctions,
@@ -47239,7 +47877,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47239
47877
  const sourceDir = resolve39(str5(flags["source-dir"]) ?? appDir2);
47240
47878
  let meta;
47241
47879
  try {
47242
- meta = JSON.parse(readFileSync63(join48(sourceDir, TEMPLATE_META_FILE), "utf8"));
47880
+ meta = JSON.parse(readFileSync63(join49(sourceDir, TEMPLATE_META_FILE), "utf8"));
47243
47881
  } catch {
47244
47882
  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>)` }));
47245
47883
  return 2;
@@ -47249,7 +47887,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47249
47887
  return 2;
47250
47888
  }
47251
47889
  try {
47252
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync63(join48(sourceDir, "tokens.json"), "utf8")));
47890
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync63(join49(sourceDir, "tokens.json"), "utf8")));
47253
47891
  meta = { ...appearance, ...meta };
47254
47892
  } catch {
47255
47893
  }
@@ -47261,7 +47899,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47261
47899
  const force = flags.force === true;
47262
47900
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
47263
47901
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
47264
- const out = mkdtempSync4(join48(tmpdir5(), "himi-template-"));
47902
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-template-"));
47265
47903
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
47266
47904
  const outcome = await buildReleaseFromContent2(buildDir, out, {
47267
47905
  ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
@@ -47420,7 +48058,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47420
48058
  const { mobileUxIssues: mobileUxIssues2 } = await Promise.resolve().then(() => (init_mobile_ux_lint(), mobile_ux_lint_exports));
47421
48059
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
47422
48060
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
47423
- const out = mkdtempSync4(join48(tmpdir5(), "himi-validate-"));
48061
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-validate-"));
47424
48062
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
47425
48063
  const outcome = await buildReleaseFromContent2(dir, out, {
47426
48064
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -47608,7 +48246,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47608
48246
  case "test": {
47609
48247
  const dir = contentDir(flags);
47610
48248
  const structuredOutput = flags.json === true || io.isTTY !== true;
47611
- if (existsSync50(join48(dir, "functions"))) {
48249
+ if (existsSync50(join49(dir, "functions"))) {
47612
48250
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
47613
48251
  const loaded = await loadFunctionsApp2(dir);
47614
48252
  const results = [];
@@ -47622,7 +48260,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47622
48260
  return results.every((result2) => result2.ok) ? 0 : 1;
47623
48261
  }
47624
48262
  const app = await appId(dir);
47625
- const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2, crawlScreen: crawlScreen2, crawlScreenJourney: crawlScreenJourney2, crawlScreenHostile: crawlScreenHostile2, findingKey: findingKey2, startWorkerCoverage: startWorkerCoverage2, uncoveredFunctions: uncoveredFunctions2, crawlScreenDeep: crawlScreenDeep2, crawlSignature: crawlSignature2, recordScreenTraffic: recordScreenTraffic2, mintVisitor: mintVisitor2, webWixCall: webWixCall2, freezeTrafficToMocks: freezeTrafficToMocks2, bootAndSnapshotState: bootAndSnapshotState2, renderDescriptorWeb: renderDescriptorWeb2, renderPngBatch: renderPngBatch2, pngAvailable: pngAvailable2, crawlBlockingPasses } = await loadAuthoring();
48263
+ const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2, crawlScreen: crawlScreen2, crawlScreenJourney: crawlScreenJourney2, crawlScreenHostile: crawlScreenHostile2, crawlScreenRelations: crawlScreenRelations2, findingKey: findingKey2, startWorkerCoverage: startWorkerCoverage2, uncoveredFunctions: uncoveredFunctions2, crawlScreenDeep: crawlScreenDeep2, crawlSignature: crawlSignature2, recordScreenTraffic: recordScreenTraffic2, mintVisitor: mintVisitor2, webWixCall: webWixCall2, freezeTrafficToMocks: freezeTrafficToMocks2, bootAndSnapshotState: bootAndSnapshotState2, renderDescriptorWeb: renderDescriptorWeb2, renderPngBatch: renderPngBatch2, pngAvailable: pngAvailable2, crawlBlockingPasses } = await loadAuthoring();
47626
48264
  const { readTestConfig: readTestConfig2, runCrawl: runCrawl2, formatReport: formatReport2, readCannedResponses: readCannedResponses2, makeRecordingWixCall: makeRecordingWixCall2, runRecordPass: runRecordPass2, runShotsPass: runShotsPass2, defaultNetRules: defaultNetRules2, readNetRules: readNetRules2, scopeDescriptors: scopeDescriptors2, writeRecordedMocks: writeRecordedMocks2 } = await Promise.resolve().then(() => (init_test(), test_exports));
47627
48265
  let config;
47628
48266
  try {
@@ -47695,7 +48333,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47695
48333
  }
47696
48334
  }
47697
48335
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
47698
- const out = mkdtempSync4(join48(tmpdir5(), "himi-test-"));
48336
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-test-"));
47699
48337
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
47700
48338
  const outcome = await buildReleaseFromContent2(dir, out, {
47701
48339
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -47740,7 +48378,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47740
48378
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
47741
48379
  return 2;
47742
48380
  }
47743
- const sources = collectWorkerSources2(join48(dir, "tier5-src"), screen.bundleName);
48381
+ const sources = collectWorkerSources2(join49(dir, "tier5-src"), screen.bundleName);
47744
48382
  if (!sources) {
47745
48383
  io.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
47746
48384
  return 2;
@@ -47860,7 +48498,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47860
48498
  ...deep ? { deep } : {},
47861
48499
  ...offline ? { offline: true } : {},
47862
48500
  ...offline || flags["no-hostile"] === true ? { hostile: false } : {},
47863
- deps: { crawlScreen: crawlScreen2, crawlScreenJourney: crawlScreenJourney2, crawlScreenHostile: crawlScreenHostile2, findingKey: findingKey2, startWorkerCoverage: startWorkerCoverage2, uncoveredFunctions: uncoveredFunctions2, crawlScreenDeep: crawlScreenDeep2, crawlSignature: crawlSignature2 }
48501
+ deps: { crawlScreen: crawlScreen2, crawlScreenJourney: crawlScreenJourney2, crawlScreenHostile: crawlScreenHostile2, crawlScreenRelations: crawlScreenRelations2, findingKey: findingKey2, startWorkerCoverage: startWorkerCoverage2, uncoveredFunctions: uncoveredFunctions2, crawlScreenDeep: crawlScreenDeep2, crawlSignature: crawlSignature2 }
47864
48502
  });
47865
48503
  } catch (e) {
47866
48504
  io.error(JSON.stringify({ error: e.message }));
@@ -47897,7 +48535,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47897
48535
  const sp = await runShotsPass2({
47898
48536
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
47899
48537
  tokens: outcome.tokens,
47900
- outDir: join48(dir, ".himi", "shots"),
48538
+ outDir: join49(dir, ".himi", "shots"),
47901
48539
  width: 390,
47902
48540
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
47903
48541
  ...budgetMs ? { budgetMs } : {},
@@ -47933,7 +48571,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47933
48571
  releaseId: result.releaseId,
47934
48572
  releasePayload: release,
47935
48573
  descriptors: attestedDescriptors,
47936
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join48(dir, ".himi", "shots", name)])),
48574
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join49(dir, ".himi", "shots", name)])),
47937
48575
  coverage: {
47938
48576
  screens: crawledScreens.size,
47939
48577
  actionsDispatched: result.actions.exercised,
@@ -48075,7 +48713,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
48075
48713
  }
48076
48714
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48077
48715
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
48078
- const out = mkdtempSync4(join48(tmpdir5(), "himi-render-"));
48716
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-render-"));
48079
48717
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48080
48718
  const outcome = await buildReleaseFromContent2(dir, out, {
48081
48719
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -48091,7 +48729,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
48091
48729
  let attestationPngFile;
48092
48730
  if (typeof flags.png === "string" && r.pngBase64) {
48093
48731
  pngFile = writePngFile(str5(flags.png), r.pngBase64);
48094
- attestationPngFile = writePngFile(join48(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
48732
+ attestationPngFile = writePngFile(join49(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
48095
48733
  }
48096
48734
  const legacyRender = {
48097
48735
  ok: r.renderable,
@@ -48398,7 +49036,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48398
49036
  const renamed = str5(flags.name);
48399
49037
  const dest = resolve39(str5(flags.out) ?? process.cwd(), renamed ?? wanted);
48400
49038
  if (existsSync50(dest)) {
48401
- if (!statSync12(dest).isDirectory()) {
49039
+ if (!statSync13(dest).isDirectory()) {
48402
49040
  io.error(JSON.stringify({
48403
49041
  error: `${dest} exists and is not a directory`,
48404
49042
  hint: "pass --out/--name to land somewhere else, or remove the file"
@@ -48471,7 +49109,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48471
49109
  const force = flags.force === true;
48472
49110
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48473
49111
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
48474
- const out = mkdtempSync4(join48(tmpdir5(), "himi-push-"));
49112
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-push-"));
48475
49113
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48476
49114
  const outcome = await buildReleaseFromContent2(dir, out, {
48477
49115
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -48763,7 +49401,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48763
49401
  const force = flags.force === true;
48764
49402
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48765
49403
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
48766
- const built = mkdtempSync4(join48(tmpdir5(), "himi-pack-"));
49404
+ const built = mkdtempSync4(join49(tmpdir5(), "himi-pack-"));
48767
49405
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48768
49406
  const outcome = await buildReleaseFromContent2(dir, built, {
48769
49407
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -48876,10 +49514,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48876
49514
  const app = await appId(dir);
48877
49515
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48878
49516
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
48879
- temp = mkdtempSync4(join48(tmpdir5(), "himi-run-build-"));
49517
+ temp = mkdtempSync4(join49(tmpdir5(), "himi-run-build-"));
48880
49518
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48881
49519
  await buildReleaseFromContent2(dir, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {} });
48882
- source = join48(temp, app);
49520
+ source = join49(temp, app);
48883
49521
  }
48884
49522
  const chromeArg = str5(flags.chrome) ?? "preview";
48885
49523
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -49677,7 +50315,7 @@ ${label2}`);
49677
50315
  const platform = str5(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
49678
50316
  {
49679
50317
  const distDir = contentDir(flags);
49680
- if (existsSync50(join48(distDir, "himalaya.content.json"))) {
50318
+ if (existsSync50(join49(distDir, "himalaya.content.json"))) {
49681
50319
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
49682
50320
  const errors = issues.filter((i) => i.severity === "error");
49683
50321
  for (const i of issues.filter((i2) => i2.severity === "warning")) {