@thallylabs/migrate 0.2.39 → 0.2.40

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.
@@ -1,9 +1,9 @@
1
1
  // src/url-run.ts
2
- import { createHash } from "crypto";
3
- import { load as load5 } from "cheerio";
4
- import remarkParse3 from "remark-parse";
5
- import remarkMdx3 from "remark-mdx";
6
- import { unified as unified3 } from "unified";
2
+ import { createHash as createHash4 } from "crypto";
3
+ import { load as load7 } from "cheerio";
4
+ import remarkParse4 from "remark-parse";
5
+ import remarkMdx4 from "remark-mdx";
6
+ import { unified as unified4 } from "unified";
7
7
 
8
8
  // src/navigation.ts
9
9
  import { existsSync, lstatSync, readFileSync, realpathSync as realpathSync2 } from "fs";
@@ -246,6 +246,10 @@ var ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
246
246
  "fill-rule",
247
247
  "stroke",
248
248
  "stroke-width",
249
+ // Some SVG exporters persist React spellings. These remain fixed static
250
+ // drawing attributes; accepting them never grants arbitrary JSX execution.
251
+ "fillrule",
252
+ "cliprule",
249
253
  "stroke-opacity",
250
254
  "stroke-linecap",
251
255
  "stroke-linejoin",
@@ -367,6 +371,8 @@ var ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
367
371
  "limitingconeangle",
368
372
  "aria-hidden",
369
373
  "aria-label",
374
+ "aria-labelledby",
375
+ "aria-describedby",
370
376
  "role",
371
377
  "focusable",
372
378
  // Validated below: only in-document fragment references.
@@ -411,7 +417,7 @@ function isPassiveSvg(content) {
411
417
  const fail = () => {
412
418
  isPassive = false;
413
419
  };
414
- const parser3 = new Parser(
420
+ const parser4 = new Parser(
415
421
  {
416
422
  onopentag(name, attributes) {
417
423
  const element = name.toLowerCase();
@@ -420,7 +426,7 @@ function isPassiveSvg(content) {
420
426
  sawRoot = true;
421
427
  }
422
428
  depth += 1;
423
- openStarts.push(parser3.startIndex);
429
+ openStarts.push(parser4.startIndex);
424
430
  if (element.includes(":") || !ALLOWED_ELEMENTS.has(element)) fail();
425
431
  for (const [rawName, value] of Object.entries(attributes)) {
426
432
  const attribute = rawName.toLowerCase();
@@ -439,7 +445,7 @@ function isPassiveSvg(content) {
439
445
  onclosetag(_name, isImplied) {
440
446
  depth -= 1;
441
447
  const start = openStarts.pop();
442
- if (isImplied && start !== parser3.startIndex) fail();
448
+ if (isImplied && start !== parser4.startIndex) fail();
443
449
  },
444
450
  // DOCTYPE (and with it custom entities) or any other declaration other
445
451
  // than the XML prolog.
@@ -455,8 +461,8 @@ function isPassiveSvg(content) {
455
461
  { xmlMode: true, decodeEntities: true, recognizeSelfClosing: true }
456
462
  );
457
463
  try {
458
- parser3.write(text2);
459
- parser3.end();
464
+ parser4.write(text2);
465
+ parser4.end();
460
466
  } catch {
461
467
  return false;
462
468
  }
@@ -577,6 +583,770 @@ function inferUrlMigrationScope(capture) {
577
583
  return { scopePath: parent, evidence: "source-parent" };
578
584
  }
579
585
 
586
+ // src/url-flight-content.ts
587
+ import { createHash } from "crypto";
588
+ import remarkMdx from "remark-mdx";
589
+ import remarkParse from "remark-parse";
590
+ import remarkGfm from "remark-gfm";
591
+ import { unified } from "unified";
592
+ var parser = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
593
+ var MAX_SOURCE_BYTES = 8e6;
594
+ var sourceTrees = /* @__PURE__ */ new Map();
595
+ var cachedSourceBytes = 0;
596
+ var MAX_CACHED_SOURCE_BYTES = 1e6;
597
+ var UNRESOLVED_FLIGHT_VALUE = /* @__PURE__ */ Symbol("unresolved-flight-value");
598
+ var expansionBudget = () => ({ visited: 0, bytes: 0, exceeded: false });
599
+ function chargeExpansion(budget, value) {
600
+ if (budget.exceeded) return false;
601
+ budget.bytes += Buffer.byteLength(value);
602
+ if (budget.bytes > MAX_SOURCE_BYTES) budget.exceeded = true;
603
+ return !budget.exceeded;
604
+ }
605
+ function chargeProjectedText(budget, value) {
606
+ if (budget.exceeded) return false;
607
+ let bytes = Buffer.byteLength(value);
608
+ for (const character of value) bytes += character === "&" ? 4 : character === "<" || character === ">" ? 3 : character === '"' ? 5 : 0;
609
+ budget.bytes += bytes + 128;
610
+ if (budget.bytes > MAX_SOURCE_BYTES) budget.exceeded = true;
611
+ return !budget.exceeded;
612
+ }
613
+ var flightDocuments = /* @__PURE__ */ new WeakMap();
614
+ function renderedRouteSlot(value) {
615
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => key !== "children")) return void 0;
616
+ const child = value.children;
617
+ if (!Array.isArray(child) || child.length < 2) return void 0;
618
+ const segment = child[0], payload = child[1];
619
+ if (!(typeof segment === "string" || Array.isArray(segment) && segment.every((part) => typeof part === "string"))) return void 0;
620
+ return Array.isArray(payload) && payload[0] === "$" || typeof payload === "string" && /^\$L?[a-f\d]+$/i.test(payload) ? child : void 0;
621
+ }
622
+ function readMigrationFlightDocument($) {
623
+ flightTextRecords($);
624
+ return flightDocuments.get($);
625
+ }
626
+ function literalJson(value) {
627
+ if (Buffer.byteLength(value) > MAX_SOURCE_BYTES) return void 0;
628
+ let depth = 0, quoted = false, escaped = false;
629
+ for (const character of value) {
630
+ if (quoted) {
631
+ if (escaped) escaped = false;
632
+ else if (character === "\\") escaped = true;
633
+ else if (character === '"') quoted = false;
634
+ } else if (character === '"') quoted = true;
635
+ else if (character === "[" || character === "{") {
636
+ if (++depth > 96) return void 0;
637
+ } else if (character === "]" || character === "}") {
638
+ if (--depth < 0) return void 0;
639
+ }
640
+ }
641
+ if (depth !== 0 || quoted) return void 0;
642
+ try {
643
+ return JSON.parse(value);
644
+ } catch {
645
+ return void 0;
646
+ }
647
+ }
648
+ function flightTextRecords($) {
649
+ const cached = flightDocuments.get($);
650
+ if (cached) return cached.sources;
651
+ const chunks = [];
652
+ let size = 0;
653
+ $("script").each((_index, node) => {
654
+ const match = /^\s*self\.__next_f\.push\(\s*(\[[\s\S]*\])\s*\)\s*;?\s*$/.exec($(node).text());
655
+ if (!match) return;
656
+ const envelope = literalJson(match[1]);
657
+ if (Array.isArray(envelope) && envelope.length === 2 && envelope[0] === 1 && typeof envelope[1] === "string") {
658
+ size += Buffer.byteLength(envelope[1]);
659
+ if (size <= MAX_SOURCE_BYTES) chunks.push(envelope[1]);
660
+ }
661
+ });
662
+ if (size > MAX_SOURCE_BYTES) return [];
663
+ const bytes = Buffer.from(chunks.join(""));
664
+ const texts = /* @__PURE__ */ new Map();
665
+ const modules = /* @__PURE__ */ new Map();
666
+ const records = /* @__PURE__ */ new Map();
667
+ let cursor = 0;
668
+ while (cursor < bytes.length && texts.size + modules.size + records.size <= 1e4) {
669
+ const colon = bytes.indexOf(58, cursor);
670
+ if (colon < 0 || colon - cursor > 16 || !/^[a-f\d]*$/i.test(bytes.toString("utf8", cursor, colon))) break;
671
+ const id = bytes.toString("utf8", cursor, colon);
672
+ cursor = colon + 1;
673
+ if (bytes[cursor] === 84) {
674
+ const comma = bytes.indexOf(44, cursor + 1);
675
+ const length = comma < 0 ? "" : bytes.toString("utf8", cursor + 1, comma);
676
+ if (!/^[a-f\d]{1,8}$/i.test(length)) break;
677
+ const end = comma + 1 + Number.parseInt(length, 16);
678
+ if (end > bytes.length) break;
679
+ const text2 = bytes.toString("utf8", comma + 1, end);
680
+ if (text2.includes("\uFFFD")) return [];
681
+ if (texts.has(id) || records.has(id) || modules.has(id)) return [];
682
+ texts.set(id, text2);
683
+ cursor = end;
684
+ } else {
685
+ const newline = bytes.indexOf(10, cursor);
686
+ const end = newline < 0 ? bytes.length : newline;
687
+ const line = bytes.toString("utf8", cursor, end);
688
+ if (id && (texts.has(id) || records.has(id) || modules.has(id))) return [];
689
+ if (line.startsWith("I[")) {
690
+ const moduleRecord = literalJson(line.slice(1));
691
+ if (id && Array.isArray(moduleRecord) && typeof moduleRecord[2] === "string") modules.set(id, moduleRecord[2]);
692
+ } else if (id && /^[\[{"\d-]|^(?:true|false|null)$/.test(line)) records.set(id, literalJson(line));
693
+ cursor = end + 1;
694
+ }
695
+ }
696
+ if (texts.size + modules.size + records.size > 1e4) return [];
697
+ const selected = /* @__PURE__ */ new Set();
698
+ let visited = 0;
699
+ let hasIncompleteSourceTree = false;
700
+ const active = /* @__PURE__ */ new Set();
701
+ const seenReferences = /* @__PURE__ */ new Set();
702
+ const visit = (value, inArticle, depth) => {
703
+ if (depth > 96 || ++visited > 1e5) {
704
+ hasIncompleteSourceTree = true;
705
+ return;
706
+ }
707
+ if (typeof value === "string") {
708
+ const reference = /^\$L?([a-f\d]+)$/i.exec(value)?.[1];
709
+ if (reference && active.has(reference)) hasIncompleteSourceTree = true;
710
+ const identity = `${reference}:${inArticle}`;
711
+ if (reference && records.has(reference) && !active.has(reference) && !seenReferences.has(identity)) {
712
+ seenReferences.add(identity);
713
+ active.add(reference);
714
+ visit(records.get(reference), inArticle, depth + 1);
715
+ active.delete(reference);
716
+ }
717
+ return;
718
+ }
719
+ if (Array.isArray(value)) {
720
+ const moduleId = value[0] === "$" && typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
721
+ const component = moduleId ? modules.get(moduleId) : void 0;
722
+ const isArticle = inArticle || component === "PageArticle";
723
+ const props = value[3];
724
+ if (isArticle && component === "CopyMarkdownButton" && props && typeof props === "object" && !Array.isArray(props)) {
725
+ const reference = /^\$([a-f\d]+)$/i.exec(String(props.content ?? ""))?.[1];
726
+ if (reference && texts.has(reference)) selected.add(texts.get(reference));
727
+ }
728
+ if (value[0] === "$" && props && typeof props === "object" && !Array.isArray(props)) {
729
+ visit(props.children, isArticle, depth + 1);
730
+ return;
731
+ }
732
+ for (const child of value) visit(child, isArticle, depth + 1);
733
+ } else if (depth === 0 && value && typeof value === "object" && Array.isArray(value.f)) {
734
+ visit(value.f, inArticle, depth + 1);
735
+ } else {
736
+ const slot = renderedRouteSlot(value);
737
+ if (slot) visit(slot, inArticle, depth + 1);
738
+ }
739
+ };
740
+ visit(records.get("0"), false, 0);
741
+ const sources = visited > 1e5 || hasIncompleteSourceTree ? [] : [...selected];
742
+ flightDocuments.set($, { texts, modules, records, sources });
743
+ return sources;
744
+ }
745
+ function descendants(root) {
746
+ const result = [];
747
+ let exceeded = false;
748
+ const visit = (node, depth) => {
749
+ if (depth > 96 || result.length >= 1e5) {
750
+ exceeded = true;
751
+ return;
752
+ }
753
+ result.push(node);
754
+ for (const child of node.children ?? []) visit(child, depth + 1);
755
+ };
756
+ visit(root, 0);
757
+ return exceeded ? [] : result;
758
+ }
759
+ function sourceText(node) {
760
+ if (["mdxjsEsm", "mdxFlowExpression", "mdxTextExpression"].includes(node.type)) return "";
761
+ return node.value ?? (node.children ?? []).map(sourceText).join("");
762
+ }
763
+ function sourceNodes(source) {
764
+ const key = createHash("sha256").update(source).digest("hex");
765
+ const cached = sourceTrees.get(key);
766
+ if (cached) {
767
+ sourceTrees.delete(key);
768
+ sourceTrees.set(key, cached);
769
+ return cached.nodes;
770
+ }
771
+ let nodes;
772
+ try {
773
+ nodes = descendants(parser.parse(source));
774
+ } catch {
775
+ nodes = [];
776
+ }
777
+ const bytes = Buffer.byteLength(source);
778
+ if (bytes <= MAX_CACHED_SOURCE_BYTES) {
779
+ while (sourceTrees.size >= 4 || cachedSourceBytes + bytes > MAX_CACHED_SOURCE_BYTES) {
780
+ const oldest = sourceTrees.keys().next().value;
781
+ if (oldest === void 0) break;
782
+ cachedSourceBytes -= sourceTrees.get(oldest).bytes;
783
+ sourceTrees.delete(oldest);
784
+ }
785
+ sourceTrees.set(key, { bytes, nodes });
786
+ cachedSourceBytes += bytes;
787
+ }
788
+ return nodes;
789
+ }
790
+ function codeText(value) {
791
+ return value.replace(/\n+$/, "");
792
+ }
793
+ function labelText(value) {
794
+ return value.trim().replace(/\s+/g, " ");
795
+ }
796
+ function renderedCode($, node) {
797
+ const copy = $(node).clone();
798
+ copy.find("code > span.line").each((_index, line) => {
799
+ if (!$(line).children().length && $(line).text() === " ") $(line).text("");
800
+ });
801
+ return codeText(copy.text());
802
+ }
803
+ function isOrderedSubsequence(values, source) {
804
+ let cursor = 0;
805
+ for (const value of values) {
806
+ while (cursor < source.length && source[cursor] !== value) cursor += 1;
807
+ if (cursor === source.length) return false;
808
+ cursor += 1;
809
+ }
810
+ return true;
811
+ }
812
+ function matchesSourceCode(rendered, source) {
813
+ const actual = codeText(rendered).split("\n"), declared = codeText(source).split("\n");
814
+ if (actual.length !== declared.length) return false;
815
+ return declared.every((line, index) => {
816
+ const directive = /\/\/\s*\[!code (?:\+\+|--|highlight|focus|error|warning)(?::\d+)?\]\s*$/.exec(line);
817
+ return directive ? line.slice(0, directive.index).replace(/[\t ]+$/, "") === actual[index].replace(/[\t ]+$/, "") : line === actual[index];
818
+ });
819
+ }
820
+ function matchesSourceCodeSequence(rendered, source) {
821
+ let cursor = 0;
822
+ for (const value of rendered) {
823
+ while (cursor < source.length && !matchesSourceCode(value, source[cursor].value ?? "")) cursor += 1;
824
+ if (cursor === source.length) return false;
825
+ cursor += 1;
826
+ }
827
+ return true;
828
+ }
829
+ function hasStaticProps(document, element, allowed) {
830
+ const common = /* @__PURE__ */ new Set(["children", "className", "style", "id", ...allowed]);
831
+ return Object.entries(element.props).every(([key, value]) => key === "ref" ? flightValue(document, value) === void 0 : common.has(key));
832
+ }
833
+ function flightValue(document, value) {
834
+ const seen = /* @__PURE__ */ new Set();
835
+ for (let depth = 0; depth < 32 && typeof value === "string"; depth += 1) {
836
+ if (value.startsWith("$$")) return value.slice(1);
837
+ const reference = /^\$L?([a-f\d]+)(?::(.+))?$/i.exec(value);
838
+ if (!reference) return value === "$undefined" ? void 0 : value.startsWith("$") ? UNRESOLVED_FLIGHT_VALUE : value;
839
+ if (seen.has(value)) return UNRESOLVED_FLIGHT_VALUE;
840
+ seen.add(value);
841
+ if (document.texts.has(reference[1])) return reference[2] ? UNRESOLVED_FLIGHT_VALUE : document.texts.get(reference[1]);
842
+ if (!document.records.has(reference[1]) || document.records.get(reference[1]) === void 0) return UNRESOLVED_FLIGHT_VALUE;
843
+ value = document.records.get(reference[1]);
844
+ if (reference[2]) for (const key of reference[2].split(":")) {
845
+ if (key === "props" && Array.isArray(value) && value[0] === "$" && typeof value[1] === "string" && value[3] && typeof value[3] === "object" && !Array.isArray(value[3])) {
846
+ value = value[3];
847
+ continue;
848
+ }
849
+ if (!value || typeof value !== "object" || !Object.hasOwn(value, key)) return UNRESOLVED_FLIGHT_VALUE;
850
+ value = value[key];
851
+ }
852
+ }
853
+ return typeof value === "string" && /^\$L?[a-f\d]+(?::|$)/i.test(value) ? UNRESOLVED_FLIGHT_VALUE : value;
854
+ }
855
+ function resolveMigrationFlightValue(document, input) {
856
+ const value = flightValue(document, input);
857
+ return value === UNRESOLVED_FLIGHT_VALUE ? { resolved: false, value: void 0 } : { resolved: true, value };
858
+ }
859
+ function flightElement(document, value) {
860
+ const node = flightValue(document, value);
861
+ if (!Array.isArray(node) || node[0] !== "$" || typeof node[1] !== "string" || !node[3] || typeof node[3] !== "object" || Array.isArray(node[3])) return void 0;
862
+ const moduleId = /^\$L?([a-f\d]+)$/i.exec(node[1])?.[1];
863
+ const symbol = moduleId ? document.records.get(moduleId) : void 0;
864
+ const kind = moduleId ? document.modules.get(moduleId) ?? (symbol === "$Sreact.fragment" ? "Fragment" : symbol === "$Sreact.suspense" ? "Suspense" : void 0) : node[1];
865
+ return kind ? { kind, props: node[3] } : void 0;
866
+ }
867
+ function visitFlightTree(document, root, visit, skipUnresolvedBranches = false) {
868
+ const budget = expansionBudget();
869
+ let hasUnresolvedValue = false;
870
+ const walk = (raw, depth) => {
871
+ if (budget.exceeded) return;
872
+ if (depth > 96 || ++budget.visited > 1e5) {
873
+ budget.exceeded = true;
874
+ return;
875
+ }
876
+ const value = flightValue(document, raw);
877
+ if (value === UNRESOLVED_FLIGHT_VALUE) {
878
+ hasUnresolvedValue = true;
879
+ return;
880
+ }
881
+ if (typeof value === "string" && !chargeExpansion(budget, value)) return;
882
+ const element = flightElement(document, value);
883
+ if (element) {
884
+ if (visit(element, value, budget) !== false) walk(element.props.children, depth + 1);
885
+ } else if (Array.isArray(value)) {
886
+ for (const child of value) walk(child, depth + 1);
887
+ } else if (depth === 0 && value && typeof value === "object" && Array.isArray(value.f)) {
888
+ walk(value.f, depth + 1);
889
+ } else {
890
+ const slot = renderedRouteSlot(value);
891
+ if (slot) walk(slot, depth + 1);
892
+ }
893
+ };
894
+ walk(root, 0);
895
+ return !budget.exceeded && (skipUnresolvedBranches || !hasUnresolvedValue);
896
+ }
897
+ function flightLiteralText(document, raw, depth = 0, code = false, budget = expansionBudget()) {
898
+ if (budget.exceeded || depth > 96 || ++budget.visited > 1e5) {
899
+ budget.exceeded = true;
900
+ return null;
901
+ }
902
+ const value = flightValue(document, raw);
903
+ if (value === null || value === void 0 || typeof value === "boolean") return "";
904
+ if (typeof value === "string" || typeof value === "number") return chargeExpansion(budget, String(value)) ? String(value) : null;
905
+ const element = flightElement(document, value);
906
+ if (element) {
907
+ if (["script", "style"].includes(element.kind) || element.props.dangerouslySetInnerHTML !== void 0) return null;
908
+ if (code && element.kind === "span" && element.props.className === "line" && element.props.children === " ") return "";
909
+ return flightLiteralText(document, element.props.children, depth + 1, code, budget);
910
+ }
911
+ if (!Array.isArray(value)) return null;
912
+ let text2 = "";
913
+ for (const child of value) {
914
+ const literal = flightLiteralText(document, child, depth + 1, code, budget);
915
+ if (literal === null) return null;
916
+ text2 += literal;
917
+ }
918
+ return text2;
919
+ }
920
+ function flightCode(document, element, budget = expansionBudget()) {
921
+ if (!["CodeBlock", "Pre", "pre"].includes(element.kind)) return null;
922
+ if (!hasStaticProps(document, element, ["language", "lang", "data-language", "tabIndex", "icon"])) return null;
923
+ const value = flightLiteralText(document, element.props.children, 0, true, budget);
924
+ if (value === null) return null;
925
+ const language = [element.props.language, element.props.lang, element.props["data-language"]].find((value2) => typeof value2 === "string" && /^[A-Za-z0-9_+-]{1,64}$/.test(value2));
926
+ return { value, ...typeof language === "string" ? { language } : {} };
927
+ }
928
+ function flightTabCode(document, raw, depth = 0, budget = expansionBudget()) {
929
+ if (budget.exceeded || depth > 96 || ++budget.visited > 1e5) {
930
+ budget.exceeded = true;
931
+ return null;
932
+ }
933
+ const value = flightValue(document, raw);
934
+ if (value === null || value === void 0 || typeof value === "boolean" || typeof value === "string" && !value.trim()) return [];
935
+ const element = flightElement(document, value);
936
+ if (element) {
937
+ const code = flightCode(document, element, budget);
938
+ if (code) return [code];
939
+ if (!["div", "figure"].includes(element.kind) || !hasStaticProps(document, element, []) || element.props.id !== void 0) return null;
940
+ return flightTabCode(document, element.props.children, depth + 1, budget);
941
+ }
942
+ if (!Array.isArray(value)) return null;
943
+ const result = [];
944
+ for (const child of value) {
945
+ const code = flightTabCode(document, child, depth + 1, budget);
946
+ if (code === null) return null;
947
+ result.push(...code);
948
+ }
949
+ return result;
950
+ }
951
+ function htmlLiteral(value) {
952
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
953
+ }
954
+ function flightStaticHtml(document, source, prefix, budget = expansionBudget()) {
955
+ let visited = 0;
956
+ const reserve = (bytes) => {
957
+ budget.bytes += bytes;
958
+ if (budget.bytes > MAX_SOURCE_BYTES) budget.exceeded = true;
959
+ };
960
+ const literal = (value) => {
961
+ if (budget.exceeded) return "";
962
+ let bytes = Buffer.byteLength(value);
963
+ for (const character of value) bytes += character === "&" ? 4 : character === "<" || character === ">" ? 3 : character === '"' ? 5 : 0;
964
+ reserve(bytes);
965
+ return budget.exceeded ? "" : htmlLiteral(value);
966
+ };
967
+ let tabIndex = 0;
968
+ const render = (raw, depth) => {
969
+ if (budget.exceeded || depth > 96 || ++visited > 1e5) return null;
970
+ const value = flightValue(document, raw);
971
+ if (value === null || value === void 0 || typeof value === "boolean") return "";
972
+ if (typeof value === "string" || typeof value === "number") return literal(String(value));
973
+ const element = flightElement(document, value);
974
+ if (!element) {
975
+ if (!Array.isArray(value)) return null;
976
+ const children2 = value.map((child) => render(child, depth + 1));
977
+ return children2.some((child) => child === null) ? null : children2.join("");
978
+ }
979
+ reserve(128);
980
+ if (budget.exceeded) return null;
981
+ const code = flightCode(document, element, budget);
982
+ if (code) return `<pre${code.language ? ` data-language="${literal(code.language)}"` : ""}><code>${literal(code.value)}</code></pre>`;
983
+ if (element.kind === "svg") {
984
+ const svg = (raw2, depth2) => {
985
+ if (budget.exceeded || depth2 > 96 || ++visited > 1e5) return null;
986
+ const value2 = flightValue(document, raw2);
987
+ if (value2 === null || value2 === void 0 || typeof value2 === "boolean") return "";
988
+ if (typeof value2 === "string" || typeof value2 === "number") return literal(String(value2));
989
+ const node = flightElement(document, value2);
990
+ if (!node) {
991
+ if (!Array.isArray(value2)) return null;
992
+ const children3 = value2.map((child) => svg(child, depth2 + 1));
993
+ return children3.some((child) => child === null) ? null : children3.join("");
994
+ }
995
+ if (!/^[a-z][a-zA-Z\d]*$/.test(node.kind)) return null;
996
+ const attributes2 = [];
997
+ for (const [rawName, rawValue] of Object.entries(node.props)) {
998
+ if (rawName === "children") continue;
999
+ const value3 = flightValue(document, rawValue);
1000
+ if (value3 === void 0) continue;
1001
+ if (!["string", "number", "boolean"].includes(typeof value3)) return null;
1002
+ const spellings = {
1003
+ className: "class",
1004
+ strokeWidth: "stroke-width",
1005
+ strokeLinecap: "stroke-linecap",
1006
+ strokeLinejoin: "stroke-linejoin",
1007
+ strokeMiterlimit: "stroke-miterlimit",
1008
+ strokeDasharray: "stroke-dasharray",
1009
+ strokeDashoffset: "stroke-dashoffset",
1010
+ fillRule: "fill-rule",
1011
+ clipRule: "clip-rule",
1012
+ fillOpacity: "fill-opacity",
1013
+ strokeOpacity: "stroke-opacity",
1014
+ clipPath: "clip-path",
1015
+ fontSize: "font-size"
1016
+ };
1017
+ const name = spellings[rawName] ?? rawName;
1018
+ if (!/^[a-zA-Z][a-zA-Z\d:._-]*$/.test(name)) return null;
1019
+ reserve(name.length + 4);
1020
+ if (budget.exceeded) return null;
1021
+ attributes2.push(` ${name}="${literal(String(value3))}"`);
1022
+ }
1023
+ const children2 = svg(node.props.children, depth2 + 1);
1024
+ return children2 === null ? null : `<${node.kind}${attributes2.join("")}>${children2}</${node.kind}>`;
1025
+ };
1026
+ const html = svg(value, depth + 1);
1027
+ return html !== null && isPassiveSvg(Buffer.from(html)) ? html : null;
1028
+ }
1029
+ if (element.kind === "ThemedImage") {
1030
+ if (Object.keys(element.props).some((key) => !["lightSrc", "darkSrc", "alt", "caption"].includes(key))) return null;
1031
+ const light = flightValue(document, element.props.lightSrc), dark = flightValue(document, element.props.darkSrc);
1032
+ const alt = flightValue(document, element.props.alt);
1033
+ if (typeof light !== "string" || typeof dark !== "string" || typeof alt !== "string") return null;
1034
+ const caption = render(element.props.caption, depth + 1);
1035
+ if (caption === null) return null;
1036
+ return `<figure><picture><source media="(prefers-color-scheme: dark)" srcset="${literal(dark)}"><img src="${literal(light)}" alt="${literal(alt)}"></picture>${caption ? `<figcaption>${caption}</figcaption>` : ""}</figure>`;
1037
+ }
1038
+ if (element.kind === "Tabs") {
1039
+ if (!hasStaticProps(document, element, ["items", "defaultIndex", "groupId", "persist"])) return null;
1040
+ const children2 = flightValue(document, element.props.children);
1041
+ const tabs = flightElement(document, children2) ? [children2] : Array.isArray(children2) ? children2 : [];
1042
+ const labels2 = flightValue(document, element.props.items);
1043
+ const group = tabIndex++;
1044
+ const choices = [];
1045
+ for (const [index, rawTab] of tabs.entries()) {
1046
+ const tab = flightElement(document, rawTab);
1047
+ const label = tab?.props.value ?? tab?.props.title;
1048
+ if (tab?.kind !== "Tab" || !hasStaticProps(document, tab, ["value", "title"]) || typeof label !== "string" || !label.trim()) return null;
1049
+ const html = render(tab.props.children, depth + 1);
1050
+ if (html === null) return null;
1051
+ choices.push({ label, html, id: `${prefix}-${group}-${index}` });
1052
+ }
1053
+ if (!choices.length || labels2 !== void 0 && (!Array.isArray(labels2) || JSON.stringify(labels2) !== JSON.stringify(choices.map((choice) => choice.label)))) return null;
1054
+ return `<div><div role="tablist">${choices.map((choice) => `<button role="tab" id="${choice.id}-tab" aria-controls="${choice.id}-panel">${literal(choice.label)}</button>`).join("")}</div>${choices.map((choice) => `<div role="tabpanel" id="${choice.id}-panel" aria-labelledby="${choice.id}-tab">${choice.html}</div>`).join("")}</div>`;
1055
+ }
1056
+ const children = render(element.props.children, depth + 1);
1057
+ if (children === null) return null;
1058
+ if (["Fragment", "Suspense", "Accordions"].includes(element.kind)) return hasStaticProps(document, element, ["type", "defaultValue", "collapsible"]) ? children : null;
1059
+ if (element.kind === "Accordion") {
1060
+ if (!hasStaticProps(document, element, ["title", "defaultOpen", "value"])) return null;
1061
+ const title = flightLiteralText(document, element.props.title);
1062
+ return title === null ? null : `<details open><summary>${literal(title)}</summary>${children}</details>`;
1063
+ }
1064
+ if (element.kind === "Callout") {
1065
+ if (!hasStaticProps(document, element, ["title", "type", "icon"])) return null;
1066
+ const title = element.props.title === void 0 ? "" : flightLiteralText(document, element.props.title);
1067
+ return title === null ? null : `<blockquote>${title ? `<p><strong>${literal(title)}</strong></p>` : ""}${children}</blockquote>`;
1068
+ }
1069
+ const isStaticLink = element.kind === "default" && typeof element.props.href === "string" && Object.keys(element.props).every((key) => ["href", "children", "ref", "prefetch", "replace", "scroll", "className", "target", "rel"].includes(key)) && ["ref", "prefetch", "replace", "scroll"].every((key) => flightValue(document, element.props[key]) === void 0);
1070
+ const tag = element.kind === "Heading" ? element.props.as ?? "h2" : element.kind === "Link" || isStaticLink ? "a" : element.kind;
1071
+ if (typeof tag !== "string" || !/^(?:p|div|section|span|em|strong|a|ul|ol|li|blockquote|table|thead|tbody|tr|td|th|code|h[1-6]|br|hr|img|picture|source|figure|figcaption|sup|sub|details|summary)$/.test(tag) || element.props.dangerouslySetInnerHTML !== void 0) return null;
1072
+ const semanticAttributes = tag === "a" ? ["href", "title", "target", "rel"] : tag === "img" ? ["src", "alt", "width", "height", "srcSet", "srcset", "sizes", "title", "loading", "decoding"] : tag === "source" ? ["srcSet", "srcset", "sizes", "media", "type", "width", "height"] : tag === "details" ? ["open"] : [];
1073
+ if (!isStaticLink && !hasStaticProps(document, element, [...semanticAttributes, ...element.kind === "Heading" ? ["as"] : []])) return null;
1074
+ const attributes = [];
1075
+ for (const name of ["id", ...semanticAttributes]) {
1076
+ if (element.props[name] === void 0) continue;
1077
+ const attribute = flightValue(document, element.props[name]);
1078
+ if (name === "open" && typeof attribute === "boolean") {
1079
+ if (attribute) attributes.push(" open");
1080
+ continue;
1081
+ }
1082
+ if (typeof attribute !== "string" && typeof attribute !== "number") return null;
1083
+ reserve(name.length + 4);
1084
+ if (budget.exceeded) return null;
1085
+ attributes.push(` ${name === "srcSet" ? "srcset" : name}="${literal(String(attribute))}"`);
1086
+ }
1087
+ if (["br", "hr", "img", "source"].includes(tag)) return `<${tag}${attributes.join("")}>`;
1088
+ return `<${tag}${attributes.join("")}>${children}</${tag}>`;
1089
+ };
1090
+ const result = render(source, 0);
1091
+ return budget.exceeded ? null : result;
1092
+ }
1093
+ function recoverFlightDisclosures($, root, document, article, projectionBudget) {
1094
+ const disclosures = [];
1095
+ if (!visitFlightTree(document, article, (element) => {
1096
+ if (element.kind === "Accordion") {
1097
+ disclosures.push(element);
1098
+ return false;
1099
+ }
1100
+ })) return;
1101
+ const controls = root.find("button[aria-expanded][aria-controls]").toArray();
1102
+ if (controls.length !== disclosures.length) return;
1103
+ const projections = [];
1104
+ for (const [index, control] of controls.entries()) {
1105
+ const button = $(control), source = disclosures[index];
1106
+ if (!hasStaticProps(document, source, ["title", "defaultOpen", "value"])) continue;
1107
+ const title = flightLiteralText(document, source.props.title);
1108
+ const id = button.attr("id"), target2 = button.attr("aria-controls");
1109
+ const panel = root.find("[id]").filter((_index, node) => $(node).attr("id") === target2);
1110
+ if (!id || !target2 || title === null || labelText(title) !== labelText(button.text()) || panel.length !== 1 || panel.attr("role") !== "region" || panel.attr("aria-labelledby") !== id || root.find("[id]").filter((_index, node) => $(node).attr("id") === id).length !== 1 || panel.is('[aria-busy="true"]') || panel.find('[aria-busy="true"],[role="progressbar"]').length) continue;
1111
+ let html;
1112
+ if (!panel.text().trim() && !panel.children().length) {
1113
+ const prefix = `thally-flight-${createHash("sha256").update(target2).digest("hex").slice(0, 16)}`;
1114
+ const rendered = flightStaticHtml(document, source.props.children, prefix, projectionBudget);
1115
+ if (rendered === null || !rendered.trim()) continue;
1116
+ html = rendered;
1117
+ }
1118
+ projections.push({ control, panel, html });
1119
+ }
1120
+ if (projectionBudget.exceeded) return;
1121
+ for (const { control, panel, html } of projections) {
1122
+ const button = $(control);
1123
+ if (html !== void 0) panel.html(html);
1124
+ control.name = "span";
1125
+ button.removeAttr("aria-expanded aria-controls role type");
1126
+ panel.removeAttr("hidden aria-hidden");
1127
+ button.parent("h1,h2,h3,h4,h5,h6").attr("data-thally-static-disclosure-heading", "");
1128
+ }
1129
+ }
1130
+ function recoverFlightTreeTabs($, root, projectionBudget) {
1131
+ flightTextRecords($);
1132
+ const document = flightDocuments.get($);
1133
+ if (!document) return;
1134
+ const articles = [];
1135
+ if (!visitFlightTree(document, document.records.get("0"), (element, raw) => {
1136
+ if (element.kind === "PageArticle" || element.kind === "article") {
1137
+ articles.push(raw);
1138
+ return false;
1139
+ }
1140
+ }, true)) return;
1141
+ const title = labelText(root.find("h1").first().text());
1142
+ const authored = root.children(".prose").length === 1 ? root.children(".prose") : root;
1143
+ const renderedParagraphs = authored.find("p").toArray().filter((node) => !$(node).parents("pre,button,nav,aside,footer").length).map((node) => labelText($(node).text())).filter(Boolean);
1144
+ const visibleCode = root.find("pre").toArray().map((node) => renderedCode($, node));
1145
+ const candidates = articles.filter((article) => {
1146
+ const headings = [], paragraphs = [], code = [], disclosureLabels = [];
1147
+ const complete2 = visitFlightTree(document, article, (element, _raw, budget) => {
1148
+ if (element.kind === "Accordion") {
1149
+ const label = flightLiteralText(document, element.props.title, 0, false, budget);
1150
+ if (label !== null) disclosureLabels.push(labelText(label));
1151
+ }
1152
+ if (element.kind === "p") paragraphs.push(labelText(flightLiteralText(document, element.props.children, 0, false, budget) ?? ""));
1153
+ const block = flightCode(document, element, budget);
1154
+ if (block) {
1155
+ code.push(codeText(block.value));
1156
+ return false;
1157
+ }
1158
+ });
1159
+ const completeHeadings = visitFlightTree(document, article, (element, _raw, budget) => {
1160
+ if (element.kind === "Accordion" || ["CodeBlock", "pre", "Pre"].includes(element.kind)) return false;
1161
+ const heading = /^h[1-6]$/.test(element.kind) ? element.kind : element.kind === "Heading" ? element.props.as : void 0;
1162
+ if (typeof heading === "string" && /^h[1-6]$/.test(heading)) headings.push(`${heading}:${labelText(flightLiteralText(document, element.props.children, 0, false, budget) ?? "")}`);
1163
+ });
1164
+ const renderedHeadings = root.find("h1,h2,h3,h4,h5,h6").toArray().filter((node) => {
1165
+ const control = $(node).children("button[aria-expanded][aria-controls]").first();
1166
+ if (!control.length || !disclosureLabels.includes(labelText(control.text()))) return true;
1167
+ const copy = $(node).clone();
1168
+ copy.find("button").remove();
1169
+ const id = control.attr("id"), target2 = control.attr("aria-controls");
1170
+ const panel = root.find("[id]").filter((_index, candidate) => $(candidate).attr("id") === target2);
1171
+ return Boolean(copy.text().trim() || !id || panel.length !== 1 || panel.attr("aria-labelledby") !== id);
1172
+ }).map((node) => `${node.name}:${labelText($(node).text())}`);
1173
+ return complete2 && completeHeadings && title && headings[0] === `h1:${title}` && headings.length === renderedHeadings.length && headings.every((heading, index) => heading === renderedHeadings[index]) && isOrderedSubsequence(renderedParagraphs, paragraphs) && isOrderedSubsequence(visibleCode, code);
1174
+ });
1175
+ if (candidates.length !== 1) return;
1176
+ recoverFlightDisclosures($, root, document, candidates[0], projectionBudget);
1177
+ const sourceGroups = [];
1178
+ if (!visitFlightTree(document, candidates[0], (element, _raw, budget) => {
1179
+ if (element.kind !== "Tabs") return;
1180
+ if (!hasStaticProps(document, element, ["items", "defaultIndex", "groupId", "persist"])) {
1181
+ sourceGroups.push([]);
1182
+ return false;
1183
+ }
1184
+ const children = flightValue(document, element.props.children);
1185
+ const nodes = flightElement(document, children) ? [children] : Array.isArray(children) ? children : [];
1186
+ const group = [];
1187
+ for (const raw of nodes) {
1188
+ const tab = flightElement(document, raw);
1189
+ const label = tab?.props.value ?? tab?.props.title;
1190
+ const code = tab?.kind === "Tab" && hasStaticProps(document, tab, ["value", "title"]) ? flightTabCode(document, tab.props.children, 0, budget) : null;
1191
+ if (typeof label !== "string" || !label.trim() || !code?.length) {
1192
+ sourceGroups.push([]);
1193
+ return false;
1194
+ }
1195
+ group.push({ label: labelText(label), code });
1196
+ }
1197
+ const items = flightValue(document, element.props.items);
1198
+ if (items !== void 0 && (!Array.isArray(items) || JSON.stringify(items) !== JSON.stringify(group.map((tab) => tab.label))) || items === void 0 && nodes.some((raw) => typeof flightElement(document, raw)?.props.title !== "string")) sourceGroups.push([]);
1199
+ else sourceGroups.push(group);
1200
+ return false;
1201
+ })) return;
1202
+ const groups = root.find('[role="tablist"]').toArray();
1203
+ if (sourceGroups.length !== groups.length) return;
1204
+ groups.forEach((group, index) => {
1205
+ const tabs = $(group).find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === group);
1206
+ const source = sourceGroups[index];
1207
+ if (!tabs.length || source.length !== tabs.length) return;
1208
+ const panels = [];
1209
+ for (const [position, tab] of tabs.entries()) {
1210
+ const id = $(tab).attr("id"), target2 = $(tab).attr("aria-controls");
1211
+ const panel = root.find("[id]").filter((_index, node) => $(node).attr("id") === target2);
1212
+ if (!id || !target2 || panel.length !== 1 || panel.attr("role") !== "tabpanel" || panel.attr("aria-labelledby") !== id || !panel.parents().toArray().includes($(group).parent()[0]) || labelText($(tab).text()) !== source[position].label || panel.is('[aria-busy="true"]') || panel.find('[aria-busy="true"],[role="progressbar"]').length) return;
1213
+ if (panel.text().trim() || panel.children().length) {
1214
+ const copy = panel.clone();
1215
+ copy.find("pre,button,style,script").remove();
1216
+ const blocks = panel.find("pre").toArray().map((node) => renderedCode($, node));
1217
+ if (copy.text().trim() || blocks.length !== source[position].code.length || !blocks.every((block, index2) => block === codeText(source[position].code[index2].value))) return;
1218
+ }
1219
+ panels.push(panel);
1220
+ }
1221
+ if (new Set(panels.map((panel) => panel[0])).size !== panels.length) return;
1222
+ panels.forEach((panel, position) => {
1223
+ if (panel.text().trim() || panel.children().length) return;
1224
+ for (const block of source[position].code) {
1225
+ if (!chargeProjectedText(projectionBudget, block.value)) return;
1226
+ }
1227
+ for (const block of source[position].code) {
1228
+ const pre = $("<pre></pre>").append($("<code></code>").text(block.value));
1229
+ if (block.language) pre.attr("data-language", block.language);
1230
+ panel.append(pre);
1231
+ }
1232
+ });
1233
+ });
1234
+ const declarations = [];
1235
+ if (!visitFlightTree(document, candidates[0], (element, _raw, budget) => {
1236
+ const code = flightCode(document, element, budget);
1237
+ if (code) {
1238
+ declarations.push(code);
1239
+ return false;
1240
+ }
1241
+ })) return;
1242
+ root.find("pre").each((_index, node) => {
1243
+ const payload = renderedCode($, node);
1244
+ const matches = declarations.filter((code) => codeText(code.value) === payload);
1245
+ const languages = new Set(matches.map((code) => code.language ?? ""));
1246
+ const language = matches[0]?.language;
1247
+ if (languages.size !== 1 || !language || $(node).attr("language") || $(node).attr("data-language") || $(node).find('code[language],code[data-language],code[class*="language-"],code[class*="lang-"]').length || /(?:^|\s)(?:language-|lang-)/.test($(node).attr("class") ?? "")) return;
1248
+ $(node).attr("data-language", language);
1249
+ });
1250
+ }
1251
+ function sourceTabs(nodes) {
1252
+ return nodes.filter((node) => node.name === "Tabs").map((group) => {
1253
+ const result = [];
1254
+ for (const tab of group.children ?? []) {
1255
+ if (tab.type === "text" && !tab.value?.trim()) continue;
1256
+ if (tab.name !== "Tab") return [];
1257
+ const label = tab.attributes?.find((attribute) => attribute.type === "mdxJsxAttribute" && attribute.name === "value")?.value;
1258
+ if (typeof label !== "string" || !label.trim() || tab.attributes?.some((attribute) => attribute.type !== "mdxJsxAttribute" || !["value", "title"].includes(attribute.name ?? "") || typeof attribute.value !== "string")) return [];
1259
+ const content = (tab.children ?? []).filter((child) => child.type !== "text" || child.value?.trim());
1260
+ if (!content.length || content.some((child) => child.type !== "code")) return [];
1261
+ result.push({ label: labelText(label), code: content });
1262
+ }
1263
+ return result;
1264
+ });
1265
+ }
1266
+ function recoverFlightArticleCode($, root) {
1267
+ root.find("[data-thally-static-disclosure-heading]").removeAttr("data-thally-static-disclosure-heading");
1268
+ if (root.find('[role="tablist"]').length > 256 || root.find('[role="tab"]').length > 1024) return;
1269
+ const projectionBudget = expansionBudget();
1270
+ recoverFlightTreeTabs($, root, projectionBudget);
1271
+ const renderedHeadings = root.find("h1,h2,h3,h4,h5,h6").toArray().filter((node) => $(node).attr("data-thally-static-disclosure-heading") === void 0);
1272
+ const headingText = renderedHeadings.map((node) => labelText($(node).text()));
1273
+ const existingCode = root.find("pre").toArray().map((node) => renderedCode($, node));
1274
+ if (!headingText.length || !existingCode.length) return;
1275
+ const prose = root.clone();
1276
+ prose.find("pre,button,script,style,nav,footer,aside").remove();
1277
+ const renderedProse = labelText(prose.text());
1278
+ const candidates = [];
1279
+ for (const source2 of flightTextRecords($)) {
1280
+ if (!/^#\s+\S/m.test(source2) || !/^\s*`{3,}/m.test(source2)) continue;
1281
+ const nodes = sourceNodes(source2);
1282
+ const headings = nodes.filter((node) => node.type === "heading").map((node, index) => {
1283
+ const text2 = labelText(sourceText(node));
1284
+ const anchor = /\s+\[#([A-Za-z\d_.:-]+)\]$/.exec(text2);
1285
+ return anchor && $(renderedHeadings[index]).attr("id") === anchor[1] ? text2.slice(0, anchor.index) : text2;
1286
+ });
1287
+ if (JSON.stringify(headings) !== JSON.stringify(headingText)) continue;
1288
+ let proseCursor = 0;
1289
+ let hasMatchingProse = true;
1290
+ for (const paragraph of nodes.filter((node) => node.type === "paragraph" || node.type === "tableCell")) {
1291
+ const text2 = labelText(sourceText(paragraph));
1292
+ if (!text2) continue;
1293
+ const position = renderedProse.indexOf(text2, proseCursor);
1294
+ if (position < 0) {
1295
+ hasMatchingProse = false;
1296
+ break;
1297
+ }
1298
+ proseCursor = position + text2.length;
1299
+ }
1300
+ if (!hasMatchingProse) continue;
1301
+ const sourceCode2 = nodes.filter((node) => node.type === "code");
1302
+ if (matchesSourceCodeSequence(existingCode, sourceCode2)) candidates.push({ nodes, groups: sourceTabs(nodes) });
1303
+ }
1304
+ if (candidates.length !== 1) return;
1305
+ const source = candidates[0];
1306
+ const groups = root.find('[role="tablist"]').toArray();
1307
+ if (groups.length === source.groups.length) groups.forEach((group, index) => {
1308
+ const tabs = $(group).find('[role="tab"]').toArray().filter((tab) => $(tab).closest('[role="tablist"]')[0] === group);
1309
+ const sourceGroup = source.groups[index];
1310
+ if (!tabs.length || tabs.length !== sourceGroup.length) return;
1311
+ const panels = [];
1312
+ for (const [position, tab] of tabs.entries()) {
1313
+ const control = $(tab).attr("aria-controls");
1314
+ const tabId = $(tab).attr("id");
1315
+ const panel = root.find("[id]").filter((_index, node) => $(node).attr("id") === control);
1316
+ if (!control || panel.length !== 1 || !panel.is('[role="tabpanel"]') || !tabId || root.find("[id]").filter((_index, node) => $(node).attr("id") === tabId).length !== 1 || panel.attr("aria-labelledby") !== tabId || !panel.parents().toArray().includes($(group).parent()[0]) || labelText($(tab).text()) !== sourceGroup[position].label || panel.is('[aria-busy="true"]') || panel.find('[aria-busy="true"],[role="progressbar"]').length) return;
1317
+ if (panel.text().trim() || panel.children().length) {
1318
+ const code = panel.find("pre").toArray().map((node) => renderedCode($, node));
1319
+ const clone = panel.clone();
1320
+ clone.find("pre,button,script,style").remove();
1321
+ const declared = sourceGroup[position].code;
1322
+ if (clone.text().trim() || code.length !== declared.length || !code.every((value, index2) => matchesSourceCode(value, declared[index2].value ?? ""))) return;
1323
+ }
1324
+ panels.push(panel);
1325
+ }
1326
+ if (new Set(panels.map((panel) => panel[0])).size !== panels.length) return;
1327
+ panels.forEach((panel, position) => {
1328
+ if (panel.text().trim() || panel.children().length) return;
1329
+ for (const code of sourceGroup[position].code) if (!chargeProjectedText(projectionBudget, code.value ?? "")) return;
1330
+ for (const code of sourceGroup[position].code) {
1331
+ const pre = $("<pre></pre>").append($("<code></code>").text(code.value ?? ""));
1332
+ if (code.lang && /^[A-Za-z0-9_+-]{1,64}$/.test(code.lang)) pre.attr("data-language", code.lang);
1333
+ panel.append(pre);
1334
+ }
1335
+ });
1336
+ });
1337
+ const sourceCode = source.nodes.filter((node) => node.type === "code");
1338
+ let cursor = 0;
1339
+ root.find("pre").each((_index, node) => {
1340
+ const payload = renderedCode($, node);
1341
+ while (cursor < sourceCode.length && !matchesSourceCode(payload, sourceCode[cursor].value ?? "")) cursor += 1;
1342
+ const code = sourceCode[cursor++];
1343
+ if (!code?.lang || !/^[A-Za-z0-9_+-]{1,64}$/.test(code.lang)) return;
1344
+ const languages = new Set(sourceCode.filter((candidate) => matchesSourceCode(payload, candidate.value ?? "")).map((candidate) => candidate.lang ?? ""));
1345
+ if (languages.size !== 1) return;
1346
+ if (!$(node).attr("language") && !$(node).attr("data-language") && !$(node).find('code[language],code[data-language],code[class*="language-"],code[class*="lang-"]').length && !/(?:^|\s)(?:language-|lang-)/.test($(node).attr("class") ?? "")) $(node).attr("data-language", code.lang);
1347
+ });
1348
+ }
1349
+
580
1350
  // src/url-navigation.ts
581
1351
  var MAX_CAPTURE_BYTES = 8e6;
582
1352
  var MAX_JSON_BYTES = 2e6;
@@ -775,6 +1545,75 @@ function resolveNavigation(nodes, base) {
775
1545
  };
776
1546
  return walk(nodes);
777
1547
  }
1548
+ function sidebarCollectionMenus($, root, base, existing) {
1549
+ const document = readMigrationFlightDocument($);
1550
+ if (!document) return [];
1551
+ const resolve4 = (input) => {
1552
+ let value = input;
1553
+ const seen = /* @__PURE__ */ new Set();
1554
+ for (let depth = 0; depth < 32 && typeof value === "string"; depth++) {
1555
+ if (value === "$undefined") return void 0;
1556
+ if (value.startsWith("$$")) return value.slice(1);
1557
+ const reference = /^\$L?([a-f\d]+)(?::(.+))?$/i.exec(value);
1558
+ if (!reference) return value;
1559
+ if (seen.has(value) || !document.records.has(reference[1])) return void 0;
1560
+ seen.add(value);
1561
+ value = document.records.get(reference[1]);
1562
+ for (const key of reference[2]?.split(":") ?? []) {
1563
+ if (key === "props" && Array.isArray(value) && value[0] === "$") {
1564
+ value = value[3];
1565
+ continue;
1566
+ }
1567
+ if (!value || typeof value !== "object" || !Object.hasOwn(value, key)) return void 0;
1568
+ value = value[key];
1569
+ }
1570
+ }
1571
+ return typeof value === "string" && /^\$L?[a-f\d]+(?::|$)/i.test(value) ? void 0 : value;
1572
+ };
1573
+ const known = /* @__PURE__ */ new Set();
1574
+ const remember = (nodes) => {
1575
+ for (const node of nodes) {
1576
+ if (node.url) known.add(node.url);
1577
+ remember(node.children ?? []);
1578
+ }
1579
+ };
1580
+ remember(existing);
1581
+ const result = [];
1582
+ let visited = 0;
1583
+ const active = /* @__PURE__ */ new Set();
1584
+ const visit = (input, inSidebar, inHeader, inArticle, depth) => {
1585
+ if (++visited > 1e5 || depth > 96 || active.has(input)) return;
1586
+ const value = resolve4(input);
1587
+ if (!value || typeof value !== "object") return;
1588
+ active.add(input);
1589
+ if (Array.isArray(value)) {
1590
+ if (value[0] === "$" && object(value[3])) {
1591
+ const componentId = typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
1592
+ const component = componentId ? document.modules.get(componentId) : value[1];
1593
+ const article = inArticle || ["PageArticle", "article", "pre", "code"].includes(String(component));
1594
+ const sidebar = !article && (inSidebar || component === "Sidebar");
1595
+ const header = sidebar && (inHeader || component === "SidebarHeader");
1596
+ if (header && component === "RootToggle") {
1597
+ const options = resolve4(value[3].options);
1598
+ if (Array.isArray(options) && options.length >= 2 && options.length <= 100) {
1599
+ const entries = options.map(resolve4).map((option) => object(option) ? { label: text(resolve4(option.title)), url: target(resolve4(option.url), base) } : {});
1600
+ const hasVisibleChoice = entries.some((entry) => entry.label && root.find("button[aria-haspopup]").toArray().some((button) => [button, ...$(button).find("*").toArray()].some((node) => $(node).text().trim() === entry.label)));
1601
+ if (hasVisibleChoice && entries.every((entry) => entry.label && entry.url)) for (const entry of entries) {
1602
+ if (!known.has(entry.url)) {
1603
+ known.add(entry.url);
1604
+ result.push({ label: entry.label, url: entry.url });
1605
+ }
1606
+ }
1607
+ }
1608
+ }
1609
+ visit(value[3].children, sidebar, header, article, depth + 1);
1610
+ } else for (const child of value) visit(child, inSidebar, inHeader, inArticle, depth + 1);
1611
+ } else for (const child of Object.values(value)) visit(child, inSidebar, inHeader, inArticle, depth + 1);
1612
+ active.delete(input);
1613
+ };
1614
+ visit(document.records.get("0"), false, false, false, 0);
1615
+ return visited > 1e5 ? [] : result;
1616
+ }
778
1617
  function extractSourceNavigation(capture, selectedScope) {
779
1618
  if (capture.navigation) return resolveNavigation(capture.navigation, capture.finalUrl);
780
1619
  if (!/html/i.test(capture.contentType) || Buffer.byteLength(capture.body) > MAX_CAPTURE_BYTES) return [];
@@ -782,34 +1621,100 @@ function extractSourceNavigation(capture, selectedScope) {
782
1621
  if (mintlify.length) return mintlify;
783
1622
  const $ = load2(capture.body);
784
1623
  const candidates = ['.theme-doc-sidebar-menu,[aria-label*="sidebar" i]', "aside", '[role="navigation"],nav'];
785
- const root = candidates.map((selector) => $(selector).filter((_index, node) => $(node).find("a[href]").length > 0 && $(node).closest("header").length === 0).first()).find((candidate) => candidate.length);
1624
+ const root = candidates.map((selector) => $(selector).filter((_index, node) => {
1625
+ const item = $(node);
1626
+ if (!item.find("a[href]").length || item.closest("header,article").length) return false;
1627
+ const current = new URL(capture.finalUrl);
1628
+ const internal = item.find("a[href]").toArray().flatMap((link) => {
1629
+ const reference = target($(link).attr("href"), capture.finalUrl);
1630
+ if (!reference) return [];
1631
+ const url = new URL(reference);
1632
+ return url.origin === current.origin ? [url] : [];
1633
+ });
1634
+ if (!internal.length) {
1635
+ if (item.is('nav,[role="navigation"]')) return true;
1636
+ const prose = item.clone();
1637
+ prose.find("a,svg,img,script,style").remove();
1638
+ return !prose.text().trim();
1639
+ }
1640
+ return internal.every((url) => !url.hash) || internal.some((url) => url.pathname !== current.pathname || !url.hash && url.search !== current.search);
1641
+ }).first()).find((candidate) => candidate.length);
786
1642
  if (!root) return [];
787
1643
  let count = 0;
1644
+ const labelOf = (item) => {
1645
+ const visible = item.text().replace(/\s+/g, " ").trim();
1646
+ if (visible) return visible;
1647
+ if (item.is("a[href]")) {
1648
+ const url = target(item.attr("href"), capture.finalUrl);
1649
+ if (url && root.find("a[href]").toArray().some((node) => node !== item[0] && $(node).text().trim() && target($(node).attr("href"), capture.finalUrl) === url)) return "";
1650
+ }
1651
+ return text(item.attr("aria-label")) || text(item.attr("title")) || text(item.find("img[alt]").first().attr("alt")) || "";
1652
+ };
788
1653
  const walk = (element, excluded, depth = 0) => {
789
1654
  if (depth > MAX_DEPTH) throw new Error("Source navigation exceeds its safety bound.");
790
1655
  const result = [];
1656
+ const siblings = $(element).children().toArray().filter((node) => node !== excluded);
1657
+ if (siblings.length > MAX_NODES) throw new Error("Source navigation exceeds its safety bound.");
1658
+ const marker = (node) => {
1659
+ const item = $(node);
1660
+ return item.is("p,span") && Boolean(item.text().trim()) && item.text().length <= 512 && item.find("*").toArray().every((child) => $(child).is("span,strong,em,b,i,small,code,br")) && !item.find("a,button,input,textarea,select,form,div,p,section,article").length && !item.closest('form,[role="search"],[aria-hidden="true"]').length;
1661
+ };
1662
+ const markerPositions = siblings.flatMap((node, index) => marker(node) ? [index] : []);
1663
+ const markers = markerPositions.map((index) => siblings[index]);
1664
+ const isLinkContainer = (node) => {
1665
+ const item = $(node);
1666
+ if (item.is("a[href]")) return true;
1667
+ if (!item.is("div,li,ul,ol,details")) return false;
1668
+ if (!item.find("a[href]").length || item.find('form,input,textarea,select,[role="search"]').length) return false;
1669
+ const remainder = item.clone();
1670
+ remainder.find("a[href],svg,img").remove();
1671
+ return !remainder.text().trim() && !remainder.find("button,form,input,textarea,select,section,article").length;
1672
+ };
1673
+ const groupedMarkers = /* @__PURE__ */ new Set();
1674
+ if (markers.length >= 2 && markers.every((node) => node.tagName === markers[0].tagName)) {
1675
+ let isComplete = true;
1676
+ for (let index = 0; index < markers.length; index++) {
1677
+ const start = markerPositions[index] + 1;
1678
+ const end = index + 1 < markers.length ? markerPositions[index + 1] : siblings.length;
1679
+ const following = siblings.slice(start, end).filter((node) => $(node).text().trim() || $(node).find("a[href],button,input,textarea,select,form,img").length);
1680
+ if (!following.length || !following.every(isLinkContainer)) {
1681
+ isComplete = false;
1682
+ break;
1683
+ }
1684
+ }
1685
+ if (isComplete) markers.forEach((node) => groupedMarkers.add(node));
1686
+ }
1687
+ let currentGroup;
1688
+ const append = (nodes) => {
1689
+ if (currentGroup) currentGroup.children.push(...nodes);
1690
+ else result.push(...nodes);
1691
+ };
791
1692
  $(element).children().each((_index, node) => {
792
1693
  if (++count > MAX_NODES) throw new Error("Source navigation exceeds its safety bound.");
793
1694
  if (node === excluded) return;
794
1695
  const item = $(node);
795
- if (item.is("a[href]")) {
796
- const label = item.text().replace(/\s+/g, " ").trim();
1696
+ if (groupedMarkers.has(node)) {
1697
+ currentGroup = { kind: "group", label: item.text().replace(/\s+/g, " ").trim(), children: [] };
1698
+ result.push(currentGroup);
1699
+ } else if (item.is("a[href]")) {
1700
+ const label = labelOf(item);
797
1701
  const url = target(item.attr("href"), capture.finalUrl);
798
- if (label && url) result.push({ label, url });
1702
+ if (label && url) append([{ label, url }]);
799
1703
  } else if (item.is("li,details") || item.attr("data-migration-group") !== void 0 || item.children("h2,h3,h4,h5,h6").length > 0) {
800
1704
  const directLink = item.children("a[href]").first();
801
1705
  const direct = directLink.length ? directLink : item.children("div").children("a[href]").first();
802
- const label = direct.text().trim() || item.children("summary,button,span,h2,h3,h4,h5,h6").first().text().trim();
1706
+ const label = labelOf(direct) || labelOf(item.children("summary,button,span,h2,h3,h4,h5,h6").first());
803
1707
  const children = walk(node, direct[0], depth + 1);
804
1708
  const url = target(direct.attr("href"), capture.finalUrl);
805
- if (label && (url || children.length)) result.push({ label, ...url ? { url } : {}, ...children.length ? { children } : {} });
806
- else result.push(...children);
807
- } else result.push(...walk(node, excluded, depth + 1));
1709
+ if (label && (url || children.length)) append([{ label, ...url ? { url } : {}, ...children.length ? { children } : {} }]);
1710
+ else append(children);
1711
+ } else append(walk(node, excluded, depth + 1));
808
1712
  });
809
1713
  return result;
810
1714
  };
811
1715
  try {
812
- return walk(root);
1716
+ const nodes = walk(root);
1717
+ return [...nodes, ...sidebarCollectionMenus($, root, capture.finalUrl, nodes)];
813
1718
  } catch {
814
1719
  return [];
815
1720
  }
@@ -974,12 +1879,16 @@ function projectMigrationMedia(html, base) {
974
1879
  }
975
1880
  }
976
1881
 
1882
+ // src/url-identity.ts
1883
+ import { createHash as createHash3 } from "crypto";
1884
+ import { load as load6 } from "cheerio";
1885
+
977
1886
  // src/url.ts
978
- import { load as load4 } from "cheerio";
979
- import remarkMdx2 from "remark-mdx";
980
- import remarkParse2 from "remark-parse";
1887
+ import { load as load5 } from "cheerio";
1888
+ import remarkMdx3 from "remark-mdx";
1889
+ import remarkParse3 from "remark-parse";
981
1890
  import TurndownService from "turndown";
982
- import { unified as unified2 } from "unified";
1891
+ import { unified as unified3 } from "unified";
983
1892
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
984
1893
 
985
1894
  // src/analytics.ts
@@ -990,10 +1899,10 @@ var HOSTNAME = new RegExp(`^${HOSTNAME_LABEL}(?:\\.${HOSTNAME_LABEL})*$`);
990
1899
  import { posix } from "path";
991
1900
  import { compileSync } from "@mdx-js/mdx";
992
1901
  import { nameToEmoji } from "gemoji";
993
- import remarkGfm from "remark-gfm";
994
- import remarkMdx from "remark-mdx";
995
- import remarkParse from "remark-parse";
996
- import { unified } from "unified";
1902
+ import remarkGfm2 from "remark-gfm";
1903
+ import remarkMdx2 from "remark-mdx";
1904
+ import remarkParse2 from "remark-parse";
1905
+ import { unified as unified2 } from "unified";
997
1906
 
998
1907
  // src/builtin-components.ts
999
1908
  var THALLY_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
@@ -1480,7 +2389,7 @@ function titleFromId(id) {
1480
2389
  if (value === "introduction") return "Introduction";
1481
2390
  return value.split(/[-_]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1482
2391
  }
1483
- var descriptionParser = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
2392
+ var descriptionParser = unified2().use(remarkParse2).use(remarkGfm2).use(remarkMdx2);
1484
2393
  function plainText(node) {
1485
2394
  if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
1486
2395
  if (node.type === "image") return node.alt ?? "";
@@ -2539,8 +3448,132 @@ function apiFrontmatter(data, warn) {
2539
3448
  return result;
2540
3449
  }
2541
3450
 
3451
+ // src/url-static-content.ts
3452
+ import { createHash as createHash2 } from "crypto";
3453
+ import { load as load4 } from "cheerio";
3454
+ function projectMigrationSvg(html) {
3455
+ const $ = load4(html, { xmlMode: true }, false);
3456
+ const svg = $.root().children();
3457
+ if (svg.length !== 1 || !svg.is("svg")) return null;
3458
+ if (svg.attr("xmlns") === void 0) svg.attr("xmlns", "http://www.w3.org/2000/svg");
3459
+ const content = Buffer.from($.xml(svg));
3460
+ if (!isPassiveSvg(content)) return null;
3461
+ const sha256 = createHash2("sha256").update(content).digest("hex");
3462
+ const labelled = (svg.attr("aria-labelledby") ?? "").split(/\s+/).filter(Boolean).map((id) => svg.find("[id]").filter((_index, node) => $(node).attr("id") === id).text().trim()).filter(Boolean);
3463
+ const alternative = svg.attr("aria-label")?.trim() || labelled.join(" ") || svg.children("title").text().trim() || svg.children("desc").text().trim() || svg.find("text").text().trim();
3464
+ return { asset: { path: `migration-assets/${sha256}.svg`, content }, alternative };
3465
+ }
3466
+ function projectProtectedEmails($, root) {
3467
+ root.find("[data-cfemail]").each((_index, node) => {
3468
+ const element = $(node);
3469
+ const anchor = element.is("a") ? element : element.closest("a");
3470
+ const href = anchor.attr("href") ?? "";
3471
+ if (!anchor.length || !/^\/cdn-cgi\/l\/email-protection(?:#[a-f\d]+)?$/i.test(href)) return;
3472
+ const encoded = element.attr("data-cfemail") ?? "";
3473
+ if (!/^(?:[a-f\d]{2}){4,1024}$/i.test(encoded)) return;
3474
+ const bytes = Buffer.from(encoded, "hex");
3475
+ const payload = Uint8Array.from(bytes.subarray(1), (byte) => byte ^ bytes[0]);
3476
+ let email;
3477
+ try {
3478
+ email = new TextDecoder("utf-8", { fatal: true }).decode(payload);
3479
+ } catch {
3480
+ return;
3481
+ }
3482
+ if (!/^[^@\s<>?#[\]\u0000-\u001f\u007f]+@[^@\s<>?#[\]\u0000-\u001f\u007f]+\.[^@\s<>?#[\]\u0000-\u001f\u007f]+$/u.test(email)) return;
3483
+ anchor.attr("href", `mailto:${email}`);
3484
+ if (/^\[email\s+protected\]$/i.test(element.text().trim())) element.text(email);
3485
+ element.removeAttr("data-cfemail");
3486
+ });
3487
+ }
3488
+ function hasCompleteCapturedPanel($, panel) {
3489
+ const busy = '[aria-busy="true"],[role="progressbar"]';
3490
+ if (panel.is(busy) || panel.find(busy).length) return false;
3491
+ const copy = panel.clone();
3492
+ copy.find('script,style,button,[role="tablist"]').remove();
3493
+ return Boolean(copy.text().trim() || copy.find("img[src],svg").length);
3494
+ }
3495
+ function projectCapturedDisclosures($, root) {
3496
+ const ids = /* @__PURE__ */ new Map();
3497
+ root.find("[id]").each((_index, node) => {
3498
+ const id = $(node).attr("id");
3499
+ ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
3500
+ });
3501
+ root.find("button[aria-expanded][aria-controls],summary[aria-expanded][aria-controls]").each((_index, node) => {
3502
+ const control = $(node);
3503
+ if (control.is('[role="tab"],[aria-haspopup],[aria-busy="true"]')) return;
3504
+ const id = control.attr("id"), target2 = control.attr("aria-controls");
3505
+ const panel = target2 ? ids.get(target2) : void 0;
3506
+ const nativeSummary = control.is("summary") && control.parent().is("details");
3507
+ let owner = control.parent();
3508
+ if (owner.is("h1,h2,h3,h4,h5,h6")) owner = owner.parent();
3509
+ const labels2 = panel?.attr("aria-labelledby")?.trim().split(/\s+/);
3510
+ const ownedLabels = labels2?.every((label) => ids.get(label)?.length === 1 && (label === id || nativeSummary && control.find("[id]").toArray().some((element) => $(element).attr("id") === label)));
3511
+ if (!nativeSummary && !id || id && ids.get(id)?.length !== 1 || !panel || panel.length !== 1 || panel.attr("role") !== "region" && !(nativeSummary && panel.is('[data-component-part="expandable-content"]')) || labels2 && !ownedLabels || !panel.parents().toArray().some((parent) => parent === owner[0]) || nativeSummary && panel.parent()[0] !== owner[0] || !hasCompleteCapturedPanel($, panel)) return;
3512
+ if (!nativeSummary) node.name = "span";
3513
+ control.removeAttr("aria-expanded aria-controls role type");
3514
+ panel.removeAttr("hidden aria-hidden");
3515
+ });
3516
+ }
3517
+ function projectCapturedTabs($, root) {
3518
+ root.find("[data-thally-static-tabs],[data-thally-static-tab]").removeAttr("data-thally-static-tabs data-thally-static-tab");
3519
+ const ids = /* @__PURE__ */ new Map();
3520
+ root.find("[id]").each((_index, node) => {
3521
+ const id = $(node).attr("id");
3522
+ ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
3523
+ });
3524
+ const claimed = /* @__PURE__ */ new Set();
3525
+ const groups = root.find('[role="tablist"]').toArray();
3526
+ if (groups.length > 256 || root.find('[role="tab"]').length > 1024) return;
3527
+ for (const groupNode of groups.reverse()) {
3528
+ const group = $(groupNode);
3529
+ const tabs = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === groupNode);
3530
+ if (!tabs.length || group.is('[aria-busy="true"]')) continue;
3531
+ if (group.text().replace(/\s+/g, "") !== tabs.map((node) => $(node).text()).join("").replace(/\s+/g, "")) continue;
3532
+ let container = group.parent();
3533
+ const explicit = tabs.every((tab) => Boolean($(tab).attr("aria-controls")));
3534
+ const panels = [];
3535
+ if (explicit) {
3536
+ for (const tab of tabs) {
3537
+ const panel = ids.get($(tab).attr("aria-controls"));
3538
+ const tabId = $(tab).attr("id");
3539
+ if (!panel || panel.length !== 1 || !panel.is('[role="tabpanel"]') || panel.find('[role="tablist"]').toArray().includes(groupNode) || tabId && ids.get(tabId)?.length !== 1 || panel.attr("aria-labelledby") !== void 0 && panel.attr("aria-labelledby") !== tabId) break;
3540
+ panels.push(panel);
3541
+ }
3542
+ for (let depth = 0; depth < 16 && container.length && panels.some((panel) => !panel.parents().toArray().some((parent) => parent === container[0])); depth += 1) container = container.parent();
3543
+ if (!container.length || !container.find('[role="tablist"]').toArray().includes(groupNode)) continue;
3544
+ } else {
3545
+ if (tabs.some((tab) => $(tab).attr("aria-controls"))) continue;
3546
+ container = group.closest(".code-group,.tabs-container,.tabs.tab-container");
3547
+ if (!container.length) continue;
3548
+ const owners = container.find('[role="tablist"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
3549
+ if (owners.length !== 1) continue;
3550
+ panels.push(...container.find('[role="tabpanel"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]).map((node) => $(node)));
3551
+ }
3552
+ if (panels.length !== tabs.length || new Set(panels.map((panel) => panel[0])).size !== panels.length || panels.some((panel) => claimed.has(panel[0]) || !hasCompleteCapturedPanel($, panel))) continue;
3553
+ const unowned = container.find('[role="tabpanel"]').toArray().filter((node) => !panels.some((panel) => panel[0] === node || $(node).parents().toArray().some((parent) => parent === panel[0])));
3554
+ if (unowned.length) continue;
3555
+ const residual = container.clone();
3556
+ residual.find('[role="tablist"],[role="tabpanel"]').remove();
3557
+ const hasSurroundingContent = Boolean(residual.text().trim() || residual.find("a[href],pre,img,svg,table,video,audio,iframe,form,input,textarea,select,object,embed,canvas,math").length);
3558
+ if (!hasSurroundingContent) container.attr("data-thally-static-tabs", "");
3559
+ panels.forEach((panel, index) => {
3560
+ claimed.add(panel[0]);
3561
+ const label = $(tabs[index]).text().trim() || `Tab ${index + 1}`;
3562
+ if (hasSurroundingContent) panel.prepend($("<p></p>").append($("<strong></strong>").text(label)));
3563
+ else panel.attr("data-thally-static-tab", label);
3564
+ panel.removeAttr("role hidden aria-hidden aria-labelledby");
3565
+ const id = $(tabs[index]).attr("id");
3566
+ if (id) panel.prepend($("<span></span>").attr("id", id));
3567
+ });
3568
+ const groupId = group.attr("id");
3569
+ if (groupId) group.replaceWith($("<span></span>").attr("id", groupId));
3570
+ else group.remove();
3571
+ }
3572
+ }
3573
+
2542
3574
  // src/url.ts
2543
- var MAX_LOCAL_RESPONSE_BYTES = 2e6;
3575
+ var MAX_LOCAL_RESPONSE_BYTES = 8e6;
3576
+ var MAX_EMBEDDED_SCHEMA_BYTES = 2e6;
2544
3577
  var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
2545
3578
  "Accordion",
2546
3579
  "AccordionGroup",
@@ -2617,8 +3650,8 @@ var MINTLIFY_ARTICLE_LABELS = [
2617
3650
  { baseUrl: "Select base URL", permalink: "Navigate to header" },
2618
3651
  { baseUrl: "\u9009\u62E9\u57FA\u7840URL", permalink: "\u5BFC\u822A\u5230\u6807\u9898" }
2619
3652
  ];
2620
- var remoteMarkdownParser = unified2().use(remarkParse2).use(remarkMdx2);
2621
- var embeddedOpenApiParser = unified2().use(remarkParse2);
3653
+ var remoteMarkdownParser = unified3().use(remarkParse3).use(remarkMdx3);
3654
+ var embeddedOpenApiParser = unified3().use(remarkParse3);
2622
3655
  function validateMigrationUrl(value) {
2623
3656
  let url;
2624
3657
  try {
@@ -2635,7 +3668,7 @@ function validateMigrationUrl(value) {
2635
3668
  }
2636
3669
  async function readResponseBody(response) {
2637
3670
  const contentLength = Number(response.headers.get("content-length") ?? 0);
2638
- if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the 2 MB import limit.");
3671
+ if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the 8 MB import limit.");
2639
3672
  if (!response.body) return "";
2640
3673
  const reader = response.body.getReader();
2641
3674
  const chunks = [];
@@ -2646,7 +3679,7 @@ async function readResponseBody(response) {
2646
3679
  size += value.byteLength;
2647
3680
  if (size > MAX_LOCAL_RESPONSE_BYTES) {
2648
3681
  await reader.cancel();
2649
- throw new Error("A documentation response exceeded the 2 MB import limit.");
3682
+ throw new Error("A documentation response exceeded the 8 MB import limit.");
2650
3683
  }
2651
3684
  chunks.push(value);
2652
3685
  }
@@ -2751,7 +3784,7 @@ function isSafePortableUrl(value) {
2751
3784
  }
2752
3785
  }
2753
3786
  function decodeHtmlAttribute(value) {
2754
- const document = load4(`<i data-thally-value="${value.replace(/"/g, "&quot;")}"></i>`);
3787
+ const document = load5(`<i data-thally-value="${value.replace(/"/g, "&quot;")}"></i>`);
2755
3788
  return document("i").attr("data-thally-value") ?? "";
2756
3789
  }
2757
3790
  function escapeMarkdownText(value, escapeTableSeparator = false) {
@@ -2837,7 +3870,7 @@ function replacePortableHtmlImages(value) {
2837
3870
  if (start < cursor) continue;
2838
3871
  const end = htmlOpeningTagEnd(value, start + match[0].length);
2839
3872
  if (end === null) continue;
2840
- const fragment = load4(value.slice(start, end));
3873
+ const fragment = load5(value.slice(start, end));
2841
3874
  const image = fragment("img").first();
2842
3875
  const source = image.attr("src");
2843
3876
  const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
@@ -2862,7 +3895,7 @@ function replacePortableHtmlAnchors(value) {
2862
3895
  const closing = closingMatch.exec(value);
2863
3896
  if (!closing) continue;
2864
3897
  const end = closing.index + closing[0].length;
2865
- const fragment = load4(value.slice(start, end));
3898
+ const fragment = load5(value.slice(start, end));
2866
3899
  const anchor = fragment("a").first();
2867
3900
  const href = anchor.attr("href");
2868
3901
  const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
@@ -3118,7 +4151,7 @@ function stripMintlifyDocumentationIndex(body) {
3118
4151
  return lines.slice(index).join("\n");
3119
4152
  }
3120
4153
  function extractEmbeddedOpenApi(content) {
3121
- if (Buffer.byteLength(content, "utf8") > MAX_LOCAL_RESPONSE_BYTES) return { content };
4154
+ if (Buffer.byteLength(content, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
3122
4155
  try {
3123
4156
  const root = embeddedOpenApiParser.parse(content);
3124
4157
  const candidates = (root.children ?? []).flatMap((node2) => {
@@ -3151,10 +4184,10 @@ function extractEmbeddedOpenApi(content) {
3151
4184
  const serialized = JSON.stringify(document, (_key, value) => {
3152
4185
  values += 1;
3153
4186
  if (typeof value === "string") textLength += value.length;
3154
- if (values > 1e5 || textLength > MAX_LOCAL_RESPONSE_BYTES || typeof value === "number" && !Number.isFinite(value)) throw new Error("OpenAPI document exceeds portable data limits.");
4187
+ if (values > 1e5 || textLength > MAX_EMBEDDED_SCHEMA_BYTES || typeof value === "number" && !Number.isFinite(value)) throw new Error("OpenAPI document exceeds portable data limits.");
3155
4188
  return value;
3156
4189
  });
3157
- if (Buffer.byteLength(serialized, "utf8") > MAX_LOCAL_RESPONSE_BYTES) return { content };
4190
+ if (Buffer.byteLength(serialized, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
3158
4191
  const before = content.slice(0, start).replace(/(?:^|\n)##\s+OpenAPI\s*\n\s*$/i, "\n");
3159
4192
  const after = content.slice(end);
3160
4193
  return {
@@ -3208,8 +4241,12 @@ function markdownPage(document, id, platform, warn) {
3208
4241
  };
3209
4242
  }
3210
4243
  function migrationHtmlArticle(body) {
3211
- const $ = load4(body);
4244
+ const $ = load5(body);
3212
4245
  const root = ["main article", "article", "main", '[role="main"]', "body"].map((selector) => $(selector).first()).find((candidate) => candidate.length);
4246
+ projectProtectedEmails($, root);
4247
+ recoverFlightArticleCode($, root);
4248
+ const assets = [];
4249
+ const svgProjections = new Map(root.find("svg").toArray().map((element) => [element, projectMigrationSvg($.html(element))]));
3213
4250
  root.find([
3214
4251
  "script",
3215
4252
  "style",
@@ -3270,10 +4307,25 @@ function migrationHtmlArticle(body) {
3270
4307
  projection.append(list);
3271
4308
  select.replaceWith(projection);
3272
4309
  });
3273
- return { $, root };
4310
+ projectCapturedDisclosures($, root);
4311
+ root.find("svg").each((_index, element) => {
4312
+ const svg = $(element);
4313
+ if (svg.parents("svg,button,nav,footer,aside").length) return;
4314
+ const projection = svgProjections.get(element);
4315
+ if (!projection) return;
4316
+ const image = $("<img>").attr("src", `/${projection.asset.path}`).attr("alt", projection.alternative);
4317
+ for (const attribute of ["id", "width", "height"]) {
4318
+ const value = svg.attr(attribute);
4319
+ if (value !== void 0) image.attr(attribute, value);
4320
+ }
4321
+ assets.push(projection.asset);
4322
+ svg.replaceWith(image);
4323
+ });
4324
+ projectCapturedTabs($, root);
4325
+ return { $, root, assets };
3274
4326
  }
3275
4327
  function htmlPage(document, id, options = {}) {
3276
- const { $, root } = migrationHtmlArticle(document.body);
4328
+ const { $, root, assets } = migrationHtmlArticle(document.body);
3277
4329
  const links = root.find("a[href]").map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
3278
4330
  const scopedTitle = options.omitDocumentHeading ? root.find("h1").first().text().trim() : "";
3279
4331
  const title = scopedTitle || $("main h1, article h1, h1").first().text().trim() || $('meta[property="og:title"]').attr("content")?.trim() || $("title").text().split("|")[0].trim();
@@ -3312,6 +4364,7 @@ function htmlPage(document, id, options = {}) {
3312
4364
  root.find("img[src]").each((_index, element) => {
3313
4365
  const src = $(element).attr("src");
3314
4366
  if (!src) return;
4367
+ if (assets.some((asset) => src === `/${asset.path}`)) return;
3315
4368
  try {
3316
4369
  const resolved = new URL(src, document.finalUrl);
3317
4370
  if (["http:", "https:"].includes(resolved.protocol)) {
@@ -3323,7 +4376,15 @@ function htmlPage(document, id, options = {}) {
3323
4376
  $(element).removeAttr("src");
3324
4377
  }
3325
4378
  });
3326
- const turndown = new TurndownService({ codeBlockStyle: "fenced", emDelimiter: "_", headingStyle: "atx", preformattedCode: true });
4379
+ const turndown = new TurndownService({
4380
+ codeBlockStyle: "fenced",
4381
+ emDelimiter: "_",
4382
+ headingStyle: "atx",
4383
+ preformattedCode: true,
4384
+ // Turndown chooses its blank rule before custom rules. Empty source anchor
4385
+ // leaves still carry navigation identity, including captured tab controls.
4386
+ blankReplacement: (_content, node) => options.preserveAnchors ? anchor(node) : ""
4387
+ });
3327
4388
  let isConvertingTableCell = false;
3328
4389
  turndown.remove(["script", "style", "noscript"]);
3329
4390
  turndown.addRule("migration-literal-table-code", {
@@ -3364,6 +4425,22 @@ ${content}
3364
4425
 
3365
4426
  ${anchor(element)}${heading ? `${"#".repeat(Number(heading[1]))} ${content.trim()}` : content}
3366
4427
 
4428
+ `;
4429
+ }
4430
+ });
4431
+ turndown.addRule("migration-flow-link", {
4432
+ filter: (node) => node.nodeName === "A" && node.hasAttribute("href") && Boolean(node.querySelector("div,p,picture,figure,blockquote,pre,table,ul,ol,h1,h2,h3,h4,h5,h6")),
4433
+ replacement: (content, node) => {
4434
+ const element = node;
4435
+ const href = migrationImageAttribute(element.getAttribute("href") ?? "");
4436
+ return `
4437
+
4438
+ ${anchor(element)}<Card href="${href}">
4439
+
4440
+ ${content.trim()}
4441
+
4442
+ </Card>
4443
+
3367
4444
  `;
3368
4445
  }
3369
4446
  });
@@ -3414,7 +4491,7 @@ ${content.trim()}
3414
4491
  const element = node;
3415
4492
  const className = element.className;
3416
4493
  const tag = /admonition-(?:danger)/.test(className) ? "Error" : /admonition-(?:caution|warning)/.test(className) ? "Warning" : /admonition-info/.test(className) ? "Info" : "Note";
3417
- const fragment = load4(element.outerHTML);
4494
+ const fragment = load5(element.outerHTML);
3418
4495
  const clone = fragment(".theme-admonition").first();
3419
4496
  const title2 = clone.find('[class*="admonitionHeading"],.admonition-heading').first();
3420
4497
  const titleText = title2.text().trim();
@@ -3435,11 +4512,15 @@ ${titleText ? `**${escapeMarkdownText(titleText)}**
3435
4512
  filter: (node) => node.nodeName === "DIV" && node.classList.contains("tabs-container"),
3436
4513
  replacement: (_content, node) => {
3437
4514
  const element = node;
3438
- const fragment = load4(element.outerHTML);
4515
+ const fragment = load5(element.outerHTML);
3439
4516
  const container = fragment(".tabs-container").first();
3440
4517
  const labels2 = container.children("ul.tabs").children("li").toArray().map((item) => fragment(item).text().trim() || "Tab");
3441
4518
  const panels = container.find('[role="tabpanel"]').toArray().filter((panel) => fragment(panel).closest(".tabs-container")[0] === container[0]);
3442
4519
  if (panels.length === 0) return _content;
4520
+ const residual = container.clone();
4521
+ residual.children("ul.tabs").remove();
4522
+ residual.find('[role="tabpanel"]').remove();
4523
+ if (residual.text().trim() || residual.find("a[href],pre,img,svg,table,video,iframe,input,select,textarea").length) return _content;
3443
4524
  const tabs = panels.map((panel, index) => {
3444
4525
  const panelId = fragment(panel).attr("id");
3445
4526
  const tab = panelId ? container.find('[role="tab"]').toArray().find((item) => fragment(item).attr("aria-controls") === panelId) : void 0;
@@ -3457,13 +4538,40 @@ ${anchor(element)}<Tabs>
3457
4538
  ${tabs.join("\n\n")}
3458
4539
  </Tabs>
3459
4540
 
4541
+ `;
4542
+ }
4543
+ });
4544
+ turndown.addRule("migration-complete-captured-tabs", {
4545
+ filter: (node) => node.hasAttribute("data-thally-static-tabs"),
4546
+ replacement: (_content, node) => {
4547
+ const element = node;
4548
+ const fragment = load5(element.outerHTML);
4549
+ const container = fragment("[data-thally-static-tabs]").first();
4550
+ const panels = container.find("[data-thally-static-tab]").toArray().filter((panel) => fragment(panel).closest("[data-thally-static-tabs]")[0] === container[0]);
4551
+ const tabs = panels.map((panel) => {
4552
+ const selection = fragment(panel);
4553
+ const title2 = migrationImageAttribute(selection.attr("data-thally-static-tab"));
4554
+ const id2 = selection.attr("id");
4555
+ const panelAnchor = options.preserveAnchors && id2 ? `<a id="${migrationImageAttribute(id2)}" />
4556
+
4557
+ ` : "";
4558
+ return `<Tab title="${title2}">
4559
+ ${panelAnchor}${turndown.turndown(selection.html() ?? "").trim()}
4560
+ </Tab>`;
4561
+ });
4562
+ return `
4563
+
4564
+ ${anchor(element)}<Tabs>
4565
+ ${tabs.join("\n\n")}
4566
+ </Tabs>
4567
+
3460
4568
  `;
3461
4569
  }
3462
4570
  });
3463
4571
  turndown.addRule("gfm-table", {
3464
4572
  filter: "table",
3465
4573
  replacement: (_content, node) => {
3466
- const fragment = load4(node.outerHTML);
4574
+ const fragment = load5(node.outerHTML);
3467
4575
  const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => {
3468
4576
  const wasConvertingTableCell = isConvertingTableCell;
3469
4577
  isConvertingTableCell = true;
@@ -3489,7 +4597,7 @@ ${normalized.slice(1).map((row) => `| ${row.join(" | ")} |`).join("\n")}
3489
4597
  filter: (node) => node.nodeName === "PRE",
3490
4598
  replacement: (_content, node) => {
3491
4599
  const element = node;
3492
- const fragment = load4(element.outerHTML);
4600
+ const fragment = load5(element.outerHTML);
3493
4601
  const tokenLines = fragment(".token-line").toArray();
3494
4602
  const code = (tokenLines.length > 0 ? tokenLines.map((line) => fragment(line).text()).join("\n") : fragment("pre").text()).replace(/\n$/, "");
3495
4603
  const language = ["code", "pre"].flatMap((selector) => {
@@ -3514,7 +4622,7 @@ ${fence}
3514
4622
  const escape = turndown.escape.bind(turndown);
3515
4623
  turndown.escape = (text2) => isConvertingTableCell ? escape(text2.replace(/[<>{}]/g, (character) => ({ "<": "&lt;", ">": "&gt;", "{": "&#123;", "}": "&#125;" })[character])) : escape(text2).replace(/[<{}]/g, (match) => `\\${match}`);
3516
4624
  const body = `${documentHeadingAnchor}${turndown.turndown(root.html() ?? "").trim()}`;
3517
- if (!title || body.length < 40 && $("article").length === 0) return { page: null, links };
4625
+ if (!title || body.length < 40 && $("article").length === 0) return { page: null, links, assets };
3518
4626
  return {
3519
4627
  page: {
3520
4628
  id,
@@ -3525,10 +4633,74 @@ ${fence}
3525
4633
  body,
3526
4634
  source: document.finalUrl.toString()
3527
4635
  },
3528
- links
4636
+ links,
4637
+ assets
3529
4638
  };
3530
4639
  }
3531
4640
 
4641
+ // src/url-identity.ts
4642
+ var directory = (path) => path.replace(/\/index\/?$/i, "").replace(/\/$/, "") || "/";
4643
+ function migrationSectionQueryCandidate(source, anchors) {
4644
+ const target2 = new URL(source);
4645
+ const sections = [...source.searchParams].filter(([, value]) => anchors.includes(value));
4646
+ const section = sections.length === 1 && source.searchParams.getAll(sections[0][0]).length === 1 ? sections[0] : void 0;
4647
+ if (section) target2.searchParams.delete(section[0]);
4648
+ target2.hash = "";
4649
+ target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
4650
+ return { canonicalUrl: target2.href, ...section ? { sectionAnchor: section[1] } : {} };
4651
+ }
4652
+ function isMigrationUtilityUrl(url) {
4653
+ return /^\/cdn-cgi\/l\/email-protection\/?$/i.test(url.pathname);
4654
+ }
4655
+ function declaredMigrationArticleUrl(document) {
4656
+ const target2 = declaredCanonical(document);
4657
+ return target2 && target2.search === document.finalUrl.search ? target2 : document.finalUrl;
4658
+ }
4659
+ function declaredCanonical(document) {
4660
+ if (!/html/i.test(document.contentType)) return void 0;
4661
+ const reference = load6(document.body)('link[rel="canonical"][href]').first().attr("href");
4662
+ if (!reference) return void 0;
4663
+ try {
4664
+ const target2 = new URL(reference, document.finalUrl);
4665
+ if (target2.origin !== document.finalUrl.origin || target2.username || target2.password || directory(target2.pathname) !== directory(document.finalUrl.pathname)) return void 0;
4666
+ target2.hash = "";
4667
+ target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
4668
+ return target2;
4669
+ } catch {
4670
+ return void 0;
4671
+ }
4672
+ }
4673
+ function observedMigrationArticleIdentity(document) {
4674
+ if (!/html/i.test(document.contentType)) return void 0;
4675
+ const { $, root } = migrationHtmlArticle(document.body);
4676
+ if (!root.length || !root.text().trim()) return void 0;
4677
+ const anchors = root.find("[id]").toArray().map((node) => $(node).attr("id"));
4678
+ root.find("nav,aside,footer,script,style").remove();
4679
+ const metadata = load6(document.body);
4680
+ const payload = JSON.stringify([
4681
+ $.html(root),
4682
+ metadata("title").text(),
4683
+ metadata('meta[name="description"]').attr("content"),
4684
+ metadata("html").attr("lang"),
4685
+ document.locale,
4686
+ document.version,
4687
+ // Deferred content can exist only in inert hydration data. Never erase its
4688
+ // differences merely because two collapsed DOM panels appear identical.
4689
+ metadata("script").toArray().map((node) => [metadata(node).attr("src"), metadata(node).text()]),
4690
+ (document.states ?? []).map((state) => [state.id, state.label, state.response.body])
4691
+ ]);
4692
+ const source = document.finalUrl;
4693
+ const canonical2 = declaredCanonical(document) ?? new URL(source);
4694
+ if (canonical2.href === source.href) {
4695
+ canonical2.href = migrationSectionQueryCandidate(source, anchors).canonicalUrl;
4696
+ }
4697
+ canonical2.hash = "";
4698
+ canonical2.pathname = canonical2.pathname.replace(/\/+$/, "") || "/";
4699
+ const removed = [...source.searchParams].filter(([key, value]) => !canonical2.searchParams.getAll(key).includes(value));
4700
+ const sectionAnchor = removed.length === 1 && anchors.includes(removed[0][1]) ? removed[0][1] : void 0;
4701
+ return { canonicalUrl: canonical2.href, articleSha256: createHash3("sha256").update(payload).digest("hex"), ...sectionAnchor ? { sectionAnchor } : {} };
4702
+ }
4703
+
3532
4704
  // src/url-api-schema-controls.ts
3533
4705
  var MAX_CAPTURE_BYTES2 = 8e6;
3534
4706
  var MAX_TAB_GROUPS = 256;
@@ -3647,17 +4819,378 @@ function apiSchemaTabGroups(body, document, operation) {
3647
4819
  for (const proof of proofs) scopeCounts.set(proof.scope, (scopeCounts.get(proof.scope) ?? 0) + 1);
3648
4820
  return proofs.filter((proof) => scopeCounts.get(proof.scope) === 1).map((proof) => proof.index);
3649
4821
  }
4822
+ function apiSchemaDisclosureGroups(body, document, operation) {
4823
+ if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(document)) return [];
4824
+ if (!/^(?:get|post|put|patch|delete|head|options|trace)$/i.test(operation.method)) return [];
4825
+ const path = object2(object2(document.paths)?.[operation.path]);
4826
+ const selected = object2(path?.[operation.method.toLowerCase()]);
4827
+ if (!selected) return [];
4828
+ const { $, root } = migrationHtmlArticle(body);
4829
+ const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
4830
+ const item = $(node);
4831
+ const endpoint = item.find('[data-component-part="scroll-area-content"]').first().clone();
4832
+ endpoint.find("[data-thally-static-base-urls]").remove();
4833
+ return item.find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
4834
+ });
4835
+ if (operations.length !== 1) return [];
4836
+ const ids = /* @__PURE__ */ new Map();
4837
+ root.find("[id]").each((_index, node) => {
4838
+ const id = $(node).attr("id");
4839
+ ids.set(id, (ids.get(id) ?? 0) + 1);
4840
+ });
4841
+ if (ids.get(operations.attr("id")) !== 1) return [];
4842
+ let visits = 0;
4843
+ const property = (value, name, active = /* @__PURE__ */ new Set()) => {
4844
+ if (++visits > 1e5 || active.has(value)) return [];
4845
+ const schema = resolve2(document, value);
4846
+ if (!schema) return [];
4847
+ active.add(value);
4848
+ const direct = object2(schema.properties)?.[name];
4849
+ const result = direct ? [direct] : [
4850
+ ...schema.items ? property(schema.items, name, active) : [],
4851
+ ...["allOf", "oneOf", "anyOf"].flatMap((key) => Array.isArray(schema[key]) ? schema[key].flatMap((child) => property(child, name, active)) : [])
4852
+ ];
4853
+ active.delete(value);
4854
+ return result;
4855
+ };
4856
+ const fieldSelector = ".object-param-field,.array-param-field";
4857
+ const proofs = [];
4858
+ if (root.find("details").length > MAX_TABS) return [];
4859
+ root.find("details").each((index, node) => {
4860
+ const details = $(node), control = details.children('summary[data-component-part="expandable-button"]');
4861
+ const panel = details.children('[data-component-part="expandable-content"]');
4862
+ const field = details.closest(fieldSelector), section = field.closest(".api-section");
4863
+ if (!details.is('.expandable[data-component-part="expandable"]') || control.length !== 1 || panel.length !== 1 || details.children().length !== 2 || !field.length || section.closest('[id^="api-playground-"][id$="-operation-page"]')[0] !== operations[0]) return;
4864
+ const target2 = control.attr("aria-controls"), controlId = control.attr("id");
4865
+ if (target2 && (panel.attr("id") !== target2 || ids.get(target2) !== 1) || controlId && ids.get(controlId) !== 1) return;
4866
+ const payload = panel.clone();
4867
+ payload.find("script,style").remove();
4868
+ if (payload.text().trim() || payload.children().length) return;
4869
+ const ancestors = field.parents(fieldSelector).toArray().reverse().filter((owner) => $(owner).is(".array-param-field") || $(owner).find('[data-component-part="expandable-content"]').toArray().some((region) => $(region).closest(fieldSelector)[0] === owner && field.parents().toArray().includes(region)));
4870
+ const chain = [...ancestors, field[0]];
4871
+ const names = chain.map((owner) => $(owner).find('[data-component-part="field-name"]').filter((_index, name) => $(name).closest(fieldSelector)[0] === owner));
4872
+ const hasNamedPath = names.every((name) => name.length === 1 && Boolean(name.text().trim()));
4873
+ const heading = section.children(".api-section-heading");
4874
+ if (heading.length !== 1) return;
4875
+ const leaves = heading.find("*").toArray().filter((element) => $(element).children().length === 0).map((element) => $(element).text().trim());
4876
+ const combined = leaves.flatMap((text2) => {
4877
+ const match = text2.match(/^([1-5]\d{2}|default) - ([a-z][a-z\d.+-]*\/[a-z\d.+-]+)$/i);
4878
+ return match ? [{ status: match[1], media: match[2] }] : [];
4879
+ });
4880
+ const media = heading.find("*").toArray().filter((element) => $(element).children().length === 0).map((element) => $(element).text().trim()).filter((text2) => /^[a-z][a-z\d.+-]*\/[a-z\d.+-]+$/i.test(text2));
4881
+ const statuses = heading.find('button[aria-haspopup="menu"]').toArray().map((button) => $(button).text().trim()).filter((text2) => /^(?:[1-5]\d{2}|default)$/.test(text2));
4882
+ if (combined.length === 1 && !media.length && !statuses.length) {
4883
+ media.push(combined[0].media);
4884
+ statuses.push(combined[0].status);
4885
+ }
4886
+ let schemas = [];
4887
+ let offset = 0;
4888
+ let prefix = "";
4889
+ if (media.length === 1 && statuses.length === 1) {
4890
+ const response = resolve2(document, object2(selected.responses)?.[statuses[0]]);
4891
+ schemas = [object2(object2(response?.content)?.[media[0]])?.schema];
4892
+ prefix = "response";
4893
+ } else if (media.length === 1 && !statuses.length) {
4894
+ const request = resolve2(document, selected.requestBody);
4895
+ schemas = [object2(object2(request?.content)?.[media[0]])?.schema];
4896
+ prefix = "body";
4897
+ } else if (!media.length && !statuses.length) {
4898
+ const head = $(chain[0]).find(".param-head[id]").filter((_index, element) => $(element).closest(fieldSelector)[0] === chain[0]).first().attr("id") ?? "";
4899
+ const location = ["query", "path", "header", "cookie"].find((value) => head.startsWith(`${value}-`));
4900
+ if (names[0]?.length !== 1 || !location && !head.startsWith("parameter-")) return;
4901
+ const parameters = [...Array.isArray(path?.parameters) ? path.parameters : [], ...Array.isArray(selected.parameters) ? selected.parameters : []].map((value) => resolve2(document, value)).filter((parameter) => (!location || parameter?.in === location) && parameter?.name === names[0].text().trim());
4902
+ if (parameters.length !== 1) return;
4903
+ schemas = [parameters[0]?.schema];
4904
+ offset = 1;
4905
+ prefix = $(chain[0]).is(".array-param-field") ? "parameter" : `parameter-${names[0].text().trim()}`;
4906
+ }
4907
+ const expandable = (value) => {
4908
+ const schema = resolve2(document, value);
4909
+ return Boolean(schema && (object2(schema.properties) || object2(schema.items) || object2(schema.additionalProperties) || ["allOf", "oneOf", "anyOf"].some((key) => Array.isArray(schema[key]))));
4910
+ };
4911
+ const roots = schemas;
4912
+ if (hasNamedPath) {
4913
+ for (const name of names.slice(offset)) schemas = schemas.flatMap((schema) => property(schema, name.text().trim()));
4914
+ if (schemas.some(expandable)) {
4915
+ proofs.push(index);
4916
+ return;
4917
+ }
4918
+ }
4919
+ const marker = details.attr("data-testid");
4920
+ if (!prefix || !marker?.endsWith("-children")) return;
4921
+ const expected = marker.slice(0, -"-children".length);
4922
+ const visibleNames = chain.flatMap((owner) => {
4923
+ const labels2 = $(owner).find('[data-component-part="field-name"]').filter((_index, label) => $(label).closest(fieldSelector)[0] === owner);
4924
+ return labels2.length === 1 ? [labels2.text().trim()] : [];
4925
+ });
4926
+ const matches = /* @__PURE__ */ new Set();
4927
+ const walk = (value, key, pathNames, active = /* @__PURE__ */ new Set()) => {
4928
+ if (++visits > 1e5 || key.length > 2048 || active.has(value)) return;
4929
+ const schema = resolve2(document, value);
4930
+ if (!schema) return;
4931
+ if (key === expected && expandable(schema)) {
4932
+ let cursor = 0;
4933
+ const labelsAgree = visibleNames.every((name) => {
4934
+ const found = pathNames.indexOf(name, cursor);
4935
+ cursor = found + 1;
4936
+ return found >= 0;
4937
+ });
4938
+ if (labelsAgree) matches.add(schema);
4939
+ }
4940
+ if (!expected.startsWith(`${key}-`)) return;
4941
+ active.add(value);
4942
+ for (const [name, child] of Object.entries(object2(schema.properties) ?? {})) {
4943
+ const segments = /* @__PURE__ */ new Set([name, name.replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase()]);
4944
+ for (const segment of segments) walk(child, `${key}-${segment}`, [...pathNames, name], active);
4945
+ }
4946
+ if (schema.items) walk(schema.items, `${key}-items`, pathNames, active);
4947
+ if ((schema.nullable === true || Array.isArray(schema.type) && schema.type.includes("null")) && !schema.oneOf) walk({ ...schema, nullable: false, type: Array.isArray(schema.type) ? schema.type.filter((value2) => value2 !== "null") : schema.type }, `${key}-one-of-0`, pathNames, active);
4948
+ if (Array.isArray(schema.allOf)) schema.allOf.forEach((child) => walk(child, key, pathNames, active));
4949
+ for (const [keyword, segment] of [["allOf", "all-of"], ["oneOf", "one-of"], ["anyOf", "any-of"]]) {
4950
+ const children = schema[keyword];
4951
+ if (Array.isArray(children)) children.forEach((child, branch) => walk(child, `${key}-${segment}-${branch}`, pathNames, active));
4952
+ }
4953
+ active.delete(value);
4954
+ };
4955
+ roots.forEach((schema) => walk(schema, prefix, offset ? [names[0].text().trim()] : []));
4956
+ if (matches.size === 1) proofs.push(index);
4957
+ });
4958
+ return visits > 1e5 ? [] : proofs;
4959
+ }
4960
+ function apiOperationCodeControls(body, specification, operation) {
4961
+ if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(specification)) return [];
4962
+ const selected = object2(object2(object2(specification.paths)?.[operation.path])?.[operation.method.toLowerCase()]);
4963
+ if (!selected) return [];
4964
+ const { $, root } = migrationHtmlArticle(body);
4965
+ const flight = readMigrationFlightDocument($);
4966
+ if (!flight) return [];
4967
+ let visited = 0, textBytes = 0, exceeded = false;
4968
+ const resolveValue = (input) => {
4969
+ if (exceeded) return void 0;
4970
+ const result = resolveMigrationFlightValue(flight, input);
4971
+ if (!result.resolved) exceeded = true;
4972
+ return result.value;
4973
+ };
4974
+ const textOf = (input, depth = 0, label = false) => {
4975
+ if (exceeded || ++visited > 1e5 || depth > 96) {
4976
+ exceeded = true;
4977
+ return void 0;
4978
+ }
4979
+ const value = resolveValue(input);
4980
+ if (value === void 0 || value === null || typeof value === "boolean") return "";
4981
+ if (typeof value === "string" || typeof value === "number") {
4982
+ const result = String(value);
4983
+ textBytes += Buffer.byteLength(result);
4984
+ if (textBytes > MAX_CAPTURE_BYTES2) {
4985
+ exceeded = true;
4986
+ return void 0;
4987
+ }
4988
+ return result;
4989
+ }
4990
+ if (!Array.isArray(value)) return void 0;
4991
+ if (value[0] === "$") {
4992
+ if (typeof value[1] !== "string" || !(label ? ["div", "p", "span", "code", "a", "br", "svg", "path", "polyline"] : ["span", "code", "a", "br"]).includes(value[1]) || !object2(value[3])) return void 0;
4993
+ return value[1] === "br" ? "\n" : textOf(value[3].children, depth + 1, label);
4994
+ }
4995
+ const children = value.map((child) => textOf(child, depth + 1, label));
4996
+ return children.some((child) => child === void 0) ? void 0 : children.join("");
4997
+ };
4998
+ const samplePairs = (value, transport = true) => {
4999
+ if (exceeded) return void 0;
5000
+ const samples = transport ? resolveValue(value) : value;
5001
+ if (!Array.isArray(samples) || samples.length > 64) return void 0;
5002
+ const pairs = [];
5003
+ for (const input of samples) {
5004
+ const sample = object2(transport ? resolveValue(input) : input);
5005
+ const language = transport ? resolveValue(sample?.lang) : sample?.lang;
5006
+ const source = transport ? resolveValue(sample?.source) : sample?.source;
5007
+ const label = transport ? resolveValue(sample?.label) : sample?.label;
5008
+ if (label !== void 0 && typeof label !== "string") return void 0;
5009
+ if (typeof language !== "string" || !language.trim() || typeof source !== "string" || !source.trim()) return void 0;
5010
+ textBytes += Buffer.byteLength(language) + Buffer.byteLength(source) + (typeof label === "string" ? Buffer.byteLength(label) : 0);
5011
+ if (textBytes > MAX_CAPTURE_BYTES2) {
5012
+ exceeded = true;
5013
+ return void 0;
5014
+ }
5015
+ pairs.push(JSON.stringify([language, source, label ?? null]));
5016
+ }
5017
+ return pairs;
5018
+ };
5019
+ const declared = selected["x-codeSamples"] === void 0 ? [] : samplePairs(selected["x-codeSamples"], false);
5020
+ if (!declared) return [];
5021
+ const groups = [];
5022
+ const providers = [];
5023
+ let providerCount = 0, generatedControllers = 0, providerMetadataMatches = false;
5024
+ const matchesOperation = (value) => {
5025
+ const metadata = object2(resolveValue(value));
5026
+ const declaration = resolveValue(metadata?.openapi);
5027
+ if (typeof declaration !== "string") return false;
5028
+ const tokens = declaration.trim().split(/\s+/);
5029
+ return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && tokens.at(-1) === operation.path;
5030
+ };
5031
+ const active = /* @__PURE__ */ new Set();
5032
+ const visit = (input, provider, group, depth, owners = []) => {
5033
+ if (exceeded || ++visited > 1e5 || depth > 96) {
5034
+ exceeded = true;
5035
+ return;
5036
+ }
5037
+ const value = resolveValue(input);
5038
+ if (!value || typeof value !== "object") return;
5039
+ if (active.has(value)) {
5040
+ exceeded = true;
5041
+ return;
5042
+ }
5043
+ active.add(value);
5044
+ if (Array.isArray(value)) {
5045
+ if (value[0] === "$" && object2(value[3])) {
5046
+ const props = value[3];
5047
+ const moduleId = typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
5048
+ const kind = moduleId ? flight.modules.get(moduleId) : value[1];
5049
+ const ownId = moduleId ? void 0 : resolveValue(props.id);
5050
+ const localOwners = typeof ownId === "string" ? [...owners, ownId] : owners;
5051
+ let owned = provider;
5052
+ if (kind === "OpenApiProvider") {
5053
+ providerCount += 1;
5054
+ const data = object2(resolveValue(props.openApiReferenceData));
5055
+ const source = object2(resolveValue(data?.operation));
5056
+ const samples = samplePairs(source?.codeSamples);
5057
+ const authored = source?.["x-codeSamples"] === void 0 ? [] : samplePairs(source["x-codeSamples"]);
5058
+ owned = Boolean(source && source.method === operation.method.toLowerCase() && source.path === operation.path && samples && authored && JSON.stringify(samples) === JSON.stringify(declared) && JSON.stringify(authored) === JSON.stringify(declared));
5059
+ if (owned) {
5060
+ providers.push(source);
5061
+ providerMetadataMatches = matchesOperation(props.pageMetadata);
5062
+ }
5063
+ }
5064
+ if (owned && kind === "MDXContentController" && matchesOperation(props.pageMetadata)) {
5065
+ const child = resolveValue(props.children);
5066
+ const childId = Array.isArray(child) && child[0] === "$" && typeof child[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(child[1])?.[1] : void 0;
5067
+ const childProps = Array.isArray(child) ? object2(child[3]) : void 0;
5068
+ const source = object2(resolveValue(childProps?.mdxSource));
5069
+ const scope = object2(resolveValue(source?.scope));
5070
+ if (childId && flight.modules.get(childId) === "default" && typeof source?.compiledSource === "string" && (() => {
5071
+ if (matchesOperation(scope?.pageMetadata)) return true;
5072
+ const hint = object2(resolveValue(scope?.pageMetadata)), metadata = object2(resolveValue(props.pageMetadata));
5073
+ const declaration = resolveValue(hint?.openapi);
5074
+ if (typeof declaration !== "string" || typeof hint?.href !== "string" || !hint.href || hint.href !== metadata?.href) return false;
5075
+ const tokens = declaration.trim().split(/\s+/);
5076
+ const template = (path) => path.split("/").map((segment) => /^\{[^{}\/]+\}$/.test(segment) ? "{}" : segment).join("/");
5077
+ return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && template(tokens.at(-1) ?? "") === template(operation.path);
5078
+ })()) generatedControllers += 1;
5079
+ }
5080
+ let local = group;
5081
+ if (owned && typeof props.className === "string" && props.className.split(/\s+/).includes("code-group")) {
5082
+ local = { owners: localOwners, code: [], controls: [], toolbar: false, generated: false, invalid: false };
5083
+ groups.push(local);
5084
+ }
5085
+ if (local && owned) {
5086
+ if (props["data-component-part"] === "code-group-tab-bar") local.toolbar = true;
5087
+ if (String(props["data-index"]) === "-1") local.generated = true;
5088
+ if (props["aria-expanded"] !== void 0) {
5089
+ const text2 = textOf(props.children, 0, true);
5090
+ if (kind !== "button" || props["aria-haspopup"] !== "menu" || text2 === void 0) local.invalid = true;
5091
+ else local.controls.push(JSON.stringify([text2, props["aria-label"] ?? "", String(props["aria-expanded"]), props["aria-haspopup"]]));
5092
+ }
5093
+ if (kind === "pre") {
5094
+ const code = textOf(props.children);
5095
+ if (code === void 0 || !code.trim()) local.invalid = true;
5096
+ else local.code.push(code);
5097
+ }
5098
+ if (props["aria-busy"] === true || props["aria-busy"] === "true" || props.role === "progressbar") local.invalid = true;
5099
+ }
5100
+ visit(props.children, owned, local, depth + 1, localOwners);
5101
+ } else for (const child of value) visit(child, provider, group, depth + 1, owners);
5102
+ } else if (depth === 0 && Array.isArray(value.f)) {
5103
+ visit(value.f, provider, group, depth + 1, owners);
5104
+ } else if (Object.keys(value).every((key) => key === "children")) {
5105
+ const children = value.children;
5106
+ if (Array.isArray(children) && children.length >= 2) visit(children, provider, group, depth + 1, owners);
5107
+ }
5108
+ active.delete(value);
5109
+ };
5110
+ visit(flight.records.get("0"), false, void 0, 0);
5111
+ if (exceeded || providerCount !== 1 || providers.length !== 1 || groups.length > MAX_TAB_GROUPS) return [];
5112
+ const sources = declared.map((pair) => JSON.parse(pair)[1]);
5113
+ const sourceCounts = /* @__PURE__ */ new Map();
5114
+ for (const group of groups) if (group.toolbar && group.generated && !group.invalid && group.controls.length >= 1 && group.controls.length <= 2) {
5115
+ const signature = JSON.stringify([group.owners, group.code, group.controls]);
5116
+ sourceCounts.set(signature, (sourceCounts.get(signature) ?? 0) + 1);
5117
+ }
5118
+ const expanded = root.find("[aria-expanded]").toArray();
5119
+ if (expanded.length > MAX_TABS) return [];
5120
+ const controlIndices = new Map(expanded.map((node, index) => [node, index]));
5121
+ const domGroups = /* @__PURE__ */ new Map();
5122
+ $(expanded).each((_index, node) => {
5123
+ const control = $(node);
5124
+ const group = control.closest('.code-group,[data-testid="code-group-select"]');
5125
+ const legacy = group.is('[data-testid="code-group-select"]');
5126
+ if (!control.is('button[aria-haspopup="menu"]') || control.attr("aria-controls") || !legacy && !control.closest('[data-component-part="code-group-tab-bar"]').length || !group.length || group.find("[aria-expanded]").length < 1 || group.find("[aria-expanded]").length > 2) return;
5127
+ if (domGroups.has(group[0])) return;
5128
+ const menus = group.find("[aria-expanded]");
5129
+ if (menus.toArray().some((menu) => !$(menu).is('button[aria-haspopup="menu"]') || !$(menu).attr("aria-label")?.trim() && !$(menu).text().trim() || $(menu).attr("aria-controls") || !legacy && $(menu).closest('[data-component-part="code-group-tab-bar"]')[0] !== group.children('[data-component-part="code-group-tab-bar"]')[0] || legacy && !$(menu).parents().toArray().includes(group.children().first()[0]))) return;
5130
+ if (menus.length === 2) {
5131
+ const samples = declared.map((pair) => JSON.parse(pair));
5132
+ const labelledChoices = samples.filter((sample) => sample[2] && samples.filter((other) => other[0] === sample[0]).length >= 2);
5133
+ if (!labelledChoices.length || !menus.toArray().some((menu) => labelledChoices.some((sample) => sample[2] === $(menu).text().trim()))) return;
5134
+ }
5135
+ const panels = group.find("[data-index]");
5136
+ const code = group.find("pre").toArray().map((pre) => {
5137
+ const lines = $(pre).find(".token-line");
5138
+ return lines.length ? lines.toArray().map((line) => $(line).text()).join("\n") : $(pre).text();
5139
+ });
5140
+ if (group.find('[aria-busy="true"],[role="progressbar"]').length) return;
5141
+ if (legacy) {
5142
+ if (menus.length !== 2 || group.children().length !== 2 || group.children().first().find("pre").length || code.length !== 1 || !code[0].trim() || panels.length) return;
5143
+ const selectedSamples = declared.map((pair) => JSON.parse(pair)).filter((sample) => sample[2] && menus.toArray().some((menu) => $(menu).text().trim() === sample[2]) && menus.toArray().some((menu) => $(menu).text().trim().toLowerCase() === sample[0]?.toLowerCase()) && (code[0] === sample[1] || code[0] === sample[1]?.replace(/\n$/, "")));
5144
+ if (!selectedSamples.length) return;
5145
+ } else if (!panels.length || panels.length > MAX_TABS || code.length !== panels.length || !panels.toArray().some((panel) => $(panel).attr("data-index") === "-1") || panels.toArray().some((panel) => $(panel).find("pre").length !== 1 || !$(panel).text().trim()) || !sources.every((source) => code.some((captured) => captured === source || captured === source.replace(/\n$/, "")))) return;
5146
+ const controls = menus.toArray().map((menu) => JSON.stringify([$(menu).text(), $(menu).attr("aria-label") ?? "", $(menu).attr("aria-expanded"), $(menu).attr("aria-haspopup")]));
5147
+ const owners = group.parents("[id]").toArray().reverse().map((owner) => $(owner).attr("id"));
5148
+ if (group.attr("id")) owners.push(group.attr("id"));
5149
+ domGroups.set(group[0], { signature: JSON.stringify([owners, code, controls]), indices: menus.toArray().map((menu) => controlIndices.get(menu)), code, legacy, title: group.children('[data-component-part="code-group-tab-bar"]').find("span[title]").first().attr("title"), label: legacy || group.children('[data-component-part="code-group-tab-bar"]').length === 1 && group.children('[data-component-part="code-group-tab-bar"]').find("span[title]").toArray().some((title) => Boolean($(title).attr("title")?.trim()) && $(title).text().trim() === $(title).attr("title")) });
5150
+ });
5151
+ const domCounts = /* @__PURE__ */ new Map();
5152
+ for (const group of domGroups.values()) domCounts.set(group.signature, (domCounts.get(group.signature) ?? 0) + 1);
5153
+ const proofs = [];
5154
+ for (const group of domGroups.values()) if (sourceCounts.get(group.signature) === 1 && domCounts.get(group.signature) === 1) proofs.push(...group.indices);
5155
+ if (groups.length === 0 && providerMetadataMatches && generatedControllers === 1 && domGroups.size === 2) {
5156
+ const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
5157
+ const endpoint = $(node).find('[data-component-part="scroll-area-content"]').first().clone();
5158
+ endpoint.find("[data-thally-static-base-urls]").remove();
5159
+ return $(node).find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
5160
+ });
5161
+ const main = $("main#content-container"), side = $('[id="content-side-layout"]');
5162
+ const content = $('[id="content-area"]');
5163
+ const slots = /* @__PURE__ */ new Map();
5164
+ const titles = [resolveValue(providers[0].title), resolveValue(providers[0].summary), ...declared.flatMap((pair) => {
5165
+ const values = JSON.parse(pair);
5166
+ return [values[0], values[2]];
5167
+ })].filter((value) => typeof value === "string").map((value) => value.toLowerCase());
5168
+ let valid = operations.length === 1 && main.length === 1 && side.length === 1 && content.length === 1 && operations.closest("#content-area")[0] === content[0] && $("[id]").filter((_index, node) => $(node).attr("id") === operations.attr("id")).length === 1 && side.closest("main")[0] === main[0];
5169
+ for (const [node, entry] of domGroups) {
5170
+ const group = $(node);
5171
+ const control = group.find("button[aria-expanded]").first();
5172
+ const owner = group.closest('[id^="api-playground-"][id$="-operation-page"]');
5173
+ const slot = owner[0] === operations[0] ? "inline" : group.closest("#content-side-layout")[0] === side[0] ? "sidebar" : void 0;
5174
+ if (!slot || slots.has(slot) || !entry.label || !entry.legacy && (!entry.title || !titles.includes(entry.title.toLowerCase()) || !control.attr("aria-label")?.trim()) || group.closest("main")[0] !== main[0] || group.closest('.mdx-content,.api-section,.object-param-field,.array-param-field,[data-component-part="expandable-content"]').length) valid = false;
5175
+ else slots.set(slot, entry);
5176
+ }
5177
+ if (valid && slots.size === 2 && JSON.stringify(slots.get("inline")?.code) === JSON.stringify(slots.get("sidebar")?.code)) {
5178
+ proofs.push(...[...slots.values()].flatMap((slot) => slot.indices));
5179
+ }
5180
+ }
5181
+ return proofs;
5182
+ }
3650
5183
 
3651
5184
  // src/url-run.ts
3652
- var CONVERTER_VERSION = "url-evidence-v5";
3653
- var parser = unified3().use(remarkParse3).use(remarkMdx3);
5185
+ var CONVERTER_VERSION = "url-evidence-v6";
5186
+ var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
3654
5187
  var ACCEPT = "text/markdown,text/html,application/xhtml+xml;q=0.9";
3655
5188
  var MAX_RESPONSE_BYTES = 8e6;
3656
5189
  var MAX_ASSET_BYTES = 1e7;
3657
5190
  var MAX_RECORDS = 1e5;
3658
5191
  var LOCALES = /* @__PURE__ */ new Set(["ar", "cs", "da", "de", "el", "en", "es", "fi", "fr", "he", "hi", "hu", "id", "it", "ja", "ko", "ms", "nl", "no", "pl", "pt", "pt-br", "ro", "ru", "sk", "sv", "th", "tr", "uk", "vi", "zh", "zh-cn", "zh-hans", "zh-hant"]);
3659
5192
  function hash(value) {
3660
- return createHash("sha256").update(value).digest("hex");
5193
+ return createHash4("sha256").update(value).digest("hex");
3661
5194
  }
3662
5195
  function canonicalJson(value) {
3663
5196
  const canonical2 = (value2) => Array.isArray(value2) ? value2.map(canonical2) : value2 !== null && typeof value2 === "object" ? Object.fromEntries(Object.entries(value2).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, field]) => [key, canonical2(field)])) : value2;
@@ -3693,14 +5226,14 @@ function markdownNodes(body) {
3693
5226
  nodes.push(node);
3694
5227
  for (const child of node.children ?? []) visit(child);
3695
5228
  };
3696
- visit(parser.parse(body));
5229
+ visit(parser2.parse(body));
3697
5230
  return nodes;
3698
5231
  }
3699
5232
  function markdownCode(body) {
3700
5233
  return markdownNodes(body).filter((node) => node.type === "code").map((node) => node.value ?? "");
3701
5234
  }
3702
5235
  function alternativeBlocks(body, baseline) {
3703
- const blocks = (value) => (parser.parse(value).children ?? []).map((node) => value.slice(node.position.start.offset, node.position.end.offset));
5236
+ const blocks = (value) => (parser2.parse(value).children ?? []).map((node) => value.slice(node.position.start.offset, node.position.end.offset));
3704
5237
  const originals = new Set(blocks(baseline));
3705
5238
  return blocks(body).filter((block) => !originals.has(block)).join("\n\n");
3706
5239
  }
@@ -3712,7 +5245,7 @@ function linksOf(document) {
3712
5245
  return [...root.find("a[href]").toArray(), ...$('link[rel="sitemap"][href],link[rel="alternate"][href]').toArray()].map((node) => $(node).attr("href")).filter(Boolean);
3713
5246
  }
3714
5247
  if (/xml/i.test(document.contentType) || /<urlset|<sitemapindex/i.test(document.body)) {
3715
- const $ = load5(document.body, { xmlMode: true });
5248
+ const $ = load7(document.body, { xmlMode: true });
3716
5249
  return $("loc").toArray().map((node) => $(node).text().trim());
3717
5250
  }
3718
5251
  return [
@@ -3742,7 +5275,8 @@ function evidenceOf(capture) {
3742
5275
  ...capture.artifactRef ? { artifactRef: capture.artifactRef } : {},
3743
5276
  states: (capture.states ?? []).map((state) => ({ id: state.id, label: state.label, sha256: hash(state.response.body) })),
3744
5277
  ...technical,
3745
- links: linksOf(capture)
5278
+ links: linksOf(capture),
5279
+ articleIdentity: observedMigrationArticleIdentity(capture)
3746
5280
  };
3747
5281
  }
3748
5282
  function markdownHeadingAnchors(body) {
@@ -3763,20 +5297,6 @@ function idForUrl(url, scope) {
3763
5297
  const id = pageIdFromReference(relative4 || "introduction", true);
3764
5298
  return id && `${id}${url.search ? `--${hash(url.search).slice(0, 12)}` : ""}`;
3765
5299
  }
3766
- function articleIdentity(document) {
3767
- if (!/html/i.test(document.contentType)) return document.finalUrl;
3768
- const $ = load5(document.body);
3769
- const reference = $('link[rel="canonical"][href]').first().attr("href");
3770
- if (!reference) return document.finalUrl;
3771
- try {
3772
- const target2 = new URL(reference, document.finalUrl);
3773
- const source = document.finalUrl;
3774
- const directory = (path) => path.replace(/\/index\/?$/i, "").replace(/\/$/, "");
3775
- return target2.origin === source.origin && !target2.username && !target2.password && target2.search === source.search && directory(target2.pathname) === directory(source.pathname) ? target2 : source;
3776
- } catch {
3777
- return document.finalUrl;
3778
- }
3779
- }
3780
5300
  function convert(document, id) {
3781
5301
  if (/html/i.test(document.contentType)) {
3782
5302
  const technical = htmlEvidence(document);
@@ -3825,24 +5345,52 @@ function assetReferences(capture) {
3825
5345
  ...root.find("[srcset]").toArray().flatMap((node) => migrationImageCandidates($(node).attr("srcset") ?? "", capture.finalUrl).map((candidate) => candidate.url))
3826
5346
  ].filter(Boolean);
3827
5347
  }
3828
- function unresolvedStaticControls(body, hasApiSchema = false, coveredSchemaGroups = []) {
5348
+ function unresolvedStaticControls(body, coveredSchemaGroups = [], coveredSchemaDisclosures = [], coveredCodeControls = []) {
3829
5349
  const { $, root } = migrationHtmlArticle(body);
5350
+ root.find("[aria-expanded]").each((index, node) => {
5351
+ if (coveredCodeControls.includes(index)) $(node).removeAttr("aria-expanded");
5352
+ });
3830
5353
  root.find('[role="tablist"],[data-component-part="tabs-list"]').each((index, node) => {
3831
5354
  if (coveredSchemaGroups.includes(index)) $(node).remove();
3832
5355
  });
5356
+ root.find("details").each((index, node) => {
5357
+ if (coveredSchemaDisclosures.includes(index)) $(node).remove();
5358
+ });
3833
5359
  const tabs = root.find('[role="tab"]').toArray();
5360
+ const idCounts = /* @__PURE__ */ new Map();
5361
+ root.find("[id]").each((_index, node) => {
5362
+ const id = $(node).attr("id");
5363
+ idCounts.set(id, (idCounts.get(id) ?? 0) + 1);
5364
+ });
3834
5365
  for (const tab of tabs) {
5366
+ const controlId = $(tab).attr("id");
5367
+ if (controlId && idCounts.get(controlId) !== 1) return true;
3835
5368
  const controls = $(tab).attr("aria-controls");
3836
- const group = $(tab).closest('[role="tablist"]');
5369
+ const group = $(tab).closest('[role="tablist"],ul.tabs');
3837
5370
  const container = group.closest(".code-group,.tabs-container,.tabs.tab-container");
3838
- const peers = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === group[0]);
5371
+ const peers = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"],ul.tabs')[0] === group[0]);
3839
5372
  const localPanels = container.find('[role="tabpanel"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
3840
- const owners = container.find('[role="tablist"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
5373
+ const owners = container.find('[role="tablist"],ul.tabs').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
3841
5374
  const panels = controls ? root.find("[id]").filter((_index, node) => $(node).attr("id") === controls) : owners.length === 1 && peers.length === localPanels.length ? $(localPanels).eq(peers.indexOf(tab)) : $([]);
3842
- if (panels.length !== 1 || !panels.text().trim() && !panels.find("pre,img,table,svg").length || panels.find('[aria-busy="true"],[role="progressbar"]').length) return true;
3843
- }
5375
+ if (panels.length !== 1 || !panels.text().trim() && !panels.find("pre,img,table,svg").length || panels.is('[aria-busy="true"],[role="progressbar"]') || panels.find('[aria-busy="true"],[role="progressbar"]').length) return true;
5376
+ const labels2 = panels.attr("aria-labelledby")?.trim().split(/\s+/);
5377
+ if (labels2 && (!controlId || !labels2.includes(controlId))) return true;
5378
+ if (controls && !labels2 && (container.length ? panels.closest(".code-group,.tabs-container,.tabs.tab-container")[0] !== container[0] : panels.parent()[0] !== group.parent()[0])) return true;
5379
+ }
5380
+ const isCapturedImageZoom = (node, depth = 0) => {
5381
+ if (depth > 8) return false;
5382
+ const control = $(node), image = control.prev("span.zoom-image-trigger[data-ramka-trigger]");
5383
+ return control.is('button[data-ramka-trigger][aria-haspopup="dialog"][aria-label]') && Boolean(control.attr("aria-label")?.trim()) && !control.attr("aria-controls") && !control.text().trim() && image.find("img[src]").length === 1 && !image.text().trim() && !image.find("form,iframe,object,embed,canvas").length && image.find("button").toArray().every((button) => isCapturedImageZoom(button, depth + 1));
5384
+ };
5385
+ if (root.find("[aria-expanded]").toArray().some((node) => {
5386
+ const control = $(node);
5387
+ if (control.attr("role") === "tab") return false;
5388
+ const actions = control.parent('[id="page-context-menu"]');
5389
+ if (control.is('button[aria-haspopup="menu"]') && !control.attr("aria-controls") && !control.text().trim() && actions.closest("header").length && actions.children("button#page-context-menu-button").length === 1) return false;
5390
+ if (isCapturedImageZoom(node)) return false;
5391
+ return true;
5392
+ })) return true;
3844
5393
  return root.find("details:not([open])").toArray().some((node) => {
3845
- if (hasApiSchema && $(node).is(".expandable") && $(node).children('summary[data-component-part="expandable-button"]').length) return false;
3846
5394
  const clone = $(node).clone();
3847
5395
  clone.children("summary").remove();
3848
5396
  return !clone.text().trim() && !clone.find("pre,img,table,svg").length;
@@ -3880,7 +5428,7 @@ function acceptedAsset(asset) {
3880
5428
  }
3881
5429
  function validRecord(record) {
3882
5430
  if (record.outcome !== "imported") return true;
3883
- return Boolean(record.page && record.pageSha256 === pageHash(record.page) && record.evidence && pageIdFromReference(record.page.id, true) === record.page.id && pageIdFromReference(record.page.navigationId, true) === record.page.navigationId && sanitizeRemoteMarkdown(record.page.body.replace(/^<a id="[^"<]*" \/>$/gm, "")) !== null && /^[a-f0-9]{64}$/.test(record.evidence.sha256) && (!record.apiSpecification || hasCompleteLocalApiSchema(record.apiSpecification.document) && record.apiSpecification.path === `openapi/migration-${record.apiSpecification.sha256}.json` && hash(canonicalJson(record.apiSpecification.document)) === record.apiSpecification.sha256) && record.assets?.every((asset) => /^migration-assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(asset.path) && hash(Buffer.from(asset.base64, "base64")) === asset.sha256));
5431
+ return Boolean(record.page && record.pageSha256 === pageHash(record.page) && record.evidence && pageIdFromReference(record.page.id, true) === record.page.id && pageIdFromReference(record.page.navigationId, true) === record.page.navigationId && sanitizeRemoteMarkdown(record.page.body.replace(/^<a id="[^"<]*" \/>$/gm, "")) !== null && /^[a-f0-9]{64}$/.test(record.evidence.sha256) && (!record.apiSpecification || hasCompleteLocalApiSchema(record.apiSpecification.document) && record.apiSpecification.path === `openapi/migration-${record.apiSpecification.sha256}.json` && hash(canonicalJson(record.apiSpecification.document)) === record.apiSpecification.sha256) && record.assets?.every((asset) => /^migration-assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(asset.path) && (!asset.derivedFromSha256 || /^[a-f0-9]{64}$/.test(asset.derivedFromSha256)) && hash(Buffer.from(asset.base64, "base64")) === asset.sha256));
3884
5432
  }
3885
5433
  function hasCompleteLocalApiSchema(document) {
3886
5434
  let visited = 0;
@@ -3986,7 +5534,7 @@ function observedLocale(document, id, scopePath) {
3986
5534
  }
3987
5535
  const urlLocale = [id.split("/", 1)[0], ...scopePath.split("/"), document.finalUrl.pathname.split("/").filter(Boolean)[0]].find((segment) => segment && LOCALES.has(segment.toLowerCase()));
3988
5536
  if (urlLocale) candidates.push({ source: "URL", value: canonicalLanguage(urlLocale) });
3989
- const declared = /html/i.test(document.contentType) ? load5(document.body)("html").attr("lang")?.trim() : void 0;
5537
+ const declared = /html/i.test(document.contentType) ? load7(document.body)("html").attr("lang")?.trim() : void 0;
3990
5538
  if (declared) {
3991
5539
  const locale = canonicalLanguage(declared);
3992
5540
  if (locale) candidates.push({ source: "html lang", value: locale });
@@ -4090,14 +5638,14 @@ function validateUrlMigration(checkpoint, cancelled = false) {
4090
5638
  continue;
4091
5639
  }
4092
5640
  if (!inScope(link, source, checkpoint.scopePath)) continue;
4093
- if (isDiscoveryIndex(link, source)) continue;
5641
+ if (isDiscoveryIndex(link, source) || isMigrationUtilityUrl(link)) continue;
4094
5642
  if (/\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js|json|ya?ml)$/i.test(link.pathname)) continue;
4095
5643
  const destination = resolve4(link.toString());
4096
5644
  if (!destination || !["imported", "excluded"].includes(destination.outcome)) {
4097
5645
  issues.push({ code: "missing-page", source: record.url, message: `Unresolved internal destination: ${link}` });
4098
- } else if (link.hash && destination.outcome === "imported") {
4099
- let anchor = link.hash.slice(1);
4100
- try {
5646
+ } else if ((link.hash || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "")?.aliasAnchor) && destination.outcome === "imported") {
5647
+ let anchor = link.hash.slice(1) || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "").aliasAnchor;
5648
+ if (link.hash) try {
4101
5649
  anchor = decodeURIComponent(anchor);
4102
5650
  } catch {
4103
5651
  }
@@ -4118,7 +5666,7 @@ function validateUrlMigration(checkpoint, cancelled = false) {
4118
5666
  const locale = node.locale ? canonicalLanguage(node.locale) : parentLocale;
4119
5667
  if (node.url) {
4120
5668
  const url = new URL(node.url, source);
4121
- if (inScope(url, source, checkpoint.scopePath)) {
5669
+ if (inScope(url, source, checkpoint.scopePath) && !isMigrationUtilityUrl(url)) {
4122
5670
  const destination = resolve4(url.toString());
4123
5671
  if (destination?.outcome === "excluded" && destination.redirectTo) continue;
4124
5672
  const page = destination?.page;
@@ -4186,6 +5734,7 @@ async function runUrlMigration(options) {
4186
5734
  delete record.redirectTo;
4187
5735
  delete record.reason;
4188
5736
  delete record.aliasOf;
5737
+ delete record.aliasAnchor;
4189
5738
  }
4190
5739
  if (record.page && pageIdFromReference(record.page.id, true) !== record.page.id) throw new Error("Checkpoint contains an unsafe page path.");
4191
5740
  records.set(record.url, record);
@@ -4196,7 +5745,8 @@ async function runUrlMigration(options) {
4196
5745
  const enqueue = (value, base, discoveredBy) => {
4197
5746
  const url = canonical(value, base);
4198
5747
  if (!url || !inScope(new URL(url), source, scopePath) && !isDiscoveryIndex(new URL(url), source)) return;
4199
- const isAttachment = discoveredBy !== "submitted" && /\.(?:json|ya?ml)$/i.test(new URL(url).pathname);
5748
+ const isUtility = isMigrationUtilityUrl(new URL(url));
5749
+ const isAttachment = isUtility || discoveredBy !== "submitted" && /\.(?:json|ya?ml)$/i.test(new URL(url).pathname);
4200
5750
  const existing = records.get(url);
4201
5751
  if (existing) {
4202
5752
  if (!existing.discoveredBy.includes(discoveredBy)) {
@@ -4210,7 +5760,7 @@ async function runUrlMigration(options) {
4210
5760
  url,
4211
5761
  discoveredBy: [discoveredBy],
4212
5762
  outcome: isAttachment ? "excluded" : "pending",
4213
- ...isAttachment ? { reason: "Machine-readable attachment, not an authored article." } : {}
5763
+ ...isAttachment ? { reason: isUtility ? "Recognized provider utility endpoint, not an authored article." : "Machine-readable attachment, not an authored article." } : {}
4214
5764
  };
4215
5765
  records.set(url, record);
4216
5766
  checkpoint.records.push(record);
@@ -4253,12 +5803,27 @@ async function runUrlMigration(options) {
4253
5803
  let imported = usedIds.size;
4254
5804
  const assetPayloads = new Map(checkpoint.records.flatMap((record) => (record.assets ?? []).map((asset) => [asset.sha256, asset])));
4255
5805
  const assetBytes = /* @__PURE__ */ new Map();
4256
- const retainAsset = (asset) => {
5806
+ const rasterizedSvgs = /* @__PURE__ */ new Map();
5807
+ const retainAsset = async (original) => {
5808
+ let asset = { ...original };
5809
+ delete asset.derivedFromSha256;
4257
5810
  if (asset.unavailable) {
4258
5811
  if ([404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) return asset;
4259
5812
  throw new Error("Invalid source asset availability evidence.");
4260
5813
  }
4261
- if (!acceptedAsset(asset)) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
5814
+ if (!acceptedAsset(asset)) {
5815
+ if (asset.content.byteLength > MAX_ASSET_BYTES || asset.contentType.split(";", 1)[0].trim().toLowerCase() !== "image/svg+xml" || !options.rasterizeSvg) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
5816
+ const sourceSha256 = hash(asset.content);
5817
+ let rendering = rasterizedSvgs.get(sourceSha256);
5818
+ if (!rendering) {
5819
+ rendering = options.rasterizeSvg({ ...asset, content: Buffer.from(asset.content) }, options.signal).then((rendered) => {
5820
+ if (rendered.unavailable || rendered.contentType.split(";", 1)[0].trim().toLowerCase() !== "image/png" || acceptedAsset(rendered) !== "png") throw new Error("Trusted SVG renderer did not return a bounded PNG image.");
5821
+ return { ...rendered, derivedFromSha256: sourceSha256 };
5822
+ });
5823
+ rasterizedSvgs.set(sourceSha256, rendering);
5824
+ }
5825
+ asset = { ...await rendering, url: original.url };
5826
+ }
4262
5827
  const sha256 = hash(asset.content);
4263
5828
  const content = assetBytes.get(sha256) ?? Buffer.from(asset.content);
4264
5829
  assetBytes.set(sha256, content);
@@ -4274,6 +5839,26 @@ async function runUrlMigration(options) {
4274
5839
  }
4275
5840
  let totalBytes = checkpoint.records.reduce((sum, record) => sum + (record.evidence?.byteLength ?? 0), 0) + [...assetPayloads.values()].reduce((sum, asset) => sum + Buffer.byteLength(asset.base64, "base64"), 0);
4276
5841
  let budgetExhausted = false;
5842
+ const exactCaptures = /* @__PURE__ */ new Map();
5843
+ const exactCaptureKey = (document) => {
5844
+ let body = document.body;
5845
+ if (body.includes("data-cfemail")) {
5846
+ const $ = load7(body);
5847
+ projectProtectedEmails($, $.root());
5848
+ body = $.html();
5849
+ }
5850
+ return hash(canonicalJson([
5851
+ hash(body),
5852
+ document.contentType,
5853
+ document.status,
5854
+ document.locale,
5855
+ document.version,
5856
+ document.headers?.["content-language"],
5857
+ document.navigation,
5858
+ (document.states ?? []).map((state) => [state.id, state.label, state.response.finalUrl.href, state.response.contentType, state.response.status, hash(state.response.body)]),
5859
+ (document.assets ?? []).map((asset) => [asset.url, asset.contentType, asset.unavailable, hash(asset.content)])
5860
+ ]));
5861
+ };
4277
5862
  let cursor = 0;
4278
5863
  const flush = async () => {
4279
5864
  const distinct = [...new Map(changed.map((record) => [record.url, record])).values()];
@@ -4288,6 +5873,7 @@ async function runUrlMigration(options) {
4288
5873
  let claimedId;
4289
5874
  let claimedFinal;
4290
5875
  let markdownRepresentation;
5876
+ let exactKey;
4291
5877
  try {
4292
5878
  delete record.reason;
4293
5879
  if (/\/llms-full\.txt$/i.test(new URL(record.url).pathname)) {
@@ -4315,7 +5901,43 @@ async function runUrlMigration(options) {
4315
5901
  throw new Error("Capture returned an unresolved redirect.");
4316
5902
  }
4317
5903
  if (Buffer.byteLength(document.body) > MAX_RESPONSE_BYTES) throw new Error("Captured response exceeds the 8 MB safety bound.");
4318
- const identity = articleIdentity(document);
5904
+ if ((document.states?.length ?? 0) > 64 || (document.assets?.length ?? 0) > 256) throw new Error("Capture contains too many alternative states or assets.");
5905
+ for (const state of document.states ?? []) if (Buffer.byteLength(state.response.body) > MAX_RESPONSE_BYTES) throw new Error("Alternative state exceeds the 8 MB safety bound.");
5906
+ if (/html/i.test(document.contentType) && (!document.status || document.status < 300) && inScope(document.finalUrl, source, scopePath)) {
5907
+ exactKey = exactCaptureKey(document);
5908
+ const peer = resolveMigrationRecord(records, records.get(exactCaptures.get(exactKey) ?? ""));
5909
+ const peerIdentity = peer?.evidence?.articleIdentity;
5910
+ if (peer?.outcome === "imported" && peer.evidence && peerIdentity) {
5911
+ const candidate = migrationSectionQueryCandidate(document.finalUrl, peer.evidence.anchors);
5912
+ if (candidate.sectionAnchor && candidate.canonicalUrl === peerIdentity.canonicalUrl) {
5913
+ const byteLength = Buffer.byteLength(document.body) + (document.states ?? []).reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0);
5914
+ totalBytes += byteLength;
5915
+ if (totalBytes > (options.maxTotalBytes ?? 5e8)) {
5916
+ budgetExhausted = true;
5917
+ throw new Error("Migration exceeded its content byte budget.");
5918
+ }
5919
+ record.outcome = "alias";
5920
+ record.aliasOf = peer.url;
5921
+ record.aliasAnchor = candidate.sectionAnchor;
5922
+ if (peerIdentity.sectionAnchor && peer.aliasAnchor !== peerIdentity.sectionAnchor) {
5923
+ peer.aliasAnchor = peerIdentity.sectionAnchor;
5924
+ changed.push(peer);
5925
+ }
5926
+ record.evidence = {
5927
+ ...peer.evidence,
5928
+ sha256: hash(document.body),
5929
+ byteLength,
5930
+ finalUrl: document.finalUrl.href,
5931
+ ...document.artifactRef ? { artifactRef: document.artifactRef } : {},
5932
+ articleIdentity: { ...peerIdentity, sectionAnchor: candidate.sectionAnchor }
5933
+ };
5934
+ if (!document.artifactRef) delete record.evidence.artifactRef;
5935
+ enqueueArticleLinks(record.evidence.links, document.finalUrl.href, record.url);
5936
+ return;
5937
+ }
5938
+ }
5939
+ }
5940
+ const identity = declaredMigrationArticleUrl(document);
4319
5941
  const final = canonical(identity.toString(), sourceUrl);
4320
5942
  const role = classifyMigrationResponse(document);
4321
5943
  if (!inScope(document.finalUrl, source, scopePath) && !(role === "index" && isDiscoveryIndex(document.finalUrl, source))) {
@@ -4339,13 +5961,13 @@ async function runUrlMigration(options) {
4339
5961
  return;
4340
5962
  }
4341
5963
  if (/html/i.test(document.contentType)) {
4342
- const $ = load5(document.body);
5964
+ const $ = load7(document.body);
4343
5965
  $('link[rel="alternate"][type="text/markdown"][href]').each((_index, node) => {
4344
5966
  const alternate = canonical($(node).attr("href"), final);
4345
5967
  if (!alternate) return;
4346
5968
  const target2 = new URL(alternate), primary = new URL(final);
4347
- const directory = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
4348
- if (target2.origin !== primary.origin || target2.search !== primary.search || directory(target2.pathname) !== directory(primary.pathname) || !/\.mdx?$/i.test(target2.pathname)) return;
5969
+ const directory2 = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
5970
+ if (target2.origin !== primary.origin || target2.search !== primary.search || directory2(target2.pathname) !== directory2(primary.pathname) || !/\.mdx?$/i.test(target2.pathname)) return;
4349
5971
  enqueue(alternate, final, "declared-markdown-representation");
4350
5972
  const sibling = records.get(alternate);
4351
5973
  sibling.representationOf = record.url;
@@ -4396,6 +6018,9 @@ async function runUrlMigration(options) {
4396
6018
  return;
4397
6019
  }
4398
6020
  let coveredSchemaGroups = [];
6021
+ let coveredSchemaDisclosures = [];
6022
+ let coveredCodeControls = [];
6023
+ let schemaOperation;
4399
6024
  const hasSchemaChoice = /html/i.test(document.contentType) && migrationHtmlArticle(document.body).root.find('select[aria-label="Select schema type"],select[aria-label="\u9009\u62E9\u67B6\u6784\u7C7B\u578B"]').length > 0;
4400
6025
  if (/html/i.test(document.contentType) && markdownRepresentation && (unresolvedStaticControls(document.body) || hasSchemaChoice)) {
4401
6026
  const schemaCapture = await capture({ url: new URL(markdownRepresentation), signal: options.signal });
@@ -4411,7 +6036,10 @@ async function runUrlMigration(options) {
4411
6036
  const path = `openapi/migration-${sha256}.json`;
4412
6037
  record.apiSpecification = { path, sha256, document: fragment.document };
4413
6038
  page.openapi = `${path} ${fragment.method} ${fragment.path}`;
6039
+ schemaOperation = fragment;
4414
6040
  coveredSchemaGroups = apiSchemaTabGroups(document.body, fragment.document, fragment);
6041
+ coveredSchemaDisclosures = apiSchemaDisclosureGroups(document.body, fragment.document, fragment);
6042
+ coveredCodeControls = apiOperationCodeControls(document.body, fragment.document, fragment);
4415
6043
  }
4416
6044
  }
4417
6045
  const states = document.states ?? [];
@@ -4442,12 +6070,13 @@ ${difference}`;
4442
6070
  record.assets = [];
4443
6071
  record.issues = [];
4444
6072
  record.warnings = [];
4445
- if (/html/i.test(document.contentType)) {
4446
- const { $, root } = migrationHtmlArticle(document.body);
6073
+ for (const captured of [document, ...states.map((state) => state.response)]) {
6074
+ if (!/html/i.test(captured.contentType)) continue;
6075
+ const { $, root } = migrationHtmlArticle(captured.body);
4447
6076
  root.find("nav,footer,aside,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
4448
6077
  root.find('button svg,a.hash-link svg,[aria-hidden="true"] svg,svg[aria-hidden="true"],svg[data-lucide],svg[data-icon]').remove();
4449
6078
  root.find("video,iframe").each((_index, node) => {
4450
- if (projectMigrationMedia($.html(node), document.finalUrl) !== null) {
6079
+ if (projectMigrationMedia($.html(node), captured.finalUrl) !== null) {
4451
6080
  record.warnings.push({ code: "source-asset-warning", source: record.url, message: "Embedded video is preserved at its original public media URL; playback depends on the original host." });
4452
6081
  $(node).remove();
4453
6082
  }
@@ -4458,9 +6087,17 @@ ${difference}`;
4458
6087
  }
4459
6088
  const unsupported = [...new Set(root.find("iframe,object,embed,canvas,svg,form,input,textarea,select,math,video,audio").toArray().map((node) => node.tagName))];
4460
6089
  if (unsupported.length) record.issues.push({ code: "unsupported-component", source: record.url, message: `Rendered content requires explicit review for: ${unsupported.join(", ")}.` });
4461
- if (!states.length && unresolvedStaticControls(document.body, Boolean(record.apiSpecification), coveredSchemaGroups)) record.issues.push({ code: "unsupported-component", source: record.url, message: "An interactive alternative has no complete captured panel or disclosure content." });
6090
+ const groups = captured === document ? coveredSchemaGroups : record.apiSpecification && schemaOperation ? apiSchemaTabGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
6091
+ const disclosures = captured === document ? coveredSchemaDisclosures : record.apiSpecification && schemaOperation ? apiSchemaDisclosureGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
6092
+ const codeControls = captured === document ? coveredCodeControls : record.apiSpecification && schemaOperation ? apiOperationCodeControls(captured.body, record.apiSpecification.document, schemaOperation) : [];
6093
+ if (unresolvedStaticControls(captured.body, groups, disclosures, codeControls)) record.issues.push({ code: "unsupported-component", source: record.url, message: "An interactive alternative has no complete captured panel or disclosure content." });
4462
6094
  }
4463
- const suppliedAssets = new Map((document.assets ?? []).map((asset) => [canonical(asset.url, final), asset]));
6095
+ const inlineAssets = [document, ...states.map((state) => state.response)].flatMap((captured) => /html/i.test(captured.contentType) ? migrationHtmlArticle(captured.body).assets.map((asset) => ({
6096
+ url: new URL(`/${asset.path}`, captured.finalUrl).href,
6097
+ contentType: "image/svg+xml",
6098
+ content: typeof asset.content === "string" ? Buffer.from(asset.content) : asset.content
6099
+ })) : []);
6100
+ const suppliedAssets = new Map([...document.assets ?? [], ...inlineAssets].map((asset) => [canonical(asset.url, final), asset]));
4464
6101
  const assetUrls = [...new Set([...assetReferences(document), ...states.flatMap((state) => assetReferences(state.response))].map((url) => canonical(url, final)).filter((url) => Boolean(url)))];
4465
6102
  if (assetUrls.length > 256 || (document.assets?.length ?? 0) > 256) throw new Error("Capture exceeds the 256 assets per page safety bound.");
4466
6103
  for (const url of assetUrls) {
@@ -4470,7 +6107,7 @@ ${difference}`;
4470
6107
  assetRequest = assetRequests.load(url, () => assetFetcher(new URL(url), options.signal).then(retainAsset));
4471
6108
  }
4472
6109
  const supplied = suppliedAssets.get(url);
4473
- const asset = supplied ? retainAsset(supplied) : assetRequest ? await assetRequest : null;
6110
+ const asset = supplied ? await retainAsset(supplied) : assetRequest ? await assetRequest : null;
4474
6111
  if (!asset) throw new Error("Capture host did not supply asset bytes or a binary fetch boundary.");
4475
6112
  if (asset.unavailable && [404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) {
4476
6113
  record.warnings.push({ code: "asset-failed", source: record.url, message: `The original source asset is unavailable (HTTP ${asset.unavailable.status}); its original URL was retained: ${url}` });
@@ -4489,7 +6126,7 @@ ${difference}`;
4489
6126
  }
4490
6127
  }
4491
6128
  const path = `migration-assets/${sha256}.${extension}`;
4492
- const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64") };
6129
+ const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64"), ...asset.derivedFromSha256 ? { derivedFromSha256: asset.derivedFromSha256 } : {} };
4493
6130
  record.assets.push(receipt);
4494
6131
  if (!existing) assetPayloads.set(sha256, receipt);
4495
6132
  if (!assetRequests.has(url)) assetRequests.remember(url, asset);
@@ -4514,6 +6151,7 @@ ${difference}`;
4514
6151
  record.pageSha256 = pageHash(page);
4515
6152
  record.outcome = "imported";
4516
6153
  imported++;
6154
+ if (exactKey) exactCaptures.set(exactKey, record.url);
4517
6155
  enqueueArticleLinks(evidence.links, final, record.url);
4518
6156
  } catch (error) {
4519
6157
  if (options.signal?.aborted) return;
@@ -4539,6 +6177,43 @@ ${difference}`;
4539
6177
  }
4540
6178
  }
4541
6179
  };
6180
+ const settleArticleIdentities = () => {
6181
+ const groups = /* @__PURE__ */ new Map();
6182
+ for (const record of records.values()) {
6183
+ const identity = record.evidence?.articleIdentity;
6184
+ if (record.outcome !== "imported" || !identity) continue;
6185
+ const key = JSON.stringify([identity.canonicalUrl, identity.articleSha256]);
6186
+ const group = groups.get(key) ?? [];
6187
+ group.push(record);
6188
+ groups.set(key, group);
6189
+ }
6190
+ for (const group of groups.values()) {
6191
+ if (group.length < 2) continue;
6192
+ const canonicalUrl = group[0].evidence.articleIdentity.canonicalUrl;
6193
+ const winner = group.find((record) => record.url === canonicalUrl) ?? group[0];
6194
+ const winnerAnchor = winner.evidence.articleIdentity.sectionAnchor;
6195
+ if (winnerAnchor && winner.aliasAnchor !== winnerAnchor) {
6196
+ winner.aliasAnchor = winnerAnchor;
6197
+ changed.push(winner);
6198
+ }
6199
+ for (const alternate of group) {
6200
+ if (alternate === winner) continue;
6201
+ imported--;
6202
+ if (alternate.page && usedIds.get(alternate.page.id) === alternate.url) usedIds.delete(alternate.page.id);
6203
+ alternate.outcome = "alias";
6204
+ alternate.aliasOf = winner.url;
6205
+ alternate.aliasAnchor = alternate.evidence.articleIdentity.sectionAnchor;
6206
+ delete alternate.page;
6207
+ delete alternate.pageSha256;
6208
+ delete alternate.assets;
6209
+ delete alternate.apiSpecification;
6210
+ delete alternate.issues;
6211
+ delete alternate.warnings;
6212
+ delete alternate.reason;
6213
+ changed.push(alternate);
6214
+ }
6215
+ }
6216
+ };
4542
6217
  const settleRepresentations = () => {
4543
6218
  for (const record of records.values()) {
4544
6219
  if (!record.representationOf) continue;
@@ -4564,6 +6239,7 @@ ${difference}`;
4564
6239
  }
4565
6240
  };
4566
6241
  settleRepresentations();
6242
+ settleArticleIdentities();
4567
6243
  while (cursor < pending.length && !options.signal?.aborted && !budgetExhausted && cursor < (options.maxCaptures ?? Infinity)) {
4568
6244
  if (imported >= maxPages) {
4569
6245
  const index = pending.findIndex((record, position) => position >= cursor && isDiscoveryIndex(new URL(record.url), source));
@@ -4574,6 +6250,7 @@ ${difference}`;
4574
6250
  cursor += batch.length;
4575
6251
  await Promise.all(batch.map(process));
4576
6252
  settleRepresentations();
6253
+ settleArticleIdentities();
4577
6254
  if (changed.length >= batchSize) await flush();
4578
6255
  }
4579
6256
  await flush();
@@ -4593,7 +6270,7 @@ ${difference}`;
4593
6270
  for (const node of nodes) {
4594
6271
  const entry = node.url ? records.get(canonical(node.url, sourceUrl) ?? "") : void 0;
4595
6272
  const target2 = resolveMigrationRecord(records, entry);
4596
- if (target2?.page && node.label && !node.children?.length) {
6273
+ if (target2?.page && node.label && !node.children?.length && !entry?.aliasAnchor) {
4597
6274
  const previous = navigationLabels.get(target2.url);
4598
6275
  if (previous && previous !== node.label) navigationIssues.push({ code: "unsupported-component", source: target2.url, message: `This page has multiple source navigation labels (${previous}, ${node.label}); review the shared sidebar title.` });
4599
6276
  else navigationLabels.set(target2.url, node.label);
@@ -4618,8 +6295,9 @@ ${difference}`;
4618
6295
  anchor = decodeURIComponent(anchor);
4619
6296
  } catch {
4620
6297
  }
6298
+ const effectiveHash = url.hash || (records.get(identity)?.aliasAnchor ? `#${encodeURIComponent(records.get(identity).aliasAnchor)}` : "");
4621
6299
  const hasBrokenSourceAnchor = Boolean(url.hash && destination?.outcome === "imported" && destination.evidence && !destination.evidence.anchors.includes(anchor));
4622
- return `${route}${hasBrokenSourceAnchor ? "" : url.hash}`;
6300
+ return `${route}${hasBrokenSourceAnchor ? "" : effectiveHash}`;
4623
6301
  }) }] : []);
4624
6302
  const validation = validateUrlMigration(checkpoint, Boolean(options.signal?.aborted));
4625
6303
  validation.issues.push(...navigationIssues);
@@ -4698,16 +6376,16 @@ function rewriteMarkdownDestinations(body, rewrite) {
4698
6376
  }
4699
6377
 
4700
6378
  // src/components.ts
4701
- import { createHash as createHash2 } from "crypto";
6379
+ import { createHash as createHash5 } from "crypto";
4702
6380
  import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
4703
6381
  import { basename, dirname as dirname2, extname as extname3, posix as posix2, relative as relative3, resolve as resolve3 } from "path";
4704
6382
  import postcss from "postcss";
4705
6383
  import selectorParser from "postcss-selector-parser";
4706
- import remarkMdx4 from "remark-mdx";
4707
- import remarkParse4 from "remark-parse";
6384
+ import remarkMdx5 from "remark-mdx";
6385
+ import remarkParse5 from "remark-parse";
4708
6386
  import ts2 from "typescript";
4709
- import { unified as unified4 } from "unified";
4710
- var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
6387
+ import { unified as unified5 } from "unified";
6388
+ var parser3 = unified5().use(remarkParse5).use(remarkMdx5);
4711
6389
  var DOCUSAURUS_THEME_SHIMS = {
4712
6390
  "@theme/CodeBlock": {
4713
6391
  filename: "docusaurus-code-block.tsx",
@@ -4747,7 +6425,7 @@ function applyReplacements(source, replacements) {
4747
6425
  );
4748
6426
  }
4749
6427
  function hash2(value) {
4750
- return createHash2("sha256").update(value).digest("hex").slice(0, 12);
6428
+ return createHash5("sha256").update(value).digest("hex").slice(0, 12);
4751
6429
  }
4752
6430
  function sourceFile(source, filename) {
4753
6431
  return ts2.createSourceFile(filename, source, ts2.ScriptTarget.Latest, true, scriptKindFor(filename));