@drskillissue/ganko 0.1.23 → 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,9 +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}}' is rendered inside a conditional mount point ({{mountTag}}) with a distant Suspense boundary. When the fetcher's Promise is pending, the SuspenseContext increment fires and unmounts the entire subtree. initialValue does NOT prevent this \u2014 it only prevents the accessor from returning undefined.",
12096
- 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 absorbs it and stays in its fallback state permanently. Wrap the component in <ErrorBoundary fallback={...}> or catch errors inside the fetcher."
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."
12097
12098
  };
12098
12099
  var options16 = {};
12099
12100
  function hasInitialValue(call) {
@@ -12179,7 +12180,8 @@ function analyzeComponentBoundaries(graph, componentName) {
12179
12180
  const result = {
12180
12181
  conditionalMountTag: null,
12181
12182
  suspenseDistance: 0,
12182
- lacksErrorBoundary: false
12183
+ lacksErrorBoundary: false,
12184
+ usagesLackingErrorBoundary: []
12183
12185
  };
12184
12186
  const usages = graph.jsxByTag.get(componentName) ?? [];
12185
12187
  if (usages.length === 0) return result;
@@ -12201,6 +12203,7 @@ function analyzeComponentBoundaries(graph, componentName) {
12201
12203
  foundSuspense = true;
12202
12204
  if (!foundErrorBoundary) {
12203
12205
  result.lacksErrorBoundary = true;
12206
+ result.usagesLackingErrorBoundary.push(usage);
12204
12207
  }
12205
12208
  if (conditionalTag !== null && componentLevels > 1) {
12206
12209
  result.conditionalMountTag = conditionalTag;
@@ -12215,6 +12218,7 @@ function analyzeComponentBoundaries(graph, componentName) {
12215
12218
  }
12216
12219
  if (!foundSuspense && !foundErrorBoundary) {
12217
12220
  result.lacksErrorBoundary = true;
12221
+ result.usagesLackingErrorBoundary.push(usage);
12218
12222
  if (conditionalTag !== null) {
12219
12223
  result.conditionalMountTag = conditionalTag;
12220
12224
  result.suspenseDistance = componentLevels;
@@ -12238,13 +12242,28 @@ function findResourceVariable(graph, name) {
12238
12242
  }
12239
12243
  return null;
12240
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
+ }
12241
12260
  var resourceImplicitSuspense = defineSolidRule({
12242
12261
  id: "resource-implicit-suspense",
12243
12262
  severity: "warn",
12244
12263
  messages: messages16,
12245
12264
  meta: {
12246
12265
  description: "Detect createResource that implicitly triggers or permanently breaks Suspense boundaries.",
12247
- fixable: false,
12266
+ fixable: true,
12248
12267
  category: "reactivity"
12249
12268
  },
12250
12269
  options: options16,
@@ -12299,6 +12318,10 @@ var resourceImplicitSuspense = defineSolidRule({
12299
12318
  if (analysis.lacksErrorBoundary) {
12300
12319
  const fetcherFn = resolveFetcherFunction(graph, call);
12301
12320
  if (fetcherFn && fetcherCanThrow(graph, fetcherFn, throwVisited)) {
12321
+ const errorBoundaryFix = buildErrorBoundaryFix(
12322
+ analysis.usagesLackingErrorBoundary,
12323
+ graph
12324
+ );
12302
12325
  emit(
12303
12326
  createDiagnostic(
12304
12327
  graph.file,
@@ -12306,7 +12329,8 @@ var resourceImplicitSuspense = defineSolidRule({
12306
12329
  "resource-implicit-suspense",
12307
12330
  "missingErrorBoundary",
12308
12331
  resolveMessage(messages16.missingErrorBoundary, { name: resourceName }),
12309
- "error"
12332
+ "error",
12333
+ errorBoundaryFix ?? void 0
12310
12334
  )
12311
12335
  );
12312
12336
  }
@@ -30243,6 +30267,11 @@ function toLayoutElementKey(solidFile, elementId) {
30243
30267
  return `${solidFile}::${elementId}`;
30244
30268
  }
30245
30269
 
30270
+ // src/cross-file/layout/context-model.ts
30271
+ function deriveAlignmentContext(base, overrides) {
30272
+ return { ...base, ...overrides };
30273
+ }
30274
+
30246
30275
  // src/cross-file/layout/signal-model.ts
30247
30276
  var layoutSignalNames = [
30248
30277
  "line-height",
@@ -30271,6 +30300,8 @@ var layoutSignalNames = [
30271
30300
  "justify-items",
30272
30301
  "place-items",
30273
30302
  "place-self",
30303
+ "flex-direction",
30304
+ "grid-auto-flow",
30274
30305
  "appearance",
30275
30306
  "box-sizing",
30276
30307
  "padding-top",
@@ -30389,13 +30420,44 @@ function expandShorthand(property, value2) {
30389
30420
  { name: blockTarget[1], value: parsed.end }
30390
30421
  ];
30391
30422
  }
30423
+ if (property === "flex-flow") {
30424
+ return expandFlexFlow(value2);
30425
+ }
30392
30426
  return void 0;
30393
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
+ }
30394
30455
  function getShorthandLonghandNames(property) {
30395
30456
  const quad = QUAD_EXPANSIONS.get(property);
30396
30457
  if (quad !== void 0) return [...quad];
30397
30458
  const block = BLOCK_EXPANSIONS.get(property);
30398
30459
  if (block !== void 0) return [...block];
30460
+ if (property === "flex-flow") return ["flex-direction", "flex-wrap"];
30399
30461
  return null;
30400
30462
  }
30401
30463
 
@@ -30406,27 +30468,90 @@ var CONTROL_ELEMENT_TAGS = /* @__PURE__ */ new Set([
30406
30468
  "textarea",
30407
30469
  "button"
30408
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+/;
30409
30481
  function clamp(value2, min, max) {
30410
30482
  if (value2 < min) return min;
30411
30483
  if (value2 > max) return max;
30412
30484
  return value2;
30413
30485
  }
30414
- function kindRank(kind) {
30415
- if (kind === "exact") return 0;
30416
- if (kind === "interval") return 1;
30417
- if (kind === "conditional") return 2;
30418
- return 3;
30419
- }
30420
30486
  function mergeEvidenceKind(left, right) {
30421
- if (kindRank(left) >= kindRank(right)) return left;
30422
- 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;
30423
30548
  }
30424
30549
  function toComparableExactValue(value2) {
30425
30550
  if (value2.value !== null) {
30426
- if (value2.kind !== "exact") return null;
30551
+ if (value2.kind !== 0 /* Exact */) return null;
30427
30552
  return value2.value;
30428
30553
  }
30429
- if (value2.kind === "exact") return 0;
30554
+ if (value2.kind === 0 /* Exact */) return 0;
30430
30555
  return null;
30431
30556
  }
30432
30557
 
@@ -30440,7 +30565,8 @@ var MONITORED_SHORTHAND_SET = /* @__PURE__ */ new Set([
30440
30565
  "border-width",
30441
30566
  "margin-block",
30442
30567
  "padding-block",
30443
- "inset-block"
30568
+ "inset-block",
30569
+ "flex-flow"
30444
30570
  ]);
30445
30571
  var LENGTH_SIGNAL_SET = /* @__PURE__ */ new Set([
30446
30572
  "font-size",
@@ -30482,13 +30608,22 @@ var KEYWORD_SIGNAL_SET = /* @__PURE__ */ new Set([
30482
30608
  "justify-items",
30483
30609
  "place-items",
30484
30610
  "place-self",
30611
+ "flex-direction",
30612
+ "grid-auto-flow",
30485
30613
  "appearance",
30486
30614
  "box-sizing",
30487
30615
  "position",
30488
30616
  "writing-mode",
30489
30617
  "direction"
30490
30618
  ]);
30491
- 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
+ ]);
30492
30627
  function isMonitoredSignal(property) {
30493
30628
  if (MONITORED_SIGNAL_SET.has(property)) return true;
30494
30629
  return MONITORED_SHORTHAND_SET.has(property);
@@ -30499,7 +30634,7 @@ function isControlTag(tag) {
30499
30634
  }
30500
30635
  function isReplacedTag(tag) {
30501
30636
  if (tag === null) return false;
30502
- return REPLACED_TAGS.has(tag.toLowerCase());
30637
+ return REPLACED_ELEMENT_TAGS.has(tag.toLowerCase());
30503
30638
  }
30504
30639
  function normalizeSignalMapWithCounts(values) {
30505
30640
  const out = /* @__PURE__ */ new Map();
@@ -30510,12 +30645,11 @@ function normalizeSignalMapWithCounts(values) {
30510
30645
  "font-size",
30511
30646
  fontSizeEntry.value,
30512
30647
  fontSizeEntry.source,
30513
- fontSizeEntry.guard,
30514
30648
  fontSizeEntry.guardProvenance,
30515
30649
  null
30516
30650
  );
30517
30651
  out.set("font-size", parsedFontSize);
30518
- if (parsedFontSize.kind === "known" && parsedFontSize.guard === "unconditional") {
30652
+ if (parsedFontSize.kind === "known" && parsedFontSize.guard.kind === 0 /* Unconditional */) {
30519
30653
  fontSizePx = parsedFontSize.px;
30520
30654
  }
30521
30655
  }
@@ -30531,7 +30665,6 @@ function normalizeSignalMapWithCounts(values) {
30531
30665
  name,
30532
30666
  declaration.value,
30533
30667
  declaration.source,
30534
- declaration.guard,
30535
30668
  declaration.guardProvenance,
30536
30669
  fontSizePx
30537
30670
  );
@@ -30541,7 +30674,7 @@ function normalizeSignalMapWithCounts(values) {
30541
30674
  let unknownSignalCount = 0;
30542
30675
  let conditionalSignalCount = 0;
30543
30676
  for (const value2 of out.values()) {
30544
- if (value2.guard === "conditional") {
30677
+ if (value2.guard.kind === 1 /* Conditional */) {
30545
30678
  conditionalSignalCount++;
30546
30679
  continue;
30547
30680
  }
@@ -30569,7 +30702,7 @@ function applyExpandedShorthand(out, property, declaration, fontSizePx) {
30569
30702
  if (!longhand) continue;
30570
30703
  const name = MONITORED_SIGNAL_NAME_MAP.get(longhand);
30571
30704
  if (name === void 0) continue;
30572
- 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));
30573
30706
  }
30574
30707
  return;
30575
30708
  }
@@ -30579,127 +30712,139 @@ function applyExpandedShorthand(out, property, declaration, fontSizePx) {
30579
30712
  if (!entry) continue;
30580
30713
  const name = MONITORED_SIGNAL_NAME_MAP.get(entry.name);
30581
30714
  if (name === void 0) continue;
30582
- 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));
30583
30716
  }
30584
30717
  }
30585
30718
  function toMonitoredSignalName(property) {
30586
30719
  return MONITORED_SIGNAL_NAME_MAP.get(property) ?? null;
30587
30720
  }
30588
- function normalizeSignal(name, raw, source, guard, guardProvenance, fontSizePx) {
30721
+ function normalizeSignal(name, raw, source, guard, fontSizePx) {
30589
30722
  switch (name) {
30590
30723
  case "line-height":
30591
- return parseLineHeight(name, raw, source, guard, guardProvenance, fontSizePx);
30724
+ return parseLineHeight(name, raw, source, guard, fontSizePx);
30592
30725
  case "aspect-ratio":
30593
- return parseAspectRatio(name, raw, source, guard, guardProvenance);
30726
+ return parseAspectRatio(name, raw, source, guard);
30594
30727
  case "contain-intrinsic-size":
30595
- return parseContainIntrinsicSize(name, raw, source, guard, guardProvenance);
30728
+ return parseContainIntrinsicSize(name, raw, source, guard);
30596
30729
  case "transform":
30597
- return parseTransform(name, raw, source, guard, guardProvenance);
30730
+ return parseTransform(name, raw, source, guard);
30598
30731
  case "translate":
30599
- return parseTranslateProperty(name, raw, source, guard, guardProvenance);
30732
+ return parseTranslateProperty(name, raw, source, guard);
30600
30733
  default:
30601
30734
  break;
30602
30735
  }
30603
- if (LENGTH_SIGNAL_SET.has(name)) return parseLength(name, raw, source, guard, guardProvenance);
30604
- if (KEYWORD_SIGNAL_SET.has(name)) return parseKeyword(name, raw, source, guard, guardProvenance);
30605
- 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");
30606
30739
  }
30607
- function parseAspectRatio(name, raw, source, guard, guardProvenance) {
30740
+ function parseAspectRatio(name, raw, source, guard) {
30608
30741
  const trimmed = raw.trim().toLowerCase();
30609
30742
  if (trimmed.length === 0) {
30610
- return createUnknown(name, raw, source, guard, guardProvenance, "aspect-ratio value is empty");
30743
+ return createUnknown(name, source, guard, "aspect-ratio value is empty");
30611
30744
  }
30612
30745
  if (hasDynamicExpression(trimmed)) {
30613
- 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");
30614
30747
  }
30615
30748
  if (trimmed === "auto") {
30616
- 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");
30617
30750
  }
30618
30751
  const slash = trimmed.indexOf("/");
30619
30752
  if (slash !== -1) {
30620
30753
  const left = Number(trimmed.slice(0, slash).trim());
30621
30754
  const right = Number(trimmed.slice(slash + 1).trim());
30622
30755
  if (!Number.isFinite(left) || !Number.isFinite(right) || left <= 0 || right <= 0) {
30623
- return createUnknown(name, raw, source, guard, guardProvenance, "aspect-ratio ratio is invalid");
30756
+ return createUnknown(name, source, guard, "aspect-ratio ratio is invalid");
30624
30757
  }
30625
- return createKnown(name, raw, source, guard, guardProvenance, null, "unitless", "exact");
30758
+ return createKnown(name, raw, source, guard, null, 1 /* Unitless */, "exact");
30626
30759
  }
30627
30760
  const ratio = Number(trimmed);
30628
30761
  if (!Number.isFinite(ratio) || ratio <= 0) {
30629
- 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");
30630
30763
  }
30631
- return createKnown(name, raw, source, guard, guardProvenance, null, "unitless", "exact");
30764
+ return createKnown(name, raw, source, guard, null, 1 /* Unitless */, "exact");
30632
30765
  }
30633
- function parseContainIntrinsicSize(name, raw, source, guard, guardProvenance) {
30766
+ function parseContainIntrinsicSize(name, raw, source, guard) {
30634
30767
  const trimmed = raw.trim().toLowerCase();
30635
30768
  if (trimmed.length === 0) {
30636
- 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");
30637
30770
  }
30638
30771
  if (hasDynamicExpression(trimmed)) {
30639
- 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");
30640
30773
  }
30641
30774
  if (trimmed === "none" || trimmed === "auto") {
30642
- 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");
30643
30776
  }
30644
30777
  const parts = splitWhitespaceTokens(trimmed);
30645
30778
  for (let i = 0; i < parts.length; i++) {
30646
30779
  const part = parts[i];
30647
30780
  if (!part) continue;
30648
30781
  const px = parseSignedPxValue(part);
30649
- 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");
30650
30783
  }
30651
- 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");
30652
30785
  }
30653
- function parseLineHeight(name, raw, source, guard, guardProvenance, fontSizePx) {
30786
+ function parseLineHeight(name, raw, source, guard, fontSizePx) {
30654
30787
  const unitless = parseUnitlessValue(raw);
30655
30788
  if (unitless !== null) {
30656
30789
  const base = fontSizePx === null ? 16 : fontSizePx;
30657
- return createKnown(name, raw, source, guard, guardProvenance, unitless * base, "unitless", "estimated");
30790
+ return createKnown(name, raw, source, guard, unitless * base, 1 /* Unitless */, "estimated");
30658
30791
  }
30659
30792
  const px = parseSignedPxValue(raw);
30660
- if (px !== null) return createKnown(name, raw, source, guard, guardProvenance, px, "px", "exact");
30661
- 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");
30662
30795
  }
30663
- 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) {
30664
30805
  const px = parseSignedPxValue(raw);
30665
- if (px === null) {
30666
- 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");
30667
30808
  }
30668
- 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");
30669
30814
  }
30670
- function parseKeyword(name, raw, source, guard, guardProvenance) {
30815
+ function parseKeyword(name, raw, source, guard) {
30671
30816
  const normalized = raw.trim().toLowerCase();
30672
30817
  if (normalized.length === 0) {
30673
- return createUnknown(name, raw, source, guard, guardProvenance, "keyword value is empty");
30818
+ return createUnknown(name, source, guard, "keyword value is empty");
30674
30819
  }
30675
30820
  if (hasDynamicExpression(normalized)) {
30676
- return createUnknown(name, raw, source, guard, guardProvenance, "keyword uses runtime-dependent function");
30821
+ return createUnknown(name, source, guard, "keyword uses runtime-dependent function");
30677
30822
  }
30678
- return createKnown(name, raw, source, guard, guardProvenance, null, "keyword", "exact");
30823
+ return createKnown(name, raw, source, guard, null, 2 /* Keyword */, "exact");
30679
30824
  }
30680
- function parseTransform(name, raw, source, guard, guardProvenance) {
30825
+ function parseTransform(name, raw, source, guard) {
30681
30826
  const normalized = raw.trim().toLowerCase();
30682
30827
  if (normalized.length === 0) {
30683
- return createUnknown(name, raw, source, guard, guardProvenance, "transform value is empty");
30828
+ return createUnknown(name, source, guard, "transform value is empty");
30684
30829
  }
30685
30830
  if (hasDynamicExpression(normalized)) {
30686
- return createUnknown(name, raw, source, guard, guardProvenance, "transform uses runtime-dependent function");
30831
+ return createUnknown(name, source, guard, "transform uses runtime-dependent function");
30687
30832
  }
30688
30833
  const y = extractTransformYPx(normalized);
30689
- if (y !== null) return createKnown(name, raw, source, guard, guardProvenance, y, "px", "exact");
30690
- 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");
30691
30836
  }
30692
- function parseTranslateProperty(name, raw, source, guard, guardProvenance) {
30837
+ function parseTranslateProperty(name, raw, source, guard) {
30693
30838
  const trimmed = raw.trim().toLowerCase();
30694
30839
  if (trimmed.length === 0) {
30695
- return createUnknown(name, raw, source, guard, guardProvenance, "translate value is empty");
30840
+ return createUnknown(name, source, guard, "translate value is empty");
30696
30841
  }
30697
30842
  if (hasDynamicExpression(trimmed)) {
30698
- return createUnknown(name, raw, source, guard, guardProvenance, "translate uses runtime-dependent function");
30843
+ return createUnknown(name, source, guard, "translate uses runtime-dependent function");
30699
30844
  }
30700
30845
  const y = extractTranslatePropertyYPx(trimmed);
30701
- if (y !== null) return createKnown(name, raw, source, guard, guardProvenance, y, "px", "exact");
30702
- 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");
30703
30848
  }
30704
30849
  function hasDynamicExpression(raw) {
30705
30850
  if (raw.includes("var(")) return true;
@@ -30711,28 +30856,24 @@ function hasDynamicExpression(raw) {
30711
30856
  if (raw.includes("clamp(")) return true;
30712
30857
  return false;
30713
30858
  }
30714
- function createKnown(name, raw, source, guard, guardProvenance, px, unit, quality) {
30859
+ function createKnown(name, raw, source, guard, px, unit, quality) {
30715
30860
  return {
30716
30861
  kind: "known",
30717
30862
  name,
30718
- raw,
30719
30863
  normalized: raw.trim().toLowerCase(),
30720
30864
  source,
30721
30865
  guard,
30722
- guardProvenance,
30723
30866
  unit,
30724
30867
  px,
30725
30868
  quality
30726
30869
  };
30727
30870
  }
30728
- function createUnknown(name, raw, source, guard, guardProvenance, reason) {
30871
+ function createUnknown(name, source, guard, reason) {
30729
30872
  return {
30730
30873
  kind: "unknown",
30731
30874
  name,
30732
- raw,
30733
30875
  source,
30734
30876
  guard,
30735
- guardProvenance,
30736
30877
  reason
30737
30878
  };
30738
30879
  }
@@ -30779,13 +30920,7 @@ function buildSnapshotForNode(node, cascadeByElementNode, snapshotByElementNode,
30779
30920
  const unknownSignalCount = normalized.unknownSignalCount + inherited.unknownDelta;
30780
30921
  const conditionalSignalCount = normalized.conditionalSignalCount + inherited.conditionalDelta;
30781
30922
  const snapshot = {
30782
- solidFile: node.solidFile,
30783
- elementId: node.elementId,
30784
- elementKey: node.key,
30785
- tag: node.tag,
30786
- textualContent: node.textualContent,
30787
- isControl: node.isControl,
30788
- isReplaced: node.isReplaced,
30923
+ node,
30789
30924
  signals: inherited.signals,
30790
30925
  knownSignalCount,
30791
30926
  unknownSignalCount,
@@ -30816,7 +30951,7 @@ function inheritSignalsFromParent(parentSnapshot, local) {
30816
30951
  if (!inheritedValue) continue;
30817
30952
  if (out === null) out = new Map(local);
30818
30953
  out.set(signal, inheritedValue);
30819
- if (inheritedValue.guard === "conditional") {
30954
+ if (inheritedValue.guard.kind === 1 /* Conditional */) {
30820
30955
  conditionalDelta++;
30821
30956
  continue;
30822
30957
  }
@@ -30860,7 +30995,7 @@ var EMPTY_LAYOUT_RESERVED_SPACE_FACT = Object.freeze({
30860
30995
  });
30861
30996
  var EMPTY_LAYOUT_SCROLL_CONTAINER_FACT = Object.freeze({
30862
30997
  isScrollContainer: false,
30863
- axis: "none",
30998
+ axis: 0 /* None */,
30864
30999
  overflow: null,
30865
31000
  overflowY: null,
30866
31001
  hasConditionalScroll: false,
@@ -30893,53 +31028,28 @@ function readKnownSignalWithGuard(snapshot, name) {
30893
31028
  return value2;
30894
31029
  }
30895
31030
  function toEvidenceKind(value2) {
30896
- if (value2.guard === "conditional") return "conditional";
30897
- if (value2.quality === "estimated") return "interval";
30898
- return "exact";
30899
- }
30900
- function readNumericSignalEvidence(snapshot, name) {
30901
- const value2 = snapshot.signals.get(name);
30902
- if (!value2) {
30903
- return {
30904
- value: null,
30905
- kind: "unknown"
30906
- };
30907
- }
30908
- if (value2.kind !== "known") {
30909
- if (value2.guard === "conditional") {
30910
- return {
30911
- value: null,
30912
- kind: "conditional"
30913
- };
30914
- }
30915
- return {
30916
- value: null,
30917
- kind: "unknown"
30918
- };
30919
- }
30920
- return {
30921
- value: value2.px,
30922
- kind: toEvidenceKind(value2)
30923
- };
31031
+ if (value2.guard.kind === 1 /* Conditional */) return 2 /* Conditional */;
31032
+ if (value2.quality === "estimated") return 1 /* Interval */;
31033
+ return 0 /* Exact */;
30924
31034
  }
30925
31035
  function readNormalizedSignalEvidence(snapshot, name) {
30926
31036
  const value2 = snapshot.signals.get(name);
30927
31037
  if (!value2) {
30928
31038
  return {
30929
31039
  value: null,
30930
- kind: "unknown"
31040
+ kind: 3 /* Unknown */
30931
31041
  };
30932
31042
  }
30933
31043
  if (value2.kind !== "known") {
30934
- if (value2.guard === "conditional") {
31044
+ if (value2.guard.kind === 1 /* Conditional */) {
30935
31045
  return {
30936
31046
  value: null,
30937
- kind: "conditional"
31047
+ kind: 2 /* Conditional */
30938
31048
  };
30939
31049
  }
30940
31050
  return {
30941
31051
  value: null,
30942
- kind: "unknown"
31052
+ kind: 3 /* Unknown */
30943
31053
  };
30944
31054
  }
30945
31055
  return {
@@ -30950,7 +31060,7 @@ function readNormalizedSignalEvidence(snapshot, name) {
30950
31060
  function readKnownSignal(snapshot, name) {
30951
31061
  const value2 = readKnownSignalWithGuard(snapshot, name);
30952
31062
  if (!value2) return null;
30953
- if (value2.guard !== "unconditional") return null;
31063
+ if (value2.guard.kind !== 0 /* Unconditional */) return null;
30954
31064
  return value2;
30955
31065
  }
30956
31066
  function readKnownPx(snapshot, name) {
@@ -30984,19 +31094,19 @@ function hasEffectivePosition(snapshot) {
30984
31094
  return position !== "static";
30985
31095
  }
30986
31096
  function readReservedSpaceFact(graph, node) {
30987
- return graph.reservedSpaceFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_RESERVED_SPACE_FACT;
31097
+ return graph.reservedSpaceFactsByNode.get(node) ?? EMPTY_LAYOUT_RESERVED_SPACE_FACT;
30988
31098
  }
30989
31099
  function readScrollContainerFact(graph, node) {
30990
- return graph.scrollContainerFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_SCROLL_CONTAINER_FACT;
31100
+ return graph.scrollContainerFactsByNode.get(node) ?? EMPTY_LAYOUT_SCROLL_CONTAINER_FACT;
30991
31101
  }
30992
31102
  function readFlowParticipationFact(graph, node) {
30993
- return graph.flowParticipationFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_FLOW_PARTICIPATION_FACT;
31103
+ return graph.flowParticipationFactsByNode.get(node) ?? EMPTY_LAYOUT_FLOW_PARTICIPATION_FACT;
30994
31104
  }
30995
31105
  function readContainingBlockFact(graph, node) {
30996
- return graph.containingBlockFactsByElementKey.get(node.key) ?? EMPTY_LAYOUT_CONTAINING_BLOCK_FACT;
31106
+ return graph.containingBlockFactsByNode.get(node) ?? EMPTY_LAYOUT_CONTAINING_BLOCK_FACT;
30997
31107
  }
30998
31108
  function readConditionalSignalDeltaFact(graph, node, name) {
30999
- const byProperty = graph.conditionalSignalDeltaFactsByElementKey.get(node.key);
31109
+ const byProperty = graph.conditionalSignalDeltaFactsByNode.get(node);
31000
31110
  if (!byProperty) return EMPTY_LAYOUT_CONDITIONAL_DELTA_FACT;
31001
31111
  return byProperty.get(name) ?? EMPTY_LAYOUT_CONDITIONAL_DELTA_FACT;
31002
31112
  }
@@ -31024,7 +31134,7 @@ function readScrollContainerElements(graph) {
31024
31134
  return graph.scrollContainerElements;
31025
31135
  }
31026
31136
  function readBaselineOffsetFacts(graph, node) {
31027
- return graph.baselineOffsetFactsByElementKey.get(node.key) ?? EMPTY_BASELINE_FACTS;
31137
+ return graph.baselineOffsetFactsByNode.get(node) ?? EMPTY_BASELINE_FACTS;
31028
31138
  }
31029
31139
  function readElementRef(graph, node) {
31030
31140
  return readElementRefById(graph, node.solidFile, node.elementId);
@@ -31046,7 +31156,6 @@ function readStatefulBaseValueIndex(graph) {
31046
31156
  }
31047
31157
 
31048
31158
  // src/cross-file/layout/context-classification.ts
31049
- var WHITESPACE_RE3 = /\s+/;
31050
31159
  var TABLE_SEMANTIC_TAGS = /* @__PURE__ */ new Set(["table", "thead", "tbody", "tfoot", "tr", "td", "th"]);
31051
31160
  var TABLE_DISPLAY_VALUES = /* @__PURE__ */ new Set([
31052
31161
  "table",
@@ -31063,7 +31172,6 @@ var TABLE_DISPLAY_VALUES = /* @__PURE__ */ new Set([
31063
31172
  var FLEX_DISPLAY_VALUES = /* @__PURE__ */ new Set(["flex", "inline-flex"]);
31064
31173
  var GRID_DISPLAY_VALUES = /* @__PURE__ */ new Set(["grid", "inline-grid"]);
31065
31174
  var INLINE_DISPLAY_VALUES = /* @__PURE__ */ new Set(["inline", "inline-block", "inline-list-item"]);
31066
- var DISPLAY_TOKEN_SPLIT_RE = /\s+/;
31067
31175
  function createAlignmentContextForParent(parent, snapshot) {
31068
31176
  const axis = resolveAxis(snapshot);
31069
31177
  const inlineDirection = resolveInlineDirection(snapshot);
@@ -31086,6 +31194,7 @@ function createAlignmentContextForParent(parent, snapshot) {
31086
31194
  const contextCertainty = combineCertainty(classified.certainty, axis.certainty);
31087
31195
  const certainty = combineCertainty(contextCertainty, inlineDirection.certainty);
31088
31196
  const baselineRelevance = computeBaselineRelevance(classified.kind, parentAlignItems, parentPlaceItems);
31197
+ const crossAxisInfo = resolveCrossAxisIsBlockAxis(classified.kind, snapshot, axis.value);
31089
31198
  const out = {
31090
31199
  kind: classified.kind,
31091
31200
  certainty,
@@ -31101,6 +31210,8 @@ function createAlignmentContextForParent(parent, snapshot) {
31101
31210
  parentAlignItems,
31102
31211
  parentPlaceItems,
31103
31212
  hasPositionedOffset: positionedOffset.hasPositionedOffset,
31213
+ crossAxisIsBlockAxis: crossAxisInfo.value,
31214
+ crossAxisIsBlockAxisCertainty: crossAxisInfo.certainty,
31104
31215
  baselineRelevance,
31105
31216
  evidence
31106
31217
  };
@@ -31110,7 +31221,7 @@ function classifyKind(evidence) {
31110
31221
  if (evidence.hasTableSemantics) {
31111
31222
  return {
31112
31223
  kind: "table-cell",
31113
- certainty: "resolved"
31224
+ certainty: 0 /* Resolved */
31114
31225
  };
31115
31226
  }
31116
31227
  if (evidence.containerKind === "table") {
@@ -31197,7 +31308,7 @@ function resolveContainerKind(parentDisplay, certainty) {
31197
31308
  certainty
31198
31309
  };
31199
31310
  }
31200
- const tokens = display.split(DISPLAY_TOKEN_SPLIT_RE);
31311
+ const tokens = display.split(WHITESPACE_RE3);
31201
31312
  if (tokens.length === 2) {
31202
31313
  const outside = tokens[0];
31203
31314
  const inside = tokens[1];
@@ -31247,7 +31358,7 @@ function resolveAxis(snapshot) {
31247
31358
  if (!snapshot.signals.has("writing-mode")) {
31248
31359
  return {
31249
31360
  value: "horizontal-tb",
31250
- certainty: "resolved"
31361
+ certainty: 0 /* Resolved */
31251
31362
  };
31252
31363
  }
31253
31364
  const writingMode = readNormalizedSignalEvidence(snapshot, "writing-mode");
@@ -31272,7 +31383,7 @@ function resolveInlineDirection(snapshot) {
31272
31383
  if (!snapshot.signals.has("direction")) {
31273
31384
  return {
31274
31385
  value: "ltr",
31275
- certainty: "resolved"
31386
+ certainty: 0 /* Resolved */
31276
31387
  };
31277
31388
  }
31278
31389
  const direction = readNormalizedSignalEvidence(snapshot, "direction");
@@ -31288,16 +31399,16 @@ function resolveInlineDirection(snapshot) {
31288
31399
  };
31289
31400
  }
31290
31401
  function toContextCertainty(kind) {
31291
- if (kind === "exact") return "resolved";
31292
- if (kind === "interval" || kind === "conditional") return "conditional";
31293
- 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 */;
31294
31405
  }
31295
31406
  function resolvePositionedOffset(snapshot) {
31296
31407
  const position = readKnownSignalWithGuard(snapshot, "position");
31297
31408
  if (!position) {
31298
31409
  return {
31299
31410
  hasPositionedOffset: false,
31300
- certainty: "unknown"
31411
+ certainty: 2 /* Unknown */
31301
31412
  };
31302
31413
  }
31303
31414
  const certainty = resolveSignalCertainty(position);
@@ -31312,15 +31423,33 @@ function resolvePositionedOffset(snapshot) {
31312
31423
  certainty
31313
31424
  };
31314
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
+ }
31315
31446
  function resolveSignalCertainty(value2) {
31316
- if (!value2) return "unknown";
31317
- if (value2.guard === "conditional") return "conditional";
31318
- return "resolved";
31447
+ if (!value2) return 2 /* Unknown */;
31448
+ if (value2.guard.kind === 1 /* Conditional */) return 1 /* Conditional */;
31449
+ return 0 /* Resolved */;
31319
31450
  }
31320
31451
  function combineCertainty(left, right) {
31321
- if (left === "unknown" || right === "unknown") return "unknown";
31322
- if (left === "conditional" || right === "conditional") return "conditional";
31323
- return "resolved";
31452
+ return left > right ? left : right;
31324
31453
  }
31325
31454
  var FLEX_GRID_GEOMETRIC_ALIGN_ITEMS = /* @__PURE__ */ new Set([
31326
31455
  "center",
@@ -31359,24 +31488,7 @@ function finalizeTableCellBaselineRelevance(contextByParentNode, cohortVerticalA
31359
31488
  if (context.kind !== "table-cell") continue;
31360
31489
  if (consensusValue === null) continue;
31361
31490
  if (!TABLE_CELL_GEOMETRIC_VERTICAL_ALIGN.has(consensusValue)) continue;
31362
- contextByParentNode.set(parent, {
31363
- kind: context.kind,
31364
- certainty: context.certainty,
31365
- parentSolidFile: context.parentSolidFile,
31366
- parentElementId: context.parentElementId,
31367
- parentElementKey: context.parentElementKey,
31368
- parentTag: context.parentTag,
31369
- axis: context.axis,
31370
- axisCertainty: context.axisCertainty,
31371
- inlineDirection: context.inlineDirection,
31372
- inlineDirectionCertainty: context.inlineDirectionCertainty,
31373
- parentDisplay: context.parentDisplay,
31374
- parentAlignItems: context.parentAlignItems,
31375
- parentPlaceItems: context.parentPlaceItems,
31376
- hasPositionedOffset: context.hasPositionedOffset,
31377
- baselineRelevance: "irrelevant",
31378
- evidence: context.evidence
31379
- });
31491
+ contextByParentNode.set(parent, deriveAlignmentContext(context, { baselineRelevance: "irrelevant" }));
31380
31492
  }
31381
31493
  }
31382
31494
 
@@ -31923,6 +32035,20 @@ function collectTransitiveCSSScope(entryPath, resolver, cssFilesByNormalizedPath
31923
32035
  }
31924
32036
 
31925
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
+ }
31926
32052
  var EMPTY_STATS = {
31927
32053
  elementsScanned: 0,
31928
32054
  selectorCandidatesChecked: 0,
@@ -31979,7 +32105,7 @@ function createLayoutPerfStats() {
31979
32105
  cohortUnimodalFalse: 0,
31980
32106
  factorCoverageSum: 0,
31981
32107
  factorCoverageCount: 0,
31982
- posteriorWidths: [],
32108
+ posteriorWidths: createReservoir(200),
31983
32109
  uncertaintyEscalations: 0,
31984
32110
  signalSnapshotsBuilt: 0,
31985
32111
  signalSnapshotCacheHits: 0,
@@ -32044,14 +32170,12 @@ function maybeLogLayoutPerf(stats, log) {
32044
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}`
32045
32171
  );
32046
32172
  }
32047
- function computeP95(values) {
32048
- if (values.length === 0) return 0;
32049
- const sorted = [...values];
32050
- sorted.sort((left, right) => left - right);
32051
- const index = Math.ceil(sorted.length * 0.95) - 1;
32052
- if (index <= 0) return sorted[0] ?? 0;
32053
- if (index >= sorted.length) return sorted[sorted.length - 1] ?? 0;
32054
- 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);
32055
32179
  }
32056
32180
 
32057
32181
  // src/cross-file/layout/component-host.ts
@@ -34021,16 +34145,16 @@ function includesAttributeWord(value2, word) {
34021
34145
 
34022
34146
  // src/cross-file/layout/guard-model.ts
34023
34147
  var UNCONDITIONAL_GUARD = {
34024
- kind: "unconditional",
34148
+ kind: 0 /* Unconditional */,
34025
34149
  conditions: [],
34026
34150
  key: "always"
34027
34151
  };
34028
- var WHITESPACE_RE4 = /\s+/g;
34152
+ var WHITESPACE_RE_GLOBAL = /\s+/g;
34029
34153
  function resolveRuleGuard(rule) {
34030
34154
  const conditions = collectRuleConditions(rule);
34031
34155
  if (conditions.length === 0) return UNCONDITIONAL_GUARD;
34032
34156
  return {
34033
- kind: "conditional",
34157
+ kind: 1 /* Conditional */,
34034
34158
  conditions,
34035
34159
  key: conditions.map((condition) => condition.key).join("&")
34036
34160
  };
@@ -34084,7 +34208,7 @@ function buildCondition(kind, query) {
34084
34208
  }
34085
34209
  function normalizeQuery(query) {
34086
34210
  if (query === null) return null;
34087
- const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE4, " ");
34211
+ const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE_GLOBAL, " ");
34088
34212
  if (normalized.length === 0) return null;
34089
34213
  return normalized;
34090
34214
  }
@@ -34154,7 +34278,7 @@ function summarizeSignalFacts(snapshots) {
34154
34278
  }
34155
34279
  function accumulateSnapshotFacts(snapshot, sink) {
34156
34280
  for (const value2 of snapshot.signals.values()) {
34157
- if (value2.guard === "conditional") {
34281
+ if (value2.guard.kind === 1 /* Conditional */) {
34158
34282
  sink.addConditional();
34159
34283
  continue;
34160
34284
  }
@@ -34348,15 +34472,6 @@ function assertUnitInterval(name, value2) {
34348
34472
  }
34349
34473
 
34350
34474
  // src/cross-file/layout/content-composition.ts
34351
- var INTRINSIC_REPLACED_TAGS = /* @__PURE__ */ new Set([
34352
- "img",
34353
- "svg",
34354
- "video",
34355
- "canvas",
34356
- "iframe",
34357
- "object",
34358
- "embed"
34359
- ]);
34360
34475
  var BLOCK_FORMATTING_CONTEXT_DISPLAYS = /* @__PURE__ */ new Set([
34361
34476
  "block",
34362
34477
  "flex",
@@ -34390,7 +34505,7 @@ var VERTICAL_ALIGN_MITIGATIONS = /* @__PURE__ */ new Set([
34390
34505
  "text-top",
34391
34506
  "text-bottom"
34392
34507
  ]);
34393
- function computeContentCompositionFingerprint(elementNode, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey) {
34508
+ function computeContentCompositionFingerprint(elementNode, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode) {
34394
34509
  const state = {
34395
34510
  hasTextContent: false,
34396
34511
  hasInlineReplaced: false,
@@ -34404,10 +34519,10 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34404
34519
  blockChildCount: 0,
34405
34520
  inlineChildCount: 0
34406
34521
  };
34407
- if (elementNode.textualContent === "yes" || elementNode.textualContent === "dynamic-text") {
34522
+ if (elementNode.textualContent === 0 /* Yes */ || elementNode.textualContent === 3 /* DynamicText */) {
34408
34523
  state.hasTextContent = true;
34409
34524
  }
34410
- const elementHotSignals = snapshotHotSignalsByElementKey.get(elementNode.key);
34525
+ const elementHotSignals = snapshotHotSignalsByNode.get(elementNode);
34411
34526
  const elementDisplay = elementHotSignals?.display.value ?? null;
34412
34527
  if (elementDisplay !== null && establishesFormattingContext(elementDisplay)) {
34413
34528
  return {
@@ -34418,7 +34533,7 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34418
34533
  wrappingContextMitigates: false,
34419
34534
  hasVerticalAlignMitigation: false,
34420
34535
  mixedContentDepth: 0,
34421
- classification: "block-segmented",
34536
+ classification: 4 /* BlockSegmented */,
34422
34537
  analyzableChildCount: 0,
34423
34538
  totalChildCount: 0,
34424
34539
  hasOnlyBlockChildren: false
@@ -34428,7 +34543,7 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34428
34543
  elementNode,
34429
34544
  childrenByParentNode,
34430
34545
  snapshotByElementNode,
34431
- snapshotHotSignalsByElementKey,
34546
+ snapshotHotSignalsByNode,
34432
34547
  state,
34433
34548
  0
34434
34549
  );
@@ -34448,7 +34563,7 @@ function computeContentCompositionFingerprint(elementNode, childrenByParentNode,
34448
34563
  hasOnlyBlockChildren
34449
34564
  };
34450
34565
  }
34451
- function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, state, depth) {
34566
+ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, state, depth) {
34452
34567
  const children = childrenByParentNode.get(node);
34453
34568
  if (!children) return;
34454
34569
  for (let i = 0; i < children.length; i++) {
@@ -34459,7 +34574,7 @@ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode
34459
34574
  if (!snapshot) continue;
34460
34575
  if (depth === 0) state.analyzableChildCount++;
34461
34576
  const childTag = child.tagName?.toLowerCase() ?? null;
34462
- const hotSignals = snapshotHotSignalsByElementKey.get(child.key);
34577
+ const hotSignals = snapshotHotSignalsByNode.get(child);
34463
34578
  const childDisplay = hotSignals?.display.value ?? null;
34464
34579
  if (childTag !== null && (isIntrinsicReplacedTag(childTag) || isControlReplacedTag(childTag))) {
34465
34580
  state.hasInlineReplaced = true;
@@ -34481,16 +34596,16 @@ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode
34481
34596
  checkVerticalAlignMitigation(snapshot, state);
34482
34597
  updateMixedContentDepth(state, depth);
34483
34598
  if (depth === 0) state.inlineChildCount++;
34484
- const parentHotSignals = snapshotHotSignalsByElementKey.get(node.key);
34599
+ const parentHotSignals = snapshotHotSignalsByNode.get(node);
34485
34600
  const parentDisplay = parentHotSignals?.display.value ?? null;
34486
34601
  if (parentDisplay !== null && isAlignmentContextWithNonBaselineAlignment(parentDisplay, parentHotSignals)) {
34487
34602
  state.wrappingContextMitigates = true;
34488
- } else if (isAlignmentContextWithNonBaselineAlignment(childDisplay, hotSignals) && containsMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey)) {
34603
+ } else if (isAlignmentContextWithNonBaselineAlignment(childDisplay, hotSignals) && containsMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode)) {
34489
34604
  state.wrappingContextMitigates = true;
34490
34605
  }
34491
34606
  continue;
34492
34607
  }
34493
- if (child.textualContent === "yes" || child.textualContent === "dynamic-text") {
34608
+ if (child.textualContent === 0 /* Yes */ || child.textualContent === 3 /* DynamicText */) {
34494
34609
  state.hasTextContent = true;
34495
34610
  }
34496
34611
  checkHeightContributions(snapshot, state);
@@ -34500,7 +34615,7 @@ function walkInlineDescendants(node, childrenByParentNode, snapshotByElementNode
34500
34615
  child,
34501
34616
  childrenByParentNode,
34502
34617
  snapshotByElementNode,
34503
- snapshotHotSignalsByElementKey,
34618
+ snapshotHotSignalsByNode,
34504
34619
  state,
34505
34620
  depth + 1
34506
34621
  );
@@ -34535,19 +34650,19 @@ function isAlignmentContextWithNonBaselineAlignment(display, hotSignals) {
34535
34650
  if (alignItems === null) return false;
34536
34651
  return alignItems !== "baseline";
34537
34652
  }
34538
- function containsMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey) {
34539
- 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 */;
34540
34655
  const hasReplaced = false;
34541
- return scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, { hasText, hasReplaced });
34656
+ return scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, { hasText, hasReplaced });
34542
34657
  }
34543
- function scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, found) {
34658
+ function scanMixedContent(node, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, found) {
34544
34659
  const children = childrenByParentNode.get(node);
34545
34660
  if (!children) return false;
34546
34661
  for (let i = 0; i < children.length; i++) {
34547
34662
  const child = children[i];
34548
34663
  if (!child) continue;
34549
34664
  const childTag = child.tagName?.toLowerCase() ?? null;
34550
- const hotSignals = snapshotHotSignalsByElementKey.get(child.key);
34665
+ const hotSignals = snapshotHotSignalsByNode.get(child);
34551
34666
  const childDisplay = hotSignals?.display.value ?? null;
34552
34667
  if (childTag !== null && (isIntrinsicReplacedTag(childTag) || isControlReplacedTag(childTag))) {
34553
34668
  found.hasReplaced = true;
@@ -34562,12 +34677,12 @@ function scanMixedContent(node, childrenByParentNode, snapshotByElementNode, sna
34562
34677
  if (found.hasText) return true;
34563
34678
  continue;
34564
34679
  }
34565
- if (child.textualContent === "yes" || child.textualContent === "dynamic-text") {
34680
+ if (child.textualContent === 0 /* Yes */ || child.textualContent === 3 /* DynamicText */) {
34566
34681
  found.hasText = true;
34567
34682
  if (found.hasReplaced) return true;
34568
34683
  }
34569
34684
  if (childDisplay === null || isInlineContinuationDisplay(childDisplay)) {
34570
- if (scanMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByElementKey, found)) {
34685
+ if (scanMixedContent(child, childrenByParentNode, snapshotByElementNode, snapshotHotSignalsByNode, found)) {
34571
34686
  return true;
34572
34687
  }
34573
34688
  }
@@ -34590,30 +34705,30 @@ function updateMixedContentDepth(state, depth) {
34590
34705
  }
34591
34706
  function classifyFromState(state, elementNode, hasOnlyBlockChildren) {
34592
34707
  if (hasOnlyBlockChildren) {
34593
- return "block-segmented";
34708
+ return 4 /* BlockSegmented */;
34594
34709
  }
34595
34710
  if (state.totalChildCount === 0 && !state.hasTextContent) {
34596
- if (elementNode.textualContent === "unknown") return "unknown";
34597
- if (elementNode.textualContent === "yes" || elementNode.textualContent === "dynamic-text") {
34598
- 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 */;
34599
34714
  }
34600
- return "unknown";
34715
+ return 5 /* Unknown */;
34601
34716
  }
34602
34717
  if (state.analyzableChildCount === 0 && state.totalChildCount > 0) {
34603
- return "unknown";
34718
+ return 5 /* Unknown */;
34604
34719
  }
34605
34720
  if (state.hasTextContent && state.hasInlineReplaced) {
34606
- if (state.wrappingContextMitigates) return "mixed-mitigated";
34607
- if (state.hasVerticalAlignMitigation) return "mixed-mitigated";
34608
- return "mixed-unmitigated";
34721
+ if (state.wrappingContextMitigates) return 3 /* MixedMitigated */;
34722
+ if (state.hasVerticalAlignMitigation) return 3 /* MixedMitigated */;
34723
+ return 2 /* MixedUnmitigated */;
34609
34724
  }
34610
34725
  if (!state.hasTextContent && state.hasInlineReplaced) {
34611
- return "replaced-only";
34726
+ return 1 /* ReplacedOnly */;
34612
34727
  }
34613
34728
  if (state.hasTextContent && !state.hasInlineReplaced) {
34614
- return "text-only";
34729
+ return 0 /* TextOnly */;
34615
34730
  }
34616
- return "unknown";
34731
+ return 5 /* Unknown */;
34617
34732
  }
34618
34733
  function isIntrinsicReplacedTag(tag) {
34619
34734
  return INTRINSIC_REPLACED_TAGS.has(tag);
@@ -34633,10 +34748,14 @@ function isInlineReplacedDisplay(display) {
34633
34748
  function isInlineContinuationDisplay(display) {
34634
34749
  return INLINE_CONTINUATION_DISPLAYS.has(display);
34635
34750
  }
34636
- function resolveCompositionDivergenceStrength(subjectFingerprint, allFingerprints, parentContext) {
34637
- 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;
34638
34757
  if (parentContext !== null && !hasSharedBaselineAlignment(parentContext)) {
34639
- return 0;
34758
+ return NO_DIVERGENCE;
34640
34759
  }
34641
34760
  const countByClassification = /* @__PURE__ */ new Map();
34642
34761
  for (let i = 0; i < allFingerprints.length; i++) {
@@ -34648,10 +34767,7 @@ function resolveCompositionDivergenceStrength(subjectFingerprint, allFingerprint
34648
34767
  }
34649
34768
  const subjectNormalized = normalizeClassificationForComparison(subjectFingerprint.classification);
34650
34769
  const subjectCount = countByClassification.get(subjectNormalized) ?? 0;
34651
- if (subjectCount === allFingerprints.length) {
34652
- return resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints);
34653
- }
34654
- let majorityClassification = "unknown";
34770
+ let majorityClassification = 5 /* Unknown */;
34655
34771
  let majorityCount = 0;
34656
34772
  for (const [classification, count] of countByClassification) {
34657
34773
  if (count > majorityCount) {
@@ -34659,35 +34775,38 @@ function resolveCompositionDivergenceStrength(subjectFingerprint, allFingerprint
34659
34775
  majorityClassification = classification;
34660
34776
  }
34661
34777
  }
34778
+ if (subjectCount === allFingerprints.length) {
34779
+ return { strength: resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints), majorityClassification };
34780
+ }
34662
34781
  if (subjectNormalized === majorityClassification) {
34663
- return resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints);
34782
+ return { strength: resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints), majorityClassification };
34664
34783
  }
34665
- if (subjectNormalized === "unknown") {
34666
- return 0;
34784
+ if (subjectNormalized === 5 /* Unknown */) {
34785
+ return { strength: 0, majorityClassification };
34667
34786
  }
34668
34787
  const cal = alignmentStrengthCalibration;
34669
- if (majorityClassification === "text-only" && subjectNormalized === "mixed-unmitigated") {
34670
- return cal.compositionMixedUnmitigatedOutlierStrength;
34788
+ if (majorityClassification === 0 /* TextOnly */ && subjectNormalized === 2 /* MixedUnmitigated */) {
34789
+ return { strength: cal.compositionMixedUnmitigatedOutlierStrength, majorityClassification };
34671
34790
  }
34672
- if (majorityClassification === "replaced-only" && subjectNormalized === "mixed-unmitigated") {
34673
- return cal.compositionMixedOutlierAmongReplacedStrength;
34791
+ if (majorityClassification === 1 /* ReplacedOnly */ && subjectNormalized === 2 /* MixedUnmitigated */) {
34792
+ return { strength: cal.compositionMixedOutlierAmongReplacedStrength, majorityClassification };
34674
34793
  }
34675
- if (majorityClassification === "mixed-unmitigated" && subjectNormalized === "text-only") {
34676
- return cal.compositionTextOutlierAmongMixedStrength;
34794
+ if (majorityClassification === 2 /* MixedUnmitigated */ && subjectNormalized === 0 /* TextOnly */) {
34795
+ return { strength: cal.compositionTextOutlierAmongMixedStrength, majorityClassification };
34677
34796
  }
34678
- if (majorityClassification === "mixed-unmitigated" && subjectNormalized === "replaced-only") {
34679
- return cal.compositionTextOutlierAmongMixedStrength;
34797
+ if (majorityClassification === 2 /* MixedUnmitigated */ && subjectNormalized === 1 /* ReplacedOnly */) {
34798
+ return { strength: cal.compositionTextOutlierAmongMixedStrength, majorityClassification };
34680
34799
  }
34681
- if (majorityClassification === "text-only" && subjectNormalized === "replaced-only") {
34682
- return cal.compositionMixedOutlierAmongReplacedStrength;
34800
+ if (majorityClassification === 0 /* TextOnly */ && subjectNormalized === 1 /* ReplacedOnly */) {
34801
+ return { strength: cal.compositionMixedOutlierAmongReplacedStrength, majorityClassification };
34683
34802
  }
34684
- if (majorityClassification === "replaced-only" && subjectNormalized === "text-only") {
34685
- return cal.compositionTextOutlierAmongMixedStrength;
34803
+ if (majorityClassification === 1 /* ReplacedOnly */ && subjectNormalized === 0 /* TextOnly */) {
34804
+ return { strength: cal.compositionTextOutlierAmongMixedStrength, majorityClassification };
34686
34805
  }
34687
- if (majorityClassification === "unknown") {
34688
- return 0;
34806
+ if (majorityClassification === 5 /* Unknown */) {
34807
+ return { strength: 0, majorityClassification };
34689
34808
  }
34690
- return cal.compositionUnknownPenalty;
34809
+ return { strength: cal.compositionUnknownPenalty, majorityClassification };
34691
34810
  }
34692
34811
  function resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints) {
34693
34812
  if (subjectFingerprint.inlineReplacedKind === null) return 0;
@@ -34708,28 +34827,9 @@ function resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints
34708
34827
  function hasSharedBaselineAlignment(context) {
34709
34828
  return context.baselineRelevance === "relevant";
34710
34829
  }
34711
- function resolveMajorityClassification(allFingerprints) {
34712
- const countByClassification = /* @__PURE__ */ new Map();
34713
- for (let i = 0; i < allFingerprints.length; i++) {
34714
- const fp = allFingerprints[i];
34715
- if (!fp) continue;
34716
- const normalized = normalizeClassificationForComparison(fp.classification);
34717
- const existing = countByClassification.get(normalized) ?? 0;
34718
- countByClassification.set(normalized, existing + 1);
34719
- }
34720
- let majorityClassification = "unknown";
34721
- let majorityCount = 0;
34722
- for (const [classification, count] of countByClassification) {
34723
- if (count > majorityCount) {
34724
- majorityCount = count;
34725
- majorityClassification = classification;
34726
- }
34727
- }
34728
- return majorityClassification;
34729
- }
34730
34830
  function normalizeClassificationForComparison(classification) {
34731
- if (classification === "mixed-mitigated") return "text-only";
34732
- if (classification === "block-segmented") return "text-only";
34831
+ if (classification === 3 /* MixedMitigated */) return 0 /* TextOnly */;
34832
+ if (classification === 4 /* BlockSegmented */) return 0 /* TextOnly */;
34733
34833
  return classification;
34734
34834
  }
34735
34835
  function resolveCompositionCoverage(subjectFingerprint, allFingerprints) {
@@ -34737,12 +34837,12 @@ function resolveCompositionCoverage(subjectFingerprint, allFingerprints) {
34737
34837
  let analyzableCount = 0;
34738
34838
  for (let i = 0; i < allFingerprints.length; i++) {
34739
34839
  const fp = allFingerprints[i];
34740
- if (fp && fp.classification !== "unknown") {
34840
+ if (fp && fp.classification !== 5 /* Unknown */) {
34741
34841
  analyzableCount++;
34742
34842
  }
34743
34843
  }
34744
34844
  const analyzableShare = analyzableCount / allFingerprints.length;
34745
- if (subjectFingerprint.classification === "unknown") {
34845
+ if (subjectFingerprint.classification === 5 /* Unknown */) {
34746
34846
  return analyzableShare * 0.3;
34747
34847
  }
34748
34848
  if (subjectFingerprint.totalChildCount > 0 && subjectFingerprint.analyzableChildCount === 0) {
@@ -34750,24 +34850,19 @@ function resolveCompositionCoverage(subjectFingerprint, allFingerprints) {
34750
34850
  }
34751
34851
  return analyzableShare;
34752
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
+ };
34753
34861
  function formatCompositionClassification(classification) {
34754
- switch (classification) {
34755
- case "text-only":
34756
- return "text-only";
34757
- case "replaced-only":
34758
- return "inline-replaced-only";
34759
- case "mixed-unmitigated":
34760
- return "mixed text + inline-replaced";
34761
- case "mixed-mitigated":
34762
- return "mixed (alignment mitigated)";
34763
- case "block-segmented":
34764
- return "block-segmented";
34765
- case "unknown":
34766
- return "unknown";
34767
- }
34862
+ return COMPOSITION_LABELS[classification];
34768
34863
  }
34769
34864
  function formatCompositionFixSuggestion(subjectFingerprint) {
34770
- if (subjectFingerprint.classification === "mixed-unmitigated") {
34865
+ if (subjectFingerprint.classification === 2 /* MixedUnmitigated */) {
34771
34866
  if (subjectFingerprint.hasVerticalAlignMitigation) {
34772
34867
  return "verify vertical-align resolves the baseline shift";
34773
34868
  }
@@ -34802,12 +34897,12 @@ function estimateBlockOffsetWithDeclaredFromHotSignals(hot, axis) {
34802
34897
  function estimateBlockOffsetWithDeclaredFromSources(axis, position, readNumeric) {
34803
34898
  let declaredTotal = 0;
34804
34899
  let declaredCount = 0;
34805
- let declaredKind = "exact";
34806
- let declaredMissingKind = "exact";
34900
+ let declaredKind = 0 /* Exact */;
34901
+ let declaredMissingKind = 0 /* Exact */;
34807
34902
  let effectiveTotal = 0;
34808
34903
  let effectiveCount = 0;
34809
- let effectiveKind = "exact";
34810
- let effectiveMissingKind = "exact";
34904
+ let effectiveKind = 0 /* Exact */;
34905
+ let effectiveMissingKind = 0 /* Exact */;
34811
34906
  const positioned = position.value !== null && position.value !== "static";
34812
34907
  const add = (name, sign, requiresPositioning) => {
34813
34908
  const v = readNumeric(name);
@@ -34878,7 +34973,7 @@ function buildCohortIndex(input) {
34878
34973
  axisCertainty: context.axisCertainty,
34879
34974
  measurementNodeByRootKey: input.measurementNodeByRootKey,
34880
34975
  snapshotByElementNode: input.snapshotByElementNode,
34881
- snapshotHotSignalsByElementKey: input.snapshotHotSignalsByElementKey
34976
+ snapshotHotSignalsByNode: input.snapshotHotSignalsByNode
34882
34977
  });
34883
34978
  measurementIndexHits += cohortMetricsResult.measurementHits;
34884
34979
  const metrics = cohortMetricsResult.metrics;
@@ -34912,7 +35007,7 @@ function buildCohortIndex(input) {
34912
35007
  subjectMetrics.rootNode,
34913
35008
  input.childrenByParentNode,
34914
35009
  input.snapshotByElementNode,
34915
- input.snapshotHotSignalsByElementKey
35010
+ input.snapshotHotSignalsByNode
34916
35011
  );
34917
35012
  subjectsByElementKey.set(subjectMetrics.key, {
34918
35013
  element: subjectMetrics.element,
@@ -34978,7 +35073,7 @@ function collectCohortMetrics(input) {
34978
35073
  if (!snapshot) {
34979
35074
  throw new Error(`missing snapshot for measurement node ${measurementNode.key}`);
34980
35075
  }
34981
- const hotSignals = input.snapshotHotSignalsByElementKey.get(measurementNode.key);
35076
+ const hotSignals = input.snapshotHotSignalsByNode.get(measurementNode);
34982
35077
  if (!hotSignals) {
34983
35078
  throw new Error(`missing hot signals for measurement node ${measurementNode.key}`);
34984
35079
  }
@@ -35303,9 +35398,9 @@ function buildCohortSignalIndex(metrics) {
35303
35398
  const verticalAlignCounts = /* @__PURE__ */ new Map();
35304
35399
  const alignSelfCounts = /* @__PURE__ */ new Map();
35305
35400
  const placeSelfCounts = /* @__PURE__ */ new Map();
35306
- let verticalAlignMergedKind = "exact";
35307
- let alignSelfMergedKind = "exact";
35308
- let placeSelfMergedKind = "exact";
35401
+ let verticalAlignMergedKind = 0 /* Exact */;
35402
+ let alignSelfMergedKind = 0 /* Exact */;
35403
+ let placeSelfMergedKind = 0 /* Exact */;
35309
35404
  let verticalAlignComparableCount = 0;
35310
35405
  let alignSelfComparableCount = 0;
35311
35406
  let placeSelfComparableCount = 0;
@@ -35319,12 +35414,12 @@ function buildCohortSignalIndex(metrics) {
35319
35414
  const snapshot = metric.element.snapshot;
35320
35415
  const alignSelf = metric.hotSignals.alignSelf;
35321
35416
  const placeSelf = metric.hotSignals.placeSelf;
35322
- const isControlOrReplaced = snapshot.isControl || snapshot.isReplaced;
35417
+ const isControlOrReplaced = snapshot.node.isControl || snapshot.node.isReplaced;
35323
35418
  const verticalAlign = resolveComparableVerticalAlign(metric.hotSignals.verticalAlign, isControlOrReplaced);
35324
35419
  if (isControlOrReplaced) controlOrReplacedCount++;
35325
- if (snapshot.textualContent === "yes" || snapshot.textualContent === "dynamic-text") textYesCount++;
35326
- if (snapshot.textualContent === "no") textNoCount++;
35327
- 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++;
35328
35423
  if (verticalAlign.value !== null) {
35329
35424
  verticalAlignMergedKind = mergeEvidenceKind(verticalAlignMergedKind, verticalAlign.kind);
35330
35425
  verticalAlignComparableCount++;
@@ -35344,24 +35439,24 @@ function buildCohortSignalIndex(metrics) {
35344
35439
  verticalAlign,
35345
35440
  alignSelf,
35346
35441
  placeSelf,
35347
- textualContent: snapshot.textualContent,
35442
+ textualContent: snapshot.node.textualContent,
35348
35443
  isControlOrReplaced
35349
35444
  });
35350
35445
  }
35351
35446
  return {
35352
35447
  byKey,
35353
35448
  verticalAlign: {
35354
- mergedKind: verticalAlignComparableCount === 0 ? "unknown" : verticalAlignMergedKind,
35449
+ mergedKind: verticalAlignComparableCount === 0 ? 3 /* Unknown */ : verticalAlignMergedKind,
35355
35450
  comparableCount: verticalAlignComparableCount,
35356
35451
  countsByValue: verticalAlignCounts
35357
35452
  },
35358
35453
  alignSelf: {
35359
- mergedKind: alignSelfComparableCount === 0 ? "unknown" : alignSelfMergedKind,
35454
+ mergedKind: alignSelfComparableCount === 0 ? 3 /* Unknown */ : alignSelfMergedKind,
35360
35455
  comparableCount: alignSelfComparableCount,
35361
35456
  countsByValue: alignSelfCounts
35362
35457
  },
35363
35458
  placeSelf: {
35364
- mergedKind: placeSelfComparableCount === 0 ? "unknown" : placeSelfMergedKind,
35459
+ mergedKind: placeSelfComparableCount === 0 ? 3 /* Unknown */ : placeSelfMergedKind,
35365
35460
  comparableCount: placeSelfComparableCount,
35366
35461
  countsByValue: placeSelfCounts
35367
35462
  },
@@ -35398,9 +35493,9 @@ function collectSubjectCohortSignals(index, subjectMetrics, context) {
35398
35493
  sawComparableVerticalAlign,
35399
35494
  sawVerticalAlignConflict
35400
35495
  );
35401
- 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;
35402
35497
  const normalizedVerticalAlign = tableCellControlFallback ? {
35403
- value: "conflict",
35498
+ value: 0 /* Conflict */,
35404
35499
  kind: verticalAlignKind
35405
35500
  } : verticalAlign;
35406
35501
  const textContrastWithPeers = resolveIndexedTextContrastWithPeers(
@@ -35433,7 +35528,7 @@ function resolveComparableVerticalAlign(verticalAlign, isControlOrReplaced) {
35433
35528
  return {
35434
35529
  present: verticalAlign.present,
35435
35530
  value: "baseline",
35436
- kind: "exact"
35531
+ kind: 0 /* Exact */
35437
35532
  };
35438
35533
  }
35439
35534
  function hasComparablePeer(aggregate, subjectValue) {
@@ -35450,37 +35545,37 @@ function hasConflictPeer(aggregate, subjectValue) {
35450
35545
  function finalizeConflictEvidence(subjectValue, kind, sawComparablePeer, sawConflict) {
35451
35546
  if (subjectValue === null) {
35452
35547
  return {
35453
- value: "unknown",
35548
+ value: 2 /* Unknown */,
35454
35549
  kind
35455
35550
  };
35456
35551
  }
35457
35552
  if (!sawComparablePeer) {
35458
35553
  return {
35459
- value: "unknown",
35554
+ value: 2 /* Unknown */,
35460
35555
  kind
35461
35556
  };
35462
35557
  }
35463
35558
  return {
35464
- value: sawConflict ? "conflict" : "aligned",
35559
+ value: sawConflict ? 0 /* Conflict */ : 1 /* Aligned */,
35465
35560
  kind
35466
35561
  };
35467
35562
  }
35468
35563
  function resolveIndexedTextContrastWithPeers(index, subjectTextualContent, subjectIsControlOrReplaced, tableCellControlFallback) {
35469
- if (subjectTextualContent === "unknown") return "unknown";
35564
+ if (subjectTextualContent === 2 /* Unknown */) return 2 /* Unknown */;
35470
35565
  const unknownPeers = index.textUnknownCount;
35471
35566
  const cohortSize = index.byKey.size;
35472
- if (subjectTextualContent === "yes" || subjectTextualContent === "dynamic-text") {
35473
- if (index.textNoCount > 0) return "different";
35474
- if (unknownPeers > 0) return "unknown";
35475
- 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 */;
35476
35571
  }
35477
- if (index.textYesCount > 0) return "different";
35478
- if (tableCellControlFallback) return "different";
35572
+ if (index.textYesCount > 0) return 0 /* Different */;
35573
+ if (tableCellControlFallback) return 0 /* Different */;
35479
35574
  if (subjectIsControlOrReplaced && index.controlOrReplacedCount === 1 && cohortSize >= 3 && unknownPeers > 0) {
35480
- return "different";
35575
+ return 0 /* Different */;
35481
35576
  }
35482
- if (unknownPeers > 0) return "unknown";
35483
- return "same";
35577
+ if (unknownPeers > 0) return 2 /* Unknown */;
35578
+ return 1 /* Same */;
35484
35579
  }
35485
35580
  function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineProfile, clusterSummary, signalIndex, cohortKind, cohortSize) {
35486
35581
  const subjectClusterKey = toComparableClusterKey(subjectMetrics);
@@ -35496,7 +35591,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35496
35591
  return {
35497
35592
  dominantShare: profile.dominantClusterShare,
35498
35593
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35499
- subjectMembership: "insufficient",
35594
+ subjectMembership: 3 /* Insufficient */,
35500
35595
  ambiguous: true,
35501
35596
  kind
35502
35597
  };
@@ -35505,7 +35600,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35505
35600
  return {
35506
35601
  dominantShare: profile.dominantClusterShare,
35507
35602
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35508
- subjectMembership: "dominant",
35603
+ subjectMembership: 0 /* Dominant */,
35509
35604
  ambiguous: false,
35510
35605
  kind
35511
35606
  };
@@ -35514,7 +35609,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35514
35609
  return {
35515
35610
  dominantShare: profile.dominantClusterShare,
35516
35611
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35517
- subjectMembership: "insufficient",
35612
+ subjectMembership: 3 /* Insufficient */,
35518
35613
  ambiguous: true,
35519
35614
  kind
35520
35615
  };
@@ -35524,7 +35619,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35524
35619
  return {
35525
35620
  dominantShare: profile.dominantClusterShare,
35526
35621
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35527
- subjectMembership: "insufficient",
35622
+ subjectMembership: 3 /* Insufficient */,
35528
35623
  ambiguous: true,
35529
35624
  kind
35530
35625
  };
@@ -35534,7 +35629,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35534
35629
  return {
35535
35630
  dominantShare: profile.dominantClusterShare,
35536
35631
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35537
- subjectMembership: "ambiguous",
35632
+ subjectMembership: 2 /* Ambiguous */,
35538
35633
  ambiguous: true,
35539
35634
  kind
35540
35635
  };
@@ -35543,7 +35638,7 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35543
35638
  return {
35544
35639
  dominantShare: profile.dominantClusterShare,
35545
35640
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35546
- subjectMembership: "dominant",
35641
+ subjectMembership: 0 /* Dominant */,
35547
35642
  ambiguous: false,
35548
35643
  kind
35549
35644
  };
@@ -35551,13 +35646,13 @@ function resolveSubjectIdentifiability(subjectMetrics, profile, subjectBaselineP
35551
35646
  return {
35552
35647
  dominantShare: profile.dominantClusterShare,
35553
35648
  subjectExcludedDominantShare: subjectBaselineProfile.dominantClusterShare,
35554
- subjectMembership: "nondominant",
35649
+ subjectMembership: 1 /* Nondominant */,
35555
35650
  ambiguous: false,
35556
35651
  kind
35557
35652
  };
35558
35653
  }
35559
35654
  function resolveControlRoleIdentifiability(subjectMetrics, signalIndex, kind, cohortSize) {
35560
- const subjectIsControlOrReplaced = subjectMetrics.element.snapshot.isControl || subjectMetrics.element.snapshot.isReplaced;
35655
+ const subjectIsControlOrReplaced = subjectMetrics.element.snapshot.node.isControl || subjectMetrics.element.snapshot.node.isReplaced;
35561
35656
  const controlCount = signalIndex.controlOrReplacedCount;
35562
35657
  const nonControlCount = cohortSize - controlCount;
35563
35658
  if (controlCount <= 0 || nonControlCount <= 0) return null;
@@ -35572,12 +35667,12 @@ function resolveControlRoleIdentifiability(subjectMetrics, signalIndex, kind, co
35572
35667
  return {
35573
35668
  dominantShare,
35574
35669
  subjectExcludedDominantShare: excludedDominantShare,
35575
- subjectMembership: subjectMembership === "ambiguous" ? "dominant" : subjectMembership,
35670
+ subjectMembership: subjectMembership === 2 /* Ambiguous */ ? 0 /* Dominant */ : subjectMembership,
35576
35671
  ambiguous: false,
35577
35672
  kind
35578
35673
  };
35579
35674
  }
35580
- if (subjectMembership === "ambiguous") {
35675
+ if (subjectMembership === 2 /* Ambiguous */) {
35581
35676
  return {
35582
35677
  dominantShare,
35583
35678
  subjectExcludedDominantShare: excludedDominantShare,
@@ -35595,9 +35690,9 @@ function resolveControlRoleIdentifiability(subjectMetrics, signalIndex, kind, co
35595
35690
  };
35596
35691
  }
35597
35692
  function resolveRoleMembership(controlCount, nonControlCount, subjectIsControlOrReplaced) {
35598
- if (controlCount === nonControlCount) return "ambiguous";
35693
+ if (controlCount === nonControlCount) return 2 /* Ambiguous */;
35599
35694
  const dominantRoleIsControl = controlCount > nonControlCount;
35600
- return dominantRoleIsControl === subjectIsControlOrReplaced ? "dominant" : "nondominant";
35695
+ return dominantRoleIsControl === subjectIsControlOrReplaced ? 0 /* Dominant */ : 1 /* Nondominant */;
35601
35696
  }
35602
35697
  function resolveExcludedRoleDominantShare(controlCount, nonControlCount, subjectIsControlOrReplaced) {
35603
35698
  const controlAfterExclusion = controlCount - (subjectIsControlOrReplaced ? 1 : 0);
@@ -35635,8 +35730,8 @@ function collectCohortProvenanceFromSnapshots(snapshots) {
35635
35730
  const snapshot = snapshots[i];
35636
35731
  if (!snapshot) continue;
35637
35732
  for (const signal of snapshot.signals.values()) {
35638
- for (let j = 0; j < signal.guardProvenance.conditions.length; j++) {
35639
- const guard = signal.guardProvenance.conditions[j];
35733
+ for (let j = 0; j < signal.guard.conditions.length; j++) {
35734
+ const guard = signal.guard.conditions[j];
35640
35735
  if (!guard) continue;
35641
35736
  if (!byKey.has(guard.key)) byKey.set(guard.key, guard);
35642
35737
  }
@@ -35666,7 +35761,7 @@ function buildGuardKey(guards) {
35666
35761
  return keys.join("&");
35667
35762
  }
35668
35763
  function resolveCohortEvidenceKind(metrics) {
35669
- let kind = "exact";
35764
+ let kind = 0 /* Exact */;
35670
35765
  for (let i = 0; i < metrics.length; i++) {
35671
35766
  const metric = metrics[i];
35672
35767
  if (!metric) continue;
@@ -35683,9 +35778,9 @@ function incrementCount(counts, key) {
35683
35778
  counts.set(key, existing + 1);
35684
35779
  }
35685
35780
  function toEvidenceKind2(certainty) {
35686
- if (certainty === "resolved") return "exact";
35687
- if (certainty === "conditional") return "conditional";
35688
- return "unknown";
35781
+ if (certainty === 0 /* Resolved */) return 0 /* Exact */;
35782
+ if (certainty === 1 /* Conditional */) return 2 /* Conditional */;
35783
+ return 3 /* Unknown */;
35689
35784
  }
35690
35785
  function computeMedian(values) {
35691
35786
  if (values.length === 0) return null;
@@ -35705,66 +35800,6 @@ function computeMedianAbsoluteDeviation(values, median, scratch) {
35705
35800
  }
35706
35801
  return computeMedian(scratch);
35707
35802
  }
35708
- function selectKth(values, targetIndex) {
35709
- let left = 0;
35710
- let right = values.length - 1;
35711
- while (left <= right) {
35712
- if (left === right) {
35713
- const result = values[left];
35714
- if (result === void 0) return 0;
35715
- return result;
35716
- }
35717
- const pivotIndex = choosePivotIndex(values, left, right);
35718
- const partitionIndex = partitionAroundPivot(values, left, right, pivotIndex);
35719
- if (partitionIndex === targetIndex) {
35720
- const result = values[partitionIndex];
35721
- if (result === void 0) return 0;
35722
- return result;
35723
- }
35724
- if (partitionIndex < targetIndex) {
35725
- left = partitionIndex + 1;
35726
- continue;
35727
- }
35728
- right = partitionIndex - 1;
35729
- }
35730
- const fallback = values[targetIndex];
35731
- if (fallback === void 0) return 0;
35732
- return fallback;
35733
- }
35734
- function choosePivotIndex(values, left, right) {
35735
- const middle = Math.floor((left + right) / 2);
35736
- const leftValue = values[left] ?? 0;
35737
- const middleValue = values[middle] ?? 0;
35738
- const rightValue = values[right] ?? 0;
35739
- if (leftValue < middleValue) {
35740
- if (middleValue < rightValue) return middle;
35741
- if (leftValue < rightValue) return right;
35742
- return left;
35743
- }
35744
- if (leftValue < rightValue) return left;
35745
- if (middleValue < rightValue) return right;
35746
- return middle;
35747
- }
35748
- function partitionAroundPivot(values, left, right, pivotIndex) {
35749
- const pivotValue = values[pivotIndex] ?? 0;
35750
- swap(values, pivotIndex, right);
35751
- let storeIndex = left;
35752
- for (let i = left; i < right; i++) {
35753
- const current = values[i];
35754
- if (current === void 0 || current > pivotValue) continue;
35755
- swap(values, storeIndex, i);
35756
- storeIndex++;
35757
- }
35758
- swap(values, storeIndex, right);
35759
- return storeIndex;
35760
- }
35761
- function swap(values, left, right) {
35762
- if (left === right) return;
35763
- const leftValue = values[left] ?? 0;
35764
- const rightValue = values[right] ?? 0;
35765
- values[left] = rightValue;
35766
- values[right] = leftValue;
35767
- }
35768
35803
  function resolveVerticalAlignConsensus(aggregate) {
35769
35804
  if (aggregate.comparableCount === 0) return null;
35770
35805
  if (aggregate.countsByValue.size !== 1) return null;
@@ -35863,7 +35898,7 @@ function resolveMeasurementCandidates(root, childrenByParentNode, snapshotByElem
35863
35898
  if (firstControlOrReplacedDescendant === null && (child.isControl || child.isReplaced)) {
35864
35899
  firstControlOrReplacedDescendant = child;
35865
35900
  }
35866
- if (firstTextualDescendant === null && child.textualContent === "yes") {
35901
+ if (firstTextualDescendant === null && child.textualContent === 0 /* Yes */) {
35867
35902
  firstTextualDescendant = child;
35868
35903
  }
35869
35904
  if (firstControlOrReplacedDescendant !== null && firstTextualDescendant !== null) break;
@@ -35889,7 +35924,7 @@ function resolveMeasurementNode(root, candidates) {
35889
35924
  if (candidates.firstControlOrReplacedDescendant !== null) return candidates.firstControlOrReplacedDescendant;
35890
35925
  if (root.isControl || root.isReplaced) return root;
35891
35926
  if (candidates.firstTextualDescendant !== null) return candidates.firstTextualDescendant;
35892
- if (root.textualContent === "yes") return root;
35927
+ if (root.textualContent === 0 /* Yes */) return root;
35893
35928
  return root;
35894
35929
  }
35895
35930
 
@@ -35936,7 +35971,7 @@ function buildScopedSelectorIndexBySolidFile(cssScopeBySolidFile, css, selectorM
35936
35971
  seenSelectorIds.add(selector.id);
35937
35972
  const metadata = selectorMetadataById.get(selector.id);
35938
35973
  if (!metadata) continue;
35939
- if (metadata.guard.kind === "conditional") {
35974
+ if (metadata.guard.kind === 1 /* Conditional */) {
35940
35975
  if (!conditionalSelectorIds.has(selector.id)) {
35941
35976
  conditionalSelectorIds.add(selector.id);
35942
35977
  perf.selectorsGuardedConditional++;
@@ -35978,7 +36013,7 @@ function buildScopedSelectorIndexBySolidFile(cssScopeBySolidFile, css, selectorM
35978
36013
  }
35979
36014
  return out;
35980
36015
  }
35981
- function buildSelectorCandidatesByElementKey(elements, scopedSelectorsBySolidFile, perf) {
36016
+ function buildSelectorCandidatesByNode(elements, scopedSelectorsBySolidFile, perf) {
35982
36017
  const out = /* @__PURE__ */ new Map();
35983
36018
  for (let i = 0; i < elements.length; i++) {
35984
36019
  const node = elements[i];
@@ -35988,7 +36023,7 @@ function buildSelectorCandidatesByElementKey(elements, scopedSelectorsBySolidFil
35988
36023
  const byTag = node.tagName !== null ? collectSelectorCandidates(scoped.bySubjectTag.get(node.tagName), node.selectorDispatchKeys) : EMPTY_SELECTOR_LIST;
35989
36024
  const withoutTag = collectSelectorCandidates(scoped.withoutSubjectTag, node.selectorDispatchKeys);
35990
36025
  const merged = mergeSelectorCandidateIds(byTag, withoutTag);
35991
- out.set(node.key, merged);
36026
+ out.set(node, merged);
35992
36027
  perf.elementsScanned++;
35993
36028
  perf.selectorCandidatesChecked += merged.length;
35994
36029
  }
@@ -36447,12 +36482,6 @@ var EMPTY_EXPANSION_RESULT = [];
36447
36482
  function collectMonitoredDeclarations(selector, layerOrder, guard) {
36448
36483
  const out = [];
36449
36484
  const declarations = selector.rule.declarations;
36450
- const signalGuard = guard.kind === "conditional" ? "conditional" : "unconditional";
36451
- const guardProvenance = {
36452
- kind: signalGuard,
36453
- conditions: guard.conditions,
36454
- key: guard.key
36455
- };
36456
36485
  for (let i = 0; i < declarations.length; i++) {
36457
36486
  const declaration = declarations[i];
36458
36487
  if (!declaration) continue;
@@ -36463,8 +36492,7 @@ function collectMonitoredDeclarations(selector, layerOrder, guard) {
36463
36492
  out.push({
36464
36493
  property: monitored,
36465
36494
  value: declaration.value,
36466
- guard: signalGuard,
36467
- guardProvenance,
36495
+ guardProvenance: guard,
36468
36496
  position: {
36469
36497
  layer: declaration.cascadePosition.layer,
36470
36498
  layerOrder,
@@ -36486,6 +36514,7 @@ function toMonitoredSignalKey(property) {
36486
36514
  case "margin-block":
36487
36515
  case "padding-block":
36488
36516
  case "inset-block":
36517
+ case "flex-flow":
36489
36518
  return property;
36490
36519
  default:
36491
36520
  return null;
@@ -36509,30 +36538,27 @@ function expandMonitoredDeclarationForDelta(declaration) {
36509
36538
  if (signalName === void 0) return EMPTY_EXPANSION_RESULT;
36510
36539
  return [{ name: signalName, value: value2 }];
36511
36540
  }
36512
- function appendMatchingEdgesFromSelectorIds(selectorIds, node, selectorMetadataById, selectorsById, applies, appliesByElementNodeMutable, perf, rootElementsByFile, logger) {
36513
- const fileRoots = rootElementsByFile.get(node.solidFile) ?? null;
36541
+ function appendMatchingEdgesFromSelectorIds(ctx, selectorIds, node, applies, appliesByElementNodeMutable) {
36542
+ const fileRoots = ctx.rootElementsByFile.get(node.solidFile) ?? null;
36514
36543
  for (let i = 0; i < selectorIds.length; i++) {
36515
36544
  const selectorId = selectorIds[i];
36516
36545
  if (selectorId === void 0) continue;
36517
- const metadata = selectorMetadataById.get(selectorId);
36546
+ const metadata = ctx.selectorMetadataById.get(selectorId);
36518
36547
  if (!metadata || !metadata.matcher) {
36519
36548
  throw new Error(`missing compiled selector matcher for selector ${selectorId}`);
36520
36549
  }
36521
- const selector = selectorsById.get(selectorId);
36550
+ const selector = ctx.selectorsById.get(selectorId);
36522
36551
  if (!selector) {
36523
36552
  throw new Error(`missing selector ${selectorId}`);
36524
36553
  }
36525
- if (!selectorMatchesLayoutElement(metadata.matcher, node, perf, fileRoots, logger)) continue;
36554
+ if (!selectorMatchesLayoutElement(metadata.matcher, node, ctx.perf, fileRoots, ctx.logger)) continue;
36526
36555
  const edge = {
36527
- solidFile: node.solidFile,
36528
- elementId: node.elementId,
36529
- elementKey: node.key,
36530
36556
  selectorId: selector.id,
36531
36557
  specificityScore: selector.specificityScore,
36532
36558
  sourceOrder: selector.rule.sourceOrder
36533
36559
  };
36534
36560
  applies.push(edge);
36535
- perf.matchEdgesCreated++;
36561
+ ctx.perf.matchEdgesCreated++;
36536
36562
  const existing = appliesByElementNodeMutable.get(node);
36537
36563
  if (existing) {
36538
36564
  existing.push(edge);
@@ -36558,7 +36584,7 @@ function augmentCascadeWithTailwind(cascade, node, tailwind) {
36558
36584
  const classTokens = node.classTokens;
36559
36585
  if (classTokens.length === 0) return;
36560
36586
  const guardProvenance = {
36561
- kind: "unconditional",
36587
+ kind: 0 /* Unconditional */,
36562
36588
  conditions: [],
36563
36589
  key: "always"
36564
36590
  };
@@ -36575,8 +36601,7 @@ function augmentCascadeWithTailwind(cascade, node, tailwind) {
36575
36601
  if (cascade.has(property)) continue;
36576
36602
  cascade.set(property, {
36577
36603
  value: value2,
36578
- source: "selector",
36579
- guard: "unconditional",
36604
+ source: 0 /* Selector */,
36580
36605
  guardProvenance
36581
36606
  });
36582
36607
  }
@@ -36596,8 +36621,7 @@ function buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorI
36596
36621
  const property = declaration.property;
36597
36622
  const newDeclaration = {
36598
36623
  value: declaration.value,
36599
- source: "selector",
36600
- guard: declaration.guard,
36624
+ source: 0 /* Selector */,
36601
36625
  guardProvenance: declaration.guardProvenance
36602
36626
  };
36603
36627
  const existingPosition = positions.get(property);
@@ -36616,33 +36640,26 @@ function buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorI
36616
36640
  positions.set(property, declaration.position);
36617
36641
  }
36618
36642
  }
36619
- const inlinePosition = createInlineCascadePosition();
36620
- const inlineGuardProvenance = {
36621
- kind: "unconditional",
36622
- conditions: [],
36623
- key: "always"
36624
- };
36625
36643
  for (const [property, value2] of node.inlineStyleValues) {
36626
36644
  const newDeclaration = {
36627
36645
  value: value2,
36628
- source: "inline-style",
36629
- guard: "unconditional",
36630
- guardProvenance: inlineGuardProvenance
36646
+ source: 1 /* InlineStyle */,
36647
+ guardProvenance: INLINE_GUARD_PROVENANCE
36631
36648
  };
36632
36649
  const existingPosition = positions.get(property);
36633
36650
  if (existingPosition === void 0) {
36634
36651
  out.set(property, newDeclaration);
36635
- positions.set(property, inlinePosition);
36652
+ positions.set(property, INLINE_CASCADE_POSITION);
36636
36653
  continue;
36637
36654
  }
36638
36655
  const existingDeclaration = out.get(property);
36639
36656
  if (existingDeclaration === void 0) continue;
36640
36657
  if (!doesCandidateOverride(
36641
36658
  { declaration: existingDeclaration, position: existingPosition },
36642
- { declaration: newDeclaration, position: inlinePosition }
36659
+ { declaration: newDeclaration, position: INLINE_CASCADE_POSITION }
36643
36660
  )) continue;
36644
36661
  out.set(property, newDeclaration);
36645
- positions.set(property, inlinePosition);
36662
+ positions.set(property, INLINE_CASCADE_POSITION);
36646
36663
  }
36647
36664
  if (tailwind !== null) {
36648
36665
  augmentCascadeWithTailwind(out, node, tailwind);
@@ -36659,7 +36676,7 @@ function doesCandidateOverride(existing, incoming) {
36659
36676
  const existingSource = existing.declaration.source;
36660
36677
  const incomingSource = incoming.declaration.source;
36661
36678
  if (existingSource !== incomingSource) {
36662
- if (incomingSource === "inline-style") {
36679
+ if (incomingSource === 1 /* InlineStyle */) {
36663
36680
  if (existing.position.isImportant && !incoming.position.isImportant) return false;
36664
36681
  return true;
36665
36682
  }
@@ -36667,16 +36684,19 @@ function doesCandidateOverride(existing, incoming) {
36667
36684
  }
36668
36685
  return compareCascadePositions(incoming.position, existing.position) > 0;
36669
36686
  }
36670
- function createInlineCascadePosition() {
36671
- return {
36672
- layer: null,
36673
- layerOrder: Number.MAX_SAFE_INTEGER,
36674
- sourceOrder: Number.MAX_SAFE_INTEGER,
36675
- specificity: [1, 0, 0, 0],
36676
- specificityScore: Number.MAX_SAFE_INTEGER,
36677
- isImportant: false
36678
- };
36679
- }
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
+ });
36680
36700
  function resolveRuleLayerOrder(rule, css) {
36681
36701
  const layer = rule.containingLayer;
36682
36702
  if (!layer) return 0;
@@ -36684,14 +36704,14 @@ function resolveRuleLayerOrder(rule, css) {
36684
36704
  if (!name) return 0;
36685
36705
  return css.layerOrder.get(name) ?? 0;
36686
36706
  }
36687
- function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDeclarationsBySelectorId) {
36688
- const conditionalSignalDeltaFactsByElementKey = /* @__PURE__ */ new Map();
36707
+ function buildConditionalDeltaIndex(elements, appliesByNode, monitoredDeclarationsBySelectorId) {
36708
+ const conditionalSignalDeltaFactsByNode = /* @__PURE__ */ new Map();
36689
36709
  const elementsWithConditionalDeltaBySignal = /* @__PURE__ */ new Map();
36690
- const baselineOffsetFactsByElementKey = /* @__PURE__ */ new Map();
36710
+ const baselineOffsetFactsByNode = /* @__PURE__ */ new Map();
36691
36711
  for (let i = 0; i < elements.length; i++) {
36692
36712
  const node = elements[i];
36693
36713
  if (!node) continue;
36694
- const edges = appliesByElementKey.get(node.key);
36714
+ const edges = appliesByNode.get(node);
36695
36715
  let factByProperty = null;
36696
36716
  if (edges !== void 0 && edges.length > 0) {
36697
36717
  const byProperty = /* @__PURE__ */ new Map();
@@ -36716,7 +36736,7 @@ function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDecl
36716
36736
  };
36717
36737
  byProperty.set(property, bucket);
36718
36738
  }
36719
- if (declaration.guard === "conditional") {
36739
+ if (declaration.guardProvenance.kind === 1 /* Conditional */) {
36720
36740
  bucket.conditional.add(expandedEntry.value);
36721
36741
  continue;
36722
36742
  }
@@ -36756,7 +36776,7 @@ function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDecl
36756
36776
  }
36757
36777
  if (facts.size > 0) {
36758
36778
  factByProperty = facts;
36759
- conditionalSignalDeltaFactsByElementKey.set(node.key, facts);
36779
+ conditionalSignalDeltaFactsByNode.set(node, facts);
36760
36780
  for (const [signal, fact] of facts) {
36761
36781
  if (!fact.hasConditional) continue;
36762
36782
  const existing = elementsWithConditionalDeltaBySignal.get(signal);
@@ -36793,13 +36813,13 @@ function buildConditionalDeltaIndex(elements, appliesByElementKey, monitoredDecl
36793
36813
  baselineBySignal.set(signal, [...values]);
36794
36814
  }
36795
36815
  if (baselineBySignal.size > 0) {
36796
- baselineOffsetFactsByElementKey.set(node.key, baselineBySignal);
36816
+ baselineOffsetFactsByNode.set(node, baselineBySignal);
36797
36817
  }
36798
36818
  }
36799
36819
  return {
36800
- conditionalSignalDeltaFactsByElementKey,
36820
+ conditionalSignalDeltaFactsByNode,
36801
36821
  elementsWithConditionalDeltaBySignal,
36802
- baselineOffsetFactsByElementKey
36822
+ baselineOffsetFactsByNode
36803
36823
  };
36804
36824
  }
36805
36825
  var EMPTY_NODE_LIST2 = [];
@@ -36926,8 +36946,8 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
36926
36946
  if (child.kind === "expression") {
36927
36947
  if (isStructuralExpression(child.node)) {
36928
36948
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 unknown (structural expression child)`);
36929
- memo.set(element.id, "unknown");
36930
- return "unknown";
36949
+ memo.set(element.id, 2 /* Unknown */);
36950
+ return 2 /* Unknown */;
36931
36951
  }
36932
36952
  hasTextOnlyExpression = true;
36933
36953
  continue;
@@ -36935,8 +36955,8 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
36935
36955
  if (child.kind !== "text") continue;
36936
36956
  if (child.node.type !== "JSXText") continue;
36937
36957
  if (isBlank(child.node.value)) continue;
36938
- memo.set(element.id, "yes");
36939
- return "yes";
36958
+ memo.set(element.id, 0 /* Yes */);
36959
+ return 0 /* Yes */;
36940
36960
  }
36941
36961
  let childHasUnknown = false;
36942
36962
  let childHasDynamicText = false;
@@ -36950,31 +36970,31 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
36950
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`);
36951
36971
  continue;
36952
36972
  }
36953
- if (childState !== "no") {
36973
+ if (childState !== 1 /* No */) {
36954
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`);
36955
36975
  childHasUnknown = true;
36956
36976
  }
36957
36977
  continue;
36958
36978
  }
36959
- if (childState === "yes") {
36960
- memo.set(element.id, "yes");
36961
- return "yes";
36979
+ if (childState === 0 /* Yes */) {
36980
+ memo.set(element.id, 0 /* Yes */);
36981
+ return 0 /* Yes */;
36962
36982
  }
36963
- if (childState === "unknown") childHasUnknown = true;
36964
- if (childState === "dynamic-text") childHasDynamicText = true;
36983
+ if (childState === 2 /* Unknown */) childHasUnknown = true;
36984
+ if (childState === 3 /* DynamicText */) childHasDynamicText = true;
36965
36985
  }
36966
36986
  if (childHasUnknown) {
36967
36987
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 unknown (child has unknown)`);
36968
- memo.set(element.id, "unknown");
36969
- return "unknown";
36988
+ memo.set(element.id, 2 /* Unknown */);
36989
+ return 2 /* Unknown */;
36970
36990
  }
36971
36991
  if (hasTextOnlyExpression || childHasDynamicText) {
36972
36992
  if (logger.enabled) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 dynamic-text`);
36973
- memo.set(element.id, "dynamic-text");
36974
- return "dynamic-text";
36993
+ memo.set(element.id, 3 /* DynamicText */);
36994
+ return 3 /* DynamicText */;
36975
36995
  }
36976
- memo.set(element.id, "no");
36977
- return "no";
36996
+ memo.set(element.id, 1 /* No */);
36997
+ return 1 /* No */;
36978
36998
  }
36979
36999
  function isStructuralExpression(node) {
36980
37000
  if (node.type !== "JSXExpressionContainer") return false;
@@ -37273,8 +37293,6 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37273
37293
  classTokens: record.classTokens,
37274
37294
  classTokenSet: record.classTokenSet,
37275
37295
  inlineStyleKeys: record.inlineStyleKeys,
37276
- parentElementId,
37277
- parentElementKey: parentNode ? parentNode.key : parentElementId === null ? null : toLayoutElementKey(solid.file, parentElementId),
37278
37296
  parentElementNode: parentNode,
37279
37297
  previousSiblingNode,
37280
37298
  siblingIndex,
@@ -37315,22 +37333,25 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37315
37333
  logger.debug(`[build] rootElementsByFile file=${file} count=${roots.length}: ${descs.join(", ")}`);
37316
37334
  }
37317
37335
  }
37318
- 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
+ };
37319
37344
  for (let i = 0; i < elements.length; i++) {
37320
37345
  const node = elements[i];
37321
37346
  if (!node) continue;
37322
- const selectorIds = selectorCandidatesByElementKey.get(node.key) ?? EMPTY_NUMBER_LIST2;
37347
+ const selectorIds = selectorCandidatesByNode.get(node) ?? EMPTY_NUMBER_LIST2;
37323
37348
  if (selectorIds.length === 0) continue;
37324
37349
  appendMatchingEdgesFromSelectorIds(
37350
+ selectorMatchCtx,
37325
37351
  selectorIds,
37326
37352
  node,
37327
- selectorMetadataById,
37328
- selectorsById,
37329
37353
  applies,
37330
- appliesByElementNodeMutable,
37331
- perf,
37332
- rootElementsByFile,
37333
- logger
37354
+ appliesByElementNodeMutable
37334
37355
  );
37335
37356
  }
37336
37357
  perf.selectorMatchMs = performance.now() - selectorMatchStartedAt;
@@ -37338,7 +37359,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37338
37359
  for (const edges of appliesByElementNodeMutable.values()) {
37339
37360
  edges.sort(compareLayoutEdge);
37340
37361
  }
37341
- const appliesByElementKey = /* @__PURE__ */ new Map();
37362
+ const appliesByNode = /* @__PURE__ */ new Map();
37342
37363
  const tailwind = css.tailwind;
37343
37364
  for (let i = 0; i < elements.length; i++) {
37344
37365
  const node = elements[i];
@@ -37346,7 +37367,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37346
37367
  const edges = appliesByElementNodeMutable.get(node) ?? [];
37347
37368
  const cascade = buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorId, tailwind);
37348
37369
  cascadeByElementNode.set(node, cascade);
37349
- appliesByElementKey.set(node.key, edges);
37370
+ appliesByNode.set(node, edges);
37350
37371
  }
37351
37372
  perf.cascadeBuildMs = performance.now() - cascadeStartedAt;
37352
37373
  const snapshotByElementNode = buildSignalSnapshotIndex(elements, cascadeByElementNode, perf);
@@ -37354,7 +37375,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37354
37375
  const factIndex = buildElementFactIndex(elements, snapshotByElementNode);
37355
37376
  const conditionalDeltaIndex = buildConditionalDeltaIndex(
37356
37377
  elements,
37357
- appliesByElementKey,
37378
+ appliesByNode,
37358
37379
  monitoredDeclarationsBySelectorId
37359
37380
  );
37360
37381
  const elementsWithConditionalOverflowDelta = buildConditionalDeltaSignalGroupElements(
@@ -37372,7 +37393,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37372
37393
  contextByParentNode,
37373
37394
  measurementNodeByRootKey,
37374
37395
  snapshotByElementNode,
37375
- snapshotHotSignalsByElementKey: factIndex.snapshotHotSignalsByElementKey
37396
+ snapshotHotSignalsByNode: factIndex.snapshotHotSignalsByNode
37376
37397
  });
37377
37398
  finalizeTableCellBaselineRelevance(contextByParentNode, cohortIndex.verticalAlignConsensusByParent);
37378
37399
  perf.conditionalSignals = cohortIndex.conditionalSignals;
@@ -37388,11 +37409,11 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37388
37409
  childrenByParentNode: childrenByParentNodeMutable,
37389
37410
  elementBySolidFileAndId: elementBySolidFileAndIdMutable,
37390
37411
  elementRefsBySolidFileAndId: elementRefsBySolidFileAndIdMutable,
37391
- appliesByElementKey,
37392
- selectorCandidatesByElementKey,
37412
+ appliesByNode,
37413
+ selectorCandidatesByNode,
37393
37414
  selectorsById,
37394
37415
  measurementNodeByRootKey,
37395
- snapshotHotSignalsByElementKey: factIndex.snapshotHotSignalsByElementKey,
37416
+ snapshotHotSignalsByNode: factIndex.snapshotHotSignalsByNode,
37396
37417
  elementsByTagName: factIndex.elementsByTagName,
37397
37418
  elementsWithConditionalDeltaBySignal: conditionalDeltaIndex.elementsWithConditionalDeltaBySignal,
37398
37419
  elementsWithConditionalOverflowDelta,
@@ -37400,12 +37421,12 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37400
37421
  elementsByKnownSignalValue: factIndex.elementsByKnownSignalValue,
37401
37422
  dynamicSlotCandidateElements: factIndex.dynamicSlotCandidateElements,
37402
37423
  scrollContainerElements: factIndex.scrollContainerElements,
37403
- reservedSpaceFactsByElementKey: factIndex.reservedSpaceFactsByElementKey,
37404
- scrollContainerFactsByElementKey: factIndex.scrollContainerFactsByElementKey,
37405
- flowParticipationFactsByElementKey: factIndex.flowParticipationFactsByElementKey,
37406
- containingBlockFactsByElementKey: factIndex.containingBlockFactsByElementKey,
37407
- conditionalSignalDeltaFactsByElementKey: conditionalDeltaIndex.conditionalSignalDeltaFactsByElementKey,
37408
- 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,
37409
37430
  statefulSelectorEntriesByRuleId: statefulRuleIndexes.selectorEntriesByRuleId,
37410
37431
  statefulNormalizedDeclarationsByRuleId: statefulRuleIndexes.normalizedDeclarationsByRuleId,
37411
37432
  statefulBaseValueIndex: statefulRuleIndexes.baseValueIndex,
@@ -37417,11 +37438,11 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37417
37438
  };
37418
37439
  }
37419
37440
  function buildElementFactIndex(elements, snapshotByElementNode) {
37420
- const reservedSpaceFactsByElementKey = /* @__PURE__ */ new Map();
37421
- const scrollContainerFactsByElementKey = /* @__PURE__ */ new Map();
37422
- const flowParticipationFactsByElementKey = /* @__PURE__ */ new Map();
37423
- const containingBlockFactsByElementKey = /* @__PURE__ */ new Map();
37424
- 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();
37425
37446
  const elementsByTagName = /* @__PURE__ */ new Map();
37426
37447
  const elementsByKnownSignalValue = /* @__PURE__ */ new Map();
37427
37448
  const dynamicSlotCandidateElements = [];
@@ -37431,7 +37452,7 @@ function buildElementFactIndex(elements, snapshotByElementNode) {
37431
37452
  const node = elements[i];
37432
37453
  if (!node) continue;
37433
37454
  const snapshot = snapshotByElementNode.get(node);
37434
- if (node.textualContent === "unknown" && node.siblingCount >= 2) {
37455
+ if (node.textualContent === 2 /* Unknown */ && node.siblingCount >= 2) {
37435
37456
  dynamicSlotCandidateElements.push(node);
37436
37457
  }
37437
37458
  if (node.tagName) {
@@ -37452,18 +37473,18 @@ function buildElementFactIndex(elements, snapshotByElementNode) {
37452
37473
  nearestPositionedAncestorHasReservedSpace = parentPositioned.hasReservedSpace;
37453
37474
  }
37454
37475
  }
37455
- containingBlockFactsByElementKey.set(node.key, {
37476
+ containingBlockFactsByNode.set(node, {
37456
37477
  nearestPositionedAncestorKey,
37457
37478
  nearestPositionedAncestorHasReservedSpace
37458
37479
  });
37459
37480
  if (!snapshot) continue;
37460
37481
  const reservedSpaceFact = computeReservedSpaceFact(snapshot);
37461
- reservedSpaceFactsByElementKey.set(node.key, reservedSpaceFact);
37482
+ reservedSpaceFactsByNode.set(node, reservedSpaceFact);
37462
37483
  const scrollFact = computeScrollContainerFact(snapshot);
37463
- scrollContainerFactsByElementKey.set(node.key, scrollFact);
37484
+ scrollContainerFactsByNode.set(node, scrollFact);
37464
37485
  if (scrollFact.isScrollContainer) scrollContainerElements.push(node);
37465
- flowParticipationFactsByElementKey.set(node.key, computeFlowParticipationFact(snapshot));
37466
- snapshotHotSignalsByElementKey.set(node.key, computeHotSignals(snapshot));
37486
+ flowParticipationFactsByNode.set(node, computeFlowParticipationFact(snapshot));
37487
+ snapshotHotSignalsByNode.set(node, computeHotSignals(snapshot));
37467
37488
  const positionSignal = snapshot.signals.get("position");
37468
37489
  const isPositioned = positionSignal !== void 0 && positionSignal.kind === "known" && positionSignal.normalized !== "static";
37469
37490
  if (isPositioned) {
@@ -37494,49 +37515,163 @@ function buildElementFactIndex(elements, snapshotByElementNode) {
37494
37515
  }
37495
37516
  }
37496
37517
  return {
37497
- reservedSpaceFactsByElementKey,
37498
- scrollContainerFactsByElementKey,
37518
+ reservedSpaceFactsByNode,
37519
+ scrollContainerFactsByNode,
37499
37520
  scrollContainerElements,
37500
- flowParticipationFactsByElementKey,
37501
- containingBlockFactsByElementKey,
37502
- snapshotHotSignalsByElementKey,
37521
+ flowParticipationFactsByNode,
37522
+ containingBlockFactsByNode,
37523
+ snapshotHotSignalsByNode,
37503
37524
  elementsByTagName,
37504
37525
  elementsByKnownSignalValue,
37505
37526
  dynamicSlotCandidateElements
37506
37527
  };
37507
37528
  }
37508
- 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
+ }
37509
37547
  return {
37510
- lineHeight: computeHotNumeric(snapshot, "line-height"),
37511
- verticalAlign: computeHotNormalized(snapshot, "vertical-align"),
37512
- alignSelf: computeHotNormalized(snapshot, "align-self"),
37513
- placeSelf: computeHotNormalized(snapshot, "place-self"),
37514
- writingMode: computeHotNormalized(snapshot, "writing-mode"),
37515
- direction: computeHotNormalized(snapshot, "direction"),
37516
- display: computeHotNormalized(snapshot, "display"),
37517
- alignItems: computeHotNormalized(snapshot, "align-items"),
37518
- placeItems: computeHotNormalized(snapshot, "place-items"),
37519
- position: computeHotNormalized(snapshot, "position"),
37520
- insetBlockStart: computeHotNumeric(snapshot, "inset-block-start"),
37521
- insetBlockEnd: computeHotNumeric(snapshot, "inset-block-end"),
37522
- transform: computeHotNumeric(snapshot, "transform"),
37523
- translate: computeHotNumeric(snapshot, "translate"),
37524
- top: computeHotNumeric(snapshot, "top"),
37525
- bottom: computeHotNumeric(snapshot, "bottom"),
37526
- marginTop: computeHotNumeric(snapshot, "margin-top"),
37527
- 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 */
37528
37551
  };
37529
37552
  }
37530
- 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
+ }
37531
37561
  return {
37532
- present: snapshot.signals.has(name),
37533
- ...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 */
37534
37565
  };
37535
37566
  }
37536
- 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
+ }
37537
37654
  return {
37538
- present: snapshot.signals.has(name),
37539
- ...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
37540
37675
  };
37541
37676
  }
37542
37677
  function computeReservedSpaceFact(snapshot) {
@@ -37571,15 +37706,14 @@ function computeReservedSpaceFact(snapshot) {
37571
37706
  function hasPositiveOrDeclaredDimension(snapshot, property) {
37572
37707
  const signal = snapshot.signals.get(property);
37573
37708
  if (!signal) return false;
37574
- if (signal.guard !== "unconditional") return false;
37709
+ if (signal.guard.kind !== 0 /* Unconditional */) return false;
37575
37710
  let normalized = "";
37576
37711
  if (signal.kind === "known") {
37577
37712
  if (signal.px !== null) return signal.px > 0;
37578
37713
  normalized = signal.normalized.trim().toLowerCase();
37579
37714
  }
37580
37715
  if (signal.kind === "unknown") {
37581
- if (signal.raw === null) return false;
37582
- normalized = signal.raw.trim().toLowerCase();
37716
+ return signal.source !== null;
37583
37717
  }
37584
37718
  if (normalized.length === 0) return false;
37585
37719
  if (isNonReservingDimension(normalized)) return false;
@@ -37588,15 +37722,14 @@ function hasPositiveOrDeclaredDimension(snapshot, property) {
37588
37722
  function hasUsableAspectRatio(snapshot) {
37589
37723
  const signal = snapshot.signals.get("aspect-ratio");
37590
37724
  if (!signal) return false;
37591
- if (signal.guard !== "unconditional") return false;
37725
+ if (signal.guard.kind !== 0 /* Unconditional */) return false;
37726
+ if (signal.kind === "unknown") {
37727
+ return false;
37728
+ }
37592
37729
  let normalized = "";
37593
37730
  if (signal.kind === "known") {
37594
37731
  normalized = signal.normalized.trim().toLowerCase();
37595
37732
  }
37596
- if (signal.kind === "unknown") {
37597
- if (signal.raw === null) return false;
37598
- normalized = signal.raw.trim().toLowerCase();
37599
- }
37600
37733
  if (normalized.length === 0) return false;
37601
37734
  return normalized !== "auto";
37602
37735
  }
@@ -37614,8 +37747,8 @@ function computeScrollContainerFact(snapshot) {
37614
37747
  const yFromLonghand = parseSingleAxisScroll(overflowY);
37615
37748
  const xScroll = shorthandAxis.x;
37616
37749
  const yScroll = yFromLonghand === null ? shorthandAxis.y : yFromLonghand;
37617
- const hasConditionalScroll = overflowSignal?.guard === "conditional" && (shorthandAxis.x || shorthandAxis.y) || overflowYSignal?.guard === "conditional" && yFromLonghand === true;
37618
- 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;
37619
37752
  return {
37620
37753
  isScrollContainer: xScroll || yScroll,
37621
37754
  axis: toScrollAxis(xScroll, yScroll),
@@ -37625,18 +37758,18 @@ function computeScrollContainerFact(snapshot) {
37625
37758
  hasUnconditionalScroll
37626
37759
  };
37627
37760
  }
37761
+ var NO_SCROLL = Object.freeze({ x: false, y: false });
37762
+ var BOTH_SCROLL = Object.freeze({ x: true, y: true });
37628
37763
  function parseOverflowShorthandAxis(value2) {
37629
- if (value2 === null) return { x: false, y: false };
37630
- const tokens = splitWhitespaceTokens(value2);
37631
- if (tokens.length === 0) return { x: false, y: false };
37632
- const first = tokens[0];
37633
- if (!first) return { x: false, y: false };
37634
- if (tokens.length === 1) {
37635
- const scroll = SCROLLABLE_VALUES.has(first);
37636
- return { x: scroll, y: scroll };
37637
- }
37638
- const second = tokens[1];
37639
- 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();
37640
37773
  return {
37641
37774
  x: SCROLLABLE_VALUES.has(first),
37642
37775
  y: SCROLLABLE_VALUES.has(second)
@@ -37644,16 +37777,16 @@ function parseOverflowShorthandAxis(value2) {
37644
37777
  }
37645
37778
  function parseSingleAxisScroll(value2) {
37646
37779
  if (value2 === null) return null;
37647
- const tokens = splitWhitespaceTokens(value2);
37648
- const first = tokens[0];
37649
- if (!first) return null;
37780
+ const trimmed = value2.trim();
37781
+ const spaceIdx = trimmed.indexOf(" ");
37782
+ const first = spaceIdx === -1 ? trimmed : trimmed.slice(0, spaceIdx);
37650
37783
  return SCROLLABLE_VALUES.has(first);
37651
37784
  }
37652
37785
  function toScrollAxis(x, y) {
37653
- if (x && y) return "both";
37654
- if (x) return "x";
37655
- if (y) return "y";
37656
- 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 */;
37657
37790
  }
37658
37791
  function computeFlowParticipationFact(snapshot) {
37659
37792
  const signal = snapshot.signals.get("position");
@@ -37670,8 +37803,8 @@ function computeFlowParticipationFact(snapshot) {
37670
37803
  return {
37671
37804
  inFlow: !outOfFlow,
37672
37805
  position,
37673
- hasConditionalOutOfFlow: signal.guard === "conditional" && outOfFlow,
37674
- hasUnconditionalOutOfFlow: signal.guard === "unconditional" && outOfFlow
37806
+ hasConditionalOutOfFlow: signal.guard.kind === 1 /* Conditional */ && outOfFlow,
37807
+ hasUnconditionalOutOfFlow: signal.guard.kind === 0 /* Unconditional */ && outOfFlow
37675
37808
  };
37676
37809
  }
37677
37810
  function buildContextIndex(childrenByParentNode, snapshotByElementNode, perf) {
@@ -37746,7 +37879,7 @@ function collectAlignmentCases(context) {
37746
37879
  subjectDeclaredOffsetDeviation,
37747
37880
  subjectEffectiveOffsetDeviation,
37748
37881
  subjectLineHeightDeviation,
37749
- subjectStats.element.snapshot.textualContent,
37882
+ subjectStats.element.snapshot.node.textualContent,
37750
37883
  subjectStats.element,
37751
37884
  subjectStats.contentComposition,
37752
37885
  cohortContentCompositions
@@ -37839,26 +37972,26 @@ function coverageFromNumeric(value2) {
37839
37972
  }
37840
37973
  function coverageFromConflict(value2) {
37841
37974
  const certainty = coverageFromKind(value2.kind);
37842
- if (value2.value === "unknown") return certainty * 0.4;
37975
+ if (value2.value === 2 /* Unknown */) return certainty * 0.4;
37843
37976
  return certainty;
37844
37977
  }
37845
37978
  function coverageFromTextContrast(value2) {
37846
- if (value2 === "unknown") return 0.35;
37979
+ if (value2 === 2 /* Unknown */) return 0.35;
37847
37980
  return 1;
37848
37981
  }
37849
37982
  function coverageFromSubjectText(subjectTextualContent) {
37850
- if (subjectTextualContent === "unknown") return 0.35;
37983
+ if (subjectTextualContent === 2 /* Unknown */) return 0.35;
37851
37984
  return 1;
37852
37985
  }
37853
37986
  function coverageFromContextCertainty(certainty) {
37854
- if (certainty === "resolved") return 1;
37855
- if (certainty === "conditional") return 0.55;
37987
+ if (certainty === 0 /* Resolved */) return 1;
37988
+ if (certainty === 1 /* Conditional */) return 0.55;
37856
37989
  return 0.25;
37857
37990
  }
37858
37991
  function coverageFromKind(kind) {
37859
- if (kind === "exact") return 1;
37860
- if (kind === "interval") return 0.78;
37861
- 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;
37862
37995
  return 0.2;
37863
37996
  }
37864
37997
  function averageCoverage(...values) {
@@ -37891,24 +38024,7 @@ function resolveEffectiveAlignmentContext(parentContext, child, measurementNode,
37891
38024
  const childContext = contextByParentNode.get(child);
37892
38025
  if (!childContext) return parentContext;
37893
38026
  if (childContext.baselineRelevance !== "irrelevant") return parentContext;
37894
- return {
37895
- kind: parentContext.kind,
37896
- certainty: parentContext.certainty,
37897
- parentSolidFile: parentContext.parentSolidFile,
37898
- parentElementId: parentContext.parentElementId,
37899
- parentElementKey: parentContext.parentElementKey,
37900
- parentTag: parentContext.parentTag,
37901
- axis: parentContext.axis,
37902
- axisCertainty: parentContext.axisCertainty,
37903
- inlineDirection: parentContext.inlineDirection,
37904
- inlineDirectionCertainty: parentContext.inlineDirectionCertainty,
37905
- parentDisplay: parentContext.parentDisplay,
37906
- parentAlignItems: parentContext.parentAlignItems,
37907
- parentPlaceItems: parentContext.parentPlaceItems,
37908
- hasPositionedOffset: parentContext.hasPositionedOffset,
37909
- baselineRelevance: "irrelevant",
37910
- evidence: parentContext.evidence
37911
- };
38027
+ return deriveAlignmentContext(parentContext, { baselineRelevance: "irrelevant" });
37912
38028
  }
37913
38029
  function compareAlignmentCaseOrder(left, right) {
37914
38030
  if (left.subject.solidFile < right.subject.solidFile) return -1;
@@ -37927,12 +38043,12 @@ function buildConsistencyEvidence(input) {
37927
38043
  input.cohortProfile.medianLineHeightPx,
37928
38044
  input.cohortProfile.medianDeclaredOffsetPx
37929
38045
  );
37930
- const offset = normalizeDeviation(
38046
+ const offsetRaw = normalizeDeviation(
37931
38047
  input.subjectEffectiveOffsetDeviation,
37932
38048
  input.cohortProfile.effectiveOffsetDispersionPx,
37933
38049
  effectiveOffsetScaleReference
37934
38050
  );
37935
- const declaredOffset = normalizeDeviation(
38051
+ const declaredOffsetRaw = normalizeDeviation(
37936
38052
  input.subjectDeclaredOffsetDeviation,
37937
38053
  input.cohortProfile.declaredOffsetDispersionPx,
37938
38054
  declaredOffsetScaleReference
@@ -37943,10 +38059,15 @@ function buildConsistencyEvidence(input) {
37943
38059
  input.cohortProfile.medianLineHeightPx
37944
38060
  );
37945
38061
  const baselinesIrrelevant = input.context.baselineRelevance === "irrelevant";
37946
- const baselineStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveBaselineStrength(input, lineHeight);
37947
- const contextStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveContextStrength(input, lineHeight);
37948
- const replacedStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveReplacedControlStrength(input, lineHeight);
37949
- 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;
37950
38071
  const contextCertaintyPenalty = resolveContextCertaintyPenalty(input);
37951
38072
  const provenance = input.cohortProvenance;
37952
38073
  const atoms = buildEvidenceAtoms(
@@ -37967,6 +38088,7 @@ function buildConsistencyEvidence(input) {
37967
38088
  contextStrength: contextStrength.strength,
37968
38089
  replacedStrength: replacedStrength.strength,
37969
38090
  compositionStrength: compositionStrength.strength,
38091
+ majorityClassification: compositionResult ? compositionResult.divergence.majorityClassification : 5 /* Unknown */,
37970
38092
  identifiability: input.subjectIdentifiability,
37971
38093
  factSummary,
37972
38094
  atoms
@@ -38002,7 +38124,7 @@ function resolveOffsetScaleReference(medianLineHeightPx, fallbackMedianOffsetPx)
38002
38124
  }
38003
38125
  function resolveBaselineStrength(input, lineHeight) {
38004
38126
  const verticalAlign = input.cohortSignals.verticalAlign;
38005
- const hasConflict = verticalAlign.value === "conflict";
38127
+ const hasConflict = verticalAlign.value === 0 /* Conflict */;
38006
38128
  const conflict = hasConflict ? alignmentStrengthCalibration.baselineConflictBoost : 0;
38007
38129
  const kind = resolveBaselineEvidenceKind(lineHeight.kind, verticalAlign.kind, hasConflict);
38008
38130
  return {
@@ -38012,7 +38134,7 @@ function resolveBaselineStrength(input, lineHeight) {
38012
38134
  }
38013
38135
  function resolveBaselineEvidenceKind(lineHeightKind, verticalAlignKind, hasConflict) {
38014
38136
  if (!hasConflict) return mergeEvidenceKind(lineHeightKind, verticalAlignKind);
38015
- if (lineHeightKind === "unknown") return verticalAlignKind;
38137
+ if (lineHeightKind === 3 /* Unknown */) return verticalAlignKind;
38016
38138
  return mergeEvidenceKind(lineHeightKind, verticalAlignKind);
38017
38139
  }
38018
38140
  function resolveContextStrength(input, lineHeight) {
@@ -38040,7 +38162,7 @@ function resolveContextConflictEvidence(input) {
38040
38162
  const alignSelf = input.cohortSignals.alignSelf;
38041
38163
  const placeSelf = input.cohortSignals.placeSelf;
38042
38164
  const kind = mergeEvidenceKind(alignSelf.kind, placeSelf.kind);
38043
- const hasConflict = alignSelf.value === "conflict" || placeSelf.value === "conflict";
38165
+ const hasConflict = alignSelf.value === 0 /* Conflict */ || placeSelf.value === 0 /* Conflict */;
38044
38166
  if (!hasConflict) {
38045
38167
  return {
38046
38168
  strength: 0,
@@ -38054,23 +38176,23 @@ function resolveContextConflictEvidence(input) {
38054
38176
  }
38055
38177
  function resolveReplacedControlStrength(input, lineHeight) {
38056
38178
  const subject = input.subject.snapshot;
38057
- const hasReplacedPair = subject.isControl || subject.isReplaced || input.cohortSignals.hasControlOrReplacedPeer;
38179
+ const hasReplacedPair = subject.node.isControl || subject.node.isReplaced || input.cohortSignals.hasControlOrReplacedPeer;
38058
38180
  if (!hasReplacedPair) {
38059
38181
  return {
38060
38182
  strength: 0,
38061
38183
  kind: lineHeight.kind
38062
38184
  };
38063
38185
  }
38064
- if (input.cohortSignals.textContrastWithPeers === "different") {
38186
+ if (input.cohortSignals.textContrastWithPeers === 0 /* Different */) {
38065
38187
  return {
38066
38188
  strength: alignmentStrengthCalibration.replacedDifferentTextBoost,
38067
- kind: "exact"
38189
+ kind: 0 /* Exact */
38068
38190
  };
38069
38191
  }
38070
- if (input.cohortSignals.textContrastWithPeers === "unknown") {
38192
+ if (input.cohortSignals.textContrastWithPeers === 2 /* Unknown */) {
38071
38193
  return {
38072
38194
  strength: alignmentStrengthCalibration.replacedUnknownTextBoost,
38073
- kind: "conditional"
38195
+ kind: 2 /* Conditional */
38074
38196
  };
38075
38197
  }
38076
38198
  return {
@@ -38079,22 +38201,28 @@ function resolveReplacedControlStrength(input, lineHeight) {
38079
38201
  };
38080
38202
  }
38081
38203
  function resolveContentCompositionStrength(input) {
38082
- const divergenceStrength = resolveCompositionDivergenceStrength(
38204
+ const divergence = resolveCompositionDivergence(
38083
38205
  input.subjectContentComposition,
38084
38206
  input.cohortContentCompositions,
38085
38207
  input.context
38086
38208
  );
38087
- if (divergenceStrength <= 0) {
38209
+ if (divergence.strength <= 0) {
38088
38210
  return {
38089
- strength: 0,
38090
- kind: "exact"
38211
+ evidence: {
38212
+ strength: 0,
38213
+ kind: 0 /* Exact */
38214
+ },
38215
+ divergence
38091
38216
  };
38092
38217
  }
38093
38218
  const subjectClassification = input.subjectContentComposition.classification;
38094
- const kind = subjectClassification === "unknown" ? "conditional" : "exact";
38219
+ const kind = subjectClassification === 5 /* Unknown */ ? 2 /* Conditional */ : 0 /* Exact */;
38095
38220
  return {
38096
- strength: clamp(divergenceStrength, 0, 1),
38097
- kind
38221
+ evidence: {
38222
+ strength: clamp(divergence.strength, 0, 1),
38223
+ kind
38224
+ },
38225
+ divergence
38098
38226
  };
38099
38227
  }
38100
38228
  function resolveContextCertaintyPenalty(input) {
@@ -38166,9 +38294,9 @@ function buildEvidenceAtoms(input, offset, declaredOffset, baselineStrength, con
38166
38294
  return out;
38167
38295
  }
38168
38296
  function mapContextCertaintyToEvidenceKind(certainty) {
38169
- if (certainty === "resolved") return "exact";
38170
- if (certainty === "conditional") return "conditional";
38171
- return "unknown";
38297
+ if (certainty === 0 /* Resolved */) return 0 /* Exact */;
38298
+ if (certainty === 1 /* Conditional */) return 2 /* Conditional */;
38299
+ return 3 /* Unknown */;
38172
38300
  }
38173
38301
  function pushSupportAtom(out, factorId, valueKind, strength, coverage, provenance) {
38174
38302
  pushAtom(out, factorId, valueKind, strength, coverage, provenance, "support");
@@ -38194,19 +38322,19 @@ function pushAtom(out, factorId, valueKind, strength, coverage, provenance, expe
38194
38322
  }
38195
38323
  function toPositiveContribution(strength, maxWeight, valueKind) {
38196
38324
  const contribution = clamp(strength, 0, 2) * maxWeight;
38197
- if (valueKind === "exact") {
38325
+ if (valueKind === 0 /* Exact */) {
38198
38326
  return {
38199
38327
  min: contribution,
38200
38328
  max: contribution
38201
38329
  };
38202
38330
  }
38203
- if (valueKind === "interval") {
38331
+ if (valueKind === 1 /* Interval */) {
38204
38332
  return {
38205
38333
  min: contribution * evidenceContributionCalibration.supportIntervalLowerScale,
38206
38334
  max: contribution
38207
38335
  };
38208
38336
  }
38209
- if (valueKind === "conditional") {
38337
+ if (valueKind === 2 /* Conditional */) {
38210
38338
  return {
38211
38339
  min: 0,
38212
38340
  max: contribution * evidenceContributionCalibration.supportConditionalUpperScale
@@ -38219,19 +38347,19 @@ function toPositiveContribution(strength, maxWeight, valueKind) {
38219
38347
  }
38220
38348
  function toNegativeContribution(strength, maxPenalty, valueKind) {
38221
38349
  const penalty = clamp(strength, 0, 1) * maxPenalty;
38222
- if (valueKind === "exact") {
38350
+ if (valueKind === 0 /* Exact */) {
38223
38351
  return {
38224
38352
  min: -penalty,
38225
38353
  max: -penalty
38226
38354
  };
38227
38355
  }
38228
- if (valueKind === "interval") {
38356
+ if (valueKind === 1 /* Interval */) {
38229
38357
  return {
38230
38358
  min: -penalty,
38231
38359
  max: -penalty * evidenceContributionCalibration.penaltyIntervalUpperScale
38232
38360
  };
38233
38361
  }
38234
- if (valueKind === "conditional") {
38362
+ if (valueKind === 2 /* Conditional */) {
38235
38363
  return {
38236
38364
  min: -penalty,
38237
38365
  max: 0
@@ -38242,7 +38370,7 @@ function toNegativeContribution(strength, maxPenalty, valueKind) {
38242
38370
  max: 0
38243
38371
  };
38244
38372
  }
38245
- var ZERO_STRENGTH = { strength: 0, kind: "exact" };
38373
+ var ZERO_STRENGTH = { strength: 0, kind: 0 /* Exact */ };
38246
38374
 
38247
38375
  // src/cross-file/layout/consistency-policy.ts
38248
38376
  function applyConsistencyPolicy(input) {
@@ -38326,23 +38454,38 @@ function resolveConfidence(posterior, evidenceMass) {
38326
38454
  const confidence = posterior.lower * weightedMass * (1 - intervalWidth * alignmentPolicyCalibration.confidenceIntervalPenalty);
38327
38455
  return clamp(confidence, 0, 1);
38328
38456
  }
38457
+ var EMPTY_FACTOR_LIST = Object.freeze([]);
38329
38458
  function selectTopFactors(evidence) {
38330
- const sorted = [...evidence.atoms];
38331
- sorted.sort((left, right) => {
38332
- const leftMagnitude = Math.abs((left.contribution.min + left.contribution.max) / 2);
38333
- const rightMagnitude = Math.abs((right.contribution.min + right.contribution.max) / 2);
38334
- if (leftMagnitude !== rightMagnitude) return rightMagnitude - leftMagnitude;
38335
- if (left.factorId < right.factorId) return -1;
38336
- 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;
38337
38486
  return 0;
38338
38487
  });
38339
- const out = [];
38340
- for (let i = 0; i < sorted.length && i < 4; i++) {
38341
- const item = sorted[i];
38342
- if (!item) continue;
38343
- out.push(item.factorId);
38344
- }
38345
- return out;
38488
+ return top.map((t) => t.id);
38346
38489
  }
38347
38490
  function logistic(value2) {
38348
38491
  if (value2 > 30) return 1;
@@ -38363,7 +38506,7 @@ function evaluateAlignmentCase(input) {
38363
38506
  evidenceMass: policy.evidenceMass
38364
38507
  };
38365
38508
  }
38366
- const signalFindings = buildFindingsFromAtoms(evidence.atoms, input);
38509
+ const signalFindings = buildFindingsFromAtoms(evidence.atoms, input, evidence);
38367
38510
  return {
38368
38511
  kind: "accept",
38369
38512
  evaluation: {
@@ -38383,12 +38526,12 @@ function evaluateAlignmentCase(input) {
38383
38526
  }
38384
38527
  };
38385
38528
  }
38386
- function buildFindingsFromAtoms(atoms, input) {
38529
+ function buildFindingsFromAtoms(atoms, input, evidence) {
38387
38530
  const byKind = /* @__PURE__ */ new Map();
38388
38531
  for (let i = 0; i < atoms.length; i++) {
38389
38532
  const atom = atoms[i];
38390
38533
  if (!atom) continue;
38391
- const factor = toFindingFactor(atom.factorId, input);
38534
+ const factor = toFindingFactor(atom.factorId, input, evidence);
38392
38535
  if (factor === null) continue;
38393
38536
  const meanContribution = (atom.contribution.min + atom.contribution.max) / 2;
38394
38537
  if (meanContribution <= 0) continue;
@@ -38409,7 +38552,7 @@ function buildFindingsFromAtoms(atoms, input) {
38409
38552
  }
38410
38553
  return [...byKind.values()];
38411
38554
  }
38412
- function toFindingFactor(factorId, input) {
38555
+ function toFindingFactor(factorId, input, evidence) {
38413
38556
  switch (factorId) {
38414
38557
  case "offset-delta":
38415
38558
  return {
@@ -38439,17 +38582,15 @@ function toFindingFactor(factorId, input) {
38439
38582
  case "content-composition-conflict":
38440
38583
  return {
38441
38584
  kind: "content-composition-conflict",
38442
- message: formatContentCompositionFinding(input)
38585
+ message: formatContentCompositionFinding(input, evidence)
38443
38586
  };
38444
38587
  default:
38445
38588
  return null;
38446
38589
  }
38447
38590
  }
38448
- function formatContentCompositionFinding(input) {
38591
+ function formatContentCompositionFinding(input, evidence) {
38449
38592
  const subjectClassification = formatCompositionClassification(input.subjectContentComposition.classification);
38450
- const majorityClassification = formatCompositionClassification(
38451
- resolveMajorityClassification(input.cohortContentCompositions)
38452
- );
38593
+ const majorityClassification = formatCompositionClassification(evidence.majorityClassification);
38453
38594
  const fixSuggestion = formatCompositionFixSuggestion(input.subjectContentComposition);
38454
38595
  return `siblings have identical CSS but different content composition (subject: ${subjectClassification}, majority: ${majorityClassification}; fix: ${fixSuggestion})`;
38455
38596
  }
@@ -38508,7 +38649,7 @@ function recordPolicyMetrics(context, evidenceMass, posteriorLower, posteriorUpp
38508
38649
  context.layout.perf.factorCoverageSum += clamp(evidenceMass, 0, 1);
38509
38650
  context.layout.perf.factorCoverageCount++;
38510
38651
  const width = clamp(posteriorUpper - posteriorLower, 0, 1);
38511
- context.layout.perf.posteriorWidths.push(width);
38652
+ reservoirPush(context.layout.perf.posteriorWidths, width);
38512
38653
  if (width > 1e-3) context.layout.perf.uncertaintyEscalations++;
38513
38654
  }
38514
38655
 
@@ -38527,16 +38668,16 @@ function readNodeRefById(layout, solidFile, elementId) {
38527
38668
  }
38528
38669
  function isFlowRelevantBySiblingsOrText(node, textualContent) {
38529
38670
  if (node.siblingCount >= 2) return true;
38530
- return textualContent === "yes" || textualContent === "unknown" || textualContent === "dynamic-text";
38671
+ return textualContent === 0 /* Yes */ || textualContent === 2 /* Unknown */ || textualContent === 3 /* DynamicText */;
38531
38672
  }
38532
38673
  function isDeferredContainerLike(node, textualContent) {
38533
38674
  if (node.siblingCount >= 2) return true;
38534
- if (textualContent === "unknown") return true;
38675
+ if (textualContent === 2 /* Unknown */) return true;
38535
38676
  if (node.tagName !== null && SECTIONING_CONTAINER_TAGS.has(node.tagName)) return true;
38536
38677
  return false;
38537
38678
  }
38538
38679
  function isDynamicContainerLike(node) {
38539
- return node.textualContent === "unknown" && node.siblingCount >= 2;
38680
+ return node.textualContent === 2 /* Unknown */ && node.siblingCount >= 2;
38540
38681
  }
38541
38682
  function isLikelyViewportAffectingContainer(node) {
38542
38683
  if (node.siblingCount >= 2) return true;
@@ -39776,7 +39917,7 @@ var cssLayoutScrollbarGutterInstability = defineCrossRule({
39776
39917
  const snapshot = collectSignalSnapshot(context, node);
39777
39918
  const scroll = readScrollContainerFact(context.layout, node);
39778
39919
  if (!scroll.isScrollContainer) continue;
39779
- if (scroll.axis !== "y" && scroll.axis !== "both") continue;
39920
+ if (scroll.axis !== 2 /* Y */ && scroll.axis !== 3 /* Both */) continue;
39780
39921
  const scrollbarWidth = readKnownNormalized(snapshot, "scrollbar-width");
39781
39922
  if (scrollbarWidth === "none") continue;
39782
39923
  const gutter = readKnownNormalized(snapshot, "scrollbar-gutter");
@@ -39906,10 +40047,10 @@ var cssLayoutConditionalDisplayCollapse = defineCrossRule({
39906
40047
  const display = readKnownNormalizedWithGuard(snapshot, "display");
39907
40048
  if (!display || !COLLAPSING_DISPLAYS.has(display)) continue;
39908
40049
  const displaySignal = snapshot.signals.get("display");
39909
- if (!displaySignal || displaySignal.guard !== "conditional") continue;
40050
+ if (!displaySignal || displaySignal.guard.kind !== 1 /* Conditional */) continue;
39910
40051
  const flow = readFlowParticipationFact(context.layout, node);
39911
40052
  if (!flow.inFlow) continue;
39912
- if (!isFlowRelevantBySiblingsOrText(node, snapshot.textualContent)) continue;
40053
+ if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
39913
40054
  const reservedSpace = readReservedSpaceFact(context.layout, node);
39914
40055
  if (reservedSpace.hasReservedSpace) continue;
39915
40056
  if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalDisplayCollapse.id, "conditionalDisplayCollapse", messages152.conditionalDisplayCollapse, cssLayoutConditionalDisplayCollapse.severity, { display })) continue;
@@ -39945,10 +40086,10 @@ var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
39945
40086
  if (!hasWrapShiftDelta(whiteSpaceDelta)) continue;
39946
40087
  if (!whiteSpace) continue;
39947
40088
  const whiteSpaceSignal = snapshot.signals.get("white-space");
39948
- if (!whiteSpaceSignal || whiteSpaceSignal.guard !== "conditional") continue;
40089
+ if (!whiteSpaceSignal || whiteSpaceSignal.guard.kind !== 1 /* Conditional */) continue;
39949
40090
  const flow = readFlowParticipationFact(context.layout, node);
39950
40091
  if (!flow.inFlow) continue;
39951
- if (!isFlowRelevantBySiblingsOrText(node, snapshot.textualContent)) continue;
40092
+ if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
39952
40093
  if (hasStableTextShell(snapshot)) continue;
39953
40094
  if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalWhiteSpaceWrapShift.id, "conditionalWhiteSpaceShift", messages153.conditionalWhiteSpaceShift, cssLayoutConditionalWhiteSpaceWrapShift.severity, { whiteSpace })) continue;
39954
40095
  }
@@ -40085,7 +40226,7 @@ var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
40085
40226
  const node = candidates[i];
40086
40227
  if (!node) continue;
40087
40228
  const snapshot = collectSignalSnapshot(context, node);
40088
- if (!isDeferredContainerLike(node, snapshot.textualContent)) continue;
40229
+ if (!isDeferredContainerLike(node, snapshot.node.textualContent)) continue;
40089
40230
  const reservedSpace = readReservedSpaceFact(context.layout, node);
40090
40231
  if (reservedSpace.hasReservedSpace) continue;
40091
40232
  if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize", messages156.missingIntrinsicSize, cssLayoutContentVisibilityNoIntrinsicSize.severity)) continue;
@@ -40144,11 +40285,11 @@ function collectConditionalOffsets(layout, node, snapshot) {
40144
40285
  if (!delta.hasConditional || !delta.hasDelta) continue;
40145
40286
  const signal = snapshot.signals.get(name);
40146
40287
  if (!signal) continue;
40147
- if (signal.guard !== "conditional") continue;
40288
+ if (signal.guard.kind !== 1 /* Conditional */) continue;
40148
40289
  if (signal.kind !== "known") continue;
40149
40290
  if (signal.px === null) continue;
40150
40291
  if (Math.abs(signal.px) <= 0.25) continue;
40151
- out.push({ property: name, value: signal.px, guardKey: signal.guardProvenance.key });
40292
+ out.push({ property: name, value: signal.px, guardKey: signal.guard.key });
40152
40293
  }
40153
40294
  return out;
40154
40295
  }
@@ -40169,8 +40310,8 @@ function hasEffectivePositionForConditionalOffset(snapshot, guardKey) {
40169
40310
  const position = snapshot.signals.get("position");
40170
40311
  if (!position) return false;
40171
40312
  if (position.kind !== "known") return false;
40172
- if (position.guard !== "conditional") return false;
40173
- if (position.guardProvenance.key !== guardKey) return false;
40313
+ if (position.guard.kind !== 1 /* Conditional */) return false;
40314
+ if (position.guard.key !== guardKey) return false;
40174
40315
  return position.normalized !== "static";
40175
40316
  }
40176
40317
  function hasStableBaseline(baselineBySignal, property, expectedPx) {
@@ -42385,13 +42526,13 @@ var RULES = [
42385
42526
  "id": "resource-implicit-suspense",
42386
42527
  "severity": "warn",
42387
42528
  "description": "Detect createResource that implicitly triggers or permanently breaks Suspense boundaries.",
42388
- "fixable": false,
42529
+ "fixable": true,
42389
42530
  "category": "reactivity",
42390
42531
  "plugin": "solid",
42391
42532
  "messages": {
42392
- "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: ... })",
42393
- "conditionalSuspense": "createResource '{{name}}' is rendered inside a conditional mount point ({{mountTag}}) with a distant Suspense boundary. When the fetcher's Promise is pending, the SuspenseContext increment fires and unmounts the entire subtree. initialValue does NOT prevent this \u2014 it only prevents the accessor from returning undefined.",
42394
- "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 absorbs it and stays in its fallback state permanently. Wrap the component in <ErrorBoundary fallback={...}> or catch errors inside the fetcher."
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."
42395
42536
  }
42396
42537
  },
42397
42538
  {
@@ -42594,7 +42735,7 @@ var RULES_BY_CATEGORY = {
42594
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." } }],
42595
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." } }],
42596
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." } }],
42597
- "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": 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}}' is rendered inside a conditional mount point ({{mountTag}}) with a distant Suspense boundary. When the fetcher's Promise is pending, the SuspenseContext increment fires and unmounts the entire subtree. initialValue does NOT prevent this \u2014 it only prevents the accessor from returning undefined.", "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 absorbs it and stays in its fallback state permanently. Wrap the component in <ErrorBoundary fallback={...}> or catch errors inside 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." } }],
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." } }],
42598
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}}' }}." } }]
42599
42740
  };
42600
42741
  function getRule2(id) {