@thallylabs/migrate 0.2.31 → 0.2.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -8
- package/dist/index.d.ts +13 -5
- package/dist/index.js +470 -158
- package/dist/index.js.map +1 -1
- package/dist/{url-migration-Coy62q1L.d.ts → url-migration-_wVauqSg.d.ts} +19 -0
- package/dist/url-migration.d.ts +1 -1
- package/dist/url-migration.js +71 -27
- package/dist/url-migration.js.map +1 -1
- package/package.json +1 -1
|
@@ -424,12 +424,21 @@ interface MigrationWarning {
|
|
|
424
424
|
source?: string;
|
|
425
425
|
}
|
|
426
426
|
/** A repository-configured remote OpenAPI spec, downloaded after the bundle is built. */
|
|
427
|
+
/** A route prefix Mintlify served one spec's generated endpoint pages under (`openapi: { source, directory }`). */
|
|
428
|
+
interface RemoteApiSpecDirectory {
|
|
429
|
+
/** The authored `directory`, without leading or trailing slashes. */
|
|
430
|
+
directory: string;
|
|
431
|
+
/** `METHOD /path` operations docs.json lists under that reference, in authored order; absent when Mintlify generates every operation. */
|
|
432
|
+
operations?: Array<string>;
|
|
433
|
+
}
|
|
427
434
|
interface RemoteApiSpec {
|
|
428
435
|
url: string;
|
|
429
436
|
tabLabel?: string;
|
|
430
437
|
parentTab?: string;
|
|
431
438
|
icon?: string;
|
|
432
439
|
hidden?: boolean;
|
|
440
|
+
/** Every `directory` docs.json mounts this spec under: several API tabs may share one spec URL. */
|
|
441
|
+
directories?: Array<RemoteApiSpecDirectory>;
|
|
433
442
|
/** Operation keys (`get /x`) only access-restricted pages document; marked `x-excluded` after download. */
|
|
434
443
|
withheldOperations?: Array<string>;
|
|
435
444
|
/** Operation keys published pages or docs.json also name; keeps a shared `$ref` path item public. */
|
|
@@ -443,6 +452,16 @@ interface MigrationBundle {
|
|
|
443
452
|
assets: Array<MigrationAsset>;
|
|
444
453
|
/** Repository-configured remote specs awaiting a bounded network fetch by the host. */
|
|
445
454
|
remoteApiSpecs?: Array<RemoteApiSpec>;
|
|
455
|
+
/**
|
|
456
|
+
* Repository OpenAPI specs docs.json mounts under a `directory` (no surrounding slashes), with the tab each is bound to; the remote ones are in
|
|
457
|
+
* `remoteApiSpecs[].directories`. Present only alongside remote specs: a spec's id depends on which remote tabs bind ahead of it, so
|
|
458
|
+
* hydration links the local and remote mounts together once every download has settled, and the links stay as authored until then.
|
|
459
|
+
*/
|
|
460
|
+
localApiMounts?: Array<{
|
|
461
|
+
filename: string;
|
|
462
|
+
directory: string;
|
|
463
|
+
tab?: string;
|
|
464
|
+
}>;
|
|
446
465
|
/** Customer-owned component source and registry; paths are repository-relative. */
|
|
447
466
|
componentFiles?: Array<RenderedMigrationFile>;
|
|
448
467
|
/**
|
package/dist/url-migration.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { a as MigrationAsset, f as MigrationBundle, h as MigrationCapture, i as MigrationCaptureProvider, j as MigrationCaptureState, k as MigrationCapturedAsset, g as MigrationDocsConfig, l as MigrationEvidence, m as MigrationFetchRequest, n as MigrationFetchResponse, d as MigrationFetcher, o as MigrationJournalAsset, b as MigrationNavigationGroup, p as MigrationNavigationOccurrence, q as MigrationNavigationTab, M as MigrationPage, e as MigrationPlatform, c as MigrationWarning, R as RenderedMigrationFile, U as UrlMigrationCheckpoint, r as UrlMigrationCheckpointDelta, s as UrlMigrationIssue, t as UrlMigrationRecord, u as UrlMigrationRun, v as UrlMigrationRunOptions, w as UrlMigrationValidation, x as classifyMigrationResponse, z as renderMigrationFiles, A as runUrlMigration, B as validateUrlMigration } from './url-migration-
|
|
1
|
+
export { a as MigrationAsset, f as MigrationBundle, h as MigrationCapture, i as MigrationCaptureProvider, j as MigrationCaptureState, k as MigrationCapturedAsset, g as MigrationDocsConfig, l as MigrationEvidence, m as MigrationFetchRequest, n as MigrationFetchResponse, d as MigrationFetcher, o as MigrationJournalAsset, b as MigrationNavigationGroup, p as MigrationNavigationOccurrence, q as MigrationNavigationTab, M as MigrationPage, e as MigrationPlatform, c as MigrationWarning, R as RenderedMigrationFile, U as UrlMigrationCheckpoint, r as UrlMigrationCheckpointDelta, s as UrlMigrationIssue, t as UrlMigrationRecord, u as UrlMigrationRun, v as UrlMigrationRunOptions, w as UrlMigrationValidation, x as classifyMigrationResponse, z as renderMigrationFiles, A as runUrlMigration, B as validateUrlMigration } from './url-migration-_wVauqSg.js';
|
package/dist/url-migration.js
CHANGED
|
@@ -3299,7 +3299,7 @@ ${fence}
|
|
|
3299
3299
|
}
|
|
3300
3300
|
|
|
3301
3301
|
// src/url-run.ts
|
|
3302
|
-
var CONVERTER_VERSION = "url-evidence-
|
|
3302
|
+
var CONVERTER_VERSION = "url-evidence-v4";
|
|
3303
3303
|
var parser = unified3().use(remarkParse3).use(remarkMdx3);
|
|
3304
3304
|
var ACCEPT = "text/markdown,text/html,application/xhtml+xml;q=0.9";
|
|
3305
3305
|
var MAX_RESPONSE_BYTES = 8e6;
|
|
@@ -3558,21 +3558,22 @@ function resolveMigrationRecord(records, record) {
|
|
|
3558
3558
|
}
|
|
3559
3559
|
return record;
|
|
3560
3560
|
}
|
|
3561
|
-
function navigationConfig(
|
|
3561
|
+
function navigationConfig(checkpoint, records) {
|
|
3562
|
+
const navigation = checkpoint.navigation;
|
|
3563
|
+
const projection = localeProjection(checkpoint);
|
|
3562
3564
|
const project = (nodes) => nodes.flatMap((node) => {
|
|
3563
3565
|
const record = node.url ? records.get(canonical(node.url, node.url) ?? "") : void 0;
|
|
3564
3566
|
const target2 = resolveMigrationRecord(records, record);
|
|
3565
3567
|
const page = target2?.page;
|
|
3566
3568
|
const children = project(node.children ?? []);
|
|
3567
3569
|
if (children.length) {
|
|
3568
|
-
const contains = (entries) => entries.some((entry) => typeof entry === "string" ? entry === page
|
|
3569
|
-
return [{ group: node.label || "Documentation", pages: [...page && !contains(children) ? [
|
|
3570
|
+
const contains = (entries) => entries.some((entry) => typeof entry === "string" ? entry === (page && projection.navigationId(page)) : contains(entry.pages));
|
|
3571
|
+
return [{ group: node.label || "Documentation", pages: [...page && !contains(children) ? [projection.navigationId(page)] : [], ...children] }];
|
|
3570
3572
|
}
|
|
3571
|
-
return page ? [
|
|
3573
|
+
return page ? [projection.navigationId(page)] : [];
|
|
3572
3574
|
});
|
|
3573
3575
|
const tabs = navigation.filter((node) => node.kind === "tab");
|
|
3574
3576
|
if (tabs.length) {
|
|
3575
|
-
const projection = localeProjection({ records: [...records.values()] });
|
|
3576
3577
|
const locales = [...new Set(tabs.map((tab) => tab.locale ?? projection.defaultLocale))];
|
|
3577
3578
|
const localizedTabs = (locale) => tabs.filter((tab) => (tab.locale ?? projection.defaultLocale) === locale).flatMap((tab) => {
|
|
3578
3579
|
const pages2 = project(tab.children ?? []);
|
|
@@ -3590,16 +3591,16 @@ function navigationConfig(navigation, records) {
|
|
|
3590
3591
|
const pages = project(navigation);
|
|
3591
3592
|
return pages.length ? { tabs: [{ tab: "Documentation", pages }] } : null;
|
|
3592
3593
|
}
|
|
3594
|
+
function canonicalLanguage(value) {
|
|
3595
|
+
if (!/^[a-z]{2,3}(?:-[a-z0-9]{2,8}){0,4}$/i.test(value)) return void 0;
|
|
3596
|
+
try {
|
|
3597
|
+
return Intl.getCanonicalLocales(value)[0];
|
|
3598
|
+
} catch {
|
|
3599
|
+
return void 0;
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3593
3602
|
function observedLocale(document, id, scopePath) {
|
|
3594
3603
|
const issues = [];
|
|
3595
|
-
const canonicalLanguage = (value) => {
|
|
3596
|
-
if (!/^[a-z]{2,3}(?:-[a-z0-9]{2,8}){0,4}$/i.test(value)) return void 0;
|
|
3597
|
-
try {
|
|
3598
|
-
return Intl.getCanonicalLocales(value)[0];
|
|
3599
|
-
} catch {
|
|
3600
|
-
return void 0;
|
|
3601
|
-
}
|
|
3602
|
-
};
|
|
3603
3604
|
const candidates = [];
|
|
3604
3605
|
if (document.locale) {
|
|
3605
3606
|
const locale = canonicalLanguage(document.locale);
|
|
@@ -3621,25 +3622,60 @@ function observedLocale(document, id, scopePath) {
|
|
|
3621
3622
|
}
|
|
3622
3623
|
function localeProjection(checkpoint) {
|
|
3623
3624
|
const pages = checkpoint.records.flatMap((record) => record.page ? [record.page] : []);
|
|
3624
|
-
const
|
|
3625
|
+
const declaredLocale = (page) => {
|
|
3625
3626
|
try {
|
|
3626
3627
|
return page.locale ? Intl.getCanonicalLocales(page.locale)[0] : void 0;
|
|
3627
3628
|
} catch {
|
|
3628
3629
|
return void 0;
|
|
3629
3630
|
}
|
|
3630
3631
|
};
|
|
3632
|
+
const declaredLocales = [...new Set(pages.map(declaredLocale).filter((locale) => Boolean(locale)))];
|
|
3633
|
+
const defaultLocale = pages.some((page) => !page.locale) || declaredLocales.includes("en") ? "en" : declaredLocales[0] ?? "en";
|
|
3634
|
+
const records = new Map(checkpoint.records.map((record) => [record.url, record]));
|
|
3635
|
+
const memberships = /* @__PURE__ */ new Map();
|
|
3636
|
+
const observe = (nodes, parentLocale) => {
|
|
3637
|
+
for (const node of nodes) {
|
|
3638
|
+
const locale = node.locale ? canonicalLanguage(node.locale) : parentLocale;
|
|
3639
|
+
const entry = node.url ? records.get(canonical(node.url, checkpoint.sourceUrl) ?? "") : void 0;
|
|
3640
|
+
const page = resolveMigrationRecord(records, entry)?.page;
|
|
3641
|
+
if (page && locale) {
|
|
3642
|
+
const languages = memberships.get(page) ?? /* @__PURE__ */ new Set();
|
|
3643
|
+
languages.add(locale);
|
|
3644
|
+
memberships.set(page, languages);
|
|
3645
|
+
}
|
|
3646
|
+
observe(node.children ?? [], locale);
|
|
3647
|
+
}
|
|
3648
|
+
};
|
|
3649
|
+
observe(checkpoint.navigation);
|
|
3650
|
+
const primaryIds = new Set(pages.filter((page) => !declaredLocale(page) || declaredLocale(page) === defaultLocale).filter((page) => memberships.get(page)?.has(defaultLocale)).map((page) => page.navigationId));
|
|
3651
|
+
const remapped = /* @__PURE__ */ new Map();
|
|
3652
|
+
for (const page of pages) {
|
|
3653
|
+
const languages = memberships.get(page);
|
|
3654
|
+
if (languages?.size !== 1) continue;
|
|
3655
|
+
const locale = [...languages][0];
|
|
3656
|
+
const prefix = `${locale.toLowerCase()}-`;
|
|
3657
|
+
if (locale === defaultLocale || !page.navigationId.toLowerCase().startsWith(prefix)) continue;
|
|
3658
|
+
const navigationId2 = page.navigationId.slice(prefix.length);
|
|
3659
|
+
if (primaryIds.has(navigationId2)) remapped.set(page, { locale, navigationId: navigationId2 });
|
|
3660
|
+
}
|
|
3661
|
+
const canonicalLocale = (page) => remapped.get(page)?.locale ?? declaredLocale(page);
|
|
3662
|
+
const navigationId = (page) => remapped.get(page)?.navigationId ?? page.navigationId;
|
|
3631
3663
|
const locales = [...new Set(pages.map(canonicalLocale).filter((locale) => Boolean(locale)))];
|
|
3632
|
-
const defaultLocale = pages.some((page) => !page.locale) || locales.includes("en") ? "en" : locales[0] ?? "en";
|
|
3633
3664
|
const pageId = (page) => {
|
|
3634
3665
|
const locale = canonicalLocale(page);
|
|
3635
|
-
return locale ? `${locale === defaultLocale ? "" : `${locale}/`}${page
|
|
3666
|
+
return locale ? `${locale === defaultLocale ? "" : `${locale}/`}${navigationId(page)}` : page.id;
|
|
3636
3667
|
};
|
|
3637
3668
|
const route = (page) => {
|
|
3638
3669
|
const locale = canonicalLocale(page);
|
|
3639
3670
|
const prefix = locale && locale !== defaultLocale ? `/${locale}` : "";
|
|
3640
|
-
return `${prefix}${page
|
|
3671
|
+
return `${prefix}${navigationId(page) === "introduction" ? "/" : `/${navigationId(page)}`}`;
|
|
3641
3672
|
};
|
|
3642
|
-
|
|
3673
|
+
const redirects = checkpoint.records.flatMap((record) => {
|
|
3674
|
+
if (!record.page || !remapped.has(record.page)) return [];
|
|
3675
|
+
const source = new URL(record.url);
|
|
3676
|
+
return source.search || !/^\/[A-Za-z0-9/_~.%+-]*$/.test(source.pathname) ? [] : [{ source: source.pathname, destination: route(record.page), permanent: true }];
|
|
3677
|
+
});
|
|
3678
|
+
return { canonicalLocale, navigationId, locales, defaultLocale, pageId, route, redirects };
|
|
3643
3679
|
}
|
|
3644
3680
|
function validateUrlMigration(checkpoint, cancelled = false) {
|
|
3645
3681
|
const issues = [];
|
|
@@ -3648,7 +3684,7 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
3648
3684
|
const records = new Map(checkpoint.records.map((record) => [record.url, record]));
|
|
3649
3685
|
const source = new URL(checkpoint.sourceUrl);
|
|
3650
3686
|
const projection = localeProjection(checkpoint);
|
|
3651
|
-
const primaryIds = new Set(checkpoint.records.flatMap((record) => record.page && (!record.page
|
|
3687
|
+
const primaryIds = new Set(checkpoint.records.flatMap((record) => record.page && (!projection.canonicalLocale(record.page) || projection.canonicalLocale(record.page) === projection.defaultLocale) ? [projection.navigationId(record.page)] : []));
|
|
3652
3688
|
const outputIds = /* @__PURE__ */ new Set();
|
|
3653
3689
|
const resolve3 = (url) => {
|
|
3654
3690
|
const record = records.get(canonical(url, checkpoint.sourceUrl) ?? "");
|
|
@@ -3662,8 +3698,8 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
3662
3698
|
const outputId = projection.pageId(record.page);
|
|
3663
3699
|
if (outputIds.has(outputId)) issues.push({ code: "collision", source: record.url, message: `Multiple source pages map to runtime path ${outputId}.` });
|
|
3664
3700
|
outputIds.add(outputId);
|
|
3665
|
-
if (record.page
|
|
3666
|
-
issues.push({ code: "missing-page", source: record.url, message: `Secondary locale ${record.page
|
|
3701
|
+
if (projection.canonicalLocale(record.page) && projection.canonicalLocale(record.page) !== projection.defaultLocale && !primaryIds.has(projection.navigationId(record.page))) {
|
|
3702
|
+
issues.push({ code: "missing-page", source: record.url, message: `Secondary locale ${projection.canonicalLocale(record.page)} requires a primary ${projection.defaultLocale} page at ${projection.navigationId(record.page)}; the runtime cannot serve an orphan translation.` });
|
|
3667
3703
|
}
|
|
3668
3704
|
const actual = markdownCode(record.page.body);
|
|
3669
3705
|
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." });
|
|
@@ -3697,13 +3733,20 @@ function validateUrlMigration(checkpoint, cancelled = false) {
|
|
|
3697
3733
|
}
|
|
3698
3734
|
}
|
|
3699
3735
|
}
|
|
3700
|
-
const visit = (nodes) => {
|
|
3736
|
+
const visit = (nodes, parentLocale) => {
|
|
3701
3737
|
for (const node of nodes) {
|
|
3738
|
+
const locale = node.locale ? canonicalLanguage(node.locale) : parentLocale;
|
|
3702
3739
|
if (node.url) {
|
|
3703
3740
|
const url = new URL(node.url, source);
|
|
3704
|
-
if (inScope(url, source, checkpoint.scopePath)
|
|
3741
|
+
if (inScope(url, source, checkpoint.scopePath)) {
|
|
3742
|
+
const page = resolve3(url.toString())?.page;
|
|
3743
|
+
if (!page) issues.push({ code: "navigation-missing", source: node.url, message: `Source navigation target ${node.label} was not imported.` });
|
|
3744
|
+
else if (locale && locale !== projection.defaultLocale && !outputIds.has(`${locale}/${projection.navigationId(page)}`)) {
|
|
3745
|
+
issues.push({ code: "navigation-missing", source: node.url, message: `The ${locale} sidebar target ${node.label} has no authored localized runtime page.` });
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3705
3748
|
}
|
|
3706
|
-
visit(node.children ?? []);
|
|
3749
|
+
visit(node.children ?? [], locale);
|
|
3707
3750
|
}
|
|
3708
3751
|
};
|
|
3709
3752
|
visit(checkpoint.navigation);
|
|
@@ -4121,7 +4164,7 @@ ${difference}`;
|
|
|
4121
4164
|
}
|
|
4122
4165
|
};
|
|
4123
4166
|
labelPages(checkpoint.navigation);
|
|
4124
|
-
const pages = checkpoint.records.flatMap((record) => record.page ? [{ ...record.page, id: projection.pageId(record.page), ...record.page
|
|
4167
|
+
const pages = checkpoint.records.flatMap((record) => record.page ? [{ ...record.page, id: projection.pageId(record.page), navigationId: projection.navigationId(record.page), ...projection.canonicalLocale(record.page) ? { locale: projection.canonicalLocale(record.page) } : {}, ...navigationLabels.has(record.url) ? { navTitle: navigationLabels.get(record.url) } : {}, body: rewriteMarkdownDestinations(record.page.body, (value) => {
|
|
4125
4168
|
let url;
|
|
4126
4169
|
try {
|
|
4127
4170
|
url = new URL(value, record.evidence.finalUrl);
|
|
@@ -4151,11 +4194,12 @@ ${difference}`;
|
|
|
4151
4194
|
platform: options.platform ?? "generic",
|
|
4152
4195
|
pages,
|
|
4153
4196
|
assets: [...assets.values()],
|
|
4154
|
-
docsConfig: navigationConfig(checkpoint
|
|
4197
|
+
docsConfig: navigationConfig(checkpoint, records) ?? buildNavigationFromPages(pages),
|
|
4155
4198
|
warnings: [...validation.issues, ...validation.warnings ?? []].map((issue) => ({ code: issue.code === "missing-page" ? "missing-page" : "unsupported-config", message: issue.message, source: issue.source })),
|
|
4156
4199
|
urlMigrationValidation: validation,
|
|
4157
4200
|
stats: { discovered: records.size, imported: pages.length, skipped: records.size - pages.length }
|
|
4158
4201
|
};
|
|
4202
|
+
if (projection.redirects.length) bundle.docsConfig.redirects = projection.redirects;
|
|
4159
4203
|
if (projection.locales.length) {
|
|
4160
4204
|
const locales = [.../* @__PURE__ */ new Set([projection.defaultLocale, ...projection.locales])];
|
|
4161
4205
|
bundle.docsConfig.i18n = { ...bundle.docsConfig.i18n, defaultLocale: projection.defaultLocale, locales: locales.map((code) => ({ code, label: code === "en" ? "English" : code })) };
|