@wix/himalaya-cli 0.808.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));
@@ -14736,8 +15019,8 @@ function ensureCountUpRuntime() {
14736
15019
  const neg = fixed.startsWith("-");
14737
15020
  const body = neg ? fixed.slice(1) : fixed;
14738
15021
  const [int, frac] = body.split(".");
14739
- const sep10 = int.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
14740
- return (neg ? "-" : "") + sep10 + (frac ? "." + frac : "");
15022
+ const sep11 = int.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
15023
+ return (neg ? "-" : "") + sep11 + (frac ? "." + frac : "");
14741
15024
  };
14742
15025
  const animateEl = (el) => {
14743
15026
  const node = el;
@@ -21914,18 +22197,18 @@ var init_motion_flags = __esm({
21914
22197
 
21915
22198
  // ../../core/dev-server/src/kits.ts
21916
22199
  import { readdirSync as readdirSync16, readFileSync as readFileSync36 } from "node:fs";
21917
- import { resolve as resolve25, join as join30 } from "node:path";
22200
+ import { resolve as resolve25, join as join31 } from "node:path";
21918
22201
  import { fileURLToPath as fileURLToPath15 } from "node:url";
21919
22202
  function listKits() {
21920
22203
  return readdirSync16(CATALOG).filter((f) => f.endsWith(".json")).map((f) => {
21921
- const { tokens, ...meta } = JSON.parse(readFileSync36(join30(CATALOG, f), "utf8"));
22204
+ const { tokens, ...meta } = JSON.parse(readFileSync36(join31(CATALOG, f), "utf8"));
21922
22205
  return meta;
21923
22206
  });
21924
22207
  }
21925
22208
  function readKit(id) {
21926
22209
  if (!/^[a-z0-9-]+$/.test(id)) return null;
21927
22210
  try {
21928
- return JSON.parse(readFileSync36(join30(CATALOG, `${id}.json`), "utf8"));
22211
+ return JSON.parse(readFileSync36(join31(CATALOG, `${id}.json`), "utf8"));
21929
22212
  } catch {
21930
22213
  return null;
21931
22214
  }
@@ -21934,7 +22217,7 @@ var ROOT5, CATALOG;
21934
22217
  var init_kits = __esm({
21935
22218
  "../../core/dev-server/src/kits.ts"() {
21936
22219
  ROOT5 = resolve25(fileURLToPath15(new URL("../../..", import.meta.url)));
21937
- CATALOG = join30(ROOT5, "stdlib/design-kits");
22220
+ CATALOG = join31(ROOT5, "stdlib/design-kits");
21938
22221
  }
21939
22222
  });
21940
22223
 
@@ -24137,7 +24420,7 @@ __export(config_exports, {
24137
24420
  validateOverlayPatch: () => validateOverlayPatch
24138
24421
  });
24139
24422
  import { mkdirSync as mkdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync12 } from "node:fs";
24140
- 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";
24141
24424
  import { fileURLToPath as fileURLToPath16 } from "node:url";
24142
24425
  function strip(line) {
24143
24426
  const h = line.indexOf("#");
@@ -24202,14 +24485,14 @@ function parseRevocationsYaml(text2) {
24202
24485
  function appConfigPath(app, file, root = REPO_ROOT6) {
24203
24486
  assertAppId(app, "appConfigPath");
24204
24487
  for (const base of ["apps", "test-apps"]) {
24205
- const p = join31(root, base, app, file);
24488
+ const p = join32(root, base, app, file);
24206
24489
  try {
24207
24490
  readFileSync38(p);
24208
24491
  return p;
24209
24492
  } catch {
24210
24493
  }
24211
24494
  }
24212
- return join31(root, "apps", app, file);
24495
+ return join32(root, "apps", app, file);
24213
24496
  }
24214
24497
  function serializeRolloutYaml(cfg) {
24215
24498
  const lines = [`app: ${cfg.app}`, "rings:"];
@@ -24353,7 +24636,7 @@ var init_config2 = __esm({
24353
24636
  import { createServer as createServer2 } from "node:http";
24354
24637
  import { readFile as readFile2, stat } from "node:fs/promises";
24355
24638
  import { readFileSync as readFileSync39, existsSync as existsSync33, watch, mkdirSync as mkdirSync16, rmSync as rmSync4 } from "node:fs";
24356
- 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";
24357
24640
  import { createHash as createHash15, randomUUID } from "node:crypto";
24358
24641
  import { fileURLToPath as fileURLToPath17, pathToFileURL as pathToFileURL6 } from "node:url";
24359
24642
  function deepMerge2(base, patch) {
@@ -24528,7 +24811,7 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
24528
24811
  }
24529
24812
  function servedFontFaces(appDir2, tokens) {
24530
24813
  try {
24531
- const policyPath = join32(appDir2, "fonts.json");
24814
+ const policyPath = join33(appDir2, "fonts.json");
24532
24815
  const policy = existsSync33(policyPath) ? JSON.parse(readFileSync39(policyPath, "utf8")) : {};
24533
24816
  const plan = planFonts(tokens, policy);
24534
24817
  if (plan.families.length === 0) return void 0;
@@ -25171,7 +25454,7 @@ function startBundleSourceWatcher(state) {
25171
25454
  try {
25172
25455
  const watcher = watch(TIER5_SRC_ROOT, { persistent: false, recursive: true }, (_event, filename) => {
25173
25456
  if (!filename || !/\.(ts|tsx)$/.test(filename)) return;
25174
- const name = filename.split(sep5)[0];
25457
+ const name = filename.split(sep6)[0];
25175
25458
  if (!name || name.startsWith(".")) return;
25176
25459
  pending.add(name);
25177
25460
  if (timer) clearTimeout(timer);
@@ -25201,7 +25484,7 @@ async function reloadAppModule(state, opts) {
25201
25484
  const outfile = resolve28(HMR_TMP_DIR, `${current.name}-${++appReloadSeq}.mjs`);
25202
25485
  try {
25203
25486
  const lib = await import(pathToFileURL6(resolve28(TIER5_SRC_ROOT, "build-lib.mjs")).href);
25204
- await lib.buildAppEntry(entry, outfile);
25487
+ await lib.buildAppEntry(entry, outfile, { confineTo: current.dir });
25205
25488
  const mod = await import(pathToFileURL6(outfile).href);
25206
25489
  const next = mod.default;
25207
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) {
@@ -25286,7 +25569,7 @@ function startAppSourceWatcher(state) {
25286
25569
  try {
25287
25570
  const watcher = watch(appDir2, { persistent: false, recursive: true }, (_event, filename) => {
25288
25571
  if (!filename) return;
25289
- const rel = String(filename).split(sep5).join("/");
25572
+ const rel = String(filename).split(sep6).join("/");
25290
25573
  if (!classifyAppPath(rel)) return;
25291
25574
  pending.add(rel);
25292
25575
  if (timer) clearTimeout(timer);
@@ -27448,7 +27731,7 @@ __export(build_exports2, {
27448
27731
  });
27449
27732
  import { createHash as createHash18 } from "node:crypto";
27450
27733
  import { readFileSync as readFileSync40, readdirSync as readdirSync18, existsSync as existsSync34 } from "node:fs";
27451
- 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";
27452
27735
  import { fileURLToPath as fileURLToPath18 } from "node:url";
27453
27736
  import { createRequire as createRequire2 } from "node:module";
27454
27737
  import { execFileSync as execFileSync4 } from "node:child_process";
@@ -27486,7 +27769,7 @@ function l10nLintOptions(locale, appDir2) {
27486
27769
  const tags = (locale.translations ?? []).filter((t) => t !== locale.default);
27487
27770
  const catalog = /* @__PURE__ */ new Set();
27488
27771
  for (const tag of tags) {
27489
- const path = join33(appDir2, "l10n", `${tag}.json`);
27772
+ const path = join34(appDir2, "l10n", `${tag}.json`);
27490
27773
  if (!existsSync34(path)) continue;
27491
27774
  try {
27492
27775
  const parsed = JSON.parse(readFileSync40(path, "utf8"));
@@ -27583,7 +27866,7 @@ function shellSha(root = REPO_ROOT7) {
27583
27866
  function isMonorepoCheckout(dir) {
27584
27867
  try {
27585
27868
  const top = execFileSync4("git", ["rev-parse", "--show-toplevel"], { cwd: dir, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
27586
- return top.length > 0 && existsSync34(join33(top, "core", "schema", "core.proto"));
27869
+ return top.length > 0 && existsSync34(join34(top, "core", "schema", "core.proto"));
27587
27870
  } catch {
27588
27871
  return false;
27589
27872
  }
@@ -27813,7 +28096,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
27813
28096
  if (addrCache.has(name)) return addrCache.get(name);
27814
28097
  let bytes2;
27815
28098
  try {
27816
- bytes2 = readFileSync40(join33(tier5Dir, `${name}.bundle.js`));
28099
+ bytes2 = readFileSync40(join34(tier5Dir, `${name}.bundle.js`));
27817
28100
  } catch {
27818
28101
  return null;
27819
28102
  }
@@ -27834,7 +28117,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
27834
28117
  const MAX_AUTHORING_DOC_BYTES = 128 * 1024;
27835
28118
  if (store.putDoc) {
27836
28119
  for (const [name, file] of [["spec", "SPEC.md"], ["mobile-ux", "MOBILE-UX.md"]]) {
27837
- const path = join33(preloaded.dir, file);
28120
+ const path = join34(preloaded.dir, file);
27838
28121
  if (!existsSync34(path)) continue;
27839
28122
  const bytes2 = readFileSync40(path);
27840
28123
  if (bytes2.length === 0 || bytes2.length > MAX_AUTHORING_DOC_BYTES) {
@@ -27894,7 +28177,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
27894
28177
  const canEmitFonts = hasVendoredFloor();
27895
28178
  try {
27896
28179
  const appTokens = readTokensForLint(preloaded) ?? {};
27897
- 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")) : {};
27898
28181
  fontPlan = planFonts(appTokens, policy);
27899
28182
  } catch (err) {
27900
28183
  throw new Error(
@@ -28359,7 +28642,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
28359
28642
  await store.reset(app);
28360
28643
  const sources = {};
28361
28644
  for (const definition of config.functions.functions) {
28362
- 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}`));
28363
28646
  const source = candidates.find((candidate) => existsSync34(candidate));
28364
28647
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
28365
28648
  sources[definition.name] = readFileSync40(source, "utf8");
@@ -28390,7 +28673,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
28390
28673
  return { manifest, failedToBuild: [], missingBundles: [], auth: {} };
28391
28674
  }
28392
28675
  function listBuildableApps() {
28393
- const appsDir = join33(REPO_ROOT7, "apps");
28676
+ const appsDir = join34(REPO_ROOT7, "apps");
28394
28677
  return readdirSync18(appsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("_")).map((d) => d.name);
28395
28678
  }
28396
28679
  var BAKE_OFFLINE_BASE, KNOWN_ICON_NAMES, KNOWN_ICONS, REPO_ROOT7, TIER5_DIR, requireFromHere, SOURCEMAP_ON_DEMAND_PATH, BAKED_SERVER_BASE_URL;
@@ -28421,9 +28704,9 @@ var init_build3 = __esm({
28421
28704
  KNOWN_ICON_NAMES = [...ICON_NAMES].sort();
28422
28705
  KNOWN_ICONS = new Set(KNOWN_ICON_NAMES);
28423
28706
  REPO_ROOT7 = resolve29(dirname22(fileURLToPath18(import.meta.url)), "../..");
28424
- TIER5_DIR = join33(REPO_ROOT7, "core/dev-server/tier5-bundles");
28707
+ TIER5_DIR = join34(REPO_ROOT7, "core/dev-server/tier5-bundles");
28425
28708
  requireFromHere = createRequire2(import.meta.url);
28426
- 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");
28427
28710
  BAKED_SERVER_BASE_URL = "http://baked.himalaya.invalid";
28428
28711
  }
28429
28712
  });
@@ -28470,6 +28753,133 @@ var init_net_scenario = __esm({
28470
28753
  }
28471
28754
  });
28472
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
+
28473
28883
  // ../../core/dev-server/src/screen-crawl.ts
28474
28884
  var screen_crawl_exports = {};
28475
28885
  __export(screen_crawl_exports, {
@@ -28482,6 +28892,7 @@ __export(screen_crawl_exports, {
28482
28892
  crawlScreenDeep: () => crawlScreenDeep,
28483
28893
  crawlScreenHostile: () => crawlScreenHostile,
28484
28894
  crawlScreenJourney: () => crawlScreenJourney,
28895
+ crawlScreenRelations: () => crawlScreenRelations,
28485
28896
  crawlSignature: () => crawlSignature,
28486
28897
  deepStateSignature: () => deepStateSignature,
28487
28898
  emptiedArrays: () => emptiedArrays,
@@ -28492,6 +28903,7 @@ __export(screen_crawl_exports, {
28492
28903
  parseWorkerAction: () => parseWorkerAction,
28493
28904
  recordScreenTraffic: () => recordScreenTraffic,
28494
28905
  rootOnAppear: () => rootOnAppear,
28906
+ snapshot: () => snapshot,
28495
28907
  structuralSignature: () => structuralSignature,
28496
28908
  unverifiableNodes: () => unverifiableNodes,
28497
28909
  workerArgs: () => workerArgs
@@ -29727,7 +30139,209 @@ async function crawlScreenDeep(opts, deep) {
29727
30139
  return { findings: dedupe(findings), nodesVisited: visited.size, dispatches, capped };
29728
30140
  });
29729
30141
  }
29730
- 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;
29731
30345
  var init_screen_crawl = __esm({
29732
30346
  "../../core/dev-server/src/screen-crawl.ts"() {
29733
30347
  init_validate_descriptor_core();
@@ -29737,6 +30351,7 @@ var init_screen_crawl = __esm({
29737
30351
  init_FakeServiceProvider();
29738
30352
  init_actionParams();
29739
30353
  init_implications();
30354
+ init_crawl_relations();
29740
30355
  ACTION_KEYS = [
29741
30356
  "action",
29742
30357
  "onTap",
@@ -29760,6 +30375,7 @@ var init_screen_crawl = __esm({
29760
30375
  Marquee: ["items", "itemsPath", "text"],
29761
30376
  MediaPlayer: ["items", "itemsPath"]
29762
30377
  };
30378
+ UNBINDABLE_ROW = Symbol("unbindable-row");
29763
30379
  }
29764
30380
  });
29765
30381
 
@@ -30315,8 +30931,8 @@ __export(pack_exports, {
30315
30931
  });
30316
30932
  import { createHash as createHash19 } from "node:crypto";
30317
30933
  import { deflateRawSync, inflateRawSync } from "node:zlib";
30318
- 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";
30319
- 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";
30320
30936
  function writeZip(entries) {
30321
30937
  const sorted = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
30322
30938
  const total = sorted.reduce((n, e) => n + e.bytes.length, 0);
@@ -30410,13 +31026,13 @@ function inflateEntry(buf, entry) {
30410
31026
  return bytes2;
30411
31027
  }
30412
31028
  function readIf(path) {
30413
- return existsSync35(path) && statSync7(path).isFile() ? readFileSync42(path) : null;
31029
+ return existsSync35(path) && statSync8(path).isFile() ? readFileSync42(path) : null;
30414
31030
  }
30415
31031
  function listDir(dir) {
30416
- 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()) : [];
30417
31033
  }
30418
31034
  function gatherReleaseEntries(appDir2) {
30419
- const manifestPath = join34(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
31035
+ const manifestPath = join35(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
30420
31036
  const manifestBytes = readIf(manifestPath);
30421
31037
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
30422
31038
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -30440,7 +31056,7 @@ function gatherReleaseEntries(appDir2) {
30440
31056
  }
30441
31057
  const missing = [];
30442
31058
  for (const rel of wanted) {
30443
- const bytes2 = readIf(join34(appDir2, rel));
31059
+ const bytes2 = readIf(join35(appDir2, rel));
30444
31060
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
30445
31061
  else missing.push(rel);
30446
31062
  }
@@ -30451,12 +31067,12 @@ function gatherReleaseEntries(appDir2) {
30451
31067
  );
30452
31068
  }
30453
31069
  for (const name of SINGLETONS) {
30454
- const bytes2 = readIf(join34(appDir2, name));
31070
+ const bytes2 = readIf(join35(appDir2, name));
30455
31071
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
30456
31072
  }
30457
31073
  const skipped = [];
30458
31074
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
30459
- for (const f of listDir(join34(appDir2, sub))) {
31075
+ for (const f of listDir(join35(appDir2, sub))) {
30460
31076
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
30461
31077
  }
30462
31078
  }
@@ -30487,18 +31103,18 @@ function buildEntries(appDir2, app, opts) {
30487
31103
  return { all, header, manifest, skipped };
30488
31104
  }
30489
31105
  function packRelease(releaseRoot, app, out, opts = {}) {
30490
- const appDir2 = join34(resolve31(releaseRoot), app);
31106
+ const appDir2 = join35(resolve31(releaseRoot), app);
30491
31107
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
30492
31108
  const buf = writeZip(all);
30493
- const isDir = existsSync35(out) && statSync7(out).isDirectory();
30494
- 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);
30495
31111
  mkdirSync17(dirname23(outFile), { recursive: true });
30496
31112
  writeFileSync13(outFile, buf);
30497
31113
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
30498
31114
  }
30499
31115
  function resolvePackageDirDest(outDir, app, releaseId) {
30500
31116
  const packagedApp = (dir) => {
30501
- const bytes2 = readIf(join34(dir, HIMI_PACKAGE_HEADER_PATH));
31117
+ const bytes2 = readIf(join35(dir, HIMI_PACKAGE_HEADER_PATH));
30502
31118
  if (!bytes2) return void 0;
30503
31119
  try {
30504
31120
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -30508,14 +31124,14 @@ function resolvePackageDirDest(outDir, app, releaseId) {
30508
31124
  };
30509
31125
  const replaceable = (dir) => {
30510
31126
  if (!existsSync35(dir)) return true;
30511
- if (!statSync7(dir).isDirectory()) return false;
31127
+ if (!statSync8(dir).isDirectory()) return false;
30512
31128
  return readdirSync19(dir).length === 0 || packagedApp(dir) === app;
30513
31129
  };
30514
- if (existsSync35(outDir) && !statSync7(outDir).isDirectory()) {
31130
+ if (existsSync35(outDir) && !statSync8(outDir).isDirectory()) {
30515
31131
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
30516
31132
  }
30517
31133
  if (replaceable(outDir)) return outDir;
30518
- const child = join34(outDir, packageDirName(app, releaseId));
31134
+ const child = join35(outDir, packageDirName(app, releaseId));
30519
31135
  if (!replaceable(child)) {
30520
31136
  throw new Error(
30521
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.`
@@ -30524,13 +31140,13 @@ function resolvePackageDirDest(outDir, app, releaseId) {
30524
31140
  return child;
30525
31141
  }
30526
31142
  function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
30527
- const appDir2 = join34(resolve31(releaseRoot), app);
31143
+ const appDir2 = join35(resolve31(releaseRoot), app);
30528
31144
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
30529
31145
  const dest = resolvePackageDirDest(resolve31(outDir), app, header.releaseId);
30530
31146
  rmSync5(dest, { recursive: true, force: true });
30531
31147
  let bytes2 = 0;
30532
31148
  for (const entry of all) {
30533
- const target = join34(dest, entry.path);
31149
+ const target = join35(dest, entry.path);
30534
31150
  mkdirSync17(dirname23(target), { recursive: true });
30535
31151
  writeFileSync13(target, entry.bytes);
30536
31152
  bytes2 += entry.bytes.length;
@@ -30559,11 +31175,11 @@ function openPackageDir(dir) {
30559
31175
  const root = resolve31(dir);
30560
31176
  const entries = /* @__PURE__ */ new Map();
30561
31177
  const walk2 = (rel) => {
30562
- const abs = rel ? join34(root, rel) : root;
31178
+ const abs = rel ? join35(root, rel) : root;
30563
31179
  for (const name of readdirSync19(abs)) {
30564
31180
  const childRel = rel ? `${rel}/${name}` : name;
30565
- const childAbs = join34(abs, name);
30566
- if (statSync7(childAbs).isDirectory()) walk2(childRel);
31181
+ const childAbs = join35(abs, name);
31182
+ if (statSync8(childAbs).isDirectory()) walk2(childRel);
30567
31183
  else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync42(childAbs));
30568
31184
  }
30569
31185
  };
@@ -30601,10 +31217,10 @@ function unpackTo(buf, dest, appFallback) {
30601
31217
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
30602
31218
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
30603
31219
  const root = resolve31(dest);
30604
- const appDir2 = join34(root, app);
31220
+ const appDir2 = join35(root, app);
30605
31221
  rmSync5(appDir2, { recursive: true, force: true });
30606
31222
  for (const [rel, bytes2] of entries) {
30607
- const target = join34(appDir2, rel);
31223
+ const target = join35(appDir2, rel);
30608
31224
  mkdirSync17(dirname23(target), { recursive: true });
30609
31225
  writeFileSync13(target, bytes2);
30610
31226
  }
@@ -30636,14 +31252,14 @@ __export(app_source_exports, {
30636
31252
  zipAppSource: () => zipAppSource
30637
31253
  });
30638
31254
  import { readdirSync as readdirSync20, lstatSync as lstatSync4, readFileSync as readFileSync43, mkdirSync as mkdirSync18, writeFileSync as writeFileSync14 } from "node:fs";
30639
- 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";
30640
31256
  function collectAppSource(dir) {
30641
31257
  const entries = [];
30642
31258
  const skipped = [];
30643
31259
  const walk2 = (relDir) => {
30644
- for (const name of readdirSync20(join35(dir, relDir))) {
30645
- const rel = relDir ? join35(relDir, name) : name;
30646
- 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));
30647
31263
  if (st.isSymbolicLink()) {
30648
31264
  skipped.push(rel);
30649
31265
  continue;
@@ -30657,7 +31273,7 @@ function collectAppSource(dir) {
30657
31273
  skipped.push(rel);
30658
31274
  continue;
30659
31275
  }
30660
- 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)) });
30661
31277
  }
30662
31278
  };
30663
31279
  walk2("");
@@ -30683,7 +31299,7 @@ function readAppSource(zip) {
30683
31299
  function writeAppSource(entries, destDir) {
30684
31300
  const written = [];
30685
31301
  for (const entry of entries) {
30686
- const target = join35(destDir, ...entry.path.split("/"));
31302
+ const target = join36(destDir, ...entry.path.split("/"));
30687
31303
  mkdirSync18(dirname24(target), { recursive: true });
30688
31304
  writeFileSync14(target, entry.bytes);
30689
31305
  written.push(entry.path);
@@ -30714,7 +31330,7 @@ __export(eject_exports, {
30714
31330
  ejectModule: () => ejectModule
30715
31331
  });
30716
31332
  import { existsSync as existsSync36, mkdirSync as mkdirSync19, readFileSync as readFileSync44, writeFileSync as writeFileSync15 } from "node:fs";
30717
- 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";
30718
31334
  function locate(repoRootOrCwd, mod) {
30719
31335
  const inRepo = resolve32(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
30720
31336
  if (existsSync36(inRepo)) return { path: inRepo, form: "source" };
@@ -30743,7 +31359,7 @@ function ejectModule(opts) {
30743
31359
  };
30744
31360
  }
30745
31361
  const ext = found.form === "source" ? "ts" : "mjs";
30746
- const target = join36(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
31362
+ const target = join37(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
30747
31363
  mkdirSync19(dirname25(target), { recursive: true });
30748
31364
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
30749
31365
  //
@@ -30767,7 +31383,7 @@ function ejectModule(opts) {
30767
31383
  if (!existsSync36(f)) continue;
30768
31384
  const before = readFileSync44(f, "utf8");
30769
31385
  let after = before;
30770
- 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");
30771
31387
  if (!rel.startsWith(".")) rel = `./${rel}`;
30772
31388
  for (const spec of specifiersFor(mod)) {
30773
31389
  const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -30814,7 +31430,7 @@ __export(test_exports, {
30814
31430
  writeRecordedMocks: () => writeRecordedMocks
30815
31431
  });
30816
31432
  import { existsSync as existsSync37, mkdirSync as mkdirSync20, readFileSync as readFileSync45, writeFileSync as writeFileSync16 } from "node:fs";
30817
- import { join as join37, resolve as resolve33 } from "node:path";
31433
+ import { join as join38, resolve as resolve33 } from "node:path";
30818
31434
  function readTestConfig(dir) {
30819
31435
  const p = resolve33(dir, TEST_CONFIG_FILE);
30820
31436
  if (!existsSync37(p)) return {};
@@ -30833,8 +31449,8 @@ function readTestConfig(dir) {
30833
31449
  if (typeof s.reason !== "string" || !s.reason.trim()) {
30834
31450
  throw new Error(`${TEST_CONFIG_FILE}: skip[${i}] needs a non-empty "reason" \u2014 suppressions have to stay auditable`);
30835
31451
  }
30836
- if (!s.screen && !s.action && !s.component) {
30837
- 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"`);
30838
31454
  }
30839
31455
  });
30840
31456
  }
@@ -30992,8 +31608,8 @@ function writeRecordedMocks(opts) {
30992
31608
  mkdir: (p) => mkdirSync20(p, { recursive: true }),
30993
31609
  write: (p, s) => writeFileSync16(p, s)
30994
31610
  };
30995
- fs.mkdir(join37(opts.dir, "dev"));
30996
- 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");
30997
31613
  return { ok: true, rules, to: "dev/net-mocks.json" };
30998
31614
  }
30999
31615
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -31059,12 +31675,15 @@ async function runCrawl(opts) {
31059
31675
  const net = opts.offline ? null : opts.netPath ? { rules: readNetRules(resolve33(opts.netPath)), from: opts.netPath } : defaultNetRules(opts.dir);
31060
31676
  const suppressions = opts.config?.skip ?? [];
31061
31677
  const suppressed = [];
31062
- 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));
31063
31679
  const results = [];
31064
31680
  const uncoveredByScreen = /* @__PURE__ */ new Map();
31065
31681
  const coverageFns = /* @__PURE__ */ new Map();
31066
31682
  const bundleOf = new Map(descriptors.map((d) => [d.screenId, d]));
31067
31683
  const hostileFindings = [];
31684
+ const relationFindings = [];
31685
+ let relationsChecked = 0;
31686
+ const relationsByKind = { inverse: 0, refresh: 0, loadMore: 0, filter: 0 };
31068
31687
  const deepFindings = [];
31069
31688
  const journeyFindings = [];
31070
31689
  const journeyResults = [];
@@ -31134,14 +31753,22 @@ async function runCrawl(opts) {
31134
31753
  hostileFindings.push(...hs.filter((f) => !already.has(keyOf(f))));
31135
31754
  }
31136
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
+ }
31137
31764
  await finishScreenCoverage();
31138
31765
  }
31139
31766
  const keep = (f) => {
31140
31767
  const actionId = typeof f.trigger === "object" ? f.trigger.action : "boot";
31141
31768
  const componentId = typeof f.trigger === "object" ? f.trigger.tap : null;
31142
- const s = matchSuppression(f.screen, actionId, componentId);
31143
- if (s && !suppressed.some((x) => x.screen === f.screen && x.action === actionId && x.component === (componentId ?? void 0))) {
31144
- 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 });
31145
31772
  }
31146
31773
  return !s;
31147
31774
  };
@@ -31222,7 +31849,7 @@ async function runCrawl(opts) {
31222
31849
  if (uncovered.length) uncoveredByScreen.set(screen, uncovered);
31223
31850
  }
31224
31851
  }
31225
- 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) => {
31226
31853
  if (f.invariant === "no-request-issued" && !f.axis && fetchProven.has(f.screen)) {
31227
31854
  const by = fetchProven.get(f.screen);
31228
31855
  if (!retired.some((x) => x.screen === f.screen)) retired.push({ screen: f.screen, from: by.from, params: by.params });
@@ -31242,7 +31869,15 @@ async function runCrawl(opts) {
31242
31869
  ...blockedReasons.length ? { blockedReasons } : {},
31243
31870
  app: opts.app,
31244
31871
  releaseId: opts.releaseId,
31245
- 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
+ }),
31246
31881
  screens: { total: targets.length, clean: targets.length - failedScreens.size, failed: failedScreens.size },
31247
31882
  actions: { exercised: results.reduce((n, r) => n + r.actionsExercised, 0) },
31248
31883
  counts: { errors: failures.length, warnings: warnings.length },
@@ -31265,22 +31900,22 @@ async function runCrawl(opts) {
31265
31900
  ...edgesCapped.length ? { edgesCapped } : {},
31266
31901
  ...retired.length ? { retired } : {},
31267
31902
  ...opts.deep && opts.deep >= 2 ? { deep: { depth: opts.deep, ...deepTotals } } : {},
31903
+ ...deps.crawlScreenRelations ? { relations: { checked: relationsChecked, byKind: relationsByKind } } : {},
31268
31904
  ...journeyResults.length ? { journeys: journeyResults } : {},
31269
31905
  ...results.some((r) => r.observations) ? { observations: Object.fromEntries(results.filter((r) => r.observations).map((r) => [r.screen, r.observations])) } : {}
31270
31906
  };
31271
31907
  }
31272
- function describeTransport(net, opts) {
31908
+ function describeTransport(net, opts, ran) {
31273
31909
  const layers = [];
31274
31910
  if (net) layers.push(`mocked (${net.from})`);
31275
31911
  const cmsCount = Object.keys(opts.cmsSeed ?? {}).length;
31276
31912
  if (cmsCount) layers.push(`cms (${cmsCount} seeded collection${cmsCount === 1 ? "" : "s"})`);
31277
31913
  if (opts.canned?.count) layers.push(`canned (${opts.canned.count} frozen endpoints, ${opts.canned.from} \u2014 exercises code paths, not API-contract truth)`);
31278
- if (!layers.length) {
31279
- return "offline (no net fixture \u2014 screens are crawled against a rejecting transport, which exercises their failure UI)";
31280
- }
31281
- const hostile = opts.hostile !== false ? " + hostile boot passes (empty-lists, all-500)" : "";
31282
- const deep = opts.deep && opts.deep >= 2 ? ` + deep chains (depth ${opts.deep})` : "";
31283
- 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;
31284
31919
  }
31285
31920
  function formatReport(r) {
31286
31921
  const out = [];
@@ -31415,9 +32050,9 @@ __export(mobile_ux_lint_exports, {
31415
32050
  mobileUxIssues: () => mobileUxIssues
31416
32051
  });
31417
32052
  import { existsSync as existsSync38, readFileSync as readFileSync46 } from "node:fs";
31418
- import { join as join38 } from "node:path";
32053
+ import { join as join39 } from "node:path";
31419
32054
  function mobileUxIssues(dir, strict) {
31420
- const file = join38(dir, "MOBILE-UX.md");
32055
+ const file = join39(dir, "MOBILE-UX.md");
31421
32056
  if (!existsSync38(file)) return [];
31422
32057
  let text2;
31423
32058
  try {
@@ -33081,7 +33716,7 @@ __export(functions_exports, {
33081
33716
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
33082
33717
  });
33083
33718
  import { readFileSync as readFileSync47, existsSync as existsSync39 } from "node:fs";
33084
- import { join as join39, resolve as resolve34 } from "node:path";
33719
+ import { join as join40, resolve as resolve34 } from "node:path";
33085
33720
  function resolveFunctionsTarget(input) {
33086
33721
  const explicitUrl = input.functionsUrl?.trim();
33087
33722
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -33100,7 +33735,7 @@ async function loadFunctionsApp(dir) {
33100
33735
  }
33101
33736
  const sources = {};
33102
33737
  for (const definition of config.functions.functions) {
33103
- 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);
33104
33739
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
33105
33740
  sources[definition.name] = readFileSync47(source, "utf8");
33106
33741
  }
@@ -33258,7 +33893,7 @@ __export(native_decode_exports, {
33258
33893
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
33259
33894
  import { existsSync as existsSync40, mkdtempSync as mkdtempSync2, readFileSync as readFileSync48, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
33260
33895
  import { tmpdir as tmpdir3 } from "node:os";
33261
- import { join as join40 } from "node:path";
33896
+ import { join as join41 } from "node:path";
33262
33897
  import { fileURLToPath as fileURLToPath19 } from "node:url";
33263
33898
  function nativeDecodePackageDir() {
33264
33899
  return fileURLToPath19(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -33274,7 +33909,7 @@ function detectNativeDecode(options = {}) {
33274
33909
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
33275
33910
  }
33276
33911
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
33277
- const packageAvailable = options.packageAvailable ?? existsSync40(join40(packageDir, "Package.swift"));
33912
+ const packageAvailable = options.packageAvailable ?? existsSync40(join41(packageDir, "Package.swift"));
33278
33913
  if (!packageAvailable) {
33279
33914
  return {
33280
33915
  platform: "ios",
@@ -33286,7 +33921,7 @@ function detectNativeDecode(options = {}) {
33286
33921
  }
33287
33922
  function localPropertiesSdk(harnessDir) {
33288
33923
  try {
33289
- 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);
33290
33925
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
33291
33926
  } catch {
33292
33927
  return null;
@@ -33294,7 +33929,7 @@ function localPropertiesSdk(harnessDir) {
33294
33929
  }
33295
33930
  function detectAndroidNativeDecode(options = {}) {
33296
33931
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
33297
- const gradlew = join40(harnessDir, "gradlew");
33932
+ const gradlew = join41(harnessDir, "gradlew");
33298
33933
  const gradlewAvailable = options.gradlewAvailable ?? existsSync40(gradlew);
33299
33934
  if (!gradlewAvailable) {
33300
33935
  return {
@@ -33377,14 +34012,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
33377
34012
  return verdicts;
33378
34013
  }
33379
34014
  function runNativeDecode(options) {
33380
- const descriptorDir = mkdtempSync2(join40(tmpdir3(), "himi-native-decode-"));
34015
+ const descriptorDir = mkdtempSync2(join41(tmpdir3(), "himi-native-decode-"));
33381
34016
  const startedAt = Date.now();
33382
34017
  try {
33383
34018
  for (const descriptor of options.descriptors) {
33384
34019
  if (typeof descriptor.descriptorJson !== "string") {
33385
34020
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
33386
34021
  }
33387
- writeFileSync17(join40(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
34022
+ writeFileSync17(join41(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
33388
34023
  }
33389
34024
  options.progress?.("building cached Swift oracle");
33390
34025
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -33396,7 +34031,7 @@ function runNativeDecode(options) {
33396
34031
  encoding: "utf8"
33397
34032
  }).trim();
33398
34033
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
33399
- const stdout = execFileSync5(join40(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
34034
+ const stdout = execFileSync5(join41(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
33400
34035
  stdio: ["ignore", "pipe", "pipe"],
33401
34036
  encoding: "utf8"
33402
34037
  });
@@ -33414,15 +34049,15 @@ function runNativeDecode(options) {
33414
34049
  }
33415
34050
  }
33416
34051
  function runAndroidNativeDecode(options) {
33417
- const descriptorDir = mkdtempSync2(join40(tmpdir3(), "himi-native-decode-android-"));
33418
- const outputPath = join40(descriptorDir, "verdicts.ndjson");
34052
+ const descriptorDir = mkdtempSync2(join41(tmpdir3(), "himi-native-decode-android-"));
34053
+ const outputPath = join41(descriptorDir, "verdicts.ndjson");
33419
34054
  const startedAt = Date.now();
33420
34055
  try {
33421
34056
  for (const descriptor of options.descriptors) {
33422
34057
  if (typeof descriptor.descriptorJson !== "string") {
33423
34058
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
33424
34059
  }
33425
- writeFileSync17(join40(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
34060
+ writeFileSync17(join41(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
33426
34061
  }
33427
34062
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
33428
34063
  execFileSync5(options.gradlew, [
@@ -33508,23 +34143,23 @@ __export(browser_authoring_exports, {
33508
34143
  import { createServer as createServer3 } from "node:http";
33509
34144
  import { createHash as createHash22, randomBytes as randomBytes5 } from "node:crypto";
33510
34145
  import { execFile as execFile2 } from "node:child_process";
33511
- import { existsSync as existsSync41, readdirSync as readdirSync21, readFileSync as readFileSync49, realpathSync as realpathSync3, statSync as statSync8 } from "node:fs";
33512
- 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";
33513
34148
  function humanMs(ms) {
33514
34149
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
33515
34150
  }
33516
34151
  function collectWorkerSources(tier5SrcDir, bundleName) {
33517
- const root = join41(tier5SrcDir, bundleName);
34152
+ const root = join42(tier5SrcDir, bundleName);
33518
34153
  const files = {};
33519
34154
  const visitedDirs = /* @__PURE__ */ new Set();
33520
34155
  const walk2 = (abs, rel) => {
33521
- const real = realpathSync3(abs);
34156
+ const real = realpathSync4(abs);
33522
34157
  if (visitedDirs.has(real)) return;
33523
34158
  visitedDirs.add(real);
33524
34159
  for (const name of readdirSync21(abs).sort()) {
33525
- const childAbs = join41(abs, name);
34160
+ const childAbs = join42(abs, name);
33526
34161
  const childRel = posix.join(rel, name);
33527
- if (statSync8(childAbs).isDirectory()) {
34162
+ if (statSync9(childAbs).isDirectory()) {
33528
34163
  walk2(childAbs, childRel);
33529
34164
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
33530
34165
  files[childRel] = readFileSync49(childAbs, "utf8");
@@ -33548,18 +34183,18 @@ function collectRelativeSiblings(files, tier5SrcDir) {
33548
34183
  const spec = m[1];
33549
34184
  const virtual = posix.normalize(posix.join(dir, spec));
33550
34185
  if (!virtual.startsWith("/tier5-src/")) continue;
33551
- const onDisk = join41(tier5SrcDir, virtual.slice("/tier5-src/".length));
34186
+ const onDisk = join42(tier5SrcDir, virtual.slice("/tier5-src/".length));
33552
34187
  const candidates = [
33553
34188
  onDisk,
33554
34189
  `${onDisk}.ts`,
33555
34190
  `${onDisk}.js`,
33556
34191
  onDisk.replace(/\.js$/, ".ts"),
33557
- join41(onDisk, "index.ts"),
33558
- join41(onDisk, "index.js")
34192
+ join42(onDisk, "index.ts"),
34193
+ join42(onDisk, "index.js")
33559
34194
  ];
33560
34195
  for (const abs of candidates) {
33561
- if (!existsSync41(abs) || statSync8(abs).isDirectory()) continue;
33562
- 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)}`;
33563
34198
  if (key2 in files) break;
33564
34199
  files[key2] = readFileSync49(abs, "utf8");
33565
34200
  queue.push(key2);
@@ -33770,7 +34405,7 @@ __export(coverage_exports, {
33770
34405
  trendAndStore: () => trendAndStore
33771
34406
  });
33772
34407
  import { existsSync as existsSync42, mkdirSync as mkdirSync21, readFileSync as readFileSync50, writeFileSync as writeFileSync18 } from "node:fs";
33773
- import { dirname as dirname26, join as join42 } from "node:path";
34408
+ import { dirname as dirname26, join as join43 } from "node:path";
33774
34409
  function assembleLedger(r) {
33775
34410
  const out = [];
33776
34411
  for (const c of r.crawled ?? []) {
@@ -33936,7 +34571,7 @@ function assembleLedger(r) {
33936
34571
  return out;
33937
34572
  }
33938
34573
  function trendAndStore(dir, entries) {
33939
- const p = join42(dir, LEDGER_FILE);
34574
+ const p = join43(dir, LEDGER_FILE);
33940
34575
  let previous = null;
33941
34576
  try {
33942
34577
  if (existsSync42(p)) {
@@ -33977,7 +34612,7 @@ function formatLedger(entries, trend) {
33977
34612
  return out.join("\n");
33978
34613
  }
33979
34614
  function diffAndStoreObservations(dir, current) {
33980
- const p = join42(dir, OBS_FILE);
34615
+ const p = join43(dir, OBS_FILE);
33981
34616
  let previous = null;
33982
34617
  try {
33983
34618
  if (existsSync42(p)) {
@@ -34029,8 +34664,8 @@ function formatDelta(delta) {
34029
34664
  var LEDGER_FILE, OBS_FILE;
34030
34665
  var init_coverage = __esm({
34031
34666
  "src/coverage.ts"() {
34032
- LEDGER_FILE = join42(".himi", "test-ledger.json");
34033
- OBS_FILE = join42(".himi", "test-observations.json");
34667
+ LEDGER_FILE = join43(".himi", "test-ledger.json");
34668
+ OBS_FILE = join43(".himi", "test-observations.json");
34034
34669
  }
34035
34670
  });
34036
34671
 
@@ -35234,9 +35869,9 @@ __export(run_exports, {
35234
35869
  runPackage: () => runPackage,
35235
35870
  withoutProdServe: () => withoutProdServe
35236
35871
  });
35237
- 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";
35238
35873
  import { tmpdir as tmpdir4 } from "node:os";
35239
- import { join as join43 } from "node:path";
35874
+ import { join as join44 } from "node:path";
35240
35875
  import { spawn as spawn3 } from "node:child_process";
35241
35876
  function makePackageRunHandler(opts) {
35242
35877
  return makeRunHandler({
@@ -35254,8 +35889,8 @@ async function fetchBytes(url) {
35254
35889
  }
35255
35890
  async function resolvePackage(source, workDir) {
35256
35891
  const isUrl2 = /^https?:\/\//i.test(source);
35257
- if (!isUrl2 && existsSync43(source) && statSync9(source).isDirectory()) {
35258
- const appDirManifest = join43(source, "release-manifest.json");
35892
+ if (!isUrl2 && existsSync43(source) && statSync10(source).isDirectory()) {
35893
+ const appDirManifest = join44(source, "release-manifest.json");
35259
35894
  if (existsSync43(appDirManifest)) {
35260
35895
  const opened2 = openPackageDir(source);
35261
35896
  if (!opened2.validation.ok) {
@@ -35265,9 +35900,9 @@ async function resolvePackage(source, workDir) {
35265
35900
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
35266
35901
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
35267
35902
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
35268
- const root2 = workDir ?? mkdtempSync3(join43(tmpdir4(), "himi-run-"));
35269
- const target = join43(root2, app2);
35270
- if (join43(source) !== target) {
35903
+ const root2 = workDir ?? mkdtempSync3(join44(tmpdir4(), "himi-run-"));
35904
+ const target = join44(root2, app2);
35905
+ if (join44(source) !== target) {
35271
35906
  mkdirSync22(root2, { recursive: true });
35272
35907
  rmSync7(target, { recursive: true, force: true });
35273
35908
  cpSync(source, target, { recursive: true });
@@ -35285,7 +35920,7 @@ async function resolvePackage(source, workDir) {
35285
35920
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
35286
35921
  }
35287
35922
  const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync52(source);
35288
- const root = workDir ?? mkdtempSync3(join43(tmpdir4(), "himi-run-"));
35923
+ const root = workDir ?? mkdtempSync3(join44(tmpdir4(), "himi-run-"));
35289
35924
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
35290
35925
  const opened = openPackageDir(appDir2);
35291
35926
  if (!opened.validation.ok) {
@@ -35994,9 +36629,9 @@ __export(preflight_app_exports, {
35994
36629
  pngSize: () => pngSize,
35995
36630
  preflightApp: () => preflightApp
35996
36631
  });
35997
- 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";
35998
36633
  import { createHash as createHash23 } from "node:crypto";
35999
- import { join as join44 } from "node:path";
36634
+ import { join as join45 } from "node:path";
36000
36635
  function analyze(projectYml, storeConfig, icon) {
36001
36636
  const f = [];
36002
36637
  const has = (re, s) => re.test(s);
@@ -36047,7 +36682,7 @@ async function preflightApp(app) {
36047
36682
  const projectYml = existsSync46(iosProjectYml(app)) ? readFileSync55(iosProjectYml(app), "utf8") : "";
36048
36683
  const storeConfig = existsSync46(storeConfigPath(app)) ? readFileSync55(storeConfigPath(app), "utf8") : "";
36049
36684
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
36050
- const findings = analyze(projectYml, storeConfig, await appIconState(join44(appDir(app), "ios")));
36685
+ const findings = analyze(projectYml, storeConfig, await appIconState(join45(appDir(app), "ios")));
36051
36686
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
36052
36687
  findings.push(
36053
36688
  ...analyzePlayListing(playListingAssets(app)).map((finding) => ({ ...finding, level: "warn" }))
@@ -36074,16 +36709,16 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
36074
36709
  return f;
36075
36710
  }
36076
36711
  function appLevelMetadataFiles(app, locale = "en-US") {
36077
- const root = join44(storeDir(app), "metadata");
36712
+ const root = join45(storeDir(app), "metadata");
36078
36713
  const out = /* @__PURE__ */ new Set();
36079
36714
  if (existsSync46(root)) {
36080
36715
  for (const n of readdirSync22(root)) if (n.endsWith(".txt")) out.add(n);
36081
36716
  }
36082
- 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");
36083
36718
  return out;
36084
36719
  }
36085
36720
  function reviewInfoFiles(app) {
36086
- const dir = join44(storeDir(app), "review_information");
36721
+ const dir = join45(storeDir(app), "review_information");
36087
36722
  if (!existsSync46(dir)) return /* @__PURE__ */ new Set();
36088
36723
  return new Set(readdirSync22(dir).filter((n) => n.endsWith(".txt")));
36089
36724
  }
@@ -36194,12 +36829,12 @@ function analyzePlayListing(assets) {
36194
36829
  return f;
36195
36830
  }
36196
36831
  function playListingAssets(app, locale = "en-US") {
36197
- const dir = join44(storeDir(app), "play", locale, "images");
36832
+ const dir = join45(storeDir(app), "play", locale, "images");
36198
36833
  const read = (names) => {
36199
36834
  for (const name of names) {
36200
- const p = join44(dir, name);
36835
+ const p = join45(dir, name);
36201
36836
  try {
36202
- if (statSync10(p).isFile()) return { name, bytes: readFileSync55(p) };
36837
+ if (statSync11(p).isFile()) return { name, bytes: readFileSync55(p) };
36203
36838
  } catch {
36204
36839
  }
36205
36840
  }
@@ -36230,26 +36865,26 @@ function analyzeListing(metaFiles, shots) {
36230
36865
  return f;
36231
36866
  }
36232
36867
  function metadataFilesPresent(app, locale = "en-US") {
36233
- const dir = join44(storeDir(app), "metadata", locale);
36868
+ const dir = join45(storeDir(app), "metadata", locale);
36234
36869
  if (!existsSync46(dir)) return /* @__PURE__ */ new Set();
36235
36870
  return new Set(readdirSync22(dir).filter((n) => n.endsWith(".txt")));
36236
36871
  }
36237
36872
  function listScreenshots(app, locale = "en-US") {
36238
36873
  const out = [];
36239
- const flat = join44(storeDir(app), "screenshots", locale);
36874
+ const flat = join45(storeDir(app), "screenshots", locale);
36240
36875
  if (existsSync46(flat)) {
36241
36876
  for (const png of readdirSync22(flat).filter((n) => n.endsWith(".png"))) {
36242
- const sz = pngSize(readFileSync55(join44(flat, png)));
36877
+ const sz = pngSize(readFileSync55(join45(flat, png)));
36243
36878
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
36244
36879
  }
36245
36880
  }
36246
- const legacy = join44(storeDir(app), "metadata", locale, "screenshots");
36881
+ const legacy = join45(storeDir(app), "metadata", locale, "screenshots");
36247
36882
  if (existsSync46(legacy)) {
36248
36883
  for (const deviceDir of readdirSync22(legacy)) {
36249
- const d = join44(legacy, deviceDir);
36250
- if (!statSync10(d).isDirectory()) continue;
36884
+ const d = join45(legacy, deviceDir);
36885
+ if (!statSync11(d).isDirectory()) continue;
36251
36886
  for (const png of readdirSync22(d).filter((n) => n.endsWith(".png"))) {
36252
- const sz = pngSize(readFileSync55(join44(d, png)));
36887
+ const sz = pngSize(readFileSync55(join45(d, png)));
36253
36888
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
36254
36889
  }
36255
36890
  }
@@ -36257,12 +36892,12 @@ function listScreenshots(app, locale = "en-US") {
36257
36892
  return out;
36258
36893
  }
36259
36894
  function placeholderHashes2() {
36260
- const templateIos = join44(appDir("_template"), "ios");
36895
+ const templateIos = join45(appDir("_template"), "ios");
36261
36896
  if (!existsSync46(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
36262
36897
  for (const target of readdirSync22(templateIos)) {
36263
- 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");
36264
36899
  try {
36265
- if (!statSync10(p).isFile()) continue;
36900
+ if (!statSync11(p).isFile()) continue;
36266
36901
  return [LEGACY_PLACEHOLDER_SHA2562, createHash23("sha256").update(readFileSync55(p)).digest("hex")];
36267
36902
  } catch {
36268
36903
  }
@@ -36296,9 +36931,9 @@ async function pixelIssues(bytes2) {
36296
36931
  async function appIconState(iosDir) {
36297
36932
  if (!existsSync46(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
36298
36933
  for (const target of readdirSync22(iosDir)) {
36299
- 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");
36300
36935
  try {
36301
- if (!statSync10(p).isFile()) continue;
36936
+ if (!statSync11(p).isFile()) continue;
36302
36937
  const bytes2 = readFileSync55(p);
36303
36938
  const hash = createHash23("sha256").update(bytes2).digest("hex");
36304
36939
  const pixels = await pixelIssues(bytes2);
@@ -36371,12 +37006,12 @@ __export(credential_profile_exports, {
36371
37006
  });
36372
37007
  import { existsSync as existsSync47, readFileSync as readFileSync56 } from "node:fs";
36373
37008
  import { homedir as homedir6 } from "node:os";
36374
- import { join as join45 } from "node:path";
37009
+ import { join as join46 } from "node:path";
36375
37010
  function credentialsDir(home = homedir6()) {
36376
- return join45(home, ".himalaya", "store-credentials");
37011
+ return join46(home, ".himalaya", "store-credentials");
36377
37012
  }
36378
37013
  function profilePath(name, home = homedir6()) {
36379
- return join45(credentialsDir(home), `${name}.json`);
37014
+ return join46(credentialsDir(home), `${name}.json`);
36380
37015
  }
36381
37016
  function parseProfile(text2, label2) {
36382
37017
  let doc;
@@ -39889,7 +40524,7 @@ var init_brand_font = __esm({
39889
40524
 
39890
40525
  // src/site-analyze/write-artifacts.ts
39891
40526
  import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync20 } from "node:fs";
39892
- import { join as join46 } from "node:path";
40527
+ import { join as join47 } from "node:path";
39893
40528
  function attachVeloSources(book, sources, put) {
39894
40529
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
39895
40530
  for (const f of book.velo.files) {
@@ -40044,12 +40679,12 @@ async function downloadMedia(book, fetchImpl, put) {
40044
40679
  }
40045
40680
  async function writeBusinessBook(result, opts) {
40046
40681
  const out = opts.outDir;
40047
- mkdirSync23(join46(out, "evidence/pages"), { recursive: true });
40048
- mkdirSync23(join46(out, "media/catalog"), { recursive: true });
40682
+ mkdirSync23(join47(out, "evidence/pages"), { recursive: true });
40683
+ mkdirSync23(join47(out, "media/catalog"), { recursive: true });
40049
40684
  const files = [];
40050
40685
  const put = (rel, body) => {
40051
- const p = join46(out, rel);
40052
- mkdirSync23(join46(p, ".."), { recursive: true });
40686
+ const p = join47(out, rel);
40687
+ mkdirSync23(join47(p, ".."), { recursive: true });
40053
40688
  writeFileSync20(p, body);
40054
40689
  files.push(rel);
40055
40690
  };
@@ -40380,8 +41015,8 @@ var init_home_descriptor = __esm({
40380
41015
  });
40381
41016
 
40382
41017
  // src/site-analyze/apply.ts
40383
- 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";
40384
- 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";
40385
41020
  import { spawnSync as spawnSync4 } from "node:child_process";
40386
41021
  function unpairedRedirectNote(overlay) {
40387
41022
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -40396,7 +41031,7 @@ function committedShellProvisioning(path) {
40396
41031
  function findRepoRoot(cwd) {
40397
41032
  let dir = cwd;
40398
41033
  for (let i = 0; i < 8; i++) {
40399
- 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;
40400
41035
  const next = dirname28(dir);
40401
41036
  if (next === dir) break;
40402
41037
  dir = next;
@@ -40464,7 +41099,7 @@ function mergeCustomerOwned(committed, overlay) {
40464
41099
  }
40465
41100
  function buildMergedContract(options) {
40466
41101
  const { appDir: appDir2, packageDir, overlay } = options;
40467
- const committedPath = join47(appDir2, "provisioning.json");
41102
+ const committedPath = join48(appDir2, "provisioning.json");
40468
41103
  if (!existsSync48(committedPath)) {
40469
41104
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
40470
41105
  }
@@ -40476,23 +41111,23 @@ function buildMergedContract(options) {
40476
41111
  const contained = (root, rel) => {
40477
41112
  if (isAbsolute3(rel)) return void 0;
40478
41113
  const full = resolve37(root, rel);
40479
- return full.startsWith(resolve37(root) + sep9) ? full : void 0;
41114
+ return full.startsWith(resolve37(root) + sep10) ? full : void 0;
40480
41115
  };
40481
41116
  const stage = (fromRel, toRel) => {
40482
41117
  const from = contained(packageDir, fromRel);
40483
- const to = contained(join47(appDir2, STAGED), toRel.slice(STAGED.length + 1));
41118
+ const to = contained(join48(appDir2, STAGED), toRel.slice(STAGED.length + 1));
40484
41119
  if (!from || !to || !existsSync48(from)) return void 0;
40485
41120
  let real;
40486
41121
  let realRoot;
40487
41122
  try {
40488
- real = realpathSync4(from);
40489
- realRoot = realpathSync4(packageDir);
41123
+ real = realpathSync5(from);
41124
+ realRoot = realpathSync5(packageDir);
40490
41125
  } catch {
40491
41126
  return void 0;
40492
41127
  }
40493
- if (!real.startsWith(realRoot + sep9)) return void 0;
41128
+ if (!real.startsWith(realRoot + sep10)) return void 0;
40494
41129
  try {
40495
- if (!statSync11(real).isFile()) return void 0;
41130
+ if (!statSync12(real).isFile()) return void 0;
40496
41131
  mkdirSync24(dirname28(to), { recursive: true });
40497
41132
  copyFileSync3(real, to);
40498
41133
  } catch {
@@ -40503,7 +41138,7 @@ function buildMergedContract(options) {
40503
41138
  };
40504
41139
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
40505
41140
  if (icon) branding.appIconPath = icon;
40506
- const facesPath = join47(packageDir, "media/fonts/faces.json");
41141
+ const facesPath = join48(packageDir, "media/fonts/faces.json");
40507
41142
  if (existsSync48(facesPath)) {
40508
41143
  let faces = [];
40509
41144
  try {
@@ -40528,9 +41163,9 @@ function buildMergedContract(options) {
40528
41163
  merged.branding = branding;
40529
41164
  const body = `${JSON.stringify(merged, null, 2)}
40530
41165
  `;
40531
- const path = join47(packageDir, "provisioning.contract.json");
41166
+ const path = join48(packageDir, "provisioning.contract.json");
40532
41167
  writeFileSync21(path, body);
40533
- const localPath = join47(appDir2, LOCAL_CONTRACT_REL);
41168
+ const localPath = join48(appDir2, LOCAL_CONTRACT_REL);
40534
41169
  mkdirSync24(dirname28(localPath), { recursive: true });
40535
41170
  writeFileSync21(localPath, body);
40536
41171
  staged.push(localPath);
@@ -40551,7 +41186,7 @@ async function applyOverlays(input) {
40551
41186
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
40552
41187
  );
40553
41188
  }
40554
- const appDir2 = join47(repoRoot2, shellAppDir(shell));
41189
+ const appDir2 = join48(repoRoot2, shellAppDir(shell));
40555
41190
  const packageDir = dirname28(resolve37(input.overlayPath));
40556
41191
  const { path: contract, staged } = buildMergedContract({ appDir: appDir2, packageDir, overlay });
40557
41192
  wrote.push(...staged);
@@ -40573,7 +41208,7 @@ async function applyOverlays(input) {
40573
41208
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
40574
41209
  };
40575
41210
  }
40576
- const pkgProv = join47(cwd, "provisioning.json");
41211
+ const pkgProv = join48(cwd, "provisioning.json");
40577
41212
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
40578
41213
  if (existsSync48(pkgProv)) {
40579
41214
  const cur = JSON.parse(readFileSync60(pkgProv, "utf8"));
@@ -40584,7 +41219,7 @@ async function applyOverlays(input) {
40584
41219
  writeFileSync21(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
40585
41220
  wrote.push(pkgProv);
40586
41221
  }
40587
- const pkgTokens = join47(cwd, "tokens.json");
41222
+ const pkgTokens = join48(cwd, "tokens.json");
40588
41223
  if (existsSync48(pkgTokens) && (tokens.colors || tokens.typography)) {
40589
41224
  const cur = JSON.parse(readFileSync60(pkgTokens, "utf8"));
40590
41225
  const merged = deepMerge3(cur, tokens);
@@ -40592,7 +41227,7 @@ async function applyOverlays(input) {
40592
41227
  wrote.push(pkgTokens);
40593
41228
  }
40594
41229
  const homeAction = homeRefreshActionId(shell);
40595
- const homeSrc = join47(cwd, "dev/index.ts");
41230
+ const homeSrc = join48(cwd, "dev/index.ts");
40596
41231
  if (homeAction && existsSync48(homeSrc)) {
40597
41232
  const prev = readFileSync60(homeSrc, "utf8");
40598
41233
  const patched = patchBrandParams(prev, overlay, homeAction);
@@ -42576,11 +43211,11 @@ var init_site_oauth_client = __esm({
42576
43211
  });
42577
43212
 
42578
43213
  // src/cli.ts
42579
- 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";
42580
43215
  import { execFileSync as execFileSync6 } from "node:child_process";
42581
43216
  import { createHash as createHash24 } from "node:crypto";
42582
43217
  import { tmpdir as tmpdir5 } from "node:os";
42583
- 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";
42584
43219
  import { fileURLToPath as fileURLToPath21 } from "node:url";
42585
43220
 
42586
43221
  // ../serve-cli/src/client.ts
@@ -45088,10 +45723,10 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
45088
45723
  // src/notify.ts
45089
45724
  import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
45090
45725
  import { homedir as homedir3 } from "node:os";
45091
- import { dirname as dirname12, join as join14 } from "node:path";
45726
+ import { dirname as dirname12, join as join15 } from "node:path";
45092
45727
  var DEFAULT_ADMIN_URL = "http://127.0.0.1:8888";
45093
45728
  function defaultSessionsPath() {
45094
- 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");
45095
45730
  }
45096
45731
  function loadSessionsFile(path = defaultSessionsPath()) {
45097
45732
  try {
@@ -45330,7 +45965,7 @@ NOTE: ${simulated} of those went to the booted iOS Simulator, NOT to the device
45330
45965
  init_car();
45331
45966
  init_preview_surfaces();
45332
45967
  import { existsSync as existsSync12, readFileSync as readFileSync15 } from "node:fs";
45333
- import { join as join15 } from "node:path";
45968
+ import { join as join16 } from "node:path";
45334
45969
  var VALID_PLATFORM_KEYS = /* @__PURE__ */ new Set([
45335
45970
  ...PREVIEW_SURFACES.map((s) => s.platformKey).filter((k) => k !== null),
45336
45971
  ...PREVIEW_EXEMPT_PLATFORM_KEYS
@@ -45341,7 +45976,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
45341
45976
  const error = (message) => issues.push({ screen: null, pass: "surfaces", severity: "error", message });
45342
45977
  let platforms = {};
45343
45978
  let envelopeUnreadable = false;
45344
- const p = join15(dir, "platforms.json");
45979
+ const p = join16(dir, "platforms.json");
45345
45980
  if (existsSync12(p)) {
45346
45981
  let raw;
45347
45982
  try {
@@ -45374,7 +46009,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
45374
46009
  });
45375
46010
  }
45376
46011
  if (envelopeUnreadable) return issues;
45377
- const configTs = join15(dir, "config.ts");
46012
+ const configTs = join16(dir, "config.ts");
45378
46013
  if (existsSync12(configTs)) {
45379
46014
  const declared = parseCarConfig(readFileSync15(configTs, "utf8")).category;
45380
46015
  const inEnvelope = platforms.androidauto === true;
@@ -45400,7 +46035,7 @@ function surfaceIssues(dir, surfaceNav, strict) {
45400
46035
  }
45401
46036
  function readLocalPlatforms(dir) {
45402
46037
  try {
45403
- const raw = JSON.parse(readFileSync15(join15(dir, "platforms.json"), "utf8"));
46038
+ const raw = JSON.parse(readFileSync15(join16(dir, "platforms.json"), "utf8"));
45404
46039
  return Object.fromEntries(
45405
46040
  Object.entries(raw).filter(([k, v]) => VALID_PLATFORM_KEYS.has(k) && typeof v === "boolean")
45406
46041
  );
@@ -45412,10 +46047,10 @@ function readLocalPlatforms(dir) {
45412
46047
  // src/token-lint.ts
45413
46048
  init_token_colors();
45414
46049
  import { existsSync as existsSync13, readFileSync as readFileSync16 } from "node:fs";
45415
- import { join as join16 } from "node:path";
46050
+ import { join as join17 } from "node:path";
45416
46051
  function configuredTokensPath(dir) {
45417
46052
  for (const name of ["config.ts", "config.js", "himi.config.ts"]) {
45418
- const f = join16(dir, name);
46053
+ const f = join17(dir, name);
45419
46054
  if (!existsSync13(f)) continue;
45420
46055
  const src = readFileSync16(f, "utf8");
45421
46056
  const m = /designTokensPath\s*:\s*["'`]([^"'`]+)["'`]/.exec(src);
@@ -45435,7 +46070,7 @@ function tokenColorIssues(dir, tokensPathOverride) {
45435
46070
  );
45436
46071
  }
45437
46072
  const tokensPath = configured.path ?? "tokens.json";
45438
- const p = join16(dir, tokensPath);
46073
+ const p = join17(dir, tokensPath);
45439
46074
  if (!existsSync13(p)) {
45440
46075
  if (tokensPath !== "tokens.json") {
45441
46076
  warn(`config designTokensPath points at ${tokensPath}, which does not exist -- colour tokens were not checked`);
@@ -45483,9 +46118,9 @@ function tokenColorIssues(dir, tokensPathOverride) {
45483
46118
  // src/icon-lint.ts
45484
46119
  init_icon();
45485
46120
  import { existsSync as existsSync16 } from "node:fs";
45486
- import { join as join19 } from "node:path";
46121
+ import { join as join20 } from "node:path";
45487
46122
  async function iconIssues(dir, strict, opts = {}) {
45488
- if (!existsSync16(join19(dir, "himalaya.content.json"))) return [];
46123
+ if (!existsSync16(join20(dir, "himalaya.content.json"))) return [];
45489
46124
  const status = await iconStatus(dir);
45490
46125
  const issue2 = (severity, message) => ({
45491
46126
  screen: null,
@@ -45553,13 +46188,13 @@ function fontIssues(dir, strict) {
45553
46188
 
45554
46189
  // src/stores-api-lint.ts
45555
46190
  import { existsSync as existsSync20, readdirSync as readdirSync10, readFileSync as readFileSync22 } from "node:fs";
45556
- import { join as join23 } from "node:path";
46191
+ import { join as join24 } from "node:path";
45557
46192
  function storesV1Issues(contentDir2, strict) {
45558
- const roots = [join23(contentDir2, "tier5-src"), join23(contentDir2, "dev")].filter(existsSync20);
46193
+ const roots = [join24(contentDir2, "tier5-src"), join24(contentDir2, "dev")].filter(existsSync20);
45559
46194
  const files = [];
45560
46195
  const visit = (dir) => {
45561
46196
  for (const entry of readdirSync10(dir, { withFileTypes: true })) {
45562
- const path = join23(dir, entry.name);
46197
+ const path = join24(dir, entry.name);
45563
46198
  if (entry.isDirectory()) visit(path);
45564
46199
  else if (/\.(?:[cm]?[jt]sx?)$/i.test(entry.name)) files.push(path);
45565
46200
  }
@@ -45580,7 +46215,7 @@ init_crawl_layers();
45580
46215
  init_wix_gateway_rules();
45581
46216
  init_mobile_ux_contract();
45582
46217
  import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync23, readdirSync as readdirSync11, writeFileSync as writeFileSync9 } from "node:fs";
45583
- 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";
45584
46219
  var EVIDENCE_RELATIVE_PATH = ".himi/ux-audit.json";
45585
46220
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".json"]);
45586
46221
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".himi", "coverage"]);
@@ -45598,12 +46233,12 @@ function walkSource(dir, root = dir) {
45598
46233
  const found = [];
45599
46234
  for (const entry of readdirSync11(dir, { withFileTypes: true })) {
45600
46235
  if (entry.isDirectory()) {
45601
- 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));
45602
46237
  continue;
45603
46238
  }
45604
46239
  const ext = entry.name.slice(entry.name.lastIndexOf("."));
45605
46240
  if (!SOURCE_EXTENSIONS.has(ext)) continue;
45606
- const absolute = join24(dir, entry.name);
46241
+ const absolute = join25(dir, entry.name);
45607
46242
  const text2 = readText(absolute);
45608
46243
  if (text2 !== null) found.push({ path: relative3(root, absolute), text: text2 });
45609
46244
  }
@@ -45633,9 +46268,9 @@ function usesAppNetwork(source) {
45633
46268
  function auditUx(content, strict = false) {
45634
46269
  const dir = resolve15(content);
45635
46270
  const issues = [];
45636
- const evidencePath = join24(dir, EVIDENCE_RELATIVE_PATH);
45637
- const spec = readText(join24(dir, "SPEC.md"));
45638
- 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"));
45639
46274
  if (!spec) issue(issues, strict, "missing-spec", "SPEC.md is missing; define the customer task before calling this UX-ready.", "SPEC.md");
45640
46275
  if (!mobile) {
45641
46276
  issue(issues, strict, "missing-mobile-ux", "MOBILE-UX.md is missing; add the mobile state and platform contract.", "MOBILE-UX.md");
@@ -45674,7 +46309,7 @@ function auditUx(content, strict = false) {
45674
46309
  const preview = matches(previewControl);
45675
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);
45676
46311
  if (usesAppNetwork(source)) {
45677
- const mocksPath = join24(dir, "dev", "net-mocks.json");
46312
+ const mocksPath = join25(dir, "dev", "net-mocks.json");
45678
46313
  if (!existsSync21(mocksPath)) {
45679
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");
45680
46315
  } else {
@@ -45696,12 +46331,12 @@ function recordNativeAudit(content, platform, status, checks, reason) {
45696
46331
  if (!normalizedChecks.length) throw new Error("--checks needs at least one comma-separated check (for example: back,keyboard,voiceover)");
45697
46332
  if (status === "unavailable" && !reason?.trim()) throw new Error("--reason is required when --status unavailable");
45698
46333
  const dir = resolve15(content);
45699
- const path = join24(dir, EVIDENCE_RELATIVE_PATH);
46334
+ const path = join25(dir, EVIDENCE_RELATIVE_PATH);
45700
46335
  const parsed = parseEvidence(path);
45701
46336
  if (parsed.error) throw new Error(`${EVIDENCE_RELATIVE_PATH} ${parsed.error}`);
45702
46337
  const evidence = parsed.evidence ?? { version: 1, platforms: {} };
45703
46338
  evidence.platforms[platform] = { status, checks: normalizedChecks, ...reason?.trim() ? { reason: reason.trim() } : {}, recordedAt: (/* @__PURE__ */ new Date()).toISOString() };
45704
- mkdirSync10(join24(dir, ".himi"), { recursive: true });
46339
+ mkdirSync10(join25(dir, ".himi"), { recursive: true });
45705
46340
  writeFileSync9(path, JSON.stringify(evidence, null, 2) + "\n");
45706
46341
  return evidence;
45707
46342
  }
@@ -45710,26 +46345,26 @@ function recordNativeAudit(content, platform, status, checks, reason) {
45710
46345
  init_mobile_ux_contract();
45711
46346
  import { readFileSync as readFileSync24, existsSync as existsSync22, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10, copyFileSync as copyFileSync2 } from "node:fs";
45712
46347
  import { fileURLToPath as fileURLToPath10 } from "node:url";
45713
- 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";
45714
46349
  var GUIDES_DIR = resolve16(dirname15(fileURLToPath10(import.meta.url)), "../guides");
45715
46350
  function guidesAvailable() {
45716
- return existsSync22(join25(GUIDES_DIR, "index.json"));
46351
+ return existsSync22(join26(GUIDES_DIR, "index.json"));
45717
46352
  }
45718
46353
  function loadIndex() {
45719
- return JSON.parse(readFileSync24(join25(GUIDES_DIR, "index.json"), "utf8"));
46354
+ return JSON.parse(readFileSync24(join26(GUIDES_DIR, "index.json"), "utf8"));
45720
46355
  }
45721
46356
  function readGuide(id) {
45722
46357
  const meta = loadIndex().guides.find((g) => g.id === id);
45723
46358
  if (!meta) return null;
45724
- return readFileSync24(join25(GUIDES_DIR, meta.file), "utf8");
46359
+ return readFileSync24(join26(GUIDES_DIR, meta.file), "utf8");
45725
46360
  }
45726
46361
  function loadComponentReference() {
45727
46362
  const idx = loadIndex();
45728
- return JSON.parse(readFileSync24(join25(GUIDES_DIR, idx.componentReferenceFile), "utf8"));
46363
+ return JSON.parse(readFileSync24(join26(GUIDES_DIR, idx.componentReferenceFile), "utf8"));
45729
46364
  }
45730
46365
  function loadSdkReference() {
45731
46366
  const idx = loadIndex();
45732
- return JSON.parse(readFileSync24(join25(GUIDES_DIR, idx.sdkReferenceFile), "utf8"));
46367
+ return JSON.parse(readFileSync24(join26(GUIDES_DIR, idx.sdkReferenceFile), "utf8"));
45733
46368
  }
45734
46369
  function buildAgentPrimer() {
45735
46370
  const idx = loadIndex();
@@ -45837,19 +46472,19 @@ function writeAgentContext(targetDir) {
45837
46472
  const idx = loadIndex();
45838
46473
  const files = [];
45839
46474
  const write2 = (rel, body) => {
45840
- const p = join25(targetDir, rel);
46475
+ const p = join26(targetDir, rel);
45841
46476
  mkdirSync11(dirname15(p), { recursive: true });
45842
46477
  writeFileSync10(p, body);
45843
46478
  files.push(rel);
45844
46479
  };
45845
46480
  const copy = (srcRel, destRel) => {
45846
- const p = join25(targetDir, destRel);
46481
+ const p = join26(targetDir, destRel);
45847
46482
  mkdirSync11(dirname15(p), { recursive: true });
45848
- copyFileSync2(join25(GUIDES_DIR, srcRel), p);
46483
+ copyFileSync2(join26(GUIDES_DIR, srcRel), p);
45849
46484
  files.push(destRel);
45850
46485
  };
45851
46486
  const writeIfMissing = (rel, body) => {
45852
- if (existsSync22(join25(targetDir, rel))) return;
46487
+ if (existsSync22(join26(targetDir, rel))) return;
45853
46488
  write2(rel, body);
45854
46489
  };
45855
46490
  write2("HIMALAYA.md", buildAgentPrimer());
@@ -45910,7 +46545,7 @@ var str5 = (v) => typeof v === "string" ? v : void 0;
45910
46545
  var num = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
45911
46546
  function embeddedTemplateCatalog() {
45912
46547
  return availableTemplates().map((name) => {
45913
- const path = join48(templateSourceDir(name), TEMPLATE_META_FILE);
46548
+ const path = join49(templateSourceDir(name), TEMPLATE_META_FILE);
45914
46549
  const meta = JSON.parse(readFileSync63(path, "utf8"));
45915
46550
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
45916
46551
  throw new Error(`${path} must be { title: string, description: string, tags: string[], category?: string }`);
@@ -46377,7 +47012,7 @@ function packageVersion(path) {
46377
47012
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
46378
47013
  let dir = resolve39(fromDir);
46379
47014
  for (; ; ) {
46380
- const version = packageVersion(join48(dir, "node_modules", "@wix", "himalaya", "package.json"));
47015
+ const version = packageVersion(join49(dir, "node_modules", "@wix", "himalaya", "package.json"));
46381
47016
  if (version) return { version, source: "local" };
46382
47017
  const parent = dirname29(dir);
46383
47018
  if (parent === dir) break;
@@ -46387,7 +47022,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
46387
47022
  if (bundled) return { version: bundled, source: "bundled" };
46388
47023
  try {
46389
47024
  const root = execFileSync6("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
46390
- 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;
46391
47026
  return version ? { version, source: "global" } : {};
46392
47027
  } catch {
46393
47028
  return {};
@@ -46762,7 +47397,7 @@ async function nativeConfigAdvisories(app, configSource) {
46762
47397
  }
46763
47398
  function readConfigSource(dir) {
46764
47399
  try {
46765
- return readFileSync63(join48(dir, "config.ts"), "utf8");
47400
+ return readFileSync63(join49(dir, "config.ts"), "utf8");
46766
47401
  } catch {
46767
47402
  return null;
46768
47403
  }
@@ -46774,7 +47409,7 @@ function writePngFile(path, base64) {
46774
47409
  }
46775
47410
  async function initFromRemoteTemplate(name, parentDir, template, c) {
46776
47411
  const dir = resolve39(parentDir, name);
46777
- if (existsSync50(join48(dir, "himalaya.content.json"))) {
47412
+ if (existsSync50(join49(dir, "himalaya.content.json"))) {
46778
47413
  throw new Error(`content package already exists at ${dir}`);
46779
47414
  }
46780
47415
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -46791,7 +47426,7 @@ function contentDir(flags) {
46791
47426
  function isHimalayaMonorepoCheckout(start) {
46792
47427
  let dir = resolve39(start);
46793
47428
  for (; ; ) {
46794
- 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;
46795
47430
  const parent = dirname29(dir);
46796
47431
  if (parent === dir) return false;
46797
47432
  dir = parent;
@@ -46822,7 +47457,7 @@ function materializeForPreview(appDir2, slug, displayName) {
46822
47457
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
46823
47458
  );
46824
47459
  const repoRoot2 = resolve39(fileURLToPath21(new URL("../../..", import.meta.url)));
46825
- const dest = join48(repoRoot2, "node_modules", ".cache", "himi-template-preview", slug);
47460
+ const dest = join49(repoRoot2, "node_modules", ".cache", "himi-template-preview", slug);
46826
47461
  rmSync8(dest, { recursive: true, force: true });
46827
47462
  mkdirSync25(dest, { recursive: true });
46828
47463
  materializeSource(forBuild, displayName, dest, slug);
@@ -46831,7 +47466,7 @@ function materializeForPreview(appDir2, slug, displayName) {
46831
47466
  async function appId(dir) {
46832
47467
  try {
46833
47468
  const { readFileSync: readFileSync64 } = await import("node:fs");
46834
- const m = JSON.parse(readFileSync64(join48(dir, "himalaya.content.json"), "utf8"));
47469
+ const m = JSON.parse(readFileSync64(join49(dir, "himalaya.content.json"), "utf8"));
46835
47470
  if (m.name) return m.name;
46836
47471
  } catch {
46837
47472
  }
@@ -46840,7 +47475,7 @@ async function appId(dir) {
46840
47475
  async function contentVisibility(dir) {
46841
47476
  try {
46842
47477
  const { readFileSync: readFileSync64 } = await import("node:fs");
46843
- const m = JSON.parse(readFileSync64(join48(dir, "himalaya.content.json"), "utf8"));
47478
+ const m = JSON.parse(readFileSync64(join49(dir, "himalaya.content.json"), "utf8"));
46844
47479
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
46845
47480
  } catch {
46846
47481
  }
@@ -47079,6 +47714,7 @@ async function loadAuthoring() {
47079
47714
  crawlScreen: crawl.crawlScreen,
47080
47715
  crawlScreenJourney: crawl.crawlScreenJourney,
47081
47716
  crawlScreenHostile: crawl.crawlScreenHostile,
47717
+ crawlScreenRelations: crawl.crawlScreenRelations,
47082
47718
  findingKey: crawl.findingKey,
47083
47719
  startWorkerCoverage: cov.startWorkerCoverage,
47084
47720
  uncoveredFunctions: cov.uncoveredFunctions,
@@ -47241,7 +47877,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47241
47877
  const sourceDir = resolve39(str5(flags["source-dir"]) ?? appDir2);
47242
47878
  let meta;
47243
47879
  try {
47244
- meta = JSON.parse(readFileSync63(join48(sourceDir, TEMPLATE_META_FILE), "utf8"));
47880
+ meta = JSON.parse(readFileSync63(join49(sourceDir, TEMPLATE_META_FILE), "utf8"));
47245
47881
  } catch {
47246
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>)` }));
47247
47883
  return 2;
@@ -47251,7 +47887,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47251
47887
  return 2;
47252
47888
  }
47253
47889
  try {
47254
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync63(join48(sourceDir, "tokens.json"), "utf8")));
47890
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync63(join49(sourceDir, "tokens.json"), "utf8")));
47255
47891
  meta = { ...appearance, ...meta };
47256
47892
  } catch {
47257
47893
  }
@@ -47263,7 +47899,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47263
47899
  const force = flags.force === true;
47264
47900
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
47265
47901
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
47266
- const out = mkdtempSync4(join48(tmpdir5(), "himi-template-"));
47902
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-template-"));
47267
47903
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
47268
47904
  const outcome = await buildReleaseFromContent2(buildDir, out, {
47269
47905
  ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
@@ -47422,7 +48058,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47422
48058
  const { mobileUxIssues: mobileUxIssues2 } = await Promise.resolve().then(() => (init_mobile_ux_lint(), mobile_ux_lint_exports));
47423
48059
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
47424
48060
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
47425
- const out = mkdtempSync4(join48(tmpdir5(), "himi-validate-"));
48061
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-validate-"));
47426
48062
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
47427
48063
  const outcome = await buildReleaseFromContent2(dir, out, {
47428
48064
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -47610,7 +48246,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47610
48246
  case "test": {
47611
48247
  const dir = contentDir(flags);
47612
48248
  const structuredOutput = flags.json === true || io.isTTY !== true;
47613
- if (existsSync50(join48(dir, "functions"))) {
48249
+ if (existsSync50(join49(dir, "functions"))) {
47614
48250
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
47615
48251
  const loaded = await loadFunctionsApp2(dir);
47616
48252
  const results = [];
@@ -47624,7 +48260,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47624
48260
  return results.every((result2) => result2.ok) ? 0 : 1;
47625
48261
  }
47626
48262
  const app = await appId(dir);
47627
- 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();
47628
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));
47629
48265
  let config;
47630
48266
  try {
@@ -47697,7 +48333,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47697
48333
  }
47698
48334
  }
47699
48335
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
47700
- const out = mkdtempSync4(join48(tmpdir5(), "himi-test-"));
48336
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-test-"));
47701
48337
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
47702
48338
  const outcome = await buildReleaseFromContent2(dir, out, {
47703
48339
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -47742,7 +48378,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47742
48378
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
47743
48379
  return 2;
47744
48380
  }
47745
- const sources = collectWorkerSources2(join48(dir, "tier5-src"), screen.bundleName);
48381
+ const sources = collectWorkerSources2(join49(dir, "tier5-src"), screen.bundleName);
47746
48382
  if (!sources) {
47747
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` }));
47748
48384
  return 2;
@@ -47862,7 +48498,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47862
48498
  ...deep ? { deep } : {},
47863
48499
  ...offline ? { offline: true } : {},
47864
48500
  ...offline || flags["no-hostile"] === true ? { hostile: false } : {},
47865
- 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 }
47866
48502
  });
47867
48503
  } catch (e) {
47868
48504
  io.error(JSON.stringify({ error: e.message }));
@@ -47899,7 +48535,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47899
48535
  const sp = await runShotsPass2({
47900
48536
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
47901
48537
  tokens: outcome.tokens,
47902
- outDir: join48(dir, ".himi", "shots"),
48538
+ outDir: join49(dir, ".himi", "shots"),
47903
48539
  width: 390,
47904
48540
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
47905
48541
  ...budgetMs ? { budgetMs } : {},
@@ -47935,7 +48571,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
47935
48571
  releaseId: result.releaseId,
47936
48572
  releasePayload: release,
47937
48573
  descriptors: attestedDescriptors,
47938
- 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)])),
47939
48575
  coverage: {
47940
48576
  screens: crawledScreens.size,
47941
48577
  actionsDispatched: result.actions.exercised,
@@ -48077,7 +48713,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
48077
48713
  }
48078
48714
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48079
48715
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
48080
- const out = mkdtempSync4(join48(tmpdir5(), "himi-render-"));
48716
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-render-"));
48081
48717
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48082
48718
  const outcome = await buildReleaseFromContent2(dir, out, {
48083
48719
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -48093,7 +48729,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
48093
48729
  let attestationPngFile;
48094
48730
  if (typeof flags.png === "string" && r.pngBase64) {
48095
48731
  pngFile = writePngFile(str5(flags.png), r.pngBase64);
48096
- attestationPngFile = writePngFile(join48(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
48732
+ attestationPngFile = writePngFile(join49(dir, ".himi", "shots", `${screenId}.png`), r.pngBase64);
48097
48733
  }
48098
48734
  const legacyRender = {
48099
48735
  ok: r.renderable,
@@ -48400,7 +49036,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48400
49036
  const renamed = str5(flags.name);
48401
49037
  const dest = resolve39(str5(flags.out) ?? process.cwd(), renamed ?? wanted);
48402
49038
  if (existsSync50(dest)) {
48403
- if (!statSync12(dest).isDirectory()) {
49039
+ if (!statSync13(dest).isDirectory()) {
48404
49040
  io.error(JSON.stringify({
48405
49041
  error: `${dest} exists and is not a directory`,
48406
49042
  hint: "pass --out/--name to land somewhere else, or remove the file"
@@ -48473,7 +49109,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48473
49109
  const force = flags.force === true;
48474
49110
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48475
49111
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
48476
- const out = mkdtempSync4(join48(tmpdir5(), "himi-push-"));
49112
+ const out = mkdtempSync4(join49(tmpdir5(), "himi-push-"));
48477
49113
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48478
49114
  const outcome = await buildReleaseFromContent2(dir, out, {
48479
49115
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -48765,7 +49401,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48765
49401
  const force = flags.force === true;
48766
49402
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48767
49403
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
48768
- const built = mkdtempSync4(join48(tmpdir5(), "himi-pack-"));
49404
+ const built = mkdtempSync4(join49(tmpdir5(), "himi-pack-"));
48769
49405
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48770
49406
  const outcome = await buildReleaseFromContent2(dir, built, {
48771
49407
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -48878,10 +49514,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
48878
49514
  const app = await appId(dir);
48879
49515
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
48880
49516
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
48881
- temp = mkdtempSync4(join48(tmpdir5(), "himi-run-build-"));
49517
+ temp = mkdtempSync4(join49(tmpdir5(), "himi-run-build-"));
48882
49518
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
48883
49519
  await buildReleaseFromContent2(dir, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {} });
48884
- source = join48(temp, app);
49520
+ source = join49(temp, app);
48885
49521
  }
48886
49522
  const chromeArg = str5(flags.chrome) ?? "preview";
48887
49523
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -49679,7 +50315,7 @@ ${label2}`);
49679
50315
  const platform = str5(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
49680
50316
  {
49681
50317
  const distDir = contentDir(flags);
49682
- if (existsSync50(join48(distDir, "himalaya.content.json"))) {
50318
+ if (existsSync50(join49(distDir, "himalaya.content.json"))) {
49683
50319
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
49684
50320
  const errors = issues.filter((i) => i.severity === "error");
49685
50321
  for (const i of issues.filter((i2) => i2.severity === "warning")) {