@wix/himalaya-cli 0.1423.0 → 0.1425.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +253 -131
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -7715,6 +7715,7 @@ function resolveTranslation(value, catalog3, sourceCatalog) {
7715
7715
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
7716
7716
  var init_bindings = __esm({
7717
7717
  "../../core/ts/src/bindings.ts"() {
7718
+ "use strict";
7718
7719
  BINDING_SIGILS = ["service", "flag", "item", "t"];
7719
7720
  BINDING_SIGIL_RE = new RegExp(
7720
7721
  `^\\$(?:${BINDING_SIGILS.filter((s2) => s2 !== "t").join("|")})\\b|^\\$t:`
@@ -10224,16 +10225,154 @@ var init_raw_json_depth = __esm({
10224
10225
  }
10225
10226
  });
10226
10227
 
10228
+ // ../../core/dev-server/src/depth-budget.ts
10229
+ function isOpaqueAuthorJson(key2, value, atRoot) {
10230
+ if (value === null || typeof value !== "object") return false;
10231
+ return key2 === "args" || atRoot && key2 === "input";
10232
+ }
10233
+ function depthBudgetFindings(descriptor) {
10234
+ const out2 = [];
10235
+ const templates = [];
10236
+ const isComponent = (v2) => {
10237
+ const body = v2?.body;
10238
+ return !!body && typeof body === "object" && typeof body.type === "string" && typeof body.package === "string";
10239
+ };
10240
+ const hostedTemplates = (v2, path2) => {
10241
+ const props = v2.body.props;
10242
+ const found = [];
10243
+ if (!props) return found;
10244
+ const itemsPath = props.itemsPath ?? props.items_path;
10245
+ const rowHost = v2.body.package === "himalaya" ? ROW_TEMPLATE_HOST_TYPES.has(v2.body.type) : true;
10246
+ if (rowHost && typeof itemsPath === "string" && itemsPath !== "" && Array.isArray(props.items) && isComponent(props.items[0])) {
10247
+ found.push({ node: props.items[0], path: `${path2}.body.props.items[0]`, host: `${v2.body.type} at ${path2}` });
10248
+ }
10249
+ if (isComponent(props.eventTemplate)) {
10250
+ found.push({ node: props.eventTemplate, path: `${path2}.body.props.eventTemplate`, host: `${v2.body.type} at ${path2}` });
10251
+ }
10252
+ return found;
10253
+ };
10254
+ const measure2 = (root, rootPath, mode) => {
10255
+ let depth = 0, nodes = 0, deepest = rootPath;
10256
+ const skip = /* @__PURE__ */ new Set();
10257
+ const stack = [
10258
+ { v: root, path: rootPath, depth: 0, atRoot: mode === "screen" }
10259
+ ];
10260
+ while (stack.length) {
10261
+ const { v: v2, path: path2, depth: parentDepth, atRoot } = stack.pop();
10262
+ if (skip.has(v2)) continue;
10263
+ if (Array.isArray(v2)) {
10264
+ for (let i2 = v2.length - 1; i2 >= 0; i2--) stack.push({ v: v2[i2], path: `${path2}[${i2}]`, depth: parentDepth, atRoot: false });
10265
+ continue;
10266
+ }
10267
+ if (!v2 || typeof v2 !== "object") continue;
10268
+ let here = parentDepth;
10269
+ if (isComponent(v2)) {
10270
+ here = parentDepth + 1;
10271
+ nodes += 1;
10272
+ if (here > depth) {
10273
+ depth = here;
10274
+ deepest = path2;
10275
+ }
10276
+ for (const t2 of hostedTemplates(v2, path2)) {
10277
+ if (mode === "screen") templates.push(t2);
10278
+ else skip.add(t2.node);
10279
+ }
10280
+ }
10281
+ const entries = Object.entries(v2);
10282
+ for (let i2 = entries.length - 1; i2 >= 0; i2--) {
10283
+ const [key2, child] = entries[i2];
10284
+ if (!child || typeof child !== "object" || isOpaqueAuthorJson(key2, child, atRoot)) continue;
10285
+ stack.push({ v: child, path: `${path2}.${key2}`, depth: here, atRoot: false });
10286
+ }
10287
+ }
10288
+ return { depth, nodes, deepest };
10289
+ };
10290
+ const flatten = "flatten the layout \u2014 drop wrapper stacks, or move a section into its own screen";
10291
+ const report3 = (what, m2, budget, rootPath) => {
10292
+ if (m2.depth > budget.maxDepth) {
10293
+ out2.push({
10294
+ measure: "depth",
10295
+ path: m2.deepest,
10296
+ limit: budget.maxDepth,
10297
+ actual: m2.depth,
10298
+ message: `${what} nests ${m2.depth} component levels; the limit is ${budget.maxDepth} (deepest node at ${m2.deepest}) \u2014 ${flatten}`
10299
+ });
10300
+ }
10301
+ if (m2.nodes > budget.maxNodes) {
10302
+ out2.push({
10303
+ measure: "nodes",
10304
+ path: rootPath,
10305
+ limit: budget.maxNodes,
10306
+ actual: m2.nodes,
10307
+ message: `${what} has ${m2.nodes} components; the limit is ${budget.maxNodes} \u2014 ${flatten}`
10308
+ });
10309
+ }
10310
+ };
10311
+ report3("screen", measure2(descriptor, "$", "screen"), DESCRIPTOR_DEPTH_BUDGETS.screen, "$");
10312
+ for (const t2 of templates) {
10313
+ report3(`row template of the ${t2.host}`, measure2(t2.node, t2.path, "template"), DESCRIPTOR_DEPTH_BUDGETS.rowTemplate, t2.path);
10314
+ }
10315
+ return out2;
10316
+ }
10317
+ function screenDepthFindings(descriptor) {
10318
+ const raw = rawJsonDepthFinding(descriptor);
10319
+ return raw ? [raw] : depthBudgetFindings(descriptor);
10320
+ }
10321
+ function depthBudgetIssue(f2) {
10322
+ return { severity: "error", pass: "depth-budget", path: f2.path, message: f2.message, limit: f2.limit, actual: f2.actual };
10323
+ }
10324
+ var DESCRIPTOR_DEPTH_BUDGETS, ROW_TEMPLATE_HOST_TYPES;
10325
+ var init_depth_budget = __esm({
10326
+ "../../core/dev-server/src/depth-budget.ts"() {
10327
+ init_worker_subtree();
10328
+ init_raw_json_depth();
10329
+ DESCRIPTOR_DEPTH_BUDGETS = Object.freeze({
10330
+ screen: Object.freeze({ maxDepth: 40, maxNodes: 2e3 }),
10331
+ rowTemplate: Object.freeze({ maxDepth: WORKER_SUBTREE_CEILINGS.maxDepth, maxNodes: WORKER_SUBTREE_CEILINGS.maxNodes })
10332
+ });
10333
+ ROW_TEMPLATE_HOST_TYPES = /* @__PURE__ */ new Set(["LazyList", "Grid", "Carousel", "Menu"]);
10334
+ }
10335
+ });
10336
+
10227
10337
  // ../../core/dev-server/src/realm-parity.ts
10228
- function withheldGlobalsSource(hide) {
10229
- if (!hide?.length) return "";
10230
- return `${JSON.stringify(hide)}.forEach(function (n) { try { delete globalThis[n]; } catch (e) { /* non-configurable: leave it */ } });`;
10338
+ function allowlistSweepSource(keep = []) {
10339
+ const key2 = [...keep].sort().join(",");
10340
+ let source = sweepSources.get(key2);
10341
+ if (source === void 0) {
10342
+ source = buildSweepSource([.../* @__PURE__ */ new Set([...WORKER_ALLOWLIST, ...keep])].sort());
10343
+ if (sweepSources.size >= 32) sweepSources.clear();
10344
+ sweepSources.set(key2, source);
10345
+ }
10346
+ return source;
10347
+ }
10348
+ function buildSweepSource(allow) {
10349
+ return `(function (allow) {
10350
+ var keep = Object.create(null);
10351
+ for (var i = 0; i < allow.length; i++) keep[allow[i]] = true;
10352
+ for (var o = globalThis; o && o !== Object.prototype; o = Object.getPrototypeOf(o)) {
10353
+ var names = Object.getOwnPropertyNames(o);
10354
+ for (var j = 0; j < names.length; j++) {
10355
+ var n = names[j];
10356
+ if (keep[n] || n === "constructor" || n.indexOf("himi.quickjs.") === 0) continue;
10357
+ try { delete o[n]; } catch (e) { /* non-configurable: leave it */ }
10358
+ }
10359
+ }
10360
+ })(${JSON.stringify(allow)});`;
10361
+ }
10362
+ function explainWithheldError(message, deviceParity) {
10363
+ if (!deviceParity || message.includes(FETCH_ADVICE)) return message;
10364
+ return BARE_FETCH_UNDEFINED.test(message) ? `${message} \u2014 ${FETCH_ADVICE}` : message;
10231
10365
  }
10232
10366
  function realmInstallsIntl(intl, hide) {
10233
10367
  return intl === true && !(hide ?? []).includes("Intl");
10234
10368
  }
10369
+ var sweepSources, FETCH_ADVICE, BARE_FETCH_UNDEFINED;
10235
10370
  var init_realm_parity = __esm({
10236
10371
  "../../core/dev-server/src/realm-parity.ts"() {
10372
+ init_allowlist();
10373
+ sweepSources = /* @__PURE__ */ new Map();
10374
+ FETCH_ADVICE = "`fetch` is not available to a Tier-5 worker (no device has it: iOS's JSContext never did and the Android worker strips it, #4499) \u2014 load data through the sanctioned Wix transport (`@himalaya/wix-*` or `globalThis.himalaya.wixApi.call`).";
10375
+ BARE_FETCH_UNDEFINED = /(?:^|[^.\w$'])'?fetch'? is not (?:defined|a function)\b/;
10237
10376
  }
10238
10377
  });
10239
10378
 
@@ -10266,15 +10405,18 @@ __export(validate_descriptor_core_exports, {
10266
10405
  RAW_JSON_MAX_DEPTH: () => RAW_JSON_MAX_DEPTH,
10267
10406
  SERVICE_ONLY_BINDING_PROPS: () => SERVICE_ONLY_BINDING_PROPS,
10268
10407
  WHOLE_VALUE_BINDING: () => WHOLE_VALUE_BINDING,
10408
+ allowlistSweepSource: () => allowlistSweepSource,
10269
10409
  bareNestedClass: () => bareNestedClass,
10270
10410
  bootBundleWorker: () => bootBundleWorker,
10271
10411
  collectServiceBindings: () => collectServiceBindings,
10272
10412
  componentPropFindings: () => componentPropFindings,
10273
10413
  depthBudgetFindings: () => depthBudgetFindings,
10414
+ depthBudgetIssue: () => depthBudgetIssue,
10274
10415
  depthBudgetIssues: () => depthBudgetIssues,
10275
10416
  deviceParityWithheldFrom: () => deviceParityWithheldFrom,
10276
10417
  dryRunBundle: () => dryRunBundle,
10277
10418
  enumFieldNames: () => enumFieldNames,
10419
+ explainWithheldError: () => explainWithheldError,
10278
10420
  extractBundleContract: () => extractBundleContract,
10279
10421
  extractBundleServices: () => extractBundleServices,
10280
10422
  findSkipDerive: () => findSkipDerive,
@@ -10301,7 +10443,6 @@ __export(validate_descriptor_core_exports, {
10301
10443
  setDefaultEvaluator: () => setDefaultEvaluator,
10302
10444
  topBarFindings: () => topBarFindings,
10303
10445
  validateScreenDescriptor: () => validateScreenDescriptor,
10304
- withheldGlobalsSource: () => withheldGlobalsSource,
10305
10446
  workerSubtreeAllowed: () => workerSubtreeAllowed
10306
10447
  });
10307
10448
  function makeInert() {
@@ -10699,6 +10840,7 @@ async function bootBundleWorker(bundleJs, opts) {
10699
10840
  timeoutMs: 2e3 + budgetMs,
10700
10841
  filename: "himi-bundle.js",
10701
10842
  hide,
10843
+ deviceParity: opts.deviceParity === true,
10702
10844
  wixCall: wixApi.call,
10703
10845
  dispatch: dispatchHost,
10704
10846
  skipLoad: opts.skipLoad === true,
@@ -10709,13 +10851,13 @@ async function bootBundleWorker(bundleJs, opts) {
10709
10851
  guestActions = ran.actions;
10710
10852
  guestSession = ran.session;
10711
10853
  if (ran.error) {
10712
- errors.push({ service: "(boot)", message: ran.error });
10854
+ errors.push({ service: "(boot)", message: explainWithheldError(ran.error, opts.deviceParity === true) });
10713
10855
  }
10714
10856
  } else {
10715
- realm.run(bundleJs, sandbox, { timeoutMs: 2e3, filename: "himi-bundle.js", hide, ...pins });
10857
+ realm.run(bundleJs, sandbox, { timeoutMs: 2e3, filename: "himi-bundle.js", hide, deviceParity: opts.deviceParity === true, ...pins });
10716
10858
  }
10717
10859
  } catch (err) {
10718
- errors.push({ service: "(boot)", message: err?.message ?? String(err), ...err?.stack ? { stack: err.stack } : {} });
10860
+ errors.push({ service: "(boot)", message: explainWithheldError(err?.message ?? String(err), opts.deviceParity === true), ...err?.stack ? { stack: err.stack } : {} });
10719
10861
  }
10720
10862
  const clone = (v2) => {
10721
10863
  try {
@@ -10788,6 +10930,17 @@ async function bootBundleWorker(bundleJs, opts) {
10788
10930
  delete state._error;
10789
10931
  return state;
10790
10932
  }
10933
+ const explainActionError = (err) => {
10934
+ if (!(err && typeof err === "object" && typeof err.message === "string")) return;
10935
+ const e2 = err;
10936
+ const explained = explainWithheldError(e2.message, opts.deviceParity === true);
10937
+ if (explained !== e2.message) {
10938
+ try {
10939
+ e2.message = explained;
10940
+ } catch {
10941
+ }
10942
+ }
10943
+ };
10791
10944
  const commitActionFailure = (def, err) => {
10792
10945
  const prev = def.state;
10793
10946
  if (!prev || typeof prev !== "object" || Array.isArray(prev)) return;
@@ -10818,6 +10971,7 @@ async function bootBundleWorker(bundleJs, opts) {
10818
10971
  const next = await Promise.race([Promise.resolve(fn2(def.state, ...args)), timeout]);
10819
10972
  applyResult(def, next);
10820
10973
  } catch (err) {
10974
+ explainActionError(err);
10821
10975
  commitActionFailure(def, err);
10822
10976
  throw err;
10823
10977
  } finally {
@@ -10833,6 +10987,7 @@ async function bootBundleWorker(bundleJs, opts) {
10833
10987
  def.state = entry.state;
10834
10988
  }
10835
10989
  if (entry.error !== void 0) {
10990
+ entry.error = explainWithheldError(entry.error, opts.deviceParity === true);
10836
10991
  commitActionFailure(def, entry.error);
10837
10992
  if (recordErrors && entry.reportError) errors.push({ service: entry.name, message: entry.error });
10838
10993
  continue;
@@ -11049,10 +11204,6 @@ function ellipsis(value, max) {
11049
11204
  function own3(table, key2) {
11050
11205
  return Object.prototype.hasOwnProperty.call(table, key2) ? table[key2] : void 0;
11051
11206
  }
11052
- function isOpaqueAuthorJson(key2, value, atRoot) {
11053
- if (value === null || typeof value !== "object") return false;
11054
- return key2 === "args" || atRoot && key2 === "input";
11055
- }
11056
11207
  function pathCrossesOpaqueAuthorJson(path2) {
11057
11208
  if (typeof path2 !== "string") return false;
11058
11209
  return /^\$\.input(?:[.[]|$)/.test(path2) || /\.args(?:[.[]|$)/.test(path2);
@@ -11783,103 +11934,8 @@ function bottomSheetFindings(descriptor) {
11783
11934
  visit(descriptor, "", true);
11784
11935
  return out2;
11785
11936
  }
11786
- function screenDepthFindings(descriptor) {
11787
- const raw = rawJsonDepthFinding(descriptor);
11788
- return raw ? [raw] : depthBudgetFindings(descriptor);
11789
- }
11790
11937
  function depthBudgetIssues(findings) {
11791
- return findings.map((f2) => ({
11792
- severity: "error",
11793
- pass: "depth-budget",
11794
- path: f2.path,
11795
- message: f2.message,
11796
- limit: f2.limit,
11797
- actual: f2.actual
11798
- }));
11799
- }
11800
- function depthBudgetFindings(descriptor) {
11801
- const out2 = [];
11802
- const templates = [];
11803
- const isComponent = (v2) => {
11804
- const body = v2?.body;
11805
- return !!body && typeof body === "object" && typeof body.type === "string" && typeof body.package === "string";
11806
- };
11807
- const hostedTemplates = (v2, path2) => {
11808
- const props = v2.body.props;
11809
- const found = [];
11810
- if (!props) return found;
11811
- const itemsPath = props.itemsPath ?? props.items_path;
11812
- const rowHost = v2.body.package === "himalaya" ? ROW_TEMPLATE_HOST_TYPES.has(v2.body.type) : true;
11813
- if (rowHost && typeof itemsPath === "string" && itemsPath !== "" && Array.isArray(props.items) && isComponent(props.items[0])) {
11814
- found.push({ node: props.items[0], path: `${path2}.body.props.items[0]`, host: `${v2.body.type} at ${path2}` });
11815
- }
11816
- if (isComponent(props.eventTemplate)) {
11817
- found.push({ node: props.eventTemplate, path: `${path2}.body.props.eventTemplate`, host: `${v2.body.type} at ${path2}` });
11818
- }
11819
- return found;
11820
- };
11821
- const measure2 = (root, rootPath, mode) => {
11822
- let depth = 0, nodes = 0, deepest = rootPath;
11823
- const skip = /* @__PURE__ */ new Set();
11824
- const stack = [
11825
- { v: root, path: rootPath, depth: 0, atRoot: mode === "screen" }
11826
- ];
11827
- while (stack.length) {
11828
- const { v: v2, path: path2, depth: parentDepth, atRoot } = stack.pop();
11829
- if (skip.has(v2)) continue;
11830
- if (Array.isArray(v2)) {
11831
- for (let i2 = v2.length - 1; i2 >= 0; i2--) stack.push({ v: v2[i2], path: `${path2}[${i2}]`, depth: parentDepth, atRoot: false });
11832
- continue;
11833
- }
11834
- if (!v2 || typeof v2 !== "object") continue;
11835
- let here = parentDepth;
11836
- if (isComponent(v2)) {
11837
- here = parentDepth + 1;
11838
- nodes += 1;
11839
- if (here > depth) {
11840
- depth = here;
11841
- deepest = path2;
11842
- }
11843
- for (const t2 of hostedTemplates(v2, path2)) {
11844
- if (mode === "screen") templates.push(t2);
11845
- else skip.add(t2.node);
11846
- }
11847
- }
11848
- const entries = Object.entries(v2);
11849
- for (let i2 = entries.length - 1; i2 >= 0; i2--) {
11850
- const [key2, child] = entries[i2];
11851
- if (!child || typeof child !== "object" || isOpaqueAuthorJson(key2, child, atRoot)) continue;
11852
- stack.push({ v: child, path: `${path2}.${key2}`, depth: here, atRoot: false });
11853
- }
11854
- }
11855
- return { depth, nodes, deepest };
11856
- };
11857
- const flatten = "flatten the layout \u2014 drop wrapper stacks, or move a section into its own screen";
11858
- const report3 = (what, m2, budget, rootPath) => {
11859
- if (m2.depth > budget.maxDepth) {
11860
- out2.push({
11861
- measure: "depth",
11862
- path: m2.deepest,
11863
- limit: budget.maxDepth,
11864
- actual: m2.depth,
11865
- message: `${what} nests ${m2.depth} component levels; the limit is ${budget.maxDepth} (deepest node at ${m2.deepest}) \u2014 ${flatten}`
11866
- });
11867
- }
11868
- if (m2.nodes > budget.maxNodes) {
11869
- out2.push({
11870
- measure: "nodes",
11871
- path: rootPath,
11872
- limit: budget.maxNodes,
11873
- actual: m2.nodes,
11874
- message: `${what} has ${m2.nodes} components; the limit is ${budget.maxNodes} \u2014 ${flatten}`
11875
- });
11876
- }
11877
- };
11878
- report3("screen", measure2(descriptor, "$", "screen"), DESCRIPTOR_DEPTH_BUDGETS.screen, "$");
11879
- for (const t2 of templates) {
11880
- report3(`row template of the ${t2.host}`, measure2(t2.node, t2.path, "template"), DESCRIPTOR_DEPTH_BUDGETS.rowTemplate, t2.path);
11881
- }
11882
- return out2;
11938
+ return findings.map(depthBudgetIssue);
11883
11939
  }
11884
11940
  function topBarFindings(descriptor) {
11885
11941
  const out2 = [];
@@ -12640,7 +12696,7 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
12640
12696
  services: Object.keys(services)
12641
12697
  };
12642
12698
  }
12643
- var CRAWL_BLOCKING_PASSES, BOOT_EVAL_TIMEOUT_MS, DRIFT_PROBES, DEVICE_NETWORK_WITHHELD, DEVICE_STRIPPED_HOST_GLOBALS, DEVICE_WITHHELD, DEVICE_PARITY_CANDIDATES, DEVICE_PARITY_WITHHELD, defaultEvaluator, bootEvaluator, actionEvaluator, NATIVE_PROP_ENUMS, KNOWN_PROPS, IN_ARRAY_BINDING_PROPS, SERVICE_SIGIL, INTEGER_PROPS, NATIVE_REQUIRED_PROPS, bareNestedClass, NATIVE_NESTED_CLASSES, NATIVE_MODIFIERS_CLASS, NATIVE_DESCRIPTOR_CLASS, NATIVE_DESCRIPTOR_NESTED, NATIVE_NESTED_PROPS, LENIENT_TOP_BAR_BLOCKS, MODIFIER_KEYS, INTEGER_MODIFIERS, MODIFIER_KINDS, SCHEMA_SCORED_NODES, CORE_PACKAGES, LEGACY_UNSCORED_KEYS, DYNAMIC_PLACEHOLDER, LITERAL_ONLY_PROPS, SERVICE_ONLY_BINDING_PROPS, WHOLE_VALUE_BINDING, EMBEDDED_BINDING, OPTIONS_BINDING_PROPS, EDITOR_SERVICE_NAME, SHEET_PLACEMENTS, DESCRIPTOR_DEPTH_BUDGETS, UNSAFE_TO_RECURSE_DEPTH, ROW_TEMPLATE_HOST_TYPES, TOP_BAR_TRAILING_LIMIT, TOP_BAR_NON_TRAILING, BUTTON_STYLES, EMPTY_STATE_ACTION_STYLES, EMPTY_STATE_ACTION_SIZES;
12699
+ var CRAWL_BLOCKING_PASSES, BOOT_EVAL_TIMEOUT_MS, DRIFT_PROBES, DEVICE_NETWORK_WITHHELD, DEVICE_STRIPPED_HOST_GLOBALS, DEVICE_WITHHELD, DEVICE_PARITY_CANDIDATES, DEVICE_PARITY_WITHHELD, defaultEvaluator, bootEvaluator, actionEvaluator, NATIVE_PROP_ENUMS, KNOWN_PROPS, IN_ARRAY_BINDING_PROPS, SERVICE_SIGIL, INTEGER_PROPS, NATIVE_REQUIRED_PROPS, bareNestedClass, NATIVE_NESTED_CLASSES, NATIVE_MODIFIERS_CLASS, NATIVE_DESCRIPTOR_CLASS, NATIVE_DESCRIPTOR_NESTED, NATIVE_NESTED_PROPS, LENIENT_TOP_BAR_BLOCKS, MODIFIER_KEYS, INTEGER_MODIFIERS, MODIFIER_KINDS, SCHEMA_SCORED_NODES, CORE_PACKAGES, LEGACY_UNSCORED_KEYS, DYNAMIC_PLACEHOLDER, LITERAL_ONLY_PROPS, SERVICE_ONLY_BINDING_PROPS, WHOLE_VALUE_BINDING, EMBEDDED_BINDING, OPTIONS_BINDING_PROPS, EDITOR_SERVICE_NAME, SHEET_PLACEMENTS, UNSAFE_TO_RECURSE_DEPTH, TOP_BAR_TRAILING_LIMIT, TOP_BAR_NON_TRAILING, BUTTON_STYLES, EMPTY_STATE_ACTION_STYLES, EMPTY_STATE_ACTION_SIZES;
12644
12700
  var init_validate_descriptor_core = __esm({
12645
12701
  "../../core/dev-server/src/validate-descriptor.core.ts"() {
12646
12702
  init_validator();
@@ -12655,9 +12711,10 @@ var init_validate_descriptor_core = __esm({
12655
12711
  init_media_value();
12656
12712
  init_frame_limits();
12657
12713
  init_rich_content();
12658
- init_worker_subtree();
12714
+ init_depth_budget();
12659
12715
  init_raw_json_depth();
12660
12716
  init_realm_parity();
12717
+ init_realm_parity();
12661
12718
  CRAWL_BLOCKING_PASSES = /* @__PURE__ */ new Set([
12662
12719
  // A screen whose bindings do not resolve has no state to crawl.
12663
12720
  "bindings",
@@ -13687,12 +13744,7 @@ var init_validate_descriptor_core = __esm({
13687
13744
  OPTIONS_BINDING_PROPS = /* @__PURE__ */ new Set(["optionsPath"]);
13688
13745
  EDITOR_SERVICE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
13689
13746
  SHEET_PLACEMENTS = /* @__PURE__ */ new Set(["overlay", "inline"]);
13690
- DESCRIPTOR_DEPTH_BUDGETS = Object.freeze({
13691
- screen: Object.freeze({ maxDepth: 40, maxNodes: 2e3 }),
13692
- rowTemplate: Object.freeze({ maxDepth: WORKER_SUBTREE_CEILINGS.maxDepth, maxNodes: WORKER_SUBTREE_CEILINGS.maxNodes })
13693
- });
13694
13747
  UNSAFE_TO_RECURSE_DEPTH = 128;
13695
- ROW_TEMPLATE_HOST_TYPES = /* @__PURE__ */ new Set(["LazyList", "Grid", "Carousel", "Menu"]);
13696
13748
  TOP_BAR_TRAILING_LIMIT = 2;
13697
13749
  TOP_BAR_NON_TRAILING = /* @__PURE__ */ new Set(["leading", "principal", "bottom", "keyboard", "overflow"]);
13698
13750
  BUTTON_STYLES = /* @__PURE__ */ new Set(["filled", "tonal", "outlined", "text", "elevated", "bordered", "prominent", "plain", "glass", "primary", "secondary", "tertiary", "link", "destructive"]);
@@ -14241,6 +14293,7 @@ __export(validate_descriptor_exports, {
14241
14293
  RAW_JSON_MAX_DEPTH: () => RAW_JSON_MAX_DEPTH,
14242
14294
  SERVICE_ONLY_BINDING_PROPS: () => SERVICE_ONLY_BINDING_PROPS,
14243
14295
  WHOLE_VALUE_BINDING: () => WHOLE_VALUE_BINDING,
14296
+ allowlistSweepSource: () => allowlistSweepSource,
14244
14297
  appWorkerActionsFor: () => appWorkerActionsFor,
14245
14298
  bareNestedClass: () => bareNestedClass,
14246
14299
  bootBundleWorker: () => bootBundleWorker,
@@ -14248,11 +14301,13 @@ __export(validate_descriptor_exports, {
14248
14301
  collectServiceBindings: () => collectServiceBindings,
14249
14302
  componentPropFindings: () => componentPropFindings,
14250
14303
  depthBudgetFindings: () => depthBudgetFindings,
14304
+ depthBudgetIssue: () => depthBudgetIssue,
14251
14305
  depthBudgetIssues: () => depthBudgetIssues,
14252
14306
  deviceParityWithheldFrom: () => deviceParityWithheldFrom,
14253
14307
  discoverNativeActions: () => discoverNativeActions,
14254
14308
  dryRunBundle: () => dryRunBundle,
14255
14309
  enumFieldNames: () => enumFieldNames,
14310
+ explainWithheldError: () => explainWithheldError,
14256
14311
  extractBundleContract: () => extractBundleContract,
14257
14312
  extractBundleServices: () => extractBundleServices,
14258
14313
  findSkipDerive: () => findSkipDerive,
@@ -14285,7 +14340,6 @@ __export(validate_descriptor_exports, {
14285
14340
  setDefaultEvaluator: () => setDefaultEvaluator,
14286
14341
  topBarFindings: () => topBarFindings,
14287
14342
  validateScreenDescriptor: () => validateScreenDescriptor,
14288
- withheldGlobalsSource: () => withheldGlobalsSource,
14289
14343
  workerSubtreeAllowed: () => workerSubtreeAllowed
14290
14344
  });
14291
14345
  import { readFileSync as readFileSync15, existsSync as existsSync13, readdirSync as readdirSync11 } from "node:fs";
@@ -14387,8 +14441,9 @@ var init_validate_descriptor = __esm({
14387
14441
  nodeVmEvaluator = {
14388
14442
  run(code, sandbox, opts) {
14389
14443
  const context = createContext(sandbox);
14390
- const hideSource = withheldGlobalsSource((opts.hide ?? []).filter((name) => !(name in sandbox)));
14391
- if (hideSource) runInContext(hideSource, context, { filename: "himi-hide.js" });
14444
+ if (opts.deviceParity) {
14445
+ runInContext(allowlistSweepSource(Object.keys(sandbox)), context, { filename: "himi-sweep.js" });
14446
+ }
14392
14447
  const pins = pinnedIntrinsicsSource(opts);
14393
14448
  if (pins) {
14394
14449
  const pinned = runInContext(`(${pins})(Date, Math)`, context, { timeout: opts.timeoutMs, filename: "himi-pins.js" });
@@ -16127,6 +16182,10 @@ ${opts.liveTransport ? ` // Every action completion crosses as a RAW, ordered r
16127
16182
  // minimum local overlay needed for the worker's own subsequent reads; the host replays these
16128
16183
  // records through applyResult, which remains the authoritative merge/directive dialect.
16129
16184
  var actionBody = "";
16185
+ // The host bridges, captured into this closure: under deviceParity the closing sweep deletes
16186
+ // them from globalThis, so a bundle cannot call a bridge that exists on no device (#4537).
16187
+ var hostWixCall = globalThis.__himiWixCall;
16188
+ var hostDispatch = globalThis.__himiDispatch;
16130
16189
  var isArray = Array.isArray;
16131
16190
  var promiseResolve = Promise.resolve.bind(Promise);
16132
16191
  var promiseReject = Promise.reject.bind(Promise);
@@ -16375,7 +16434,7 @@ ${opts.liveTransport ? ` var handle = { getState: function () { return def
16375
16434
  call: function (method, path, body, headers) {
16376
16435
  var raw;
16377
16436
  try {
16378
- raw = __himiWixCall(
16437
+ raw = hostWixCall(
16379
16438
  String(method),
16380
16439
  String(path),
16381
16440
  stringify(body === undefined ? null : body),
@@ -16397,7 +16456,7 @@ ${opts.liveTransport ? ` var handle = { getState: function () { return def
16397
16456
  dispatch: function (action, params) {
16398
16457
  var raw;
16399
16458
  try {
16400
- raw = __himiDispatch(stringValue(action), stringify(params === undefined ? null : params));
16459
+ raw = hostDispatch(stringValue(action), stringify(params === undefined ? null : params));
16401
16460
  } catch (e) {
16402
16461
  return promiseReject(e instanceof Error ? e : new Error(stringValue(e)));
16403
16462
  }
@@ -16526,9 +16585,11 @@ ${opts.clock === void 0 ? "" : opts.freezeDate ? ` globalThis.Date = (${pinnedI
16526
16585
  return sweepPromise;
16527
16586
  },
16528
16587
  });` : ""}
16529
- // LAST, so nothing the prelude installed (the \`fetch\` stub) or the engine carries natively
16530
- // (QuickJS-ng 0.15's \`performance\`, \`queueMicrotask\`) survives deviceParity (#4511).
16531
- ${withheldGlobalsSource(opts.hide)}
16588
+ // LAST, under deviceParity: the realm sweeps its OWN globals against the worker allow-list
16589
+ // (#4537), so nothing the prelude installed (the \`fetch\` stub), nothing the engine carries
16590
+ // natively (QuickJS-ng 0.15's \`performance\`, \`queueMicrotask\`) and none of the host bridges
16591
+ // (\`__himiWixCall\`, \`__himiDispatch\` \u2014 captured into this closure above) survives.
16592
+ ${opts.deviceParity ? allowlistSweepSource() : ""}
16532
16593
  return ${opts.liveTransport ? "{ readback: readback, dispatch: dispatchAction, actions: readActions }" : "readback"};
16533
16594
  })();
16534
16595
  `;
@@ -16597,6 +16658,7 @@ async function createQuickJsEvaluator(options = {}) {
16597
16658
  clock: opts.clock ?? options.clock,
16598
16659
  freezeDate: opts.clock !== void 0,
16599
16660
  hide: opts.hide,
16661
+ deviceParity: opts.deviceParity === true,
16600
16662
  ...opts.seed === void 0 ? {} : { seed: opts.seed },
16601
16663
  ...opts.mirrorRandom ? { mirrorRandom: true } : {}
16602
16664
  }),
@@ -132826,6 +132888,28 @@ function refuseTooDeep(screenId, descriptor) {
132826
132888
  if (finding) throw new DescriptorDepthRefusal(screenId, finding);
132827
132889
  return descriptor;
132828
132890
  }
132891
+ async function liveDescriptorFor(state, screenId) {
132892
+ try {
132893
+ const override = state.localOverrides.get(screenId);
132894
+ return {
132895
+ descriptor: override !== void 0 ? refuseTooDeep(screenId, override) : await buildDescriptorForScreenId(state, screenId, new URLSearchParams(), { skipOverride: true })
132896
+ };
132897
+ } catch (err) {
132898
+ if (err instanceof DescriptorDepthRefusal) return { refusal: err };
132899
+ throw err;
132900
+ }
132901
+ }
132902
+ function depthRefusalReport(refusal, strict = false) {
132903
+ return { screenId: refusal.screenId, ok: false, strict, issues: [depthBudgetIssue(refusal.finding)], services: [] };
132904
+ }
132905
+ function depthRefusalDiagnostics(state, refusal) {
132906
+ const legacy = { ...depthRefusalReport(refusal), bundle: null, telemetry: [], lastError: null };
132907
+ return foldLegacyReport(legacy, {
132908
+ source: "diagnostics",
132909
+ screenId: refusal.screenId,
132910
+ evidence: { runId: `dev:${state.startedAt}:${refusal.screenId}`, ts: (/* @__PURE__ */ new Date()).toISOString() }
132911
+ });
132912
+ }
132829
132913
  async function buildDescriptorForScreenId(state, screenId, query = new URLSearchParams(), options = {}) {
132830
132914
  if (!options.skipOverride) {
132831
132915
  const preview = state.localOverrides.get(screenId);
@@ -132855,13 +132939,13 @@ async function buildDescriptorForScreenId(state, screenId, query = new URLSearch
132855
132939
  }
132856
132940
  return null;
132857
132941
  })();
132858
- return built ? postprocessDescriptor(refuseTooDeep(screenId, built), {
132942
+ return built ? refuseTooDeep(screenId, postprocessDescriptor(refuseTooDeep(screenId, built), {
132859
132943
  config: uiConfig(state.app.module.config),
132860
132944
  flagDefinitions: state.flagStore.list(),
132861
132945
  evalContext: evalCtx,
132862
132946
  flagOverrides: cfg?.overrides,
132863
132947
  readBundle: tier5BundleReader(state.tier5Dir ?? TIER5_ROOT2)
132864
- }) : null;
132948
+ })) : null;
132865
132949
  }
132866
132950
  return null;
132867
132951
  }
@@ -134101,7 +134185,21 @@ async function handleDev(req, res, state) {
134101
134185
  }
134102
134186
  let descriptor = body.descriptor;
134103
134187
  if (!descriptor && body.screenId) {
134104
- descriptor = state.localOverrides.get(body.screenId) ?? await buildDescriptorForScreenId(state, body.screenId, new URLSearchParams(), { skipOverride: true });
134188
+ const live3 = await liveDescriptorFor(state, body.screenId);
134189
+ if ("refusal" in live3) {
134190
+ const diagnostics2 = depthRefusalDiagnostics(state, live3.refusal);
134191
+ const legacy2 = { screenId: body.screenId, scheme, renderable: false, reason: `depth_budget_exceeded: ${live3.refusal.finding.message}`, diagnostics: diagnostics2 };
134192
+ const report4 = foldLegacyReport(legacy2, {
134193
+ source: "render",
134194
+ screenId: body.screenId,
134195
+ ok: false,
134196
+ findings: diagnostics2.findings ?? [],
134197
+ artifacts: [],
134198
+ evidence: { runId: `dev:${state.startedAt}:${body.screenId}`, ts: (/* @__PURE__ */ new Date()).toISOString() }
134199
+ });
134200
+ return sendJson(res, body.envelope === 1 ? 422 : 200, report4);
134201
+ }
134202
+ descriptor = live3.descriptor;
134105
134203
  }
134106
134204
  if (!descriptor || typeof descriptor !== "object") {
134107
134205
  return sendJson(res, 404, { error: "screen_not_found", screenId: body.screenId ?? null });
@@ -134148,7 +134246,9 @@ async function handleDev(req, res, state) {
134148
134246
  const diagMatch = /^\/__dev\/screen\/(.+?)\/diagnostics(?:\?|$)/.exec(url);
134149
134247
  if (diagMatch && req.method === "GET") {
134150
134248
  const screenId = decodeURIComponent(diagMatch[1]);
134151
- const descriptor = state.localOverrides.get(screenId) ?? await buildDescriptorForScreenId(state, screenId, new URLSearchParams(), { skipOverride: true });
134249
+ const live3 = await liveDescriptorFor(state, screenId);
134250
+ if ("refusal" in live3) return sendJson(res, 422, depthRefusalDiagnostics(state, live3.refusal));
134251
+ const descriptor = live3.descriptor;
134152
134252
  if (!descriptor) return sendJson(res, 404, { error: "screen_not_found", screenId });
134153
134253
  const diag = await runScreenDiagnostics(state, screenId, descriptor);
134154
134254
  return sendJson(res, diag.ok ? 200 : 422, diag);
@@ -134590,7 +134690,9 @@ async function handleAdminPlayground(req, res, state) {
134590
134690
  if (validateMatch && req.method === "GET") {
134591
134691
  const screenId = decodeURIComponent(validateMatch[1]);
134592
134692
  const strict = /[?&]strict=1(?:&|$)/.test(url);
134593
- const descriptor = state.localOverrides.get(screenId) ?? await buildDescriptorForScreenId(state, screenId, new URLSearchParams(), { skipOverride: true });
134693
+ const live3 = await liveDescriptorFor(state, screenId);
134694
+ if ("refusal" in live3) return sendJson(res, 422, depthRefusalReport(live3.refusal, strict));
134695
+ const descriptor = live3.descriptor;
134594
134696
  if (!descriptor) return sendJson(res, 404, { error: "screen_not_found", screenId });
134595
134697
  const { validateScreenDescriptor: validateScreenDescriptor2, loadAllowedComponents: loadAllowedComponents2, readBuiltBundle: readBuiltBundle2, appWorkerActionsFor: appWorkerActionsFor2 } = await Promise.resolve().then(() => (init_validate_descriptor(), validate_descriptor_exports));
134596
134698
  const tokens2 = readAppTokens(state) ?? void 0;
@@ -134622,7 +134724,7 @@ async function handleAdminPlayground(req, res, state) {
134622
134724
  if (!wantBuilt) {
134623
134725
  const override = state.localOverrides.get(screenId);
134624
134726
  if (override) {
134625
- return sendJson(res, 200, { screenId, source: "override", descriptor: { ...override, screenId } });
134727
+ return sendJson(res, 200, { screenId, source: "override", descriptor: refuseTooDeep(screenId, { ...override, screenId }) });
134626
134728
  }
134627
134729
  }
134628
134730
  const built = await buildDescriptorForScreenId(state, screenId, new URLSearchParams(), { skipOverride: true });
@@ -134640,6 +134742,7 @@ async function handleAdminPlayground(req, res, state) {
134640
134742
  if (!payload.descriptor || typeof payload.descriptor !== "object") {
134641
134743
  return sendJson(res, 400, { error: "descriptor_required" });
134642
134744
  }
134745
+ refuseTooDeep(screenId, payload.descriptor);
134643
134746
  state.localOverrides.set(screenId, payload.descriptor);
134644
134747
  recordOverrideHistory(state, screenId, "put", payload.descriptor, payload.notes ?? null);
134645
134748
  invalidate(state, screenId);
@@ -135660,6 +135763,7 @@ var init_src6 = __esm({
135660
135763
  init_app_icon();
135661
135764
  init_app_assets();
135662
135765
  init_raw_json_depth();
135766
+ init_depth_budget();
135663
135767
  init_app_module();
135664
135768
  init_provisioning_facts();
135665
135769
  init_validate();
@@ -139365,6 +139469,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139365
139469
  const store = typeof dest === "string" ? new FileReleaseStore(dest) : dest;
139366
139470
  const screenIds = Object.keys(preloaded.module.screens.exact);
139367
139471
  const failedToBuild = [];
139472
+ const depthRefusals = [];
139368
139473
  const missingBundles = [];
139369
139474
  const widgetBakeFailures = [];
139370
139475
  const lint = [];
@@ -139746,6 +139851,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139746
139851
  const finding = depthRefusalFromText(res.status, text9);
139747
139852
  if (finding) {
139748
139853
  failedToBuild.push({ screenId, status: res.status, error: finding.message });
139854
+ if (!depthRefusals.some((r2) => r2.screenId === screenId)) depthRefusals.push({ screenId, finding });
139749
139855
  if (collect2?.lint && !lint.some((l2) => l2.screenId === screenId)) {
139750
139856
  lint.push({ screenId, ok: false, issues: depthBudgetIssues([finding]) });
139751
139857
  }
@@ -139850,10 +139956,11 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139850
139956
  const { services, liveCalls, liveReturned, liveErrors, mockCalls, mockUnserved, mockBootFailed, liveFailed } = await renderServicesFor(bundleName, descriptor, wantedRender);
139851
139957
  const rr2 = await renderDescriptorWeb(descriptor, renderTokens, renderScheme, collect2.render.png === true, services, collect2.render.width, collect2.render.rtl === true);
139852
139958
  renders.push({ screenId: wantedRender, scheme: renderScheme, ...rr2, ...await renderCompare(descriptor, services), ...collect2.render.dumpState && services ? { serviceState: services } : {}, ...liveCalls ? { liveCalls } : {}, ...liveReturned !== void 0 ? { liveReturned } : {}, ...liveFailed ? { liveFailed } : {}, ...liveErrors ? { liveErrors } : {}, ...mockCalls ? { mockCalls, mockUnserved } : {}, ...mockBootFailed ? { mockBootFailed } : {} });
139853
- } else if (res.status === 422 && collect2?.lint) {
139959
+ } else if (res.status === 422) {
139854
139960
  const finding = await depthRefusalOf(res);
139961
+ if (finding && !depthRefusals.some((r2) => r2.screenId === wantedRender)) depthRefusals.push({ screenId: wantedRender, finding });
139855
139962
  const recorded = lint.some((l2) => l2.screenId === wantedRender) || failedToBuild.some((f2) => f2.screenId === wantedRender);
139856
- if (finding && !recorded) lint.push({ screenId: wantedRender, ok: false, issues: depthBudgetIssues([finding]) });
139963
+ if (finding && collect2?.lint && !recorded) lint.push({ screenId: wantedRender, ok: false, issues: depthBudgetIssues([finding]) });
139857
139964
  }
139858
139965
  }
139859
139966
  for (const t2 of preloaded.module.screens.templated ?? []) {
@@ -139935,6 +140042,18 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139935
140042
  }
139936
140043
  delete descriptor.etag;
139937
140044
  if (typeof descriptor.screenId === "string" && descriptor.screenId !== screenId) descriptor.screenId = screenId;
140045
+ if (collect2?.lint) {
140046
+ const depth = depthBudgetIssues(screenDepthFindings(descriptor));
140047
+ const existing = lint.find((l2) => l2.screenId === screenId);
140048
+ const seen = new Set((existing?.issues ?? []).filter((i2) => i2.pass === "depth-budget").map((i2) => `${i2.path}|${i2.limit}`));
140049
+ const fresh = depth.filter((i2) => !seen.has(`${i2.path}|${i2.limit}`));
140050
+ if (fresh.length && existing) {
140051
+ existing.issues.push(...fresh);
140052
+ existing.ok = false;
140053
+ } else if (fresh.length) {
140054
+ lint.push({ screenId, ok: false, issues: fresh });
140055
+ }
140056
+ }
139938
140057
  const json7 = pinBakedServerBaseUrl(JSON.stringify(descriptor), base);
139939
140058
  await store.putScreen(app, screenId, json7);
139940
140059
  screens[screenId] = { descriptorHash: sha2564(json7), bundle: bundleName, ...screenMetadataFor(screenMetadata, screenId, canonical4) };
@@ -140148,6 +140267,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140148
140267
  return {
140149
140268
  manifest,
140150
140269
  failedToBuild,
140270
+ depthRefusals,
140151
140271
  missingBundles,
140152
140272
  // Only bundles a screen actually referenced are weighed — `tierBundles` is filled by
140153
140273
  // addressBundle, which runs on reference, so an unreferenced file on disk never counts.
@@ -140214,7 +140334,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
140214
140334
  const sha2 = gitSha();
140215
140335
  const manifest = { releaseId: contentReleaseId(content), gitSha: sha2, ...content };
140216
140336
  await store.putManifest(app, manifest);
140217
- return { manifest, failedToBuild: [], missingBundles: [], auth: {}, tierCapViolations: [] };
140337
+ return { manifest, failedToBuild: [], depthRefusals: [], missingBundles: [], auth: {}, tierCapViolations: [] };
140218
140338
  }
140219
140339
  function listBuildableApps() {
140220
140340
  const appsDir = join47(REPO_ROOT7, "apps");
@@ -163757,6 +163877,8 @@ function createReviewScreenRenderer() {
163757
163877
  collect: { render: { screenId, scheme: "light", png: false, hydrate: true, live: true, dumpState: true, width: REVIEW_SCREENSHOT_FRAME.width, host, ...input ? { input } : {} } }
163758
163878
  });
163759
163879
  const render = (outcome.renders ?? [])[0];
163880
+ const refused2 = outcome.depthRefusals.find((r2) => r2.screenId === screenId);
163881
+ if (!render && refused2) throw new Error(`${screenId} cannot be rendered: ${refused2.finding.message}`);
163760
163882
  if (!render) throw new MissingScreenError(screenId);
163761
163883
  if (!render.renderable || !render.html) throw new Error(render.reason ?? `${screenId} is not an SDUI screen`);
163762
163884
  return { html: render.html, problems: renderProblems(render, entityId), state: render.serviceState ?? {} };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1423.0",
3
+ "version": "0.1425.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "843d4fd1bc22694e9fd665efd8f59bb6fd4e4e9aa78612885a3ce661"
74
+ "falconPackageHash": "1ad8e9d41b385905df3fb24715044289df3fb2389c6b704c4d14e96d"
75
75
  }