why-hydration 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,6 +10,230 @@ function readNodeEnv() {
10
10
  }
11
11
  var isDev = readNodeEnv() !== "production";
12
12
 
13
+ // src/core/classify/detectors.ts
14
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
15
+ var REACT_ID_RE = /^:[rR][0-9a-z]*:$/;
16
+ var HEX_TOKEN_RE = /^[0-9a-f]{16,}$/i;
17
+ var RANDOM_DECIMAL_RE = /^0?\.\d{6,}$/;
18
+ var NANOID_RE = /^[A-Za-z0-9_-]{10,}$/;
19
+ var ARABIC_INDIC_DIGITS = /[٠-٩۰-۹]/;
20
+ var LATIN_DIGITS = /[0-9]/;
21
+ var BIDI_CONTROLS = /[\u200e\u200f\u061c\u2066-\u2069]/g;
22
+ function stripBidiControls(value2) {
23
+ return value2.replace(BIDI_CONTROLS, "");
24
+ }
25
+ function differsOnlyByBidiControls(a, b) {
26
+ const sa = stripBidiControls(a);
27
+ const sb = stripBidiControls(b);
28
+ return a !== b && sa === sb && sa.trim() !== "";
29
+ }
30
+ function normalizeNumerals(value2) {
31
+ return stripBidiControls(value2).replace(/[٠-٩۰-۹٫٬]/g, (ch) => {
32
+ if (ch === "\u066B") return ".";
33
+ if (ch === "\u066C") return ",";
34
+ const code2 = ch.charCodeAt(0);
35
+ const zero = code2 >= 1776 ? 1776 : 1632;
36
+ return String(code2 - zero);
37
+ });
38
+ }
39
+ function isRandomLike(value2) {
40
+ const v = value2.trim();
41
+ if (!v) return false;
42
+ if (UUID_RE.test(v)) return true;
43
+ if (REACT_ID_RE.test(v)) return true;
44
+ if (HEX_TOKEN_RE.test(v)) return true;
45
+ if (RANDOM_DECIMAL_RE.test(v)) return true;
46
+ if (NANOID_RE.test(v) && /[A-Za-z]/.test(v) && /[0-9]/.test(v)) return true;
47
+ return false;
48
+ }
49
+ function looksLikeTime(value2) {
50
+ return /\b\d{1,2}:\d{2}(?::\d{2})?(?:\s?[AaPp][Mm])?\b/.test(
51
+ normalizeNumerals(value2).trim()
52
+ );
53
+ }
54
+ var ISO_DATE = /^\d{4}-\d{1,2}(?:-\d{1,2})?(?:[T ]\d{1,2}:\d{2}(?::\d{2})?)?/;
55
+ var NUMERIC_DATE = /^\d{1,4}[/.-]\d{1,2}[/.-]\d{1,4}$/;
56
+ var TIME_OF_DAY = /^\d{1,2}:\d{2}(?::\d{2})?(?:\s?[AaPp]\.?[Mm]\.?)?$/;
57
+ var MONTH_NAME = /\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\b/i;
58
+ function looksLikeDate(v) {
59
+ return ISO_DATE.test(v) || NUMERIC_DATE.test(v) || TIME_OF_DAY.test(v) || MONTH_NAME.test(v) && /\d/.test(v);
60
+ }
61
+ function toTimestamp(value2) {
62
+ const v = value2.trim();
63
+ if (!v) return null;
64
+ if (/^\d{10,13}$/.test(v)) {
65
+ const n = Number(v);
66
+ return v.length === 10 ? n * 1e3 : n;
67
+ }
68
+ if (!looksLikeDate(v)) return null;
69
+ const parsed = Date.parse(v);
70
+ if (!Number.isNaN(parsed)) return parsed;
71
+ if (looksLikeTime(v)) {
72
+ const anchored = Date.parse(`1970-01-01 ${v}`);
73
+ if (!Number.isNaN(anchored)) return anchored;
74
+ }
75
+ return null;
76
+ }
77
+ function hasArabicIndicDigits(value2) {
78
+ return ARABIC_INDIC_DIGITS.test(value2);
79
+ }
80
+ function hasLatinDigits(value2) {
81
+ return LATIN_DIGITS.test(value2);
82
+ }
83
+ var NUMERIC_LIKE = /^[+-]?[\d.,\s\u00a0\u2009]+$/;
84
+ function isSameNumberDifferentSeparators(a, b) {
85
+ const at = normalizeNumerals(a).trim();
86
+ const bt = normalizeNumerals(b).trim();
87
+ if (!NUMERIC_LIKE.test(at) || !NUMERIC_LIKE.test(bt)) return false;
88
+ const digitsOnly = (s) => s.replace(/\D/g, "");
89
+ const da = digitsOnly(at);
90
+ const db = digitsOnly(bt);
91
+ if (!da || da !== db) return false;
92
+ const hasSep = (s) => /[.,\s\u00a0\u2009]/.test(s);
93
+ return (hasSep(at) || hasSep(bt)) && a.trim() !== b.trim();
94
+ }
95
+ var CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set([
96
+ "value",
97
+ "placeholder",
98
+ "title",
99
+ "alt",
100
+ "label",
101
+ "aria-label",
102
+ "aria-valuetext",
103
+ "content",
104
+ "datetime"
105
+ ]);
106
+ function isContentAttribute(name) {
107
+ return CONTENT_ATTRIBUTES.has(name.toLowerCase());
108
+ }
109
+ var THIRD_PARTY_MARKERS = /googlefc|adsbygoogle|google_ads|googletag|__tcfapi|onetrust|optanon|cookiebot|usercentrics|iubenda|didomi|quantcast|grammarly|data-gramm|gtm-|_hjsettings|hotjar|fullstory|intercom|drift|zendesk|livechat|tawk|hubspot|turnstile|recaptcha/i;
110
+ function looksLikeThirdPartyNode(html, tagName) {
111
+ const tag = (tagName ?? "").toUpperCase();
112
+ if (tag === "IFRAME" || tag === "EMBED" || tag === "OBJECT") return true;
113
+ const h = (html ?? "").toLowerCase();
114
+ if (!h) return false;
115
+ return THIRD_PARTY_MARKERS.test(h) || h.includes("about:blank");
116
+ }
117
+ var DATE_PARTS_RE = /^(\d{1,4})[/.-](\d{1,2})[/.-](\d{1,4})$/;
118
+ function isSameDateDifferentOrder(a, b) {
119
+ const partsA = normalizeNumerals(a).trim().match(DATE_PARTS_RE);
120
+ const partsB = normalizeNumerals(b).trim().match(DATE_PARTS_RE);
121
+ if (!partsA || !partsB) return false;
122
+ const setA = [partsA[1], partsA[2], partsA[3]].sort().join("|");
123
+ const setB = [partsB[1], partsB[2], partsB[3]].sort().join("|");
124
+ return setA === setB && a.trim() !== b.trim();
125
+ }
126
+ var EXTENSION_ATTRIBUTES = /* @__PURE__ */ new Set([
127
+ "cz-shortcut-listen",
128
+ "data-gramm",
129
+ "data-gramm_editor",
130
+ "data-gramm_id",
131
+ "data-gr-c-s-loaded",
132
+ "data-lt-installed",
133
+ "data-new-gr-c-s-check-loaded",
134
+ "data-new-gr-c-s-loaded",
135
+ "spellcheck-extension",
136
+ "bis_register",
137
+ "__processed_by_react_dev_tools"
138
+ ]);
139
+ function isExtensionAttribute(name) {
140
+ const n = name.toLowerCase();
141
+ if (EXTENSION_ATTRIBUTES.has(n)) return true;
142
+ return n.startsWith("data-gr-") || n.startsWith("data-gramm") || n.startsWith("__bis") || n.startsWith("bis_");
143
+ }
144
+ var CLOSES_P = [
145
+ "address",
146
+ "article",
147
+ "aside",
148
+ "blockquote",
149
+ "center",
150
+ "details",
151
+ "dialog",
152
+ "dir",
153
+ "div",
154
+ "dl",
155
+ "fieldset",
156
+ "figcaption",
157
+ "figure",
158
+ "footer",
159
+ "form",
160
+ "h1",
161
+ "h2",
162
+ "h3",
163
+ "h4",
164
+ "h5",
165
+ "h6",
166
+ "header",
167
+ "hgroup",
168
+ "hr",
169
+ "li",
170
+ "dd",
171
+ "dt",
172
+ "listing",
173
+ "main",
174
+ "menu",
175
+ "nav",
176
+ "ol",
177
+ "p",
178
+ "plaintext",
179
+ "pre",
180
+ "search",
181
+ "section",
182
+ "summary",
183
+ "table",
184
+ "ul",
185
+ "xmp"
186
+ ];
187
+ var CLOSES_P_SET = new Set(CLOSES_P.map((t) => t.toUpperCase()));
188
+ var TABLE_CHILDREN = /* @__PURE__ */ new Set([
189
+ "CAPTION",
190
+ "COLGROUP",
191
+ "THEAD",
192
+ "TBODY",
193
+ "TFOOT"
194
+ ]);
195
+ var TABLE_SCAFFOLD = ["script", "template", "style"];
196
+ var TABLE_LEVEL = ["caption", "colgroup", "thead", "tbody", "tfoot"];
197
+ var SECTION_LEVEL = ["tr"];
198
+ var ROW_LEVEL = ["td", "th"];
199
+ var notIn = (tags) => `:not(${[...tags, ...TABLE_SCAFFOLD].join(", ")})`;
200
+ var PARSER_REPAIRS = {
201
+ P: CLOSES_P.join(", "),
202
+ A: "a",
203
+ BUTTON: "button",
204
+ FORM: "form",
205
+ TABLE: [
206
+ `:scope > ${notIn([...TABLE_LEVEL, ...SECTION_LEVEL])}`,
207
+ ...["thead", "tbody", "tfoot"].flatMap((section) => [
208
+ `:scope > ${section} > ${notIn(SECTION_LEVEL)}`,
209
+ `:scope > ${section} > tr > ${notIn(ROW_LEVEL)}`
210
+ ]),
211
+ `:scope > tr > ${notIn(ROW_LEVEL)}`
212
+ ].join(", ")
213
+ };
214
+ function isInvalidNesting(parentTag, childTag) {
215
+ if (!parentTag || !childTag) return false;
216
+ const p = parentTag.toUpperCase();
217
+ const c = childTag.toUpperCase();
218
+ if (p === "P") return CLOSES_P_SET.has(c);
219
+ if (p === "A") return c === "A";
220
+ if (p === "BUTTON") return c === "BUTTON" || c === "A";
221
+ if (p === "FORM") return c === "FORM";
222
+ if (p === "TABLE") return !TABLE_CHILDREN.has(c) && !isScaffold(c);
223
+ if (p === "THEAD" || p === "TBODY" || p === "TFOOT") {
224
+ return c !== "TR" && !isScaffold(c);
225
+ }
226
+ if (p === "TR") return c !== "TD" && c !== "TH" && !isScaffold(c);
227
+ return false;
228
+ }
229
+ function isScaffold(tag) {
230
+ return TABLE_SCAFFOLD.includes(tag.toLowerCase());
231
+ }
232
+ function messageIndicatesInvalidNesting(message) {
233
+ if (!message) return false;
234
+ return /validateDOMNesting/i.test(message) || /cannot (?:be a|contain).*(?:descendant|child)/i.test(message) || /cannot appear as a (?:child|descendant)/i.test(message);
235
+ }
236
+
13
237
  // src/core/diff.ts
14
238
  var IGNORED_ATTRIBUTES = /* @__PURE__ */ new Set(["data-reactroot"]);
15
239
  function parseServerHtml(html, rootTagName) {
@@ -36,9 +260,15 @@ function diffSnapshotAgainstDom(serverHtml, clientRoot) {
36
260
  function collectChildren(serverParent, clientParent, parentPath, out, limit) {
37
261
  if (out.length >= limit) return;
38
262
  const serverChildren = meaningfulChildNodes(serverParent);
39
- const clientChildren = meaningfulChildNodes(clientParent);
40
263
  const parentPending = hasPendingSuspense(serverParent);
41
264
  const parentTag = elementTag(clientParent);
265
+ const clientChildren = repairClientChildren(
266
+ meaningfulChildNodes(clientParent),
267
+ parentTag,
268
+ parentPath,
269
+ out,
270
+ limit
271
+ );
42
272
  const pairs = alignChildren(serverChildren, clientChildren);
43
273
  let index = 0;
44
274
  for (let k = 0; k < pairs.length; k++) {
@@ -86,7 +316,10 @@ function collectChildren(serverParent, clientParent, parentPath, out, limit) {
86
316
  parentTagName: parentTag,
87
317
  server: null,
88
318
  client: serialize(clientNode),
89
- element: asElement(clientNode)
319
+ // An added text node has no element of its own, so point at its
320
+ // parent, as text changes do. With `null` the component lookup found
321
+ // nothing and fell back to whichever error was reported last.
322
+ element: asElement(clientNode) ?? asElement(clientParent)
90
323
  });
91
324
  } else if (serverNode && clientNode) {
92
325
  collectNode(serverNode, clientNode, path, parentTag, out, limit);
@@ -132,7 +365,7 @@ function collectNode(serverNode, clientNode, path, parentTag, out, limit) {
132
365
  parentTagName: parentTag,
133
366
  server: serialize(serverEl),
134
367
  client: serialize(clientEl),
135
- element: clientEl
368
+ element: liveElement(clientEl)
136
369
  });
137
370
  return;
138
371
  }
@@ -160,7 +393,7 @@ function diffAttributes(serverEl, clientEl, path, parentTag) {
160
393
  attribute: name,
161
394
  server: serverValue,
162
395
  client: clientValue,
163
- element: clientEl
396
+ element: liveElement(clientEl)
164
397
  };
165
398
  }
166
399
  return null;
@@ -307,7 +540,8 @@ function meaningfulChildNodes(parent) {
307
540
  for (const node of raw) {
308
541
  if (node.nodeType === Node.COMMENT_NODE) {
309
542
  const data = node.nodeValue ?? "";
310
- if (data === "$?" || data === "$" || data === "$!") boundaryStack.push(data);
543
+ if (data === "$?" || data === "$" || data === "$!")
544
+ boundaryStack.push(data);
311
545
  else if (data === "/$") boundaryStack.pop();
312
546
  continue;
313
547
  }
@@ -330,7 +564,94 @@ function serialize(node) {
330
564
  }
331
565
  function asElement(node) {
332
566
  if (!node) return null;
333
- return node.nodeType === Node.ELEMENT_NODE ? node : null;
567
+ return node.nodeType === Node.ELEMENT_NODE ? liveElement(node) : null;
568
+ }
569
+ var LIVE = /* @__PURE__ */ new WeakMap();
570
+ function liveElement(el) {
571
+ return LIVE.get(el) ?? el;
572
+ }
573
+ function repairClientChildren(children, parentTag, parentPath, out, limit) {
574
+ let repaired = null;
575
+ children.forEach((child, i) => {
576
+ const expansion = repairedForm(child, parentTag);
577
+ if (!expansion) {
578
+ repaired?.push(child);
579
+ return;
580
+ }
581
+ repaired ??= children.slice(0, i);
582
+ repaired.push(...expansion.nodes);
583
+ if (out.length < limit) {
584
+ out.push({
585
+ kind: "structure",
586
+ path: childPath(parentPath, child, i),
587
+ tagName: expansion.offender.tagName,
588
+ parentTagName: invalidParent(expansion.offender, child),
589
+ // No values, on purpose: React reports the same nesting in its own
590
+ // warning with none, and matching it lets the two collapse into one.
591
+ server: null,
592
+ client: null,
593
+ element: expansion.offender
594
+ });
595
+ }
596
+ });
597
+ return repaired ?? children;
598
+ }
599
+ function invalidParent(offender, top) {
600
+ for (let el = offender.parentElement; el; el = el.parentElement) {
601
+ if (isInvalidNesting(el.tagName, offender.tagName)) return el.tagName;
602
+ if (el === top) break;
603
+ }
604
+ return top.tagName;
605
+ }
606
+ function repairedForm(node, parentTag) {
607
+ if (node.nodeType !== Node.ELEMENT_NODE) return null;
608
+ const el = node;
609
+ const selector = PARSER_REPAIRS[el.tagName];
610
+ if (!selector) return null;
611
+ let offender = null;
612
+ try {
613
+ offender = el.querySelector(selector);
614
+ } catch {
615
+ return null;
616
+ }
617
+ if (!offender) return null;
618
+ const html = serializeKeepingTextBoundaries(el);
619
+ const container = parseServerHtml(html, parentTag ?? "div");
620
+ if (container.innerHTML === html) return null;
621
+ const nodes = meaningfulChildNodes(container);
622
+ mapToLive(el, nodes);
623
+ return { nodes, offender };
624
+ }
625
+ function serializeKeepingTextBoundaries(el) {
626
+ const copy = el.cloneNode(true);
627
+ const walker = copy.ownerDocument.createTreeWalker(
628
+ copy,
629
+ NodeFilter.SHOW_TEXT
630
+ );
631
+ const texts = [];
632
+ while (walker.nextNode()) texts.push(walker.currentNode);
633
+ for (const t of texts) {
634
+ if (t.previousSibling?.nodeType === Node.TEXT_NODE) {
635
+ t.parentNode.insertBefore(copy.ownerDocument.createComment(" "), t);
636
+ }
637
+ }
638
+ return copy.outerHTML;
639
+ }
640
+ function mapToLive(live, repaired) {
641
+ const liveEls = [live, ...Array.from(live.querySelectorAll("*"))];
642
+ let i = 0;
643
+ for (const root of repaired) {
644
+ if (root.nodeType !== Node.ELEMENT_NODE) continue;
645
+ const el = root;
646
+ for (const r of [el, ...Array.from(el.querySelectorAll("*"))]) {
647
+ let j = i;
648
+ while (j < liveEls.length && liveEls[j].tagName !== r.tagName) j++;
649
+ if (j < liveEls.length) {
650
+ LIVE.set(r, liveEls[j]);
651
+ i = j + 1;
652
+ }
653
+ }
654
+ }
334
655
  }
335
656
  function elementTag(node) {
336
657
  return node.nodeType === Node.ELEMENT_NODE ? node.tagName : void 0;
@@ -351,179 +672,6 @@ function childPath(parentPath, node, index) {
351
672
  return `${parentPath} > #text[${index}]`;
352
673
  }
353
674
 
354
- // src/core/classify/detectors.ts
355
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
356
- var REACT_ID_RE = /^:[rR][0-9a-z]*:$/;
357
- var HEX_TOKEN_RE = /^[0-9a-f]{16,}$/i;
358
- var RANDOM_DECIMAL_RE = /^0?\.\d{6,}$/;
359
- var NANOID_RE = /^[A-Za-z0-9_-]{10,}$/;
360
- var ARABIC_INDIC_DIGITS = /[٠-٩۰-۹]/;
361
- var LATIN_DIGITS = /[0-9]/;
362
- var BIDI_CONTROLS = /[\u200e\u200f\u061c\u2066-\u2069]/g;
363
- function stripBidiControls(value2) {
364
- return value2.replace(BIDI_CONTROLS, "");
365
- }
366
- function differsOnlyByBidiControls(a, b) {
367
- const sa = stripBidiControls(a);
368
- const sb = stripBidiControls(b);
369
- return a !== b && sa === sb && sa.trim() !== "";
370
- }
371
- function normalizeNumerals(value2) {
372
- return stripBidiControls(value2).replace(/[٠-٩۰-۹٫٬]/g, (ch) => {
373
- if (ch === "\u066B") return ".";
374
- if (ch === "\u066C") return ",";
375
- const code2 = ch.charCodeAt(0);
376
- const zero = code2 >= 1776 ? 1776 : 1632;
377
- return String(code2 - zero);
378
- });
379
- }
380
- function isRandomLike(value2) {
381
- const v = value2.trim();
382
- if (!v) return false;
383
- if (UUID_RE.test(v)) return true;
384
- if (REACT_ID_RE.test(v)) return true;
385
- if (HEX_TOKEN_RE.test(v)) return true;
386
- if (RANDOM_DECIMAL_RE.test(v)) return true;
387
- if (NANOID_RE.test(v) && /[A-Za-z]/.test(v) && /[0-9]/.test(v)) return true;
388
- return false;
389
- }
390
- function looksLikeTime(value2) {
391
- return /\b\d{1,2}:\d{2}(?::\d{2})?(?:\s?[AaPp][Mm])?\b/.test(
392
- normalizeNumerals(value2).trim()
393
- );
394
- }
395
- var ISO_DATE = /^\d{4}-\d{1,2}(?:-\d{1,2})?(?:[T ]\d{1,2}:\d{2}(?::\d{2})?)?/;
396
- var NUMERIC_DATE = /^\d{1,4}[/.-]\d{1,2}[/.-]\d{1,4}$/;
397
- var TIME_OF_DAY = /^\d{1,2}:\d{2}(?::\d{2})?(?:\s?[AaPp]\.?[Mm]\.?)?$/;
398
- var MONTH_NAME = /\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\b/i;
399
- function looksLikeDate(v) {
400
- return ISO_DATE.test(v) || NUMERIC_DATE.test(v) || TIME_OF_DAY.test(v) || MONTH_NAME.test(v) && /\d/.test(v);
401
- }
402
- function toTimestamp(value2) {
403
- const v = value2.trim();
404
- if (!v) return null;
405
- if (/^\d{10,13}$/.test(v)) {
406
- const n = Number(v);
407
- return v.length === 10 ? n * 1e3 : n;
408
- }
409
- if (!looksLikeDate(v)) return null;
410
- const parsed = Date.parse(v);
411
- if (!Number.isNaN(parsed)) return parsed;
412
- if (looksLikeTime(v)) {
413
- const anchored = Date.parse(`1970-01-01 ${v}`);
414
- if (!Number.isNaN(anchored)) return anchored;
415
- }
416
- return null;
417
- }
418
- function hasArabicIndicDigits(value2) {
419
- return ARABIC_INDIC_DIGITS.test(value2);
420
- }
421
- function hasLatinDigits(value2) {
422
- return LATIN_DIGITS.test(value2);
423
- }
424
- var NUMERIC_LIKE = /^[+-]?[\d.,\s\u00a0\u2009]+$/;
425
- function isSameNumberDifferentSeparators(a, b) {
426
- const at = normalizeNumerals(a).trim();
427
- const bt = normalizeNumerals(b).trim();
428
- if (!NUMERIC_LIKE.test(at) || !NUMERIC_LIKE.test(bt)) return false;
429
- const digitsOnly = (s) => s.replace(/\D/g, "");
430
- const da = digitsOnly(at);
431
- const db = digitsOnly(bt);
432
- if (!da || da !== db) return false;
433
- const hasSep = (s) => /[.,\s\u00a0\u2009]/.test(s);
434
- return (hasSep(at) || hasSep(bt)) && a.trim() !== b.trim();
435
- }
436
- var CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set([
437
- "value",
438
- "placeholder",
439
- "title",
440
- "alt",
441
- "label",
442
- "aria-label",
443
- "aria-valuetext",
444
- "content",
445
- "datetime"
446
- ]);
447
- function isContentAttribute(name) {
448
- return CONTENT_ATTRIBUTES.has(name.toLowerCase());
449
- }
450
- var THIRD_PARTY_MARKERS = /googlefc|adsbygoogle|google_ads|googletag|__tcfapi|onetrust|optanon|cookiebot|usercentrics|iubenda|didomi|quantcast|grammarly|data-gramm|gtm-|_hjsettings|hotjar|fullstory|intercom|drift|zendesk|livechat|tawk|hubspot|turnstile|recaptcha/i;
451
- function looksLikeThirdPartyNode(html, tagName) {
452
- const tag = (tagName ?? "").toUpperCase();
453
- if (tag === "IFRAME" || tag === "EMBED" || tag === "OBJECT") return true;
454
- const h = (html ?? "").toLowerCase();
455
- if (!h) return false;
456
- return THIRD_PARTY_MARKERS.test(h) || h.includes("about:blank");
457
- }
458
- var DATE_PARTS_RE = /^(\d{1,4})[/.-](\d{1,2})[/.-](\d{1,4})$/;
459
- function isSameDateDifferentOrder(a, b) {
460
- const partsA = normalizeNumerals(a).trim().match(DATE_PARTS_RE);
461
- const partsB = normalizeNumerals(b).trim().match(DATE_PARTS_RE);
462
- if (!partsA || !partsB) return false;
463
- const setA = [partsA[1], partsA[2], partsA[3]].sort().join("|");
464
- const setB = [partsB[1], partsB[2], partsB[3]].sort().join("|");
465
- return setA === setB && a.trim() !== b.trim();
466
- }
467
- var EXTENSION_ATTRIBUTES = /* @__PURE__ */ new Set([
468
- "cz-shortcut-listen",
469
- "data-gramm",
470
- "data-gramm_editor",
471
- "data-gramm_id",
472
- "data-gr-c-s-loaded",
473
- "data-lt-installed",
474
- "data-new-gr-c-s-check-loaded",
475
- "data-new-gr-c-s-loaded",
476
- "spellcheck-extension",
477
- "bis_register",
478
- "__processed_by_react_dev_tools"
479
- ]);
480
- function isExtensionAttribute(name) {
481
- const n = name.toLowerCase();
482
- if (EXTENSION_ATTRIBUTES.has(n)) return true;
483
- return n.startsWith("data-gr-") || n.startsWith("data-gramm") || n.startsWith("__bis") || n.startsWith("bis_");
484
- }
485
- var BLOCK_TAGS = /* @__PURE__ */ new Set([
486
- "DIV",
487
- "P",
488
- "SECTION",
489
- "ARTICLE",
490
- "UL",
491
- "OL",
492
- "LI",
493
- "TABLE",
494
- "HEADER",
495
- "FOOTER",
496
- "MAIN",
497
- "ASIDE",
498
- "NAV",
499
- "H1",
500
- "H2",
501
- "H3",
502
- "H4",
503
- "H5",
504
- "H6",
505
- "FORM",
506
- "BLOCKQUOTE",
507
- "PRE",
508
- "HR"
509
- ]);
510
- function isInvalidNesting(parentTag, childTag) {
511
- if (!parentTag || !childTag) return false;
512
- const p = parentTag.toUpperCase();
513
- const c = childTag.toUpperCase();
514
- if (p === "P" && BLOCK_TAGS.has(c)) return true;
515
- if (p === "A" && c === "A") return true;
516
- if (p === "BUTTON" && (c === "BUTTON" || c === "A")) return true;
517
- if ((p === "TABLE" || p === "THEAD" || p === "TBODY") && c === "DIV") {
518
- return true;
519
- }
520
- return false;
521
- }
522
- function messageIndicatesInvalidNesting(message) {
523
- if (!message) return false;
524
- return /validateDOMNesting/i.test(message) || /cannot (?:be a|contain).*(?:descendant|child)/i.test(message) || /cannot appear as a (?:child|descendant)/i.test(message);
525
- }
526
-
527
675
  // src/core/classify/messages.ts
528
676
  var code = (text) => ({ code: text });
529
677
  var value = (text) => ({ value: text });
@@ -990,6 +1138,16 @@ var ReportCollector = class {
990
1138
  this.options = options;
991
1139
  this.maxReports = options.maxReports ?? 25;
992
1140
  }
1141
+ /**
1142
+ * Swap the rules, threshold, ignore list and cap for everything reported
1143
+ * from now on. Reports already collected are kept as they are: they were
1144
+ * correct under the options in force when they were made, and re-deciding
1145
+ * them would re-fire `onReport` for mismatches the caller already saw.
1146
+ */
1147
+ configure(options) {
1148
+ this.options = options;
1149
+ this.maxReports = options.maxReports ?? 25;
1150
+ }
993
1151
  /**
994
1152
  * Register a sink. Pass `replay` for sinks that render *state* (the overlay)
995
1153
  * rather than react to *events* (`onReport`, the console): they need the
@@ -1324,5 +1482,5 @@ function reportFromMessage(message, collector, context = {}, options = {}) {
1324
1482
  }
1325
1483
 
1326
1484
  export { BUILT_IN_RULES, CONFIDENCE_THRESHOLD, EN_MESSAGES, ReportCollector, UNKNOWN_CAUSE, UNKNOWN_NO_LOCATION_CAUSE, buildReport, classDetail, classify, code, diffSnapshotAgainstDom, diffTrees, formatConsoleArgs, inspectRoot, isDev, isHydrationMessage, isInternalComponent, isLocationless, param, parseHydrationMessage, parseServerHtml, plainText, renderMessage, reportFromMessage, signatureOf };
1327
- //# sourceMappingURL=chunk-3OAI2ZHY.js.map
1328
- //# sourceMappingURL=chunk-3OAI2ZHY.js.map
1485
+ //# sourceMappingURL=chunk-KP4S6CRL.js.map
1486
+ //# sourceMappingURL=chunk-KP4S6CRL.js.map