@wix/himalaya-cli 0.1418.0 → 0.1420.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
@@ -10096,7 +10096,10 @@ var validate_descriptor_core_exports = {};
10096
10096
  __export(validate_descriptor_core_exports, {
10097
10097
  CORE_PACKAGES: () => CORE_PACKAGES,
10098
10098
  CRAWL_BLOCKING_PASSES: () => CRAWL_BLOCKING_PASSES,
10099
+ DESCRIPTOR_DEPTH_BUDGETS: () => DESCRIPTOR_DEPTH_BUDGETS,
10100
+ DEVICE_NETWORK_WITHHELD: () => DEVICE_NETWORK_WITHHELD,
10099
10101
  DEVICE_PARITY_WITHHELD: () => DEVICE_PARITY_WITHHELD,
10102
+ DEVICE_WITHHELD: () => DEVICE_WITHHELD,
10100
10103
  EMBEDDED_BINDING: () => EMBEDDED_BINDING,
10101
10104
  INTEGER_MODIFIERS: () => INTEGER_MODIFIERS,
10102
10105
  INTEGER_PROPS: () => INTEGER_PROPS,
@@ -10118,6 +10121,8 @@ __export(validate_descriptor_core_exports, {
10118
10121
  bootBundleWorker: () => bootBundleWorker,
10119
10122
  collectServiceBindings: () => collectServiceBindings,
10120
10123
  componentPropFindings: () => componentPropFindings,
10124
+ depthBudgetFindings: () => depthBudgetFindings,
10125
+ depthBudgetIssues: () => depthBudgetIssues,
10121
10126
  dryRunBundle: () => dryRunBundle,
10122
10127
  enumFieldNames: () => enumFieldNames,
10123
10128
  extractBundleContract: () => extractBundleContract,
@@ -10473,9 +10478,12 @@ async function bootBundleWorker(bundleJs, opts) {
10473
10478
  }, info() {
10474
10479
  }, debug() {
10475
10480
  } },
10476
- // A raw fetch is still a signal, not a silent hang (mirrors runBundleSandbox).
10477
- fetch: () => {
10478
- throw new Error("`fetch` is not available \u2014 workers load data via the sanctioned Wix transport (`@himalaya/wix-*` or `globalThis.himalaya.wixApi.call`), not raw `fetch`.");
10481
+ // A raw fetch is still a signal, not a silent hang (mirrors runBundleSandbox). Under
10482
+ // deviceParity it is ABSENT instead, as on every device (#4499 — DEVICE_NETWORK_WITHHELD).
10483
+ ...opts.deviceParity ? {} : {
10484
+ fetch: () => {
10485
+ throw new Error("`fetch` is not available \u2014 workers load data via the sanctioned Wix transport (`@himalaya/wix-*` or `globalThis.himalaya.wixApi.call`), not raw `fetch`.");
10486
+ }
10479
10487
  },
10480
10488
  // Under deviceParity these are ABSENT so a bundle touching one throws here exactly as it
10481
10489
  // would on the FLOOR. Measured, not assumed (engine-parity runners, #1367): JSC and the
@@ -10522,7 +10530,7 @@ async function bootBundleWorker(bundleJs, opts) {
10522
10530
  let guestActions;
10523
10531
  let guestSession;
10524
10532
  try {
10525
- const hide = opts.deviceParity ? DEVICE_PARITY_WITHHELD : void 0;
10533
+ const hide = opts.deviceParity ? DEVICE_WITHHELD : void 0;
10526
10534
  if (realm.runWithActions) {
10527
10535
  const ran = await realm.runWithActions(bundleJs, sandbox, {
10528
10536
  timeoutMs: 2e3 + budgetMs,
@@ -11612,6 +11620,100 @@ function bottomSheetFindings(descriptor) {
11612
11620
  visit(descriptor, "", true);
11613
11621
  return out2;
11614
11622
  }
11623
+ function depthBudgetIssues(findings) {
11624
+ return findings.map((f2) => ({
11625
+ severity: "error",
11626
+ pass: "depth-budget",
11627
+ path: f2.path,
11628
+ message: f2.message,
11629
+ limit: f2.limit,
11630
+ actual: f2.actual
11631
+ }));
11632
+ }
11633
+ function depthBudgetFindings(descriptor) {
11634
+ const out2 = [];
11635
+ const templates = [];
11636
+ const isComponent = (v2) => {
11637
+ const body = v2?.body;
11638
+ return !!body && typeof body === "object" && typeof body.type === "string" && typeof body.package === "string";
11639
+ };
11640
+ const hostedTemplates = (v2, path2) => {
11641
+ const props = v2.body.props;
11642
+ const found = [];
11643
+ if (!props) return found;
11644
+ const itemsPath = props.itemsPath ?? props.items_path;
11645
+ const rowHost = v2.body.package === "himalaya" ? ROW_TEMPLATE_HOST_TYPES.has(v2.body.type) : true;
11646
+ if (rowHost && typeof itemsPath === "string" && itemsPath !== "" && Array.isArray(props.items) && isComponent(props.items[0])) {
11647
+ found.push({ node: props.items[0], path: `${path2}.body.props.items[0]`, host: `${v2.body.type} at ${path2}` });
11648
+ }
11649
+ if (isComponent(props.eventTemplate)) {
11650
+ found.push({ node: props.eventTemplate, path: `${path2}.body.props.eventTemplate`, host: `${v2.body.type} at ${path2}` });
11651
+ }
11652
+ return found;
11653
+ };
11654
+ const measure2 = (root, rootPath, mode) => {
11655
+ let depth = 0, nodes = 0, deepest = rootPath;
11656
+ const skip = /* @__PURE__ */ new Set();
11657
+ const stack = [
11658
+ { v: root, path: rootPath, depth: 0, atRoot: mode === "screen" }
11659
+ ];
11660
+ while (stack.length) {
11661
+ const { v: v2, path: path2, depth: parentDepth, atRoot } = stack.pop();
11662
+ if (skip.has(v2)) continue;
11663
+ if (Array.isArray(v2)) {
11664
+ for (let i2 = v2.length - 1; i2 >= 0; i2--) stack.push({ v: v2[i2], path: `${path2}[${i2}]`, depth: parentDepth, atRoot: false });
11665
+ continue;
11666
+ }
11667
+ if (!v2 || typeof v2 !== "object") continue;
11668
+ let here = parentDepth;
11669
+ if (isComponent(v2)) {
11670
+ here = parentDepth + 1;
11671
+ nodes += 1;
11672
+ if (here > depth) {
11673
+ depth = here;
11674
+ deepest = path2;
11675
+ }
11676
+ for (const t2 of hostedTemplates(v2, path2)) {
11677
+ if (mode === "screen") templates.push(t2);
11678
+ else skip.add(t2.node);
11679
+ }
11680
+ }
11681
+ const entries = Object.entries(v2);
11682
+ for (let i2 = entries.length - 1; i2 >= 0; i2--) {
11683
+ const [key2, child] = entries[i2];
11684
+ if (!child || typeof child !== "object" || isOpaqueAuthorJson(key2, child, atRoot)) continue;
11685
+ stack.push({ v: child, path: `${path2}.${key2}`, depth: here, atRoot: false });
11686
+ }
11687
+ }
11688
+ return { depth, nodes, deepest };
11689
+ };
11690
+ const flatten = "flatten the layout \u2014 drop wrapper stacks, or move a section into its own screen";
11691
+ const report3 = (what, m2, budget, rootPath) => {
11692
+ if (m2.depth > budget.maxDepth) {
11693
+ out2.push({
11694
+ measure: "depth",
11695
+ path: m2.deepest,
11696
+ limit: budget.maxDepth,
11697
+ actual: m2.depth,
11698
+ message: `${what} nests ${m2.depth} component levels; the limit is ${budget.maxDepth} (deepest node at ${m2.deepest}) \u2014 ${flatten}`
11699
+ });
11700
+ }
11701
+ if (m2.nodes > budget.maxNodes) {
11702
+ out2.push({
11703
+ measure: "nodes",
11704
+ path: rootPath,
11705
+ limit: budget.maxNodes,
11706
+ actual: m2.nodes,
11707
+ message: `${what} has ${m2.nodes} components; the limit is ${budget.maxNodes} \u2014 ${flatten}`
11708
+ });
11709
+ }
11710
+ };
11711
+ report3("screen", measure2(descriptor, "$", "screen"), DESCRIPTOR_DEPTH_BUDGETS.screen, "$");
11712
+ for (const t2 of templates) {
11713
+ report3(`row template of the ${t2.host}`, measure2(t2.node, t2.path, "template"), DESCRIPTOR_DEPTH_BUDGETS.rowTemplate, t2.path);
11714
+ }
11715
+ return out2;
11716
+ }
11615
11717
  function topBarFindings(descriptor) {
11616
11718
  const out2 = [];
11617
11719
  const bar = descriptor?.topBar;
@@ -11763,6 +11865,11 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
11763
11865
  const issues = [];
11764
11866
  const strict = opts.strict ?? false;
11765
11867
  const hygiene = strict ? "error" : "warning";
11868
+ const depthFindings = depthBudgetFindings(descriptor);
11869
+ const depthIssues = depthBudgetIssues(depthFindings);
11870
+ if (depthFindings.some((f2) => f2.measure === "depth" && f2.actual > UNSAFE_TO_RECURSE_DEPTH)) {
11871
+ return { screenId, ok: false, strict, issues: depthIssues, services: [] };
11872
+ }
11766
11873
  const fixture = new FixtureValidator({ tokens: opts.tokens, allowedComponents: opts.allowedComponents });
11767
11874
  const v2 = fixture.validate(descriptor);
11768
11875
  const lbIntegrity = descriptor?.logicBundle?.integrity;
@@ -11866,6 +11973,7 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
11866
11973
  for (const finding of topBarFindings(descriptor)) {
11867
11974
  issues.push({ severity: "warning", pass: "top-bar", path: finding.path, message: finding.message });
11868
11975
  }
11976
+ issues.push(...depthIssues);
11869
11977
  const bindings = collectServiceBindings(descriptor);
11870
11978
  const descriptorActions = collectDescriptorActions(descriptor);
11871
11979
  const workerActions = descriptorActions.filter(({ actionId }) => actionId.startsWith("tier5."));
@@ -12365,7 +12473,7 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
12365
12473
  services: Object.keys(services)
12366
12474
  };
12367
12475
  }
12368
- var CRAWL_BLOCKING_PASSES, BOOT_EVAL_TIMEOUT_MS, DRIFT_PROBES, 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, TOP_BAR_TRAILING_LIMIT, TOP_BAR_NON_TRAILING, BUTTON_STYLES, EMPTY_STATE_ACTION_STYLES, EMPTY_STATE_ACTION_SIZES;
12476
+ var CRAWL_BLOCKING_PASSES, BOOT_EVAL_TIMEOUT_MS, DRIFT_PROBES, DEVICE_PARITY_WITHHELD, DEVICE_NETWORK_WITHHELD, DEVICE_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;
12369
12477
  var init_validate_descriptor_core = __esm({
12370
12478
  "../../core/dev-server/src/validate-descriptor.core.ts"() {
12371
12479
  init_validator();
@@ -12379,6 +12487,7 @@ var init_validate_descriptor_core = __esm({
12379
12487
  init_media_value();
12380
12488
  init_frame_limits();
12381
12489
  init_rich_content();
12490
+ init_worker_subtree();
12382
12491
  CRAWL_BLOCKING_PASSES = /* @__PURE__ */ new Set([
12383
12492
  // A screen whose bindings do not resolve has no state to crawl.
12384
12493
  "bindings",
@@ -12386,7 +12495,11 @@ var init_validate_descriptor_core = __esm({
12386
12495
  "worker-subtree",
12387
12496
  // A value outside a strict native enum decodes to nothing on device, so the crawl would be
12388
12497
  // exercising a screen the device cannot render.
12389
- "strict-enum"
12498
+ "strict-enum",
12499
+ // #4498 — a screen or row template over the nesting/node budget overflows a native stack on
12500
+ // decode/render, so there is no screen to crawl. (The publish refusal is separate: the pass is
12501
+ // always an error, and `himi push` refuses a depth-budget error even under `--force`.)
12502
+ "depth-budget"
12390
12503
  ]);
12391
12504
  BOOT_EVAL_TIMEOUT_MS = 2e3;
12392
12505
  DRIFT_PROBES = [
@@ -12406,6 +12519,25 @@ var init_validate_descriptor_core = __esm({
12406
12519
  "TextDecoder",
12407
12520
  "AbortController"
12408
12521
  ];
12522
+ DEVICE_NETWORK_WITHHELD = [
12523
+ "fetch",
12524
+ "XMLHttpRequest",
12525
+ "WebSocket",
12526
+ "EventSource",
12527
+ "importScripts",
12528
+ "WebTransport",
12529
+ "RTCPeerConnection",
12530
+ "webkitRTCPeerConnection",
12531
+ "Worker",
12532
+ "SharedWorker",
12533
+ "Image",
12534
+ "Audio",
12535
+ "open",
12536
+ "Request",
12537
+ "Response",
12538
+ "Headers"
12539
+ ];
12540
+ DEVICE_WITHHELD = [...DEVICE_NETWORK_WITHHELD, ...DEVICE_PARITY_WITHHELD];
12409
12541
  defaultEvaluator = null;
12410
12542
  bootEvaluator = null;
12411
12543
  actionEvaluator = null;
@@ -13358,6 +13490,12 @@ var init_validate_descriptor_core = __esm({
13358
13490
  OPTIONS_BINDING_PROPS = /* @__PURE__ */ new Set(["optionsPath"]);
13359
13491
  EDITOR_SERVICE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
13360
13492
  SHEET_PLACEMENTS = /* @__PURE__ */ new Set(["overlay", "inline"]);
13493
+ DESCRIPTOR_DEPTH_BUDGETS = Object.freeze({
13494
+ screen: Object.freeze({ maxDepth: 40, maxNodes: 2e3 }),
13495
+ rowTemplate: Object.freeze({ maxDepth: WORKER_SUBTREE_CEILINGS.maxDepth, maxNodes: WORKER_SUBTREE_CEILINGS.maxNodes })
13496
+ });
13497
+ UNSAFE_TO_RECURSE_DEPTH = 128;
13498
+ ROW_TEMPLATE_HOST_TYPES = /* @__PURE__ */ new Set(["LazyList", "Grid", "Carousel", "Menu"]);
13361
13499
  TOP_BAR_TRAILING_LIMIT = 2;
13362
13500
  TOP_BAR_NON_TRAILING = /* @__PURE__ */ new Set(["leading", "principal", "bottom", "keyboard", "overflow"]);
13363
13501
  BUTTON_STYLES = /* @__PURE__ */ new Set(["filled", "tonal", "outlined", "text", "elevated", "bordered", "prominent", "plain", "glass", "primary", "secondary", "tertiary", "link", "destructive"]);
@@ -13882,7 +14020,10 @@ var validate_descriptor_exports = {};
13882
14020
  __export(validate_descriptor_exports, {
13883
14021
  CORE_PACKAGES: () => CORE_PACKAGES,
13884
14022
  CRAWL_BLOCKING_PASSES: () => CRAWL_BLOCKING_PASSES,
14023
+ DESCRIPTOR_DEPTH_BUDGETS: () => DESCRIPTOR_DEPTH_BUDGETS,
14024
+ DEVICE_NETWORK_WITHHELD: () => DEVICE_NETWORK_WITHHELD,
13885
14025
  DEVICE_PARITY_WITHHELD: () => DEVICE_PARITY_WITHHELD,
14026
+ DEVICE_WITHHELD: () => DEVICE_WITHHELD,
13886
14027
  EMBEDDED_BINDING: () => EMBEDDED_BINDING,
13887
14028
  INTEGER_MODIFIERS: () => INTEGER_MODIFIERS,
13888
14029
  INTEGER_PROPS: () => INTEGER_PROPS,
@@ -13906,6 +14047,8 @@ __export(validate_descriptor_exports, {
13906
14047
  bundleNameFromUrl: () => bundleNameFromUrl,
13907
14048
  collectServiceBindings: () => collectServiceBindings,
13908
14049
  componentPropFindings: () => componentPropFindings,
14050
+ depthBudgetFindings: () => depthBudgetFindings,
14051
+ depthBudgetIssues: () => depthBudgetIssues,
13909
14052
  discoverNativeActions: () => discoverNativeActions,
13910
14053
  dryRunBundle: () => dryRunBundle,
13911
14054
  enumFieldNames: () => enumFieldNames,
@@ -16083,6 +16226,9 @@ ${opts.liveTransport ? ` var handle = { getState: function () { return def
16083
16226
  globalThis.fetch = function () {
16084
16227
  throw new Error("\`fetch\` is not available in the author-time sandbox \u2014 workers load data via the sanctioned Wix transport (\`@himalaya/wix-*\` or \`globalThis.himalaya.wixApi.call\`), not raw \`fetch\`.");
16085
16228
  };
16229
+ // ...unless \`hide\` withholds it (deviceParity, #4499): every device has NO \`fetch\`, so a
16230
+ // stub here would let a \`typeof fetch === "function"\` branch pass and fail on the device.
16231
+ ${(opts.hide ?? []).includes("fetch") ? " delete globalThis.fetch;" : ""}
16086
16232
  // Timers honour \`hide\`. Installing them unconditionally made \`deviceParity\` report
16087
16233
  // \`typeof setTimeout === "function"\` where node:vm correctly reported "undefined" \u2014 a FALSE
16088
16234
  // GREEN on exactly the floor gate this realm exists to enforce. The URL family is never
@@ -141168,7 +141314,8 @@ var init_push_errors = __esm({
141168
141314
  "not_carried_by_patch",
141169
141315
  "authoring_unavailable",
141170
141316
  "push_failed",
141171
- "binary_capability_gap"
141317
+ "binary_capability_gap",
141318
+ "depth_budget_exceeded"
141172
141319
  ];
141173
141320
  UX_CODES = {
141174
141321
  absent: "ux_evidence_absent",
@@ -180506,6 +180653,10 @@ function describeNondeterministicWorkers(workers) {
180506
180653
  return workers.length <= 1 ? workers.map(one).join("") : `${workers.slice(0, -1).map(one).join(", ")} and ${one(workers[workers.length - 1])}`;
180507
180654
  }
180508
180655
  var NONDETERMINISTIC_FIX = "move those clock or random values out of the service's initial state and compute them in an action instead (for example one the screen's onAppear dispatches)";
180656
+ function depthBudgetExceeded(report3) {
180657
+ return (report3.issues ?? []).some((i2) => i2.severity === "error" && i2.pass === "depth-budget");
180658
+ }
180659
+ var DEPTH_BUDGET_HINT = "a screen or row template is over the depth budget (see the depth-budget issues' path and limit) \u2014 flatten it; `--force` does not override this";
180509
180660
  function summarizeLint(app, bres, outcome, strict, extraIssues = []) {
180510
180661
  const issues = (outcome.lint ?? []).flatMap((s2) => s2.issues.map((i2) => ({ screen: s2.screenId, ...i2 })));
180511
180662
  issues.push(...extraIssues);
@@ -184630,6 +184781,10 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
184630
184781
  ...await navIssues2(buildDir, strict),
184631
184782
  ...scaffoldSourceFindings
184632
184783
  ]);
184784
+ if (depthBudgetExceeded(report3)) {
184785
+ print({ ...report3, published: false, code: "depth_budget_exceeded", hint: DEPTH_BUDGET_HINT });
184786
+ return 1;
184787
+ }
184633
184788
  if (hasErrors && !force) {
184634
184789
  print({ ...report3, published: false, hint: "lint found errors \u2014 fix them, or re-run with --force to publish anyway" });
184635
184790
  return 1;
@@ -186445,6 +186600,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
186445
186600
  ...storesV1Issues(dir, strict),
186446
186601
  ...await navIssues2(dir, strict)
186447
186602
  ]);
186603
+ if (depthBudgetExceeded(report3)) {
186604
+ print(pushFailure2("depth_budget_exceeded", { ...report3, pushed: false, hint: DEPTH_BUDGET_HINT }));
186605
+ return 1;
186606
+ }
186448
186607
  if (hasErrors && !force) {
186449
186608
  print(pushFailure2("lint_errors", { ...report3, pushed: false, hint: "lint found errors \u2014 fix them, or re-run `himi push --force` to publish anyway" }));
186450
186609
  return 1;
@@ -187012,6 +187171,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
187012
187171
  ...storesV1Issues(dir, strict),
187013
187172
  ...await navIssues2(dir, strict)
187014
187173
  ]);
187174
+ if (depthBudgetExceeded(report3)) {
187175
+ rmSync24(built, { recursive: true, force: true });
187176
+ print({ ...report3, packed: false, code: "depth_budget_exceeded", hint: DEPTH_BUDGET_HINT });
187177
+ return 1;
187178
+ }
187015
187179
  if (hasErrors && !force) {
187016
187180
  rmSync24(built, { recursive: true, force: true });
187017
187181
  print({ ...report3, packed: false, hint: "lint found errors \u2014 fix them, or re-run `himi pack --force` to pack anyway" });
@@ -188143,12 +188307,29 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
188143
188307
  return 2;
188144
188308
  }
188145
188309
  const descriptor = readFileSync103(file2, "utf8");
188310
+ let parsed;
188146
188311
  try {
188147
- JSON.parse(descriptor);
188312
+ parsed = JSON.parse(descriptor);
188148
188313
  } catch (e2) {
188149
188314
  io2.error(JSON.stringify({ ok: false, error: "invalid_descriptor", file: file2, screen: id, message: `${file2} is not valid JSON: ${e2.message}` }));
188150
188315
  return 1;
188151
188316
  }
188317
+ const { depthBudgetFindings: depthBudgetFindings2, depthBudgetIssues: depthBudgetIssues2 } = await Promise.resolve().then(() => (init_validate_descriptor_core(), validate_descriptor_core_exports));
188318
+ const tooDeep = depthBudgetIssues2(depthBudgetFindings2(parsed));
188319
+ if (tooDeep.length > 0) {
188320
+ io2.error(JSON.stringify({
188321
+ ok: false,
188322
+ code: "depth_budget_exceeded",
188323
+ error: "depth_budget_exceeded",
188324
+ patched: false,
188325
+ app,
188326
+ screen: id,
188327
+ file: file2,
188328
+ issues: tooDeep,
188329
+ hint: "flatten the descriptor below the depth budget (see each issue's path and limit); `--force` does not override this"
188330
+ }, null, 2));
188331
+ return 1;
188332
+ }
188152
188333
  const baseFound = await fetchReleaseManifest(client2, app, fromRelease);
188153
188334
  if ("error" in baseFound) {
188154
188335
  io2.error(JSON.stringify(baseFound));
@@ -47,6 +47,7 @@ keeps its existing fields and adds the code:
47
47
  | `ux_evidence_inherited_screens` | A partial push inherits screens not covered by local UX evidence. |
48
48
  | `ux_evidence_unreadable` | The UX evidence artifact cannot be read. |
49
49
  | `lint_errors` | Build/lint validation found errors. |
50
+ | `depth_budget_exceeded` | A screen or a bound list/grid row template nests deeper, or has more components, than the depth budget (`depth-budget` validator pass). Each `issues[]` entry with `pass: "depth-budget"` carries the JSON `path` of the deepest node (or the tree root for a node count), the `limit` and the `actual` value. Takes precedence over `lint_errors`, and `--force` does not override it: content over the budget overflows the renderer's stack on devices already in the field. Flatten the layout and push again. `himi patch screen` refuses an over-deep raw descriptor with the same code, before any network request, and `himi pack` / `himi template publish` refuse with it even under `--force`. |
50
51
  | `control_token_expired` | The selected control token has expired. |
51
52
  | `app_source_contains_secrets` | Source archiving detected a possible credential. |
52
53
  | `app_source_too_large` | The encoded source archive exceeds its limit. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1418.0",
3
+ "version": "0.1420.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": "94d6b78b2f9a4cc26ea336364cbbfa4d015375f8a456cf857359fa46"
74
+ "falconPackageHash": "d2a780fce1a8d60a36a6f5cf4f8860d5d454764049e49a0fa39c2885"
75
75
  }