@wix/himalaya-cli 0.1424.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 +183 -116
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -10225,6 +10225,115 @@ var init_raw_json_depth = __esm({
10225
10225
  }
10226
10226
  });
10227
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
+
10228
10337
  // ../../core/dev-server/src/realm-parity.ts
10229
10338
  function allowlistSweepSource(keep = []) {
10230
10339
  const key2 = [...keep].sort().join(",");
@@ -10302,6 +10411,7 @@ __export(validate_descriptor_core_exports, {
10302
10411
  collectServiceBindings: () => collectServiceBindings,
10303
10412
  componentPropFindings: () => componentPropFindings,
10304
10413
  depthBudgetFindings: () => depthBudgetFindings,
10414
+ depthBudgetIssue: () => depthBudgetIssue,
10305
10415
  depthBudgetIssues: () => depthBudgetIssues,
10306
10416
  deviceParityWithheldFrom: () => deviceParityWithheldFrom,
10307
10417
  dryRunBundle: () => dryRunBundle,
@@ -11094,10 +11204,6 @@ function ellipsis(value, max) {
11094
11204
  function own3(table, key2) {
11095
11205
  return Object.prototype.hasOwnProperty.call(table, key2) ? table[key2] : void 0;
11096
11206
  }
11097
- function isOpaqueAuthorJson(key2, value, atRoot) {
11098
- if (value === null || typeof value !== "object") return false;
11099
- return key2 === "args" || atRoot && key2 === "input";
11100
- }
11101
11207
  function pathCrossesOpaqueAuthorJson(path2) {
11102
11208
  if (typeof path2 !== "string") return false;
11103
11209
  return /^\$\.input(?:[.[]|$)/.test(path2) || /\.args(?:[.[]|$)/.test(path2);
@@ -11828,103 +11934,8 @@ function bottomSheetFindings(descriptor) {
11828
11934
  visit(descriptor, "", true);
11829
11935
  return out2;
11830
11936
  }
11831
- function screenDepthFindings(descriptor) {
11832
- const raw = rawJsonDepthFinding(descriptor);
11833
- return raw ? [raw] : depthBudgetFindings(descriptor);
11834
- }
11835
11937
  function depthBudgetIssues(findings) {
11836
- return findings.map((f2) => ({
11837
- severity: "error",
11838
- pass: "depth-budget",
11839
- path: f2.path,
11840
- message: f2.message,
11841
- limit: f2.limit,
11842
- actual: f2.actual
11843
- }));
11844
- }
11845
- function depthBudgetFindings(descriptor) {
11846
- const out2 = [];
11847
- const templates = [];
11848
- const isComponent = (v2) => {
11849
- const body = v2?.body;
11850
- return !!body && typeof body === "object" && typeof body.type === "string" && typeof body.package === "string";
11851
- };
11852
- const hostedTemplates = (v2, path2) => {
11853
- const props = v2.body.props;
11854
- const found = [];
11855
- if (!props) return found;
11856
- const itemsPath = props.itemsPath ?? props.items_path;
11857
- const rowHost = v2.body.package === "himalaya" ? ROW_TEMPLATE_HOST_TYPES.has(v2.body.type) : true;
11858
- if (rowHost && typeof itemsPath === "string" && itemsPath !== "" && Array.isArray(props.items) && isComponent(props.items[0])) {
11859
- found.push({ node: props.items[0], path: `${path2}.body.props.items[0]`, host: `${v2.body.type} at ${path2}` });
11860
- }
11861
- if (isComponent(props.eventTemplate)) {
11862
- found.push({ node: props.eventTemplate, path: `${path2}.body.props.eventTemplate`, host: `${v2.body.type} at ${path2}` });
11863
- }
11864
- return found;
11865
- };
11866
- const measure2 = (root, rootPath, mode) => {
11867
- let depth = 0, nodes = 0, deepest = rootPath;
11868
- const skip = /* @__PURE__ */ new Set();
11869
- const stack = [
11870
- { v: root, path: rootPath, depth: 0, atRoot: mode === "screen" }
11871
- ];
11872
- while (stack.length) {
11873
- const { v: v2, path: path2, depth: parentDepth, atRoot } = stack.pop();
11874
- if (skip.has(v2)) continue;
11875
- if (Array.isArray(v2)) {
11876
- for (let i2 = v2.length - 1; i2 >= 0; i2--) stack.push({ v: v2[i2], path: `${path2}[${i2}]`, depth: parentDepth, atRoot: false });
11877
- continue;
11878
- }
11879
- if (!v2 || typeof v2 !== "object") continue;
11880
- let here = parentDepth;
11881
- if (isComponent(v2)) {
11882
- here = parentDepth + 1;
11883
- nodes += 1;
11884
- if (here > depth) {
11885
- depth = here;
11886
- deepest = path2;
11887
- }
11888
- for (const t2 of hostedTemplates(v2, path2)) {
11889
- if (mode === "screen") templates.push(t2);
11890
- else skip.add(t2.node);
11891
- }
11892
- }
11893
- const entries = Object.entries(v2);
11894
- for (let i2 = entries.length - 1; i2 >= 0; i2--) {
11895
- const [key2, child] = entries[i2];
11896
- if (!child || typeof child !== "object" || isOpaqueAuthorJson(key2, child, atRoot)) continue;
11897
- stack.push({ v: child, path: `${path2}.${key2}`, depth: here, atRoot: false });
11898
- }
11899
- }
11900
- return { depth, nodes, deepest };
11901
- };
11902
- const flatten = "flatten the layout \u2014 drop wrapper stacks, or move a section into its own screen";
11903
- const report3 = (what, m2, budget, rootPath) => {
11904
- if (m2.depth > budget.maxDepth) {
11905
- out2.push({
11906
- measure: "depth",
11907
- path: m2.deepest,
11908
- limit: budget.maxDepth,
11909
- actual: m2.depth,
11910
- message: `${what} nests ${m2.depth} component levels; the limit is ${budget.maxDepth} (deepest node at ${m2.deepest}) \u2014 ${flatten}`
11911
- });
11912
- }
11913
- if (m2.nodes > budget.maxNodes) {
11914
- out2.push({
11915
- measure: "nodes",
11916
- path: rootPath,
11917
- limit: budget.maxNodes,
11918
- actual: m2.nodes,
11919
- message: `${what} has ${m2.nodes} components; the limit is ${budget.maxNodes} \u2014 ${flatten}`
11920
- });
11921
- }
11922
- };
11923
- report3("screen", measure2(descriptor, "$", "screen"), DESCRIPTOR_DEPTH_BUDGETS.screen, "$");
11924
- for (const t2 of templates) {
11925
- report3(`row template of the ${t2.host}`, measure2(t2.node, t2.path, "template"), DESCRIPTOR_DEPTH_BUDGETS.rowTemplate, t2.path);
11926
- }
11927
- return out2;
11938
+ return findings.map(depthBudgetIssue);
11928
11939
  }
11929
11940
  function topBarFindings(descriptor) {
11930
11941
  const out2 = [];
@@ -12685,7 +12696,7 @@ function validateScreenDescriptor(screenId, descriptor, opts = {}) {
12685
12696
  services: Object.keys(services)
12686
12697
  };
12687
12698
  }
12688
- 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;
12689
12700
  var init_validate_descriptor_core = __esm({
12690
12701
  "../../core/dev-server/src/validate-descriptor.core.ts"() {
12691
12702
  init_validator();
@@ -12700,7 +12711,7 @@ var init_validate_descriptor_core = __esm({
12700
12711
  init_media_value();
12701
12712
  init_frame_limits();
12702
12713
  init_rich_content();
12703
- init_worker_subtree();
12714
+ init_depth_budget();
12704
12715
  init_raw_json_depth();
12705
12716
  init_realm_parity();
12706
12717
  init_realm_parity();
@@ -13733,12 +13744,7 @@ var init_validate_descriptor_core = __esm({
13733
13744
  OPTIONS_BINDING_PROPS = /* @__PURE__ */ new Set(["optionsPath"]);
13734
13745
  EDITOR_SERVICE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
13735
13746
  SHEET_PLACEMENTS = /* @__PURE__ */ new Set(["overlay", "inline"]);
13736
- DESCRIPTOR_DEPTH_BUDGETS = Object.freeze({
13737
- screen: Object.freeze({ maxDepth: 40, maxNodes: 2e3 }),
13738
- rowTemplate: Object.freeze({ maxDepth: WORKER_SUBTREE_CEILINGS.maxDepth, maxNodes: WORKER_SUBTREE_CEILINGS.maxNodes })
13739
- });
13740
13747
  UNSAFE_TO_RECURSE_DEPTH = 128;
13741
- ROW_TEMPLATE_HOST_TYPES = /* @__PURE__ */ new Set(["LazyList", "Grid", "Carousel", "Menu"]);
13742
13748
  TOP_BAR_TRAILING_LIMIT = 2;
13743
13749
  TOP_BAR_NON_TRAILING = /* @__PURE__ */ new Set(["leading", "principal", "bottom", "keyboard", "overflow"]);
13744
13750
  BUTTON_STYLES = /* @__PURE__ */ new Set(["filled", "tonal", "outlined", "text", "elevated", "bordered", "prominent", "plain", "glass", "primary", "secondary", "tertiary", "link", "destructive"]);
@@ -14295,6 +14301,7 @@ __export(validate_descriptor_exports, {
14295
14301
  collectServiceBindings: () => collectServiceBindings,
14296
14302
  componentPropFindings: () => componentPropFindings,
14297
14303
  depthBudgetFindings: () => depthBudgetFindings,
14304
+ depthBudgetIssue: () => depthBudgetIssue,
14298
14305
  depthBudgetIssues: () => depthBudgetIssues,
14299
14306
  deviceParityWithheldFrom: () => deviceParityWithheldFrom,
14300
14307
  discoverNativeActions: () => discoverNativeActions,
@@ -132881,6 +132888,28 @@ function refuseTooDeep(screenId, descriptor) {
132881
132888
  if (finding) throw new DescriptorDepthRefusal(screenId, finding);
132882
132889
  return descriptor;
132883
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
+ }
132884
132913
  async function buildDescriptorForScreenId(state, screenId, query = new URLSearchParams(), options = {}) {
132885
132914
  if (!options.skipOverride) {
132886
132915
  const preview = state.localOverrides.get(screenId);
@@ -132910,13 +132939,13 @@ async function buildDescriptorForScreenId(state, screenId, query = new URLSearch
132910
132939
  }
132911
132940
  return null;
132912
132941
  })();
132913
- return built ? postprocessDescriptor(refuseTooDeep(screenId, built), {
132942
+ return built ? refuseTooDeep(screenId, postprocessDescriptor(refuseTooDeep(screenId, built), {
132914
132943
  config: uiConfig(state.app.module.config),
132915
132944
  flagDefinitions: state.flagStore.list(),
132916
132945
  evalContext: evalCtx,
132917
132946
  flagOverrides: cfg?.overrides,
132918
132947
  readBundle: tier5BundleReader(state.tier5Dir ?? TIER5_ROOT2)
132919
- }) : null;
132948
+ })) : null;
132920
132949
  }
132921
132950
  return null;
132922
132951
  }
@@ -134156,7 +134185,21 @@ async function handleDev(req, res, state) {
134156
134185
  }
134157
134186
  let descriptor = body.descriptor;
134158
134187
  if (!descriptor && body.screenId) {
134159
- 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;
134160
134203
  }
134161
134204
  if (!descriptor || typeof descriptor !== "object") {
134162
134205
  return sendJson(res, 404, { error: "screen_not_found", screenId: body.screenId ?? null });
@@ -134203,7 +134246,9 @@ async function handleDev(req, res, state) {
134203
134246
  const diagMatch = /^\/__dev\/screen\/(.+?)\/diagnostics(?:\?|$)/.exec(url);
134204
134247
  if (diagMatch && req.method === "GET") {
134205
134248
  const screenId = decodeURIComponent(diagMatch[1]);
134206
- 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;
134207
134252
  if (!descriptor) return sendJson(res, 404, { error: "screen_not_found", screenId });
134208
134253
  const diag = await runScreenDiagnostics(state, screenId, descriptor);
134209
134254
  return sendJson(res, diag.ok ? 200 : 422, diag);
@@ -134645,7 +134690,9 @@ async function handleAdminPlayground(req, res, state) {
134645
134690
  if (validateMatch && req.method === "GET") {
134646
134691
  const screenId = decodeURIComponent(validateMatch[1]);
134647
134692
  const strict = /[?&]strict=1(?:&|$)/.test(url);
134648
- 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;
134649
134696
  if (!descriptor) return sendJson(res, 404, { error: "screen_not_found", screenId });
134650
134697
  const { validateScreenDescriptor: validateScreenDescriptor2, loadAllowedComponents: loadAllowedComponents2, readBuiltBundle: readBuiltBundle2, appWorkerActionsFor: appWorkerActionsFor2 } = await Promise.resolve().then(() => (init_validate_descriptor(), validate_descriptor_exports));
134651
134698
  const tokens2 = readAppTokens(state) ?? void 0;
@@ -134677,7 +134724,7 @@ async function handleAdminPlayground(req, res, state) {
134677
134724
  if (!wantBuilt) {
134678
134725
  const override = state.localOverrides.get(screenId);
134679
134726
  if (override) {
134680
- return sendJson(res, 200, { screenId, source: "override", descriptor: { ...override, screenId } });
134727
+ return sendJson(res, 200, { screenId, source: "override", descriptor: refuseTooDeep(screenId, { ...override, screenId }) });
134681
134728
  }
134682
134729
  }
134683
134730
  const built = await buildDescriptorForScreenId(state, screenId, new URLSearchParams(), { skipOverride: true });
@@ -134695,6 +134742,7 @@ async function handleAdminPlayground(req, res, state) {
134695
134742
  if (!payload.descriptor || typeof payload.descriptor !== "object") {
134696
134743
  return sendJson(res, 400, { error: "descriptor_required" });
134697
134744
  }
134745
+ refuseTooDeep(screenId, payload.descriptor);
134698
134746
  state.localOverrides.set(screenId, payload.descriptor);
134699
134747
  recordOverrideHistory(state, screenId, "put", payload.descriptor, payload.notes ?? null);
134700
134748
  invalidate(state, screenId);
@@ -135715,6 +135763,7 @@ var init_src6 = __esm({
135715
135763
  init_app_icon();
135716
135764
  init_app_assets();
135717
135765
  init_raw_json_depth();
135766
+ init_depth_budget();
135718
135767
  init_app_module();
135719
135768
  init_provisioning_facts();
135720
135769
  init_validate();
@@ -139420,6 +139469,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139420
139469
  const store = typeof dest === "string" ? new FileReleaseStore(dest) : dest;
139421
139470
  const screenIds = Object.keys(preloaded.module.screens.exact);
139422
139471
  const failedToBuild = [];
139472
+ const depthRefusals = [];
139423
139473
  const missingBundles = [];
139424
139474
  const widgetBakeFailures = [];
139425
139475
  const lint = [];
@@ -139801,6 +139851,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139801
139851
  const finding = depthRefusalFromText(res.status, text9);
139802
139852
  if (finding) {
139803
139853
  failedToBuild.push({ screenId, status: res.status, error: finding.message });
139854
+ if (!depthRefusals.some((r2) => r2.screenId === screenId)) depthRefusals.push({ screenId, finding });
139804
139855
  if (collect2?.lint && !lint.some((l2) => l2.screenId === screenId)) {
139805
139856
  lint.push({ screenId, ok: false, issues: depthBudgetIssues([finding]) });
139806
139857
  }
@@ -139905,10 +139956,11 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139905
139956
  const { services, liveCalls, liveReturned, liveErrors, mockCalls, mockUnserved, mockBootFailed, liveFailed } = await renderServicesFor(bundleName, descriptor, wantedRender);
139906
139957
  const rr2 = await renderDescriptorWeb(descriptor, renderTokens, renderScheme, collect2.render.png === true, services, collect2.render.width, collect2.render.rtl === true);
139907
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 } : {} });
139908
- } else if (res.status === 422 && collect2?.lint) {
139959
+ } else if (res.status === 422) {
139909
139960
  const finding = await depthRefusalOf(res);
139961
+ if (finding && !depthRefusals.some((r2) => r2.screenId === wantedRender)) depthRefusals.push({ screenId: wantedRender, finding });
139910
139962
  const recorded = lint.some((l2) => l2.screenId === wantedRender) || failedToBuild.some((f2) => f2.screenId === wantedRender);
139911
- 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]) });
139912
139964
  }
139913
139965
  }
139914
139966
  for (const t2 of preloaded.module.screens.templated ?? []) {
@@ -139990,6 +140042,18 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
139990
140042
  }
139991
140043
  delete descriptor.etag;
139992
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
+ }
139993
140057
  const json7 = pinBakedServerBaseUrl(JSON.stringify(descriptor), base);
139994
140058
  await store.putScreen(app, screenId, json7);
139995
140059
  screens[screenId] = { descriptorHash: sha2564(json7), bundle: bundleName, ...screenMetadataFor(screenMetadata, screenId, canonical4) };
@@ -140203,6 +140267,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140203
140267
  return {
140204
140268
  manifest,
140205
140269
  failedToBuild,
140270
+ depthRefusals,
140206
140271
  missingBundles,
140207
140272
  // Only bundles a screen actually referenced are weighed — `tierBundles` is filled by
140208
140273
  // addressBundle, which runs on reference, so an unreferenced file on disk never counts.
@@ -140269,7 +140334,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
140269
140334
  const sha2 = gitSha();
140270
140335
  const manifest = { releaseId: contentReleaseId(content), gitSha: sha2, ...content };
140271
140336
  await store.putManifest(app, manifest);
140272
- return { manifest, failedToBuild: [], missingBundles: [], auth: {}, tierCapViolations: [] };
140337
+ return { manifest, failedToBuild: [], depthRefusals: [], missingBundles: [], auth: {}, tierCapViolations: [] };
140273
140338
  }
140274
140339
  function listBuildableApps() {
140275
140340
  const appsDir = join47(REPO_ROOT7, "apps");
@@ -163812,6 +163877,8 @@ function createReviewScreenRenderer() {
163812
163877
  collect: { render: { screenId, scheme: "light", png: false, hydrate: true, live: true, dumpState: true, width: REVIEW_SCREENSHOT_FRAME.width, host, ...input ? { input } : {} } }
163813
163878
  });
163814
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}`);
163815
163882
  if (!render) throw new MissingScreenError(screenId);
163816
163883
  if (!render.renderable || !render.html) throw new Error(render.reason ?? `${screenId} is not an SDUI screen`);
163817
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.1424.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": "a0147b33ea7375fa7cf11b0027a0388feef592ede284346c3d5c9919"
74
+ "falconPackageHash": "1ad8e9d41b385905df3fb24715044289df3fb2389c6b704c4d14e96d"
75
75
  }