@thallylabs/migrate 0.2.38 → 0.2.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8707 -6632
- 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 +11 -1
- package/dist/url-migration.js +2220 -148
- package/dist/url-migration.js.map +1 -1
- package/dist/{url-migration-peVzTLjL.d.ts → url-run-DFnoqEi-.d.ts} +25 -1
- 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
|
}
|
|
@@ -531,7 +537,817 @@ function cloneUrlMigrationCheckpoint(checkpoint) {
|
|
|
531
537
|
}
|
|
532
538
|
|
|
533
539
|
// src/url-navigation.ts
|
|
540
|
+
import { load as load2 } from "cheerio";
|
|
541
|
+
|
|
542
|
+
// src/url-scope.ts
|
|
534
543
|
import { load } from "cheerio";
|
|
544
|
+
function inferUrlMigrationScope(capture) {
|
|
545
|
+
const source = capture.finalUrl;
|
|
546
|
+
let end = source.pathname.length;
|
|
547
|
+
while (end > 0 && source.pathname.charCodeAt(end - 1) === 47) end--;
|
|
548
|
+
const parent = end < source.pathname.length ? source.pathname.slice(0, end) || "/" : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/";
|
|
549
|
+
if (!/html/i.test(capture.contentType) || Buffer.byteLength(capture.body) > 8e6) return { scopePath: parent, evidence: "source-parent" };
|
|
550
|
+
const $ = load(capture.body);
|
|
551
|
+
const ancestor = (path) => path === "/" || source.pathname === path || source.pathname.startsWith(`${path}/`);
|
|
552
|
+
const observed = (reference) => {
|
|
553
|
+
if (!reference || reference.length > 8192) return void 0;
|
|
554
|
+
try {
|
|
555
|
+
const url = new URL(reference, source);
|
|
556
|
+
if (url.origin !== source.origin || url.username || url.password || url.search || url.hash || /%2f|%5c/i.test(url.pathname)) return void 0;
|
|
557
|
+
return url;
|
|
558
|
+
} catch {
|
|
559
|
+
return void 0;
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
const indexes = [];
|
|
563
|
+
$("link[href],a[href]").slice(0, 1e4).each((_, node) => {
|
|
564
|
+
const url = observed($(node).attr("href"));
|
|
565
|
+
if (!url || !/\/(?:llms(?:-full)?\.txt|sitemap(?:[-_][^/]*)?\.xml)$/i.test(url.pathname)) return;
|
|
566
|
+
const path = url.pathname.slice(0, url.pathname.lastIndexOf("/")) || "/";
|
|
567
|
+
if (ancestor(path)) indexes.push(path);
|
|
568
|
+
});
|
|
569
|
+
if (indexes.length) return { scopePath: indexes.sort((a, b) => b.length - a.length)[0], evidence: "discovery-index" };
|
|
570
|
+
const paths = [];
|
|
571
|
+
$('nav a[href],aside a[href],[role="navigation"] a[href]').slice(0, 1e4).each((_, node) => {
|
|
572
|
+
if ($(node).closest("header,footer").length) return;
|
|
573
|
+
const url = observed($(node).attr("href"));
|
|
574
|
+
if (url && !/\.[a-z0-9]+$/i.test(url.pathname)) paths.push(url.pathname);
|
|
575
|
+
});
|
|
576
|
+
if (new Set(paths).size >= 2 && paths.some((path) => path === source.pathname)) {
|
|
577
|
+
const segments = paths.map((path) => path.split("/").filter(Boolean));
|
|
578
|
+
let count = 0;
|
|
579
|
+
while (segments.every((parts) => parts[count] !== void 0 && parts[count] === segments[0][count])) count++;
|
|
580
|
+
const common = "/" + segments[0].slice(0, count).join("/");
|
|
581
|
+
if (ancestor(common)) return { scopePath: common, evidence: "navigation" };
|
|
582
|
+
}
|
|
583
|
+
return { scopePath: parent, evidence: "source-parent" };
|
|
584
|
+
}
|
|
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
|
+
|
|
1350
|
+
// src/url-navigation.ts
|
|
535
1351
|
var MAX_CAPTURE_BYTES = 8e6;
|
|
536
1352
|
var MAX_JSON_BYTES = 2e6;
|
|
537
1353
|
var MAX_NODES = 1e4;
|
|
@@ -598,8 +1414,18 @@ function flightRecords(stream) {
|
|
|
598
1414
|
}
|
|
599
1415
|
return records;
|
|
600
1416
|
}
|
|
601
|
-
function mintlifyNavigation(body, base, locale) {
|
|
602
|
-
const $ =
|
|
1417
|
+
function mintlifyNavigation(body, base, locale, selectedScope) {
|
|
1418
|
+
const $ = load2(body);
|
|
1419
|
+
const scope = inferUrlMigrationScope({ body, contentType: "text/html", finalUrl: base });
|
|
1420
|
+
const mount = scope.evidence === "source-parent" ? selectedScope ?? "/" : scope.scopePath;
|
|
1421
|
+
const configTarget = (value) => {
|
|
1422
|
+
const reference = text(value);
|
|
1423
|
+
if (!reference) return void 0;
|
|
1424
|
+
if (/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(reference)) return target(reference, base);
|
|
1425
|
+
const path = reference.replace(/^\/+/, "");
|
|
1426
|
+
const prefix = mount.replace(/^\/|\/$/g, "");
|
|
1427
|
+
return target(prefix && path !== prefix && !path.startsWith(`${prefix}/`) ? `${prefix}/${path}` : path, new URL("/", base));
|
|
1428
|
+
};
|
|
603
1429
|
let stream = "";
|
|
604
1430
|
const records = [];
|
|
605
1431
|
$("script").each((_index, script) => {
|
|
@@ -636,7 +1462,7 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
636
1462
|
const walk = (value, depth = 0, activeLocale) => {
|
|
637
1463
|
if (depth > MAX_DEPTH || ++count > MAX_NODES) throw new Error("Source navigation exceeds its safety bound.");
|
|
638
1464
|
if (typeof value === "string") {
|
|
639
|
-
const url2 =
|
|
1465
|
+
const url2 = configTarget(value);
|
|
640
1466
|
return url2 ? [{ kind: "page", label: value, url: url2 }] : [];
|
|
641
1467
|
}
|
|
642
1468
|
if (Array.isArray(value)) return value.flatMap((child) => walk(child, depth + 1, activeLocale));
|
|
@@ -656,7 +1482,7 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
656
1482
|
return;
|
|
657
1483
|
}
|
|
658
1484
|
if (!object(node)) return;
|
|
659
|
-
const href =
|
|
1485
|
+
const href = configTarget(node.href);
|
|
660
1486
|
if (href) {
|
|
661
1487
|
populated++;
|
|
662
1488
|
const url2 = new URL(href);
|
|
@@ -674,10 +1500,10 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
674
1500
|
const selected = matching[0]?.item ?? languages.find((item) => text(item.language)?.toLowerCase() === language?.toLowerCase()) ?? languages.find((item) => text(item.language)?.toLowerCase() === "en") ?? languages[0];
|
|
675
1501
|
return selected ? walk({ ...selected, languages: void 0 }, depth + 1, text(selected.language)) : [];
|
|
676
1502
|
}
|
|
677
|
-
const label = text(value.tab) ?? text(value.group) ?? text(value.title) ?? text(value.label);
|
|
678
|
-
const kind = text(value.tab) ? "tab" : text(value.group) ? "group" : "page";
|
|
1503
|
+
const label = text(value.tab) ?? text(value.dropdown) ?? text(value.group) ?? text(value.sidebarTitle) ?? text(value.title) ?? text(value.label);
|
|
1504
|
+
const kind = text(value.tab) || text(value.dropdown) ? "tab" : text(value.group) ? "group" : "page";
|
|
679
1505
|
const children = ["tabs", "groups", "pages", "dropdowns", "anchors", "versions"].flatMap((key) => Array.isArray(value[key]) ? walk(value[key], depth + 1, activeLocale) : []);
|
|
680
|
-
const url =
|
|
1506
|
+
const url = configTarget(value.href);
|
|
681
1507
|
if (!label) return children;
|
|
682
1508
|
if (!url && !children.length) return [];
|
|
683
1509
|
return [{
|
|
@@ -685,6 +1511,7 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
685
1511
|
label,
|
|
686
1512
|
...url ? { url } : {},
|
|
687
1513
|
...children.length ? { children } : {},
|
|
1514
|
+
...text(value.dropdown) ? { presentation: "dropdown" } : {},
|
|
688
1515
|
...kind === "tab" && activeLocale ? { locale: activeLocale } : {}
|
|
689
1516
|
}];
|
|
690
1517
|
};
|
|
@@ -711,47 +1538,183 @@ function resolveNavigation(nodes, base) {
|
|
|
711
1538
|
...url ? { url } : {},
|
|
712
1539
|
...children.length ? { children } : {},
|
|
713
1540
|
...["tab", "group", "page"].includes(node.kind ?? "") ? { kind: node.kind } : {},
|
|
1541
|
+
...node.presentation === "dropdown" ? { presentation: "dropdown" } : {},
|
|
714
1542
|
...text(node.locale) ? { locale: text(node.locale) } : {}
|
|
715
1543
|
}];
|
|
716
1544
|
});
|
|
717
1545
|
};
|
|
718
1546
|
return walk(nodes);
|
|
719
1547
|
}
|
|
720
|
-
function
|
|
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
|
+
}
|
|
1617
|
+
function extractSourceNavigation(capture, selectedScope) {
|
|
721
1618
|
if (capture.navigation) return resolveNavigation(capture.navigation, capture.finalUrl);
|
|
722
1619
|
if (!/html/i.test(capture.contentType) || Buffer.byteLength(capture.body) > MAX_CAPTURE_BYTES) return [];
|
|
723
|
-
const mintlify = mintlifyNavigation(capture.body, capture.finalUrl, capture.locale);
|
|
1620
|
+
const mintlify = mintlifyNavigation(capture.body, capture.finalUrl, capture.locale, selectedScope);
|
|
724
1621
|
if (mintlify.length) return mintlify;
|
|
725
|
-
const $ =
|
|
1622
|
+
const $ = load2(capture.body);
|
|
726
1623
|
const candidates = ['.theme-doc-sidebar-menu,[aria-label*="sidebar" i]', "aside", '[role="navigation"],nav'];
|
|
727
|
-
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);
|
|
728
1642
|
if (!root) return [];
|
|
729
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
|
+
};
|
|
730
1653
|
const walk = (element, excluded, depth = 0) => {
|
|
731
1654
|
if (depth > MAX_DEPTH) throw new Error("Source navigation exceeds its safety bound.");
|
|
732
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
|
+
};
|
|
733
1692
|
$(element).children().each((_index, node) => {
|
|
734
1693
|
if (++count > MAX_NODES) throw new Error("Source navigation exceeds its safety bound.");
|
|
735
1694
|
if (node === excluded) return;
|
|
736
1695
|
const item = $(node);
|
|
737
|
-
if (
|
|
738
|
-
|
|
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);
|
|
739
1701
|
const url = target(item.attr("href"), capture.finalUrl);
|
|
740
|
-
if (label && url)
|
|
1702
|
+
if (label && url) append([{ label, url }]);
|
|
741
1703
|
} else if (item.is("li,details") || item.attr("data-migration-group") !== void 0 || item.children("h2,h3,h4,h5,h6").length > 0) {
|
|
742
1704
|
const directLink = item.children("a[href]").first();
|
|
743
1705
|
const direct = directLink.length ? directLink : item.children("div").children("a[href]").first();
|
|
744
|
-
const label = direct
|
|
1706
|
+
const label = labelOf(direct) || labelOf(item.children("summary,button,span,h2,h3,h4,h5,h6").first());
|
|
745
1707
|
const children = walk(node, direct[0], depth + 1);
|
|
746
1708
|
const url = target(direct.attr("href"), capture.finalUrl);
|
|
747
|
-
if (label && (url || children.length))
|
|
748
|
-
else
|
|
749
|
-
} 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));
|
|
750
1712
|
});
|
|
751
1713
|
return result;
|
|
752
1714
|
};
|
|
753
1715
|
try {
|
|
754
|
-
|
|
1716
|
+
const nodes = walk(root);
|
|
1717
|
+
return [...nodes, ...sidebarCollectionMenus($, root, capture.finalUrl, nodes)];
|
|
755
1718
|
} catch {
|
|
756
1719
|
return [];
|
|
757
1720
|
}
|
|
@@ -804,7 +1767,7 @@ function migrationImageUrl(value, base) {
|
|
|
804
1767
|
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error("Responsive image URL is not a credential-free HTTP(S) URL.");
|
|
805
1768
|
return url.toString();
|
|
806
1769
|
}
|
|
807
|
-
function migrationImageCandidates(value, base,
|
|
1770
|
+
function migrationImageCandidates(value, base, resolve4 = true) {
|
|
808
1771
|
if (value.length > 64e3) throw new Error("Responsive image candidate list exceeds its safety bound.");
|
|
809
1772
|
const result = [];
|
|
810
1773
|
let index = 0;
|
|
@@ -822,7 +1785,7 @@ function migrationImageCandidates(value, base, resolve3 = true) {
|
|
|
822
1785
|
if (descriptor && !/^(?:[1-9]\d*w|(?:\d+(?:\.\d+)?|\.\d+)x)$/.test(descriptor)) throw new Error("Responsive image has an unsupported descriptor.");
|
|
823
1786
|
if (descriptor.endsWith("x") && Number(descriptor.slice(0, -1)) <= 0) throw new Error("Responsive image density must be positive.");
|
|
824
1787
|
const resolved = migrationImageUrl(url, base);
|
|
825
|
-
result.push({ url:
|
|
1788
|
+
result.push({ url: resolve4 ? resolved : url, descriptor });
|
|
826
1789
|
if (result.length > 128) throw new Error("Responsive image has too many candidates.");
|
|
827
1790
|
if (value[index] === ",") index++;
|
|
828
1791
|
}
|
|
@@ -836,12 +1799,96 @@ function migrationImageAttribute(value) {
|
|
|
836
1799
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/\{/g, "{").replace(/\}/g, "}").replace(/\r/g, " ").replace(/\n/g, " ");
|
|
837
1800
|
}
|
|
838
1801
|
|
|
1802
|
+
// src/url-media.ts
|
|
1803
|
+
import { load as load3 } from "cheerio";
|
|
1804
|
+
var VIDEO_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
1805
|
+
"src",
|
|
1806
|
+
"title",
|
|
1807
|
+
"poster",
|
|
1808
|
+
"controls",
|
|
1809
|
+
"muted",
|
|
1810
|
+
"autoplay",
|
|
1811
|
+
"loop",
|
|
1812
|
+
"playsinline",
|
|
1813
|
+
"class",
|
|
1814
|
+
"id",
|
|
1815
|
+
"width",
|
|
1816
|
+
"height",
|
|
1817
|
+
"preload",
|
|
1818
|
+
"aria-label"
|
|
1819
|
+
]);
|
|
1820
|
+
var YOUTUBE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
1821
|
+
"src",
|
|
1822
|
+
"title",
|
|
1823
|
+
"width",
|
|
1824
|
+
"height",
|
|
1825
|
+
"allow",
|
|
1826
|
+
"allowfullscreen",
|
|
1827
|
+
"referrerpolicy",
|
|
1828
|
+
"frameborder",
|
|
1829
|
+
"loading",
|
|
1830
|
+
"class",
|
|
1831
|
+
"id",
|
|
1832
|
+
"aria-label"
|
|
1833
|
+
]);
|
|
1834
|
+
var YOUTUBE_HOSTS = /* @__PURE__ */ new Set([
|
|
1835
|
+
"youtube.com",
|
|
1836
|
+
"www.youtube.com",
|
|
1837
|
+
"youtube-nocookie.com",
|
|
1838
|
+
"www.youtube-nocookie.com"
|
|
1839
|
+
]);
|
|
1840
|
+
function projectMigrationMedia(html, base) {
|
|
1841
|
+
const $ = load3(html, void 0, false);
|
|
1842
|
+
const nodes = $.root().contents().filter((_index, node2) => node2.type !== "text" || Boolean(node2.data.trim()));
|
|
1843
|
+
if (nodes.length !== 1) return null;
|
|
1844
|
+
const element = nodes.first();
|
|
1845
|
+
const node = element[0];
|
|
1846
|
+
if (node.type !== "tag" || !["video", "iframe"].includes(node.name)) return null;
|
|
1847
|
+
if (element.children().length || element.text().trim()) return null;
|
|
1848
|
+
const allowed = node.name === "video" ? VIDEO_ATTRIBUTES : YOUTUBE_ATTRIBUTES;
|
|
1849
|
+
if (Object.keys(node.attribs).some((name) => !allowed.has(name))) return null;
|
|
1850
|
+
const source = element.attr("src");
|
|
1851
|
+
if (!source) return null;
|
|
1852
|
+
try {
|
|
1853
|
+
let src = migrationImageUrl(source, base);
|
|
1854
|
+
let component = "Video";
|
|
1855
|
+
const props = [];
|
|
1856
|
+
if (node.name === "video") {
|
|
1857
|
+
if (!/\.(?:mp4|webm|ogg|mov|m4v)(?:[?#]|$)/i.test(src)) component = "video";
|
|
1858
|
+
const isMuted = element.attr("muted") !== void 0;
|
|
1859
|
+
const hasAutoplay = element.attr("autoplay") !== void 0;
|
|
1860
|
+
if (hasAutoplay && !isMuted) return null;
|
|
1861
|
+
props.push("controls={true}");
|
|
1862
|
+
for (const [attribute, prop] of [["muted", "muted"], ["loop", "loop"], ["playsinline", "playsInline"], ["autoplay", "autoPlay"]]) {
|
|
1863
|
+
if (element.attr(attribute) !== void 0) props.push(`${prop}={true}`);
|
|
1864
|
+
}
|
|
1865
|
+
const poster = element.attr("poster");
|
|
1866
|
+
if (poster) props.push(`poster="${migrationImageAttribute(migrationImageUrl(poster, base))}"`);
|
|
1867
|
+
} else {
|
|
1868
|
+
const url = new URL(src);
|
|
1869
|
+
if (url.protocol !== "https:" || url.port || !YOUTUBE_HOSTS.has(url.hostname)) return null;
|
|
1870
|
+
const id = url.pathname.match(/^\/embed\/([A-Za-z0-9_-]{11})$/)?.[1];
|
|
1871
|
+
if (!id || url.hash || [...url.searchParams.keys()].some((name) => name !== "si")) return null;
|
|
1872
|
+
src = `https://www.youtube-nocookie.com/embed/${id}`;
|
|
1873
|
+
}
|
|
1874
|
+
const title = element.attr("title") ?? element.attr("aria-label");
|
|
1875
|
+
if (title !== void 0) props.push(`title="${migrationImageAttribute(title)}"`);
|
|
1876
|
+
return `<${component} src="${migrationImageAttribute(src)}"${props.length ? ` ${props.join(" ")}` : ""} />`;
|
|
1877
|
+
} catch {
|
|
1878
|
+
return null;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// src/url-identity.ts
|
|
1883
|
+
import { createHash as createHash3 } from "crypto";
|
|
1884
|
+
import { load as load6 } from "cheerio";
|
|
1885
|
+
|
|
839
1886
|
// src/url.ts
|
|
840
|
-
import { load as
|
|
841
|
-
import
|
|
842
|
-
import
|
|
1887
|
+
import { load as load5 } from "cheerio";
|
|
1888
|
+
import remarkMdx3 from "remark-mdx";
|
|
1889
|
+
import remarkParse3 from "remark-parse";
|
|
843
1890
|
import TurndownService from "turndown";
|
|
844
|
-
import { unified as
|
|
1891
|
+
import { unified as unified3 } from "unified";
|
|
845
1892
|
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
846
1893
|
|
|
847
1894
|
// src/analytics.ts
|
|
@@ -852,10 +1899,10 @@ var HOSTNAME = new RegExp(`^${HOSTNAME_LABEL}(?:\\.${HOSTNAME_LABEL})*$`);
|
|
|
852
1899
|
import { posix } from "path";
|
|
853
1900
|
import { compileSync } from "@mdx-js/mdx";
|
|
854
1901
|
import { nameToEmoji } from "gemoji";
|
|
855
|
-
import
|
|
856
|
-
import
|
|
857
|
-
import
|
|
858
|
-
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";
|
|
859
1906
|
|
|
860
1907
|
// src/builtin-components.ts
|
|
861
1908
|
var THALLY_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
@@ -1342,7 +2389,7 @@ function titleFromId(id) {
|
|
|
1342
2389
|
if (value === "introduction") return "Introduction";
|
|
1343
2390
|
return value.split(/[-_]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1344
2391
|
}
|
|
1345
|
-
var descriptionParser =
|
|
2392
|
+
var descriptionParser = unified2().use(remarkParse2).use(remarkGfm2).use(remarkMdx2);
|
|
1346
2393
|
function plainText(node) {
|
|
1347
2394
|
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
1348
2395
|
if (node.type === "image") return node.alt ?? "";
|
|
@@ -2401,8 +3448,132 @@ function apiFrontmatter(data, warn) {
|
|
|
2401
3448
|
return result;
|
|
2402
3449
|
}
|
|
2403
3450
|
|
|
3451
|
+
// src/url-static-content.ts
|
|
3452
|
+
import { createHash as createHash2 } from "crypto";
|
|
3453
|
+
import { load as load4 } from "cheerio";
|
|
3454
|
+
function projectMigrationSvg(html) {
|
|
3455
|
+
const $ = load4(html, { xmlMode: true }, false);
|
|
3456
|
+
const svg = $.root().children();
|
|
3457
|
+
if (svg.length !== 1 || !svg.is("svg")) return null;
|
|
3458
|
+
if (svg.attr("xmlns") === void 0) svg.attr("xmlns", "http://www.w3.org/2000/svg");
|
|
3459
|
+
const content = Buffer.from($.xml(svg));
|
|
3460
|
+
if (!isPassiveSvg(content)) return null;
|
|
3461
|
+
const sha256 = createHash2("sha256").update(content).digest("hex");
|
|
3462
|
+
const labelled = (svg.attr("aria-labelledby") ?? "").split(/\s+/).filter(Boolean).map((id) => svg.find("[id]").filter((_index, node) => $(node).attr("id") === id).text().trim()).filter(Boolean);
|
|
3463
|
+
const alternative = svg.attr("aria-label")?.trim() || labelled.join(" ") || svg.children("title").text().trim() || svg.children("desc").text().trim() || svg.find("text").text().trim();
|
|
3464
|
+
return { asset: { path: `migration-assets/${sha256}.svg`, content }, alternative };
|
|
3465
|
+
}
|
|
3466
|
+
function projectProtectedEmails($, root) {
|
|
3467
|
+
root.find("[data-cfemail]").each((_index, node) => {
|
|
3468
|
+
const element = $(node);
|
|
3469
|
+
const anchor = element.is("a") ? element : element.closest("a");
|
|
3470
|
+
const href = anchor.attr("href") ?? "";
|
|
3471
|
+
if (!anchor.length || !/^\/cdn-cgi\/l\/email-protection(?:#[a-f\d]+)?$/i.test(href)) return;
|
|
3472
|
+
const encoded = element.attr("data-cfemail") ?? "";
|
|
3473
|
+
if (!/^(?:[a-f\d]{2}){4,1024}$/i.test(encoded)) return;
|
|
3474
|
+
const bytes = Buffer.from(encoded, "hex");
|
|
3475
|
+
const payload = Uint8Array.from(bytes.subarray(1), (byte) => byte ^ bytes[0]);
|
|
3476
|
+
let email;
|
|
3477
|
+
try {
|
|
3478
|
+
email = new TextDecoder("utf-8", { fatal: true }).decode(payload);
|
|
3479
|
+
} catch {
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3482
|
+
if (!/^[^@\s<>?#[\]\u0000-\u001f\u007f]+@[^@\s<>?#[\]\u0000-\u001f\u007f]+\.[^@\s<>?#[\]\u0000-\u001f\u007f]+$/u.test(email)) return;
|
|
3483
|
+
anchor.attr("href", `mailto:${email}`);
|
|
3484
|
+
if (/^\[email\s+protected\]$/i.test(element.text().trim())) element.text(email);
|
|
3485
|
+
element.removeAttr("data-cfemail");
|
|
3486
|
+
});
|
|
3487
|
+
}
|
|
3488
|
+
function hasCompleteCapturedPanel($, panel) {
|
|
3489
|
+
const busy = '[aria-busy="true"],[role="progressbar"]';
|
|
3490
|
+
if (panel.is(busy) || panel.find(busy).length) return false;
|
|
3491
|
+
const copy = panel.clone();
|
|
3492
|
+
copy.find('script,style,button,[role="tablist"]').remove();
|
|
3493
|
+
return Boolean(copy.text().trim() || copy.find("img[src],svg").length);
|
|
3494
|
+
}
|
|
3495
|
+
function projectCapturedDisclosures($, root) {
|
|
3496
|
+
const ids = /* @__PURE__ */ new Map();
|
|
3497
|
+
root.find("[id]").each((_index, node) => {
|
|
3498
|
+
const id = $(node).attr("id");
|
|
3499
|
+
ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
|
|
3500
|
+
});
|
|
3501
|
+
root.find("button[aria-expanded][aria-controls],summary[aria-expanded][aria-controls]").each((_index, node) => {
|
|
3502
|
+
const control = $(node);
|
|
3503
|
+
if (control.is('[role="tab"],[aria-haspopup],[aria-busy="true"]')) return;
|
|
3504
|
+
const id = control.attr("id"), target2 = control.attr("aria-controls");
|
|
3505
|
+
const panel = target2 ? ids.get(target2) : void 0;
|
|
3506
|
+
const nativeSummary = control.is("summary") && control.parent().is("details");
|
|
3507
|
+
let owner = control.parent();
|
|
3508
|
+
if (owner.is("h1,h2,h3,h4,h5,h6")) owner = owner.parent();
|
|
3509
|
+
const labels2 = panel?.attr("aria-labelledby")?.trim().split(/\s+/);
|
|
3510
|
+
const ownedLabels = labels2?.every((label) => ids.get(label)?.length === 1 && (label === id || nativeSummary && control.find("[id]").toArray().some((element) => $(element).attr("id") === label)));
|
|
3511
|
+
if (!nativeSummary && !id || id && ids.get(id)?.length !== 1 || !panel || panel.length !== 1 || panel.attr("role") !== "region" && !(nativeSummary && panel.is('[data-component-part="expandable-content"]')) || labels2 && !ownedLabels || !panel.parents().toArray().some((parent) => parent === owner[0]) || nativeSummary && panel.parent()[0] !== owner[0] || !hasCompleteCapturedPanel($, panel)) return;
|
|
3512
|
+
if (!nativeSummary) node.name = "span";
|
|
3513
|
+
control.removeAttr("aria-expanded aria-controls role type");
|
|
3514
|
+
panel.removeAttr("hidden aria-hidden");
|
|
3515
|
+
});
|
|
3516
|
+
}
|
|
3517
|
+
function projectCapturedTabs($, root) {
|
|
3518
|
+
root.find("[data-thally-static-tabs],[data-thally-static-tab]").removeAttr("data-thally-static-tabs data-thally-static-tab");
|
|
3519
|
+
const ids = /* @__PURE__ */ new Map();
|
|
3520
|
+
root.find("[id]").each((_index, node) => {
|
|
3521
|
+
const id = $(node).attr("id");
|
|
3522
|
+
ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
|
|
3523
|
+
});
|
|
3524
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
3525
|
+
const groups = root.find('[role="tablist"]').toArray();
|
|
3526
|
+
if (groups.length > 256 || root.find('[role="tab"]').length > 1024) return;
|
|
3527
|
+
for (const groupNode of groups.reverse()) {
|
|
3528
|
+
const group = $(groupNode);
|
|
3529
|
+
const tabs = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === groupNode);
|
|
3530
|
+
if (!tabs.length || group.is('[aria-busy="true"]')) continue;
|
|
3531
|
+
if (group.text().replace(/\s+/g, "") !== tabs.map((node) => $(node).text()).join("").replace(/\s+/g, "")) continue;
|
|
3532
|
+
let container = group.parent();
|
|
3533
|
+
const explicit = tabs.every((tab) => Boolean($(tab).attr("aria-controls")));
|
|
3534
|
+
const panels = [];
|
|
3535
|
+
if (explicit) {
|
|
3536
|
+
for (const tab of tabs) {
|
|
3537
|
+
const panel = ids.get($(tab).attr("aria-controls"));
|
|
3538
|
+
const tabId = $(tab).attr("id");
|
|
3539
|
+
if (!panel || panel.length !== 1 || !panel.is('[role="tabpanel"]') || panel.find('[role="tablist"]').toArray().includes(groupNode) || tabId && ids.get(tabId)?.length !== 1 || panel.attr("aria-labelledby") !== void 0 && panel.attr("aria-labelledby") !== tabId) break;
|
|
3540
|
+
panels.push(panel);
|
|
3541
|
+
}
|
|
3542
|
+
for (let depth = 0; depth < 16 && container.length && panels.some((panel) => !panel.parents().toArray().some((parent) => parent === container[0])); depth += 1) container = container.parent();
|
|
3543
|
+
if (!container.length || !container.find('[role="tablist"]').toArray().includes(groupNode)) continue;
|
|
3544
|
+
} else {
|
|
3545
|
+
if (tabs.some((tab) => $(tab).attr("aria-controls"))) continue;
|
|
3546
|
+
container = group.closest(".code-group,.tabs-container,.tabs.tab-container");
|
|
3547
|
+
if (!container.length) continue;
|
|
3548
|
+
const owners = container.find('[role="tablist"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
|
|
3549
|
+
if (owners.length !== 1) continue;
|
|
3550
|
+
panels.push(...container.find('[role="tabpanel"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]).map((node) => $(node)));
|
|
3551
|
+
}
|
|
3552
|
+
if (panels.length !== tabs.length || new Set(panels.map((panel) => panel[0])).size !== panels.length || panels.some((panel) => claimed.has(panel[0]) || !hasCompleteCapturedPanel($, panel))) continue;
|
|
3553
|
+
const unowned = container.find('[role="tabpanel"]').toArray().filter((node) => !panels.some((panel) => panel[0] === node || $(node).parents().toArray().some((parent) => parent === panel[0])));
|
|
3554
|
+
if (unowned.length) continue;
|
|
3555
|
+
const residual = container.clone();
|
|
3556
|
+
residual.find('[role="tablist"],[role="tabpanel"]').remove();
|
|
3557
|
+
const hasSurroundingContent = Boolean(residual.text().trim() || residual.find("a[href],pre,img,svg,table,video,audio,iframe,form,input,textarea,select,object,embed,canvas,math").length);
|
|
3558
|
+
if (!hasSurroundingContent) container.attr("data-thally-static-tabs", "");
|
|
3559
|
+
panels.forEach((panel, index) => {
|
|
3560
|
+
claimed.add(panel[0]);
|
|
3561
|
+
const label = $(tabs[index]).text().trim() || `Tab ${index + 1}`;
|
|
3562
|
+
if (hasSurroundingContent) panel.prepend($("<p></p>").append($("<strong></strong>").text(label)));
|
|
3563
|
+
else panel.attr("data-thally-static-tab", label);
|
|
3564
|
+
panel.removeAttr("role hidden aria-hidden aria-labelledby");
|
|
3565
|
+
const id = $(tabs[index]).attr("id");
|
|
3566
|
+
if (id) panel.prepend($("<span></span>").attr("id", id));
|
|
3567
|
+
});
|
|
3568
|
+
const groupId = group.attr("id");
|
|
3569
|
+
if (groupId) group.replaceWith($("<span></span>").attr("id", groupId));
|
|
3570
|
+
else group.remove();
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
|
|
2404
3574
|
// src/url.ts
|
|
2405
|
-
var MAX_LOCAL_RESPONSE_BYTES =
|
|
3575
|
+
var MAX_LOCAL_RESPONSE_BYTES = 8e6;
|
|
3576
|
+
var MAX_EMBEDDED_SCHEMA_BYTES = 2e6;
|
|
2406
3577
|
var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
2407
3578
|
"Accordion",
|
|
2408
3579
|
"AccordionGroup",
|
|
@@ -2470,16 +3641,17 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
2470
3641
|
"Warning",
|
|
2471
3642
|
"YouTube",
|
|
2472
3643
|
"picture",
|
|
2473
|
-
"source"
|
|
3644
|
+
"source",
|
|
3645
|
+
"video"
|
|
2474
3646
|
]);
|
|
2475
|
-
var PORTABLE_URL_PROPS = "href|src|img|primaryHref|secondaryHref";
|
|
3647
|
+
var PORTABLE_URL_PROPS = "href|src|poster|img|primaryHref|secondaryHref";
|
|
2476
3648
|
var PORTABLE_URL_PROP_NAMES = new Set(PORTABLE_URL_PROPS.split("|"));
|
|
2477
3649
|
var MINTLIFY_ARTICLE_LABELS = [
|
|
2478
3650
|
{ baseUrl: "Select base URL", permalink: "Navigate to header" },
|
|
2479
3651
|
{ baseUrl: "\u9009\u62E9\u57FA\u7840URL", permalink: "\u5BFC\u822A\u5230\u6807\u9898" }
|
|
2480
3652
|
];
|
|
2481
|
-
var remoteMarkdownParser =
|
|
2482
|
-
var embeddedOpenApiParser =
|
|
3653
|
+
var remoteMarkdownParser = unified3().use(remarkParse3).use(remarkMdx3);
|
|
3654
|
+
var embeddedOpenApiParser = unified3().use(remarkParse3);
|
|
2483
3655
|
function validateMigrationUrl(value) {
|
|
2484
3656
|
let url;
|
|
2485
3657
|
try {
|
|
@@ -2496,7 +3668,7 @@ function validateMigrationUrl(value) {
|
|
|
2496
3668
|
}
|
|
2497
3669
|
async function readResponseBody(response) {
|
|
2498
3670
|
const contentLength = Number(response.headers.get("content-length") ?? 0);
|
|
2499
|
-
if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the
|
|
3671
|
+
if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the 8 MB import limit.");
|
|
2500
3672
|
if (!response.body) return "";
|
|
2501
3673
|
const reader = response.body.getReader();
|
|
2502
3674
|
const chunks = [];
|
|
@@ -2507,7 +3679,7 @@ async function readResponseBody(response) {
|
|
|
2507
3679
|
size += value.byteLength;
|
|
2508
3680
|
if (size > MAX_LOCAL_RESPONSE_BYTES) {
|
|
2509
3681
|
await reader.cancel();
|
|
2510
|
-
throw new Error("A documentation response exceeded the
|
|
3682
|
+
throw new Error("A documentation response exceeded the 8 MB import limit.");
|
|
2511
3683
|
}
|
|
2512
3684
|
chunks.push(value);
|
|
2513
3685
|
}
|
|
@@ -2612,7 +3784,7 @@ function isSafePortableUrl(value) {
|
|
|
2612
3784
|
}
|
|
2613
3785
|
}
|
|
2614
3786
|
function decodeHtmlAttribute(value) {
|
|
2615
|
-
const document =
|
|
3787
|
+
const document = load5(`<i data-thally-value="${value.replace(/"/g, """)}"></i>`);
|
|
2616
3788
|
return document("i").attr("data-thally-value") ?? "";
|
|
2617
3789
|
}
|
|
2618
3790
|
function escapeMarkdownText(value, escapeTableSeparator = false) {
|
|
@@ -2698,7 +3870,7 @@ function replacePortableHtmlImages(value) {
|
|
|
2698
3870
|
if (start < cursor) continue;
|
|
2699
3871
|
const end = htmlOpeningTagEnd(value, start + match[0].length);
|
|
2700
3872
|
if (end === null) continue;
|
|
2701
|
-
const fragment =
|
|
3873
|
+
const fragment = load5(value.slice(start, end));
|
|
2702
3874
|
const image = fragment("img").first();
|
|
2703
3875
|
const source = image.attr("src");
|
|
2704
3876
|
const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
|
|
@@ -2723,7 +3895,7 @@ function replacePortableHtmlAnchors(value) {
|
|
|
2723
3895
|
const closing = closingMatch.exec(value);
|
|
2724
3896
|
if (!closing) continue;
|
|
2725
3897
|
const end = closing.index + closing[0].length;
|
|
2726
|
-
const fragment =
|
|
3898
|
+
const fragment = load5(value.slice(start, end));
|
|
2727
3899
|
const anchor = fragment("a").first();
|
|
2728
3900
|
const href = anchor.attr("href");
|
|
2729
3901
|
const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
|
|
@@ -2758,6 +3930,10 @@ function sanitizeMarkdownUrls(value) {
|
|
|
2758
3930
|
hasUnsafeMdxSyntax = true;
|
|
2759
3931
|
return;
|
|
2760
3932
|
}
|
|
3933
|
+
if (node.name === "video" && node.children?.length) {
|
|
3934
|
+
hasUnsafeMdxSyntax = true;
|
|
3935
|
+
return;
|
|
3936
|
+
}
|
|
2761
3937
|
for (const attribute of node.attributes ?? []) {
|
|
2762
3938
|
if (attribute.type !== "mdxJsxAttribute" || !attribute.name || /^on[A-Z]/.test(attribute.name)) {
|
|
2763
3939
|
hasUnsafeMdxSyntax = true;
|
|
@@ -2794,6 +3970,22 @@ function sanitizeMarkdownUrls(value) {
|
|
|
2794
3970
|
return;
|
|
2795
3971
|
}
|
|
2796
3972
|
}
|
|
3973
|
+
if (node.name === "video") {
|
|
3974
|
+
const isBoolean = ["controls", "muted", "autoPlay", "loop", "playsInline"].includes(attribute.name);
|
|
3975
|
+
const isText = ["src", "poster", "title"].includes(attribute.name);
|
|
3976
|
+
if (isBoolean && typeof staticValue !== "boolean" || isText && typeof attribute.value !== "string" || !isBoolean && !isText) {
|
|
3977
|
+
hasUnsafeMdxSyntax = true;
|
|
3978
|
+
return;
|
|
3979
|
+
}
|
|
3980
|
+
if (attribute.name === "src" || attribute.name === "poster") {
|
|
3981
|
+
try {
|
|
3982
|
+
migrationImageUrl(decodeHtmlAttribute(attribute.value), new URL("https://migration.invalid"));
|
|
3983
|
+
} catch {
|
|
3984
|
+
hasUnsafeMdxSyntax = true;
|
|
3985
|
+
return;
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
2797
3989
|
}
|
|
2798
3990
|
}
|
|
2799
3991
|
for (const child of node.children ?? []) validateMdxSyntax(child);
|
|
@@ -2906,7 +4098,7 @@ function sanitizeRemoteMarkdown(body) {
|
|
|
2906
4098
|
const urlSafeMarkdown = sanitizeMarkdownUrls(sanitized);
|
|
2907
4099
|
if (urlSafeMarkdown === null) return null;
|
|
2908
4100
|
const executable = withoutFencedCode(urlSafeMarkdown).replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/<(?:https?:\/\/|mailto:)[^>]+>/gi, "").replace(/(`+)[\s\S]*?\1/g, "");
|
|
2909
|
-
if (
|
|
4101
|
+
if (/^[\t ]*(?:import|export)[\t ]/m.test(executable)) return null;
|
|
2910
4102
|
let hasUnsafeExpression = false;
|
|
2911
4103
|
const withoutStaticProps = executable.replace(/=\{([^{}\n]*)\}/g, (original, value) => {
|
|
2912
4104
|
try {
|
|
@@ -2959,12 +4151,12 @@ function stripMintlifyDocumentationIndex(body) {
|
|
|
2959
4151
|
return lines.slice(index).join("\n");
|
|
2960
4152
|
}
|
|
2961
4153
|
function extractEmbeddedOpenApi(content) {
|
|
2962
|
-
if (Buffer.byteLength(content, "utf8") >
|
|
4154
|
+
if (Buffer.byteLength(content, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
|
|
2963
4155
|
try {
|
|
2964
4156
|
const root = embeddedOpenApiParser.parse(content);
|
|
2965
4157
|
const candidates = (root.children ?? []).flatMap((node2) => {
|
|
2966
4158
|
if (node2.type !== "code" || !/^(?:yaml|yml|json)$/i.test(node2.lang ?? "")) return [];
|
|
2967
|
-
const metadata = node2.meta?.match(/^(?:(
|
|
4159
|
+
const metadata = node2.meta?.match(/^(?:((?:\S+\.(?:yaml|yml|json)|[A-Za-z][A-Za-z0-9_-]{0,127}))\s+)?(delete|get|head|options|patch|post|put|trace)\s+(\/\S+)\s*$/i);
|
|
2968
4160
|
return metadata ? [{ node: node2, method: metadata[2].toUpperCase(), path: metadata[3] }] : [];
|
|
2969
4161
|
});
|
|
2970
4162
|
if (candidates.length !== 1) return { content };
|
|
@@ -2992,10 +4184,10 @@ function extractEmbeddedOpenApi(content) {
|
|
|
2992
4184
|
const serialized = JSON.stringify(document, (_key, value) => {
|
|
2993
4185
|
values += 1;
|
|
2994
4186
|
if (typeof value === "string") textLength += value.length;
|
|
2995
|
-
if (values > 1e5 || textLength >
|
|
4187
|
+
if (values > 1e5 || textLength > MAX_EMBEDDED_SCHEMA_BYTES || typeof value === "number" && !Number.isFinite(value)) throw new Error("OpenAPI document exceeds portable data limits.");
|
|
2996
4188
|
return value;
|
|
2997
4189
|
});
|
|
2998
|
-
if (Buffer.byteLength(serialized, "utf8") >
|
|
4190
|
+
if (Buffer.byteLength(serialized, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
|
|
2999
4191
|
const before = content.slice(0, start).replace(/(?:^|\n)##\s+OpenAPI\s*\n\s*$/i, "\n");
|
|
3000
4192
|
const after = content.slice(end);
|
|
3001
4193
|
return {
|
|
@@ -3049,8 +4241,12 @@ function markdownPage(document, id, platform, warn) {
|
|
|
3049
4241
|
};
|
|
3050
4242
|
}
|
|
3051
4243
|
function migrationHtmlArticle(body) {
|
|
3052
|
-
const $ =
|
|
4244
|
+
const $ = load5(body);
|
|
3053
4245
|
const root = ["main article", "article", "main", '[role="main"]', "body"].map((selector) => $(selector).first()).find((candidate) => candidate.length);
|
|
4246
|
+
projectProtectedEmails($, root);
|
|
4247
|
+
recoverFlightArticleCode($, root);
|
|
4248
|
+
const assets = [];
|
|
4249
|
+
const svgProjections = new Map(root.find("svg").toArray().map((element) => [element, projectMigrationSvg($.html(element))]));
|
|
3054
4250
|
root.find([
|
|
3055
4251
|
"script",
|
|
3056
4252
|
"style",
|
|
@@ -3067,7 +4263,10 @@ function migrationHtmlArticle(body) {
|
|
|
3067
4263
|
"#chat-assistant-sheet",
|
|
3068
4264
|
"#chat-assistant-textarea"
|
|
3069
4265
|
].join(",")).remove();
|
|
3070
|
-
|
|
4266
|
+
root.find('div[data-component-part="code-group-tab-content"]').each((_index, element) => {
|
|
4267
|
+
if ($(element).parents("pre").length === 0 && $(element).find("pre").length === 0) element.name = "pre";
|
|
4268
|
+
});
|
|
4269
|
+
const permalinkSelectors = MINTLIFY_ARTICLE_LABELS.map((labels2) => `a[aria-label="${labels2.permalink}"]`);
|
|
3071
4270
|
root.find([...permalinkSelectors, "h1 .hash-link", "h2 .hash-link", "h3 .hash-link", "h4 .hash-link", "h5 .hash-link", "h6 .hash-link"].join(",")).each((_index, link) => {
|
|
3072
4271
|
const parent = $(link).parent();
|
|
3073
4272
|
$(link).remove();
|
|
@@ -3078,7 +4277,7 @@ function migrationHtmlArticle(body) {
|
|
|
3078
4277
|
const isChecked = $(element).attr("checked") !== void 0;
|
|
3079
4278
|
$(element).replaceWith($("<span></span>").attr("data-thally-static-task-checkbox", isChecked ? "checked" : "unchecked").text(isChecked ? "[x] " : "[ ] "));
|
|
3080
4279
|
});
|
|
3081
|
-
root.find(MINTLIFY_ARTICLE_LABELS.map((
|
|
4280
|
+
root.find(MINTLIFY_ARTICLE_LABELS.map((labels2) => `select[aria-label="${labels2.baseUrl}"]`).join(",")).each((_index, element) => {
|
|
3082
4281
|
const select = $(element);
|
|
3083
4282
|
const options = select.children("option").toArray();
|
|
3084
4283
|
if (!options.length || options.length !== select.children().length) return;
|
|
@@ -3108,16 +4307,31 @@ function migrationHtmlArticle(body) {
|
|
|
3108
4307
|
projection.append(list);
|
|
3109
4308
|
select.replaceWith(projection);
|
|
3110
4309
|
});
|
|
3111
|
-
|
|
4310
|
+
projectCapturedDisclosures($, root);
|
|
4311
|
+
root.find("svg").each((_index, element) => {
|
|
4312
|
+
const svg = $(element);
|
|
4313
|
+
if (svg.parents("svg,button,nav,footer,aside").length) return;
|
|
4314
|
+
const projection = svgProjections.get(element);
|
|
4315
|
+
if (!projection) return;
|
|
4316
|
+
const image = $("<img>").attr("src", `/${projection.asset.path}`).attr("alt", projection.alternative);
|
|
4317
|
+
for (const attribute of ["id", "width", "height"]) {
|
|
4318
|
+
const value = svg.attr(attribute);
|
|
4319
|
+
if (value !== void 0) image.attr(attribute, value);
|
|
4320
|
+
}
|
|
4321
|
+
assets.push(projection.asset);
|
|
4322
|
+
svg.replaceWith(image);
|
|
4323
|
+
});
|
|
4324
|
+
projectCapturedTabs($, root);
|
|
4325
|
+
return { $, root, assets };
|
|
3112
4326
|
}
|
|
3113
4327
|
function htmlPage(document, id, options = {}) {
|
|
3114
|
-
const { $, root } = migrationHtmlArticle(document.body);
|
|
4328
|
+
const { $, root, assets } = migrationHtmlArticle(document.body);
|
|
3115
4329
|
const links = root.find("a[href]").map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
|
|
3116
4330
|
const scopedTitle = options.omitDocumentHeading ? root.find("h1").first().text().trim() : "";
|
|
3117
4331
|
const title = scopedTitle || $("main h1, article h1, h1").first().text().trim() || $('meta[property="og:title"]').attr("content")?.trim() || $("title").text().split("|")[0].trim();
|
|
3118
4332
|
const description = $('meta[name="description"]').attr("content")?.trim() || $('meta[property="og:description"]').attr("content")?.trim();
|
|
3119
4333
|
let documentHeadingAnchor = "";
|
|
3120
|
-
const excludedElements = options.omitDocumentHeading ? "script,style,noscript,nav,footer,aside,form,button,svg,
|
|
4334
|
+
const excludedElements = options.omitDocumentHeading ? "script,style,noscript,nav,footer,aside,form,button,svg,object,embed,link,meta" : "script,style,noscript,nav,header,footer,aside,form,button,svg,object,embed,link,meta";
|
|
3121
4335
|
root.find(excludedElements).remove();
|
|
3122
4336
|
root.find(".theme-doc-version-badge,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
|
|
3123
4337
|
if (options.omitDocumentHeading) {
|
|
@@ -3150,6 +4364,7 @@ function htmlPage(document, id, options = {}) {
|
|
|
3150
4364
|
root.find("img[src]").each((_index, element) => {
|
|
3151
4365
|
const src = $(element).attr("src");
|
|
3152
4366
|
if (!src) return;
|
|
4367
|
+
if (assets.some((asset) => src === `/${asset.path}`)) return;
|
|
3153
4368
|
try {
|
|
3154
4369
|
const resolved = new URL(src, document.finalUrl);
|
|
3155
4370
|
if (["http:", "https:"].includes(resolved.protocol)) {
|
|
@@ -3161,7 +4376,15 @@ function htmlPage(document, id, options = {}) {
|
|
|
3161
4376
|
$(element).removeAttr("src");
|
|
3162
4377
|
}
|
|
3163
4378
|
});
|
|
3164
|
-
const turndown = new TurndownService({
|
|
4379
|
+
const turndown = new TurndownService({
|
|
4380
|
+
codeBlockStyle: "fenced",
|
|
4381
|
+
emDelimiter: "_",
|
|
4382
|
+
headingStyle: "atx",
|
|
4383
|
+
preformattedCode: true,
|
|
4384
|
+
// Turndown chooses its blank rule before custom rules. Empty source anchor
|
|
4385
|
+
// leaves still carry navigation identity, including captured tab controls.
|
|
4386
|
+
blankReplacement: (_content, node) => options.preserveAnchors ? anchor(node) : ""
|
|
4387
|
+
});
|
|
3165
4388
|
let isConvertingTableCell = false;
|
|
3166
4389
|
turndown.remove(["script", "style", "noscript"]);
|
|
3167
4390
|
turndown.addRule("migration-literal-table-code", {
|
|
@@ -3175,6 +4398,18 @@ function htmlPage(document, id, options = {}) {
|
|
|
3175
4398
|
// Emit literal task-list syntax rather than escaping its brackets as copy.
|
|
3176
4399
|
replacement: (_content, node) => node.getAttribute("data-thally-static-task-checkbox") === "checked" ? "[x] " : "[ ] "
|
|
3177
4400
|
});
|
|
4401
|
+
turndown.addRule("migration-authored-paragraph", {
|
|
4402
|
+
filter: (node) => node.nodeName === "SPAN" && node.getAttribute("data-as") === "p",
|
|
4403
|
+
// Some publishers render authored paragraphs as spans. Their explicit
|
|
4404
|
+
// semantic hint must retain a block boundary: otherwise following copy
|
|
4405
|
+
// joins a picture's closing tag or an accordion's source anchor and can
|
|
4406
|
+
// change the resulting MDX grammar. Ordinary spans remain inline.
|
|
4407
|
+
replacement: (content) => `
|
|
4408
|
+
|
|
4409
|
+
${content}
|
|
4410
|
+
|
|
4411
|
+
`
|
|
4412
|
+
});
|
|
3178
4413
|
const anchor = (element) => {
|
|
3179
4414
|
const value = element.getAttribute("id");
|
|
3180
4415
|
return options.preserveAnchors && value ? `<a id="${value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/\{/g, "{")}" />
|
|
@@ -3193,6 +4428,34 @@ ${anchor(element)}${heading ? `${"#".repeat(Number(heading[1]))} ${content.trim(
|
|
|
3193
4428
|
`;
|
|
3194
4429
|
}
|
|
3195
4430
|
});
|
|
4431
|
+
turndown.addRule("migration-flow-link", {
|
|
4432
|
+
filter: (node) => node.nodeName === "A" && node.hasAttribute("href") && Boolean(node.querySelector("div,p,picture,figure,blockquote,pre,table,ul,ol,h1,h2,h3,h4,h5,h6")),
|
|
4433
|
+
replacement: (content, node) => {
|
|
4434
|
+
const element = node;
|
|
4435
|
+
const href = migrationImageAttribute(element.getAttribute("href") ?? "");
|
|
4436
|
+
return `
|
|
4437
|
+
|
|
4438
|
+
${anchor(element)}<Card href="${href}">
|
|
4439
|
+
|
|
4440
|
+
${content.trim()}
|
|
4441
|
+
|
|
4442
|
+
</Card>
|
|
4443
|
+
|
|
4444
|
+
`;
|
|
4445
|
+
}
|
|
4446
|
+
});
|
|
4447
|
+
turndown.addRule("migration-static-media", {
|
|
4448
|
+
filter: (node) => node.nodeName === "VIDEO" || node.nodeName === "IFRAME",
|
|
4449
|
+
replacement: (_content, node) => {
|
|
4450
|
+
const element = node;
|
|
4451
|
+
const projected = projectMigrationMedia(element.outerHTML, document.finalUrl);
|
|
4452
|
+
return projected ? `
|
|
4453
|
+
|
|
4454
|
+
${anchor(element)}${projected}
|
|
4455
|
+
|
|
4456
|
+
` : "";
|
|
4457
|
+
}
|
|
4458
|
+
});
|
|
3196
4459
|
if (options.preserveResponsiveImages) {
|
|
3197
4460
|
const image = (element) => {
|
|
3198
4461
|
const tag = element.nodeName.toLowerCase();
|
|
@@ -3228,7 +4491,7 @@ ${content.trim()}
|
|
|
3228
4491
|
const element = node;
|
|
3229
4492
|
const className = element.className;
|
|
3230
4493
|
const tag = /admonition-(?:danger)/.test(className) ? "Error" : /admonition-(?:caution|warning)/.test(className) ? "Warning" : /admonition-info/.test(className) ? "Info" : "Note";
|
|
3231
|
-
const fragment =
|
|
4494
|
+
const fragment = load5(element.outerHTML);
|
|
3232
4495
|
const clone = fragment(".theme-admonition").first();
|
|
3233
4496
|
const title2 = clone.find('[class*="admonitionHeading"],.admonition-heading').first();
|
|
3234
4497
|
const titleText = title2.text().trim();
|
|
@@ -3249,15 +4512,19 @@ ${titleText ? `**${escapeMarkdownText(titleText)}**
|
|
|
3249
4512
|
filter: (node) => node.nodeName === "DIV" && node.classList.contains("tabs-container"),
|
|
3250
4513
|
replacement: (_content, node) => {
|
|
3251
4514
|
const element = node;
|
|
3252
|
-
const fragment =
|
|
4515
|
+
const fragment = load5(element.outerHTML);
|
|
3253
4516
|
const container = fragment(".tabs-container").first();
|
|
3254
|
-
const
|
|
4517
|
+
const labels2 = container.children("ul.tabs").children("li").toArray().map((item) => fragment(item).text().trim() || "Tab");
|
|
3255
4518
|
const panels = container.find('[role="tabpanel"]').toArray().filter((panel) => fragment(panel).closest(".tabs-container")[0] === container[0]);
|
|
3256
4519
|
if (panels.length === 0) return _content;
|
|
4520
|
+
const residual = container.clone();
|
|
4521
|
+
residual.children("ul.tabs").remove();
|
|
4522
|
+
residual.find('[role="tabpanel"]').remove();
|
|
4523
|
+
if (residual.text().trim() || residual.find("a[href],pre,img,svg,table,video,iframe,input,select,textarea").length) return _content;
|
|
3257
4524
|
const tabs = panels.map((panel, index) => {
|
|
3258
4525
|
const panelId = fragment(panel).attr("id");
|
|
3259
4526
|
const tab = panelId ? container.find('[role="tab"]').toArray().find((item) => fragment(item).attr("aria-controls") === panelId) : void 0;
|
|
3260
|
-
const title2 = migrationImageAttribute((tab ? fragment(tab).text().trim() :
|
|
4527
|
+
const title2 = migrationImageAttribute((tab ? fragment(tab).text().trim() : labels2[index]) || `Tab ${index + 1}`);
|
|
3261
4528
|
const panelAnchor = options.preserveAnchors && panelId ? `<a id="${migrationImageAttribute(panelId)}" />
|
|
3262
4529
|
|
|
3263
4530
|
` : "";
|
|
@@ -3271,13 +4538,40 @@ ${anchor(element)}<Tabs>
|
|
|
3271
4538
|
${tabs.join("\n\n")}
|
|
3272
4539
|
</Tabs>
|
|
3273
4540
|
|
|
4541
|
+
`;
|
|
4542
|
+
}
|
|
4543
|
+
});
|
|
4544
|
+
turndown.addRule("migration-complete-captured-tabs", {
|
|
4545
|
+
filter: (node) => node.hasAttribute("data-thally-static-tabs"),
|
|
4546
|
+
replacement: (_content, node) => {
|
|
4547
|
+
const element = node;
|
|
4548
|
+
const fragment = load5(element.outerHTML);
|
|
4549
|
+
const container = fragment("[data-thally-static-tabs]").first();
|
|
4550
|
+
const panels = container.find("[data-thally-static-tab]").toArray().filter((panel) => fragment(panel).closest("[data-thally-static-tabs]")[0] === container[0]);
|
|
4551
|
+
const tabs = panels.map((panel) => {
|
|
4552
|
+
const selection = fragment(panel);
|
|
4553
|
+
const title2 = migrationImageAttribute(selection.attr("data-thally-static-tab"));
|
|
4554
|
+
const id2 = selection.attr("id");
|
|
4555
|
+
const panelAnchor = options.preserveAnchors && id2 ? `<a id="${migrationImageAttribute(id2)}" />
|
|
4556
|
+
|
|
4557
|
+
` : "";
|
|
4558
|
+
return `<Tab title="${title2}">
|
|
4559
|
+
${panelAnchor}${turndown.turndown(selection.html() ?? "").trim()}
|
|
4560
|
+
</Tab>`;
|
|
4561
|
+
});
|
|
4562
|
+
return `
|
|
4563
|
+
|
|
4564
|
+
${anchor(element)}<Tabs>
|
|
4565
|
+
${tabs.join("\n\n")}
|
|
4566
|
+
</Tabs>
|
|
4567
|
+
|
|
3274
4568
|
`;
|
|
3275
4569
|
}
|
|
3276
4570
|
});
|
|
3277
4571
|
turndown.addRule("gfm-table", {
|
|
3278
4572
|
filter: "table",
|
|
3279
4573
|
replacement: (_content, node) => {
|
|
3280
|
-
const fragment =
|
|
4574
|
+
const fragment = load5(node.outerHTML);
|
|
3281
4575
|
const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => {
|
|
3282
4576
|
const wasConvertingTableCell = isConvertingTableCell;
|
|
3283
4577
|
isConvertingTableCell = true;
|
|
@@ -3303,7 +4597,7 @@ ${normalized.slice(1).map((row) => `| ${row.join(" | ")} |`).join("\n")}
|
|
|
3303
4597
|
filter: (node) => node.nodeName === "PRE",
|
|
3304
4598
|
replacement: (_content, node) => {
|
|
3305
4599
|
const element = node;
|
|
3306
|
-
const fragment =
|
|
4600
|
+
const fragment = load5(element.outerHTML);
|
|
3307
4601
|
const tokenLines = fragment(".token-line").toArray();
|
|
3308
4602
|
const code = (tokenLines.length > 0 ? tokenLines.map((line) => fragment(line).text()).join("\n") : fragment("pre").text()).replace(/\n$/, "");
|
|
3309
4603
|
const language = ["code", "pre"].flatMap((selector) => {
|
|
@@ -3328,7 +4622,7 @@ ${fence}
|
|
|
3328
4622
|
const escape = turndown.escape.bind(turndown);
|
|
3329
4623
|
turndown.escape = (text2) => isConvertingTableCell ? escape(text2.replace(/[<>{}]/g, (character) => ({ "<": "<", ">": ">", "{": "{", "}": "}" })[character])) : escape(text2).replace(/[<{}]/g, (match) => `\\${match}`);
|
|
3330
4624
|
const body = `${documentHeadingAnchor}${turndown.turndown(root.html() ?? "").trim()}`;
|
|
3331
|
-
if (!title || body.length < 40 && $("article").length === 0) return { page: null, links };
|
|
4625
|
+
if (!title || body.length < 40 && $("article").length === 0) return { page: null, links, assets };
|
|
3332
4626
|
return {
|
|
3333
4627
|
page: {
|
|
3334
4628
|
id,
|
|
@@ -3339,20 +4633,564 @@ ${fence}
|
|
|
3339
4633
|
body,
|
|
3340
4634
|
source: document.finalUrl.toString()
|
|
3341
4635
|
},
|
|
3342
|
-
links
|
|
4636
|
+
links,
|
|
4637
|
+
assets
|
|
3343
4638
|
};
|
|
3344
4639
|
}
|
|
3345
4640
|
|
|
4641
|
+
// src/url-identity.ts
|
|
4642
|
+
var directory = (path) => path.replace(/\/index\/?$/i, "").replace(/\/$/, "") || "/";
|
|
4643
|
+
function migrationSectionQueryCandidate(source, anchors) {
|
|
4644
|
+
const target2 = new URL(source);
|
|
4645
|
+
const sections = [...source.searchParams].filter(([, value]) => anchors.includes(value));
|
|
4646
|
+
const section = sections.length === 1 && source.searchParams.getAll(sections[0][0]).length === 1 ? sections[0] : void 0;
|
|
4647
|
+
if (section) target2.searchParams.delete(section[0]);
|
|
4648
|
+
target2.hash = "";
|
|
4649
|
+
target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
|
|
4650
|
+
return { canonicalUrl: target2.href, ...section ? { sectionAnchor: section[1] } : {} };
|
|
4651
|
+
}
|
|
4652
|
+
function isMigrationUtilityUrl(url) {
|
|
4653
|
+
return /^\/cdn-cgi\/l\/email-protection\/?$/i.test(url.pathname);
|
|
4654
|
+
}
|
|
4655
|
+
function declaredMigrationArticleUrl(document) {
|
|
4656
|
+
const target2 = declaredCanonical(document);
|
|
4657
|
+
return target2 && target2.search === document.finalUrl.search ? target2 : document.finalUrl;
|
|
4658
|
+
}
|
|
4659
|
+
function declaredCanonical(document) {
|
|
4660
|
+
if (!/html/i.test(document.contentType)) return void 0;
|
|
4661
|
+
const reference = load6(document.body)('link[rel="canonical"][href]').first().attr("href");
|
|
4662
|
+
if (!reference) return void 0;
|
|
4663
|
+
try {
|
|
4664
|
+
const target2 = new URL(reference, document.finalUrl);
|
|
4665
|
+
if (target2.origin !== document.finalUrl.origin || target2.username || target2.password || directory(target2.pathname) !== directory(document.finalUrl.pathname)) return void 0;
|
|
4666
|
+
target2.hash = "";
|
|
4667
|
+
target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
|
|
4668
|
+
return target2;
|
|
4669
|
+
} catch {
|
|
4670
|
+
return void 0;
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
function observedMigrationArticleIdentity(document) {
|
|
4674
|
+
if (!/html/i.test(document.contentType)) return void 0;
|
|
4675
|
+
const { $, root } = migrationHtmlArticle(document.body);
|
|
4676
|
+
if (!root.length || !root.text().trim()) return void 0;
|
|
4677
|
+
const anchors = root.find("[id]").toArray().map((node) => $(node).attr("id"));
|
|
4678
|
+
root.find("nav,aside,footer,script,style").remove();
|
|
4679
|
+
const metadata = load6(document.body);
|
|
4680
|
+
const payload = JSON.stringify([
|
|
4681
|
+
$.html(root),
|
|
4682
|
+
metadata("title").text(),
|
|
4683
|
+
metadata('meta[name="description"]').attr("content"),
|
|
4684
|
+
metadata("html").attr("lang"),
|
|
4685
|
+
document.locale,
|
|
4686
|
+
document.version,
|
|
4687
|
+
// Deferred content can exist only in inert hydration data. Never erase its
|
|
4688
|
+
// differences merely because two collapsed DOM panels appear identical.
|
|
4689
|
+
metadata("script").toArray().map((node) => [metadata(node).attr("src"), metadata(node).text()]),
|
|
4690
|
+
(document.states ?? []).map((state) => [state.id, state.label, state.response.body])
|
|
4691
|
+
]);
|
|
4692
|
+
const source = document.finalUrl;
|
|
4693
|
+
const canonical2 = declaredCanonical(document) ?? new URL(source);
|
|
4694
|
+
if (canonical2.href === source.href) {
|
|
4695
|
+
canonical2.href = migrationSectionQueryCandidate(source, anchors).canonicalUrl;
|
|
4696
|
+
}
|
|
4697
|
+
canonical2.hash = "";
|
|
4698
|
+
canonical2.pathname = canonical2.pathname.replace(/\/+$/, "") || "/";
|
|
4699
|
+
const removed = [...source.searchParams].filter(([key, value]) => !canonical2.searchParams.getAll(key).includes(value));
|
|
4700
|
+
const sectionAnchor = removed.length === 1 && anchors.includes(removed[0][1]) ? removed[0][1] : void 0;
|
|
4701
|
+
return { canonicalUrl: canonical2.href, articleSha256: createHash3("sha256").update(payload).digest("hex"), ...sectionAnchor ? { sectionAnchor } : {} };
|
|
4702
|
+
}
|
|
4703
|
+
|
|
4704
|
+
// src/url-api-schema-controls.ts
|
|
4705
|
+
var MAX_CAPTURE_BYTES2 = 8e6;
|
|
4706
|
+
var MAX_TAB_GROUPS = 256;
|
|
4707
|
+
var MAX_TABS = 1024;
|
|
4708
|
+
function object2(value) {
|
|
4709
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
4710
|
+
}
|
|
4711
|
+
function localReference(document, reference) {
|
|
4712
|
+
if (typeof reference !== "string" || !reference.startsWith("#/")) return void 0;
|
|
4713
|
+
let target2 = document;
|
|
4714
|
+
try {
|
|
4715
|
+
for (const part of reference.slice(2).split("/")) {
|
|
4716
|
+
const key = decodeURIComponent(part).replace(/~1/g, "/").replace(/~0/g, "~");
|
|
4717
|
+
if (!target2 || typeof target2 !== "object" || !Object.hasOwn(target2, key)) return void 0;
|
|
4718
|
+
target2 = target2[key];
|
|
4719
|
+
}
|
|
4720
|
+
} catch {
|
|
4721
|
+
return void 0;
|
|
4722
|
+
}
|
|
4723
|
+
return target2;
|
|
4724
|
+
}
|
|
4725
|
+
function resolve2(document, value) {
|
|
4726
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4727
|
+
for (let depth = 0; depth < 32; depth += 1) {
|
|
4728
|
+
const schema = object2(value);
|
|
4729
|
+
if (!schema || seen.has(schema)) return void 0;
|
|
4730
|
+
seen.add(schema);
|
|
4731
|
+
if (!Object.hasOwn(schema, "$ref")) return schema;
|
|
4732
|
+
value = localReference(document, schema.$ref);
|
|
4733
|
+
}
|
|
4734
|
+
return void 0;
|
|
4735
|
+
}
|
|
4736
|
+
function complete(document) {
|
|
4737
|
+
let visited = 0;
|
|
4738
|
+
const active = /* @__PURE__ */ new Set();
|
|
4739
|
+
const visit = (value, depth) => {
|
|
4740
|
+
if (++visited > 1e5 || depth > 96) return false;
|
|
4741
|
+
if (typeof value === "number" && !Number.isFinite(value)) return false;
|
|
4742
|
+
if (!value || typeof value !== "object") return true;
|
|
4743
|
+
if (active.has(value)) return false;
|
|
4744
|
+
active.add(value);
|
|
4745
|
+
const schema = object2(value);
|
|
4746
|
+
const valid = (!schema || !Object.hasOwn(schema, "$ref") || Boolean(object2(localReference(document, schema.$ref)))) && Object.values(value).every((child) => visit(child, depth + 1));
|
|
4747
|
+
active.delete(value);
|
|
4748
|
+
return valid;
|
|
4749
|
+
};
|
|
4750
|
+
return visit(document, 0);
|
|
4751
|
+
}
|
|
4752
|
+
function labels(document, value) {
|
|
4753
|
+
const schema = resolve2(document, value);
|
|
4754
|
+
if (!Array.isArray(schema?.oneOf) || schema.oneOf.length < 2 || schema.oneOf.length > 64) return void 0;
|
|
4755
|
+
const result = [];
|
|
4756
|
+
for (const [index, branch] of schema.oneOf.entries()) {
|
|
4757
|
+
const target2 = resolve2(document, branch);
|
|
4758
|
+
if (!target2) return void 0;
|
|
4759
|
+
const title = object2(branch)?.title ?? target2.title;
|
|
4760
|
+
result.push(typeof title === "string" && title.trim() ? title.trim().replace(/\s+/g, " ") : `Option ${index + 1}`);
|
|
4761
|
+
}
|
|
4762
|
+
return new Set(result).size === result.length ? result : void 0;
|
|
4763
|
+
}
|
|
4764
|
+
function apiSchemaTabGroups(body, document, operation) {
|
|
4765
|
+
if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(document)) return [];
|
|
4766
|
+
const path = object2(document.paths)?.[operation.path];
|
|
4767
|
+
const method = operation.method.toLowerCase();
|
|
4768
|
+
if (!/^(?:get|post|put|patch|delete|head|options|trace)$/.test(method)) return [];
|
|
4769
|
+
const selected = object2(object2(path)?.[method]);
|
|
4770
|
+
if (!selected) return [];
|
|
4771
|
+
const { $, root } = migrationHtmlArticle(body);
|
|
4772
|
+
const groups = root.find('[role="tablist"],[data-component-part="tabs-list"]');
|
|
4773
|
+
if (groups.length > MAX_TAB_GROUPS || root.find('[role="tab"]').length > MAX_TABS) return [];
|
|
4774
|
+
const idCounts = /* @__PURE__ */ new Map();
|
|
4775
|
+
root.find("[id]").each((_index, node) => {
|
|
4776
|
+
const id = $(node).attr("id");
|
|
4777
|
+
idCounts.set(id, (idCounts.get(id) ?? 0) + 1);
|
|
4778
|
+
});
|
|
4779
|
+
const proofs = [];
|
|
4780
|
+
groups.each((index, node) => {
|
|
4781
|
+
const group = $(node);
|
|
4782
|
+
const container = group.parent(".tabs.tab-container");
|
|
4783
|
+
const field = group.closest(".object-param-field");
|
|
4784
|
+
const section = field.parent(".api-section");
|
|
4785
|
+
if (!group.is('[role="tablist"][data-component-part="tabs-list"]') || !container.length || !section.length || !section.closest('[id^="api-playground-"][id$="-operation-page"]').length) return;
|
|
4786
|
+
if (field.parents(".object-param-field").length) return;
|
|
4787
|
+
const heading = section.children(".api-section-heading");
|
|
4788
|
+
const title = heading.find(".api-section-heading-title").text().trim().toLowerCase();
|
|
4789
|
+
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));
|
|
4790
|
+
if (heading.length !== 1 || !media || media.length !== 1) return;
|
|
4791
|
+
let schema;
|
|
4792
|
+
let scope;
|
|
4793
|
+
if (title === "body") {
|
|
4794
|
+
const request = resolve2(document, selected.requestBody);
|
|
4795
|
+
schema = object2(object2(request?.content)?.[media[0]])?.schema;
|
|
4796
|
+
scope = `request:${media[0]}`;
|
|
4797
|
+
} else if (title === "response") {
|
|
4798
|
+
const statuses = heading.find('button[aria-haspopup="menu"]').toArray().map((button) => $(button).text().trim()).filter((text2) => /^(?:[1-5]\d{2}|default)$/.test(text2));
|
|
4799
|
+
if (statuses.length !== 1) return;
|
|
4800
|
+
const response = resolve2(document, object2(selected.responses)?.[statuses[0]]);
|
|
4801
|
+
schema = object2(object2(response?.content)?.[media[0]])?.schema;
|
|
4802
|
+
scope = `response:${statuses[0]}:${media[0]}`;
|
|
4803
|
+
} else return;
|
|
4804
|
+
const expected = labels(document, schema);
|
|
4805
|
+
const tabs = group.children('[role="tab"][data-component-part="tab"]');
|
|
4806
|
+
if (!expected || tabs.length !== expected.length || group.children().length !== tabs.length) return;
|
|
4807
|
+
const panels = container.children('[data-component-part="tabs-panels"]').children('[role="tabpanel"][data-component-part="tab-content"]');
|
|
4808
|
+
if (panels.length !== tabs.length) return;
|
|
4809
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4810
|
+
for (const [position, tab] of tabs.toArray().entries()) {
|
|
4811
|
+
const control = $(tab).attr("aria-controls");
|
|
4812
|
+
const panel = panels.eq(position);
|
|
4813
|
+
if ($(tab).text().trim().replace(/\s+/g, " ") !== expected[position] || !control || ids.has(control) || panel.attr("id") !== control || !$(tab).attr("id") || panel.attr("aria-labelledby") !== $(tab).attr("id") || idCounts.get(control) !== 1 || panel.text().trim() || panel.children().length) return;
|
|
4814
|
+
ids.add(control);
|
|
4815
|
+
}
|
|
4816
|
+
proofs.push({ index, scope });
|
|
4817
|
+
});
|
|
4818
|
+
const scopeCounts = /* @__PURE__ */ new Map();
|
|
4819
|
+
for (const proof of proofs) scopeCounts.set(proof.scope, (scopeCounts.get(proof.scope) ?? 0) + 1);
|
|
4820
|
+
return proofs.filter((proof) => scopeCounts.get(proof.scope) === 1).map((proof) => proof.index);
|
|
4821
|
+
}
|
|
4822
|
+
function apiSchemaDisclosureGroups(body, document, operation) {
|
|
4823
|
+
if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(document)) return [];
|
|
4824
|
+
if (!/^(?:get|post|put|patch|delete|head|options|trace)$/i.test(operation.method)) return [];
|
|
4825
|
+
const path = object2(object2(document.paths)?.[operation.path]);
|
|
4826
|
+
const selected = object2(path?.[operation.method.toLowerCase()]);
|
|
4827
|
+
if (!selected) return [];
|
|
4828
|
+
const { $, root } = migrationHtmlArticle(body);
|
|
4829
|
+
const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
|
|
4830
|
+
const item = $(node);
|
|
4831
|
+
const endpoint = item.find('[data-component-part="scroll-area-content"]').first().clone();
|
|
4832
|
+
endpoint.find("[data-thally-static-base-urls]").remove();
|
|
4833
|
+
return item.find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
|
|
4834
|
+
});
|
|
4835
|
+
if (operations.length !== 1) return [];
|
|
4836
|
+
const ids = /* @__PURE__ */ new Map();
|
|
4837
|
+
root.find("[id]").each((_index, node) => {
|
|
4838
|
+
const id = $(node).attr("id");
|
|
4839
|
+
ids.set(id, (ids.get(id) ?? 0) + 1);
|
|
4840
|
+
});
|
|
4841
|
+
if (ids.get(operations.attr("id")) !== 1) return [];
|
|
4842
|
+
let visits = 0;
|
|
4843
|
+
const property = (value, name, active = /* @__PURE__ */ new Set()) => {
|
|
4844
|
+
if (++visits > 1e5 || active.has(value)) return [];
|
|
4845
|
+
const schema = resolve2(document, value);
|
|
4846
|
+
if (!schema) return [];
|
|
4847
|
+
active.add(value);
|
|
4848
|
+
const direct = object2(schema.properties)?.[name];
|
|
4849
|
+
const result = direct ? [direct] : [
|
|
4850
|
+
...schema.items ? property(schema.items, name, active) : [],
|
|
4851
|
+
...["allOf", "oneOf", "anyOf"].flatMap((key) => Array.isArray(schema[key]) ? schema[key].flatMap((child) => property(child, name, active)) : [])
|
|
4852
|
+
];
|
|
4853
|
+
active.delete(value);
|
|
4854
|
+
return result;
|
|
4855
|
+
};
|
|
4856
|
+
const fieldSelector = ".object-param-field,.array-param-field";
|
|
4857
|
+
const proofs = [];
|
|
4858
|
+
if (root.find("details").length > MAX_TABS) return [];
|
|
4859
|
+
root.find("details").each((index, node) => {
|
|
4860
|
+
const details = $(node), control = details.children('summary[data-component-part="expandable-button"]');
|
|
4861
|
+
const panel = details.children('[data-component-part="expandable-content"]');
|
|
4862
|
+
const field = details.closest(fieldSelector), section = field.closest(".api-section");
|
|
4863
|
+
if (!details.is('.expandable[data-component-part="expandable"]') || control.length !== 1 || panel.length !== 1 || details.children().length !== 2 || !field.length || section.closest('[id^="api-playground-"][id$="-operation-page"]')[0] !== operations[0]) return;
|
|
4864
|
+
const target2 = control.attr("aria-controls"), controlId = control.attr("id");
|
|
4865
|
+
if (target2 && (panel.attr("id") !== target2 || ids.get(target2) !== 1) || controlId && ids.get(controlId) !== 1) return;
|
|
4866
|
+
const payload = panel.clone();
|
|
4867
|
+
payload.find("script,style").remove();
|
|
4868
|
+
if (payload.text().trim() || payload.children().length) return;
|
|
4869
|
+
const ancestors = field.parents(fieldSelector).toArray().reverse().filter((owner) => $(owner).is(".array-param-field") || $(owner).find('[data-component-part="expandable-content"]').toArray().some((region) => $(region).closest(fieldSelector)[0] === owner && field.parents().toArray().includes(region)));
|
|
4870
|
+
const chain = [...ancestors, field[0]];
|
|
4871
|
+
const names = chain.map((owner) => $(owner).find('[data-component-part="field-name"]').filter((_index, name) => $(name).closest(fieldSelector)[0] === owner));
|
|
4872
|
+
const hasNamedPath = names.every((name) => name.length === 1 && Boolean(name.text().trim()));
|
|
4873
|
+
const heading = section.children(".api-section-heading");
|
|
4874
|
+
if (heading.length !== 1) return;
|
|
4875
|
+
const leaves = heading.find("*").toArray().filter((element) => $(element).children().length === 0).map((element) => $(element).text().trim());
|
|
4876
|
+
const combined = leaves.flatMap((text2) => {
|
|
4877
|
+
const match = text2.match(/^([1-5]\d{2}|default) - ([a-z][a-z\d.+-]*\/[a-z\d.+-]+)$/i);
|
|
4878
|
+
return match ? [{ status: match[1], media: match[2] }] : [];
|
|
4879
|
+
});
|
|
4880
|
+
const media = heading.find("*").toArray().filter((element) => $(element).children().length === 0).map((element) => $(element).text().trim()).filter((text2) => /^[a-z][a-z\d.+-]*\/[a-z\d.+-]+$/i.test(text2));
|
|
4881
|
+
const statuses = heading.find('button[aria-haspopup="menu"]').toArray().map((button) => $(button).text().trim()).filter((text2) => /^(?:[1-5]\d{2}|default)$/.test(text2));
|
|
4882
|
+
if (combined.length === 1 && !media.length && !statuses.length) {
|
|
4883
|
+
media.push(combined[0].media);
|
|
4884
|
+
statuses.push(combined[0].status);
|
|
4885
|
+
}
|
|
4886
|
+
let schemas = [];
|
|
4887
|
+
let offset = 0;
|
|
4888
|
+
let prefix = "";
|
|
4889
|
+
if (media.length === 1 && statuses.length === 1) {
|
|
4890
|
+
const response = resolve2(document, object2(selected.responses)?.[statuses[0]]);
|
|
4891
|
+
schemas = [object2(object2(response?.content)?.[media[0]])?.schema];
|
|
4892
|
+
prefix = "response";
|
|
4893
|
+
} else if (media.length === 1 && !statuses.length) {
|
|
4894
|
+
const request = resolve2(document, selected.requestBody);
|
|
4895
|
+
schemas = [object2(object2(request?.content)?.[media[0]])?.schema];
|
|
4896
|
+
prefix = "body";
|
|
4897
|
+
} else if (!media.length && !statuses.length) {
|
|
4898
|
+
const head = $(chain[0]).find(".param-head[id]").filter((_index, element) => $(element).closest(fieldSelector)[0] === chain[0]).first().attr("id") ?? "";
|
|
4899
|
+
const location = ["query", "path", "header", "cookie"].find((value) => head.startsWith(`${value}-`));
|
|
4900
|
+
if (names[0]?.length !== 1 || !location && !head.startsWith("parameter-")) return;
|
|
4901
|
+
const parameters = [...Array.isArray(path?.parameters) ? path.parameters : [], ...Array.isArray(selected.parameters) ? selected.parameters : []].map((value) => resolve2(document, value)).filter((parameter) => (!location || parameter?.in === location) && parameter?.name === names[0].text().trim());
|
|
4902
|
+
if (parameters.length !== 1) return;
|
|
4903
|
+
schemas = [parameters[0]?.schema];
|
|
4904
|
+
offset = 1;
|
|
4905
|
+
prefix = $(chain[0]).is(".array-param-field") ? "parameter" : `parameter-${names[0].text().trim()}`;
|
|
4906
|
+
}
|
|
4907
|
+
const expandable = (value) => {
|
|
4908
|
+
const schema = resolve2(document, value);
|
|
4909
|
+
return Boolean(schema && (object2(schema.properties) || object2(schema.items) || object2(schema.additionalProperties) || ["allOf", "oneOf", "anyOf"].some((key) => Array.isArray(schema[key]))));
|
|
4910
|
+
};
|
|
4911
|
+
const roots = schemas;
|
|
4912
|
+
if (hasNamedPath) {
|
|
4913
|
+
for (const name of names.slice(offset)) schemas = schemas.flatMap((schema) => property(schema, name.text().trim()));
|
|
4914
|
+
if (schemas.some(expandable)) {
|
|
4915
|
+
proofs.push(index);
|
|
4916
|
+
return;
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
const marker = details.attr("data-testid");
|
|
4920
|
+
if (!prefix || !marker?.endsWith("-children")) return;
|
|
4921
|
+
const expected = marker.slice(0, -"-children".length);
|
|
4922
|
+
const visibleNames = chain.flatMap((owner) => {
|
|
4923
|
+
const labels2 = $(owner).find('[data-component-part="field-name"]').filter((_index, label) => $(label).closest(fieldSelector)[0] === owner);
|
|
4924
|
+
return labels2.length === 1 ? [labels2.text().trim()] : [];
|
|
4925
|
+
});
|
|
4926
|
+
const matches = /* @__PURE__ */ new Set();
|
|
4927
|
+
const walk = (value, key, pathNames, active = /* @__PURE__ */ new Set()) => {
|
|
4928
|
+
if (++visits > 1e5 || key.length > 2048 || active.has(value)) return;
|
|
4929
|
+
const schema = resolve2(document, value);
|
|
4930
|
+
if (!schema) return;
|
|
4931
|
+
if (key === expected && expandable(schema)) {
|
|
4932
|
+
let cursor = 0;
|
|
4933
|
+
const labelsAgree = visibleNames.every((name) => {
|
|
4934
|
+
const found = pathNames.indexOf(name, cursor);
|
|
4935
|
+
cursor = found + 1;
|
|
4936
|
+
return found >= 0;
|
|
4937
|
+
});
|
|
4938
|
+
if (labelsAgree) matches.add(schema);
|
|
4939
|
+
}
|
|
4940
|
+
if (!expected.startsWith(`${key}-`)) return;
|
|
4941
|
+
active.add(value);
|
|
4942
|
+
for (const [name, child] of Object.entries(object2(schema.properties) ?? {})) {
|
|
4943
|
+
const segments = /* @__PURE__ */ new Set([name, name.replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase()]);
|
|
4944
|
+
for (const segment of segments) walk(child, `${key}-${segment}`, [...pathNames, name], active);
|
|
4945
|
+
}
|
|
4946
|
+
if (schema.items) walk(schema.items, `${key}-items`, pathNames, active);
|
|
4947
|
+
if ((schema.nullable === true || Array.isArray(schema.type) && schema.type.includes("null")) && !schema.oneOf) walk({ ...schema, nullable: false, type: Array.isArray(schema.type) ? schema.type.filter((value2) => value2 !== "null") : schema.type }, `${key}-one-of-0`, pathNames, active);
|
|
4948
|
+
if (Array.isArray(schema.allOf)) schema.allOf.forEach((child) => walk(child, key, pathNames, active));
|
|
4949
|
+
for (const [keyword, segment] of [["allOf", "all-of"], ["oneOf", "one-of"], ["anyOf", "any-of"]]) {
|
|
4950
|
+
const children = schema[keyword];
|
|
4951
|
+
if (Array.isArray(children)) children.forEach((child, branch) => walk(child, `${key}-${segment}-${branch}`, pathNames, active));
|
|
4952
|
+
}
|
|
4953
|
+
active.delete(value);
|
|
4954
|
+
};
|
|
4955
|
+
roots.forEach((schema) => walk(schema, prefix, offset ? [names[0].text().trim()] : []));
|
|
4956
|
+
if (matches.size === 1) proofs.push(index);
|
|
4957
|
+
});
|
|
4958
|
+
return visits > 1e5 ? [] : proofs;
|
|
4959
|
+
}
|
|
4960
|
+
function apiOperationCodeControls(body, specification, operation) {
|
|
4961
|
+
if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(specification)) return [];
|
|
4962
|
+
const selected = object2(object2(object2(specification.paths)?.[operation.path])?.[operation.method.toLowerCase()]);
|
|
4963
|
+
if (!selected) return [];
|
|
4964
|
+
const { $, root } = migrationHtmlArticle(body);
|
|
4965
|
+
const flight = readMigrationFlightDocument($);
|
|
4966
|
+
if (!flight) return [];
|
|
4967
|
+
let visited = 0, textBytes = 0, exceeded = false;
|
|
4968
|
+
const resolveValue = (input) => {
|
|
4969
|
+
if (exceeded) return void 0;
|
|
4970
|
+
const result = resolveMigrationFlightValue(flight, input);
|
|
4971
|
+
if (!result.resolved) exceeded = true;
|
|
4972
|
+
return result.value;
|
|
4973
|
+
};
|
|
4974
|
+
const textOf = (input, depth = 0, label = false) => {
|
|
4975
|
+
if (exceeded || ++visited > 1e5 || depth > 96) {
|
|
4976
|
+
exceeded = true;
|
|
4977
|
+
return void 0;
|
|
4978
|
+
}
|
|
4979
|
+
const value = resolveValue(input);
|
|
4980
|
+
if (value === void 0 || value === null || typeof value === "boolean") return "";
|
|
4981
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
4982
|
+
const result = String(value);
|
|
4983
|
+
textBytes += Buffer.byteLength(result);
|
|
4984
|
+
if (textBytes > MAX_CAPTURE_BYTES2) {
|
|
4985
|
+
exceeded = true;
|
|
4986
|
+
return void 0;
|
|
4987
|
+
}
|
|
4988
|
+
return result;
|
|
4989
|
+
}
|
|
4990
|
+
if (!Array.isArray(value)) return void 0;
|
|
4991
|
+
if (value[0] === "$") {
|
|
4992
|
+
if (typeof value[1] !== "string" || !(label ? ["div", "p", "span", "code", "a", "br", "svg", "path", "polyline"] : ["span", "code", "a", "br"]).includes(value[1]) || !object2(value[3])) return void 0;
|
|
4993
|
+
return value[1] === "br" ? "\n" : textOf(value[3].children, depth + 1, label);
|
|
4994
|
+
}
|
|
4995
|
+
const children = value.map((child) => textOf(child, depth + 1, label));
|
|
4996
|
+
return children.some((child) => child === void 0) ? void 0 : children.join("");
|
|
4997
|
+
};
|
|
4998
|
+
const samplePairs = (value, transport = true) => {
|
|
4999
|
+
if (exceeded) return void 0;
|
|
5000
|
+
const samples = transport ? resolveValue(value) : value;
|
|
5001
|
+
if (!Array.isArray(samples) || samples.length > 64) return void 0;
|
|
5002
|
+
const pairs = [];
|
|
5003
|
+
for (const input of samples) {
|
|
5004
|
+
const sample = object2(transport ? resolveValue(input) : input);
|
|
5005
|
+
const language = transport ? resolveValue(sample?.lang) : sample?.lang;
|
|
5006
|
+
const source = transport ? resolveValue(sample?.source) : sample?.source;
|
|
5007
|
+
const label = transport ? resolveValue(sample?.label) : sample?.label;
|
|
5008
|
+
if (label !== void 0 && typeof label !== "string") return void 0;
|
|
5009
|
+
if (typeof language !== "string" || !language.trim() || typeof source !== "string" || !source.trim()) return void 0;
|
|
5010
|
+
textBytes += Buffer.byteLength(language) + Buffer.byteLength(source) + (typeof label === "string" ? Buffer.byteLength(label) : 0);
|
|
5011
|
+
if (textBytes > MAX_CAPTURE_BYTES2) {
|
|
5012
|
+
exceeded = true;
|
|
5013
|
+
return void 0;
|
|
5014
|
+
}
|
|
5015
|
+
pairs.push(JSON.stringify([language, source, label ?? null]));
|
|
5016
|
+
}
|
|
5017
|
+
return pairs;
|
|
5018
|
+
};
|
|
5019
|
+
const declared = selected["x-codeSamples"] === void 0 ? [] : samplePairs(selected["x-codeSamples"], false);
|
|
5020
|
+
if (!declared) return [];
|
|
5021
|
+
const groups = [];
|
|
5022
|
+
const providers = [];
|
|
5023
|
+
let providerCount = 0, generatedControllers = 0, providerMetadataMatches = false;
|
|
5024
|
+
const matchesOperation = (value) => {
|
|
5025
|
+
const metadata = object2(resolveValue(value));
|
|
5026
|
+
const declaration = resolveValue(metadata?.openapi);
|
|
5027
|
+
if (typeof declaration !== "string") return false;
|
|
5028
|
+
const tokens = declaration.trim().split(/\s+/);
|
|
5029
|
+
return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && tokens.at(-1) === operation.path;
|
|
5030
|
+
};
|
|
5031
|
+
const active = /* @__PURE__ */ new Set();
|
|
5032
|
+
const visit = (input, provider, group, depth, owners = []) => {
|
|
5033
|
+
if (exceeded || ++visited > 1e5 || depth > 96) {
|
|
5034
|
+
exceeded = true;
|
|
5035
|
+
return;
|
|
5036
|
+
}
|
|
5037
|
+
const value = resolveValue(input);
|
|
5038
|
+
if (!value || typeof value !== "object") return;
|
|
5039
|
+
if (active.has(value)) {
|
|
5040
|
+
exceeded = true;
|
|
5041
|
+
return;
|
|
5042
|
+
}
|
|
5043
|
+
active.add(value);
|
|
5044
|
+
if (Array.isArray(value)) {
|
|
5045
|
+
if (value[0] === "$" && object2(value[3])) {
|
|
5046
|
+
const props = value[3];
|
|
5047
|
+
const moduleId = typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
|
|
5048
|
+
const kind = moduleId ? flight.modules.get(moduleId) : value[1];
|
|
5049
|
+
const ownId = moduleId ? void 0 : resolveValue(props.id);
|
|
5050
|
+
const localOwners = typeof ownId === "string" ? [...owners, ownId] : owners;
|
|
5051
|
+
let owned = provider;
|
|
5052
|
+
if (kind === "OpenApiProvider") {
|
|
5053
|
+
providerCount += 1;
|
|
5054
|
+
const data = object2(resolveValue(props.openApiReferenceData));
|
|
5055
|
+
const source = object2(resolveValue(data?.operation));
|
|
5056
|
+
const samples = samplePairs(source?.codeSamples);
|
|
5057
|
+
const authored = source?.["x-codeSamples"] === void 0 ? [] : samplePairs(source["x-codeSamples"]);
|
|
5058
|
+
owned = Boolean(source && source.method === operation.method.toLowerCase() && source.path === operation.path && samples && authored && JSON.stringify(samples) === JSON.stringify(declared) && JSON.stringify(authored) === JSON.stringify(declared));
|
|
5059
|
+
if (owned) {
|
|
5060
|
+
providers.push(source);
|
|
5061
|
+
providerMetadataMatches = matchesOperation(props.pageMetadata);
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
if (owned && kind === "MDXContentController" && matchesOperation(props.pageMetadata)) {
|
|
5065
|
+
const child = resolveValue(props.children);
|
|
5066
|
+
const childId = Array.isArray(child) && child[0] === "$" && typeof child[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(child[1])?.[1] : void 0;
|
|
5067
|
+
const childProps = Array.isArray(child) ? object2(child[3]) : void 0;
|
|
5068
|
+
const source = object2(resolveValue(childProps?.mdxSource));
|
|
5069
|
+
const scope = object2(resolveValue(source?.scope));
|
|
5070
|
+
if (childId && flight.modules.get(childId) === "default" && typeof source?.compiledSource === "string" && (() => {
|
|
5071
|
+
if (matchesOperation(scope?.pageMetadata)) return true;
|
|
5072
|
+
const hint = object2(resolveValue(scope?.pageMetadata)), metadata = object2(resolveValue(props.pageMetadata));
|
|
5073
|
+
const declaration = resolveValue(hint?.openapi);
|
|
5074
|
+
if (typeof declaration !== "string" || typeof hint?.href !== "string" || !hint.href || hint.href !== metadata?.href) return false;
|
|
5075
|
+
const tokens = declaration.trim().split(/\s+/);
|
|
5076
|
+
const template = (path) => path.split("/").map((segment) => /^\{[^{}\/]+\}$/.test(segment) ? "{}" : segment).join("/");
|
|
5077
|
+
return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && template(tokens.at(-1) ?? "") === template(operation.path);
|
|
5078
|
+
})()) generatedControllers += 1;
|
|
5079
|
+
}
|
|
5080
|
+
let local = group;
|
|
5081
|
+
if (owned && typeof props.className === "string" && props.className.split(/\s+/).includes("code-group")) {
|
|
5082
|
+
local = { owners: localOwners, code: [], controls: [], toolbar: false, generated: false, invalid: false };
|
|
5083
|
+
groups.push(local);
|
|
5084
|
+
}
|
|
5085
|
+
if (local && owned) {
|
|
5086
|
+
if (props["data-component-part"] === "code-group-tab-bar") local.toolbar = true;
|
|
5087
|
+
if (String(props["data-index"]) === "-1") local.generated = true;
|
|
5088
|
+
if (props["aria-expanded"] !== void 0) {
|
|
5089
|
+
const text2 = textOf(props.children, 0, true);
|
|
5090
|
+
if (kind !== "button" || props["aria-haspopup"] !== "menu" || text2 === void 0) local.invalid = true;
|
|
5091
|
+
else local.controls.push(JSON.stringify([text2, props["aria-label"] ?? "", String(props["aria-expanded"]), props["aria-haspopup"]]));
|
|
5092
|
+
}
|
|
5093
|
+
if (kind === "pre") {
|
|
5094
|
+
const code = textOf(props.children);
|
|
5095
|
+
if (code === void 0 || !code.trim()) local.invalid = true;
|
|
5096
|
+
else local.code.push(code);
|
|
5097
|
+
}
|
|
5098
|
+
if (props["aria-busy"] === true || props["aria-busy"] === "true" || props.role === "progressbar") local.invalid = true;
|
|
5099
|
+
}
|
|
5100
|
+
visit(props.children, owned, local, depth + 1, localOwners);
|
|
5101
|
+
} else for (const child of value) visit(child, provider, group, depth + 1, owners);
|
|
5102
|
+
} else if (depth === 0 && Array.isArray(value.f)) {
|
|
5103
|
+
visit(value.f, provider, group, depth + 1, owners);
|
|
5104
|
+
} else if (Object.keys(value).every((key) => key === "children")) {
|
|
5105
|
+
const children = value.children;
|
|
5106
|
+
if (Array.isArray(children) && children.length >= 2) visit(children, provider, group, depth + 1, owners);
|
|
5107
|
+
}
|
|
5108
|
+
active.delete(value);
|
|
5109
|
+
};
|
|
5110
|
+
visit(flight.records.get("0"), false, void 0, 0);
|
|
5111
|
+
if (exceeded || providerCount !== 1 || providers.length !== 1 || groups.length > MAX_TAB_GROUPS) return [];
|
|
5112
|
+
const sources = declared.map((pair) => JSON.parse(pair)[1]);
|
|
5113
|
+
const sourceCounts = /* @__PURE__ */ new Map();
|
|
5114
|
+
for (const group of groups) if (group.toolbar && group.generated && !group.invalid && group.controls.length >= 1 && group.controls.length <= 2) {
|
|
5115
|
+
const signature = JSON.stringify([group.owners, group.code, group.controls]);
|
|
5116
|
+
sourceCounts.set(signature, (sourceCounts.get(signature) ?? 0) + 1);
|
|
5117
|
+
}
|
|
5118
|
+
const expanded = root.find("[aria-expanded]").toArray();
|
|
5119
|
+
if (expanded.length > MAX_TABS) return [];
|
|
5120
|
+
const controlIndices = new Map(expanded.map((node, index) => [node, index]));
|
|
5121
|
+
const domGroups = /* @__PURE__ */ new Map();
|
|
5122
|
+
$(expanded).each((_index, node) => {
|
|
5123
|
+
const control = $(node);
|
|
5124
|
+
const group = control.closest('.code-group,[data-testid="code-group-select"]');
|
|
5125
|
+
const legacy = group.is('[data-testid="code-group-select"]');
|
|
5126
|
+
if (!control.is('button[aria-haspopup="menu"]') || control.attr("aria-controls") || !legacy && !control.closest('[data-component-part="code-group-tab-bar"]').length || !group.length || group.find("[aria-expanded]").length < 1 || group.find("[aria-expanded]").length > 2) return;
|
|
5127
|
+
if (domGroups.has(group[0])) return;
|
|
5128
|
+
const menus = group.find("[aria-expanded]");
|
|
5129
|
+
if (menus.toArray().some((menu) => !$(menu).is('button[aria-haspopup="menu"]') || !$(menu).attr("aria-label")?.trim() && !$(menu).text().trim() || $(menu).attr("aria-controls") || !legacy && $(menu).closest('[data-component-part="code-group-tab-bar"]')[0] !== group.children('[data-component-part="code-group-tab-bar"]')[0] || legacy && !$(menu).parents().toArray().includes(group.children().first()[0]))) return;
|
|
5130
|
+
if (menus.length === 2) {
|
|
5131
|
+
const samples = declared.map((pair) => JSON.parse(pair));
|
|
5132
|
+
const labelledChoices = samples.filter((sample) => sample[2] && samples.filter((other) => other[0] === sample[0]).length >= 2);
|
|
5133
|
+
if (!labelledChoices.length || !menus.toArray().some((menu) => labelledChoices.some((sample) => sample[2] === $(menu).text().trim()))) return;
|
|
5134
|
+
}
|
|
5135
|
+
const panels = group.find("[data-index]");
|
|
5136
|
+
const code = group.find("pre").toArray().map((pre) => {
|
|
5137
|
+
const lines = $(pre).find(".token-line");
|
|
5138
|
+
return lines.length ? lines.toArray().map((line) => $(line).text()).join("\n") : $(pre).text();
|
|
5139
|
+
});
|
|
5140
|
+
if (group.find('[aria-busy="true"],[role="progressbar"]').length) return;
|
|
5141
|
+
if (legacy) {
|
|
5142
|
+
if (menus.length !== 2 || group.children().length !== 2 || group.children().first().find("pre").length || code.length !== 1 || !code[0].trim() || panels.length) return;
|
|
5143
|
+
const selectedSamples = declared.map((pair) => JSON.parse(pair)).filter((sample) => sample[2] && menus.toArray().some((menu) => $(menu).text().trim() === sample[2]) && menus.toArray().some((menu) => $(menu).text().trim().toLowerCase() === sample[0]?.toLowerCase()) && (code[0] === sample[1] || code[0] === sample[1]?.replace(/\n$/, "")));
|
|
5144
|
+
if (!selectedSamples.length) return;
|
|
5145
|
+
} else if (!panels.length || panels.length > MAX_TABS || code.length !== panels.length || !panels.toArray().some((panel) => $(panel).attr("data-index") === "-1") || panels.toArray().some((panel) => $(panel).find("pre").length !== 1 || !$(panel).text().trim()) || !sources.every((source) => code.some((captured) => captured === source || captured === source.replace(/\n$/, "")))) return;
|
|
5146
|
+
const controls = menus.toArray().map((menu) => JSON.stringify([$(menu).text(), $(menu).attr("aria-label") ?? "", $(menu).attr("aria-expanded"), $(menu).attr("aria-haspopup")]));
|
|
5147
|
+
const owners = group.parents("[id]").toArray().reverse().map((owner) => $(owner).attr("id"));
|
|
5148
|
+
if (group.attr("id")) owners.push(group.attr("id"));
|
|
5149
|
+
domGroups.set(group[0], { signature: JSON.stringify([owners, code, controls]), indices: menus.toArray().map((menu) => controlIndices.get(menu)), code, legacy, title: group.children('[data-component-part="code-group-tab-bar"]').find("span[title]").first().attr("title"), label: legacy || group.children('[data-component-part="code-group-tab-bar"]').length === 1 && group.children('[data-component-part="code-group-tab-bar"]').find("span[title]").toArray().some((title) => Boolean($(title).attr("title")?.trim()) && $(title).text().trim() === $(title).attr("title")) });
|
|
5150
|
+
});
|
|
5151
|
+
const domCounts = /* @__PURE__ */ new Map();
|
|
5152
|
+
for (const group of domGroups.values()) domCounts.set(group.signature, (domCounts.get(group.signature) ?? 0) + 1);
|
|
5153
|
+
const proofs = [];
|
|
5154
|
+
for (const group of domGroups.values()) if (sourceCounts.get(group.signature) === 1 && domCounts.get(group.signature) === 1) proofs.push(...group.indices);
|
|
5155
|
+
if (groups.length === 0 && providerMetadataMatches && generatedControllers === 1 && domGroups.size === 2) {
|
|
5156
|
+
const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
|
|
5157
|
+
const endpoint = $(node).find('[data-component-part="scroll-area-content"]').first().clone();
|
|
5158
|
+
endpoint.find("[data-thally-static-base-urls]").remove();
|
|
5159
|
+
return $(node).find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
|
|
5160
|
+
});
|
|
5161
|
+
const main = $("main#content-container"), side = $('[id="content-side-layout"]');
|
|
5162
|
+
const content = $('[id="content-area"]');
|
|
5163
|
+
const slots = /* @__PURE__ */ new Map();
|
|
5164
|
+
const titles = [resolveValue(providers[0].title), resolveValue(providers[0].summary), ...declared.flatMap((pair) => {
|
|
5165
|
+
const values = JSON.parse(pair);
|
|
5166
|
+
return [values[0], values[2]];
|
|
5167
|
+
})].filter((value) => typeof value === "string").map((value) => value.toLowerCase());
|
|
5168
|
+
let valid = operations.length === 1 && main.length === 1 && side.length === 1 && content.length === 1 && operations.closest("#content-area")[0] === content[0] && $("[id]").filter((_index, node) => $(node).attr("id") === operations.attr("id")).length === 1 && side.closest("main")[0] === main[0];
|
|
5169
|
+
for (const [node, entry] of domGroups) {
|
|
5170
|
+
const group = $(node);
|
|
5171
|
+
const control = group.find("button[aria-expanded]").first();
|
|
5172
|
+
const owner = group.closest('[id^="api-playground-"][id$="-operation-page"]');
|
|
5173
|
+
const slot = owner[0] === operations[0] ? "inline" : group.closest("#content-side-layout")[0] === side[0] ? "sidebar" : void 0;
|
|
5174
|
+
if (!slot || slots.has(slot) || !entry.label || !entry.legacy && (!entry.title || !titles.includes(entry.title.toLowerCase()) || !control.attr("aria-label")?.trim()) || group.closest("main")[0] !== main[0] || group.closest('.mdx-content,.api-section,.object-param-field,.array-param-field,[data-component-part="expandable-content"]').length) valid = false;
|
|
5175
|
+
else slots.set(slot, entry);
|
|
5176
|
+
}
|
|
5177
|
+
if (valid && slots.size === 2 && JSON.stringify(slots.get("inline")?.code) === JSON.stringify(slots.get("sidebar")?.code)) {
|
|
5178
|
+
proofs.push(...[...slots.values()].flatMap((slot) => slot.indices));
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
return proofs;
|
|
5182
|
+
}
|
|
5183
|
+
|
|
3346
5184
|
// src/url-run.ts
|
|
3347
|
-
var CONVERTER_VERSION = "url-evidence-
|
|
3348
|
-
var
|
|
5185
|
+
var CONVERTER_VERSION = "url-evidence-v6";
|
|
5186
|
+
var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
|
|
3349
5187
|
var ACCEPT = "text/markdown,text/html,application/xhtml+xml;q=0.9";
|
|
3350
5188
|
var MAX_RESPONSE_BYTES = 8e6;
|
|
3351
5189
|
var MAX_ASSET_BYTES = 1e7;
|
|
3352
5190
|
var MAX_RECORDS = 1e5;
|
|
3353
5191
|
var LOCALES = /* @__PURE__ */ new Set(["ar", "cs", "da", "de", "el", "en", "es", "fi", "fr", "he", "hi", "hu", "id", "it", "ja", "ko", "ms", "nl", "no", "pl", "pt", "pt-br", "ro", "ru", "sk", "sv", "th", "tr", "uk", "vi", "zh", "zh-cn", "zh-hans", "zh-hant"]);
|
|
3354
5192
|
function hash(value) {
|
|
3355
|
-
return
|
|
5193
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
3356
5194
|
}
|
|
3357
5195
|
function canonicalJson(value) {
|
|
3358
5196
|
const canonical2 = (value2) => Array.isArray(value2) ? value2.map(canonical2) : value2 !== null && typeof value2 === "object" ? Object.fromEntries(Object.entries(value2).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, field]) => [key, canonical2(field)])) : value2;
|
|
@@ -3388,14 +5226,14 @@ function markdownNodes(body) {
|
|
|
3388
5226
|
nodes.push(node);
|
|
3389
5227
|
for (const child of node.children ?? []) visit(child);
|
|
3390
5228
|
};
|
|
3391
|
-
visit(
|
|
5229
|
+
visit(parser2.parse(body));
|
|
3392
5230
|
return nodes;
|
|
3393
5231
|
}
|
|
3394
5232
|
function markdownCode(body) {
|
|
3395
5233
|
return markdownNodes(body).filter((node) => node.type === "code").map((node) => node.value ?? "");
|
|
3396
5234
|
}
|
|
3397
5235
|
function alternativeBlocks(body, baseline) {
|
|
3398
|
-
const blocks = (value) => (
|
|
5236
|
+
const blocks = (value) => (parser2.parse(value).children ?? []).map((node) => value.slice(node.position.start.offset, node.position.end.offset));
|
|
3399
5237
|
const originals = new Set(blocks(baseline));
|
|
3400
5238
|
return blocks(body).filter((block) => !originals.has(block)).join("\n\n");
|
|
3401
5239
|
}
|
|
@@ -3407,7 +5245,7 @@ function linksOf(document) {
|
|
|
3407
5245
|
return [...root.find("a[href]").toArray(), ...$('link[rel="sitemap"][href],link[rel="alternate"][href]').toArray()].map((node) => $(node).attr("href")).filter(Boolean);
|
|
3408
5246
|
}
|
|
3409
5247
|
if (/xml/i.test(document.contentType) || /<urlset|<sitemapindex/i.test(document.body)) {
|
|
3410
|
-
const $ =
|
|
5248
|
+
const $ = load7(document.body, { xmlMode: true });
|
|
3411
5249
|
return $("loc").toArray().map((node) => $(node).text().trim());
|
|
3412
5250
|
}
|
|
3413
5251
|
return [
|
|
@@ -3437,7 +5275,8 @@ function evidenceOf(capture) {
|
|
|
3437
5275
|
...capture.artifactRef ? { artifactRef: capture.artifactRef } : {},
|
|
3438
5276
|
states: (capture.states ?? []).map((state) => ({ id: state.id, label: state.label, sha256: hash(state.response.body) })),
|
|
3439
5277
|
...technical,
|
|
3440
|
-
links: linksOf(capture)
|
|
5278
|
+
links: linksOf(capture),
|
|
5279
|
+
articleIdentity: observedMigrationArticleIdentity(capture)
|
|
3441
5280
|
};
|
|
3442
5281
|
}
|
|
3443
5282
|
function markdownHeadingAnchors(body) {
|
|
@@ -3450,28 +5289,14 @@ function markdownHeadingAnchors(body) {
|
|
|
3450
5289
|
return occurrence ? `${base}-${occurrence}` : base;
|
|
3451
5290
|
});
|
|
3452
5291
|
}
|
|
3453
|
-
function sourceNavigation(capture) {
|
|
3454
|
-
return extractSourceNavigation(capture);
|
|
5292
|
+
function sourceNavigation(capture, selectedScope) {
|
|
5293
|
+
return extractSourceNavigation(capture, selectedScope);
|
|
3455
5294
|
}
|
|
3456
5295
|
function idForUrl(url, scope) {
|
|
3457
5296
|
const relative4 = scope === "/" ? url.pathname.slice(1) : url.pathname.slice(scope.length).replace(/^\//, "");
|
|
3458
5297
|
const id = pageIdFromReference(relative4 || "introduction", true);
|
|
3459
5298
|
return id && `${id}${url.search ? `--${hash(url.search).slice(0, 12)}` : ""}`;
|
|
3460
5299
|
}
|
|
3461
|
-
function articleIdentity(document) {
|
|
3462
|
-
if (!/html/i.test(document.contentType)) return document.finalUrl;
|
|
3463
|
-
const $ = load3(document.body);
|
|
3464
|
-
const reference = $('link[rel="canonical"][href]').first().attr("href");
|
|
3465
|
-
if (!reference) return document.finalUrl;
|
|
3466
|
-
try {
|
|
3467
|
-
const target2 = new URL(reference, document.finalUrl);
|
|
3468
|
-
const source = document.finalUrl;
|
|
3469
|
-
const directory = (path) => path.replace(/\/index\/?$/i, "").replace(/\/$/, "");
|
|
3470
|
-
return target2.origin === source.origin && !target2.username && !target2.password && target2.search === source.search && directory(target2.pathname) === directory(source.pathname) ? target2 : source;
|
|
3471
|
-
} catch {
|
|
3472
|
-
return document.finalUrl;
|
|
3473
|
-
}
|
|
3474
|
-
}
|
|
3475
5300
|
function convert(document, id) {
|
|
3476
5301
|
if (/html/i.test(document.contentType)) {
|
|
3477
5302
|
const technical = htmlEvidence(document);
|
|
@@ -3516,20 +5341,56 @@ function assetReferences(capture) {
|
|
|
3516
5341
|
}
|
|
3517
5342
|
const { $, root } = migrationHtmlArticle(capture.body);
|
|
3518
5343
|
return [
|
|
3519
|
-
...root.find("img[src],video[poster],a[download][href]").toArray().map((node) => $(node).
|
|
5344
|
+
...root.find("img[src],video[poster],a[download][href]").toArray().map((node) => $(node).is("video") ? $(node).attr("poster") : $(node).is("a") ? $(node).attr("href") : $(node).attr("src")),
|
|
3520
5345
|
...root.find("[srcset]").toArray().flatMap((node) => migrationImageCandidates($(node).attr("srcset") ?? "", capture.finalUrl).map((candidate) => candidate.url))
|
|
3521
5346
|
].filter(Boolean);
|
|
3522
5347
|
}
|
|
3523
|
-
function unresolvedStaticControls(body,
|
|
5348
|
+
function unresolvedStaticControls(body, coveredSchemaGroups = [], coveredSchemaDisclosures = [], coveredCodeControls = []) {
|
|
3524
5349
|
const { $, root } = migrationHtmlArticle(body);
|
|
5350
|
+
root.find("[aria-expanded]").each((index, node) => {
|
|
5351
|
+
if (coveredCodeControls.includes(index)) $(node).removeAttr("aria-expanded");
|
|
5352
|
+
});
|
|
5353
|
+
root.find('[role="tablist"],[data-component-part="tabs-list"]').each((index, node) => {
|
|
5354
|
+
if (coveredSchemaGroups.includes(index)) $(node).remove();
|
|
5355
|
+
});
|
|
5356
|
+
root.find("details").each((index, node) => {
|
|
5357
|
+
if (coveredSchemaDisclosures.includes(index)) $(node).remove();
|
|
5358
|
+
});
|
|
3525
5359
|
const tabs = root.find('[role="tab"]').toArray();
|
|
5360
|
+
const idCounts = /* @__PURE__ */ new Map();
|
|
5361
|
+
root.find("[id]").each((_index, node) => {
|
|
5362
|
+
const id = $(node).attr("id");
|
|
5363
|
+
idCounts.set(id, (idCounts.get(id) ?? 0) + 1);
|
|
5364
|
+
});
|
|
3526
5365
|
for (const tab of tabs) {
|
|
5366
|
+
const controlId = $(tab).attr("id");
|
|
5367
|
+
if (controlId && idCounts.get(controlId) !== 1) return true;
|
|
3527
5368
|
const controls = $(tab).attr("aria-controls");
|
|
3528
|
-
const
|
|
3529
|
-
|
|
5369
|
+
const group = $(tab).closest('[role="tablist"],ul.tabs');
|
|
5370
|
+
const container = group.closest(".code-group,.tabs-container,.tabs.tab-container");
|
|
5371
|
+
const peers = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"],ul.tabs')[0] === group[0]);
|
|
5372
|
+
const localPanels = container.find('[role="tabpanel"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
|
|
5373
|
+
const owners = container.find('[role="tablist"],ul.tabs').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
|
|
5374
|
+
const panels = controls ? root.find("[id]").filter((_index, node) => $(node).attr("id") === controls) : owners.length === 1 && peers.length === localPanels.length ? $(localPanels).eq(peers.indexOf(tab)) : $([]);
|
|
5375
|
+
if (panels.length !== 1 || !panels.text().trim() && !panels.find("pre,img,table,svg").length || panels.is('[aria-busy="true"],[role="progressbar"]') || panels.find('[aria-busy="true"],[role="progressbar"]').length) return true;
|
|
5376
|
+
const labels2 = panels.attr("aria-labelledby")?.trim().split(/\s+/);
|
|
5377
|
+
if (labels2 && (!controlId || !labels2.includes(controlId))) return true;
|
|
5378
|
+
if (controls && !labels2 && (container.length ? panels.closest(".code-group,.tabs-container,.tabs.tab-container")[0] !== container[0] : panels.parent()[0] !== group.parent()[0])) return true;
|
|
3530
5379
|
}
|
|
5380
|
+
const isCapturedImageZoom = (node, depth = 0) => {
|
|
5381
|
+
if (depth > 8) return false;
|
|
5382
|
+
const control = $(node), image = control.prev("span.zoom-image-trigger[data-ramka-trigger]");
|
|
5383
|
+
return control.is('button[data-ramka-trigger][aria-haspopup="dialog"][aria-label]') && Boolean(control.attr("aria-label")?.trim()) && !control.attr("aria-controls") && !control.text().trim() && image.find("img[src]").length === 1 && !image.text().trim() && !image.find("form,iframe,object,embed,canvas").length && image.find("button").toArray().every((button) => isCapturedImageZoom(button, depth + 1));
|
|
5384
|
+
};
|
|
5385
|
+
if (root.find("[aria-expanded]").toArray().some((node) => {
|
|
5386
|
+
const control = $(node);
|
|
5387
|
+
if (control.attr("role") === "tab") return false;
|
|
5388
|
+
const actions = control.parent('[id="page-context-menu"]');
|
|
5389
|
+
if (control.is('button[aria-haspopup="menu"]') && !control.attr("aria-controls") && !control.text().trim() && actions.closest("header").length && actions.children("button#page-context-menu-button").length === 1) return false;
|
|
5390
|
+
if (isCapturedImageZoom(node)) return false;
|
|
5391
|
+
return true;
|
|
5392
|
+
})) return true;
|
|
3531
5393
|
return root.find("details:not([open])").toArray().some((node) => {
|
|
3532
|
-
if (hasApiSchema && $(node).is(".expandable") && $(node).children('summary[data-component-part="expandable-button"]').length) return false;
|
|
3533
5394
|
const clone = $(node).clone();
|
|
3534
5395
|
clone.children("summary").remove();
|
|
3535
5396
|
return !clone.text().trim() && !clone.find("pre,img,table,svg").length;
|
|
@@ -3567,7 +5428,7 @@ function acceptedAsset(asset) {
|
|
|
3567
5428
|
}
|
|
3568
5429
|
function validRecord(record) {
|
|
3569
5430
|
if (record.outcome !== "imported") return true;
|
|
3570
|
-
return Boolean(record.page && record.pageSha256 === pageHash(record.page) && record.evidence && pageIdFromReference(record.page.id, true) === record.page.id && pageIdFromReference(record.page.navigationId, true) === record.page.navigationId && sanitizeRemoteMarkdown(record.page.body.replace(/^<a id="[^"<]*" \/>$/gm, "")) !== null && /^[a-f0-9]{64}$/.test(record.evidence.sha256) && (!record.apiSpecification || hasCompleteLocalApiSchema(record.apiSpecification.document) && record.apiSpecification.path === `openapi/migration-${record.apiSpecification.sha256}.json` && hash(canonicalJson(record.apiSpecification.document)) === record.apiSpecification.sha256) && record.assets?.every((asset) => /^migration-assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(asset.path) && hash(Buffer.from(asset.base64, "base64")) === asset.sha256));
|
|
5431
|
+
return Boolean(record.page && record.pageSha256 === pageHash(record.page) && record.evidence && pageIdFromReference(record.page.id, true) === record.page.id && pageIdFromReference(record.page.navigationId, true) === record.page.navigationId && sanitizeRemoteMarkdown(record.page.body.replace(/^<a id="[^"<]*" \/>$/gm, "")) !== null && /^[a-f0-9]{64}$/.test(record.evidence.sha256) && (!record.apiSpecification || hasCompleteLocalApiSchema(record.apiSpecification.document) && record.apiSpecification.path === `openapi/migration-${record.apiSpecification.sha256}.json` && hash(canonicalJson(record.apiSpecification.document)) === record.apiSpecification.sha256) && record.assets?.every((asset) => /^migration-assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(asset.path) && (!asset.derivedFromSha256 || /^[a-f0-9]{64}$/.test(asset.derivedFromSha256)) && hash(Buffer.from(asset.base64, "base64")) === asset.sha256));
|
|
3571
5432
|
}
|
|
3572
5433
|
function hasCompleteLocalApiSchema(document) {
|
|
3573
5434
|
let visited = 0;
|
|
@@ -3576,12 +5437,12 @@ function hasCompleteLocalApiSchema(document) {
|
|
|
3576
5437
|
if (typeof value === "number" && !Number.isFinite(value)) return false;
|
|
3577
5438
|
if (!value || typeof value !== "object") return true;
|
|
3578
5439
|
if (Array.isArray(value)) return value.every((child) => visit(child, depth + 1));
|
|
3579
|
-
const
|
|
3580
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
3581
|
-
if (typeof
|
|
5440
|
+
const object3 = value;
|
|
5441
|
+
if (Object.prototype.hasOwnProperty.call(object3, "$ref")) {
|
|
5442
|
+
if (typeof object3.$ref !== "string" || !object3.$ref.startsWith("#/")) return false;
|
|
3582
5443
|
let target2 = document;
|
|
3583
5444
|
try {
|
|
3584
|
-
for (const encoded of
|
|
5445
|
+
for (const encoded of object3.$ref.slice(2).split("/")) {
|
|
3585
5446
|
const key = decodeURIComponent(encoded).replace(/~1/g, "/").replace(/~0/g, "~");
|
|
3586
5447
|
if (!target2 || typeof target2 !== "object" || !Object.prototype.hasOwnProperty.call(target2, key)) return false;
|
|
3587
5448
|
target2 = target2[key];
|
|
@@ -3590,7 +5451,7 @@ function hasCompleteLocalApiSchema(document) {
|
|
|
3590
5451
|
return false;
|
|
3591
5452
|
}
|
|
3592
5453
|
}
|
|
3593
|
-
return Object.values(
|
|
5454
|
+
return Object.values(object3).every((child) => visit(child, depth + 1));
|
|
3594
5455
|
};
|
|
3595
5456
|
return visit(document);
|
|
3596
5457
|
}
|
|
@@ -3605,6 +5466,25 @@ function resolveMigrationRecord(records, record) {
|
|
|
3605
5466
|
}
|
|
3606
5467
|
function navigationConfig(checkpoint, records) {
|
|
3607
5468
|
const navigation = checkpoint.navigation;
|
|
5469
|
+
const shortcuts = [];
|
|
5470
|
+
const collectShortcuts = (nodes) => {
|
|
5471
|
+
for (const node of nodes) {
|
|
5472
|
+
if (node.url) {
|
|
5473
|
+
const record = records.get(canonical(node.url, checkpoint.sourceUrl) ?? "");
|
|
5474
|
+
const href = record?.redirectTo ?? node.url;
|
|
5475
|
+
const identity = canonical(href, checkpoint.sourceUrl);
|
|
5476
|
+
const target2 = identity ? new URL(href, checkpoint.sourceUrl).href : null;
|
|
5477
|
+
if (target2 && !inScope(new URL(target2), new URL(checkpoint.sourceUrl), checkpoint.scopePath) && !shortcuts.some((link) => link.label === node.label && link.href === target2)) shortcuts.push({ label: node.label, href: target2 });
|
|
5478
|
+
}
|
|
5479
|
+
collectShortcuts(node.children ?? []);
|
|
5480
|
+
}
|
|
5481
|
+
};
|
|
5482
|
+
collectShortcuts(navigation);
|
|
5483
|
+
const hasDropdown = navigation.some((node) => node.presentation === "dropdown");
|
|
5484
|
+
const externalNavigation = shortcuts.length || hasDropdown ? { navigation: {
|
|
5485
|
+
...shortcuts.length ? { shortcuts } : {},
|
|
5486
|
+
...hasDropdown ? { display: "dropdown" } : {}
|
|
5487
|
+
} } : {};
|
|
3608
5488
|
const projection = localeProjection(checkpoint);
|
|
3609
5489
|
const project = (nodes) => nodes.flatMap((node) => {
|
|
3610
5490
|
const record = node.url ? records.get(canonical(node.url, node.url) ?? "") : void 0;
|
|
@@ -3625,7 +5505,7 @@ function navigationConfig(checkpoint, records) {
|
|
|
3625
5505
|
return pages2.length || tab.url ? [{ tab: tab.label, ...pages2.length ? { pages: pages2 } : {}, ...tab.url ? { href: tab.url } : {} }] : [];
|
|
3626
5506
|
});
|
|
3627
5507
|
const primary = localizedTabs(projection.defaultLocale);
|
|
3628
|
-
return { tabs: primary.length ? primary : localizedTabs(locales[0]), ...locales.length > 1 ? {
|
|
5508
|
+
return { ...externalNavigation, tabs: primary.length ? primary : localizedTabs(locales[0]), ...locales.length > 1 ? {
|
|
3629
5509
|
i18n: {
|
|
3630
5510
|
defaultLocale: projection.defaultLocale,
|
|
3631
5511
|
locales: locales.map((code) => ({ code, label: code === "en" ? "English" : code })),
|
|
@@ -3634,7 +5514,7 @@ function navigationConfig(checkpoint, records) {
|
|
|
3634
5514
|
} : {} };
|
|
3635
5515
|
}
|
|
3636
5516
|
const pages = project(navigation);
|
|
3637
|
-
return pages.length ? { tabs: [{ tab: "Documentation", pages }] } : null;
|
|
5517
|
+
return pages.length || shortcuts.length ? { ...externalNavigation, tabs: [{ tab: "Documentation", pages }] } : null;
|
|
3638
5518
|
}
|
|
3639
5519
|
function canonicalLanguage(value) {
|
|
3640
5520
|
if (!/^[a-z]{2,3}(?:-[a-z0-9]{2,8}){0,4}$/i.test(value)) return void 0;
|
|
@@ -3654,7 +5534,7 @@ function observedLocale(document, id, scopePath) {
|
|
|
3654
5534
|
}
|
|
3655
5535
|
const urlLocale = [id.split("/", 1)[0], ...scopePath.split("/"), document.finalUrl.pathname.split("/").filter(Boolean)[0]].find((segment) => segment && LOCALES.has(segment.toLowerCase()));
|
|
3656
5536
|
if (urlLocale) candidates.push({ source: "URL", value: canonicalLanguage(urlLocale) });
|
|
3657
|
-
const declared = /html/i.test(document.contentType) ?
|
|
5537
|
+
const declared = /html/i.test(document.contentType) ? load7(document.body)("html").attr("lang")?.trim() : void 0;
|
|
3658
5538
|
if (declared) {
|
|
3659
5539
|
const locale = canonicalLanguage(declared);
|
|
3660
5540
|
if (locale) candidates.push({ source: "html lang", value: locale });
|
|
@@ -3731,13 +5611,15 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
3731
5611
|
const projection = localeProjection(checkpoint);
|
|
3732
5612
|
const primaryIds = new Set(checkpoint.records.flatMap((record) => record.page && (!projection.canonicalLocale(record.page) || projection.canonicalLocale(record.page) === projection.defaultLocale) ? [projection.navigationId(record.page)] : []));
|
|
3733
5613
|
const outputIds = /* @__PURE__ */ new Set();
|
|
3734
|
-
const
|
|
5614
|
+
const resolve4 = (url) => {
|
|
3735
5615
|
const record = records.get(canonical(url, checkpoint.sourceUrl) ?? "");
|
|
3736
5616
|
return resolveMigrationRecord(records, record);
|
|
3737
5617
|
};
|
|
3738
5618
|
for (const record of checkpoint.records) {
|
|
3739
5619
|
counts[record.outcome]++;
|
|
3740
5620
|
issues.push(...record.issues ?? []);
|
|
5621
|
+
warnings.push(...record.warnings ?? []);
|
|
5622
|
+
if (record.outcome === "alias" && !resolveMigrationRecord(records, record)?.page) issues.push({ code: "missing-page", source: record.url, message: "An alias does not resolve to an imported source page." });
|
|
3741
5623
|
if (record.outcome === "pending" || record.outcome === "failed") issues.push({ code: "missing-page", source: record.url, message: record.reason ?? `Inventory entry is ${record.outcome}.` });
|
|
3742
5624
|
if (!record.page || !record.evidence) continue;
|
|
3743
5625
|
const outputId = projection.pageId(record.page);
|
|
@@ -3756,13 +5638,14 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
3756
5638
|
continue;
|
|
3757
5639
|
}
|
|
3758
5640
|
if (!inScope(link, source, checkpoint.scopePath)) continue;
|
|
5641
|
+
if (isDiscoveryIndex(link, source) || isMigrationUtilityUrl(link)) continue;
|
|
3759
5642
|
if (/\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js|json|ya?ml)$/i.test(link.pathname)) continue;
|
|
3760
|
-
const destination =
|
|
5643
|
+
const destination = resolve4(link.toString());
|
|
3761
5644
|
if (!destination || !["imported", "excluded"].includes(destination.outcome)) {
|
|
3762
5645
|
issues.push({ code: "missing-page", source: record.url, message: `Unresolved internal destination: ${link}` });
|
|
3763
|
-
} else if (link.hash && destination.outcome === "imported") {
|
|
3764
|
-
let anchor = link.hash.slice(1);
|
|
3765
|
-
try {
|
|
5646
|
+
} else if ((link.hash || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "")?.aliasAnchor) && destination.outcome === "imported") {
|
|
5647
|
+
let anchor = link.hash.slice(1) || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "").aliasAnchor;
|
|
5648
|
+
if (link.hash) try {
|
|
3766
5649
|
anchor = decodeURIComponent(anchor);
|
|
3767
5650
|
} catch {
|
|
3768
5651
|
}
|
|
@@ -3783,8 +5666,10 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
3783
5666
|
const locale = node.locale ? canonicalLanguage(node.locale) : parentLocale;
|
|
3784
5667
|
if (node.url) {
|
|
3785
5668
|
const url = new URL(node.url, source);
|
|
3786
|
-
if (inScope(url, source, checkpoint.scopePath)) {
|
|
3787
|
-
const
|
|
5669
|
+
if (inScope(url, source, checkpoint.scopePath) && !isMigrationUtilityUrl(url)) {
|
|
5670
|
+
const destination = resolve4(url.toString());
|
|
5671
|
+
if (destination?.outcome === "excluded" && destination.redirectTo) continue;
|
|
5672
|
+
const page = destination?.page;
|
|
3788
5673
|
if (!page) issues.push({ code: "navigation-missing", source: node.url, message: `Source navigation target ${node.label} was not imported.` });
|
|
3789
5674
|
else if (locale && locale !== projection.defaultLocale && !outputIds.has(`${locale}/${projection.navigationId(page)}`)) {
|
|
3790
5675
|
issues.push({ code: "navigation-missing", source: node.url, message: `The ${locale} sidebar target ${node.label} has no authored localized runtime page.` });
|
|
@@ -3801,16 +5686,32 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
3801
5686
|
async function runUrlMigration(options) {
|
|
3802
5687
|
const source = validateMigrationUrl(options.sourceUrl);
|
|
3803
5688
|
const sourceUrl = canonical(source.toString(), source.toString());
|
|
3804
|
-
const requestedScope = options.scopePath ?? (source.pathname.endsWith("/") ? source.pathname : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/");
|
|
3805
|
-
let scopeEnd = requestedScope.length;
|
|
3806
|
-
while (scopeEnd > 0 && requestedScope.charCodeAt(scopeEnd - 1) === 47) scopeEnd--;
|
|
3807
|
-
const scopePath = requestedScope.slice(0, scopeEnd) || "/";
|
|
3808
|
-
if (!scopePath.startsWith("/") || scopePath.includes("?") || scopePath.includes("#") || !inScope(source, source, scopePath)) throw new Error("The selected documentation scope must contain the source URL.");
|
|
3809
5689
|
const maxPages = options.maxPages ?? 1e3;
|
|
3810
5690
|
if (!Number.isInteger(maxPages) || maxPages < 1 || maxPages > 1e4) throw new Error("maxPages must be between 1 and 10000.");
|
|
3811
5691
|
const concurrency = Math.max(1, Math.min(32, Math.floor(options.concurrency ?? 5)));
|
|
3812
5692
|
const batchSize = Math.max(1, Math.min(500, Math.floor(options.batchSize ?? 50)));
|
|
3813
5693
|
if (!Number.isFinite(concurrency) || !Number.isFinite(batchSize) || options.maxCaptures !== void 0 && (!Number.isInteger(options.maxCaptures) || options.maxCaptures < 1) || options.maxTotalBytes !== void 0 && (!Number.isFinite(options.maxTotalBytes) || options.maxTotalBytes < 1)) throw new Error("Migration batch, concurrency and byte limits must be positive finite numbers.");
|
|
5694
|
+
const fetcher = options.fetcher ?? defaultMigrationFetcher;
|
|
5695
|
+
const acquire = options.captureProvider?.capture.bind(options.captureProvider) ?? (async ({ url }) => fetcher(url, { accept: ACCEPT }));
|
|
5696
|
+
let initial;
|
|
5697
|
+
let inferredScope;
|
|
5698
|
+
if (options.scopePath === void 0 && !options.checkpoint && !options.signal?.aborted) {
|
|
5699
|
+
initial = Promise.resolve().then(() => acquire({ url: source, signal: options.signal }));
|
|
5700
|
+
try {
|
|
5701
|
+
const document = await initial;
|
|
5702
|
+
if (document.finalUrl.origin === source.origin) {
|
|
5703
|
+
const inferred = inferUrlMigrationScope(document).scopePath;
|
|
5704
|
+
if (inScope(source, source, inferred)) inferredScope = inferred;
|
|
5705
|
+
}
|
|
5706
|
+
} catch {
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
const capture = (request) => initial && canonical(request.url.href, sourceUrl) === sourceUrl ? initial : acquire(request);
|
|
5710
|
+
const requestedScope = options.scopePath ?? options.checkpoint?.scopePath ?? inferredScope ?? (source.pathname.endsWith("/") ? source.pathname : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/");
|
|
5711
|
+
let scopeEnd = requestedScope.length;
|
|
5712
|
+
while (scopeEnd > 0 && requestedScope.charCodeAt(scopeEnd - 1) === 47) scopeEnd--;
|
|
5713
|
+
const scopePath = requestedScope.slice(0, scopeEnd) || "/";
|
|
5714
|
+
if (!scopePath.startsWith("/") || scopePath.includes("?") || scopePath.includes("#") || !inScope(source, source, scopePath)) throw new Error("The selected documentation scope must contain the source URL.");
|
|
3814
5715
|
const captureVersion = options.captureVersion ?? "fetch-v1";
|
|
3815
5716
|
if (options.checkpoint && options.checkpoint.records.length > MAX_RECORDS) throw new Error("Checkpoint inventory exceeds its bound.");
|
|
3816
5717
|
const checkpoint = options.checkpoint ? cloneUrlMigrationCheckpoint(options.checkpoint) : { schemaVersion: 1, converterVersion: CONVERTER_VERSION, sourceUrl, scopePath, captureVersion, records: [], navigation: [] };
|
|
@@ -3829,8 +5730,11 @@ async function runUrlMigration(options) {
|
|
|
3829
5730
|
delete record.apiSpecification;
|
|
3830
5731
|
delete record.version;
|
|
3831
5732
|
delete record.issues;
|
|
5733
|
+
delete record.warnings;
|
|
5734
|
+
delete record.redirectTo;
|
|
3832
5735
|
delete record.reason;
|
|
3833
5736
|
delete record.aliasOf;
|
|
5737
|
+
delete record.aliasAnchor;
|
|
3834
5738
|
}
|
|
3835
5739
|
if (record.page && pageIdFromReference(record.page.id, true) !== record.page.id) throw new Error("Checkpoint contains an unsafe page path.");
|
|
3836
5740
|
records.set(record.url, record);
|
|
@@ -3841,6 +5745,8 @@ async function runUrlMigration(options) {
|
|
|
3841
5745
|
const enqueue = (value, base, discoveredBy) => {
|
|
3842
5746
|
const url = canonical(value, base);
|
|
3843
5747
|
if (!url || !inScope(new URL(url), source, scopePath) && !isDiscoveryIndex(new URL(url), source)) return;
|
|
5748
|
+
const isUtility = isMigrationUtilityUrl(new URL(url));
|
|
5749
|
+
const isAttachment = isUtility || discoveredBy !== "submitted" && /\.(?:json|ya?ml)$/i.test(new URL(url).pathname);
|
|
3844
5750
|
const existing = records.get(url);
|
|
3845
5751
|
if (existing) {
|
|
3846
5752
|
if (!existing.discoveredBy.includes(discoveredBy)) {
|
|
@@ -3850,10 +5756,15 @@ async function runUrlMigration(options) {
|
|
|
3850
5756
|
return;
|
|
3851
5757
|
}
|
|
3852
5758
|
if (records.size >= MAX_RECORDS) throw new Error("Discovered URL inventory exceeded the 100000 URL safety bound.");
|
|
3853
|
-
const record = {
|
|
5759
|
+
const record = {
|
|
5760
|
+
url,
|
|
5761
|
+
discoveredBy: [discoveredBy],
|
|
5762
|
+
outcome: isAttachment ? "excluded" : "pending",
|
|
5763
|
+
...isAttachment ? { reason: isUtility ? "Recognized provider utility endpoint, not an authored article." : "Machine-readable attachment, not an authored article." } : {}
|
|
5764
|
+
};
|
|
3854
5765
|
records.set(url, record);
|
|
3855
5766
|
checkpoint.records.push(record);
|
|
3856
|
-
pending.push(record);
|
|
5767
|
+
if (!isAttachment) pending.push(record);
|
|
3857
5768
|
changed.push(record);
|
|
3858
5769
|
};
|
|
3859
5770
|
enqueue(sourceUrl, sourceUrl, "submitted");
|
|
@@ -3886,16 +5797,33 @@ async function runUrlMigration(options) {
|
|
|
3886
5797
|
}
|
|
3887
5798
|
}
|
|
3888
5799
|
enqueueNavigation(checkpoint.navigation, sourceUrl);
|
|
3889
|
-
const fetcher = options.fetcher ?? defaultMigrationFetcher;
|
|
3890
|
-
const capture = options.captureProvider?.capture.bind(options.captureProvider) ?? (async ({ url }) => fetcher(url, { accept: ACCEPT }));
|
|
3891
5800
|
const assetFetcher = options.assetFetcher ?? (!options.fetcher && !options.captureProvider ? defaultAssetFetcher : void 0);
|
|
3892
5801
|
const usedIds = new Map([...records.values()].filter((record) => record.page).map((record) => [record.page.id, record.url]));
|
|
3893
5802
|
const finalUrls = new Map([...records.values()].filter((record) => record.evidence && record.page).map((record) => [canonical(record.evidence.finalUrl, sourceUrl), record.url]));
|
|
3894
5803
|
let imported = usedIds.size;
|
|
3895
5804
|
const assetPayloads = new Map(checkpoint.records.flatMap((record) => (record.assets ?? []).map((asset) => [asset.sha256, asset])));
|
|
3896
5805
|
const assetBytes = /* @__PURE__ */ new Map();
|
|
3897
|
-
const
|
|
3898
|
-
|
|
5806
|
+
const rasterizedSvgs = /* @__PURE__ */ new Map();
|
|
5807
|
+
const retainAsset = async (original) => {
|
|
5808
|
+
let asset = { ...original };
|
|
5809
|
+
delete asset.derivedFromSha256;
|
|
5810
|
+
if (asset.unavailable) {
|
|
5811
|
+
if ([404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) return asset;
|
|
5812
|
+
throw new Error("Invalid source asset availability evidence.");
|
|
5813
|
+
}
|
|
5814
|
+
if (!acceptedAsset(asset)) {
|
|
5815
|
+
if (asset.content.byteLength > MAX_ASSET_BYTES || asset.contentType.split(";", 1)[0].trim().toLowerCase() !== "image/svg+xml" || !options.rasterizeSvg) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
|
|
5816
|
+
const sourceSha256 = hash(asset.content);
|
|
5817
|
+
let rendering = rasterizedSvgs.get(sourceSha256);
|
|
5818
|
+
if (!rendering) {
|
|
5819
|
+
rendering = options.rasterizeSvg({ ...asset, content: Buffer.from(asset.content) }, options.signal).then((rendered) => {
|
|
5820
|
+
if (rendered.unavailable || rendered.contentType.split(";", 1)[0].trim().toLowerCase() !== "image/png" || acceptedAsset(rendered) !== "png") throw new Error("Trusted SVG renderer did not return a bounded PNG image.");
|
|
5821
|
+
return { ...rendered, derivedFromSha256: sourceSha256 };
|
|
5822
|
+
});
|
|
5823
|
+
rasterizedSvgs.set(sourceSha256, rendering);
|
|
5824
|
+
}
|
|
5825
|
+
asset = { ...await rendering, url: original.url };
|
|
5826
|
+
}
|
|
3899
5827
|
const sha256 = hash(asset.content);
|
|
3900
5828
|
const content = assetBytes.get(sha256) ?? Buffer.from(asset.content);
|
|
3901
5829
|
assetBytes.set(sha256, content);
|
|
@@ -3911,6 +5839,26 @@ async function runUrlMigration(options) {
|
|
|
3911
5839
|
}
|
|
3912
5840
|
let totalBytes = checkpoint.records.reduce((sum, record) => sum + (record.evidence?.byteLength ?? 0), 0) + [...assetPayloads.values()].reduce((sum, asset) => sum + Buffer.byteLength(asset.base64, "base64"), 0);
|
|
3913
5841
|
let budgetExhausted = false;
|
|
5842
|
+
const exactCaptures = /* @__PURE__ */ new Map();
|
|
5843
|
+
const exactCaptureKey = (document) => {
|
|
5844
|
+
let body = document.body;
|
|
5845
|
+
if (body.includes("data-cfemail")) {
|
|
5846
|
+
const $ = load7(body);
|
|
5847
|
+
projectProtectedEmails($, $.root());
|
|
5848
|
+
body = $.html();
|
|
5849
|
+
}
|
|
5850
|
+
return hash(canonicalJson([
|
|
5851
|
+
hash(body),
|
|
5852
|
+
document.contentType,
|
|
5853
|
+
document.status,
|
|
5854
|
+
document.locale,
|
|
5855
|
+
document.version,
|
|
5856
|
+
document.headers?.["content-language"],
|
|
5857
|
+
document.navigation,
|
|
5858
|
+
(document.states ?? []).map((state) => [state.id, state.label, state.response.finalUrl.href, state.response.contentType, state.response.status, hash(state.response.body)]),
|
|
5859
|
+
(document.assets ?? []).map((asset) => [asset.url, asset.contentType, asset.unavailable, hash(asset.content)])
|
|
5860
|
+
]));
|
|
5861
|
+
};
|
|
3914
5862
|
let cursor = 0;
|
|
3915
5863
|
const flush = async () => {
|
|
3916
5864
|
const distinct = [...new Map(changed.map((record) => [record.url, record])).values()];
|
|
@@ -3925,8 +5873,14 @@ async function runUrlMigration(options) {
|
|
|
3925
5873
|
let claimedId;
|
|
3926
5874
|
let claimedFinal;
|
|
3927
5875
|
let markdownRepresentation;
|
|
5876
|
+
let exactKey;
|
|
3928
5877
|
try {
|
|
3929
5878
|
delete record.reason;
|
|
5879
|
+
if (/\/llms-full\.txt$/i.test(new URL(record.url).pathname)) {
|
|
5880
|
+
record.outcome = "excluded";
|
|
5881
|
+
record.reason = "Aggregate documentation corpus; individual articles are inventoried separately.";
|
|
5882
|
+
return;
|
|
5883
|
+
}
|
|
3930
5884
|
delete record.apiSpecification;
|
|
3931
5885
|
const represented = resolveMigrationRecord(records, record.representationOf ? records.get(record.representationOf) : void 0);
|
|
3932
5886
|
if (represented?.outcome === "imported") {
|
|
@@ -3936,13 +5890,60 @@ async function runUrlMigration(options) {
|
|
|
3936
5890
|
}
|
|
3937
5891
|
const document = await capture({ url: new URL(record.url), signal: options.signal });
|
|
3938
5892
|
if (options.signal?.aborted) return;
|
|
5893
|
+
if (document.status && document.status >= 300 && document.status < 400 && document.headers?.location) {
|
|
5894
|
+
const destination = canonical(document.headers.location, document.finalUrl.href);
|
|
5895
|
+
if (destination && new URL(destination).origin !== source.origin) {
|
|
5896
|
+
record.outcome = "excluded";
|
|
5897
|
+
record.redirectTo = new URL(document.headers.location, document.finalUrl).href;
|
|
5898
|
+
record.reason = "The source entry is a redirect to an external public resource, retained as a link.";
|
|
5899
|
+
return;
|
|
5900
|
+
}
|
|
5901
|
+
throw new Error("Capture returned an unresolved redirect.");
|
|
5902
|
+
}
|
|
3939
5903
|
if (Buffer.byteLength(document.body) > MAX_RESPONSE_BYTES) throw new Error("Captured response exceeds the 8 MB safety bound.");
|
|
3940
|
-
|
|
5904
|
+
if ((document.states?.length ?? 0) > 64 || (document.assets?.length ?? 0) > 256) throw new Error("Capture contains too many alternative states or assets.");
|
|
5905
|
+
for (const state of document.states ?? []) if (Buffer.byteLength(state.response.body) > MAX_RESPONSE_BYTES) throw new Error("Alternative state exceeds the 8 MB safety bound.");
|
|
5906
|
+
if (/html/i.test(document.contentType) && (!document.status || document.status < 300) && inScope(document.finalUrl, source, scopePath)) {
|
|
5907
|
+
exactKey = exactCaptureKey(document);
|
|
5908
|
+
const peer = resolveMigrationRecord(records, records.get(exactCaptures.get(exactKey) ?? ""));
|
|
5909
|
+
const peerIdentity = peer?.evidence?.articleIdentity;
|
|
5910
|
+
if (peer?.outcome === "imported" && peer.evidence && peerIdentity) {
|
|
5911
|
+
const candidate = migrationSectionQueryCandidate(document.finalUrl, peer.evidence.anchors);
|
|
5912
|
+
if (candidate.sectionAnchor && candidate.canonicalUrl === peerIdentity.canonicalUrl) {
|
|
5913
|
+
const byteLength = Buffer.byteLength(document.body) + (document.states ?? []).reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0);
|
|
5914
|
+
totalBytes += byteLength;
|
|
5915
|
+
if (totalBytes > (options.maxTotalBytes ?? 5e8)) {
|
|
5916
|
+
budgetExhausted = true;
|
|
5917
|
+
throw new Error("Migration exceeded its content byte budget.");
|
|
5918
|
+
}
|
|
5919
|
+
record.outcome = "alias";
|
|
5920
|
+
record.aliasOf = peer.url;
|
|
5921
|
+
record.aliasAnchor = candidate.sectionAnchor;
|
|
5922
|
+
if (peerIdentity.sectionAnchor && peer.aliasAnchor !== peerIdentity.sectionAnchor) {
|
|
5923
|
+
peer.aliasAnchor = peerIdentity.sectionAnchor;
|
|
5924
|
+
changed.push(peer);
|
|
5925
|
+
}
|
|
5926
|
+
record.evidence = {
|
|
5927
|
+
...peer.evidence,
|
|
5928
|
+
sha256: hash(document.body),
|
|
5929
|
+
byteLength,
|
|
5930
|
+
finalUrl: document.finalUrl.href,
|
|
5931
|
+
...document.artifactRef ? { artifactRef: document.artifactRef } : {},
|
|
5932
|
+
articleIdentity: { ...peerIdentity, sectionAnchor: candidate.sectionAnchor }
|
|
5933
|
+
};
|
|
5934
|
+
if (!document.artifactRef) delete record.evidence.artifactRef;
|
|
5935
|
+
enqueueArticleLinks(record.evidence.links, document.finalUrl.href, record.url);
|
|
5936
|
+
return;
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
const identity = declaredMigrationArticleUrl(document);
|
|
3941
5941
|
const final = canonical(identity.toString(), sourceUrl);
|
|
3942
5942
|
const role = classifyMigrationResponse(document);
|
|
3943
5943
|
if (!inScope(document.finalUrl, source, scopePath) && !(role === "index" && isDiscoveryIndex(document.finalUrl, source))) {
|
|
3944
5944
|
record.outcome = "excluded";
|
|
3945
5945
|
record.reason = "Redirected outside the selected origin/path.";
|
|
5946
|
+
if (document.finalUrl.origin === source.origin && (!document.status || document.status >= 200 && document.status < 300)) record.redirectTo = document.finalUrl.href;
|
|
3946
5947
|
return;
|
|
3947
5948
|
}
|
|
3948
5949
|
if (document.status && document.status >= 400) throw new Error(`Source page returned HTTP ${document.status}.`);
|
|
@@ -3960,13 +5961,13 @@ async function runUrlMigration(options) {
|
|
|
3960
5961
|
return;
|
|
3961
5962
|
}
|
|
3962
5963
|
if (/html/i.test(document.contentType)) {
|
|
3963
|
-
const $ =
|
|
5964
|
+
const $ = load7(document.body);
|
|
3964
5965
|
$('link[rel="alternate"][type="text/markdown"][href]').each((_index, node) => {
|
|
3965
5966
|
const alternate = canonical($(node).attr("href"), final);
|
|
3966
5967
|
if (!alternate) return;
|
|
3967
5968
|
const target2 = new URL(alternate), primary = new URL(final);
|
|
3968
|
-
const
|
|
3969
|
-
if (target2.origin !== primary.origin || target2.search !== primary.search ||
|
|
5969
|
+
const directory2 = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
|
|
5970
|
+
if (target2.origin !== primary.origin || target2.search !== primary.search || directory2(target2.pathname) !== directory2(primary.pathname) || !/\.mdx?$/i.test(target2.pathname)) return;
|
|
3970
5971
|
enqueue(alternate, final, "declared-markdown-representation");
|
|
3971
5972
|
const sibling = records.get(alternate);
|
|
3972
5973
|
sibling.representationOf = record.url;
|
|
@@ -3974,7 +5975,7 @@ async function runUrlMigration(options) {
|
|
|
3974
5975
|
changed.push(sibling);
|
|
3975
5976
|
});
|
|
3976
5977
|
}
|
|
3977
|
-
const observedNavigation = sourceNavigation(document);
|
|
5978
|
+
const observedNavigation = sourceNavigation(document, scopePath);
|
|
3978
5979
|
if (observedNavigation.length && !(checkpoint.navigation.some((node) => node.kind === "tab") && !observedNavigation.some((node) => node.kind === "tab"))) {
|
|
3979
5980
|
const merged = mergeSourceNavigation(checkpoint.navigation, observedNavigation);
|
|
3980
5981
|
if (JSON.stringify(merged) !== JSON.stringify(checkpoint.navigation)) {
|
|
@@ -4016,6 +6017,10 @@ async function runUrlMigration(options) {
|
|
|
4016
6017
|
record.reason = "Content could not be represented as safe inert MDX.";
|
|
4017
6018
|
return;
|
|
4018
6019
|
}
|
|
6020
|
+
let coveredSchemaGroups = [];
|
|
6021
|
+
let coveredSchemaDisclosures = [];
|
|
6022
|
+
let coveredCodeControls = [];
|
|
6023
|
+
let schemaOperation;
|
|
4019
6024
|
const hasSchemaChoice = /html/i.test(document.contentType) && migrationHtmlArticle(document.body).root.find('select[aria-label="Select schema type"],select[aria-label="\u9009\u62E9\u67B6\u6784\u7C7B\u578B"]').length > 0;
|
|
4020
6025
|
if (/html/i.test(document.contentType) && markdownRepresentation && (unresolvedStaticControls(document.body) || hasSchemaChoice)) {
|
|
4021
6026
|
const schemaCapture = await capture({ url: new URL(markdownRepresentation), signal: options.signal });
|
|
@@ -4031,6 +6036,10 @@ async function runUrlMigration(options) {
|
|
|
4031
6036
|
const path = `openapi/migration-${sha256}.json`;
|
|
4032
6037
|
record.apiSpecification = { path, sha256, document: fragment.document };
|
|
4033
6038
|
page.openapi = `${path} ${fragment.method} ${fragment.path}`;
|
|
6039
|
+
schemaOperation = fragment;
|
|
6040
|
+
coveredSchemaGroups = apiSchemaTabGroups(document.body, fragment.document, fragment);
|
|
6041
|
+
coveredSchemaDisclosures = apiSchemaDisclosureGroups(document.body, fragment.document, fragment);
|
|
6042
|
+
coveredCodeControls = apiOperationCodeControls(document.body, fragment.document, fragment);
|
|
4034
6043
|
}
|
|
4035
6044
|
}
|
|
4036
6045
|
const states = document.states ?? [];
|
|
@@ -4060,19 +6069,35 @@ ${difference}`;
|
|
|
4060
6069
|
}
|
|
4061
6070
|
record.assets = [];
|
|
4062
6071
|
record.issues = [];
|
|
4063
|
-
|
|
4064
|
-
|
|
6072
|
+
record.warnings = [];
|
|
6073
|
+
for (const captured of [document, ...states.map((state) => state.response)]) {
|
|
6074
|
+
if (!/html/i.test(captured.contentType)) continue;
|
|
6075
|
+
const { $, root } = migrationHtmlArticle(captured.body);
|
|
4065
6076
|
root.find("nav,footer,aside,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
|
|
4066
6077
|
root.find('button svg,a.hash-link svg,[aria-hidden="true"] svg,svg[aria-hidden="true"],svg[data-lucide],svg[data-icon]').remove();
|
|
6078
|
+
root.find("video,iframe").each((_index, node) => {
|
|
6079
|
+
if (projectMigrationMedia($.html(node), captured.finalUrl) !== null) {
|
|
6080
|
+
record.warnings.push({ code: "source-asset-warning", source: record.url, message: "Embedded video is preserved at its original public media URL; playback depends on the original host." });
|
|
6081
|
+
$(node).remove();
|
|
6082
|
+
}
|
|
6083
|
+
});
|
|
4067
6084
|
if (record.apiSpecification) {
|
|
4068
6085
|
root.find('select[aria-label="Select schema type"],select[aria-label="\u9009\u62E9\u67B6\u6784\u7C7B\u578B"]').remove();
|
|
4069
6086
|
root.find('.option-dropdown svg[data-component-part="icon-svg"]').remove();
|
|
4070
6087
|
}
|
|
4071
6088
|
const unsupported = [...new Set(root.find("iframe,object,embed,canvas,svg,form,input,textarea,select,math,video,audio").toArray().map((node) => node.tagName))];
|
|
4072
6089
|
if (unsupported.length) record.issues.push({ code: "unsupported-component", source: record.url, message: `Rendered content requires explicit review for: ${unsupported.join(", ")}.` });
|
|
4073
|
-
|
|
6090
|
+
const groups = captured === document ? coveredSchemaGroups : record.apiSpecification && schemaOperation ? apiSchemaTabGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
|
|
6091
|
+
const disclosures = captured === document ? coveredSchemaDisclosures : record.apiSpecification && schemaOperation ? apiSchemaDisclosureGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
|
|
6092
|
+
const codeControls = captured === document ? coveredCodeControls : record.apiSpecification && schemaOperation ? apiOperationCodeControls(captured.body, record.apiSpecification.document, schemaOperation) : [];
|
|
6093
|
+
if (unresolvedStaticControls(captured.body, groups, disclosures, codeControls)) record.issues.push({ code: "unsupported-component", source: record.url, message: "An interactive alternative has no complete captured panel or disclosure content." });
|
|
4074
6094
|
}
|
|
4075
|
-
const
|
|
6095
|
+
const inlineAssets = [document, ...states.map((state) => state.response)].flatMap((captured) => /html/i.test(captured.contentType) ? migrationHtmlArticle(captured.body).assets.map((asset) => ({
|
|
6096
|
+
url: new URL(`/${asset.path}`, captured.finalUrl).href,
|
|
6097
|
+
contentType: "image/svg+xml",
|
|
6098
|
+
content: typeof asset.content === "string" ? Buffer.from(asset.content) : asset.content
|
|
6099
|
+
})) : []);
|
|
6100
|
+
const suppliedAssets = new Map([...document.assets ?? [], ...inlineAssets].map((asset) => [canonical(asset.url, final), asset]));
|
|
4076
6101
|
const assetUrls = [...new Set([...assetReferences(document), ...states.flatMap((state) => assetReferences(state.response))].map((url) => canonical(url, final)).filter((url) => Boolean(url)))];
|
|
4077
6102
|
if (assetUrls.length > 256 || (document.assets?.length ?? 0) > 256) throw new Error("Capture exceeds the 256 assets per page safety bound.");
|
|
4078
6103
|
for (const url of assetUrls) {
|
|
@@ -4082,8 +6107,13 @@ ${difference}`;
|
|
|
4082
6107
|
assetRequest = assetRequests.load(url, () => assetFetcher(new URL(url), options.signal).then(retainAsset));
|
|
4083
6108
|
}
|
|
4084
6109
|
const supplied = suppliedAssets.get(url);
|
|
4085
|
-
const asset = supplied ? retainAsset(supplied) : assetRequest ? await assetRequest : null;
|
|
6110
|
+
const asset = supplied ? await retainAsset(supplied) : assetRequest ? await assetRequest : null;
|
|
4086
6111
|
if (!asset) throw new Error("Capture host did not supply asset bytes or a binary fetch boundary.");
|
|
6112
|
+
if (asset.unavailable && [404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) {
|
|
6113
|
+
record.warnings.push({ code: "asset-failed", source: record.url, message: `The original source asset is unavailable (HTTP ${asset.unavailable.status}); its original URL was retained: ${url}` });
|
|
6114
|
+
page.body = rewriteMarkdownDestinations(page.body, (destination) => canonical(destination, final) === url ? url : destination);
|
|
6115
|
+
continue;
|
|
6116
|
+
}
|
|
4087
6117
|
const extension = acceptedAsset(asset);
|
|
4088
6118
|
if (!extension) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
|
|
4089
6119
|
const sha256 = hash(asset.content);
|
|
@@ -4096,7 +6126,7 @@ ${difference}`;
|
|
|
4096
6126
|
}
|
|
4097
6127
|
}
|
|
4098
6128
|
const path = `migration-assets/${sha256}.${extension}`;
|
|
4099
|
-
const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64") };
|
|
6129
|
+
const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64"), ...asset.derivedFromSha256 ? { derivedFromSha256: asset.derivedFromSha256 } : {} };
|
|
4100
6130
|
record.assets.push(receipt);
|
|
4101
6131
|
if (!existing) assetPayloads.set(sha256, receipt);
|
|
4102
6132
|
if (!assetRequests.has(url)) assetRequests.remember(url, asset);
|
|
@@ -4121,11 +6151,12 @@ ${difference}`;
|
|
|
4121
6151
|
record.pageSha256 = pageHash(page);
|
|
4122
6152
|
record.outcome = "imported";
|
|
4123
6153
|
imported++;
|
|
6154
|
+
if (exactKey) exactCaptures.set(exactKey, record.url);
|
|
4124
6155
|
enqueueArticleLinks(evidence.links, final, record.url);
|
|
4125
6156
|
} catch (error) {
|
|
4126
6157
|
if (options.signal?.aborted) return;
|
|
4127
6158
|
const wasImported = record.outcome === "imported";
|
|
4128
|
-
record.outcome = record.discoveredBy.every((origin) => origin === "discovery-index") ? "excluded" : "failed";
|
|
6159
|
+
record.outcome = isDiscoveryIndex(new URL(record.url), source) || record.discoveredBy.every((origin) => origin === "discovery-index") ? "excluded" : "failed";
|
|
4129
6160
|
record.reason = error instanceof Error ? error.message : String(error);
|
|
4130
6161
|
if (record.page) {
|
|
4131
6162
|
if (wasImported) imported--;
|
|
@@ -4146,11 +6177,48 @@ ${difference}`;
|
|
|
4146
6177
|
}
|
|
4147
6178
|
}
|
|
4148
6179
|
};
|
|
6180
|
+
const settleArticleIdentities = () => {
|
|
6181
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6182
|
+
for (const record of records.values()) {
|
|
6183
|
+
const identity = record.evidence?.articleIdentity;
|
|
6184
|
+
if (record.outcome !== "imported" || !identity) continue;
|
|
6185
|
+
const key = JSON.stringify([identity.canonicalUrl, identity.articleSha256]);
|
|
6186
|
+
const group = groups.get(key) ?? [];
|
|
6187
|
+
group.push(record);
|
|
6188
|
+
groups.set(key, group);
|
|
6189
|
+
}
|
|
6190
|
+
for (const group of groups.values()) {
|
|
6191
|
+
if (group.length < 2) continue;
|
|
6192
|
+
const canonicalUrl = group[0].evidence.articleIdentity.canonicalUrl;
|
|
6193
|
+
const winner = group.find((record) => record.url === canonicalUrl) ?? group[0];
|
|
6194
|
+
const winnerAnchor = winner.evidence.articleIdentity.sectionAnchor;
|
|
6195
|
+
if (winnerAnchor && winner.aliasAnchor !== winnerAnchor) {
|
|
6196
|
+
winner.aliasAnchor = winnerAnchor;
|
|
6197
|
+
changed.push(winner);
|
|
6198
|
+
}
|
|
6199
|
+
for (const alternate of group) {
|
|
6200
|
+
if (alternate === winner) continue;
|
|
6201
|
+
imported--;
|
|
6202
|
+
if (alternate.page && usedIds.get(alternate.page.id) === alternate.url) usedIds.delete(alternate.page.id);
|
|
6203
|
+
alternate.outcome = "alias";
|
|
6204
|
+
alternate.aliasOf = winner.url;
|
|
6205
|
+
alternate.aliasAnchor = alternate.evidence.articleIdentity.sectionAnchor;
|
|
6206
|
+
delete alternate.page;
|
|
6207
|
+
delete alternate.pageSha256;
|
|
6208
|
+
delete alternate.assets;
|
|
6209
|
+
delete alternate.apiSpecification;
|
|
6210
|
+
delete alternate.issues;
|
|
6211
|
+
delete alternate.warnings;
|
|
6212
|
+
delete alternate.reason;
|
|
6213
|
+
changed.push(alternate);
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
};
|
|
4149
6217
|
const settleRepresentations = () => {
|
|
4150
6218
|
for (const record of records.values()) {
|
|
4151
6219
|
if (!record.representationOf) continue;
|
|
4152
6220
|
const primary = resolveMigrationRecord(records, records.get(record.representationOf));
|
|
4153
|
-
if (!primary) continue;
|
|
6221
|
+
if (!primary || primary === record) continue;
|
|
4154
6222
|
const winner = primary.outcome === "imported" ? primary : record.outcome === "imported" && primary.outcome === "failed" ? record : void 0;
|
|
4155
6223
|
if (!winner) continue;
|
|
4156
6224
|
const alternate = winner === primary ? record : primary;
|
|
@@ -4171,6 +6239,7 @@ ${difference}`;
|
|
|
4171
6239
|
}
|
|
4172
6240
|
};
|
|
4173
6241
|
settleRepresentations();
|
|
6242
|
+
settleArticleIdentities();
|
|
4174
6243
|
while (cursor < pending.length && !options.signal?.aborted && !budgetExhausted && cursor < (options.maxCaptures ?? Infinity)) {
|
|
4175
6244
|
if (imported >= maxPages) {
|
|
4176
6245
|
const index = pending.findIndex((record, position) => position >= cursor && isDiscoveryIndex(new URL(record.url), source));
|
|
@@ -4181,6 +6250,7 @@ ${difference}`;
|
|
|
4181
6250
|
cursor += batch.length;
|
|
4182
6251
|
await Promise.all(batch.map(process));
|
|
4183
6252
|
settleRepresentations();
|
|
6253
|
+
settleArticleIdentities();
|
|
4184
6254
|
if (changed.length >= batchSize) await flush();
|
|
4185
6255
|
}
|
|
4186
6256
|
await flush();
|
|
@@ -4200,7 +6270,7 @@ ${difference}`;
|
|
|
4200
6270
|
for (const node of nodes) {
|
|
4201
6271
|
const entry = node.url ? records.get(canonical(node.url, sourceUrl) ?? "") : void 0;
|
|
4202
6272
|
const target2 = resolveMigrationRecord(records, entry);
|
|
4203
|
-
if (target2?.page && node.label && !node.children?.length) {
|
|
6273
|
+
if (target2?.page && node.label && !node.children?.length && !entry?.aliasAnchor) {
|
|
4204
6274
|
const previous = navigationLabels.get(target2.url);
|
|
4205
6275
|
if (previous && previous !== node.label) navigationIssues.push({ code: "unsupported-component", source: target2.url, message: `This page has multiple source navigation labels (${previous}, ${node.label}); review the shared sidebar title.` });
|
|
4206
6276
|
else navigationLabels.set(target2.url, node.label);
|
|
@@ -4225,8 +6295,9 @@ ${difference}`;
|
|
|
4225
6295
|
anchor = decodeURIComponent(anchor);
|
|
4226
6296
|
} catch {
|
|
4227
6297
|
}
|
|
6298
|
+
const effectiveHash = url.hash || (records.get(identity)?.aliasAnchor ? `#${encodeURIComponent(records.get(identity).aliasAnchor)}` : "");
|
|
4228
6299
|
const hasBrokenSourceAnchor = Boolean(url.hash && destination?.outcome === "imported" && destination.evidence && !destination.evidence.anchors.includes(anchor));
|
|
4229
|
-
return `${route}${hasBrokenSourceAnchor ? "" :
|
|
6300
|
+
return `${route}${hasBrokenSourceAnchor ? "" : effectiveHash}`;
|
|
4230
6301
|
}) }] : []);
|
|
4231
6302
|
const validation = validateUrlMigration(checkpoint, Boolean(options.signal?.aborted));
|
|
4232
6303
|
validation.issues.push(...navigationIssues);
|
|
@@ -4276,7 +6347,7 @@ function rewriteMarkdownDestinations(body, rewrite) {
|
|
|
4276
6347
|
return { start: start + index, end: start + index + node.url.length, replacement };
|
|
4277
6348
|
}).filter((edit) => edit !== null).sort((a, b) => b.start - a.start);
|
|
4278
6349
|
for (const node of markdownNodes(body)) for (const attribute of node.attributes ?? []) {
|
|
4279
|
-
if (!attribute.name || !["src", "href", "srcSet", "srcset", "img", "primaryHref", "secondaryHref"].includes(attribute.name) || !attribute.position) continue;
|
|
6350
|
+
if (!attribute.name || !["src", "poster", "href", "srcSet", "srcset", "img", "primaryHref", "secondaryHref"].includes(attribute.name) || !attribute.position) continue;
|
|
4280
6351
|
let value = attribute.value;
|
|
4281
6352
|
if (typeof value !== "string") {
|
|
4282
6353
|
try {
|
|
@@ -4305,16 +6376,16 @@ function rewriteMarkdownDestinations(body, rewrite) {
|
|
|
4305
6376
|
}
|
|
4306
6377
|
|
|
4307
6378
|
// src/components.ts
|
|
4308
|
-
import { createHash as
|
|
6379
|
+
import { createHash as createHash5 } from "crypto";
|
|
4309
6380
|
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
|
|
4310
|
-
import { basename, dirname as dirname2, extname as extname3, posix as posix2, relative as relative3, resolve as
|
|
6381
|
+
import { basename, dirname as dirname2, extname as extname3, posix as posix2, relative as relative3, resolve as resolve3 } from "path";
|
|
4311
6382
|
import postcss from "postcss";
|
|
4312
6383
|
import selectorParser from "postcss-selector-parser";
|
|
4313
|
-
import
|
|
4314
|
-
import
|
|
6384
|
+
import remarkMdx5 from "remark-mdx";
|
|
6385
|
+
import remarkParse5 from "remark-parse";
|
|
4315
6386
|
import ts2 from "typescript";
|
|
4316
|
-
import { unified as
|
|
4317
|
-
var
|
|
6387
|
+
import { unified as unified5 } from "unified";
|
|
6388
|
+
var parser3 = unified5().use(remarkParse5).use(remarkMdx5);
|
|
4318
6389
|
var DOCUSAURUS_THEME_SHIMS = {
|
|
4319
6390
|
"@theme/CodeBlock": {
|
|
4320
6391
|
filename: "docusaurus-code-block.tsx",
|
|
@@ -4354,7 +6425,7 @@ function applyReplacements(source, replacements) {
|
|
|
4354
6425
|
);
|
|
4355
6426
|
}
|
|
4356
6427
|
function hash2(value) {
|
|
4357
|
-
return
|
|
6428
|
+
return createHash5("sha256").update(value).digest("hex").slice(0, 12);
|
|
4358
6429
|
}
|
|
4359
6430
|
function sourceFile(source, filename) {
|
|
4360
6431
|
return ts2.createSourceFile(filename, source, ts2.ScriptTarget.Latest, true, scriptKindFor(filename));
|
|
@@ -4529,6 +6600,7 @@ function renderMigrationFiles(bundle, options = {}) {
|
|
|
4529
6600
|
}
|
|
4530
6601
|
export {
|
|
4531
6602
|
classifyMigrationResponse,
|
|
6603
|
+
inferUrlMigrationScope,
|
|
4532
6604
|
renderMigrationFiles,
|
|
4533
6605
|
runUrlMigration,
|
|
4534
6606
|
validateUrlMigration
|