@drskillissue/ganko 0.1.22 → 0.1.24

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/index.cjs CHANGED
@@ -8919,7 +8919,7 @@ function resolveMessage(template, data) {
8919
8919
  }
8920
8920
  return result;
8921
8921
  }
8922
- function createDiagnosticFromLoc(file, loc, rule, messageId, message, severity, fix) {
8922
+ function createDiagnosticFromLoc(file, loc, rule, messageId, message, severity, fix, suggest) {
8923
8923
  const result = {
8924
8924
  file,
8925
8925
  rule,
@@ -8929,13 +8929,14 @@ function createDiagnosticFromLoc(file, loc, rule, messageId, message, severity,
8929
8929
  loc: firstLine(loc)
8930
8930
  };
8931
8931
  if (fix !== void 0) result.fix = fix;
8932
+ if (suggest !== void 0 && suggest.length > 0) result.suggest = suggest;
8932
8933
  return result;
8933
8934
  }
8934
- function createDiagnosticFromComment(file, comment, rule, messageId, message, severity, fix) {
8935
- return createDiagnosticFromLoc(file, comment.loc, rule, messageId, message, severity, fix);
8935
+ function createDiagnosticFromComment(file, comment, rule, messageId, message, severity, fix, suggest) {
8936
+ return createDiagnosticFromLoc(file, comment.loc, rule, messageId, message, severity, fix, suggest);
8936
8937
  }
8937
- function createDiagnostic(file, node, rule, messageId, message, severity, fix) {
8938
- return createDiagnosticFromLoc(file, node.loc, rule, messageId, message, severity, fix);
8938
+ function createDiagnostic(file, node, rule, messageId, message, severity, fix, suggest) {
8939
+ return createDiagnosticFromLoc(file, node.loc, rule, messageId, message, severity, fix, suggest);
8939
8940
  }
8940
8941
 
8941
8942
  // src/solid/rule.ts
@@ -12091,8 +12092,9 @@ var CONDITIONAL_MOUNT_TAGS = /* @__PURE__ */ new Set([
12091
12092
  "TabPanel"
12092
12093
  ]);
12093
12094
  var messages16 = {
12094
- loadingMismatch: "createResource '{{name}}' has no initialValue but uses manual loading checks ({{name}}.loading). Without initialValue, Suspense intercepts before your loading UI renders. Add initialValue to the options: createResource(fetcher, { initialValue: ... })",
12095
- conditionalSuspense: "createResource '{{name}}' has no initialValue and is rendered inside a conditional mount point ({{mountTag}}). This will trigger a distant Suspense boundary and unmount the entire subtree. Add initialValue to the options: createResource(fetcher, { initialValue: ... })"
12095
+ loadingMismatch: "createResource '{{name}}' has no initialValue but uses {{name}}.loading for manual loading UI. Suspense intercepts before your loading UI renders \u2014 the component is unmounted before the <Show>/<Switch> evaluates. Replace createResource with onMount + createSignal to decouple from Suspense entirely.",
12096
+ conditionalSuspense: "createResource '{{name}}' is inside a conditional mount point ({{mountTag}}) with a distant Suspense boundary. The SuspenseContext increment fires when the fetcher's Promise is pending and unmounts the entire page subtree \u2014 initialValue does NOT prevent this. Replace createResource with onMount + createSignal to avoid Suspense interaction.",
12097
+ missingErrorBoundary: "createResource '{{name}}' has no <ErrorBoundary> between its component and the nearest <Suspense>. When the fetcher throws (network error, 401/403/503, timeout), the error propagates to Suspense which has no error handling \u2014 the boundary breaks permanently. Wrap the component in <ErrorBoundary> or replace createResource with onMount + createSignal and catch errors in the fetcher."
12096
12098
  };
12097
12099
  var options16 = {};
12098
12100
  function hasInitialValue(call) {
@@ -12135,35 +12137,95 @@ function hasLoadingRead(resourceVariable) {
12135
12137
  }
12136
12138
  return false;
12137
12139
  }
12138
- function findConditionalMountAncestor(graph, componentName) {
12140
+ function resolveFetcherFunction(graph, call) {
12141
+ const args = call.node.arguments;
12142
+ if (args.length === 0) return null;
12143
+ let fetcherNode;
12144
+ if (args.length === 1) {
12145
+ fetcherNode = args[0];
12146
+ } else if (args.length === 2) {
12147
+ const lastArg = args[1];
12148
+ fetcherNode = lastArg && lastArg.type === "ObjectExpression" ? args[0] : args[1];
12149
+ } else {
12150
+ fetcherNode = args[1];
12151
+ }
12152
+ if (!fetcherNode) return null;
12153
+ if (fetcherNode.type === "ArrowFunctionExpression" || fetcherNode.type === "FunctionExpression") {
12154
+ return graph.functionsByNode.get(fetcherNode) ?? null;
12155
+ }
12156
+ if (fetcherNode.type === "Identifier") {
12157
+ const fns = graph.functionsByName.get(fetcherNode.name);
12158
+ if (fns && fns.length > 0) {
12159
+ const fn = fns[0];
12160
+ if (fn) return fn;
12161
+ }
12162
+ }
12163
+ return null;
12164
+ }
12165
+ function fetcherCanThrow(graph, fn, visited) {
12166
+ if (visited.has(fn.id)) return false;
12167
+ visited.add(fn.id);
12168
+ if (fn.async && fn.awaitRanges.length > 0) return true;
12169
+ if (fn.hasThrowStatement) return true;
12170
+ const callSites = fn.callSites;
12171
+ for (let i = 0, len = callSites.length; i < len; i++) {
12172
+ const callSite = callSites[i];
12173
+ if (!callSite) continue;
12174
+ if (!callSite.resolvedTarget) return true;
12175
+ if (fetcherCanThrow(graph, callSite.resolvedTarget, visited)) return true;
12176
+ }
12177
+ return false;
12178
+ }
12179
+ function analyzeComponentBoundaries(graph, componentName) {
12180
+ const result = {
12181
+ conditionalMountTag: null,
12182
+ suspenseDistance: 0,
12183
+ lacksErrorBoundary: false,
12184
+ usagesLackingErrorBoundary: []
12185
+ };
12139
12186
  const usages = graph.jsxByTag.get(componentName) ?? [];
12187
+ if (usages.length === 0) return result;
12140
12188
  for (let i = 0, len = usages.length; i < len; i++) {
12141
12189
  const usage = usages[i];
12142
12190
  if (!usage) continue;
12143
12191
  let current = usage.parent;
12144
12192
  let conditionalTag = null;
12145
12193
  let componentLevels = 0;
12194
+ let foundErrorBoundary = false;
12195
+ let foundSuspense = false;
12146
12196
  while (current) {
12147
12197
  const tag = current.tag;
12148
12198
  if (tag && !current.isDomElement) {
12149
12199
  componentLevels++;
12150
- if (tag === "Suspense") {
12200
+ if (tag === "ErrorBoundary") {
12201
+ foundErrorBoundary = true;
12202
+ } else if (tag === "Suspense") {
12203
+ foundSuspense = true;
12204
+ if (!foundErrorBoundary) {
12205
+ result.lacksErrorBoundary = true;
12206
+ result.usagesLackingErrorBoundary.push(usage);
12207
+ }
12151
12208
  if (conditionalTag !== null && componentLevels > 1) {
12152
- return { tag: conditionalTag, suspenseDistance: componentLevels };
12209
+ result.conditionalMountTag = conditionalTag;
12210
+ result.suspenseDistance = componentLevels;
12153
12211
  }
12154
- return null;
12155
- }
12156
- if (conditionalTag === null && CONDITIONAL_MOUNT_TAGS.has(tag)) {
12212
+ break;
12213
+ } else if (conditionalTag === null && CONDITIONAL_MOUNT_TAGS.has(tag)) {
12157
12214
  conditionalTag = tag;
12158
12215
  }
12159
12216
  }
12160
12217
  current = current.parent;
12161
12218
  }
12162
- if (conditionalTag !== null) {
12163
- return { tag: conditionalTag, suspenseDistance: componentLevels };
12219
+ if (!foundSuspense && !foundErrorBoundary) {
12220
+ result.lacksErrorBoundary = true;
12221
+ result.usagesLackingErrorBoundary.push(usage);
12222
+ if (conditionalTag !== null) {
12223
+ result.conditionalMountTag = conditionalTag;
12224
+ result.suspenseDistance = componentLevels;
12225
+ }
12164
12226
  }
12165
12227
  }
12166
- return null;
12228
+ return result;
12167
12229
  }
12168
12230
  function getContainingComponentName(graph, call) {
12169
12231
  const selfComponent = graph.componentScopes.get(call.scope);
@@ -12180,43 +12242,65 @@ function findResourceVariable(graph, name) {
12180
12242
  }
12181
12243
  return null;
12182
12244
  }
12245
+ function buildErrorBoundaryFix(usages, graph) {
12246
+ if (usages.length === 0) return null;
12247
+ const usage = usages[0];
12248
+ if (!usage) return null;
12249
+ const jsxNode = usage.node;
12250
+ const startPos = jsxNode.range[0];
12251
+ const endPos = jsxNode.range[1];
12252
+ const ops = [
12253
+ { range: [startPos, startPos], text: "<ErrorBoundary fallback={<div>Error</div>}>" },
12254
+ { range: [endPos, endPos], text: "</ErrorBoundary>" }
12255
+ ];
12256
+ const importFix = buildSolidImportFix(graph, "ErrorBoundary");
12257
+ if (importFix) ops.unshift(importFix);
12258
+ return ops;
12259
+ }
12183
12260
  var resourceImplicitSuspense = defineSolidRule({
12184
12261
  id: "resource-implicit-suspense",
12185
12262
  severity: "warn",
12186
12263
  messages: messages16,
12187
12264
  meta: {
12188
- description: "Detect createResource without initialValue that implicitly triggers Suspense boundaries.",
12189
- fixable: false,
12265
+ description: "Detect createResource that implicitly triggers or permanently breaks Suspense boundaries.",
12266
+ fixable: true,
12190
12267
  category: "reactivity"
12191
12268
  },
12192
12269
  options: options16,
12193
12270
  check(graph, emit) {
12194
12271
  const resourceCalls = getCallsByPrimitive(graph, "createResource");
12195
12272
  if (resourceCalls.length === 0) return;
12273
+ const throwVisited = /* @__PURE__ */ new Set();
12274
+ const boundaryCache = /* @__PURE__ */ new Map();
12196
12275
  for (let i = 0, len = resourceCalls.length; i < len; i++) {
12197
12276
  const call = resourceCalls[i];
12198
12277
  if (!call) continue;
12199
- if (hasInitialValue(call)) continue;
12200
12278
  const resourceName = getResourceVariableName(call);
12201
12279
  if (!resourceName) continue;
12202
- const resourceVariable = findResourceVariable(graph, resourceName);
12203
- if (resourceVariable && hasLoadingRead(resourceVariable)) {
12204
- emit(
12205
- createDiagnostic(
12206
- graph.file,
12207
- call.node,
12208
- "resource-implicit-suspense",
12209
- "loadingMismatch",
12210
- resolveMessage(messages16.loadingMismatch, { name: resourceName }),
12211
- "warn"
12212
- )
12213
- );
12214
- continue;
12215
- }
12280
+ const hasInitial = hasInitialValue(call);
12216
12281
  const componentName = getContainingComponentName(graph, call);
12282
+ if (!hasInitial) {
12283
+ const resourceVariable = findResourceVariable(graph, resourceName);
12284
+ if (resourceVariable && hasLoadingRead(resourceVariable)) {
12285
+ emit(
12286
+ createDiagnostic(
12287
+ graph.file,
12288
+ call.node,
12289
+ "resource-implicit-suspense",
12290
+ "loadingMismatch",
12291
+ resolveMessage(messages16.loadingMismatch, { name: resourceName }),
12292
+ "warn"
12293
+ )
12294
+ );
12295
+ }
12296
+ }
12217
12297
  if (!componentName) continue;
12218
- const conditional = findConditionalMountAncestor(graph, componentName);
12219
- if (conditional) {
12298
+ let analysis = boundaryCache.get(componentName);
12299
+ if (!analysis) {
12300
+ analysis = analyzeComponentBoundaries(graph, componentName);
12301
+ boundaryCache.set(componentName, analysis);
12302
+ }
12303
+ if (analysis.conditionalMountTag) {
12220
12304
  emit(
12221
12305
  createDiagnostic(
12222
12306
  graph.file,
@@ -12225,12 +12309,32 @@ var resourceImplicitSuspense = defineSolidRule({
12225
12309
  "conditionalSuspense",
12226
12310
  resolveMessage(messages16.conditionalSuspense, {
12227
12311
  name: resourceName,
12228
- mountTag: conditional.tag
12312
+ mountTag: analysis.conditionalMountTag
12229
12313
  }),
12230
12314
  "error"
12231
12315
  )
12232
12316
  );
12233
12317
  }
12318
+ if (analysis.lacksErrorBoundary) {
12319
+ const fetcherFn = resolveFetcherFunction(graph, call);
12320
+ if (fetcherFn && fetcherCanThrow(graph, fetcherFn, throwVisited)) {
12321
+ const errorBoundaryFix = buildErrorBoundaryFix(
12322
+ analysis.usagesLackingErrorBoundary,
12323
+ graph
12324
+ );
12325
+ emit(
12326
+ createDiagnostic(
12327
+ graph.file,
12328
+ call.node,
12329
+ "resource-implicit-suspense",
12330
+ "missingErrorBoundary",
12331
+ resolveMessage(messages16.missingErrorBoundary, { name: resourceName }),
12332
+ "error",
12333
+ errorBoundaryFix ?? void 0
12334
+ )
12335
+ );
12336
+ }
12337
+ }
12234
12338
  }
12235
12339
  }
12236
12340
  });
@@ -30163,6 +30267,11 @@ function toLayoutElementKey(solidFile, elementId) {
30163
30267
  return `${solidFile}::${elementId}`;
30164
30268
  }
30165
30269
 
30270
+ // src/cross-file/layout/context-model.ts
30271
+ function deriveAlignmentContext(base, overrides) {
30272
+ return { ...base, ...overrides };
30273
+ }
30274
+
30166
30275
  // src/cross-file/layout/signal-model.ts
30167
30276
  var layoutSignalNames = [
30168
30277
  "line-height",
@@ -30191,6 +30300,8 @@ var layoutSignalNames = [
30191
30300
  "justify-items",
30192
30301
  "place-items",
30193
30302
  "place-self",
30303
+ "flex-direction",
30304
+ "grid-auto-flow",
30194
30305
  "appearance",
30195
30306
  "box-sizing",
30196
30307
  "padding-top",
@@ -30309,13 +30420,44 @@ function expandShorthand(property, value2) {
30309
30420
  { name: blockTarget[1], value: parsed.end }
30310
30421
  ];
30311
30422
  }
30423
+ if (property === "flex-flow") {
30424
+ return expandFlexFlow(value2);
30425
+ }
30312
30426
  return void 0;
30313
30427
  }
30428
+ var FLEX_DIRECTION_VALUES = /* @__PURE__ */ new Set(["row", "row-reverse", "column", "column-reverse"]);
30429
+ function expandFlexFlow(value2) {
30430
+ const tokens = splitWhitespaceTokens(value2.trim().toLowerCase());
30431
+ if (tokens.length === 0) return null;
30432
+ if (tokens.length > 2) return null;
30433
+ let direction = null;
30434
+ let wrap = null;
30435
+ for (let i = 0; i < tokens.length; i++) {
30436
+ const token = tokens[i];
30437
+ if (!token) continue;
30438
+ if (FLEX_DIRECTION_VALUES.has(token)) {
30439
+ if (direction !== null) return null;
30440
+ direction = token;
30441
+ } else {
30442
+ if (wrap !== null) return null;
30443
+ wrap = token;
30444
+ }
30445
+ }
30446
+ const out = [];
30447
+ if (direction !== null) {
30448
+ out.push({ name: "flex-direction", value: direction });
30449
+ }
30450
+ if (wrap !== null) {
30451
+ out.push({ name: "flex-wrap", value: wrap });
30452
+ }
30453
+ return out.length > 0 ? out : null;
30454
+ }
30314
30455
  function getShorthandLonghandNames(property) {
30315
30456
  const quad = QUAD_EXPANSIONS.get(property);
30316
30457
  if (quad !== void 0) return [...quad];
30317
30458
  const block = BLOCK_EXPANSIONS.get(property);
30318
30459
  if (block !== void 0) return [...block];
30460
+ if (property === "flex-flow") return ["flex-direction", "flex-wrap"];
30319
30461
  return null;
30320
30462
  }
30321
30463
 
@@ -30326,27 +30468,90 @@ var CONTROL_ELEMENT_TAGS = /* @__PURE__ */ new Set([
30326
30468
  "textarea",
30327
30469
  "button"
30328
30470
  ]);
30471
+ var INTRINSIC_REPLACED_TAGS = /* @__PURE__ */ new Set([
30472
+ "img",
30473
+ "svg",
30474
+ "video",
30475
+ "canvas",
30476
+ "iframe",
30477
+ "object",
30478
+ "embed"
30479
+ ]);
30480
+ var WHITESPACE_RE3 = /\s+/;
30329
30481
  function clamp(value2, min, max) {
30330
30482
  if (value2 < min) return min;
30331
30483
  if (value2 > max) return max;
30332
30484
  return value2;
30333
30485
  }
30334
- function kindRank(kind) {
30335
- if (kind === "exact") return 0;
30336
- if (kind === "interval") return 1;
30337
- if (kind === "conditional") return 2;
30338
- return 3;
30339
- }
30340
30486
  function mergeEvidenceKind(left, right) {
30341
- if (kindRank(left) >= kindRank(right)) return left;
30342
- return right;
30487
+ return left > right ? left : right;
30488
+ }
30489
+ function selectKth(values, targetIndex) {
30490
+ let left = 0;
30491
+ let right = values.length - 1;
30492
+ while (left <= right) {
30493
+ if (left === right) {
30494
+ const result = values[left];
30495
+ if (result === void 0) return 0;
30496
+ return result;
30497
+ }
30498
+ const pivotIndex = choosePivotIndex(values, left, right);
30499
+ const partitionIndex = partitionAroundPivot(values, left, right, pivotIndex);
30500
+ if (partitionIndex === targetIndex) {
30501
+ const result = values[partitionIndex];
30502
+ if (result === void 0) return 0;
30503
+ return result;
30504
+ }
30505
+ if (partitionIndex < targetIndex) {
30506
+ left = partitionIndex + 1;
30507
+ continue;
30508
+ }
30509
+ right = partitionIndex - 1;
30510
+ }
30511
+ const fallback = values[targetIndex];
30512
+ if (fallback === void 0) return 0;
30513
+ return fallback;
30514
+ }
30515
+ function choosePivotIndex(values, left, right) {
30516
+ const middle = Math.floor((left + right) / 2);
30517
+ const leftValue = values[left] ?? 0;
30518
+ const middleValue = values[middle] ?? 0;
30519
+ const rightValue = values[right] ?? 0;
30520
+ if (leftValue < middleValue) {
30521
+ if (middleValue < rightValue) return middle;
30522
+ if (leftValue < rightValue) return right;
30523
+ return left;
30524
+ }
30525
+ if (leftValue < rightValue) return left;
30526
+ if (middleValue < rightValue) return right;
30527
+ return middle;
30528
+ }
30529
+ function partitionAroundPivot(values, left, right, pivotIndex) {
30530
+ const pivotValue = values[pivotIndex] ?? 0;
30531
+ swap(values, pivotIndex, right);
30532
+ let storeIndex = left;
30533
+ for (let i = left; i < right; i++) {
30534
+ const current = values[i];
30535
+ if (current === void 0 || current > pivotValue) continue;
30536
+ swap(values, storeIndex, i);
30537
+ storeIndex++;
30538
+ }
30539
+ swap(values, storeIndex, right);
30540
+ return storeIndex;
30541
+ }
30542
+ function swap(values, left, right) {
30543
+ if (left === right) return;
30544
+ const leftValue = values[left] ?? 0;
30545
+ const rightValue = values[right] ?? 0;
30546
+ values[left] = rightValue;
30547
+ values[right] = leftValue;
30343
30548
  }
30344
30549
  function toComparableExactValue(value2) {
30345
30550
  if (value2.value !== null) {
30346
- if (value2.kind !== "exact") return null;
30551
+ if (value2.kind !== 0 /* Exact */) return null;
30347
30552
  return value2.value;
30348
30553
  }
30349
- if (value2.kind === "exact") return 0;
30554
+ if (value2.kind === 0 /* Exact */) return 0;
30350
30555
  return null;
30351
30556
  }
30352
30557
 
@@ -30360,7 +30565,8 @@ var MONITORED_SHORTHAND_SET = /* @__PURE__ */ new Set([
30360
30565
  "border-width",
30361
30566
  "margin-block",
30362
30567
  "padding-block",
30363
- "inset-block"
30568
+ "inset-block",
30569
+ "flex-flow"
30364
30570
  ]);
30365
30571
  var LENGTH_SIGNAL_SET = /* @__PURE__ */ new Set([
30366
30572
  "font-size",
@@ -30402,13 +30608,22 @@ var KEYWORD_SIGNAL_SET = /* @__PURE__ */ new Set([
30402
30608
  "justify-items",
30403
30609
  "place-items",
30404
30610
  "place-self",
30611
+ "flex-direction",
30612
+ "grid-auto-flow",
30405
30613
  "appearance",
30406
30614
  "box-sizing",
30407
30615
  "position",
30408
30616
  "writing-mode",
30409
30617
  "direction"
30410
30618
  ]);
30411
- var REPLACED_TAGS = /* @__PURE__ */ new Set(["input", "select", "textarea", "button", "img", "video", "canvas", "svg", "iframe"]);
30619
+ var REPLACED_ELEMENT_TAGS = /* @__PURE__ */ new Set([
30620
+ ...CONTROL_ELEMENT_TAGS,
30621
+ "img",
30622
+ "video",
30623
+ "canvas",
30624
+ "svg",
30625
+ "iframe"
30626
+ ]);
30412
30627
  function isMonitoredSignal(property) {
30413
30628
  if (MONITORED_SIGNAL_SET.has(property)) return true;
30414
30629
  return MONITORED_SHORTHAND_SET.has(property);
@@ -30419,7 +30634,7 @@ function isControlTag(tag) {
30419
30634
  }
30420
30635
  function isReplacedTag(tag) {
30421
30636
  if (tag === null) return false;
30422
- return REPLACED_TAGS.has(tag.toLowerCase());
30637
+ return REPLACED_ELEMENT_TAGS.has(tag.toLowerCase());
30423
30638
  }
30424
30639
  function normalizeSignalMapWithCounts(values) {
30425
30640
  const out = /* @__PURE__ */ new Map();
@@ -30430,12 +30645,11 @@ function normalizeSignalMapWithCounts(values) {
30430
30645
  "font-size",
30431
30646
  fontSizeEntry.value,
30432
30647
  fontSizeEntry.source,
30433
- fontSizeEntry.guard,
30434
30648
  fontSizeEntry.guardProvenance,
30435
30649
  null
30436
30650
  );
30437
30651
  out.set("font-size", parsedFontSize);
30438
- if (parsedFontSize.kind === "known" && parsedFontSize.guard === "unconditional") {
30652
+ if (parsedFontSize.kind === "known" && parsedFontSize.guard.kind === 0 /* Unconditional */) {
30439
30653
  fontSizePx = parsedFontSize.px;
30440
30654
  }
30441
30655
  }
@@ -30451,7 +30665,6 @@ function normalizeSignalMapWithCounts(values) {
30451
30665
  name,
30452
30666
  declaration.value,
30453
30667
  declaration.source,
30454
- declaration.guard,
30455
30668
  declaration.guardProvenance,
30456
30669
  fontSizePx
30457
30670
  );
@@ -30461,7 +30674,7 @@ function normalizeSignalMapWithCounts(values) {
30461
30674
  let unknownSignalCount = 0;
30462
30675
  let conditionalSignalCount = 0;
30463
30676
  for (const value2 of out.values()) {
30464
- if (value2.guard === "conditional") {
30677
+ if (value2.guard.kind === 1 /* Conditional */) {
30465
30678
  conditionalSignalCount++;
30466
30679
  continue;
30467
30680
  }
@@ -30489,7 +30702,7 @@ function applyExpandedShorthand(out, property, declaration, fontSizePx) {
30489
30702
  if (!longhand) continue;
30490
30703
  const name = MONITORED_SIGNAL_NAME_MAP.get(longhand);
30491
30704
  if (name === void 0) continue;
30492
- out.set(name, createUnknown(name, declaration.value, declaration.source, declaration.guard, declaration.guardProvenance, reason));
30705
+ out.set(name, createUnknown(name, declaration.source, declaration.guardProvenance, reason));
30493
30706
  }
30494
30707
  return;
30495
30708
  }
@@ -30499,127 +30712,139 @@ function applyExpandedShorthand(out, property, declaration, fontSizePx) {
30499
30712
  if (!entry) continue;
30500
30713
  const name = MONITORED_SIGNAL_NAME_MAP.get(entry.name);
30501
30714
  if (name === void 0) continue;
30502
- out.set(name, normalizeSignal(name, entry.value, declaration.source, declaration.guard, declaration.guardProvenance, fontSizePx));
30715
+ out.set(name, normalizeSignal(name, entry.value, declaration.source, declaration.guardProvenance, fontSizePx));
30503
30716
  }
30504
30717
  }
30505
30718
  function toMonitoredSignalName(property) {
30506
30719
  return MONITORED_SIGNAL_NAME_MAP.get(property) ?? null;
30507
30720
  }
30508
- function normalizeSignal(name, raw, source, guard, guardProvenance, fontSizePx) {
30721
+ function normalizeSignal(name, raw, source, guard, fontSizePx) {
30509
30722
  switch (name) {
30510
30723
  case "line-height":
30511
- return parseLineHeight(name, raw, source, guard, guardProvenance, fontSizePx);
30724
+ return parseLineHeight(name, raw, source, guard, fontSizePx);
30512
30725
  case "aspect-ratio":
30513
- return parseAspectRatio(name, raw, source, guard, guardProvenance);
30726
+ return parseAspectRatio(name, raw, source, guard);
30514
30727
  case "contain-intrinsic-size":
30515
- return parseContainIntrinsicSize(name, raw, source, guard, guardProvenance);
30728
+ return parseContainIntrinsicSize(name, raw, source, guard);
30516
30729
  case "transform":
30517
- return parseTransform(name, raw, source, guard, guardProvenance);
30730
+ return parseTransform(name, raw, source, guard);
30518
30731
  case "translate":
30519
- return parseTranslateProperty(name, raw, source, guard, guardProvenance);
30732
+ return parseTranslateProperty(name, raw, source, guard);
30520
30733
  default:
30521
30734
  break;
30522
30735
  }
30523
- if (LENGTH_SIGNAL_SET.has(name)) return parseLength(name, raw, source, guard, guardProvenance);
30524
- if (KEYWORD_SIGNAL_SET.has(name)) return parseKeyword(name, raw, source, guard, guardProvenance);
30525
- return createUnknown(name, raw, source, guard, guardProvenance, "unsupported signal");
30736
+ if (LENGTH_SIGNAL_SET.has(name)) return parseLength(name, raw, source, guard);
30737
+ if (KEYWORD_SIGNAL_SET.has(name)) return parseKeyword(name, raw, source, guard);
30738
+ return createUnknown(name, source, guard, "unsupported signal");
30526
30739
  }
30527
- function parseAspectRatio(name, raw, source, guard, guardProvenance) {
30740
+ function parseAspectRatio(name, raw, source, guard) {
30528
30741
  const trimmed = raw.trim().toLowerCase();
30529
30742
  if (trimmed.length === 0) {
30530
- return createUnknown(name, raw, source, guard, guardProvenance, "aspect-ratio value is empty");
30743
+ return createUnknown(name, source, guard, "aspect-ratio value is empty");
30531
30744
  }
30532
30745
  if (hasDynamicExpression(trimmed)) {
30533
- return createUnknown(name, raw, source, guard, guardProvenance, "aspect-ratio uses runtime-dependent function");
30746
+ return createUnknown(name, source, guard, "aspect-ratio uses runtime-dependent function");
30534
30747
  }
30535
30748
  if (trimmed === "auto") {
30536
- return createUnknown(name, raw, source, guard, guardProvenance, "aspect-ratio auto does not reserve ratio");
30749
+ return createUnknown(name, source, guard, "aspect-ratio auto does not reserve ratio");
30537
30750
  }
30538
30751
  const slash = trimmed.indexOf("/");
30539
30752
  if (slash !== -1) {
30540
30753
  const left = Number(trimmed.slice(0, slash).trim());
30541
30754
  const right = Number(trimmed.slice(slash + 1).trim());
30542
30755
  if (!Number.isFinite(left) || !Number.isFinite(right) || left <= 0 || right <= 0) {
30543
- return createUnknown(name, raw, source, guard, guardProvenance, "aspect-ratio ratio is invalid");
30756
+ return createUnknown(name, source, guard, "aspect-ratio ratio is invalid");
30544
30757
  }
30545
- return createKnown(name, raw, source, guard, guardProvenance, null, "unitless", "exact");
30758
+ return createKnown(name, raw, source, guard, null, 1 /* Unitless */, "exact");
30546
30759
  }
30547
30760
  const ratio = Number(trimmed);
30548
30761
  if (!Number.isFinite(ratio) || ratio <= 0) {
30549
- return createUnknown(name, raw, source, guard, guardProvenance, "aspect-ratio is not statically parseable");
30762
+ return createUnknown(name, source, guard, "aspect-ratio is not statically parseable");
30550
30763
  }
30551
- return createKnown(name, raw, source, guard, guardProvenance, null, "unitless", "exact");
30764
+ return createKnown(name, raw, source, guard, null, 1 /* Unitless */, "exact");
30552
30765
  }
30553
- function parseContainIntrinsicSize(name, raw, source, guard, guardProvenance) {
30766
+ function parseContainIntrinsicSize(name, raw, source, guard) {
30554
30767
  const trimmed = raw.trim().toLowerCase();
30555
30768
  if (trimmed.length === 0) {
30556
- return createUnknown(name, raw, source, guard, guardProvenance, "contain-intrinsic-size value is empty");
30769
+ return createUnknown(name, source, guard, "contain-intrinsic-size value is empty");
30557
30770
  }
30558
30771
  if (hasDynamicExpression(trimmed)) {
30559
- return createUnknown(name, raw, source, guard, guardProvenance, "contain-intrinsic-size uses runtime-dependent function");
30772
+ return createUnknown(name, source, guard, "contain-intrinsic-size uses runtime-dependent function");
30560
30773
  }
30561
30774
  if (trimmed === "none" || trimmed === "auto") {
30562
- return createUnknown(name, raw, source, guard, guardProvenance, "contain-intrinsic-size does not reserve space");
30775
+ return createUnknown(name, source, guard, "contain-intrinsic-size does not reserve space");
30563
30776
  }
30564
30777
  const parts = splitWhitespaceTokens(trimmed);
30565
30778
  for (let i = 0; i < parts.length; i++) {
30566
30779
  const part = parts[i];
30567
30780
  if (!part) continue;
30568
30781
  const px = parseSignedPxValue(part);
30569
- if (px !== null) return createKnown(name, raw, source, guard, guardProvenance, px, "px", "exact");
30782
+ if (px !== null) return createKnown(name, raw, source, guard, px, 0 /* Px */, "exact");
30570
30783
  }
30571
- return createUnknown(name, raw, source, guard, guardProvenance, "contain-intrinsic-size is not statically parseable in px");
30784
+ return createUnknown(name, source, guard, "contain-intrinsic-size is not statically parseable in px");
30572
30785
  }
30573
- function parseLineHeight(name, raw, source, guard, guardProvenance, fontSizePx) {
30786
+ function parseLineHeight(name, raw, source, guard, fontSizePx) {
30574
30787
  const unitless = parseUnitlessValue(raw);
30575
30788
  if (unitless !== null) {
30576
30789
  const base = fontSizePx === null ? 16 : fontSizePx;
30577
- return createKnown(name, raw, source, guard, guardProvenance, unitless * base, "unitless", "estimated");
30790
+ return createKnown(name, raw, source, guard, unitless * base, 1 /* Unitless */, "estimated");
30578
30791
  }
30579
30792
  const px = parseSignedPxValue(raw);
30580
- if (px !== null) return createKnown(name, raw, source, guard, guardProvenance, px, "px", "exact");
30581
- return createUnknown(name, raw, source, guard, guardProvenance, "line-height is not statically parseable");
30793
+ if (px !== null) return createKnown(name, raw, source, guard, px, 0 /* Px */, "exact");
30794
+ return createUnknown(name, source, guard, "line-height is not statically parseable");
30582
30795
  }
30583
- function parseLength(name, raw, source, guard, guardProvenance) {
30796
+ var DIMENSION_KEYWORD_SET = /* @__PURE__ */ new Set([
30797
+ "auto",
30798
+ "none",
30799
+ "fit-content",
30800
+ "min-content",
30801
+ "max-content",
30802
+ "stretch"
30803
+ ]);
30804
+ function parseLength(name, raw, source, guard) {
30584
30805
  const px = parseSignedPxValue(raw);
30585
- if (px === null) {
30586
- return createUnknown(name, raw, source, guard, guardProvenance, "length is not statically parseable in px");
30806
+ if (px !== null) {
30807
+ return createKnown(name, raw, source, guard, px, 0 /* Px */, "exact");
30587
30808
  }
30588
- return createKnown(name, raw, source, guard, guardProvenance, px, "px", "exact");
30809
+ const normalized = raw.trim().toLowerCase();
30810
+ if (DIMENSION_KEYWORD_SET.has(normalized) || normalized.startsWith("fit-content(")) {
30811
+ return createKnown(name, raw, source, guard, null, 2 /* Keyword */, "exact");
30812
+ }
30813
+ return createUnknown(name, source, guard, "length is not statically parseable in px");
30589
30814
  }
30590
- function parseKeyword(name, raw, source, guard, guardProvenance) {
30815
+ function parseKeyword(name, raw, source, guard) {
30591
30816
  const normalized = raw.trim().toLowerCase();
30592
30817
  if (normalized.length === 0) {
30593
- return createUnknown(name, raw, source, guard, guardProvenance, "keyword value is empty");
30818
+ return createUnknown(name, source, guard, "keyword value is empty");
30594
30819
  }
30595
30820
  if (hasDynamicExpression(normalized)) {
30596
- return createUnknown(name, raw, source, guard, guardProvenance, "keyword uses runtime-dependent function");
30821
+ return createUnknown(name, source, guard, "keyword uses runtime-dependent function");
30597
30822
  }
30598
- return createKnown(name, raw, source, guard, guardProvenance, null, "keyword", "exact");
30823
+ return createKnown(name, raw, source, guard, null, 2 /* Keyword */, "exact");
30599
30824
  }
30600
- function parseTransform(name, raw, source, guard, guardProvenance) {
30825
+ function parseTransform(name, raw, source, guard) {
30601
30826
  const normalized = raw.trim().toLowerCase();
30602
30827
  if (normalized.length === 0) {
30603
- return createUnknown(name, raw, source, guard, guardProvenance, "transform value is empty");
30828
+ return createUnknown(name, source, guard, "transform value is empty");
30604
30829
  }
30605
30830
  if (hasDynamicExpression(normalized)) {
30606
- return createUnknown(name, raw, source, guard, guardProvenance, "transform uses runtime-dependent function");
30831
+ return createUnknown(name, source, guard, "transform uses runtime-dependent function");
30607
30832
  }
30608
30833
  const y = extractTransformYPx(normalized);
30609
- if (y !== null) return createKnown(name, raw, source, guard, guardProvenance, y, "px", "exact");
30610
- return createUnknown(name, raw, source, guard, guardProvenance, "transform has non-translational or non-px functions");
30834
+ if (y !== null) return createKnown(name, raw, source, guard, y, 0 /* Px */, "exact");
30835
+ return createUnknown(name, source, guard, "transform has non-translational or non-px functions");
30611
30836
  }
30612
- function parseTranslateProperty(name, raw, source, guard, guardProvenance) {
30837
+ function parseTranslateProperty(name, raw, source, guard) {
30613
30838
  const trimmed = raw.trim().toLowerCase();
30614
30839
  if (trimmed.length === 0) {
30615
- return createUnknown(name, raw, source, guard, guardProvenance, "translate value is empty");
30840
+ return createUnknown(name, source, guard, "translate value is empty");
30616
30841
  }
30617
30842
  if (hasDynamicExpression(trimmed)) {
30618
- return createUnknown(name, raw, source, guard, guardProvenance, "translate uses runtime-dependent function");
30843
+ return createUnknown(name, source, guard, "translate uses runtime-dependent function");
30619
30844
  }
30620
30845
  const y = extractTranslatePropertyYPx(trimmed);
30621
- if (y !== null) return createKnown(name, raw, source, guard, guardProvenance, y, "px", "exact");
30622
- return createUnknown(name, raw, source, guard, guardProvenance, "translate property vertical component is not px");
30846
+ if (y !== null) return createKnown(name, raw, source, guard, y, 0 /* Px */, "exact");
30847
+ return createUnknown(name, source, guard, "translate property vertical component is not px");
30623
30848
  }
30624
30849
  function hasDynamicExpression(raw) {
30625
30850
  if (raw.includes("var(")) return true;
@@ -30631,28 +30856,24 @@ function hasDynamicExpression(raw) {
30631
30856
  if (raw.includes("clamp(")) return true;
30632
30857
  return false;
30633
30858
  }
30634
- function createKnown(name, raw, source, guard, guardProvenance, px, unit, quality) {
30859
+ function createKnown(name, raw, source, guard, px, unit, quality) {
30635
30860
  return {
30636
30861
  kind: "known",
30637
30862
  name,
30638
- raw,
30639
30863
  normalized: raw.trim().toLowerCase(),
30640
30864
  source,
30641
30865
  guard,
30642
- guardProvenance,
30643
30866
  unit,
30644
30867
  px,
30645
30868
  quality
30646
30869
  };
30647
30870
  }
30648
- function createUnknown(name, raw, source, guard, guardProvenance, reason) {
30871
+ function createUnknown(name, source, guard, reason) {
30649
30872
  return {
30650
30873
  kind: "unknown",
30651
30874
  name,
30652
- raw,
30653
30875
  source,
30654
30876
  guard,
30655
- guardProvenance,
30656
30877
  reason
30657
30878
  };
30658
30879
  }
@@ -30699,13 +30920,7 @@ function buildSnapshotForNode(node, cascadeByElementNode, snapshotByElementNode,
30699
30920
  const unknownSignalCount = normalized.unknownSignalCount + inherited.unknownDelta;
30700
30921
  const conditionalSignalCount = normalized.conditionalSignalCount + inherited.conditionalDelta;
30701
30922
  const snapshot = {
30702
- solidFile: node.solidFile,
30703
- elementId: node.elementId,
30704
- elementKey: node.key,
30705
- tag: node.tag,
30706
- textualContent: node.textualContent,
30707
- isControl: node.isControl,
30708
- isReplaced: node.isReplaced,
30923
+ node,
30709
30924
  signals: inherited.signals,
30710
30925
  knownSignalCount,
30711
30926
  unknownSignalCount,
@@ -30736,7 +30951,7 @@ function inheritSignalsFromParent(parentSnapshot, local) {
30736
30951
  if (!inheritedValue) continue;
30737
30952
  if (out === null) out = new Map(local);
30738
30953
  out.set(signal, inheritedValue);
30739
- if (inheritedValue.guard === "conditional") {
30954
+ if (inheritedValue.guard.kind === 1 /* Conditional */) {
30740
30955
  conditionalDelta++;
30741
30956
  continue;
30742
30957
  }
@@ -30780,7 +30995,7 @@ var EMPTY_LAYOUT_RESERVED_SPACE_FACT = Object.freeze({
30780
30995
  });
30781
30996
  var EMPTY_LAYOUT_SCROLL_CONTAINER_FACT = Object.freeze({
30782
30997
  isScrollContainer: false,
30783
- axis: "none",
30998
+ axis: 0 /* None */,
30784
30999
  overflow: null,
30785
31000
  overflowY: null,
30786
31001
  hasConditionalScroll: false,
@@ -30813,53 +31028,28 @@ function readKnownSignalWithGuard(snapshot, name) {
30813
31028
  return value2;
30814
31029
  }
30815
31030
  function toEvidenceKind(value2) {
30816
- if (value2.guard === "conditional") return "conditional";
30817
- if (value2.quality === "estimated") return "interval";
30818
- return "exact";
30819
- }
30820
- function readNumericSignalEvidence(snapshot, name) {
30821
- const value2 = snapshot.signals.get(name);
30822
- if (!value2) {
30823
- return {
30824
- value: null,
30825
- kind: "unknown"
30826
- };
30827
- }
30828
- if (value2.kind !== "known") {
30829
- if (value2.guard === "conditional") {
30830
- return {
30831
- value: null,
30832
- kind: "conditional"
30833
- };
30834
- }
30835
- return {
30836
- value: null,
30837
- kind: "unknown"
30838
- };
30839
- }
30840
- return {
30841
- value: value2.px,
30842
- kind: toEvidenceKind(value2)
30843
- };
31031
+ if (value2.guard.kind === 1 /* Conditional */) return 2 /* Conditional */;
31032
+ if (value2.quality === "estimated") return 1 /* Interval */;
31033
+ return 0 /* Exact */;
30844
31034
  }
30845
31035
  function readNormalizedSignalEvidence(snapshot, name) {
30846
31036
  const value2 = snapshot.signals.get(name);
30847
31037
  if (!value2) {
30848
31038
  return {
30849
31039
  value: null,
30850
- kind: "unknown"
31040
+ kind: 3 /* Unknown */
30851
31041
  };
30852
31042
  }
30853
31043
  if (value2.kind !== "known") {
30854
- if (value2.guard === "conditional") {
31044
+ if (value2.guard.kind === 1 /* Conditional */) {
30855
31045
  return {
30856
31046
  value: null,
30857
- kind: "conditional"
31047
+ kind: 2 /* Conditional */
30858
31048
  };
30859
31049
  }
30860
31050
  return {
30861
31051
  value: null,
30862
- kind: "unknown"
31052
+ kind: 3 /* Unknown */
30863
31053
  };
30864
31054
  }
30865
31055
  return {
@@ -30870,7 +31060,7 @@ function readNormalizedSignalEvidence(snapshot, name) {
30870
31060
  function readKnownSignal(snapshot, name) {
30871
31061
  const value2 = readKnownSignalWithGuard(snapshot, name);
30872
31062
  if (!value2) return null;
30873
- if (value2.guard !== "unconditional") return null;
31063
+ if (value2.guard.kind !== 0 /* Unconditional */) return null;
30874
31064
  return value2;
30875
31065
  }
30876
31066
  function readKnownPx(snapshot, name) {
@@ -30904,19 +31094,19 @@ function hasEffectivePosition(snapshot) {
30904
31094
  return position !== "static";
30905
31095
  }
30906
31096
  function readReservedSpaceFact(graph, node) {
30907
- return graph.reservedSpaceFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_RESERVED_SPACE_FACT;
31097
+ return graph.reservedSpaceFactsByNode.get(node) ?? EMPTY_LAYOUT_RESERVED_SPACE_FACT;
30908
31098
  }
30909
31099
  function readScrollContainerFact(graph, node) {
30910
- return graph.scrollContainerFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_SCROLL_CONTAINER_FACT;
31100
+ return graph.scrollContainerFactsByNode.get(node) ?? EMPTY_LAYOUT_SCROLL_CONTAINER_FACT;
30911
31101
  }
30912
31102
  function readFlowParticipationFact(graph, node) {
30913
- return graph.flowParticipationFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_FLOW_PARTICIPATION_FACT;
31103
+ return graph.flowParticipationFactsByNode.get(node) ?? EMPTY_LAYOUT_FLOW_PARTICIPATION_FACT;
30914
31104
  }
30915
31105
  function readContainingBlockFact(graph, node) {
30916
- return graph.containingBlockFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_CONTAINING_BLOCK_FACT;
31106
+ return graph.containingBlockFactsByNode.get(node) ?? EMPTY_LAYOUT_CONTAINING_BLOCK_FACT;
30917
31107
  }
30918
31108
  function readConditionalSignalDeltaFact(graph, node, name) {
30919
- const byProperty = graph.conditionalSignalDeltaFactsByElementKey.get(node.key);
31109
+ const byProperty = graph.conditionalSignalDeltaFactsByNode.get(node);
30920
31110
  if (!byProperty) return EMPTY_LAYOUT_CONDITIONAL_DELTA_FACT;
30921
31111
  return byProperty.get(name) ?? EMPTY_LAYOUT_CONDITIONAL_DELTA_FACT;
30922
31112
  }
@@ -30944,7 +31134,7 @@ function readScrollContainerElements(graph) {
30944
31134
  return graph.scrollContainerElements;
30945
31135
  }
30946
31136
  function readBaselineOffsetFacts(graph, node) {
30947
- return graph.baselineOffsetFactsByElementKey.get(node.key) ?? EMPTY_BASELINE_FACTS;
31137
+ return graph.baselineOffsetFactsByNode.get(node) ?? EMPTY_BASELINE_FACTS;
30948
31138
  }
30949
31139
  function readElementRef(graph, node) {
30950
31140
  return readElementRefById(graph, node.solidFile, node.elementId);
@@ -30966,7 +31156,6 @@ function readStatefulBaseValueIndex(graph) {
30966
31156
  }
30967
31157
 
30968
31158
  // src/cross-file/layout/context-classification.ts
30969
- var WHITESPACE_RE3 = /\s+/;
30970
31159
  var TABLE_SEMANTIC_TAGS = /* @__PURE__ */ new Set(["table", "thead", "tbody", "tfoot", "tr", "td", "th"]);
30971
31160
  var TABLE_DISPLAY_VALUES = /* @__PURE__ */ new Set([
30972
31161
  "table",
@@ -30983,7 +31172,6 @@ var TABLE_DISPLAY_VALUES = /* @__PURE__ */ new Set([
30983
31172
  var FLEX_DISPLAY_VALUES = /* @__PURE__ */ new Set(["flex", "inline-flex"]);
30984
31173
  var GRID_DISPLAY_VALUES = /* @__PURE__ */ new Set(["grid", "inline-grid"]);
30985
31174
  var INLINE_DISPLAY_VALUES = /* @__PURE__ */ new Set(["inline", "inline-block", "inline-list-item"]);
30986
- var DISPLAY_TOKEN_SPLIT_RE = /\s+/;
30987
31175
  function createAlignmentContextForParent(parent, snapshot) {
30988
31176
  const axis = resolveAxis(snapshot);
30989
31177
  const inlineDirection = resolveInlineDirection(snapshot);
@@ -31006,6 +31194,7 @@ function createAlignmentContextForParent(parent, snapshot) {
31006
31194
  const contextCertainty = combineCertainty(classified.certainty, axis.certainty);
31007
31195
  const certainty = combineCertainty(contextCertainty, inlineDirection.certainty);
31008
31196
  const baselineRelevance = computeBaselineRelevance(classified.kind, parentAlignItems, parentPlaceItems);
31197
+ const crossAxisInfo = resolveCrossAxisIsBlockAxis(classified.kind, snapshot, axis.value);
31009
31198
  const out = {
31010
31199
  kind: classified.kind,
31011
31200
  certainty,
@@ -31021,6 +31210,8 @@ function createAlignmentContextForParent(parent, snapshot) {
31021
31210
  parentAlignItems,
31022
31211
  parentPlaceItems,
31023
31212
  hasPositionedOffset: positionedOffset.hasPositionedOffset,
31213
+ crossAxisIsBlockAxis: crossAxisInfo.value,
31214
+ crossAxisIsBlockAxisCertainty: crossAxisInfo.certainty,
31024
31215
  baselineRelevance,
31025
31216
  evidence
31026
31217
  };
@@ -31030,7 +31221,7 @@ function classifyKind(evidence) {
31030
31221
  if (evidence.hasTableSemantics) {
31031
31222
  return {
31032
31223
  kind: "table-cell",
31033
- certainty: "resolved"
31224
+ certainty: 0 /* Resolved */
31034
31225
  };
31035
31226
  }
31036
31227
  if (evidence.containerKind === "table") {
@@ -31117,7 +31308,7 @@ function resolveContainerKind(parentDisplay, certainty) {
31117
31308
  certainty
31118
31309
  };
31119
31310
  }
31120
- const tokens = display.split(DISPLAY_TOKEN_SPLIT_RE);
31311
+ const tokens = display.split(WHITESPACE_RE3);
31121
31312
  if (tokens.length === 2) {
31122
31313
  const outside = tokens[0];
31123
31314
  const inside = tokens[1];
@@ -31167,7 +31358,7 @@ function resolveAxis(snapshot) {
31167
31358
  if (!snapshot.signals.has("writing-mode")) {
31168
31359
  return {
31169
31360
  value: "horizontal-tb",
31170
- certainty: "resolved"
31361
+ certainty: 0 /* Resolved */
31171
31362
  };
31172
31363
  }
31173
31364
  const writingMode = readNormalizedSignalEvidence(snapshot, "writing-mode");
@@ -31192,7 +31383,7 @@ function resolveInlineDirection(snapshot) {
31192
31383
  if (!snapshot.signals.has("direction")) {
31193
31384
  return {
31194
31385
  value: "ltr",
31195
- certainty: "resolved"
31386
+ certainty: 0 /* Resolved */
31196
31387
  };
31197
31388
  }
31198
31389
  const direction = readNormalizedSignalEvidence(snapshot, "direction");
@@ -31208,16 +31399,16 @@ function resolveInlineDirection(snapshot) {
31208
31399
  };
31209
31400
  }
31210
31401
  function toContextCertainty(kind) {
31211
- if (kind === "exact") return "resolved";
31212
- if (kind === "interval" || kind === "conditional") return "conditional";
31213
- return "unknown";
31402
+ if (kind === 0 /* Exact */) return 0 /* Resolved */;
31403
+ if (kind === 1 /* Interval */ || kind === 2 /* Conditional */) return 1 /* Conditional */;
31404
+ return 2 /* Unknown */;
31214
31405
  }
31215
31406
  function resolvePositionedOffset(snapshot) {
31216
31407
  const position = readKnownSignalWithGuard(snapshot, "position");
31217
31408
  if (!position) {
31218
31409
  return {
31219
31410
  hasPositionedOffset: false,
31220
- certainty: "unknown"
31411
+ certainty: 2 /* Unknown */
31221
31412
  };
31222
31413
  }
31223
31414
  const certainty = resolveSignalCertainty(position);
@@ -31232,15 +31423,33 @@ function resolvePositionedOffset(snapshot) {
31232
31423
  certainty
31233
31424
  };
31234
31425
  }
31426
+ var FLEX_ROW_VALUES = /* @__PURE__ */ new Set(["row", "row-reverse"]);
31427
+ function resolveCrossAxisIsBlockAxis(kind, snapshot, _axis) {
31428
+ if (kind !== "flex-cross-axis" && kind !== "grid-cross-axis") {
31429
+ return { value: true, certainty: 0 /* Resolved */ };
31430
+ }
31431
+ if (kind === "flex-cross-axis") {
31432
+ const signal2 = readKnownSignalWithGuard(snapshot, "flex-direction");
31433
+ if (!signal2) {
31434
+ return { value: true, certainty: 0 /* Resolved */ };
31435
+ }
31436
+ const certainty2 = resolveSignalCertainty(signal2);
31437
+ return { value: FLEX_ROW_VALUES.has(signal2.normalized), certainty: certainty2 };
31438
+ }
31439
+ const signal = readKnownSignalWithGuard(snapshot, "grid-auto-flow");
31440
+ if (!signal) {
31441
+ return { value: true, certainty: 0 /* Resolved */ };
31442
+ }
31443
+ const certainty = resolveSignalCertainty(signal);
31444
+ return { value: !signal.normalized.startsWith("column"), certainty };
31445
+ }
31235
31446
  function resolveSignalCertainty(value2) {
31236
- if (!value2) return "unknown";
31237
- if (value2.guard === "conditional") return "conditional";
31238
- return "resolved";
31447
+ if (!value2) return 2 /* Unknown */;
31448
+ if (value2.guard.kind === 1 /* Conditional */) return 1 /* Conditional */;
31449
+ return 0 /* Resolved */;
31239
31450
  }
31240
31451
  function combineCertainty(left, right) {
31241
- if (left === "unknown" || right === "unknown") return "unknown";
31242
- if (left === "conditional" || right === "conditional") return "conditional";
31243
- return "resolved";
31452
+ return left > right ? left : right;
31244
31453
  }
31245
31454
  var FLEX_GRID_GEOMETRIC_ALIGN_ITEMS = /* @__PURE__ */ new Set([
31246
31455
  "center",
@@ -31279,24 +31488,7 @@ function finalizeTableCellBaselineRelevance(contextByParentNode, cohortVerticalA
31279
31488
  if (context.kind !== "table-cell") continue;
31280
31489
  if (consensusValue === null) continue;
31281
31490
  if (!TABLE_CELL_GEOMETRIC_VERTICAL_ALIGN.has(consensusValue)) continue;
31282
- contextByParentNode.set(parent, {
31283
- kind: context.kind,
31284
- certainty: context.certainty,
31285
- parentSolidFile: context.parentSolidFile,
31286
- parentElementId: context.parentElementId,
31287
- parentElementKey: context.parentElementKey,
31288
- parentTag: context.parentTag,
31289
- axis: context.axis,
31290
- axisCertainty: context.axisCertainty,
31291
- inlineDirection: context.inlineDirection,
31292
- inlineDirectionCertainty: context.inlineDirectionCertainty,
31293
- parentDisplay: context.parentDisplay,
31294
- parentAlignItems: context.parentAlignItems,
31295
- parentPlaceItems: context.parentPlaceItems,
31296
- hasPositionedOffset: context.hasPositionedOffset,
31297
- baselineRelevance: "irrelevant",
31298
- evidence: context.evidence
31299
- });
31491
+ contextByParentNode.set(parent, deriveAlignmentContext(context, { baselineRelevance: "irrelevant" }));
31300
31492
  }
31301
31493
  }
31302
31494
 
@@ -31843,6 +32035,20 @@ function collectTransitiveCSSScope(entryPath, resolver, cssFilesByNormalizedPath
31843
32035
  }
31844
32036
 
31845
32037
  // src/cross-file/layout/perf.ts
32038
+ function createReservoir(capacity) {
32039
+ return { buffer: [], count: 0, capacity };
32040
+ }
32041
+ function reservoirPush(r, value2) {
32042
+ r.count++;
32043
+ if (r.buffer.length < r.capacity) {
32044
+ r.buffer.push(value2);
32045
+ return;
32046
+ }
32047
+ const j = Math.floor(Math.random() * r.count);
32048
+ if (j < r.capacity) {
32049
+ r.buffer[j] = value2;
32050
+ }
32051
+ }
31846
32052
  var EMPTY_STATS = {
31847
32053
  elementsScanned: 0,
31848
32054
  selectorCandidatesChecked: 0,
@@ -31899,7 +32105,7 @@ function createLayoutPerfStats() {
31899
32105
  cohortUnimodalFalse: 0,
31900
32106
  factorCoverageSum: 0,
31901
32107
  factorCoverageCount: 0,
31902
- posteriorWidths: [],
32108
+ posteriorWidths: createReservoir(200),
31903
32109
  uncertaintyEscalations: 0,
31904
32110
  signalSnapshotsBuilt: 0,
31905
32111
  signalSnapshotCacheHits: 0,
@@ -31964,14 +32170,12 @@ function maybeLogLayoutPerf(stats, log) {
31964
32170
  `[layout] elements=${view.elementsScanned} candidates=${view.selectorCandidatesChecked} compiledSelectors=${view.compiledSelectorCount} unsupportedSelectors=${view.selectorsRejectedUnsupported} conditionalSelectors=${view.selectorsGuardedConditional} ancestryChecks=${view.ancestryChecks} edges=${view.matchEdgesCreated} collected=${view.casesCollected} cases=${view.casesScored} rejectLowEvidence=${view.casesRejectedLowEvidence} rejectThreshold=${view.casesRejectedThreshold} rejectUndecidable=${view.casesRejectedUndecidable} rejectIdentifiability=${view.casesRejectedIdentifiability} undecidableInterval=${view.undecidableInterval} conditionalSignalRatio=${Math.round(view.conditionalSignalRatio * 1e3) / 1e3} conditionalSignals=${view.conditionalSignals} totalSignals=${view.totalSignals} cohortUnimodalFalse=${view.cohortUnimodalFalse} factorCoverageMean=${Math.round(view.factorCoverageMean * 1e3) / 1e3} posteriorWidthP95=${Math.round(view.posteriorWidthP95 * 1e3) / 1e3} uncertaintyEscalations=${view.uncertaintyEscalations} snapshots=${view.signalSnapshotsBuilt} snapshotHits=${view.signalSnapshotCacheHits} measurementIndexHits=${view.measurementIndexHits} contexts=${view.contextsClassified} diagnostics=${view.diagnosticsEmitted} selectorIndexMs=${Math.round(view.selectorIndexMs * 100) / 100} selectorMatchMs=${Math.round(view.selectorMatchMs * 100) / 100} cascadeBuildMs=${Math.round(view.cascadeBuildMs * 100) / 100} caseBuildMs=${Math.round(view.caseBuildMs * 100) / 100} scoringMs=${Math.round(view.scoringMs * 100) / 100} elapsedMs=${Math.round(view.elapsedMs * 100) / 100}`
31965
32171
  );
31966
32172
  }
31967
- function computeP95(values) {
31968
- if (values.length === 0) return 0;
31969
- const sorted = [...values];
31970
- sorted.sort((left, right) => left - right);
31971
- const index = Math.ceil(sorted.length * 0.95) - 1;
31972
- if (index <= 0) return sorted[0] ?? 0;
31973
- if (index >= sorted.length) return sorted[sorted.length - 1] ?? 0;
31974
- return sorted[index] ?? 0;
32173
+ function computeP95(sampler) {
32174
+ if (sampler.buffer.length === 0) return 0;
32175
+ const scratch = [...sampler.buffer];
32176
+ const index = Math.ceil(scratch.length * 0.95) - 1;
32177
+ const clamped = index <= 0 ? 0 : index >= scratch.length ? scratch.length - 1 : index;
32178
+ return selectKth(scratch, clamped);
31975
32179
  }
31976
32180
 
31977
32181
  // src/cross-file/layout/component-host.ts
@@ -33941,16 +34145,16 @@ function includesAttributeWord(value2, word) {
33941
34145
 
33942
34146
  // src/cross-file/layout/guard-model.ts
33943
34147
  var UNCONDITIONAL_GUARD = {
33944
- kind: "unconditional",
34148
+ kind: 0 /* Unconditional */,
33945
34149
  conditions: [],
33946
34150
  key: "always"
33947
34151
  };
33948
- var WHITESPACE_RE4 = /\s+/g;
34152
+ var WHITESPACE_RE_GLOBAL = /\s+/g;
33949
34153
  function resolveRuleGuard(rule) {
33950
34154
  const conditions = collectRuleConditions(rule);
33951
34155
  if (conditions.length === 0) return UNCONDITIONAL_GUARD;
33952
34156
  return {
33953
- kind: "conditional",
34157
+ kind: 1 /* Conditional */,
33954
34158
  conditions,
33955
34159
  key: conditions.map((condition) => condition.key).join("&")
33956
34160
  };
@@ -34004,7 +34208,7 @@ function buildCondition(kind, query) {
34004
34208
  }
34005
34209
  function normalizeQuery(query) {
34006
34210
  if (query === null) return null;
34007
- const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE4, " ");
34211
+ const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE_GLOBAL, " ");
34008
34212
  if (normalized.length === 0) return null;
34009
34213
  return normalized;
34010
34214
  }
@@ -34074,7 +34278,7 @@ function summarizeSignalFacts(snapshots) {
34074
34278
  }
34075
34279
  function accumulateSnapshotFacts(snapshot, sink) {
34076
34280
  for (const value2 of snapshot.signals.values()) {
34077
- if (value2.guard === "conditional") {
34281
+ if (value2.guard.kind === 1 /* Conditional */) {
34078
34282
  sink.addConditional();
34079
34283
  continue;
34080
34284
  }
@@ -34268,15 +34472,6 @@ function assertUnitInterval(name, value2) {
34268
34472
  }
34269
34473
 
34270
34474
  // src/cross-file/layout/content-composition.ts
34271
- var INTRINSIC_REPLACED_TAGS = /* @__PURE__ */ new Set([
34272
- "img",
34273
- "svg",
34274
- "video",
34275
- "canvas",
34276
- "iframe",
34277
- "object",
34278
- "embed"
34279
- ]);
34280
34475
  var BLOCK_FORMATTING_CONTEXT_DISPLAYS = /* @__PURE__ */ new Set([
34281
34476
  "block",
34282
34477
  "flex",
@@ -34310,7 +34505,7 @@ var VERTICAL_ALIGN_MITIGATIONS = /* @__PURE__ */ new Set([
34310
34505
  "text-top",
34311
34506
  "text-bottom"
34312
34507
  ]);
34313
- function computeContentCompositionFingerprint(elementNode, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey) {
34508
+ function computeContentCompositionFingerprint(elementNode, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode) {
34314
34509
  const state = {
34315
34510
  hasTextContent: false,
34316
34511
  hasInlineReplaced: false,
@@ -34324,10 +34519,10 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34324
34519
  blockChildCount: 0,
34325
34520
  inlineChildCount: 0
34326
34521
  };
34327
- if (elementNode.textualContent === "yes" || elementNode.textualContent === "dynamic-text") {
34522
+ if (elementNode.textualContent === 0 /* Yes */ || elementNode.textualContent === 3 /* DynamicText */) {
34328
34523
  state.hasTextContent = true;
34329
34524
  }
34330
- const elementHotSignals = snapshotHotSignalsByElementKey.get(elementNode.key);
34525
+ const elementHotSignals = snapshotHotSignalsByNode.get(elementNode);
34331
34526
  const elementDisplay = elementHotSignals?.display.value ?? null;
34332
34527
  if (elementDisplay !== null && establishesFormattingContext(elementDisplay)) {
34333
34528
  return {
@@ -34338,7 +34533,7 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34338
34533
  wrappingContextMitigates: false,
34339
34534
  hasVerticalAlignMitigation: false,
34340
34535
  mixedContentDepth: 0,
34341
- classification: "block-segmented",
34536
+ classification: 4 /* BlockSegmented */,
34342
34537
  analyzableChildCount: 0,
34343
34538
  totalChildCount: 0,
34344
34539
  hasOnlyBlockChildren: false
@@ -34348,7 +34543,7 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34348
34543
  elementNode,
34349
34544
  childrenByParentNode,
34350
34545
  snapshotByElementNode,
34351
- snapshotHotSignalsByElementKey,
34546
+ snapshotHotSignalsByNode,
34352
34547
  state,
34353
34548
  0
34354
34549
  );
@@ -34368,7 +34563,7 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34368
34563
  hasOnlyBlockChildren
34369
34564
  };
34370
34565
  }
34371
- function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, state, depth) {
34566
+ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, state, depth) {
34372
34567
  const children = childrenByParentNode.get(node);
34373
34568
  if (!children) return;
34374
34569
  for (let i = 0; i < children.length; i++) {
@@ -34379,7 +34574,7 @@ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode
34379
34574
  if (!snapshot) continue;
34380
34575
  if (depth === 0) state.analyzableChildCount++;
34381
34576
  const childTag = child.tagName?.toLowerCase() ?? null;
34382
- const hotSignals = snapshotHotSignalsByElementKey.get(child.key);
34577
+ const hotSignals = snapshotHotSignalsByNode.get(child);
34383
34578
  const childDisplay = hotSignals?.display.value ?? null;
34384
34579
  if (childTag !== null && (isIntrinsicReplacedTag(childTag) || isControlReplacedTag(childTag))) {
34385
34580
  state.hasInlineReplaced = true;
@@ -34401,16 +34596,16 @@ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode
34401
34596
  checkVerticalAlignMitigation(snapshot, state);
34402
34597
  updateMixedContentDepth(state, depth);
34403
34598
  if (depth === 0) state.inlineChildCount++;
34404
- const parentHotSignals = snapshotHotSignalsByElementKey.get(node.key);
34599
+ const parentHotSignals = snapshotHotSignalsByNode.get(node);
34405
34600
  const parentDisplay = parentHotSignals?.display.value ?? null;
34406
34601
  if (parentDisplay !== null && isAlignmentContextWithNonBaselineAlignment(parentDisplay, parentHotSignals)) {
34407
34602
  state.wrappingContextMitigates = true;
34408
- } else if (isAlignmentContextWithNonBaselineAlignment(childDisplay, hotSignals) && containsMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey)) {
34603
+ } else if (isAlignmentContextWithNonBaselineAlignment(childDisplay, hotSignals) && containsMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode)) {
34409
34604
  state.wrappingContextMitigates = true;
34410
34605
  }
34411
34606
  continue;
34412
34607
  }
34413
- if (child.textualContent === "yes" || child.textualContent === "dynamic-text") {
34608
+ if (child.textualContent === 0 /* Yes */ || child.textualContent === 3 /* DynamicText */) {
34414
34609
  state.hasTextContent = true;
34415
34610
  }
34416
34611
  checkHeightContributions(snapshot, state);
@@ -34420,7 +34615,7 @@ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode
34420
34615
  child,
34421
34616
  childrenByParentNode,
34422
34617
  snapshotByElementNode,
34423
- snapshotHotSignalsByElementKey,
34618
+ snapshotHotSignalsByNode,
34424
34619
  state,
34425
34620
  depth + 1
34426
34621
  );
@@ -34455,19 +34650,19 @@ function isAlignmentContextWithNonBaselineAlignment(display, hotSignals) {
34455
34650
  if (alignItems === null) return false;
34456
34651
  return alignItems !== "baseline";
34457
34652
  }
34458
- function containsMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey) {
34459
- const hasText = node.textualContent === "yes" || node.textualContent === "dynamic-text";
34653
+ function containsMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode) {
34654
+ const hasText = node.textualContent === 0 /* Yes */ || node.textualContent === 3 /* DynamicText */;
34460
34655
  const hasReplaced = false;
34461
- return scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, { hasText, hasReplaced });
34656
+ return scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, { hasText, hasReplaced });
34462
34657
  }
34463
- function scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, found) {
34658
+ function scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, found) {
34464
34659
  const children = childrenByParentNode.get(node);
34465
34660
  if (!children) return false;
34466
34661
  for (let i = 0; i < children.length; i++) {
34467
34662
  const child = children[i];
34468
34663
  if (!child) continue;
34469
34664
  const childTag = child.tagName?.toLowerCase() ?? null;
34470
- const hotSignals = snapshotHotSignalsByElementKey.get(child.key);
34665
+ const hotSignals = snapshotHotSignalsByNode.get(child);
34471
34666
  const childDisplay = hotSignals?.display.value ?? null;
34472
34667
  if (childTag !== null && (isIntrinsicReplacedTag(childTag) || isControlReplacedTag(childTag))) {
34473
34668
  found.hasReplaced = true;
@@ -34482,12 +34677,12 @@ function scanMixedContent(node, childrenByParentNode, snapshotByElementNode, sna
34482
34677
  if (found.hasText) return true;
34483
34678
  continue;
34484
34679
  }
34485
- if (child.textualContent === "yes" || child.textualContent === "dynamic-text") {
34680
+ if (child.textualContent === 0 /* Yes */ || child.textualContent === 3 /* DynamicText */) {
34486
34681
  found.hasText = true;
34487
34682
  if (found.hasReplaced) return true;
34488
34683
  }
34489
34684
  if (childDisplay === null || isInlineContinuationDisplay(childDisplay)) {
34490
- if (scanMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, found)) {
34685
+ if (scanMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, found)) {
34491
34686
  return true;
34492
34687
  }
34493
34688
  }
@@ -34510,30 +34705,30 @@ function updateMixedContentDepth(state, depth) {
34510
34705
  }
34511
34706
  function classifyFromState(state, elementNode, hasOnlyBlockChildren) {
34512
34707
  if (hasOnlyBlockChildren) {
34513
- return "block-segmented";
34708
+ return 4 /* BlockSegmented */;
34514
34709
  }
34515
34710
  if (state.totalChildCount === 0 && !state.hasTextContent) {
34516
- if (elementNode.textualContent === "unknown") return "unknown";
34517
- if (elementNode.textualContent === "yes" || elementNode.textualContent === "dynamic-text") {
34518
- return "text-only";
34711
+ if (elementNode.textualContent === 2 /* Unknown */) return 5 /* Unknown */;
34712
+ if (elementNode.textualContent === 0 /* Yes */ || elementNode.textualContent === 3 /* DynamicText */) {
34713
+ return 0 /* TextOnly */;
34519
34714
  }
34520
- return "unknown";
34715
+ return 5 /* Unknown */;
34521
34716
  }
34522
34717
  if (state.analyzableChildCount === 0 && state.totalChildCount > 0) {
34523
- return "unknown";
34718
+ return 5 /* Unknown */;
34524
34719
  }
34525
34720
  if (state.hasTextContent && state.hasInlineReplaced) {
34526
- if (state.wrappingContextMitigates) return "mixed-mitigated";
34527
- if (state.hasVerticalAlignMitigation) return "mixed-mitigated";
34528
- return "mixed-unmitigated";
34721
+ if (state.wrappingContextMitigates) return 3 /* MixedMitigated */;
34722
+ if (state.hasVerticalAlignMitigation) return 3 /* MixedMitigated */;
34723
+ return 2 /* MixedUnmitigated */;
34529
34724
  }
34530
34725
  if (!state.hasTextContent && state.hasInlineReplaced) {
34531
- return "replaced-only";
34726
+ return 1 /* ReplacedOnly */;
34532
34727
  }
34533
34728
  if (state.hasTextContent && !state.hasInlineReplaced) {
34534
- return "text-only";
34729
+ return 0 /* TextOnly */;
34535
34730
  }
34536
- return "unknown";
34731
+ return 5 /* Unknown */;
34537
34732
  }
34538
34733
  function isIntrinsicReplacedTag(tag) {
34539
34734
  return INTRINSIC_REPLACED_TAGS.has(tag);
@@ -34553,10 +34748,14 @@ function isInlineReplacedDisplay(display) {
34553
34748
  function isInlineContinuationDisplay(display) {
34554
34749
  return INLINE_CONTINUATION_DISPLAYS.has(display);
34555
34750
  }
34556
- function resolveCompositionDivergenceStrength(subjectFingerprint, allFingerprints, parentContext) {
34557
- if (allFingerprints.length < 2) return 0;
34751
+ var NO_DIVERGENCE = Object.freeze({
34752
+ strength: 0,
34753
+ majorityClassification: 5 /* Unknown */
34754
+ });
34755
+ function resolveCompositionDivergence(subjectFingerprint, allFingerprints, parentContext) {
34756
+ if (allFingerprints.length < 2) return NO_DIVERGENCE;
34558
34757
  if (parentContext !== null && !hasSharedBaselineAlignment(parentContext)) {
34559
- return 0;
34758
+ return NO_DIVERGENCE;
34560
34759
  }
34561
34760
  const countByClassification = /* @__PURE__ */ new Map();
34562
34761
  for (let i = 0; i < allFingerprints.length; i++) {
@@ -34568,10 +34767,7 @@ function resolveCompositionDivergenceStrength(subjectFingerprint, allFingerprint
34568
34767
  }
34569
34768
  const subjectNormalized = normalizeClassificationForComparison(subjectFingerprint.classification);
34570
34769
  const subjectCount = countByClassification.get(subjectNormalized) ?? 0;
34571
- if (subjectCount === allFingerprints.length) {
34572
- return resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints);
34573
- }
34574
- let majorityClassification = "unknown";
34770
+ let majorityClassification = 5 /* Unknown */;
34575
34771
  let majorityCount = 0;
34576
34772
  for (const [classification, count] of countByClassification) {
34577
34773
  if (count > majorityCount) {
@@ -34579,35 +34775,38 @@ function resolveCompositionDivergenceStrength(subjectFingerprint, allFingerprint
34579
34775
  majorityClassification = classification;
34580
34776
  }
34581
34777
  }
34778
+ if (subjectCount === allFingerprints.length) {
34779
+ return { strength: resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints), majorityClassification };
34780
+ }
34582
34781
  if (subjectNormalized === majorityClassification) {
34583
- return resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints);
34782
+ return { strength: resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints), majorityClassification };
34584
34783
  }
34585
- if (subjectNormalized === "unknown") {
34586
- return 0;
34784
+ if (subjectNormalized === 5 /* Unknown */) {
34785
+ return { strength: 0, majorityClassification };
34587
34786
  }
34588
34787
  const cal = alignmentStrengthCalibration;
34589
- if (majorityClassification === "text-only" && subjectNormalized === "mixed-unmitigated") {
34590
- return cal.compositionMixedUnmitigatedOutlierStrength;
34788
+ if (majorityClassification === 0 /* TextOnly */ && subjectNormalized === 2 /* MixedUnmitigated */) {
34789
+ return { strength: cal.compositionMixedUnmitigatedOutlierStrength, majorityClassification };
34591
34790
  }
34592
- if (majorityClassification === "replaced-only" && subjectNormalized === "mixed-unmitigated") {
34593
- return cal.compositionMixedOutlierAmongReplacedStrength;
34791
+ if (majorityClassification === 1 /* ReplacedOnly */ && subjectNormalized === 2 /* MixedUnmitigated */) {
34792
+ return { strength: cal.compositionMixedOutlierAmongReplacedStrength, majorityClassification };
34594
34793
  }
34595
- if (majorityClassification === "mixed-unmitigated" && subjectNormalized === "text-only") {
34596
- return cal.compositionTextOutlierAmongMixedStrength;
34794
+ if (majorityClassification === 2 /* MixedUnmitigated */ && subjectNormalized === 0 /* TextOnly */) {
34795
+ return { strength: cal.compositionTextOutlierAmongMixedStrength, majorityClassification };
34597
34796
  }
34598
- if (majorityClassification === "mixed-unmitigated" && subjectNormalized === "replaced-only") {
34599
- return cal.compositionTextOutlierAmongMixedStrength;
34797
+ if (majorityClassification === 2 /* MixedUnmitigated */ && subjectNormalized === 1 /* ReplacedOnly */) {
34798
+ return { strength: cal.compositionTextOutlierAmongMixedStrength, majorityClassification };
34600
34799
  }
34601
- if (majorityClassification === "text-only" && subjectNormalized === "replaced-only") {
34602
- return cal.compositionMixedOutlierAmongReplacedStrength;
34800
+ if (majorityClassification === 0 /* TextOnly */ && subjectNormalized === 1 /* ReplacedOnly */) {
34801
+ return { strength: cal.compositionMixedOutlierAmongReplacedStrength, majorityClassification };
34603
34802
  }
34604
- if (majorityClassification === "replaced-only" && subjectNormalized === "text-only") {
34605
- return cal.compositionTextOutlierAmongMixedStrength;
34803
+ if (majorityClassification === 1 /* ReplacedOnly */ && subjectNormalized === 0 /* TextOnly */) {
34804
+ return { strength: cal.compositionTextOutlierAmongMixedStrength, majorityClassification };
34606
34805
  }
34607
- if (majorityClassification === "unknown") {
34608
- return 0;
34806
+ if (majorityClassification === 5 /* Unknown */) {
34807
+ return { strength: 0, majorityClassification };
34609
34808
  }
34610
- return cal.compositionUnknownPenalty;
34809
+ return { strength: cal.compositionUnknownPenalty, majorityClassification };
34611
34810
  }
34612
34811
  function resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints) {
34613
34812
  if (subjectFingerprint.inlineReplacedKind === null) return 0;
@@ -34628,28 +34827,9 @@ function resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints
34628
34827
  function hasSharedBaselineAlignment(context) {
34629
34828
  return context.baselineRelevance === "relevant";
34630
34829
  }
34631
- function resolveMajorityClassification(allFingerprints) {
34632
- const countByClassification = /* @__PURE__ */ new Map();
34633
- for (let i = 0; i < allFingerprints.length; i++) {
34634
- const fp = allFingerprints[i];
34635
- if (!fp) continue;
34636
- const normalized = normalizeClassificationForComparison(fp.classification);
34637
- const existing = countByClassification.get(normalized) ?? 0;
34638
- countByClassification.set(normalized, existing + 1);
34639
- }
34640
- let majorityClassification = "unknown";
34641
- let majorityCount = 0;
34642
- for (const [classification, count] of countByClassification) {
34643
- if (count > majorityCount) {
34644
- majorityCount = count;
34645
- majorityClassification = classification;
34646
- }
34647
- }
34648
- return majorityClassification;
34649
- }
34650
34830
  function normalizeClassificationForComparison(classification) {
34651
- if (classification === "mixed-mitigated") return "text-only";
34652
- if (classification === "block-segmented") return "text-only";
34831
+ if (classification === 3 /* MixedMitigated */) return 0 /* TextOnly */;
34832
+ if (classification === 4 /* BlockSegmented */) return 0 /* TextOnly */;
34653
34833
  return classification;
34654
34834
  }
34655
34835
  function resolveCompositionCoverage(subjectFingerprint, allFingerprints) {
@@ -34657,12 +34837,12 @@ function resolveCompositionCoverage(subjectFingerprint, allFingerprints) {
34657
34837
  let analyzableCount = 0;
34658
34838
  for (let i = 0; i < allFingerprints.length; i++) {
34659
34839
  const fp = allFingerprints[i];
34660
- if (fp && fp.classification !== "unknown") {
34840
+ if (fp && fp.classification !== 5 /* Unknown */) {
34661
34841
  analyzableCount++;
34662
34842
  }
34663
34843
  }
34664
34844
  const analyzableShare = analyzableCount / allFingerprints.length;
34665
- if (subjectFingerprint.classification === "unknown") {
34845
+ if (subjectFingerprint.classification === 5 /* Unknown */) {
34666
34846
  return analyzableShare * 0.3;
34667
34847
  }
34668
34848
  if (subjectFingerprint.totalChildCount > 0 && subjectFingerprint.analyzableChildCount === 0) {
@@ -34670,24 +34850,19 @@ function resolveCompositionCoverage(subjectFingerprint, allFingerprints) {
34670
34850
  }
34671
34851
  return analyzableShare;
34672
34852
  }
34853
+ var COMPOSITION_LABELS = {
34854
+ [0 /* TextOnly */]: "text-only",
34855
+ [1 /* ReplacedOnly */]: "inline-replaced-only",
34856
+ [2 /* MixedUnmitigated */]: "mixed text + inline-replaced",
34857
+ [3 /* MixedMitigated */]: "mixed (alignment mitigated)",
34858
+ [4 /* BlockSegmented */]: "block-segmented",
34859
+ [5 /* Unknown */]: "unknown"
34860
+ };
34673
34861
  function formatCompositionClassification(classification) {
34674
- switch (classification) {
34675
- case "text-only":
34676
- return "text-only";
34677
- case "replaced-only":
34678
- return "inline-replaced-only";
34679
- case "mixed-unmitigated":
34680
- return "mixed text + inline-replaced";
34681
- case "mixed-mitigated":
34682
- return "mixed (alignment mitigated)";
34683
- case "block-segmented":
34684
- return "block-segmented";
34685
- case "unknown":
34686
- return "unknown";
34687
- }
34862
+ return COMPOSITION_LABELS[classification];
34688
34863
  }
34689
34864
  function formatCompositionFixSuggestion(subjectFingerprint) {
34690
- if (subjectFingerprint.classification === "mixed-unmitigated") {
34865
+ if (subjectFingerprint.classification === 2 /* MixedUnmitigated */) {
34691
34866
  if (subjectFingerprint.hasVerticalAlignMitigation) {
34692
34867
  return "verify vertical-align resolves the baseline shift";
34693
34868
  }
@@ -34722,12 +34897,12 @@ function estimateBlockOffsetWithDeclaredFromHotSignals(hot, axis) {
34722
34897
  function estimateBlockOffsetWithDeclaredFromSources(axis, position, readNumeric) {
34723
34898
  let declaredTotal = 0;
34724
34899
  let declaredCount = 0;
34725
- let declaredKind = "exact";
34726
- let declaredMissingKind = "exact";
34900
+ let declaredKind = 0 /* Exact */;
34901
+ let declaredMissingKind = 0 /* Exact */;
34727
34902
  let effectiveTotal = 0;
34728
34903
  let effectiveCount = 0;
34729
- let effectiveKind = "exact";
34730
- let effectiveMissingKind = "exact";
34904
+ let effectiveKind = 0 /* Exact */;
34905
+ let effectiveMissingKind = 0 /* Exact */;
34731
34906
  const positioned = position.value !== null && position.value !== "static";
34732
34907
  const add = (name, sign, requiresPositioning) => {
34733
34908
  const v = readNumeric(name);
@@ -34798,7 +34973,7 @@ function buildCohortIndex(input) {
34798
34973
  axisCertainty: context.axisCertainty,
34799
34974
  measurementNodeByRootKey: input.measurementNodeByRootKey,
34800
34975
  snapshotByElementNode: input.snapshotByElementNode,
34801
- snapshotHotSignalsByElementKey: input.snapshotHotSignalsByElementKey
34976
+ snapshotHotSignalsByNode: input.snapshotHotSignalsByNode
34802
34977
  });
34803
34978
  measurementIndexHits += cohortMetricsResult.measurementHits;
34804
34979
  const metrics = cohortMetricsResult.metrics;
@@ -34832,7 +35007,7 @@ function buildCohortIndex(input) {
34832
35007
  subjectMetrics.rootNode,
34833
35008
  input.childrenByParentNode,
34834
35009
  input.snapshotByElementNode,
34835
- input.snapshotHotSignalsByElementKey
35010
+ input.snapshotHotSignalsByNode
34836
35011
  );
34837
35012
  subjectsByElementKey.set(subjectMetrics.key, {
34838
35013
  element: subjectMetrics.element,
@@ -34898,7 +35073,7 @@ function collectCohortMetrics(input) {
34898
35073
  if (!snapshot) {
34899
35074
  throw new Error(`missing snapshot for measurement node ${measurementNode.key}`);
34900
35075
  }
34901
- const hotSignals = input.snapshotHotSignalsByElementKey.get(measurementNode.key);
35076
+ const hotSignals = input.snapshotHotSignalsByNode.get(measurementNode);
34902
35077
  if (!hotSignals) {
34903
35078
  throw new Error(`missing hot signals for measurement node ${measurementNode.key}`);
34904
35079
  }
@@ -35223,9 +35398,9 @@ function buildCohortSignalIndex(metrics) {
35223
35398
  const verticalAlignCounts = /* @__PURE__ */ new Map();
35224
35399
  const alignSelfCounts = /* @__PURE__ */ new Map();
35225
35400
  const placeSelfCounts = /* @__PURE__ */ new Map();
35226
- let verticalAlignMergedKind = "exact";
35227
- let alignSelfMergedKind = "exact";
35228
- let placeSelfMergedKind = "exact";
35401
+ let verticalAlignMergedKind = 0 /* Exact */;
35402
+ let alignSelfMergedKind = 0 /* Exact */;
35403
+ let placeSelfMergedKind = 0 /* Exact */;
35229
35404
  let verticalAlignComparableCount = 0;
35230
35405
  let alignSelfComparableCount = 0;
35231
35406
  let placeSelfComparableCount = 0;
@@ -35239,12 +35414,12 @@ function buildCohortSignalIndex(metrics) {
35239
35414
  const snapshot = metric.element.snapshot;
35240
35415
  const alignSelf = metric.hotSignals.alignSelf;
35241
35416
  const placeSelf = metric.hotSignals.placeSelf;
35242
- const isControlOrReplaced = snapshot.isControl || snapshot.isReplaced;
35417
+ const isControlOrReplaced = snapshot.node.isControl || snapshot.node.isReplaced;
35243
35418
  const verticalAlign = resolveComparableVerticalAlign(metric.hotSignals.verticalAlign, isControlOrReplaced);
35244
35419
  if (isControlOrReplaced) controlOrReplacedCount++;
35245
- if (snapshot.textualContent === "yes" || snapshot.textualContent === "dynamic-text") textYesCount++;
35246
- if (snapshot.textualContent === "no") textNoCount++;
35247
- if (snapshot.textualContent === "unknown") textUnknownCount++;
35420
+ if (snapshot.node.textualContent === 0 /* Yes */ || snapshot.node.textualContent === 3 /* DynamicText */) textYesCount++;
35421
+ if (snapshot.node.textualContent === 1 /* No */) textNoCount++;
35422
+ if (snapshot.node.textualContent === 2 /* Unknown */) textUnknownCount++;
35248
35423
  if (verticalAlign.value !== null) {
35249
35424
  verticalAlignMergedKind = mergeEvidenceKind(verticalAlignMergedKind, verticalAlign.kind);
35250
35425
  verticalAlignComparableCount++;
@@ -35264,24 +35439,24 @@ function buildCohortSignalIndex(metrics) {
35264
35439
  verticalAlign,
35265
35440
  alignSelf,
35266
35441
  placeSelf,
35267
- textualContent: snapshot.textualContent,
35442
+ textualContent: snapshot.node.textualContent,
35268
35443
  isControlOrReplaced
35269
35444
  });
35270
35445
  }
35271
35446
  return {
35272
35447
  byKey,
35273
35448
  verticalAlign: {
35274
- mergedKind: verticalAlignComparableCount === 0 ? "unknown" : verticalAlignMergedKind,
35449
+ mergedKind: verticalAlignComparableCount === 0 ? 3 /* Unknown */ : verticalAlignMergedKind,
35275
35450
  comparableCount: verticalAlignComparableCount,
35276
35451
  countsByValue: verticalAlignCounts
35277
35452
  },
35278
35453
  alignSelf: {
35279
- mergedKind: alignSelfComparableCount === 0 ? "unknown" : alignSelfMergedKind,
35454
+ mergedKind: alignSelfComparableCount === 0 ? 3 /* Unknown */ : alignSelfMergedKind,
35280
35455
  comparableCount: alignSelfComparableCount,
35281
35456
  countsByValue: alignSelfCounts
35282
35457
  },
35283
35458
  placeSelf: {
35284
- mergedKind: placeSelfComparableCount === 0 ? "unknown" : placeSelfMergedKind,
35459
+ mergedKind: placeSelfComparableCount === 0 ? 3 /* Unknown */ : placeSelfMergedKind,
35285
35460
  comparableCount: placeSelfComparableCount,
35286
35461
  countsByValue: placeSelfCounts
35287
35462
  },
@@ -35318,9 +35493,9 @@ function collectSubjectCohortSignals(index, subjectMetrics, context) {
35318
35493
  sawComparableVerticalAlign,
35319
35494
  sawVerticalAlignConflict
35320
35495
  );
35321
- const tableCellControlFallback = context.kind === "table-cell" && subject.isControlOrReplaced && verticalAlign.value === "unknown" && index.byKey.size > index.controlOrReplacedCount;
35496
+ const tableCellControlFallback = context.kind === "table-cell" && subject.isControlOrReplaced && verticalAlign.value === 2 /* Unknown */ && index.byKey.size > index.controlOrReplacedCount;
35322
35497
  const normalizedVerticalAlign = tableCellControlFallback ? {
35323
- value: "conflict",
35498
+ value: 0 /* Conflict */,
35324
35499
  kind: verticalAlignKind
35325
35500
  } : verticalAlign;
35326
35501
  const textContrastWithPeers = resolveIndexedTextContrastWithPeers(
@@ -35353,7 +35528,7 @@ function resolveComparableVerticalAlign(verticalAlign, isControlOrReplaced) {
35353
35528
  return {
35354
35529
  present: verticalAlign.present,
35355
35530
  value: "baseline",
35356
- kind: "exact"
35531
+ kind: 0 /* Exact */
35357
35532
  };
35358
35533
  }
35359
35534
  function hasComparablePeer(aggregate, subjectValue) {
@@ -35370,37 +35545,37 @@ function hasConflictPeer(aggregate, subjectValue) {
35370
35545
  function finalizeConflictEvidence(subjectValue, kind, sawComparablePeer, sawConflict) {
35371
35546
  if (subjectValue === null) {
35372
35547
  return {
35373
- value: "unknown",
35548
+ value: 2 /* Unknown */,
35374
35549
  kind
35375
35550
  };
35376
35551
  }
35377
35552
  if (!sawComparablePeer) {
35378
35553
  return {
35379
- value: "unknown",
35554
+ value: 2 /* Unknown */,
35380
35555
  kind
35381
35556
  };
35382
35557
  }
35383
35558
  return {
35384
- value: sawConflict ? "conflict" : "aligned",
35559
+ value: sawConflict ? 0 /* Conflict */ : 1 /* Aligned */,
35385
35560
  kind
35386
35561
  };
35387
35562
  }
35388
35563
  function resolveIndexedTextContrastWithPeers(index, subjectTextualContent, subjectIsControlOrReplaced, tableCellControlFallback) {
35389
- if (subjectTextualContent === "unknown") return "unknown";
35564
+ if (subjectTextualContent === 2 /* Unknown */) return 2 /* Unknown */;
35390
35565
  const unknownPeers = index.textUnknownCount;
35391
35566
  const cohortSize = index.byKey.size;
35392
- if (subjectTextualContent === "yes" || subjectTextualContent === "dynamic-text") {
35393
- if (index.textNoCount > 0) return "different";
35394
- if (unknownPeers > 0) return "unknown";
35395
- return "same";
35567
+ if (subjectTextualContent === 0 /* Yes */ || subjectTextualContent === 3 /* DynamicText */) {
35568
+ if (index.textNoCount > 0) return 0 /* Different */;
35569
+ if (unknownPeers > 0) return 2 /* Unknown */;
35570
+ return 1 /* Same */;
35396
35571
  }
35397
- if (index.textYesCount > 0) return "different";
35398
- if (tableCellControlFallback) return "different";
35572
+ if (index.textYesCount > 0) return 0 /* Different */;
35573
+ if (tableCellControlFallback) return 0 /* Different */;
35399
35574
  if (subjectIsControlOrReplaced && index.controlOrReplacedCount === 1 && cohortSize >= 3 && unknownPeers > 0) {
35400
- return "different";
35575
+ return 0 /* Different */;
35401
35576
  }
35402
- if (unknownPeers > 0) return "unknown";
35403
- return "same";
35577
+ if (unknownPeers > 0) return 2 /* Unknown */;
35578
+ return 1 /* Same */;
35404
35579
  }
35405
35580
  function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineProfile, clusterSummary, signalIndex, cohortKind, cohortSize) {
35406
35581
  const subjectClusterKey = toComparableClusterKey(subjectMetrics);
@@ -35416,7 +35591,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35416
35591
  return {
35417
35592
  dominantShare: profile.dominantClusterShare,
35418
35593
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35419
- subjectMembership: "insufficient",
35594
+ subjectMembership: 3 /* Insufficient */,
35420
35595
  ambiguous: true,
35421
35596
  kind
35422
35597
  };
@@ -35425,7 +35600,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35425
35600
  return {
35426
35601
  dominantShare: profile.dominantClusterShare,
35427
35602
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35428
- subjectMembership: "dominant",
35603
+ subjectMembership: 0 /* Dominant */,
35429
35604
  ambiguous: false,
35430
35605
  kind
35431
35606
  };
@@ -35434,7 +35609,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35434
35609
  return {
35435
35610
  dominantShare: profile.dominantClusterShare,
35436
35611
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35437
- subjectMembership: "insufficient",
35612
+ subjectMembership: 3 /* Insufficient */,
35438
35613
  ambiguous: true,
35439
35614
  kind
35440
35615
  };
@@ -35444,7 +35619,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35444
35619
  return {
35445
35620
  dominantShare: profile.dominantClusterShare,
35446
35621
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35447
- subjectMembership: "insufficient",
35622
+ subjectMembership: 3 /* Insufficient */,
35448
35623
  ambiguous: true,
35449
35624
  kind
35450
35625
  };
@@ -35454,7 +35629,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35454
35629
  return {
35455
35630
  dominantShare: profile.dominantClusterShare,
35456
35631
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35457
- subjectMembership: "ambiguous",
35632
+ subjectMembership: 2 /* Ambiguous */,
35458
35633
  ambiguous: true,
35459
35634
  kind
35460
35635
  };
@@ -35463,7 +35638,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35463
35638
  return {
35464
35639
  dominantShare: profile.dominantClusterShare,
35465
35640
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35466
- subjectMembership: "dominant",
35641
+ subjectMembership: 0 /* Dominant */,
35467
35642
  ambiguous: false,
35468
35643
  kind
35469
35644
  };
@@ -35471,13 +35646,13 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35471
35646
  return {
35472
35647
  dominantShare: profile.dominantClusterShare,
35473
35648
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35474
- subjectMembership: "nondominant",
35649
+ subjectMembership: 1 /* Nondominant */,
35475
35650
  ambiguous: false,
35476
35651
  kind
35477
35652
  };
35478
35653
  }
35479
35654
  function resolveControlRoleIdentifiability(subjectMetrics, signalIndex, kind, cohortSize) {
35480
- const subjectIsControlOrReplaced = subjectMetrics.element.snapshot.isControl || subjectMetrics.element.snapshot.isReplaced;
35655
+ const subjectIsControlOrReplaced = subjectMetrics.element.snapshot.node.isControl || subjectMetrics.element.snapshot.node.isReplaced;
35481
35656
  const controlCount = signalIndex.controlOrReplacedCount;
35482
35657
  const nonControlCount = cohortSize - controlCount;
35483
35658
  if (controlCount <= 0 || nonControlCount <= 0) return null;
@@ -35492,12 +35667,12 @@ function resolveControlRoleIdentifiability(subjectMetrics, signalIndex, kind, co
35492
35667
  return {
35493
35668
  dominantShare,
35494
35669
  subjectExcludedDominantShare: excludedDominantShare,
35495
- subjectMembership: subjectMembership === "ambiguous" ? "dominant" : subjectMembership,
35670
+ subjectMembership: subjectMembership === 2 /* Ambiguous */ ? 0 /* Dominant */ : subjectMembership,
35496
35671
  ambiguous: false,
35497
35672
  kind
35498
35673
  };
35499
35674
  }
35500
- if (subjectMembership === "ambiguous") {
35675
+ if (subjectMembership === 2 /* Ambiguous */) {
35501
35676
  return {
35502
35677
  dominantShare,
35503
35678
  subjectExcludedDominantShare: excludedDominantShare,
@@ -35515,9 +35690,9 @@ function resolveControlRoleIdentifiability(subjectMetrics, signalIndex, kind, co
35515
35690
  };
35516
35691
  }
35517
35692
  function resolveRoleMembership(controlCount, nonControlCount, subjectIsControlOrReplaced) {
35518
- if (controlCount === nonControlCount) return "ambiguous";
35693
+ if (controlCount === nonControlCount) return 2 /* Ambiguous */;
35519
35694
  const dominantRoleIsControl = controlCount > nonControlCount;
35520
- return dominantRoleIsControl === subjectIsControlOrReplaced ? "dominant" : "nondominant";
35695
+ return dominantRoleIsControl === subjectIsControlOrReplaced ? 0 /* Dominant */ : 1 /* Nondominant */;
35521
35696
  }
35522
35697
  function resolveExcludedRoleDominantShare(controlCount, nonControlCount, subjectIsControlOrReplaced) {
35523
35698
  const controlAfterExclusion = controlCount - (subjectIsControlOrReplaced ? 1 : 0);
@@ -35555,8 +35730,8 @@ function collectCohortProvenanceFromSnapshots(snapshots) {
35555
35730
  const snapshot = snapshots[i];
35556
35731
  if (!snapshot) continue;
35557
35732
  for (const signal of snapshot.signals.values()) {
35558
- for (let j = 0; j < signal.guardProvenance.conditions.length; j++) {
35559
- const guard = signal.guardProvenance.conditions[j];
35733
+ for (let j = 0; j < signal.guard.conditions.length; j++) {
35734
+ const guard = signal.guard.conditions[j];
35560
35735
  if (!guard) continue;
35561
35736
  if (!byKey.has(guard.key)) byKey.set(guard.key, guard);
35562
35737
  }
@@ -35586,7 +35761,7 @@ function buildGuardKey(guards) {
35586
35761
  return keys.join("&");
35587
35762
  }
35588
35763
  function resolveCohortEvidenceKind(metrics) {
35589
- let kind = "exact";
35764
+ let kind = 0 /* Exact */;
35590
35765
  for (let i = 0; i < metrics.length; i++) {
35591
35766
  const metric = metrics[i];
35592
35767
  if (!metric) continue;
@@ -35603,9 +35778,9 @@ function incrementCount(counts, key) {
35603
35778
  counts.set(key, existing + 1);
35604
35779
  }
35605
35780
  function toEvidenceKind2(certainty) {
35606
- if (certainty === "resolved") return "exact";
35607
- if (certainty === "conditional") return "conditional";
35608
- return "unknown";
35781
+ if (certainty === 0 /* Resolved */) return 0 /* Exact */;
35782
+ if (certainty === 1 /* Conditional */) return 2 /* Conditional */;
35783
+ return 3 /* Unknown */;
35609
35784
  }
35610
35785
  function computeMedian(values) {
35611
35786
  if (values.length === 0) return null;
@@ -35625,66 +35800,6 @@ function computeMedianAbsoluteDeviation(values, median, scratch) {
35625
35800
  }
35626
35801
  return computeMedian(scratch);
35627
35802
  }
35628
- function selectKth(values, targetIndex) {
35629
- let left = 0;
35630
- let right = values.length - 1;
35631
- while (left <= right) {
35632
- if (left === right) {
35633
- const result = values[left];
35634
- if (result === void 0) return 0;
35635
- return result;
35636
- }
35637
- const pivotIndex = choosePivotIndex(values, left, right);
35638
- const partitionIndex = partitionAroundPivot(values, left, right, pivotIndex);
35639
- if (partitionIndex === targetIndex) {
35640
- const result = values[partitionIndex];
35641
- if (result === void 0) return 0;
35642
- return result;
35643
- }
35644
- if (partitionIndex < targetIndex) {
35645
- left = partitionIndex + 1;
35646
- continue;
35647
- }
35648
- right = partitionIndex - 1;
35649
- }
35650
- const fallback = values[targetIndex];
35651
- if (fallback === void 0) return 0;
35652
- return fallback;
35653
- }
35654
- function choosePivotIndex(values, left, right) {
35655
- const middle = Math.floor((left + right) / 2);
35656
- const leftValue = values[left] ?? 0;
35657
- const middleValue = values[middle] ?? 0;
35658
- const rightValue = values[right] ?? 0;
35659
- if (leftValue < middleValue) {
35660
- if (middleValue < rightValue) return middle;
35661
- if (leftValue < rightValue) return right;
35662
- return left;
35663
- }
35664
- if (leftValue < rightValue) return left;
35665
- if (middleValue < rightValue) return right;
35666
- return middle;
35667
- }
35668
- function partitionAroundPivot(values, left, right, pivotIndex) {
35669
- const pivotValue = values[pivotIndex] ?? 0;
35670
- swap(values, pivotIndex, right);
35671
- let storeIndex = left;
35672
- for (let i = left; i < right; i++) {
35673
- const current = values[i];
35674
- if (current === void 0 || current > pivotValue) continue;
35675
- swap(values, storeIndex, i);
35676
- storeIndex++;
35677
- }
35678
- swap(values, storeIndex, right);
35679
- return storeIndex;
35680
- }
35681
- function swap(values, left, right) {
35682
- if (left === right) return;
35683
- const leftValue = values[left] ?? 0;
35684
- const rightValue = values[right] ?? 0;
35685
- values[left] = rightValue;
35686
- values[right] = leftValue;
35687
- }
35688
35803
  function resolveVerticalAlignConsensus(aggregate) {
35689
35804
  if (aggregate.comparableCount === 0) return null;
35690
35805
  if (aggregate.countsByValue.size !== 1) return null;
@@ -35783,7 +35898,7 @@ function resolveMeasurementCandidates(root, childrenByParentNode, snapshotByElem
35783
35898
  if (firstControlOrReplacedDescendant === null && (child.isControl || child.isReplaced)) {
35784
35899
  firstControlOrReplacedDescendant = child;
35785
35900
  }
35786
- if (firstTextualDescendant === null && child.textualContent === "yes") {
35901
+ if (firstTextualDescendant === null && child.textualContent === 0 /* Yes */) {
35787
35902
  firstTextualDescendant = child;
35788
35903
  }
35789
35904
  if (firstControlOrReplacedDescendant !== null && firstTextualDescendant !== null) break;
@@ -35809,7 +35924,7 @@ function resolveMeasurementNode(root, candidates) {
35809
35924
  if (candidates.firstControlOrReplacedDescendant !== null) return candidates.firstControlOrReplacedDescendant;
35810
35925
  if (root.isControl || root.isReplaced) return root;
35811
35926
  if (candidates.firstTextualDescendant !== null) return candidates.firstTextualDescendant;
35812
- if (root.textualContent === "yes") return root;
35927
+ if (root.textualContent === 0 /* Yes */) return root;
35813
35928
  return root;
35814
35929
  }
35815
35930
 
@@ -35856,7 +35971,7 @@ function buildScopedSelectorIndexBySolidFile(cssScopeBySolidFile, css, selectorM
35856
35971
  seenSelectorIds.add(selector.id);
35857
35972
  const metadata = selectorMetadataById.get(selector.id);
35858
35973
  if (!metadata) continue;
35859
- if (metadata.guard.kind === "conditional") {
35974
+ if (metadata.guard.kind === 1 /* Conditional */) {
35860
35975
  if (!conditionalSelectorIds.has(selector.id)) {
35861
35976
  conditionalSelectorIds.add(selector.id);
35862
35977
  perf.selectorsGuardedConditional++;
@@ -35898,7 +36013,7 @@ function buildScopedSelectorIndexBySolidFile(cssScopeBySolidFile, css, selectorM
35898
36013
  }
35899
36014
  return out;
35900
36015
  }
35901
- function buildSelectorCandidatesByElementKey(elements, scopedSelectorsBySolidFile, perf) {
36016
+ function buildSelectorCandidatesByNode(elements, scopedSelectorsBySolidFile, perf) {
35902
36017
  const out = /* @__PURE__ */ new Map();
35903
36018
  for (let i = 0; i < elements.length; i++) {
35904
36019
  const node = elements[i];
@@ -35908,7 +36023,7 @@ function buildSelectorCandidatesByElementKey(elements, scopedSelectorsBySolidFil
35908
36023
  const byTag = node.tagName !== null ? collectSelectorCandidates(scoped.bySubjectTag.get(node.tagName), node.selectorDispatchKeys) : EMPTY_SELECTOR_LIST;
35909
36024
  const withoutTag = collectSelectorCandidates(scoped.withoutSubjectTag, node.selectorDispatchKeys);
35910
36025
  const merged = mergeSelectorCandidateIds(byTag, withoutTag);
35911
- out.set(node.key, merged);
36026
+ out.set(node, merged);
35912
36027
  perf.elementsScanned++;
35913
36028
  perf.selectorCandidatesChecked += merged.length;
35914
36029
  }
@@ -36367,12 +36482,6 @@ var EMPTY_EXPANSION_RESULT = [];
36367
36482
  function collectMonitoredDeclarations(selector, layerOrder, guard) {
36368
36483
  const out = [];
36369
36484
  const declarations = selector.rule.declarations;
36370
- const signalGuard = guard.kind === "conditional" ? "conditional" : "unconditional";
36371
- const guardProvenance = {
36372
- kind: signalGuard,
36373
- conditions: guard.conditions,
36374
- key: guard.key
36375
- };
36376
36485
  for (let i = 0; i < declarations.length; i++) {
36377
36486
  const declaration = declarations[i];
36378
36487
  if (!declaration) continue;
@@ -36383,8 +36492,7 @@ function collectMonitoredDeclarations(selector, layerOrder, guard) {
36383
36492
  out.push({
36384
36493
  property: monitored,
36385
36494
  value: declaration.value,
36386
- guard: signalGuard,
36387
- guardProvenance,
36495
+ guardProvenance: guard,
36388
36496
  position: {
36389
36497
  layer: declaration.cascadePosition.layer,
36390
36498
  layerOrder,
@@ -36406,6 +36514,7 @@ function toMonitoredSignalKey(property) {
36406
36514
  case "margin-block":
36407
36515
  case "padding-block":
36408
36516
  case "inset-block":
36517
+ case "flex-flow":
36409
36518
  return property;
36410
36519
  default:
36411
36520
  return null;
@@ -36429,30 +36538,27 @@ function expandMonitoredDeclarationForDelta(declaration) {
36429
36538
  if (signalName === void 0) return EMPTY_EXPANSION_RESULT;
36430
36539
  return [{ name: signalName, value: value2 }];
36431
36540
  }
36432
- function appendMatchingEdgesFromSelectorIds(selectorIds, node, selectorMetadataById, selectorsById, applies, appliesByElementNodeMutable, perf, rootElementsByFile, logger) {
36433
- const fileRoots = rootElementsByFile.get(node.solidFile) ?? null;
36541
+ function appendMatchingEdgesFromSelectorIds(ctx, selectorIds, node, applies, appliesByElementNodeMutable) {
36542
+ const fileRoots = ctx.rootElementsByFile.get(node.solidFile) ?? null;
36434
36543
  for (let i = 0; i < selectorIds.length; i++) {
36435
36544
  const selectorId = selectorIds[i];
36436
36545
  if (selectorId === void 0) continue;
36437
- const metadata = selectorMetadataById.get(selectorId);
36546
+ const metadata = ctx.selectorMetadataById.get(selectorId);
36438
36547
  if (!metadata || !metadata.matcher) {
36439
36548
  throw new Error(`missing compiled selector matcher for selector ${selectorId}`);
36440
36549
  }
36441
- const selector = selectorsById.get(selectorId);
36550
+ const selector = ctx.selectorsById.get(selectorId);
36442
36551
  if (!selector) {
36443
36552
  throw new Error(`missing selector ${selectorId}`);
36444
36553
  }
36445
- if (!selectorMatchesLayoutElement(metadata.matcher, node, perf, fileRoots, logger)) continue;
36554
+ if (!selectorMatchesLayoutElement(metadata.matcher, node, ctx.perf, fileRoots, ctx.logger)) continue;
36446
36555
  const edge = {
36447
- solidFile: node.solidFile,
36448
- elementId: node.elementId,
36449
- elementKey: node.key,
36450
36556
  selectorId: selector.id,
36451
36557
  specificityScore: selector.specificityScore,
36452
36558
  sourceOrder: selector.rule.sourceOrder
36453
36559
  };
36454
36560
  applies.push(edge);
36455
- perf.matchEdgesCreated++;
36561
+ ctx.perf.matchEdgesCreated++;
36456
36562
  const existing = appliesByElementNodeMutable.get(node);
36457
36563
  if (existing) {
36458
36564
  existing.push(edge);
@@ -36478,7 +36584,7 @@ function augmentCascadeWithTailwind(cascade, node, tailwind) {
36478
36584
  const classTokens = node.classTokens;
36479
36585
  if (classTokens.length === 0) return;
36480
36586
  const guardProvenance = {
36481
- kind: "unconditional",
36587
+ kind: 0 /* Unconditional */,
36482
36588
  conditions: [],
36483
36589
  key: "always"
36484
36590
  };
@@ -36495,8 +36601,7 @@ function augmentCascadeWithTailwind(cascade, node, tailwind) {
36495
36601
  if (cascade.has(property)) continue;
36496
36602
  cascade.set(property, {
36497
36603
  value: value2,
36498
- source: "selector",
36499
- guard: "unconditional",
36604
+ source: 0 /* Selector */,
36500
36605
  guardProvenance
36501
36606
  });
36502
36607
  }
@@ -36516,8 +36621,7 @@ function buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorI
36516
36621
  const property = declaration.property;
36517
36622
  const newDeclaration = {
36518
36623
  value: declaration.value,
36519
- source: "selector",
36520
- guard: declaration.guard,
36624
+ source: 0 /* Selector */,
36521
36625
  guardProvenance: declaration.guardProvenance
36522
36626
  };
36523
36627
  const existingPosition = positions.get(property);
@@ -36536,33 +36640,26 @@ function buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorI
36536
36640
  positions.set(property, declaration.position);
36537
36641
  }
36538
36642
  }
36539
- const inlinePosition = createInlineCascadePosition();
36540
- const inlineGuardProvenance = {
36541
- kind: "unconditional",
36542
- conditions: [],
36543
- key: "always"
36544
- };
36545
36643
  for (const [property, value2] of node.inlineStyleValues) {
36546
36644
  const newDeclaration = {
36547
36645
  value: value2,
36548
- source: "inline-style",
36549
- guard: "unconditional",
36550
- guardProvenance: inlineGuardProvenance
36646
+ source: 1 /* InlineStyle */,
36647
+ guardProvenance: INLINE_GUARD_PROVENANCE
36551
36648
  };
36552
36649
  const existingPosition = positions.get(property);
36553
36650
  if (existingPosition === void 0) {
36554
36651
  out.set(property, newDeclaration);
36555
- positions.set(property, inlinePosition);
36652
+ positions.set(property, INLINE_CASCADE_POSITION);
36556
36653
  continue;
36557
36654
  }
36558
36655
  const existingDeclaration = out.get(property);
36559
36656
  if (existingDeclaration === void 0) continue;
36560
36657
  if (!doesCandidateOverride(
36561
36658
  { declaration: existingDeclaration, position: existingPosition },
36562
- { declaration: newDeclaration, position: inlinePosition }
36659
+ { declaration: newDeclaration, position: INLINE_CASCADE_POSITION }
36563
36660
  )) continue;
36564
36661
  out.set(property, newDeclaration);
36565
- positions.set(property, inlinePosition);
36662
+ positions.set(property, INLINE_CASCADE_POSITION);
36566
36663
  }
36567
36664
  if (tailwind !== null) {
36568
36665
  augmentCascadeWithTailwind(out, node, tailwind);
@@ -36579,7 +36676,7 @@ function doesCandidateOverride(existing, incoming) {
36579
36676
  const existingSource = existing.declaration.source;
36580
36677
  const incomingSource = incoming.declaration.source;
36581
36678
  if (existingSource !== incomingSource) {
36582
- if (incomingSource === "inline-style") {
36679
+ if (incomingSource === 1 /* InlineStyle */) {
36583
36680
  if (existing.position.isImportant && !incoming.position.isImportant) return false;
36584
36681
  return true;
36585
36682
  }
@@ -36587,16 +36684,19 @@ function doesCandidateOverride(existing, incoming) {
36587
36684
  }
36588
36685
  return compareCascadePositions(incoming.position, existing.position) > 0;
36589
36686
  }
36590
- function createInlineCascadePosition() {
36591
- return {
36592
- layer: null,
36593
- layerOrder: Number.MAX_SAFE_INTEGER,
36594
- sourceOrder: Number.MAX_SAFE_INTEGER,
36595
- specificity: [1, 0, 0, 0],
36596
- specificityScore: Number.MAX_SAFE_INTEGER,
36597
- isImportant: false
36598
- };
36599
- }
36687
+ var INLINE_CASCADE_POSITION = Object.freeze({
36688
+ layer: null,
36689
+ layerOrder: Number.MAX_SAFE_INTEGER,
36690
+ sourceOrder: Number.MAX_SAFE_INTEGER,
36691
+ specificity: [1, 0, 0, 0],
36692
+ specificityScore: Number.MAX_SAFE_INTEGER,
36693
+ isImportant: false
36694
+ });
36695
+ var INLINE_GUARD_PROVENANCE = Object.freeze({
36696
+ kind: 0 /* Unconditional */,
36697
+ conditions: [],
36698
+ key: "always"
36699
+ });
36600
36700
  function resolveRuleLayerOrder(rule, css) {
36601
36701
  const layer = rule.containingLayer;
36602
36702
  if (!layer) return 0;
@@ -36604,14 +36704,14 @@ function resolveRuleLayerOrder(rule, css) {
36604
36704
  if (!name) return 0;
36605
36705
  return css.layerOrder.get(name) ?? 0;
36606
36706
  }
36607
- function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDeclarationsBySelectorId) {
36608
- const conditionalSignalDeltaFactsByElementKey = /* @__PURE__ */ new Map();
36707
+ function buildConditionalDeltaIndex(elements, appliesByNode, monitoredDeclarationsBySelectorId) {
36708
+ const conditionalSignalDeltaFactsByNode = /* @__PURE__ */ new Map();
36609
36709
  const elementsWithConditionalDeltaBySignal = /* @__PURE__ */ new Map();
36610
- const baselineOffsetFactsByElementKey = /* @__PURE__ */ new Map();
36710
+ const baselineOffsetFactsByNode = /* @__PURE__ */ new Map();
36611
36711
  for (let i = 0; i < elements.length; i++) {
36612
36712
  const node = elements[i];
36613
36713
  if (!node) continue;
36614
- const edges = appliesByElementKey.get(node.key);
36714
+ const edges = appliesByNode.get(node);
36615
36715
  let factByProperty = null;
36616
36716
  if (edges !== void 0 && edges.length > 0) {
36617
36717
  const byProperty = /* @__PURE__ */ new Map();
@@ -36636,7 +36736,7 @@ function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDecl
36636
36736
  };
36637
36737
  byProperty.set(property, bucket);
36638
36738
  }
36639
- if (declaration.guard === "conditional") {
36739
+ if (declaration.guardProvenance.kind === 1 /* Conditional */) {
36640
36740
  bucket.conditional.add(expandedEntry.value);
36641
36741
  continue;
36642
36742
  }
@@ -36676,7 +36776,7 @@ function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDecl
36676
36776
  }
36677
36777
  if (facts.size > 0) {
36678
36778
  factByProperty = facts;
36679
- conditionalSignalDeltaFactsByElementKey.set(node.key, facts);
36779
+ conditionalSignalDeltaFactsByNode.set(node, facts);
36680
36780
  for (const [signal, fact] of facts) {
36681
36781
  if (!fact.hasConditional) continue;
36682
36782
  const existing = elementsWithConditionalDeltaBySignal.get(signal);
@@ -36713,13 +36813,13 @@ function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDecl
36713
36813
  baselineBySignal.set(signal, [...values]);
36714
36814
  }
36715
36815
  if (baselineBySignal.size > 0) {
36716
- baselineOffsetFactsByElementKey.set(node.key, baselineBySignal);
36816
+ baselineOffsetFactsByNode.set(node, baselineBySignal);
36717
36817
  }
36718
36818
  }
36719
36819
  return {
36720
- conditionalSignalDeltaFactsByElementKey,
36820
+ conditionalSignalDeltaFactsByNode,
36721
36821
  elementsWithConditionalDeltaBySignal,
36722
- baselineOffsetFactsByElementKey
36822
+ baselineOffsetFactsByNode
36723
36823
  };
36724
36824
  }
36725
36825
  var EMPTY_NODE_LIST2 = [];
@@ -36846,8 +36946,8 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
36846
36946
  if (child.kind === "expression") {
36847
36947
  if (isStructuralExpression(child.node)) {
36848
36948
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 unknown (structural expression child)`);
36849
- memo.set(element.id, "unknown");
36850
- return "unknown";
36949
+ memo.set(element.id, 2 /* Unknown */);
36950
+ return 2 /* Unknown */;
36851
36951
  }
36852
36952
  hasTextOnlyExpression = true;
36853
36953
  continue;
@@ -36855,8 +36955,8 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
36855
36955
  if (child.kind !== "text") continue;
36856
36956
  if (child.node.type !== "JSXText") continue;
36857
36957
  if (isBlank(child.node.value)) continue;
36858
- memo.set(element.id, "yes");
36859
- return "yes";
36958
+ memo.set(element.id, 0 /* Yes */);
36959
+ return 0 /* Yes */;
36860
36960
  }
36861
36961
  let childHasUnknown = false;
36862
36962
  let childHasDynamicText = false;
@@ -36870,31 +36970,31 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
36870
36970
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id}: non-DOM child ${child.tag}#${child.id} resolves to control tag=${childMeta.tagName}, skipping`);
36871
36971
  continue;
36872
36972
  }
36873
- if (childState !== "no") {
36973
+ if (childState !== 1 /* No */) {
36874
36974
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id}: non-DOM child ${child.tag ?? child.id}#${child.id} has state=${childState} \u2192 childHasUnknown`);
36875
36975
  childHasUnknown = true;
36876
36976
  }
36877
36977
  continue;
36878
36978
  }
36879
- if (childState === "yes") {
36880
- memo.set(element.id, "yes");
36881
- return "yes";
36979
+ if (childState === 0 /* Yes */) {
36980
+ memo.set(element.id, 0 /* Yes */);
36981
+ return 0 /* Yes */;
36882
36982
  }
36883
- if (childState === "unknown") childHasUnknown = true;
36884
- if (childState === "dynamic-text") childHasDynamicText = true;
36983
+ if (childState === 2 /* Unknown */) childHasUnknown = true;
36984
+ if (childState === 3 /* DynamicText */) childHasDynamicText = true;
36885
36985
  }
36886
36986
  if (childHasUnknown) {
36887
36987
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 unknown (child has unknown)`);
36888
- memo.set(element.id, "unknown");
36889
- return "unknown";
36988
+ memo.set(element.id, 2 /* Unknown */);
36989
+ return 2 /* Unknown */;
36890
36990
  }
36891
36991
  if (hasTextOnlyExpression || childHasDynamicText) {
36892
36992
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 dynamic-text`);
36893
- memo.set(element.id, "dynamic-text");
36894
- return "dynamic-text";
36993
+ memo.set(element.id, 3 /* DynamicText */);
36994
+ return 3 /* DynamicText */;
36895
36995
  }
36896
- memo.set(element.id, "no");
36897
- return "no";
36996
+ memo.set(element.id, 1 /* No */);
36997
+ return 1 /* No */;
36898
36998
  }
36899
36999
  function isStructuralExpression(node) {
36900
37000
  if (node.type !== "JSXExpressionContainer") return false;
@@ -37193,8 +37293,6 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37193
37293
  classTokens: record.classTokens,
37194
37294
  classTokenSet: record.classTokenSet,
37195
37295
  inlineStyleKeys: record.inlineStyleKeys,
37196
- parentElementId,
37197
- parentElementKey: parentNode ? parentNode.key : parentElementId === null ? null : toLayoutElementKey(solid.file, parentElementId),
37198
37296
  parentElementNode: parentNode,
37199
37297
  previousSiblingNode,
37200
37298
  siblingIndex,
@@ -37235,22 +37333,25 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37235
37333
  logger.debug(`[build] rootElementsByFile file=${file} count=${roots.length}: ${descs.join(", ")}`);
37236
37334
  }
37237
37335
  }
37238
- const selectorCandidatesByElementKey = buildSelectorCandidatesByElementKey(elements, scopedSelectorsBySolidFile, perf);
37336
+ const selectorCandidatesByNode = buildSelectorCandidatesByNode(elements, scopedSelectorsBySolidFile, perf);
37337
+ const selectorMatchCtx = {
37338
+ selectorMetadataById,
37339
+ selectorsById,
37340
+ rootElementsByFile,
37341
+ perf,
37342
+ logger
37343
+ };
37239
37344
  for (let i = 0; i < elements.length; i++) {
37240
37345
  const node = elements[i];
37241
37346
  if (!node) continue;
37242
- const selectorIds = selectorCandidatesByElementKey.get(node.key) ?? EMPTY_NUMBER_LIST2;
37347
+ const selectorIds = selectorCandidatesByNode.get(node) ?? EMPTY_NUMBER_LIST2;
37243
37348
  if (selectorIds.length === 0) continue;
37244
37349
  appendMatchingEdgesFromSelectorIds(
37350
+ selectorMatchCtx,
37245
37351
  selectorIds,
37246
37352
  node,
37247
- selectorMetadataById,
37248
- selectorsById,
37249
37353
  applies,
37250
- appliesByElementNodeMutable,
37251
- perf,
37252
- rootElementsByFile,
37253
- logger
37354
+ appliesByElementNodeMutable
37254
37355
  );
37255
37356
  }
37256
37357
  perf.selectorMatchMs = performance.now() - selectorMatchStartedAt;
@@ -37258,7 +37359,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37258
37359
  for (const edges of appliesByElementNodeMutable.values()) {
37259
37360
  edges.sort(compareLayoutEdge);
37260
37361
  }
37261
- const appliesByElementKey = /* @__PURE__ */ new Map();
37362
+ const appliesByNode = /* @__PURE__ */ new Map();
37262
37363
  const tailwind = css.tailwind;
37263
37364
  for (let i = 0; i < elements.length; i++) {
37264
37365
  const node = elements[i];
@@ -37266,7 +37367,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37266
37367
  const edges = appliesByElementNodeMutable.get(node) ?? [];
37267
37368
  const cascade = buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorId, tailwind);
37268
37369
  cascadeByElementNode.set(node, cascade);
37269
- appliesByElementKey.set(node.key, edges);
37370
+ appliesByNode.set(node, edges);
37270
37371
  }
37271
37372
  perf.cascadeBuildMs = performance.now() - cascadeStartedAt;
37272
37373
  const snapshotByElementNode = buildSignalSnapshotIndex(elements, cascadeByElementNode, perf);
@@ -37274,7 +37375,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37274
37375
  const factIndex = buildElementFactIndex(elements, snapshotByElementNode);
37275
37376
  const conditionalDeltaIndex = buildConditionalDeltaIndex(
37276
37377
  elements,
37277
- appliesByElementKey,
37378
+ appliesByNode,
37278
37379
  monitoredDeclarationsBySelectorId
37279
37380
  );
37280
37381
  const elementsWithConditionalOverflowDelta = buildConditionalDeltaSignalGroupElements(
@@ -37292,7 +37393,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37292
37393
  contextByParentNode,
37293
37394
  measurementNodeByRootKey,
37294
37395
  snapshotByElementNode,
37295
- snapshotHotSignalsByElementKey: factIndex.snapshotHotSignalsByElementKey
37396
+ snapshotHotSignalsByNode: factIndex.snapshotHotSignalsByNode
37296
37397
  });
37297
37398
  finalizeTableCellBaselineRelevance(contextByParentNode, cohortIndex.verticalAlignConsensusByParent);
37298
37399
  perf.conditionalSignals = cohortIndex.conditionalSignals;
@@ -37308,11 +37409,11 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37308
37409
  childrenByParentNode: childrenByParentNodeMutable,
37309
37410
  elementBySolidFileAndId: elementBySolidFileAndIdMutable,
37310
37411
  elementRefsBySolidFileAndId: elementRefsBySolidFileAndIdMutable,
37311
- appliesByElementKey,
37312
- selectorCandidatesByElementKey,
37412
+ appliesByNode,
37413
+ selectorCandidatesByNode,
37313
37414
  selectorsById,
37314
37415
  measurementNodeByRootKey,
37315
- snapshotHotSignalsByElementKey: factIndex.snapshotHotSignalsByElementKey,
37416
+ snapshotHotSignalsByNode: factIndex.snapshotHotSignalsByNode,
37316
37417
  elementsByTagName: factIndex.elementsByTagName,
37317
37418
  elementsWithConditionalDeltaBySignal: conditionalDeltaIndex.elementsWithConditionalDeltaBySignal,
37318
37419
  elementsWithConditionalOverflowDelta,
@@ -37320,12 +37421,12 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37320
37421
  elementsByKnownSignalValue: factIndex.elementsByKnownSignalValue,
37321
37422
  dynamicSlotCandidateElements: factIndex.dynamicSlotCandidateElements,
37322
37423
  scrollContainerElements: factIndex.scrollContainerElements,
37323
- reservedSpaceFactsByElementKey: factIndex.reservedSpaceFactsByElementKey,
37324
- scrollContainerFactsByElementKey: factIndex.scrollContainerFactsByElementKey,
37325
- flowParticipationFactsByElementKey: factIndex.flowParticipationFactsByElementKey,
37326
- containingBlockFactsByElementKey: factIndex.containingBlockFactsByElementKey,
37327
- conditionalSignalDeltaFactsByElementKey: conditionalDeltaIndex.conditionalSignalDeltaFactsByElementKey,
37328
- baselineOffsetFactsByElementKey: conditionalDeltaIndex.baselineOffsetFactsByElementKey,
37424
+ reservedSpaceFactsByNode: factIndex.reservedSpaceFactsByNode,
37425
+ scrollContainerFactsByNode: factIndex.scrollContainerFactsByNode,
37426
+ flowParticipationFactsByNode: factIndex.flowParticipationFactsByNode,
37427
+ containingBlockFactsByNode: factIndex.containingBlockFactsByNode,
37428
+ conditionalSignalDeltaFactsByNode: conditionalDeltaIndex.conditionalSignalDeltaFactsByNode,
37429
+ baselineOffsetFactsByNode: conditionalDeltaIndex.baselineOffsetFactsByNode,
37329
37430
  statefulSelectorEntriesByRuleId: statefulRuleIndexes.selectorEntriesByRuleId,
37330
37431
  statefulNormalizedDeclarationsByRuleId: statefulRuleIndexes.normalizedDeclarationsByRuleId,
37331
37432
  statefulBaseValueIndex: statefulRuleIndexes.baseValueIndex,
@@ -37337,11 +37438,11 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37337
37438
  };
37338
37439
  }
37339
37440
  function buildElementFactIndex(elements, snapshotByElementNode) {
37340
- const reservedSpaceFactsByElementKey = /* @__PURE__ */ new Map();
37341
- const scrollContainerFactsByElementKey = /* @__PURE__ */ new Map();
37342
- const flowParticipationFactsByElementKey = /* @__PURE__ */ new Map();
37343
- const containingBlockFactsByElementKey = /* @__PURE__ */ new Map();
37344
- const snapshotHotSignalsByElementKey = /* @__PURE__ */ new Map();
37441
+ const reservedSpaceFactsByNode = /* @__PURE__ */ new Map();
37442
+ const scrollContainerFactsByNode = /* @__PURE__ */ new Map();
37443
+ const flowParticipationFactsByNode = /* @__PURE__ */ new Map();
37444
+ const containingBlockFactsByNode = /* @__PURE__ */ new Map();
37445
+ const snapshotHotSignalsByNode = /* @__PURE__ */ new Map();
37345
37446
  const elementsByTagName = /* @__PURE__ */ new Map();
37346
37447
  const elementsByKnownSignalValue = /* @__PURE__ */ new Map();
37347
37448
  const dynamicSlotCandidateElements = [];
@@ -37351,7 +37452,7 @@ function buildElementFactIndex(elements, snapshotByElementNode) {
37351
37452
  const node = elements[i];
37352
37453
  if (!node) continue;
37353
37454
  const snapshot = snapshotByElementNode.get(node);
37354
- if (node.textualContent === "unknown" && node.siblingCount >= 2) {
37455
+ if (node.textualContent === 2 /* Unknown */ && node.siblingCount >= 2) {
37355
37456
  dynamicSlotCandidateElements.push(node);
37356
37457
  }
37357
37458
  if (node.tagName) {
@@ -37372,18 +37473,18 @@ function buildElementFactIndex(elements, snapshotByElementNode) {
37372
37473
  nearestPositionedAncestorHasReservedSpace = parentPositioned.hasReservedSpace;
37373
37474
  }
37374
37475
  }
37375
- containingBlockFactsByElementKey.set(node.key, {
37476
+ containingBlockFactsByNode.set(node, {
37376
37477
  nearestPositionedAncestorKey,
37377
37478
  nearestPositionedAncestorHasReservedSpace
37378
37479
  });
37379
37480
  if (!snapshot) continue;
37380
37481
  const reservedSpaceFact = computeReservedSpaceFact(snapshot);
37381
- reservedSpaceFactsByElementKey.set(node.key, reservedSpaceFact);
37482
+ reservedSpaceFactsByNode.set(node, reservedSpaceFact);
37382
37483
  const scrollFact = computeScrollContainerFact(snapshot);
37383
- scrollContainerFactsByElementKey.set(node.key, scrollFact);
37484
+ scrollContainerFactsByNode.set(node, scrollFact);
37384
37485
  if (scrollFact.isScrollContainer) scrollContainerElements.push(node);
37385
- flowParticipationFactsByElementKey.set(node.key, computeFlowParticipationFact(snapshot));
37386
- snapshotHotSignalsByElementKey.set(node.key, computeHotSignals(snapshot));
37486
+ flowParticipationFactsByNode.set(node, computeFlowParticipationFact(snapshot));
37487
+ snapshotHotSignalsByNode.set(node, computeHotSignals(snapshot));
37387
37488
  const positionSignal = snapshot.signals.get("position");
37388
37489
  const isPositioned = positionSignal !== void 0 && positionSignal.kind === "known" && positionSignal.normalized !== "static";
37389
37490
  if (isPositioned) {
@@ -37414,49 +37515,163 @@ function buildElementFactIndex(elements, snapshotByElementNode) {
37414
37515
  }
37415
37516
  }
37416
37517
  return {
37417
- reservedSpaceFactsByElementKey,
37418
- scrollContainerFactsByElementKey,
37518
+ reservedSpaceFactsByNode,
37519
+ scrollContainerFactsByNode,
37419
37520
  scrollContainerElements,
37420
- flowParticipationFactsByElementKey,
37421
- containingBlockFactsByElementKey,
37422
- snapshotHotSignalsByElementKey,
37521
+ flowParticipationFactsByNode,
37522
+ containingBlockFactsByNode,
37523
+ snapshotHotSignalsByNode,
37423
37524
  elementsByTagName,
37424
37525
  elementsByKnownSignalValue,
37425
37526
  dynamicSlotCandidateElements
37426
37527
  };
37427
37528
  }
37428
- function computeHotSignals(snapshot) {
37529
+ var ABSENT_NUMERIC = Object.freeze({
37530
+ present: false,
37531
+ value: null,
37532
+ kind: 3 /* Unknown */
37533
+ });
37534
+ var ABSENT_NORMALIZED = Object.freeze({
37535
+ present: false,
37536
+ value: null,
37537
+ kind: 3 /* Unknown */
37538
+ });
37539
+ function toHotNumeric(signal) {
37540
+ if (signal.kind !== "known") {
37541
+ return {
37542
+ present: true,
37543
+ value: null,
37544
+ kind: signal.guard.kind === 1 /* Conditional */ ? 2 /* Conditional */ : 3 /* Unknown */
37545
+ };
37546
+ }
37429
37547
  return {
37430
- lineHeight: computeHotNumeric(snapshot, "line-height"),
37431
- verticalAlign: computeHotNormalized(snapshot, "vertical-align"),
37432
- alignSelf: computeHotNormalized(snapshot, "align-self"),
37433
- placeSelf: computeHotNormalized(snapshot, "place-self"),
37434
- writingMode: computeHotNormalized(snapshot, "writing-mode"),
37435
- direction: computeHotNormalized(snapshot, "direction"),
37436
- display: computeHotNormalized(snapshot, "display"),
37437
- alignItems: computeHotNormalized(snapshot, "align-items"),
37438
- placeItems: computeHotNormalized(snapshot, "place-items"),
37439
- position: computeHotNormalized(snapshot, "position"),
37440
- insetBlockStart: computeHotNumeric(snapshot, "inset-block-start"),
37441
- insetBlockEnd: computeHotNumeric(snapshot, "inset-block-end"),
37442
- transform: computeHotNumeric(snapshot, "transform"),
37443
- translate: computeHotNumeric(snapshot, "translate"),
37444
- top: computeHotNumeric(snapshot, "top"),
37445
- bottom: computeHotNumeric(snapshot, "bottom"),
37446
- marginTop: computeHotNumeric(snapshot, "margin-top"),
37447
- marginBottom: computeHotNumeric(snapshot, "margin-bottom")
37548
+ present: true,
37549
+ value: signal.px,
37550
+ kind: signal.guard.kind === 1 /* Conditional */ ? 2 /* Conditional */ : signal.quality === "estimated" ? 1 /* Interval */ : 0 /* Exact */
37448
37551
  };
37449
37552
  }
37450
- function computeHotNumeric(snapshot, name) {
37553
+ function toHotNormalized(signal) {
37554
+ if (signal.kind !== "known") {
37555
+ return {
37556
+ present: true,
37557
+ value: null,
37558
+ kind: signal.guard.kind === 1 /* Conditional */ ? 2 /* Conditional */ : 3 /* Unknown */
37559
+ };
37560
+ }
37451
37561
  return {
37452
- present: snapshot.signals.has(name),
37453
- ...readNumericSignalEvidence(snapshot, name)
37562
+ present: true,
37563
+ value: signal.normalized,
37564
+ kind: signal.guard.kind === 1 /* Conditional */ ? 2 /* Conditional */ : signal.quality === "estimated" ? 1 /* Interval */ : 0 /* Exact */
37454
37565
  };
37455
37566
  }
37456
- function computeHotNormalized(snapshot, name) {
37567
+ function computeHotSignals(snapshot) {
37568
+ let lineHeight = ABSENT_NUMERIC;
37569
+ let verticalAlign = ABSENT_NORMALIZED;
37570
+ let alignSelf = ABSENT_NORMALIZED;
37571
+ let placeSelf = ABSENT_NORMALIZED;
37572
+ let flexDirection = ABSENT_NORMALIZED;
37573
+ let gridAutoFlow = ABSENT_NORMALIZED;
37574
+ let writingMode = ABSENT_NORMALIZED;
37575
+ let direction = ABSENT_NORMALIZED;
37576
+ let display = ABSENT_NORMALIZED;
37577
+ let alignItems = ABSENT_NORMALIZED;
37578
+ let placeItems = ABSENT_NORMALIZED;
37579
+ let position = ABSENT_NORMALIZED;
37580
+ let insetBlockStart = ABSENT_NUMERIC;
37581
+ let insetBlockEnd = ABSENT_NUMERIC;
37582
+ let transform = ABSENT_NUMERIC;
37583
+ let translate = ABSENT_NUMERIC;
37584
+ let top = ABSENT_NUMERIC;
37585
+ let bottom = ABSENT_NUMERIC;
37586
+ let marginTop = ABSENT_NUMERIC;
37587
+ let marginBottom = ABSENT_NUMERIC;
37588
+ for (const [name, value2] of snapshot.signals) {
37589
+ switch (name) {
37590
+ case "line-height":
37591
+ lineHeight = toHotNumeric(value2);
37592
+ break;
37593
+ case "vertical-align":
37594
+ verticalAlign = toHotNormalized(value2);
37595
+ break;
37596
+ case "align-self":
37597
+ alignSelf = toHotNormalized(value2);
37598
+ break;
37599
+ case "place-self":
37600
+ placeSelf = toHotNormalized(value2);
37601
+ break;
37602
+ case "flex-direction":
37603
+ flexDirection = toHotNormalized(value2);
37604
+ break;
37605
+ case "grid-auto-flow":
37606
+ gridAutoFlow = toHotNormalized(value2);
37607
+ break;
37608
+ case "writing-mode":
37609
+ writingMode = toHotNormalized(value2);
37610
+ break;
37611
+ case "direction":
37612
+ direction = toHotNormalized(value2);
37613
+ break;
37614
+ case "display":
37615
+ display = toHotNormalized(value2);
37616
+ break;
37617
+ case "align-items":
37618
+ alignItems = toHotNormalized(value2);
37619
+ break;
37620
+ case "place-items":
37621
+ placeItems = toHotNormalized(value2);
37622
+ break;
37623
+ case "position":
37624
+ position = toHotNormalized(value2);
37625
+ break;
37626
+ case "inset-block-start":
37627
+ insetBlockStart = toHotNumeric(value2);
37628
+ break;
37629
+ case "inset-block-end":
37630
+ insetBlockEnd = toHotNumeric(value2);
37631
+ break;
37632
+ case "transform":
37633
+ transform = toHotNumeric(value2);
37634
+ break;
37635
+ case "translate":
37636
+ translate = toHotNumeric(value2);
37637
+ break;
37638
+ case "top":
37639
+ top = toHotNumeric(value2);
37640
+ break;
37641
+ case "bottom":
37642
+ bottom = toHotNumeric(value2);
37643
+ break;
37644
+ case "margin-top":
37645
+ marginTop = toHotNumeric(value2);
37646
+ break;
37647
+ case "margin-bottom":
37648
+ marginBottom = toHotNumeric(value2);
37649
+ break;
37650
+ default:
37651
+ break;
37652
+ }
37653
+ }
37457
37654
  return {
37458
- present: snapshot.signals.has(name),
37459
- ...readNormalizedSignalEvidence(snapshot, name)
37655
+ lineHeight,
37656
+ verticalAlign,
37657
+ alignSelf,
37658
+ placeSelf,
37659
+ flexDirection,
37660
+ gridAutoFlow,
37661
+ writingMode,
37662
+ direction,
37663
+ display,
37664
+ alignItems,
37665
+ placeItems,
37666
+ position,
37667
+ insetBlockStart,
37668
+ insetBlockEnd,
37669
+ transform,
37670
+ translate,
37671
+ top,
37672
+ bottom,
37673
+ marginTop,
37674
+ marginBottom
37460
37675
  };
37461
37676
  }
37462
37677
  function computeReservedSpaceFact(snapshot) {
@@ -37491,15 +37706,14 @@ function computeReservedSpaceFact(snapshot) {
37491
37706
  function hasPositiveOrDeclaredDimension(snapshot, property) {
37492
37707
  const signal = snapshot.signals.get(property);
37493
37708
  if (!signal) return false;
37494
- if (signal.guard !== "unconditional") return false;
37709
+ if (signal.guard.kind !== 0 /* Unconditional */) return false;
37495
37710
  let normalized = "";
37496
37711
  if (signal.kind === "known") {
37497
37712
  if (signal.px !== null) return signal.px > 0;
37498
37713
  normalized = signal.normalized.trim().toLowerCase();
37499
37714
  }
37500
37715
  if (signal.kind === "unknown") {
37501
- if (signal.raw === null) return false;
37502
- normalized = signal.raw.trim().toLowerCase();
37716
+ return signal.source !== null;
37503
37717
  }
37504
37718
  if (normalized.length === 0) return false;
37505
37719
  if (isNonReservingDimension(normalized)) return false;
@@ -37508,15 +37722,14 @@ function hasPositiveOrDeclaredDimension(snapshot, property) {
37508
37722
  function hasUsableAspectRatio(snapshot) {
37509
37723
  const signal = snapshot.signals.get("aspect-ratio");
37510
37724
  if (!signal) return false;
37511
- if (signal.guard !== "unconditional") return false;
37725
+ if (signal.guard.kind !== 0 /* Unconditional */) return false;
37726
+ if (signal.kind === "unknown") {
37727
+ return false;
37728
+ }
37512
37729
  let normalized = "";
37513
37730
  if (signal.kind === "known") {
37514
37731
  normalized = signal.normalized.trim().toLowerCase();
37515
37732
  }
37516
- if (signal.kind === "unknown") {
37517
- if (signal.raw === null) return false;
37518
- normalized = signal.raw.trim().toLowerCase();
37519
- }
37520
37733
  if (normalized.length === 0) return false;
37521
37734
  return normalized !== "auto";
37522
37735
  }
@@ -37534,8 +37747,8 @@ function computeScrollContainerFact(snapshot) {
37534
37747
  const yFromLonghand = parseSingleAxisScroll(overflowY);
37535
37748
  const xScroll = shorthandAxis.x;
37536
37749
  const yScroll = yFromLonghand === null ? shorthandAxis.y : yFromLonghand;
37537
- const hasConditionalScroll = overflowSignal?.guard === "conditional" && (shorthandAxis.x || shorthandAxis.y) || overflowYSignal?.guard === "conditional" && yFromLonghand === true;
37538
- const hasUnconditionalScroll = overflowSignal?.guard === "unconditional" && (shorthandAxis.x || shorthandAxis.y) || overflowYSignal?.guard === "unconditional" && yFromLonghand === true;
37750
+ const hasConditionalScroll = overflowSignal?.guard.kind === 1 /* Conditional */ && (shorthandAxis.x || shorthandAxis.y) || overflowYSignal?.guard.kind === 1 /* Conditional */ && yFromLonghand === true;
37751
+ const hasUnconditionalScroll = overflowSignal?.guard.kind === 0 /* Unconditional */ && (shorthandAxis.x || shorthandAxis.y) || overflowYSignal?.guard.kind === 0 /* Unconditional */ && yFromLonghand === true;
37539
37752
  return {
37540
37753
  isScrollContainer: xScroll || yScroll,
37541
37754
  axis: toScrollAxis(xScroll, yScroll),
@@ -37545,18 +37758,18 @@ function computeScrollContainerFact(snapshot) {
37545
37758
  hasUnconditionalScroll
37546
37759
  };
37547
37760
  }
37761
+ var NO_SCROLL = Object.freeze({ x: false, y: false });
37762
+ var BOTH_SCROLL = Object.freeze({ x: true, y: true });
37548
37763
  function parseOverflowShorthandAxis(value2) {
37549
- if (value2 === null) return { x: false, y: false };
37550
- const tokens = splitWhitespaceTokens(value2);
37551
- if (tokens.length === 0) return { x: false, y: false };
37552
- const first = tokens[0];
37553
- if (!first) return { x: false, y: false };
37554
- if (tokens.length === 1) {
37555
- const scroll = SCROLLABLE_VALUES.has(first);
37556
- return { x: scroll, y: scroll };
37557
- }
37558
- const second = tokens[1];
37559
- if (!second) return { x: SCROLLABLE_VALUES.has(first), y: false };
37764
+ if (value2 === null) return NO_SCROLL;
37765
+ const trimmed = value2.trim();
37766
+ const spaceIdx = trimmed.indexOf(" ");
37767
+ if (spaceIdx === -1) {
37768
+ const scroll = SCROLLABLE_VALUES.has(trimmed);
37769
+ return scroll ? BOTH_SCROLL : NO_SCROLL;
37770
+ }
37771
+ const first = trimmed.slice(0, spaceIdx);
37772
+ const second = trimmed.slice(spaceIdx + 1).trimStart();
37560
37773
  return {
37561
37774
  x: SCROLLABLE_VALUES.has(first),
37562
37775
  y: SCROLLABLE_VALUES.has(second)
@@ -37564,16 +37777,16 @@ function parseOverflowShorthandAxis(value2) {
37564
37777
  }
37565
37778
  function parseSingleAxisScroll(value2) {
37566
37779
  if (value2 === null) return null;
37567
- const tokens = splitWhitespaceTokens(value2);
37568
- const first = tokens[0];
37569
- if (!first) return null;
37780
+ const trimmed = value2.trim();
37781
+ const spaceIdx = trimmed.indexOf(" ");
37782
+ const first = spaceIdx === -1 ? trimmed : trimmed.slice(0, spaceIdx);
37570
37783
  return SCROLLABLE_VALUES.has(first);
37571
37784
  }
37572
37785
  function toScrollAxis(x, y) {
37573
- if (x && y) return "both";
37574
- if (x) return "x";
37575
- if (y) return "y";
37576
- return "none";
37786
+ if (x && y) return 3 /* Both */;
37787
+ if (x) return 1 /* X */;
37788
+ if (y) return 2 /* Y */;
37789
+ return 0 /* None */;
37577
37790
  }
37578
37791
  function computeFlowParticipationFact(snapshot) {
37579
37792
  const signal = snapshot.signals.get("position");
@@ -37590,8 +37803,8 @@ function computeFlowParticipationFact(snapshot) {
37590
37803
  return {
37591
37804
  inFlow: !outOfFlow,
37592
37805
  position,
37593
- hasConditionalOutOfFlow: signal.guard === "conditional" && outOfFlow,
37594
- hasUnconditionalOutOfFlow: signal.guard === "unconditional" && outOfFlow
37806
+ hasConditionalOutOfFlow: signal.guard.kind === 1 /* Conditional */ && outOfFlow,
37807
+ hasUnconditionalOutOfFlow: signal.guard.kind === 0 /* Unconditional */ && outOfFlow
37595
37808
  };
37596
37809
  }
37597
37810
  function buildContextIndex(childrenByParentNode, snapshotByElementNode, perf) {
@@ -37666,7 +37879,7 @@ function collectAlignmentCases(context) {
37666
37879
  subjectDeclaredOffsetDeviation,
37667
37880
  subjectEffectiveOffsetDeviation,
37668
37881
  subjectLineHeightDeviation,
37669
- subjectStats.element.snapshot.textualContent,
37882
+ subjectStats.element.snapshot.node.textualContent,
37670
37883
  subjectStats.element,
37671
37884
  subjectStats.contentComposition,
37672
37885
  cohortContentCompositions
@@ -37759,26 +37972,26 @@ function coverageFromNumeric(value2) {
37759
37972
  }
37760
37973
  function coverageFromConflict(value2) {
37761
37974
  const certainty = coverageFromKind(value2.kind);
37762
- if (value2.value === "unknown") return certainty * 0.4;
37975
+ if (value2.value === 2 /* Unknown */) return certainty * 0.4;
37763
37976
  return certainty;
37764
37977
  }
37765
37978
  function coverageFromTextContrast(value2) {
37766
- if (value2 === "unknown") return 0.35;
37979
+ if (value2 === 2 /* Unknown */) return 0.35;
37767
37980
  return 1;
37768
37981
  }
37769
37982
  function coverageFromSubjectText(subjectTextualContent) {
37770
- if (subjectTextualContent === "unknown") return 0.35;
37983
+ if (subjectTextualContent === 2 /* Unknown */) return 0.35;
37771
37984
  return 1;
37772
37985
  }
37773
37986
  function coverageFromContextCertainty(certainty) {
37774
- if (certainty === "resolved") return 1;
37775
- if (certainty === "conditional") return 0.55;
37987
+ if (certainty === 0 /* Resolved */) return 1;
37988
+ if (certainty === 1 /* Conditional */) return 0.55;
37776
37989
  return 0.25;
37777
37990
  }
37778
37991
  function coverageFromKind(kind) {
37779
- if (kind === "exact") return 1;
37780
- if (kind === "interval") return 0.78;
37781
- if (kind === "conditional") return 0.5;
37992
+ if (kind === 0 /* Exact */) return 1;
37993
+ if (kind === 1 /* Interval */) return 0.78;
37994
+ if (kind === 2 /* Conditional */) return 0.5;
37782
37995
  return 0.2;
37783
37996
  }
37784
37997
  function averageCoverage(...values) {
@@ -37811,24 +38024,7 @@ function resolveEffectiveAlignmentContext(parentContext, child, measurementNode,
37811
38024
  const childContext = contextByParentNode.get(child);
37812
38025
  if (!childContext) return parentContext;
37813
38026
  if (childContext.baselineRelevance !== "irrelevant") return parentContext;
37814
- return {
37815
- kind: parentContext.kind,
37816
- certainty: parentContext.certainty,
37817
- parentSolidFile: parentContext.parentSolidFile,
37818
- parentElementId: parentContext.parentElementId,
37819
- parentElementKey: parentContext.parentElementKey,
37820
- parentTag: parentContext.parentTag,
37821
- axis: parentContext.axis,
37822
- axisCertainty: parentContext.axisCertainty,
37823
- inlineDirection: parentContext.inlineDirection,
37824
- inlineDirectionCertainty: parentContext.inlineDirectionCertainty,
37825
- parentDisplay: parentContext.parentDisplay,
37826
- parentAlignItems: parentContext.parentAlignItems,
37827
- parentPlaceItems: parentContext.parentPlaceItems,
37828
- hasPositionedOffset: parentContext.hasPositionedOffset,
37829
- baselineRelevance: "irrelevant",
37830
- evidence: parentContext.evidence
37831
- };
38027
+ return deriveAlignmentContext(parentContext, { baselineRelevance: "irrelevant" });
37832
38028
  }
37833
38029
  function compareAlignmentCaseOrder(left, right) {
37834
38030
  if (left.subject.solidFile < right.subject.solidFile) return -1;
@@ -37847,12 +38043,12 @@ function buildConsistencyEvidence(input) {
37847
38043
  input.cohortProfile.medianLineHeightPx,
37848
38044
  input.cohortProfile.medianDeclaredOffsetPx
37849
38045
  );
37850
- const offset = normalizeDeviation(
38046
+ const offsetRaw = normalizeDeviation(
37851
38047
  input.subjectEffectiveOffsetDeviation,
37852
38048
  input.cohortProfile.effectiveOffsetDispersionPx,
37853
38049
  effectiveOffsetScaleReference
37854
38050
  );
37855
- const declaredOffset = normalizeDeviation(
38051
+ const declaredOffsetRaw = normalizeDeviation(
37856
38052
  input.subjectDeclaredOffsetDeviation,
37857
38053
  input.cohortProfile.declaredOffsetDispersionPx,
37858
38054
  declaredOffsetScaleReference
@@ -37863,10 +38059,15 @@ function buildConsistencyEvidence(input) {
37863
38059
  input.cohortProfile.medianLineHeightPx
37864
38060
  );
37865
38061
  const baselinesIrrelevant = input.context.baselineRelevance === "irrelevant";
37866
- const baselineStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveBaselineStrength(input, lineHeight);
37867
- const contextStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveContextStrength(input, lineHeight);
37868
- const replacedStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveReplacedControlStrength(input, lineHeight);
37869
- const compositionStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveContentCompositionStrength(input);
38062
+ const blockAxisIsMainAxis = !input.context.crossAxisIsBlockAxis;
38063
+ const suppressAll = blockAxisIsMainAxis;
38064
+ const offset = suppressAll ? ZERO_STRENGTH : offsetRaw;
38065
+ const declaredOffset = suppressAll ? ZERO_STRENGTH : declaredOffsetRaw;
38066
+ const baselineStrength = baselinesIrrelevant || suppressAll ? ZERO_STRENGTH : resolveBaselineStrength(input, lineHeight);
38067
+ const contextStrength = baselinesIrrelevant || suppressAll ? ZERO_STRENGTH : resolveContextStrength(input, lineHeight);
38068
+ const replacedStrength = baselinesIrrelevant || suppressAll ? ZERO_STRENGTH : resolveReplacedControlStrength(input, lineHeight);
38069
+ const compositionResult = baselinesIrrelevant || suppressAll ? null : resolveContentCompositionStrength(input);
38070
+ const compositionStrength = compositionResult ? compositionResult.evidence : ZERO_STRENGTH;
37870
38071
  const contextCertaintyPenalty = resolveContextCertaintyPenalty(input);
37871
38072
  const provenance = input.cohortProvenance;
37872
38073
  const atoms = buildEvidenceAtoms(
@@ -37887,6 +38088,7 @@ function buildConsistencyEvidence(input) {
37887
38088
  contextStrength: contextStrength.strength,
37888
38089
  replacedStrength: replacedStrength.strength,
37889
38090
  compositionStrength: compositionStrength.strength,
38091
+ majorityClassification: compositionResult ? compositionResult.divergence.majorityClassification : 5 /* Unknown */,
37890
38092
  identifiability: input.subjectIdentifiability,
37891
38093
  factSummary,
37892
38094
  atoms
@@ -37922,7 +38124,7 @@ function resolveOffsetScaleReference(medianLineHeightPx, fallbackMedianOffsetPx)
37922
38124
  }
37923
38125
  function resolveBaselineStrength(input, lineHeight) {
37924
38126
  const verticalAlign = input.cohortSignals.verticalAlign;
37925
- const hasConflict = verticalAlign.value === "conflict";
38127
+ const hasConflict = verticalAlign.value === 0 /* Conflict */;
37926
38128
  const conflict = hasConflict ? alignmentStrengthCalibration.baselineConflictBoost : 0;
37927
38129
  const kind = resolveBaselineEvidenceKind(lineHeight.kind, verticalAlign.kind, hasConflict);
37928
38130
  return {
@@ -37932,7 +38134,7 @@ function resolveBaselineStrength(input, lineHeight) {
37932
38134
  }
37933
38135
  function resolveBaselineEvidenceKind(lineHeightKind, verticalAlignKind, hasConflict) {
37934
38136
  if (!hasConflict) return mergeEvidenceKind(lineHeightKind, verticalAlignKind);
37935
- if (lineHeightKind === "unknown") return verticalAlignKind;
38137
+ if (lineHeightKind === 3 /* Unknown */) return verticalAlignKind;
37936
38138
  return mergeEvidenceKind(lineHeightKind, verticalAlignKind);
37937
38139
  }
37938
38140
  function resolveContextStrength(input, lineHeight) {
@@ -37960,7 +38162,7 @@ function resolveContextConflictEvidence(input) {
37960
38162
  const alignSelf = input.cohortSignals.alignSelf;
37961
38163
  const placeSelf = input.cohortSignals.placeSelf;
37962
38164
  const kind = mergeEvidenceKind(alignSelf.kind, placeSelf.kind);
37963
- const hasConflict = alignSelf.value === "conflict" || placeSelf.value === "conflict";
38165
+ const hasConflict = alignSelf.value === 0 /* Conflict */ || placeSelf.value === 0 /* Conflict */;
37964
38166
  if (!hasConflict) {
37965
38167
  return {
37966
38168
  strength: 0,
@@ -37974,23 +38176,23 @@ function resolveContextConflictEvidence(input) {
37974
38176
  }
37975
38177
  function resolveReplacedControlStrength(input, lineHeight) {
37976
38178
  const subject = input.subject.snapshot;
37977
- const hasReplacedPair = subject.isControl || subject.isReplaced || input.cohortSignals.hasControlOrReplacedPeer;
38179
+ const hasReplacedPair = subject.node.isControl || subject.node.isReplaced || input.cohortSignals.hasControlOrReplacedPeer;
37978
38180
  if (!hasReplacedPair) {
37979
38181
  return {
37980
38182
  strength: 0,
37981
38183
  kind: lineHeight.kind
37982
38184
  };
37983
38185
  }
37984
- if (input.cohortSignals.textContrastWithPeers === "different") {
38186
+ if (input.cohortSignals.textContrastWithPeers === 0 /* Different */) {
37985
38187
  return {
37986
38188
  strength: alignmentStrengthCalibration.replacedDifferentTextBoost,
37987
- kind: "exact"
38189
+ kind: 0 /* Exact */
37988
38190
  };
37989
38191
  }
37990
- if (input.cohortSignals.textContrastWithPeers === "unknown") {
38192
+ if (input.cohortSignals.textContrastWithPeers === 2 /* Unknown */) {
37991
38193
  return {
37992
38194
  strength: alignmentStrengthCalibration.replacedUnknownTextBoost,
37993
- kind: "conditional"
38195
+ kind: 2 /* Conditional */
37994
38196
  };
37995
38197
  }
37996
38198
  return {
@@ -37999,22 +38201,28 @@ function resolveReplacedControlStrength(input, lineHeight) {
37999
38201
  };
38000
38202
  }
38001
38203
  function resolveContentCompositionStrength(input) {
38002
- const divergenceStrength = resolveCompositionDivergenceStrength(
38204
+ const divergence = resolveCompositionDivergence(
38003
38205
  input.subjectContentComposition,
38004
38206
  input.cohortContentCompositions,
38005
38207
  input.context
38006
38208
  );
38007
- if (divergenceStrength <= 0) {
38209
+ if (divergence.strength <= 0) {
38008
38210
  return {
38009
- strength: 0,
38010
- kind: "exact"
38211
+ evidence: {
38212
+ strength: 0,
38213
+ kind: 0 /* Exact */
38214
+ },
38215
+ divergence
38011
38216
  };
38012
38217
  }
38013
38218
  const subjectClassification = input.subjectContentComposition.classification;
38014
- const kind = subjectClassification === "unknown" ? "conditional" : "exact";
38219
+ const kind = subjectClassification === 5 /* Unknown */ ? 2 /* Conditional */ : 0 /* Exact */;
38015
38220
  return {
38016
- strength: clamp(divergenceStrength, 0, 1),
38017
- kind
38221
+ evidence: {
38222
+ strength: clamp(divergence.strength, 0, 1),
38223
+ kind
38224
+ },
38225
+ divergence
38018
38226
  };
38019
38227
  }
38020
38228
  function resolveContextCertaintyPenalty(input) {
@@ -38086,9 +38294,9 @@ function buildEvidenceAtoms(input, offset, declaredOffset, baselineStrength, con
38086
38294
  return out;
38087
38295
  }
38088
38296
  function mapContextCertaintyToEvidenceKind(certainty) {
38089
- if (certainty === "resolved") return "exact";
38090
- if (certainty === "conditional") return "conditional";
38091
- return "unknown";
38297
+ if (certainty === 0 /* Resolved */) return 0 /* Exact */;
38298
+ if (certainty === 1 /* Conditional */) return 2 /* Conditional */;
38299
+ return 3 /* Unknown */;
38092
38300
  }
38093
38301
  function pushSupportAtom(out, factorId, valueKind, strength, coverage, provenance) {
38094
38302
  pushAtom(out, factorId, valueKind, strength, coverage, provenance, "support");
@@ -38114,19 +38322,19 @@ function pushAtom(out, factorId, valueKind, strength, coverage, provenance, expe
38114
38322
  }
38115
38323
  function toPositiveContribution(strength, maxWeight, valueKind) {
38116
38324
  const contribution = clamp(strength, 0, 2) * maxWeight;
38117
- if (valueKind === "exact") {
38325
+ if (valueKind === 0 /* Exact */) {
38118
38326
  return {
38119
38327
  min: contribution,
38120
38328
  max: contribution
38121
38329
  };
38122
38330
  }
38123
- if (valueKind === "interval") {
38331
+ if (valueKind === 1 /* Interval */) {
38124
38332
  return {
38125
38333
  min: contribution * evidenceContributionCalibration.supportIntervalLowerScale,
38126
38334
  max: contribution
38127
38335
  };
38128
38336
  }
38129
- if (valueKind === "conditional") {
38337
+ if (valueKind === 2 /* Conditional */) {
38130
38338
  return {
38131
38339
  min: 0,
38132
38340
  max: contribution * evidenceContributionCalibration.supportConditionalUpperScale
@@ -38139,19 +38347,19 @@ function toPositiveContribution(strength, maxWeight, valueKind) {
38139
38347
  }
38140
38348
  function toNegativeContribution(strength, maxPenalty, valueKind) {
38141
38349
  const penalty = clamp(strength, 0, 1) * maxPenalty;
38142
- if (valueKind === "exact") {
38350
+ if (valueKind === 0 /* Exact */) {
38143
38351
  return {
38144
38352
  min: -penalty,
38145
38353
  max: -penalty
38146
38354
  };
38147
38355
  }
38148
- if (valueKind === "interval") {
38356
+ if (valueKind === 1 /* Interval */) {
38149
38357
  return {
38150
38358
  min: -penalty,
38151
38359
  max: -penalty * evidenceContributionCalibration.penaltyIntervalUpperScale
38152
38360
  };
38153
38361
  }
38154
- if (valueKind === "conditional") {
38362
+ if (valueKind === 2 /* Conditional */) {
38155
38363
  return {
38156
38364
  min: -penalty,
38157
38365
  max: 0
@@ -38162,7 +38370,7 @@ function toNegativeContribution(strength, maxPenalty, valueKind) {
38162
38370
  max: 0
38163
38371
  };
38164
38372
  }
38165
- var ZERO_STRENGTH = { strength: 0, kind: "exact" };
38373
+ var ZERO_STRENGTH = { strength: 0, kind: 0 /* Exact */ };
38166
38374
 
38167
38375
  // src/cross-file/layout/consistency-policy.ts
38168
38376
  function applyConsistencyPolicy(input) {
@@ -38246,23 +38454,38 @@ function resolveConfidence(posterior, evidenceMass) {
38246
38454
  const confidence = posterior.lower * weightedMass * (1 - intervalWidth * alignmentPolicyCalibration.confidenceIntervalPenalty);
38247
38455
  return clamp(confidence, 0, 1);
38248
38456
  }
38457
+ var EMPTY_FACTOR_LIST = Object.freeze([]);
38249
38458
  function selectTopFactors(evidence) {
38250
- const sorted = [...evidence.atoms];
38251
- sorted.sort((left, right) => {
38252
- const leftMagnitude = Math.abs((left.contribution.min + left.contribution.max) / 2);
38253
- const rightMagnitude = Math.abs((right.contribution.min + right.contribution.max) / 2);
38254
- if (leftMagnitude !== rightMagnitude) return rightMagnitude - leftMagnitude;
38255
- if (left.factorId < right.factorId) return -1;
38256
- if (left.factorId > right.factorId) return 1;
38459
+ const atoms = evidence.atoms;
38460
+ if (atoms.length === 0) return EMPTY_FACTOR_LIST;
38461
+ const top = [];
38462
+ for (let i = 0; i < atoms.length; i++) {
38463
+ const atom = atoms[i];
38464
+ if (!atom) continue;
38465
+ const mag = Math.abs((atom.contribution.min + atom.contribution.max) / 2);
38466
+ if (mag <= 0) continue;
38467
+ if (top.length < 4) {
38468
+ top.push({ id: atom.factorId, mag });
38469
+ continue;
38470
+ }
38471
+ let minIdx = 0;
38472
+ for (let j = 1; j < top.length; j++) {
38473
+ const curr = top[j];
38474
+ const best = top[minIdx];
38475
+ if (curr && best && curr.mag < best.mag) minIdx = j;
38476
+ }
38477
+ const minEntry = top[minIdx];
38478
+ if (minEntry && mag > minEntry.mag) {
38479
+ top[minIdx] = { id: atom.factorId, mag };
38480
+ }
38481
+ }
38482
+ top.sort((a, b) => {
38483
+ if (a.mag !== b.mag) return b.mag - a.mag;
38484
+ if (a.id < b.id) return -1;
38485
+ if (a.id > b.id) return 1;
38257
38486
  return 0;
38258
38487
  });
38259
- const out = [];
38260
- for (let i = 0; i < sorted.length && i < 4; i++) {
38261
- const item = sorted[i];
38262
- if (!item) continue;
38263
- out.push(item.factorId);
38264
- }
38265
- return out;
38488
+ return top.map((t) => t.id);
38266
38489
  }
38267
38490
  function logistic(value2) {
38268
38491
  if (value2 > 30) return 1;
@@ -38283,7 +38506,7 @@ function evaluateAlignmentCase(input) {
38283
38506
  evidenceMass: policy.evidenceMass
38284
38507
  };
38285
38508
  }
38286
- const signalFindings = buildFindingsFromAtoms(evidence.atoms, input);
38509
+ const signalFindings = buildFindingsFromAtoms(evidence.atoms, input, evidence);
38287
38510
  return {
38288
38511
  kind: "accept",
38289
38512
  evaluation: {
@@ -38303,12 +38526,12 @@ function evaluateAlignmentCase(input) {
38303
38526
  }
38304
38527
  };
38305
38528
  }
38306
- function buildFindingsFromAtoms(atoms, input) {
38529
+ function buildFindingsFromAtoms(atoms, input, evidence) {
38307
38530
  const byKind = /* @__PURE__ */ new Map();
38308
38531
  for (let i = 0; i < atoms.length; i++) {
38309
38532
  const atom = atoms[i];
38310
38533
  if (!atom) continue;
38311
- const factor = toFindingFactor(atom.factorId, input);
38534
+ const factor = toFindingFactor(atom.factorId, input, evidence);
38312
38535
  if (factor === null) continue;
38313
38536
  const meanContribution = (atom.contribution.min + atom.contribution.max) / 2;
38314
38537
  if (meanContribution <= 0) continue;
@@ -38329,7 +38552,7 @@ function buildFindingsFromAtoms(atoms, input) {
38329
38552
  }
38330
38553
  return [...byKind.values()];
38331
38554
  }
38332
- function toFindingFactor(factorId, input) {
38555
+ function toFindingFactor(factorId, input, evidence) {
38333
38556
  switch (factorId) {
38334
38557
  case "offset-delta":
38335
38558
  return {
@@ -38359,17 +38582,15 @@ function toFindingFactor(factorId, input) {
38359
38582
  case "content-composition-conflict":
38360
38583
  return {
38361
38584
  kind: "content-composition-conflict",
38362
- message: formatContentCompositionFinding(input)
38585
+ message: formatContentCompositionFinding(input, evidence)
38363
38586
  };
38364
38587
  default:
38365
38588
  return null;
38366
38589
  }
38367
38590
  }
38368
- function formatContentCompositionFinding(input) {
38591
+ function formatContentCompositionFinding(input, evidence) {
38369
38592
  const subjectClassification = formatCompositionClassification(input.subjectContentComposition.classification);
38370
- const majorityClassification = formatCompositionClassification(
38371
- resolveMajorityClassification(input.cohortContentCompositions)
38372
- );
38593
+ const majorityClassification = formatCompositionClassification(evidence.majorityClassification);
38373
38594
  const fixSuggestion = formatCompositionFixSuggestion(input.subjectContentComposition);
38374
38595
  return `siblings have identical CSS but different content composition (subject: ${subjectClassification}, majority: ${majorityClassification}; fix: ${fixSuggestion})`;
38375
38596
  }
@@ -38428,7 +38649,7 @@ function recordPolicyMetrics(context, evidenceMass, posteriorLower, posteriorUpp
38428
38649
  context.layout.perf.factorCoverageSum += clamp(evidenceMass, 0, 1);
38429
38650
  context.layout.perf.factorCoverageCount++;
38430
38651
  const width = clamp(posteriorUpper - posteriorLower, 0, 1);
38431
- context.layout.perf.posteriorWidths.push(width);
38652
+ reservoirPush(context.layout.perf.posteriorWidths, width);
38432
38653
  if (width > 1e-3) context.layout.perf.uncertaintyEscalations++;
38433
38654
  }
38434
38655
 
@@ -38447,16 +38668,16 @@ function readNodeRefById(layout, solidFile, elementId) {
38447
38668
  }
38448
38669
  function isFlowRelevantBySiblingsOrText(node, textualContent) {
38449
38670
  if (node.siblingCount >= 2) return true;
38450
- return textualContent === "yes" || textualContent === "unknown" || textualContent === "dynamic-text";
38671
+ return textualContent === 0 /* Yes */ || textualContent === 2 /* Unknown */ || textualContent === 3 /* DynamicText */;
38451
38672
  }
38452
38673
  function isDeferredContainerLike(node, textualContent) {
38453
38674
  if (node.siblingCount >= 2) return true;
38454
- if (textualContent === "unknown") return true;
38675
+ if (textualContent === 2 /* Unknown */) return true;
38455
38676
  if (node.tagName !== null && SECTIONING_CONTAINER_TAGS.has(node.tagName)) return true;
38456
38677
  return false;
38457
38678
  }
38458
38679
  function isDynamicContainerLike(node) {
38459
- return node.textualContent === "unknown" && node.siblingCount >= 2;
38680
+ return node.textualContent === 2 /* Unknown */ && node.siblingCount >= 2;
38460
38681
  }
38461
38682
  function isLikelyViewportAffectingContainer(node) {
38462
38683
  if (node.siblingCount >= 2) return true;
@@ -39696,7 +39917,7 @@ var cssLayoutScrollbarGutterInstability = defineCrossRule({
39696
39917
  const snapshot = collectSignalSnapshot(context, node);
39697
39918
  const scroll = readScrollContainerFact(context.layout, node);
39698
39919
  if (!scroll.isScrollContainer) continue;
39699
- if (scroll.axis !== "y" && scroll.axis !== "both") continue;
39920
+ if (scroll.axis !== 2 /* Y */ && scroll.axis !== 3 /* Both */) continue;
39700
39921
  const scrollbarWidth = readKnownNormalized(snapshot, "scrollbar-width");
39701
39922
  if (scrollbarWidth === "none") continue;
39702
39923
  const gutter = readKnownNormalized(snapshot, "scrollbar-gutter");
@@ -39826,10 +40047,10 @@ var cssLayoutConditionalDisplayCollapse = defineCrossRule({
39826
40047
  const display = readKnownNormalizedWithGuard(snapshot, "display");
39827
40048
  if (!display || !COLLAPSING_DISPLAYS.has(display)) continue;
39828
40049
  const displaySignal = snapshot.signals.get("display");
39829
- if (!displaySignal || displaySignal.guard !== "conditional") continue;
40050
+ if (!displaySignal || displaySignal.guard.kind !== 1 /* Conditional */) continue;
39830
40051
  const flow = readFlowParticipationFact(context.layout, node);
39831
40052
  if (!flow.inFlow) continue;
39832
- if (!isFlowRelevantBySiblingsOrText(node, snapshot.textualContent)) continue;
40053
+ if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
39833
40054
  const reservedSpace = readReservedSpaceFact(context.layout, node);
39834
40055
  if (reservedSpace.hasReservedSpace) continue;
39835
40056
  if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalDisplayCollapse.id, "conditionalDisplayCollapse", messages152.conditionalDisplayCollapse, cssLayoutConditionalDisplayCollapse.severity, { display })) continue;
@@ -39865,10 +40086,10 @@ var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
39865
40086
  if (!hasWrapShiftDelta(whiteSpaceDelta)) continue;
39866
40087
  if (!whiteSpace) continue;
39867
40088
  const whiteSpaceSignal = snapshot.signals.get("white-space");
39868
- if (!whiteSpaceSignal || whiteSpaceSignal.guard !== "conditional") continue;
40089
+ if (!whiteSpaceSignal || whiteSpaceSignal.guard.kind !== 1 /* Conditional */) continue;
39869
40090
  const flow = readFlowParticipationFact(context.layout, node);
39870
40091
  if (!flow.inFlow) continue;
39871
- if (!isFlowRelevantBySiblingsOrText(node, snapshot.textualContent)) continue;
40092
+ if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
39872
40093
  if (hasStableTextShell(snapshot)) continue;
39873
40094
  if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalWhiteSpaceWrapShift.id, "conditionalWhiteSpaceShift", messages153.conditionalWhiteSpaceShift, cssLayoutConditionalWhiteSpaceWrapShift.severity, { whiteSpace })) continue;
39874
40095
  }
@@ -40005,7 +40226,7 @@ var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
40005
40226
  const node = candidates[i];
40006
40227
  if (!node) continue;
40007
40228
  const snapshot = collectSignalSnapshot(context, node);
40008
- if (!isDeferredContainerLike(node, snapshot.textualContent)) continue;
40229
+ if (!isDeferredContainerLike(node, snapshot.node.textualContent)) continue;
40009
40230
  const reservedSpace = readReservedSpaceFact(context.layout, node);
40010
40231
  if (reservedSpace.hasReservedSpace) continue;
40011
40232
  if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize", messages156.missingIntrinsicSize, cssLayoutContentVisibilityNoIntrinsicSize.severity)) continue;
@@ -40064,11 +40285,11 @@ function collectConditionalOffsets(layout, node, snapshot) {
40064
40285
  if (!delta.hasConditional || !delta.hasDelta) continue;
40065
40286
  const signal = snapshot.signals.get(name);
40066
40287
  if (!signal) continue;
40067
- if (signal.guard !== "conditional") continue;
40288
+ if (signal.guard.kind !== 1 /* Conditional */) continue;
40068
40289
  if (signal.kind !== "known") continue;
40069
40290
  if (signal.px === null) continue;
40070
40291
  if (Math.abs(signal.px) <= 0.25) continue;
40071
- out.push({ property: name, value: signal.px, guardKey: signal.guardProvenance.key });
40292
+ out.push({ property: name, value: signal.px, guardKey: signal.guard.key });
40072
40293
  }
40073
40294
  return out;
40074
40295
  }
@@ -40089,8 +40310,8 @@ function hasEffectivePositionForConditionalOffset(snapshot, guardKey) {
40089
40310
  const position = snapshot.signals.get("position");
40090
40311
  if (!position) return false;
40091
40312
  if (position.kind !== "known") return false;
40092
- if (position.guard !== "conditional") return false;
40093
- if (position.guardProvenance.key !== guardKey) return false;
40313
+ if (position.guard.kind !== 1 /* Conditional */) return false;
40314
+ if (position.guard.key !== guardKey) return false;
40094
40315
  return position.normalized !== "static";
40095
40316
  }
40096
40317
  function hasStableBaseline(baselineBySignal, property, expectedPx) {
@@ -42304,13 +42525,14 @@ var RULES = [
42304
42525
  {
42305
42526
  "id": "resource-implicit-suspense",
42306
42527
  "severity": "warn",
42307
- "description": "Detect createResource without initialValue that implicitly triggers Suspense boundaries.",
42308
- "fixable": false,
42528
+ "description": "Detect createResource that implicitly triggers or permanently breaks Suspense boundaries.",
42529
+ "fixable": true,
42309
42530
  "category": "reactivity",
42310
42531
  "plugin": "solid",
42311
42532
  "messages": {
42312
- "loadingMismatch": "createResource '{{name}}' has no initialValue but uses manual loading checks ({{name}}.loading). Without initialValue, Suspense intercepts before your loading UI renders. Add initialValue to the options: createResource(fetcher, { initialValue: ... })",
42313
- "conditionalSuspense": "createResource '{{name}}' has no initialValue and is rendered inside a conditional mount point ({{mountTag}}). This will trigger a distant Suspense boundary and unmount the entire subtree. Add initialValue to the options: createResource(fetcher, { initialValue: ... })"
42533
+ "loadingMismatch": "createResource '{{name}}' has no initialValue but uses {{name}}.loading for manual loading UI. Suspense intercepts before your loading UI renders \u2014 the component is unmounted before the <Show>/<Switch> evaluates. Replace createResource with onMount + createSignal to decouple from Suspense entirely.",
42534
+ "conditionalSuspense": "createResource '{{name}}' is inside a conditional mount point ({{mountTag}}) with a distant Suspense boundary. The SuspenseContext increment fires when the fetcher's Promise is pending and unmounts the entire page subtree \u2014 initialValue does NOT prevent this. Replace createResource with onMount + createSignal to avoid Suspense interaction.",
42535
+ "missingErrorBoundary": "createResource '{{name}}' has no <ErrorBoundary> between its component and the nearest <Suspense>. When the fetcher throws (network error, 401/403/503, timeout), the error propagates to Suspense which has no error handling \u2014 the boundary breaks permanently. Wrap the component in <ErrorBoundary> or replace createResource with onMount + createSignal and catch errors in the fetcher."
42314
42536
  }
42315
42537
  },
42316
42538
  {
@@ -42513,7 +42735,7 @@ var RULES_BY_CATEGORY = {
42513
42735
  "css-structure": [{ "id": "css-no-empty-rule", "severity": "warn", "description": "Disallow empty CSS rules.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "emptyRule": "Empty rule `{{selector}}` should be removed." } }, { "id": "css-no-unknown-container-name", "severity": "error", "description": "Disallow unknown named containers in @container queries.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "unknownContainer": "Unknown container name `{{name}}` in @container query." } }, { "id": "css-no-unused-container-name", "severity": "warn", "description": "Disallow unused named containers.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "unusedContainer": "Container name `{{name}}` is declared but never queried." } }, { "id": "layer-requirement-for-component-rules", "severity": "warn", "description": "Require style rules to be inside @layer when the file defines layers.", "fixable": false, "category": "css-structure", "plugin": "css", "messages": { "missingLayer": "Rule `{{selector}}` is not inside any @layer block while this file uses @layer. Place component rules inside an explicit layer." } }],
42514
42736
  "jsx": [{ "id": "components-return-once", "severity": "error", "description": "Disallow early returns in components. Solid components only run once, and so conditionals should be inside JSX.", "fixable": true, "category": "jsx", "plugin": "solid", "messages": { "noEarlyReturn": "Early returns in Solid components break reactivity because the component function only runs once. Use <Show> or <Switch>/<Match> inside the JSX to conditionally render content instead of returning early from the function.", "noConditionalReturn": "Conditional expressions in return statements break reactivity because Solid components only run once. Wrap the condition in <Show when={...}> for a single condition, or <Switch>/<Match> for multiple conditions." } }, { "id": "jsx-no-duplicate-props", "severity": "error", "description": "Disallow passing the same prop twice in JSX.", "fixable": true, "category": "jsx", "plugin": "solid", "messages": { "noDuplicateProps": "Duplicate prop detected. Each prop should only be specified once; the second value will override the first.", "noDuplicateClass": "Duplicate `class` prop detected. While this might appear to work, it can break unexpectedly because only one class binding is applied. Use `classList` to conditionally apply multiple classes.", "noDuplicateChildren": "Conflicting children: {{used}}. Only one method of setting children is allowed at a time." } }, { "id": "jsx-no-script-url", "severity": "error", "description": "Disallow javascript: URLs.", "fixable": true, "category": "jsx", "plugin": "solid", "messages": { "noJSURL": "Using javascript: URLs is a security risk because it can enable cross-site scripting (XSS) attacks. Use an event handler like onClick instead, or navigate programmatically with useNavigate()." } }, { "id": "jsx-no-undef", "severity": "error", "description": "Disallow references to undefined variables in JSX. Handles custom directives.", "fixable": false, "category": "jsx", "plugin": "solid", "messages": { "customDirectiveUndefined": "Custom directive '{{identifier}}' is not defined. Directives must be imported or declared in scope before use (e.g., `const {{identifier}} = (el, accessor) => { ... }`)." } }, { "id": "jsx-uses-vars", "severity": "warn", "description": "Detect imported components and directives that are never used in JSX.", "fixable": false, "category": "jsx", "plugin": "solid", "messages": { "unusedComponent": "Component '{{name}}' is imported but never used in JSX.", "unusedDirective": "Directive '{{name}}' is imported but never used in JSX." } }, { "id": "no-innerhtml", "severity": "error", "description": "Disallow usage of the innerHTML attribute, which can lead to security vulnerabilities.", "fixable": true, "category": "jsx", "plugin": "solid", "messages": { "dangerous": "Using innerHTML with dynamic content is a security risk. Unsanitized user input can lead to cross-site scripting (XSS) attacks. Use a sanitization library or render content safely.", "conflict": "The innerHTML prop will overwrite all child elements. Remove the children or use innerHTML on an empty element.", "notHtml": "The innerHTML value doesn't appear to be HTML. If you're setting text content, use innerText instead for clarity and safety.", "dangerouslySetInnerHTML": "The dangerouslySetInnerHTML is a React prop that Solid doesn't support. Use innerHTML instead." } }, { "id": "no-unknown-namespaces", "severity": "error", "description": "Enforce using only Solid-specific namespaced attribute names (i.e. `'on:'` in `<div on:click={...} />`).", "fixable": false, "category": "jsx", "plugin": "solid", "messages": { "unknownNamespace": "'{{namespace}}:' is not a recognized Solid namespace. Valid namespaces are: {{validNamespaces}}.", "styleNamespace": "The 'style:' namespace works but is discouraged. Use the style prop with an object instead: style={{ {{property}}: value }}.", "classNamespace": `The 'class:' namespace works but is discouraged. Use the classList prop instead: classList={{ "{{className}}": condition }}.`, "componentNamespace": "Namespaced attributes like '{{namespace}}:' only work on DOM elements, not components. The '{{fullName}}' attribute will be passed as a regular prop named '{{fullName}}'." } }, { "id": "show-truthy-conversion", "severity": "error", "description": "Detect <Show when={expr}> where expr is not explicitly boolean, which may have unexpected truthy/falsy behavior.", "fixable": true, "category": "jsx", "plugin": "solid", "messages": { "showNonBoolean": "<Show when={{{{expr}}}}> uses truthy/falsy conversion. Value '0' or empty string '' will hide content. Use explicit boolean: when={Boolean({{expr}})} or when={{{expr}}} != null}" } }, { "id": "suspense-boundary-missing", "severity": "error", "description": "Detect missing fallback props on Suspense/ErrorBoundary, and lazy components without Suspense wrapper.", "fixable": false, "category": "jsx", "plugin": "solid", "messages": { "suspenseNoFallback": "<Suspense> should have a fallback prop to show while children are loading. Add: fallback={<Loading />}", "errorBoundaryNoFallback": "<ErrorBoundary> should have a fallback prop to show when an error occurs. Add: fallback={(err) => <Error error={err} />}", "lazyNoSuspense": "Lazy component '{{name}}' must be wrapped in a <Suspense> boundary. Add a <Suspense fallback={...}> ancestor." } }, { "id": "validate-jsx-nesting", "severity": "error", "description": "Validates that HTML elements are nested according to the HTML5 specification.", "fixable": false, "category": "jsx", "plugin": "solid", "messages": { "invalidNesting": "Invalid HTML nesting: <{{child}}> cannot be a child of <{{parent}}>. {{reason}}.", "voidElementWithChildren": "<{{parent}}> is a void element and cannot have children. Found <{{child}}> as a child.", "invalidListChild": "<{{child}}> is not a valid direct child of <{{parent}}>. Only <li> elements can be direct children of <ul> and <ol>.", "invalidSelectChild": "<{{child}}> is not a valid direct child of <select>. Only <option> and <optgroup> elements are allowed.", "invalidTableChild": "<{{child}}> is not a valid direct child of <{{parent}}>. Expected: {{expected}}.", "invalidDlChild": "<{{child}}> is not a valid direct child of <dl>. Only <dt>, <dd>, and <div> elements are allowed." } }],
42515
42737
  "performance": [{ "id": "avoid-arguments-object", "severity": "warn", "description": "Disallow arguments object (use rest parameters instead).", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "avoidArguments": "arguments object can prevent V8 optimization. Use rest parameters (...args) instead." } }, { "id": "avoid-chained-array-methods", "severity": "warn", "description": "Flags chained array methods creating 3+ intermediate arrays, or filter().map() pattern.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "avoidChainedArrayMethods": "Chain creates {{count}} intermediate array(s). Consider reduce() or a loop. Chain: {{chain}}", "mapJoinHotPath": "map().join() inside loops allocates intermediate arrays on a hot path. Prefer single-pass string construction." } }, { "id": "avoid-defensive-copy-for-scalar-stat", "severity": "warn", "description": "Disallow defensive array copies passed into scalar statistic calls.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "defensiveCopy": "Defensive copy before scalar statistic '{{stat}}' allocates unnecessarily. Prefer readonly/non-mutating scalar computation." } }, { "id": "avoid-delete-operator", "severity": "warn", "description": "Disallow delete operator on objects (causes V8 deoptimization).", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "avoidDelete": "delete operator transitions object to slow mode. Use `obj.prop = undefined` or destructuring instead." } }, { "id": "avoid-function-allocation-in-hot-loop", "severity": "warn", "description": "Disallow creating closures inside loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "closureInLoop": "Function created inside loop allocates new closure per iteration. Consider hoisting or using event delegation." } }, { "id": "avoid-hidden-class-transition", "severity": "warn", "description": "Suggest consistent object shapes to avoid V8 hidden class transitions.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "hiddenClassTransition": "Property '{{property}}' added conditionally to '{{object}}' creates inconsistent object shapes. Initialize '{{property}}' in the object literal." } }, { "id": "avoid-intermediate-map-copy", "severity": "warn", "description": "Disallow temporary Map allocations that are copied key-for-key into another Map.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "intermediateMapCopy": "Intermediate Map '{{tempName}}' is copied into '{{outName}}' key-for-key. Build output directly to avoid extra allocation." } }, { "id": "avoid-megamorphic-property-access", "severity": "warn", "description": "Avoid property access on `any` or wide union types to prevent V8 deoptimization.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "megamorphicAccess": "Property access on `any` or wide union type causes V8 deoptimization. Consider narrowing the type." } }, { "id": "avoid-quadratic-pair-comparison", "severity": "warn", "description": "Disallow nested for-loops over the same collection creating O(n\xB2) pair comparison.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "quadraticPair": "Nested loops over `{{collection}}` create O(n\xB2) pair comparison. Group by a key property first." } }, { "id": "avoid-quadratic-spread", "severity": "error", "description": "Disallow spreading accumulator in reduce callbacks (O(n\xB2) complexity).", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "quadraticSpread": "Spreading accumulator in reduce creates O(n\xB2) complexity. Use push() instead." } }, { "id": "avoid-repeated-indexof-check", "severity": "warn", "description": "Disallow 3+ .indexOf() calls on the same array variable in one function.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "repeatedIndexOf": "{{count}} .indexOf() calls on `{{name}}` in the same function. Use a Set, regex, or single-pass scan instead." } }, { "id": "avoid-slice-sort-pattern", "severity": "warn", "description": "Disallow .slice().sort() and .slice().reverse() chains. Use .toSorted()/.toReversed().", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "sliceSort": ".slice().sort() creates an intermediate array. Use .toSorted() instead.", "sliceReverse": ".slice().reverse() creates an intermediate array. Use .toReversed() instead.", "spreadSort": "[...array].sort() creates an intermediate array. Use .toSorted() instead.", "spreadReverse": "[...array].reverse() creates an intermediate array. Use .toReversed() instead." } }, { "id": "avoid-sparse-arrays", "severity": "warn", "description": "Disallow new Array(n) without fill (creates holey array).", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "sparseArray": "new Array(n) creates a holey array. Use Array.from() or .fill() instead." } }, { "id": "avoid-spread-sort-map-join-pipeline", "severity": "warn", "description": "Disallow [...iterable].sort().map().join() pipelines on hot paths.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "spreadSortMapJoin": "Spread+sort+map+join pipeline allocates multiple intermediates. Prefer single-pass string construction on hot paths." } }, { "id": "bounded-worklist-traversal", "severity": "warn", "description": "Detect queue/worklist traversals with unbounded growth and no guard.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "boundedWorklist": "Worklist '{{name}}' grows via push() without visited set or explicit size bound. Add traversal guard to prevent pathological growth." } }, { "id": "closure-captured-scope", "severity": "warn", "description": "Detect closures returned from scopes containing large allocations that may be retained.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "capturedScope": "Returned closure shares scope with large allocation '{{name}}'. V8 may retain the allocation via scope capture even though the closure doesn't reference it. Move the allocation to an inner scope." } }, { "id": "closure-dom-circular", "severity": "warn", "description": "Detect event handler property assignments that create closure-DOM circular references.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "circularRef": "Event handler on '{{param}}' creates a closure that captures '{{param}}', forming a closure-DOM circular reference. Use addEventListener with a named handler for easier cleanup." } }, { "id": "create-root-dispose", "severity": "warn", "description": "Detect createRoot with unused dispose parameter.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "unusedDispose": "createRoot() dispose parameter is never used. The reactive tree will never be cleaned up. Call dispose(), return it, or pass it to onCleanup()." } }, { "id": "detached-dom-reference", "severity": "warn", "description": "Detect DOM query results stored in module-scoped variables that may hold detached nodes.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "detachedRef": "DOM query result from '{{method}}' stored in module-scoped variable '{{name}}'. If the DOM node is removed, this reference prevents garbage collection. Use a local variable or WeakRef instead." } }, { "id": "effect-outside-root", "severity": "error", "description": "Detect reactive computations created outside a reactive root (no Owner).", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "orphanedEffect": "{{primitive}}() called outside a reactive root. Without an Owner, this computation is never disposed and leaks memory. Wrap in a component, createRoot, or runWithOwner." } }, { "id": "finalization-registry-leak", "severity": "error", "description": "Detect FinalizationRegistry.register() where heldValue references the target.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "selfReference": "FinalizationRegistry.register() heldValue references the target '{{name}}'. This strong reference prevents the target from being garbage collected, defeating the purpose of the registry." } }, { "id": "no-char-array-materialization", "severity": "warn", "description": 'Disallow split(""), Array.from(str), or [...str] in parsing loops.', "fixable": false, "category": "performance", "plugin": "solid", "messages": { "charArrayMaterialization": "Character array materialization via {{pattern}} in parsing loops allocates O(n) extra memory. Prefer index-based scanning." } }, { "id": "no-double-pass-delimiter-count", "severity": "warn", "description": "Disallow split-based delimiter counting followed by additional split passes.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "doublePassDelimiterCount": "Delimiter counting via `split(...).length` plus another `split(...)` repeats full-string passes. Prefer one indexed scan." } }, { "id": "no-full-split-in-hot-parse", "severity": "warn", "description": "Disallow full split() materialization inside hot string parsing loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "fullSplitInHotParse": "`split()` inside parsing loops materializes full token arrays each iteration. Prefer cursor/index scanning." } }, { "id": "no-heavy-parser-constructor-in-loop", "severity": "warn", "description": "Disallow constructing heavy parsing helpers inside loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "heavyParserConstructor": "`new {{ctor}}(...)` inside parsing loops repeatedly allocates heavy parser helpers. Hoist and reuse instances." } }, { "id": "no-leaked-abort-controller", "severity": "warn", "description": "Detect AbortController in effects without abort() in onCleanup.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "leakedAbort": "new AbortController() inside a reactive effect without onCleanup. Add onCleanup(() => controller.abort())." } }, { "id": "no-leaked-animation-frame", "severity": "warn", "description": "Detect requestAnimationFrame in effects without cancelAnimationFrame in onCleanup.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "leakedRaf": "requestAnimationFrame() inside a reactive effect without onCleanup. Add onCleanup(() => cancelAnimationFrame(id))." } }, { "id": "no-leaked-event-listener", "severity": "warn", "description": "Detect addEventListener in effects without removeEventListener in onCleanup.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "leakedListener": "addEventListener() inside a reactive effect without onCleanup. Each re-run leaks a listener. Add onCleanup(() => removeEventListener(...))." } }, { "id": "no-leaked-observer", "severity": "warn", "description": "Detect Observer APIs in effects without disconnect() in onCleanup.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "leakedObserver": "new {{type}}() inside a reactive effect without onCleanup. Add onCleanup(() => observer.disconnect())." } }, { "id": "no-leaked-subscription", "severity": "warn", "description": "Detect WebSocket/EventSource/BroadcastChannel in effects without close() in onCleanup.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "leakedSubscription": "new {{type}}() inside a reactive effect without onCleanup. Add onCleanup(() => instance.close())." } }, { "id": "no-leaked-timer", "severity": "warn", "description": "Detect setInterval/setTimeout in effects without onCleanup to clear them.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "leakedTimer": "{{setter}}() inside a reactive effect without onCleanup. Each re-run leaks a timer. Add onCleanup(() => {{clearer}}(id))." } }, { "id": "no-loop-string-plus-equals", "severity": "warn", "description": "Disallow repeated string += accumulation in parsing loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "loopStringPlusEquals": "Repeated string `+=` in parsing loops creates avoidable allocations. Buffer chunks and join once." } }, { "id": "no-multipass-split-pipeline", "severity": "warn", "description": "Disallow multipass split/map/filter pipelines in parsing code.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "multipassSplit": "`split()` followed by multiple array passes allocates heavily on parsing paths. Prefer single-pass parsing." } }, { "id": "no-per-char-substring-scan", "severity": "warn", "description": "Disallow per-character substring/charAt scanning patterns in loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "perCharSubstring": "Per-character `{{method}}()` scanning in loops allocates extra strings. Prefer index + charCodeAt scanning." } }, { "id": "no-repeated-token-normalization", "severity": "warn", "description": "Disallow repeated trim/lower/upper normalization chains on the same token in one function.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "repeatedTokenNormalization": "Repeated token normalization `{{chain}}` on `{{name}}` in one function. Compute once and reuse." } }, { "id": "no-rescan-indexof-loop", "severity": "warn", "description": "Disallow repeated indexOf/includes scans from start in parsing loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "rescanIndexOf": "Repeated `{{method}}()` from string start inside loops rescans prior text. Pass a cursor start index." } }, { "id": "no-rest-slice-loop", "severity": "warn", "description": "Disallow repeated self-slice reassignment loops in string parsing code.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "restSliceLoop": "Repeated `{{name}} = {{name}}.{{method}}(...)` in loops creates string churn. Track cursor indexes instead." } }, { "id": "no-shift-splice-head-consume", "severity": "warn", "description": "Disallow shift/splice(0,1) head-consume patterns in loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "headConsume": "Head-consuming `{{method}}()` inside loops causes array reindexing costs. Use index cursor iteration instead." } }, { "id": "no-write-only-index", "severity": "warn", "description": "Detect index structures that are written but never queried by key.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "writeOnlyIndex": "Index '{{name}}' is built via writes but never queried by key. Remove it or use direct collection flow." } }, { "id": "prefer-charcode-over-regex-test", "severity": "warn", "description": "Prefer charCodeAt() range checks over regex .test() for single-character classification.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "regexTest": "Regex `{{pattern}}`.test() on a single character. Use charCodeAt() range checks instead." } }, { "id": "prefer-index-scan-over-string-iterator", "severity": "warn", "description": "Prefer index-based string scanning over for-of iteration in ASCII parser code.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "preferIndexScan": "ASCII parsing loops should avoid `for...of` string iteration. Prefer indexed scanning with charCodeAt for lower overhead." } }, { "id": "prefer-lazy-property-access", "severity": "warn", "description": "Suggests moving property access after early returns when not used immediately.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "preferLazyPropertyAccess": "Property '{{propertyName}}' assigned to '{{variableName}}' before early return but not used there. Move assignment after early returns." } }, { "id": "prefer-map-lookup-over-linear-scan", "severity": "warn", "description": "Disallow repeated linear scans over fixed literal collections in hot paths.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "preferMapLookup": "Linear scan over fixed collection '{{name}}' in '{{fnName}}'. Precompute Map/Set lookup for O(1) access." } }, { "id": "prefer-map-over-object-dictionary", "severity": "warn", "description": "Suggest Map for dictionary-like objects with dynamic keys.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "preferMap": "Dynamic key assignment on dictionary object causes hidden class transitions. Consider using Map." } }, { "id": "prefer-precompiled-regex", "severity": "warn", "description": "Prefer hoisting regex literals to module-level constants to avoid repeated compilation.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "inlineRegex": "Regex `{{pattern}}` is compiled on every call. Hoist to a module-level constant." } }, { "id": "prefer-set-has-over-equality-chain", "severity": "warn", "description": "Disallow 4+ guard-style equality checks against string literals on the same variable. Use a Set.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "equalityChain": "{{count}} equality checks against `{{name}}`. Extract literals to a Set and use .has() instead." } }, { "id": "prefer-set-lookup-in-loop", "severity": "warn", "description": "Disallow linear search methods (.includes/.indexOf) on arrays inside loops.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "preferSet": "`.{{method}}()` on `{{name}}` called inside a loop. Convert to a Set for O(1) lookups." } }, { "id": "recursive-timer", "severity": "warn", "description": "Detect setTimeout that recursively calls its enclosing function.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "recursiveTimer": "setTimeout() recursively calls '{{name}}', creating an unbreakable polling loop. Add a termination condition or use setInterval with cleanup." } }, { "id": "self-referencing-store", "severity": "error", "description": "Detect setStore() where the value argument references the store itself.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "selfReference": "setStore() value references the store variable '{{name}}', creating a circular proxy reference. This prevents garbage collection and can cause infinite loops." } }, { "id": "unbounded-collection", "severity": "warn", "description": "Detect module-scoped Map/Set/Array that only grow without removal.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "unboundedCollection": "Module-scoped {{type}} '{{name}}' only uses additive methods ({{methods}}). Without removal or clearing, this grows unbounded. Consider WeakMap, LRU eviction, or periodic clear()." } }, { "id": "unbounded-signal-accumulation", "severity": "warn", "description": "Detect signal setters that accumulate data without truncation via spread+append pattern.", "fixable": false, "category": "performance", "plugin": "solid", "messages": { "unbounded": "Signal setter '{{name}}' accumulates data without bounds. The array grows monotonically via spread+append. Add truncation (e.g. prev.slice(-limit)) to prevent unbounded growth." } }],
42516
- "reactivity": [{ "id": "async-tracked", "severity": "error", "description": "Disallow async functions in tracked scopes (createEffect, createMemo, etc.)", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "asyncCreateEffect": "Async function{{fnName}} in createEffect loses tracking after await. Read all signals before the first await, or use createResource for async data fetching.", "asyncCreateMemo": "Async function{{fnName}} in createMemo won't work correctly. createMemo must be synchronous. For async derived data, use createResource instead.", "asyncCreateComputed": "Async function{{fnName}} in createComputed won't track properly. createComputed must be synchronous\u2014signal reads after await won't trigger re-computation.", "asyncCreateRenderEffect": "Async function{{fnName}} in createRenderEffect breaks DOM update timing. createRenderEffect must be synchronous. Move async work to onMount or createResource.", "asyncTrackedGeneric": "Async function{{fnName}} in {{source}} won't track reactivity after await. Solid's tracking only works synchronously\u2014signal reads after await are ignored." } }, { "id": "children-helper-misuse", "severity": "error", "description": "Detect misuse of the children() helper that causes unnecessary re-computation or breaks reactivity", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "multipleChildrenCalls": "The children() helper should only be called once per component. Each call re-resolves children, causing unnecessary computation. Store the result and reuse the accessor.", "directChildrenAccess": "Access props.children through the children() helper in reactive contexts. Direct access won't properly resolve or track children. Use: const resolved = children(() => props.children);" } }, { "id": "cleanup-scope", "severity": "error", "description": "Detect onCleanup called outside of a valid reactive scope", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "cleanupOutsideScope": "onCleanup() called outside a reactive scope ({{location}}). The cleanup function will never execute unless this code runs within a component, effect, createRoot, or runWithOwner." } }, { "id": "derived-signal", "severity": "error", "description": "Detect functions that capture reactive values but are called in untracked contexts", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "moduleScopeInit": "Assigning '{{fnName}}()' to '{{varName}}' at module scope runs once at startup. It captures {{vars}} which won't trigger updates.", "moduleScopeCall": "'{{fnName}}()' at module scope executes once when the module loads. It captures {{vars}}\u2014changes won't cause this to re-run.", "componentTopLevelInit": "'{{fnName}}()' assigned to '{{varName}}' in '{{componentName}}' captures a one-time snapshot of {{vars}}. Changes won't update '{{varName}}'. Call in JSX or use createMemo().", "componentTopLevelCall": "'{{fnName}}()' at top-level of '{{componentName}}' runs once and captures a snapshot of {{vars}}. Changes won't re-run this. Move inside JSX: {{{fnName}}()} or wrap with createMemo().", "utilityFnCall": "'{{fnName}}()' inside '{{utilityName}}' won't be reactive. Call '{{utilityName}}' from a tracked scope (createEffect, JSX), or pass {{vars}} as parameters.", "syncCallbackCall": "'{{fnName}}()' inside {{methodName}}() callback runs outside a tracking scope. The result captures a snapshot of {{vars}} that won't update.", "untrackedCall": "'{{fnName}}()' called in an untracked context. It captures {{vars}} which won't trigger updates here. Move to JSX or a tracked scope." } }, { "id": "effect-as-memo", "severity": "error", "description": "Detect createEffect that only sets a derived signal value, which should be createMemo instead", "fixable": true, "category": "reactivity", "plugin": "solid", "messages": { "effectAsMemo": "This createEffect only computes a derived value. Use createMemo() instead: const {{signalName}} = createMemo(() => {{expression}});" } }, { "id": "effect-as-mount", "severity": "error", "description": "Detect createEffect/createRenderEffect with no reactive dependencies that should be onMount instead", "fixable": true, "category": "reactivity", "plugin": "solid", "messages": { "effectAsMount": "This {{primitive}} has no reactive dependencies and runs only once. Use onMount() for initialization logic that doesn't need to re-run." } }, { "id": "inline-component", "severity": "error", "description": "Detect component functions defined inside other components, which causes remount on every parent update", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "inlineComponent": "Component '{{name}}' is defined inside another component. This creates a new component type on every render, causing unmount/remount. Move the component definition outside." } }, { "id": "no-top-level-signal-call", "severity": "error", "description": "Disallow calling signals at component top-level (captures stale snapshots)", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "assignedToVar": "'{{name}}()' assigned to '{{varName}}' in {{componentName}} captures a one-time snapshot. '{{varName}}' won't update when {{name}} changes. Use createMemo(): `const {{varName}} = createMemo(() => {{name}}());`", "computedValue": "'{{name}}()' in computation at top-level of {{componentName}} captures a stale snapshot. Wrap with createMemo(): `const {{varName}} = createMemo(() => /* computation using {{name}}() */);`", "templateLiteral": "'{{name}}()' in template literal at top-level of {{componentName}} captures a stale snapshot. Use createMemo() or compute directly in JSX: `{`Hello, ${{{name}}()}!`}`", "destructuring": "Destructuring '{{name}}()' at top-level of {{componentName}} captures a stale snapshot. Access properties in JSX or createMemo(): `{{{name}}().propertyName}`", "objectLiteral": "'{{name}}()' in object literal at top-level of {{componentName}} captures a stale snapshot. Use createMemo() for the object, or spread in JSX.", "arrayCreation": "'{{name}}()' in array creation at top-level of {{componentName}} captures a stale snapshot. Wrap with createMemo(): `const items = createMemo(() => Array.from(...));`", "earlyReturn": "'{{name}}()' in early return at top-level of {{componentName}} captures a stale snapshot. Use <Show when={{{name}}()}> for conditional rendering instead.", "conditionalAssign": "'{{name}}()' in ternary at top-level of {{componentName}} captures a stale snapshot. Use createMemo() or compute in JSX: `{{{name}}() ? 'Yes' : 'No'}`", "functionArgument": "'{{name}}()' passed as argument at top-level of {{componentName}} captures a stale snapshot. Move to createEffect() or compute in JSX.", "syncCallback": "'{{name}}()' inside {{methodName}}() at top-level of {{componentName}} captures a stale snapshot. Wrap the entire computation in createMemo(): `const result = createMemo(() => items.{{methodName}}(...));`", "topLevelCall": "'{{name}}()' at top-level of {{componentName}} captures a one-time snapshot. Changes to {{name}} won't update the result. Call directly in JSX or wrap in createMemo()." } }, { "id": "ref-early-access", "severity": "error", "description": "Detect accessing refs before they are assigned (before mount)", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "refBeforeMount": "Ref '{{name}}' is accessed before component mounts. Refs are undefined until after mount. Access in onMount(), createEffect(), or event handlers." } }, { "id": "resource-access-unchecked", "severity": "error", "description": "Detect accessing resource data without checking loading/error state.", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "resourceUnchecked": "Accessing resource '{{name}}' without checking loading/error state may return undefined. Wrap in <Show when={!{{name}}.loading}> or <Suspense>." } }, { "id": "resource-implicit-suspense", "severity": "warn", "description": "Detect createResource without initialValue that implicitly triggers Suspense boundaries.", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "loadingMismatch": "createResource '{{name}}' has no initialValue but uses manual loading checks ({{name}}.loading). Without initialValue, Suspense intercepts before your loading UI renders. Add initialValue to the options: createResource(fetcher, { initialValue: ... })", "conditionalSuspense": "createResource '{{name}}' has no initialValue and is rendered inside a conditional mount point ({{mountTag}}). This will trigger a distant Suspense boundary and unmount the entire subtree. Add initialValue to the options: createResource(fetcher, { initialValue: ... })" } }, { "id": "resource-refetch-loop", "severity": "error", "description": "Detect refetch() calls inside createEffect which can cause infinite loops", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "refetchInEffect": "Calling {{name}}.refetch() inside createEffect may cause infinite loops. The resource tracks its own dependencies. Move refetch to an event handler or use on() to control dependencies." } }, { "id": "signal-call", "severity": "error", "description": "Require signals to be called as functions when used in tracked contexts", "fixable": true, "category": "reactivity", "plugin": "solid", "messages": { "signalInJsxText": "Signal '{{name}}' in JSX text should be called: {{{name}}()}. Without (), you're rendering the function, not its value.", "signalInJsxAttribute": "Signal '{{name}}' in JSX attribute should be called: {{attr}}={{{name}}()}. Without (), the attribute won't update reactively.", "signalInTernary": "Signal '{{name}}' in ternary should be called: {{name}}() ? ... : .... The condition won't react to changes without ().", "signalInLogical": "Signal '{{name}}' in logical expression should be called: {{name}}() && .... Without (), this always evaluates to truthy (functions are truthy).", "signalInComparison": "Signal '{{name}}' in comparison should be called: {{name}}() === .... Comparing functions always returns false.", "signalInArithmetic": "Signal '{{name}}' in arithmetic should be called: {{name}}() + .... Math on functions produces NaN.", "signalInTemplate": "Signal '{{name}}' in template literal should be called: `...${{{name}}()}...`. Without (), you're embedding '[Function]'.", "signalInTrackedScope": "Signal '{{name}}' in {{where}} should be called: {{name}}(). Without (), reactivity is lost.", "badSignal": "The reactive variable '{{name}}' should be called as a function when used in {{where}}." } }, { "id": "signal-in-loop", "severity": "error", "description": "Detect problematic signal usage inside For/Index loop callbacks", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "signalInLoop": "Creating signals inside <{{component}}> callback creates new signals on each render. Use a store at the parent level, or derive state from the index.", "signalCallInvariant": "Signal '{{name}}' called inside <{{component}}> produces the same value for every item. Extract to a variable or memoize with createMemo() before the loop.", "derivedCallInvariant": "'{{name}}()' inside <{{component}}> captures {{captures}} but doesn't use the loop item. Extract the call before the loop or pass the item as a parameter." } }, { "id": "store-reactive-break", "severity": "error", "description": "Detect patterns that break store reactivity: spreading stores, top-level property extraction, or destructuring", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "storeSpread": "Spreading a store ({...store}) creates a static snapshot that won't update. Access store properties directly in JSX or tracked contexts.", "storeTopLevelAccess": "Accessing store property '{{property}}' at component top-level captures the value once. Access store.{{property}} directly in JSX or wrap in createMemo().", "storeDestructure": "Destructuring a store breaks reactivity. Access properties via store.{{property}} instead of destructuring." } }, { "id": "transition-pending-unchecked", "severity": "error", "description": "Detect useTransition usage without handling the isPending state", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "pendingUnchecked": "useTransition returns [isPending, startTransition]. The isPending state should be used to show loading UI during transitions." } }],
42738
+ "reactivity": [{ "id": "async-tracked", "severity": "error", "description": "Disallow async functions in tracked scopes (createEffect, createMemo, etc.)", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "asyncCreateEffect": "Async function{{fnName}} in createEffect loses tracking after await. Read all signals before the first await, or use createResource for async data fetching.", "asyncCreateMemo": "Async function{{fnName}} in createMemo won't work correctly. createMemo must be synchronous. For async derived data, use createResource instead.", "asyncCreateComputed": "Async function{{fnName}} in createComputed won't track properly. createComputed must be synchronous\u2014signal reads after await won't trigger re-computation.", "asyncCreateRenderEffect": "Async function{{fnName}} in createRenderEffect breaks DOM update timing. createRenderEffect must be synchronous. Move async work to onMount or createResource.", "asyncTrackedGeneric": "Async function{{fnName}} in {{source}} won't track reactivity after await. Solid's tracking only works synchronously\u2014signal reads after await are ignored." } }, { "id": "children-helper-misuse", "severity": "error", "description": "Detect misuse of the children() helper that causes unnecessary re-computation or breaks reactivity", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "multipleChildrenCalls": "The children() helper should only be called once per component. Each call re-resolves children, causing unnecessary computation. Store the result and reuse the accessor.", "directChildrenAccess": "Access props.children through the children() helper in reactive contexts. Direct access won't properly resolve or track children. Use: const resolved = children(() => props.children);" } }, { "id": "cleanup-scope", "severity": "error", "description": "Detect onCleanup called outside of a valid reactive scope", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "cleanupOutsideScope": "onCleanup() called outside a reactive scope ({{location}}). The cleanup function will never execute unless this code runs within a component, effect, createRoot, or runWithOwner." } }, { "id": "derived-signal", "severity": "error", "description": "Detect functions that capture reactive values but are called in untracked contexts", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "moduleScopeInit": "Assigning '{{fnName}}()' to '{{varName}}' at module scope runs once at startup. It captures {{vars}} which won't trigger updates.", "moduleScopeCall": "'{{fnName}}()' at module scope executes once when the module loads. It captures {{vars}}\u2014changes won't cause this to re-run.", "componentTopLevelInit": "'{{fnName}}()' assigned to '{{varName}}' in '{{componentName}}' captures a one-time snapshot of {{vars}}. Changes won't update '{{varName}}'. Call in JSX or use createMemo().", "componentTopLevelCall": "'{{fnName}}()' at top-level of '{{componentName}}' runs once and captures a snapshot of {{vars}}. Changes won't re-run this. Move inside JSX: {{{fnName}}()} or wrap with createMemo().", "utilityFnCall": "'{{fnName}}()' inside '{{utilityName}}' won't be reactive. Call '{{utilityName}}' from a tracked scope (createEffect, JSX), or pass {{vars}} as parameters.", "syncCallbackCall": "'{{fnName}}()' inside {{methodName}}() callback runs outside a tracking scope. The result captures a snapshot of {{vars}} that won't update.", "untrackedCall": "'{{fnName}}()' called in an untracked context. It captures {{vars}} which won't trigger updates here. Move to JSX or a tracked scope." } }, { "id": "effect-as-memo", "severity": "error", "description": "Detect createEffect that only sets a derived signal value, which should be createMemo instead", "fixable": true, "category": "reactivity", "plugin": "solid", "messages": { "effectAsMemo": "This createEffect only computes a derived value. Use createMemo() instead: const {{signalName}} = createMemo(() => {{expression}});" } }, { "id": "effect-as-mount", "severity": "error", "description": "Detect createEffect/createRenderEffect with no reactive dependencies that should be onMount instead", "fixable": true, "category": "reactivity", "plugin": "solid", "messages": { "effectAsMount": "This {{primitive}} has no reactive dependencies and runs only once. Use onMount() for initialization logic that doesn't need to re-run." } }, { "id": "inline-component", "severity": "error", "description": "Detect component functions defined inside other components, which causes remount on every parent update", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "inlineComponent": "Component '{{name}}' is defined inside another component. This creates a new component type on every render, causing unmount/remount. Move the component definition outside." } }, { "id": "no-top-level-signal-call", "severity": "error", "description": "Disallow calling signals at component top-level (captures stale snapshots)", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "assignedToVar": "'{{name}}()' assigned to '{{varName}}' in {{componentName}} captures a one-time snapshot. '{{varName}}' won't update when {{name}} changes. Use createMemo(): `const {{varName}} = createMemo(() => {{name}}());`", "computedValue": "'{{name}}()' in computation at top-level of {{componentName}} captures a stale snapshot. Wrap with createMemo(): `const {{varName}} = createMemo(() => /* computation using {{name}}() */);`", "templateLiteral": "'{{name}}()' in template literal at top-level of {{componentName}} captures a stale snapshot. Use createMemo() or compute directly in JSX: `{`Hello, ${{{name}}()}!`}`", "destructuring": "Destructuring '{{name}}()' at top-level of {{componentName}} captures a stale snapshot. Access properties in JSX or createMemo(): `{{{name}}().propertyName}`", "objectLiteral": "'{{name}}()' in object literal at top-level of {{componentName}} captures a stale snapshot. Use createMemo() for the object, or spread in JSX.", "arrayCreation": "'{{name}}()' in array creation at top-level of {{componentName}} captures a stale snapshot. Wrap with createMemo(): `const items = createMemo(() => Array.from(...));`", "earlyReturn": "'{{name}}()' in early return at top-level of {{componentName}} captures a stale snapshot. Use <Show when={{{name}}()}> for conditional rendering instead.", "conditionalAssign": "'{{name}}()' in ternary at top-level of {{componentName}} captures a stale snapshot. Use createMemo() or compute in JSX: `{{{name}}() ? 'Yes' : 'No'}`", "functionArgument": "'{{name}}()' passed as argument at top-level of {{componentName}} captures a stale snapshot. Move to createEffect() or compute in JSX.", "syncCallback": "'{{name}}()' inside {{methodName}}() at top-level of {{componentName}} captures a stale snapshot. Wrap the entire computation in createMemo(): `const result = createMemo(() => items.{{methodName}}(...));`", "topLevelCall": "'{{name}}()' at top-level of {{componentName}} captures a one-time snapshot. Changes to {{name}} won't update the result. Call directly in JSX or wrap in createMemo()." } }, { "id": "ref-early-access", "severity": "error", "description": "Detect accessing refs before they are assigned (before mount)", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "refBeforeMount": "Ref '{{name}}' is accessed before component mounts. Refs are undefined until after mount. Access in onMount(), createEffect(), or event handlers." } }, { "id": "resource-access-unchecked", "severity": "error", "description": "Detect accessing resource data without checking loading/error state.", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "resourceUnchecked": "Accessing resource '{{name}}' without checking loading/error state may return undefined. Wrap in <Show when={!{{name}}.loading}> or <Suspense>." } }, { "id": "resource-implicit-suspense", "severity": "warn", "description": "Detect createResource that implicitly triggers or permanently breaks Suspense boundaries.", "fixable": true, "category": "reactivity", "plugin": "solid", "messages": { "loadingMismatch": "createResource '{{name}}' has no initialValue but uses {{name}}.loading for manual loading UI. Suspense intercepts before your loading UI renders \u2014 the component is unmounted before the <Show>/<Switch> evaluates. Replace createResource with onMount + createSignal to decouple from Suspense entirely.", "conditionalSuspense": "createResource '{{name}}' is inside a conditional mount point ({{mountTag}}) with a distant Suspense boundary. The SuspenseContext increment fires when the fetcher's Promise is pending and unmounts the entire page subtree \u2014 initialValue does NOT prevent this. Replace createResource with onMount + createSignal to avoid Suspense interaction.", "missingErrorBoundary": "createResource '{{name}}' has no <ErrorBoundary> between its component and the nearest <Suspense>. When the fetcher throws (network error, 401/403/503, timeout), the error propagates to Suspense which has no error handling \u2014 the boundary breaks permanently. Wrap the component in <ErrorBoundary> or replace createResource with onMount + createSignal and catch errors in the fetcher." } }, { "id": "resource-refetch-loop", "severity": "error", "description": "Detect refetch() calls inside createEffect which can cause infinite loops", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "refetchInEffect": "Calling {{name}}.refetch() inside createEffect may cause infinite loops. The resource tracks its own dependencies. Move refetch to an event handler or use on() to control dependencies." } }, { "id": "signal-call", "severity": "error", "description": "Require signals to be called as functions when used in tracked contexts", "fixable": true, "category": "reactivity", "plugin": "solid", "messages": { "signalInJsxText": "Signal '{{name}}' in JSX text should be called: {{{name}}()}. Without (), you're rendering the function, not its value.", "signalInJsxAttribute": "Signal '{{name}}' in JSX attribute should be called: {{attr}}={{{name}}()}. Without (), the attribute won't update reactively.", "signalInTernary": "Signal '{{name}}' in ternary should be called: {{name}}() ? ... : .... The condition won't react to changes without ().", "signalInLogical": "Signal '{{name}}' in logical expression should be called: {{name}}() && .... Without (), this always evaluates to truthy (functions are truthy).", "signalInComparison": "Signal '{{name}}' in comparison should be called: {{name}}() === .... Comparing functions always returns false.", "signalInArithmetic": "Signal '{{name}}' in arithmetic should be called: {{name}}() + .... Math on functions produces NaN.", "signalInTemplate": "Signal '{{name}}' in template literal should be called: `...${{{name}}()}...`. Without (), you're embedding '[Function]'.", "signalInTrackedScope": "Signal '{{name}}' in {{where}} should be called: {{name}}(). Without (), reactivity is lost.", "badSignal": "The reactive variable '{{name}}' should be called as a function when used in {{where}}." } }, { "id": "signal-in-loop", "severity": "error", "description": "Detect problematic signal usage inside For/Index loop callbacks", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "signalInLoop": "Creating signals inside <{{component}}> callback creates new signals on each render. Use a store at the parent level, or derive state from the index.", "signalCallInvariant": "Signal '{{name}}' called inside <{{component}}> produces the same value for every item. Extract to a variable or memoize with createMemo() before the loop.", "derivedCallInvariant": "'{{name}}()' inside <{{component}}> captures {{captures}} but doesn't use the loop item. Extract the call before the loop or pass the item as a parameter." } }, { "id": "store-reactive-break", "severity": "error", "description": "Detect patterns that break store reactivity: spreading stores, top-level property extraction, or destructuring", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "storeSpread": "Spreading a store ({...store}) creates a static snapshot that won't update. Access store properties directly in JSX or tracked contexts.", "storeTopLevelAccess": "Accessing store property '{{property}}' at component top-level captures the value once. Access store.{{property}} directly in JSX or wrap in createMemo().", "storeDestructure": "Destructuring a store breaks reactivity. Access properties via store.{{property}} instead of destructuring." } }, { "id": "transition-pending-unchecked", "severity": "error", "description": "Detect useTransition usage without handling the isPending state", "fixable": false, "category": "reactivity", "plugin": "solid", "messages": { "pendingUnchecked": "useTransition returns [isPending, startTransition]. The isPending state should be used to show loading UI during transitions." } }],
42517
42739
  "solid": [{ "id": "batch-optimization", "severity": "warn", "description": "Suggest using batch() when multiple signal setters are called in the same synchronous scope", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "multipleSetters": "Multiple signal updates in the same scope cause multiple re-renders. Wrap in batch() for a single update: batch(() => { {{setters}} });" } }, { "id": "imports", "severity": "error", "description": 'Enforce consistent imports from "solid-js", "solid-js/web", and "solid-js/store".', "fixable": false, "category": "solid", "plugin": "solid", "messages": { "preferSource": 'Prefer importing {{name}} from "{{source}}".' } }, { "id": "index-vs-for", "severity": "warn", "description": "Suggest <For> for object arrays and <Index> for primitive arrays.", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "indexWithObjects": "<Index> with object arrays causes the item accessor to change on any array mutation. Use <For> for objects to maintain reference stability.", "forWithPrimitives": "<For> with primitive arrays (strings, numbers) keys by value, which may cause unexpected re-renders. Consider <Index> if index stability is preferred." } }, { "id": "no-react-deps", "severity": "error", "description": "Disallow usage of dependency arrays in `createEffect`, `createMemo`, and `createRenderEffect`.", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "noUselessDep": "In Solid, `{{name}}` doesn't accept a dependency array because it automatically tracks its dependencies. If you really need to override the list of dependencies, use `on`." } }, { "id": "no-react-specific-props", "severity": "error", "description": "Disallow usage of React-specific `className`/`htmlFor` props, which were deprecated in v1.4.0.", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "prefer": "Prefer the `{{to}}` prop over the deprecated `{{from}}` prop.", "noUselessKey": "Elements in a <For> or <Index> list do not need a key prop." } }, { "id": "prefer-for", "severity": "warn", "description": "Enforce using Solid's `<For />` component for mapping an array to JSX elements.", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "preferFor": "Prefer Solid's `<For each={...}>` component for rendering lists of objects. Array#map recreates all DOM elements on every update, while <For> updates only changed items by keying on reference.", "preferIndex": "Prefer Solid's `<Index each={...}>` component for rendering lists of primitives. Array#map recreates all DOM elements on every update, while <Index> updates only changed items by keying on index position.", "preferForOrIndex": "Prefer Solid's `<For />` or `<Index />` component for rendering lists. Use <For> when items are objects (keys by reference), or <Index> when items are primitives like strings/numbers (keys by index). Array#map recreates all DOM elements on every update." } }, { "id": "prefer-memo-complex-styles", "severity": "warn", "description": "Enforce extracting complex style computations to createMemo for better approach. Complex inline style objects are rebuilt on every render, which can impact approach.", "fixable": false, "category": "solid", "plugin": "solid", "messages": { "preferMemoComplexStyle": "Complex style computation should be extracted to createMemo() for better approach. This style object contains {{complexity}} conditional expressions that are recalculated on every render.", "preferMemoConditionalSpread": "Conditional spread operators in style objects should be extracted to createMemo(). Pattern like `...(condition ? {...} : {})` creates new objects on every render." } }, { "id": "prefer-show", "severity": "warn", "description": "Enforce using Solid's `<Show />` component for conditionally showing content. Solid's compiler covers this case, so it's a stylistic rule only.", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "preferShowAnd": "Prefer Solid's `<Show when={...}>` component for conditional rendering. While Solid's compiler handles `&&` expressions, <Show> is more explicit and provides better readability for conditional content.", "preferShowTernary": "Prefer Solid's `<Show when={...} fallback={...}>` component for conditional rendering with a fallback. This provides clearer intent and better readability than ternary expressions." } }, { "id": "self-closing-comp", "severity": "warn", "description": "Disallow extra closing tags for components without children.", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "selfClose": "Empty elements should be self-closing. Use `<{{name}} />` instead of `<{{name}}></{{name}}>` for cleaner, more concise JSX.", "dontSelfClose": "This element should not be self-closing based on your configuration. Use `<{{name}}></{{name}}>` instead of `<{{name}} />` for explicit opening and closing tags." } }, { "id": "style-prop", "severity": "warn", "description": "Require CSS properties in the `style` prop to be valid and kebab-cased (ex. 'font-size'), not camel-cased (ex. 'fontSize') like in React, and that property values with dimensions are strings, not numbers with implicit 'px' units.", "fixable": true, "category": "solid", "plugin": "solid", "messages": { "kebabStyleProp": "Solid uses kebab-case for CSS property names, not camelCase like React. Use '{{kebabName}}' instead of '{{name}}'.", "invalidStyleProp": "'{{name}}' is not a valid CSS property. Check for typos, or if this is a custom property, prefix it with '--' (e.g., '--{{name}}').", "numericStyleValue": "Numeric values for dimensional properties need explicit units in Solid. Unlike React, Solid does not auto-append 'px'. Use '{{value}}px' or another appropriate unit.", "stringStyle": "Use an object for the style prop instead of a string for better approach and type safety. Example: style={{ '{{prop}}': '{{value}}' }}." } }]
42518
42740
  };
42519
42741
  function getRule2(id) {