@thallylabs/migrate 0.2.37 → 0.2.39
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 +17 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +628 -228
- package/dist/index.js.map +1 -1
- package/dist/url-migration.d.ts +11 -1
- package/dist/url-migration.js +468 -67
- package/dist/url-migration.js.map +1 -1
- package/dist/{url-migration-peVzTLjL.d.ts → url-run-BOZvcGs1.d.ts} +11 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4462,9 +4462,9 @@ function jsonPointer(root, pointer) {
|
|
|
4462
4462
|
const index = Number(token);
|
|
4463
4463
|
return Number.isSafeInteger(index) && index >= 0 ? current[index] : void 0;
|
|
4464
4464
|
}
|
|
4465
|
-
const
|
|
4466
|
-
if (!
|
|
4467
|
-
return
|
|
4465
|
+
const object3 = objectValue2(current);
|
|
4466
|
+
if (!object3) return void 0;
|
|
4467
|
+
return object3[token.replace(/~1/g, "/").replace(/~0/g, "~")];
|
|
4468
4468
|
}, root);
|
|
4469
4469
|
}
|
|
4470
4470
|
function readMintlifyJson(path, repositoryRoot) {
|
|
@@ -4483,22 +4483,22 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
4483
4483
|
if (Array.isArray(value)) {
|
|
4484
4484
|
return value.map((entry) => resolveJsonReferences(entry, currentFile, repositoryRoot, stack));
|
|
4485
4485
|
}
|
|
4486
|
-
const
|
|
4487
|
-
if (!
|
|
4488
|
-
if (typeof
|
|
4489
|
-
const [filePart, pointer = ""] =
|
|
4486
|
+
const object3 = objectValue2(value);
|
|
4487
|
+
if (!object3) return value;
|
|
4488
|
+
if (typeof object3.$ref === "string") {
|
|
4489
|
+
const [filePart, pointer = ""] = object3.$ref.split("#", 2);
|
|
4490
4490
|
const referencedFile = filePart ? resolveWithin(dirname2(currentFile), filePart) : currentFile;
|
|
4491
4491
|
const relativePath = relative3(repositoryRoot, referencedFile);
|
|
4492
4492
|
resolveWithin(repositoryRoot, relativePath);
|
|
4493
4493
|
const stackKey = `${referencedFile}#${pointer}`;
|
|
4494
|
-
if (stack.has(stackKey)) throw new Error(`Circular Mintlify $ref: ${
|
|
4494
|
+
if (stack.has(stackKey)) throw new Error(`Circular Mintlify $ref: ${object3.$ref}`);
|
|
4495
4495
|
stack.add(stackKey);
|
|
4496
4496
|
const referencedRoot = readMintlifyJson(referencedFile, repositoryRoot);
|
|
4497
4497
|
const referenced = jsonPointer(referencedRoot, pointer ? `#${pointer}` : "");
|
|
4498
4498
|
const resolved = resolveJsonReferences(referenced, referencedFile, repositoryRoot, stack);
|
|
4499
4499
|
stack.delete(stackKey);
|
|
4500
4500
|
const siblings = Object.fromEntries(
|
|
4501
|
-
Object.entries(
|
|
4501
|
+
Object.entries(object3).filter(([key]) => key !== "$ref").map(([key, entry]) => [
|
|
4502
4502
|
key,
|
|
4503
4503
|
resolveJsonReferences(entry, currentFile, repositoryRoot, stack)
|
|
4504
4504
|
])
|
|
@@ -4506,7 +4506,7 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
4506
4506
|
return objectValue2(resolved) ? { ...resolved, ...siblings } : resolved;
|
|
4507
4507
|
}
|
|
4508
4508
|
return Object.fromEntries(
|
|
4509
|
-
Object.entries(
|
|
4509
|
+
Object.entries(object3).map(([key, entry]) => [
|
|
4510
4510
|
key,
|
|
4511
4511
|
resolveJsonReferences(entry, currentFile, repositoryRoot, stack)
|
|
4512
4512
|
])
|
|
@@ -4529,10 +4529,10 @@ function listedOperations(container) {
|
|
|
4529
4529
|
if (OPENAPI_OPERATION_REF.test(entry)) operations.push(entry.trim());
|
|
4530
4530
|
return;
|
|
4531
4531
|
}
|
|
4532
|
-
const
|
|
4533
|
-
if (!
|
|
4532
|
+
const object3 = objectValue2(entry);
|
|
4533
|
+
if (!object3 || depth > SWEEP_DEPTH_LIMIT || depth > 0 && (specSource(object3.openapi) || specSource(object3.asyncapi))) return;
|
|
4534
4534
|
for (const key of ["groups", "pages"]) {
|
|
4535
|
-
if (Array.isArray(
|
|
4535
|
+
if (Array.isArray(object3[key])) object3[key].forEach((child) => visit(child, depth + 1));
|
|
4536
4536
|
}
|
|
4537
4537
|
};
|
|
4538
4538
|
visit(container, 0);
|
|
@@ -4545,8 +4545,8 @@ var operationKey = (entry) => entry.trim().replace(/^(\S+)\s+/, "$1 ");
|
|
|
4545
4545
|
function operationGroups(container, maxLevel = API_GROUPS_MAX_DEPTH) {
|
|
4546
4546
|
const entries = (node, level) => ["groups", "pages"].flatMap((key) => (Array.isArray(node[key]) ? node[key] : []).flatMap((entry) => {
|
|
4547
4547
|
if (typeof entry === "string") return OPENAPI_OPERATION_REF.test(entry) ? [operationKey(entry)] : [];
|
|
4548
|
-
const
|
|
4549
|
-
const group =
|
|
4548
|
+
const object3 = objectValue2(entry);
|
|
4549
|
+
const group = object3 && !bindsSpec(object3) ? groupOf(object3, level + 1) : void 0;
|
|
4550
4550
|
return group ? [group] : [];
|
|
4551
4551
|
}));
|
|
4552
4552
|
const groupOf = (node, level) => {
|
|
@@ -4641,9 +4641,9 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
4641
4641
|
const entries = node[key];
|
|
4642
4642
|
if (!Array.isArray(entries)) continue;
|
|
4643
4643
|
for (const entry of entries) {
|
|
4644
|
-
const
|
|
4645
|
-
if (!
|
|
4646
|
-
visit(
|
|
4644
|
+
const object3 = objectValue2(entry);
|
|
4645
|
+
if (!object3) continue;
|
|
4646
|
+
visit(object3, labelFor(object3, tabLabel ?? "Documentation"), gated, otherLanguage || key === "languages" && rawNode === navigation && object3 !== defaultLanguage, hidden);
|
|
4647
4647
|
}
|
|
4648
4648
|
}
|
|
4649
4649
|
for (const key of nestedKeys) {
|
|
@@ -4651,15 +4651,15 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
4651
4651
|
if (!Array.isArray(entries)) continue;
|
|
4652
4652
|
for (const entry of entries) {
|
|
4653
4653
|
if (typeof entry === "string") continue;
|
|
4654
|
-
const
|
|
4655
|
-
if (
|
|
4654
|
+
const object3 = objectValue2(entry);
|
|
4655
|
+
if (object3) visit(object3, tabLabel, gated, otherLanguage, hidden || object3.hidden === true);
|
|
4656
4656
|
}
|
|
4657
4657
|
}
|
|
4658
4658
|
if (Array.isArray(node.menu)) {
|
|
4659
4659
|
const tab = tabLabel ?? "Documentation";
|
|
4660
4660
|
const items = node.menu.flatMap((entry) => {
|
|
4661
|
-
const
|
|
4662
|
-
return
|
|
4661
|
+
const object3 = objectValue2(entry);
|
|
4662
|
+
return object3 ? [object3] : [];
|
|
4663
4663
|
});
|
|
4664
4664
|
const apiItems = items.filter((item) => specSource(item.openapi) || specSource(item.asyncapi));
|
|
4665
4665
|
const soleContent = apiItems.length === 1 && !hasNavigationChildren(node) && !items.some(hasNavigationChildren);
|
|
@@ -4727,11 +4727,11 @@ function collectGatedSourceRefs(node, gate, out, limits, depth = 0) {
|
|
|
4727
4727
|
for (const item of node) collectGatedSourceRefs(item, gate, out, limits, depth + 1);
|
|
4728
4728
|
return;
|
|
4729
4729
|
}
|
|
4730
|
-
const
|
|
4731
|
-
if (!
|
|
4732
|
-
const reason = gate ?? navigationGateReason(
|
|
4733
|
-
if (reason && typeof
|
|
4734
|
-
for (const value of Object.values(
|
|
4730
|
+
const object3 = objectValue2(node);
|
|
4731
|
+
if (!object3) return;
|
|
4732
|
+
const reason = gate ?? navigationGateReason(object3);
|
|
4733
|
+
if (reason && typeof object3.sourceRef === "string") out.set(object3, reason);
|
|
4734
|
+
for (const value of Object.values(object3)) collectGatedSourceRefs(value, reason, out, limits, depth + 1);
|
|
4735
4735
|
}
|
|
4736
4736
|
function collectReferenceAccess(node, access, pathPrefix, out, depth = 0) {
|
|
4737
4737
|
if (depth > SWEEP_DEPTH_LIMIT) return;
|
|
@@ -4739,29 +4739,29 @@ function collectReferenceAccess(node, access, pathPrefix, out, depth = 0) {
|
|
|
4739
4739
|
for (const item of node) collectReferenceAccess(item, access, pathPrefix, out, depth + 1);
|
|
4740
4740
|
return;
|
|
4741
4741
|
}
|
|
4742
|
-
const
|
|
4743
|
-
if (!
|
|
4744
|
-
const nested = descendNavigationAccess(access, navigationAccess(
|
|
4742
|
+
const object3 = objectValue2(node);
|
|
4743
|
+
if (!object3) return;
|
|
4744
|
+
const nested = descendNavigationAccess(access, navigationAccess(object3));
|
|
4745
4745
|
if (hasNavigationRules(nested)) {
|
|
4746
|
-
const refs = [
|
|
4746
|
+
const refs = [object3.page, object3.root, object3.href, ...Array.isArray(object3.pages) ? object3.pages : []];
|
|
4747
4747
|
for (const ref of refs) {
|
|
4748
4748
|
if (typeof ref === "string" && normalizePageRef(ref, pathPrefix)) out.push({ ref, access: nested });
|
|
4749
4749
|
}
|
|
4750
4750
|
}
|
|
4751
|
-
for (const value of Object.values(
|
|
4751
|
+
for (const value of Object.values(object3)) collectReferenceAccess(value, nested, pathPrefix, out, depth + 1);
|
|
4752
4752
|
}
|
|
4753
|
-
function withContainerAccess(
|
|
4753
|
+
function withContainerAccess(object3, context, walk2, nested = false) {
|
|
4754
4754
|
const outer = context.access;
|
|
4755
|
-
context.access = descendNavigationAccess(outer, navigationAccess(
|
|
4755
|
+
context.access = descendNavigationAccess(outer, navigationAccess(object3));
|
|
4756
4756
|
try {
|
|
4757
|
-
return withOperationScope(
|
|
4757
|
+
return withOperationScope(object3, context, nested, walk2);
|
|
4758
4758
|
} finally {
|
|
4759
4759
|
context.access = outer;
|
|
4760
4760
|
}
|
|
4761
4761
|
}
|
|
4762
|
-
function withOperationScope(
|
|
4762
|
+
function withOperationScope(object3, context, nested, walk2) {
|
|
4763
4763
|
const outer = context.operations;
|
|
4764
|
-
context.operations = descendOperationScope(outer,
|
|
4764
|
+
context.operations = descendOperationScope(outer, object3, nested);
|
|
4765
4765
|
try {
|
|
4766
4766
|
return walk2();
|
|
4767
4767
|
} finally {
|
|
@@ -4835,9 +4835,9 @@ function convertPage(value, context) {
|
|
|
4835
4835
|
}
|
|
4836
4836
|
return registerReference(value, context);
|
|
4837
4837
|
}
|
|
4838
|
-
const
|
|
4839
|
-
if (!
|
|
4840
|
-
return withContainerAccess(
|
|
4838
|
+
const object3 = objectValue2(value);
|
|
4839
|
+
if (!object3) return null;
|
|
4840
|
+
return withContainerAccess(object3, context, () => convertPageObject(object3, context));
|
|
4841
4841
|
}
|
|
4842
4842
|
function gatedSourceRef(repo, context) {
|
|
4843
4843
|
if (!context.gatedSourceRefs.includes(repo)) context.gatedSourceRefs.push(repo);
|
|
@@ -4848,10 +4848,10 @@ function gatedSourceRef(repo, context) {
|
|
|
4848
4848
|
);
|
|
4849
4849
|
return null;
|
|
4850
4850
|
}
|
|
4851
|
-
function convertPageObject(
|
|
4852
|
-
if (typeof
|
|
4853
|
-
const href = projectedHref(
|
|
4854
|
-
if (href && !
|
|
4851
|
+
function convertPageObject(object3, context) {
|
|
4852
|
+
if (typeof object3.page === "string") return registerReference(object3.page, context);
|
|
4853
|
+
const href = projectedHref(object3.href);
|
|
4854
|
+
if (href && !object3.pages && !object3.groups) {
|
|
4855
4855
|
const page = registerReference(href, context);
|
|
4856
4856
|
if (page) return page;
|
|
4857
4857
|
warnOnce(
|
|
@@ -4861,25 +4861,25 @@ function convertPageObject(object2, context) {
|
|
|
4861
4861
|
);
|
|
4862
4862
|
return null;
|
|
4863
4863
|
}
|
|
4864
|
-
if (typeof
|
|
4865
|
-
if (isNavigationRestricted(context.access) || context.gatedNodes.has(
|
|
4866
|
-
const imported = context.resolveSourceRef?.(
|
|
4867
|
-
if (imported) return { group:
|
|
4864
|
+
if (typeof object3.sourceRef === "string") {
|
|
4865
|
+
if (isNavigationRestricted(context.access) || context.gatedNodes.has(object3) || context.limits.tooDeep) return gatedSourceRef(object3.sourceRef, context);
|
|
4866
|
+
const imported = context.resolveSourceRef?.(object3.sourceRef);
|
|
4867
|
+
if (imported) return { group: object3.sourceRef.split("/").pop() ?? object3.sourceRef, pages: imported };
|
|
4868
4868
|
const where = context.groupLabel ? `the "${context.groupLabel}" navigation group` : "the navigation";
|
|
4869
4869
|
warnOnce(
|
|
4870
4870
|
context,
|
|
4871
|
-
`source-ref:${context.groupLabel ?? ""}:${
|
|
4872
|
-
`Navigation in ${where} mounts the docs of the repository ${
|
|
4871
|
+
`source-ref:${context.groupLabel ?? ""}:${object3.sourceRef}`,
|
|
4872
|
+
`Navigation in ${where} mounts the docs of the repository ${object3.sourceRef} (sourceRef). Those pages were not migrated; pass --source-ref ${object3.sourceRef}=<path> to import them.`
|
|
4873
4873
|
);
|
|
4874
4874
|
return null;
|
|
4875
4875
|
}
|
|
4876
|
-
const pages = Array.isArray(
|
|
4877
|
-
if ("group" in
|
|
4876
|
+
const pages = Array.isArray(object3.pages) ? object3.pages : [];
|
|
4877
|
+
if ("group" in object3 || pages.length > 0) {
|
|
4878
4878
|
const children = [];
|
|
4879
4879
|
const outerGroupLabel = context.groupLabel;
|
|
4880
|
-
context.groupLabel = labelFor(
|
|
4881
|
-
if (typeof
|
|
4882
|
-
const root = registerReference(
|
|
4880
|
+
context.groupLabel = labelFor(object3, "Documentation");
|
|
4881
|
+
if (typeof object3.root === "string") {
|
|
4882
|
+
const root = registerReference(object3.root, context);
|
|
4883
4883
|
if (root) children.push(root);
|
|
4884
4884
|
}
|
|
4885
4885
|
try {
|
|
@@ -4899,9 +4899,9 @@ function convertPageObject(object2, context) {
|
|
|
4899
4899
|
}
|
|
4900
4900
|
if (children.length === 0) return null;
|
|
4901
4901
|
return {
|
|
4902
|
-
group: labelFor(
|
|
4903
|
-
...iconName(
|
|
4904
|
-
...
|
|
4902
|
+
group: labelFor(object3, "Documentation"),
|
|
4903
|
+
...iconName(object3.icon) ? { icon: iconName(object3.icon) } : {},
|
|
4904
|
+
...object3.hidden === true ? { hidden: true } : {},
|
|
4905
4905
|
pages: children
|
|
4906
4906
|
};
|
|
4907
4907
|
}
|
|
@@ -4991,37 +4991,37 @@ function convertContainerToTabs(containerValue, context, fallbackTab, trace, dep
|
|
|
4991
4991
|
entries.sort((left, right) => Number(objectValue2(right)?.default === true) - Number(objectValue2(left)?.default === true));
|
|
4992
4992
|
}
|
|
4993
4993
|
const tabs = entries.flatMap((value, index) => {
|
|
4994
|
-
const
|
|
4995
|
-
if (!
|
|
4996
|
-
const tab = labelFor(
|
|
4997
|
-
const href = projectedHref(
|
|
4998
|
-
const hasNestedContainers = [...containerKeys, "menu"].some((containerKey) => Array.isArray(
|
|
4999
|
-
if (href && !
|
|
4994
|
+
const object3 = objectValue2(value);
|
|
4995
|
+
if (!object3) return [];
|
|
4996
|
+
const tab = labelFor(object3, `${fallbackTab} ${index + 1}`);
|
|
4997
|
+
const href = projectedHref(object3.href);
|
|
4998
|
+
const hasNestedContainers = [...containerKeys, "menu"].some((containerKey) => Array.isArray(object3[containerKey]));
|
|
4999
|
+
if (href && !object3.pages && !object3.groups && !hasNestedContainers) {
|
|
5000
5000
|
return [{
|
|
5001
5001
|
tab,
|
|
5002
5002
|
href,
|
|
5003
|
-
...containerPresentation(
|
|
5004
|
-
...
|
|
5003
|
+
...containerPresentation(object3),
|
|
5004
|
+
...object3.hidden === true ? { hidden: true } : {}
|
|
5005
5005
|
}];
|
|
5006
5006
|
}
|
|
5007
|
-
const nested = withContainerAccess(
|
|
5007
|
+
const nested = withContainerAccess(object3, context, () => convertContainerToTabs(object3, context, tab, trace, depth + 1), true);
|
|
5008
5008
|
if (nested.length > 0) {
|
|
5009
5009
|
if (nested.length === 1) {
|
|
5010
5010
|
return [{
|
|
5011
5011
|
...nested[0],
|
|
5012
5012
|
tab,
|
|
5013
|
-
...containerPresentation(
|
|
5013
|
+
...containerPresentation(object3),
|
|
5014
5014
|
...href ? { href } : {},
|
|
5015
|
-
...
|
|
5015
|
+
...object3.hidden === true ? { hidden: true } : {}
|
|
5016
5016
|
}];
|
|
5017
5017
|
}
|
|
5018
|
-
const presentation = containerPresentation(
|
|
5018
|
+
const presentation = containerPresentation(object3);
|
|
5019
5019
|
return nested.map((item, childIndex) => ({
|
|
5020
5020
|
...presentation,
|
|
5021
5021
|
...item,
|
|
5022
5022
|
tab: `${tab}: ${item.tab}`,
|
|
5023
5023
|
...childIndex === 0 && href && !item.href ? { href } : {},
|
|
5024
|
-
...
|
|
5024
|
+
...object3.hidden === true ? { hidden: true } : {}
|
|
5025
5025
|
}));
|
|
5026
5026
|
}
|
|
5027
5027
|
return [];
|
|
@@ -5820,8 +5820,8 @@ function registerPageAt(rawPath, base, context, navTitle, hidden = false) {
|
|
|
5820
5820
|
context.sourcePathToNavigationId.set(sourcePath, navigationId);
|
|
5821
5821
|
return navigationId;
|
|
5822
5822
|
}
|
|
5823
|
-
function registerFolder(
|
|
5824
|
-
const rawPath = String(
|
|
5823
|
+
function registerFolder(object3, parentSegments, context) {
|
|
5824
|
+
const rawPath = String(object3.folder).trim();
|
|
5825
5825
|
let folder;
|
|
5826
5826
|
try {
|
|
5827
5827
|
folder = withinRealRoot(resolveWithinRoot(
|
|
@@ -5837,8 +5837,8 @@ function registerFolder(object2, parentSegments, context) {
|
|
|
5837
5837
|
warnOnce2(context, `fern-missing-folder-${rawPath}`, `Fern folder "${rawPath}" does not exist and was skipped.`);
|
|
5838
5838
|
return null;
|
|
5839
5839
|
}
|
|
5840
|
-
const label = typeof
|
|
5841
|
-
const segment = typeof
|
|
5840
|
+
const label = typeof object3.title === "string" && object3.title.trim() ? object3.title.trim() : titleCase2(basename2(folder));
|
|
5841
|
+
const segment = typeof object3.slug === "string" || object3["skip-slug"] === true ? segmentFor(object3, label, context) : fernBasicSlug(basename2(folder));
|
|
5842
5842
|
const segments = segment ? [...parentSegments, segment] : parentSegments;
|
|
5843
5843
|
const visit = (directory, routeSegments, depth) => {
|
|
5844
5844
|
if (depth > 12 || context.descriptors.length >= 5e3) {
|
|
@@ -5871,17 +5871,17 @@ function registerFolder(object2, parentSegments, context) {
|
|
|
5871
5871
|
addBareRouteRedirect(segments, pages, context);
|
|
5872
5872
|
return { group: label, pages };
|
|
5873
5873
|
}
|
|
5874
|
-
function registerPage(
|
|
5875
|
-
if (typeof
|
|
5876
|
-
const label = typeof
|
|
5877
|
-
const segment = segmentFor(
|
|
5874
|
+
function registerPage(object3, parentSegments, context) {
|
|
5875
|
+
if (typeof object3.path !== "string" || !object3.path.trim()) return null;
|
|
5876
|
+
const label = typeof object3.page === "string" && object3.page.trim() ? object3.page.trim() : "Untitled";
|
|
5877
|
+
const segment = segmentFor(object3, label, context);
|
|
5878
5878
|
const base = [...parentSegments, segment].filter(Boolean).join("/");
|
|
5879
|
-
return registerPageAt(
|
|
5879
|
+
return registerPageAt(object3.path, base, context, label, object3.hidden === true) || null;
|
|
5880
5880
|
}
|
|
5881
|
-
function isRestrictedNode(
|
|
5882
|
-
const viewers =
|
|
5881
|
+
function isRestrictedNode(object3) {
|
|
5882
|
+
const viewers = object3.viewers;
|
|
5883
5883
|
if (Array.isArray(viewers) ? viewers.length > 0 : typeof viewers === "string" ? viewers.trim() !== "" : viewers !== void 0 && viewers !== null && viewers !== false) return true;
|
|
5884
|
-
const authed =
|
|
5884
|
+
const authed = object3.authed;
|
|
5885
5885
|
return authed !== void 0 && authed !== null && authed !== false && !(typeof authed === "string" && ["", "false"].includes(authed.trim().toLowerCase()));
|
|
5886
5886
|
}
|
|
5887
5887
|
function restrictionMark(context) {
|
|
@@ -5892,67 +5892,67 @@ function markRestricted(context, mark) {
|
|
|
5892
5892
|
context.restrictedApiSections.push(...context.apiSections.splice(mark.apis));
|
|
5893
5893
|
}
|
|
5894
5894
|
function convertNode(node, parentSegments, context) {
|
|
5895
|
-
const
|
|
5896
|
-
if (!
|
|
5895
|
+
const object3 = objectValue3(node);
|
|
5896
|
+
if (!object3) return null;
|
|
5897
5897
|
const walk2 = context.navigationWalk;
|
|
5898
|
-
const reason = walk2.active.has(
|
|
5898
|
+
const reason = walk2.active.has(object3) ? "cyclic section" : walk2.active.size >= 64 ? "section depth exceeds 64" : walk2.visits >= 5e3 ? "node budget exceeds 5000" : void 0;
|
|
5899
5899
|
if (reason) {
|
|
5900
5900
|
walk2.incomplete = true;
|
|
5901
5901
|
warnOnce2(context, `navigation-${reason}`, `Fern navigation was skipped: ${reason}.`);
|
|
5902
5902
|
return null;
|
|
5903
5903
|
}
|
|
5904
5904
|
walk2.visits++;
|
|
5905
|
-
walk2.active.add(
|
|
5905
|
+
walk2.active.add(object3);
|
|
5906
5906
|
try {
|
|
5907
|
-
if (isRestrictedNode(
|
|
5907
|
+
if (isRestrictedNode(object3)) {
|
|
5908
5908
|
const before = restrictionMark(context);
|
|
5909
|
-
convertNodeContents({ ...
|
|
5909
|
+
convertNodeContents({ ...object3, viewers: void 0, authed: void 0 }, parentSegments, context);
|
|
5910
5910
|
markRestricted(context, before);
|
|
5911
5911
|
return null;
|
|
5912
5912
|
}
|
|
5913
|
-
return convertNodeContents(
|
|
5913
|
+
return convertNodeContents(object3, parentSegments, context);
|
|
5914
5914
|
} finally {
|
|
5915
|
-
walk2.active.delete(
|
|
5915
|
+
walk2.active.delete(object3);
|
|
5916
5916
|
}
|
|
5917
5917
|
}
|
|
5918
|
-
function convertNodeContents(
|
|
5919
|
-
if (typeof
|
|
5920
|
-
const page = registerPage(
|
|
5921
|
-
return
|
|
5918
|
+
function convertNodeContents(object3, parentSegments, context) {
|
|
5919
|
+
if (typeof object3.page === "string") {
|
|
5920
|
+
const page = registerPage(object3, parentSegments, context);
|
|
5921
|
+
return object3.hidden === true || context.hiddenInherited ? null : page;
|
|
5922
5922
|
}
|
|
5923
|
-
if (typeof
|
|
5924
|
-
const folder = registerFolder(
|
|
5925
|
-
return
|
|
5923
|
+
if (typeof object3.folder === "string") {
|
|
5924
|
+
const folder = registerFolder(object3, parentSegments, object3.hidden === true ? { ...context, hiddenInherited: true } : context);
|
|
5925
|
+
return object3.hidden === true || context.hiddenInherited ? null : folder;
|
|
5926
5926
|
}
|
|
5927
|
-
if (typeof
|
|
5928
|
-
const label =
|
|
5929
|
-
const segment = segmentFor(
|
|
5927
|
+
if (typeof object3.section === "string") {
|
|
5928
|
+
const label = object3.section;
|
|
5929
|
+
const segment = segmentFor(object3, label, context);
|
|
5930
5930
|
const segments = segment ? [...parentSegments, segment] : parentSegments;
|
|
5931
|
-
const contents = Array.isArray(
|
|
5931
|
+
const contents = Array.isArray(object3.contents) ? object3.contents : [];
|
|
5932
5932
|
const pages = [];
|
|
5933
|
-
const hasOwnPath = typeof
|
|
5933
|
+
const hasOwnPath = typeof object3.path === "string" && object3.path.trim().length > 0;
|
|
5934
5934
|
if (hasOwnPath) {
|
|
5935
|
-
const registered = registerPageAt(
|
|
5935
|
+
const registered = registerPageAt(object3.path, segments.join("/"), context, label, object3.hidden === true);
|
|
5936
5936
|
if (registered) pages.push(registered);
|
|
5937
5937
|
}
|
|
5938
5938
|
for (const child of contents) {
|
|
5939
|
-
const converted = convertNode(child, segments,
|
|
5939
|
+
const converted = convertNode(child, segments, object3.hidden === true ? { ...context, hiddenInherited: true } : context);
|
|
5940
5940
|
if (converted) pages.push(converted);
|
|
5941
5941
|
}
|
|
5942
5942
|
if (pages.length === 0) return null;
|
|
5943
5943
|
if (!hasOwnPath && segment) addBareRouteRedirect(segments, pages, context);
|
|
5944
|
-
if (
|
|
5944
|
+
if (object3.hidden === true || context.hiddenInherited) return null;
|
|
5945
5945
|
return {
|
|
5946
5946
|
group: label,
|
|
5947
|
-
...["open-by-default", "closed-by-default", true, false].includes(
|
|
5948
|
-
...typeof
|
|
5949
|
-
...
|
|
5947
|
+
...["open-by-default", "closed-by-default", true, false].includes(object3.collapsed) ? { defaultOpen: object3.collapsed === "open-by-default" || object3.collapsed === false } : {},
|
|
5948
|
+
...typeof object3.icon === "string" ? { icon: object3.icon } : {},
|
|
5949
|
+
...object3.hidden === true ? { hidden: true } : {},
|
|
5950
5950
|
pages
|
|
5951
5951
|
};
|
|
5952
5952
|
}
|
|
5953
|
-
if (typeof
|
|
5954
|
-
const label =
|
|
5955
|
-
const rawHref = typeof
|
|
5953
|
+
if (typeof object3.link === "string") {
|
|
5954
|
+
const label = object3.link.trim();
|
|
5955
|
+
const rawHref = typeof object3.href === "string" ? object3.href.trim() : "";
|
|
5956
5956
|
let href;
|
|
5957
5957
|
if (/^https?:\/\//i.test(rawHref) && !/[\\\u0000-\u0020\u007f]/.test(rawHref)) {
|
|
5958
5958
|
try {
|
|
@@ -5961,26 +5961,26 @@ function convertNodeContents(object2, parentSegments, context) {
|
|
|
5961
5961
|
} catch {
|
|
5962
5962
|
}
|
|
5963
5963
|
}
|
|
5964
|
-
if (label && href &&
|
|
5964
|
+
if (label && href && object3.hidden !== true && !context.hiddenInherited) {
|
|
5965
5965
|
if (!context.externalLinks.some((link) => link.label === label && link.href === href)) {
|
|
5966
5966
|
context.externalLinks.push({ label, href });
|
|
5967
5967
|
}
|
|
5968
|
-
} else if (!href &&
|
|
5968
|
+
} else if (!href && object3.hidden !== true && !context.hiddenInherited) {
|
|
5969
5969
|
warnOnce2(context, `fern-link-${rawHref}`, `Fern navigation link "${label}" has an unsafe or invalid external URL and was skipped.`);
|
|
5970
5970
|
}
|
|
5971
5971
|
return null;
|
|
5972
5972
|
}
|
|
5973
|
-
if (typeof
|
|
5974
|
-
const ownSegment = segmentFor(
|
|
5973
|
+
if (typeof object3.api === "string") {
|
|
5974
|
+
const ownSegment = segmentFor(object3, object3.api, context);
|
|
5975
5975
|
context.apiSections.push({
|
|
5976
|
-
name: typeof
|
|
5977
|
-
nameExplicit: typeof
|
|
5976
|
+
name: typeof object3["api-name"] === "string" ? object3["api-name"] : object3.api,
|
|
5977
|
+
nameExplicit: typeof object3["api-name"] === "string",
|
|
5978
5978
|
routeSegments: ownSegment ? [...parentSegments, ownSegment] : parentSegments
|
|
5979
5979
|
});
|
|
5980
5980
|
return null;
|
|
5981
5981
|
}
|
|
5982
|
-
if (typeof
|
|
5983
|
-
return registerFolder({ folder:
|
|
5982
|
+
if (typeof object3.changelog === "string") {
|
|
5983
|
+
return registerFolder({ folder: object3.changelog, title: "Changelog", "skip-slug": true }, parentSegments, context);
|
|
5984
5984
|
}
|
|
5985
5985
|
return null;
|
|
5986
5986
|
}
|
|
@@ -6643,14 +6643,14 @@ function parseStaticSidebarModule(source) {
|
|
|
6643
6643
|
literal
|
|
6644
6644
|
);
|
|
6645
6645
|
const parsed = JSON5.parse(substituted);
|
|
6646
|
-
const
|
|
6647
|
-
if (!
|
|
6646
|
+
const object3 = objectValue4(parsed);
|
|
6647
|
+
if (!object3) continue;
|
|
6648
6648
|
const bindingName = candidate[0].match(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/)?.[1];
|
|
6649
6649
|
if (bindingName) {
|
|
6650
|
-
bindings.set(bindingName,
|
|
6650
|
+
bindings.set(bindingName, object3);
|
|
6651
6651
|
continue;
|
|
6652
6652
|
}
|
|
6653
|
-
return
|
|
6653
|
+
return object3;
|
|
6654
6654
|
} catch (error) {
|
|
6655
6655
|
const message = error instanceof Error ? error.message : String(error);
|
|
6656
6656
|
const line = Number(message.match(/at (\d+):/)?.[1] ?? 0);
|
|
@@ -6739,8 +6739,8 @@ function configuredSidebarPath(repositoryRoot) {
|
|
|
6739
6739
|
if (!existsSync4(path)) continue;
|
|
6740
6740
|
const source = readBoundedText(path);
|
|
6741
6741
|
for (const docs of source.matchAll(/\bdocs\s*:\s*/g)) {
|
|
6742
|
-
const
|
|
6743
|
-
const candidate2 =
|
|
6742
|
+
const object3 = matchingObjectLiteral(source, (docs.index ?? 0) + docs[0].length);
|
|
6743
|
+
const candidate2 = object3 && staticStringField(object3, "sidebarPath")?.replace(/^\.\//, "");
|
|
6744
6744
|
if (candidate2) {
|
|
6745
6745
|
const resolved2 = withinRealRoot(resolveWithin(repositoryRoot, candidate2), repositoryRoot);
|
|
6746
6746
|
if (existsSync4(resolved2) && lstatSync4(resolved2).isFile()) return candidate2;
|
|
@@ -6851,12 +6851,12 @@ function readCategoryMetadata(contentRoot, directory) {
|
|
|
6851
6851
|
try {
|
|
6852
6852
|
const raw = readBoundedText(path);
|
|
6853
6853
|
const parsed = filename.endsWith(".json") ? JSON5.parse(raw) : parseYaml3(raw);
|
|
6854
|
-
const
|
|
6855
|
-
if (!
|
|
6854
|
+
const object3 = objectValue4(parsed);
|
|
6855
|
+
if (!object3) return {};
|
|
6856
6856
|
return {
|
|
6857
|
-
...typeof
|
|
6858
|
-
...typeof
|
|
6859
|
-
..."link" in
|
|
6857
|
+
...typeof object3.label === "string" ? { label: object3.label } : {},
|
|
6858
|
+
...typeof object3.position === "number" ? { position: object3.position } : {},
|
|
6859
|
+
..."link" in object3 ? { link: object3.link } : {}
|
|
6860
6860
|
};
|
|
6861
6861
|
} catch {
|
|
6862
6862
|
return {};
|
|
@@ -7040,7 +7040,7 @@ function readDocusaurusSiteSettings(repositoryRoot) {
|
|
|
7040
7040
|
const source = readBoundedText(configPath);
|
|
7041
7041
|
const bindings = staticStringBindings(source);
|
|
7042
7042
|
const presets = staticArrayField(source, "presets");
|
|
7043
|
-
const classicDocs = presets && [...presets.matchAll(/["']?docs["']?\s*:\s*/g)].map((match) => matchingObjectLiteral(presets, (match.index ?? 0) + match[0].length)).find((
|
|
7043
|
+
const classicDocs = presets && [...presets.matchAll(/["']?docs["']?\s*:\s*/g)].map((match) => matchingObjectLiteral(presets, (match.index ?? 0) + match[0].length)).find((object3) => Boolean(object3));
|
|
7044
7044
|
const docsRouteBasePath = classicDocs && staticStringField(classicDocs, "routeBasePath") || "docs";
|
|
7045
7045
|
const themeStart = propertyStart(source, "themeConfig");
|
|
7046
7046
|
const theme = themeStart < 0 ? null : matchingObjectLiteral(source, themeStart);
|
|
@@ -7163,24 +7163,24 @@ function convertItems(value, context) {
|
|
|
7163
7163
|
return page ? [page] : [];
|
|
7164
7164
|
}
|
|
7165
7165
|
if (Array.isArray(value)) return value.flatMap((entry) => convertItems(entry, context));
|
|
7166
|
-
const
|
|
7167
|
-
if (!
|
|
7168
|
-
if (
|
|
7169
|
-
const page = typeof
|
|
7166
|
+
const object3 = objectValue4(value);
|
|
7167
|
+
if (!object3) return [];
|
|
7168
|
+
if (object3.type === "doc" || object3.type === "ref") {
|
|
7169
|
+
const page = typeof object3.id === "string" ? registerDoc(object3.id, context) : null;
|
|
7170
7170
|
return page ? [page] : [];
|
|
7171
7171
|
}
|
|
7172
|
-
if (
|
|
7173
|
-
return autogeneratedItems(typeof
|
|
7172
|
+
if (object3.type === "autogenerated") {
|
|
7173
|
+
return autogeneratedItems(typeof object3.dirName === "string" ? object3.dirName : ".", context);
|
|
7174
7174
|
}
|
|
7175
|
-
if (
|
|
7176
|
-
const label = typeof
|
|
7177
|
-
const pages = convertItems(
|
|
7178
|
-
const landing = generatedIndexPage(label,
|
|
7175
|
+
if (object3.type === "category") {
|
|
7176
|
+
const label = typeof object3.label === "string" ? object3.label : "Documentation";
|
|
7177
|
+
const pages = convertItems(object3.items, context);
|
|
7178
|
+
const landing = generatedIndexPage(label, object3.link, context);
|
|
7179
7179
|
if (landing && !pages.includes(landing)) pages.unshift(landing);
|
|
7180
7180
|
return pages.length > 0 ? [{ group: label, pages }] : [];
|
|
7181
7181
|
}
|
|
7182
|
-
if (
|
|
7183
|
-
return Object.entries(
|
|
7182
|
+
if (object3.type === "link" || object3.type === "html") return [];
|
|
7183
|
+
return Object.entries(object3).flatMap(([label, items]) => {
|
|
7184
7184
|
const pages = convertItems(items, context);
|
|
7185
7185
|
return pages.length > 0 ? [{ group: label, pages }] : [];
|
|
7186
7186
|
});
|
|
@@ -7552,7 +7552,7 @@ import { lstatSync as lstatSync5, readdirSync as readdirSync3, readFileSync as r
|
|
|
7552
7552
|
import { isAbsolute as isAbsolute2, join, relative as relative5, resolve as resolve3, sep as sep2 } from "path";
|
|
7553
7553
|
|
|
7554
7554
|
// src/url.ts
|
|
7555
|
-
import { load as
|
|
7555
|
+
import { load as load3 } from "cheerio";
|
|
7556
7556
|
import remarkMdx3 from "remark-mdx";
|
|
7557
7557
|
import remarkParse3 from "remark-parse";
|
|
7558
7558
|
import TurndownService from "turndown";
|
|
@@ -7738,7 +7738,7 @@ function migrationImageUrl(value, base) {
|
|
|
7738
7738
|
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error("Responsive image URL is not a credential-free HTTP(S) URL.");
|
|
7739
7739
|
return url.toString();
|
|
7740
7740
|
}
|
|
7741
|
-
function migrationImageCandidates(value, base,
|
|
7741
|
+
function migrationImageCandidates(value, base, resolve5 = true) {
|
|
7742
7742
|
if (value.length > 64e3) throw new Error("Responsive image candidate list exceeds its safety bound.");
|
|
7743
7743
|
const result = [];
|
|
7744
7744
|
let index = 0;
|
|
@@ -7756,7 +7756,7 @@ function migrationImageCandidates(value, base, resolve4 = true) {
|
|
|
7756
7756
|
if (descriptor && !/^(?:[1-9]\d*w|(?:\d+(?:\.\d+)?|\.\d+)x)$/.test(descriptor)) throw new Error("Responsive image has an unsupported descriptor.");
|
|
7757
7757
|
if (descriptor.endsWith("x") && Number(descriptor.slice(0, -1)) <= 0) throw new Error("Responsive image density must be positive.");
|
|
7758
7758
|
const resolved = migrationImageUrl(url, base);
|
|
7759
|
-
result.push({ url:
|
|
7759
|
+
result.push({ url: resolve5 ? resolved : url, descriptor });
|
|
7760
7760
|
if (result.length > 128) throw new Error("Responsive image has too many candidates.");
|
|
7761
7761
|
if (value[index] === ",") index++;
|
|
7762
7762
|
}
|
|
@@ -7770,6 +7770,86 @@ function migrationImageAttribute(value) {
|
|
|
7770
7770
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/\{/g, "{").replace(/\}/g, "}").replace(/\r/g, " ").replace(/\n/g, " ");
|
|
7771
7771
|
}
|
|
7772
7772
|
|
|
7773
|
+
// src/url-media.ts
|
|
7774
|
+
import { load as load2 } from "cheerio";
|
|
7775
|
+
var VIDEO_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
7776
|
+
"src",
|
|
7777
|
+
"title",
|
|
7778
|
+
"poster",
|
|
7779
|
+
"controls",
|
|
7780
|
+
"muted",
|
|
7781
|
+
"autoplay",
|
|
7782
|
+
"loop",
|
|
7783
|
+
"playsinline",
|
|
7784
|
+
"class",
|
|
7785
|
+
"id",
|
|
7786
|
+
"width",
|
|
7787
|
+
"height",
|
|
7788
|
+
"preload",
|
|
7789
|
+
"aria-label"
|
|
7790
|
+
]);
|
|
7791
|
+
var YOUTUBE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
7792
|
+
"src",
|
|
7793
|
+
"title",
|
|
7794
|
+
"width",
|
|
7795
|
+
"height",
|
|
7796
|
+
"allow",
|
|
7797
|
+
"allowfullscreen",
|
|
7798
|
+
"referrerpolicy",
|
|
7799
|
+
"frameborder",
|
|
7800
|
+
"loading",
|
|
7801
|
+
"class",
|
|
7802
|
+
"id",
|
|
7803
|
+
"aria-label"
|
|
7804
|
+
]);
|
|
7805
|
+
var YOUTUBE_HOSTS = /* @__PURE__ */ new Set([
|
|
7806
|
+
"youtube.com",
|
|
7807
|
+
"www.youtube.com",
|
|
7808
|
+
"youtube-nocookie.com",
|
|
7809
|
+
"www.youtube-nocookie.com"
|
|
7810
|
+
]);
|
|
7811
|
+
function projectMigrationMedia(html, base) {
|
|
7812
|
+
const $ = load2(html, void 0, false);
|
|
7813
|
+
const nodes = $.root().contents().filter((_index, node2) => node2.type !== "text" || Boolean(node2.data.trim()));
|
|
7814
|
+
if (nodes.length !== 1) return null;
|
|
7815
|
+
const element = nodes.first();
|
|
7816
|
+
const node = element[0];
|
|
7817
|
+
if (node.type !== "tag" || !["video", "iframe"].includes(node.name)) return null;
|
|
7818
|
+
if (element.children().length || element.text().trim()) return null;
|
|
7819
|
+
const allowed = node.name === "video" ? VIDEO_ATTRIBUTES : YOUTUBE_ATTRIBUTES;
|
|
7820
|
+
if (Object.keys(node.attribs).some((name) => !allowed.has(name))) return null;
|
|
7821
|
+
const source = element.attr("src");
|
|
7822
|
+
if (!source) return null;
|
|
7823
|
+
try {
|
|
7824
|
+
let src = migrationImageUrl(source, base);
|
|
7825
|
+
let component = "Video";
|
|
7826
|
+
const props = [];
|
|
7827
|
+
if (node.name === "video") {
|
|
7828
|
+
if (!/\.(?:mp4|webm|ogg|mov|m4v)(?:[?#]|$)/i.test(src)) component = "video";
|
|
7829
|
+
const isMuted = element.attr("muted") !== void 0;
|
|
7830
|
+
const hasAutoplay = element.attr("autoplay") !== void 0;
|
|
7831
|
+
if (hasAutoplay && !isMuted) return null;
|
|
7832
|
+
props.push("controls={true}");
|
|
7833
|
+
for (const [attribute, prop] of [["muted", "muted"], ["loop", "loop"], ["playsinline", "playsInline"], ["autoplay", "autoPlay"]]) {
|
|
7834
|
+
if (element.attr(attribute) !== void 0) props.push(`${prop}={true}`);
|
|
7835
|
+
}
|
|
7836
|
+
const poster = element.attr("poster");
|
|
7837
|
+
if (poster) props.push(`poster="${migrationImageAttribute(migrationImageUrl(poster, base))}"`);
|
|
7838
|
+
} else {
|
|
7839
|
+
const url = new URL(src);
|
|
7840
|
+
if (url.protocol !== "https:" || url.port || !YOUTUBE_HOSTS.has(url.hostname)) return null;
|
|
7841
|
+
const id = url.pathname.match(/^\/embed\/([A-Za-z0-9_-]{11})$/)?.[1];
|
|
7842
|
+
if (!id || url.hash || [...url.searchParams.keys()].some((name) => name !== "si")) return null;
|
|
7843
|
+
src = `https://www.youtube-nocookie.com/embed/${id}`;
|
|
7844
|
+
}
|
|
7845
|
+
const title = element.attr("title") ?? element.attr("aria-label");
|
|
7846
|
+
if (title !== void 0) props.push(`title="${migrationImageAttribute(title)}"`);
|
|
7847
|
+
return `<${component} src="${migrationImageAttribute(src)}"${props.length ? ` ${props.join(" ")}` : ""} />`;
|
|
7848
|
+
} catch {
|
|
7849
|
+
return null;
|
|
7850
|
+
}
|
|
7851
|
+
}
|
|
7852
|
+
|
|
7773
7853
|
// src/redirects.ts
|
|
7774
7854
|
var LISTED = 10;
|
|
7775
7855
|
function exactRoute(route) {
|
|
@@ -7987,7 +8067,8 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
7987
8067
|
"Warning",
|
|
7988
8068
|
"YouTube",
|
|
7989
8069
|
"picture",
|
|
7990
|
-
"source"
|
|
8070
|
+
"source",
|
|
8071
|
+
"video"
|
|
7991
8072
|
]);
|
|
7992
8073
|
var LOCALE_CODES = /* @__PURE__ */ new Set([
|
|
7993
8074
|
"ar",
|
|
@@ -8023,7 +8104,7 @@ var LOCALE_CODES = /* @__PURE__ */ new Set([
|
|
|
8023
8104
|
"zh-hans",
|
|
8024
8105
|
"zh-hant"
|
|
8025
8106
|
]);
|
|
8026
|
-
var PORTABLE_URL_PROPS = "href|src|img|primaryHref|secondaryHref";
|
|
8107
|
+
var PORTABLE_URL_PROPS = "href|src|poster|img|primaryHref|secondaryHref";
|
|
8027
8108
|
var PORTABLE_URL_PROP_NAMES = new Set(PORTABLE_URL_PROPS.split("|"));
|
|
8028
8109
|
var MINTLIFY_ARTICLE_LABELS = [
|
|
8029
8110
|
{ baseUrl: "Select base URL", permalink: "Navigate to header" },
|
|
@@ -8099,7 +8180,7 @@ function machineIndexScopePath(source, documents) {
|
|
|
8099
8180
|
if (!document) continue;
|
|
8100
8181
|
const references = [...headerLinks(document)];
|
|
8101
8182
|
if (/html/i.test(document.contentType)) {
|
|
8102
|
-
const $ =
|
|
8183
|
+
const $ = load3(document.body);
|
|
8103
8184
|
references.push(...$("link[href], a[href]").map((_index, element) => $(element).attr("href") ?? "").get().filter((href) => /\/(?:llms(?:-full)?\.txt|sitemap\.xml)(?:[?#]|$)/i.test(href)));
|
|
8104
8185
|
}
|
|
8105
8186
|
for (const reference of references) {
|
|
@@ -8210,7 +8291,7 @@ function jsonObjectAfterKey(value, key) {
|
|
|
8210
8291
|
return null;
|
|
8211
8292
|
}
|
|
8212
8293
|
function embeddedMintlifyConfig(body) {
|
|
8213
|
-
const $ =
|
|
8294
|
+
const $ = load3(body);
|
|
8214
8295
|
const flightPayloads = [];
|
|
8215
8296
|
$("script").each((_index, element) => {
|
|
8216
8297
|
const script = $(element).text();
|
|
@@ -8308,7 +8389,7 @@ function isSafePortableUrl(value) {
|
|
|
8308
8389
|
}
|
|
8309
8390
|
}
|
|
8310
8391
|
function decodeHtmlAttribute(value) {
|
|
8311
|
-
const document =
|
|
8392
|
+
const document = load3(`<i data-thally-value="${value.replace(/"/g, """)}"></i>`);
|
|
8312
8393
|
return document("i").attr("data-thally-value") ?? "";
|
|
8313
8394
|
}
|
|
8314
8395
|
function escapeMarkdownText(value, escapeTableSeparator = false) {
|
|
@@ -8394,7 +8475,7 @@ function replacePortableHtmlImages(value) {
|
|
|
8394
8475
|
if (start < cursor) continue;
|
|
8395
8476
|
const end = htmlOpeningTagEnd(value, start + match[0].length);
|
|
8396
8477
|
if (end === null) continue;
|
|
8397
|
-
const fragment =
|
|
8478
|
+
const fragment = load3(value.slice(start, end));
|
|
8398
8479
|
const image = fragment("img").first();
|
|
8399
8480
|
const source = image.attr("src");
|
|
8400
8481
|
const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
|
|
@@ -8419,7 +8500,7 @@ function replacePortableHtmlAnchors(value) {
|
|
|
8419
8500
|
const closing = closingMatch.exec(value);
|
|
8420
8501
|
if (!closing) continue;
|
|
8421
8502
|
const end = closing.index + closing[0].length;
|
|
8422
|
-
const fragment =
|
|
8503
|
+
const fragment = load3(value.slice(start, end));
|
|
8423
8504
|
const anchor = fragment("a").first();
|
|
8424
8505
|
const href = anchor.attr("href");
|
|
8425
8506
|
const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
|
|
@@ -8454,6 +8535,10 @@ function sanitizeMarkdownUrls(value) {
|
|
|
8454
8535
|
hasUnsafeMdxSyntax = true;
|
|
8455
8536
|
return;
|
|
8456
8537
|
}
|
|
8538
|
+
if (node.name === "video" && node.children?.length) {
|
|
8539
|
+
hasUnsafeMdxSyntax = true;
|
|
8540
|
+
return;
|
|
8541
|
+
}
|
|
8457
8542
|
for (const attribute of node.attributes ?? []) {
|
|
8458
8543
|
if (attribute.type !== "mdxJsxAttribute" || !attribute.name || /^on[A-Z]/.test(attribute.name)) {
|
|
8459
8544
|
hasUnsafeMdxSyntax = true;
|
|
@@ -8490,6 +8575,22 @@ function sanitizeMarkdownUrls(value) {
|
|
|
8490
8575
|
return;
|
|
8491
8576
|
}
|
|
8492
8577
|
}
|
|
8578
|
+
if (node.name === "video") {
|
|
8579
|
+
const isBoolean = ["controls", "muted", "autoPlay", "loop", "playsInline"].includes(attribute.name);
|
|
8580
|
+
const isText = ["src", "poster", "title"].includes(attribute.name);
|
|
8581
|
+
if (isBoolean && typeof staticValue !== "boolean" || isText && typeof attribute.value !== "string" || !isBoolean && !isText) {
|
|
8582
|
+
hasUnsafeMdxSyntax = true;
|
|
8583
|
+
return;
|
|
8584
|
+
}
|
|
8585
|
+
if (attribute.name === "src" || attribute.name === "poster") {
|
|
8586
|
+
try {
|
|
8587
|
+
migrationImageUrl(decodeHtmlAttribute(attribute.value), new URL("https://migration.invalid"));
|
|
8588
|
+
} catch {
|
|
8589
|
+
hasUnsafeMdxSyntax = true;
|
|
8590
|
+
return;
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8593
|
+
}
|
|
8493
8594
|
}
|
|
8494
8595
|
}
|
|
8495
8596
|
for (const child of node.children ?? []) validateMdxSyntax(child);
|
|
@@ -8602,7 +8703,7 @@ function sanitizeRemoteMarkdown(body) {
|
|
|
8602
8703
|
const urlSafeMarkdown = sanitizeMarkdownUrls(sanitized);
|
|
8603
8704
|
if (urlSafeMarkdown === null) return null;
|
|
8604
8705
|
const executable = withoutFencedCode2(urlSafeMarkdown).replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/<(?:https?:\/\/|mailto:)[^>]+>/gi, "").replace(/(`+)[\s\S]*?\1/g, "");
|
|
8605
|
-
if (
|
|
8706
|
+
if (/^[\t ]*(?:import|export)[\t ]/m.test(executable)) return null;
|
|
8606
8707
|
let hasUnsafeExpression = false;
|
|
8607
8708
|
const withoutStaticProps = executable.replace(/=\{([^{}\n]*)\}/g, (original, value) => {
|
|
8608
8709
|
try {
|
|
@@ -8699,7 +8800,7 @@ function extractEmbeddedOpenApi(content) {
|
|
|
8699
8800
|
const root = embeddedOpenApiParser.parse(content);
|
|
8700
8801
|
const candidates = (root.children ?? []).flatMap((node2) => {
|
|
8701
8802
|
if (node2.type !== "code" || !/^(?:yaml|yml|json)$/i.test(node2.lang ?? "")) return [];
|
|
8702
|
-
const metadata = node2.meta?.match(/^(?:(
|
|
8803
|
+
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);
|
|
8703
8804
|
return metadata ? [{ node: node2, method: metadata[2].toUpperCase(), path: metadata[3] }] : [];
|
|
8704
8805
|
});
|
|
8705
8806
|
if (candidates.length !== 1) return { content };
|
|
@@ -8838,7 +8939,7 @@ function mergeEmbeddedOpenApi(fragments) {
|
|
|
8838
8939
|
return new TextEncoder().encode(stringifyYaml2(combined));
|
|
8839
8940
|
}
|
|
8840
8941
|
function migrationHtmlArticle(body) {
|
|
8841
|
-
const $ =
|
|
8942
|
+
const $ = load3(body);
|
|
8842
8943
|
const root = ["main article", "article", "main", '[role="main"]', "body"].map((selector) => $(selector).first()).find((candidate) => candidate.length);
|
|
8843
8944
|
root.find([
|
|
8844
8945
|
"script",
|
|
@@ -8856,7 +8957,10 @@ function migrationHtmlArticle(body) {
|
|
|
8856
8957
|
"#chat-assistant-sheet",
|
|
8857
8958
|
"#chat-assistant-textarea"
|
|
8858
8959
|
].join(",")).remove();
|
|
8859
|
-
|
|
8960
|
+
root.find('div[data-component-part="code-group-tab-content"]').each((_index, element) => {
|
|
8961
|
+
if ($(element).parents("pre").length === 0 && $(element).find("pre").length === 0) element.name = "pre";
|
|
8962
|
+
});
|
|
8963
|
+
const permalinkSelectors = MINTLIFY_ARTICLE_LABELS.map((labels2) => `a[aria-label="${labels2.permalink}"]`);
|
|
8860
8964
|
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) => {
|
|
8861
8965
|
const parent = $(link).parent();
|
|
8862
8966
|
$(link).remove();
|
|
@@ -8867,7 +8971,7 @@ function migrationHtmlArticle(body) {
|
|
|
8867
8971
|
const isChecked = $(element).attr("checked") !== void 0;
|
|
8868
8972
|
$(element).replaceWith($("<span></span>").attr("data-thally-static-task-checkbox", isChecked ? "checked" : "unchecked").text(isChecked ? "[x] " : "[ ] "));
|
|
8869
8973
|
});
|
|
8870
|
-
root.find(MINTLIFY_ARTICLE_LABELS.map((
|
|
8974
|
+
root.find(MINTLIFY_ARTICLE_LABELS.map((labels2) => `select[aria-label="${labels2.baseUrl}"]`).join(",")).each((_index, element) => {
|
|
8871
8975
|
const select = $(element);
|
|
8872
8976
|
const options = select.children("option").toArray();
|
|
8873
8977
|
if (!options.length || options.length !== select.children().length) return;
|
|
@@ -8906,7 +9010,7 @@ function htmlPage(document, id, options = {}) {
|
|
|
8906
9010
|
const title = scopedTitle || $("main h1, article h1, h1").first().text().trim() || $('meta[property="og:title"]').attr("content")?.trim() || $("title").text().split("|")[0].trim();
|
|
8907
9011
|
const description = $('meta[name="description"]').attr("content")?.trim() || $('meta[property="og:description"]').attr("content")?.trim();
|
|
8908
9012
|
let documentHeadingAnchor = "";
|
|
8909
|
-
const excludedElements = options.omitDocumentHeading ? "script,style,noscript,nav,footer,aside,form,button,svg,
|
|
9013
|
+
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";
|
|
8910
9014
|
root.find(excludedElements).remove();
|
|
8911
9015
|
root.find(".theme-doc-version-badge,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
|
|
8912
9016
|
if (options.omitDocumentHeading) {
|
|
@@ -8964,6 +9068,18 @@ function htmlPage(document, id, options = {}) {
|
|
|
8964
9068
|
// Emit literal task-list syntax rather than escaping its brackets as copy.
|
|
8965
9069
|
replacement: (_content, node) => node.getAttribute("data-thally-static-task-checkbox") === "checked" ? "[x] " : "[ ] "
|
|
8966
9070
|
});
|
|
9071
|
+
turndown.addRule("migration-authored-paragraph", {
|
|
9072
|
+
filter: (node) => node.nodeName === "SPAN" && node.getAttribute("data-as") === "p",
|
|
9073
|
+
// Some publishers render authored paragraphs as spans. Their explicit
|
|
9074
|
+
// semantic hint must retain a block boundary: otherwise following copy
|
|
9075
|
+
// joins a picture's closing tag or an accordion's source anchor and can
|
|
9076
|
+
// change the resulting MDX grammar. Ordinary spans remain inline.
|
|
9077
|
+
replacement: (content) => `
|
|
9078
|
+
|
|
9079
|
+
${content}
|
|
9080
|
+
|
|
9081
|
+
`
|
|
9082
|
+
});
|
|
8967
9083
|
const anchor = (element) => {
|
|
8968
9084
|
const value = element.getAttribute("id");
|
|
8969
9085
|
return options.preserveAnchors && value ? `<a id="${value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/\{/g, "{")}" />
|
|
@@ -8982,6 +9098,18 @@ ${anchor(element)}${heading ? `${"#".repeat(Number(heading[1]))} ${content.trim(
|
|
|
8982
9098
|
`;
|
|
8983
9099
|
}
|
|
8984
9100
|
});
|
|
9101
|
+
turndown.addRule("migration-static-media", {
|
|
9102
|
+
filter: (node) => node.nodeName === "VIDEO" || node.nodeName === "IFRAME",
|
|
9103
|
+
replacement: (_content, node) => {
|
|
9104
|
+
const element = node;
|
|
9105
|
+
const projected = projectMigrationMedia(element.outerHTML, document.finalUrl);
|
|
9106
|
+
return projected ? `
|
|
9107
|
+
|
|
9108
|
+
${anchor(element)}${projected}
|
|
9109
|
+
|
|
9110
|
+
` : "";
|
|
9111
|
+
}
|
|
9112
|
+
});
|
|
8985
9113
|
if (options.preserveResponsiveImages) {
|
|
8986
9114
|
const image = (element) => {
|
|
8987
9115
|
const tag = element.nodeName.toLowerCase();
|
|
@@ -9017,7 +9145,7 @@ ${content.trim()}
|
|
|
9017
9145
|
const element = node;
|
|
9018
9146
|
const className = element.className;
|
|
9019
9147
|
const tag = /admonition-(?:danger)/.test(className) ? "Error" : /admonition-(?:caution|warning)/.test(className) ? "Warning" : /admonition-info/.test(className) ? "Info" : "Note";
|
|
9020
|
-
const fragment =
|
|
9148
|
+
const fragment = load3(element.outerHTML);
|
|
9021
9149
|
const clone = fragment(".theme-admonition").first();
|
|
9022
9150
|
const title2 = clone.find('[class*="admonitionHeading"],.admonition-heading').first();
|
|
9023
9151
|
const titleText = title2.text().trim();
|
|
@@ -9038,15 +9166,15 @@ ${titleText ? `**${escapeMarkdownText(titleText)}**
|
|
|
9038
9166
|
filter: (node) => node.nodeName === "DIV" && node.classList.contains("tabs-container"),
|
|
9039
9167
|
replacement: (_content, node) => {
|
|
9040
9168
|
const element = node;
|
|
9041
|
-
const fragment =
|
|
9169
|
+
const fragment = load3(element.outerHTML);
|
|
9042
9170
|
const container = fragment(".tabs-container").first();
|
|
9043
|
-
const
|
|
9171
|
+
const labels2 = container.children("ul.tabs").children("li").toArray().map((item) => fragment(item).text().trim() || "Tab");
|
|
9044
9172
|
const panels = container.find('[role="tabpanel"]').toArray().filter((panel) => fragment(panel).closest(".tabs-container")[0] === container[0]);
|
|
9045
9173
|
if (panels.length === 0) return _content;
|
|
9046
9174
|
const tabs = panels.map((panel, index) => {
|
|
9047
9175
|
const panelId = fragment(panel).attr("id");
|
|
9048
9176
|
const tab = panelId ? container.find('[role="tab"]').toArray().find((item) => fragment(item).attr("aria-controls") === panelId) : void 0;
|
|
9049
|
-
const title2 = migrationImageAttribute((tab ? fragment(tab).text().trim() :
|
|
9177
|
+
const title2 = migrationImageAttribute((tab ? fragment(tab).text().trim() : labels2[index]) || `Tab ${index + 1}`);
|
|
9050
9178
|
const panelAnchor = options.preserveAnchors && panelId ? `<a id="${migrationImageAttribute(panelId)}" />
|
|
9051
9179
|
|
|
9052
9180
|
` : "";
|
|
@@ -9066,7 +9194,7 @@ ${tabs.join("\n\n")}
|
|
|
9066
9194
|
turndown.addRule("gfm-table", {
|
|
9067
9195
|
filter: "table",
|
|
9068
9196
|
replacement: (_content, node) => {
|
|
9069
|
-
const fragment =
|
|
9197
|
+
const fragment = load3(node.outerHTML);
|
|
9070
9198
|
const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => {
|
|
9071
9199
|
const wasConvertingTableCell = isConvertingTableCell;
|
|
9072
9200
|
isConvertingTableCell = true;
|
|
@@ -9092,10 +9220,17 @@ ${normalized.slice(1).map((row) => `| ${row.join(" | ")} |`).join("\n")}
|
|
|
9092
9220
|
filter: (node) => node.nodeName === "PRE",
|
|
9093
9221
|
replacement: (_content, node) => {
|
|
9094
9222
|
const element = node;
|
|
9095
|
-
const fragment =
|
|
9223
|
+
const fragment = load3(element.outerHTML);
|
|
9096
9224
|
const tokenLines = fragment(".token-line").toArray();
|
|
9097
9225
|
const code = (tokenLines.length > 0 ? tokenLines.map((line) => fragment(line).text()).join("\n") : fragment("pre").text()).replace(/\n$/, "");
|
|
9098
|
-
const language =
|
|
9226
|
+
const language = ["code", "pre"].flatMap((selector) => {
|
|
9227
|
+
const block = fragment(selector).first();
|
|
9228
|
+
return [
|
|
9229
|
+
block.attr("language"),
|
|
9230
|
+
block.attr("data-language"),
|
|
9231
|
+
block.attr("class")?.match(/(?:^|\s)(?:language-|lang-)([A-Za-z0-9_+-]+)(?=\s|$)/)?.[1]
|
|
9232
|
+
];
|
|
9233
|
+
}).find((value) => value !== void 0 && /^[A-Za-z0-9_+-]{1,64}$/.test(value)) ?? "";
|
|
9099
9234
|
const longestFence = Math.max(0, ...[...code.matchAll(/`+/g)].map((match) => match[0].length));
|
|
9100
9235
|
const fence = "`".repeat(Math.max(3, longestFence + 1));
|
|
9101
9236
|
return `
|
|
@@ -9125,7 +9260,7 @@ ${fence}
|
|
|
9125
9260
|
};
|
|
9126
9261
|
}
|
|
9127
9262
|
function htmlNavigationLinks(document) {
|
|
9128
|
-
const $ =
|
|
9263
|
+
const $ = load3(document.body);
|
|
9129
9264
|
return $('.nav-tabs a[href], nav a[href], aside a[href], [role="navigation"] a[href]').map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
|
|
9130
9265
|
}
|
|
9131
9266
|
function normalizedLinkLabel(value) {
|
|
@@ -9133,7 +9268,7 @@ function normalizedLinkLabel(value) {
|
|
|
9133
9268
|
}
|
|
9134
9269
|
function readDocusaurusSidebar(document) {
|
|
9135
9270
|
if (!/html/i.test(document.contentType)) return null;
|
|
9136
|
-
const $ =
|
|
9271
|
+
const $ = load3(document.body);
|
|
9137
9272
|
const sidebar = $("ul.theme-doc-sidebar-menu").first();
|
|
9138
9273
|
if (sidebar.length === 0) return null;
|
|
9139
9274
|
function readList(list2) {
|
|
@@ -9169,7 +9304,7 @@ function docusaurusSidebarHrefs(snapshot) {
|
|
|
9169
9304
|
return hrefs;
|
|
9170
9305
|
}
|
|
9171
9306
|
function deriveDocusaurusScope(source, document, submittedScopePath) {
|
|
9172
|
-
const $ =
|
|
9307
|
+
const $ = load3(document.body);
|
|
9173
9308
|
const sidebar = readDocusaurusSidebar(document);
|
|
9174
9309
|
const internalPaths = docusaurusSidebarHrefs(sidebar).flatMap((href) => {
|
|
9175
9310
|
try {
|
|
@@ -9299,7 +9434,7 @@ function docusaurusNavigationFromSnapshots(snapshots, pages, scopePath) {
|
|
|
9299
9434
|
return { tabs };
|
|
9300
9435
|
}
|
|
9301
9436
|
function htmlTopLevelNavigation(document, source, scopePath) {
|
|
9302
|
-
const $ =
|
|
9437
|
+
const $ = load3(document.body);
|
|
9303
9438
|
const collectCandidates = (selector) => {
|
|
9304
9439
|
const candidates2 = [];
|
|
9305
9440
|
$(selector).each((_containerIndex, container) => {
|
|
@@ -9360,7 +9495,7 @@ function markdownLinks(body) {
|
|
|
9360
9495
|
return [...body.matchAll(/\[[^\]]+\]\(([^)\s]+)(?:\s+['"][^'"]*['"])?\)/g)].map((match) => match[1]);
|
|
9361
9496
|
}
|
|
9362
9497
|
function sitemapLinks(body) {
|
|
9363
|
-
const $ =
|
|
9498
|
+
const $ = load3(body, { xmlMode: true });
|
|
9364
9499
|
return $("url > loc, sitemap > loc").map((_index, element) => $(element).text().trim()).get().filter(Boolean);
|
|
9365
9500
|
}
|
|
9366
9501
|
function robotsSitemaps(body) {
|
|
@@ -9399,7 +9534,7 @@ async function migrateUrl(options) {
|
|
|
9399
9534
|
const platform = options.platform ?? detectUrlPlatform(scopedHtmlProbe ?? initial);
|
|
9400
9535
|
let docusaurusHtmlProbe = platform === "docusaurus" ? scopedHtmlProbe : null;
|
|
9401
9536
|
if (docusaurusHtmlProbe && !readDocusaurusSidebar(docusaurusHtmlProbe)) {
|
|
9402
|
-
const $ =
|
|
9537
|
+
const $ = load3(docusaurusHtmlProbe.body);
|
|
9403
9538
|
const docsHref = $("nav a[href]").toArray().map((element) => ({
|
|
9404
9539
|
href: $(element).attr("href"),
|
|
9405
9540
|
label: normalizedLinkLabel($(element).text())
|
|
@@ -9849,10 +9984,10 @@ function mainSiteLinkTarget(target2, mountPath, index, unresolved) {
|
|
|
9849
9984
|
unresolved.push(target2);
|
|
9850
9985
|
return void 0;
|
|
9851
9986
|
}
|
|
9852
|
-
function prefixRootLinks(body, mountPath,
|
|
9987
|
+
function prefixRootLinks(body, mountPath, resolve5, markdown = true) {
|
|
9853
9988
|
const mounted = `/${mountPath}`;
|
|
9854
9989
|
const alreadyMounted = (target2) => target2 === mounted || ["/", "?", "#"].some((next) => target2.startsWith(mounted + next));
|
|
9855
|
-
const fix = (target2) => alreadyMounted(target2) ? target2 :
|
|
9990
|
+
const fix = (target2) => alreadyMounted(target2) ? target2 : resolve5?.(target2) ?? `${mounted}${target2}`;
|
|
9856
9991
|
return replaceOutsideCodeAndComments(body, (text3) => text3.replace(markdown ? /(\]\(<?)(\/(?!\/)[^\s)>]*)/g : /(?!)/g, (_match, before, target2) => `${before}${fix(target2)}`).replace(/(\b(?:href|src|to|poster)=(['"]))(\/(?!\/)[^'"\n]*)\2/g, (_match, before, quote, target2) => `${before}${fix(target2)}${quote}`).replace(/(\b(?:href|src|to|poster)=\{\s*(['"]))(\/(?!\/)[^'"\n]*)\2(\s*\})/g, (_match, before, quote, target2, after) => `${before}${fix(target2)}${quote}${after}`).replace(markdown ? /^(\[[^\]\n]+\]:[ \t]*<?)(\/(?!\/)\S*)/gm : /(?!)/g, (_match, before, target2) => `${before}${fix(target2)}`));
|
|
9857
9992
|
}
|
|
9858
9993
|
function prefixNavigation(entries, mountPath) {
|
|
@@ -10376,8 +10511,8 @@ var HARD_TIMEOUT_REASON = `still running after ${MAX_GIT_PROCESS_MS / 6e4} minut
|
|
|
10376
10511
|
var GIT_EXIT_DRAIN_MS = 2e3;
|
|
10377
10512
|
var RETRYABLE_CLONE_ERROR = /RPC failed|Recv failure|early EOF|curl \d+|Could not resolve host|Connection (?:reset|refused|timed out)|The remote end hung up|SSL[_ ]?(?:read|connect|write) error|timed out|network is unreachable/i;
|
|
10378
10513
|
function delay(ms) {
|
|
10379
|
-
return new Promise((
|
|
10380
|
-
setTimeout(
|
|
10514
|
+
return new Promise((resolve5) => {
|
|
10515
|
+
setTimeout(resolve5, ms);
|
|
10381
10516
|
});
|
|
10382
10517
|
}
|
|
10383
10518
|
function gitIdleTimeoutMs() {
|
|
@@ -10389,7 +10524,7 @@ function gitErrorTail(stderr) {
|
|
|
10389
10524
|
return stderr.split(/[\r\n]+/).map((line) => line.trim()).filter((line) => line && !/^(?:remote: )?[A-Za-z][A-Za-z ]*:\s+\d+%/.test(line)).slice(-5).join("\n");
|
|
10390
10525
|
}
|
|
10391
10526
|
function runGit(args, options) {
|
|
10392
|
-
return new Promise((
|
|
10527
|
+
return new Promise((resolve5, reject) => {
|
|
10393
10528
|
const child = spawn("git", args, {
|
|
10394
10529
|
stdio: ["ignore", "ignore", "pipe"],
|
|
10395
10530
|
...options.cwd ? { cwd: options.cwd } : {},
|
|
@@ -10412,7 +10547,7 @@ function runGit(args, options) {
|
|
|
10412
10547
|
settled = true;
|
|
10413
10548
|
clearTimers();
|
|
10414
10549
|
child.stderr.destroy();
|
|
10415
|
-
if (code === 0)
|
|
10550
|
+
if (code === 0) resolve5();
|
|
10416
10551
|
else if (timeoutReason) {
|
|
10417
10552
|
const hint = timeoutReason === HARD_TIMEOUT_REASON ? "" : "; on a very slow link, raise THALLY_MIGRATE_CLONE_TIMEOUT_MS (milliseconds of allowed silence)";
|
|
10418
10553
|
reject(new Error(`${options.label}: timed out (${timeoutReason}) and was killed. Check your network connection and try again${hint}.`));
|
|
@@ -14343,7 +14478,7 @@ function renderMigrationFiles(bundle, options = {}) {
|
|
|
14343
14478
|
|
|
14344
14479
|
// src/url-run.ts
|
|
14345
14480
|
import { createHash as createHash3 } from "crypto";
|
|
14346
|
-
import { load as
|
|
14481
|
+
import { load as load6 } from "cheerio";
|
|
14347
14482
|
import remarkParse4 from "remark-parse";
|
|
14348
14483
|
import remarkMdx4 from "remark-mdx";
|
|
14349
14484
|
import { unified as unified4 } from "unified";
|
|
@@ -14721,7 +14856,53 @@ function cloneUrlMigrationCheckpoint(checkpoint) {
|
|
|
14721
14856
|
}
|
|
14722
14857
|
|
|
14723
14858
|
// src/url-navigation.ts
|
|
14724
|
-
import { load as
|
|
14859
|
+
import { load as load5 } from "cheerio";
|
|
14860
|
+
|
|
14861
|
+
// src/url-scope.ts
|
|
14862
|
+
import { load as load4 } from "cheerio";
|
|
14863
|
+
function inferUrlMigrationScope(capture) {
|
|
14864
|
+
const source = capture.finalUrl;
|
|
14865
|
+
let end = source.pathname.length;
|
|
14866
|
+
while (end > 0 && source.pathname.charCodeAt(end - 1) === 47) end--;
|
|
14867
|
+
const parent = end < source.pathname.length ? source.pathname.slice(0, end) || "/" : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/";
|
|
14868
|
+
if (!/html/i.test(capture.contentType) || Buffer.byteLength(capture.body) > 8e6) return { scopePath: parent, evidence: "source-parent" };
|
|
14869
|
+
const $ = load4(capture.body);
|
|
14870
|
+
const ancestor = (path) => path === "/" || source.pathname === path || source.pathname.startsWith(`${path}/`);
|
|
14871
|
+
const observed = (reference) => {
|
|
14872
|
+
if (!reference || reference.length > 8192) return void 0;
|
|
14873
|
+
try {
|
|
14874
|
+
const url = new URL(reference, source);
|
|
14875
|
+
if (url.origin !== source.origin || url.username || url.password || url.search || url.hash || /%2f|%5c/i.test(url.pathname)) return void 0;
|
|
14876
|
+
return url;
|
|
14877
|
+
} catch {
|
|
14878
|
+
return void 0;
|
|
14879
|
+
}
|
|
14880
|
+
};
|
|
14881
|
+
const indexes = [];
|
|
14882
|
+
$("link[href],a[href]").slice(0, 1e4).each((_, node) => {
|
|
14883
|
+
const url = observed($(node).attr("href"));
|
|
14884
|
+
if (!url || !/\/(?:llms(?:-full)?\.txt|sitemap(?:[-_][^/]*)?\.xml)$/i.test(url.pathname)) return;
|
|
14885
|
+
const path = url.pathname.slice(0, url.pathname.lastIndexOf("/")) || "/";
|
|
14886
|
+
if (ancestor(path)) indexes.push(path);
|
|
14887
|
+
});
|
|
14888
|
+
if (indexes.length) return { scopePath: indexes.sort((a, b) => b.length - a.length)[0], evidence: "discovery-index" };
|
|
14889
|
+
const paths = [];
|
|
14890
|
+
$('nav a[href],aside a[href],[role="navigation"] a[href]').slice(0, 1e4).each((_, node) => {
|
|
14891
|
+
if ($(node).closest("header,footer").length) return;
|
|
14892
|
+
const url = observed($(node).attr("href"));
|
|
14893
|
+
if (url && !/\.[a-z0-9]+$/i.test(url.pathname)) paths.push(url.pathname);
|
|
14894
|
+
});
|
|
14895
|
+
if (new Set(paths).size >= 2 && paths.some((path) => path === source.pathname)) {
|
|
14896
|
+
const segments = paths.map((path) => path.split("/").filter(Boolean));
|
|
14897
|
+
let count = 0;
|
|
14898
|
+
while (segments.every((parts) => parts[count] !== void 0 && parts[count] === segments[0][count])) count++;
|
|
14899
|
+
const common = "/" + segments[0].slice(0, count).join("/");
|
|
14900
|
+
if (ancestor(common)) return { scopePath: common, evidence: "navigation" };
|
|
14901
|
+
}
|
|
14902
|
+
return { scopePath: parent, evidence: "source-parent" };
|
|
14903
|
+
}
|
|
14904
|
+
|
|
14905
|
+
// src/url-navigation.ts
|
|
14725
14906
|
var MAX_CAPTURE_BYTES = 8e6;
|
|
14726
14907
|
var MAX_JSON_BYTES = 2e6;
|
|
14727
14908
|
var MAX_NODES = 1e4;
|
|
@@ -14788,8 +14969,18 @@ function flightRecords(stream) {
|
|
|
14788
14969
|
}
|
|
14789
14970
|
return records;
|
|
14790
14971
|
}
|
|
14791
|
-
function mintlifyNavigation(body, base, locale) {
|
|
14792
|
-
const $ =
|
|
14972
|
+
function mintlifyNavigation(body, base, locale, selectedScope) {
|
|
14973
|
+
const $ = load5(body);
|
|
14974
|
+
const scope = inferUrlMigrationScope({ body, contentType: "text/html", finalUrl: base });
|
|
14975
|
+
const mount = scope.evidence === "source-parent" ? selectedScope ?? "/" : scope.scopePath;
|
|
14976
|
+
const configTarget = (value) => {
|
|
14977
|
+
const reference = text2(value);
|
|
14978
|
+
if (!reference) return void 0;
|
|
14979
|
+
if (/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(reference)) return target(reference, base);
|
|
14980
|
+
const path = reference.replace(/^\/+/, "");
|
|
14981
|
+
const prefix = mount.replace(/^\/|\/$/g, "");
|
|
14982
|
+
return target(prefix && path !== prefix && !path.startsWith(`${prefix}/`) ? `${prefix}/${path}` : path, new URL("/", base));
|
|
14983
|
+
};
|
|
14793
14984
|
let stream = "";
|
|
14794
14985
|
const records = [];
|
|
14795
14986
|
$("script").each((_index, script) => {
|
|
@@ -14826,7 +15017,7 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
14826
15017
|
const walk2 = (value, depth = 0, activeLocale) => {
|
|
14827
15018
|
if (depth > MAX_DEPTH || ++count > MAX_NODES) throw new Error("Source navigation exceeds its safety bound.");
|
|
14828
15019
|
if (typeof value === "string") {
|
|
14829
|
-
const url2 =
|
|
15020
|
+
const url2 = configTarget(value);
|
|
14830
15021
|
return url2 ? [{ kind: "page", label: value, url: url2 }] : [];
|
|
14831
15022
|
}
|
|
14832
15023
|
if (Array.isArray(value)) return value.flatMap((child) => walk2(child, depth + 1, activeLocale));
|
|
@@ -14846,7 +15037,7 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
14846
15037
|
return;
|
|
14847
15038
|
}
|
|
14848
15039
|
if (!object(node)) return;
|
|
14849
|
-
const href =
|
|
15040
|
+
const href = configTarget(node.href);
|
|
14850
15041
|
if (href) {
|
|
14851
15042
|
populated++;
|
|
14852
15043
|
const url2 = new URL(href);
|
|
@@ -14864,10 +15055,10 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
14864
15055
|
const selected = matching[0]?.item ?? languages.find((item) => text2(item.language)?.toLowerCase() === language?.toLowerCase()) ?? languages.find((item) => text2(item.language)?.toLowerCase() === "en") ?? languages[0];
|
|
14865
15056
|
return selected ? walk2({ ...selected, languages: void 0 }, depth + 1, text2(selected.language)) : [];
|
|
14866
15057
|
}
|
|
14867
|
-
const label = text2(value.tab) ?? text2(value.group) ?? text2(value.title) ?? text2(value.label);
|
|
14868
|
-
const kind = text2(value.tab) ? "tab" : text2(value.group) ? "group" : "page";
|
|
15058
|
+
const label = text2(value.tab) ?? text2(value.dropdown) ?? text2(value.group) ?? text2(value.sidebarTitle) ?? text2(value.title) ?? text2(value.label);
|
|
15059
|
+
const kind = text2(value.tab) || text2(value.dropdown) ? "tab" : text2(value.group) ? "group" : "page";
|
|
14869
15060
|
const children = ["tabs", "groups", "pages", "dropdowns", "anchors", "versions"].flatMap((key) => Array.isArray(value[key]) ? walk2(value[key], depth + 1, activeLocale) : []);
|
|
14870
|
-
const url =
|
|
15061
|
+
const url = configTarget(value.href);
|
|
14871
15062
|
if (!label) return children;
|
|
14872
15063
|
if (!url && !children.length) return [];
|
|
14873
15064
|
return [{
|
|
@@ -14875,6 +15066,7 @@ function mintlifyNavigation(body, base, locale) {
|
|
|
14875
15066
|
label,
|
|
14876
15067
|
...url ? { url } : {},
|
|
14877
15068
|
...children.length ? { children } : {},
|
|
15069
|
+
...text2(value.dropdown) ? { presentation: "dropdown" } : {},
|
|
14878
15070
|
...kind === "tab" && activeLocale ? { locale: activeLocale } : {}
|
|
14879
15071
|
}];
|
|
14880
15072
|
};
|
|
@@ -14901,18 +15093,19 @@ function resolveNavigation(nodes, base) {
|
|
|
14901
15093
|
...url ? { url } : {},
|
|
14902
15094
|
...children.length ? { children } : {},
|
|
14903
15095
|
...["tab", "group", "page"].includes(node.kind ?? "") ? { kind: node.kind } : {},
|
|
15096
|
+
...node.presentation === "dropdown" ? { presentation: "dropdown" } : {},
|
|
14904
15097
|
...text2(node.locale) ? { locale: text2(node.locale) } : {}
|
|
14905
15098
|
}];
|
|
14906
15099
|
});
|
|
14907
15100
|
};
|
|
14908
15101
|
return walk2(nodes);
|
|
14909
15102
|
}
|
|
14910
|
-
function extractSourceNavigation(capture) {
|
|
15103
|
+
function extractSourceNavigation(capture, selectedScope) {
|
|
14911
15104
|
if (capture.navigation) return resolveNavigation(capture.navigation, capture.finalUrl);
|
|
14912
15105
|
if (!/html/i.test(capture.contentType) || Buffer.byteLength(capture.body) > MAX_CAPTURE_BYTES) return [];
|
|
14913
|
-
const mintlify = mintlifyNavigation(capture.body, capture.finalUrl, capture.locale);
|
|
15106
|
+
const mintlify = mintlifyNavigation(capture.body, capture.finalUrl, capture.locale, selectedScope);
|
|
14914
15107
|
if (mintlify.length) return mintlify;
|
|
14915
|
-
const $ =
|
|
15108
|
+
const $ = load5(capture.body);
|
|
14916
15109
|
const candidates = ['.theme-doc-sidebar-menu,[aria-label*="sidebar" i]', "aside", '[role="navigation"],nav'];
|
|
14917
15110
|
const root = candidates.map((selector) => $(selector).filter((_index, node) => $(node).find("a[href]").length > 0 && $(node).closest("header").length === 0).first()).find((candidate) => candidate.length);
|
|
14918
15111
|
if (!root) return [];
|
|
@@ -14988,8 +15181,127 @@ function mergeSourceNavigation(existing, incoming) {
|
|
|
14988
15181
|
return merge(existing, incoming);
|
|
14989
15182
|
}
|
|
14990
15183
|
|
|
15184
|
+
// src/url-api-schema-controls.ts
|
|
15185
|
+
var MAX_CAPTURE_BYTES2 = 8e6;
|
|
15186
|
+
var MAX_TAB_GROUPS = 256;
|
|
15187
|
+
var MAX_TABS = 1024;
|
|
15188
|
+
function object2(value) {
|
|
15189
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
15190
|
+
}
|
|
15191
|
+
function localReference2(document, reference) {
|
|
15192
|
+
if (typeof reference !== "string" || !reference.startsWith("#/")) return void 0;
|
|
15193
|
+
let target2 = document;
|
|
15194
|
+
try {
|
|
15195
|
+
for (const part of reference.slice(2).split("/")) {
|
|
15196
|
+
const key = decodeURIComponent(part).replace(/~1/g, "/").replace(/~0/g, "~");
|
|
15197
|
+
if (!target2 || typeof target2 !== "object" || !Object.hasOwn(target2, key)) return void 0;
|
|
15198
|
+
target2 = target2[key];
|
|
15199
|
+
}
|
|
15200
|
+
} catch {
|
|
15201
|
+
return void 0;
|
|
15202
|
+
}
|
|
15203
|
+
return target2;
|
|
15204
|
+
}
|
|
15205
|
+
function resolve4(document, value) {
|
|
15206
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15207
|
+
for (let depth = 0; depth < 32; depth += 1) {
|
|
15208
|
+
const schema = object2(value);
|
|
15209
|
+
if (!schema || seen.has(schema)) return void 0;
|
|
15210
|
+
seen.add(schema);
|
|
15211
|
+
if (!Object.hasOwn(schema, "$ref")) return schema;
|
|
15212
|
+
value = localReference2(document, schema.$ref);
|
|
15213
|
+
}
|
|
15214
|
+
return void 0;
|
|
15215
|
+
}
|
|
15216
|
+
function complete(document) {
|
|
15217
|
+
let visited = 0;
|
|
15218
|
+
const active = /* @__PURE__ */ new Set();
|
|
15219
|
+
const visit = (value, depth) => {
|
|
15220
|
+
if (++visited > 1e5 || depth > 96) return false;
|
|
15221
|
+
if (typeof value === "number" && !Number.isFinite(value)) return false;
|
|
15222
|
+
if (!value || typeof value !== "object") return true;
|
|
15223
|
+
if (active.has(value)) return false;
|
|
15224
|
+
active.add(value);
|
|
15225
|
+
const schema = object2(value);
|
|
15226
|
+
const valid = (!schema || !Object.hasOwn(schema, "$ref") || Boolean(object2(localReference2(document, schema.$ref)))) && Object.values(value).every((child) => visit(child, depth + 1));
|
|
15227
|
+
active.delete(value);
|
|
15228
|
+
return valid;
|
|
15229
|
+
};
|
|
15230
|
+
return visit(document, 0);
|
|
15231
|
+
}
|
|
15232
|
+
function labels(document, value) {
|
|
15233
|
+
const schema = resolve4(document, value);
|
|
15234
|
+
if (!Array.isArray(schema?.oneOf) || schema.oneOf.length < 2 || schema.oneOf.length > 64) return void 0;
|
|
15235
|
+
const result = [];
|
|
15236
|
+
for (const [index, branch] of schema.oneOf.entries()) {
|
|
15237
|
+
const target2 = resolve4(document, branch);
|
|
15238
|
+
if (!target2) return void 0;
|
|
15239
|
+
const title = object2(branch)?.title ?? target2.title;
|
|
15240
|
+
result.push(typeof title === "string" && title.trim() ? title.trim().replace(/\s+/g, " ") : `Option ${index + 1}`);
|
|
15241
|
+
}
|
|
15242
|
+
return new Set(result).size === result.length ? result : void 0;
|
|
15243
|
+
}
|
|
15244
|
+
function apiSchemaTabGroups(body, document, operation) {
|
|
15245
|
+
if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(document)) return [];
|
|
15246
|
+
const path = object2(document.paths)?.[operation.path];
|
|
15247
|
+
const method = operation.method.toLowerCase();
|
|
15248
|
+
if (!/^(?:get|post|put|patch|delete|head|options|trace)$/.test(method)) return [];
|
|
15249
|
+
const selected = object2(object2(path)?.[method]);
|
|
15250
|
+
if (!selected) return [];
|
|
15251
|
+
const { $, root } = migrationHtmlArticle(body);
|
|
15252
|
+
const groups = root.find('[role="tablist"],[data-component-part="tabs-list"]');
|
|
15253
|
+
if (groups.length > MAX_TAB_GROUPS || root.find('[role="tab"]').length > MAX_TABS) return [];
|
|
15254
|
+
const idCounts = /* @__PURE__ */ new Map();
|
|
15255
|
+
root.find("[id]").each((_index, node) => {
|
|
15256
|
+
const id = $(node).attr("id");
|
|
15257
|
+
idCounts.set(id, (idCounts.get(id) ?? 0) + 1);
|
|
15258
|
+
});
|
|
15259
|
+
const proofs = [];
|
|
15260
|
+
groups.each((index, node) => {
|
|
15261
|
+
const group = $(node);
|
|
15262
|
+
const container = group.parent(".tabs.tab-container");
|
|
15263
|
+
const field = group.closest(".object-param-field");
|
|
15264
|
+
const section = field.parent(".api-section");
|
|
15265
|
+
if (!group.is('[role="tablist"][data-component-part="tabs-list"]') || !container.length || !section.length || !section.closest('[id^="api-playground-"][id$="-operation-page"]').length) return;
|
|
15266
|
+
if (field.parents(".object-param-field").length) return;
|
|
15267
|
+
const heading = section.children(".api-section-heading");
|
|
15268
|
+
const title = heading.find(".api-section-heading-title").text().trim().toLowerCase();
|
|
15269
|
+
const media = heading.find("*").toArray().filter((element) => $(element).children().length === 0).map((element) => $(element).text().trim()).filter((text3) => /^[a-z][a-z\d.+-]*\/[a-z\d.+-]+$/i.test(text3));
|
|
15270
|
+
if (heading.length !== 1 || !media || media.length !== 1) return;
|
|
15271
|
+
let schema;
|
|
15272
|
+
let scope;
|
|
15273
|
+
if (title === "body") {
|
|
15274
|
+
const request2 = resolve4(document, selected.requestBody);
|
|
15275
|
+
schema = object2(object2(request2?.content)?.[media[0]])?.schema;
|
|
15276
|
+
scope = `request:${media[0]}`;
|
|
15277
|
+
} else if (title === "response") {
|
|
15278
|
+
const statuses = heading.find('button[aria-haspopup="menu"]').toArray().map((button) => $(button).text().trim()).filter((text3) => /^(?:[1-5]\d{2}|default)$/.test(text3));
|
|
15279
|
+
if (statuses.length !== 1) return;
|
|
15280
|
+
const response = resolve4(document, object2(selected.responses)?.[statuses[0]]);
|
|
15281
|
+
schema = object2(object2(response?.content)?.[media[0]])?.schema;
|
|
15282
|
+
scope = `response:${statuses[0]}:${media[0]}`;
|
|
15283
|
+
} else return;
|
|
15284
|
+
const expected = labels(document, schema);
|
|
15285
|
+
const tabs = group.children('[role="tab"][data-component-part="tab"]');
|
|
15286
|
+
if (!expected || tabs.length !== expected.length || group.children().length !== tabs.length) return;
|
|
15287
|
+
const panels = container.children('[data-component-part="tabs-panels"]').children('[role="tabpanel"][data-component-part="tab-content"]');
|
|
15288
|
+
if (panels.length !== tabs.length) return;
|
|
15289
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15290
|
+
for (const [position, tab] of tabs.toArray().entries()) {
|
|
15291
|
+
const control = $(tab).attr("aria-controls");
|
|
15292
|
+
const panel = panels.eq(position);
|
|
15293
|
+
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;
|
|
15294
|
+
ids.add(control);
|
|
15295
|
+
}
|
|
15296
|
+
proofs.push({ index, scope });
|
|
15297
|
+
});
|
|
15298
|
+
const scopeCounts = /* @__PURE__ */ new Map();
|
|
15299
|
+
for (const proof of proofs) scopeCounts.set(proof.scope, (scopeCounts.get(proof.scope) ?? 0) + 1);
|
|
15300
|
+
return proofs.filter((proof) => scopeCounts.get(proof.scope) === 1).map((proof) => proof.index);
|
|
15301
|
+
}
|
|
15302
|
+
|
|
14991
15303
|
// src/url-run.ts
|
|
14992
|
-
var CONVERTER_VERSION = "url-evidence-
|
|
15304
|
+
var CONVERTER_VERSION = "url-evidence-v5";
|
|
14993
15305
|
var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
|
|
14994
15306
|
var ACCEPT = "text/markdown,text/html,application/xhtml+xml;q=0.9";
|
|
14995
15307
|
var MAX_RESPONSE_BYTES = 8e6;
|
|
@@ -15052,7 +15364,7 @@ function linksOf(document) {
|
|
|
15052
15364
|
return [...root.find("a[href]").toArray(), ...$('link[rel="sitemap"][href],link[rel="alternate"][href]').toArray()].map((node) => $(node).attr("href")).filter(Boolean);
|
|
15053
15365
|
}
|
|
15054
15366
|
if (/xml/i.test(document.contentType) || /<urlset|<sitemapindex/i.test(document.body)) {
|
|
15055
|
-
const $ =
|
|
15367
|
+
const $ = load6(document.body, { xmlMode: true });
|
|
15056
15368
|
return $("loc").toArray().map((node) => $(node).text().trim());
|
|
15057
15369
|
}
|
|
15058
15370
|
return [
|
|
@@ -15095,8 +15407,8 @@ function markdownHeadingAnchors(body) {
|
|
|
15095
15407
|
return occurrence ? `${base}-${occurrence}` : base;
|
|
15096
15408
|
});
|
|
15097
15409
|
}
|
|
15098
|
-
function sourceNavigation(capture) {
|
|
15099
|
-
return extractSourceNavigation(capture);
|
|
15410
|
+
function sourceNavigation(capture, selectedScope) {
|
|
15411
|
+
return extractSourceNavigation(capture, selectedScope);
|
|
15100
15412
|
}
|
|
15101
15413
|
function idForUrl(url, scope) {
|
|
15102
15414
|
const relative7 = scope === "/" ? url.pathname.slice(1) : url.pathname.slice(scope.length).replace(/^\//, "");
|
|
@@ -15105,7 +15417,7 @@ function idForUrl(url, scope) {
|
|
|
15105
15417
|
}
|
|
15106
15418
|
function articleIdentity(document) {
|
|
15107
15419
|
if (!/html/i.test(document.contentType)) return document.finalUrl;
|
|
15108
|
-
const $ =
|
|
15420
|
+
const $ = load6(document.body);
|
|
15109
15421
|
const reference = $('link[rel="canonical"][href]').first().attr("href");
|
|
15110
15422
|
if (!reference) return document.finalUrl;
|
|
15111
15423
|
try {
|
|
@@ -15161,16 +15473,24 @@ function assetReferences(capture) {
|
|
|
15161
15473
|
}
|
|
15162
15474
|
const { $, root } = migrationHtmlArticle(capture.body);
|
|
15163
15475
|
return [
|
|
15164
|
-
...root.find("img[src],video[poster],a[download][href]").toArray().map((node) => $(node).
|
|
15476
|
+
...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")),
|
|
15165
15477
|
...root.find("[srcset]").toArray().flatMap((node) => migrationImageCandidates($(node).attr("srcset") ?? "", capture.finalUrl).map((candidate) => candidate.url))
|
|
15166
15478
|
].filter(Boolean);
|
|
15167
15479
|
}
|
|
15168
|
-
function unresolvedStaticControls(body, hasApiSchema = false) {
|
|
15480
|
+
function unresolvedStaticControls(body, hasApiSchema = false, coveredSchemaGroups = []) {
|
|
15169
15481
|
const { $, root } = migrationHtmlArticle(body);
|
|
15482
|
+
root.find('[role="tablist"],[data-component-part="tabs-list"]').each((index, node) => {
|
|
15483
|
+
if (coveredSchemaGroups.includes(index)) $(node).remove();
|
|
15484
|
+
});
|
|
15170
15485
|
const tabs = root.find('[role="tab"]').toArray();
|
|
15171
15486
|
for (const tab of tabs) {
|
|
15172
15487
|
const controls = $(tab).attr("aria-controls");
|
|
15173
|
-
const
|
|
15488
|
+
const group = $(tab).closest('[role="tablist"]');
|
|
15489
|
+
const container = group.closest(".code-group,.tabs-container,.tabs.tab-container");
|
|
15490
|
+
const peers = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === group[0]);
|
|
15491
|
+
const localPanels = container.find('[role="tabpanel"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
|
|
15492
|
+
const owners = container.find('[role="tablist"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
|
|
15493
|
+
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)) : $([]);
|
|
15174
15494
|
if (panels.length !== 1 || !panels.text().trim() && !panels.find("pre,img,table,svg").length || panels.find('[aria-busy="true"],[role="progressbar"]').length) return true;
|
|
15175
15495
|
}
|
|
15176
15496
|
return root.find("details:not([open])").toArray().some((node) => {
|
|
@@ -15221,12 +15541,12 @@ function hasCompleteLocalApiSchema(document) {
|
|
|
15221
15541
|
if (typeof value === "number" && !Number.isFinite(value)) return false;
|
|
15222
15542
|
if (!value || typeof value !== "object") return true;
|
|
15223
15543
|
if (Array.isArray(value)) return value.every((child) => visit(child, depth + 1));
|
|
15224
|
-
const
|
|
15225
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
15226
|
-
if (typeof
|
|
15544
|
+
const object3 = value;
|
|
15545
|
+
if (Object.prototype.hasOwnProperty.call(object3, "$ref")) {
|
|
15546
|
+
if (typeof object3.$ref !== "string" || !object3.$ref.startsWith("#/")) return false;
|
|
15227
15547
|
let target2 = document;
|
|
15228
15548
|
try {
|
|
15229
|
-
for (const encoded of
|
|
15549
|
+
for (const encoded of object3.$ref.slice(2).split("/")) {
|
|
15230
15550
|
const key = decodeURIComponent(encoded).replace(/~1/g, "/").replace(/~0/g, "~");
|
|
15231
15551
|
if (!target2 || typeof target2 !== "object" || !Object.prototype.hasOwnProperty.call(target2, key)) return false;
|
|
15232
15552
|
target2 = target2[key];
|
|
@@ -15235,7 +15555,7 @@ function hasCompleteLocalApiSchema(document) {
|
|
|
15235
15555
|
return false;
|
|
15236
15556
|
}
|
|
15237
15557
|
}
|
|
15238
|
-
return Object.values(
|
|
15558
|
+
return Object.values(object3).every((child) => visit(child, depth + 1));
|
|
15239
15559
|
};
|
|
15240
15560
|
return visit(document);
|
|
15241
15561
|
}
|
|
@@ -15250,6 +15570,25 @@ function resolveMigrationRecord(records, record) {
|
|
|
15250
15570
|
}
|
|
15251
15571
|
function navigationConfig(checkpoint, records) {
|
|
15252
15572
|
const navigation = checkpoint.navigation;
|
|
15573
|
+
const shortcuts = [];
|
|
15574
|
+
const collectShortcuts = (nodes) => {
|
|
15575
|
+
for (const node of nodes) {
|
|
15576
|
+
if (node.url) {
|
|
15577
|
+
const record = records.get(canonical(node.url, checkpoint.sourceUrl) ?? "");
|
|
15578
|
+
const href = record?.redirectTo ?? node.url;
|
|
15579
|
+
const identity = canonical(href, checkpoint.sourceUrl);
|
|
15580
|
+
const target2 = identity ? new URL(href, checkpoint.sourceUrl).href : null;
|
|
15581
|
+
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 });
|
|
15582
|
+
}
|
|
15583
|
+
collectShortcuts(node.children ?? []);
|
|
15584
|
+
}
|
|
15585
|
+
};
|
|
15586
|
+
collectShortcuts(navigation);
|
|
15587
|
+
const hasDropdown = navigation.some((node) => node.presentation === "dropdown");
|
|
15588
|
+
const externalNavigation = shortcuts.length || hasDropdown ? { navigation: {
|
|
15589
|
+
...shortcuts.length ? { shortcuts } : {},
|
|
15590
|
+
...hasDropdown ? { display: "dropdown" } : {}
|
|
15591
|
+
} } : {};
|
|
15253
15592
|
const projection = localeProjection(checkpoint);
|
|
15254
15593
|
const project2 = (nodes) => nodes.flatMap((node) => {
|
|
15255
15594
|
const record = node.url ? records.get(canonical(node.url, node.url) ?? "") : void 0;
|
|
@@ -15270,7 +15609,7 @@ function navigationConfig(checkpoint, records) {
|
|
|
15270
15609
|
return pages2.length || tab.url ? [{ tab: tab.label, ...pages2.length ? { pages: pages2 } : {}, ...tab.url ? { href: tab.url } : {} }] : [];
|
|
15271
15610
|
});
|
|
15272
15611
|
const primary = localizedTabs(projection.defaultLocale);
|
|
15273
|
-
return { tabs: primary.length ? primary : localizedTabs(locales[0]), ...locales.length > 1 ? {
|
|
15612
|
+
return { ...externalNavigation, tabs: primary.length ? primary : localizedTabs(locales[0]), ...locales.length > 1 ? {
|
|
15274
15613
|
i18n: {
|
|
15275
15614
|
defaultLocale: projection.defaultLocale,
|
|
15276
15615
|
locales: locales.map((code) => ({ code, label: code === "en" ? "English" : code })),
|
|
@@ -15279,7 +15618,7 @@ function navigationConfig(checkpoint, records) {
|
|
|
15279
15618
|
} : {} };
|
|
15280
15619
|
}
|
|
15281
15620
|
const pages = project2(navigation);
|
|
15282
|
-
return pages.length ? { tabs: [{ tab: "Documentation", pages }] } : null;
|
|
15621
|
+
return pages.length || shortcuts.length ? { ...externalNavigation, tabs: [{ tab: "Documentation", pages }] } : null;
|
|
15283
15622
|
}
|
|
15284
15623
|
function canonicalLanguage(value) {
|
|
15285
15624
|
if (!/^[a-z]{2,3}(?:-[a-z0-9]{2,8}){0,4}$/i.test(value)) return void 0;
|
|
@@ -15299,7 +15638,7 @@ function observedLocale(document, id, scopePath) {
|
|
|
15299
15638
|
}
|
|
15300
15639
|
const urlLocale = [id.split("/", 1)[0], ...scopePath.split("/"), document.finalUrl.pathname.split("/").filter(Boolean)[0]].find((segment) => segment && LOCALES.has(segment.toLowerCase()));
|
|
15301
15640
|
if (urlLocale) candidates.push({ source: "URL", value: canonicalLanguage(urlLocale) });
|
|
15302
|
-
const declared = /html/i.test(document.contentType) ?
|
|
15641
|
+
const declared = /html/i.test(document.contentType) ? load6(document.body)("html").attr("lang")?.trim() : void 0;
|
|
15303
15642
|
if (declared) {
|
|
15304
15643
|
const locale = canonicalLanguage(declared);
|
|
15305
15644
|
if (locale) candidates.push({ source: "html lang", value: locale });
|
|
@@ -15376,13 +15715,15 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
15376
15715
|
const projection = localeProjection(checkpoint);
|
|
15377
15716
|
const primaryIds = new Set(checkpoint.records.flatMap((record) => record.page && (!projection.canonicalLocale(record.page) || projection.canonicalLocale(record.page) === projection.defaultLocale) ? [projection.navigationId(record.page)] : []));
|
|
15378
15717
|
const outputIds = /* @__PURE__ */ new Set();
|
|
15379
|
-
const
|
|
15718
|
+
const resolve5 = (url) => {
|
|
15380
15719
|
const record = records.get(canonical(url, checkpoint.sourceUrl) ?? "");
|
|
15381
15720
|
return resolveMigrationRecord(records, record);
|
|
15382
15721
|
};
|
|
15383
15722
|
for (const record of checkpoint.records) {
|
|
15384
15723
|
counts[record.outcome]++;
|
|
15385
15724
|
issues.push(...record.issues ?? []);
|
|
15725
|
+
warnings.push(...record.warnings ?? []);
|
|
15726
|
+
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." });
|
|
15386
15727
|
if (record.outcome === "pending" || record.outcome === "failed") issues.push({ code: "missing-page", source: record.url, message: record.reason ?? `Inventory entry is ${record.outcome}.` });
|
|
15387
15728
|
if (!record.page || !record.evidence) continue;
|
|
15388
15729
|
const outputId = projection.pageId(record.page);
|
|
@@ -15401,8 +15742,9 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
15401
15742
|
continue;
|
|
15402
15743
|
}
|
|
15403
15744
|
if (!inScope(link, source, checkpoint.scopePath)) continue;
|
|
15745
|
+
if (isDiscoveryIndex(link, source)) continue;
|
|
15404
15746
|
if (/\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js|json|ya?ml)$/i.test(link.pathname)) continue;
|
|
15405
|
-
const destination =
|
|
15747
|
+
const destination = resolve5(link.toString());
|
|
15406
15748
|
if (!destination || !["imported", "excluded"].includes(destination.outcome)) {
|
|
15407
15749
|
issues.push({ code: "missing-page", source: record.url, message: `Unresolved internal destination: ${link}` });
|
|
15408
15750
|
} else if (link.hash && destination.outcome === "imported") {
|
|
@@ -15429,7 +15771,9 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
15429
15771
|
if (node.url) {
|
|
15430
15772
|
const url = new URL(node.url, source);
|
|
15431
15773
|
if (inScope(url, source, checkpoint.scopePath)) {
|
|
15432
|
-
const
|
|
15774
|
+
const destination = resolve5(url.toString());
|
|
15775
|
+
if (destination?.outcome === "excluded" && destination.redirectTo) continue;
|
|
15776
|
+
const page = destination?.page;
|
|
15433
15777
|
if (!page) issues.push({ code: "navigation-missing", source: node.url, message: `Source navigation target ${node.label} was not imported.` });
|
|
15434
15778
|
else if (locale && locale !== projection.defaultLocale && !outputIds.has(`${locale}/${projection.navigationId(page)}`)) {
|
|
15435
15779
|
issues.push({ code: "navigation-missing", source: node.url, message: `The ${locale} sidebar target ${node.label} has no authored localized runtime page.` });
|
|
@@ -15446,16 +15790,32 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
15446
15790
|
async function runUrlMigration(options) {
|
|
15447
15791
|
const source = validateMigrationUrl(options.sourceUrl);
|
|
15448
15792
|
const sourceUrl = canonical(source.toString(), source.toString());
|
|
15449
|
-
const requestedScope = options.scopePath ?? (source.pathname.endsWith("/") ? source.pathname : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/");
|
|
15450
|
-
let scopeEnd = requestedScope.length;
|
|
15451
|
-
while (scopeEnd > 0 && requestedScope.charCodeAt(scopeEnd - 1) === 47) scopeEnd--;
|
|
15452
|
-
const scopePath = requestedScope.slice(0, scopeEnd) || "/";
|
|
15453
|
-
if (!scopePath.startsWith("/") || scopePath.includes("?") || scopePath.includes("#") || !inScope(source, source, scopePath)) throw new Error("The selected documentation scope must contain the source URL.");
|
|
15454
15793
|
const maxPages = options.maxPages ?? 1e3;
|
|
15455
15794
|
if (!Number.isInteger(maxPages) || maxPages < 1 || maxPages > 1e4) throw new Error("maxPages must be between 1 and 10000.");
|
|
15456
15795
|
const concurrency = Math.max(1, Math.min(32, Math.floor(options.concurrency ?? 5)));
|
|
15457
15796
|
const batchSize = Math.max(1, Math.min(500, Math.floor(options.batchSize ?? 50)));
|
|
15458
15797
|
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.");
|
|
15798
|
+
const fetcher = options.fetcher ?? defaultMigrationFetcher;
|
|
15799
|
+
const acquire = options.captureProvider?.capture.bind(options.captureProvider) ?? (async ({ url }) => fetcher(url, { accept: ACCEPT }));
|
|
15800
|
+
let initial;
|
|
15801
|
+
let inferredScope;
|
|
15802
|
+
if (options.scopePath === void 0 && !options.checkpoint && !options.signal?.aborted) {
|
|
15803
|
+
initial = Promise.resolve().then(() => acquire({ url: source, signal: options.signal }));
|
|
15804
|
+
try {
|
|
15805
|
+
const document = await initial;
|
|
15806
|
+
if (document.finalUrl.origin === source.origin) {
|
|
15807
|
+
const inferred = inferUrlMigrationScope(document).scopePath;
|
|
15808
|
+
if (inScope(source, source, inferred)) inferredScope = inferred;
|
|
15809
|
+
}
|
|
15810
|
+
} catch {
|
|
15811
|
+
}
|
|
15812
|
+
}
|
|
15813
|
+
const capture = (request2) => initial && canonical(request2.url.href, sourceUrl) === sourceUrl ? initial : acquire(request2);
|
|
15814
|
+
const requestedScope = options.scopePath ?? options.checkpoint?.scopePath ?? inferredScope ?? (source.pathname.endsWith("/") ? source.pathname : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/");
|
|
15815
|
+
let scopeEnd = requestedScope.length;
|
|
15816
|
+
while (scopeEnd > 0 && requestedScope.charCodeAt(scopeEnd - 1) === 47) scopeEnd--;
|
|
15817
|
+
const scopePath = requestedScope.slice(0, scopeEnd) || "/";
|
|
15818
|
+
if (!scopePath.startsWith("/") || scopePath.includes("?") || scopePath.includes("#") || !inScope(source, source, scopePath)) throw new Error("The selected documentation scope must contain the source URL.");
|
|
15459
15819
|
const captureVersion = options.captureVersion ?? "fetch-v1";
|
|
15460
15820
|
if (options.checkpoint && options.checkpoint.records.length > MAX_RECORDS) throw new Error("Checkpoint inventory exceeds its bound.");
|
|
15461
15821
|
const checkpoint = options.checkpoint ? cloneUrlMigrationCheckpoint(options.checkpoint) : { schemaVersion: 1, converterVersion: CONVERTER_VERSION, sourceUrl, scopePath, captureVersion, records: [], navigation: [] };
|
|
@@ -15474,6 +15834,8 @@ async function runUrlMigration(options) {
|
|
|
15474
15834
|
delete record.apiSpecification;
|
|
15475
15835
|
delete record.version;
|
|
15476
15836
|
delete record.issues;
|
|
15837
|
+
delete record.warnings;
|
|
15838
|
+
delete record.redirectTo;
|
|
15477
15839
|
delete record.reason;
|
|
15478
15840
|
delete record.aliasOf;
|
|
15479
15841
|
}
|
|
@@ -15486,6 +15848,7 @@ async function runUrlMigration(options) {
|
|
|
15486
15848
|
const enqueue = (value, base, discoveredBy) => {
|
|
15487
15849
|
const url = canonical(value, base);
|
|
15488
15850
|
if (!url || !inScope(new URL(url), source, scopePath) && !isDiscoveryIndex(new URL(url), source)) return;
|
|
15851
|
+
const isAttachment = discoveredBy !== "submitted" && /\.(?:json|ya?ml)$/i.test(new URL(url).pathname);
|
|
15489
15852
|
const existing = records.get(url);
|
|
15490
15853
|
if (existing) {
|
|
15491
15854
|
if (!existing.discoveredBy.includes(discoveredBy)) {
|
|
@@ -15495,10 +15858,15 @@ async function runUrlMigration(options) {
|
|
|
15495
15858
|
return;
|
|
15496
15859
|
}
|
|
15497
15860
|
if (records.size >= MAX_RECORDS) throw new Error("Discovered URL inventory exceeded the 100000 URL safety bound.");
|
|
15498
|
-
const record = {
|
|
15861
|
+
const record = {
|
|
15862
|
+
url,
|
|
15863
|
+
discoveredBy: [discoveredBy],
|
|
15864
|
+
outcome: isAttachment ? "excluded" : "pending",
|
|
15865
|
+
...isAttachment ? { reason: "Machine-readable attachment, not an authored article." } : {}
|
|
15866
|
+
};
|
|
15499
15867
|
records.set(url, record);
|
|
15500
15868
|
checkpoint.records.push(record);
|
|
15501
|
-
pending.push(record);
|
|
15869
|
+
if (!isAttachment) pending.push(record);
|
|
15502
15870
|
changed.push(record);
|
|
15503
15871
|
};
|
|
15504
15872
|
enqueue(sourceUrl, sourceUrl, "submitted");
|
|
@@ -15531,8 +15899,6 @@ async function runUrlMigration(options) {
|
|
|
15531
15899
|
}
|
|
15532
15900
|
}
|
|
15533
15901
|
enqueueNavigation(checkpoint.navigation, sourceUrl);
|
|
15534
|
-
const fetcher = options.fetcher ?? defaultMigrationFetcher;
|
|
15535
|
-
const capture = options.captureProvider?.capture.bind(options.captureProvider) ?? (async ({ url }) => fetcher(url, { accept: ACCEPT }));
|
|
15536
15902
|
const assetFetcher = options.assetFetcher ?? (!options.fetcher && !options.captureProvider ? defaultAssetFetcher : void 0);
|
|
15537
15903
|
const usedIds = new Map([...records.values()].filter((record) => record.page).map((record) => [record.page.id, record.url]));
|
|
15538
15904
|
const finalUrls = new Map([...records.values()].filter((record) => record.evidence && record.page).map((record) => [canonical(record.evidence.finalUrl, sourceUrl), record.url]));
|
|
@@ -15540,6 +15906,10 @@ async function runUrlMigration(options) {
|
|
|
15540
15906
|
const assetPayloads = new Map(checkpoint.records.flatMap((record) => (record.assets ?? []).map((asset) => [asset.sha256, asset])));
|
|
15541
15907
|
const assetBytes = /* @__PURE__ */ new Map();
|
|
15542
15908
|
const retainAsset = (asset) => {
|
|
15909
|
+
if (asset.unavailable) {
|
|
15910
|
+
if ([404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) return asset;
|
|
15911
|
+
throw new Error("Invalid source asset availability evidence.");
|
|
15912
|
+
}
|
|
15543
15913
|
if (!acceptedAsset(asset)) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
|
|
15544
15914
|
const sha256 = hash2(asset.content);
|
|
15545
15915
|
const content = assetBytes.get(sha256) ?? Buffer.from(asset.content);
|
|
@@ -15572,6 +15942,11 @@ async function runUrlMigration(options) {
|
|
|
15572
15942
|
let markdownRepresentation;
|
|
15573
15943
|
try {
|
|
15574
15944
|
delete record.reason;
|
|
15945
|
+
if (/\/llms-full\.txt$/i.test(new URL(record.url).pathname)) {
|
|
15946
|
+
record.outcome = "excluded";
|
|
15947
|
+
record.reason = "Aggregate documentation corpus; individual articles are inventoried separately.";
|
|
15948
|
+
return;
|
|
15949
|
+
}
|
|
15575
15950
|
delete record.apiSpecification;
|
|
15576
15951
|
const represented = resolveMigrationRecord(records, record.representationOf ? records.get(record.representationOf) : void 0);
|
|
15577
15952
|
if (represented?.outcome === "imported") {
|
|
@@ -15581,6 +15956,16 @@ async function runUrlMigration(options) {
|
|
|
15581
15956
|
}
|
|
15582
15957
|
const document = await capture({ url: new URL(record.url), signal: options.signal });
|
|
15583
15958
|
if (options.signal?.aborted) return;
|
|
15959
|
+
if (document.status && document.status >= 300 && document.status < 400 && document.headers?.location) {
|
|
15960
|
+
const destination = canonical(document.headers.location, document.finalUrl.href);
|
|
15961
|
+
if (destination && new URL(destination).origin !== source.origin) {
|
|
15962
|
+
record.outcome = "excluded";
|
|
15963
|
+
record.redirectTo = new URL(document.headers.location, document.finalUrl).href;
|
|
15964
|
+
record.reason = "The source entry is a redirect to an external public resource, retained as a link.";
|
|
15965
|
+
return;
|
|
15966
|
+
}
|
|
15967
|
+
throw new Error("Capture returned an unresolved redirect.");
|
|
15968
|
+
}
|
|
15584
15969
|
if (Buffer.byteLength(document.body) > MAX_RESPONSE_BYTES) throw new Error("Captured response exceeds the 8 MB safety bound.");
|
|
15585
15970
|
const identity = articleIdentity(document);
|
|
15586
15971
|
const final = canonical(identity.toString(), sourceUrl);
|
|
@@ -15588,6 +15973,7 @@ async function runUrlMigration(options) {
|
|
|
15588
15973
|
if (!inScope(document.finalUrl, source, scopePath) && !(role === "index" && isDiscoveryIndex(document.finalUrl, source))) {
|
|
15589
15974
|
record.outcome = "excluded";
|
|
15590
15975
|
record.reason = "Redirected outside the selected origin/path.";
|
|
15976
|
+
if (document.finalUrl.origin === source.origin && (!document.status || document.status >= 200 && document.status < 300)) record.redirectTo = document.finalUrl.href;
|
|
15591
15977
|
return;
|
|
15592
15978
|
}
|
|
15593
15979
|
if (document.status && document.status >= 400) throw new Error(`Source page returned HTTP ${document.status}.`);
|
|
@@ -15605,7 +15991,7 @@ async function runUrlMigration(options) {
|
|
|
15605
15991
|
return;
|
|
15606
15992
|
}
|
|
15607
15993
|
if (/html/i.test(document.contentType)) {
|
|
15608
|
-
const $ =
|
|
15994
|
+
const $ = load6(document.body);
|
|
15609
15995
|
$('link[rel="alternate"][type="text/markdown"][href]').each((_index, node) => {
|
|
15610
15996
|
const alternate = canonical($(node).attr("href"), final);
|
|
15611
15997
|
if (!alternate) return;
|
|
@@ -15619,7 +16005,7 @@ async function runUrlMigration(options) {
|
|
|
15619
16005
|
changed.push(sibling);
|
|
15620
16006
|
});
|
|
15621
16007
|
}
|
|
15622
|
-
const observedNavigation = sourceNavigation(document);
|
|
16008
|
+
const observedNavigation = sourceNavigation(document, scopePath);
|
|
15623
16009
|
if (observedNavigation.length && !(checkpoint.navigation.some((node) => node.kind === "tab") && !observedNavigation.some((node) => node.kind === "tab"))) {
|
|
15624
16010
|
const merged = mergeSourceNavigation(checkpoint.navigation, observedNavigation);
|
|
15625
16011
|
if (JSON.stringify(merged) !== JSON.stringify(checkpoint.navigation)) {
|
|
@@ -15661,6 +16047,7 @@ async function runUrlMigration(options) {
|
|
|
15661
16047
|
record.reason = "Content could not be represented as safe inert MDX.";
|
|
15662
16048
|
return;
|
|
15663
16049
|
}
|
|
16050
|
+
let coveredSchemaGroups = [];
|
|
15664
16051
|
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;
|
|
15665
16052
|
if (/html/i.test(document.contentType) && markdownRepresentation && (unresolvedStaticControls(document.body) || hasSchemaChoice)) {
|
|
15666
16053
|
const schemaCapture = await capture({ url: new URL(markdownRepresentation), signal: options.signal });
|
|
@@ -15676,6 +16063,7 @@ async function runUrlMigration(options) {
|
|
|
15676
16063
|
const path = `openapi/migration-${sha256}.json`;
|
|
15677
16064
|
record.apiSpecification = { path, sha256, document: fragment.document };
|
|
15678
16065
|
page.openapi = `${path} ${fragment.method} ${fragment.path}`;
|
|
16066
|
+
coveredSchemaGroups = apiSchemaTabGroups(document.body, fragment.document, fragment);
|
|
15679
16067
|
}
|
|
15680
16068
|
}
|
|
15681
16069
|
const states = document.states ?? [];
|
|
@@ -15705,17 +16093,24 @@ ${difference}`;
|
|
|
15705
16093
|
}
|
|
15706
16094
|
record.assets = [];
|
|
15707
16095
|
record.issues = [];
|
|
16096
|
+
record.warnings = [];
|
|
15708
16097
|
if (/html/i.test(document.contentType)) {
|
|
15709
16098
|
const { $, root } = migrationHtmlArticle(document.body);
|
|
15710
16099
|
root.find("nav,footer,aside,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
|
|
15711
16100
|
root.find('button svg,a.hash-link svg,[aria-hidden="true"] svg,svg[aria-hidden="true"],svg[data-lucide],svg[data-icon]').remove();
|
|
16101
|
+
root.find("video,iframe").each((_index, node) => {
|
|
16102
|
+
if (projectMigrationMedia($.html(node), document.finalUrl) !== null) {
|
|
16103
|
+
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." });
|
|
16104
|
+
$(node).remove();
|
|
16105
|
+
}
|
|
16106
|
+
});
|
|
15712
16107
|
if (record.apiSpecification) {
|
|
15713
16108
|
root.find('select[aria-label="Select schema type"],select[aria-label="\u9009\u62E9\u67B6\u6784\u7C7B\u578B"]').remove();
|
|
15714
16109
|
root.find('.option-dropdown svg[data-component-part="icon-svg"]').remove();
|
|
15715
16110
|
}
|
|
15716
16111
|
const unsupported = [...new Set(root.find("iframe,object,embed,canvas,svg,form,input,textarea,select,math,video,audio").toArray().map((node) => node.tagName))];
|
|
15717
16112
|
if (unsupported.length) record.issues.push({ code: "unsupported-component", source: record.url, message: `Rendered content requires explicit review for: ${unsupported.join(", ")}.` });
|
|
15718
|
-
if (!states.length && unresolvedStaticControls(document.body, Boolean(record.apiSpecification))) record.issues.push({ code: "unsupported-component", source: record.url, message: "An interactive alternative has no complete captured panel or disclosure content." });
|
|
16113
|
+
if (!states.length && unresolvedStaticControls(document.body, Boolean(record.apiSpecification), coveredSchemaGroups)) record.issues.push({ code: "unsupported-component", source: record.url, message: "An interactive alternative has no complete captured panel or disclosure content." });
|
|
15719
16114
|
}
|
|
15720
16115
|
const suppliedAssets = new Map((document.assets ?? []).map((asset) => [canonical(asset.url, final), asset]));
|
|
15721
16116
|
const assetUrls = [...new Set([...assetReferences(document), ...states.flatMap((state) => assetReferences(state.response))].map((url) => canonical(url, final)).filter((url) => Boolean(url)))];
|
|
@@ -15729,6 +16124,11 @@ ${difference}`;
|
|
|
15729
16124
|
const supplied = suppliedAssets.get(url);
|
|
15730
16125
|
const asset = supplied ? retainAsset(supplied) : assetRequest ? await assetRequest : null;
|
|
15731
16126
|
if (!asset) throw new Error("Capture host did not supply asset bytes or a binary fetch boundary.");
|
|
16127
|
+
if (asset.unavailable && [404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) {
|
|
16128
|
+
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}` });
|
|
16129
|
+
page.body = rewriteMarkdownDestinations(page.body, (destination) => canonical(destination, final) === url ? url : destination);
|
|
16130
|
+
continue;
|
|
16131
|
+
}
|
|
15732
16132
|
const extension = acceptedAsset(asset);
|
|
15733
16133
|
if (!extension) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
|
|
15734
16134
|
const sha256 = hash2(asset.content);
|
|
@@ -15770,7 +16170,7 @@ ${difference}`;
|
|
|
15770
16170
|
} catch (error) {
|
|
15771
16171
|
if (options.signal?.aborted) return;
|
|
15772
16172
|
const wasImported = record.outcome === "imported";
|
|
15773
|
-
record.outcome = record.discoveredBy.every((origin) => origin === "discovery-index") ? "excluded" : "failed";
|
|
16173
|
+
record.outcome = isDiscoveryIndex(new URL(record.url), source) || record.discoveredBy.every((origin) => origin === "discovery-index") ? "excluded" : "failed";
|
|
15774
16174
|
record.reason = error instanceof Error ? error.message : String(error);
|
|
15775
16175
|
if (record.page) {
|
|
15776
16176
|
if (wasImported) imported--;
|
|
@@ -15795,7 +16195,7 @@ ${difference}`;
|
|
|
15795
16195
|
for (const record of records.values()) {
|
|
15796
16196
|
if (!record.representationOf) continue;
|
|
15797
16197
|
const primary = resolveMigrationRecord(records, records.get(record.representationOf));
|
|
15798
|
-
if (!primary) continue;
|
|
16198
|
+
if (!primary || primary === record) continue;
|
|
15799
16199
|
const winner = primary.outcome === "imported" ? primary : record.outcome === "imported" && primary.outcome === "failed" ? record : void 0;
|
|
15800
16200
|
if (!winner) continue;
|
|
15801
16201
|
const alternate = winner === primary ? record : primary;
|
|
@@ -15921,7 +16321,7 @@ function rewriteMarkdownDestinations(body, rewrite) {
|
|
|
15921
16321
|
return { start: start + index, end: start + index + node.url.length, replacement };
|
|
15922
16322
|
}).filter((edit) => edit !== null).sort((a, b) => b.start - a.start);
|
|
15923
16323
|
for (const node of markdownNodes(body)) for (const attribute of node.attributes ?? []) {
|
|
15924
|
-
if (!attribute.name || !["src", "href", "srcSet", "srcset", "img", "primaryHref", "secondaryHref"].includes(attribute.name) || !attribute.position) continue;
|
|
16324
|
+
if (!attribute.name || !["src", "poster", "href", "srcSet", "srcset", "img", "primaryHref", "secondaryHref"].includes(attribute.name) || !attribute.position) continue;
|
|
15925
16325
|
let value = attribute.value;
|
|
15926
16326
|
if (typeof value !== "string") {
|
|
15927
16327
|
try {
|
|
@@ -15982,7 +16382,7 @@ async function getPinnedAddress(url) {
|
|
|
15982
16382
|
}
|
|
15983
16383
|
async function requestPinnedSpec(url) {
|
|
15984
16384
|
const pinned = await getPinnedAddress(url);
|
|
15985
|
-
return new Promise((
|
|
16385
|
+
return new Promise((resolve5, reject) => {
|
|
15986
16386
|
const req = request(url, {
|
|
15987
16387
|
headers: { Accept: "application/json, application/yaml, text/yaml, */*", "User-Agent": "Thally-Migrate/1.0 (+https://thally.io)" },
|
|
15988
16388
|
signal: AbortSignal.timeout(3e4),
|
|
@@ -15996,7 +16396,7 @@ async function requestPinnedSpec(url) {
|
|
|
15996
16396
|
const status = response.statusCode ?? 0;
|
|
15997
16397
|
if (status >= 300 && status < 400) {
|
|
15998
16398
|
response.resume();
|
|
15999
|
-
|
|
16399
|
+
resolve5({ status, location: response.headers.location, body: Buffer.alloc(0) });
|
|
16000
16400
|
return;
|
|
16001
16401
|
}
|
|
16002
16402
|
if (status < 200 || status >= 300) {
|
|
@@ -16020,7 +16420,7 @@ async function requestPinnedSpec(url) {
|
|
|
16020
16420
|
}
|
|
16021
16421
|
chunks.push(chunk);
|
|
16022
16422
|
});
|
|
16023
|
-
response.on("end", () =>
|
|
16423
|
+
response.on("end", () => resolve5({ status, body: Buffer.concat(chunks) }));
|
|
16024
16424
|
response.on("error", reject);
|
|
16025
16425
|
});
|
|
16026
16426
|
req.on("error", reject);
|