@thallylabs/migrate 0.2.39 → 0.2.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/repository.ts
2
2
  import { compileSync as compileSync2 } from "@mdx-js/mdx";
3
- import { createHash as createHash2 } from "crypto";
3
+ import { createHash as createHash4 } from "crypto";
4
4
  import { spawn } from "child_process";
5
5
  import {
6
6
  closeSync,
@@ -4362,14 +4362,14 @@ function addMintlifyDirectoryRedirects(config, pages) {
4362
4362
  for (const page of pagesByNavigationId.get(navigationId) ?? []) {
4363
4363
  const segments = page.id.split("/");
4364
4364
  for (let length = 1; length < segments.length; length++) {
4365
- const directory = segments.slice(0, length).join("/");
4366
- if (!firstDescendantByDirectory.has(directory)) firstDescendantByDirectory.set(directory, page);
4365
+ const directory2 = segments.slice(0, length).join("/");
4366
+ if (!firstDescendantByDirectory.has(directory2)) firstDescendantByDirectory.set(directory2, page);
4367
4367
  }
4368
4368
  }
4369
4369
  }
4370
- for (const [directory, landing] of firstDescendantByDirectory) {
4371
- if (pageIds.has(directory.toLowerCase())) continue;
4372
- const source = `/${directory}`;
4370
+ for (const [directory2, landing] of firstDescendantByDirectory) {
4371
+ if (pageIds.has(directory2.toLowerCase())) continue;
4372
+ const source = `/${directory2}`;
4373
4373
  if (hasAuthoredRedirect(source)) continue;
4374
4374
  redirects.push({ source, destination: `/${landing.id}`, permanent: false });
4375
4375
  redirectSources.add(source.toLowerCase());
@@ -4607,10 +4607,10 @@ function mintlifyNavigationApiReferences(config) {
4607
4607
  function collect(node, tabLabel, gated, otherLanguage, hidden, sibling) {
4608
4608
  for (const kind of ["openapi", "asyncapi"]) {
4609
4609
  const value = specSource(node[kind]);
4610
- const directory = objectValue2(node[kind])?.directory;
4610
+ const directory2 = objectValue2(node[kind])?.directory;
4611
4611
  if (value) {
4612
4612
  const listed = listedOperations(node);
4613
- const operations = typeof directory === "string" && directory.trim() ? listed : [];
4613
+ const operations = typeof directory2 === "string" && directory2.trim() ? listed : [];
4614
4614
  const groups = gated || otherLanguage || hidden ? [] : operationGroups(node);
4615
4615
  const tooDeepOnly = groups.length === 0 && listed.length > 0 && !gated && !hidden && groupOperationKeys(operationGroups(node, SWEEP_DEPTH_LIMIT)).length === listed.length;
4616
4616
  const operationsWithheld = !otherLanguage && (hidden || groups.length === 0 && listed.length > 0 && !tooDeepOnly);
@@ -4619,7 +4619,7 @@ function mintlifyNavigationApiReferences(config) {
4619
4619
  kind,
4620
4620
  tabLabel,
4621
4621
  ...sibling,
4622
- ...typeof directory === "string" && directory.trim() ? { directory: directory.trim() } : {},
4622
+ ...typeof directory2 === "string" && directory2.trim() ? { directory: directory2.trim() } : {},
4623
4623
  ...operations.length > 0 ? { operations } : {},
4624
4624
  ...groups.length > 0 ? { groups } : {},
4625
4625
  ...operationsWithheld ? { operationsWithheld: true } : {},
@@ -5840,16 +5840,16 @@ function registerFolder(object3, parentSegments, context) {
5840
5840
  const label = typeof object3.title === "string" && object3.title.trim() ? object3.title.trim() : titleCase2(basename2(folder));
5841
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
- const visit = (directory, routeSegments, depth) => {
5843
+ const visit = (directory2, routeSegments, depth) => {
5844
5844
  if (depth > 12 || context.descriptors.length >= 5e3) {
5845
5845
  context.navigationWalk.incomplete = true;
5846
5846
  warnOnce2(context, "fern-folder-limit", "Fern folder navigation exceeded its depth or page budget; remaining access rules could not be classified.");
5847
5847
  return [];
5848
5848
  }
5849
5849
  const pages2 = [];
5850
- const entries = readdirSync(directory, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".") && !entry.isSymbolicLink()).sort((left, right) => Number(left.name !== "index.mdx" && left.name !== "index.md") - Number(right.name !== "index.mdx" && right.name !== "index.md") || left.name.localeCompare(right.name));
5850
+ const entries = readdirSync(directory2, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".") && !entry.isSymbolicLink()).sort((left, right) => Number(left.name !== "index.mdx" && left.name !== "index.md") - Number(right.name !== "index.mdx" && right.name !== "index.md") || left.name.localeCompare(right.name));
5851
5851
  for (const entry of entries) {
5852
- const path = resolveWithin(directory, entry.name);
5852
+ const path = resolveWithin(directory2, entry.name);
5853
5853
  if (entry.isDirectory()) {
5854
5854
  const nested = visit(path, [...routeSegments, fernBasicSlug(entry.name)], depth + 1);
5855
5855
  if (nested.length) pages2.push({ group: titleCase2(entry.name), pages: nested });
@@ -6839,9 +6839,9 @@ function readDocusaurusRedirects(repositoryRoot, warnings) {
6839
6839
  const literal = namedExportObjectLiteralText(moduleSource, identifier);
6840
6840
  return literal ? redirectEntriesFromObjectLiteralText(literal, warnings) : [];
6841
6841
  }
6842
- function readCategoryMetadata(contentRoot, directory) {
6842
+ function readCategoryMetadata(contentRoot, directory2) {
6843
6843
  for (const filename of CATEGORY_FILENAMES) {
6844
- const path = resolveWithin(contentRoot, posix3.join(directory, filename));
6844
+ const path = resolveWithin(contentRoot, posix3.join(directory2, filename));
6845
6845
  try {
6846
6846
  withinRealRoot(path, contentRoot);
6847
6847
  } catch {
@@ -7128,9 +7128,9 @@ function autogeneratedItems(dirName, context) {
7128
7128
  item: descriptor.navigationId
7129
7129
  }));
7130
7130
  const groups = [...childDirectories].map((segment) => {
7131
- const directory = normalizedDir ? posix3.join(normalizedDir, segment) : segment;
7132
- const metadata = readCategoryMetadata(context.contentRoot, directory);
7133
- const pages2 = autogeneratedItems(directory, context);
7131
+ const directory2 = normalizedDir ? posix3.join(normalizedDir, segment) : segment;
7132
+ const metadata = readCategoryMetadata(context.contentRoot, directory2);
7133
+ const pages2 = autogeneratedItems(directory2, context);
7134
7134
  if (pages2.length === 1 && typeof pages2[0] === "string" && !metadata.label && metadata.position === void 0 && !metadata.link) {
7135
7135
  const onlyPage = context.descriptors.find((descriptor) => descriptor.navigationId === pages2[0]);
7136
7136
  if (onlyPage && /(?:^|\/)(?:index|readme)\.mdx?$/i.test(onlyPage.sourcePath)) {
@@ -7141,7 +7141,7 @@ function autogeneratedItems(dirName, context) {
7141
7141
  };
7142
7142
  }
7143
7143
  }
7144
- const landing = generatedIndexPage(metadata.label ?? titleCase3(segment), metadata.link, context, directory);
7144
+ const landing = generatedIndexPage(metadata.label ?? titleCase3(segment), metadata.link, context, directory2);
7145
7145
  if (landing && !pages2.includes(landing)) pages2.unshift(landing);
7146
7146
  return {
7147
7147
  position: metadata.position ?? Number.MAX_SAFE_INTEGER,
@@ -7249,11 +7249,11 @@ function readDocusaurusPresentation(repositoryRoot) {
7249
7249
  if (/\btheme\(/.test(css) || /\.s[ac]ss$/.test(relative7)) {
7250
7250
  const candidateDirectory = resolveWithin(repositoryRoot, "build/assets/css");
7251
7251
  if (existsSync4(candidateDirectory)) {
7252
- const directory = withinRealRoot(candidateDirectory, repositoryRoot);
7253
- const compiled = readdirSync2(directory).filter((name) => /^styles[.\w-]*\.css$/.test(name)).sort().slice(0, 8);
7252
+ const directory2 = withinRealRoot(candidateDirectory, repositoryRoot);
7253
+ const compiled = readdirSync2(directory2).filter((name) => /^styles[.\w-]*\.css$/.test(name)).sort().slice(0, 8);
7254
7254
  let totalBytes = 0;
7255
7255
  css = compiled.map((name) => {
7256
- const path2 = withinRealRoot(resolveWithin(directory, name), repositoryRoot);
7256
+ const path2 = withinRealRoot(resolveWithin(directory2, name), repositoryRoot);
7257
7257
  const stat = lstatSync4(path2);
7258
7258
  if (!stat.isFile()) return "";
7259
7259
  totalBytes += stat.size;
@@ -7552,11 +7552,11 @@ 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 load3 } from "cheerio";
7556
- import remarkMdx3 from "remark-mdx";
7557
- import remarkParse3 from "remark-parse";
7555
+ import { load as load4 } from "cheerio";
7556
+ import remarkMdx4 from "remark-mdx";
7557
+ import remarkParse4 from "remark-parse";
7558
7558
  import TurndownService from "turndown";
7559
- import { unified as unified3 } from "unified";
7559
+ import { unified as unified4 } from "unified";
7560
7560
  import { parse as parseYaml5, stringify as stringifyYaml2 } from "yaml";
7561
7561
 
7562
7562
  // src/analytics.ts
@@ -7850,139 +7850,1339 @@ function projectMigrationMedia(html, base) {
7850
7850
  }
7851
7851
  }
7852
7852
 
7853
- // src/redirects.ts
7854
- var LISTED = 10;
7855
- function exactRoute(route) {
7856
- return route.startsWith("/") && !/[?:*#()[\]{}]/.test(route) ? route.replace(/\/+$/, "") || "/" : null;
7857
- }
7858
- function literalRoute(route) {
7859
- return exactRoute(route)?.toLowerCase() ?? null;
7860
- }
7861
- function resolveDotSegments(route) {
7862
- const segments = [];
7863
- for (const segment of route.split("/").slice(1)) {
7864
- const dots = segment.replace(/%2e/gi, ".");
7865
- if (dots === "..") segments.pop();
7866
- else if (dots !== ".") segments.push(segment);
7853
+ // src/url-static-content.ts
7854
+ import { createHash as createHash2 } from "crypto";
7855
+ import { load as load3 } from "cheerio";
7856
+
7857
+ // src/svg-policy.ts
7858
+ import { Parser } from "htmlparser2";
7859
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
7860
+ var XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
7861
+ var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
7862
+ "svg",
7863
+ "g",
7864
+ "defs",
7865
+ "symbol",
7866
+ "use",
7867
+ "title",
7868
+ "desc",
7869
+ "path",
7870
+ "rect",
7871
+ "circle",
7872
+ "ellipse",
7873
+ "line",
7874
+ "polyline",
7875
+ "polygon",
7876
+ "text",
7877
+ "tspan",
7878
+ "textpath",
7879
+ "lineargradient",
7880
+ "radialgradient",
7881
+ "stop",
7882
+ "clippath",
7883
+ "mask",
7884
+ "pattern",
7885
+ "marker",
7886
+ "filter",
7887
+ "feblend",
7888
+ "fecolormatrix",
7889
+ "fecomponenttransfer",
7890
+ "fecomposite",
7891
+ "feconvolvematrix",
7892
+ "fediffuselighting",
7893
+ "fedisplacementmap",
7894
+ "fedistantlight",
7895
+ "fedropshadow",
7896
+ "feflood",
7897
+ "fefunca",
7898
+ "fefuncb",
7899
+ "fefuncg",
7900
+ "fefuncr",
7901
+ "fegaussianblur",
7902
+ "femerge",
7903
+ "femergenode",
7904
+ "femorphology",
7905
+ "feoffset",
7906
+ "fepointlight",
7907
+ "fespecularlighting",
7908
+ "fespotlight",
7909
+ "fetile",
7910
+ "feturbulence"
7911
+ ]);
7912
+ var ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
7913
+ "xmlns",
7914
+ "xmlns:xlink",
7915
+ "xml:space",
7916
+ "version",
7917
+ "baseprofile",
7918
+ "id",
7919
+ "class",
7920
+ "viewbox",
7921
+ "preserveaspectratio",
7922
+ "width",
7923
+ "height",
7924
+ "x",
7925
+ "y",
7926
+ "x1",
7927
+ "y1",
7928
+ "x2",
7929
+ "y2",
7930
+ "cx",
7931
+ "cy",
7932
+ "r",
7933
+ "rx",
7934
+ "ry",
7935
+ "d",
7936
+ "points",
7937
+ "pathlength",
7938
+ "transform",
7939
+ "transform-origin",
7940
+ "fill",
7941
+ "fill-opacity",
7942
+ "fill-rule",
7943
+ "stroke",
7944
+ "stroke-width",
7945
+ // Some SVG exporters persist React spellings. These remain fixed static
7946
+ // drawing attributes; accepting them never grants arbitrary JSX execution.
7947
+ "fillrule",
7948
+ "cliprule",
7949
+ "stroke-opacity",
7950
+ "stroke-linecap",
7951
+ "stroke-linejoin",
7952
+ "stroke-miterlimit",
7953
+ "stroke-dasharray",
7954
+ "stroke-dashoffset",
7955
+ "opacity",
7956
+ "color",
7957
+ "display",
7958
+ "visibility",
7959
+ "overflow",
7960
+ "clip-path",
7961
+ "clip-rule",
7962
+ "clippathunits",
7963
+ "mask",
7964
+ "maskunits",
7965
+ "maskcontentunits",
7966
+ "filter",
7967
+ "filterunits",
7968
+ "primitiveunits",
7969
+ "marker-start",
7970
+ "marker-mid",
7971
+ "marker-end",
7972
+ "markerwidth",
7973
+ "markerheight",
7974
+ "markerunits",
7975
+ "refx",
7976
+ "refy",
7977
+ "orient",
7978
+ "patternunits",
7979
+ "patterncontentunits",
7980
+ "patterntransform",
7981
+ "gradientunits",
7982
+ "gradienttransform",
7983
+ "spreadmethod",
7984
+ "fx",
7985
+ "fy",
7986
+ "fr",
7987
+ "offset",
7988
+ "stop-color",
7989
+ "stop-opacity",
7990
+ "font-family",
7991
+ "font-size",
7992
+ "font-weight",
7993
+ "font-style",
7994
+ "font-variant",
7995
+ "letter-spacing",
7996
+ "word-spacing",
7997
+ "text-anchor",
7998
+ "dominant-baseline",
7999
+ "alignment-baseline",
8000
+ "baseline-shift",
8001
+ "text-decoration",
8002
+ "dx",
8003
+ "dy",
8004
+ "rotate",
8005
+ "textlength",
8006
+ "lengthadjust",
8007
+ "startoffset",
8008
+ "writing-mode",
8009
+ "direction",
8010
+ "unicode-bidi",
8011
+ "shape-rendering",
8012
+ "text-rendering",
8013
+ "image-rendering",
8014
+ "color-interpolation",
8015
+ "color-interpolation-filters",
8016
+ "vector-effect",
8017
+ "paint-order",
8018
+ "mix-blend-mode",
8019
+ "isolation",
8020
+ "in",
8021
+ "in2",
8022
+ "result",
8023
+ "stddeviation",
8024
+ "mode",
8025
+ "type",
8026
+ "values",
8027
+ "tablevalues",
8028
+ "slope",
8029
+ "intercept",
8030
+ "amplitude",
8031
+ "exponent",
8032
+ "operator",
8033
+ "k1",
8034
+ "k2",
8035
+ "k3",
8036
+ "k4",
8037
+ "radius",
8038
+ "scale",
8039
+ "xchannelselector",
8040
+ "ychannelselector",
8041
+ "basefrequency",
8042
+ "numoctaves",
8043
+ "seed",
8044
+ "stitchtiles",
8045
+ "flood-color",
8046
+ "flood-opacity",
8047
+ "lighting-color",
8048
+ "surfacescale",
8049
+ "diffuseconstant",
8050
+ "specularconstant",
8051
+ "specularexponent",
8052
+ "kernelmatrix",
8053
+ "kernelunitlength",
8054
+ "order",
8055
+ "divisor",
8056
+ "bias",
8057
+ "targetx",
8058
+ "targety",
8059
+ "edgemode",
8060
+ "preservealpha",
8061
+ "azimuth",
8062
+ "elevation",
8063
+ "z",
8064
+ "pointsatx",
8065
+ "pointsaty",
8066
+ "pointsatz",
8067
+ "limitingconeangle",
8068
+ "aria-hidden",
8069
+ "aria-label",
8070
+ "aria-labelledby",
8071
+ "aria-describedby",
8072
+ "role",
8073
+ "focusable",
8074
+ // Validated below: only in-document fragment references.
8075
+ "href",
8076
+ "xlink:href"
8077
+ ]);
8078
+ function hasExternalReference(value) {
8079
+ if (value.includes("\\")) return true;
8080
+ const compact = value.replace(/[\u0000- ]/gu, "").toLowerCase();
8081
+ if (/(?:java|vb)script:|data:|expression\(|@import|<|>/u.test(compact)) {
8082
+ return true;
7867
8083
  }
7868
- return `/${segments.join("/")}`;
7869
- }
7870
- function destinationRoute(destination) {
7871
- const route = destination.startsWith("//") ? null : exactRoute(destination.split(/[?#]/, 1)[0]);
7872
- return route === null ? null : resolveDotSegments(route);
8084
+ for (const match of compact.matchAll(/url\(([^)]*)\)/gu)) {
8085
+ if (!/^["']?#[a-z0-9_.:-]+["']?$/u.test(match[1])) return true;
8086
+ }
8087
+ return compact.replace(/url\(([^)]*)\)/gu, "").includes("url(");
7873
8088
  }
7874
- function pageRoutes(pages) {
7875
- return pages.flatMap((page) => [`/${page.id}`, `/${page.id.replace(/\/index$/, "")}`]);
7876
- }
7877
- function servedRoutes(pages) {
7878
- return new Set(pageRoutes(pages).map((route) => route.toLowerCase()));
8089
+ function utf8SvgText(content) {
8090
+ const hasUtf8Bom = content[0] === 239 && content[1] === 187 && content[2] === 191;
8091
+ const body = hasUtf8Bom ? content.subarray(3) : content;
8092
+ if (body[0] === 254 && body[1] === 255 || body[0] === 255 && body[1] === 254 || body[0] === 0 && body[1] === 0 || body.includes(0)) {
8093
+ return null;
8094
+ }
8095
+ const text3 = body.toString("utf8");
8096
+ if (text3.includes("\uFFFD")) return null;
8097
+ const declaration = /^<\?xml\b[^>]*\?>/iu.exec(text3);
8098
+ if (declaration) {
8099
+ const encoding = /\bencoding\s*=\s*["']([^"']*)["']/iu.exec(declaration[0]);
8100
+ if (encoding && !/^utf-?8$/iu.test(encoding[1].trim())) return null;
8101
+ } else if (/<\?xml\b/iu.test(text3)) {
8102
+ return null;
8103
+ }
8104
+ return text3;
7879
8105
  }
7880
- function list(sources) {
7881
- return `${sources.slice(0, LISTED).join(", ")}${sources.length > LISTED ? ", ..." : ""}`;
8106
+ function isPassiveSvg(content) {
8107
+ const text3 = utf8SvgText(Buffer.from(content.buffer, content.byteOffset, content.byteLength));
8108
+ if (text3 === null) return false;
8109
+ let isPassive = true;
8110
+ let depth = 0;
8111
+ let sawRoot = false;
8112
+ const openStarts = [];
8113
+ const fail = () => {
8114
+ isPassive = false;
8115
+ };
8116
+ const parser4 = new Parser(
8117
+ {
8118
+ onopentag(name, attributes) {
8119
+ const element = name.toLowerCase();
8120
+ if (depth === 0) {
8121
+ if (sawRoot || element !== "svg") fail();
8122
+ sawRoot = true;
8123
+ }
8124
+ depth += 1;
8125
+ openStarts.push(parser4.startIndex);
8126
+ if (element.includes(":") || !ALLOWED_ELEMENTS.has(element)) fail();
8127
+ for (const [rawName, value] of Object.entries(attributes)) {
8128
+ const attribute = rawName.toLowerCase();
8129
+ if (!ALLOWED_ATTRIBUTES.has(attribute)) {
8130
+ fail();
8131
+ continue;
8132
+ }
8133
+ if (attribute === "xmlns" && value !== SVG_NAMESPACE) fail();
8134
+ if (attribute === "xmlns:xlink" && value !== XLINK_NAMESPACE) fail();
8135
+ if ((attribute === "href" || attribute === "xlink:href") && !/^#[A-Za-z0-9_.:-]+$/u.test(value.trim())) {
8136
+ fail();
8137
+ }
8138
+ if (hasExternalReference(value)) fail();
8139
+ }
8140
+ },
8141
+ onclosetag(_name, isImplied) {
8142
+ depth -= 1;
8143
+ const start = openStarts.pop();
8144
+ if (isImplied && start !== parser4.startIndex) fail();
8145
+ },
8146
+ // DOCTYPE (and with it custom entities) or any other declaration other
8147
+ // than the XML prolog.
8148
+ onprocessinginstruction(name) {
8149
+ if (name.toLowerCase() !== "?xml") fail();
8150
+ },
8151
+ oncdatastart: fail,
8152
+ ontext(value) {
8153
+ if (depth === 0 && value.trim()) fail();
8154
+ },
8155
+ onerror: fail
8156
+ },
8157
+ { xmlMode: true, decodeEntities: true, recognizeSelfClosing: true }
8158
+ );
8159
+ try {
8160
+ parser4.write(text3);
8161
+ parser4.end();
8162
+ } catch {
8163
+ return false;
8164
+ }
8165
+ return isPassive && sawRoot && depth === 0;
7882
8166
  }
7883
- var ROUTE_PATTERN_OPERATORS = /[+?()[\]{}]/;
7884
- function dropUnroutableRedirects(config, warnings) {
7885
- if (!config.redirects?.length) return config;
7886
- const unmatchable = [];
7887
- const redirects = config.redirects.filter((redirect) => {
7888
- const source = exactRoute(redirect.source);
7889
- if (source && source === exactRoute(redirect.destination)) {
7890
- warnings.push({ code: "unsupported-config", message: `Self-redirect from ${redirect.source} to ${redirect.destination} was omitted.` });
7891
- return false;
8167
+
8168
+ // src/url-static-content.ts
8169
+ function projectMigrationSvg(html) {
8170
+ const $ = load3(html, { xmlMode: true }, false);
8171
+ const svg = $.root().children();
8172
+ if (svg.length !== 1 || !svg.is("svg")) return null;
8173
+ if (svg.attr("xmlns") === void 0) svg.attr("xmlns", "http://www.w3.org/2000/svg");
8174
+ const content = Buffer.from($.xml(svg));
8175
+ if (!isPassiveSvg(content)) return null;
8176
+ const sha256 = createHash2("sha256").update(content).digest("hex");
8177
+ const labelled = (svg.attr("aria-labelledby") ?? "").split(/\s+/).filter(Boolean).map((id) => svg.find("[id]").filter((_index, node) => $(node).attr("id") === id).text().trim()).filter(Boolean);
8178
+ const alternative = svg.attr("aria-label")?.trim() || labelled.join(" ") || svg.children("title").text().trim() || svg.children("desc").text().trim() || svg.find("text").text().trim();
8179
+ return { asset: { path: `migration-assets/${sha256}.svg`, content }, alternative };
8180
+ }
8181
+ function projectProtectedEmails($, root) {
8182
+ root.find("[data-cfemail]").each((_index, node) => {
8183
+ const element = $(node);
8184
+ const anchor = element.is("a") ? element : element.closest("a");
8185
+ const href = anchor.attr("href") ?? "";
8186
+ if (!anchor.length || !/^\/cdn-cgi\/l\/email-protection(?:#[a-f\d]+)?$/i.test(href)) return;
8187
+ const encoded = element.attr("data-cfemail") ?? "";
8188
+ if (!/^(?:[a-f\d]{2}){4,1024}$/i.test(encoded)) return;
8189
+ const bytes = Buffer.from(encoded, "hex");
8190
+ const payload = Uint8Array.from(bytes.subarray(1), (byte) => byte ^ bytes[0]);
8191
+ let email;
8192
+ try {
8193
+ email = new TextDecoder("utf-8", { fatal: true }).decode(payload);
8194
+ } catch {
8195
+ return;
7892
8196
  }
7893
- if (!ROUTE_PATTERN_OPERATORS.test(redirect.source)) return true;
7894
- unmatchable.push(redirect.source);
7895
- return false;
8197
+ if (!/^[^@\s<>?#[\]\u0000-\u001f\u007f]+@[^@\s<>?#[\]\u0000-\u001f\u007f]+\.[^@\s<>?#[\]\u0000-\u001f\u007f]+$/u.test(email)) return;
8198
+ anchor.attr("href", `mailto:${email}`);
8199
+ if (/^\[email\s+protected\]$/i.test(element.text().trim())) element.text(email);
8200
+ element.removeAttr("data-cfemail");
7896
8201
  });
7897
- if (unmatchable.length > 0) {
7898
- warnings.push({
7899
- code: "unsupported-config",
7900
- message: `Skipped ${unmatchable.length} redirect${unmatchable.length === 1 ? "" : "s"} from ${list(unmatchable)}: ${unmatchable.length === 1 ? "that address has" : "those addresses have"} a character (+ ? ( ) [ ] { }) that Next.js reads as route syntax and cannot match literally, so visits to ${unmatchable.length === 1 ? "it" : "them"} will not redirect.`
8202
+ }
8203
+ function hasCompleteCapturedPanel($, panel) {
8204
+ const busy = '[aria-busy="true"],[role="progressbar"]';
8205
+ if (panel.is(busy) || panel.find(busy).length) return false;
8206
+ const copy = panel.clone();
8207
+ copy.find('script,style,button,[role="tablist"]').remove();
8208
+ return Boolean(copy.text().trim() || copy.find("img[src],svg").length);
8209
+ }
8210
+ function projectCapturedDisclosures($, root) {
8211
+ const ids = /* @__PURE__ */ new Map();
8212
+ root.find("[id]").each((_index, node) => {
8213
+ const id = $(node).attr("id");
8214
+ ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
8215
+ });
8216
+ root.find("button[aria-expanded][aria-controls],summary[aria-expanded][aria-controls]").each((_index, node) => {
8217
+ const control = $(node);
8218
+ if (control.is('[role="tab"],[aria-haspopup],[aria-busy="true"]')) return;
8219
+ const id = control.attr("id"), target2 = control.attr("aria-controls");
8220
+ const panel = target2 ? ids.get(target2) : void 0;
8221
+ const nativeSummary = control.is("summary") && control.parent().is("details");
8222
+ let owner = control.parent();
8223
+ if (owner.is("h1,h2,h3,h4,h5,h6")) owner = owner.parent();
8224
+ const labels2 = panel?.attr("aria-labelledby")?.trim().split(/\s+/);
8225
+ const ownedLabels = labels2?.every((label) => ids.get(label)?.length === 1 && (label === id || nativeSummary && control.find("[id]").toArray().some((element) => $(element).attr("id") === label)));
8226
+ if (!nativeSummary && !id || id && ids.get(id)?.length !== 1 || !panel || panel.length !== 1 || panel.attr("role") !== "region" && !(nativeSummary && panel.is('[data-component-part="expandable-content"]')) || labels2 && !ownedLabels || !panel.parents().toArray().some((parent) => parent === owner[0]) || nativeSummary && panel.parent()[0] !== owner[0] || !hasCompleteCapturedPanel($, panel)) return;
8227
+ if (!nativeSummary) node.name = "span";
8228
+ control.removeAttr("aria-expanded aria-controls role type");
8229
+ panel.removeAttr("hidden aria-hidden");
8230
+ });
8231
+ }
8232
+ function projectCapturedTabs($, root) {
8233
+ root.find("[data-thally-static-tabs],[data-thally-static-tab]").removeAttr("data-thally-static-tabs data-thally-static-tab");
8234
+ const ids = /* @__PURE__ */ new Map();
8235
+ root.find("[id]").each((_index, node) => {
8236
+ const id = $(node).attr("id");
8237
+ ids.set(id, ids.has(id) ? ids.get(id).add(node) : $(node));
8238
+ });
8239
+ const claimed = /* @__PURE__ */ new Set();
8240
+ const groups = root.find('[role="tablist"]').toArray();
8241
+ if (groups.length > 256 || root.find('[role="tab"]').length > 1024) return;
8242
+ for (const groupNode of groups.reverse()) {
8243
+ const group = $(groupNode);
8244
+ const tabs = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === groupNode);
8245
+ if (!tabs.length || group.is('[aria-busy="true"]')) continue;
8246
+ if (group.text().replace(/\s+/g, "") !== tabs.map((node) => $(node).text()).join("").replace(/\s+/g, "")) continue;
8247
+ let container = group.parent();
8248
+ const explicit = tabs.every((tab) => Boolean($(tab).attr("aria-controls")));
8249
+ const panels = [];
8250
+ if (explicit) {
8251
+ for (const tab of tabs) {
8252
+ const panel = ids.get($(tab).attr("aria-controls"));
8253
+ const tabId = $(tab).attr("id");
8254
+ if (!panel || panel.length !== 1 || !panel.is('[role="tabpanel"]') || panel.find('[role="tablist"]').toArray().includes(groupNode) || tabId && ids.get(tabId)?.length !== 1 || panel.attr("aria-labelledby") !== void 0 && panel.attr("aria-labelledby") !== tabId) break;
8255
+ panels.push(panel);
8256
+ }
8257
+ for (let depth = 0; depth < 16 && container.length && panels.some((panel) => !panel.parents().toArray().some((parent) => parent === container[0])); depth += 1) container = container.parent();
8258
+ if (!container.length || !container.find('[role="tablist"]').toArray().includes(groupNode)) continue;
8259
+ } else {
8260
+ if (tabs.some((tab) => $(tab).attr("aria-controls"))) continue;
8261
+ container = group.closest(".code-group,.tabs-container,.tabs.tab-container");
8262
+ if (!container.length) continue;
8263
+ const owners = container.find('[role="tablist"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
8264
+ if (owners.length !== 1) continue;
8265
+ panels.push(...container.find('[role="tabpanel"]').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]).map((node) => $(node)));
8266
+ }
8267
+ if (panels.length !== tabs.length || new Set(panels.map((panel) => panel[0])).size !== panels.length || panels.some((panel) => claimed.has(panel[0]) || !hasCompleteCapturedPanel($, panel))) continue;
8268
+ const unowned = container.find('[role="tabpanel"]').toArray().filter((node) => !panels.some((panel) => panel[0] === node || $(node).parents().toArray().some((parent) => parent === panel[0])));
8269
+ if (unowned.length) continue;
8270
+ const residual = container.clone();
8271
+ residual.find('[role="tablist"],[role="tabpanel"]').remove();
8272
+ const hasSurroundingContent = Boolean(residual.text().trim() || residual.find("a[href],pre,img,svg,table,video,audio,iframe,form,input,textarea,select,object,embed,canvas,math").length);
8273
+ if (!hasSurroundingContent) container.attr("data-thally-static-tabs", "");
8274
+ panels.forEach((panel, index) => {
8275
+ claimed.add(panel[0]);
8276
+ const label = $(tabs[index]).text().trim() || `Tab ${index + 1}`;
8277
+ if (hasSurroundingContent) panel.prepend($("<p></p>").append($("<strong></strong>").text(label)));
8278
+ else panel.attr("data-thally-static-tab", label);
8279
+ panel.removeAttr("role hidden aria-hidden aria-labelledby");
8280
+ const id = $(tabs[index]).attr("id");
8281
+ if (id) panel.prepend($("<span></span>").attr("id", id));
7901
8282
  });
8283
+ const groupId = group.attr("id");
8284
+ if (groupId) group.replaceWith($("<span></span>").attr("id", groupId));
8285
+ else group.remove();
7902
8286
  }
7903
- if (redirects.length === config.redirects.length) return config;
7904
- const result = { ...config, redirects };
7905
- if (redirects.length === 0) delete result.redirects;
7906
- return result;
7907
8287
  }
7908
- function redirectChains() {
7909
- const kept = /* @__PURE__ */ new Map();
7910
- const terminal = /* @__PURE__ */ new Set();
7911
- const has = (source) => kept.has(source) || terminal.has(source);
7912
- const add = (source, destination) => {
7913
- if (destination === null) terminal.add(source);
7914
- else kept.set(source, destination);
7915
- };
7916
- const closesLoop = (source, destination) => {
7917
- let end = destination;
7918
- for (let steps = 0; kept.has(end); steps++) {
7919
- if (steps >= kept.size) return true;
7920
- end = kept.get(end);
8288
+
8289
+ // src/url-flight-content.ts
8290
+ import { createHash as createHash3 } from "crypto";
8291
+ import remarkMdx3 from "remark-mdx";
8292
+ import remarkParse3 from "remark-parse";
8293
+ import remarkGfm2 from "remark-gfm";
8294
+ import { unified as unified3 } from "unified";
8295
+ var parser2 = unified3().use(remarkParse3).use(remarkGfm2).use(remarkMdx3);
8296
+ var MAX_SOURCE_BYTES = 8e6;
8297
+ var sourceTrees = /* @__PURE__ */ new Map();
8298
+ var cachedSourceBytes = 0;
8299
+ var MAX_CACHED_SOURCE_BYTES = 1e6;
8300
+ var UNRESOLVED_FLIGHT_VALUE = /* @__PURE__ */ Symbol("unresolved-flight-value");
8301
+ var expansionBudget = () => ({ visited: 0, bytes: 0, exceeded: false });
8302
+ function chargeExpansion(budget, value) {
8303
+ if (budget.exceeded) return false;
8304
+ budget.bytes += Buffer.byteLength(value);
8305
+ if (budget.bytes > MAX_SOURCE_BYTES) budget.exceeded = true;
8306
+ return !budget.exceeded;
8307
+ }
8308
+ function chargeProjectedText(budget, value) {
8309
+ if (budget.exceeded) return false;
8310
+ let bytes = Buffer.byteLength(value);
8311
+ for (const character of value) bytes += character === "&" ? 4 : character === "<" || character === ">" ? 3 : character === '"' ? 5 : 0;
8312
+ budget.bytes += bytes + 128;
8313
+ if (budget.bytes > MAX_SOURCE_BYTES) budget.exceeded = true;
8314
+ return !budget.exceeded;
8315
+ }
8316
+ var flightDocuments = /* @__PURE__ */ new WeakMap();
8317
+ function renderedRouteSlot(value) {
8318
+ if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => key !== "children")) return void 0;
8319
+ const child = value.children;
8320
+ if (!Array.isArray(child) || child.length < 2) return void 0;
8321
+ const segment = child[0], payload = child[1];
8322
+ if (!(typeof segment === "string" || Array.isArray(segment) && segment.every((part) => typeof part === "string"))) return void 0;
8323
+ return Array.isArray(payload) && payload[0] === "$" || typeof payload === "string" && /^\$L?[a-f\d]+$/i.test(payload) ? child : void 0;
8324
+ }
8325
+ function readMigrationFlightDocument($) {
8326
+ flightTextRecords($);
8327
+ return flightDocuments.get($);
8328
+ }
8329
+ function literalJson(value) {
8330
+ if (Buffer.byteLength(value) > MAX_SOURCE_BYTES) return void 0;
8331
+ let depth = 0, quoted = false, escaped = false;
8332
+ for (const character of value) {
8333
+ if (quoted) {
8334
+ if (escaped) escaped = false;
8335
+ else if (character === "\\") escaped = true;
8336
+ else if (character === '"') quoted = false;
8337
+ } else if (character === '"') quoted = true;
8338
+ else if (character === "[" || character === "{") {
8339
+ if (++depth > 96) return void 0;
8340
+ } else if (character === "]" || character === "}") {
8341
+ if (--depth < 0) return void 0;
7921
8342
  }
7922
- for (let step = destination; step !== end; ) {
7923
- const next = kept.get(step);
7924
- kept.set(step, end);
7925
- step = next;
8343
+ }
8344
+ if (depth !== 0 || quoted) return void 0;
8345
+ try {
8346
+ return JSON.parse(value);
8347
+ } catch {
8348
+ return void 0;
8349
+ }
8350
+ }
8351
+ function flightTextRecords($) {
8352
+ const cached = flightDocuments.get($);
8353
+ if (cached) return cached.sources;
8354
+ const chunks = [];
8355
+ let size = 0;
8356
+ $("script").each((_index, node) => {
8357
+ const match = /^\s*self\.__next_f\.push\(\s*(\[[\s\S]*\])\s*\)\s*;?\s*$/.exec($(node).text());
8358
+ if (!match) return;
8359
+ const envelope = literalJson(match[1]);
8360
+ if (Array.isArray(envelope) && envelope.length === 2 && envelope[0] === 1 && typeof envelope[1] === "string") {
8361
+ size += Buffer.byteLength(envelope[1]);
8362
+ if (size <= MAX_SOURCE_BYTES) chunks.push(envelope[1]);
8363
+ }
8364
+ });
8365
+ if (size > MAX_SOURCE_BYTES) return [];
8366
+ const bytes = Buffer.from(chunks.join(""));
8367
+ const texts = /* @__PURE__ */ new Map();
8368
+ const modules = /* @__PURE__ */ new Map();
8369
+ const records = /* @__PURE__ */ new Map();
8370
+ let cursor = 0;
8371
+ while (cursor < bytes.length && texts.size + modules.size + records.size <= 1e4) {
8372
+ const colon = bytes.indexOf(58, cursor);
8373
+ if (colon < 0 || colon - cursor > 16 || !/^[a-f\d]*$/i.test(bytes.toString("utf8", cursor, colon))) break;
8374
+ const id = bytes.toString("utf8", cursor, colon);
8375
+ cursor = colon + 1;
8376
+ if (bytes[cursor] === 84) {
8377
+ const comma = bytes.indexOf(44, cursor + 1);
8378
+ const length = comma < 0 ? "" : bytes.toString("utf8", cursor + 1, comma);
8379
+ if (!/^[a-f\d]{1,8}$/i.test(length)) break;
8380
+ const end = comma + 1 + Number.parseInt(length, 16);
8381
+ if (end > bytes.length) break;
8382
+ const text3 = bytes.toString("utf8", comma + 1, end);
8383
+ if (text3.includes("\uFFFD")) return [];
8384
+ if (texts.has(id) || records.has(id) || modules.has(id)) return [];
8385
+ texts.set(id, text3);
8386
+ cursor = end;
8387
+ } else {
8388
+ const newline = bytes.indexOf(10, cursor);
8389
+ const end = newline < 0 ? bytes.length : newline;
8390
+ const line = bytes.toString("utf8", cursor, end);
8391
+ if (id && (texts.has(id) || records.has(id) || modules.has(id))) return [];
8392
+ if (line.startsWith("I[")) {
8393
+ const moduleRecord = literalJson(line.slice(1));
8394
+ if (id && Array.isArray(moduleRecord) && typeof moduleRecord[2] === "string") modules.set(id, moduleRecord[2]);
8395
+ } else if (id && /^[\[{"\d-]|^(?:true|false|null)$/.test(line)) records.set(id, literalJson(line));
8396
+ cursor = end + 1;
8397
+ }
8398
+ }
8399
+ if (texts.size + modules.size + records.size > 1e4) return [];
8400
+ const selected = /* @__PURE__ */ new Set();
8401
+ let visited = 0;
8402
+ let hasIncompleteSourceTree = false;
8403
+ const active = /* @__PURE__ */ new Set();
8404
+ const seenReferences = /* @__PURE__ */ new Set();
8405
+ const visit = (value, inArticle, depth) => {
8406
+ if (depth > 96 || ++visited > 1e5) {
8407
+ hasIncompleteSourceTree = true;
8408
+ return;
8409
+ }
8410
+ if (typeof value === "string") {
8411
+ const reference = /^\$L?([a-f\d]+)$/i.exec(value)?.[1];
8412
+ if (reference && active.has(reference)) hasIncompleteSourceTree = true;
8413
+ const identity = `${reference}:${inArticle}`;
8414
+ if (reference && records.has(reference) && !active.has(reference) && !seenReferences.has(identity)) {
8415
+ seenReferences.add(identity);
8416
+ active.add(reference);
8417
+ visit(records.get(reference), inArticle, depth + 1);
8418
+ active.delete(reference);
8419
+ }
8420
+ return;
8421
+ }
8422
+ if (Array.isArray(value)) {
8423
+ const moduleId = value[0] === "$" && typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
8424
+ const component = moduleId ? modules.get(moduleId) : void 0;
8425
+ const isArticle = inArticle || component === "PageArticle";
8426
+ const props = value[3];
8427
+ if (isArticle && component === "CopyMarkdownButton" && props && typeof props === "object" && !Array.isArray(props)) {
8428
+ const reference = /^\$([a-f\d]+)$/i.exec(String(props.content ?? ""))?.[1];
8429
+ if (reference && texts.has(reference)) selected.add(texts.get(reference));
8430
+ }
8431
+ if (value[0] === "$" && props && typeof props === "object" && !Array.isArray(props)) {
8432
+ visit(props.children, isArticle, depth + 1);
8433
+ return;
8434
+ }
8435
+ for (const child of value) visit(child, isArticle, depth + 1);
8436
+ } else if (depth === 0 && value && typeof value === "object" && Array.isArray(value.f)) {
8437
+ visit(value.f, inArticle, depth + 1);
8438
+ } else {
8439
+ const slot = renderedRouteSlot(value);
8440
+ if (slot) visit(slot, inArticle, depth + 1);
7926
8441
  }
7927
- return end === source;
7928
8442
  };
7929
- return { has, add, closesLoop };
8443
+ visit(records.get("0"), false, 0);
8444
+ const sources = visited > 1e5 || hasIncompleteSourceTree ? [] : [...selected];
8445
+ flightDocuments.set($, { texts, modules, records, sources });
8446
+ return sources;
7930
8447
  }
7931
- function dropConflictingRedirects(config, pages, warnings) {
7932
- if (!config.redirects?.length) return config;
7933
- const served = servedRoutes(pages);
7934
- const servedAsWritten = new Set(pageRoutes(pages));
7935
- const hidden = [];
7936
- const hiddenByCase = [];
7937
- const looping = [];
7938
- const loopingByCase = [];
7939
- const folded = redirectChains();
7940
- const asWritten = redirectChains();
7941
- const redirects = config.redirects.filter((redirect) => {
7942
- const source = literalRoute(redirect.source);
7943
- if (!source) return true;
7944
- const writtenSource = exactRoute(redirect.source);
7945
- if (served.has(source)) {
7946
- const hides = servedAsWritten.has(writtenSource) ? hidden : hiddenByCase;
7947
- hides.push(redirect.source);
7948
- return false;
8448
+ function descendants(root) {
8449
+ const result = [];
8450
+ let exceeded = false;
8451
+ const visit = (node, depth) => {
8452
+ if (depth > 96 || result.length >= 1e5) {
8453
+ exceeded = true;
8454
+ return;
7949
8455
  }
7950
- const writtenDestination = destinationRoute(redirect.destination);
7951
- const destination = writtenDestination?.toLowerCase() ?? null;
7952
- const firesFolded = !folded.has(source);
7953
- const firesAsWritten = !asWritten.has(writtenSource);
7954
- const loopsFolded = firesFolded && destination !== null && folded.closesLoop(source, destination);
7955
- const loopsAsWritten = firesAsWritten && writtenDestination !== null && asWritten.closesLoop(writtenSource, writtenDestination);
7956
- if (loopsFolded || loopsAsWritten) {
7957
- const loops = loopsAsWritten ? looping : loopingByCase;
7958
- loops.push(`${redirect.source} -> ${redirect.destination}`);
7959
- return false;
8456
+ result.push(node);
8457
+ for (const child of node.children ?? []) visit(child, depth + 1);
8458
+ };
8459
+ visit(root, 0);
8460
+ return exceeded ? [] : result;
8461
+ }
8462
+ function sourceText(node) {
8463
+ if (["mdxjsEsm", "mdxFlowExpression", "mdxTextExpression"].includes(node.type)) return "";
8464
+ return node.value ?? (node.children ?? []).map(sourceText).join("");
8465
+ }
8466
+ function sourceNodes(source) {
8467
+ const key = createHash3("sha256").update(source).digest("hex");
8468
+ const cached = sourceTrees.get(key);
8469
+ if (cached) {
8470
+ sourceTrees.delete(key);
8471
+ sourceTrees.set(key, cached);
8472
+ return cached.nodes;
8473
+ }
8474
+ let nodes;
8475
+ try {
8476
+ nodes = descendants(parser2.parse(source));
8477
+ } catch {
8478
+ nodes = [];
8479
+ }
8480
+ const bytes = Buffer.byteLength(source);
8481
+ if (bytes <= MAX_CACHED_SOURCE_BYTES) {
8482
+ while (sourceTrees.size >= 4 || cachedSourceBytes + bytes > MAX_CACHED_SOURCE_BYTES) {
8483
+ const oldest = sourceTrees.keys().next().value;
8484
+ if (oldest === void 0) break;
8485
+ cachedSourceBytes -= sourceTrees.get(oldest).bytes;
8486
+ sourceTrees.delete(oldest);
7960
8487
  }
7961
- if (firesFolded) folded.add(source, destination);
7962
- if (firesAsWritten) asWritten.add(writtenSource, writtenDestination);
7963
- return true;
8488
+ sourceTrees.set(key, { bytes, nodes });
8489
+ cachedSourceBytes += bytes;
8490
+ }
8491
+ return nodes;
8492
+ }
8493
+ function codeText(value) {
8494
+ return value.replace(/\n+$/, "");
8495
+ }
8496
+ function labelText(value) {
8497
+ return value.trim().replace(/\s+/g, " ");
8498
+ }
8499
+ function renderedCode($, node) {
8500
+ const copy = $(node).clone();
8501
+ copy.find("code > span.line").each((_index, line) => {
8502
+ if (!$(line).children().length && $(line).text() === " ") $(line).text("");
7964
8503
  });
7965
- if (hidden.length > 0) {
7966
- warnings.push({
7967
- code: "unsupported-config",
7968
- message: `Skipped ${hidden.length} redirect${hidden.length === 1 ? "" : "s"} from ${list(hidden)}: ${hidden.length === 1 ? "that URL is" : "those URLs are"} also ${hidden.length === 1 ? "a page" : "pages"} in this site, and a redirect would hide ${hidden.length === 1 ? "it" : "them"}. To redirect instead, delete the page and add the redirect to docs.json.`
7969
- });
8504
+ return codeText(copy.text());
8505
+ }
8506
+ function isOrderedSubsequence(values, source) {
8507
+ let cursor = 0;
8508
+ for (const value of values) {
8509
+ while (cursor < source.length && source[cursor] !== value) cursor += 1;
8510
+ if (cursor === source.length) return false;
8511
+ cursor += 1;
7970
8512
  }
7971
- if (hiddenByCase.length > 0) {
7972
- const one = hiddenByCase.length === 1;
7973
- warnings.push({
7974
- code: "unsupported-config",
7975
- message: `Skipped ${hiddenByCase.length} redirect${one ? "" : "s"} from ${list(hiddenByCase)}: ${one ? "that URL differs" : "those URLs differ"} only in letter case from ${one ? "a page" : "pages"} in this site, so on a server that ignores letter case ${one ? "it" : "they"} would hide ${one ? "that page" : "those pages"}. If your host matches URLs case-sensitively, add ${one ? "it" : "them"} back to docs.json.`
7976
- });
8513
+ return true;
8514
+ }
8515
+ function matchesSourceCode(rendered, source) {
8516
+ const actual = codeText(rendered).split("\n"), declared = codeText(source).split("\n");
8517
+ if (actual.length !== declared.length) return false;
8518
+ return declared.every((line, index) => {
8519
+ const directive = /\/\/\s*\[!code (?:\+\+|--|highlight|focus|error|warning)(?::\d+)?\]\s*$/.exec(line);
8520
+ return directive ? line.slice(0, directive.index).replace(/[\t ]+$/, "") === actual[index].replace(/[\t ]+$/, "") : line === actual[index];
8521
+ });
8522
+ }
8523
+ function matchesSourceCodeSequence(rendered, source) {
8524
+ let cursor = 0;
8525
+ for (const value of rendered) {
8526
+ while (cursor < source.length && !matchesSourceCode(value, source[cursor].value ?? "")) cursor += 1;
8527
+ if (cursor === source.length) return false;
8528
+ cursor += 1;
7977
8529
  }
7978
- if (looping.length > 0) {
7979
- warnings.push({
7980
- code: "unsupported-config",
7981
- message: `Skipped ${looping.length} redirect${looping.length === 1 ? "" : "s"} that would send visitors in a circle: ${list(looping)}. Check these redirects in the source docs.`
7982
- });
8530
+ return true;
8531
+ }
8532
+ function hasStaticProps(document, element, allowed) {
8533
+ const common = /* @__PURE__ */ new Set(["children", "className", "style", "id", ...allowed]);
8534
+ return Object.entries(element.props).every(([key, value]) => key === "ref" ? flightValue(document, value) === void 0 : common.has(key));
8535
+ }
8536
+ function flightValue(document, value) {
8537
+ const seen = /* @__PURE__ */ new Set();
8538
+ for (let depth = 0; depth < 32 && typeof value === "string"; depth += 1) {
8539
+ if (value.startsWith("$$")) return value.slice(1);
8540
+ const reference = /^\$L?([a-f\d]+)(?::(.+))?$/i.exec(value);
8541
+ if (!reference) return value === "$undefined" ? void 0 : value.startsWith("$") ? UNRESOLVED_FLIGHT_VALUE : value;
8542
+ if (seen.has(value)) return UNRESOLVED_FLIGHT_VALUE;
8543
+ seen.add(value);
8544
+ if (document.texts.has(reference[1])) return reference[2] ? UNRESOLVED_FLIGHT_VALUE : document.texts.get(reference[1]);
8545
+ if (!document.records.has(reference[1]) || document.records.get(reference[1]) === void 0) return UNRESOLVED_FLIGHT_VALUE;
8546
+ value = document.records.get(reference[1]);
8547
+ if (reference[2]) for (const key of reference[2].split(":")) {
8548
+ if (key === "props" && Array.isArray(value) && value[0] === "$" && typeof value[1] === "string" && value[3] && typeof value[3] === "object" && !Array.isArray(value[3])) {
8549
+ value = value[3];
8550
+ continue;
8551
+ }
8552
+ if (!value || typeof value !== "object" || !Object.hasOwn(value, key)) return UNRESOLVED_FLIGHT_VALUE;
8553
+ value = value[key];
8554
+ }
7983
8555
  }
7984
- if (loopingByCase.length > 0) {
7985
- const one = loopingByCase.length === 1;
8556
+ return typeof value === "string" && /^\$L?[a-f\d]+(?::|$)/i.test(value) ? UNRESOLVED_FLIGHT_VALUE : value;
8557
+ }
8558
+ function resolveMigrationFlightValue(document, input) {
8559
+ const value = flightValue(document, input);
8560
+ return value === UNRESOLVED_FLIGHT_VALUE ? { resolved: false, value: void 0 } : { resolved: true, value };
8561
+ }
8562
+ function flightElement(document, value) {
8563
+ const node = flightValue(document, value);
8564
+ if (!Array.isArray(node) || node[0] !== "$" || typeof node[1] !== "string" || !node[3] || typeof node[3] !== "object" || Array.isArray(node[3])) return void 0;
8565
+ const moduleId = /^\$L?([a-f\d]+)$/i.exec(node[1])?.[1];
8566
+ const symbol = moduleId ? document.records.get(moduleId) : void 0;
8567
+ const kind = moduleId ? document.modules.get(moduleId) ?? (symbol === "$Sreact.fragment" ? "Fragment" : symbol === "$Sreact.suspense" ? "Suspense" : void 0) : node[1];
8568
+ return kind ? { kind, props: node[3] } : void 0;
8569
+ }
8570
+ function visitFlightTree(document, root, visit, skipUnresolvedBranches = false) {
8571
+ const budget = expansionBudget();
8572
+ let hasUnresolvedValue = false;
8573
+ const walk2 = (raw, depth) => {
8574
+ if (budget.exceeded) return;
8575
+ if (depth > 96 || ++budget.visited > 1e5) {
8576
+ budget.exceeded = true;
8577
+ return;
8578
+ }
8579
+ const value = flightValue(document, raw);
8580
+ if (value === UNRESOLVED_FLIGHT_VALUE) {
8581
+ hasUnresolvedValue = true;
8582
+ return;
8583
+ }
8584
+ if (typeof value === "string" && !chargeExpansion(budget, value)) return;
8585
+ const element = flightElement(document, value);
8586
+ if (element) {
8587
+ if (visit(element, value, budget) !== false) walk2(element.props.children, depth + 1);
8588
+ } else if (Array.isArray(value)) {
8589
+ for (const child of value) walk2(child, depth + 1);
8590
+ } else if (depth === 0 && value && typeof value === "object" && Array.isArray(value.f)) {
8591
+ walk2(value.f, depth + 1);
8592
+ } else {
8593
+ const slot = renderedRouteSlot(value);
8594
+ if (slot) walk2(slot, depth + 1);
8595
+ }
8596
+ };
8597
+ walk2(root, 0);
8598
+ return !budget.exceeded && (skipUnresolvedBranches || !hasUnresolvedValue);
8599
+ }
8600
+ function flightLiteralText(document, raw, depth = 0, code = false, budget = expansionBudget()) {
8601
+ if (budget.exceeded || depth > 96 || ++budget.visited > 1e5) {
8602
+ budget.exceeded = true;
8603
+ return null;
8604
+ }
8605
+ const value = flightValue(document, raw);
8606
+ if (value === null || value === void 0 || typeof value === "boolean") return "";
8607
+ if (typeof value === "string" || typeof value === "number") return chargeExpansion(budget, String(value)) ? String(value) : null;
8608
+ const element = flightElement(document, value);
8609
+ if (element) {
8610
+ if (["script", "style"].includes(element.kind) || element.props.dangerouslySetInnerHTML !== void 0) return null;
8611
+ if (code && element.kind === "span" && element.props.className === "line" && element.props.children === " ") return "";
8612
+ return flightLiteralText(document, element.props.children, depth + 1, code, budget);
8613
+ }
8614
+ if (!Array.isArray(value)) return null;
8615
+ let text3 = "";
8616
+ for (const child of value) {
8617
+ const literal = flightLiteralText(document, child, depth + 1, code, budget);
8618
+ if (literal === null) return null;
8619
+ text3 += literal;
8620
+ }
8621
+ return text3;
8622
+ }
8623
+ function flightCode(document, element, budget = expansionBudget()) {
8624
+ if (!["CodeBlock", "Pre", "pre"].includes(element.kind)) return null;
8625
+ if (!hasStaticProps(document, element, ["language", "lang", "data-language", "tabIndex", "icon"])) return null;
8626
+ const value = flightLiteralText(document, element.props.children, 0, true, budget);
8627
+ if (value === null) return null;
8628
+ const language = [element.props.language, element.props.lang, element.props["data-language"]].find((value2) => typeof value2 === "string" && /^[A-Za-z0-9_+-]{1,64}$/.test(value2));
8629
+ return { value, ...typeof language === "string" ? { language } : {} };
8630
+ }
8631
+ function flightTabCode(document, raw, depth = 0, budget = expansionBudget()) {
8632
+ if (budget.exceeded || depth > 96 || ++budget.visited > 1e5) {
8633
+ budget.exceeded = true;
8634
+ return null;
8635
+ }
8636
+ const value = flightValue(document, raw);
8637
+ if (value === null || value === void 0 || typeof value === "boolean" || typeof value === "string" && !value.trim()) return [];
8638
+ const element = flightElement(document, value);
8639
+ if (element) {
8640
+ const code = flightCode(document, element, budget);
8641
+ if (code) return [code];
8642
+ if (!["div", "figure"].includes(element.kind) || !hasStaticProps(document, element, []) || element.props.id !== void 0) return null;
8643
+ return flightTabCode(document, element.props.children, depth + 1, budget);
8644
+ }
8645
+ if (!Array.isArray(value)) return null;
8646
+ const result = [];
8647
+ for (const child of value) {
8648
+ const code = flightTabCode(document, child, depth + 1, budget);
8649
+ if (code === null) return null;
8650
+ result.push(...code);
8651
+ }
8652
+ return result;
8653
+ }
8654
+ function htmlLiteral(value) {
8655
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
8656
+ }
8657
+ function flightStaticHtml(document, source, prefix, budget = expansionBudget()) {
8658
+ let visited = 0;
8659
+ const reserve = (bytes) => {
8660
+ budget.bytes += bytes;
8661
+ if (budget.bytes > MAX_SOURCE_BYTES) budget.exceeded = true;
8662
+ };
8663
+ const literal = (value) => {
8664
+ if (budget.exceeded) return "";
8665
+ let bytes = Buffer.byteLength(value);
8666
+ for (const character of value) bytes += character === "&" ? 4 : character === "<" || character === ">" ? 3 : character === '"' ? 5 : 0;
8667
+ reserve(bytes);
8668
+ return budget.exceeded ? "" : htmlLiteral(value);
8669
+ };
8670
+ let tabIndex = 0;
8671
+ const render = (raw, depth) => {
8672
+ if (budget.exceeded || depth > 96 || ++visited > 1e5) return null;
8673
+ const value = flightValue(document, raw);
8674
+ if (value === null || value === void 0 || typeof value === "boolean") return "";
8675
+ if (typeof value === "string" || typeof value === "number") return literal(String(value));
8676
+ const element = flightElement(document, value);
8677
+ if (!element) {
8678
+ if (!Array.isArray(value)) return null;
8679
+ const children2 = value.map((child) => render(child, depth + 1));
8680
+ return children2.some((child) => child === null) ? null : children2.join("");
8681
+ }
8682
+ reserve(128);
8683
+ if (budget.exceeded) return null;
8684
+ const code = flightCode(document, element, budget);
8685
+ if (code) return `<pre${code.language ? ` data-language="${literal(code.language)}"` : ""}><code>${literal(code.value)}</code></pre>`;
8686
+ if (element.kind === "svg") {
8687
+ const svg = (raw2, depth2) => {
8688
+ if (budget.exceeded || depth2 > 96 || ++visited > 1e5) return null;
8689
+ const value2 = flightValue(document, raw2);
8690
+ if (value2 === null || value2 === void 0 || typeof value2 === "boolean") return "";
8691
+ if (typeof value2 === "string" || typeof value2 === "number") return literal(String(value2));
8692
+ const node = flightElement(document, value2);
8693
+ if (!node) {
8694
+ if (!Array.isArray(value2)) return null;
8695
+ const children3 = value2.map((child) => svg(child, depth2 + 1));
8696
+ return children3.some((child) => child === null) ? null : children3.join("");
8697
+ }
8698
+ if (!/^[a-z][a-zA-Z\d]*$/.test(node.kind)) return null;
8699
+ const attributes2 = [];
8700
+ for (const [rawName, rawValue] of Object.entries(node.props)) {
8701
+ if (rawName === "children") continue;
8702
+ const value3 = flightValue(document, rawValue);
8703
+ if (value3 === void 0) continue;
8704
+ if (!["string", "number", "boolean"].includes(typeof value3)) return null;
8705
+ const spellings = {
8706
+ className: "class",
8707
+ strokeWidth: "stroke-width",
8708
+ strokeLinecap: "stroke-linecap",
8709
+ strokeLinejoin: "stroke-linejoin",
8710
+ strokeMiterlimit: "stroke-miterlimit",
8711
+ strokeDasharray: "stroke-dasharray",
8712
+ strokeDashoffset: "stroke-dashoffset",
8713
+ fillRule: "fill-rule",
8714
+ clipRule: "clip-rule",
8715
+ fillOpacity: "fill-opacity",
8716
+ strokeOpacity: "stroke-opacity",
8717
+ clipPath: "clip-path",
8718
+ fontSize: "font-size"
8719
+ };
8720
+ const name = spellings[rawName] ?? rawName;
8721
+ if (!/^[a-zA-Z][a-zA-Z\d:._-]*$/.test(name)) return null;
8722
+ reserve(name.length + 4);
8723
+ if (budget.exceeded) return null;
8724
+ attributes2.push(` ${name}="${literal(String(value3))}"`);
8725
+ }
8726
+ const children2 = svg(node.props.children, depth2 + 1);
8727
+ return children2 === null ? null : `<${node.kind}${attributes2.join("")}>${children2}</${node.kind}>`;
8728
+ };
8729
+ const html = svg(value, depth + 1);
8730
+ return html !== null && isPassiveSvg(Buffer.from(html)) ? html : null;
8731
+ }
8732
+ if (element.kind === "ThemedImage") {
8733
+ if (Object.keys(element.props).some((key) => !["lightSrc", "darkSrc", "alt", "caption"].includes(key))) return null;
8734
+ const light = flightValue(document, element.props.lightSrc), dark = flightValue(document, element.props.darkSrc);
8735
+ const alt = flightValue(document, element.props.alt);
8736
+ if (typeof light !== "string" || typeof dark !== "string" || typeof alt !== "string") return null;
8737
+ const caption = render(element.props.caption, depth + 1);
8738
+ if (caption === null) return null;
8739
+ return `<figure><picture><source media="(prefers-color-scheme: dark)" srcset="${literal(dark)}"><img src="${literal(light)}" alt="${literal(alt)}"></picture>${caption ? `<figcaption>${caption}</figcaption>` : ""}</figure>`;
8740
+ }
8741
+ if (element.kind === "Tabs") {
8742
+ if (!hasStaticProps(document, element, ["items", "defaultIndex", "groupId", "persist"])) return null;
8743
+ const children2 = flightValue(document, element.props.children);
8744
+ const tabs = flightElement(document, children2) ? [children2] : Array.isArray(children2) ? children2 : [];
8745
+ const labels2 = flightValue(document, element.props.items);
8746
+ const group = tabIndex++;
8747
+ const choices = [];
8748
+ for (const [index, rawTab] of tabs.entries()) {
8749
+ const tab = flightElement(document, rawTab);
8750
+ const label = tab?.props.value ?? tab?.props.title;
8751
+ if (tab?.kind !== "Tab" || !hasStaticProps(document, tab, ["value", "title"]) || typeof label !== "string" || !label.trim()) return null;
8752
+ const html = render(tab.props.children, depth + 1);
8753
+ if (html === null) return null;
8754
+ choices.push({ label, html, id: `${prefix}-${group}-${index}` });
8755
+ }
8756
+ if (!choices.length || labels2 !== void 0 && (!Array.isArray(labels2) || JSON.stringify(labels2) !== JSON.stringify(choices.map((choice) => choice.label)))) return null;
8757
+ return `<div><div role="tablist">${choices.map((choice) => `<button role="tab" id="${choice.id}-tab" aria-controls="${choice.id}-panel">${literal(choice.label)}</button>`).join("")}</div>${choices.map((choice) => `<div role="tabpanel" id="${choice.id}-panel" aria-labelledby="${choice.id}-tab">${choice.html}</div>`).join("")}</div>`;
8758
+ }
8759
+ const children = render(element.props.children, depth + 1);
8760
+ if (children === null) return null;
8761
+ if (["Fragment", "Suspense", "Accordions"].includes(element.kind)) return hasStaticProps(document, element, ["type", "defaultValue", "collapsible"]) ? children : null;
8762
+ if (element.kind === "Accordion") {
8763
+ if (!hasStaticProps(document, element, ["title", "defaultOpen", "value"])) return null;
8764
+ const title = flightLiteralText(document, element.props.title);
8765
+ return title === null ? null : `<details open><summary>${literal(title)}</summary>${children}</details>`;
8766
+ }
8767
+ if (element.kind === "Callout") {
8768
+ if (!hasStaticProps(document, element, ["title", "type", "icon"])) return null;
8769
+ const title = element.props.title === void 0 ? "" : flightLiteralText(document, element.props.title);
8770
+ return title === null ? null : `<blockquote>${title ? `<p><strong>${literal(title)}</strong></p>` : ""}${children}</blockquote>`;
8771
+ }
8772
+ const isStaticLink = element.kind === "default" && typeof element.props.href === "string" && Object.keys(element.props).every((key) => ["href", "children", "ref", "prefetch", "replace", "scroll", "className", "target", "rel"].includes(key)) && ["ref", "prefetch", "replace", "scroll"].every((key) => flightValue(document, element.props[key]) === void 0);
8773
+ const tag = element.kind === "Heading" ? element.props.as ?? "h2" : element.kind === "Link" || isStaticLink ? "a" : element.kind;
8774
+ if (typeof tag !== "string" || !/^(?:p|div|section|span|em|strong|a|ul|ol|li|blockquote|table|thead|tbody|tr|td|th|code|h[1-6]|br|hr|img|picture|source|figure|figcaption|sup|sub|details|summary)$/.test(tag) || element.props.dangerouslySetInnerHTML !== void 0) return null;
8775
+ const semanticAttributes = tag === "a" ? ["href", "title", "target", "rel"] : tag === "img" ? ["src", "alt", "width", "height", "srcSet", "srcset", "sizes", "title", "loading", "decoding"] : tag === "source" ? ["srcSet", "srcset", "sizes", "media", "type", "width", "height"] : tag === "details" ? ["open"] : [];
8776
+ if (!isStaticLink && !hasStaticProps(document, element, [...semanticAttributes, ...element.kind === "Heading" ? ["as"] : []])) return null;
8777
+ const attributes = [];
8778
+ for (const name of ["id", ...semanticAttributes]) {
8779
+ if (element.props[name] === void 0) continue;
8780
+ const attribute = flightValue(document, element.props[name]);
8781
+ if (name === "open" && typeof attribute === "boolean") {
8782
+ if (attribute) attributes.push(" open");
8783
+ continue;
8784
+ }
8785
+ if (typeof attribute !== "string" && typeof attribute !== "number") return null;
8786
+ reserve(name.length + 4);
8787
+ if (budget.exceeded) return null;
8788
+ attributes.push(` ${name === "srcSet" ? "srcset" : name}="${literal(String(attribute))}"`);
8789
+ }
8790
+ if (["br", "hr", "img", "source"].includes(tag)) return `<${tag}${attributes.join("")}>`;
8791
+ return `<${tag}${attributes.join("")}>${children}</${tag}>`;
8792
+ };
8793
+ const result = render(source, 0);
8794
+ return budget.exceeded ? null : result;
8795
+ }
8796
+ function recoverFlightDisclosures($, root, document, article, projectionBudget) {
8797
+ const disclosures = [];
8798
+ if (!visitFlightTree(document, article, (element) => {
8799
+ if (element.kind === "Accordion") {
8800
+ disclosures.push(element);
8801
+ return false;
8802
+ }
8803
+ })) return;
8804
+ const controls = root.find("button[aria-expanded][aria-controls]").toArray();
8805
+ if (controls.length !== disclosures.length) return;
8806
+ const projections = [];
8807
+ for (const [index, control] of controls.entries()) {
8808
+ const button = $(control), source = disclosures[index];
8809
+ if (!hasStaticProps(document, source, ["title", "defaultOpen", "value"])) continue;
8810
+ const title = flightLiteralText(document, source.props.title);
8811
+ const id = button.attr("id"), target2 = button.attr("aria-controls");
8812
+ const panel = root.find("[id]").filter((_index, node) => $(node).attr("id") === target2);
8813
+ if (!id || !target2 || title === null || labelText(title) !== labelText(button.text()) || panel.length !== 1 || panel.attr("role") !== "region" || panel.attr("aria-labelledby") !== id || root.find("[id]").filter((_index, node) => $(node).attr("id") === id).length !== 1 || panel.is('[aria-busy="true"]') || panel.find('[aria-busy="true"],[role="progressbar"]').length) continue;
8814
+ let html;
8815
+ if (!panel.text().trim() && !panel.children().length) {
8816
+ const prefix = `thally-flight-${createHash3("sha256").update(target2).digest("hex").slice(0, 16)}`;
8817
+ const rendered = flightStaticHtml(document, source.props.children, prefix, projectionBudget);
8818
+ if (rendered === null || !rendered.trim()) continue;
8819
+ html = rendered;
8820
+ }
8821
+ projections.push({ control, panel, html });
8822
+ }
8823
+ if (projectionBudget.exceeded) return;
8824
+ for (const { control, panel, html } of projections) {
8825
+ const button = $(control);
8826
+ if (html !== void 0) panel.html(html);
8827
+ control.name = "span";
8828
+ button.removeAttr("aria-expanded aria-controls role type");
8829
+ panel.removeAttr("hidden aria-hidden");
8830
+ button.parent("h1,h2,h3,h4,h5,h6").attr("data-thally-static-disclosure-heading", "");
8831
+ }
8832
+ }
8833
+ function recoverFlightTreeTabs($, root, projectionBudget) {
8834
+ flightTextRecords($);
8835
+ const document = flightDocuments.get($);
8836
+ if (!document) return;
8837
+ const articles = [];
8838
+ if (!visitFlightTree(document, document.records.get("0"), (element, raw) => {
8839
+ if (element.kind === "PageArticle" || element.kind === "article") {
8840
+ articles.push(raw);
8841
+ return false;
8842
+ }
8843
+ }, true)) return;
8844
+ const title = labelText(root.find("h1").first().text());
8845
+ const authored = root.children(".prose").length === 1 ? root.children(".prose") : root;
8846
+ const renderedParagraphs = authored.find("p").toArray().filter((node) => !$(node).parents("pre,button,nav,aside,footer").length).map((node) => labelText($(node).text())).filter(Boolean);
8847
+ const visibleCode = root.find("pre").toArray().map((node) => renderedCode($, node));
8848
+ const candidates = articles.filter((article) => {
8849
+ const headings = [], paragraphs = [], code = [], disclosureLabels = [];
8850
+ const complete2 = visitFlightTree(document, article, (element, _raw, budget) => {
8851
+ if (element.kind === "Accordion") {
8852
+ const label = flightLiteralText(document, element.props.title, 0, false, budget);
8853
+ if (label !== null) disclosureLabels.push(labelText(label));
8854
+ }
8855
+ if (element.kind === "p") paragraphs.push(labelText(flightLiteralText(document, element.props.children, 0, false, budget) ?? ""));
8856
+ const block = flightCode(document, element, budget);
8857
+ if (block) {
8858
+ code.push(codeText(block.value));
8859
+ return false;
8860
+ }
8861
+ });
8862
+ const completeHeadings = visitFlightTree(document, article, (element, _raw, budget) => {
8863
+ if (element.kind === "Accordion" || ["CodeBlock", "pre", "Pre"].includes(element.kind)) return false;
8864
+ const heading = /^h[1-6]$/.test(element.kind) ? element.kind : element.kind === "Heading" ? element.props.as : void 0;
8865
+ if (typeof heading === "string" && /^h[1-6]$/.test(heading)) headings.push(`${heading}:${labelText(flightLiteralText(document, element.props.children, 0, false, budget) ?? "")}`);
8866
+ });
8867
+ const renderedHeadings = root.find("h1,h2,h3,h4,h5,h6").toArray().filter((node) => {
8868
+ const control = $(node).children("button[aria-expanded][aria-controls]").first();
8869
+ if (!control.length || !disclosureLabels.includes(labelText(control.text()))) return true;
8870
+ const copy = $(node).clone();
8871
+ copy.find("button").remove();
8872
+ const id = control.attr("id"), target2 = control.attr("aria-controls");
8873
+ const panel = root.find("[id]").filter((_index, candidate) => $(candidate).attr("id") === target2);
8874
+ return Boolean(copy.text().trim() || !id || panel.length !== 1 || panel.attr("aria-labelledby") !== id);
8875
+ }).map((node) => `${node.name}:${labelText($(node).text())}`);
8876
+ return complete2 && completeHeadings && title && headings[0] === `h1:${title}` && headings.length === renderedHeadings.length && headings.every((heading, index) => heading === renderedHeadings[index]) && isOrderedSubsequence(renderedParagraphs, paragraphs) && isOrderedSubsequence(visibleCode, code);
8877
+ });
8878
+ if (candidates.length !== 1) return;
8879
+ recoverFlightDisclosures($, root, document, candidates[0], projectionBudget);
8880
+ const sourceGroups = [];
8881
+ if (!visitFlightTree(document, candidates[0], (element, _raw, budget) => {
8882
+ if (element.kind !== "Tabs") return;
8883
+ if (!hasStaticProps(document, element, ["items", "defaultIndex", "groupId", "persist"])) {
8884
+ sourceGroups.push([]);
8885
+ return false;
8886
+ }
8887
+ const children = flightValue(document, element.props.children);
8888
+ const nodes = flightElement(document, children) ? [children] : Array.isArray(children) ? children : [];
8889
+ const group = [];
8890
+ for (const raw of nodes) {
8891
+ const tab = flightElement(document, raw);
8892
+ const label = tab?.props.value ?? tab?.props.title;
8893
+ const code = tab?.kind === "Tab" && hasStaticProps(document, tab, ["value", "title"]) ? flightTabCode(document, tab.props.children, 0, budget) : null;
8894
+ if (typeof label !== "string" || !label.trim() || !code?.length) {
8895
+ sourceGroups.push([]);
8896
+ return false;
8897
+ }
8898
+ group.push({ label: labelText(label), code });
8899
+ }
8900
+ const items = flightValue(document, element.props.items);
8901
+ if (items !== void 0 && (!Array.isArray(items) || JSON.stringify(items) !== JSON.stringify(group.map((tab) => tab.label))) || items === void 0 && nodes.some((raw) => typeof flightElement(document, raw)?.props.title !== "string")) sourceGroups.push([]);
8902
+ else sourceGroups.push(group);
8903
+ return false;
8904
+ })) return;
8905
+ const groups = root.find('[role="tablist"]').toArray();
8906
+ if (sourceGroups.length !== groups.length) return;
8907
+ groups.forEach((group, index) => {
8908
+ const tabs = $(group).find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"]')[0] === group);
8909
+ const source = sourceGroups[index];
8910
+ if (!tabs.length || source.length !== tabs.length) return;
8911
+ const panels = [];
8912
+ for (const [position, tab] of tabs.entries()) {
8913
+ const id = $(tab).attr("id"), target2 = $(tab).attr("aria-controls");
8914
+ const panel = root.find("[id]").filter((_index, node) => $(node).attr("id") === target2);
8915
+ if (!id || !target2 || panel.length !== 1 || panel.attr("role") !== "tabpanel" || panel.attr("aria-labelledby") !== id || !panel.parents().toArray().includes($(group).parent()[0]) || labelText($(tab).text()) !== source[position].label || panel.is('[aria-busy="true"]') || panel.find('[aria-busy="true"],[role="progressbar"]').length) return;
8916
+ if (panel.text().trim() || panel.children().length) {
8917
+ const copy = panel.clone();
8918
+ copy.find("pre,button,style,script").remove();
8919
+ const blocks = panel.find("pre").toArray().map((node) => renderedCode($, node));
8920
+ if (copy.text().trim() || blocks.length !== source[position].code.length || !blocks.every((block, index2) => block === codeText(source[position].code[index2].value))) return;
8921
+ }
8922
+ panels.push(panel);
8923
+ }
8924
+ if (new Set(panels.map((panel) => panel[0])).size !== panels.length) return;
8925
+ panels.forEach((panel, position) => {
8926
+ if (panel.text().trim() || panel.children().length) return;
8927
+ for (const block of source[position].code) {
8928
+ if (!chargeProjectedText(projectionBudget, block.value)) return;
8929
+ }
8930
+ for (const block of source[position].code) {
8931
+ const pre = $("<pre></pre>").append($("<code></code>").text(block.value));
8932
+ if (block.language) pre.attr("data-language", block.language);
8933
+ panel.append(pre);
8934
+ }
8935
+ });
8936
+ });
8937
+ const declarations2 = [];
8938
+ if (!visitFlightTree(document, candidates[0], (element, _raw, budget) => {
8939
+ const code = flightCode(document, element, budget);
8940
+ if (code) {
8941
+ declarations2.push(code);
8942
+ return false;
8943
+ }
8944
+ })) return;
8945
+ root.find("pre").each((_index, node) => {
8946
+ const payload = renderedCode($, node);
8947
+ const matches = declarations2.filter((code) => codeText(code.value) === payload);
8948
+ const languages = new Set(matches.map((code) => code.language ?? ""));
8949
+ const language = matches[0]?.language;
8950
+ if (languages.size !== 1 || !language || $(node).attr("language") || $(node).attr("data-language") || $(node).find('code[language],code[data-language],code[class*="language-"],code[class*="lang-"]').length || /(?:^|\s)(?:language-|lang-)/.test($(node).attr("class") ?? "")) return;
8951
+ $(node).attr("data-language", language);
8952
+ });
8953
+ }
8954
+ function sourceTabs(nodes) {
8955
+ return nodes.filter((node) => node.name === "Tabs").map((group) => {
8956
+ const result = [];
8957
+ for (const tab of group.children ?? []) {
8958
+ if (tab.type === "text" && !tab.value?.trim()) continue;
8959
+ if (tab.name !== "Tab") return [];
8960
+ const label = tab.attributes?.find((attribute) => attribute.type === "mdxJsxAttribute" && attribute.name === "value")?.value;
8961
+ if (typeof label !== "string" || !label.trim() || tab.attributes?.some((attribute) => attribute.type !== "mdxJsxAttribute" || !["value", "title"].includes(attribute.name ?? "") || typeof attribute.value !== "string")) return [];
8962
+ const content = (tab.children ?? []).filter((child) => child.type !== "text" || child.value?.trim());
8963
+ if (!content.length || content.some((child) => child.type !== "code")) return [];
8964
+ result.push({ label: labelText(label), code: content });
8965
+ }
8966
+ return result;
8967
+ });
8968
+ }
8969
+ function recoverFlightArticleCode($, root) {
8970
+ root.find("[data-thally-static-disclosure-heading]").removeAttr("data-thally-static-disclosure-heading");
8971
+ if (root.find('[role="tablist"]').length > 256 || root.find('[role="tab"]').length > 1024) return;
8972
+ const projectionBudget = expansionBudget();
8973
+ recoverFlightTreeTabs($, root, projectionBudget);
8974
+ const renderedHeadings = root.find("h1,h2,h3,h4,h5,h6").toArray().filter((node) => $(node).attr("data-thally-static-disclosure-heading") === void 0);
8975
+ const headingText = renderedHeadings.map((node) => labelText($(node).text()));
8976
+ const existingCode = root.find("pre").toArray().map((node) => renderedCode($, node));
8977
+ if (!headingText.length || !existingCode.length) return;
8978
+ const prose = root.clone();
8979
+ prose.find("pre,button,script,style,nav,footer,aside").remove();
8980
+ const renderedProse = labelText(prose.text());
8981
+ const candidates = [];
8982
+ for (const source2 of flightTextRecords($)) {
8983
+ if (!/^#\s+\S/m.test(source2) || !/^\s*`{3,}/m.test(source2)) continue;
8984
+ const nodes = sourceNodes(source2);
8985
+ const headings = nodes.filter((node) => node.type === "heading").map((node, index) => {
8986
+ const text3 = labelText(sourceText(node));
8987
+ const anchor = /\s+\[#([A-Za-z\d_.:-]+)\]$/.exec(text3);
8988
+ return anchor && $(renderedHeadings[index]).attr("id") === anchor[1] ? text3.slice(0, anchor.index) : text3;
8989
+ });
8990
+ if (JSON.stringify(headings) !== JSON.stringify(headingText)) continue;
8991
+ let proseCursor = 0;
8992
+ let hasMatchingProse = true;
8993
+ for (const paragraph of nodes.filter((node) => node.type === "paragraph" || node.type === "tableCell")) {
8994
+ const text3 = labelText(sourceText(paragraph));
8995
+ if (!text3) continue;
8996
+ const position = renderedProse.indexOf(text3, proseCursor);
8997
+ if (position < 0) {
8998
+ hasMatchingProse = false;
8999
+ break;
9000
+ }
9001
+ proseCursor = position + text3.length;
9002
+ }
9003
+ if (!hasMatchingProse) continue;
9004
+ const sourceCode2 = nodes.filter((node) => node.type === "code");
9005
+ if (matchesSourceCodeSequence(existingCode, sourceCode2)) candidates.push({ nodes, groups: sourceTabs(nodes) });
9006
+ }
9007
+ if (candidates.length !== 1) return;
9008
+ const source = candidates[0];
9009
+ const groups = root.find('[role="tablist"]').toArray();
9010
+ if (groups.length === source.groups.length) groups.forEach((group, index) => {
9011
+ const tabs = $(group).find('[role="tab"]').toArray().filter((tab) => $(tab).closest('[role="tablist"]')[0] === group);
9012
+ const sourceGroup = source.groups[index];
9013
+ if (!tabs.length || tabs.length !== sourceGroup.length) return;
9014
+ const panels = [];
9015
+ for (const [position, tab] of tabs.entries()) {
9016
+ const control = $(tab).attr("aria-controls");
9017
+ const tabId = $(tab).attr("id");
9018
+ const panel = root.find("[id]").filter((_index, node) => $(node).attr("id") === control);
9019
+ if (!control || panel.length !== 1 || !panel.is('[role="tabpanel"]') || !tabId || root.find("[id]").filter((_index, node) => $(node).attr("id") === tabId).length !== 1 || panel.attr("aria-labelledby") !== tabId || !panel.parents().toArray().includes($(group).parent()[0]) || labelText($(tab).text()) !== sourceGroup[position].label || panel.is('[aria-busy="true"]') || panel.find('[aria-busy="true"],[role="progressbar"]').length) return;
9020
+ if (panel.text().trim() || panel.children().length) {
9021
+ const code = panel.find("pre").toArray().map((node) => renderedCode($, node));
9022
+ const clone = panel.clone();
9023
+ clone.find("pre,button,script,style").remove();
9024
+ const declared = sourceGroup[position].code;
9025
+ if (clone.text().trim() || code.length !== declared.length || !code.every((value, index2) => matchesSourceCode(value, declared[index2].value ?? ""))) return;
9026
+ }
9027
+ panels.push(panel);
9028
+ }
9029
+ if (new Set(panels.map((panel) => panel[0])).size !== panels.length) return;
9030
+ panels.forEach((panel, position) => {
9031
+ if (panel.text().trim() || panel.children().length) return;
9032
+ for (const code of sourceGroup[position].code) if (!chargeProjectedText(projectionBudget, code.value ?? "")) return;
9033
+ for (const code of sourceGroup[position].code) {
9034
+ const pre = $("<pre></pre>").append($("<code></code>").text(code.value ?? ""));
9035
+ if (code.lang && /^[A-Za-z0-9_+-]{1,64}$/.test(code.lang)) pre.attr("data-language", code.lang);
9036
+ panel.append(pre);
9037
+ }
9038
+ });
9039
+ });
9040
+ const sourceCode = source.nodes.filter((node) => node.type === "code");
9041
+ let cursor = 0;
9042
+ root.find("pre").each((_index, node) => {
9043
+ const payload = renderedCode($, node);
9044
+ while (cursor < sourceCode.length && !matchesSourceCode(payload, sourceCode[cursor].value ?? "")) cursor += 1;
9045
+ const code = sourceCode[cursor++];
9046
+ if (!code?.lang || !/^[A-Za-z0-9_+-]{1,64}$/.test(code.lang)) return;
9047
+ const languages = new Set(sourceCode.filter((candidate) => matchesSourceCode(payload, candidate.value ?? "")).map((candidate) => candidate.lang ?? ""));
9048
+ if (languages.size !== 1) return;
9049
+ if (!$(node).attr("language") && !$(node).attr("data-language") && !$(node).find('code[language],code[data-language],code[class*="language-"],code[class*="lang-"]').length && !/(?:^|\s)(?:language-|lang-)/.test($(node).attr("class") ?? "")) $(node).attr("data-language", code.lang);
9050
+ });
9051
+ }
9052
+
9053
+ // src/redirects.ts
9054
+ var LISTED = 10;
9055
+ function exactRoute(route) {
9056
+ return route.startsWith("/") && !/[?:*#()[\]{}]/.test(route) ? route.replace(/\/+$/, "") || "/" : null;
9057
+ }
9058
+ function literalRoute(route) {
9059
+ return exactRoute(route)?.toLowerCase() ?? null;
9060
+ }
9061
+ function resolveDotSegments(route) {
9062
+ const segments = [];
9063
+ for (const segment of route.split("/").slice(1)) {
9064
+ const dots = segment.replace(/%2e/gi, ".");
9065
+ if (dots === "..") segments.pop();
9066
+ else if (dots !== ".") segments.push(segment);
9067
+ }
9068
+ return `/${segments.join("/")}`;
9069
+ }
9070
+ function destinationRoute(destination) {
9071
+ const route = destination.startsWith("//") ? null : exactRoute(destination.split(/[?#]/, 1)[0]);
9072
+ return route === null ? null : resolveDotSegments(route);
9073
+ }
9074
+ function pageRoutes(pages) {
9075
+ return pages.flatMap((page) => [`/${page.id}`, `/${page.id.replace(/\/index$/, "")}`]);
9076
+ }
9077
+ function servedRoutes(pages) {
9078
+ return new Set(pageRoutes(pages).map((route) => route.toLowerCase()));
9079
+ }
9080
+ function list(sources) {
9081
+ return `${sources.slice(0, LISTED).join(", ")}${sources.length > LISTED ? ", ..." : ""}`;
9082
+ }
9083
+ var ROUTE_PATTERN_OPERATORS = /[+?()[\]{}]/;
9084
+ function dropUnroutableRedirects(config, warnings) {
9085
+ if (!config.redirects?.length) return config;
9086
+ const unmatchable = [];
9087
+ const redirects = config.redirects.filter((redirect) => {
9088
+ const source = exactRoute(redirect.source);
9089
+ if (source && source === exactRoute(redirect.destination)) {
9090
+ warnings.push({ code: "unsupported-config", message: `Self-redirect from ${redirect.source} to ${redirect.destination} was omitted.` });
9091
+ return false;
9092
+ }
9093
+ if (!ROUTE_PATTERN_OPERATORS.test(redirect.source)) return true;
9094
+ unmatchable.push(redirect.source);
9095
+ return false;
9096
+ });
9097
+ if (unmatchable.length > 0) {
9098
+ warnings.push({
9099
+ code: "unsupported-config",
9100
+ message: `Skipped ${unmatchable.length} redirect${unmatchable.length === 1 ? "" : "s"} from ${list(unmatchable)}: ${unmatchable.length === 1 ? "that address has" : "those addresses have"} a character (+ ? ( ) [ ] { }) that Next.js reads as route syntax and cannot match literally, so visits to ${unmatchable.length === 1 ? "it" : "them"} will not redirect.`
9101
+ });
9102
+ }
9103
+ if (redirects.length === config.redirects.length) return config;
9104
+ const result = { ...config, redirects };
9105
+ if (redirects.length === 0) delete result.redirects;
9106
+ return result;
9107
+ }
9108
+ function redirectChains() {
9109
+ const kept = /* @__PURE__ */ new Map();
9110
+ const terminal = /* @__PURE__ */ new Set();
9111
+ const has = (source) => kept.has(source) || terminal.has(source);
9112
+ const add = (source, destination) => {
9113
+ if (destination === null) terminal.add(source);
9114
+ else kept.set(source, destination);
9115
+ };
9116
+ const closesLoop = (source, destination) => {
9117
+ let end = destination;
9118
+ for (let steps = 0; kept.has(end); steps++) {
9119
+ if (steps >= kept.size) return true;
9120
+ end = kept.get(end);
9121
+ }
9122
+ for (let step = destination; step !== end; ) {
9123
+ const next = kept.get(step);
9124
+ kept.set(step, end);
9125
+ step = next;
9126
+ }
9127
+ return end === source;
9128
+ };
9129
+ return { has, add, closesLoop };
9130
+ }
9131
+ function dropConflictingRedirects(config, pages, warnings) {
9132
+ if (!config.redirects?.length) return config;
9133
+ const served = servedRoutes(pages);
9134
+ const servedAsWritten = new Set(pageRoutes(pages));
9135
+ const hidden = [];
9136
+ const hiddenByCase = [];
9137
+ const looping = [];
9138
+ const loopingByCase = [];
9139
+ const folded = redirectChains();
9140
+ const asWritten = redirectChains();
9141
+ const redirects = config.redirects.filter((redirect) => {
9142
+ const source = literalRoute(redirect.source);
9143
+ if (!source) return true;
9144
+ const writtenSource = exactRoute(redirect.source);
9145
+ if (served.has(source)) {
9146
+ const hides = servedAsWritten.has(writtenSource) ? hidden : hiddenByCase;
9147
+ hides.push(redirect.source);
9148
+ return false;
9149
+ }
9150
+ const writtenDestination = destinationRoute(redirect.destination);
9151
+ const destination = writtenDestination?.toLowerCase() ?? null;
9152
+ const firesFolded = !folded.has(source);
9153
+ const firesAsWritten = !asWritten.has(writtenSource);
9154
+ const loopsFolded = firesFolded && destination !== null && folded.closesLoop(source, destination);
9155
+ const loopsAsWritten = firesAsWritten && writtenDestination !== null && asWritten.closesLoop(writtenSource, writtenDestination);
9156
+ if (loopsFolded || loopsAsWritten) {
9157
+ const loops = loopsAsWritten ? looping : loopingByCase;
9158
+ loops.push(`${redirect.source} -> ${redirect.destination}`);
9159
+ return false;
9160
+ }
9161
+ if (firesFolded) folded.add(source, destination);
9162
+ if (firesAsWritten) asWritten.add(writtenSource, writtenDestination);
9163
+ return true;
9164
+ });
9165
+ if (hidden.length > 0) {
9166
+ warnings.push({
9167
+ code: "unsupported-config",
9168
+ message: `Skipped ${hidden.length} redirect${hidden.length === 1 ? "" : "s"} from ${list(hidden)}: ${hidden.length === 1 ? "that URL is" : "those URLs are"} also ${hidden.length === 1 ? "a page" : "pages"} in this site, and a redirect would hide ${hidden.length === 1 ? "it" : "them"}. To redirect instead, delete the page and add the redirect to docs.json.`
9169
+ });
9170
+ }
9171
+ if (hiddenByCase.length > 0) {
9172
+ const one = hiddenByCase.length === 1;
9173
+ warnings.push({
9174
+ code: "unsupported-config",
9175
+ message: `Skipped ${hiddenByCase.length} redirect${one ? "" : "s"} from ${list(hiddenByCase)}: ${one ? "that URL differs" : "those URLs differ"} only in letter case from ${one ? "a page" : "pages"} in this site, so on a server that ignores letter case ${one ? "it" : "they"} would hide ${one ? "that page" : "those pages"}. If your host matches URLs case-sensitively, add ${one ? "it" : "them"} back to docs.json.`
9176
+ });
9177
+ }
9178
+ if (looping.length > 0) {
9179
+ warnings.push({
9180
+ code: "unsupported-config",
9181
+ message: `Skipped ${looping.length} redirect${looping.length === 1 ? "" : "s"} that would send visitors in a circle: ${list(looping)}. Check these redirects in the source docs.`
9182
+ });
9183
+ }
9184
+ if (loopingByCase.length > 0) {
9185
+ const one = loopingByCase.length === 1;
7986
9186
  warnings.push({
7987
9187
  code: "unsupported-config",
7988
9188
  message: `Skipped ${loopingByCase.length} redirect${one ? "" : "s"} that would send visitors in a circle on a server that ignores letter case, since the addresses involved differ only in letter case: ${list(loopingByCase)}. If your host matches URLs case-sensitively, add ${one ? "it" : "them"} back to docs.json.`
@@ -7998,7 +9198,8 @@ function dropConflictingRedirects(config, pages, warnings) {
7998
9198
  var DEFAULT_MAX_PAGES = 1e3;
7999
9199
  var DEFAULT_MAX_TOTAL_BYTES = 1e8;
8000
9200
  var MAX_DISCOVERED_URLS = 1e5;
8001
- var MAX_LOCAL_RESPONSE_BYTES = 2e6;
9201
+ var MAX_LOCAL_RESPONSE_BYTES = 8e6;
9202
+ var MAX_EMBEDDED_SCHEMA_BYTES = 2e6;
8002
9203
  var MAX_SITEMAP_DOCUMENTS = 25;
8003
9204
  var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
8004
9205
  "Accordion",
@@ -8110,8 +9311,8 @@ var MINTLIFY_ARTICLE_LABELS = [
8110
9311
  { baseUrl: "Select base URL", permalink: "Navigate to header" },
8111
9312
  { baseUrl: "\u9009\u62E9\u57FA\u7840URL", permalink: "\u5BFC\u822A\u5230\u6807\u9898" }
8112
9313
  ];
8113
- var remoteMarkdownParser = unified3().use(remarkParse3).use(remarkMdx3);
8114
- var embeddedOpenApiParser = unified3().use(remarkParse3);
9314
+ var remoteMarkdownParser = unified4().use(remarkParse4).use(remarkMdx4);
9315
+ var embeddedOpenApiParser = unified4().use(remarkParse4);
8115
9316
  function validateMigrationUrl(value) {
8116
9317
  let url;
8117
9318
  try {
@@ -8128,7 +9329,7 @@ function validateMigrationUrl(value) {
8128
9329
  }
8129
9330
  async function readResponseBody(response) {
8130
9331
  const contentLength = Number(response.headers.get("content-length") ?? 0);
8131
- if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the 2 MB import limit.");
9332
+ if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the 8 MB import limit.");
8132
9333
  if (!response.body) return "";
8133
9334
  const reader = response.body.getReader();
8134
9335
  const chunks = [];
@@ -8139,7 +9340,7 @@ async function readResponseBody(response) {
8139
9340
  size += value.byteLength;
8140
9341
  if (size > MAX_LOCAL_RESPONSE_BYTES) {
8141
9342
  await reader.cancel();
8142
- throw new Error("A documentation response exceeded the 2 MB import limit.");
9343
+ throw new Error("A documentation response exceeded the 8 MB import limit.");
8143
9344
  }
8144
9345
  chunks.push(value);
8145
9346
  }
@@ -8180,7 +9381,7 @@ function machineIndexScopePath(source, documents) {
8180
9381
  if (!document) continue;
8181
9382
  const references = [...headerLinks(document)];
8182
9383
  if (/html/i.test(document.contentType)) {
8183
- const $ = load3(document.body);
9384
+ const $ = load4(document.body);
8184
9385
  references.push(...$("link[href], a[href]").map((_index, element) => $(element).attr("href") ?? "").get().filter((href) => /\/(?:llms(?:-full)?\.txt|sitemap\.xml)(?:[?#]|$)/i.test(href)));
8185
9386
  }
8186
9387
  for (const reference of references) {
@@ -8291,7 +9492,7 @@ function jsonObjectAfterKey(value, key) {
8291
9492
  return null;
8292
9493
  }
8293
9494
  function embeddedMintlifyConfig(body) {
8294
- const $ = load3(body);
9495
+ const $ = load4(body);
8295
9496
  const flightPayloads = [];
8296
9497
  $("script").each((_index, element) => {
8297
9498
  const script = $(element).text();
@@ -8389,7 +9590,7 @@ function isSafePortableUrl(value) {
8389
9590
  }
8390
9591
  }
8391
9592
  function decodeHtmlAttribute(value) {
8392
- const document = load3(`<i data-thally-value="${value.replace(/"/g, "&quot;")}"></i>`);
9593
+ const document = load4(`<i data-thally-value="${value.replace(/"/g, "&quot;")}"></i>`);
8393
9594
  return document("i").attr("data-thally-value") ?? "";
8394
9595
  }
8395
9596
  function escapeMarkdownText(value, escapeTableSeparator = false) {
@@ -8475,7 +9676,7 @@ function replacePortableHtmlImages(value) {
8475
9676
  if (start < cursor) continue;
8476
9677
  const end = htmlOpeningTagEnd(value, start + match[0].length);
8477
9678
  if (end === null) continue;
8478
- const fragment = load3(value.slice(start, end));
9679
+ const fragment = load4(value.slice(start, end));
8479
9680
  const image = fragment("img").first();
8480
9681
  const source = image.attr("src");
8481
9682
  const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
@@ -8500,7 +9701,7 @@ function replacePortableHtmlAnchors(value) {
8500
9701
  const closing = closingMatch.exec(value);
8501
9702
  if (!closing) continue;
8502
9703
  const end = closing.index + closing[0].length;
8503
- const fragment = load3(value.slice(start, end));
9704
+ const fragment = load4(value.slice(start, end));
8504
9705
  const anchor = fragment("a").first();
8505
9706
  const href = anchor.attr("href");
8506
9707
  const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
@@ -8795,7 +9996,7 @@ function stripMintlifyDocumentationIndex(body) {
8795
9996
  return lines.slice(index).join("\n");
8796
9997
  }
8797
9998
  function extractEmbeddedOpenApi(content) {
8798
- if (Buffer.byteLength(content, "utf8") > MAX_LOCAL_RESPONSE_BYTES) return { content };
9999
+ if (Buffer.byteLength(content, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
8799
10000
  try {
8800
10001
  const root = embeddedOpenApiParser.parse(content);
8801
10002
  const candidates = (root.children ?? []).flatMap((node2) => {
@@ -8828,10 +10029,10 @@ function extractEmbeddedOpenApi(content) {
8828
10029
  const serialized = JSON.stringify(document, (_key, value) => {
8829
10030
  values += 1;
8830
10031
  if (typeof value === "string") textLength += value.length;
8831
- if (values > 1e5 || textLength > MAX_LOCAL_RESPONSE_BYTES || typeof value === "number" && !Number.isFinite(value)) throw new Error("OpenAPI document exceeds portable data limits.");
10032
+ if (values > 1e5 || textLength > MAX_EMBEDDED_SCHEMA_BYTES || typeof value === "number" && !Number.isFinite(value)) throw new Error("OpenAPI document exceeds portable data limits.");
8832
10033
  return value;
8833
10034
  });
8834
- if (Buffer.byteLength(serialized, "utf8") > MAX_LOCAL_RESPONSE_BYTES) return { content };
10035
+ if (Buffer.byteLength(serialized, "utf8") > MAX_EMBEDDED_SCHEMA_BYTES) return { content };
8835
10036
  const before = content.slice(0, start).replace(/(?:^|\n)##\s+OpenAPI\s*\n\s*$/i, "\n");
8836
10037
  const after = content.slice(end);
8837
10038
  return {
@@ -8939,8 +10140,12 @@ function mergeEmbeddedOpenApi(fragments) {
8939
10140
  return new TextEncoder().encode(stringifyYaml2(combined));
8940
10141
  }
8941
10142
  function migrationHtmlArticle(body) {
8942
- const $ = load3(body);
10143
+ const $ = load4(body);
8943
10144
  const root = ["main article", "article", "main", '[role="main"]', "body"].map((selector) => $(selector).first()).find((candidate) => candidate.length);
10145
+ projectProtectedEmails($, root);
10146
+ recoverFlightArticleCode($, root);
10147
+ const assets = [];
10148
+ const svgProjections = new Map(root.find("svg").toArray().map((element) => [element, projectMigrationSvg($.html(element))]));
8944
10149
  root.find([
8945
10150
  "script",
8946
10151
  "style",
@@ -9001,10 +10206,25 @@ function migrationHtmlArticle(body) {
9001
10206
  projection.append(list2);
9002
10207
  select.replaceWith(projection);
9003
10208
  });
9004
- return { $, root };
10209
+ projectCapturedDisclosures($, root);
10210
+ root.find("svg").each((_index, element) => {
10211
+ const svg = $(element);
10212
+ if (svg.parents("svg,button,nav,footer,aside").length) return;
10213
+ const projection = svgProjections.get(element);
10214
+ if (!projection) return;
10215
+ const image = $("<img>").attr("src", `/${projection.asset.path}`).attr("alt", projection.alternative);
10216
+ for (const attribute of ["id", "width", "height"]) {
10217
+ const value = svg.attr(attribute);
10218
+ if (value !== void 0) image.attr(attribute, value);
10219
+ }
10220
+ assets.push(projection.asset);
10221
+ svg.replaceWith(image);
10222
+ });
10223
+ projectCapturedTabs($, root);
10224
+ return { $, root, assets };
9005
10225
  }
9006
10226
  function htmlPage(document, id, options = {}) {
9007
- const { $, root } = migrationHtmlArticle(document.body);
10227
+ const { $, root, assets } = migrationHtmlArticle(document.body);
9008
10228
  const links = root.find("a[href]").map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
9009
10229
  const scopedTitle = options.omitDocumentHeading ? root.find("h1").first().text().trim() : "";
9010
10230
  const title = scopedTitle || $("main h1, article h1, h1").first().text().trim() || $('meta[property="og:title"]').attr("content")?.trim() || $("title").text().split("|")[0].trim();
@@ -9043,6 +10263,7 @@ function htmlPage(document, id, options = {}) {
9043
10263
  root.find("img[src]").each((_index, element) => {
9044
10264
  const src = $(element).attr("src");
9045
10265
  if (!src) return;
10266
+ if (assets.some((asset) => src === `/${asset.path}`)) return;
9046
10267
  try {
9047
10268
  const resolved = new URL(src, document.finalUrl);
9048
10269
  if (["http:", "https:"].includes(resolved.protocol)) {
@@ -9054,7 +10275,15 @@ function htmlPage(document, id, options = {}) {
9054
10275
  $(element).removeAttr("src");
9055
10276
  }
9056
10277
  });
9057
- const turndown = new TurndownService({ codeBlockStyle: "fenced", emDelimiter: "_", headingStyle: "atx", preformattedCode: true });
10278
+ const turndown = new TurndownService({
10279
+ codeBlockStyle: "fenced",
10280
+ emDelimiter: "_",
10281
+ headingStyle: "atx",
10282
+ preformattedCode: true,
10283
+ // Turndown chooses its blank rule before custom rules. Empty source anchor
10284
+ // leaves still carry navigation identity, including captured tab controls.
10285
+ blankReplacement: (_content, node) => options.preserveAnchors ? anchor(node) : ""
10286
+ });
9058
10287
  let isConvertingTableCell = false;
9059
10288
  turndown.remove(["script", "style", "noscript"]);
9060
10289
  turndown.addRule("migration-literal-table-code", {
@@ -9095,6 +10324,22 @@ ${content}
9095
10324
 
9096
10325
  ${anchor(element)}${heading ? `${"#".repeat(Number(heading[1]))} ${content.trim()}` : content}
9097
10326
 
10327
+ `;
10328
+ }
10329
+ });
10330
+ turndown.addRule("migration-flow-link", {
10331
+ filter: (node) => node.nodeName === "A" && node.hasAttribute("href") && Boolean(node.querySelector("div,p,picture,figure,blockquote,pre,table,ul,ol,h1,h2,h3,h4,h5,h6")),
10332
+ replacement: (content, node) => {
10333
+ const element = node;
10334
+ const href = migrationImageAttribute(element.getAttribute("href") ?? "");
10335
+ return `
10336
+
10337
+ ${anchor(element)}<Card href="${href}">
10338
+
10339
+ ${content.trim()}
10340
+
10341
+ </Card>
10342
+
9098
10343
  `;
9099
10344
  }
9100
10345
  });
@@ -9145,7 +10390,7 @@ ${content.trim()}
9145
10390
  const element = node;
9146
10391
  const className = element.className;
9147
10392
  const tag = /admonition-(?:danger)/.test(className) ? "Error" : /admonition-(?:caution|warning)/.test(className) ? "Warning" : /admonition-info/.test(className) ? "Info" : "Note";
9148
- const fragment = load3(element.outerHTML);
10393
+ const fragment = load4(element.outerHTML);
9149
10394
  const clone = fragment(".theme-admonition").first();
9150
10395
  const title2 = clone.find('[class*="admonitionHeading"],.admonition-heading').first();
9151
10396
  const titleText = title2.text().trim();
@@ -9166,11 +10411,15 @@ ${titleText ? `**${escapeMarkdownText(titleText)}**
9166
10411
  filter: (node) => node.nodeName === "DIV" && node.classList.contains("tabs-container"),
9167
10412
  replacement: (_content, node) => {
9168
10413
  const element = node;
9169
- const fragment = load3(element.outerHTML);
10414
+ const fragment = load4(element.outerHTML);
9170
10415
  const container = fragment(".tabs-container").first();
9171
10416
  const labels2 = container.children("ul.tabs").children("li").toArray().map((item) => fragment(item).text().trim() || "Tab");
9172
10417
  const panels = container.find('[role="tabpanel"]').toArray().filter((panel) => fragment(panel).closest(".tabs-container")[0] === container[0]);
9173
10418
  if (panels.length === 0) return _content;
10419
+ const residual = container.clone();
10420
+ residual.children("ul.tabs").remove();
10421
+ residual.find('[role="tabpanel"]').remove();
10422
+ if (residual.text().trim() || residual.find("a[href],pre,img,svg,table,video,iframe,input,select,textarea").length) return _content;
9174
10423
  const tabs = panels.map((panel, index) => {
9175
10424
  const panelId = fragment(panel).attr("id");
9176
10425
  const tab = panelId ? container.find('[role="tab"]').toArray().find((item) => fragment(item).attr("aria-controls") === panelId) : void 0;
@@ -9188,13 +10437,40 @@ ${anchor(element)}<Tabs>
9188
10437
  ${tabs.join("\n\n")}
9189
10438
  </Tabs>
9190
10439
 
10440
+ `;
10441
+ }
10442
+ });
10443
+ turndown.addRule("migration-complete-captured-tabs", {
10444
+ filter: (node) => node.hasAttribute("data-thally-static-tabs"),
10445
+ replacement: (_content, node) => {
10446
+ const element = node;
10447
+ const fragment = load4(element.outerHTML);
10448
+ const container = fragment("[data-thally-static-tabs]").first();
10449
+ const panels = container.find("[data-thally-static-tab]").toArray().filter((panel) => fragment(panel).closest("[data-thally-static-tabs]")[0] === container[0]);
10450
+ const tabs = panels.map((panel) => {
10451
+ const selection = fragment(panel);
10452
+ const title2 = migrationImageAttribute(selection.attr("data-thally-static-tab"));
10453
+ const id2 = selection.attr("id");
10454
+ const panelAnchor = options.preserveAnchors && id2 ? `<a id="${migrationImageAttribute(id2)}" />
10455
+
10456
+ ` : "";
10457
+ return `<Tab title="${title2}">
10458
+ ${panelAnchor}${turndown.turndown(selection.html() ?? "").trim()}
10459
+ </Tab>`;
10460
+ });
10461
+ return `
10462
+
10463
+ ${anchor(element)}<Tabs>
10464
+ ${tabs.join("\n\n")}
10465
+ </Tabs>
10466
+
9191
10467
  `;
9192
10468
  }
9193
10469
  });
9194
10470
  turndown.addRule("gfm-table", {
9195
10471
  filter: "table",
9196
10472
  replacement: (_content, node) => {
9197
- const fragment = load3(node.outerHTML);
10473
+ const fragment = load4(node.outerHTML);
9198
10474
  const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => {
9199
10475
  const wasConvertingTableCell = isConvertingTableCell;
9200
10476
  isConvertingTableCell = true;
@@ -9220,7 +10496,7 @@ ${normalized.slice(1).map((row) => `| ${row.join(" | ")} |`).join("\n")}
9220
10496
  filter: (node) => node.nodeName === "PRE",
9221
10497
  replacement: (_content, node) => {
9222
10498
  const element = node;
9223
- const fragment = load3(element.outerHTML);
10499
+ const fragment = load4(element.outerHTML);
9224
10500
  const tokenLines = fragment(".token-line").toArray();
9225
10501
  const code = (tokenLines.length > 0 ? tokenLines.map((line) => fragment(line).text()).join("\n") : fragment("pre").text()).replace(/\n$/, "");
9226
10502
  const language = ["code", "pre"].flatMap((selector) => {
@@ -9245,7 +10521,7 @@ ${fence}
9245
10521
  const escape = turndown.escape.bind(turndown);
9246
10522
  turndown.escape = (text3) => isConvertingTableCell ? escape(text3.replace(/[<>{}]/g, (character) => ({ "<": "&lt;", ">": "&gt;", "{": "&#123;", "}": "&#125;" })[character])) : escape(text3).replace(/[<{}]/g, (match) => `\\${match}`);
9247
10523
  const body = `${documentHeadingAnchor}${turndown.turndown(root.html() ?? "").trim()}`;
9248
- if (!title || body.length < 40 && $("article").length === 0) return { page: null, links };
10524
+ if (!title || body.length < 40 && $("article").length === 0) return { page: null, links, assets };
9249
10525
  return {
9250
10526
  page: {
9251
10527
  id,
@@ -9256,11 +10532,12 @@ ${fence}
9256
10532
  body,
9257
10533
  source: document.finalUrl.toString()
9258
10534
  },
9259
- links
10535
+ links,
10536
+ assets
9260
10537
  };
9261
10538
  }
9262
10539
  function htmlNavigationLinks(document) {
9263
- const $ = load3(document.body);
10540
+ const $ = load4(document.body);
9264
10541
  return $('.nav-tabs a[href], nav a[href], aside a[href], [role="navigation"] a[href]').map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
9265
10542
  }
9266
10543
  function normalizedLinkLabel(value) {
@@ -9268,7 +10545,7 @@ function normalizedLinkLabel(value) {
9268
10545
  }
9269
10546
  function readDocusaurusSidebar(document) {
9270
10547
  if (!/html/i.test(document.contentType)) return null;
9271
- const $ = load3(document.body);
10548
+ const $ = load4(document.body);
9272
10549
  const sidebar = $("ul.theme-doc-sidebar-menu").first();
9273
10550
  if (sidebar.length === 0) return null;
9274
10551
  function readList(list2) {
@@ -9304,7 +10581,7 @@ function docusaurusSidebarHrefs(snapshot) {
9304
10581
  return hrefs;
9305
10582
  }
9306
10583
  function deriveDocusaurusScope(source, document, submittedScopePath) {
9307
- const $ = load3(document.body);
10584
+ const $ = load4(document.body);
9308
10585
  const sidebar = readDocusaurusSidebar(document);
9309
10586
  const internalPaths = docusaurusSidebarHrefs(sidebar).flatMap((href) => {
9310
10587
  try {
@@ -9434,7 +10711,7 @@ function docusaurusNavigationFromSnapshots(snapshots, pages, scopePath) {
9434
10711
  return { tabs };
9435
10712
  }
9436
10713
  function htmlTopLevelNavigation(document, source, scopePath) {
9437
- const $ = load3(document.body);
10714
+ const $ = load4(document.body);
9438
10715
  const collectCandidates = (selector) => {
9439
10716
  const candidates2 = [];
9440
10717
  $(selector).each((_containerIndex, container) => {
@@ -9495,7 +10772,7 @@ function markdownLinks(body) {
9495
10772
  return [...body.matchAll(/\[[^\]]+\]\(([^)\s]+)(?:\s+['"][^'"]*['"])?\)/g)].map((match) => match[1]);
9496
10773
  }
9497
10774
  function sitemapLinks(body) {
9498
- const $ = load3(body, { xmlMode: true });
10775
+ const $ = load4(body, { xmlMode: true });
9499
10776
  return $("url > loc, sitemap > loc").map((_index, element) => $(element).text().trim()).get().filter(Boolean);
9500
10777
  }
9501
10778
  function robotsSitemaps(body) {
@@ -9534,7 +10811,7 @@ async function migrateUrl(options) {
9534
10811
  const platform = options.platform ?? detectUrlPlatform(scopedHtmlProbe ?? initial);
9535
10812
  let docusaurusHtmlProbe = platform === "docusaurus" ? scopedHtmlProbe : null;
9536
10813
  if (docusaurusHtmlProbe && !readDocusaurusSidebar(docusaurusHtmlProbe)) {
9537
- const $ = load3(docusaurusHtmlProbe.body);
10814
+ const $ = load4(docusaurusHtmlProbe.body);
9538
10815
  const docsHref = $("nav a[href]").toArray().map((element) => ({
9539
10816
  href: $(element).attr("href"),
9540
10817
  label: normalizedLinkLabel($(element).text())
@@ -9566,6 +10843,7 @@ async function migrateUrl(options) {
9566
10843
  const queue = [source];
9567
10844
  const queued = /* @__PURE__ */ new Set([candidateIdentity(source)]);
9568
10845
  const warnings = [];
10846
+ const importedAssets = /* @__PURE__ */ new Map();
9569
10847
  const fetchedSitemaps = /* @__PURE__ */ new Set();
9570
10848
  const docusaurusSidebars = docusaurusDiscovery?.sidebar ? [docusaurusDiscovery.sidebar] : [];
9571
10849
  const docusaurusRedirects = [];
@@ -9733,7 +11011,7 @@ async function migrateUrl(options) {
9733
11011
  const extracted = htmlPage(document, id);
9734
11012
  if (extracted.page) extracted.page.navigationId = navigationId;
9735
11013
  const sidebar = platform === "docusaurus" ? readDocusaurusSidebar(document) : null;
9736
- return { candidate, page: extracted.page, links: extracted.links, base: document.finalUrl, sidebar, failure: false };
11014
+ return { candidate, page: extracted.page, links: extracted.links, assets: extracted.assets, base: document.finalUrl, sidebar, failure: false };
9737
11015
  }));
9738
11016
  for (const result of results) {
9739
11017
  if (result.failure) {
@@ -9748,6 +11026,9 @@ async function migrateUrl(options) {
9748
11026
  const sourceIdentity = result.page ? candidateIdentity(new URL(result.page.source)) : null;
9749
11027
  if (result.page && sourceIdentity && !seenSources.has(sourceIdentity) && !seenIds.has(result.page.id) && pages.length < maxPages) {
9750
11028
  pages.push(result.page);
11029
+ if ("assets" in result && result.assets) {
11030
+ for (const asset of result.assets) importedAssets.set(asset.path, asset);
11031
+ }
9751
11032
  if ("pageWarnings" in result && result.pageWarnings) {
9752
11033
  for (const message of result.pageWarnings) warnings.push({ code: "unsupported-config", message, source: result.page.source });
9753
11034
  }
@@ -9841,7 +11122,7 @@ async function migrateUrl(options) {
9841
11122
  sourceKind: "url",
9842
11123
  platform,
9843
11124
  pages,
9844
- assets: openApiAsset ? [{ path: "openapi.yaml", content: openApiAsset }] : [],
11125
+ assets: [...importedAssets.values(), ...openApiAsset ? [{ path: "openapi.yaml", content: openApiAsset }] : []],
9845
11126
  docsConfig,
9846
11127
  ...sourceMintlifyConfig ? {
9847
11128
  site: {
@@ -9931,12 +11212,12 @@ async function sourceRefOversizeReason(repo, fetcher = defaultMigrationFetcher)
9931
11212
  return null;
9932
11213
  }
9933
11214
  function findSourceRefDocsRoot(repositoryDir) {
9934
- for (const directory of [join(repositoryDir, "docs"), repositoryDir]) {
11215
+ for (const directory2 of [join(repositoryDir, "docs"), repositoryDir]) {
9935
11216
  try {
9936
- const config = join(directory, "docs.json");
9937
- if (lstatSync5(directory).isSymbolicLink() || !lstatSync5(config).isFile() || lstatSync5(config).size > DOCS_JSON_MAX_BYTES) continue;
11217
+ const config = join(directory2, "docs.json");
11218
+ if (lstatSync5(directory2).isSymbolicLink() || !lstatSync5(config).isFile() || lstatSync5(config).size > DOCS_JSON_MAX_BYTES) continue;
9938
11219
  const parsed = JSON.parse(readFileSync5(config, "utf8"));
9939
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && "navigation" in parsed) return directory;
11220
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && "navigation" in parsed) return directory2;
9940
11221
  } catch {
9941
11222
  }
9942
11223
  }
@@ -9948,9 +11229,9 @@ function sanitizeAndMeasure(root) {
9948
11229
  let removedLinks = 0;
9949
11230
  const stack = [root];
9950
11231
  while (stack.length > 0) {
9951
- const directory = stack.pop();
9952
- for (const entry of readdirSync3(directory, { withFileTypes: true })) {
9953
- const path = join(directory, entry.name);
11232
+ const directory2 = stack.pop();
11233
+ for (const entry of readdirSync3(directory2, { withFileTypes: true })) {
11234
+ const path = join(directory2, entry.name);
9954
11235
  if (entry.isSymbolicLink()) {
9955
11236
  unlinkSync(path);
9956
11237
  removedLinks++;
@@ -10150,15 +11431,15 @@ var DOCUSAURUS_CONFIG_FILENAMES = [
10150
11431
  "docusaurus.config.ts",
10151
11432
  "docusaurus.config.mjs"
10152
11433
  ];
10153
- function hasDocusaurusConfig(directory) {
11434
+ function hasDocusaurusConfig(directory2) {
10154
11435
  return DOCUSAURUS_CONFIG_FILENAMES.some((filename) => {
10155
- const path = resolveWithin(directory, filename);
11436
+ const path = resolveWithin(directory2, filename);
10156
11437
  return existsSync5(path) && lstatSync6(path).isFile();
10157
11438
  });
10158
11439
  }
10159
- function hasMintlifyConfig(directory) {
11440
+ function hasMintlifyConfig(directory2) {
10160
11441
  return ["docs.json", "mint.json"].some((filename) => {
10161
- const path = resolveWithin(directory, filename);
11442
+ const path = resolveWithin(directory2, filename);
10162
11443
  if (!existsSync5(path) || !lstatSync6(path).isFile()) return false;
10163
11444
  if (filename === "mint.json") return true;
10164
11445
  try {
@@ -10169,9 +11450,9 @@ function hasMintlifyConfig(directory) {
10169
11450
  }
10170
11451
  });
10171
11452
  }
10172
- function countMarkdownPages(directory, limit = 2e4) {
11453
+ function countMarkdownPages(directory2, limit = 2e4) {
10173
11454
  let count = 0;
10174
- const stack = [directory];
11455
+ const stack = [directory2];
10175
11456
  while (stack.length > 0 && count < limit) {
10176
11457
  const current = stack.pop();
10177
11458
  let entries;
@@ -10235,11 +11516,11 @@ function findMintlifyProjectRoot(repositoryDir, docsDir, warnings) {
10235
11516
  }
10236
11517
  }
10237
11518
  if (candidates.length === 0) return null;
10238
- return pickPreferredDocsRoot(candidates, repositoryDir, "Mintlify", warnings, (directory) => existsSync5(resolveWithin(directory, "docs.json")) ? 1 : 0);
11519
+ return pickPreferredDocsRoot(candidates, repositoryDir, "Mintlify", warnings, (directory2) => existsSync5(resolveWithin(directory2, "docs.json")) ? 1 : 0);
10239
11520
  }
10240
- function hasFernConfig(directory) {
11521
+ function hasFernConfig(directory2) {
10241
11522
  return ["docs.yml", "fern.config.json"].every((filename) => {
10242
- const path = resolveWithin(directory, filename);
11523
+ const path = resolveWithin(directory2, filename);
10243
11524
  return existsSync5(path) && lstatSync6(path).isFile();
10244
11525
  });
10245
11526
  }
@@ -10394,8 +11675,8 @@ function primaryDocusaurusDocsDirectory(projectRoot) {
10394
11675
  function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
10395
11676
  const archives = [];
10396
11677
  const relativeRoot = relative6(repositoryDir, projectRoot).replace(/\\/g, "/");
10397
- const directoryNames = (directory) => {
10398
- const path = resolveWithin(projectRoot, directory);
11678
+ const directoryNames = (directory2) => {
11679
+ const path = resolveWithin(projectRoot, directory2);
10399
11680
  if (!existsSync5(path) || !lstatSync6(path).isDirectory()) return [];
10400
11681
  return readdirSync4(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map((entry) => entry.name).sort();
10401
11682
  };
@@ -10419,8 +11700,8 @@ function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
10419
11700
  return existsSync5(path) && lstatSync6(path).isFile();
10420
11701
  });
10421
11702
  };
10422
- const add = (directory, routePrefix, label, sidebar) => {
10423
- const docsDir = [relativeRoot, directory].filter(Boolean).join("/");
11703
+ const add = (directory2, routePrefix, label, sidebar) => {
11704
+ const docsDir = [relativeRoot, directory2].filter(Boolean).join("/");
10424
11705
  archives.push({ docsDir, routePrefix, label, ...sidebar ? { sidebarPath: sidebar } : {} });
10425
11706
  };
10426
11707
  for (const version of versions) add(`versioned_docs/${version}`, version.slice("version-".length), `Version ${version.slice("version-".length)}`, sidebarPath(version));
@@ -10710,11 +11991,11 @@ function detectRepositoryPlatform(repositoryDir, docsDir) {
10710
11991
  if (["pages/_meta.json", "_meta.json"].some((name) => existsSync5(resolveWithin(repositoryDir, name)))) return "nextra";
10711
11992
  return "unknown";
10712
11993
  }
10713
- function containsMarkdown(directory, depth = 0) {
11994
+ function containsMarkdown(directory2, depth = 0) {
10714
11995
  if (depth > 4) return false;
10715
- for (const entry of readdirSync4(directory, { withFileTypes: true })) {
11996
+ for (const entry of readdirSync4(directory2, { withFileTypes: true })) {
10716
11997
  if (isIgnoredDirectory(entry.name) || entry.isSymbolicLink()) continue;
10717
- const path = resolveWithin(directory, entry.name);
11998
+ const path = resolveWithin(directory2, entry.name);
10718
11999
  if (entry.isFile() && [".md", ".mdx"].includes(extname6(entry.name).toLowerCase())) return true;
10719
12000
  if (entry.isDirectory() && containsMarkdown(path, depth + 1)) return true;
10720
12001
  }
@@ -10738,11 +12019,11 @@ function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SO
10738
12019
  confinementReal = confinementRoot;
10739
12020
  }
10740
12021
  const visitedRealPaths = /* @__PURE__ */ new Set();
10741
- function visit(directory, logicalDirectory = directory) {
12022
+ function visit(directory2, logicalDirectory = directory2) {
10742
12023
  if (files.length >= walkCap) return;
10743
12024
  let directoryReal;
10744
12025
  try {
10745
- directoryReal = realpathSync4(directory);
12026
+ directoryReal = realpathSync4(directory2);
10746
12027
  } catch {
10747
12028
  return;
10748
12029
  }
@@ -10750,23 +12031,23 @@ function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SO
10750
12031
  visitedRealPaths.add(directoryReal);
10751
12032
  let entries;
10752
12033
  try {
10753
- entries = readdirSync4(directory, { withFileTypes: true });
12034
+ entries = readdirSync4(directory2, { withFileTypes: true });
10754
12035
  } catch {
10755
12036
  return;
10756
12037
  }
10757
12038
  for (const entry of entries) {
10758
12039
  if (files.length >= walkCap) return;
10759
12040
  if (isIgnoredContentDirectory(entry.name)) {
10760
- if (warnings && entry.isDirectory() && !entry.isSymbolicLink() && containsMarkdown(resolveWithin(directory, entry.name))) {
12041
+ if (warnings && entry.isDirectory() && !entry.isSymbolicLink() && containsMarkdown(resolveWithin(directory2, entry.name))) {
10761
12042
  warnings.push({
10762
12043
  code: "unsupported-config",
10763
12044
  message: `The "${entry.name}" directory was skipped during migration but contains Markdown/MDX files; if any are real pages, move them out or pass --docs-dir to include them.`,
10764
- source: relative6(root, resolveWithin(directory, entry.name)).replace(/\\/g, "/")
12045
+ source: relative6(root, resolveWithin(directory2, entry.name)).replace(/\\/g, "/")
10765
12046
  });
10766
12047
  }
10767
12048
  continue;
10768
12049
  }
10769
- const path = resolveWithin(directory, entry.name);
12050
+ const path = resolveWithin(directory2, entry.name);
10770
12051
  const logicalPath = resolveWithin(logicalDirectory, entry.name);
10771
12052
  if (entry.isSymbolicLink()) {
10772
12053
  let real;
@@ -11078,11 +12359,11 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, n
11078
12359
  const seen = /* @__PURE__ */ new Set();
11079
12360
  const remoteByKey = /* @__PURE__ */ new Map();
11080
12361
  const mountDirectory = (remote, reference) => {
11081
- const directory = reference.directory?.replace(/^\/+|\/+$/g, "");
11082
- if (!remote || !directory) return;
11083
- const mount = remote.directories?.find((existing) => existing.directory === directory);
12362
+ const directory2 = reference.directory?.replace(/^\/+|\/+$/g, "");
12363
+ if (!remote || !directory2) return;
12364
+ const mount = remote.directories?.find((existing) => existing.directory === directory2);
11084
12365
  if (!mount) {
11085
- remote.directories = [...remote.directories ?? [], { directory, ...reference.operations ? { operations: reference.operations } : {} }];
12366
+ remote.directories = [...remote.directories ?? [], { directory: directory2, ...reference.operations ? { operations: reference.operations } : {} }];
11086
12367
  } else if (mount.operations && reference.operations) {
11087
12368
  mount.operations = [.../* @__PURE__ */ new Set([...mount.operations, ...reference.operations])];
11088
12369
  } else {
@@ -11267,7 +12548,7 @@ function servedOperations(source, parsed) {
11267
12548
  return operation ? [operation] : [];
11268
12549
  });
11269
12550
  }
11270
- var mountPrefix = (directory) => directory.replace(/^\/+|\/+$/g, "").toLowerCase();
12551
+ var mountPrefix = (directory2) => directory2.replace(/^\/+|\/+$/g, "").toLowerCase();
11271
12552
  function apiOperationLinkMap(sources, docsConfig, otherMounts = []) {
11272
12553
  const map = /* @__PURE__ */ new Map();
11273
12554
  const prefixLandings = /* @__PURE__ */ new Map();
@@ -13363,12 +14644,12 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
13363
14644
  }
13364
14645
  }
13365
14646
  }
13366
- const repositoryAssets = platform === "docusaurus" && configuredDocsDir && !options.docusaurusSkipAssets ? ["static", "public"].flatMap((directory) => {
13367
- const root = resolveWithin(docusaurusAssetRoot, directory);
14647
+ const repositoryAssets = platform === "docusaurus" && configuredDocsDir && !options.docusaurusSkipAssets ? ["static", "public"].flatMap((directory2) => {
14648
+ const root = resolveWithin(docusaurusAssetRoot, directory2);
13368
14649
  if (!existsSync5(root) || !lstatSync6(root).isDirectory()) return [];
13369
14650
  return scanFiles(root, repositoryDir).map((file) => ({
13370
14651
  ...file,
13371
- relativePath: `${directory}/${file.relativePath}`
14652
+ relativePath: `${directory2}/${file.relativePath}`
13372
14653
  }));
13373
14654
  }) : [];
13374
14655
  const assetCandidates = [];
@@ -13560,7 +14841,7 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
13560
14841
  message: `Skipped ${rootStyleProjection.omittedSelectors} stylesheet selector${rootStyleProjection.omittedSelectors === 1 ? "" : "s"} that target platform-owned or unreferenced markup. Recreate any intentional shell customization with Thally configuration.`
13561
14842
  });
13562
14843
  if (rootStyleProjection.css.trim()) {
13563
- const identity = createHash2("sha256").update(componentSourceIdentity(options.sourceUrl, repositoryDir, mintlifyProjectRoot)).digest("hex").slice(0, 12);
14844
+ const identity = createHash4("sha256").update(componentSourceIdentity(options.sourceUrl, repositoryDir, mintlifyProjectRoot)).digest("hex").slice(0, 12);
13564
14845
  const assetPath = `migrated/${identity}/style.css`;
13565
14846
  const content = Buffer.from(rootStyleProjection.css);
13566
14847
  assets.push({ path: assetPath, content });
@@ -14426,367 +15707,62 @@ function mergeMigrationConfig(existing, incoming) {
14426
15707
  ...uniqueTab.pages ?? [],
14427
15708
  ...uniqueTab.groups ?? []
14428
15709
  ];
14429
- if (uniqueTab.pages?.length) {
14430
- target2.pages = [...target2.pages ?? target2.groups ?? [], ...incomingRootNodes];
14431
- delete target2.groups;
14432
- } else if (uniqueTab.groups?.length) {
14433
- if (target2.pages) target2.pages = [...target2.pages, ...uniqueTab.groups];
14434
- else target2.groups = [...target2.groups ?? [], ...uniqueTab.groups];
14435
- }
14436
- }
14437
- const changelog = existing.tabs.find((tab) => tab.tab.toLowerCase() === "changelog") ?? incoming.tabs.find((tab) => tab.tab.toLowerCase() === "changelog");
14438
- if (changelog) tabs.push(changelog);
14439
- const i18n = existing.i18n || incoming.i18n ? {
14440
- defaultLocale: existing.i18n?.defaultLocale ?? incoming.i18n?.defaultLocale ?? "en",
14441
- locales: [...new Map(
14442
- [...existing.i18n?.locales ?? [], ...incoming.i18n?.locales ?? []].map((locale) => [locale.code, locale])
14443
- ).values()]
14444
- } : void 0;
14445
- return {
14446
- ...incoming,
14447
- ...existing,
14448
- tabs,
14449
- // Collection presentation belongs to the imported information
14450
- // architecture; starter defaults must not coerce source dropdowns to tabs.
14451
- navigation: incoming.navigation ?? existing.navigation,
14452
- ...existing.stylesheets?.length || incoming.stylesheets?.length ? { stylesheets: [.../* @__PURE__ */ new Set([...existing.stylesheets ?? [], ...incoming.stylesheets ?? []])] } : {},
14453
- ...i18n ? { i18n } : {},
14454
- // Existing providers win per provider; imported ones fill the gaps.
14455
- // A malformed (string/array) existing value must not spread into index keys.
14456
- ...isIntegrationsObject(existing.integrations) || isIntegrationsObject(incoming.integrations) ? {
14457
- integrations: {
14458
- ...isIntegrationsObject(incoming.integrations) ? incoming.integrations : {},
14459
- ...isIntegrationsObject(existing.integrations) ? existing.integrations : {}
14460
- }
14461
- } : {}
14462
- };
14463
- }
14464
- function renderMigrationFiles(bundle, options = {}) {
14465
- const config = options.existingConfig ? mergeMigrationConfig(options.existingConfig, bundle.docsConfig) : bundle.docsConfig;
14466
- return [
14467
- ...bundle.pages.map((page) => ({
14468
- path: `src/content/${page.id}.mdx`,
14469
- content: renderPage(bundle, page)
14470
- })),
14471
- ...bundle.assets.map((asset) => ({ path: asset.projectRelative ? asset.path : `public/${asset.path}`, content: asset.content })),
14472
- ...(bundle.componentFiles ?? []).flatMap((file) => file.path === "src/mdx/custom-components.tsx" && options.existingComponentRegistry !== void 0 && typeof file.content === "string" ? mergeComponentRegistry(options.existingComponentRegistry, file.content) : [file]),
14473
- ...bundle.quarantinedFiles ?? [],
14474
- { path: "docs.json", content: `${JSON.stringify(config, null, 2)}
14475
- ` }
14476
- ];
14477
- }
14478
-
14479
- // src/url-run.ts
14480
- import { createHash as createHash3 } from "crypto";
14481
- import { load as load6 } from "cheerio";
14482
- import remarkParse4 from "remark-parse";
14483
- import remarkMdx4 from "remark-mdx";
14484
- import { unified as unified4 } from "unified";
14485
-
14486
- // src/svg-policy.ts
14487
- import { Parser } from "htmlparser2";
14488
- var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
14489
- var XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
14490
- var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
14491
- "svg",
14492
- "g",
14493
- "defs",
14494
- "symbol",
14495
- "use",
14496
- "title",
14497
- "desc",
14498
- "path",
14499
- "rect",
14500
- "circle",
14501
- "ellipse",
14502
- "line",
14503
- "polyline",
14504
- "polygon",
14505
- "text",
14506
- "tspan",
14507
- "textpath",
14508
- "lineargradient",
14509
- "radialgradient",
14510
- "stop",
14511
- "clippath",
14512
- "mask",
14513
- "pattern",
14514
- "marker",
14515
- "filter",
14516
- "feblend",
14517
- "fecolormatrix",
14518
- "fecomponenttransfer",
14519
- "fecomposite",
14520
- "feconvolvematrix",
14521
- "fediffuselighting",
14522
- "fedisplacementmap",
14523
- "fedistantlight",
14524
- "fedropshadow",
14525
- "feflood",
14526
- "fefunca",
14527
- "fefuncb",
14528
- "fefuncg",
14529
- "fefuncr",
14530
- "fegaussianblur",
14531
- "femerge",
14532
- "femergenode",
14533
- "femorphology",
14534
- "feoffset",
14535
- "fepointlight",
14536
- "fespecularlighting",
14537
- "fespotlight",
14538
- "fetile",
14539
- "feturbulence"
14540
- ]);
14541
- var ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
14542
- "xmlns",
14543
- "xmlns:xlink",
14544
- "xml:space",
14545
- "version",
14546
- "baseprofile",
14547
- "id",
14548
- "class",
14549
- "viewbox",
14550
- "preserveaspectratio",
14551
- "width",
14552
- "height",
14553
- "x",
14554
- "y",
14555
- "x1",
14556
- "y1",
14557
- "x2",
14558
- "y2",
14559
- "cx",
14560
- "cy",
14561
- "r",
14562
- "rx",
14563
- "ry",
14564
- "d",
14565
- "points",
14566
- "pathlength",
14567
- "transform",
14568
- "transform-origin",
14569
- "fill",
14570
- "fill-opacity",
14571
- "fill-rule",
14572
- "stroke",
14573
- "stroke-width",
14574
- "stroke-opacity",
14575
- "stroke-linecap",
14576
- "stroke-linejoin",
14577
- "stroke-miterlimit",
14578
- "stroke-dasharray",
14579
- "stroke-dashoffset",
14580
- "opacity",
14581
- "color",
14582
- "display",
14583
- "visibility",
14584
- "overflow",
14585
- "clip-path",
14586
- "clip-rule",
14587
- "clippathunits",
14588
- "mask",
14589
- "maskunits",
14590
- "maskcontentunits",
14591
- "filter",
14592
- "filterunits",
14593
- "primitiveunits",
14594
- "marker-start",
14595
- "marker-mid",
14596
- "marker-end",
14597
- "markerwidth",
14598
- "markerheight",
14599
- "markerunits",
14600
- "refx",
14601
- "refy",
14602
- "orient",
14603
- "patternunits",
14604
- "patterncontentunits",
14605
- "patterntransform",
14606
- "gradientunits",
14607
- "gradienttransform",
14608
- "spreadmethod",
14609
- "fx",
14610
- "fy",
14611
- "fr",
14612
- "offset",
14613
- "stop-color",
14614
- "stop-opacity",
14615
- "font-family",
14616
- "font-size",
14617
- "font-weight",
14618
- "font-style",
14619
- "font-variant",
14620
- "letter-spacing",
14621
- "word-spacing",
14622
- "text-anchor",
14623
- "dominant-baseline",
14624
- "alignment-baseline",
14625
- "baseline-shift",
14626
- "text-decoration",
14627
- "dx",
14628
- "dy",
14629
- "rotate",
14630
- "textlength",
14631
- "lengthadjust",
14632
- "startoffset",
14633
- "writing-mode",
14634
- "direction",
14635
- "unicode-bidi",
14636
- "shape-rendering",
14637
- "text-rendering",
14638
- "image-rendering",
14639
- "color-interpolation",
14640
- "color-interpolation-filters",
14641
- "vector-effect",
14642
- "paint-order",
14643
- "mix-blend-mode",
14644
- "isolation",
14645
- "in",
14646
- "in2",
14647
- "result",
14648
- "stddeviation",
14649
- "mode",
14650
- "type",
14651
- "values",
14652
- "tablevalues",
14653
- "slope",
14654
- "intercept",
14655
- "amplitude",
14656
- "exponent",
14657
- "operator",
14658
- "k1",
14659
- "k2",
14660
- "k3",
14661
- "k4",
14662
- "radius",
14663
- "scale",
14664
- "xchannelselector",
14665
- "ychannelselector",
14666
- "basefrequency",
14667
- "numoctaves",
14668
- "seed",
14669
- "stitchtiles",
14670
- "flood-color",
14671
- "flood-opacity",
14672
- "lighting-color",
14673
- "surfacescale",
14674
- "diffuseconstant",
14675
- "specularconstant",
14676
- "specularexponent",
14677
- "kernelmatrix",
14678
- "kernelunitlength",
14679
- "order",
14680
- "divisor",
14681
- "bias",
14682
- "targetx",
14683
- "targety",
14684
- "edgemode",
14685
- "preservealpha",
14686
- "azimuth",
14687
- "elevation",
14688
- "z",
14689
- "pointsatx",
14690
- "pointsaty",
14691
- "pointsatz",
14692
- "limitingconeangle",
14693
- "aria-hidden",
14694
- "aria-label",
14695
- "role",
14696
- "focusable",
14697
- // Validated below: only in-document fragment references.
14698
- "href",
14699
- "xlink:href"
14700
- ]);
14701
- function hasExternalReference(value) {
14702
- if (value.includes("\\")) return true;
14703
- const compact = value.replace(/[\u0000- ]/gu, "").toLowerCase();
14704
- if (/(?:java|vb)script:|data:|expression\(|@import|<|>/u.test(compact)) {
14705
- return true;
14706
- }
14707
- for (const match of compact.matchAll(/url\(([^)]*)\)/gu)) {
14708
- if (!/^["']?#[a-z0-9_.:-]+["']?$/u.test(match[1])) return true;
14709
- }
14710
- return compact.replace(/url\(([^)]*)\)/gu, "").includes("url(");
14711
- }
14712
- function utf8SvgText(content) {
14713
- const hasUtf8Bom = content[0] === 239 && content[1] === 187 && content[2] === 191;
14714
- const body = hasUtf8Bom ? content.subarray(3) : content;
14715
- if (body[0] === 254 && body[1] === 255 || body[0] === 255 && body[1] === 254 || body[0] === 0 && body[1] === 0 || body.includes(0)) {
14716
- return null;
14717
- }
14718
- const text3 = body.toString("utf8");
14719
- if (text3.includes("\uFFFD")) return null;
14720
- const declaration = /^<\?xml\b[^>]*\?>/iu.exec(text3);
14721
- if (declaration) {
14722
- const encoding = /\bencoding\s*=\s*["']([^"']*)["']/iu.exec(declaration[0]);
14723
- if (encoding && !/^utf-?8$/iu.test(encoding[1].trim())) return null;
14724
- } else if (/<\?xml\b/iu.test(text3)) {
14725
- return null;
15710
+ if (uniqueTab.pages?.length) {
15711
+ target2.pages = [...target2.pages ?? target2.groups ?? [], ...incomingRootNodes];
15712
+ delete target2.groups;
15713
+ } else if (uniqueTab.groups?.length) {
15714
+ if (target2.pages) target2.pages = [...target2.pages, ...uniqueTab.groups];
15715
+ else target2.groups = [...target2.groups ?? [], ...uniqueTab.groups];
15716
+ }
14726
15717
  }
14727
- return text3;
14728
- }
14729
- function isPassiveSvg(content) {
14730
- const text3 = utf8SvgText(Buffer.from(content.buffer, content.byteOffset, content.byteLength));
14731
- if (text3 === null) return false;
14732
- let isPassive = true;
14733
- let depth = 0;
14734
- let sawRoot = false;
14735
- const openStarts = [];
14736
- const fail = () => {
14737
- isPassive = false;
15718
+ const changelog = existing.tabs.find((tab) => tab.tab.toLowerCase() === "changelog") ?? incoming.tabs.find((tab) => tab.tab.toLowerCase() === "changelog");
15719
+ if (changelog) tabs.push(changelog);
15720
+ const i18n = existing.i18n || incoming.i18n ? {
15721
+ defaultLocale: existing.i18n?.defaultLocale ?? incoming.i18n?.defaultLocale ?? "en",
15722
+ locales: [...new Map(
15723
+ [...existing.i18n?.locales ?? [], ...incoming.i18n?.locales ?? []].map((locale) => [locale.code, locale])
15724
+ ).values()]
15725
+ } : void 0;
15726
+ return {
15727
+ ...incoming,
15728
+ ...existing,
15729
+ tabs,
15730
+ // Collection presentation belongs to the imported information
15731
+ // architecture; starter defaults must not coerce source dropdowns to tabs.
15732
+ navigation: incoming.navigation ?? existing.navigation,
15733
+ ...existing.stylesheets?.length || incoming.stylesheets?.length ? { stylesheets: [.../* @__PURE__ */ new Set([...existing.stylesheets ?? [], ...incoming.stylesheets ?? []])] } : {},
15734
+ ...i18n ? { i18n } : {},
15735
+ // Existing providers win per provider; imported ones fill the gaps.
15736
+ // A malformed (string/array) existing value must not spread into index keys.
15737
+ ...isIntegrationsObject(existing.integrations) || isIntegrationsObject(incoming.integrations) ? {
15738
+ integrations: {
15739
+ ...isIntegrationsObject(incoming.integrations) ? incoming.integrations : {},
15740
+ ...isIntegrationsObject(existing.integrations) ? existing.integrations : {}
15741
+ }
15742
+ } : {}
14738
15743
  };
14739
- const parser3 = new Parser(
14740
- {
14741
- onopentag(name, attributes) {
14742
- const element = name.toLowerCase();
14743
- if (depth === 0) {
14744
- if (sawRoot || element !== "svg") fail();
14745
- sawRoot = true;
14746
- }
14747
- depth += 1;
14748
- openStarts.push(parser3.startIndex);
14749
- if (element.includes(":") || !ALLOWED_ELEMENTS.has(element)) fail();
14750
- for (const [rawName, value] of Object.entries(attributes)) {
14751
- const attribute = rawName.toLowerCase();
14752
- if (!ALLOWED_ATTRIBUTES.has(attribute)) {
14753
- fail();
14754
- continue;
14755
- }
14756
- if (attribute === "xmlns" && value !== SVG_NAMESPACE) fail();
14757
- if (attribute === "xmlns:xlink" && value !== XLINK_NAMESPACE) fail();
14758
- if ((attribute === "href" || attribute === "xlink:href") && !/^#[A-Za-z0-9_.:-]+$/u.test(value.trim())) {
14759
- fail();
14760
- }
14761
- if (hasExternalReference(value)) fail();
14762
- }
14763
- },
14764
- onclosetag(_name, isImplied) {
14765
- depth -= 1;
14766
- const start = openStarts.pop();
14767
- if (isImplied && start !== parser3.startIndex) fail();
14768
- },
14769
- // DOCTYPE (and with it custom entities) or any other declaration other
14770
- // than the XML prolog.
14771
- onprocessinginstruction(name) {
14772
- if (name.toLowerCase() !== "?xml") fail();
14773
- },
14774
- oncdatastart: fail,
14775
- ontext(value) {
14776
- if (depth === 0 && value.trim()) fail();
14777
- },
14778
- onerror: fail
14779
- },
14780
- { xmlMode: true, decodeEntities: true, recognizeSelfClosing: true }
14781
- );
14782
- try {
14783
- parser3.write(text3);
14784
- parser3.end();
14785
- } catch {
14786
- return false;
14787
- }
14788
- return isPassive && sawRoot && depth === 0;
14789
15744
  }
15745
+ function renderMigrationFiles(bundle, options = {}) {
15746
+ const config = options.existingConfig ? mergeMigrationConfig(options.existingConfig, bundle.docsConfig) : bundle.docsConfig;
15747
+ return [
15748
+ ...bundle.pages.map((page) => ({
15749
+ path: `src/content/${page.id}.mdx`,
15750
+ content: renderPage(bundle, page)
15751
+ })),
15752
+ ...bundle.assets.map((asset) => ({ path: asset.projectRelative ? asset.path : `public/${asset.path}`, content: asset.content })),
15753
+ ...(bundle.componentFiles ?? []).flatMap((file) => file.path === "src/mdx/custom-components.tsx" && options.existingComponentRegistry !== void 0 && typeof file.content === "string" ? mergeComponentRegistry(options.existingComponentRegistry, file.content) : [file]),
15754
+ ...bundle.quarantinedFiles ?? [],
15755
+ { path: "docs.json", content: `${JSON.stringify(config, null, 2)}
15756
+ ` }
15757
+ ];
15758
+ }
15759
+
15760
+ // src/url-run.ts
15761
+ import { createHash as createHash6 } from "crypto";
15762
+ import { load as load8 } from "cheerio";
15763
+ import remarkParse5 from "remark-parse";
15764
+ import remarkMdx5 from "remark-mdx";
15765
+ import { unified as unified5 } from "unified";
14790
15766
 
14791
15767
  // src/url-asset-cache.ts
14792
15768
  function createMigrationAssetCache() {
@@ -14856,17 +15832,17 @@ function cloneUrlMigrationCheckpoint(checkpoint) {
14856
15832
  }
14857
15833
 
14858
15834
  // src/url-navigation.ts
14859
- import { load as load5 } from "cheerio";
15835
+ import { load as load6 } from "cheerio";
14860
15836
 
14861
15837
  // src/url-scope.ts
14862
- import { load as load4 } from "cheerio";
15838
+ import { load as load5 } from "cheerio";
14863
15839
  function inferUrlMigrationScope(capture) {
14864
15840
  const source = capture.finalUrl;
14865
15841
  let end = source.pathname.length;
14866
15842
  while (end > 0 && source.pathname.charCodeAt(end - 1) === 47) end--;
14867
15843
  const parent = end < source.pathname.length ? source.pathname.slice(0, end) || "/" : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/";
14868
15844
  if (!/html/i.test(capture.contentType) || Buffer.byteLength(capture.body) > 8e6) return { scopePath: parent, evidence: "source-parent" };
14869
- const $ = load4(capture.body);
15845
+ const $ = load5(capture.body);
14870
15846
  const ancestor = (path) => path === "/" || source.pathname === path || source.pathname.startsWith(`${path}/`);
14871
15847
  const observed = (reference) => {
14872
15848
  if (!reference || reference.length > 8192) return void 0;
@@ -14970,7 +15946,7 @@ function flightRecords(stream) {
14970
15946
  return records;
14971
15947
  }
14972
15948
  function mintlifyNavigation(body, base, locale, selectedScope) {
14973
- const $ = load5(body);
15949
+ const $ = load6(body);
14974
15950
  const scope = inferUrlMigrationScope({ body, contentType: "text/html", finalUrl: base });
14975
15951
  const mount = scope.evidence === "source-parent" ? selectedScope ?? "/" : scope.scopePath;
14976
15952
  const configTarget = (value) => {
@@ -15100,41 +16076,176 @@ function resolveNavigation(nodes, base) {
15100
16076
  };
15101
16077
  return walk2(nodes);
15102
16078
  }
16079
+ function sidebarCollectionMenus($, root, base, existing) {
16080
+ const document = readMigrationFlightDocument($);
16081
+ if (!document) return [];
16082
+ const resolve5 = (input) => {
16083
+ let value = input;
16084
+ const seen = /* @__PURE__ */ new Set();
16085
+ for (let depth = 0; depth < 32 && typeof value === "string"; depth++) {
16086
+ if (value === "$undefined") return void 0;
16087
+ if (value.startsWith("$$")) return value.slice(1);
16088
+ const reference = /^\$L?([a-f\d]+)(?::(.+))?$/i.exec(value);
16089
+ if (!reference) return value;
16090
+ if (seen.has(value) || !document.records.has(reference[1])) return void 0;
16091
+ seen.add(value);
16092
+ value = document.records.get(reference[1]);
16093
+ for (const key of reference[2]?.split(":") ?? []) {
16094
+ if (key === "props" && Array.isArray(value) && value[0] === "$") {
16095
+ value = value[3];
16096
+ continue;
16097
+ }
16098
+ if (!value || typeof value !== "object" || !Object.hasOwn(value, key)) return void 0;
16099
+ value = value[key];
16100
+ }
16101
+ }
16102
+ return typeof value === "string" && /^\$L?[a-f\d]+(?::|$)/i.test(value) ? void 0 : value;
16103
+ };
16104
+ const known = /* @__PURE__ */ new Set();
16105
+ const remember = (nodes) => {
16106
+ for (const node of nodes) {
16107
+ if (node.url) known.add(node.url);
16108
+ remember(node.children ?? []);
16109
+ }
16110
+ };
16111
+ remember(existing);
16112
+ const result = [];
16113
+ let visited = 0;
16114
+ const active = /* @__PURE__ */ new Set();
16115
+ const visit = (input, inSidebar, inHeader, inArticle, depth) => {
16116
+ if (++visited > 1e5 || depth > 96 || active.has(input)) return;
16117
+ const value = resolve5(input);
16118
+ if (!value || typeof value !== "object") return;
16119
+ active.add(input);
16120
+ if (Array.isArray(value)) {
16121
+ if (value[0] === "$" && object(value[3])) {
16122
+ const componentId = typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
16123
+ const component = componentId ? document.modules.get(componentId) : value[1];
16124
+ const article = inArticle || ["PageArticle", "article", "pre", "code"].includes(String(component));
16125
+ const sidebar = !article && (inSidebar || component === "Sidebar");
16126
+ const header = sidebar && (inHeader || component === "SidebarHeader");
16127
+ if (header && component === "RootToggle") {
16128
+ const options = resolve5(value[3].options);
16129
+ if (Array.isArray(options) && options.length >= 2 && options.length <= 100) {
16130
+ const entries = options.map(resolve5).map((option) => object(option) ? { label: text2(resolve5(option.title)), url: target(resolve5(option.url), base) } : {});
16131
+ const hasVisibleChoice = entries.some((entry) => entry.label && root.find("button[aria-haspopup]").toArray().some((button) => [button, ...$(button).find("*").toArray()].some((node) => $(node).text().trim() === entry.label)));
16132
+ if (hasVisibleChoice && entries.every((entry) => entry.label && entry.url)) for (const entry of entries) {
16133
+ if (!known.has(entry.url)) {
16134
+ known.add(entry.url);
16135
+ result.push({ label: entry.label, url: entry.url });
16136
+ }
16137
+ }
16138
+ }
16139
+ }
16140
+ visit(value[3].children, sidebar, header, article, depth + 1);
16141
+ } else for (const child of value) visit(child, inSidebar, inHeader, inArticle, depth + 1);
16142
+ } else for (const child of Object.values(value)) visit(child, inSidebar, inHeader, inArticle, depth + 1);
16143
+ active.delete(input);
16144
+ };
16145
+ visit(document.records.get("0"), false, false, false, 0);
16146
+ return visited > 1e5 ? [] : result;
16147
+ }
15103
16148
  function extractSourceNavigation(capture, selectedScope) {
15104
16149
  if (capture.navigation) return resolveNavigation(capture.navigation, capture.finalUrl);
15105
16150
  if (!/html/i.test(capture.contentType) || Buffer.byteLength(capture.body) > MAX_CAPTURE_BYTES) return [];
15106
16151
  const mintlify = mintlifyNavigation(capture.body, capture.finalUrl, capture.locale, selectedScope);
15107
16152
  if (mintlify.length) return mintlify;
15108
- const $ = load5(capture.body);
16153
+ const $ = load6(capture.body);
15109
16154
  const candidates = ['.theme-doc-sidebar-menu,[aria-label*="sidebar" i]', "aside", '[role="navigation"],nav'];
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);
16155
+ const root = candidates.map((selector) => $(selector).filter((_index, node) => {
16156
+ const item = $(node);
16157
+ if (!item.find("a[href]").length || item.closest("header,article").length) return false;
16158
+ const current = new URL(capture.finalUrl);
16159
+ const internal = item.find("a[href]").toArray().flatMap((link) => {
16160
+ const reference = target($(link).attr("href"), capture.finalUrl);
16161
+ if (!reference) return [];
16162
+ const url = new URL(reference);
16163
+ return url.origin === current.origin ? [url] : [];
16164
+ });
16165
+ if (!internal.length) {
16166
+ if (item.is('nav,[role="navigation"]')) return true;
16167
+ const prose = item.clone();
16168
+ prose.find("a,svg,img,script,style").remove();
16169
+ return !prose.text().trim();
16170
+ }
16171
+ return internal.every((url) => !url.hash) || internal.some((url) => url.pathname !== current.pathname || !url.hash && url.search !== current.search);
16172
+ }).first()).find((candidate) => candidate.length);
15111
16173
  if (!root) return [];
15112
16174
  let count = 0;
16175
+ const labelOf = (item) => {
16176
+ const visible = item.text().replace(/\s+/g, " ").trim();
16177
+ if (visible) return visible;
16178
+ if (item.is("a[href]")) {
16179
+ const url = target(item.attr("href"), capture.finalUrl);
16180
+ if (url && root.find("a[href]").toArray().some((node) => node !== item[0] && $(node).text().trim() && target($(node).attr("href"), capture.finalUrl) === url)) return "";
16181
+ }
16182
+ return text2(item.attr("aria-label")) || text2(item.attr("title")) || text2(item.find("img[alt]").first().attr("alt")) || "";
16183
+ };
15113
16184
  const walk2 = (element, excluded, depth = 0) => {
15114
16185
  if (depth > MAX_DEPTH) throw new Error("Source navigation exceeds its safety bound.");
15115
16186
  const result = [];
16187
+ const siblings = $(element).children().toArray().filter((node) => node !== excluded);
16188
+ if (siblings.length > MAX_NODES) throw new Error("Source navigation exceeds its safety bound.");
16189
+ const marker = (node) => {
16190
+ const item = $(node);
16191
+ return item.is("p,span") && Boolean(item.text().trim()) && item.text().length <= 512 && item.find("*").toArray().every((child) => $(child).is("span,strong,em,b,i,small,code,br")) && !item.find("a,button,input,textarea,select,form,div,p,section,article").length && !item.closest('form,[role="search"],[aria-hidden="true"]').length;
16192
+ };
16193
+ const markerPositions = siblings.flatMap((node, index) => marker(node) ? [index] : []);
16194
+ const markers = markerPositions.map((index) => siblings[index]);
16195
+ const isLinkContainer = (node) => {
16196
+ const item = $(node);
16197
+ if (item.is("a[href]")) return true;
16198
+ if (!item.is("div,li,ul,ol,details")) return false;
16199
+ if (!item.find("a[href]").length || item.find('form,input,textarea,select,[role="search"]').length) return false;
16200
+ const remainder = item.clone();
16201
+ remainder.find("a[href],svg,img").remove();
16202
+ return !remainder.text().trim() && !remainder.find("button,form,input,textarea,select,section,article").length;
16203
+ };
16204
+ const groupedMarkers = /* @__PURE__ */ new Set();
16205
+ if (markers.length >= 2 && markers.every((node) => node.tagName === markers[0].tagName)) {
16206
+ let isComplete = true;
16207
+ for (let index = 0; index < markers.length; index++) {
16208
+ const start = markerPositions[index] + 1;
16209
+ const end = index + 1 < markers.length ? markerPositions[index + 1] : siblings.length;
16210
+ const following = siblings.slice(start, end).filter((node) => $(node).text().trim() || $(node).find("a[href],button,input,textarea,select,form,img").length);
16211
+ if (!following.length || !following.every(isLinkContainer)) {
16212
+ isComplete = false;
16213
+ break;
16214
+ }
16215
+ }
16216
+ if (isComplete) markers.forEach((node) => groupedMarkers.add(node));
16217
+ }
16218
+ let currentGroup;
16219
+ const append = (nodes) => {
16220
+ if (currentGroup) currentGroup.children.push(...nodes);
16221
+ else result.push(...nodes);
16222
+ };
15116
16223
  $(element).children().each((_index, node) => {
15117
16224
  if (++count > MAX_NODES) throw new Error("Source navigation exceeds its safety bound.");
15118
16225
  if (node === excluded) return;
15119
16226
  const item = $(node);
15120
- if (item.is("a[href]")) {
15121
- const label = item.text().replace(/\s+/g, " ").trim();
16227
+ if (groupedMarkers.has(node)) {
16228
+ currentGroup = { kind: "group", label: item.text().replace(/\s+/g, " ").trim(), children: [] };
16229
+ result.push(currentGroup);
16230
+ } else if (item.is("a[href]")) {
16231
+ const label = labelOf(item);
15122
16232
  const url = target(item.attr("href"), capture.finalUrl);
15123
- if (label && url) result.push({ label, url });
16233
+ if (label && url) append([{ label, url }]);
15124
16234
  } else if (item.is("li,details") || item.attr("data-migration-group") !== void 0 || item.children("h2,h3,h4,h5,h6").length > 0) {
15125
16235
  const directLink = item.children("a[href]").first();
15126
16236
  const direct = directLink.length ? directLink : item.children("div").children("a[href]").first();
15127
- const label = direct.text().trim() || item.children("summary,button,span,h2,h3,h4,h5,h6").first().text().trim();
16237
+ const label = labelOf(direct) || labelOf(item.children("summary,button,span,h2,h3,h4,h5,h6").first());
15128
16238
  const children = walk2(node, direct[0], depth + 1);
15129
16239
  const url = target(direct.attr("href"), capture.finalUrl);
15130
- if (label && (url || children.length)) result.push({ label, ...url ? { url } : {}, ...children.length ? { children } : {} });
15131
- else result.push(...children);
15132
- } else result.push(...walk2(node, excluded, depth + 1));
16240
+ if (label && (url || children.length)) append([{ label, ...url ? { url } : {}, ...children.length ? { children } : {} }]);
16241
+ else append(children);
16242
+ } else append(walk2(node, excluded, depth + 1));
15133
16243
  });
15134
16244
  return result;
15135
16245
  };
15136
16246
  try {
15137
- return walk2(root);
16247
+ const nodes = walk2(root);
16248
+ return [...nodes, ...sidebarCollectionMenus($, root, capture.finalUrl, nodes)];
15138
16249
  } catch {
15139
16250
  return [];
15140
16251
  }
@@ -15181,6 +16292,71 @@ function mergeSourceNavigation(existing, incoming) {
15181
16292
  return merge(existing, incoming);
15182
16293
  }
15183
16294
 
16295
+ // src/url-identity.ts
16296
+ import { createHash as createHash5 } from "crypto";
16297
+ import { load as load7 } from "cheerio";
16298
+ var directory = (path) => path.replace(/\/index\/?$/i, "").replace(/\/$/, "") || "/";
16299
+ function migrationSectionQueryCandidate(source, anchors) {
16300
+ const target2 = new URL(source);
16301
+ const sections = [...source.searchParams].filter(([, value]) => anchors.includes(value));
16302
+ const section = sections.length === 1 && source.searchParams.getAll(sections[0][0]).length === 1 ? sections[0] : void 0;
16303
+ if (section) target2.searchParams.delete(section[0]);
16304
+ target2.hash = "";
16305
+ target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
16306
+ return { canonicalUrl: target2.href, ...section ? { sectionAnchor: section[1] } : {} };
16307
+ }
16308
+ function isMigrationUtilityUrl(url) {
16309
+ return /^\/cdn-cgi\/l\/email-protection\/?$/i.test(url.pathname);
16310
+ }
16311
+ function declaredMigrationArticleUrl(document) {
16312
+ const target2 = declaredCanonical(document);
16313
+ return target2 && target2.search === document.finalUrl.search ? target2 : document.finalUrl;
16314
+ }
16315
+ function declaredCanonical(document) {
16316
+ if (!/html/i.test(document.contentType)) return void 0;
16317
+ const reference = load7(document.body)('link[rel="canonical"][href]').first().attr("href");
16318
+ if (!reference) return void 0;
16319
+ try {
16320
+ const target2 = new URL(reference, document.finalUrl);
16321
+ if (target2.origin !== document.finalUrl.origin || target2.username || target2.password || directory(target2.pathname) !== directory(document.finalUrl.pathname)) return void 0;
16322
+ target2.hash = "";
16323
+ target2.pathname = target2.pathname.replace(/\/+$/, "") || "/";
16324
+ return target2;
16325
+ } catch {
16326
+ return void 0;
16327
+ }
16328
+ }
16329
+ function observedMigrationArticleIdentity(document) {
16330
+ if (!/html/i.test(document.contentType)) return void 0;
16331
+ const { $, root } = migrationHtmlArticle(document.body);
16332
+ if (!root.length || !root.text().trim()) return void 0;
16333
+ const anchors = root.find("[id]").toArray().map((node) => $(node).attr("id"));
16334
+ root.find("nav,aside,footer,script,style").remove();
16335
+ const metadata = load7(document.body);
16336
+ const payload = JSON.stringify([
16337
+ $.html(root),
16338
+ metadata("title").text(),
16339
+ metadata('meta[name="description"]').attr("content"),
16340
+ metadata("html").attr("lang"),
16341
+ document.locale,
16342
+ document.version,
16343
+ // Deferred content can exist only in inert hydration data. Never erase its
16344
+ // differences merely because two collapsed DOM panels appear identical.
16345
+ metadata("script").toArray().map((node) => [metadata(node).attr("src"), metadata(node).text()]),
16346
+ (document.states ?? []).map((state) => [state.id, state.label, state.response.body])
16347
+ ]);
16348
+ const source = document.finalUrl;
16349
+ const canonical2 = declaredCanonical(document) ?? new URL(source);
16350
+ if (canonical2.href === source.href) {
16351
+ canonical2.href = migrationSectionQueryCandidate(source, anchors).canonicalUrl;
16352
+ }
16353
+ canonical2.hash = "";
16354
+ canonical2.pathname = canonical2.pathname.replace(/\/+$/, "") || "/";
16355
+ const removed = [...source.searchParams].filter(([key, value]) => !canonical2.searchParams.getAll(key).includes(value));
16356
+ const sectionAnchor = removed.length === 1 && anchors.includes(removed[0][1]) ? removed[0][1] : void 0;
16357
+ return { canonicalUrl: canonical2.href, articleSha256: createHash5("sha256").update(payload).digest("hex"), ...sectionAnchor ? { sectionAnchor } : {} };
16358
+ }
16359
+
15184
16360
  // src/url-api-schema-controls.ts
15185
16361
  var MAX_CAPTURE_BYTES2 = 8e6;
15186
16362
  var MAX_TAB_GROUPS = 256;
@@ -15299,17 +16475,378 @@ function apiSchemaTabGroups(body, document, operation) {
15299
16475
  for (const proof of proofs) scopeCounts.set(proof.scope, (scopeCounts.get(proof.scope) ?? 0) + 1);
15300
16476
  return proofs.filter((proof) => scopeCounts.get(proof.scope) === 1).map((proof) => proof.index);
15301
16477
  }
16478
+ function apiSchemaDisclosureGroups(body, document, operation) {
16479
+ if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(document)) return [];
16480
+ if (!/^(?:get|post|put|patch|delete|head|options|trace)$/i.test(operation.method)) return [];
16481
+ const path = object2(object2(document.paths)?.[operation.path]);
16482
+ const selected = object2(path?.[operation.method.toLowerCase()]);
16483
+ if (!selected) return [];
16484
+ const { $, root } = migrationHtmlArticle(body);
16485
+ const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
16486
+ const item = $(node);
16487
+ const endpoint = item.find('[data-component-part="scroll-area-content"]').first().clone();
16488
+ endpoint.find("[data-thally-static-base-urls]").remove();
16489
+ return item.find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
16490
+ });
16491
+ if (operations.length !== 1) return [];
16492
+ const ids = /* @__PURE__ */ new Map();
16493
+ root.find("[id]").each((_index, node) => {
16494
+ const id = $(node).attr("id");
16495
+ ids.set(id, (ids.get(id) ?? 0) + 1);
16496
+ });
16497
+ if (ids.get(operations.attr("id")) !== 1) return [];
16498
+ let visits = 0;
16499
+ const property = (value, name, active = /* @__PURE__ */ new Set()) => {
16500
+ if (++visits > 1e5 || active.has(value)) return [];
16501
+ const schema = resolve4(document, value);
16502
+ if (!schema) return [];
16503
+ active.add(value);
16504
+ const direct = object2(schema.properties)?.[name];
16505
+ const result = direct ? [direct] : [
16506
+ ...schema.items ? property(schema.items, name, active) : [],
16507
+ ...["allOf", "oneOf", "anyOf"].flatMap((key) => Array.isArray(schema[key]) ? schema[key].flatMap((child) => property(child, name, active)) : [])
16508
+ ];
16509
+ active.delete(value);
16510
+ return result;
16511
+ };
16512
+ const fieldSelector = ".object-param-field,.array-param-field";
16513
+ const proofs = [];
16514
+ if (root.find("details").length > MAX_TABS) return [];
16515
+ root.find("details").each((index, node) => {
16516
+ const details = $(node), control = details.children('summary[data-component-part="expandable-button"]');
16517
+ const panel = details.children('[data-component-part="expandable-content"]');
16518
+ const field = details.closest(fieldSelector), section = field.closest(".api-section");
16519
+ if (!details.is('.expandable[data-component-part="expandable"]') || control.length !== 1 || panel.length !== 1 || details.children().length !== 2 || !field.length || section.closest('[id^="api-playground-"][id$="-operation-page"]')[0] !== operations[0]) return;
16520
+ const target2 = control.attr("aria-controls"), controlId = control.attr("id");
16521
+ if (target2 && (panel.attr("id") !== target2 || ids.get(target2) !== 1) || controlId && ids.get(controlId) !== 1) return;
16522
+ const payload = panel.clone();
16523
+ payload.find("script,style").remove();
16524
+ if (payload.text().trim() || payload.children().length) return;
16525
+ const ancestors = field.parents(fieldSelector).toArray().reverse().filter((owner) => $(owner).is(".array-param-field") || $(owner).find('[data-component-part="expandable-content"]').toArray().some((region) => $(region).closest(fieldSelector)[0] === owner && field.parents().toArray().includes(region)));
16526
+ const chain = [...ancestors, field[0]];
16527
+ const names = chain.map((owner) => $(owner).find('[data-component-part="field-name"]').filter((_index, name) => $(name).closest(fieldSelector)[0] === owner));
16528
+ const hasNamedPath = names.every((name) => name.length === 1 && Boolean(name.text().trim()));
16529
+ const heading = section.children(".api-section-heading");
16530
+ if (heading.length !== 1) return;
16531
+ const leaves = heading.find("*").toArray().filter((element) => $(element).children().length === 0).map((element) => $(element).text().trim());
16532
+ const combined = leaves.flatMap((text3) => {
16533
+ const match = text3.match(/^([1-5]\d{2}|default) - ([a-z][a-z\d.+-]*\/[a-z\d.+-]+)$/i);
16534
+ return match ? [{ status: match[1], media: match[2] }] : [];
16535
+ });
16536
+ 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));
16537
+ const statuses = heading.find('button[aria-haspopup="menu"]').toArray().map((button) => $(button).text().trim()).filter((text3) => /^(?:[1-5]\d{2}|default)$/.test(text3));
16538
+ if (combined.length === 1 && !media.length && !statuses.length) {
16539
+ media.push(combined[0].media);
16540
+ statuses.push(combined[0].status);
16541
+ }
16542
+ let schemas = [];
16543
+ let offset = 0;
16544
+ let prefix = "";
16545
+ if (media.length === 1 && statuses.length === 1) {
16546
+ const response = resolve4(document, object2(selected.responses)?.[statuses[0]]);
16547
+ schemas = [object2(object2(response?.content)?.[media[0]])?.schema];
16548
+ prefix = "response";
16549
+ } else if (media.length === 1 && !statuses.length) {
16550
+ const request2 = resolve4(document, selected.requestBody);
16551
+ schemas = [object2(object2(request2?.content)?.[media[0]])?.schema];
16552
+ prefix = "body";
16553
+ } else if (!media.length && !statuses.length) {
16554
+ const head = $(chain[0]).find(".param-head[id]").filter((_index, element) => $(element).closest(fieldSelector)[0] === chain[0]).first().attr("id") ?? "";
16555
+ const location = ["query", "path", "header", "cookie"].find((value) => head.startsWith(`${value}-`));
16556
+ if (names[0]?.length !== 1 || !location && !head.startsWith("parameter-")) return;
16557
+ const parameters = [...Array.isArray(path?.parameters) ? path.parameters : [], ...Array.isArray(selected.parameters) ? selected.parameters : []].map((value) => resolve4(document, value)).filter((parameter) => (!location || parameter?.in === location) && parameter?.name === names[0].text().trim());
16558
+ if (parameters.length !== 1) return;
16559
+ schemas = [parameters[0]?.schema];
16560
+ offset = 1;
16561
+ prefix = $(chain[0]).is(".array-param-field") ? "parameter" : `parameter-${names[0].text().trim()}`;
16562
+ }
16563
+ const expandable = (value) => {
16564
+ const schema = resolve4(document, value);
16565
+ return Boolean(schema && (object2(schema.properties) || object2(schema.items) || object2(schema.additionalProperties) || ["allOf", "oneOf", "anyOf"].some((key) => Array.isArray(schema[key]))));
16566
+ };
16567
+ const roots = schemas;
16568
+ if (hasNamedPath) {
16569
+ for (const name of names.slice(offset)) schemas = schemas.flatMap((schema) => property(schema, name.text().trim()));
16570
+ if (schemas.some(expandable)) {
16571
+ proofs.push(index);
16572
+ return;
16573
+ }
16574
+ }
16575
+ const marker = details.attr("data-testid");
16576
+ if (!prefix || !marker?.endsWith("-children")) return;
16577
+ const expected = marker.slice(0, -"-children".length);
16578
+ const visibleNames = chain.flatMap((owner) => {
16579
+ const labels2 = $(owner).find('[data-component-part="field-name"]').filter((_index, label) => $(label).closest(fieldSelector)[0] === owner);
16580
+ return labels2.length === 1 ? [labels2.text().trim()] : [];
16581
+ });
16582
+ const matches = /* @__PURE__ */ new Set();
16583
+ const walk2 = (value, key, pathNames, active = /* @__PURE__ */ new Set()) => {
16584
+ if (++visits > 1e5 || key.length > 2048 || active.has(value)) return;
16585
+ const schema = resolve4(document, value);
16586
+ if (!schema) return;
16587
+ if (key === expected && expandable(schema)) {
16588
+ let cursor = 0;
16589
+ const labelsAgree = visibleNames.every((name) => {
16590
+ const found = pathNames.indexOf(name, cursor);
16591
+ cursor = found + 1;
16592
+ return found >= 0;
16593
+ });
16594
+ if (labelsAgree) matches.add(schema);
16595
+ }
16596
+ if (!expected.startsWith(`${key}-`)) return;
16597
+ active.add(value);
16598
+ for (const [name, child] of Object.entries(object2(schema.properties) ?? {})) {
16599
+ const segments = /* @__PURE__ */ new Set([name, name.replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase()]);
16600
+ for (const segment of segments) walk2(child, `${key}-${segment}`, [...pathNames, name], active);
16601
+ }
16602
+ if (schema.items) walk2(schema.items, `${key}-items`, pathNames, active);
16603
+ if ((schema.nullable === true || Array.isArray(schema.type) && schema.type.includes("null")) && !schema.oneOf) walk2({ ...schema, nullable: false, type: Array.isArray(schema.type) ? schema.type.filter((value2) => value2 !== "null") : schema.type }, `${key}-one-of-0`, pathNames, active);
16604
+ if (Array.isArray(schema.allOf)) schema.allOf.forEach((child) => walk2(child, key, pathNames, active));
16605
+ for (const [keyword, segment] of [["allOf", "all-of"], ["oneOf", "one-of"], ["anyOf", "any-of"]]) {
16606
+ const children = schema[keyword];
16607
+ if (Array.isArray(children)) children.forEach((child, branch) => walk2(child, `${key}-${segment}-${branch}`, pathNames, active));
16608
+ }
16609
+ active.delete(value);
16610
+ };
16611
+ roots.forEach((schema) => walk2(schema, prefix, offset ? [names[0].text().trim()] : []));
16612
+ if (matches.size === 1) proofs.push(index);
16613
+ });
16614
+ return visits > 1e5 ? [] : proofs;
16615
+ }
16616
+ function apiOperationCodeControls(body, specification, operation) {
16617
+ if (Buffer.byteLength(body) > MAX_CAPTURE_BYTES2 || !complete(specification)) return [];
16618
+ const selected = object2(object2(object2(specification.paths)?.[operation.path])?.[operation.method.toLowerCase()]);
16619
+ if (!selected) return [];
16620
+ const { $, root } = migrationHtmlArticle(body);
16621
+ const flight = readMigrationFlightDocument($);
16622
+ if (!flight) return [];
16623
+ let visited = 0, textBytes = 0, exceeded = false;
16624
+ const resolveValue = (input) => {
16625
+ if (exceeded) return void 0;
16626
+ const result = resolveMigrationFlightValue(flight, input);
16627
+ if (!result.resolved) exceeded = true;
16628
+ return result.value;
16629
+ };
16630
+ const textOf = (input, depth = 0, label = false) => {
16631
+ if (exceeded || ++visited > 1e5 || depth > 96) {
16632
+ exceeded = true;
16633
+ return void 0;
16634
+ }
16635
+ const value = resolveValue(input);
16636
+ if (value === void 0 || value === null || typeof value === "boolean") return "";
16637
+ if (typeof value === "string" || typeof value === "number") {
16638
+ const result = String(value);
16639
+ textBytes += Buffer.byteLength(result);
16640
+ if (textBytes > MAX_CAPTURE_BYTES2) {
16641
+ exceeded = true;
16642
+ return void 0;
16643
+ }
16644
+ return result;
16645
+ }
16646
+ if (!Array.isArray(value)) return void 0;
16647
+ if (value[0] === "$") {
16648
+ if (typeof value[1] !== "string" || !(label ? ["div", "p", "span", "code", "a", "br", "svg", "path", "polyline"] : ["span", "code", "a", "br"]).includes(value[1]) || !object2(value[3])) return void 0;
16649
+ return value[1] === "br" ? "\n" : textOf(value[3].children, depth + 1, label);
16650
+ }
16651
+ const children = value.map((child) => textOf(child, depth + 1, label));
16652
+ return children.some((child) => child === void 0) ? void 0 : children.join("");
16653
+ };
16654
+ const samplePairs = (value, transport = true) => {
16655
+ if (exceeded) return void 0;
16656
+ const samples = transport ? resolveValue(value) : value;
16657
+ if (!Array.isArray(samples) || samples.length > 64) return void 0;
16658
+ const pairs = [];
16659
+ for (const input of samples) {
16660
+ const sample = object2(transport ? resolveValue(input) : input);
16661
+ const language = transport ? resolveValue(sample?.lang) : sample?.lang;
16662
+ const source = transport ? resolveValue(sample?.source) : sample?.source;
16663
+ const label = transport ? resolveValue(sample?.label) : sample?.label;
16664
+ if (label !== void 0 && typeof label !== "string") return void 0;
16665
+ if (typeof language !== "string" || !language.trim() || typeof source !== "string" || !source.trim()) return void 0;
16666
+ textBytes += Buffer.byteLength(language) + Buffer.byteLength(source) + (typeof label === "string" ? Buffer.byteLength(label) : 0);
16667
+ if (textBytes > MAX_CAPTURE_BYTES2) {
16668
+ exceeded = true;
16669
+ return void 0;
16670
+ }
16671
+ pairs.push(JSON.stringify([language, source, label ?? null]));
16672
+ }
16673
+ return pairs;
16674
+ };
16675
+ const declared = selected["x-codeSamples"] === void 0 ? [] : samplePairs(selected["x-codeSamples"], false);
16676
+ if (!declared) return [];
16677
+ const groups = [];
16678
+ const providers = [];
16679
+ let providerCount = 0, generatedControllers = 0, providerMetadataMatches = false;
16680
+ const matchesOperation = (value) => {
16681
+ const metadata = object2(resolveValue(value));
16682
+ const declaration = resolveValue(metadata?.openapi);
16683
+ if (typeof declaration !== "string") return false;
16684
+ const tokens = declaration.trim().split(/\s+/);
16685
+ return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && tokens.at(-1) === operation.path;
16686
+ };
16687
+ const active = /* @__PURE__ */ new Set();
16688
+ const visit = (input, provider, group, depth, owners = []) => {
16689
+ if (exceeded || ++visited > 1e5 || depth > 96) {
16690
+ exceeded = true;
16691
+ return;
16692
+ }
16693
+ const value = resolveValue(input);
16694
+ if (!value || typeof value !== "object") return;
16695
+ if (active.has(value)) {
16696
+ exceeded = true;
16697
+ return;
16698
+ }
16699
+ active.add(value);
16700
+ if (Array.isArray(value)) {
16701
+ if (value[0] === "$" && object2(value[3])) {
16702
+ const props = value[3];
16703
+ const moduleId = typeof value[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(value[1])?.[1] : void 0;
16704
+ const kind = moduleId ? flight.modules.get(moduleId) : value[1];
16705
+ const ownId = moduleId ? void 0 : resolveValue(props.id);
16706
+ const localOwners = typeof ownId === "string" ? [...owners, ownId] : owners;
16707
+ let owned = provider;
16708
+ if (kind === "OpenApiProvider") {
16709
+ providerCount += 1;
16710
+ const data = object2(resolveValue(props.openApiReferenceData));
16711
+ const source = object2(resolveValue(data?.operation));
16712
+ const samples = samplePairs(source?.codeSamples);
16713
+ const authored = source?.["x-codeSamples"] === void 0 ? [] : samplePairs(source["x-codeSamples"]);
16714
+ owned = Boolean(source && source.method === operation.method.toLowerCase() && source.path === operation.path && samples && authored && JSON.stringify(samples) === JSON.stringify(declared) && JSON.stringify(authored) === JSON.stringify(declared));
16715
+ if (owned) {
16716
+ providers.push(source);
16717
+ providerMetadataMatches = matchesOperation(props.pageMetadata);
16718
+ }
16719
+ }
16720
+ if (owned && kind === "MDXContentController" && matchesOperation(props.pageMetadata)) {
16721
+ const child = resolveValue(props.children);
16722
+ const childId = Array.isArray(child) && child[0] === "$" && typeof child[1] === "string" ? /^\$L([a-f\d]+)$/i.exec(child[1])?.[1] : void 0;
16723
+ const childProps = Array.isArray(child) ? object2(child[3]) : void 0;
16724
+ const source = object2(resolveValue(childProps?.mdxSource));
16725
+ const scope = object2(resolveValue(source?.scope));
16726
+ if (childId && flight.modules.get(childId) === "default" && typeof source?.compiledSource === "string" && (() => {
16727
+ if (matchesOperation(scope?.pageMetadata)) return true;
16728
+ const hint = object2(resolveValue(scope?.pageMetadata)), metadata = object2(resolveValue(props.pageMetadata));
16729
+ const declaration = resolveValue(hint?.openapi);
16730
+ if (typeof declaration !== "string" || typeof hint?.href !== "string" || !hint.href || hint.href !== metadata?.href) return false;
16731
+ const tokens = declaration.trim().split(/\s+/);
16732
+ const template = (path) => path.split("/").map((segment) => /^\{[^{}\/]+\}$/.test(segment) ? "{}" : segment).join("/");
16733
+ return tokens.at(-2)?.toLowerCase() === operation.method.toLowerCase() && template(tokens.at(-1) ?? "") === template(operation.path);
16734
+ })()) generatedControllers += 1;
16735
+ }
16736
+ let local = group;
16737
+ if (owned && typeof props.className === "string" && props.className.split(/\s+/).includes("code-group")) {
16738
+ local = { owners: localOwners, code: [], controls: [], toolbar: false, generated: false, invalid: false };
16739
+ groups.push(local);
16740
+ }
16741
+ if (local && owned) {
16742
+ if (props["data-component-part"] === "code-group-tab-bar") local.toolbar = true;
16743
+ if (String(props["data-index"]) === "-1") local.generated = true;
16744
+ if (props["aria-expanded"] !== void 0) {
16745
+ const text3 = textOf(props.children, 0, true);
16746
+ if (kind !== "button" || props["aria-haspopup"] !== "menu" || text3 === void 0) local.invalid = true;
16747
+ else local.controls.push(JSON.stringify([text3, props["aria-label"] ?? "", String(props["aria-expanded"]), props["aria-haspopup"]]));
16748
+ }
16749
+ if (kind === "pre") {
16750
+ const code = textOf(props.children);
16751
+ if (code === void 0 || !code.trim()) local.invalid = true;
16752
+ else local.code.push(code);
16753
+ }
16754
+ if (props["aria-busy"] === true || props["aria-busy"] === "true" || props.role === "progressbar") local.invalid = true;
16755
+ }
16756
+ visit(props.children, owned, local, depth + 1, localOwners);
16757
+ } else for (const child of value) visit(child, provider, group, depth + 1, owners);
16758
+ } else if (depth === 0 && Array.isArray(value.f)) {
16759
+ visit(value.f, provider, group, depth + 1, owners);
16760
+ } else if (Object.keys(value).every((key) => key === "children")) {
16761
+ const children = value.children;
16762
+ if (Array.isArray(children) && children.length >= 2) visit(children, provider, group, depth + 1, owners);
16763
+ }
16764
+ active.delete(value);
16765
+ };
16766
+ visit(flight.records.get("0"), false, void 0, 0);
16767
+ if (exceeded || providerCount !== 1 || providers.length !== 1 || groups.length > MAX_TAB_GROUPS) return [];
16768
+ const sources = declared.map((pair) => JSON.parse(pair)[1]);
16769
+ const sourceCounts = /* @__PURE__ */ new Map();
16770
+ for (const group of groups) if (group.toolbar && group.generated && !group.invalid && group.controls.length >= 1 && group.controls.length <= 2) {
16771
+ const signature = JSON.stringify([group.owners, group.code, group.controls]);
16772
+ sourceCounts.set(signature, (sourceCounts.get(signature) ?? 0) + 1);
16773
+ }
16774
+ const expanded = root.find("[aria-expanded]").toArray();
16775
+ if (expanded.length > MAX_TABS) return [];
16776
+ const controlIndices = new Map(expanded.map((node, index) => [node, index]));
16777
+ const domGroups = /* @__PURE__ */ new Map();
16778
+ $(expanded).each((_index, node) => {
16779
+ const control = $(node);
16780
+ const group = control.closest('.code-group,[data-testid="code-group-select"]');
16781
+ const legacy = group.is('[data-testid="code-group-select"]');
16782
+ if (!control.is('button[aria-haspopup="menu"]') || control.attr("aria-controls") || !legacy && !control.closest('[data-component-part="code-group-tab-bar"]').length || !group.length || group.find("[aria-expanded]").length < 1 || group.find("[aria-expanded]").length > 2) return;
16783
+ if (domGroups.has(group[0])) return;
16784
+ const menus = group.find("[aria-expanded]");
16785
+ if (menus.toArray().some((menu) => !$(menu).is('button[aria-haspopup="menu"]') || !$(menu).attr("aria-label")?.trim() && !$(menu).text().trim() || $(menu).attr("aria-controls") || !legacy && $(menu).closest('[data-component-part="code-group-tab-bar"]')[0] !== group.children('[data-component-part="code-group-tab-bar"]')[0] || legacy && !$(menu).parents().toArray().includes(group.children().first()[0]))) return;
16786
+ if (menus.length === 2) {
16787
+ const samples = declared.map((pair) => JSON.parse(pair));
16788
+ const labelledChoices = samples.filter((sample) => sample[2] && samples.filter((other) => other[0] === sample[0]).length >= 2);
16789
+ if (!labelledChoices.length || !menus.toArray().some((menu) => labelledChoices.some((sample) => sample[2] === $(menu).text().trim()))) return;
16790
+ }
16791
+ const panels = group.find("[data-index]");
16792
+ const code = group.find("pre").toArray().map((pre) => {
16793
+ const lines = $(pre).find(".token-line");
16794
+ return lines.length ? lines.toArray().map((line) => $(line).text()).join("\n") : $(pre).text();
16795
+ });
16796
+ if (group.find('[aria-busy="true"],[role="progressbar"]').length) return;
16797
+ if (legacy) {
16798
+ if (menus.length !== 2 || group.children().length !== 2 || group.children().first().find("pre").length || code.length !== 1 || !code[0].trim() || panels.length) return;
16799
+ const selectedSamples = declared.map((pair) => JSON.parse(pair)).filter((sample) => sample[2] && menus.toArray().some((menu) => $(menu).text().trim() === sample[2]) && menus.toArray().some((menu) => $(menu).text().trim().toLowerCase() === sample[0]?.toLowerCase()) && (code[0] === sample[1] || code[0] === sample[1]?.replace(/\n$/, "")));
16800
+ if (!selectedSamples.length) return;
16801
+ } else if (!panels.length || panels.length > MAX_TABS || code.length !== panels.length || !panels.toArray().some((panel) => $(panel).attr("data-index") === "-1") || panels.toArray().some((panel) => $(panel).find("pre").length !== 1 || !$(panel).text().trim()) || !sources.every((source) => code.some((captured) => captured === source || captured === source.replace(/\n$/, "")))) return;
16802
+ const controls = menus.toArray().map((menu) => JSON.stringify([$(menu).text(), $(menu).attr("aria-label") ?? "", $(menu).attr("aria-expanded"), $(menu).attr("aria-haspopup")]));
16803
+ const owners = group.parents("[id]").toArray().reverse().map((owner) => $(owner).attr("id"));
16804
+ if (group.attr("id")) owners.push(group.attr("id"));
16805
+ domGroups.set(group[0], { signature: JSON.stringify([owners, code, controls]), indices: menus.toArray().map((menu) => controlIndices.get(menu)), code, legacy, title: group.children('[data-component-part="code-group-tab-bar"]').find("span[title]").first().attr("title"), label: legacy || group.children('[data-component-part="code-group-tab-bar"]').length === 1 && group.children('[data-component-part="code-group-tab-bar"]').find("span[title]").toArray().some((title) => Boolean($(title).attr("title")?.trim()) && $(title).text().trim() === $(title).attr("title")) });
16806
+ });
16807
+ const domCounts = /* @__PURE__ */ new Map();
16808
+ for (const group of domGroups.values()) domCounts.set(group.signature, (domCounts.get(group.signature) ?? 0) + 1);
16809
+ const proofs = [];
16810
+ for (const group of domGroups.values()) if (sourceCounts.get(group.signature) === 1 && domCounts.get(group.signature) === 1) proofs.push(...group.indices);
16811
+ if (groups.length === 0 && providerMetadataMatches && generatedControllers === 1 && domGroups.size === 2) {
16812
+ const operations = root.find('[id^="api-playground-"][id$="-operation-page"]').filter((_index, node) => {
16813
+ const endpoint = $(node).find('[data-component-part="scroll-area-content"]').first().clone();
16814
+ endpoint.find("[data-thally-static-base-urls]").remove();
16815
+ return $(node).find(".method-pill").first().text().trim().toLowerCase() === operation.method.toLowerCase() && endpoint.text().trim() === operation.path;
16816
+ });
16817
+ const main = $("main#content-container"), side = $('[id="content-side-layout"]');
16818
+ const content = $('[id="content-area"]');
16819
+ const slots = /* @__PURE__ */ new Map();
16820
+ const titles = [resolveValue(providers[0].title), resolveValue(providers[0].summary), ...declared.flatMap((pair) => {
16821
+ const values = JSON.parse(pair);
16822
+ return [values[0], values[2]];
16823
+ })].filter((value) => typeof value === "string").map((value) => value.toLowerCase());
16824
+ let valid = operations.length === 1 && main.length === 1 && side.length === 1 && content.length === 1 && operations.closest("#content-area")[0] === content[0] && $("[id]").filter((_index, node) => $(node).attr("id") === operations.attr("id")).length === 1 && side.closest("main")[0] === main[0];
16825
+ for (const [node, entry] of domGroups) {
16826
+ const group = $(node);
16827
+ const control = group.find("button[aria-expanded]").first();
16828
+ const owner = group.closest('[id^="api-playground-"][id$="-operation-page"]');
16829
+ const slot = owner[0] === operations[0] ? "inline" : group.closest("#content-side-layout")[0] === side[0] ? "sidebar" : void 0;
16830
+ if (!slot || slots.has(slot) || !entry.label || !entry.legacy && (!entry.title || !titles.includes(entry.title.toLowerCase()) || !control.attr("aria-label")?.trim()) || group.closest("main")[0] !== main[0] || group.closest('.mdx-content,.api-section,.object-param-field,.array-param-field,[data-component-part="expandable-content"]').length) valid = false;
16831
+ else slots.set(slot, entry);
16832
+ }
16833
+ if (valid && slots.size === 2 && JSON.stringify(slots.get("inline")?.code) === JSON.stringify(slots.get("sidebar")?.code)) {
16834
+ proofs.push(...[...slots.values()].flatMap((slot) => slot.indices));
16835
+ }
16836
+ }
16837
+ return proofs;
16838
+ }
15302
16839
 
15303
16840
  // src/url-run.ts
15304
- var CONVERTER_VERSION = "url-evidence-v5";
15305
- var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
16841
+ var CONVERTER_VERSION = "url-evidence-v6";
16842
+ var parser3 = unified5().use(remarkParse5).use(remarkMdx5);
15306
16843
  var ACCEPT = "text/markdown,text/html,application/xhtml+xml;q=0.9";
15307
16844
  var MAX_RESPONSE_BYTES = 8e6;
15308
16845
  var MAX_ASSET_BYTES2 = 1e7;
15309
16846
  var MAX_RECORDS = 1e5;
15310
16847
  var LOCALES = /* @__PURE__ */ new Set(["ar", "cs", "da", "de", "el", "en", "es", "fi", "fr", "he", "hi", "hu", "id", "it", "ja", "ko", "ms", "nl", "no", "pl", "pt", "pt-br", "ro", "ru", "sk", "sv", "th", "tr", "uk", "vi", "zh", "zh-cn", "zh-hans", "zh-hant"]);
15311
16848
  function hash2(value) {
15312
- return createHash3("sha256").update(value).digest("hex");
16849
+ return createHash6("sha256").update(value).digest("hex");
15313
16850
  }
15314
16851
  function canonicalJson(value) {
15315
16852
  const canonical2 = (value2) => Array.isArray(value2) ? value2.map(canonical2) : value2 !== null && typeof value2 === "object" ? Object.fromEntries(Object.entries(value2).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, field]) => [key, canonical2(field)])) : value2;
@@ -15345,14 +16882,14 @@ function markdownNodes(body) {
15345
16882
  nodes.push(node);
15346
16883
  for (const child of node.children ?? []) visit(child);
15347
16884
  };
15348
- visit(parser2.parse(body));
16885
+ visit(parser3.parse(body));
15349
16886
  return nodes;
15350
16887
  }
15351
16888
  function markdownCode(body) {
15352
16889
  return markdownNodes(body).filter((node) => node.type === "code").map((node) => node.value ?? "");
15353
16890
  }
15354
16891
  function alternativeBlocks(body, baseline) {
15355
- const blocks = (value) => (parser2.parse(value).children ?? []).map((node) => value.slice(node.position.start.offset, node.position.end.offset));
16892
+ const blocks = (value) => (parser3.parse(value).children ?? []).map((node) => value.slice(node.position.start.offset, node.position.end.offset));
15356
16893
  const originals = new Set(blocks(baseline));
15357
16894
  return blocks(body).filter((block) => !originals.has(block)).join("\n\n");
15358
16895
  }
@@ -15364,7 +16901,7 @@ function linksOf(document) {
15364
16901
  return [...root.find("a[href]").toArray(), ...$('link[rel="sitemap"][href],link[rel="alternate"][href]').toArray()].map((node) => $(node).attr("href")).filter(Boolean);
15365
16902
  }
15366
16903
  if (/xml/i.test(document.contentType) || /<urlset|<sitemapindex/i.test(document.body)) {
15367
- const $ = load6(document.body, { xmlMode: true });
16904
+ const $ = load8(document.body, { xmlMode: true });
15368
16905
  return $("loc").toArray().map((node) => $(node).text().trim());
15369
16906
  }
15370
16907
  return [
@@ -15394,7 +16931,8 @@ function evidenceOf(capture) {
15394
16931
  ...capture.artifactRef ? { artifactRef: capture.artifactRef } : {},
15395
16932
  states: (capture.states ?? []).map((state) => ({ id: state.id, label: state.label, sha256: hash2(state.response.body) })),
15396
16933
  ...technical,
15397
- links: linksOf(capture)
16934
+ links: linksOf(capture),
16935
+ articleIdentity: observedMigrationArticleIdentity(capture)
15398
16936
  };
15399
16937
  }
15400
16938
  function markdownHeadingAnchors(body) {
@@ -15415,20 +16953,6 @@ function idForUrl(url, scope) {
15415
16953
  const id = pageIdFromReference(relative7 || "introduction", true);
15416
16954
  return id && `${id}${url.search ? `--${hash2(url.search).slice(0, 12)}` : ""}`;
15417
16955
  }
15418
- function articleIdentity(document) {
15419
- if (!/html/i.test(document.contentType)) return document.finalUrl;
15420
- const $ = load6(document.body);
15421
- const reference = $('link[rel="canonical"][href]').first().attr("href");
15422
- if (!reference) return document.finalUrl;
15423
- try {
15424
- const target2 = new URL(reference, document.finalUrl);
15425
- const source = document.finalUrl;
15426
- const directory = (path) => path.replace(/\/index\/?$/i, "").replace(/\/$/, "");
15427
- return target2.origin === source.origin && !target2.username && !target2.password && target2.search === source.search && directory(target2.pathname) === directory(source.pathname) ? target2 : source;
15428
- } catch {
15429
- return document.finalUrl;
15430
- }
15431
- }
15432
16956
  function convert(document, id) {
15433
16957
  if (/html/i.test(document.contentType)) {
15434
16958
  const technical = htmlEvidence(document);
@@ -15477,24 +17001,52 @@ function assetReferences(capture) {
15477
17001
  ...root.find("[srcset]").toArray().flatMap((node) => migrationImageCandidates($(node).attr("srcset") ?? "", capture.finalUrl).map((candidate) => candidate.url))
15478
17002
  ].filter(Boolean);
15479
17003
  }
15480
- function unresolvedStaticControls(body, hasApiSchema = false, coveredSchemaGroups = []) {
17004
+ function unresolvedStaticControls(body, coveredSchemaGroups = [], coveredSchemaDisclosures = [], coveredCodeControls = []) {
15481
17005
  const { $, root } = migrationHtmlArticle(body);
17006
+ root.find("[aria-expanded]").each((index, node) => {
17007
+ if (coveredCodeControls.includes(index)) $(node).removeAttr("aria-expanded");
17008
+ });
15482
17009
  root.find('[role="tablist"],[data-component-part="tabs-list"]').each((index, node) => {
15483
17010
  if (coveredSchemaGroups.includes(index)) $(node).remove();
15484
17011
  });
17012
+ root.find("details").each((index, node) => {
17013
+ if (coveredSchemaDisclosures.includes(index)) $(node).remove();
17014
+ });
15485
17015
  const tabs = root.find('[role="tab"]').toArray();
17016
+ const idCounts = /* @__PURE__ */ new Map();
17017
+ root.find("[id]").each((_index, node) => {
17018
+ const id = $(node).attr("id");
17019
+ idCounts.set(id, (idCounts.get(id) ?? 0) + 1);
17020
+ });
15486
17021
  for (const tab of tabs) {
17022
+ const controlId = $(tab).attr("id");
17023
+ if (controlId && idCounts.get(controlId) !== 1) return true;
15487
17024
  const controls = $(tab).attr("aria-controls");
15488
- const group = $(tab).closest('[role="tablist"]');
17025
+ const group = $(tab).closest('[role="tablist"],ul.tabs');
15489
17026
  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]);
17027
+ const peers = group.find('[role="tab"]').toArray().filter((node) => $(node).closest('[role="tablist"],ul.tabs')[0] === group[0]);
15491
17028
  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]);
17029
+ const owners = container.find('[role="tablist"],ul.tabs').toArray().filter((node) => $(node).closest(".code-group,.tabs-container,.tabs.tab-container")[0] === container[0]);
15493
17030
  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)) : $([]);
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;
15495
- }
17031
+ if (panels.length !== 1 || !panels.text().trim() && !panels.find("pre,img,table,svg").length || panels.is('[aria-busy="true"],[role="progressbar"]') || panels.find('[aria-busy="true"],[role="progressbar"]').length) return true;
17032
+ const labels2 = panels.attr("aria-labelledby")?.trim().split(/\s+/);
17033
+ if (labels2 && (!controlId || !labels2.includes(controlId))) return true;
17034
+ if (controls && !labels2 && (container.length ? panels.closest(".code-group,.tabs-container,.tabs.tab-container")[0] !== container[0] : panels.parent()[0] !== group.parent()[0])) return true;
17035
+ }
17036
+ const isCapturedImageZoom = (node, depth = 0) => {
17037
+ if (depth > 8) return false;
17038
+ const control = $(node), image = control.prev("span.zoom-image-trigger[data-ramka-trigger]");
17039
+ return control.is('button[data-ramka-trigger][aria-haspopup="dialog"][aria-label]') && Boolean(control.attr("aria-label")?.trim()) && !control.attr("aria-controls") && !control.text().trim() && image.find("img[src]").length === 1 && !image.text().trim() && !image.find("form,iframe,object,embed,canvas").length && image.find("button").toArray().every((button) => isCapturedImageZoom(button, depth + 1));
17040
+ };
17041
+ if (root.find("[aria-expanded]").toArray().some((node) => {
17042
+ const control = $(node);
17043
+ if (control.attr("role") === "tab") return false;
17044
+ const actions = control.parent('[id="page-context-menu"]');
17045
+ if (control.is('button[aria-haspopup="menu"]') && !control.attr("aria-controls") && !control.text().trim() && actions.closest("header").length && actions.children("button#page-context-menu-button").length === 1) return false;
17046
+ if (isCapturedImageZoom(node)) return false;
17047
+ return true;
17048
+ })) return true;
15496
17049
  return root.find("details:not([open])").toArray().some((node) => {
15497
- if (hasApiSchema && $(node).is(".expandable") && $(node).children('summary[data-component-part="expandable-button"]').length) return false;
15498
17050
  const clone = $(node).clone();
15499
17051
  clone.children("summary").remove();
15500
17052
  return !clone.text().trim() && !clone.find("pre,img,table,svg").length;
@@ -15532,7 +17084,7 @@ function acceptedAsset(asset) {
15532
17084
  }
15533
17085
  function validRecord(record) {
15534
17086
  if (record.outcome !== "imported") return true;
15535
- return Boolean(record.page && record.pageSha256 === pageHash(record.page) && record.evidence && pageIdFromReference(record.page.id, true) === record.page.id && pageIdFromReference(record.page.navigationId, true) === record.page.navigationId && sanitizeRemoteMarkdown(record.page.body.replace(/^<a id="[^"<]*" \/>$/gm, "")) !== null && /^[a-f0-9]{64}$/.test(record.evidence.sha256) && (!record.apiSpecification || hasCompleteLocalApiSchema(record.apiSpecification.document) && record.apiSpecification.path === `openapi/migration-${record.apiSpecification.sha256}.json` && hash2(canonicalJson(record.apiSpecification.document)) === record.apiSpecification.sha256) && record.assets?.every((asset) => /^migration-assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(asset.path) && hash2(Buffer.from(asset.base64, "base64")) === asset.sha256));
17087
+ return Boolean(record.page && record.pageSha256 === pageHash(record.page) && record.evidence && pageIdFromReference(record.page.id, true) === record.page.id && pageIdFromReference(record.page.navigationId, true) === record.page.navigationId && sanitizeRemoteMarkdown(record.page.body.replace(/^<a id="[^"<]*" \/>$/gm, "")) !== null && /^[a-f0-9]{64}$/.test(record.evidence.sha256) && (!record.apiSpecification || hasCompleteLocalApiSchema(record.apiSpecification.document) && record.apiSpecification.path === `openapi/migration-${record.apiSpecification.sha256}.json` && hash2(canonicalJson(record.apiSpecification.document)) === record.apiSpecification.sha256) && record.assets?.every((asset) => /^migration-assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(asset.path) && (!asset.derivedFromSha256 || /^[a-f0-9]{64}$/.test(asset.derivedFromSha256)) && hash2(Buffer.from(asset.base64, "base64")) === asset.sha256));
15536
17088
  }
15537
17089
  function hasCompleteLocalApiSchema(document) {
15538
17090
  let visited = 0;
@@ -15638,7 +17190,7 @@ function observedLocale(document, id, scopePath) {
15638
17190
  }
15639
17191
  const urlLocale = [id.split("/", 1)[0], ...scopePath.split("/"), document.finalUrl.pathname.split("/").filter(Boolean)[0]].find((segment) => segment && LOCALES.has(segment.toLowerCase()));
15640
17192
  if (urlLocale) candidates.push({ source: "URL", value: canonicalLanguage(urlLocale) });
15641
- const declared = /html/i.test(document.contentType) ? load6(document.body)("html").attr("lang")?.trim() : void 0;
17193
+ const declared = /html/i.test(document.contentType) ? load8(document.body)("html").attr("lang")?.trim() : void 0;
15642
17194
  if (declared) {
15643
17195
  const locale = canonicalLanguage(declared);
15644
17196
  if (locale) candidates.push({ source: "html lang", value: locale });
@@ -15742,14 +17294,14 @@ function validateUrlMigration(checkpoint, cancelled = false) {
15742
17294
  continue;
15743
17295
  }
15744
17296
  if (!inScope(link, source, checkpoint.scopePath)) continue;
15745
- if (isDiscoveryIndex(link, source)) continue;
17297
+ if (isDiscoveryIndex(link, source) || isMigrationUtilityUrl(link)) continue;
15746
17298
  if (/\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js|json|ya?ml)$/i.test(link.pathname)) continue;
15747
17299
  const destination = resolve5(link.toString());
15748
17300
  if (!destination || !["imported", "excluded"].includes(destination.outcome)) {
15749
17301
  issues.push({ code: "missing-page", source: record.url, message: `Unresolved internal destination: ${link}` });
15750
- } else if (link.hash && destination.outcome === "imported") {
15751
- let anchor = link.hash.slice(1);
15752
- try {
17302
+ } else if ((link.hash || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "")?.aliasAnchor) && destination.outcome === "imported") {
17303
+ let anchor = link.hash.slice(1) || records.get(canonical(link.href, checkpoint.sourceUrl) ?? "").aliasAnchor;
17304
+ if (link.hash) try {
15753
17305
  anchor = decodeURIComponent(anchor);
15754
17306
  } catch {
15755
17307
  }
@@ -15770,7 +17322,7 @@ function validateUrlMigration(checkpoint, cancelled = false) {
15770
17322
  const locale = node.locale ? canonicalLanguage(node.locale) : parentLocale;
15771
17323
  if (node.url) {
15772
17324
  const url = new URL(node.url, source);
15773
- if (inScope(url, source, checkpoint.scopePath)) {
17325
+ if (inScope(url, source, checkpoint.scopePath) && !isMigrationUtilityUrl(url)) {
15774
17326
  const destination = resolve5(url.toString());
15775
17327
  if (destination?.outcome === "excluded" && destination.redirectTo) continue;
15776
17328
  const page = destination?.page;
@@ -15838,6 +17390,7 @@ async function runUrlMigration(options) {
15838
17390
  delete record.redirectTo;
15839
17391
  delete record.reason;
15840
17392
  delete record.aliasOf;
17393
+ delete record.aliasAnchor;
15841
17394
  }
15842
17395
  if (record.page && pageIdFromReference(record.page.id, true) !== record.page.id) throw new Error("Checkpoint contains an unsafe page path.");
15843
17396
  records.set(record.url, record);
@@ -15848,7 +17401,8 @@ async function runUrlMigration(options) {
15848
17401
  const enqueue = (value, base, discoveredBy) => {
15849
17402
  const url = canonical(value, base);
15850
17403
  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);
17404
+ const isUtility = isMigrationUtilityUrl(new URL(url));
17405
+ const isAttachment = isUtility || discoveredBy !== "submitted" && /\.(?:json|ya?ml)$/i.test(new URL(url).pathname);
15852
17406
  const existing = records.get(url);
15853
17407
  if (existing) {
15854
17408
  if (!existing.discoveredBy.includes(discoveredBy)) {
@@ -15862,7 +17416,7 @@ async function runUrlMigration(options) {
15862
17416
  url,
15863
17417
  discoveredBy: [discoveredBy],
15864
17418
  outcome: isAttachment ? "excluded" : "pending",
15865
- ...isAttachment ? { reason: "Machine-readable attachment, not an authored article." } : {}
17419
+ ...isAttachment ? { reason: isUtility ? "Recognized provider utility endpoint, not an authored article." : "Machine-readable attachment, not an authored article." } : {}
15866
17420
  };
15867
17421
  records.set(url, record);
15868
17422
  checkpoint.records.push(record);
@@ -15905,12 +17459,27 @@ async function runUrlMigration(options) {
15905
17459
  let imported = usedIds.size;
15906
17460
  const assetPayloads = new Map(checkpoint.records.flatMap((record) => (record.assets ?? []).map((asset) => [asset.sha256, asset])));
15907
17461
  const assetBytes = /* @__PURE__ */ new Map();
15908
- const retainAsset = (asset) => {
17462
+ const rasterizedSvgs = /* @__PURE__ */ new Map();
17463
+ const retainAsset = async (original) => {
17464
+ let asset = { ...original };
17465
+ delete asset.derivedFromSha256;
15909
17466
  if (asset.unavailable) {
15910
17467
  if ([404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) return asset;
15911
17468
  throw new Error("Invalid source asset availability evidence.");
15912
17469
  }
15913
- if (!acceptedAsset(asset)) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
17470
+ if (!acceptedAsset(asset)) {
17471
+ if (asset.content.byteLength > MAX_ASSET_BYTES2 || asset.contentType.split(";", 1)[0].trim().toLowerCase() !== "image/svg+xml" || !options.rasterizeSvg) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
17472
+ const sourceSha256 = hash2(asset.content);
17473
+ let rendering = rasterizedSvgs.get(sourceSha256);
17474
+ if (!rendering) {
17475
+ rendering = options.rasterizeSvg({ ...asset, content: Buffer.from(asset.content) }, options.signal).then((rendered) => {
17476
+ if (rendered.unavailable || rendered.contentType.split(";", 1)[0].trim().toLowerCase() !== "image/png" || acceptedAsset(rendered) !== "png") throw new Error("Trusted SVG renderer did not return a bounded PNG image.");
17477
+ return { ...rendered, derivedFromSha256: sourceSha256 };
17478
+ });
17479
+ rasterizedSvgs.set(sourceSha256, rendering);
17480
+ }
17481
+ asset = { ...await rendering, url: original.url };
17482
+ }
15914
17483
  const sha256 = hash2(asset.content);
15915
17484
  const content = assetBytes.get(sha256) ?? Buffer.from(asset.content);
15916
17485
  assetBytes.set(sha256, content);
@@ -15926,6 +17495,26 @@ async function runUrlMigration(options) {
15926
17495
  }
15927
17496
  let totalBytes = checkpoint.records.reduce((sum, record) => sum + (record.evidence?.byteLength ?? 0), 0) + [...assetPayloads.values()].reduce((sum, asset) => sum + Buffer.byteLength(asset.base64, "base64"), 0);
15928
17497
  let budgetExhausted = false;
17498
+ const exactCaptures = /* @__PURE__ */ new Map();
17499
+ const exactCaptureKey = (document) => {
17500
+ let body = document.body;
17501
+ if (body.includes("data-cfemail")) {
17502
+ const $ = load8(body);
17503
+ projectProtectedEmails($, $.root());
17504
+ body = $.html();
17505
+ }
17506
+ return hash2(canonicalJson([
17507
+ hash2(body),
17508
+ document.contentType,
17509
+ document.status,
17510
+ document.locale,
17511
+ document.version,
17512
+ document.headers?.["content-language"],
17513
+ document.navigation,
17514
+ (document.states ?? []).map((state) => [state.id, state.label, state.response.finalUrl.href, state.response.contentType, state.response.status, hash2(state.response.body)]),
17515
+ (document.assets ?? []).map((asset) => [asset.url, asset.contentType, asset.unavailable, hash2(asset.content)])
17516
+ ]));
17517
+ };
15929
17518
  let cursor = 0;
15930
17519
  const flush = async () => {
15931
17520
  const distinct = [...new Map(changed.map((record) => [record.url, record])).values()];
@@ -15940,6 +17529,7 @@ async function runUrlMigration(options) {
15940
17529
  let claimedId;
15941
17530
  let claimedFinal;
15942
17531
  let markdownRepresentation;
17532
+ let exactKey;
15943
17533
  try {
15944
17534
  delete record.reason;
15945
17535
  if (/\/llms-full\.txt$/i.test(new URL(record.url).pathname)) {
@@ -15967,7 +17557,43 @@ async function runUrlMigration(options) {
15967
17557
  throw new Error("Capture returned an unresolved redirect.");
15968
17558
  }
15969
17559
  if (Buffer.byteLength(document.body) > MAX_RESPONSE_BYTES) throw new Error("Captured response exceeds the 8 MB safety bound.");
15970
- const identity = articleIdentity(document);
17560
+ if ((document.states?.length ?? 0) > 64 || (document.assets?.length ?? 0) > 256) throw new Error("Capture contains too many alternative states or assets.");
17561
+ for (const state of document.states ?? []) if (Buffer.byteLength(state.response.body) > MAX_RESPONSE_BYTES) throw new Error("Alternative state exceeds the 8 MB safety bound.");
17562
+ if (/html/i.test(document.contentType) && (!document.status || document.status < 300) && inScope(document.finalUrl, source, scopePath)) {
17563
+ exactKey = exactCaptureKey(document);
17564
+ const peer = resolveMigrationRecord(records, records.get(exactCaptures.get(exactKey) ?? ""));
17565
+ const peerIdentity = peer?.evidence?.articleIdentity;
17566
+ if (peer?.outcome === "imported" && peer.evidence && peerIdentity) {
17567
+ const candidate = migrationSectionQueryCandidate(document.finalUrl, peer.evidence.anchors);
17568
+ if (candidate.sectionAnchor && candidate.canonicalUrl === peerIdentity.canonicalUrl) {
17569
+ const byteLength = Buffer.byteLength(document.body) + (document.states ?? []).reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0);
17570
+ totalBytes += byteLength;
17571
+ if (totalBytes > (options.maxTotalBytes ?? 5e8)) {
17572
+ budgetExhausted = true;
17573
+ throw new Error("Migration exceeded its content byte budget.");
17574
+ }
17575
+ record.outcome = "alias";
17576
+ record.aliasOf = peer.url;
17577
+ record.aliasAnchor = candidate.sectionAnchor;
17578
+ if (peerIdentity.sectionAnchor && peer.aliasAnchor !== peerIdentity.sectionAnchor) {
17579
+ peer.aliasAnchor = peerIdentity.sectionAnchor;
17580
+ changed.push(peer);
17581
+ }
17582
+ record.evidence = {
17583
+ ...peer.evidence,
17584
+ sha256: hash2(document.body),
17585
+ byteLength,
17586
+ finalUrl: document.finalUrl.href,
17587
+ ...document.artifactRef ? { artifactRef: document.artifactRef } : {},
17588
+ articleIdentity: { ...peerIdentity, sectionAnchor: candidate.sectionAnchor }
17589
+ };
17590
+ if (!document.artifactRef) delete record.evidence.artifactRef;
17591
+ enqueueArticleLinks(record.evidence.links, document.finalUrl.href, record.url);
17592
+ return;
17593
+ }
17594
+ }
17595
+ }
17596
+ const identity = declaredMigrationArticleUrl(document);
15971
17597
  const final = canonical(identity.toString(), sourceUrl);
15972
17598
  const role = classifyMigrationResponse(document);
15973
17599
  if (!inScope(document.finalUrl, source, scopePath) && !(role === "index" && isDiscoveryIndex(document.finalUrl, source))) {
@@ -15991,13 +17617,13 @@ async function runUrlMigration(options) {
15991
17617
  return;
15992
17618
  }
15993
17619
  if (/html/i.test(document.contentType)) {
15994
- const $ = load6(document.body);
17620
+ const $ = load8(document.body);
15995
17621
  $('link[rel="alternate"][type="text/markdown"][href]').each((_index, node) => {
15996
17622
  const alternate = canonical($(node).attr("href"), final);
15997
17623
  if (!alternate) return;
15998
17624
  const target2 = new URL(alternate), primary = new URL(final);
15999
- const directory = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
16000
- if (target2.origin !== primary.origin || target2.search !== primary.search || directory(target2.pathname) !== directory(primary.pathname) || !/\.mdx?$/i.test(target2.pathname)) return;
17625
+ const directory2 = (path) => path.replace(/\.mdx?$/i, "").replace(/\/index\/?$/i, "").replace(/\/$/, "");
17626
+ if (target2.origin !== primary.origin || target2.search !== primary.search || directory2(target2.pathname) !== directory2(primary.pathname) || !/\.mdx?$/i.test(target2.pathname)) return;
16001
17627
  enqueue(alternate, final, "declared-markdown-representation");
16002
17628
  const sibling = records.get(alternate);
16003
17629
  sibling.representationOf = record.url;
@@ -16048,6 +17674,9 @@ async function runUrlMigration(options) {
16048
17674
  return;
16049
17675
  }
16050
17676
  let coveredSchemaGroups = [];
17677
+ let coveredSchemaDisclosures = [];
17678
+ let coveredCodeControls = [];
17679
+ let schemaOperation;
16051
17680
  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;
16052
17681
  if (/html/i.test(document.contentType) && markdownRepresentation && (unresolvedStaticControls(document.body) || hasSchemaChoice)) {
16053
17682
  const schemaCapture = await capture({ url: new URL(markdownRepresentation), signal: options.signal });
@@ -16063,7 +17692,10 @@ async function runUrlMigration(options) {
16063
17692
  const path = `openapi/migration-${sha256}.json`;
16064
17693
  record.apiSpecification = { path, sha256, document: fragment.document };
16065
17694
  page.openapi = `${path} ${fragment.method} ${fragment.path}`;
17695
+ schemaOperation = fragment;
16066
17696
  coveredSchemaGroups = apiSchemaTabGroups(document.body, fragment.document, fragment);
17697
+ coveredSchemaDisclosures = apiSchemaDisclosureGroups(document.body, fragment.document, fragment);
17698
+ coveredCodeControls = apiOperationCodeControls(document.body, fragment.document, fragment);
16067
17699
  }
16068
17700
  }
16069
17701
  const states = document.states ?? [];
@@ -16094,12 +17726,13 @@ ${difference}`;
16094
17726
  record.assets = [];
16095
17727
  record.issues = [];
16096
17728
  record.warnings = [];
16097
- if (/html/i.test(document.contentType)) {
16098
- const { $, root } = migrationHtmlArticle(document.body);
17729
+ for (const captured of [document, ...states.map((state) => state.response)]) {
17730
+ if (!/html/i.test(captured.contentType)) continue;
17731
+ const { $, root } = migrationHtmlArticle(captured.body);
16099
17732
  root.find("nav,footer,aside,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
16100
17733
  root.find('button svg,a.hash-link svg,[aria-hidden="true"] svg,svg[aria-hidden="true"],svg[data-lucide],svg[data-icon]').remove();
16101
17734
  root.find("video,iframe").each((_index, node) => {
16102
- if (projectMigrationMedia($.html(node), document.finalUrl) !== null) {
17735
+ if (projectMigrationMedia($.html(node), captured.finalUrl) !== null) {
16103
17736
  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
17737
  $(node).remove();
16105
17738
  }
@@ -16110,9 +17743,17 @@ ${difference}`;
16110
17743
  }
16111
17744
  const unsupported = [...new Set(root.find("iframe,object,embed,canvas,svg,form,input,textarea,select,math,video,audio").toArray().map((node) => node.tagName))];
16112
17745
  if (unsupported.length) record.issues.push({ code: "unsupported-component", source: record.url, message: `Rendered content requires explicit review for: ${unsupported.join(", ")}.` });
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." });
16114
- }
16115
- const suppliedAssets = new Map((document.assets ?? []).map((asset) => [canonical(asset.url, final), asset]));
17746
+ const groups = captured === document ? coveredSchemaGroups : record.apiSpecification && schemaOperation ? apiSchemaTabGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
17747
+ const disclosures = captured === document ? coveredSchemaDisclosures : record.apiSpecification && schemaOperation ? apiSchemaDisclosureGroups(captured.body, record.apiSpecification.document, schemaOperation) : [];
17748
+ const codeControls = captured === document ? coveredCodeControls : record.apiSpecification && schemaOperation ? apiOperationCodeControls(captured.body, record.apiSpecification.document, schemaOperation) : [];
17749
+ if (unresolvedStaticControls(captured.body, groups, disclosures, codeControls)) record.issues.push({ code: "unsupported-component", source: record.url, message: "An interactive alternative has no complete captured panel or disclosure content." });
17750
+ }
17751
+ const inlineAssets = [document, ...states.map((state) => state.response)].flatMap((captured) => /html/i.test(captured.contentType) ? migrationHtmlArticle(captured.body).assets.map((asset) => ({
17752
+ url: new URL(`/${asset.path}`, captured.finalUrl).href,
17753
+ contentType: "image/svg+xml",
17754
+ content: typeof asset.content === "string" ? Buffer.from(asset.content) : asset.content
17755
+ })) : []);
17756
+ const suppliedAssets = new Map([...document.assets ?? [], ...inlineAssets].map((asset) => [canonical(asset.url, final), asset]));
16116
17757
  const assetUrls = [...new Set([...assetReferences(document), ...states.flatMap((state) => assetReferences(state.response))].map((url) => canonical(url, final)).filter((url) => Boolean(url)))];
16117
17758
  if (assetUrls.length > 256 || (document.assets?.length ?? 0) > 256) throw new Error("Capture exceeds the 256 assets per page safety bound.");
16118
17759
  for (const url of assetUrls) {
@@ -16122,7 +17763,7 @@ ${difference}`;
16122
17763
  assetRequest = assetRequests.load(url, () => assetFetcher(new URL(url), options.signal).then(retainAsset));
16123
17764
  }
16124
17765
  const supplied = suppliedAssets.get(url);
16125
- const asset = supplied ? retainAsset(supplied) : assetRequest ? await assetRequest : null;
17766
+ const asset = supplied ? await retainAsset(supplied) : assetRequest ? await assetRequest : null;
16126
17767
  if (!asset) throw new Error("Capture host did not supply asset bytes or a binary fetch boundary.");
16127
17768
  if (asset.unavailable && [404, 410].includes(asset.unavailable.status) && asset.content.byteLength === 0) {
16128
17769
  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}` });
@@ -16141,7 +17782,7 @@ ${difference}`;
16141
17782
  }
16142
17783
  }
16143
17784
  const path = `migration-assets/${sha256}.${extension}`;
16144
- const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64") };
17785
+ const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64"), ...asset.derivedFromSha256 ? { derivedFromSha256: asset.derivedFromSha256 } : {} };
16145
17786
  record.assets.push(receipt);
16146
17787
  if (!existing) assetPayloads.set(sha256, receipt);
16147
17788
  if (!assetRequests.has(url)) assetRequests.remember(url, asset);
@@ -16166,6 +17807,7 @@ ${difference}`;
16166
17807
  record.pageSha256 = pageHash(page);
16167
17808
  record.outcome = "imported";
16168
17809
  imported++;
17810
+ if (exactKey) exactCaptures.set(exactKey, record.url);
16169
17811
  enqueueArticleLinks(evidence.links, final, record.url);
16170
17812
  } catch (error) {
16171
17813
  if (options.signal?.aborted) return;
@@ -16191,6 +17833,43 @@ ${difference}`;
16191
17833
  }
16192
17834
  }
16193
17835
  };
17836
+ const settleArticleIdentities = () => {
17837
+ const groups = /* @__PURE__ */ new Map();
17838
+ for (const record of records.values()) {
17839
+ const identity = record.evidence?.articleIdentity;
17840
+ if (record.outcome !== "imported" || !identity) continue;
17841
+ const key = JSON.stringify([identity.canonicalUrl, identity.articleSha256]);
17842
+ const group = groups.get(key) ?? [];
17843
+ group.push(record);
17844
+ groups.set(key, group);
17845
+ }
17846
+ for (const group of groups.values()) {
17847
+ if (group.length < 2) continue;
17848
+ const canonicalUrl = group[0].evidence.articleIdentity.canonicalUrl;
17849
+ const winner = group.find((record) => record.url === canonicalUrl) ?? group[0];
17850
+ const winnerAnchor = winner.evidence.articleIdentity.sectionAnchor;
17851
+ if (winnerAnchor && winner.aliasAnchor !== winnerAnchor) {
17852
+ winner.aliasAnchor = winnerAnchor;
17853
+ changed.push(winner);
17854
+ }
17855
+ for (const alternate of group) {
17856
+ if (alternate === winner) continue;
17857
+ imported--;
17858
+ if (alternate.page && usedIds.get(alternate.page.id) === alternate.url) usedIds.delete(alternate.page.id);
17859
+ alternate.outcome = "alias";
17860
+ alternate.aliasOf = winner.url;
17861
+ alternate.aliasAnchor = alternate.evidence.articleIdentity.sectionAnchor;
17862
+ delete alternate.page;
17863
+ delete alternate.pageSha256;
17864
+ delete alternate.assets;
17865
+ delete alternate.apiSpecification;
17866
+ delete alternate.issues;
17867
+ delete alternate.warnings;
17868
+ delete alternate.reason;
17869
+ changed.push(alternate);
17870
+ }
17871
+ }
17872
+ };
16194
17873
  const settleRepresentations = () => {
16195
17874
  for (const record of records.values()) {
16196
17875
  if (!record.representationOf) continue;
@@ -16216,6 +17895,7 @@ ${difference}`;
16216
17895
  }
16217
17896
  };
16218
17897
  settleRepresentations();
17898
+ settleArticleIdentities();
16219
17899
  while (cursor < pending.length && !options.signal?.aborted && !budgetExhausted && cursor < (options.maxCaptures ?? Infinity)) {
16220
17900
  if (imported >= maxPages) {
16221
17901
  const index = pending.findIndex((record, position) => position >= cursor && isDiscoveryIndex(new URL(record.url), source));
@@ -16226,6 +17906,7 @@ ${difference}`;
16226
17906
  cursor += batch.length;
16227
17907
  await Promise.all(batch.map(process2));
16228
17908
  settleRepresentations();
17909
+ settleArticleIdentities();
16229
17910
  if (changed.length >= batchSize) await flush();
16230
17911
  }
16231
17912
  await flush();
@@ -16245,7 +17926,7 @@ ${difference}`;
16245
17926
  for (const node of nodes) {
16246
17927
  const entry = node.url ? records.get(canonical(node.url, sourceUrl) ?? "") : void 0;
16247
17928
  const target2 = resolveMigrationRecord(records, entry);
16248
- if (target2?.page && node.label && !node.children?.length) {
17929
+ if (target2?.page && node.label && !node.children?.length && !entry?.aliasAnchor) {
16249
17930
  const previous = navigationLabels.get(target2.url);
16250
17931
  if (previous && previous !== node.label) navigationIssues.push({ code: "unsupported-component", source: target2.url, message: `This page has multiple source navigation labels (${previous}, ${node.label}); review the shared sidebar title.` });
16251
17932
  else navigationLabels.set(target2.url, node.label);
@@ -16270,8 +17951,9 @@ ${difference}`;
16270
17951
  anchor = decodeURIComponent(anchor);
16271
17952
  } catch {
16272
17953
  }
17954
+ const effectiveHash = url.hash || (records.get(identity)?.aliasAnchor ? `#${encodeURIComponent(records.get(identity).aliasAnchor)}` : "");
16273
17955
  const hasBrokenSourceAnchor = Boolean(url.hash && destination?.outcome === "imported" && destination.evidence && !destination.evidence.anchors.includes(anchor));
16274
- return `${route}${hasBrokenSourceAnchor ? "" : url.hash}`;
17956
+ return `${route}${hasBrokenSourceAnchor ? "" : effectiveHash}`;
16275
17957
  }) }] : []);
16276
17958
  const validation = validateUrlMigration(checkpoint, Boolean(options.signal?.aborted));
16277
17959
  validation.issues.push(...navigationIssues);
@@ -16350,7 +18032,7 @@ function rewriteMarkdownDestinations(body, rewrite) {
16350
18032
  }
16351
18033
 
16352
18034
  // src/remote-api.ts
16353
- import { createHash as createHash4 } from "crypto";
18035
+ import { createHash as createHash7 } from "crypto";
16354
18036
  import { lookup } from "dns/promises";
16355
18037
  import { request } from "https";
16356
18038
  import { isIP } from "net";
@@ -16514,7 +18196,7 @@ async function hydrateRemoteApiSpecs(bundle, fetcher) {
16514
18196
  }
16515
18197
  const { format: extension, document } = parseSpecDocument(body.toString("utf8"));
16516
18198
  const operations = parseOpenApi(document);
16517
- const filename = `openapi-${createHash4("sha256").update(reference.url).digest("hex").slice(0, 12)}.${extension}`;
18199
+ const filename = `openapi-${createHash7("sha256").update(reference.url).digest("hex").slice(0, 12)}.${extension}`;
16518
18200
  if (!assets.some((asset) => asset.path === `openapi/${filename}`)) assets.push({ path: `openapi/${filename}`, content: body, projectRelative: true });
16519
18201
  for (const page of pages) {
16520
18202
  const ref = page.openapi ? splitOpenApiRef(page.openapi) : null;
@@ -16534,18 +18216,18 @@ async function hydrateRemoteApiSpecs(bundle, fetcher) {
16534
18216
  }
16535
18217
  }
16536
18218
  const directories = [
16537
- ...(bundle.localApiMounts ?? []).map(({ directory }) => directory),
16538
- ...bundle.remoteApiSpecs.flatMap((reference) => (reference.directories ?? []).map(({ directory }) => directory))
18219
+ ...(bundle.localApiMounts ?? []).map(({ directory: directory2 }) => directory2),
18220
+ ...bundle.remoteApiSpecs.flatMap((reference) => (reference.directories ?? []).map(({ directory: directory2 }) => directory2))
16539
18221
  ];
16540
18222
  const endpointRedirects = linkApiMounts(
16541
18223
  [
16542
- ...(bundle.localApiMounts ?? []).map(({ filename, directory, tab }) => ({
18224
+ ...(bundle.localApiMounts ?? []).map(({ filename, directory: directory2, tab }) => ({
16543
18225
  filename,
16544
18226
  content: Buffer.from(assets.find((asset) => asset.path === `openapi/${filename}`)?.content ?? []),
16545
- prefix: directory,
18227
+ prefix: directory2,
16546
18228
  ...tab ? { tab } : {}
16547
18229
  })),
16548
- ...bound.flatMap(({ reference, filename, content, tabName }) => (reference.directories ?? []).map(({ directory, operations: listed }) => ({ filename, content, prefix: directory, tab: tabName, ...listed ? { operations: listed } : {} })))
18230
+ ...bound.flatMap(({ reference, filename, content, tabName }) => (reference.directories ?? []).map(({ directory: directory2, operations: listed }) => ({ filename, content, prefix: directory2, tab: tabName, ...listed ? { operations: listed } : {} })))
16549
18231
  ],
16550
18232
  { ...bundle.docsConfig, tabs, redirects },
16551
18233
  pages,
@@ -16563,7 +18245,7 @@ async function hydrateRemoteApiSpecs(bundle, fetcher) {
16563
18245
  const specIds = apiTabSpecIds(tabs);
16564
18246
  for (const { reference, filename, operations, tabName } of bound) {
16565
18247
  const specId = specIds.find(({ tab }) => tab.tab === tabName && tab.api?.source === `openapi/${filename}`)?.specId;
16566
- const listed = reference.directories?.length && reference.directories.every((directory) => directory.operations) ? new Set(reference.directories.flatMap((directory) => directory.operations ?? []).map((name) => name.trim().split(/\s+/)).map(([method, ...path]) => `${method.toLowerCase()} ${path.join(" ")}`)) : null;
18248
+ const listed = reference.directories?.length && reference.directories.every((directory2) => directory2.operations) ? new Set(reference.directories.flatMap((directory2) => directory2.operations ?? []).map((name) => name.trim().split(/\s+/)).map(([method, ...path]) => `${method.toLowerCase()} ${path.join(" ")}`)) : null;
16567
18249
  for (const operation of operations) {
16568
18250
  if (listed && !listed.has(`${operation.method} ${operation.path}`)) continue;
16569
18251
  const key = `${slug(operation.tag)}/${slug(operation.summary)}`;