@thallylabs/migrate 0.2.39 → 0.2.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -5
- package/dist/index.d.ts +10 -2
- package/dist/index.js +8574 -6482
- package/dist/index.js.map +1 -1
- package/dist/svg-policy.js +6 -0
- package/dist/svg-policy.js.map +1 -1
- package/dist/url-migration.d.ts +2 -2
- package/dist/url-migration.js +1825 -113
- package/dist/url-migration.js.map +1 -1
- package/dist/{url-run-BOZvcGs1.d.ts → url-run-mSKFqHx_.d.ts} +47 -3
- package/package.json +1 -1
package/dist/url-migration.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/url-run.ts
|
|
2
|
-
import { createHash } from "crypto";
|
|
3
|
-
import { load as
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { unified as
|
|
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
|
|
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(
|
|
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 !==
|
|
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
|
-
|
|
459
|
-
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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) =>
|
|
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 (
|
|
796
|
-
|
|
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)
|
|
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
|
|
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))
|
|
806
|
-
else
|
|
807
|
-
} else
|
|
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
|
-
|
|
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
|
|
979
|
-
import
|
|
980
|
-
import
|
|
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
|
|
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
|
|
994
|
-
import
|
|
995
|
-
import
|
|
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 =
|
|
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 ?? "";
|
|
@@ -1709,8 +2618,29 @@ ${trimmed.slice(0, marker).trimEnd()}`;
|
|
|
1709
2618
|
function headingIdMarker(id) {
|
|
1710
2619
|
return `{/* #${id} */}`;
|
|
1711
2620
|
}
|
|
2621
|
+
var HEADING_ID_WORD = /[\p{L}\p{M}\p{N}_+&/@\u2019\u201c\u201d\u2014\u2013\u2192]/u;
|
|
1712
2622
|
function mintlifyHeadingSlug(text2) {
|
|
1713
|
-
|
|
2623
|
+
const chars = [...text2.replace(/[\u200b*]/g, "").normalize("NFC").trim().toLowerCase()];
|
|
2624
|
+
const isWord = (char) => char !== void 0 && HEADING_ID_WORD.test(char);
|
|
2625
|
+
let id = "";
|
|
2626
|
+
let afterDash = false;
|
|
2627
|
+
let endsWithDash = false;
|
|
2628
|
+
chars.forEach((char, index) => {
|
|
2629
|
+
const before = chars[index - 1];
|
|
2630
|
+
const after = chars[index + 1];
|
|
2631
|
+
endsWithDash = false;
|
|
2632
|
+
if (isWord(char)) {
|
|
2633
|
+
id += char;
|
|
2634
|
+
afterDash = false;
|
|
2635
|
+
} else if (/[\s-]/.test(char) || char === "." && isWord(after) || char === "(" && isWord(before) && (isWord(after) || after === "{") || char === ")" && after !== void 0 && !/\s/.test(after)) {
|
|
2636
|
+
if (id && !afterDash) id += "-";
|
|
2637
|
+
afterDash = true;
|
|
2638
|
+
endsWithDash = true;
|
|
2639
|
+
} else if (char === "{" || char === ":" || char === '"') {
|
|
2640
|
+
afterDash = false;
|
|
2641
|
+
}
|
|
2642
|
+
});
|
|
2643
|
+
return endsWithDash ? id.replace(/-$/, "") : id;
|
|
1714
2644
|
}
|
|
1715
2645
|
function thallyHeadingSlug(text2) {
|
|
1716
2646
|
return text2.normalize("NFC").toLowerCase().replace(/[^\p{L}\p{M}\p{N}]+/gu, "-").replace(/(^-|-$)/g, "");
|
|
@@ -1722,7 +2652,7 @@ function typesetQuotes(text2) {
|
|
|
1722
2652
|
}).join("");
|
|
1723
2653
|
}
|
|
1724
2654
|
function plainHeadingText(source) {
|
|
1725
|
-
return source.replace(/\{\/\*[\s\S]*?\*\/\}/g, " ").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<\/?[A-Za-z][^>]*>/g, "").replace(/`([^`]*)`/g, "$1").replace(/(\*\*|__|~~|\*)/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/\\([\\`*_{}[\]()#+.!&<>|~-])/g, "$1");
|
|
2655
|
+
return source.replace(/\{\/\*[\s\S]*?\*\/\}/g, " ").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<\/?[A-Za-z][^>]*>/g, "").replace(/`([^`]*)`/g, "$1").replace(/(?<!\\)(\*\*|__|~~|\*)/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/\\([\\`*_{}[\]()#+.!&<>|~-])/g, "$1");
|
|
1726
2656
|
}
|
|
1727
2657
|
function markMintlifyHeadings(body) {
|
|
1728
2658
|
let fence;
|
|
@@ -2434,7 +3364,7 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
2434
3364
|
}
|
|
2435
3365
|
if (runMintlify) rewritten = normalizeMintlifyParameterAnchors(rewritten);
|
|
2436
3366
|
rewritten = replaceOutsideCode(rewritten, (segment) => {
|
|
2437
|
-
let result = convertHtmlStyleAttributes(segment).replace(/<!--([\s\S]*?)-->/g, (_match, content) => mdxComment(content)).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").replace(/<Warn(\s[^>]*)?>/g, "<Warning$1>").replace(/<\/Warn>/g, "</Warning>").replace(/<
|
|
3367
|
+
let result = convertHtmlStyleAttributes(segment).replace(/<!--([\s\S]*?)-->/g, (_match, content) => mdxComment(content)).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").replace(/<Warn(\s[^>]*)?>/g, "<Warning$1>").replace(/<\/Warn>/g, "</Warning>").replace(/<Tree\.Folder/g, "<Folder").replace(/<\/Tree\.Folder>/g, "</Folder>").replace(/<Tree\.File/g, "<File").replace(/<\/Tree\.File>/g, "</File>");
|
|
2438
3368
|
if (runDocusaurus) {
|
|
2439
3369
|
result = result.replace(/<TabItem\b([^<>]*)>/g, (_match, attributes) => {
|
|
2440
3370
|
const title = attributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? attributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? "Tab";
|
|
@@ -2443,7 +3373,7 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
2443
3373
|
result = normalizeDocusaurusLinkTags(result).replace(/<(?:DocCardList|TOCInline)\b[^<>]*\/>/g, "");
|
|
2444
3374
|
}
|
|
2445
3375
|
if (runMintlify) {
|
|
2446
|
-
result = normalizeMintlifyUpdateLabels(result).replace(/<FileTree(\s[^>]*)?>/g, "<Tree$1>").replace(/<\/FileTree>/g, "</Tree>").replace(/<GitHub\.Repo/g, "<GitHub").replace(/<\/GitHub\.Repo>/g, "</GitHub>").replace(/<Column(\s[^>]*)?>/g, "<div$1>").replace(/<\/Column>/g, "</div>");
|
|
3376
|
+
result = normalizeMintlifyUpdateLabels(result).replace(/<FileTree(\s[^>]*)?>/g, "<Tree$1>").replace(/<\/FileTree>/g, "</Tree>").replace(/<GitHub\.Repo/g, "<GitHub").replace(/<\/GitHub\.Repo>/g, "</GitHub>").replace(/<Callout(\s[^<>]*)?>/g, (tag) => tag.replace(/(\s)type=(?:"[^"]*"|'[^']*'|\{[^{}]*\})/, '$1type="info"')).replace(/<Column(\s[^>]*)?>/g, "<div$1>").replace(/<\/Column>/g, "</div>");
|
|
2447
3377
|
result = pinStepsTitleSize(result);
|
|
2448
3378
|
}
|
|
2449
3379
|
if (runFern) {
|
|
@@ -2462,6 +3392,16 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
2462
3392
|
}
|
|
2463
3393
|
return normalizeFlowParagraphContainers(normalized);
|
|
2464
3394
|
}
|
|
3395
|
+
function externalPageUrl(value) {
|
|
3396
|
+
const url = typeof value === "string" ? value.trim() : "";
|
|
3397
|
+
if (!/^https?:\/\/[^\s]+$/i.test(url)) return void 0;
|
|
3398
|
+
try {
|
|
3399
|
+
new URL(url);
|
|
3400
|
+
return url;
|
|
3401
|
+
} catch {
|
|
3402
|
+
return void 0;
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
2465
3405
|
function parseMarkdownPage(input) {
|
|
2466
3406
|
const parsed = parseFrontmatter(input.raw);
|
|
2467
3407
|
const fallbackIdentity = {
|
|
@@ -2486,6 +3426,7 @@ function parseMarkdownPage(input) {
|
|
|
2486
3426
|
const badge = typeof parsed.data.tag === "string" && parsed.data.tag.trim() ? parsed.data.tag.trim() : typeof parsed.data.badge === "string" && parsed.data.badge.trim() ? parsed.data.badge.trim() : void 0;
|
|
2487
3427
|
const sourceMode = typeof parsed.data.mode === "string" ? parsed.data.mode : void 0;
|
|
2488
3428
|
const mode = sourceMode === "frame" ? "wide" : ["default", "wide", "custom", "center", "home"].includes(sourceMode ?? "") ? sourceMode : void 0;
|
|
3429
|
+
const externalUrl = input.platform === "mintlify" ? externalPageUrl(parsed.data.url) : void 0;
|
|
2489
3430
|
const description = typeof parsed.data.description === "string" && parsed.data.description.trim() ? parsed.data.description.trim() : typeof parsed.data.openapi === "string" && parsed.data.openapi.trim() ? "" : firstParagraph(body);
|
|
2490
3431
|
const page = {
|
|
2491
3432
|
id: identity.id,
|
|
@@ -2501,7 +3442,9 @@ function parseMarkdownPage(input) {
|
|
|
2501
3442
|
keywords,
|
|
2502
3443
|
mode,
|
|
2503
3444
|
hidden: parsed.data.hidden === true ? true : void 0,
|
|
2504
|
-
|
|
3445
|
+
// The path of a page that links elsewhere redirects, so it has no business in a sitemap.
|
|
3446
|
+
noindex: parsed.data.noindex === true || parsed.data.noindex === "true" || externalUrl ? true : void 0,
|
|
3447
|
+
...externalUrl ? { externalUrl } : {},
|
|
2505
3448
|
...Object.keys(meta).length > 0 ? { meta } : {},
|
|
2506
3449
|
openapi: typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0,
|
|
2507
3450
|
...apiFrontmatter(parsed.data, input.warn),
|
|
@@ -2539,8 +3482,132 @@ function apiFrontmatter(data, warn) {
|
|
|
2539
3482
|
return result;
|
|
2540
3483
|
}
|
|
2541
3484
|
|
|
3485
|
+
// src/url-static-content.ts
|
|
3486
|
+
import { createHash as createHash2 } from "crypto";
|
|
3487
|
+
import { load as load4 } from "cheerio";
|
|
3488
|
+
function projectMigrationSvg(html) {
|
|
3489
|
+
const $ = load4(html, { xmlMode: true }, false);
|
|
3490
|
+
const svg = $.root().children();
|
|
3491
|
+
if (svg.length !== 1 || !svg.is("svg")) return null;
|
|
3492
|
+
if (svg.attr("xmlns") === void 0) svg.attr("xmlns", "http://www.w3.org/2000/svg");
|
|
3493
|
+
const content = Buffer.from($.xml(svg));
|
|
3494
|
+
if (!isPassiveSvg(content)) return null;
|
|
3495
|
+
const sha256 = createHash2("sha256").update(content).digest("hex");
|
|
3496
|
+
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);
|
|
3497
|
+
const alternative = svg.attr("aria-label")?.trim() || labelled.join(" ") || svg.children("title").text().trim() || svg.children("desc").text().trim() || svg.find("text").text().trim();
|
|
3498
|
+
return { asset: { path: `migration-assets/${sha256}.svg`, content }, alternative };
|
|
3499
|
+
}
|
|
3500
|
+
function projectProtectedEmails($, root) {
|
|
3501
|
+
root.find("[data-cfemail]").each((_index, node) => {
|
|
3502
|
+
const element = $(node);
|
|
3503
|
+
const anchor = element.is("a") ? element : element.closest("a");
|
|
3504
|
+
const href = anchor.attr("href") ?? "";
|
|
3505
|
+
if (!anchor.length || !/^\/cdn-cgi\/l\/email-protection(?:#[a-f\d]+)?$/i.test(href)) return;
|
|
3506
|
+
const encoded = element.attr("data-cfemail") ?? "";
|
|
3507
|
+
if (!/^(?:[a-f\d]{2}){4,1024}$/i.test(encoded)) return;
|
|
3508
|
+
const bytes = Buffer.from(encoded, "hex");
|
|
3509
|
+
const payload = Uint8Array.from(bytes.subarray(1), (byte) => byte ^ bytes[0]);
|
|
3510
|
+
let email;
|
|
3511
|
+
try {
|
|
3512
|
+
email = new TextDecoder("utf-8", { fatal: true }).decode(payload);
|
|
3513
|
+
} catch {
|
|
3514
|
+
return;
|
|
3515
|
+
}
|
|
3516
|
+
if (!/^[^@\s<>?#[\]\u0000-\u001f\u007f]+@[^@\s<>?#[\]\u0000-\u001f\u007f]+\.[^@\s<>?#[\]\u0000-\u001f\u007f]+$/u.test(email)) return;
|
|
3517
|
+
anchor.attr("href", `mailto:${email}`);
|
|
3518
|
+
if (/^\[email\s+protected\]$/i.test(element.text().trim())) element.text(email);
|
|
3519
|
+
element.removeAttr("data-cfemail");
|
|
3520
|
+
});
|
|
3521
|
+
}
|
|
3522
|
+
function hasCompleteCapturedPanel($, panel) {
|
|
3523
|
+
const busy = '[aria-busy="true"],[role="progressbar"]';
|
|
3524
|
+
if (panel.is(busy) || panel.find(busy).length) return false;
|
|
3525
|
+
const copy = panel.clone();
|
|
3526
|
+
copy.find('script,style,button,[role="tablist"]').remove();
|
|
3527
|
+
return Boolean(copy.text().trim() || copy.find("img[src],svg").length);
|
|
3528
|
+
}
|
|
3529
|
+
function projectCapturedDisclosures($, root) {
|
|
3530
|
+
const ids = /* @__PURE__ */ new Map();
|
|
3531
|
+
root.find("[id]").each((_index, node) => {
|
|
3532
|
+
const id = $(node).attr("id");
|
|
3533
|
+
ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
|
|
3534
|
+
});
|
|
3535
|
+
root.find("button[aria-expanded][aria-controls],summary[aria-expanded][aria-controls]").each((_index, node) => {
|
|
3536
|
+
const control = $(node);
|
|
3537
|
+
if (control.is('[role="tab"],[aria-haspopup],[aria-busy="true"]')) return;
|
|
3538
|
+
const id = control.attr("id"), target2 = control.attr("aria-controls");
|
|
3539
|
+
const panel = target2 ? ids.get(target2) : void 0;
|
|
3540
|
+
const nativeSummary = control.is("summary") && control.parent().is("details");
|
|
3541
|
+
let owner = control.parent();
|
|
3542
|
+
if (owner.is("h1,h2,h3,h4,h5,h6")) owner = owner.parent();
|
|
3543
|
+
const labels2 = panel?.attr("aria-labelledby")?.trim().split(/\s+/);
|
|
3544
|
+
const ownedLabels = labels2?.every((label) => ids.get(label)?.length === 1 && (label === id || nativeSummary && control.find("[id]").toArray().some((element) => $(element).attr("id") === label)));
|
|
3545
|
+
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;
|
|
3546
|
+
if (!nativeSummary) node.name = "span";
|
|
3547
|
+
control.removeAttr("aria-expanded aria-controls role type");
|
|
3548
|
+
panel.removeAttr("hidden aria-hidden");
|
|
3549
|
+
});
|
|
3550
|
+
}
|
|
3551
|
+
function projectCapturedTabs($, root) {
|
|
3552
|
+
root.find("[data-thally-static-tabs],[data-thally-static-tab]").removeAttr("data-thally-static-tabs data-thally-static-tab");
|
|
3553
|
+
const ids = /* @__PURE__ */ new Map();
|
|
3554
|
+
root.find("[id]").each((_index, node) => {
|
|
3555
|
+
const id = $(node).attr("id");
|
|
3556
|
+
ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
|
|
3557
|
+
});
|
|
3558
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
3559
|
+
const groups = root.find('[role="tablist"]').toArray();
|
|
3560
|
+
if (groups.length > 256 || root.find('[role="tab"]').length > 1024) return;
|
|
3561
|
+
for (const groupNode of groups.reverse()) {
|
|
3562
|
+
const group = $(groupNode);
|
|
3563
|
+
const tabs = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === groupNode);
|
|
3564
|
+
if (!tabs.length || group.is('[aria-busy="true"]')) continue;
|
|
3565
|
+
if (group.text().replace(/\s+/g, "") !== tabs.map((node) => $(node).text()).join("").replace(/\s+/g, "")) continue;
|
|
3566
|
+
let container = group.parent();
|
|
3567
|
+
const explicit = tabs.every((tab) => Boolean($(tab).attr("aria-controls")));
|
|
3568
|
+
const panels = [];
|
|
3569
|
+
if (explicit) {
|
|
3570
|
+
for (const tab of tabs) {
|
|
3571
|
+
const panel = ids.get($(tab).attr("aria-controls"));
|
|
3572
|
+
const tabId = $(tab).attr("id");
|
|
3573
|
+
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;
|
|
3574
|
+
panels.push(panel);
|
|
3575
|
+
}
|
|
3576
|
+
for (let depth = 0; depth < 16 && container.length && panels.some((panel) => !panel.parents().toArray().some((parent) => parent === container[0])); depth += 1) container = container.parent();
|
|
3577
|
+
if (!container.length || !container.find('[role="tablist"]').toArray().includes(groupNode)) continue;
|
|
3578
|
+
} else {
|
|
3579
|
+
if (tabs.some((tab) => $(tab).attr("aria-controls"))) continue;
|
|
3580
|
+
container = group.closest(".code-group,.tabs-container,.tabs.tab-container");
|
|
3581
|
+
if (!container.length) continue;
|
|
3582
|
+
const owners = container.find('[role="tablist"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
|
|
3583
|
+
if (owners.length !== 1) continue;
|
|
3584
|
+
panels.push(...container.find('[role="tabpanel"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]).map((node) => $(node)));
|
|
3585
|
+
}
|
|
3586
|
+
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;
|
|
3587
|
+
const unowned = container.find('[role="tabpanel"]').toArray().filter((node) => !panels.some((panel) => panel[0] === node || $(node).parents().toArray().some((parent) => parent === panel[0])));
|
|
3588
|
+
if (unowned.length) continue;
|
|
3589
|
+
const residual = container.clone();
|
|
3590
|
+
residual.find('[role="tablist"],[role="tabpanel"]').remove();
|
|
3591
|
+
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);
|
|
3592
|
+
if (!hasSurroundingContent) container.attr("data-thally-static-tabs", "");
|
|
3593
|
+
panels.forEach((panel, index) => {
|
|
3594
|
+
claimed.add(panel[0]);
|
|
3595
|
+
const label = $(tabs[index]).text().trim() || `Tab ${index + 1}`;
|
|
3596
|
+
if (hasSurroundingContent) panel.prepend($("<p></p>").append($("<strong></strong>").text(label)));
|
|
3597
|
+
else panel.attr("data-thally-static-tab", label);
|
|
3598
|
+
panel.removeAttr("role hidden aria-hidden aria-labelledby");
|
|
3599
|
+
const id = $(tabs[index]).attr("id");
|
|
3600
|
+
if (id) panel.prepend($("<span></span>").attr("id", id));
|
|
3601
|
+
});
|
|
3602
|
+
const groupId = group.attr("id");
|
|
3603
|
+
if (groupId) group.replaceWith($("<span></span>").attr("id", groupId));
|
|
3604
|
+
else group.remove();
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
|
|
2542
3608
|
// src/url.ts
|
|
2543
|
-
var MAX_LOCAL_RESPONSE_BYTES =
|
|
3609
|
+
var MAX_LOCAL_RESPONSE_BYTES = 8e6;
|
|
3610
|
+
var MAX_EMBEDDED_SCHEMA_BYTES = 2e6;
|
|
2544
3611
|
var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
2545
3612
|
"Accordion",
|
|
2546
3613
|
"AccordionGroup",
|
|
@@ -2617,8 +3684,8 @@ var MINTLIFY_ARTICLE_LABELS = [
|
|
|
2617
3684
|
{ baseUrl: "Select base URL", permalink: "Navigate to header" },
|
|
2618
3685
|
{ baseUrl: "\u9009\u62E9\u57FA\u7840URL", permalink: "\u5BFC\u822A\u5230\u6807\u9898" }
|
|
2619
3686
|
];
|
|
2620
|
-
var remoteMarkdownParser =
|
|
2621
|
-
var embeddedOpenApiParser =
|
|
3687
|
+
var remoteMarkdownParser = unified3().use(remarkParse3).use(remarkMdx3);
|
|
3688
|
+
var embeddedOpenApiParser = unified3().use(remarkParse3);
|
|
2622
3689
|
function validateMigrationUrl(value) {
|
|
2623
3690
|
let url;
|
|
2624
3691
|
try {
|
|
@@ -2635,7 +3702,7 @@ function validateMigrationUrl(value) {
|
|
|
2635
3702
|
}
|
|
2636
3703
|
async function readResponseBody(response) {
|
|
2637
3704
|
const contentLength = Number(response.headers.get("content-length") ?? 0);
|
|
2638
|
-
if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the
|
|
3705
|
+
if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the 8 MB import limit.");
|
|
2639
3706
|
if (!response.body) return "";
|
|
2640
3707
|
const reader = response.body.getReader();
|
|
2641
3708
|
const chunks = [];
|
|
@@ -2646,7 +3713,7 @@ async function readResponseBody(response) {
|
|
|
2646
3713
|
size += value.byteLength;
|
|
2647
3714
|
if (size > MAX_LOCAL_RESPONSE_BYTES) {
|
|
2648
3715
|
await reader.cancel();
|
|
2649
|
-
throw new Error("A documentation response exceeded the
|
|
3716
|
+
throw new Error("A documentation response exceeded the 8 MB import limit.");
|
|
2650
3717
|
}
|
|
2651
3718
|
chunks.push(value);
|
|
2652
3719
|
}
|
|
@@ -2751,7 +3818,7 @@ function isSafePortableUrl(value) {
|
|
|
2751
3818
|
}
|
|
2752
3819
|
}
|
|
2753
3820
|
function decodeHtmlAttribute(value) {
|
|
2754
|
-
const document =
|
|
3821
|
+
const document = load5(`<i data-thally-value="${value.replace(/"/g, """)}"></i>`);
|
|
2755
3822
|
return document("i").attr("data-thally-value") ?? "";
|
|
2756
3823
|
}
|
|
2757
3824
|
function escapeMarkdownText(value, escapeTableSeparator = false) {
|
|
@@ -2837,7 +3904,7 @@ function replacePortableHtmlImages(value) {
|
|
|
2837
3904
|
if (start < cursor) continue;
|
|
2838
3905
|
const end = htmlOpeningTagEnd(value, start + match[0].length);
|
|
2839
3906
|
if (end === null) continue;
|
|
2840
|
-
const fragment =
|
|
3907
|
+
const fragment = load5(value.slice(start, end));
|
|
2841
3908
|
const image = fragment("img").first();
|
|
2842
3909
|
const source = image.attr("src");
|
|
2843
3910
|
const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
|
|
@@ -2862,7 +3929,7 @@ function replacePortableHtmlAnchors(value) {
|
|
|
2862
3929
|
const closing = closingMatch.exec(value);
|
|
2863
3930
|
if (!closing) continue;
|
|
2864
3931
|
const end = closing.index + closing[0].length;
|
|
2865
|
-
const fragment =
|
|
3932
|
+
const fragment = load5(value.slice(start, end));
|
|
2866
3933
|
const anchor = fragment("a").first();
|
|
2867
3934
|
const href = anchor.attr("href");
|
|
2868
3935
|
const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
|
|
@@ -3118,7 +4185,7 @@ function stripMintlifyDocumentationIndex(body) {
|
|
|
3118
4185
|
return lines.slice(index).join("\n");
|
|
3119
4186
|
}
|
|
3120
4187
|
function extractEmbeddedOpenApi(content) {
|
|
3121
|
-
if (Buffer.byteLength(content, "utf8") >
|
|
4188
|
+
if (Buffer.byteLength(content, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
|
|
3122
4189
|
try {
|
|
3123
4190
|
const root = embeddedOpenApiParser.parse(content);
|
|
3124
4191
|
const candidates = (root.children ?? []).flatMap((node2) => {
|
|
@@ -3151,10 +4218,10 @@ function extractEmbeddedOpenApi(content) {
|
|
|
3151
4218
|
const serialized = JSON.stringify(document, (_key, value) => {
|
|
3152
4219
|
values += 1;
|
|
3153
4220
|
if (typeof value === "string") textLength += value.length;
|
|
3154
|
-
if (values > 1e5 || textLength >
|
|
4221
|
+
if (values > 1e5 || textLength > MAX_EMBEDDED_SCHEMA_BYTES || typeof value === "number" && !Number.isFinite(value)) throw new Error("OpenAPI document exceeds portable data limits.");
|
|
3155
4222
|
return value;
|
|
3156
4223
|
});
|
|
3157
|
-
if (Buffer.byteLength(serialized, "utf8") >
|
|
4224
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
|
|
3158
4225
|
const before = content.slice(0, start).replace(/(?:^|\n)##\s+OpenAPI\s*\n\s*$/i, "\n");
|
|
3159
4226
|
const after = content.slice(end);
|
|
3160
4227
|
return {
|
|
@@ -3208,8 +4275,12 @@ function markdownPage(document, id, platform, warn) {
|
|
|
3208
4275
|
};
|
|
3209
4276
|
}
|
|
3210
4277
|
function migrationHtmlArticle(body) {
|
|
3211
|
-
const $ =
|
|
4278
|
+
const $ = load5(body);
|
|
3212
4279
|
const root = ["main article", "article", "main", '[role="main"]', "body"].map((selector) => $(selector).first()).find((candidate) => candidate.length);
|
|
4280
|
+
projectProtectedEmails($, root);
|
|
4281
|
+
recoverFlightArticleCode($, root);
|
|
4282
|
+
const assets = [];
|
|
4283
|
+
const svgProjections = new Map(root.find("svg").toArray().map((element) => [element, projectMigrationSvg($.html(element))]));
|
|
3213
4284
|
root.find([
|
|
3214
4285
|
"script",
|
|
3215
4286
|
"style",
|
|
@@ -3270,10 +4341,25 @@ function migrationHtmlArticle(body) {
|
|
|
3270
4341
|
projection.append(list);
|
|
3271
4342
|
select.replaceWith(projection);
|
|
3272
4343
|
});
|
|
3273
|
-
|
|
4344
|
+
projectCapturedDisclosures($, root);
|
|
4345
|
+
root.find("svg").each((_index, element) => {
|
|
4346
|
+
const svg = $(element);
|
|
4347
|
+
if (svg.parents("svg,button,nav,footer,aside").length) return;
|
|
4348
|
+
const projection = svgProjections.get(element);
|
|
4349
|
+
if (!projection) return;
|
|
4350
|
+
const image = $("<img>").attr("src", `/${projection.asset.path}`).attr("alt", projection.alternative);
|
|
4351
|
+
for (const attribute of ["id", "width", "height"]) {
|
|
4352
|
+
const value = svg.attr(attribute);
|
|
4353
|
+
if (value !== void 0) image.attr(attribute, value);
|
|
4354
|
+
}
|
|
4355
|
+
assets.push(projection.asset);
|
|
4356
|
+
svg.replaceWith(image);
|
|
4357
|
+
});
|
|
4358
|
+
projectCapturedTabs($, root);
|
|
4359
|
+
return { $, root, assets };
|
|
3274
4360
|
}
|
|
3275
4361
|
function htmlPage(document, id, options = {}) {
|
|
3276
|
-
const { $, root } = migrationHtmlArticle(document.body);
|
|
4362
|
+
const { $, root, assets } = migrationHtmlArticle(document.body);
|
|
3277
4363
|
const links = root.find("a[href]").map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
|
|
3278
4364
|
const scopedTitle = options.omitDocumentHeading ? root.find("h1").first().text().trim() : "";
|
|
3279
4365
|
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 +4398,7 @@ function htmlPage(document, id, options = {}) {
|
|
|
3312
4398
|
root.find("img[src]").each((_index, element) => {
|
|
3313
4399
|
const src = $(element).attr("src");
|
|
3314
4400
|
if (!src) return;
|
|
4401
|
+
if (assets.some((asset) => src === `/${asset.path}`)) return;
|
|
3315
4402
|
try {
|
|
3316
4403
|
const resolved = new URL(src, document.finalUrl);
|
|
3317
4404
|
if (["http:", "https:"].includes(resolved.protocol)) {
|
|
@@ -3323,7 +4410,15 @@ function htmlPage(document, id, options = {}) {
|
|
|
3323
4410
|
$(element).removeAttr("src");
|
|
3324
4411
|
}
|
|
3325
4412
|
});
|
|
3326
|
-
const turndown = new TurndownService({
|
|
4413
|
+
const turndown = new TurndownService({
|
|
4414
|
+
codeBlockStyle: "fenced",
|
|
4415
|
+
emDelimiter: "_",
|
|
4416
|
+
headingStyle: "atx",
|
|
4417
|
+
preformattedCode: true,
|
|
4418
|
+
// Turndown chooses its blank rule before custom rules. Empty source anchor
|
|
4419
|
+
// leaves still carry navigation identity, including captured tab controls.
|
|
4420
|
+
blankReplacement: (_content, node) => options.preserveAnchors ? anchor(node) : ""
|
|
4421
|
+
});
|
|
3327
4422
|
let isConvertingTableCell = false;
|
|
3328
4423
|
turndown.remove(["script", "style", "noscript"]);
|
|
3329
4424
|
turndown.addRule("migration-literal-table-code", {
|
|
@@ -3364,6 +4459,22 @@ ${content}
|
|
|
3364
4459
|
|
|
3365
4460
|
${anchor(element)}${heading ? `${"#".repeat(Number(heading[1]))} ${content.trim()}` : content}
|
|
3366
4461
|
|
|
4462
|
+
`;
|
|
4463
|
+
}
|
|
4464
|
+
});
|
|
4465
|
+
turndown.addRule("migration-flow-link", {
|
|
4466
|
+
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")),
|
|
4467
|
+
replacement: (content, node) => {
|
|
4468
|
+
const element = node;
|
|
4469
|
+
const href = migrationImageAttribute(element.getAttribute("href") ?? "");
|
|
4470
|
+
return `
|
|
4471
|
+
|
|
4472
|
+
${anchor(element)}<Card href="${href}">
|
|
4473
|
+
|
|
4474
|
+
${content.trim()}
|
|
4475
|
+
|
|
4476
|
+
</Card>
|
|
4477
|
+
|
|
3367
4478
|
`;
|
|
3368
4479
|
}
|
|
3369
4480
|
});
|
|
@@ -3414,7 +4525,7 @@ ${content.trim()}
|
|
|
3414
4525
|
const element = node;
|
|
3415
4526
|
const className = element.className;
|
|
3416
4527
|
const tag = /admonition-(?:danger)/.test(className) ? "Error" : /admonition-(?:caution|warning)/.test(className) ? "Warning" : /admonition-info/.test(className) ? "Info" : "Note";
|
|
3417
|
-
const fragment =
|
|
4528
|
+
const fragment = load5(element.outerHTML);
|
|
3418
4529
|
const clone = fragment(".theme-admonition").first();
|
|
3419
4530
|
const title2 = clone.find('[class*="admonitionHeading"],.admonition-heading').first();
|
|
3420
4531
|
const titleText = title2.text().trim();
|
|
@@ -3435,11 +4546,15 @@ ${titleText ? `**${escapeMarkdownText(titleText)}**
|
|
|
3435
4546
|
filter: (node) => node.nodeName === "DIV" && node.classList.contains("tabs-container"),
|
|
3436
4547
|
replacement: (_content, node) => {
|
|
3437
4548
|
const element = node;
|
|
3438
|
-
const fragment =
|
|
4549
|
+
const fragment = load5(element.outerHTML);
|
|
3439
4550
|
const container = fragment(".tabs-container").first();
|
|
3440
4551
|
const labels2 = container.children("ul.tabs").children("li").toArray().map((item) => fragment(item).text().trim() || "Tab");
|
|
3441
4552
|
const panels = container.find('[role="tabpanel"]').toArray().filter((panel) => fragment(panel).closest(".tabs-container")[0] === container[0]);
|
|
3442
4553
|
if (panels.length === 0) return _content;
|
|
4554
|
+
const residual = container.clone();
|
|
4555
|
+
residual.children("ul.tabs").remove();
|
|
4556
|
+
residual.find('[role="tabpanel"]').remove();
|
|
4557
|
+
if (residual.text().trim() || residual.find("a[href],pre,img,svg,table,video,iframe,input,select,textarea").length) return _content;
|
|
3443
4558
|
const tabs = panels.map((panel, index) => {
|
|
3444
4559
|
const panelId = fragment(panel).attr("id");
|
|
3445
4560
|
const tab = panelId ? container.find('[role="tab"]').toArray().find((item) => fragment(item).attr("aria-controls") === panelId) : void 0;
|
|
@@ -3457,13 +4572,40 @@ ${anchor(element)}<Tabs>
|
|
|
3457
4572
|
${tabs.join("\n\n")}
|
|
3458
4573
|
</Tabs>
|
|
3459
4574
|
|
|
4575
|
+
`;
|
|
4576
|
+
}
|
|
4577
|
+
});
|
|
4578
|
+
turndown.addRule("migration-complete-captured-tabs", {
|
|
4579
|
+
filter: (node) => node.hasAttribute("data-thally-static-tabs"),
|
|
4580
|
+
replacement: (_content, node) => {
|
|
4581
|
+
const element = node;
|
|
4582
|
+
const fragment = load5(element.outerHTML);
|
|
4583
|
+
const container = fragment("[data-thally-static-tabs]").first();
|
|
4584
|
+
const panels = container.find("[data-thally-static-tab]").toArray().filter((panel) => fragment(panel).closest("[data-thally-static-tabs]")[0] === container[0]);
|
|
4585
|
+
const tabs = panels.map((panel) => {
|
|
4586
|
+
const selection = fragment(panel);
|
|
4587
|
+
const title2 = migrationImageAttribute(selection.attr("data-thally-static-tab"));
|
|
4588
|
+
const id2 = selection.attr("id");
|
|
4589
|
+
const panelAnchor = options.preserveAnchors && id2 ? `<a id="${migrationImageAttribute(id2)}" />
|
|
4590
|
+
|
|
4591
|
+
` : "";
|
|
4592
|
+
return `<Tab title="${title2}">
|
|
4593
|
+
${panelAnchor}${turndown.turndown(selection.html() ?? "").trim()}
|
|
4594
|
+
</Tab>`;
|
|
4595
|
+
});
|
|
4596
|
+
return `
|
|
4597
|
+
|
|
4598
|
+
${anchor(element)}<Tabs>
|
|
4599
|
+
${tabs.join("\n\n")}
|
|
4600
|
+
</Tabs>
|
|
4601
|
+
|
|
3460
4602
|
`;
|
|
3461
4603
|
}
|
|
3462
4604
|
});
|
|
3463
4605
|
turndown.addRule("gfm-table", {
|
|
3464
4606
|
filter: "table",
|
|
3465
4607
|
replacement: (_content, node) => {
|
|
3466
|
-
const fragment =
|
|
4608
|
+
const fragment = load5(node.outerHTML);
|
|
3467
4609
|
const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => {
|
|
3468
4610
|
const wasConvertingTableCell = isConvertingTableCell;
|
|
3469
4611
|
isConvertingTableCell = true;
|
|
@@ -3489,7 +4631,7 @@ ${normalized.slice(1).map((row) => `| ${row.join(" | ")} |`).join("\n")}
|
|
|
3489
4631
|
filter: (node) => node.nodeName === "PRE",
|
|
3490
4632
|
replacement: (_content, node) => {
|
|
3491
4633
|
const element = node;
|
|
3492
|
-
const fragment =
|
|
4634
|
+
const fragment = load5(element.outerHTML);
|
|
3493
4635
|
const tokenLines = fragment(".token-line").toArray();
|
|
3494
4636
|
const code = (tokenLines.length > 0 ? tokenLines.map((line) => fragment(line).text()).join("\n") : fragment("pre").text()).replace(/\n$/, "");
|
|
3495
4637
|
const language = ["code", "pre"].flatMap((selector) => {
|
|
@@ -3514,7 +4656,7 @@ ${fence}
|
|
|
3514
4656
|
const escape = turndown.escape.bind(turndown);
|
|
3515
4657
|
turndown.escape = (text2) => isConvertingTableCell ? escape(text2.replace(/[<>{}]/g, (character) => ({ "<": "<", ">": ">", "{": "{", "}": "}" })[character])) : escape(text2).replace(/[<{}]/g, (match) => `\\${match}`);
|
|
3516
4658
|
const body = `${documentHeadingAnchor}${turndown.turndown(root.html() ?? "").trim()}`;
|
|
3517
|
-
if (!title || body.length < 40 && $("article").length === 0) return { page: null, links };
|
|
4659
|
+
if (!title || body.length < 40 && $("article").length === 0) return { page: null, links, assets };
|
|
3518
4660
|
return {
|
|
3519
4661
|
page: {
|
|
3520
4662
|
id,
|
|
@@ -3525,10 +4667,74 @@ ${fence}
|
|
|
3525
4667
|
body,
|
|
3526
4668
|
source: document.finalUrl.toString()
|
|
3527
4669
|
},
|
|
3528
|
-
links
|
|
4670
|
+
links,
|
|
4671
|
+
assets
|
|
3529
4672
|
};
|
|
3530
4673
|
}
|
|
3531
4674
|
|
|
4675
|
+
// src/url-identity.ts
|
|
4676
|
+
var directory = (path) => path.replace(/\/index\/?$/i, "").replace(/\/$/, "") || "/";
|
|
4677
|
+
function migrationSectionQueryCandidate(source, anchors) {
|
|
4678
|
+
const target2 = new URL(source);
|
|
4679
|
+
const sections = [...source.searchParams].filter(([, value]) => anchors.includes(value));
|
|
4680
|
+
const section = sections.length === 1 && source.searchParams.getAll(sections[0][0]).length === 1 ? sections[0] : void 0;
|
|
4681
|
+
if (section) target2.searchParams.delete(section[0]);
|
|
4682
|
+
target2.hash = "";
|
|
4683
|
+
target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
|
|
4684
|
+
return { canonicalUrl: target2.href, ...section ? { sectionAnchor: section[1] } : {} };
|
|
4685
|
+
}
|
|
4686
|
+
function isMigrationUtilityUrl(url) {
|
|
4687
|
+
return /^\/cdn-cgi\/l\/email-protection\/?$/i.test(url.pathname);
|
|
4688
|
+
}
|
|
4689
|
+
function declaredMigrationArticleUrl(document) {
|
|
4690
|
+
const target2 = declaredCanonical(document);
|
|
4691
|
+
return target2 && target2.search === document.finalUrl.search ? target2 : document.finalUrl;
|
|
4692
|
+
}
|
|
4693
|
+
function declaredCanonical(document) {
|
|
4694
|
+
if (!/html/i.test(document.contentType)) return void 0;
|
|
4695
|
+
const reference = load6(document.body)('link[rel="canonical"][href]').first().attr("href");
|
|
4696
|
+
if (!reference) return void 0;
|
|
4697
|
+
try {
|
|
4698
|
+
const target2 = new URL(reference, document.finalUrl);
|
|
4699
|
+
if (target2.origin !== document.finalUrl.origin || target2.username || target2.password || directory(target2.pathname) !== directory(document.finalUrl.pathname)) return void 0;
|
|
4700
|
+
target2.hash = "";
|
|
4701
|
+
target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
|
|
4702
|
+
return target2;
|
|
4703
|
+
} catch {
|
|
4704
|
+
return void 0;
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
function observedMigrationArticleIdentity(document) {
|
|
4708
|
+
if (!/html/i.test(document.contentType)) return void 0;
|
|
4709
|
+
const { $, root } = migrationHtmlArticle(document.body);
|
|
4710
|
+
if (!root.length || !root.text().trim()) return void 0;
|
|
4711
|
+
const anchors = root.find("[id]").toArray().map((node) => $(node).attr("id"));
|
|
4712
|
+
root.find("nav,aside,footer,script,style").remove();
|
|
4713
|
+
const metadata = load6(document.body);
|
|
4714
|
+
const payload = JSON.stringify([
|
|
4715
|
+
$.html(root),
|
|
4716
|
+
metadata("title").text(),
|
|
4717
|
+
metadata('meta[name="description"]').attr("content"),
|
|
4718
|
+
metadata("html").attr("lang"),
|
|
4719
|
+
document.locale,
|
|
4720
|
+
document.version,
|
|
4721
|
+
// Deferred content can exist only in inert hydration data. Never erase its
|
|
4722
|
+
// differences merely because two collapsed DOM panels appear identical.
|
|
4723
|
+
metadata("script").toArray().map((node) => [metadata(node).attr("src"), metadata(node).text()]),
|
|
4724
|
+
(document.states ?? []).map((state) => [state.id, state.label, state.response.body])
|
|
4725
|
+
]);
|
|
4726
|
+
const source = document.finalUrl;
|
|
4727
|
+
const canonical2 = declaredCanonical(document) ?? new URL(source);
|
|
4728
|
+
if (canonical2.href === source.href) {
|
|
4729
|
+
canonical2.href = migrationSectionQueryCandidate(source, anchors).canonicalUrl;
|
|
4730
|
+
}
|
|
4731
|
+
canonical2.hash = "";
|
|
4732
|
+
canonical2.pathname = canonical2.pathname.replace(/\/+$/, "") || "/";
|
|
4733
|
+
const removed = [...source.searchParams].filter(([key, value]) => !canonical2.searchParams.getAll(key).includes(value));
|
|
4734
|
+
const sectionAnchor = removed.length === 1 && anchors.includes(removed[0][1]) ? removed[0][1] : void 0;
|
|
4735
|
+
return { canonicalUrl: canonical2.href, articleSha256: createHash3("sha256").update(payload).digest("hex"), ...sectionAnchor ? { sectionAnchor } : {} };
|
|
4736
|
+
}
|
|
4737
|
+
|
|
3532
4738
|
// src/url-api-schema-controls.ts
|
|
3533
4739
|
var MAX_CAPTURE_BYTES2 = 8e6;
|
|
3534
4740
|
var MAX_TAB_GROUPS = 256;
|
|
@@ -3647,17 +4853,378 @@ function apiSchemaTabGroups(body, document, operation) {
|
|
|
3647
4853
|
for (const proof of proofs) scopeCounts.set(proof.scope, (scopeCounts.get(proof.scope) ?? 0) + 1);
|
|
3648
4854
|
return proofs.filter((proof) => scopeCounts.get(proof.scope) === 1).map((proof) => proof.index);
|
|
3649
4855
|
}
|
|
4856
|
+
function apiSchemaDisclosureGroups(body, document, operation) {
|
|
4857
|
+
if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(document)) return [];
|
|
4858
|
+
if (!/^(?:get|post|put|patch|delete|head|options|trace)$/i.test(operation.method)) return [];
|
|
4859
|
+
const path = object2(object2(document.paths)?.[operation.path]);
|
|
4860
|
+
const selected = object2(path?.[operation.method.toLowerCase()]);
|
|
4861
|
+
if (!selected) return [];
|
|
4862
|
+
const { $, root } = migrationHtmlArticle(body);
|
|
4863
|
+
const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
|
|
4864
|
+
const item = $(node);
|
|
4865
|
+
const endpoint = item.find('[data-component-part="scroll-area-content"]').first().clone();
|
|
4866
|
+
endpoint.find("[data-thally-static-base-urls]").remove();
|
|
4867
|
+
return item.find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
|
|
4868
|
+
});
|
|
4869
|
+
if (operations.length !== 1) return [];
|
|
4870
|
+
const ids = /* @__PURE__ */ new Map();
|
|
4871
|
+
root.find("[id]").each((_index, node) => {
|
|
4872
|
+
const id = $(node).attr("id");
|
|
4873
|
+
ids.set(id, (ids.get(id) ?? 0) + 1);
|
|
4874
|
+
});
|
|
4875
|
+
if (ids.get(operations.attr("id")) !== 1) return [];
|
|
4876
|
+
let visits = 0;
|
|
4877
|
+
const property = (value, name, active = /* @__PURE__ */ new Set()) => {
|
|
4878
|
+
if (++visits > 1e5 || active.has(value)) return [];
|
|
4879
|
+
const schema = resolve2(document, value);
|
|
4880
|
+
if (!schema) return [];
|
|
4881
|
+
active.add(value);
|
|
4882
|
+
const direct = object2(schema.properties)?.[name];
|
|
4883
|
+
const result = direct ? [direct] : [
|
|
4884
|
+
...schema.items ? property(schema.items, name, active) : [],
|
|
4885
|
+
...["allOf", "oneOf", "anyOf"].flatMap((key) => Array.isArray(schema[key]) ? schema[key].flatMap((child) => property(child, name, active)) : [])
|
|
4886
|
+
];
|
|
4887
|
+
active.delete(value);
|
|
4888
|
+
return result;
|
|
4889
|
+
};
|
|
4890
|
+
const fieldSelector = ".object-param-field,.array-param-field";
|
|
4891
|
+
const proofs = [];
|
|
4892
|
+
if (root.find("details").length > MAX_TABS) return [];
|
|
4893
|
+
root.find("details").each((index, node) => {
|
|
4894
|
+
const details = $(node), control = details.children('summary[data-component-part="expandable-button"]');
|
|
4895
|
+
const panel = details.children('[data-component-part="expandable-content"]');
|
|
4896
|
+
const field = details.closest(fieldSelector), section = field.closest(".api-section");
|
|
4897
|
+
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;
|
|
4898
|
+
const target2 = control.attr("aria-controls"), controlId = control.attr("id");
|
|
4899
|
+
if (target2 && (panel.attr("id") !== target2 || ids.get(target2) !== 1) || controlId && ids.get(controlId) !== 1) return;
|
|
4900
|
+
const payload = panel.clone();
|
|
4901
|
+
payload.find("script,style").remove();
|
|
4902
|
+
if (payload.text().trim() || payload.children().length) return;
|
|
4903
|
+
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)));
|
|
4904
|
+
const chain = [...ancestors, field[0]];
|
|
4905
|
+
const names = chain.map((owner) => $(owner).find('[data-component-part="field-name"]').filter((_index, name) => $(name).closest(fieldSelector)[0] === owner));
|
|
4906
|
+
const hasNamedPath = names.every((name) => name.length === 1 && Boolean(name.text().trim()));
|
|
4907
|
+
const heading = section.children(".api-section-heading");
|
|
4908
|
+
if (heading.length !== 1) return;
|
|
4909
|
+
const leaves = heading.find("*").toArray().filter((element) => $(element).children().length === 0).map((element) => $(element).text().trim());
|
|
4910
|
+
const combined = leaves.flatMap((text2) => {
|
|
4911
|
+
const match = text2.match(/^([1-5]\d{2}|default) - ([a-z][a-z\d.+-]*\/[a-z\d.+-]+)$/i);
|
|
4912
|
+
return match ? [{ status: match[1], media: match[2] }] : [];
|
|
4913
|
+
});
|
|
4914
|
+
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));
|
|
4915
|
+
const statuses = heading.find('button[aria-haspopup="menu"]').toArray().map((button) => $(button).text().trim()).filter((text2) => /^(?:[1-5]\d{2}|default)$/.test(text2));
|
|
4916
|
+
if (combined.length === 1 && !media.length && !statuses.length) {
|
|
4917
|
+
media.push(combined[0].media);
|
|
4918
|
+
statuses.push(combined[0].status);
|
|
4919
|
+
}
|
|
4920
|
+
let schemas = [];
|
|
4921
|
+
let offset = 0;
|
|
4922
|
+
let prefix = "";
|
|
4923
|
+
if (media.length === 1 && statuses.length === 1) {
|
|
4924
|
+
const response = resolve2(document, object2(selected.responses)?.[statuses[0]]);
|
|
4925
|
+
schemas = [object2(object2(response?.content)?.[media[0]])?.schema];
|
|
4926
|
+
prefix = "response";
|
|
4927
|
+
} else if (media.length === 1 && !statuses.length) {
|
|
4928
|
+
const request = resolve2(document, selected.requestBody);
|
|
4929
|
+
schemas = [object2(object2(request?.content)?.[media[0]])?.schema];
|
|
4930
|
+
prefix = "body";
|
|
4931
|
+
} else if (!media.length && !statuses.length) {
|
|
4932
|
+
const head = $(chain[0]).find(".param-head[id]").filter((_index, element) => $(element).closest(fieldSelector)[0] === chain[0]).first().attr("id") ?? "";
|
|
4933
|
+
const location = ["query", "path", "header", "cookie"].find((value) => head.startsWith(`${value}-`));
|
|
4934
|
+
if (names[0]?.length !== 1 || !location && !head.startsWith("parameter-")) return;
|
|
4935
|
+
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());
|
|
4936
|
+
if (parameters.length !== 1) return;
|
|
4937
|
+
schemas = [parameters[0]?.schema];
|
|
4938
|
+
offset = 1;
|
|
4939
|
+
prefix = $(chain[0]).is(".array-param-field") ? "parameter" : `parameter-${names[0].text().trim()}`;
|
|
4940
|
+
}
|
|
4941
|
+
const expandable = (value) => {
|
|
4942
|
+
const schema = resolve2(document, value);
|
|
4943
|
+
return Boolean(schema && (object2(schema.properties) || object2(schema.items) || object2(schema.additionalProperties) || ["allOf", "oneOf", "anyOf"].some((key) => Array.isArray(schema[key]))));
|
|
4944
|
+
};
|
|
4945
|
+
const roots = schemas;
|
|
4946
|
+
if (hasNamedPath) {
|
|
4947
|
+
for (const name of names.slice(offset)) schemas = schemas.flatMap((schema) => property(schema, name.text().trim()));
|
|
4948
|
+
if (schemas.some(expandable)) {
|
|
4949
|
+
proofs.push(index);
|
|
4950
|
+
return;
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
const marker = details.attr("data-testid");
|
|
4954
|
+
if (!prefix || !marker?.endsWith("-children")) return;
|
|
4955
|
+
const expected = marker.slice(0, -"-children".length);
|
|
4956
|
+
const visibleNames = chain.flatMap((owner) => {
|
|
4957
|
+
const labels2 = $(owner).find('[data-component-part="field-name"]').filter((_index, label) => $(label).closest(fieldSelector)[0] === owner);
|
|
4958
|
+
return labels2.length === 1 ? [labels2.text().trim()] : [];
|
|
4959
|
+
});
|
|
4960
|
+
const matches = /* @__PURE__ */ new Set();
|
|
4961
|
+
const walk = (value, key, pathNames, active = /* @__PURE__ */ new Set()) => {
|
|
4962
|
+
if (++visits > 1e5 || key.length > 2048 || active.has(value)) return;
|
|
4963
|
+
const schema = resolve2(document, value);
|
|
4964
|
+
if (!schema) return;
|
|
4965
|
+
if (key === expected && expandable(schema)) {
|
|
4966
|
+
let cursor = 0;
|
|
4967
|
+
const labelsAgree = visibleNames.every((name) => {
|
|
4968
|
+
const found = pathNames.indexOf(name, cursor);
|
|
4969
|
+
cursor = found + 1;
|
|
4970
|
+
return found >= 0;
|
|
4971
|
+
});
|
|
4972
|
+
if (labelsAgree) matches.add(schema);
|
|
4973
|
+
}
|
|
4974
|
+
if (!expected.startsWith(`${key}-`)) return;
|
|
4975
|
+
active.add(value);
|
|
4976
|
+
for (const [name, child] of Object.entries(object2(schema.properties) ?? {})) {
|
|
4977
|
+
const segments = /* @__PURE__ */ new Set([name, name.replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase()]);
|
|
4978
|
+
for (const segment of segments) walk(child, `${key}-${segment}`, [...pathNames, name], active);
|
|
4979
|
+
}
|
|
4980
|
+
if (schema.items) walk(schema.items, `${key}-items`, pathNames, active);
|
|
4981
|
+
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);
|
|
4982
|
+
if (Array.isArray(schema.allOf)) schema.allOf.forEach((child) => walk(child, key, pathNames, active));
|
|
4983
|
+
for (const [keyword, segment] of [["allOf", "all-of"], ["oneOf", "one-of"], ["anyOf", "any-of"]]) {
|
|
4984
|
+
const children = schema[keyword];
|
|
4985
|
+
if (Array.isArray(children)) children.forEach((child, branch) => walk(child, `${key}-${segment}-${branch}`, pathNames, active));
|
|
4986
|
+
}
|
|
4987
|
+
active.delete(value);
|
|
4988
|
+
};
|
|
4989
|
+
roots.forEach((schema) => walk(schema, prefix, offset ? [names[0].text().trim()] : []));
|
|
4990
|
+
if (matches.size === 1) proofs.push(index);
|
|
4991
|
+
});
|
|
4992
|
+
return visits > 1e5 ? [] : proofs;
|
|
4993
|
+
}
|
|
4994
|
+
function apiOperationCodeControls(body, specification, operation) {
|
|
4995
|
+
if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(specification)) return [];
|
|
4996
|
+
const selected = object2(object2(object2(specification.paths)?.[operation.path])?.[operation.method.toLowerCase()]);
|
|
4997
|
+
if (!selected) return [];
|
|
4998
|
+
const { $, root } = migrationHtmlArticle(body);
|
|
4999
|
+
const flight = readMigrationFlightDocument($);
|
|
5000
|
+
if (!flight) return [];
|
|
5001
|
+
let visited = 0, textBytes = 0, exceeded = false;
|
|
5002
|
+
const resolveValue = (input) => {
|
|
5003
|
+
if (exceeded) return void 0;
|
|
5004
|
+
const result = resolveMigrationFlightValue(flight, input);
|
|
5005
|
+
if (!result.resolved) exceeded = true;
|
|
5006
|
+
return result.value;
|
|
5007
|
+
};
|
|
5008
|
+
const textOf = (input, depth = 0, label = false) => {
|
|
5009
|
+
if (exceeded || ++visited > 1e5 || depth > 96) {
|
|
5010
|
+
exceeded = true;
|
|
5011
|
+
return void 0;
|
|
5012
|
+
}
|
|
5013
|
+
const value = resolveValue(input);
|
|
5014
|
+
if (value === void 0 || value === null || typeof value === "boolean") return "";
|
|
5015
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
5016
|
+
const result = String(value);
|
|
5017
|
+
textBytes += Buffer.byteLength(result);
|
|
5018
|
+
if (textBytes > MAX_CAPTURE_BYTES2) {
|
|
5019
|
+
exceeded = true;
|
|
5020
|
+
return void 0;
|
|
5021
|
+
}
|
|
5022
|
+
return result;
|
|
5023
|
+
}
|
|
5024
|
+
if (!Array.isArray(value)) return void 0;
|
|
5025
|
+
if (value[0] === "$") {
|
|
5026
|
+
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;
|
|
5027
|
+
return value[1] === "br" ? "\n" : textOf(value[3].children, depth + 1, label);
|
|
5028
|
+
}
|
|
5029
|
+
const children = value.map((child) => textOf(child, depth + 1, label));
|
|
5030
|
+
return children.some((child) => child === void 0) ? void 0 : children.join("");
|
|
5031
|
+
};
|
|
5032
|
+
const samplePairs = (value, transport = true) => {
|
|
5033
|
+
if (exceeded) return void 0;
|
|
5034
|
+
const samples = transport ? resolveValue(value) : value;
|
|
5035
|
+
if (!Array.isArray(samples) || samples.length > 64) return void 0;
|
|
5036
|
+
const pairs = [];
|
|
5037
|
+
for (const input of samples) {
|
|
5038
|
+
const sample = object2(transport ? resolveValue(input) : input);
|
|
5039
|
+
const language = transport ? resolveValue(sample?.lang) : sample?.lang;
|
|
5040
|
+
const source = transport ? resolveValue(sample?.source) : sample?.source;
|
|
5041
|
+
const label = transport ? resolveValue(sample?.label) : sample?.label;
|
|
5042
|
+
if (label !== void 0 && typeof label !== "string") return void 0;
|
|
5043
|
+
if (typeof language !== "string" || !language.trim() || typeof source !== "string" || !source.trim()) return void 0;
|
|
5044
|
+
textBytes += Buffer.byteLength(language) + Buffer.byteLength(source) + (typeof label === "string" ? Buffer.byteLength(label) : 0);
|
|
5045
|
+
if (textBytes > MAX_CAPTURE_BYTES2) {
|
|
5046
|
+
exceeded = true;
|
|
5047
|
+
return void 0;
|
|
5048
|
+
}
|
|
5049
|
+
pairs.push(JSON.stringify([language, source, label ?? null]));
|
|
5050
|
+
}
|
|
5051
|
+
return pairs;
|
|
5052
|
+
};
|
|
5053
|
+
const declared = selected["x-codeSamples"] === void 0 ? [] : samplePairs(selected["x-codeSamples"], false);
|
|
5054
|
+
if (!declared) return [];
|
|
5055
|
+
const groups = [];
|
|
5056
|
+
const providers = [];
|
|
5057
|
+
let providerCount = 0, generatedControllers = 0, providerMetadataMatches = false;
|
|
5058
|
+
const matchesOperation = (value) => {
|
|
5059
|
+
const metadata = object2(resolveValue(value));
|
|
5060
|
+
const declaration = resolveValue(metadata?.openapi);
|
|
5061
|
+
if (typeof declaration !== "string") return false;
|
|
5062
|
+
const tokens = declaration.trim().split(/\s+/);
|
|
5063
|
+
return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && tokens.at(-1) === operation.path;
|
|
5064
|
+
};
|
|
5065
|
+
const active = /* @__PURE__ */ new Set();
|
|
5066
|
+
const visit = (input, provider, group, depth, owners = []) => {
|
|
5067
|
+
if (exceeded || ++visited > 1e5 || depth > 96) {
|
|
5068
|
+
exceeded = true;
|
|
5069
|
+
return;
|
|
5070
|
+
}
|
|
5071
|
+
const value = resolveValue(input);
|
|
5072
|
+
if (!value || typeof value !== "object") return;
|
|
5073
|
+
if (active.has(value)) {
|
|
5074
|
+
exceeded = true;
|
|
5075
|
+
return;
|
|
5076
|
+
}
|
|
5077
|
+
active.add(value);
|
|
5078
|
+
if (Array.isArray(value)) {
|
|
5079
|
+
if (value[0] === "$" && object2(value[3])) {
|
|
5080
|
+
const props = value[3];
|
|
5081
|
+
const moduleId = typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
|
|
5082
|
+
const kind = moduleId ? flight.modules.get(moduleId) : value[1];
|
|
5083
|
+
const ownId = moduleId ? void 0 : resolveValue(props.id);
|
|
5084
|
+
const localOwners = typeof ownId === "string" ? [...owners, ownId] : owners;
|
|
5085
|
+
let owned = provider;
|
|
5086
|
+
if (kind === "OpenApiProvider") {
|
|
5087
|
+
providerCount += 1;
|
|
5088
|
+
const data = object2(resolveValue(props.openApiReferenceData));
|
|
5089
|
+
const source = object2(resolveValue(data?.operation));
|
|
5090
|
+
const samples = samplePairs(source?.codeSamples);
|
|
5091
|
+
const authored = source?.["x-codeSamples"] === void 0 ? [] : samplePairs(source["x-codeSamples"]);
|
|
5092
|
+
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));
|
|
5093
|
+
if (owned) {
|
|
5094
|
+
providers.push(source);
|
|
5095
|
+
providerMetadataMatches = matchesOperation(props.pageMetadata);
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
if (owned && kind === "MDXContentController" && matchesOperation(props.pageMetadata)) {
|
|
5099
|
+
const child = resolveValue(props.children);
|
|
5100
|
+
const childId = Array.isArray(child) && child[0] === "$" && typeof child[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(child[1])?.[1] : void 0;
|
|
5101
|
+
const childProps = Array.isArray(child) ? object2(child[3]) : void 0;
|
|
5102
|
+
const source = object2(resolveValue(childProps?.mdxSource));
|
|
5103
|
+
const scope = object2(resolveValue(source?.scope));
|
|
5104
|
+
if (childId && flight.modules.get(childId) === "default" && typeof source?.compiledSource === "string" && (() => {
|
|
5105
|
+
if (matchesOperation(scope?.pageMetadata)) return true;
|
|
5106
|
+
const hint = object2(resolveValue(scope?.pageMetadata)), metadata = object2(resolveValue(props.pageMetadata));
|
|
5107
|
+
const declaration = resolveValue(hint?.openapi);
|
|
5108
|
+
if (typeof declaration !== "string" || typeof hint?.href !== "string" || !hint.href || hint.href !== metadata?.href) return false;
|
|
5109
|
+
const tokens = declaration.trim().split(/\s+/);
|
|
5110
|
+
const template = (path) => path.split("/").map((segment) => /^\{[^{}\/]+\}$/.test(segment) ? "{}" : segment).join("/");
|
|
5111
|
+
return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && template(tokens.at(-1) ?? "") === template(operation.path);
|
|
5112
|
+
})()) generatedControllers += 1;
|
|
5113
|
+
}
|
|
5114
|
+
let local = group;
|
|
5115
|
+
if (owned && typeof props.className === "string" && props.className.split(/\s+/).includes("code-group")) {
|
|
5116
|
+
local = { owners: localOwners, code: [], controls: [], toolbar: false, generated: false, invalid: false };
|
|
5117
|
+
groups.push(local);
|
|
5118
|
+
}
|
|
5119
|
+
if (local && owned) {
|
|
5120
|
+
if (props["data-component-part"] === "code-group-tab-bar") local.toolbar = true;
|
|
5121
|
+
if (String(props["data-index"]) === "-1") local.generated = true;
|
|
5122
|
+
if (props["aria-expanded"] !== void 0) {
|
|
5123
|
+
const text2 = textOf(props.children, 0, true);
|
|
5124
|
+
if (kind !== "button" || props["aria-haspopup"] !== "menu" || text2 === void 0) local.invalid = true;
|
|
5125
|
+
else local.controls.push(JSON.stringify([text2, props["aria-label"] ?? "", String(props["aria-expanded"]), props["aria-haspopup"]]));
|
|
5126
|
+
}
|
|
5127
|
+
if (kind === "pre") {
|
|
5128
|
+
const code = textOf(props.children);
|
|
5129
|
+
if (code === void 0 || !code.trim()) local.invalid = true;
|
|
5130
|
+
else local.code.push(code);
|
|
5131
|
+
}
|
|
5132
|
+
if (props["aria-busy"] === true || props["aria-busy"] === "true" || props.role === "progressbar") local.invalid = true;
|
|
5133
|
+
}
|
|
5134
|
+
visit(props.children, owned, local, depth + 1, localOwners);
|
|
5135
|
+
} else for (const child of value) visit(child, provider, group, depth + 1, owners);
|
|
5136
|
+
} else if (depth === 0 && Array.isArray(value.f)) {
|
|
5137
|
+
visit(value.f, provider, group, depth + 1, owners);
|
|
5138
|
+
} else if (Object.keys(value).every((key) => key === "children")) {
|
|
5139
|
+
const children = value.children;
|
|
5140
|
+
if (Array.isArray(children) && children.length >= 2) visit(children, provider, group, depth + 1, owners);
|
|
5141
|
+
}
|
|
5142
|
+
active.delete(value);
|
|
5143
|
+
};
|
|
5144
|
+
visit(flight.records.get("0"), false, void 0, 0);
|
|
5145
|
+
if (exceeded || providerCount !== 1 || providers.length !== 1 || groups.length > MAX_TAB_GROUPS) return [];
|
|
5146
|
+
const sources = declared.map((pair) => JSON.parse(pair)[1]);
|
|
5147
|
+
const sourceCounts = /* @__PURE__ */ new Map();
|
|
5148
|
+
for (const group of groups) if (group.toolbar && group.generated && !group.invalid && group.controls.length >= 1 && group.controls.length <= 2) {
|
|
5149
|
+
const signature = JSON.stringify([group.owners, group.code, group.controls]);
|
|
5150
|
+
sourceCounts.set(signature, (sourceCounts.get(signature) ?? 0) + 1);
|
|
5151
|
+
}
|
|
5152
|
+
const expanded = root.find("[aria-expanded]").toArray();
|
|
5153
|
+
if (expanded.length > MAX_TABS) return [];
|
|
5154
|
+
const controlIndices = new Map(expanded.map((node, index) => [node, index]));
|
|
5155
|
+
const domGroups = /* @__PURE__ */ new Map();
|
|
5156
|
+
$(expanded).each((_index, node) => {
|
|
5157
|
+
const control = $(node);
|
|
5158
|
+
const group = control.closest('.code-group,[data-testid="code-group-select"]');
|
|
5159
|
+
const legacy = group.is('[data-testid="code-group-select"]');
|
|
5160
|
+
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;
|
|
5161
|
+
if (domGroups.has(group[0])) return;
|
|
5162
|
+
const menus = group.find("[aria-expanded]");
|
|
5163
|
+
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;
|
|
5164
|
+
if (menus.length === 2) {
|
|
5165
|
+
const samples = declared.map((pair) => JSON.parse(pair));
|
|
5166
|
+
const labelledChoices = samples.filter((sample) => sample[2] && samples.filter((other) => other[0] === sample[0]).length >= 2);
|
|
5167
|
+
if (!labelledChoices.length || !menus.toArray().some((menu) => labelledChoices.some((sample) => sample[2] === $(menu).text().trim()))) return;
|
|
5168
|
+
}
|
|
5169
|
+
const panels = group.find("[data-index]");
|
|
5170
|
+
const code = group.find("pre").toArray().map((pre) => {
|
|
5171
|
+
const lines = $(pre).find(".token-line");
|
|
5172
|
+
return lines.length ? lines.toArray().map((line) => $(line).text()).join("\n") : $(pre).text();
|
|
5173
|
+
});
|
|
5174
|
+
if (group.find('[aria-busy="true"],[role="progressbar"]').length) return;
|
|
5175
|
+
if (legacy) {
|
|
5176
|
+
if (menus.length !== 2 || group.children().length !== 2 || group.children().first().find("pre").length || code.length !== 1 || !code[0].trim() || panels.length) return;
|
|
5177
|
+
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$/, "")));
|
|
5178
|
+
if (!selectedSamples.length) return;
|
|
5179
|
+
} 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;
|
|
5180
|
+
const controls = menus.toArray().map((menu) => JSON.stringify([$(menu).text(), $(menu).attr("aria-label") ?? "", $(menu).attr("aria-expanded"), $(menu).attr("aria-haspopup")]));
|
|
5181
|
+
const owners = group.parents("[id]").toArray().reverse().map((owner) => $(owner).attr("id"));
|
|
5182
|
+
if (group.attr("id")) owners.push(group.attr("id"));
|
|
5183
|
+
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")) });
|
|
5184
|
+
});
|
|
5185
|
+
const domCounts = /* @__PURE__ */ new Map();
|
|
5186
|
+
for (const group of domGroups.values()) domCounts.set(group.signature, (domCounts.get(group.signature) ?? 0) + 1);
|
|
5187
|
+
const proofs = [];
|
|
5188
|
+
for (const group of domGroups.values()) if (sourceCounts.get(group.signature) === 1 && domCounts.get(group.signature) === 1) proofs.push(...group.indices);
|
|
5189
|
+
if (groups.length === 0 && providerMetadataMatches && generatedControllers === 1 && domGroups.size === 2) {
|
|
5190
|
+
const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
|
|
5191
|
+
const endpoint = $(node).find('[data-component-part="scroll-area-content"]').first().clone();
|
|
5192
|
+
endpoint.find("[data-thally-static-base-urls]").remove();
|
|
5193
|
+
return $(node).find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
|
|
5194
|
+
});
|
|
5195
|
+
const main = $("main#content-container"), side = $('[id="content-side-layout"]');
|
|
5196
|
+
const content = $('[id="content-area"]');
|
|
5197
|
+
const slots = /* @__PURE__ */ new Map();
|
|
5198
|
+
const titles = [resolveValue(providers[0].title), resolveValue(providers[0].summary), ...declared.flatMap((pair) => {
|
|
5199
|
+
const values = JSON.parse(pair);
|
|
5200
|
+
return [values[0], values[2]];
|
|
5201
|
+
})].filter((value) => typeof value === "string").map((value) => value.toLowerCase());
|
|
5202
|
+
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];
|
|
5203
|
+
for (const [node, entry] of domGroups) {
|
|
5204
|
+
const group = $(node);
|
|
5205
|
+
const control = group.find("button[aria-expanded]").first();
|
|
5206
|
+
const owner = group.closest('[id^="api-playground-"][id$="-operation-page"]');
|
|
5207
|
+
const slot = owner[0] === operations[0] ? "inline" : group.closest("#content-side-layout")[0] === side[0] ? "sidebar" : void 0;
|
|
5208
|
+
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;
|
|
5209
|
+
else slots.set(slot, entry);
|
|
5210
|
+
}
|
|
5211
|
+
if (valid && slots.size === 2 && JSON.stringify(slots.get("inline")?.code) === JSON.stringify(slots.get("sidebar")?.code)) {
|
|
5212
|
+
proofs.push(...[...slots.values()].flatMap((slot) => slot.indices));
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
return proofs;
|
|
5216
|
+
}
|
|
3650
5217
|
|
|
3651
5218
|
// src/url-run.ts
|
|
3652
|
-
var CONVERTER_VERSION = "url-evidence-
|
|
3653
|
-
var
|
|
5219
|
+
var CONVERTER_VERSION = "url-evidence-v6";
|
|
5220
|
+
var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
|
|
3654
5221
|
var ACCEPT = "text/markdown,text/html,application/xhtml+xml;q=0.9";
|
|
3655
5222
|
var MAX_RESPONSE_BYTES = 8e6;
|
|
3656
5223
|
var MAX_ASSET_BYTES = 1e7;
|
|
3657
5224
|
var MAX_RECORDS = 1e5;
|
|
3658
5225
|
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
5226
|
function hash(value) {
|
|
3660
|
-
return
|
|
5227
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
3661
5228
|
}
|
|
3662
5229
|
function canonicalJson(value) {
|
|
3663
5230
|
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 +5260,14 @@ function markdownNodes(body) {
|
|
|
3693
5260
|
nodes.push(node);
|
|
3694
5261
|
for (const child of node.children ?? []) visit(child);
|
|
3695
5262
|
};
|
|
3696
|
-
visit(
|
|
5263
|
+
visit(parser2.parse(body));
|
|
3697
5264
|
return nodes;
|
|
3698
5265
|
}
|
|
3699
5266
|
function markdownCode(body) {
|
|
3700
5267
|
return markdownNodes(body).filter((node) => node.type === "code").map((node) => node.value ?? "");
|
|
3701
5268
|
}
|
|
3702
5269
|
function alternativeBlocks(body, baseline) {
|
|
3703
|
-
const blocks = (value) => (
|
|
5270
|
+
const blocks = (value) => (parser2.parse(value).children ?? []).map((node) => value.slice(node.position.start.offset, node.position.end.offset));
|
|
3704
5271
|
const originals = new Set(blocks(baseline));
|
|
3705
5272
|
return blocks(body).filter((block) => !originals.has(block)).join("\n\n");
|
|
3706
5273
|
}
|
|
@@ -3712,7 +5279,7 @@ function linksOf(document) {
|
|
|
3712
5279
|
return [...root.find("a[href]").toArray(), ...$('link[rel="sitemap"][href],link[rel="alternate"][href]').toArray()].map((node) => $(node).attr("href")).filter(Boolean);
|
|
3713
5280
|
}
|
|
3714
5281
|
if (/xml/i.test(document.contentType) || /<urlset|<sitemapindex/i.test(document.body)) {
|
|
3715
|
-
const $ =
|
|
5282
|
+
const $ = load7(document.body, { xmlMode: true });
|
|
3716
5283
|
return $("loc").toArray().map((node) => $(node).text().trim());
|
|
3717
5284
|
}
|
|
3718
5285
|
return [
|
|
@@ -3742,7 +5309,8 @@ function evidenceOf(capture) {
|
|
|
3742
5309
|
...capture.artifactRef ? { artifactRef: capture.artifactRef } : {},
|
|
3743
5310
|
states: (capture.states ?? []).map((state) => ({ id: state.id, label: state.label, sha256: hash(state.response.body) })),
|
|
3744
5311
|
...technical,
|
|
3745
|
-
links: linksOf(capture)
|
|
5312
|
+
links: linksOf(capture),
|
|
5313
|
+
articleIdentity: observedMigrationArticleIdentity(capture)
|
|
3746
5314
|
};
|
|
3747
5315
|
}
|
|
3748
5316
|
function markdownHeadingAnchors(body) {
|
|
@@ -3763,20 +5331,6 @@ function idForUrl(url, scope) {
|
|
|
3763
5331
|
const id = pageIdFromReference(relative4 || "introduction", true);
|
|
3764
5332
|
return id && `${id}${url.search ? `--${hash(url.search).slice(0, 12)}` : ""}`;
|
|
3765
5333
|
}
|
|
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
5334
|
function convert(document, id) {
|
|
3781
5335
|
if (/html/i.test(document.contentType)) {
|
|
3782
5336
|
const technical = htmlEvidence(document);
|
|
@@ -3825,24 +5379,52 @@ function assetReferences(capture) {
|
|
|
3825
5379
|
...root.find("[srcset]").toArray().flatMap((node) => migrationImageCandidates($(node).attr("srcset") ?? "", capture.finalUrl).map((candidate) => candidate.url))
|
|
3826
5380
|
].filter(Boolean);
|
|
3827
5381
|
}
|
|
3828
|
-
function unresolvedStaticControls(body,
|
|
5382
|
+
function unresolvedStaticControls(body, coveredSchemaGroups = [], coveredSchemaDisclosures = [], coveredCodeControls = []) {
|
|
3829
5383
|
const { $, root } = migrationHtmlArticle(body);
|
|
5384
|
+
root.find("[aria-expanded]").each((index, node) => {
|
|
5385
|
+
if (coveredCodeControls.includes(index)) $(node).removeAttr("aria-expanded");
|
|
5386
|
+
});
|
|
3830
5387
|
root.find('[role="tablist"],[data-component-part="tabs-list"]').each((index, node) => {
|
|
3831
5388
|
if (coveredSchemaGroups.includes(index)) $(node).remove();
|
|
3832
5389
|
});
|
|
5390
|
+
root.find("details").each((index, node) => {
|
|
5391
|
+
if (coveredSchemaDisclosures.includes(index)) $(node).remove();
|
|
5392
|
+
});
|
|
3833
5393
|
const tabs = root.find('[role="tab"]').toArray();
|
|
5394
|
+
const idCounts = /* @__PURE__ */ new Map();
|
|
5395
|
+
root.find("[id]").each((_index, node) => {
|
|
5396
|
+
const id = $(node).attr("id");
|
|
5397
|
+
idCounts.set(id, (idCounts.get(id) ?? 0) + 1);
|
|
5398
|
+
});
|
|
3834
5399
|
for (const tab of tabs) {
|
|
5400
|
+
const controlId = $(tab).attr("id");
|
|
5401
|
+
if (controlId && idCounts.get(controlId) !== 1) return true;
|
|
3835
5402
|
const controls = $(tab).attr("aria-controls");
|
|
3836
|
-
const group = $(tab).closest('[role="tablist"]');
|
|
5403
|
+
const group = $(tab).closest('[role="tablist"],ul.tabs');
|
|
3837
5404
|
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]);
|
|
5405
|
+
const peers = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"],ul.tabs')[0] === group[0]);
|
|
3839
5406
|
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]);
|
|
5407
|
+
const owners = container.find('[role="tablist"],ul.tabs').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
|
|
3841
5408
|
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
|
-
|
|
5409
|
+
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;
|
|
5410
|
+
const labels2 = panels.attr("aria-labelledby")?.trim().split(/\s+/);
|
|
5411
|
+
if (labels2 && (!controlId || !labels2.includes(controlId))) return true;
|
|
5412
|
+
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;
|
|
5413
|
+
}
|
|
5414
|
+
const isCapturedImageZoom = (node, depth = 0) => {
|
|
5415
|
+
if (depth > 8) return false;
|
|
5416
|
+
const control = $(node), image = control.prev("span.zoom-image-trigger[data-ramka-trigger]");
|
|
5417
|
+
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));
|
|
5418
|
+
};
|
|
5419
|
+
if (root.find("[aria-expanded]").toArray().some((node) => {
|
|
5420
|
+
const control = $(node);
|
|
5421
|
+
if (control.attr("role") === "tab") return false;
|
|
5422
|
+
const actions = control.parent('[id="page-context-menu"]');
|
|
5423
|
+
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;
|
|
5424
|
+
if (isCapturedImageZoom(node)) return false;
|
|
5425
|
+
return true;
|
|
5426
|
+
})) return true;
|
|
3844
5427
|
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
5428
|
const clone = $(node).clone();
|
|
3847
5429
|
clone.children("summary").remove();
|
|
3848
5430
|
return !clone.text().trim() && !clone.find("pre,img,table,svg").length;
|
|
@@ -3880,7 +5462,7 @@ function acceptedAsset(asset) {
|
|
|
3880
5462
|
}
|
|
3881
5463
|
function validRecord(record) {
|
|
3882
5464
|
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));
|
|
5465
|
+
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
5466
|
}
|
|
3885
5467
|
function hasCompleteLocalApiSchema(document) {
|
|
3886
5468
|
let visited = 0;
|
|
@@ -3986,7 +5568,7 @@ function observedLocale(document, id, scopePath) {
|
|
|
3986
5568
|
}
|
|
3987
5569
|
const urlLocale = [id.split("/", 1)[0], ...scopePath.split("/"), document.finalUrl.pathname.split("/").filter(Boolean)[0]].find((segment) => segment && LOCALES.has(segment.toLowerCase()));
|
|
3988
5570
|
if (urlLocale) candidates.push({ source: "URL", value: canonicalLanguage(urlLocale) });
|
|
3989
|
-
const declared = /html/i.test(document.contentType) ?
|
|
5571
|
+
const declared = /html/i.test(document.contentType) ? load7(document.body)("html").attr("lang")?.trim() : void 0;
|
|
3990
5572
|
if (declared) {
|
|
3991
5573
|
const locale = canonicalLanguage(declared);
|
|
3992
5574
|
if (locale) candidates.push({ source: "html lang", value: locale });
|
|
@@ -4090,14 +5672,14 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
4090
5672
|
continue;
|
|
4091
5673
|
}
|
|
4092
5674
|
if (!inScope(link, source, checkpoint.scopePath)) continue;
|
|
4093
|
-
if (isDiscoveryIndex(link, source)) continue;
|
|
5675
|
+
if (isDiscoveryIndex(link, source) || isMigrationUtilityUrl(link)) continue;
|
|
4094
5676
|
if (/\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js|json|ya?ml)$/i.test(link.pathname)) continue;
|
|
4095
5677
|
const destination = resolve4(link.toString());
|
|
4096
5678
|
if (!destination || !["imported", "excluded"].includes(destination.outcome)) {
|
|
4097
5679
|
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 {
|
|
5680
|
+
} else if ((link.hash || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "")?.aliasAnchor) && destination.outcome === "imported") {
|
|
5681
|
+
let anchor = link.hash.slice(1) || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "").aliasAnchor;
|
|
5682
|
+
if (link.hash) try {
|
|
4101
5683
|
anchor = decodeURIComponent(anchor);
|
|
4102
5684
|
} catch {
|
|
4103
5685
|
}
|
|
@@ -4118,7 +5700,7 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
4118
5700
|
const locale = node.locale ? canonicalLanguage(node.locale) : parentLocale;
|
|
4119
5701
|
if (node.url) {
|
|
4120
5702
|
const url = new URL(node.url, source);
|
|
4121
|
-
if (inScope(url, source, checkpoint.scopePath)) {
|
|
5703
|
+
if (inScope(url, source, checkpoint.scopePath) && !isMigrationUtilityUrl(url)) {
|
|
4122
5704
|
const destination = resolve4(url.toString());
|
|
4123
5705
|
if (destination?.outcome === "excluded" && destination.redirectTo) continue;
|
|
4124
5706
|
const page = destination?.page;
|
|
@@ -4186,6 +5768,7 @@ async function runUrlMigration(options) {
|
|
|
4186
5768
|
delete record.redirectTo;
|
|
4187
5769
|
delete record.reason;
|
|
4188
5770
|
delete record.aliasOf;
|
|
5771
|
+
delete record.aliasAnchor;
|
|
4189
5772
|
}
|
|
4190
5773
|
if (record.page && pageIdFromReference(record.page.id, true) !== record.page.id) throw new Error("Checkpoint contains an unsafe page path.");
|
|
4191
5774
|
records.set(record.url, record);
|
|
@@ -4196,7 +5779,8 @@ async function runUrlMigration(options) {
|
|
|
4196
5779
|
const enqueue = (value, base, discoveredBy) => {
|
|
4197
5780
|
const url = canonical(value, base);
|
|
4198
5781
|
if (!url || !inScope(new URL(url), source, scopePath) && !isDiscoveryIndex(new URL(url), source)) return;
|
|
4199
|
-
const
|
|
5782
|
+
const isUtility = isMigrationUtilityUrl(new URL(url));
|
|
5783
|
+
const isAttachment = isUtility || discoveredBy !== "submitted" && /\.(?:json|ya?ml)$/i.test(new URL(url).pathname);
|
|
4200
5784
|
const existing = records.get(url);
|
|
4201
5785
|
if (existing) {
|
|
4202
5786
|
if (!existing.discoveredBy.includes(discoveredBy)) {
|
|
@@ -4210,7 +5794,7 @@ async function runUrlMigration(options) {
|
|
|
4210
5794
|
url,
|
|
4211
5795
|
discoveredBy: [discoveredBy],
|
|
4212
5796
|
outcome: isAttachment ? "excluded" : "pending",
|
|
4213
|
-
...isAttachment ? { reason: "Machine-readable attachment, not an authored article." } : {}
|
|
5797
|
+
...isAttachment ? { reason: isUtility ? "Recognized provider utility endpoint, not an authored article." : "Machine-readable attachment, not an authored article." } : {}
|
|
4214
5798
|
};
|
|
4215
5799
|
records.set(url, record);
|
|
4216
5800
|
checkpoint.records.push(record);
|
|
@@ -4253,12 +5837,27 @@ async function runUrlMigration(options) {
|
|
|
4253
5837
|
let imported = usedIds.size;
|
|
4254
5838
|
const assetPayloads = new Map(checkpoint.records.flatMap((record) => (record.assets ?? []).map((asset) => [asset.sha256, asset])));
|
|
4255
5839
|
const assetBytes = /* @__PURE__ */ new Map();
|
|
4256
|
-
const
|
|
5840
|
+
const rasterizedSvgs = /* @__PURE__ */ new Map();
|
|
5841
|
+
const retainAsset = async (original) => {
|
|
5842
|
+
let asset = { ...original };
|
|
5843
|
+
delete asset.derivedFromSha256;
|
|
4257
5844
|
if (asset.unavailable) {
|
|
4258
5845
|
if ([404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) return asset;
|
|
4259
5846
|
throw new Error("Invalid source asset availability evidence.");
|
|
4260
5847
|
}
|
|
4261
|
-
if (!acceptedAsset(asset))
|
|
5848
|
+
if (!acceptedAsset(asset)) {
|
|
5849
|
+
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.");
|
|
5850
|
+
const sourceSha256 = hash(asset.content);
|
|
5851
|
+
let rendering = rasterizedSvgs.get(sourceSha256);
|
|
5852
|
+
if (!rendering) {
|
|
5853
|
+
rendering = options.rasterizeSvg({ ...asset, content: Buffer.from(asset.content) }, options.signal).then((rendered) => {
|
|
5854
|
+
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.");
|
|
5855
|
+
return { ...rendered, derivedFromSha256: sourceSha256 };
|
|
5856
|
+
});
|
|
5857
|
+
rasterizedSvgs.set(sourceSha256, rendering);
|
|
5858
|
+
}
|
|
5859
|
+
asset = { ...await rendering, url: original.url };
|
|
5860
|
+
}
|
|
4262
5861
|
const sha256 = hash(asset.content);
|
|
4263
5862
|
const content = assetBytes.get(sha256) ?? Buffer.from(asset.content);
|
|
4264
5863
|
assetBytes.set(sha256, content);
|
|
@@ -4274,6 +5873,26 @@ async function runUrlMigration(options) {
|
|
|
4274
5873
|
}
|
|
4275
5874
|
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
5875
|
let budgetExhausted = false;
|
|
5876
|
+
const exactCaptures = /* @__PURE__ */ new Map();
|
|
5877
|
+
const exactCaptureKey = (document) => {
|
|
5878
|
+
let body = document.body;
|
|
5879
|
+
if (body.includes("data-cfemail")) {
|
|
5880
|
+
const $ = load7(body);
|
|
5881
|
+
projectProtectedEmails($, $.root());
|
|
5882
|
+
body = $.html();
|
|
5883
|
+
}
|
|
5884
|
+
return hash(canonicalJson([
|
|
5885
|
+
hash(body),
|
|
5886
|
+
document.contentType,
|
|
5887
|
+
document.status,
|
|
5888
|
+
document.locale,
|
|
5889
|
+
document.version,
|
|
5890
|
+
document.headers?.["content-language"],
|
|
5891
|
+
document.navigation,
|
|
5892
|
+
(document.states ?? []).map((state) => [state.id, state.label, state.response.finalUrl.href, state.response.contentType, state.response.status, hash(state.response.body)]),
|
|
5893
|
+
(document.assets ?? []).map((asset) => [asset.url, asset.contentType, asset.unavailable, hash(asset.content)])
|
|
5894
|
+
]));
|
|
5895
|
+
};
|
|
4277
5896
|
let cursor = 0;
|
|
4278
5897
|
const flush = async () => {
|
|
4279
5898
|
const distinct = [...new Map(changed.map((record) => [record.url, record])).values()];
|
|
@@ -4288,6 +5907,7 @@ async function runUrlMigration(options) {
|
|
|
4288
5907
|
let claimedId;
|
|
4289
5908
|
let claimedFinal;
|
|
4290
5909
|
let markdownRepresentation;
|
|
5910
|
+
let exactKey;
|
|
4291
5911
|
try {
|
|
4292
5912
|
delete record.reason;
|
|
4293
5913
|
if (/\/llms-full\.txt$/i.test(new URL(record.url).pathname)) {
|
|
@@ -4315,7 +5935,43 @@ async function runUrlMigration(options) {
|
|
|
4315
5935
|
throw new Error("Capture returned an unresolved redirect.");
|
|
4316
5936
|
}
|
|
4317
5937
|
if (Buffer.byteLength(document.body) > MAX_RESPONSE_BYTES) throw new Error("Captured response exceeds the 8 MB safety bound.");
|
|
4318
|
-
|
|
5938
|
+
if ((document.states?.length ?? 0) > 64 || (document.assets?.length ?? 0) > 256) throw new Error("Capture contains too many alternative states or assets.");
|
|
5939
|
+
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.");
|
|
5940
|
+
if (/html/i.test(document.contentType) && (!document.status || document.status < 300) && inScope(document.finalUrl, source, scopePath)) {
|
|
5941
|
+
exactKey = exactCaptureKey(document);
|
|
5942
|
+
const peer = resolveMigrationRecord(records, records.get(exactCaptures.get(exactKey) ?? ""));
|
|
5943
|
+
const peerIdentity = peer?.evidence?.articleIdentity;
|
|
5944
|
+
if (peer?.outcome === "imported" && peer.evidence && peerIdentity) {
|
|
5945
|
+
const candidate = migrationSectionQueryCandidate(document.finalUrl, peer.evidence.anchors);
|
|
5946
|
+
if (candidate.sectionAnchor && candidate.canonicalUrl === peerIdentity.canonicalUrl) {
|
|
5947
|
+
const byteLength = Buffer.byteLength(document.body) + (document.states ?? []).reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0);
|
|
5948
|
+
totalBytes += byteLength;
|
|
5949
|
+
if (totalBytes > (options.maxTotalBytes ?? 5e8)) {
|
|
5950
|
+
budgetExhausted = true;
|
|
5951
|
+
throw new Error("Migration exceeded its content byte budget.");
|
|
5952
|
+
}
|
|
5953
|
+
record.outcome = "alias";
|
|
5954
|
+
record.aliasOf = peer.url;
|
|
5955
|
+
record.aliasAnchor = candidate.sectionAnchor;
|
|
5956
|
+
if (peerIdentity.sectionAnchor && peer.aliasAnchor !== peerIdentity.sectionAnchor) {
|
|
5957
|
+
peer.aliasAnchor = peerIdentity.sectionAnchor;
|
|
5958
|
+
changed.push(peer);
|
|
5959
|
+
}
|
|
5960
|
+
record.evidence = {
|
|
5961
|
+
...peer.evidence,
|
|
5962
|
+
sha256: hash(document.body),
|
|
5963
|
+
byteLength,
|
|
5964
|
+
finalUrl: document.finalUrl.href,
|
|
5965
|
+
...document.artifactRef ? { artifactRef: document.artifactRef } : {},
|
|
5966
|
+
articleIdentity: { ...peerIdentity, sectionAnchor: candidate.sectionAnchor }
|
|
5967
|
+
};
|
|
5968
|
+
if (!document.artifactRef) delete record.evidence.artifactRef;
|
|
5969
|
+
enqueueArticleLinks(record.evidence.links, document.finalUrl.href, record.url);
|
|
5970
|
+
return;
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
const identity = declaredMigrationArticleUrl(document);
|
|
4319
5975
|
const final = canonical(identity.toString(), sourceUrl);
|
|
4320
5976
|
const role = classifyMigrationResponse(document);
|
|
4321
5977
|
if (!inScope(document.finalUrl, source, scopePath) && !(role === "index" && isDiscoveryIndex(document.finalUrl, source))) {
|
|
@@ -4339,13 +5995,13 @@ async function runUrlMigration(options) {
|
|
|
4339
5995
|
return;
|
|
4340
5996
|
}
|
|
4341
5997
|
if (/html/i.test(document.contentType)) {
|
|
4342
|
-
const $ =
|
|
5998
|
+
const $ = load7(document.body);
|
|
4343
5999
|
$('link[rel="alternate"][type="text/markdown"][href]').each((_index, node) => {
|
|
4344
6000
|
const alternate = canonical($(node).attr("href"), final);
|
|
4345
6001
|
if (!alternate) return;
|
|
4346
6002
|
const target2 = new URL(alternate), primary = new URL(final);
|
|
4347
|
-
const
|
|
4348
|
-
if (target2.origin !== primary.origin || target2.search !== primary.search ||
|
|
6003
|
+
const directory2 = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
|
|
6004
|
+
if (target2.origin !== primary.origin || target2.search !== primary.search || directory2(target2.pathname) !== directory2(primary.pathname) || !/\.mdx?$/i.test(target2.pathname)) return;
|
|
4349
6005
|
enqueue(alternate, final, "declared-markdown-representation");
|
|
4350
6006
|
const sibling = records.get(alternate);
|
|
4351
6007
|
sibling.representationOf = record.url;
|
|
@@ -4396,6 +6052,9 @@ async function runUrlMigration(options) {
|
|
|
4396
6052
|
return;
|
|
4397
6053
|
}
|
|
4398
6054
|
let coveredSchemaGroups = [];
|
|
6055
|
+
let coveredSchemaDisclosures = [];
|
|
6056
|
+
let coveredCodeControls = [];
|
|
6057
|
+
let schemaOperation;
|
|
4399
6058
|
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
6059
|
if (/html/i.test(document.contentType) && markdownRepresentation && (unresolvedStaticControls(document.body) || hasSchemaChoice)) {
|
|
4401
6060
|
const schemaCapture = await capture({ url: new URL(markdownRepresentation), signal: options.signal });
|
|
@@ -4411,7 +6070,10 @@ async function runUrlMigration(options) {
|
|
|
4411
6070
|
const path = `openapi/migration-${sha256}.json`;
|
|
4412
6071
|
record.apiSpecification = { path, sha256, document: fragment.document };
|
|
4413
6072
|
page.openapi = `${path} ${fragment.method} ${fragment.path}`;
|
|
6073
|
+
schemaOperation = fragment;
|
|
4414
6074
|
coveredSchemaGroups = apiSchemaTabGroups(document.body, fragment.document, fragment);
|
|
6075
|
+
coveredSchemaDisclosures = apiSchemaDisclosureGroups(document.body, fragment.document, fragment);
|
|
6076
|
+
coveredCodeControls = apiOperationCodeControls(document.body, fragment.document, fragment);
|
|
4415
6077
|
}
|
|
4416
6078
|
}
|
|
4417
6079
|
const states = document.states ?? [];
|
|
@@ -4442,12 +6104,13 @@ ${difference}`;
|
|
|
4442
6104
|
record.assets = [];
|
|
4443
6105
|
record.issues = [];
|
|
4444
6106
|
record.warnings = [];
|
|
4445
|
-
|
|
4446
|
-
|
|
6107
|
+
for (const captured of [document, ...states.map((state) => state.response)]) {
|
|
6108
|
+
if (!/html/i.test(captured.contentType)) continue;
|
|
6109
|
+
const { $, root } = migrationHtmlArticle(captured.body);
|
|
4447
6110
|
root.find("nav,footer,aside,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
|
|
4448
6111
|
root.find('button svg,a.hash-link svg,[aria-hidden="true"] svg,svg[aria-hidden="true"],svg[data-lucide],svg[data-icon]').remove();
|
|
4449
6112
|
root.find("video,iframe").each((_index, node) => {
|
|
4450
|
-
if (projectMigrationMedia($.html(node),
|
|
6113
|
+
if (projectMigrationMedia($.html(node), captured.finalUrl) !== null) {
|
|
4451
6114
|
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
6115
|
$(node).remove();
|
|
4453
6116
|
}
|
|
@@ -4458,9 +6121,17 @@ ${difference}`;
|
|
|
4458
6121
|
}
|
|
4459
6122
|
const unsupported = [...new Set(root.find("iframe,object,embed,canvas,svg,form,input,textarea,select,math,video,audio").toArray().map((node) => node.tagName))];
|
|
4460
6123
|
if (unsupported.length) record.issues.push({ code: "unsupported-component", source: record.url, message: `Rendered content requires explicit review for: ${unsupported.join(", ")}.` });
|
|
4461
|
-
|
|
6124
|
+
const groups = captured === document ? coveredSchemaGroups : record.apiSpecification && schemaOperation ? apiSchemaTabGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
|
|
6125
|
+
const disclosures = captured === document ? coveredSchemaDisclosures : record.apiSpecification && schemaOperation ? apiSchemaDisclosureGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
|
|
6126
|
+
const codeControls = captured === document ? coveredCodeControls : record.apiSpecification && schemaOperation ? apiOperationCodeControls(captured.body, record.apiSpecification.document, schemaOperation) : [];
|
|
6127
|
+
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
6128
|
}
|
|
4463
|
-
const
|
|
6129
|
+
const inlineAssets = [document, ...states.map((state) => state.response)].flatMap((captured) => /html/i.test(captured.contentType) ? migrationHtmlArticle(captured.body).assets.map((asset) => ({
|
|
6130
|
+
url: new URL(`/${asset.path}`, captured.finalUrl).href,
|
|
6131
|
+
contentType: "image/svg+xml",
|
|
6132
|
+
content: typeof asset.content === "string" ? Buffer.from(asset.content) : asset.content
|
|
6133
|
+
})) : []);
|
|
6134
|
+
const suppliedAssets = new Map([...document.assets ?? [], ...inlineAssets].map((asset) => [canonical(asset.url, final), asset]));
|
|
4464
6135
|
const assetUrls = [...new Set([...assetReferences(document), ...states.flatMap((state) => assetReferences(state.response))].map((url) => canonical(url, final)).filter((url) => Boolean(url)))];
|
|
4465
6136
|
if (assetUrls.length > 256 || (document.assets?.length ?? 0) > 256) throw new Error("Capture exceeds the 256 assets per page safety bound.");
|
|
4466
6137
|
for (const url of assetUrls) {
|
|
@@ -4470,7 +6141,7 @@ ${difference}`;
|
|
|
4470
6141
|
assetRequest = assetRequests.load(url, () => assetFetcher(new URL(url), options.signal).then(retainAsset));
|
|
4471
6142
|
}
|
|
4472
6143
|
const supplied = suppliedAssets.get(url);
|
|
4473
|
-
const asset = supplied ? retainAsset(supplied) : assetRequest ? await assetRequest : null;
|
|
6144
|
+
const asset = supplied ? await retainAsset(supplied) : assetRequest ? await assetRequest : null;
|
|
4474
6145
|
if (!asset) throw new Error("Capture host did not supply asset bytes or a binary fetch boundary.");
|
|
4475
6146
|
if (asset.unavailable && [404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) {
|
|
4476
6147
|
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 +6160,7 @@ ${difference}`;
|
|
|
4489
6160
|
}
|
|
4490
6161
|
}
|
|
4491
6162
|
const path = `migration-assets/${sha256}.${extension}`;
|
|
4492
|
-
const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64") };
|
|
6163
|
+
const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64"), ...asset.derivedFromSha256 ? { derivedFromSha256: asset.derivedFromSha256 } : {} };
|
|
4493
6164
|
record.assets.push(receipt);
|
|
4494
6165
|
if (!existing) assetPayloads.set(sha256, receipt);
|
|
4495
6166
|
if (!assetRequests.has(url)) assetRequests.remember(url, asset);
|
|
@@ -4514,6 +6185,7 @@ ${difference}`;
|
|
|
4514
6185
|
record.pageSha256 = pageHash(page);
|
|
4515
6186
|
record.outcome = "imported";
|
|
4516
6187
|
imported++;
|
|
6188
|
+
if (exactKey) exactCaptures.set(exactKey, record.url);
|
|
4517
6189
|
enqueueArticleLinks(evidence.links, final, record.url);
|
|
4518
6190
|
} catch (error) {
|
|
4519
6191
|
if (options.signal?.aborted) return;
|
|
@@ -4539,6 +6211,43 @@ ${difference}`;
|
|
|
4539
6211
|
}
|
|
4540
6212
|
}
|
|
4541
6213
|
};
|
|
6214
|
+
const settleArticleIdentities = () => {
|
|
6215
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6216
|
+
for (const record of records.values()) {
|
|
6217
|
+
const identity = record.evidence?.articleIdentity;
|
|
6218
|
+
if (record.outcome !== "imported" || !identity) continue;
|
|
6219
|
+
const key = JSON.stringify([identity.canonicalUrl, identity.articleSha256]);
|
|
6220
|
+
const group = groups.get(key) ?? [];
|
|
6221
|
+
group.push(record);
|
|
6222
|
+
groups.set(key, group);
|
|
6223
|
+
}
|
|
6224
|
+
for (const group of groups.values()) {
|
|
6225
|
+
if (group.length < 2) continue;
|
|
6226
|
+
const canonicalUrl = group[0].evidence.articleIdentity.canonicalUrl;
|
|
6227
|
+
const winner = group.find((record) => record.url === canonicalUrl) ?? group[0];
|
|
6228
|
+
const winnerAnchor = winner.evidence.articleIdentity.sectionAnchor;
|
|
6229
|
+
if (winnerAnchor && winner.aliasAnchor !== winnerAnchor) {
|
|
6230
|
+
winner.aliasAnchor = winnerAnchor;
|
|
6231
|
+
changed.push(winner);
|
|
6232
|
+
}
|
|
6233
|
+
for (const alternate of group) {
|
|
6234
|
+
if (alternate === winner) continue;
|
|
6235
|
+
imported--;
|
|
6236
|
+
if (alternate.page && usedIds.get(alternate.page.id) === alternate.url) usedIds.delete(alternate.page.id);
|
|
6237
|
+
alternate.outcome = "alias";
|
|
6238
|
+
alternate.aliasOf = winner.url;
|
|
6239
|
+
alternate.aliasAnchor = alternate.evidence.articleIdentity.sectionAnchor;
|
|
6240
|
+
delete alternate.page;
|
|
6241
|
+
delete alternate.pageSha256;
|
|
6242
|
+
delete alternate.assets;
|
|
6243
|
+
delete alternate.apiSpecification;
|
|
6244
|
+
delete alternate.issues;
|
|
6245
|
+
delete alternate.warnings;
|
|
6246
|
+
delete alternate.reason;
|
|
6247
|
+
changed.push(alternate);
|
|
6248
|
+
}
|
|
6249
|
+
}
|
|
6250
|
+
};
|
|
4542
6251
|
const settleRepresentations = () => {
|
|
4543
6252
|
for (const record of records.values()) {
|
|
4544
6253
|
if (!record.representationOf) continue;
|
|
@@ -4564,6 +6273,7 @@ ${difference}`;
|
|
|
4564
6273
|
}
|
|
4565
6274
|
};
|
|
4566
6275
|
settleRepresentations();
|
|
6276
|
+
settleArticleIdentities();
|
|
4567
6277
|
while (cursor < pending.length && !options.signal?.aborted && !budgetExhausted && cursor < (options.maxCaptures ?? Infinity)) {
|
|
4568
6278
|
if (imported >= maxPages) {
|
|
4569
6279
|
const index = pending.findIndex((record, position) => position >= cursor && isDiscoveryIndex(new URL(record.url), source));
|
|
@@ -4574,6 +6284,7 @@ ${difference}`;
|
|
|
4574
6284
|
cursor += batch.length;
|
|
4575
6285
|
await Promise.all(batch.map(process));
|
|
4576
6286
|
settleRepresentations();
|
|
6287
|
+
settleArticleIdentities();
|
|
4577
6288
|
if (changed.length >= batchSize) await flush();
|
|
4578
6289
|
}
|
|
4579
6290
|
await flush();
|
|
@@ -4593,7 +6304,7 @@ ${difference}`;
|
|
|
4593
6304
|
for (const node of nodes) {
|
|
4594
6305
|
const entry = node.url ? records.get(canonical(node.url, sourceUrl) ?? "") : void 0;
|
|
4595
6306
|
const target2 = resolveMigrationRecord(records, entry);
|
|
4596
|
-
if (target2?.page && node.label && !node.children?.length) {
|
|
6307
|
+
if (target2?.page && node.label && !node.children?.length && !entry?.aliasAnchor) {
|
|
4597
6308
|
const previous = navigationLabels.get(target2.url);
|
|
4598
6309
|
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
6310
|
else navigationLabels.set(target2.url, node.label);
|
|
@@ -4618,8 +6329,9 @@ ${difference}`;
|
|
|
4618
6329
|
anchor = decodeURIComponent(anchor);
|
|
4619
6330
|
} catch {
|
|
4620
6331
|
}
|
|
6332
|
+
const effectiveHash = url.hash || (records.get(identity)?.aliasAnchor ? `#${encodeURIComponent(records.get(identity).aliasAnchor)}` : "");
|
|
4621
6333
|
const hasBrokenSourceAnchor = Boolean(url.hash && destination?.outcome === "imported" && destination.evidence && !destination.evidence.anchors.includes(anchor));
|
|
4622
|
-
return `${route}${hasBrokenSourceAnchor ? "" :
|
|
6334
|
+
return `${route}${hasBrokenSourceAnchor ? "" : effectiveHash}`;
|
|
4623
6335
|
}) }] : []);
|
|
4624
6336
|
const validation = validateUrlMigration(checkpoint, Boolean(options.signal?.aborted));
|
|
4625
6337
|
validation.issues.push(...navigationIssues);
|
|
@@ -4698,16 +6410,16 @@ function rewriteMarkdownDestinations(body, rewrite) {
|
|
|
4698
6410
|
}
|
|
4699
6411
|
|
|
4700
6412
|
// src/components.ts
|
|
4701
|
-
import { createHash as
|
|
6413
|
+
import { createHash as createHash5 } from "crypto";
|
|
4702
6414
|
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
|
|
4703
6415
|
import { basename, dirname as dirname2, extname as extname3, posix as posix2, relative as relative3, resolve as resolve3 } from "path";
|
|
4704
6416
|
import postcss from "postcss";
|
|
4705
6417
|
import selectorParser from "postcss-selector-parser";
|
|
4706
|
-
import
|
|
4707
|
-
import
|
|
6418
|
+
import remarkMdx5 from "remark-mdx";
|
|
6419
|
+
import remarkParse5 from "remark-parse";
|
|
4708
6420
|
import ts2 from "typescript";
|
|
4709
|
-
import { unified as
|
|
4710
|
-
var
|
|
6421
|
+
import { unified as unified5 } from "unified";
|
|
6422
|
+
var parser3 = unified5().use(remarkParse5).use(remarkMdx5);
|
|
4711
6423
|
var DOCUSAURUS_THEME_SHIMS = {
|
|
4712
6424
|
"@theme/CodeBlock": {
|
|
4713
6425
|
filename: "docusaurus-code-block.tsx",
|
|
@@ -4747,7 +6459,7 @@ function applyReplacements(source, replacements) {
|
|
|
4747
6459
|
);
|
|
4748
6460
|
}
|
|
4749
6461
|
function hash2(value) {
|
|
4750
|
-
return
|
|
6462
|
+
return createHash5("sha256").update(value).digest("hex").slice(0, 12);
|
|
4751
6463
|
}
|
|
4752
6464
|
function sourceFile(source, filename) {
|
|
4753
6465
|
return ts2.createSourceFile(filename, source, ts2.ScriptTarget.Latest, true, scriptKindFor(filename));
|
|
@@ -4799,7 +6511,7 @@ function renderPage(bundle, page) {
|
|
|
4799
6511
|
page.icon ? `icon: ${yamlString(page.icon)}` : null,
|
|
4800
6512
|
page.iconType ? `iconType: ${yamlString(page.iconType)}` : null,
|
|
4801
6513
|
`description: ${yamlString(page.description)}`,
|
|
4802
|
-
page.descriptionPlacement === "body" ?
|
|
6514
|
+
page.descriptionPlacement === "body" || page.descriptionPlacement === "operation" ? `descriptionPlacement: ${page.descriptionPlacement}` : null,
|
|
4803
6515
|
page.badge ? `badge: ${yamlString(page.badge)}` : null,
|
|
4804
6516
|
page.keywords.length > 0 ? `keywords: [${page.keywords.map(yamlString).join(", ")}]` : null,
|
|
4805
6517
|
page.mode ? `mode: ${yamlString(page.mode)}` : null,
|