@thallylabs/migrate 0.2.27 → 0.2.29

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.
@@ -0,0 +1,3872 @@
1
+ // src/url-run.ts
2
+ import { createHash } from "crypto";
3
+ import { load as load2 } from "cheerio";
4
+ import remarkParse3 from "remark-parse";
5
+ import remarkMdx3 from "remark-mdx";
6
+ import { unified as unified3 } from "unified";
7
+
8
+ // src/navigation.ts
9
+ import { existsSync, lstatSync, readFileSync, realpathSync as realpathSync2 } from "fs";
10
+ import { dirname, extname as extname2, relative as relative2 } from "path";
11
+
12
+ // src/mintlify-extras.ts
13
+ import { extname } from "path";
14
+
15
+ // src/path.ts
16
+ import { realpathSync } from "fs";
17
+ import { isAbsolute, relative, resolve, sep } from "path";
18
+ var SAFE_SEGMENT = /[^a-z0-9._-]+/gi;
19
+ function slugifySegment(value, preserveCase = false) {
20
+ let decoded = value;
21
+ try {
22
+ decoded = decodeURIComponent(value);
23
+ } catch {
24
+ }
25
+ const normalized = preserveCase ? decoded : decoded.toLowerCase();
26
+ const slug = normalized.replace(/\.(?:html?|mdx?)$/i, "").replace(SAFE_SEGMENT, "-").replace(/(^-|-$)/g, "");
27
+ return slug === "." || slug === ".." ? "" : slug;
28
+ }
29
+ function pageIdFromReference(value, preserveCase = false) {
30
+ const withoutQuery = value.split(/[?#]/, 1)[0].replace(/\\/g, "/").replace(/^\/+/, "");
31
+ if (!withoutQuery || withoutQuery.includes("\0")) return null;
32
+ const rawSegments = withoutQuery.split("/").filter(Boolean);
33
+ if (rawSegments.some((segment) => segment === ".." || segment === ".")) return null;
34
+ const last = rawSegments.at(-1)?.replace(/\.(?:mdx?|rst|txt)$/i, "") ?? "";
35
+ const isRootLevelReadme = rawSegments.length === 1 && /^readme$/i.test(last);
36
+ const collapsesToParent = /^index$/i.test(last) || isRootLevelReadme;
37
+ const baseSegments = collapsesToParent ? rawSegments.slice(0, -1) : rawSegments;
38
+ if (!collapsesToParent) {
39
+ baseSegments[baseSegments.length - 1] = last;
40
+ }
41
+ const segments = baseSegments.map((segment) => slugifySegment(segment, preserveCase)).filter(Boolean);
42
+ return segments.join("/") || "introduction";
43
+ }
44
+
45
+ // src/mintlify-extras.ts
46
+ var OPEN_NAVIGATION_ACCESS = Object.freeze({ hasPublicFalse: false, hasPublicTrue: false });
47
+ function playgroundDisplay(value, where, warn) {
48
+ const mode = typeof value === "string" ? value.trim().toLowerCase() : "";
49
+ if (mode === "interactive" || mode === "simple" || mode === "none") return mode;
50
+ if (mode === "auth") {
51
+ warn?.(`${where} "auth" requires reader sign-in, which Thally does not support. Migrated as "simple" (no Try it). Set it to "interactive" to allow everyone.`);
52
+ return "simple";
53
+ }
54
+ warn?.(`${where} is not one of interactive, simple, none, auth and was dropped.`);
55
+ return void 0;
56
+ }
57
+
58
+ // src/navigation.ts
59
+ function titleCase(value) {
60
+ return value.split(/[-_]/).map((word) => ["api", "cli", "sdk", "ui"].includes(word.toLowerCase()) ? word.toUpperCase() : word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
61
+ }
62
+ var TOP_LEVEL_LABELS = {
63
+ "api-reference": "API Reference",
64
+ cli: "CLI",
65
+ faqs: "FAQs",
66
+ introduction: "Overview",
67
+ "mcp-server": "MCP Server",
68
+ sdk: "SDK"
69
+ };
70
+ function topLevelLabel(segment) {
71
+ return TOP_LEVEL_LABELS[segment] ?? titleCase(segment);
72
+ }
73
+ function groupsWithinSection(section, pageIds, sectionLabel = topLevelLabel(section)) {
74
+ const groups = /* @__PURE__ */ new Map();
75
+ for (const id of pageIds) {
76
+ const relative4 = id === "introduction" ? "" : id.startsWith(`${section}/`) ? id.slice(section.length + 1) : id;
77
+ const nestedSegment = relative4.includes("/") ? relative4.split("/", 1)[0] : "overview";
78
+ const group = groups.get(nestedSegment) ?? [];
79
+ group.push(id);
80
+ groups.set(nestedSegment, group);
81
+ }
82
+ return [...groups].map(([segment, pages]) => ({
83
+ group: segment === "overview" ? sectionLabel : titleCase(segment),
84
+ pages
85
+ }));
86
+ }
87
+ function preferredLandingPage(section, pageIds) {
88
+ const candidates = [
89
+ section === "introduction" ? "introduction" : void 0,
90
+ `${section}/overview`,
91
+ `${section}/introduction`,
92
+ section
93
+ ];
94
+ return candidates.find((candidate) => Boolean(candidate && pageIds.includes(candidate)));
95
+ }
96
+ function buildNavigationFromPages(pages, options = {}) {
97
+ const ids = pages.filter((page) => !page.locale || page.locale === "en").map((page) => page.navigationId);
98
+ const ordered = [...new Set(ids)];
99
+ if (options.topLevelTabs) {
100
+ const sourceNavigation2 = (options.topLevelNavigation ?? []).flatMap((entry) => {
101
+ const sectionPages = ordered.filter((id) => id === entry.section || id.startsWith(`${entry.section}/`));
102
+ const pageId = ordered.includes(entry.pageId) ? entry.pageId : preferredLandingPage(entry.section, sectionPages) ?? sectionPages[0];
103
+ return pageId ? [{ ...entry, pageId }] : [];
104
+ });
105
+ if (sourceNavigation2.length > 1) {
106
+ const claimedIds = /* @__PURE__ */ new Set();
107
+ const tabs = sourceNavigation2.map((entry) => {
108
+ const pageIds = ordered.filter((id) => {
109
+ const matches = id === entry.pageId || id === entry.section || id.startsWith(`${entry.section}/`);
110
+ if (matches) claimedIds.add(id);
111
+ return matches;
112
+ });
113
+ return { entry, pageIds };
114
+ });
115
+ tabs[0].pageIds.push(...ordered.filter((id) => !claimedIds.has(id)));
116
+ return {
117
+ tabs: tabs.map(({ entry, pageIds }) => ({
118
+ tab: entry.label,
119
+ href: entry.pageId === "introduction" ? "/" : `/${entry.pageId}`,
120
+ groups: groupsWithinSection(entry.section, [...new Set(pageIds)], entry.label)
121
+ }))
122
+ };
123
+ }
124
+ const sectionNames = [...new Set(ordered.filter((id) => id.includes("/")).map((id) => id.split("/", 1)[0]))];
125
+ if (sectionNames.length > 1) {
126
+ const defaultSection = sectionNames.includes("introduction") ? "introduction" : sectionNames[0];
127
+ const sections = /* @__PURE__ */ new Map();
128
+ for (const id of ordered) {
129
+ const section = id.includes("/") ? id.split("/", 1)[0] : defaultSection;
130
+ const bucket = sections.get(section) ?? [];
131
+ bucket.push(id);
132
+ sections.set(section, bucket);
133
+ }
134
+ return {
135
+ tabs: [...sections].map(([section, pageIds]) => {
136
+ const label = topLevelLabel(section);
137
+ const landingPage = preferredLandingPage(section, pageIds);
138
+ return {
139
+ tab: label,
140
+ ...landingPage ? { href: landingPage === "introduction" ? "/" : `/${landingPage}` } : {},
141
+ groups: groupsWithinSection(section, pageIds)
142
+ };
143
+ })
144
+ };
145
+ }
146
+ }
147
+ const buckets = /* @__PURE__ */ new Map();
148
+ for (const id of ordered) {
149
+ const segment = id.includes("/") ? id.split("/", 1)[0] : "overview";
150
+ const bucket = buckets.get(segment) ?? [];
151
+ bucket.push(id);
152
+ buckets.set(segment, bucket);
153
+ }
154
+ const groups = [...buckets].map(([segment, pageIds]) => ({
155
+ group: segment === "overview" ? "Overview" : titleCase(segment),
156
+ pages: pageIds
157
+ }));
158
+ return { tabs: [{ tab: "Documentation", groups }] };
159
+ }
160
+
161
+ // src/svg-policy.ts
162
+ import { Parser } from "htmlparser2";
163
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
164
+ var XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
165
+ var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
166
+ "svg",
167
+ "g",
168
+ "defs",
169
+ "symbol",
170
+ "use",
171
+ "title",
172
+ "desc",
173
+ "path",
174
+ "rect",
175
+ "circle",
176
+ "ellipse",
177
+ "line",
178
+ "polyline",
179
+ "polygon",
180
+ "text",
181
+ "tspan",
182
+ "textpath",
183
+ "lineargradient",
184
+ "radialgradient",
185
+ "stop",
186
+ "clippath",
187
+ "mask",
188
+ "pattern",
189
+ "marker",
190
+ "filter",
191
+ "feblend",
192
+ "fecolormatrix",
193
+ "fecomponenttransfer",
194
+ "fecomposite",
195
+ "feconvolvematrix",
196
+ "fediffuselighting",
197
+ "fedisplacementmap",
198
+ "fedistantlight",
199
+ "fedropshadow",
200
+ "feflood",
201
+ "fefunca",
202
+ "fefuncb",
203
+ "fefuncg",
204
+ "fefuncr",
205
+ "fegaussianblur",
206
+ "femerge",
207
+ "femergenode",
208
+ "femorphology",
209
+ "feoffset",
210
+ "fepointlight",
211
+ "fespecularlighting",
212
+ "fespotlight",
213
+ "fetile",
214
+ "feturbulence"
215
+ ]);
216
+ var ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
217
+ "xmlns",
218
+ "xmlns:xlink",
219
+ "xml:space",
220
+ "version",
221
+ "baseprofile",
222
+ "id",
223
+ "class",
224
+ "viewbox",
225
+ "preserveaspectratio",
226
+ "width",
227
+ "height",
228
+ "x",
229
+ "y",
230
+ "x1",
231
+ "y1",
232
+ "x2",
233
+ "y2",
234
+ "cx",
235
+ "cy",
236
+ "r",
237
+ "rx",
238
+ "ry",
239
+ "d",
240
+ "points",
241
+ "pathlength",
242
+ "transform",
243
+ "transform-origin",
244
+ "fill",
245
+ "fill-opacity",
246
+ "fill-rule",
247
+ "stroke",
248
+ "stroke-width",
249
+ "stroke-opacity",
250
+ "stroke-linecap",
251
+ "stroke-linejoin",
252
+ "stroke-miterlimit",
253
+ "stroke-dasharray",
254
+ "stroke-dashoffset",
255
+ "opacity",
256
+ "color",
257
+ "display",
258
+ "visibility",
259
+ "overflow",
260
+ "clip-path",
261
+ "clip-rule",
262
+ "clippathunits",
263
+ "mask",
264
+ "maskunits",
265
+ "maskcontentunits",
266
+ "filter",
267
+ "filterunits",
268
+ "primitiveunits",
269
+ "marker-start",
270
+ "marker-mid",
271
+ "marker-end",
272
+ "markerwidth",
273
+ "markerheight",
274
+ "markerunits",
275
+ "refx",
276
+ "refy",
277
+ "orient",
278
+ "patternunits",
279
+ "patterncontentunits",
280
+ "patterntransform",
281
+ "gradientunits",
282
+ "gradienttransform",
283
+ "spreadmethod",
284
+ "fx",
285
+ "fy",
286
+ "fr",
287
+ "offset",
288
+ "stop-color",
289
+ "stop-opacity",
290
+ "font-family",
291
+ "font-size",
292
+ "font-weight",
293
+ "font-style",
294
+ "font-variant",
295
+ "letter-spacing",
296
+ "word-spacing",
297
+ "text-anchor",
298
+ "dominant-baseline",
299
+ "alignment-baseline",
300
+ "baseline-shift",
301
+ "text-decoration",
302
+ "dx",
303
+ "dy",
304
+ "rotate",
305
+ "textlength",
306
+ "lengthadjust",
307
+ "startoffset",
308
+ "writing-mode",
309
+ "direction",
310
+ "unicode-bidi",
311
+ "shape-rendering",
312
+ "text-rendering",
313
+ "image-rendering",
314
+ "color-interpolation",
315
+ "color-interpolation-filters",
316
+ "vector-effect",
317
+ "paint-order",
318
+ "mix-blend-mode",
319
+ "isolation",
320
+ "in",
321
+ "in2",
322
+ "result",
323
+ "stddeviation",
324
+ "mode",
325
+ "type",
326
+ "values",
327
+ "tablevalues",
328
+ "slope",
329
+ "intercept",
330
+ "amplitude",
331
+ "exponent",
332
+ "operator",
333
+ "k1",
334
+ "k2",
335
+ "k3",
336
+ "k4",
337
+ "radius",
338
+ "scale",
339
+ "xchannelselector",
340
+ "ychannelselector",
341
+ "basefrequency",
342
+ "numoctaves",
343
+ "seed",
344
+ "stitchtiles",
345
+ "flood-color",
346
+ "flood-opacity",
347
+ "lighting-color",
348
+ "surfacescale",
349
+ "diffuseconstant",
350
+ "specularconstant",
351
+ "specularexponent",
352
+ "kernelmatrix",
353
+ "kernelunitlength",
354
+ "order",
355
+ "divisor",
356
+ "bias",
357
+ "targetx",
358
+ "targety",
359
+ "edgemode",
360
+ "preservealpha",
361
+ "azimuth",
362
+ "elevation",
363
+ "z",
364
+ "pointsatx",
365
+ "pointsaty",
366
+ "pointsatz",
367
+ "limitingconeangle",
368
+ "aria-hidden",
369
+ "aria-label",
370
+ "role",
371
+ "focusable",
372
+ // Validated below: only in-document fragment references.
373
+ "href",
374
+ "xlink:href"
375
+ ]);
376
+ function hasExternalReference(value) {
377
+ if (value.includes("\\")) return true;
378
+ const compact = value.replace(/[\u0000- ]/gu, "").toLowerCase();
379
+ if (/(?:java|vb)script:|data:|expression\(|@import|<|>/u.test(compact)) {
380
+ return true;
381
+ }
382
+ for (const match of compact.matchAll(/url\(([^)]*)\)/gu)) {
383
+ if (!/^["']?#[a-z0-9_.:-]+["']?$/u.test(match[1])) return true;
384
+ }
385
+ return compact.replace(/url\(([^)]*)\)/gu, "").includes("url(");
386
+ }
387
+ function utf8SvgText(content) {
388
+ const hasUtf8Bom = content[0] === 239 && content[1] === 187 && content[2] === 191;
389
+ const body = hasUtf8Bom ? content.subarray(3) : content;
390
+ if (body[0] === 254 && body[1] === 255 || body[0] === 255 && body[1] === 254 || body[0] === 0 && body[1] === 0 || body.includes(0)) {
391
+ return null;
392
+ }
393
+ const text = body.toString("utf8");
394
+ if (text.includes("\uFFFD")) return null;
395
+ const declaration = /^<\?xml\b[^>]*\?>/iu.exec(text);
396
+ if (declaration) {
397
+ const encoding = /\bencoding\s*=\s*["']([^"']*)["']/iu.exec(declaration[0]);
398
+ if (encoding && !/^utf-?8$/iu.test(encoding[1].trim())) return null;
399
+ } else if (/<\?xml\b/iu.test(text)) {
400
+ return null;
401
+ }
402
+ return text;
403
+ }
404
+ function isPassiveSvg(content) {
405
+ const text = utf8SvgText(Buffer.from(content.buffer, content.byteOffset, content.byteLength));
406
+ if (text === null) return false;
407
+ let isPassive = true;
408
+ let depth = 0;
409
+ let sawRoot = false;
410
+ const openStarts = [];
411
+ const fail = () => {
412
+ isPassive = false;
413
+ };
414
+ const parser3 = new Parser(
415
+ {
416
+ onopentag(name, attributes) {
417
+ const element = name.toLowerCase();
418
+ if (depth === 0) {
419
+ if (sawRoot || element !== "svg") fail();
420
+ sawRoot = true;
421
+ }
422
+ depth += 1;
423
+ openStarts.push(parser3.startIndex);
424
+ if (element.includes(":") || !ALLOWED_ELEMENTS.has(element)) fail();
425
+ for (const [rawName, value] of Object.entries(attributes)) {
426
+ const attribute = rawName.toLowerCase();
427
+ if (!ALLOWED_ATTRIBUTES.has(attribute)) {
428
+ fail();
429
+ continue;
430
+ }
431
+ if (attribute === "xmlns" && value !== SVG_NAMESPACE) fail();
432
+ if (attribute === "xmlns:xlink" && value !== XLINK_NAMESPACE) fail();
433
+ if ((attribute === "href" || attribute === "xlink:href") && !/^#[A-Za-z0-9_.:-]+$/u.test(value.trim())) {
434
+ fail();
435
+ }
436
+ if (hasExternalReference(value)) fail();
437
+ }
438
+ },
439
+ onclosetag(_name, isImplied) {
440
+ depth -= 1;
441
+ const start = openStarts.pop();
442
+ if (isImplied && start !== parser3.startIndex) fail();
443
+ },
444
+ // DOCTYPE (and with it custom entities) or any other declaration other
445
+ // than the XML prolog.
446
+ onprocessinginstruction(name) {
447
+ if (name.toLowerCase() !== "?xml") fail();
448
+ },
449
+ oncdatastart: fail,
450
+ ontext(value) {
451
+ if (depth === 0 && value.trim()) fail();
452
+ },
453
+ onerror: fail
454
+ },
455
+ { xmlMode: true, decodeEntities: true, recognizeSelfClosing: true }
456
+ );
457
+ try {
458
+ parser3.write(text);
459
+ parser3.end();
460
+ } catch {
461
+ return false;
462
+ }
463
+ return isPassive && sawRoot && depth === 0;
464
+ }
465
+
466
+ // src/url-asset-cache.ts
467
+ function createMigrationAssetCache() {
468
+ const settled = /* @__PURE__ */ new Map();
469
+ const pending = /* @__PURE__ */ new Map();
470
+ const remember = (url, asset) => {
471
+ settled.delete(url);
472
+ settled.set(url, asset);
473
+ if (settled.size > 480) settled.delete(settled.keys().next().value);
474
+ return asset;
475
+ };
476
+ const get = (url) => {
477
+ const asset = settled.get(url);
478
+ if (asset) return Promise.resolve(remember(url, asset));
479
+ return pending.get(url);
480
+ };
481
+ return {
482
+ remember,
483
+ get,
484
+ has(url) {
485
+ return settled.has(url) || pending.has(url);
486
+ },
487
+ get size() {
488
+ return settled.size + pending.size;
489
+ },
490
+ load(url, acquire) {
491
+ const existing = get(url);
492
+ if (existing) return existing;
493
+ const request = Promise.resolve().then(acquire).then((asset) => {
494
+ pending.delete(url);
495
+ return remember(url, asset);
496
+ }, (error) => {
497
+ pending.delete(url);
498
+ throw error;
499
+ });
500
+ pending.set(url, request);
501
+ return request;
502
+ },
503
+ delete(url) {
504
+ settled.delete(url);
505
+ }
506
+ };
507
+ }
508
+
509
+ // src/url-journal-clone.ts
510
+ function cloneUrlMigrationCheckpoint(checkpoint) {
511
+ const ancestors = /* @__PURE__ */ new Set();
512
+ const clone = (value) => {
513
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
514
+ if (typeof value === "number") return Number.isFinite(value) ? value === 0 ? 0 : value : null;
515
+ if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") return void 0;
516
+ if (typeof value !== "object") throw new TypeError("Checkpoint must contain JSON-safe values.");
517
+ if (ancestors.has(value)) throw new TypeError("Checkpoint must not contain circular references.");
518
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("Checkpoint must contain plain JSON containers.");
519
+ ancestors.add(value);
520
+ try {
521
+ if (Array.isArray(value)) return Array.from(value, (entry) => clone(entry) ?? null);
522
+ return Object.fromEntries(Object.entries(value).flatMap(([key, entry]) => {
523
+ const copied = clone(entry);
524
+ return copied === void 0 ? [] : [[key, copied]];
525
+ }));
526
+ } finally {
527
+ ancestors.delete(value);
528
+ }
529
+ };
530
+ return clone(checkpoint);
531
+ }
532
+
533
+ // src/url-images.ts
534
+ function migrationImageUrl(value, base) {
535
+ const url = new URL(value, base);
536
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error("Responsive image URL is not a credential-free HTTP(S) URL.");
537
+ return url.toString();
538
+ }
539
+ function migrationImageCandidates(value, base, resolve3 = true) {
540
+ if (value.length > 64e3) throw new Error("Responsive image candidate list exceeds its safety bound.");
541
+ const result = [];
542
+ let index = 0;
543
+ while (index < value.length) {
544
+ while (index < value.length && /[\t\n\f\r ,]/.test(value[index])) index++;
545
+ if (index === value.length) break;
546
+ const start = index;
547
+ while (index < value.length && !/[\t\n\f\r ]/.test(value[index])) index++;
548
+ let url = value.slice(start, index);
549
+ const hasTerminatingComma = url.endsWith(",");
550
+ if (hasTerminatingComma) url = url.replace(/,+$/, "");
551
+ const descriptorStart = index;
552
+ if (!hasTerminatingComma) while (index < value.length && value[index] !== ",") index++;
553
+ const descriptor = hasTerminatingComma ? "" : value.slice(descriptorStart, index).trim();
554
+ if (descriptor && !/^(?:[1-9]\d*w|(?:\d+(?:\.\d+)?|\.\d+)x)$/.test(descriptor)) throw new Error("Responsive image has an unsupported descriptor.");
555
+ if (descriptor.endsWith("x") && Number(descriptor.slice(0, -1)) <= 0) throw new Error("Responsive image density must be positive.");
556
+ const resolved = migrationImageUrl(url, base);
557
+ result.push({ url: resolve3 ? resolved : url, descriptor });
558
+ if (result.length > 128) throw new Error("Responsive image has too many candidates.");
559
+ if (value[index] === ",") index++;
560
+ }
561
+ if (!result.length) throw new Error("Responsive image has no candidates.");
562
+ return result;
563
+ }
564
+ function renderMigrationSrcset(candidates) {
565
+ return candidates.map((candidate) => `${candidate.url}${candidate.descriptor ? ` ${candidate.descriptor}` : ""}`).join(", ");
566
+ }
567
+ function migrationImageAttribute(value) {
568
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\{/g, "&#123;").replace(/\}/g, "&#125;").replace(/\r/g, "&#13;").replace(/\n/g, "&#10;");
569
+ }
570
+
571
+ // src/url.ts
572
+ import { load } from "cheerio";
573
+ import remarkMdx2 from "remark-mdx";
574
+ import remarkParse2 from "remark-parse";
575
+ import TurndownService from "turndown";
576
+ import { unified as unified2 } from "unified";
577
+ import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
578
+
579
+ // src/analytics.ts
580
+ var HOSTNAME_LABEL = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
581
+ var HOSTNAME = new RegExp(`^${HOSTNAME_LABEL}(?:\\.${HOSTNAME_LABEL})*$`);
582
+
583
+ // src/mdx.ts
584
+ import { posix } from "path";
585
+ import { compileSync } from "@mdx-js/mdx";
586
+ import { nameToEmoji } from "gemoji";
587
+ import remarkGfm from "remark-gfm";
588
+ import remarkMdx from "remark-mdx";
589
+ import remarkParse from "remark-parse";
590
+ import { unified } from "unified";
591
+
592
+ // src/builtin-components.ts
593
+ var THALLY_BUILTIN_COMPONENTS = /* @__PURE__ */ new Set([
594
+ "CodeGroup",
595
+ "Info",
596
+ "Warning",
597
+ "Check",
598
+ "Danger",
599
+ "Error",
600
+ "Note",
601
+ "Tip",
602
+ "Callout",
603
+ "AccordionGroup",
604
+ "Latex",
605
+ "Hero",
606
+ "Card",
607
+ "CardGroup",
608
+ "Columns",
609
+ "Frame",
610
+ "Accordion",
611
+ "Tooltip",
612
+ "Icon",
613
+ "Steps",
614
+ "Step",
615
+ "Tabs",
616
+ "Tab",
617
+ "Badge",
618
+ "Update",
619
+ "RequestExample",
620
+ "ResponseExample",
621
+ "Panel",
622
+ "ContentPanel",
623
+ "InlinePanel",
624
+ "InlineRequestExample",
625
+ "InlineResponseExample",
626
+ "Tile",
627
+ "TileGroup",
628
+ "Prompt",
629
+ "PromptUser",
630
+ "PromptAssistant",
631
+ "Terminal",
632
+ "TerminalInput",
633
+ "TerminalOutput",
634
+ "AgentPrompt",
635
+ "Color",
636
+ "Tree",
637
+ "Folder",
638
+ "File",
639
+ "ResponseField",
640
+ "ParamField",
641
+ "Expandable",
642
+ "Mermaid",
643
+ "View",
644
+ "Embed",
645
+ "LegacyView",
646
+ "GitHub",
647
+ "Github",
648
+ "Visibility",
649
+ "Human",
650
+ "Agent",
651
+ "BannerPreview",
652
+ "Banner",
653
+ "CodeBlock",
654
+ "Column",
655
+ "FileTree",
656
+ "LiteYouTubeEmbed",
657
+ "MDX",
658
+ "Video",
659
+ "YouTube"
660
+ ]);
661
+ function isThallyBuiltinComponent(name) {
662
+ return THALLY_BUILTIN_COMPONENTS.has(name) || THALLY_BUILTIN_COMPONENTS.has(name.split(".")[0]);
663
+ }
664
+
665
+ // src/inline-extraction.ts
666
+ import ts from "typescript";
667
+ var SERVER_ONLY_IDENTIFIERS = /* @__PURE__ */ new Set(["process", "require", "module", "exports", "__dirname", "__filename", "Buffer"]);
668
+ var BROWSER_IDENTIFIERS = /* @__PURE__ */ new Set([
669
+ "window",
670
+ "document",
671
+ "localStorage",
672
+ "sessionStorage",
673
+ "navigator",
674
+ "location",
675
+ "history",
676
+ "IntersectionObserver",
677
+ "ResizeObserver",
678
+ "MutationObserver",
679
+ "matchMedia",
680
+ "requestAnimationFrame",
681
+ "cancelAnimationFrame",
682
+ "alert",
683
+ "confirm",
684
+ "prompt",
685
+ "HTMLElement",
686
+ "Element",
687
+ "Node",
688
+ "Event",
689
+ "KeyboardEvent",
690
+ "MouseEvent",
691
+ "CustomEvent",
692
+ "FormData",
693
+ "File",
694
+ "FileReader",
695
+ "Image",
696
+ "Audio",
697
+ "XMLHttpRequest",
698
+ "WebSocket",
699
+ "getComputedStyle",
700
+ "innerWidth",
701
+ "innerHeight",
702
+ "scrollTo"
703
+ ]);
704
+ var JS_GLOBALS = /* @__PURE__ */ new Set([
705
+ "undefined",
706
+ "NaN",
707
+ "Infinity",
708
+ "globalThis",
709
+ "arguments",
710
+ "Object",
711
+ "Function",
712
+ "Array",
713
+ "Number",
714
+ "Boolean",
715
+ "String",
716
+ "Symbol",
717
+ "BigInt",
718
+ "Date",
719
+ "RegExp",
720
+ "Error",
721
+ "AggregateError",
722
+ "EvalError",
723
+ "RangeError",
724
+ "ReferenceError",
725
+ "SyntaxError",
726
+ "TypeError",
727
+ "URIError",
728
+ "JSON",
729
+ "Math",
730
+ "Intl",
731
+ "Reflect",
732
+ "Proxy",
733
+ "Promise",
734
+ "Map",
735
+ "Set",
736
+ "WeakMap",
737
+ "WeakSet",
738
+ "WeakRef",
739
+ "ArrayBuffer",
740
+ "SharedArrayBuffer",
741
+ "DataView",
742
+ "Atomics",
743
+ "Int8Array",
744
+ "Uint8Array",
745
+ "Uint8ClampedArray",
746
+ "Int16Array",
747
+ "Uint16Array",
748
+ "Int32Array",
749
+ "Uint32Array",
750
+ "Float32Array",
751
+ "Float64Array",
752
+ "BigInt64Array",
753
+ "BigUint64Array",
754
+ "parseInt",
755
+ "parseFloat",
756
+ "isNaN",
757
+ "isFinite",
758
+ "encodeURI",
759
+ "encodeURIComponent",
760
+ "decodeURI",
761
+ "decodeURIComponent",
762
+ "console",
763
+ "fetch",
764
+ "URL",
765
+ "URLSearchParams",
766
+ "Headers",
767
+ "Request",
768
+ "Response",
769
+ "AbortController",
770
+ "AbortSignal",
771
+ "TextEncoder",
772
+ "TextDecoder",
773
+ "atob",
774
+ "btoa",
775
+ "structuredClone",
776
+ "queueMicrotask",
777
+ "performance",
778
+ "setTimeout",
779
+ "clearTimeout",
780
+ "setInterval",
781
+ "clearInterval",
782
+ "crypto",
783
+ "Blob",
784
+ "EventTarget",
785
+ "escape",
786
+ "unescape",
787
+ "eval",
788
+ "WebAssembly",
789
+ "Iterator",
790
+ "self",
791
+ "screen",
792
+ "FinalizationRegistry",
793
+ "DOMException",
794
+ "ReadableStream",
795
+ "WritableStream",
796
+ "TransformStream",
797
+ "MessageChannel",
798
+ "BroadcastChannel"
799
+ ]);
800
+ function parse(source) {
801
+ return ts.createSourceFile("inline.tsx", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
802
+ }
803
+ function bindingNames(name, into) {
804
+ if (ts.isIdentifier(name)) into.add(name.text);
805
+ else for (const element of name.elements) if (ts.isBindingElement(element)) bindingNames(element.name, into);
806
+ }
807
+ function isFunctionLike(node) {
808
+ return ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
809
+ }
810
+ function functionScope(fn) {
811
+ const scope = /* @__PURE__ */ new Set();
812
+ if ((ts.isFunctionExpression(fn) || ts.isFunctionDeclaration(fn)) && fn.name) scope.add(fn.name.text);
813
+ for (const parameter of fn.parameters) bindingNames(parameter.name, scope);
814
+ function collect(node) {
815
+ if (ts.isVariableDeclaration(node)) bindingNames(node.name, scope);
816
+ if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name) scope.add(node.name.text);
817
+ if (isFunctionLike(node)) return;
818
+ ts.forEachChild(node, collect);
819
+ }
820
+ if (fn.body) ts.forEachChild(fn.body, collect);
821
+ if (fn.body && !ts.isBlock(fn.body)) collect(fn.body);
822
+ return scope;
823
+ }
824
+ function collectRefs(root) {
825
+ const refs = [];
826
+ const assigned = [];
827
+ let awaitOutsideAsync = false;
828
+ let dynamicImport = false;
829
+ function bound(name, scopes) {
830
+ return scopes.some((scope) => scope.has(name));
831
+ }
832
+ function push(name, kind, scopes) {
833
+ if (!bound(name, scopes)) refs.push({ name, kind });
834
+ }
835
+ function walkNode(node, scopes, inAsync) {
836
+ if (ts.isTypeNode(node)) return;
837
+ if (isFunctionLike(node)) {
838
+ const nextScopes = [...scopes, functionScope(node)];
839
+ const async = !!node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword);
840
+ for (const parameter of node.parameters) {
841
+ if (!ts.isIdentifier(parameter.name)) walkNode(parameter.name, nextScopes, async);
842
+ if (parameter.initializer) walkNode(parameter.initializer, nextScopes, async);
843
+ }
844
+ if (node.body) walkNode(node.body, nextScopes, async);
845
+ return;
846
+ }
847
+ if (ts.isAwaitExpression(node) && !inAsync) awaitOutsideAsync = true;
848
+ const target = ts.isBinaryExpression(node) && node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && node.operatorToken.kind <= ts.SyntaxKind.LastAssignment ? node.left : (ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) && (node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken) ? node.operand : void 0;
849
+ if (target) {
850
+ let base = target;
851
+ while (ts.isPropertyAccessExpression(base) || ts.isElementAccessExpression(base)) base = base.expression;
852
+ if (ts.isIdentifier(base) && !bound(base.text, scopes)) assigned.push(base.text);
853
+ }
854
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) dynamicImport = true;
855
+ if (ts.isIdentifier(node)) {
856
+ const parent = node.parent;
857
+ let operandOf = parent;
858
+ while (ts.isParenthesizedExpression(operandOf)) operandOf = operandOf.parent;
859
+ const kind = ts.isTypeOfExpression(operandOf) ? "typeof" : ts.isJsxExpression(parent) && parent.parent && ts.isJsxAttribute(parent.parent) ? "prop" : "other";
860
+ push(node.text, kind, scopes);
861
+ return;
862
+ }
863
+ if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
864
+ let tag = node.tagName;
865
+ while (ts.isPropertyAccessExpression(tag)) tag = tag.expression;
866
+ if (ts.isIdentifier(tag)) {
867
+ if (node.tagName !== tag || /^[A-Z]/.test(tag.text)) push(tag.text, node.tagName === tag ? "tag" : "member", scopes);
868
+ }
869
+ walkNode(node.attributes, scopes, inAsync);
870
+ return;
871
+ }
872
+ if (ts.isJsxClosingElement(node)) return;
873
+ if (ts.isJsxAttribute(node)) {
874
+ if (node.initializer) walkNode(node.initializer, scopes, inAsync);
875
+ return;
876
+ }
877
+ if (ts.isPropertyAccessExpression(node)) {
878
+ walkNode(node.expression, scopes, inAsync);
879
+ return;
880
+ }
881
+ if (ts.isQualifiedName(node)) return;
882
+ if (ts.isPropertyAssignment(node)) {
883
+ if (ts.isComputedPropertyName(node.name)) walkNode(node.name.expression, scopes, inAsync);
884
+ walkNode(node.initializer, scopes, inAsync);
885
+ return;
886
+ }
887
+ if (ts.isShorthandPropertyAssignment(node)) {
888
+ push(node.name.text, "other", scopes);
889
+ if (node.objectAssignmentInitializer) walkNode(node.objectAssignmentInitializer, scopes, inAsync);
890
+ return;
891
+ }
892
+ if (ts.isBindingElement(node)) {
893
+ if (node.propertyName && ts.isComputedPropertyName(node.propertyName)) walkNode(node.propertyName.expression, scopes, inAsync);
894
+ if (node.initializer) walkNode(node.initializer, scopes, inAsync);
895
+ if (!ts.isIdentifier(node.name)) walkNode(node.name, scopes, inAsync);
896
+ return;
897
+ }
898
+ if (ts.isVariableDeclaration(node)) {
899
+ if (!ts.isIdentifier(node.name)) walkNode(node.name, scopes, inAsync);
900
+ if (node.initializer) walkNode(node.initializer, scopes, inAsync);
901
+ return;
902
+ }
903
+ if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
904
+ const inner = ts.isClassExpression(node) && node.name ? [...scopes, /* @__PURE__ */ new Set([node.name.text])] : scopes;
905
+ node.members.forEach((member) => walkNode(member, inner, inAsync));
906
+ for (const clause of node.heritageClauses ?? []) for (const type of clause.types) walkNode(type.expression, scopes, inAsync);
907
+ return;
908
+ }
909
+ if (ts.isMetaProperty(node)) return;
910
+ if (ts.isPropertyDeclaration(node)) {
911
+ if (node.initializer) walkNode(node.initializer, scopes, inAsync);
912
+ return;
913
+ }
914
+ if (ts.isLabeledStatement(node)) {
915
+ walkNode(node.statement, scopes, inAsync);
916
+ return;
917
+ }
918
+ if (ts.isBreakOrContinueStatement(node)) return;
919
+ ts.forEachChild(node, (child) => walkNode(child, scopes, inAsync));
920
+ }
921
+ walkNode(root, [], false);
922
+ return { refs, assigned, awaitOutsideAsync, dynamicImport };
923
+ }
924
+ function declaredNames(statement) {
925
+ const isDefault = !!(ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : void 0)?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) || ts.isExportAssignment(statement);
926
+ const names = /* @__PURE__ */ new Set();
927
+ let componentLike = false;
928
+ if (ts.isVariableStatement(statement)) {
929
+ for (const declaration of statement.declarationList.declarations) {
930
+ bindingNames(declaration.name, names);
931
+ const initializer = declaration.initializer && unwrap(declaration.initializer);
932
+ if (ts.isIdentifier(declaration.name) && /^[A-Z]/.test(declaration.name.text) && initializer && (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer) || ts.isClassExpression(initializer))) {
933
+ componentLike = true;
934
+ }
935
+ }
936
+ } else if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name) {
937
+ names.add(statement.name.text);
938
+ componentLike = /^[A-Z]/.test(statement.name.text);
939
+ }
940
+ return { names: [...names], componentLike, defaultExport: isDefault };
941
+ }
942
+ function unwrap(expression) {
943
+ let current = expression;
944
+ while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current)) current = current.expression;
945
+ return current;
946
+ }
947
+ function importLocals(statementText) {
948
+ const sf = parse(statementText);
949
+ const locals = [];
950
+ for (const statement of sf.statements) {
951
+ if (!ts.isImportDeclaration(statement) || !statement.importClause) continue;
952
+ const clause = statement.importClause;
953
+ if (clause.name) locals.push(clause.name.text);
954
+ const named = clause.namedBindings;
955
+ if (named && ts.isNamespaceImport(named)) locals.push(named.name.text);
956
+ if (named && ts.isNamedImports(named)) for (const element of named.elements) locals.push(element.name.text);
957
+ }
958
+ return locals;
959
+ }
960
+ function expressionRefs(expression) {
961
+ const sf = parse(`const __expression = (${expression})`);
962
+ const statement = sf.statements[0];
963
+ return statement ? collectRefs(statement).refs : [];
964
+ }
965
+ function pageScopeNames(esmSources) {
966
+ const names = /* @__PURE__ */ new Set();
967
+ for (const source of esmSources) {
968
+ for (const statement of parse(source).statements) {
969
+ for (const local of importLocals(statement.getText())) names.add(local);
970
+ if (ts.isImportDeclaration(statement)) continue;
971
+ const target = ts.isExportDeclaration(statement) || ts.isExportAssignment(statement) ? void 0 : statement;
972
+ if (target) for (const name of declaredNames(target).names) names.add(name);
973
+ }
974
+ }
975
+ return names;
976
+ }
977
+ function unresolvedExpressionNames(expression, pageNames) {
978
+ const names = /* @__PURE__ */ new Set();
979
+ for (const { name, kind } of expressionRefs(expression)) {
980
+ if (kind === "typeof") continue;
981
+ if (!name || pageNames.has(name) || JS_GLOBALS.has(name) || BROWSER_IDENTIFIERS.has(name) || SERVER_ONLY_IDENTIFIERS.has(name) || name === "props" || name === "React") continue;
982
+ if ((kind === "tag" || kind === "member") && isThallyBuiltinComponent(name)) continue;
983
+ names.add(name);
984
+ }
985
+ return [...names];
986
+ }
987
+
988
+ // src/frontmatter.ts
989
+ import { parse as parseYaml } from "yaml";
990
+ function salvageFrontmatterLines(matter) {
991
+ const data = {};
992
+ for (const line of matter.split(/\r?\n/)) {
993
+ if (!/^[A-Za-z_][\w-]*:\s?/.test(line)) continue;
994
+ try {
995
+ const parsedLine = parseYaml(line);
996
+ if (parsedLine && typeof parsedLine === "object" && !Array.isArray(parsedLine)) {
997
+ Object.assign(data, parsedLine);
998
+ }
999
+ } catch {
1000
+ }
1001
+ }
1002
+ return data;
1003
+ }
1004
+ function parseFrontmatter(raw) {
1005
+ const source = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
1006
+ const opening = /^---([^\r\n]*)\r?\n/.exec(source);
1007
+ if (!opening || opening[1].startsWith("-")) return { content: source, data: {} };
1008
+ const language = opening[1].trim().toLowerCase();
1009
+ const remainder = source.slice(opening[0].length);
1010
+ const closing = /^---[ \t]*\r?$/m.exec(remainder);
1011
+ const matter = closing ? remainder.slice(0, closing.index) : remainder;
1012
+ let content = closing ? remainder.slice(closing.index + closing[0].length) : "";
1013
+ if (content.startsWith("\r\n")) content = content.slice(2);
1014
+ else if (content.startsWith("\n")) content = content.slice(1);
1015
+ if (matter.trim() === "" || !["", "yaml", "yml"].includes(language)) {
1016
+ return { content, data: {} };
1017
+ }
1018
+ try {
1019
+ const parsed = parseYaml(matter);
1020
+ return {
1021
+ content,
1022
+ data: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}
1023
+ };
1024
+ } catch (err) {
1025
+ return {
1026
+ content,
1027
+ data: salvageFrontmatterLines(matter),
1028
+ error: err instanceof Error ? err.message : String(err)
1029
+ };
1030
+ }
1031
+ }
1032
+
1033
+ // src/mdx.ts
1034
+ function titleFromId(id) {
1035
+ const value = id.split("/").at(-1) ?? id;
1036
+ if (value === "introduction") return "Introduction";
1037
+ return value.split(/[-_]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1038
+ }
1039
+ var descriptionParser = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
1040
+ function plainText(node) {
1041
+ if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
1042
+ if (node.type === "image") return node.alt ?? "";
1043
+ if (node.type === "break") return " ";
1044
+ return (node.children ?? []).map(plainText).join("");
1045
+ }
1046
+ function firstParagraph(content) {
1047
+ function findParagraph(node) {
1048
+ if (node.type === "paragraph") {
1049
+ const value = plainText(node).replace(/\s+/g, " ").trim();
1050
+ if (value) return value.slice(0, 240);
1051
+ }
1052
+ for (const child of node.children ?? []) {
1053
+ const value = findParagraph(child);
1054
+ if (value) return value;
1055
+ }
1056
+ return "";
1057
+ }
1058
+ try {
1059
+ return findParagraph(descriptionParser.parse(content));
1060
+ } catch {
1061
+ return "";
1062
+ }
1063
+ }
1064
+ function docusaurusAdmonitionTag(kind) {
1065
+ if (kind === "danger") return "Error";
1066
+ if (kind === "info") return "Info";
1067
+ if (kind === "caution" || kind === "warning") return "Warning";
1068
+ return "Note";
1069
+ }
1070
+ function scanJsxOpeningTag(source, start, name) {
1071
+ const opener = `<${name}`;
1072
+ let quote = null;
1073
+ let depth = 0;
1074
+ let braceQuote = null;
1075
+ let lineComment = false;
1076
+ let blockComment = false;
1077
+ const attributesStart = start + opener.length;
1078
+ for (let index = attributesStart; index < source.length; index++) {
1079
+ const char = source[index];
1080
+ if (char === "<" && !quote && !braceQuote && depth === 0 && !lineComment && !blockComment && source.startsWith(opener, index) && !/[\w$.-]/.test(source[index + opener.length] ?? "")) {
1081
+ return { attributes: source.slice(attributesStart, index), end: null, next: index };
1082
+ }
1083
+ if (lineComment) {
1084
+ if (char === "\n" || char === "\r") lineComment = false;
1085
+ } else if (blockComment) {
1086
+ if (char === "*" && source[index + 1] === "/") {
1087
+ blockComment = false;
1088
+ index++;
1089
+ }
1090
+ } else if (braceQuote) {
1091
+ if (char === "\\") index++;
1092
+ else if (char === braceQuote) braceQuote = null;
1093
+ } else if (depth > 0) {
1094
+ if (char === "/" && source[index + 1] === "/") {
1095
+ lineComment = true;
1096
+ index++;
1097
+ } else if (char === "/" && source[index + 1] === "*") {
1098
+ blockComment = true;
1099
+ index++;
1100
+ } else if (char === '"' || char === "'" || char === "`") braceQuote = char;
1101
+ else if (char === "{") depth++;
1102
+ else if (char === "}") depth--;
1103
+ } else if (quote) {
1104
+ if (char === quote) quote = null;
1105
+ } else if (char === '"' || char === "'") quote = char;
1106
+ else if (char === "{") depth++;
1107
+ else if (char === ">") return { attributes: source.slice(attributesStart, index), end: index, next: index + 1 };
1108
+ }
1109
+ return { attributes: source.slice(attributesStart), end: null, next: source.length };
1110
+ }
1111
+ function normalizeDocusaurusAdmonitionTags(segment) {
1112
+ const open = [];
1113
+ const marker = /<\/Admonition>|<Admonition(?![\w$.-])/g;
1114
+ let output = "";
1115
+ let cursor = 0;
1116
+ for (let match = marker.exec(segment); match; match = marker.exec(segment)) {
1117
+ if (match[0] === "</Admonition>") {
1118
+ output += `${segment.slice(cursor, match.index)}
1119
+ </${open.pop() ?? "Note"}>`;
1120
+ cursor = marker.lastIndex;
1121
+ continue;
1122
+ }
1123
+ const tag = scanJsxOpeningTag(segment, match.index, "Admonition");
1124
+ marker.lastIndex = tag.next;
1125
+ if (tag.end === null) continue;
1126
+ const type = tag.attributes.match(/\btype=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean)?.toLowerCase() ?? "note";
1127
+ const title = tag.attributes.match(/\btitle=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean)?.trim();
1128
+ const callout = docusaurusAdmonitionTag(type);
1129
+ open.push(callout);
1130
+ output += `${segment.slice(cursor, match.index)}${title ? `<${callout}>
1131
+ **${title}**
1132
+ ` : `<${callout}>
1133
+ `}`;
1134
+ cursor = tag.end + 1;
1135
+ marker.lastIndex = cursor;
1136
+ }
1137
+ return output + segment.slice(cursor);
1138
+ }
1139
+ function normalizeDocusaurusLinkTags(segment) {
1140
+ const marker = /<Link(?![\w$.-])/g;
1141
+ let output = "";
1142
+ let cursor = 0;
1143
+ for (let match = marker.exec(segment); match; match = marker.exec(segment)) {
1144
+ const tag = scanJsxOpeningTag(segment, match.index, "Link");
1145
+ marker.lastIndex = tag.next;
1146
+ if (tag.end === null) continue;
1147
+ output += `${segment.slice(cursor, match.index)}<a${tag.attributes.replace(/(^|\s)to=/, "$1href=")}>`;
1148
+ cursor = tag.end + 1;
1149
+ marker.lastIndex = cursor;
1150
+ }
1151
+ return (output + segment.slice(cursor)).replace(/<\/Link>/g, "</a>");
1152
+ }
1153
+ function normalizeDocusaurusThemedImages(segment) {
1154
+ return segment.replace(/<ThemedImage\b([^<]*?)\/>/g, (original, attributes) => {
1155
+ const light = attributes.match(/\blight\s*:\s*(?:useBaseUrl\(\s*)?(?:"([^"]+)"|'([^']+)')/)?.slice(1).find(Boolean);
1156
+ if (!light) return original;
1157
+ const alt = attributes.match(/\balt=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find((value) => value !== void 0);
1158
+ return `<img src="${light.replace(/"/g, "&quot;")}"${alt === void 0 ? "" : ` alt="${alt.replace(/"/g, "&quot;")}"`} />`;
1159
+ });
1160
+ }
1161
+ function normalizeDocusaurusAdmonitions(body) {
1162
+ const lines = body.split("\n");
1163
+ const openAdmonitions = [];
1164
+ const headingLines = [];
1165
+ let codeFence = null;
1166
+ const output = lines.map((line, index) => {
1167
+ const codeMatch = line.match(/^\s{0,3}(`{3,}|~{3,})/);
1168
+ if (codeMatch) {
1169
+ if (!codeFence) codeFence = codeMatch[1];
1170
+ else if (codeMatch[1][0] === codeFence[0] && codeMatch[1].length >= codeFence.length && /^\s*$/.test(line.slice(codeMatch[0].length))) codeFence = null;
1171
+ return line;
1172
+ }
1173
+ if (codeFence) return line;
1174
+ if (/^#{1,6}\s/.test(line)) headingLines.push(index);
1175
+ const opening = line.match(/^\s*(:{3,})(note|tip|info|warning|caution|danger)(?:\[([^\]]+)\]|\s+(.+))?\s*$/i);
1176
+ if (opening) {
1177
+ const tag = docusaurusAdmonitionTag(opening[2].toLowerCase());
1178
+ openAdmonitions.push({ delimiter: opening[1], tag, openedAt: index });
1179
+ const title = (opening[3] ?? opening[4])?.trim();
1180
+ return title ? `<${tag}>
1181
+ **${title}**` : `<${tag}>`;
1182
+ }
1183
+ const closing = line.match(/^\s*(:{3,})\s*$/);
1184
+ const current = openAdmonitions.at(-1);
1185
+ if (closing && current?.delimiter === closing[1]) {
1186
+ openAdmonitions.pop();
1187
+ return `</${current.tag}>`;
1188
+ }
1189
+ return line;
1190
+ });
1191
+ const closingByLine = /* @__PURE__ */ new Map();
1192
+ for (const open of openAdmonitions.reverse()) {
1193
+ const at = headingLines.find((line) => line > open.openedAt) ?? output.length;
1194
+ closingByLine.set(at, [...closingByLine.get(at) ?? [], `</${open.tag}>`]);
1195
+ }
1196
+ for (const [at, closing] of [...closingByLine].sort(([left], [right]) => right - left)) output.splice(at, 0, ...closing);
1197
+ return output.join("\n");
1198
+ }
1199
+ function unwrapDocusaurusMdxCodeBlocks(body) {
1200
+ const output = [];
1201
+ let fence;
1202
+ let isMdxCodeBlock = false;
1203
+ for (const line of body.split("\n")) {
1204
+ const opening = !fence && line.match(/^ {0,3}(`{3,}|~{3,})mdx-code-block\s*$/);
1205
+ if (opening) {
1206
+ fence = opening[1];
1207
+ isMdxCodeBlock = true;
1208
+ continue;
1209
+ }
1210
+ if (fence) {
1211
+ const closing = line.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
1212
+ if (closing && closing[1][0] === fence[0] && closing[1].length >= fence.length) {
1213
+ if (!isMdxCodeBlock) output.push(line);
1214
+ fence = void 0;
1215
+ isMdxCodeBlock = false;
1216
+ continue;
1217
+ }
1218
+ if (isMdxCodeBlock && /^\s*(?:import|export)\b/.test(line)) return body;
1219
+ output.push(line);
1220
+ continue;
1221
+ }
1222
+ const ordinaryFence = line.match(/^ {0,3}(`{3,}|~{3,})/);
1223
+ if (ordinaryFence) {
1224
+ fence = ordinaryFence[1];
1225
+ isMdxCodeBlock = false;
1226
+ }
1227
+ output.push(line);
1228
+ }
1229
+ return isMdxCodeBlock ? body : output.join("\n");
1230
+ }
1231
+ var HTML_ID = /^[^\s"'<>`{}&]+$/u;
1232
+ function normalizeExplicitHeadingIds(raw, warn, options = {}) {
1233
+ const { front, body } = splitFrontmatterBlock(raw);
1234
+ return front + replaceOutsideCode(body, (segment) => segment.split("\n").map((line) => {
1235
+ if (!/^[ \t]{0,3}#{1,6}[ \t]+/.test(line)) return line;
1236
+ const trimmed = line.trimEnd();
1237
+ if (trimmed.endsWith("}")) {
1238
+ const marker = trimmed.lastIndexOf(" {#");
1239
+ if (marker >= 0) {
1240
+ const id = trimmed.slice(marker + 3, -1);
1241
+ const heading = trimmed.slice(0, marker).trimEnd();
1242
+ if (/[{}]/.test(id)) return line;
1243
+ const usable = options.headingMarkers ? id && !/\s/.test(id) && !id.includes("*/") : HTML_ID.test(id);
1244
+ if (usable && options.headingMarkers) return `${heading} ${headingIdMarker(id)}`;
1245
+ if (usable) return `<a id="${id}"></a>
1246
+ ${heading}`;
1247
+ warn?.(`Heading anchor {#${id}} ${options.headingMarkers ? 'contains whitespace or "*/"' : "is not a valid HTML id"} and was removed from "${heading.replace(/^\s*#+\s*/, "")}".`);
1248
+ return heading;
1249
+ }
1250
+ }
1251
+ if (!options.headingMarkers && !options.keepIdComments && trimmed.endsWith("*/}")) {
1252
+ const marker = trimmed.lastIndexOf(" {/*");
1253
+ if (marker >= 0) {
1254
+ const comment = trimmed.slice(marker + 4, -3).trim();
1255
+ const id = comment.startsWith("#") ? comment.slice(1) : "";
1256
+ if (HTML_ID.test(id)) {
1257
+ return `<a id="${id}"></a>
1258
+ ${trimmed.slice(0, marker).trimEnd()}`;
1259
+ }
1260
+ }
1261
+ }
1262
+ return line;
1263
+ }).join("\n"));
1264
+ }
1265
+ function headingIdMarker(id) {
1266
+ return `{/* #${id} */}`;
1267
+ }
1268
+ function mintlifyHeadingSlug(text) {
1269
+ return text.replace(/\u200b/g, "").normalize("NFC").trim().toLowerCase().replace(/[.\s]+/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "").replace(/[^\p{L}\p{M}\p{N}_\-+&/\u2019\u201c\u201d\u2014\u2013\u2192]/gu, "");
1270
+ }
1271
+ function thallyHeadingSlug(text) {
1272
+ return text.normalize("NFC").toLowerCase().replace(/[^\p{L}\p{M}\p{N}]+/gu, "-").replace(/(^-|-$)/g, "");
1273
+ }
1274
+ function typesetQuotes(text) {
1275
+ return text.split(/(`[^`]*`|<[^>]*>|\]\([^)]*\))/).map((part, index) => {
1276
+ if (index % 2) return part;
1277
+ return part.replace(/(^|[\s([{\u2014\u2013])"/g, "$1\u201C").replace(/"/g, "\u201D").replace(/(^|[\s([{\u2014\u2013\u201c])'(?=\S)/g, "$1\u2018").replace(/'/g, "\u2019");
1278
+ }).join("");
1279
+ }
1280
+ function plainHeadingText(source) {
1281
+ return source.replace(/\{\/\*[\s\S]*?\*\/\}/g, " ").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<\/?[A-Za-z][^>]*>/g, "").replace(/`([^`]*)`/g, "$1").replace(/(\*\*|__|~~|\*)/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/\\([\\`*_{}[\]()#+.!&<>|~-])/g, "$1");
1282
+ }
1283
+ function markMintlifyHeadings(body) {
1284
+ let fence;
1285
+ return body.split("\n").map((line) => {
1286
+ const marker = line.match(/^ {0,3}(`{3,}|~{3,})/);
1287
+ if (marker) {
1288
+ if (!fence) fence = marker[1];
1289
+ else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && /^\s*$/.test(line.slice(marker[0].length))) fence = void 0;
1290
+ return line;
1291
+ }
1292
+ if (fence) return line;
1293
+ const heading = line.match(/^( {0,3}#{2,6}[ \t]+)(.+?)(?:[ \t]+#+)?[ \t]*$/);
1294
+ if (!heading) return line;
1295
+ const explicit = heading[2].match(/^(.*?)([ \t]*\{\/\*[ \t]*#\S+[ \t]*\*\/\})$/);
1296
+ const source = typesetQuotes(explicit ? explicit[1] : heading[2]);
1297
+ if (explicit) return `${heading[1]}${source}${explicit[2]}`;
1298
+ const text = plainHeadingText(source);
1299
+ const id = mintlifyHeadingSlug(text);
1300
+ return `${heading[1]}${source}${id && id !== thallyHeadingSlug(text) ? ` ${headingIdMarker(id)}` : ""}`;
1301
+ }).join("\n");
1302
+ }
1303
+ var GLOBAL_DOCUSARUS_COMPONENTS = /* @__PURE__ */ new Set([
1304
+ "Tabs",
1305
+ "TabItem",
1306
+ "Link",
1307
+ "DocCardList",
1308
+ "TOCInline"
1309
+ ]);
1310
+ function removeGlobalDocusaurusImports(body) {
1311
+ return replaceOutsideCode(body, (segment) => segment.replace(
1312
+ /\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])(@(?:theme|docusaurus)\/[^'"]+)\2\s*;?/g,
1313
+ (statement, component) => GLOBAL_DOCUSARUS_COMPONENTS.has(component) ? "" : statement
1314
+ ));
1315
+ }
1316
+ function leadingPageHeading(body) {
1317
+ if (!/^ {0,3}(?:#(?:[ \t]|$)|=+[ \t\r]*$)/m.test(body)) return void 0;
1318
+ try {
1319
+ const root = descriptionParser.parse(body);
1320
+ const first = root.children?.find((node) => node.type !== "mdxjsEsm" && node.type !== "html" && !(node.type === "mdxFlowExpression" && node.data?.estree?.body.length === 0));
1321
+ if (first?.type !== "heading" || first.depth !== 1) return void 0;
1322
+ const text = (node) => node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" ? "" : node.value ?? (node.children ?? []).map(text).join("");
1323
+ const title = text(first).replace(/\s+/g, " ").trim();
1324
+ const start = first.position?.start.offset;
1325
+ const end = first.position?.end.offset;
1326
+ return title && start !== void 0 && end !== void 0 ? { title, start, end } : void 0;
1327
+ } catch {
1328
+ return void 0;
1329
+ }
1330
+ }
1331
+ function resolvePageDisplayHeading(page, platform) {
1332
+ const heading = leadingPageHeading(page.body);
1333
+ if (!heading || heading.title !== page.title && platform !== "docusaurus") return;
1334
+ if (heading.title !== page.title) page.headingTitle = heading.title;
1335
+ page.body = `${page.body.slice(0, heading.start)}${page.body.slice(heading.end)}`.trim();
1336
+ }
1337
+ function splitFrontmatterBlock(raw) {
1338
+ const hasBom = raw.charCodeAt(0) === 65279;
1339
+ const bom = hasBom ? raw[0] : "";
1340
+ const source = hasBom ? raw.slice(1) : raw;
1341
+ const opening = /^---([^\r\n]*)\r?\n/.exec(source);
1342
+ if (!opening || opening[1].startsWith("-")) return { front: bom, body: source };
1343
+ const remainder = source.slice(opening[0].length);
1344
+ const closing = /^---[ \t]*\r?$/m.exec(remainder);
1345
+ if (!closing) return { front: bom, body: source };
1346
+ const end = opening[0].length + closing.index + closing[0].length;
1347
+ return { front: bom + source.slice(0, end), body: source.slice(end) };
1348
+ }
1349
+ function maskCode(body) {
1350
+ const source = body.replace(/\u0000/g, "");
1351
+ const blocks = [];
1352
+ const stash = (text) => {
1353
+ blocks.push(text);
1354
+ return `\0${blocks.length - 1}\0`;
1355
+ };
1356
+ const lines = [];
1357
+ let codeFence = null;
1358
+ let fenceBuffer = [];
1359
+ for (const line of source.split("\n")) {
1360
+ const fenceMatch = line.match(/^\s{0,3}(`{3,}|~{3,})/);
1361
+ if (codeFence) {
1362
+ fenceBuffer.push(line);
1363
+ if (fenceMatch && fenceMatch[1][0] === codeFence[0] && fenceMatch[1].length >= codeFence.length && /^\s*$/.test(line.slice(fenceMatch[0].length))) {
1364
+ lines.push(stash(fenceBuffer.join("\n")));
1365
+ codeFence = null;
1366
+ fenceBuffer = [];
1367
+ }
1368
+ continue;
1369
+ }
1370
+ if (fenceMatch) {
1371
+ codeFence = fenceMatch[1];
1372
+ fenceBuffer = [line];
1373
+ continue;
1374
+ }
1375
+ lines.push(line.split(/(`[^`]*`)/).map((segment, index) => index % 2 === 1 ? stash(segment) : segment).join(""));
1376
+ }
1377
+ if (fenceBuffer.length) lines.push(stash(fenceBuffer.join("\n")));
1378
+ return {
1379
+ masked: lines.join("\n"),
1380
+ unmask: (text) => text.replace(/\u0000(\d+)\u0000/g, (_match, index) => blocks[Number(index)]),
1381
+ openFence: codeFence
1382
+ };
1383
+ }
1384
+ function replaceOutsideCode(body, transform) {
1385
+ const { masked, unmask } = maskCode(body);
1386
+ return unmask(transform(masked));
1387
+ }
1388
+ function mdxComment(text) {
1389
+ return `{/*${text.replace(/\*\//g, "* /")}*/}`;
1390
+ }
1391
+ function normalizeFernFileTrees(body) {
1392
+ return replaceOutsideCode(body, (whole) => whole.replace(
1393
+ /<FileTree\s*>\s*\n([\s\S]*?)\n\s*<\/FileTree>/g,
1394
+ (original, contents) => {
1395
+ const lines = contents.split(/\r?\n/).filter((line) => line.trim());
1396
+ if (!lines.length) return original;
1397
+ const nodes = [];
1398
+ for (const line of lines) {
1399
+ const match = /^( *)(?:-|\*|\+)\s+(.+?)\s*$/.exec(line);
1400
+ if (!match || match[1].length % 2 !== 0 || match[2].includes("\0")) return original;
1401
+ nodes.push({ indent: match[1].length / 2, name: match[2], isFolder: match[2].endsWith("/") });
1402
+ }
1403
+ const output = ["<Tree>"];
1404
+ const folders = [];
1405
+ for (const node of nodes) {
1406
+ while (folders.length > node.indent) {
1407
+ output.push("</Folder>");
1408
+ folders.pop();
1409
+ }
1410
+ if (node.indent > folders.length || node.indent > 0 && folders.length === 0) return original;
1411
+ const name = `{${JSON.stringify(node.name)}}`;
1412
+ if (node.isFolder) {
1413
+ output.push(`<Folder name=${name} defaultOpen>`);
1414
+ folders.push(node.indent);
1415
+ } else {
1416
+ output.push(`<File name=${name} />`);
1417
+ }
1418
+ }
1419
+ while (folders.length) {
1420
+ output.push("</Folder>");
1421
+ folders.pop();
1422
+ }
1423
+ output.push("</Tree>");
1424
+ return output.join("\n");
1425
+ }
1426
+ ));
1427
+ }
1428
+ function normalizeFernCallouts(body) {
1429
+ return replaceOutsideCode(body, (whole) => {
1430
+ const openTags = [];
1431
+ const output = [];
1432
+ let cursor = 0;
1433
+ while (cursor < whole.length) {
1434
+ const opening = whole.indexOf("<Callout", cursor);
1435
+ const closing = whole.indexOf("</Callout>", cursor);
1436
+ const next = opening < 0 ? closing : closing < 0 ? opening : Math.min(opening, closing);
1437
+ if (next < 0) break;
1438
+ output.push(whole.slice(cursor, next));
1439
+ if (next === closing) {
1440
+ const tag = openTags.pop();
1441
+ output.push(tag ? `</${tag}>` : "</Callout>");
1442
+ cursor = next + "</Callout>".length;
1443
+ continue;
1444
+ }
1445
+ const attributeStart = next + "<Callout".length;
1446
+ if (!/[\s/>]/.test(whole[attributeStart] ?? "")) {
1447
+ output.push("<Callout");
1448
+ cursor = attributeStart;
1449
+ continue;
1450
+ }
1451
+ let end = attributeStart;
1452
+ let quote = null;
1453
+ let expressionDepth = 0;
1454
+ for (; end < whole.length; end++) {
1455
+ const character = whole[end];
1456
+ if (quote) {
1457
+ if (character === quote && whole[end - 1] !== "\\") quote = null;
1458
+ } else if (character === '"' || character === "'") {
1459
+ quote = character;
1460
+ } else if (character === "{") {
1461
+ expressionDepth++;
1462
+ } else if (character === "}" && expressionDepth > 0) {
1463
+ expressionDepth--;
1464
+ } else if (character === ">" && expressionDepth === 0) {
1465
+ break;
1466
+ }
1467
+ }
1468
+ if (end === whole.length) {
1469
+ output.push(whole.slice(next));
1470
+ cursor = whole.length;
1471
+ break;
1472
+ }
1473
+ const attributes = whole.slice(attributeStart, end);
1474
+ const intent = findStaticCalloutIntent(attributes);
1475
+ const isSelfClosing = attributes.trimEnd().endsWith("/");
1476
+ if (!intent) {
1477
+ output.push(whole.slice(next, end + 1));
1478
+ if (!isSelfClosing) openTags.push(null);
1479
+ } else {
1480
+ const value = intent.value.toLowerCase();
1481
+ const tag = value === "warning" ? "Warning" : value === "success" || value === "tip" ? "Tip" : value === "error" || value === "danger" ? "Error" : "Note";
1482
+ output.push(`<${tag}${attributes.slice(0, intent.start)}${attributes.slice(intent.end)}>`);
1483
+ if (!isSelfClosing) openTags.push(tag);
1484
+ }
1485
+ cursor = end + 1;
1486
+ }
1487
+ output.push(whole.slice(cursor));
1488
+ return output.join("");
1489
+ });
1490
+ }
1491
+ function findStaticCalloutIntent(attributes) {
1492
+ let cursor = 0;
1493
+ while (cursor < attributes.length) {
1494
+ const start = cursor;
1495
+ while (/\s/.test(attributes[cursor] ?? "")) cursor++;
1496
+ if (cursor === start) {
1497
+ cursor++;
1498
+ continue;
1499
+ }
1500
+ const nameStart = cursor;
1501
+ while (/[A-Za-z0-9_-]/.test(attributes[cursor] ?? "")) cursor++;
1502
+ const name = attributes.slice(nameStart, cursor);
1503
+ while (/\s/.test(attributes[cursor] ?? "")) cursor++;
1504
+ if (attributes[cursor] !== "=") continue;
1505
+ cursor++;
1506
+ while (/\s/.test(attributes[cursor] ?? "")) cursor++;
1507
+ const quote = attributes[cursor];
1508
+ if (quote !== '"' && quote !== "'") {
1509
+ if (quote === "{") {
1510
+ let depth = 0;
1511
+ do {
1512
+ if (attributes[cursor] === "{") depth++;
1513
+ if (attributes[cursor] === "}") depth--;
1514
+ cursor++;
1515
+ } while (cursor < attributes.length && depth > 0);
1516
+ } else {
1517
+ while (cursor < attributes.length && !/\s/.test(attributes[cursor])) cursor++;
1518
+ }
1519
+ continue;
1520
+ }
1521
+ const valueStart = ++cursor;
1522
+ while (cursor < attributes.length && attributes[cursor] !== quote) cursor++;
1523
+ const value = attributes.slice(valueStart, cursor);
1524
+ if (cursor < attributes.length) cursor++;
1525
+ if (name === "intent") return { start, end: cursor, value };
1526
+ }
1527
+ return null;
1528
+ }
1529
+ function unwrapFernCodeBlockTabs(body) {
1530
+ return replaceOutsideCode(body, (whole) => {
1531
+ let groupDepth = 0;
1532
+ return whole.replace(/<CodeGroup\b[^>]*>|<\/CodeGroup>|<CodeBlock\b[^>]*>|<\/CodeBlock>/g, (match) => {
1533
+ if (match.startsWith("<CodeGroup")) {
1534
+ groupDepth++;
1535
+ return match;
1536
+ }
1537
+ if (match === "</CodeGroup>") {
1538
+ groupDepth = Math.max(0, groupDepth - 1);
1539
+ return match;
1540
+ }
1541
+ return groupDepth > 0 ? "" : match;
1542
+ });
1543
+ });
1544
+ }
1545
+ var STANDALONE_CODE_BLOCK_SHIM = [
1546
+ "export const CodeBlock = ({ children, filename, ...rest }) => (",
1547
+ " <pre {...rest}>",
1548
+ " {filename ? <div>{filename}</div> : null}",
1549
+ " <code>{children}</code>",
1550
+ " </pre>",
1551
+ ");"
1552
+ ].join("\n");
1553
+ function exportDeclarationBodies(body) {
1554
+ const bodies = [];
1555
+ const opener = /export const \w+ = [^\n]*=> ([({])/g;
1556
+ for (const match of body.matchAll(opener)) {
1557
+ const open = match[1];
1558
+ const close = open === "(" ? ")" : "}";
1559
+ let depth = 1;
1560
+ let i = (match.index ?? 0) + match[0].length;
1561
+ while (i < body.length && depth > 0) {
1562
+ if (body[i] === open) depth++;
1563
+ else if (body[i] === close) depth--;
1564
+ i++;
1565
+ }
1566
+ bodies.push(body.slice((match.index ?? 0) + match[0].length, i));
1567
+ }
1568
+ return bodies;
1569
+ }
1570
+ function provideScopedGlobalReferences(body) {
1571
+ const alreadyBound = (name) => new RegExp(`\\b${name}\\s*[=:]|\\bimport\\s+.*\\b${name}\\b`).test(body);
1572
+ const referencedIn = exportDeclarationBodies(body).join("\n");
1573
+ let result = body;
1574
+ if (/<Icon\b/.test(referencedIn) && !alreadyBound("Icon")) {
1575
+ result = `import { Icon } from '@/components/mdx/content-icon';
1576
+
1577
+ ${result}`;
1578
+ }
1579
+ if (/<CodeBlock\b/.test(referencedIn) && !alreadyBound("CodeBlock")) {
1580
+ result = `${STANDALONE_CODE_BLOCK_SHIM}
1581
+
1582
+ ${result}`;
1583
+ }
1584
+ return result;
1585
+ }
1586
+ function normalizeDocusaurusTabs(body) {
1587
+ let result = body;
1588
+ for (let depth = 0; depth < 20; depth += 1) {
1589
+ const updated = result.replace(/<Tabs\b([^>]*)>((?:(?!<Tabs\b)[\s\S])*?)<\/Tabs>/g, (_match, attributes, inner) => {
1590
+ const labelByValue = /* @__PURE__ */ new Map();
1591
+ const valuesSource = attributes.match(/\bvalues=\{(\[[\s\S]*?\])\s*\}/)?.[1];
1592
+ if (valuesSource) {
1593
+ for (const entry of valuesSource.matchAll(/\{([^{}]*)\}/g)) {
1594
+ const label = entry[1].match(/\blabel:\s*(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
1595
+ const value = entry[1].match(/\bvalue:\s*(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
1596
+ if (value) labelByValue.set(value, label ?? value);
1597
+ }
1598
+ }
1599
+ const cleanedAttributes = attributes.replace(/\s*defaultValue=(?:\{[^}]*\}|"[^"]*"|'[^']*')/g, "").replace(/\s*values=\{[\s\S]*?\]\s*\}/g, "").replace(/\s*values=\{[^{}]*\}/g, "").replace(/\s*groupId=(?:"[^"]*"|'[^']*')/g, "").replace(/\s*queryString(?:=(?:\{[^}]*\}|"[^"]*"|'[^']*'))?/g, "");
1600
+ const rewrittenInner = inner.replace(/<TabItem\b([^>]*)>/g, (_itemMatch, itemAttributes) => {
1601
+ const label = itemAttributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
1602
+ const value = itemAttributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
1603
+ const title = label ?? (value && labelByValue.get(value)) ?? value ?? "Tab";
1604
+ return `<Tab title="${title.replace(/"/g, "&quot;")}">`;
1605
+ }).replace(/<\/TabItem>/g, "</Tab>");
1606
+ return `<ThallyTabs${cleanedAttributes}>${rewrittenInner}</ThallyTabs>`;
1607
+ });
1608
+ if (updated === result) break;
1609
+ result = updated;
1610
+ }
1611
+ return result.replace(/<ThallyTabs\b/g, "<Tabs").replace(/<\/ThallyTabs>/g, "</Tabs>");
1612
+ }
1613
+ var TAB_FENCE_OPEN = /^(`{3,}|~{3,})(\S*)\s+tab\b(.*)$/;
1614
+ function normalizeDocusaurusTabBlocks(body) {
1615
+ const lines = body.split("\n");
1616
+ const output = [];
1617
+ let index = 0;
1618
+ while (index < lines.length) {
1619
+ const open = lines[index].match(TAB_FENCE_OPEN);
1620
+ if (!open) {
1621
+ output.push(lines[index]);
1622
+ index++;
1623
+ continue;
1624
+ }
1625
+ const blocks = [];
1626
+ while (index < lines.length) {
1627
+ const start = lines[index].match(TAB_FENCE_OPEN);
1628
+ if (!start) break;
1629
+ const marker = start[1];
1630
+ const block = [`${start[1]}${start[2]}${start[3]}`];
1631
+ index++;
1632
+ while (index < lines.length && lines[index].trim() !== marker) {
1633
+ block.push(lines[index]);
1634
+ index++;
1635
+ }
1636
+ block.push(lines[index] ?? marker);
1637
+ index++;
1638
+ blocks.push(block);
1639
+ let lookahead = index;
1640
+ while (lookahead < lines.length && lines[lookahead].trim() === "") lookahead++;
1641
+ if (!lines[lookahead] || !TAB_FENCE_OPEN.test(lines[lookahead])) break;
1642
+ index = lookahead;
1643
+ }
1644
+ if (blocks.length > 1) {
1645
+ output.push("<CodeGroup>", "");
1646
+ for (const block of blocks) output.push(...block, "");
1647
+ output.pop();
1648
+ output.push("</CodeGroup>");
1649
+ } else {
1650
+ output.push(...blocks[0]);
1651
+ }
1652
+ }
1653
+ return output.join("\n");
1654
+ }
1655
+ function splitCssDeclarations(value) {
1656
+ const parts = [];
1657
+ let depth = 0;
1658
+ let current = "";
1659
+ for (const char of value) {
1660
+ if (char === "(") depth++;
1661
+ else if (char === ")") depth = Math.max(0, depth - 1);
1662
+ if (char === ";" && depth === 0) {
1663
+ parts.push(current);
1664
+ current = "";
1665
+ } else {
1666
+ current += char;
1667
+ }
1668
+ }
1669
+ if (current.trim()) parts.push(current);
1670
+ return parts;
1671
+ }
1672
+ function cssPropertyToJsKey(property) {
1673
+ if (property.startsWith("--")) return JSON.stringify(property);
1674
+ const camel = property.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
1675
+ return camel.startsWith("Ms") ? camel.slice(0, 1).toLowerCase() + camel.slice(1) : camel;
1676
+ }
1677
+ function cssTextToStyleObjectLiteral(cssText) {
1678
+ const entries = splitCssDeclarations(cssText).flatMap((declaration) => {
1679
+ const colon = declaration.indexOf(":");
1680
+ if (colon === -1) return [];
1681
+ const property = declaration.slice(0, colon).trim();
1682
+ const value = declaration.slice(colon + 1).replace(/\s*!\s*important\s*$/i, "").trim();
1683
+ if (!property || !value || !/^-{0,2}[a-zA-Z][a-zA-Z0-9-]*$/.test(property)) return [];
1684
+ return [`${cssPropertyToJsKey(property)}: ${JSON.stringify(value)}`];
1685
+ });
1686
+ return `{${entries.join(", ")}}`;
1687
+ }
1688
+ function convertHtmlStyleAttributes(body) {
1689
+ return body.replace(
1690
+ // The separator before `style=` must be `\s+`, not a single `\s`: a
1691
+ // multi-line tag indents its attributes, so more than one whitespace
1692
+ // character (a newline plus spaces) commonly precedes it.
1693
+ /<([a-z][a-zA-Z0-9]*)((?:\s+[^\s"'=<>/]+(?:=(?:"[^"]*"|'[^']*'))?)*)\s+style=(["'])([\s\S]*?)\3((?:\s+[^\s"'=<>/]+(?:=(?:"[^"]*"|'[^']*'))?)*)\s*(\/?)>/g,
1694
+ (_match, tag, before, _quote, styleText, after, selfClose) => `<${tag}${before} style={${cssTextToStyleObjectLiteral(styleText)}}${after}${selfClose ? " /" : ""}>`
1695
+ );
1696
+ }
1697
+ function normalizeMintlifyUpdateLabels(body) {
1698
+ return body.replace(/(<Update\b[^>]*\blabel=)\{(<\>[\s\S]*?<\/\>)\}/g, (match, prefix, fragment) => {
1699
+ let root;
1700
+ try {
1701
+ root = descriptionParser.parse(fragment);
1702
+ } catch {
1703
+ return match;
1704
+ }
1705
+ const plainText2 = (node) => {
1706
+ if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
1707
+ if (node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" || node.type === "mdxjsEsm") return null;
1708
+ const parts = (node.children ?? []).map(plainText2);
1709
+ return parts.some((part) => part === null) ? null : parts.join("");
1710
+ };
1711
+ const label = plainText2(root)?.replace(/\s+/g, " ").trim();
1712
+ return label && !/[{}]/.test(label) ? `${prefix}{${JSON.stringify(label)}}` : match;
1713
+ });
1714
+ }
1715
+ function normalizeMintlifyParameterAnchors(body) {
1716
+ const seen = /* @__PURE__ */ new Set();
1717
+ const anchorFor = (name) => {
1718
+ const id = `param-${name.toLowerCase().replace(/[._]/g, "-")}`;
1719
+ if (seen.has(id)) return "";
1720
+ seen.add(id);
1721
+ return `<a id="${id}"></a>
1722
+ `;
1723
+ };
1724
+ let fence;
1725
+ const withHeadings = body.split("\n").map((line) => {
1726
+ const marker = line.match(/^ {0,3}(`{3,}|~{3,})/);
1727
+ if (marker) {
1728
+ if (!fence) fence = marker[1];
1729
+ else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && /^\s*$/.test(line.slice(marker[0].length))) fence = void 0;
1730
+ return line;
1731
+ }
1732
+ if (fence) return line;
1733
+ const heading = line.match(/^ {0,3}#{1,6}\s+`([A-Za-z][A-Za-z0-9._-]*)`(?:\s|$)/);
1734
+ return heading ? `${anchorFor(heading[1])}${line}` : line;
1735
+ }).join("\n");
1736
+ return replaceOutsideCode(withHeadings, (segment) => segment.replace(
1737
+ /<(ResponseField|ParamField)\b([^>]*?)\bname=(?:"([A-Za-z][A-Za-z0-9._-]*)"|'([A-Za-z][A-Za-z0-9._-]*)')([^>]*)>/g,
1738
+ (match, _tag, _before, doubleQuoted, singleQuoted) => `${anchorFor(doubleQuoted ?? singleQuoted)}${match}`
1739
+ ));
1740
+ }
1741
+ function matchFenceLine(line) {
1742
+ const match = line.match(/^(\s*)(`{3,}|~{3,})(.*)$/);
1743
+ if (!match) return null;
1744
+ return { index: -1, indent: match[1], char: match[2][0], length: match[2].length, info: match[3].trim(), rest: match[3] };
1745
+ }
1746
+ function normalizeNestedCodeFences(body) {
1747
+ const lines = body.split("\n");
1748
+ const fences = lines.map((line, index) => {
1749
+ const fence = matchFenceLine(line);
1750
+ return fence ? { ...fence, index } : null;
1751
+ }).filter((fence) => fence !== null);
1752
+ if (fences.length === 0) return body;
1753
+ const stack = [];
1754
+ const roots = [];
1755
+ for (const fence of fences) {
1756
+ const top = stack.at(-1);
1757
+ if (top && fence.info === "" && fence.char === top.open.char && fence.length >= top.open.length) {
1758
+ stack.pop();
1759
+ const block = { open: top.open, close: fence, children: top.children };
1760
+ const parent = stack.at(-1);
1761
+ if (parent) parent.children.push(block);
1762
+ else roots.push(block);
1763
+ } else {
1764
+ stack.push({ open: fence, children: [] });
1765
+ }
1766
+ }
1767
+ if (stack.length > 0) return body;
1768
+ const widenedLengths = /* @__PURE__ */ new Map();
1769
+ function requiredLength(block) {
1770
+ const deepestChild = Math.max(0, ...block.children.map(requiredLength));
1771
+ const length = Math.max(block.open.length, deepestChild > 0 ? deepestChild + 1 : 0);
1772
+ widenedLengths.set(block.open.index, length);
1773
+ widenedLengths.set(block.close.index, length);
1774
+ return length;
1775
+ }
1776
+ roots.forEach(requiredLength);
1777
+ return lines.map((line, index) => {
1778
+ const targetLength = widenedLengths.get(index);
1779
+ const fence = targetLength ? matchFenceLine(line) : null;
1780
+ if (!targetLength || !fence || targetLength <= fence.length) return line;
1781
+ return `${fence.indent}${fence.char.repeat(targetLength)}${fence.rest}`;
1782
+ }).join("\n");
1783
+ }
1784
+ function maskCodeRegions(body) {
1785
+ let inFence = false;
1786
+ return body.split("\n").map((line) => {
1787
+ if (/^\s*(`{3,}|~{3,})/.test(line)) {
1788
+ inFence = !inFence;
1789
+ return " ".repeat(line.length);
1790
+ }
1791
+ if (inFence) return " ".repeat(line.length);
1792
+ return line.replace(/`[^`]*`/g, (span) => " ".repeat(span.length));
1793
+ }).join("\n");
1794
+ }
1795
+ var UNTERMINATED = "unterminated";
1796
+ function readTagEnd(text, from) {
1797
+ if (from < text.length && !/[\s/>]/.test(text[from])) return null;
1798
+ let depth = 0;
1799
+ let quote = "";
1800
+ for (let i = from; i < text.length; i++) {
1801
+ const char = text[i];
1802
+ if (quote) {
1803
+ if (char === "\\" && depth > 0) i++;
1804
+ else if (char === quote) quote = "";
1805
+ } else if (char === '"' || char === "'" || char === "`" && depth > 0) {
1806
+ quote = char;
1807
+ } else if (char === "{") {
1808
+ depth++;
1809
+ } else if (char === "}") {
1810
+ depth = Math.max(0, depth - 1);
1811
+ } else if (depth === 0) {
1812
+ if (char === "<") return null;
1813
+ if (char === ">") {
1814
+ const selfClosing = text[i - 1] === "/" && i > from;
1815
+ return { end: i + 1, selfClosing, attributes: text.slice(from, selfClosing ? i - 1 : i) };
1816
+ }
1817
+ }
1818
+ }
1819
+ return quote || depth > 0 ? UNTERMINATED : null;
1820
+ }
1821
+ function pinStepsTitleSize(body) {
1822
+ let result = "";
1823
+ let cursor = 0;
1824
+ for (const match of body.matchAll(/<Steps(?=[\s/>])/g)) {
1825
+ if (match.index < cursor) continue;
1826
+ const tag = readTagEnd(body, match.index + match[0].length);
1827
+ if (tag === UNTERMINATED) break;
1828
+ if (!tag) continue;
1829
+ const attributesEnd = tag.end - (tag.selfClosing ? 2 : 1);
1830
+ if (/\btitleSize\s*=/.test(tag.attributes)) continue;
1831
+ result += `${body.slice(cursor, attributesEnd).trimEnd()} titleSize="p"${tag.selfClosing ? " /" : ""}>`;
1832
+ cursor = tag.end;
1833
+ }
1834
+ return result + body.slice(cursor);
1835
+ }
1836
+ function isKnownComponentName(name, body) {
1837
+ if (isThallyBuiltinComponent(name)) return true;
1838
+ if (new RegExp(`^\\s*export\\s+(?:const|function|default\\s+function)\\s+${name}\\b`, "m").test(body)) return true;
1839
+ if (new RegExp(`^\\s*import\\s+(?:\\{[^}]*\\b${name}\\b[^}]*\\}|${name})\\s+from\\s+['"][^'"]+['"]`, "m").test(body)) return true;
1840
+ return false;
1841
+ }
1842
+ function escapeOrphanCapitalizedTags(body) {
1843
+ const masked = maskCodeRegions(body);
1844
+ const tagPattern = /<(\/?)([A-Z][A-Za-z0-9]*)(?:\s+[^<>]*?)?(\/?)>/g;
1845
+ const opens = /* @__PURE__ */ new Map();
1846
+ const closedOrSelfClosed = /* @__PURE__ */ new Set();
1847
+ for (const match of masked.matchAll(tagPattern)) {
1848
+ const [full, closing, name, selfClosing] = match;
1849
+ if (closing || selfClosing) {
1850
+ closedOrSelfClosed.add(name);
1851
+ continue;
1852
+ }
1853
+ const start = match.index;
1854
+ const list = opens.get(name) ?? [];
1855
+ list.push({ start, end: start + full.length });
1856
+ opens.set(name, list);
1857
+ }
1858
+ const edits = [];
1859
+ for (const [name, ranges] of opens) {
1860
+ if (closedOrSelfClosed.has(name) || isKnownComponentName(name, body)) continue;
1861
+ edits.push(...ranges);
1862
+ }
1863
+ if (edits.length === 0) return body;
1864
+ try {
1865
+ compileSync(body, { outputFormat: "program" });
1866
+ return body;
1867
+ } catch {
1868
+ }
1869
+ edits.sort((a, b) => a.start - b.start);
1870
+ let result = "";
1871
+ let cursor = 0;
1872
+ for (const edit of edits) {
1873
+ result += body.slice(cursor, edit.start);
1874
+ result += body.slice(edit.start, edit.end).replace(/^</, "&lt;").replace(/>$/, "&gt;");
1875
+ cursor = edit.end;
1876
+ }
1877
+ return result + body.slice(cursor);
1878
+ }
1879
+ var REACT_HOOK_NAMES = ["useState", "useEffect", "useRef", "useCallback", "useMemo", "useContext", "useReducer"];
1880
+ function injectReactHookImports(body) {
1881
+ const masked = maskCodeRegions(body);
1882
+ const used = REACT_HOOK_NAMES.filter((hook) => new RegExp(`\\b${hook}\\s*\\(`).test(masked) && !new RegExp(`import\\s*\\{[^}]*\\b${hook}\\b[^}]*\\}\\s*from\\s*['"]react['"]`).test(body));
1883
+ return used.length > 0 ? `import { ${used.join(", ")} } from 'react'
1884
+
1885
+ ${body}` : body;
1886
+ }
1887
+ function removeUndefinedExpressions(body, warn) {
1888
+ if (!body.includes("{")) return body;
1889
+ let tree;
1890
+ try {
1891
+ tree = descriptionParser.parse(body);
1892
+ } catch {
1893
+ return body;
1894
+ }
1895
+ const esm = [];
1896
+ const collect = (node) => {
1897
+ if (node.type === "mdxjsEsm" && node.value) esm.push(node.value);
1898
+ for (const child of node.children ?? []) collect(child);
1899
+ };
1900
+ collect(tree);
1901
+ const scope = pageScopeNames(esm);
1902
+ const edits = [];
1903
+ const removed = /* @__PURE__ */ new Set();
1904
+ const check = (expression, position) => {
1905
+ const names = unresolvedExpressionNames(expression, scope);
1906
+ const start = position?.start.offset;
1907
+ const end = position?.end.offset;
1908
+ if (names.length === 0 || start === void 0 || end === void 0) return;
1909
+ names.forEach((name) => removed.add(name));
1910
+ edits.push({ start, end });
1911
+ };
1912
+ const visit = (node) => {
1913
+ if ((node.type === "mdxFlowExpression" || node.type === "mdxTextExpression") && node.value !== void 0) {
1914
+ check(node.value, node.position);
1915
+ }
1916
+ for (const attribute of node.attributes ?? []) {
1917
+ const value = attribute.value;
1918
+ if (attribute.type === "mdxJsxExpressionAttribute" && typeof value === "string") check(`{${value}}`, attribute.position);
1919
+ else if (value && typeof value === "object" && typeof value.value === "string") {
1920
+ check(value.value, attribute.position);
1921
+ }
1922
+ }
1923
+ for (const child of node.children ?? []) visit(child);
1924
+ };
1925
+ visit(tree);
1926
+ if (edits.length === 0) return body;
1927
+ warn?.(`Expressions that use ${[...removed].map((name) => `"${name}"`).join(", ")} were removed so the page builds: ${removed.size === 1 ? "the name is" : "the names are"} not defined on this page. Define ${removed.size === 1 ? "the name" : "the names"} or escape the braces as \\{ \\} to show the text.`);
1928
+ return edits.sort((a, b) => b.start - a.start).reduce((text, edit) => text.slice(0, edit.start) + text.slice(edit.end), body);
1929
+ }
1930
+ function normalizeFlowParagraphContainers(body) {
1931
+ if (!body.includes("<p")) return body;
1932
+ let tree;
1933
+ try {
1934
+ tree = descriptionParser.parse(body);
1935
+ } catch {
1936
+ return body;
1937
+ }
1938
+ const edits = [];
1939
+ function visit(node) {
1940
+ if (node.type === "mdxJsxFlowElement" && node.name === "p" && node.children?.some((child) => child.type === "paragraph" || child.type === "list" || child.type === "mdxJsxFlowElement")) {
1941
+ const start = node.position?.start.offset;
1942
+ const end = node.position?.end.offset;
1943
+ if (start !== void 0 && end !== void 0 && body.slice(start, start + 2) === "<p") {
1944
+ const closing = body.slice(start, end).match(/<\/p\s*>\s*$/i);
1945
+ if (closing?.index !== void 0) {
1946
+ edits.push({ offset: start + 1, value: "div" });
1947
+ edits.push({ offset: start + closing.index + 2, value: "div" });
1948
+ }
1949
+ }
1950
+ }
1951
+ for (const child of node.children ?? []) visit(child);
1952
+ }
1953
+ visit(tree);
1954
+ return edits.sort((a, b) => b.offset - a.offset).reduce((text, edit) => text.slice(0, edit.offset) + edit.value + text.slice(edit.offset + 1), body);
1955
+ }
1956
+ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
1957
+ const runFern = platform === "fern";
1958
+ const runMintlify = platform === "mintlify";
1959
+ const runDocusaurus = platform === "docusaurus";
1960
+ const sourceBody = runDocusaurus && unwrapMdxCodeBlocks ? unwrapDocusaurusMdxCodeBlocks(body) : body;
1961
+ let rewritten = normalizeExplicitHeadingIds(
1962
+ runFern ? normalizeFernFileTrees(normalizeFernCallouts(normalizeNestedCodeFences(sourceBody))) : normalizeNestedCodeFences(sourceBody),
1963
+ void 0,
1964
+ { headingMarkers: runMintlify }
1965
+ );
1966
+ if (runDocusaurus) rewritten = normalizeDocusaurusAdmonitions(rewritten);
1967
+ if (runMintlify) rewritten = markMintlifyHeadings(rewritten);
1968
+ if (runDocusaurus) {
1969
+ rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
1970
+ /:([+\w-]+):/g,
1971
+ (shortcode, name) => nameToEmoji[name] ?? shortcode
1972
+ ));
1973
+ rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
1974
+ /\b(href|src)=\{\s*require\(\s*(['"])@docusaurus\/useBaseUrl\2\s*\)\.default\(\s*(['"])([^'"]+)\3\s*\)\s*\}/g,
1975
+ (original, attribute, _moduleQuote, _pathQuote, path) => {
1976
+ if (!/^[\w./~%-]+$/.test(path) || path.split("/").includes("..")) return original;
1977
+ return `${attribute}="/${path.replace(/^\.\//, "").replace(/^\/+/, "")}"`;
1978
+ }
1979
+ ));
1980
+ rewritten = replaceOutsideCode(rewritten, (segment) => normalizeDocusaurusThemedImages(normalizeDocusaurusAdmonitionTags(segment)));
1981
+ rewritten = normalizeDocusaurusTabBlocks(normalizeDocusaurusTabs(removeGlobalDocusaurusImports(rewritten)));
1982
+ rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
1983
+ /\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])(@site\/[^'"]+)\2\s*;?/g,
1984
+ (statement, binding, _quote, _source, offset) => {
1985
+ const withoutStatement = segment.slice(0, offset) + segment.slice(offset + statement.length);
1986
+ return new RegExp(`\\b${binding}\\s*(?:\\.|\\[|\\})`).test(withoutStatement) ? statement : "";
1987
+ }
1988
+ ));
1989
+ rewritten = rewritten.replace(/^\n{2,}/, "\n");
1990
+ }
1991
+ if (runMintlify) rewritten = normalizeMintlifyParameterAnchors(rewritten);
1992
+ rewritten = replaceOutsideCode(rewritten, (segment) => {
1993
+ let result = convertHtmlStyleAttributes(segment).replace(/<!--([\s\S]*?)-->/g, (_match, content) => mdxComment(content)).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").replace(/<Warn(\s[^>]*)?>/g, "<Warning$1>").replace(/<\/Warn>/g, "</Warning>").replace(/<Check(\s[^>]*)?>/g, "<Note$1>").replace(/<\/Check>/g, "</Note>").replace(/<Tree\.Folder/g, "<Folder").replace(/<\/Tree\.Folder>/g, "</Folder>").replace(/<Tree\.File/g, "<File").replace(/<\/Tree\.File>/g, "</File>");
1994
+ if (runDocusaurus) {
1995
+ result = result.replace(/<TabItem\b([^<>]*)>/g, (_match, attributes) => {
1996
+ const title = attributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? attributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? "Tab";
1997
+ return `<Tab title="${title.replace(/"/g, "&quot;")}">`;
1998
+ }).replace(/<\/TabItem>/g, "</Tab>");
1999
+ result = normalizeDocusaurusLinkTags(result).replace(/<(?:DocCardList|TOCInline)\b[^<>]*\/>/g, "");
2000
+ }
2001
+ if (runMintlify) {
2002
+ result = normalizeMintlifyUpdateLabels(result).replace(/<FileTree(\s[^>]*)?>/g, "<Tree$1>").replace(/<\/FileTree>/g, "</Tree>").replace(/<GitHub\.Repo/g, "<GitHub").replace(/<\/GitHub\.Repo>/g, "</GitHub>").replace(/<Column(\s[^>]*)?>/g, "<div$1>").replace(/<\/Column>/g, "</div>");
2003
+ result = pinStepsTitleSize(result);
2004
+ }
2005
+ if (runFern) {
2006
+ result = result.replace(/<CodeBlocks(\s[^>]*)?>/g, "<CodeGroup$1>").replace(/<\/CodeBlocks>/g, "</CodeGroup>").replace(/<ParameterField\b/g, "<ParamField").replace(/<\/ParameterField>/g, "</ParamField>").replace(/<Cards(\s[^>]*)?>/g, "<CardGroup$1>").replace(/<\/Cards>/g, "</CardGroup>").replace(/<Success(\s[^>]*)?>/g, "<Tip$1>").replace(/<\/Success>/g, "</Tip>").replace(/<Launch(\s[^>]*)?>/g, "<Note$1>").replace(/<\/Launch>/g, "</Note>").replace(/<Files(\s[^>]*)?>\n?/g, "").replace(/<\/Files>/g, "");
2007
+ }
2008
+ return result;
2009
+ });
2010
+ if (runFern) rewritten = unwrapFernCodeBlockTabs(rewritten);
2011
+ const normalized = escapeOrphanCapitalizedTags(provideScopedGlobalReferences(rewritten));
2012
+ if (runDocusaurus && sourceBody !== body) {
2013
+ try {
2014
+ compileSync(normalized, { outputFormat: "program" });
2015
+ } catch {
2016
+ return normalizeMdx(body, platform, false);
2017
+ }
2018
+ }
2019
+ return normalizeFlowParagraphContainers(normalized);
2020
+ }
2021
+ function parseMarkdownPage(input) {
2022
+ const parsed = parseFrontmatter(input.raw);
2023
+ const fallbackIdentity = {
2024
+ id: input.id,
2025
+ navigationId: input.navigationId ?? input.id,
2026
+ ...input.locale ? { locale: input.locale } : {}
2027
+ };
2028
+ const identity = input.resolveIdentity?.(parsed.data, fallbackIdentity) ?? fallbackIdentity;
2029
+ const body = removeUndefinedExpressions(injectReactHookImports(normalizeMdx(parsed.content, input.platform)), input.warn).trim();
2030
+ const keywords = Array.isArray(parsed.data.keywords) ? parsed.data.keywords.filter((value) => typeof value === "string") : [];
2031
+ const heading = leadingPageHeading(body);
2032
+ const title = typeof parsed.data.title === "string" && parsed.data.title.trim() ? parsed.data.title.trim() : heading?.title ?? titleFromId(identity.navigationId);
2033
+ const sidebarTitle = input.platform === "docusaurus" ? parsed.data.sidebar_label : parsed.data.sidebarTitle;
2034
+ const navTitle = typeof sidebarTitle === "string" && sidebarTitle.trim() ? sidebarTitle.trim() : typeof parsed.data.navTitle === "string" && parsed.data.navTitle.trim() ? parsed.data.navTitle.trim() : void 0;
2035
+ const icon = typeof parsed.data.icon === "string" && parsed.data.icon.trim() ? parsed.data.icon.trim() : void 0;
2036
+ const iconType = icon && typeof parsed.data.iconType === "string" && ["regular", "solid", "outline", "brands"].includes(parsed.data.iconType) ? parsed.data.iconType : void 0;
2037
+ const meta = {};
2038
+ for (const [source, field] of PAGE_META_FIELDS) {
2039
+ const value = parsed.data[source];
2040
+ if (typeof value === "string" && value.trim()) meta[field] = value.trim();
2041
+ }
2042
+ const badge = typeof parsed.data.tag === "string" && parsed.data.tag.trim() ? parsed.data.tag.trim() : typeof parsed.data.badge === "string" && parsed.data.badge.trim() ? parsed.data.badge.trim() : void 0;
2043
+ const sourceMode = typeof parsed.data.mode === "string" ? parsed.data.mode : void 0;
2044
+ const mode = sourceMode === "frame" ? "wide" : ["default", "wide", "custom", "center", "home"].includes(sourceMode ?? "") ? sourceMode : void 0;
2045
+ const description = typeof parsed.data.description === "string" && parsed.data.description.trim() ? parsed.data.description.trim() : typeof parsed.data.openapi === "string" && parsed.data.openapi.trim() ? "" : firstParagraph(body);
2046
+ const page = {
2047
+ id: identity.id,
2048
+ navigationId: identity.navigationId,
2049
+ locale: identity.locale,
2050
+ title,
2051
+ navTitle,
2052
+ icon,
2053
+ iconType,
2054
+ description,
2055
+ ...input.platform === "docusaurus" || input.platform === "fern" ? { descriptionPlacement: "body" } : {},
2056
+ badge,
2057
+ keywords,
2058
+ mode,
2059
+ hidden: parsed.data.hidden === true ? true : void 0,
2060
+ noindex: parsed.data.noindex === true || parsed.data.noindex === "true" ? true : void 0,
2061
+ ...Object.keys(meta).length > 0 ? { meta } : {},
2062
+ openapi: typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0,
2063
+ ...apiFrontmatter(parsed.data, input.warn),
2064
+ body,
2065
+ source: input.source,
2066
+ ...parsed.error ? { frontmatterError: parsed.error } : {}
2067
+ };
2068
+ resolvePageDisplayHeading(page, input.platform);
2069
+ return page;
2070
+ }
2071
+ var PAGE_META_FIELDS = [
2072
+ ["og:title", "ogTitle"],
2073
+ ["og:description", "ogDescription"],
2074
+ ["og:image", "ogImage"],
2075
+ ["twitter:title", "twitterTitle"],
2076
+ ["twitter:description", "twitterDescription"],
2077
+ ["twitter:image", "twitterImage"]
2078
+ ];
2079
+ var AUTH_METHODS = /* @__PURE__ */ new Set(["bearer", "basic", "key", "none"]);
2080
+ function apiFrontmatter(data, warn) {
2081
+ const result = {};
2082
+ if (data.playground !== void 0 && data.playground !== null) {
2083
+ const display = playgroundDisplay(data.playground, `The page's "playground" frontmatter`, warn);
2084
+ if (display) result.playground = display;
2085
+ }
2086
+ if (data.api !== void 0 && data.api !== null) {
2087
+ if (typeof data.api === "string" && data.api.trim()) result.api = data.api.trim();
2088
+ else warn?.(`The page's "api" frontmatter is not a "METHOD url-or-path" string and was dropped.`);
2089
+ }
2090
+ if (data.authMethod !== void 0 && data.authMethod !== null) {
2091
+ const method = typeof data.authMethod === "string" ? data.authMethod.trim().toLowerCase() : "";
2092
+ if (AUTH_METHODS.has(method)) result.authMethod = method;
2093
+ else warn?.(`The page's "authMethod" frontmatter is not one of bearer, basic, key, none and was dropped.`);
2094
+ }
2095
+ return result;
2096
+ }
2097
+
2098
+ // src/url.ts
2099
+ var MAX_LOCAL_RESPONSE_BYTES = 2e6;
2100
+ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
2101
+ "Accordion",
2102
+ "AccordionGroup",
2103
+ "Badge",
2104
+ "Callout",
2105
+ "Card",
2106
+ "CardGroup",
2107
+ "Check",
2108
+ "CodeBlock",
2109
+ "CodeGroup",
2110
+ "Color",
2111
+ "Column",
2112
+ "Columns",
2113
+ "ContentPanel",
2114
+ "Danger",
2115
+ "Error",
2116
+ "Expandable",
2117
+ "Agent",
2118
+ "AgentPrompt",
2119
+ "Banner",
2120
+ "BannerPreview",
2121
+ "Embed",
2122
+ "File",
2123
+ "Folder",
2124
+ "Frame",
2125
+ "GitHub",
2126
+ "Github",
2127
+ "Hero",
2128
+ "Human",
2129
+ "Icon",
2130
+ "Info",
2131
+ "InlinePanel",
2132
+ "InlineRequestExample",
2133
+ "InlineResponseExample",
2134
+ "Latex",
2135
+ "LegacyView",
2136
+ "LiteYouTubeEmbed",
2137
+ "MDX",
2138
+ "Mermaid",
2139
+ "Note",
2140
+ "Panel",
2141
+ "ParamField",
2142
+ "Prompt",
2143
+ "PromptAssistant",
2144
+ "PromptUser",
2145
+ "RequestExample",
2146
+ "ResponseExample",
2147
+ "ResponseField",
2148
+ "Step",
2149
+ "Steps",
2150
+ "Tab",
2151
+ "Tabs",
2152
+ "Terminal",
2153
+ "TerminalInput",
2154
+ "TerminalOutput",
2155
+ "Tile",
2156
+ "TileGroup",
2157
+ "Tip",
2158
+ "Tooltip",
2159
+ "Tree",
2160
+ "Update",
2161
+ "Video",
2162
+ "View",
2163
+ "Visibility",
2164
+ "Warning",
2165
+ "YouTube",
2166
+ "picture",
2167
+ "source"
2168
+ ]);
2169
+ var PORTABLE_URL_PROPS = "href|src|img|primaryHref|secondaryHref";
2170
+ var PORTABLE_URL_PROP_NAMES = new Set(PORTABLE_URL_PROPS.split("|"));
2171
+ var remoteMarkdownParser = unified2().use(remarkParse2).use(remarkMdx2);
2172
+ function validateMigrationUrl(value) {
2173
+ let url;
2174
+ try {
2175
+ url = new URL(value);
2176
+ } catch {
2177
+ throw new Error("Enter a complete documentation URL, including https://.");
2178
+ }
2179
+ if (!["http:", "https:"].includes(url.protocol)) {
2180
+ throw new Error("Documentation imports support only HTTP and HTTPS URLs.");
2181
+ }
2182
+ if (url.username || url.password) throw new Error("Documentation URLs cannot contain credentials.");
2183
+ url.hash = "";
2184
+ return url;
2185
+ }
2186
+ async function readResponseBody(response) {
2187
+ const contentLength = Number(response.headers.get("content-length") ?? 0);
2188
+ if (contentLength > MAX_LOCAL_RESPONSE_BYTES) throw new Error("A documentation response exceeded the 2 MB import limit.");
2189
+ if (!response.body) return "";
2190
+ const reader = response.body.getReader();
2191
+ const chunks = [];
2192
+ let size = 0;
2193
+ while (true) {
2194
+ const { done, value } = await reader.read();
2195
+ if (done) break;
2196
+ size += value.byteLength;
2197
+ if (size > MAX_LOCAL_RESPONSE_BYTES) {
2198
+ await reader.cancel();
2199
+ throw new Error("A documentation response exceeded the 2 MB import limit.");
2200
+ }
2201
+ chunks.push(value);
2202
+ }
2203
+ const body = new Uint8Array(size);
2204
+ let offset = 0;
2205
+ for (const chunk of chunks) {
2206
+ body.set(chunk, offset);
2207
+ offset += chunk.byteLength;
2208
+ }
2209
+ return new TextDecoder().decode(body);
2210
+ }
2211
+ var defaultMigrationFetcher = async (url, request) => {
2212
+ const response = await fetch(url, {
2213
+ headers: {
2214
+ Accept: request.accept,
2215
+ "User-Agent": "Thally-Migrate/1.0 (+https://thally.io)"
2216
+ },
2217
+ redirect: "follow",
2218
+ signal: AbortSignal.timeout(15e3)
2219
+ });
2220
+ if (!response.ok) throw new Error(`Documentation server returned ${response.status}.`);
2221
+ return {
2222
+ finalUrl: new URL(response.url),
2223
+ body: await readResponseBody(response),
2224
+ contentType: response.headers.get("content-type") ?? "",
2225
+ headers: Object.fromEntries(response.headers.entries())
2226
+ };
2227
+ };
2228
+ function plainDescription(value) {
2229
+ return value.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[`*_>#-]/g, "").replace(/\s+/g, " ").trim().slice(0, 240);
2230
+ }
2231
+ function withoutFencedCode(body) {
2232
+ let fenceCharacter = "";
2233
+ let fenceLength = 0;
2234
+ return body.split("\n").map((line) => {
2235
+ const match = line.match(/^\s{0,3}(`{3,}|~{3,})(.*)$/);
2236
+ if (match && !fenceCharacter) {
2237
+ fenceCharacter = match[1][0];
2238
+ fenceLength = match[1].length;
2239
+ return "";
2240
+ }
2241
+ if (match && match[1][0] === fenceCharacter && match[1].length >= fenceLength && match[2].trim() === "") {
2242
+ fenceCharacter = "";
2243
+ fenceLength = 0;
2244
+ return "";
2245
+ }
2246
+ return fenceCharacter ? "" : line;
2247
+ }).join("\n");
2248
+ }
2249
+ function eventHandlerPropEnd(value, start) {
2250
+ const opening = value[start];
2251
+ if (opening === '"' || opening === "'") {
2252
+ for (let index = start + 1; index < value.length; index += 1) {
2253
+ if (value[index] === opening && value[index - 1] !== "\\") return index + 1;
2254
+ }
2255
+ return null;
2256
+ }
2257
+ if (opening !== "{") {
2258
+ const match = value.slice(start).match(/^[^\s>]+/);
2259
+ return match ? start + match[0].length : null;
2260
+ }
2261
+ let depth = 0;
2262
+ let quote = "";
2263
+ for (let index = start; index < value.length; index += 1) {
2264
+ const character = value[index];
2265
+ if (quote) {
2266
+ if (character === quote && value[index - 1] !== "\\") quote = "";
2267
+ continue;
2268
+ }
2269
+ if (character === '"' || character === "'" || character === "`") {
2270
+ quote = character;
2271
+ continue;
2272
+ }
2273
+ if (character === "{") depth += 1;
2274
+ if (character === "}") {
2275
+ depth -= 1;
2276
+ if (depth === 0) return index + 1;
2277
+ }
2278
+ }
2279
+ return null;
2280
+ }
2281
+ function stripEventHandlerProps(line) {
2282
+ const matcher = /\s+on[A-Z][A-Za-z0-9]*\s*=\s*/g;
2283
+ let cursor = 0;
2284
+ let result = "";
2285
+ for (const match of line.matchAll(matcher)) {
2286
+ const start = match.index;
2287
+ const valueStart = start + match[0].length;
2288
+ const end = eventHandlerPropEnd(line, valueStart);
2289
+ if (end === null) continue;
2290
+ result += line.slice(cursor, start);
2291
+ cursor = end;
2292
+ }
2293
+ return cursor === 0 ? line : `${result}${line.slice(cursor)}`;
2294
+ }
2295
+ function isSafePortableUrl(value) {
2296
+ try {
2297
+ const parsed = new URL(value, "https://relative.thally.invalid");
2298
+ return ["http:", "https:", "mailto:", "tel:"].includes(parsed.protocol);
2299
+ } catch {
2300
+ return false;
2301
+ }
2302
+ }
2303
+ function decodeHtmlAttribute(value) {
2304
+ const document = load(`<i data-thally-value="${value.replace(/"/g, "&quot;")}"></i>`);
2305
+ return document("i").attr("data-thally-value") ?? "";
2306
+ }
2307
+ function escapeMarkdownText(value, escapeTableSeparator = false) {
2308
+ let escaped = "";
2309
+ for (const character of value) {
2310
+ if (character === "\\") escaped += "\\\\";
2311
+ else if (character === "[" || character === "]") escaped += `\\${character}`;
2312
+ else if (character === "<") escaped += "&lt;";
2313
+ else if (character === ">") escaped += "&gt;";
2314
+ else if (character === "{") escaped += "&#123;";
2315
+ else if (character === "}") escaped += "&#125;";
2316
+ else if (character === "|" && escapeTableSeparator) escaped += "\\|";
2317
+ else escaped += character;
2318
+ }
2319
+ return escaped;
2320
+ }
2321
+ function markdownLinkDestination(value) {
2322
+ try {
2323
+ return encodeURI(value).replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/&/g, "&amp;");
2324
+ } catch {
2325
+ return null;
2326
+ }
2327
+ }
2328
+ function htmlOpeningTagEnd(value, start) {
2329
+ let quote = "";
2330
+ for (let index = start; index < value.length; index += 1) {
2331
+ const character = value[index];
2332
+ if (quote) {
2333
+ if (character === quote) quote = "";
2334
+ continue;
2335
+ }
2336
+ if (character === '"' || character === "'") {
2337
+ quote = character;
2338
+ continue;
2339
+ }
2340
+ if (character === ">") return index + 1;
2341
+ }
2342
+ return null;
2343
+ }
2344
+ function replacePortableHtmlImages(value) {
2345
+ const matcher = /<img\b/gi;
2346
+ let cursor = 0;
2347
+ let output = "";
2348
+ for (const match of value.matchAll(matcher)) {
2349
+ const start = match.index;
2350
+ if (start < cursor) continue;
2351
+ const end = htmlOpeningTagEnd(value, start + match[0].length);
2352
+ if (end === null) continue;
2353
+ const fragment = load(value.slice(start, end));
2354
+ const image = fragment("img").first();
2355
+ const source = image.attr("src");
2356
+ const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
2357
+ const alternative = escapeMarkdownText(image.attr("alt") ?? "");
2358
+ output += value.slice(cursor, start);
2359
+ output += destination ? `![${alternative}](${destination})` : alternative;
2360
+ cursor = end;
2361
+ }
2362
+ return cursor === 0 ? value : `${output}${value.slice(cursor)}`;
2363
+ }
2364
+ function replacePortableHtmlAnchors(value) {
2365
+ const matcher = /<a\b/gi;
2366
+ let cursor = 0;
2367
+ let output = "";
2368
+ for (const match of value.matchAll(matcher)) {
2369
+ const start = match.index;
2370
+ if (start < cursor) continue;
2371
+ const openingEnd = htmlOpeningTagEnd(value, start + match[0].length);
2372
+ if (openingEnd === null) continue;
2373
+ const closingMatch = /<\/a\s*>/gi;
2374
+ closingMatch.lastIndex = openingEnd;
2375
+ const closing = closingMatch.exec(value);
2376
+ if (!closing) continue;
2377
+ const end = closing.index + closing[0].length;
2378
+ const fragment = load(value.slice(start, end));
2379
+ const anchor = fragment("a").first();
2380
+ const href = anchor.attr("href");
2381
+ const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
2382
+ const destination = href && isSafePortableUrl(href) ? markdownLinkDestination(href) : null;
2383
+ output += value.slice(cursor, start);
2384
+ output += destination ? `[${label}](${destination})` : label;
2385
+ cursor = end;
2386
+ }
2387
+ return cursor === 0 ? value : `${output}${value.slice(cursor)}`;
2388
+ }
2389
+ function markdownNodeText(node) {
2390
+ if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
2391
+ if (node.type === "image" || node.type === "imageReference") return node.alt ?? "";
2392
+ return (node.children ?? []).map(markdownNodeText).join("");
2393
+ }
2394
+ function sanitizeMarkdownUrls(value) {
2395
+ let root;
2396
+ try {
2397
+ root = remoteMarkdownParser.parse(value);
2398
+ } catch {
2399
+ return null;
2400
+ }
2401
+ let hasUnsafeMdxSyntax = false;
2402
+ const validateMdxSyntax = (node) => {
2403
+ if (node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" || node.type === "mdxjsEsm") {
2404
+ hasUnsafeMdxSyntax = true;
2405
+ return;
2406
+ }
2407
+ if (node.type === "mdxJsxTextElement" || node.type === "mdxJsxFlowElement") {
2408
+ for (const attribute of node.attributes ?? []) {
2409
+ if (attribute.type !== "mdxJsxAttribute" || !attribute.name || /^on[A-Z]/.test(attribute.name)) {
2410
+ hasUnsafeMdxSyntax = true;
2411
+ return;
2412
+ }
2413
+ let staticValue = attribute.value;
2414
+ if (attribute.value && typeof attribute.value === "object") {
2415
+ if (attribute.value.type !== "mdxJsxAttributeValueExpression") {
2416
+ hasUnsafeMdxSyntax = true;
2417
+ return;
2418
+ }
2419
+ try {
2420
+ staticValue = JSON.parse(attribute.value.value);
2421
+ } catch {
2422
+ hasUnsafeMdxSyntax = true;
2423
+ return;
2424
+ }
2425
+ }
2426
+ if (PORTABLE_URL_PROP_NAMES.has(attribute.name) && (typeof staticValue !== "string" || !isSafePortableUrl(decodeHtmlAttribute(staticValue)))) {
2427
+ hasUnsafeMdxSyntax = true;
2428
+ return;
2429
+ }
2430
+ if (node.name === "source" || node.name === "picture") {
2431
+ const allowed = node.name === "picture" ? [] : ["src", "srcSet", "srcset", "sizes", "media", "type", "width", "height", "id"];
2432
+ if (!allowed.includes(attribute.name) || typeof attribute.value !== "string") {
2433
+ hasUnsafeMdxSyntax = true;
2434
+ return;
2435
+ }
2436
+ try {
2437
+ if (/^srcset$/i.test(attribute.name)) migrationImageCandidates(decodeHtmlAttribute(attribute.value), new URL("https://migration.invalid"));
2438
+ if (attribute.name === "src") migrationImageUrl(decodeHtmlAttribute(attribute.value), new URL("https://migration.invalid"));
2439
+ } catch {
2440
+ hasUnsafeMdxSyntax = true;
2441
+ return;
2442
+ }
2443
+ }
2444
+ }
2445
+ }
2446
+ for (const child of node.children ?? []) validateMdxSyntax(child);
2447
+ };
2448
+ validateMdxSyntax(root);
2449
+ if (hasUnsafeMdxSyntax) return null;
2450
+ const unsafeDefinitions = /* @__PURE__ */ new Set();
2451
+ const visitDefinitions = (node) => {
2452
+ if (node.type === "definition" && node.identifier && node.url && !isSafePortableUrl(node.url)) unsafeDefinitions.add(node.identifier.toLowerCase());
2453
+ for (const child of node.children ?? []) visitDefinitions(child);
2454
+ };
2455
+ visitDefinitions(root);
2456
+ const replacements = [];
2457
+ const replaceNode = (node, replacement) => {
2458
+ const start = node.position?.start.offset;
2459
+ const end = node.position?.end.offset;
2460
+ if (start === void 0 || end === void 0) return false;
2461
+ replacements.push({ start, end, value: replacement });
2462
+ return true;
2463
+ };
2464
+ const visitUrls = (node) => {
2465
+ const hasUnsafeDirectUrl = (node.type === "link" || node.type === "image") && typeof node.url === "string" && !isSafePortableUrl(node.url);
2466
+ const hasUnsafeReference = (node.type === "linkReference" || node.type === "imageReference") && typeof node.identifier === "string" && unsafeDefinitions.has(node.identifier.toLowerCase());
2467
+ if (hasUnsafeDirectUrl || hasUnsafeReference) {
2468
+ if (replaceNode(node, escapeMarkdownText(markdownNodeText(node)))) return;
2469
+ }
2470
+ if (node.type === "definition" && node.identifier && unsafeDefinitions.has(node.identifier.toLowerCase())) {
2471
+ if (replaceNode(node, "")) return;
2472
+ }
2473
+ for (const child of node.children ?? []) visitUrls(child);
2474
+ };
2475
+ visitUrls(root);
2476
+ return replacements.sort((left, right) => right.start - left.start).reduce((result, replacement) => `${result.slice(0, replacement.start)}${replacement.value}${result.slice(replacement.end)}`, value);
2477
+ }
2478
+ function hasUnsafePortableUrlProp(value) {
2479
+ const quotedProps = new RegExp(`\\b(?:${PORTABLE_URL_PROPS})\\s*=\\s*(['"])([^'"\\r\\n]*)\\1`, "gi");
2480
+ for (const match of value.matchAll(quotedProps)) {
2481
+ if (!isSafePortableUrl(decodeHtmlAttribute(match[2]))) return true;
2482
+ }
2483
+ const expressionProps = new RegExp(`\\b(?:${PORTABLE_URL_PROPS})\\s*=\\s*\\{\\s*("(?:\\\\.|[^"\\\\])*")\\s*\\}`, "gi");
2484
+ for (const match of value.matchAll(expressionProps)) {
2485
+ try {
2486
+ if (!isSafePortableUrl(decodeHtmlAttribute(JSON.parse(match[1])))) return true;
2487
+ } catch {
2488
+ return true;
2489
+ }
2490
+ }
2491
+ return false;
2492
+ }
2493
+ function portableHtmlToMarkdown(value) {
2494
+ return replacePortableHtmlAnchors(replacePortableHtmlImages(value)).replace(/<span\b[^>]*>|<\/span>/gi, "").replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_original, level, content) => {
2495
+ return `
2496
+ ${"#".repeat(Number(level))} ${content.trim()}
2497
+ `;
2498
+ }).replace(/<p\b[^>]*>([\s\S]*?)<\/p>/gi, (_original, content) => `
2499
+ ${content.trim()}
2500
+ `).replace(/<br\s*\/?\s*>/gi, "\n").replace(/<\/?(?:article|div|main|section)\b[^>]*>/gi, "");
2501
+ }
2502
+ function transformMarkdownProse(body, transform) {
2503
+ const output = [];
2504
+ let prose = [];
2505
+ let fenceCharacter = "";
2506
+ let fenceLength = 0;
2507
+ const flushProse = () => {
2508
+ if (prose.length === 0) return;
2509
+ output.push(transform(prose.join("\n")));
2510
+ prose = [];
2511
+ };
2512
+ for (const line of body.split("\n")) {
2513
+ const fence = line.match(/^\s{0,3}(`{3,}|~{3,})(.*)$/);
2514
+ if (fence && !fenceCharacter) {
2515
+ flushProse();
2516
+ fenceCharacter = fence[1][0];
2517
+ fenceLength = fence[1].length;
2518
+ output.push(line);
2519
+ continue;
2520
+ }
2521
+ if (fenceCharacter) {
2522
+ output.push(line);
2523
+ if (fence && fence[1][0] === fenceCharacter && fence[1].length >= fenceLength && fence[2].trim() === "") {
2524
+ fenceCharacter = "";
2525
+ fenceLength = 0;
2526
+ }
2527
+ continue;
2528
+ }
2529
+ prose.push(line);
2530
+ }
2531
+ flushProse();
2532
+ return output.join("\n");
2533
+ }
2534
+ function sanitizeRemoteMarkdown(body) {
2535
+ let fenceCharacter = "";
2536
+ let fenceLength = 0;
2537
+ const portableBody = transformMarkdownProse(body, portableHtmlToMarkdown);
2538
+ const sanitized = portableBody.split("\n").map((line) => {
2539
+ const fence = line.match(/^\s{0,3}(`{3,}|~{3,})(.*)$/);
2540
+ if (fence && !fenceCharacter) {
2541
+ fenceCharacter = fence[1][0];
2542
+ fenceLength = fence[1].length;
2543
+ return line;
2544
+ }
2545
+ if (fence && fence[1][0] === fenceCharacter && fence[1].length >= fenceLength && fence[2].trim() === "") {
2546
+ fenceCharacter = "";
2547
+ fenceLength = 0;
2548
+ return line;
2549
+ }
2550
+ if (fenceCharacter) return line;
2551
+ return portableHtmlToMarkdown(stripEventHandlerProps(line.replace(/!\[([^\]]*)\]\(\s*([^)]+)\)/g, (original, alternative, destination) => isSafePortableUrl(decodeHtmlAttribute(destination.replace(/^<|>$/g, ""))) ? original : escapeMarkdownText(alternative))));
2552
+ }).join("\n");
2553
+ const urlSafeMarkdown = sanitizeMarkdownUrls(sanitized);
2554
+ if (urlSafeMarkdown === null) return null;
2555
+ const executable = withoutFencedCode(urlSafeMarkdown).replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/<(?:https?:\/\/|mailto:)[^>]+>/gi, "").replace(/(`+)[\s\S]*?\1/g, "");
2556
+ if (/^\s*(?:import|export)\s/m.test(executable)) return null;
2557
+ let hasUnsafeExpression = false;
2558
+ const withoutStaticProps = executable.replace(/=\{([^{}\n]*)\}/g, (original, value) => {
2559
+ try {
2560
+ JSON.parse(value);
2561
+ return '=""';
2562
+ } catch {
2563
+ hasUnsafeExpression = true;
2564
+ return original;
2565
+ }
2566
+ }).replace(/\\[{}]/g, "");
2567
+ if (hasUnsafeExpression || /[{}]/.test(withoutStaticProps) || /\bon[A-Z][A-Za-z]*\s*=/g.test(withoutStaticProps) || hasUnsafePortableUrlProp(executable)) return null;
2568
+ for (const match of withoutStaticProps.matchAll(/<\/?([A-Za-z][A-Za-z0-9.]*)(?:\s|>|\/)/g)) {
2569
+ const component = match[1].split(".", 1)[0];
2570
+ if (!PORTABLE_MDX_COMPONENTS.has(component)) return null;
2571
+ }
2572
+ return urlSafeMarkdown;
2573
+ }
2574
+ function remoteMarkdownMetadata(body) {
2575
+ if (!/^---\r?\n/.test(body)) return { content: body };
2576
+ const end = body.slice(4, 65540).search(/\r?\n---(?:\r?\n|$)/);
2577
+ if (end < 0) return { content: body };
2578
+ const frontmatter = body.slice(4, end + 4);
2579
+ const contentStart = end + 4 + body.slice(end + 4).match(/^\r?\n---(?:\r?\n|$)/)[0].length;
2580
+ function scalar(key) {
2581
+ const value = frontmatter.match(new RegExp(`^${key}:\\s*(.+)$`, "m"))?.[1].trim();
2582
+ if (!value) return void 0;
2583
+ if (/^['"].*['"]$/.test(value)) {
2584
+ try {
2585
+ const parsed = JSON.parse(value.replace(/^'/, '"').replace(/'$/, '"'));
2586
+ return typeof parsed === "string" ? parsed : void 0;
2587
+ } catch {
2588
+ return value.slice(1, -1);
2589
+ }
2590
+ }
2591
+ return value.slice(0, 500);
2592
+ }
2593
+ return {
2594
+ content: body.slice(contentStart),
2595
+ title: scalar("title"),
2596
+ description: scalar("description"),
2597
+ sidebarTitle: scalar("sidebarTitle"),
2598
+ tag: scalar("tag"),
2599
+ mode: scalar("mode"),
2600
+ hidden: scalar("hidden") === "true" ? true : void 0,
2601
+ noindex: scalar("noindex") === "true" ? true : void 0
2602
+ };
2603
+ }
2604
+ function stripMintlifyDocumentationIndex(body) {
2605
+ const lines = body.split(/\r?\n/);
2606
+ if (!/^>\s*##\s+Documentation Index\s*$/i.test(lines[0] ?? "") || !/^>.*\/llms(?:-full)?\.txt/i.test(lines[1] ?? "") || !/^>.*discover all available pages/i.test(lines[2] ?? "")) return body;
2607
+ let index = 3;
2608
+ while (index < lines.length && /^>/.test(lines[index])) index += 1;
2609
+ while (index < lines.length && lines[index].trim() === "") index += 1;
2610
+ return lines.slice(index).join("\n");
2611
+ }
2612
+ function extractEmbeddedOpenApi(content) {
2613
+ const fencePattern = /^\s{0,3}(`{3,}|~{3,})(?:yaml|yml|json)\s+(\S+\.(?:yaml|yml|json))\s+(delete|get|head|options|patch|post|put|trace)\s+(\/\S+)\s*\r?\n([\s\S]*?)^\s{0,3}\1\s*$/im;
2614
+ const match = fencePattern.exec(content);
2615
+ if (!match) return { content };
2616
+ try {
2617
+ const parsed = parseYaml2(match[5], { maxAliasCount: 50 });
2618
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { content };
2619
+ const document = parsed;
2620
+ if (typeof document.openapi !== "string" && typeof document.swagger !== "string") return { content };
2621
+ const method = match[3].toUpperCase();
2622
+ const path = match[4];
2623
+ const paths = document.paths;
2624
+ if (!paths || typeof paths !== "object" || Array.isArray(paths)) return { content };
2625
+ const pathItem = paths[path];
2626
+ if (!pathItem || typeof pathItem !== "object" || Array.isArray(pathItem) || !(match[3].toLowerCase() in pathItem)) return { content };
2627
+ const before = content.slice(0, match.index).replace(/(?:^|\n)##\s+OpenAPI\s*\n\s*$/i, "\n");
2628
+ const after = content.slice(match.index + match[0].length);
2629
+ return {
2630
+ content: `${before}${after}`.trim(),
2631
+ openapi: `${method} ${path}`,
2632
+ fragment: {
2633
+ method,
2634
+ path,
2635
+ document
2636
+ }
2637
+ };
2638
+ } catch {
2639
+ return { content };
2640
+ }
2641
+ }
2642
+ function markdownPage(document, id, platform, warn) {
2643
+ const parsed = remoteMarkdownMetadata(document.body);
2644
+ let content = stripMintlifyDocumentationIndex(parsed.content).trimStart();
2645
+ const titleMatch = content.match(/^#\s+(.+)\r?\n/);
2646
+ const title = parsed.title ?? titleMatch?.[1] ?? id.split("/").at(-1) ?? "Introduction";
2647
+ if (titleMatch) content = content.slice(titleMatch[0].length).trimStart();
2648
+ let description = parsed.description;
2649
+ if (!description) {
2650
+ const quoteMatch = content.match(/^(>[^\r\n]*(?:\r?\n>[^\r\n]*)*)\r?\n/);
2651
+ if (quoteMatch) {
2652
+ description = plainDescription(quoteMatch[1].replace(/^>\s?/gm, ""));
2653
+ content = content.slice(quoteMatch[0].length).trimStart();
2654
+ }
2655
+ }
2656
+ const embeddedOpenApi = extractEmbeddedOpenApi(content);
2657
+ const raw = [
2658
+ "---",
2659
+ `title: ${JSON.stringify(title)}`,
2660
+ ...description ? [`description: ${JSON.stringify(description)}`] : [],
2661
+ ...parsed.sidebarTitle ? [`sidebarTitle: ${JSON.stringify(parsed.sidebarTitle)}`] : [],
2662
+ ...parsed.tag ? [`tag: ${JSON.stringify(parsed.tag)}`] : [],
2663
+ ...parsed.mode ? [`mode: ${JSON.stringify(parsed.mode)}`] : [],
2664
+ ...parsed.hidden ? ["hidden: true"] : [],
2665
+ ...parsed.noindex ? ["noindex: true"] : [],
2666
+ ...embeddedOpenApi.openapi ? [`openapi: ${JSON.stringify(embeddedOpenApi.openapi)}`] : [],
2667
+ "---",
2668
+ "",
2669
+ embeddedOpenApi.content
2670
+ ].join("\n");
2671
+ return {
2672
+ // The crawler knows the source platform (detected or caller-provided)
2673
+ // before it ever parses a page; an omitted platform would otherwise
2674
+ // apply every platform's textual renames to content of unknown origin.
2675
+ page: parseMarkdownPage({ id, raw, source: document.finalUrl.toString(), platform, ...warn ? { warn } : {} }),
2676
+ openApiFragment: embeddedOpenApi.fragment
2677
+ };
2678
+ }
2679
+ function htmlPage(document, id, options = {}) {
2680
+ const $ = load(document.body);
2681
+ const links = $('nav a[href], main a[href], article a[href], [role="main"] a[href]').map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
2682
+ const scopedTitle = options.omitDocumentHeading ? $('main article, article, main, [role="main"]').first().find("h1").first().text().trim() : "";
2683
+ const title = scopedTitle || $("main h1, article h1, h1").first().text().trim() || $('meta[property="og:title"]').attr("content")?.trim() || $("title").text().split("|")[0].trim();
2684
+ const description = $('meta[name="description"]').attr("content")?.trim() || $('meta[property="og:description"]').attr("content")?.trim();
2685
+ const content = $('main article, article, main, [role="main"]').first();
2686
+ const root = content.length ? content : $("body");
2687
+ let documentHeadingAnchor = "";
2688
+ const excludedElements = options.omitDocumentHeading ? "script,style,noscript,nav,footer,aside,form,button,svg,iframe,object,embed,link,meta" : "script,style,noscript,nav,header,footer,aside,form,button,svg,iframe,object,embed,link,meta";
2689
+ root.find(excludedElements).remove();
2690
+ root.find(".theme-doc-version-badge,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
2691
+ if (options.omitDocumentHeading) {
2692
+ const heading = root.find("h1").first();
2693
+ if (heading.length && heading.text().trim() === title) {
2694
+ const anchorId = heading.attr("id");
2695
+ if (anchorId) documentHeadingAnchor = `<a id="${migrationImageAttribute(anchorId)}" />
2696
+
2697
+ `;
2698
+ heading.remove();
2699
+ }
2700
+ }
2701
+ root.find("a[href]").each((_index, element) => {
2702
+ const href = $(element).attr("href");
2703
+ if (!href) return;
2704
+ try {
2705
+ const resolved = new URL(href, document.finalUrl);
2706
+ if (["http:", "https:", "mailto:", "tel:"].includes(resolved.protocol)) {
2707
+ $(element).attr("href", resolved.toString());
2708
+ } else {
2709
+ $(element).removeAttr("href");
2710
+ }
2711
+ } catch {
2712
+ $(element).removeAttr("href");
2713
+ }
2714
+ });
2715
+ root.find("img[src]").each((_index, element) => {
2716
+ const src = $(element).attr("src");
2717
+ if (!src) return;
2718
+ try {
2719
+ const resolved = new URL(src, document.finalUrl);
2720
+ if (["http:", "https:"].includes(resolved.protocol)) {
2721
+ $(element).attr("src", resolved.toString());
2722
+ } else {
2723
+ $(element).removeAttr("src");
2724
+ }
2725
+ } catch {
2726
+ $(element).removeAttr("src");
2727
+ }
2728
+ });
2729
+ const turndown = new TurndownService({ codeBlockStyle: "fenced", emDelimiter: "_", headingStyle: "atx" });
2730
+ turndown.remove(["script", "style", "noscript"]);
2731
+ const anchor = (element) => {
2732
+ const value = element.getAttribute("id");
2733
+ return options.preserveAnchors && value ? `<a id="${value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/\{/g, "&#123;")}" />
2734
+
2735
+ ` : "";
2736
+ };
2737
+ if (options.preserveAnchors) turndown.addRule("migration-source-anchor", {
2738
+ filter: (node) => node.hasAttribute("id") && node.nodeName !== "PRE",
2739
+ replacement: (content2, node) => {
2740
+ const element = node;
2741
+ const heading = element.nodeName.match(/^H([1-6])$/);
2742
+ return `
2743
+
2744
+ ${anchor(element)}${heading ? `${"#".repeat(Number(heading[1]))} ` : ""}${content2}
2745
+
2746
+ `;
2747
+ }
2748
+ });
2749
+ if (options.preserveResponsiveImages) {
2750
+ const image = (element) => {
2751
+ const tag = element.nodeName.toLowerCase();
2752
+ const attributes = [];
2753
+ for (const name of ["src", "srcset", "sizes", "alt", "title", "media", "type", "width", "height", "id"]) {
2754
+ let value = element.getAttribute(name);
2755
+ if (value === null) continue;
2756
+ if (name === "src") value = migrationImageUrl(value, document.finalUrl);
2757
+ if (name === "srcset") value = renderMigrationSrcset(migrationImageCandidates(value, document.finalUrl));
2758
+ const prop = name === "srcset" ? "srcSet" : name;
2759
+ attributes.push(`${prop}="${migrationImageAttribute(value)}"`);
2760
+ }
2761
+ return `<${tag}${attributes.length ? ` ${attributes.join(" ")}` : ""} />`;
2762
+ };
2763
+ turndown.addRule("migration-responsive-image", {
2764
+ filter: (node) => ["IMG", "SOURCE"].includes(node.nodeName),
2765
+ replacement: (_content, node) => image(node)
2766
+ });
2767
+ turndown.addRule("migration-responsive-picture", {
2768
+ filter: "picture",
2769
+ replacement: (content2) => `
2770
+
2771
+ <picture>
2772
+ ${content2.trim()}
2773
+ </picture>
2774
+
2775
+ `
2776
+ });
2777
+ }
2778
+ turndown.addRule("docusaurus-admonition", {
2779
+ filter: (node) => node.nodeName === "DIV" && node.classList.contains("theme-admonition"),
2780
+ replacement: (_content, node) => {
2781
+ const element = node;
2782
+ const className = element.className;
2783
+ const tag = /admonition-(?:danger)/.test(className) ? "Error" : /admonition-(?:caution|warning)/.test(className) ? "Warning" : /admonition-info/.test(className) ? "Info" : "Note";
2784
+ const fragment = load(element.outerHTML);
2785
+ const clone = fragment(".theme-admonition").first();
2786
+ const title2 = clone.find('[class*="admonitionHeading"],.admonition-heading').first();
2787
+ const titleText = title2.text().trim();
2788
+ title2.remove();
2789
+ const markdown = turndown.turndown(clone.html() ?? "").trim();
2790
+ return `
2791
+
2792
+ ${anchor(element)}<${tag}>
2793
+ ${titleText ? `**${escapeMarkdownText(titleText)}**
2794
+
2795
+ ` : ""}${markdown}
2796
+ </${tag}>
2797
+
2798
+ `;
2799
+ }
2800
+ });
2801
+ turndown.addRule("docusaurus-tabs", {
2802
+ filter: (node) => node.nodeName === "DIV" && node.classList.contains("tabs-container"),
2803
+ replacement: (_content, node) => {
2804
+ const element = node;
2805
+ const fragment = load(element.outerHTML);
2806
+ const container = fragment(".tabs-container").first();
2807
+ const labels = container.children("ul.tabs").children("li").toArray().map((item) => fragment(item).text().trim() || "Tab");
2808
+ const panels = container.find('[role="tabpanel"]').toArray().filter((panel) => fragment(panel).closest(".tabs-container")[0] === container[0]);
2809
+ if (panels.length === 0) return _content;
2810
+ const tabs = panels.map((panel, index) => {
2811
+ const panelId = fragment(panel).attr("id");
2812
+ const tab = panelId ? container.find('[role="tab"]').toArray().find((item) => fragment(item).attr("aria-controls") === panelId) : void 0;
2813
+ const title2 = migrationImageAttribute((tab ? fragment(tab).text().trim() : labels[index]) || `Tab ${index + 1}`);
2814
+ const panelAnchor = options.preserveAnchors && panelId ? `<a id="${migrationImageAttribute(panelId)}" />
2815
+
2816
+ ` : "";
2817
+ return `<Tab title="${title2}">
2818
+ ${panelAnchor}${turndown.turndown(fragment(panel).html() ?? "").trim()}
2819
+ </Tab>`;
2820
+ });
2821
+ return `
2822
+
2823
+ ${anchor(element)}<Tabs>
2824
+ ${tabs.join("\n\n")}
2825
+ </Tabs>
2826
+
2827
+ `;
2828
+ }
2829
+ });
2830
+ turndown.addRule("gfm-table", {
2831
+ filter: "table",
2832
+ replacement: (_content, node) => {
2833
+ const fragment = load(node.outerHTML);
2834
+ const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => escapeMarkdownText(
2835
+ fragment(cell).text().replace(/\s+/g, " ").trim(),
2836
+ true
2837
+ ))).filter((row) => row.length > 0);
2838
+ if (rows.length === 0) return "";
2839
+ const width = Math.max(...rows.map((row) => row.length));
2840
+ const normalized = rows.map((row) => [...row, ...Array(width - row.length).fill("")]);
2841
+ return `
2842
+
2843
+ | ${normalized[0].join(" | ")} |
2844
+ | ${Array(width).fill("---").join(" | ")} |
2845
+ ${normalized.slice(1).map((row) => `| ${row.join(" | ")} |`).join("\n")}
2846
+
2847
+ `;
2848
+ }
2849
+ });
2850
+ turndown.addRule("migration-safe-fenced-code", {
2851
+ filter: (node) => node.nodeName === "PRE",
2852
+ replacement: (_content, node) => {
2853
+ const element = node;
2854
+ const fragment = load(element.outerHTML);
2855
+ const tokenLines = fragment(".token-line").toArray();
2856
+ const code = (tokenLines.length > 0 ? tokenLines.map((line) => fragment(line).text()).join("\n") : fragment("pre").text()).replace(/\n$/, "");
2857
+ const language = fragment("code").attr("class")?.match(/(?:language-|lang-)([A-Za-z0-9_+-]+)/)?.[1] ?? "";
2858
+ const longestFence = Math.max(0, ...[...code.matchAll(/`+/g)].map((match) => match[0].length));
2859
+ const fence = "`".repeat(Math.max(3, longestFence + 1));
2860
+ return `
2861
+
2862
+ ${anchor(element)}${fence}${language}
2863
+ ${code}
2864
+ ${fence}
2865
+
2866
+ `;
2867
+ }
2868
+ });
2869
+ const escape = turndown.escape.bind(turndown);
2870
+ turndown.escape = (text) => escape(text).replace(/[<{}]/g, (match) => `\\${match}`);
2871
+ const body = `${documentHeadingAnchor}${turndown.turndown(root.html() ?? "").trim()}`;
2872
+ if (!title || body.length < 40 && $("article").length === 0) return { page: null, links };
2873
+ return {
2874
+ page: {
2875
+ id,
2876
+ navigationId: id,
2877
+ title,
2878
+ description: description || plainDescription(body) || `Documentation imported from ${document.finalUrl.hostname}.`,
2879
+ keywords: [],
2880
+ body,
2881
+ source: document.finalUrl.toString()
2882
+ },
2883
+ links
2884
+ };
2885
+ }
2886
+
2887
+ // src/url-run.ts
2888
+ var CONVERTER_VERSION = "url-evidence-v2";
2889
+ var parser = unified3().use(remarkParse3).use(remarkMdx3);
2890
+ var ACCEPT = "text/markdown,text/html,application/xhtml+xml;q=0.9";
2891
+ var MAX_RESPONSE_BYTES = 8e6;
2892
+ var MAX_ASSET_BYTES = 1e7;
2893
+ var MAX_RECORDS = 1e5;
2894
+ 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"]);
2895
+ function hash(value) {
2896
+ return createHash("sha256").update(value).digest("hex");
2897
+ }
2898
+ function pageHash(page) {
2899
+ const canonical2 = (value) => Array.isArray(value) ? value.map(canonical2) : value !== null && typeof value === "object" ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, field]) => [key, canonical2(field)])) : value;
2900
+ return hash(JSON.stringify(canonical2(page)));
2901
+ }
2902
+ function inScope(url, source, scope) {
2903
+ return url.origin === source.origin && !url.username && !url.password && (scope === "/" || url.pathname === scope || url.pathname.startsWith(`${scope}/`));
2904
+ }
2905
+ function isDiscoveryIndex(url, source) {
2906
+ return url.origin === source.origin && /\/(?:llms(?:-full)?|robots)\.txt$|\/sitemap[^/]*\.xml$/i.test(url.pathname);
2907
+ }
2908
+ function canonical(value, base) {
2909
+ try {
2910
+ const url = validateMigrationUrl(new URL(value, base).toString());
2911
+ url.pathname = url.pathname.replace(/\/+$/, "") || "/";
2912
+ return url.toString();
2913
+ } catch {
2914
+ return null;
2915
+ }
2916
+ }
2917
+ function classifyMigrationResponse(document) {
2918
+ if (document.status && document.status >= 400) return "unsupported";
2919
+ if (/\/(?:llms(?:-full)?|robots)\.txt$|\/sitemap[^/]*\.xml$|\/search(?:\.html)?$/i.test(document.finalUrl.pathname) || /(?:xml|json)/i.test(document.contentType)) return "index";
2920
+ if (/^(?:image|audio|video)\//i.test(document.contentType) || /\.(?:pdf|zip|png|jpe?g|webp|gif|svg|ico|mp4|webm)$/i.test(document.finalUrl.pathname)) return "asset";
2921
+ return /(?:html|markdown|text\/plain)/i.test(document.contentType) || /\.mdx?$/i.test(document.finalUrl.pathname) ? "page" : "unsupported";
2922
+ }
2923
+ function markdownNodes(body) {
2924
+ const nodes = [];
2925
+ const visit = (node) => {
2926
+ nodes.push(node);
2927
+ for (const child of node.children ?? []) visit(child);
2928
+ };
2929
+ visit(parser.parse(body));
2930
+ return nodes;
2931
+ }
2932
+ function markdownCode(body) {
2933
+ return markdownNodes(body).filter((node) => node.type === "code").map((node) => node.value ?? "");
2934
+ }
2935
+ function alternativeBlocks(body, baseline) {
2936
+ const blocks = (value) => (parser.parse(value).children ?? []).map((node) => value.slice(node.position.start.offset, node.position.end.offset));
2937
+ const originals = new Set(blocks(baseline));
2938
+ return blocks(body).filter((block) => !originals.has(block)).join("\n\n");
2939
+ }
2940
+ function linksOf(document) {
2941
+ if (/html/i.test(document.contentType)) {
2942
+ const $ = load2(document.body);
2943
+ const root = $('main article,article,main,[role="main"]').first();
2944
+ root.find("nav,footer,aside").remove();
2945
+ return [...root.find("a[href]").toArray(), ...$('link[rel="sitemap"][href],link[rel="alternate"][href]').toArray()].map((node) => $(node).attr("href")).filter(Boolean);
2946
+ }
2947
+ if (/xml/i.test(document.contentType) || /<urlset|<sitemapindex/i.test(document.body)) {
2948
+ const $ = load2(document.body, { xmlMode: true });
2949
+ return $("loc").toArray().map((node) => $(node).text().trim());
2950
+ }
2951
+ return [
2952
+ ...markdownNodes(document.body).flatMap((node) => node.url ? [node.url] : []),
2953
+ ...[...document.body.matchAll(/^Sitemap:\s*(\S+)/gmi)].map((match) => match[1])
2954
+ ];
2955
+ }
2956
+ function htmlEvidence(document) {
2957
+ const $ = load2(document.body);
2958
+ const root = $('main article,article,main,[role="main"]').first();
2959
+ const article = root.length ? root : $("body");
2960
+ article.find("nav,footer,aside,form,script,style").remove();
2961
+ return {
2962
+ anchors: article.find("[id]").toArray().map((node) => $(node).attr("id")).filter(Boolean),
2963
+ code: article.find("pre").toArray().map((node) => {
2964
+ const lines = $(node).find(".token-line");
2965
+ return lines.length ? lines.toArray().map((line) => $(line).text()).join("\n") : $(node).text();
2966
+ })
2967
+ };
2968
+ }
2969
+ function evidenceOf(capture) {
2970
+ const technical = /html/i.test(capture.contentType) ? htmlEvidence(capture) : { code: markdownCode(capture.body), anchors: markdownHeadingAnchors(capture.body) };
2971
+ return {
2972
+ sha256: hash(capture.body),
2973
+ byteLength: Buffer.byteLength(capture.body),
2974
+ finalUrl: capture.finalUrl.toString(),
2975
+ contentType: capture.contentType,
2976
+ ...capture.artifactRef ? { artifactRef: capture.artifactRef } : {},
2977
+ states: (capture.states ?? []).map((state) => ({ id: state.id, label: state.label, sha256: hash(state.response.body) })),
2978
+ ...technical,
2979
+ links: linksOf(capture)
2980
+ };
2981
+ }
2982
+ function markdownHeadingAnchors(body) {
2983
+ const seen = /* @__PURE__ */ new Map();
2984
+ return markdownNodes(body).filter((node) => node.type === "heading").map((node) => {
2985
+ const text = (value) => value.value ?? (value.children ?? []).map(text).join("");
2986
+ const base = text(node).toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s/g, "-");
2987
+ const occurrence = seen.get(base) ?? 0;
2988
+ seen.set(base, occurrence + 1);
2989
+ return occurrence ? `${base}-${occurrence}` : base;
2990
+ });
2991
+ }
2992
+ function sourceNavigation(capture) {
2993
+ if (capture.navigation) {
2994
+ const resolve3 = (nodes, depth = 0) => {
2995
+ if (depth > 32 || nodes.length > 1e4) throw new Error("Source navigation exceeds its depth or width safety bound.");
2996
+ return nodes.map((node) => ({ label: node.label, ...node.url ? { url: new URL(node.url, capture.finalUrl).toString() } : {}, ...node.children ? { children: resolve3(node.children, depth + 1) } : {} }));
2997
+ };
2998
+ return resolve3(capture.navigation);
2999
+ }
3000
+ if (!/html/i.test(capture.contentType)) return [];
3001
+ const $ = load2(capture.body);
3002
+ const candidates = ['.theme-doc-sidebar-menu,[aria-label*="sidebar" i]', "aside", '[role="navigation"],nav'];
3003
+ 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) ?? $("aside").first();
3004
+ if (!root.length) return [];
3005
+ const walk = (element, excluded) => {
3006
+ const result = [];
3007
+ $(element).children().each((_index, node) => {
3008
+ if (node === excluded) return;
3009
+ const item = $(node);
3010
+ if (item.is("a[href]")) {
3011
+ result.push({ label: item.text().replace(/\s+/g, " ").trim(), url: new URL(item.attr("href"), capture.finalUrl).toString() });
3012
+ } else if (item.is("li,details") || item.attr("data-migration-group") !== void 0 || item.children("h2,h3,h4,h5,h6").length > 0) {
3013
+ const directLink = item.children("a[href]").first();
3014
+ const direct = directLink.length ? directLink : item.children("div").children("a[href]").first();
3015
+ const label = direct.text().trim() || item.children("summary,button,span,h2,h3,h4,h5,h6").first().text().trim();
3016
+ const children = walk(node, direct[0]);
3017
+ if (label) result.push({ label, ...direct.length ? { url: new URL(direct.attr("href"), capture.finalUrl).toString() } : {}, ...children.length ? { children } : {} });
3018
+ else result.push(...children);
3019
+ } else result.push(...walk(node, excluded));
3020
+ });
3021
+ return result;
3022
+ };
3023
+ try {
3024
+ return walk(root);
3025
+ } catch {
3026
+ return [];
3027
+ }
3028
+ }
3029
+ function idForUrl(url, scope) {
3030
+ const relative4 = scope === "/" ? url.pathname.slice(1) : url.pathname.slice(scope.length).replace(/^\//, "");
3031
+ const id = pageIdFromReference(relative4 || "introduction", true);
3032
+ return id && `${id}${url.search ? `--${hash(url.search).slice(0, 12)}` : ""}`;
3033
+ }
3034
+ function convert(document, id) {
3035
+ if (/html/i.test(document.contentType)) {
3036
+ const technical = htmlEvidence(document);
3037
+ const page = htmlPage(document, id, { preserveAnchors: true, preserveResponsiveImages: true, omitDocumentHeading: true }).page;
3038
+ if (!page) return null;
3039
+ page.descriptionPlacement = "body";
3040
+ const nodes = markdownNodes(page.body).filter((node) => node.type === "code");
3041
+ if (nodes.length === technical.code.length) {
3042
+ for (let index = nodes.length - 1; index >= 0; index--) {
3043
+ const node = nodes[index];
3044
+ const start = node.position?.start.offset;
3045
+ const end = node.position?.end.offset;
3046
+ if (start === void 0 || end === void 0) continue;
3047
+ const original = page.body.slice(start, end);
3048
+ const info = original.split("\n", 1)[0].replace(/^`+|^~+/, "");
3049
+ const code = technical.code[index];
3050
+ const fence = "`".repeat(Math.max(3, ...[...code.matchAll(/`+/g)].map((match) => match[0].length + 1)));
3051
+ page.body = `${page.body.slice(0, start)}${fence}${info}
3052
+ ${code}
3053
+ ${fence}${page.body.slice(end)}`;
3054
+ }
3055
+ }
3056
+ return page;
3057
+ }
3058
+ const safe = sanitizeRemoteMarkdown(document.body);
3059
+ return safe === null ? null : markdownPage({ ...document, body: safe }, id, "generic").page;
3060
+ }
3061
+ function assetReferences(capture) {
3062
+ if (!/html/i.test(capture.contentType)) {
3063
+ return markdownNodes(capture.body).filter((node) => node.type === "image").flatMap((node) => node.url ? [node.url] : []);
3064
+ }
3065
+ const $ = load2(capture.body);
3066
+ const root = $('main article,article,main,[role="main"]').first();
3067
+ return [
3068
+ ...root.find("img[src],video[poster],a[download][href]").toArray().map((node) => $(node).attr("src") ?? $(node).attr("poster") ?? $(node).attr("href")),
3069
+ ...root.find("[srcset]").toArray().flatMap((node) => migrationImageCandidates($(node).attr("srcset") ?? "", capture.finalUrl).map((candidate) => candidate.url))
3070
+ ].filter(Boolean);
3071
+ }
3072
+ function unresolvedStaticControls(body) {
3073
+ const $ = load2(body);
3074
+ const root = $('main article,article,main,[role="main"]').first();
3075
+ const tabs = root.find('[role="tab"]').toArray();
3076
+ for (const tab of tabs) {
3077
+ const controls = $(tab).attr("aria-controls");
3078
+ const panels = controls ? root.find("[id]").filter((_index, node) => $(node).attr("id") === controls) : root.find('[role="tabpanel"]').eq(tabs.indexOf(tab));
3079
+ if (panels.length !== 1 || !panels.text().trim() && !panels.find("pre,img,table,svg").length || panels.find('[aria-busy="true"],[role="progressbar"]').length) return true;
3080
+ }
3081
+ return root.find("details:not([open])").toArray().some((node) => {
3082
+ const clone = $(node).clone();
3083
+ clone.children("summary").remove();
3084
+ return !clone.text().trim() && !clone.find("pre,img,table,svg").length;
3085
+ });
3086
+ }
3087
+ async function defaultAssetFetcher(url, signal) {
3088
+ const response = await fetch(url, { signal: signal ?? AbortSignal.timeout(15e3) });
3089
+ if (!response.ok) throw new Error(`Asset returned ${response.status}.`);
3090
+ if (Number(response.headers.get("content-length")) > MAX_ASSET_BYTES) throw new Error("Asset exceeds size limit.");
3091
+ const reader = response.body?.getReader();
3092
+ const chunks = [];
3093
+ let size = 0;
3094
+ if (reader) while (true) {
3095
+ const next = await reader.read();
3096
+ if (next.done) break;
3097
+ size += next.value.length;
3098
+ if (size > MAX_ASSET_BYTES) {
3099
+ await reader.cancel();
3100
+ throw new Error("Asset exceeds size limit.");
3101
+ }
3102
+ chunks.push(next.value);
3103
+ }
3104
+ return { url: response.url || url.toString(), contentType: response.headers.get("content-type") ?? "", content: Buffer.concat(chunks) };
3105
+ }
3106
+ function acceptedAsset(asset) {
3107
+ if (asset.content.byteLength > MAX_ASSET_BYTES) return null;
3108
+ const mime = asset.contentType.split(";", 1)[0].toLowerCase().trim();
3109
+ if (mime === "image/svg+xml") return isPassiveSvg(asset.content) ? "svg" : null;
3110
+ const signature = Buffer.from(asset.content.slice(0, 16));
3111
+ const extensions = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp", "application/pdf": "pdf", "image/avif": "avif" };
3112
+ const extension = extensions[mime];
3113
+ if (!extension) return null;
3114
+ const isSignatureValid = extension === "png" ? signature.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) : extension === "jpg" ? signature[0] === 255 && signature[1] === 216 : extension === "gif" ? /^GIF8[79]a/.test(signature.toString()) : extension === "webp" ? signature.toString("ascii", 0, 4) === "RIFF" && signature.toString("ascii", 8, 12) === "WEBP" : extension === "pdf" ? signature.toString("ascii", 0, 5) === "%PDF-" : signature.toString("ascii", 4, 8) === "ftyp" && /avif|avis/.test(signature.toString("ascii", 8));
3115
+ return isSignatureValid ? extension : null;
3116
+ }
3117
+ function validRecord(record) {
3118
+ if (record.outcome !== "imported") return true;
3119
+ 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.assets?.every((asset) => /^migration-assets\/[a-f0-9]{64}\.[a-z0-9]+$/.test(asset.path) && hash(Buffer.from(asset.base64, "base64")) === asset.sha256));
3120
+ }
3121
+ function navigationConfig(navigation, records) {
3122
+ const project = (nodes) => nodes.flatMap((node) => {
3123
+ const record = node.url ? records.get(canonical(node.url, node.url) ?? "") : void 0;
3124
+ const target = record?.outcome === "alias" ? records.get(record.aliasOf ?? "") : record;
3125
+ const page = target?.page;
3126
+ const children = project(node.children ?? []);
3127
+ if (children.length) {
3128
+ const contains = (entries) => entries.some((entry) => typeof entry === "string" ? entry === page?.navigationId : contains(entry.pages));
3129
+ return [{ group: node.label || "Documentation", pages: [...page && !contains(children) ? [page.navigationId] : [], ...children] }];
3130
+ }
3131
+ return page ? [page.navigationId] : [];
3132
+ });
3133
+ const pages = project(navigation);
3134
+ return pages.length ? { tabs: [{ tab: "Documentation", pages }] } : null;
3135
+ }
3136
+ function observedLocale(document, id, scopePath) {
3137
+ const issues = [];
3138
+ const canonicalLanguage = (value) => {
3139
+ if (!/^[a-z]{2,3}(?:-[a-z0-9]{2,8}){0,4}$/i.test(value)) return void 0;
3140
+ try {
3141
+ return Intl.getCanonicalLocales(value)[0];
3142
+ } catch {
3143
+ return void 0;
3144
+ }
3145
+ };
3146
+ const candidates = [];
3147
+ if (document.locale) {
3148
+ const locale = canonicalLanguage(document.locale);
3149
+ if (!locale) throw new Error("Capture locale is not a portable language code.");
3150
+ candidates.push({ source: "capture", value: locale });
3151
+ }
3152
+ const urlLocale = [id.split("/", 1)[0], ...scopePath.split("/"), document.finalUrl.pathname.split("/").filter(Boolean)[0]].find((segment) => segment && LOCALES.has(segment.toLowerCase()));
3153
+ if (urlLocale) candidates.push({ source: "URL", value: canonicalLanguage(urlLocale) });
3154
+ const declared = /html/i.test(document.contentType) ? load2(document.body)("html").attr("lang")?.trim() : void 0;
3155
+ if (declared) {
3156
+ const locale = canonicalLanguage(declared);
3157
+ if (locale) candidates.push({ source: "html lang", value: locale });
3158
+ else issues.push({ code: "unsupported-component", source: document.finalUrl.href, message: "The source declares an unsupported HTML language; review its locale configuration." });
3159
+ }
3160
+ if (new Set(candidates.map((candidate) => candidate.value)).size > 1) {
3161
+ issues.push({ code: "unsupported-component", source: document.finalUrl.href, message: `Observed locale metadata disagrees (${candidates.map((candidate) => `${candidate.source}: ${candidate.value}`).join(", ")}); review the source language.` });
3162
+ }
3163
+ return { locale: candidates[0]?.value, issues };
3164
+ }
3165
+ function localeProjection(checkpoint) {
3166
+ const pages = checkpoint.records.flatMap((record) => record.page ? [record.page] : []);
3167
+ const canonicalLocale = (page) => {
3168
+ try {
3169
+ return page.locale ? Intl.getCanonicalLocales(page.locale)[0] : void 0;
3170
+ } catch {
3171
+ return void 0;
3172
+ }
3173
+ };
3174
+ const locales = [...new Set(pages.map(canonicalLocale).filter((locale) => Boolean(locale)))];
3175
+ const defaultLocale = pages.some((page) => !page.locale) || locales.includes("en") ? "en" : locales[0] ?? "en";
3176
+ const pageId = (page) => {
3177
+ const locale = canonicalLocale(page);
3178
+ return locale ? `${locale === defaultLocale ? "" : `${locale}/`}${page.navigationId}` : page.id;
3179
+ };
3180
+ const route = (page) => {
3181
+ const locale = canonicalLocale(page);
3182
+ const prefix = locale && locale !== defaultLocale ? `/${locale}` : "";
3183
+ return `${prefix}${page.navigationId === "introduction" ? "/" : `/${page.navigationId}`}`;
3184
+ };
3185
+ return { canonicalLocale, locales, defaultLocale, pageId, route };
3186
+ }
3187
+ function validateUrlMigration(checkpoint, cancelled = false) {
3188
+ const issues = [];
3189
+ const counts = { pending: 0, imported: 0, excluded: 0, failed: 0, alias: 0 };
3190
+ const records = new Map(checkpoint.records.map((record) => [record.url, record]));
3191
+ const source = new URL(checkpoint.sourceUrl);
3192
+ const projection = localeProjection(checkpoint);
3193
+ const primaryIds = new Set(checkpoint.records.flatMap((record) => record.page && (!record.page.locale || projection.canonicalLocale(record.page) === projection.defaultLocale) ? [record.page.navigationId] : []));
3194
+ const outputIds = /* @__PURE__ */ new Set();
3195
+ const resolve3 = (url) => {
3196
+ const record = records.get(canonical(url, checkpoint.sourceUrl) ?? "");
3197
+ return record?.outcome === "alias" ? records.get(record.aliasOf ?? "") : record;
3198
+ };
3199
+ for (const record of checkpoint.records) {
3200
+ counts[record.outcome]++;
3201
+ issues.push(...record.issues ?? []);
3202
+ if (record.outcome === "pending" || record.outcome === "failed") issues.push({ code: "missing-page", source: record.url, message: record.reason ?? `Inventory entry is ${record.outcome}.` });
3203
+ if (!record.page || !record.evidence) continue;
3204
+ const outputId = projection.pageId(record.page);
3205
+ if (outputIds.has(outputId)) issues.push({ code: "collision", source: record.url, message: `Multiple source pages map to runtime path ${outputId}.` });
3206
+ outputIds.add(outputId);
3207
+ if (record.page.locale && projection.canonicalLocale(record.page) !== projection.defaultLocale && !primaryIds.has(record.page.navigationId)) {
3208
+ issues.push({ code: "missing-page", source: record.url, message: `Secondary locale ${record.page.locale} requires a primary ${projection.defaultLocale} page at ${record.page.navigationId}; the runtime cannot serve an orphan translation.` });
3209
+ }
3210
+ const actual = markdownCode(record.page.body);
3211
+ if (JSON.stringify(actual) !== JSON.stringify(record.evidence.code)) issues.push({ code: "code-mismatch", source: record.url, message: "Source code payloads differ from converted code payloads." });
3212
+ for (const value of record.evidence.links) {
3213
+ let link;
3214
+ try {
3215
+ link = new URL(value, record.evidence.finalUrl);
3216
+ } catch {
3217
+ continue;
3218
+ }
3219
+ if (!inScope(link, source, checkpoint.scopePath)) continue;
3220
+ if (/\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js|json|ya?ml)$/i.test(link.pathname)) continue;
3221
+ const destination = resolve3(link.toString());
3222
+ if (!destination || !["imported", "excluded"].includes(destination.outcome)) {
3223
+ issues.push({ code: "missing-page", source: record.url, message: `Unresolved internal destination: ${link}` });
3224
+ } else if (link.hash && destination.outcome === "imported") {
3225
+ let anchor = link.hash.slice(1);
3226
+ try {
3227
+ anchor = decodeURIComponent(anchor);
3228
+ } catch {
3229
+ }
3230
+ if (!destination.evidence?.anchors.includes(anchor)) issues.push({ code: "missing-anchor", source: record.url, message: `Missing source anchor: ${link}` });
3231
+ }
3232
+ }
3233
+ }
3234
+ const visit = (nodes) => {
3235
+ for (const node of nodes) {
3236
+ if (node.url) {
3237
+ const url = new URL(node.url, source);
3238
+ if (inScope(url, source, checkpoint.scopePath) && !resolve3(url.toString())?.page) issues.push({ code: "navigation-missing", source: node.url, message: `Source navigation target ${node.label} was not imported.` });
3239
+ }
3240
+ visit(node.children ?? []);
3241
+ }
3242
+ };
3243
+ visit(checkpoint.navigation);
3244
+ if (!counts.imported) issues.push({ code: "missing-page", source: checkpoint.sourceUrl, message: "No readable article pages were imported." });
3245
+ return { status: cancelled ? "cancelled" : issues.length ? "partial" : "passed", issues, counts };
3246
+ }
3247
+ async function runUrlMigration(options) {
3248
+ const source = validateMigrationUrl(options.sourceUrl);
3249
+ const sourceUrl = canonical(source.toString(), source.toString());
3250
+ const requestedScope = options.scopePath ?? (source.pathname.endsWith("/") ? source.pathname : source.pathname.slice(0, source.pathname.lastIndexOf("/")) || "/");
3251
+ let scopeEnd = requestedScope.length;
3252
+ while (scopeEnd > 0 && requestedScope.charCodeAt(scopeEnd - 1) === 47) scopeEnd--;
3253
+ const scopePath = requestedScope.slice(0, scopeEnd) || "/";
3254
+ if (!scopePath.startsWith("/") || scopePath.includes("?") || scopePath.includes("#") || !inScope(source, source, scopePath)) throw new Error("The selected documentation scope must contain the source URL.");
3255
+ const maxPages = options.maxPages ?? 1e3;
3256
+ if (!Number.isInteger(maxPages) || maxPages < 1 || maxPages > 1e4) throw new Error("maxPages must be between 1 and 10000.");
3257
+ const concurrency = Math.max(1, Math.min(32, Math.floor(options.concurrency ?? 5)));
3258
+ const batchSize = Math.max(1, Math.min(500, Math.floor(options.batchSize ?? 50)));
3259
+ if (!Number.isFinite(concurrency) || !Number.isFinite(batchSize) || options.maxCaptures !== void 0 && (!Number.isInteger(options.maxCaptures) || options.maxCaptures < 1) || options.maxTotalBytes !== void 0 && (!Number.isFinite(options.maxTotalBytes) || options.maxTotalBytes < 1)) throw new Error("Migration batch, concurrency and byte limits must be positive finite numbers.");
3260
+ const captureVersion = options.captureVersion ?? "fetch-v1";
3261
+ if (options.checkpoint && options.checkpoint.records.length > MAX_RECORDS) throw new Error("Checkpoint inventory exceeds its bound.");
3262
+ const checkpoint = options.checkpoint ? cloneUrlMigrationCheckpoint(options.checkpoint) : { schemaVersion: 1, converterVersion: CONVERTER_VERSION, sourceUrl, scopePath, captureVersion, records: [], navigation: [] };
3263
+ if (checkpoint.schemaVersion !== 1 || checkpoint.converterVersion !== CONVERTER_VERSION || checkpoint.sourceUrl !== sourceUrl || checkpoint.scopePath !== scopePath || checkpoint.captureVersion !== captureVersion) throw new Error("Checkpoint does not match this source, scope or capture/converter version.");
3264
+ if (checkpoint.records.length > MAX_RECORDS) throw new Error("Checkpoint inventory exceeds its bound.");
3265
+ const records = /* @__PURE__ */ new Map();
3266
+ for (const record of checkpoint.records) {
3267
+ if (canonical(record.url, sourceUrl) !== record.url || !inScope(new URL(record.url), source, scopePath) && !isDiscoveryIndex(new URL(record.url), source)) throw new Error("Checkpoint contains an unsafe inventory URL.");
3268
+ if (records.has(record.url)) throw new Error("Checkpoint contains a duplicate inventory URL.");
3269
+ if (!validRecord(record) || record.outcome === "failed" && options.retryFailed !== false || record.outcome !== "pending" && options.reconcile && !await options.reconcile(record)) {
3270
+ record.outcome = "pending";
3271
+ delete record.page;
3272
+ delete record.pageSha256;
3273
+ delete record.evidence;
3274
+ delete record.assets;
3275
+ }
3276
+ if (record.page && pageIdFromReference(record.page.id, true) !== record.page.id) throw new Error("Checkpoint contains an unsafe page path.");
3277
+ records.set(record.url, record);
3278
+ }
3279
+ const pending = [...records.values()].filter((record) => record.outcome === "pending");
3280
+ let changed = [];
3281
+ let navigationChanged = false;
3282
+ const enqueue = (value, base, discoveredBy) => {
3283
+ const url = canonical(value, base);
3284
+ if (!url || !inScope(new URL(url), source, scopePath) && !isDiscoveryIndex(new URL(url), source)) return;
3285
+ const existing = records.get(url);
3286
+ if (existing) {
3287
+ if (!existing.discoveredBy.includes(discoveredBy)) {
3288
+ existing.discoveredBy.push(discoveredBy);
3289
+ changed.push(existing);
3290
+ }
3291
+ return;
3292
+ }
3293
+ if (records.size >= MAX_RECORDS) throw new Error("Discovered URL inventory exceeded the 100000 URL safety bound.");
3294
+ const record = { url, discoveredBy: [discoveredBy], outcome: "pending" };
3295
+ records.set(url, record);
3296
+ checkpoint.records.push(record);
3297
+ pending.push(record);
3298
+ changed.push(record);
3299
+ };
3300
+ enqueue(sourceUrl, sourceUrl, "submitted");
3301
+ for (const path of ["llms.txt", "sitemap.xml", "robots.txt"]) enqueue(`${scopePath === "/" ? "" : scopePath}/${path}`, sourceUrl, "discovery-index");
3302
+ if (scopePath !== "/") for (const path of ["llms.txt", "sitemap.xml", "robots.txt"]) enqueue(`/${path}`, sourceUrl, "discovery-index");
3303
+ const fetcher = options.fetcher ?? defaultMigrationFetcher;
3304
+ const capture = options.captureProvider?.capture.bind(options.captureProvider) ?? (async ({ url }) => fetcher(url, { accept: ACCEPT }));
3305
+ const assetFetcher = options.assetFetcher ?? (!options.fetcher && !options.captureProvider ? defaultAssetFetcher : void 0);
3306
+ const usedIds = new Map([...records.values()].filter((record) => record.page).map((record) => [record.page.id, record.url]));
3307
+ const finalUrls = new Map([...records.values()].filter((record) => record.evidence && record.page).map((record) => [canonical(record.evidence.finalUrl, sourceUrl), record.url]));
3308
+ let imported = usedIds.size;
3309
+ const assetPayloads = new Map(checkpoint.records.flatMap((record) => (record.assets ?? []).map((asset) => [asset.sha256, asset])));
3310
+ const assetBytes = /* @__PURE__ */ new Map();
3311
+ const retainAsset = (asset) => {
3312
+ if (!acceptedAsset(asset)) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
3313
+ const sha256 = hash(asset.content);
3314
+ const content = assetBytes.get(sha256) ?? Buffer.from(asset.content);
3315
+ assetBytes.set(sha256, content);
3316
+ return { ...asset, content };
3317
+ };
3318
+ const assetRequests = createMigrationAssetCache();
3319
+ for (const record of checkpoint.records) for (const asset of record.assets ?? []) {
3320
+ if (!assetRequests.has(asset.url)) {
3321
+ const content = assetBytes.get(asset.sha256) ?? Buffer.from(asset.base64, "base64");
3322
+ assetBytes.set(asset.sha256, content);
3323
+ assetRequests.remember(asset.url, { url: asset.url, contentType: asset.contentType, content });
3324
+ }
3325
+ }
3326
+ 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);
3327
+ let budgetExhausted = false;
3328
+ let cursor = 0;
3329
+ const flush = async () => {
3330
+ const distinct = [...new Map(changed.map((record) => [record.url, record])).values()];
3331
+ if (options.onCheckpoint) for (let index = 0; index < distinct.length || index === 0 && navigationChanged; index += batchSize) {
3332
+ await options.onCheckpoint({ schemaVersion: 1, converterVersion: CONVERTER_VERSION, scopePath, captureVersion, sourceUrl, records: distinct.slice(index, index + batchSize), ...navigationChanged && index === 0 ? { navigation: checkpoint.navigation } : {} });
3333
+ }
3334
+ changed = [];
3335
+ navigationChanged = false;
3336
+ };
3337
+ const process = async (record) => {
3338
+ if (options.signal?.aborted) return;
3339
+ try {
3340
+ delete record.reason;
3341
+ const document = await capture({ url: new URL(record.url), signal: options.signal });
3342
+ if (options.signal?.aborted) return;
3343
+ if (Buffer.byteLength(document.body) > MAX_RESPONSE_BYTES) throw new Error("Captured response exceeds the 8 MB safety bound.");
3344
+ const final = canonical(document.finalUrl.toString(), sourceUrl);
3345
+ const role = classifyMigrationResponse(document);
3346
+ if (!inScope(document.finalUrl, source, scopePath) && !(role === "index" && isDiscoveryIndex(document.finalUrl, source))) {
3347
+ record.outcome = "excluded";
3348
+ record.reason = "Redirected outside the selected origin/path.";
3349
+ return;
3350
+ }
3351
+ if (document.status && document.status >= 400) throw new Error(`Source page returned HTTP ${document.status}.`);
3352
+ if (role !== "page") {
3353
+ totalBytes += Buffer.byteLength(document.body);
3354
+ if (totalBytes > (options.maxTotalBytes ?? 5e8)) {
3355
+ budgetExhausted = true;
3356
+ throw new Error("Migration exceeded its content byte budget.");
3357
+ }
3358
+ record.outcome = "excluded";
3359
+ record.reason = `Final response role: ${role}.`;
3360
+ const links = role === "index" ? linksOf(document) : [];
3361
+ record.evidence = { sha256: hash(document.body), byteLength: Buffer.byteLength(document.body), finalUrl: final, contentType: document.contentType, ...document.artifactRef ? { artifactRef: document.artifactRef } : {}, states: [], code: [], anchors: [], links };
3362
+ for (const link of links) enqueue(link, final, record.url);
3363
+ return;
3364
+ }
3365
+ if (imported >= maxPages) {
3366
+ record.outcome = "excluded";
3367
+ record.reason = "A discovery endpoint resolved to an article after the selected page limit.";
3368
+ return;
3369
+ }
3370
+ const already = finalUrls.get(final);
3371
+ if (already && already !== record.url) {
3372
+ record.outcome = "alias";
3373
+ record.aliasOf = already;
3374
+ return;
3375
+ }
3376
+ finalUrls.set(final, record.url);
3377
+ const id = idForUrl(document.finalUrl, scopePath);
3378
+ if (!id) throw new Error("Source URL has no safe destination path.");
3379
+ if (usedIds.has(id) && usedIds.get(id) !== record.url) {
3380
+ record.outcome = "failed";
3381
+ record.reason = `Destination collision for ${id}.`;
3382
+ return;
3383
+ }
3384
+ usedIds.set(id, record.url);
3385
+ const evidence = evidenceOf(document);
3386
+ const page = convert(document, id);
3387
+ if (!page) {
3388
+ record.outcome = "failed";
3389
+ record.reason = "Content could not be represented as safe inert MDX.";
3390
+ return;
3391
+ }
3392
+ const states = document.states ?? [];
3393
+ if (states.length > 64) throw new Error("Capture contains too many alternative states.");
3394
+ for (const state of states) {
3395
+ if (Buffer.byteLength(state.response.body) > MAX_RESPONSE_BYTES) throw new Error("Alternative state exceeds the 8 MB safety bound.");
3396
+ if (!inScope(state.response.finalUrl, source, scopePath) || classifyMigrationResponse(state.response) !== "page") throw new Error("Capture state escaped its selected scope or article role.");
3397
+ const alternative = convert(state.response, id);
3398
+ if (!alternative) throw new Error(`Alternative state ${state.id} is not inert content.`);
3399
+ const stateCode = /html/i.test(state.response.contentType) ? htmlEvidence(state.response).code : markdownCode(state.response.body);
3400
+ evidence.states.find((entry) => entry.id === state.id).code = stateCode;
3401
+ const difference = alternativeBlocks(alternative.body, page.body);
3402
+ evidence.code.push(...markdownCode(difference));
3403
+ evidence.links.push(...linksOf(state.response));
3404
+ evidence.anchors.push(.../html/i.test(state.response.contentType) ? htmlEvidence(state.response).anchors : markdownHeadingAnchors(state.response.body));
3405
+ if (difference) page.body += `
3406
+
3407
+ ### ${state.label.replace(/[<>{}#\n\r]/g, "")}
3408
+
3409
+ ${difference}`;
3410
+ }
3411
+ totalBytes += Buffer.byteLength(document.body) + states.reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0);
3412
+ evidence.byteLength = Buffer.byteLength(document.body) + states.reduce((sum, state) => sum + Buffer.byteLength(state.response.body), 0);
3413
+ if (totalBytes > (options.maxTotalBytes ?? 5e8)) {
3414
+ budgetExhausted = true;
3415
+ throw new Error("Migration exceeded its content byte budget.");
3416
+ }
3417
+ record.assets = [];
3418
+ record.issues = [];
3419
+ if (/html/i.test(document.contentType)) {
3420
+ const $ = load2(document.body);
3421
+ const root = $('main article,article,main,[role="main"]').first();
3422
+ root.find("nav,footer,aside,.theme-doc-breadcrumbs,.theme-doc-toc-mobile,.table-of-contents,.hash-link").remove();
3423
+ root.find('button svg,a.hash-link svg,[aria-hidden="true"] svg,svg[aria-hidden="true"],svg[data-lucide],svg[data-icon]').remove();
3424
+ if (root.find("h1").length > 1) record.issues.push({ code: "unsupported-component", source: record.url, message: "The article contains multiple level-one headings; review their hierarchy." });
3425
+ const unsupported = [...new Set(root.find("iframe,object,embed,canvas,svg,form,input,textarea,select,math,video,audio").toArray().map((node) => node.tagName))];
3426
+ if (unsupported.length) record.issues.push({ code: "unsupported-component", source: record.url, message: `Rendered content requires explicit review for: ${unsupported.join(", ")}.` });
3427
+ if (!states.length && unresolvedStaticControls(document.body)) record.issues.push({ code: "unsupported-component", source: record.url, message: "An interactive alternative has no complete captured panel or disclosure content." });
3428
+ }
3429
+ const suppliedAssets = new Map((document.assets ?? []).map((asset) => [canonical(asset.url, final), asset]));
3430
+ const assetUrls = [...new Set([...assetReferences(document), ...states.flatMap((state) => assetReferences(state.response))].map((url) => canonical(url, final)).filter((url) => Boolean(url)))];
3431
+ if (assetUrls.length > 256 || (document.assets?.length ?? 0) > 256) throw new Error("Capture exceeds the 256 assets per page safety bound.");
3432
+ for (const url of assetUrls) {
3433
+ try {
3434
+ let assetRequest = assetRequests.get(url);
3435
+ if (!suppliedAssets.has(url) && !assetRequest && assetFetcher) {
3436
+ assetRequest = assetRequests.load(url, () => assetFetcher(new URL(url), options.signal).then(retainAsset));
3437
+ }
3438
+ const supplied = suppliedAssets.get(url);
3439
+ const asset = supplied ? retainAsset(supplied) : assetRequest ? await assetRequest : null;
3440
+ if (!asset) throw new Error("Capture host did not supply asset bytes or a binary fetch boundary.");
3441
+ const extension = acceptedAsset(asset);
3442
+ if (!extension) throw new Error("Asset MIME/signature is unsupported or unsafe to serve.");
3443
+ const sha256 = hash(asset.content);
3444
+ const existing = assetPayloads.get(sha256);
3445
+ if (!existing) {
3446
+ totalBytes += asset.content.byteLength;
3447
+ if (totalBytes > (options.maxTotalBytes ?? 5e8)) {
3448
+ budgetExhausted = true;
3449
+ throw new Error("Migration exceeded its content and asset byte budget.");
3450
+ }
3451
+ }
3452
+ const path = `migration-assets/${sha256}.${extension}`;
3453
+ const receipt = { url, path, sha256, contentType: asset.contentType, base64: existing?.base64 ?? Buffer.from(asset.content).toString("base64") };
3454
+ record.assets.push(receipt);
3455
+ if (!existing) assetPayloads.set(sha256, receipt);
3456
+ if (!assetRequests.has(url)) assetRequests.remember(url, asset);
3457
+ page.body = rewriteMarkdownDestinations(page.body, (destination) => canonical(destination, final) === url ? `/${path}${new URL(destination, final).hash}` : destination);
3458
+ } catch (error) {
3459
+ assetRequests.delete(url);
3460
+ record.issues.push({ code: "asset-failed", source: record.url, message: `${url}: ${error instanceof Error ? error.message : String(error)}` });
3461
+ }
3462
+ }
3463
+ if (options.signal?.aborted) return;
3464
+ if (sanitizeRemoteMarkdown(page.body.replace(/^<a id="[^"<]*" \/>$/gm, "")) === null) throw new Error("Converted content did not pass inert MDX validation.");
3465
+ record.evidence = evidence;
3466
+ record.page = page;
3467
+ const language = observedLocale(document, page.id, scopePath);
3468
+ record.issues.push(...language.issues);
3469
+ const canonicalLocale = language.locale;
3470
+ if (canonicalLocale) {
3471
+ page.locale = canonicalLocale;
3472
+ const segments = page.id.split("/");
3473
+ const localeIndex = segments.findIndex((segment) => segment.toLowerCase() === canonicalLocale.toLowerCase());
3474
+ page.navigationId = segments.filter((_segment, index) => index !== localeIndex).join("/") || "introduction";
3475
+ page.id = `${canonicalLocale}/${page.navigationId}`;
3476
+ if (usedIds.has(page.id) && usedIds.get(page.id) !== record.url) throw new Error(`Destination collision for locale page ${page.id}.`);
3477
+ usedIds.delete(id);
3478
+ usedIds.set(page.id, record.url);
3479
+ }
3480
+ record.version = document.version ?? [...page.id.split("/"), ...document.finalUrl.pathname.split("/")].find((segment) => /^v\d+(?:\.\d+)*$/i.test(segment));
3481
+ record.pageSha256 = pageHash(page);
3482
+ record.outcome = "imported";
3483
+ imported++;
3484
+ const navigation = checkpoint.navigation.length ? [] : sourceNavigation(document);
3485
+ if (!checkpoint.navigation.length && navigation.length) {
3486
+ checkpoint.navigation = navigation;
3487
+ navigationChanged = true;
3488
+ }
3489
+ for (const link of evidence.links) {
3490
+ try {
3491
+ const url = new URL(link, final);
3492
+ if (!/\.(?:png|jpe?g|gif|svg|webp|avif|pdf|zip|mp4|webm|css|js)$/i.test(url.pathname)) enqueue(link, final, record.url);
3493
+ } catch {
3494
+ }
3495
+ }
3496
+ const visit = (nodes) => {
3497
+ for (const node of nodes) {
3498
+ if (node.url) enqueue(node.url, final, "source-navigation");
3499
+ visit(node.children ?? []);
3500
+ }
3501
+ };
3502
+ visit(navigation);
3503
+ } catch (error) {
3504
+ if (options.signal?.aborted) return;
3505
+ const wasImported = record.outcome === "imported";
3506
+ record.outcome = record.discoveredBy.every((origin) => origin === "discovery-index") ? "excluded" : "failed";
3507
+ record.reason = error instanceof Error ? error.message : String(error);
3508
+ if (record.page) {
3509
+ if (wasImported) imported--;
3510
+ usedIds.delete(record.page.id);
3511
+ }
3512
+ delete record.page;
3513
+ delete record.pageSha256;
3514
+ delete record.assets;
3515
+ } finally {
3516
+ if (record.outcome !== "pending") {
3517
+ if (options.onRecord) await options.onRecord(record);
3518
+ changed.push(record);
3519
+ }
3520
+ }
3521
+ };
3522
+ while (cursor < pending.length && !options.signal?.aborted && !budgetExhausted && cursor < (options.maxCaptures ?? Infinity)) {
3523
+ if (imported >= maxPages) {
3524
+ const index = pending.findIndex((record, position) => position >= cursor && isDiscoveryIndex(new URL(record.url), source));
3525
+ if (index < 0) break;
3526
+ pending.splice(cursor, 0, pending.splice(index, 1)[0]);
3527
+ }
3528
+ const batch = pending.slice(cursor, cursor + Math.min(concurrency, imported >= maxPages ? 1 : maxPages - imported, (options.maxCaptures ?? Infinity) - cursor));
3529
+ cursor += batch.length;
3530
+ await Promise.all(batch.map(process));
3531
+ if (changed.length >= batchSize) await flush();
3532
+ }
3533
+ await flush();
3534
+ const projection = localeProjection(checkpoint);
3535
+ const routeMap = /* @__PURE__ */ new Map();
3536
+ for (const record of checkpoint.records) {
3537
+ const target = record.outcome === "alias" ? records.get(record.aliasOf ?? "") : record;
3538
+ if (target?.page) {
3539
+ const route = projection.route(target.page);
3540
+ routeMap.set(record.url, route);
3541
+ if (target.evidence) routeMap.set(canonical(target.evidence.finalUrl, sourceUrl), route);
3542
+ }
3543
+ }
3544
+ const navigationLabels = /* @__PURE__ */ new Map();
3545
+ const navigationIssues = [];
3546
+ const labelPages = (nodes) => {
3547
+ for (const node of nodes) {
3548
+ const entry = node.url ? records.get(canonical(node.url, sourceUrl) ?? "") : void 0;
3549
+ const target = entry?.outcome === "alias" ? records.get(entry.aliasOf ?? "") : entry;
3550
+ if (target?.page && node.label && !node.children?.length) {
3551
+ const previous = navigationLabels.get(target.url);
3552
+ if (previous && previous !== node.label) navigationIssues.push({ code: "unsupported-component", source: target.url, message: `This page has multiple source navigation labels (${previous}, ${node.label}); review the shared sidebar title.` });
3553
+ else navigationLabels.set(target.url, node.label);
3554
+ }
3555
+ labelPages(node.children ?? []);
3556
+ }
3557
+ };
3558
+ labelPages(checkpoint.navigation);
3559
+ const pages = checkpoint.records.flatMap((record) => record.page ? [{ ...record.page, id: projection.pageId(record.page), ...record.page.locale ? { locale: projection.canonicalLocale(record.page) } : {}, ...navigationLabels.has(record.url) ? { navTitle: navigationLabels.get(record.url) } : {}, body: rewriteMarkdownDestinations(record.page.body, (value) => {
3560
+ let url;
3561
+ try {
3562
+ url = new URL(value, record.evidence.finalUrl);
3563
+ } catch {
3564
+ return value;
3565
+ }
3566
+ const route = routeMap.get(canonical(url.toString(), sourceUrl));
3567
+ return route ? `${route}${url.hash}` : value;
3568
+ }) }] : []);
3569
+ const validation = validateUrlMigration(checkpoint, Boolean(options.signal?.aborted));
3570
+ validation.issues.push(...navigationIssues);
3571
+ if (navigationIssues.length && validation.status === "passed") validation.status = "partial";
3572
+ const assets = /* @__PURE__ */ new Map();
3573
+ for (const record of checkpoint.records) for (const asset of record.assets ?? []) {
3574
+ if (!assets.has(asset.path)) {
3575
+ const content = assetBytes.get(asset.sha256) ?? Buffer.from(asset.base64, "base64");
3576
+ assets.set(asset.path, { path: asset.path, content: Buffer.from(content.buffer, content.byteOffset, content.byteLength) });
3577
+ }
3578
+ }
3579
+ const bundle = {
3580
+ sourceUrl,
3581
+ sourceKind: "url",
3582
+ platform: options.platform ?? "generic",
3583
+ pages,
3584
+ assets: [...assets.values()],
3585
+ docsConfig: navigationConfig(checkpoint.navigation, records) ?? buildNavigationFromPages(pages),
3586
+ warnings: validation.issues.map((issue) => ({ code: issue.code === "missing-page" ? "missing-page" : "unsupported-config", message: issue.message, source: issue.source })),
3587
+ urlMigrationValidation: validation,
3588
+ stats: { discovered: records.size, imported: pages.length, skipped: records.size - pages.length }
3589
+ };
3590
+ if (projection.locales.length) {
3591
+ const locales = [.../* @__PURE__ */ new Set([projection.defaultLocale, ...projection.locales])];
3592
+ bundle.docsConfig.i18n = { defaultLocale: projection.defaultLocale, locales: locales.map((code) => ({ code, label: code === "en" ? "English" : code })) };
3593
+ }
3594
+ const stopReason = options.signal?.aborted ? "cancelled" : budgetExhausted ? "byte-limit" : cursor < pending.length && cursor >= (options.maxCaptures ?? Infinity) ? "capture-limit" : imported >= maxPages && cursor < pending.length ? "page-limit" : cursor < pending.length ? "capture-limit" : "complete";
3595
+ return { bundle, checkpoint, validation, stopReason };
3596
+ }
3597
+ function rewriteMarkdownDestinations(body, rewrite) {
3598
+ const edits = markdownNodes(body).filter((node) => node.url && node.position).map((node) => {
3599
+ const start = node.position.start.offset;
3600
+ const end = node.position.end.offset;
3601
+ const original = body.slice(start, end);
3602
+ const replacement = rewrite(node.url);
3603
+ if (replacement === node.url) return null;
3604
+ const index = original.lastIndexOf(node.url);
3605
+ if (index < 0) return null;
3606
+ return { start: start + index, end: start + index + node.url.length, replacement };
3607
+ }).filter((edit) => edit !== null).sort((a, b) => b.start - a.start);
3608
+ for (const node of markdownNodes(body)) for (const attribute of node.attributes ?? []) {
3609
+ if (!attribute.name || !["src", "href", "srcSet", "srcset", "img", "primaryHref", "secondaryHref"].includes(attribute.name) || !attribute.position) continue;
3610
+ let value = attribute.value;
3611
+ if (typeof value !== "string") {
3612
+ try {
3613
+ value = JSON.parse(value?.value ?? "");
3614
+ } catch {
3615
+ continue;
3616
+ }
3617
+ }
3618
+ if (typeof value !== "string") continue;
3619
+ let replacement;
3620
+ if (/^srcset$/i.test(attribute.name)) {
3621
+ const candidates = migrationImageCandidates(value, new URL("https://migration.invalid"), false);
3622
+ replacement = renderMigrationSrcset(candidates.map((candidate) => ({ ...candidate, url: rewrite(candidate.url) })));
3623
+ if (replacement === renderMigrationSrcset(candidates)) continue;
3624
+ } else {
3625
+ replacement = rewrite(value);
3626
+ if (replacement === value) continue;
3627
+ }
3628
+ const start = attribute.position.start.offset, end = attribute.position.end.offset;
3629
+ if (start === void 0 || end === void 0) continue;
3630
+ edits.push({ start, end, replacement: `${attribute.name}="${migrationImageAttribute(replacement)}"` });
3631
+ }
3632
+ edits.sort((a, b) => b.start - a.start);
3633
+ for (const edit of edits) body = body.slice(0, edit.start) + edit.replacement + body.slice(edit.end);
3634
+ return body;
3635
+ }
3636
+
3637
+ // src/components.ts
3638
+ import { createHash as createHash2 } from "crypto";
3639
+ import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2 } from "fs";
3640
+ import { basename, dirname as dirname2, extname as extname3, posix as posix2, relative as relative3, resolve as resolve2 } from "path";
3641
+ import postcss from "postcss";
3642
+ import selectorParser from "postcss-selector-parser";
3643
+ import remarkMdx4 from "remark-mdx";
3644
+ import remarkParse4 from "remark-parse";
3645
+ import ts2 from "typescript";
3646
+ import { unified as unified4 } from "unified";
3647
+ var parser2 = unified4().use(remarkParse4).use(remarkMdx4);
3648
+ var DOCUSAURUS_THEME_SHIMS = {
3649
+ "@theme/CodeBlock": {
3650
+ filename: "docusaurus-code-block.tsx",
3651
+ content: [
3652
+ "import type { ReactNode } from 'react'",
3653
+ "",
3654
+ "export default function CodeBlock({ children, className }: { children?: ReactNode; className?: string }) {",
3655
+ " return (",
3656
+ " <pre className={className}>",
3657
+ " <code>{children}</code>",
3658
+ " </pre>",
3659
+ " )",
3660
+ "}",
3661
+ ""
3662
+ ].join("\n")
3663
+ },
3664
+ "@docusaurus/BrowserOnly": {
3665
+ filename: "docusaurus-browser-only.tsx",
3666
+ content: [
3667
+ "'use client'",
3668
+ "",
3669
+ "import { useEffect, useState, type ReactNode } from 'react'",
3670
+ "",
3671
+ "export default function BrowserOnly({ children, fallback = null }: { children: () => ReactNode; fallback?: ReactNode }) {",
3672
+ " const [mounted, setMounted] = useState(false)",
3673
+ " useEffect(() => { setMounted(true) }, [])",
3674
+ " return mounted ? <>{children()}</> : <>{fallback}</>",
3675
+ "}",
3676
+ ""
3677
+ ].join("\n")
3678
+ }
3679
+ };
3680
+ function applyReplacements(source, replacements) {
3681
+ return replacements.sort((a, b) => b.start - a.start).reduce(
3682
+ (text, edit) => text.slice(0, edit.start) + edit.value + text.slice(edit.end),
3683
+ source
3684
+ );
3685
+ }
3686
+ function hash2(value) {
3687
+ return createHash2("sha256").update(value).digest("hex").slice(0, 12);
3688
+ }
3689
+ function sourceFile(source, filename) {
3690
+ return ts2.createSourceFile(
3691
+ filename,
3692
+ source,
3693
+ ts2.ScriptTarget.Latest,
3694
+ true,
3695
+ /\.[cm]?ts$/.test(filename) ? ts2.ScriptKind.TS : ts2.ScriptKind.TSX
3696
+ );
3697
+ }
3698
+ function mergeComponentRegistry(existing, incoming) {
3699
+ const fingerprint = hash2(incoming);
3700
+ const name = `MigratedRegistry${fingerprint}`;
3701
+ const registryPath = `./migrated-components-${fingerprint}`;
3702
+ const source = sourceFile(existing, "custom-components.tsx");
3703
+ const alreadyImported = source.statements.some((statement) => ts2.isImportDeclaration(statement) && ts2.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === registryPath);
3704
+ let initializer;
3705
+ for (const statement of source.statements) {
3706
+ if (!ts2.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword)) continue;
3707
+ for (const declaration of statement.declarationList.declarations) {
3708
+ if (ts2.isIdentifier(declaration.name) && declaration.name.text === "customComponents") initializer = declaration.initializer;
3709
+ }
3710
+ }
3711
+ while (initializer && (ts2.isAsExpression(initializer) || ts2.isSatisfiesExpression(initializer) || ts2.isParenthesizedExpression(initializer))) initializer = initializer.expression;
3712
+ if (!initializer || !ts2.isObjectLiteralExpression(initializer)) {
3713
+ throw new Error("Cannot safely merge src/mdx/custom-components.tsx: export customComponents as an object literal before importing components. The existing registry was preserved.");
3714
+ }
3715
+ const content = alreadyImported ? existing : applyReplacements(existing, [
3716
+ {
3717
+ start: source.statements.filter((statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression)).at(-1)?.end ?? 0,
3718
+ end: source.statements.filter((statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression)).at(-1)?.end ?? 0,
3719
+ value: `
3720
+ import { customComponents as ${name} } from ${JSON.stringify(registryPath)}
3721
+ `
3722
+ },
3723
+ { start: initializer.getStart(source) + 1, end: initializer.getStart(source) + 1, value: `
3724
+ ...${name},` }
3725
+ ]);
3726
+ return [
3727
+ { path: `src/mdx/migrated-components-${fingerprint}.tsx`, content: incoming },
3728
+ { path: "src/mdx/custom-components.tsx", content }
3729
+ ];
3730
+ }
3731
+
3732
+ // src/render.ts
3733
+ function yamlString(value) {
3734
+ return JSON.stringify(value.replace(/\s+/g, " ").trim());
3735
+ }
3736
+ function renderPage(bundle, page) {
3737
+ return [
3738
+ "---",
3739
+ `title: ${yamlString(page.title)}`,
3740
+ page.headingTitle ? `headingTitle: ${yamlString(page.headingTitle)}` : null,
3741
+ page.navTitle ? `navTitle: ${yamlString(page.navTitle)}` : null,
3742
+ page.icon ? `icon: ${yamlString(page.icon)}` : null,
3743
+ page.iconType ? `iconType: ${yamlString(page.iconType)}` : null,
3744
+ `description: ${yamlString(page.description)}`,
3745
+ page.descriptionPlacement === "body" ? "descriptionPlacement: body" : null,
3746
+ page.badge ? `badge: ${yamlString(page.badge)}` : null,
3747
+ page.keywords.length > 0 ? `keywords: [${page.keywords.map(yamlString).join(", ")}]` : null,
3748
+ page.mode ? `mode: ${yamlString(page.mode)}` : null,
3749
+ page.hidden ? "hidden: true" : null,
3750
+ page.noindex ? "noindex: true" : null,
3751
+ // Group names must match identity-provider claims exactly, so they are
3752
+ // quoted as-is rather than whitespace-collapsed like display strings.
3753
+ page.groups?.length ? `groups: [${page.groups.map((group) => JSON.stringify(group)).join(", ")}]` : null,
3754
+ page.public !== void 0 ? `public: ${page.public ? "true" : "false"}` : null,
3755
+ ...Object.entries(page.meta ?? {}).map(([field, value]) => `${field}: ${yamlString(value)}`),
3756
+ page.openapi ? `openapi: ${yamlString(page.openapi)}` : null,
3757
+ page.api ? `api: ${yamlString(page.api)}` : null,
3758
+ page.authMethod ? `authMethod: ${yamlString(page.authMethod)}` : null,
3759
+ page.playground ? `playground: ${yamlString(page.playground)}` : null,
3760
+ bundle.sourceKind === "url" ? `source: ${yamlString(page.source)}` : null,
3761
+ "---",
3762
+ "",
3763
+ page.body,
3764
+ ""
3765
+ ].filter((line) => line !== null).join("\n");
3766
+ }
3767
+ function isIntegrationsObject(value) {
3768
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
3769
+ }
3770
+ function mergeMigrationConfig(existing, incoming) {
3771
+ const tabs = existing.tabs.filter((tab) => tab.tab.toLowerCase() !== "changelog");
3772
+ const names = new Set(tabs.map((tab) => tab.tab.toLowerCase()));
3773
+ const seenPages = /* @__PURE__ */ new Set();
3774
+ function recordPages(pages = []) {
3775
+ for (const page of pages) {
3776
+ if (typeof page === "string") seenPages.add(page);
3777
+ else recordPages(page.pages);
3778
+ }
3779
+ }
3780
+ for (const tab of tabs) {
3781
+ recordPages(tab.pages);
3782
+ recordPages(tab.groups);
3783
+ }
3784
+ function uniquePages(pages = []) {
3785
+ return pages.flatMap((page) => {
3786
+ if (typeof page === "string") {
3787
+ if (seenPages.has(page)) return [];
3788
+ seenPages.add(page);
3789
+ return [page];
3790
+ }
3791
+ const children = uniquePages(page.pages);
3792
+ return children.length > 0 ? [{ ...page, pages: children }] : [];
3793
+ });
3794
+ }
3795
+ function uniqueGroups(groups) {
3796
+ return uniquePages(groups ?? []);
3797
+ }
3798
+ for (const tab of incoming.tabs) {
3799
+ if (tab.tab.toLowerCase() === "changelog") continue;
3800
+ const uniqueTab = {
3801
+ ...tab,
3802
+ ...tab.pages ? { pages: uniquePages(tab.pages) } : {},
3803
+ ...tab.groups ? { groups: uniqueGroups(tab.groups) } : {}
3804
+ };
3805
+ if ((tab.pages || tab.groups) && !uniqueTab.pages?.length && !uniqueTab.groups?.length && !tab.href && !tab.api) continue;
3806
+ if (!names.has(tab.tab.toLowerCase())) {
3807
+ tabs.push(uniqueTab);
3808
+ names.add(tab.tab.toLowerCase());
3809
+ continue;
3810
+ }
3811
+ const target = tabs.find((item) => item.tab.toLowerCase() === tab.tab.toLowerCase());
3812
+ if (!target) continue;
3813
+ const incomingRootNodes = [
3814
+ ...uniqueTab.pages ?? [],
3815
+ ...uniqueTab.groups ?? []
3816
+ ];
3817
+ if (uniqueTab.pages?.length) {
3818
+ target.pages = [...target.pages ?? target.groups ?? [], ...incomingRootNodes];
3819
+ delete target.groups;
3820
+ } else if (uniqueTab.groups?.length) {
3821
+ if (target.pages) target.pages = [...target.pages, ...uniqueTab.groups];
3822
+ else target.groups = [...target.groups ?? [], ...uniqueTab.groups];
3823
+ }
3824
+ }
3825
+ const changelog = existing.tabs.find((tab) => tab.tab.toLowerCase() === "changelog") ?? incoming.tabs.find((tab) => tab.tab.toLowerCase() === "changelog");
3826
+ if (changelog) tabs.push(changelog);
3827
+ const i18n = existing.i18n || incoming.i18n ? {
3828
+ defaultLocale: existing.i18n?.defaultLocale ?? incoming.i18n?.defaultLocale ?? "en",
3829
+ locales: [...new Map(
3830
+ [...existing.i18n?.locales ?? [], ...incoming.i18n?.locales ?? []].map((locale) => [locale.code, locale])
3831
+ ).values()]
3832
+ } : void 0;
3833
+ return {
3834
+ ...incoming,
3835
+ ...existing,
3836
+ tabs,
3837
+ // Collection presentation belongs to the imported information
3838
+ // architecture; starter defaults must not coerce source dropdowns to tabs.
3839
+ navigation: incoming.navigation ?? existing.navigation,
3840
+ ...existing.stylesheets?.length || incoming.stylesheets?.length ? { stylesheets: [.../* @__PURE__ */ new Set([...existing.stylesheets ?? [], ...incoming.stylesheets ?? []])] } : {},
3841
+ ...i18n ? { i18n } : {},
3842
+ // Existing providers win per provider; imported ones fill the gaps.
3843
+ // A malformed (string/array) existing value must not spread into index keys.
3844
+ ...isIntegrationsObject(existing.integrations) || isIntegrationsObject(incoming.integrations) ? {
3845
+ integrations: {
3846
+ ...isIntegrationsObject(incoming.integrations) ? incoming.integrations : {},
3847
+ ...isIntegrationsObject(existing.integrations) ? existing.integrations : {}
3848
+ }
3849
+ } : {}
3850
+ };
3851
+ }
3852
+ function renderMigrationFiles(bundle, options = {}) {
3853
+ const config = options.existingConfig ? mergeMigrationConfig(options.existingConfig, bundle.docsConfig) : bundle.docsConfig;
3854
+ return [
3855
+ ...bundle.pages.map((page) => ({
3856
+ path: `src/content/${page.id}.mdx`,
3857
+ content: renderPage(bundle, page)
3858
+ })),
3859
+ ...bundle.assets.map((asset) => ({ path: asset.projectRelative ? asset.path : `public/${asset.path}`, content: asset.content })),
3860
+ ...(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]),
3861
+ ...bundle.quarantinedFiles ?? [],
3862
+ { path: "docs.json", content: `${JSON.stringify(config, null, 2)}
3863
+ ` }
3864
+ ];
3865
+ }
3866
+ export {
3867
+ classifyMigrationResponse,
3868
+ renderMigrationFiles,
3869
+ runUrlMigration,
3870
+ validateUrlMigration
3871
+ };
3872
+ //# sourceMappingURL=url-migration.js.map