@thallylabs/migrate 0.2.34 → 0.2.35
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 +34 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +203 -52
- package/dist/index.js.map +1 -1
- package/dist/{url-migration-EwtII4P-.d.ts → url-migration-peVzTLjL.d.ts} +13 -0
- package/dist/url-migration.d.ts +1 -1
- package/dist/url-migration.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,6 +92,40 @@ instead of silently dropping that API tab's reference. AsyncAPI has no Thally
|
|
|
92
92
|
renderer, so it always produces a clear warning naming the spec instead of
|
|
93
93
|
disappearing.
|
|
94
94
|
|
|
95
|
+
`METHOD /path` entries (`"POST /v2/keys.createKey"`) under a container that
|
|
96
|
+
binds an `openapi` spec become `api.groups` on the tab that spec binds to:
|
|
97
|
+
nested groups, icons and authored order are kept, and page entries stay in the
|
|
98
|
+
tab's own groups (a group that mixes pages and operations is split, so the
|
|
99
|
+
sidebar lists the API groups after the page groups). Operations written
|
|
100
|
+
straight under a tab form a group named after the tab, at their place among the
|
|
101
|
+
tab's own groups (each uninterrupted run is its own group, so authored order is
|
|
102
|
+
kept). Hidden groups (and containers under one), groups under access-restricted
|
|
103
|
+
containers, and groups left empty are left out. Groups nest at most 32 levels
|
|
104
|
+
deep, as the renderer follows no deeper: operations below that are not migrated,
|
|
105
|
+
and one warning says so. With `navigation.languages`, only the default language's
|
|
106
|
+
containers contribute groups; operation entries under another language are
|
|
107
|
+
reported, since its API tab is the default language's.
|
|
108
|
+
When several containers bind one spec to the same tab (one API tab per product,
|
|
109
|
+
say), their groups are concatenated in order and an operation already listed
|
|
110
|
+
earlier in that tab is dropped (the method is compared without case, the path
|
|
111
|
+
exactly), unless one of those containers lists no operation (Mintlify generated
|
|
112
|
+
every operation there), in which case the tab gets no `api.groups` and keeps the
|
|
113
|
+
generated, tag-grouped navigation. A container whose operations are all hidden or
|
|
114
|
+
access-restricted adds no groups but still counts as listing operations; when
|
|
115
|
+
every container bound to a tab is like that, the tab gets `navigation: false`
|
|
116
|
+
(the source sidebar showed none of those operations, so no generated list is
|
|
117
|
+
added). Operations dropped only for nesting too deep are Thally's limit, not the
|
|
118
|
+
author's choice: such a container counts as listing nothing, so the tab keeps
|
|
119
|
+
the generated navigation. Groups with the same title are not folded together. With `api.groups`
|
|
120
|
+
the authored list is the tab's navigation, so `navigation: false` is not
|
|
121
|
+
written. Operation entries with no `openapi` container above them, and every one in a
|
|
122
|
+
site imported by URL, cannot be placed: one warning says they were not added to the API navigation, and the
|
|
123
|
+
operations still have their pages. This is separate from a `directory`'s flat
|
|
124
|
+
operation list, which still only drives the endpoint redirects.
|
|
125
|
+
|
|
126
|
+
Mintlify `styling.eyebrows: "breadcrumbs"` becomes `eyebrow: "path"` (the full
|
|
127
|
+
group path above the page title); `"section"` or no setting writes nothing.
|
|
128
|
+
|
|
95
129
|
Page `openapi:` frontmatter with a spec prefix
|
|
96
130
|
(`openapi: "specs/openapi-b.yaml GET /widgets/{id}"`) is rewritten to where the
|
|
97
131
|
spec lands in the output (`/openapi-b.yaml GET /widgets/{id}`). As in Mintlify,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MigrationPage, a as MigrationAsset, b as MigrationNavigationGroup, R as RenderedMigrationFile, c as MigrationWarning, d as MigrationFetcher, e as MigrationPlatform, f as MigrationBundle, g as MigrationDocsConfig } from './url-migration-
|
|
2
|
-
export { h as MigrationCapture, i as MigrationCaptureProvider, j as MigrationCaptureState, k as MigrationCapturedAsset, l as MigrationEvidence, m as MigrationFetchRequest, n as MigrationFetchResponse, o as MigrationJournalAsset, p as MigrationNavigationOccurrence, q as MigrationNavigationTab, U as UrlMigrationCheckpoint, r as UrlMigrationCheckpointDelta, s as UrlMigrationIssue, t as UrlMigrationRecord, u as UrlMigrationRun, v as UrlMigrationRunOptions, w as UrlMigrationValidation, x as classifyMigrationResponse, y as mergeMigrationConfig, z as renderMigrationFiles, A as runUrlMigration, B as validateUrlMigration } from './url-migration-
|
|
1
|
+
import { M as MigrationPage, a as MigrationAsset, b as MigrationNavigationGroup, R as RenderedMigrationFile, c as MigrationWarning, d as MigrationFetcher, e as MigrationPlatform, f as MigrationBundle, g as MigrationDocsConfig } from './url-migration-peVzTLjL.js';
|
|
2
|
+
export { h as MigrationCapture, i as MigrationCaptureProvider, j as MigrationCaptureState, k as MigrationCapturedAsset, l as MigrationEvidence, m as MigrationFetchRequest, n as MigrationFetchResponse, o as MigrationJournalAsset, p as MigrationNavigationOccurrence, q as MigrationNavigationTab, U as UrlMigrationCheckpoint, r as UrlMigrationCheckpointDelta, s as UrlMigrationIssue, t as UrlMigrationRecord, u as UrlMigrationRun, v as UrlMigrationRunOptions, w as UrlMigrationValidation, x as classifyMigrationResponse, y as mergeMigrationConfig, z as renderMigrationFiles, A as runUrlMigration, B as validateUrlMigration } from './url-migration-peVzTLjL.js';
|
|
3
3
|
export { isPassiveSvg } from './svg-policy.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -182,6 +182,11 @@ interface MintlifyProjectionOptions {
|
|
|
182
182
|
* when the repository was not mapped with `--source-ref`.
|
|
183
183
|
*/
|
|
184
184
|
resolveSourceRef?: (repo: string) => Array<string | MigrationNavigationGroup> | null;
|
|
185
|
+
/**
|
|
186
|
+
* Set by a caller that binds the authored operation lists (`mintlifyNavigationApiReferences`
|
|
187
|
+
* `groups`) to their API tabs. Without it every "METHOD /path" entry is reported as not carried over.
|
|
188
|
+
*/
|
|
189
|
+
operationGroups?: boolean;
|
|
185
190
|
}
|
|
186
191
|
/** Preserve Mintlify's implicit `/section` route for section landing pages. */
|
|
187
192
|
declare function addMintlifyDirectoryRedirects(config: MigrationDocsConfig, pages: Array<MigrationPage>): MigrationDocsConfig;
|
package/dist/index.js
CHANGED
|
@@ -4524,39 +4524,106 @@ function specSource(value) {
|
|
|
4524
4524
|
}
|
|
4525
4525
|
function listedOperations(container) {
|
|
4526
4526
|
const operations = [];
|
|
4527
|
-
const visit = (entry,
|
|
4527
|
+
const visit = (entry, depth) => {
|
|
4528
4528
|
if (typeof entry === "string") {
|
|
4529
4529
|
if (OPENAPI_OPERATION_REF.test(entry)) operations.push(entry.trim());
|
|
4530
4530
|
return;
|
|
4531
4531
|
}
|
|
4532
4532
|
const object2 = objectValue2(entry);
|
|
4533
|
-
if (!object2 ||
|
|
4533
|
+
if (!object2 || depth > SWEEP_DEPTH_LIMIT || depth > 0 && (specSource(object2.openapi) || specSource(object2.asyncapi))) return;
|
|
4534
4534
|
for (const key of ["groups", "pages"]) {
|
|
4535
|
-
if (Array.isArray(object2[key])) object2[key].forEach((child) => visit(child,
|
|
4535
|
+
if (Array.isArray(object2[key])) object2[key].forEach((child) => visit(child, depth + 1));
|
|
4536
4536
|
}
|
|
4537
4537
|
};
|
|
4538
|
-
visit(container,
|
|
4538
|
+
visit(container, 0);
|
|
4539
4539
|
return operations;
|
|
4540
4540
|
}
|
|
4541
|
+
var bindsSpec = (node) => Boolean(specSource(node.openapi) || specSource(node.asyncapi));
|
|
4542
|
+
var hasGroupShape = (node) => "group" in node || Array.isArray(node.pages);
|
|
4543
|
+
var API_GROUPS_MAX_DEPTH = 32;
|
|
4544
|
+
var operationKey = (entry) => entry.trim().replace(/^(\S+)\s+/, "$1 ");
|
|
4545
|
+
function operationGroups(container, maxLevel = API_GROUPS_MAX_DEPTH) {
|
|
4546
|
+
const entries = (node, level) => ["groups", "pages"].flatMap((key) => (Array.isArray(node[key]) ? node[key] : []).flatMap((entry) => {
|
|
4547
|
+
if (typeof entry === "string") return OPENAPI_OPERATION_REF.test(entry) ? [operationKey(entry)] : [];
|
|
4548
|
+
const object2 = objectValue2(entry);
|
|
4549
|
+
const group = object2 && !bindsSpec(object2) ? groupOf(object2, level + 1) : void 0;
|
|
4550
|
+
return group ? [group] : [];
|
|
4551
|
+
}));
|
|
4552
|
+
const groupOf = (node, level) => {
|
|
4553
|
+
if (level > maxLevel || node.hidden === true || navigationGateReason(node)) return void 0;
|
|
4554
|
+
if (!hasGroupShape(node)) return void 0;
|
|
4555
|
+
const pages = entries(node, level);
|
|
4556
|
+
const icon = iconName(node.icon);
|
|
4557
|
+
return pages.length > 0 ? { group: labelFor(node, "Documentation"), ...icon ? { icon } : {}, pages } : void 0;
|
|
4558
|
+
};
|
|
4559
|
+
if (typeof container.group === "string") {
|
|
4560
|
+
const group = groupOf(container, 1);
|
|
4561
|
+
return group ? [group] : [];
|
|
4562
|
+
}
|
|
4563
|
+
const label = labelFor(container, "API reference");
|
|
4564
|
+
const groups = [];
|
|
4565
|
+
let run;
|
|
4566
|
+
for (const entry of entries(container, 0)) {
|
|
4567
|
+
if (typeof entry !== "string") {
|
|
4568
|
+
groups.push(entry);
|
|
4569
|
+
run = void 0;
|
|
4570
|
+
} else if (run) {
|
|
4571
|
+
run.push(entry);
|
|
4572
|
+
} else {
|
|
4573
|
+
run = [entry];
|
|
4574
|
+
groups.push({ group: label, pages: run });
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
return groups;
|
|
4578
|
+
}
|
|
4579
|
+
var groupOperationKeys = (groups) => groups.flatMap(({ pages }) => pages.flatMap((page) => typeof page === "string" ? [page] : groupOperationKeys([page])));
|
|
4580
|
+
function mergeApiGroups(existing, incoming) {
|
|
4581
|
+
const identity = (key) => key.replace(/^(\S+)/, (method) => method.toLowerCase());
|
|
4582
|
+
const seen = new Set(groupOperationKeys(existing ?? []).map(identity));
|
|
4583
|
+
const unseen = (groups) => groups.flatMap((group) => {
|
|
4584
|
+
const pages = group.pages.flatMap((page) => {
|
|
4585
|
+
if (typeof page !== "string") return unseen([page]);
|
|
4586
|
+
if (seen.has(identity(page))) return [];
|
|
4587
|
+
seen.add(identity(page));
|
|
4588
|
+
return [page];
|
|
4589
|
+
});
|
|
4590
|
+
return pages.length > 0 ? [{ ...group, pages }] : [];
|
|
4591
|
+
});
|
|
4592
|
+
const merged = [...existing ?? [], ...unseen(incoming ?? [])];
|
|
4593
|
+
return merged.length > 0 ? merged : void 0;
|
|
4594
|
+
}
|
|
4541
4595
|
var hasNavigationChildren = (node) => ["groups", "pages"].some((key) => Array.isArray(node[key]) && node[key].length > 0);
|
|
4542
4596
|
function mintlifyNavigationApiReferences(config) {
|
|
4543
4597
|
const navigation = objectValue2(config.navigation) ?? config;
|
|
4544
4598
|
const references = [];
|
|
4599
|
+
const defaultLanguage = pickDefaultLanguage(
|
|
4600
|
+
(Array.isArray(navigation.languages) ? navigation.languages : []).flatMap((value) => {
|
|
4601
|
+
const language = objectValue2(value);
|
|
4602
|
+
return language && withLanguageCode(language) ? [language] : [];
|
|
4603
|
+
})
|
|
4604
|
+
);
|
|
4545
4605
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus", "languages"];
|
|
4546
4606
|
const nestedKeys = ["groups", "pages"];
|
|
4547
|
-
function collect(node, tabLabel, sibling) {
|
|
4607
|
+
function collect(node, tabLabel, gated, otherLanguage, hidden, sibling) {
|
|
4548
4608
|
for (const kind of ["openapi", "asyncapi"]) {
|
|
4549
4609
|
const value = specSource(node[kind]);
|
|
4550
4610
|
const directory = objectValue2(node[kind])?.directory;
|
|
4551
4611
|
if (value) {
|
|
4552
|
-
const
|
|
4612
|
+
const listed = listedOperations(node);
|
|
4613
|
+
const operations = typeof directory === "string" && directory.trim() ? listed : [];
|
|
4614
|
+
const groups = gated || otherLanguage || hidden ? [] : operationGroups(node);
|
|
4615
|
+
const tooDeepOnly = groups.length === 0 && listed.length > 0 && !gated && !hidden && groupOperationKeys(operationGroups(node, SWEEP_DEPTH_LIMIT)).length === listed.length;
|
|
4616
|
+
const operationsWithheld = !otherLanguage && (hidden || groups.length === 0 && listed.length > 0 && !tooDeepOnly);
|
|
4553
4617
|
references.push({
|
|
4554
4618
|
value,
|
|
4555
4619
|
kind,
|
|
4556
4620
|
tabLabel,
|
|
4557
4621
|
...sibling,
|
|
4558
4622
|
...typeof directory === "string" && directory.trim() ? { directory: directory.trim() } : {},
|
|
4559
|
-
...operations.length > 0 ? { operations } : {}
|
|
4623
|
+
...operations.length > 0 ? { operations } : {},
|
|
4624
|
+
...groups.length > 0 ? { groups } : {},
|
|
4625
|
+
...operationsWithheld ? { operationsWithheld: true } : {},
|
|
4626
|
+
...otherLanguage ? { otherLanguage: true } : {}
|
|
4560
4627
|
});
|
|
4561
4628
|
}
|
|
4562
4629
|
}
|
|
@@ -4568,7 +4635,7 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
4568
4635
|
siblingSources.set(candidate, source);
|
|
4569
4636
|
return candidate;
|
|
4570
4637
|
}
|
|
4571
|
-
function visitChildren(rawNode, tabLabel) {
|
|
4638
|
+
function visitChildren(rawNode, tabLabel, gated, otherLanguage, hidden) {
|
|
4572
4639
|
const node = withFlattenedProductGroups(rawNode);
|
|
4573
4640
|
for (const key of containerKeys) {
|
|
4574
4641
|
const entries = node[key];
|
|
@@ -4576,7 +4643,7 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
4576
4643
|
for (const entry of entries) {
|
|
4577
4644
|
const object2 = objectValue2(entry);
|
|
4578
4645
|
if (!object2) continue;
|
|
4579
|
-
visit(object2, labelFor(object2, tabLabel ?? "Documentation"));
|
|
4646
|
+
visit(object2, labelFor(object2, tabLabel ?? "Documentation"), gated, otherLanguage || key === "languages" && rawNode === navigation && object2 !== defaultLanguage, hidden);
|
|
4580
4647
|
}
|
|
4581
4648
|
}
|
|
4582
4649
|
for (const key of nestedKeys) {
|
|
@@ -4585,7 +4652,7 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
4585
4652
|
for (const entry of entries) {
|
|
4586
4653
|
if (typeof entry === "string") continue;
|
|
4587
4654
|
const object2 = objectValue2(entry);
|
|
4588
|
-
if (object2) visit(object2, tabLabel);
|
|
4655
|
+
if (object2) visit(object2, tabLabel, gated, otherLanguage, hidden || object2.hidden === true);
|
|
4589
4656
|
}
|
|
4590
4657
|
}
|
|
4591
4658
|
if (Array.isArray(node.menu)) {
|
|
@@ -4597,24 +4664,26 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
4597
4664
|
const apiItems = items.filter((item) => specSource(item.openapi) || specSource(item.asyncapi));
|
|
4598
4665
|
const soleContent = apiItems.length === 1 && !hasNavigationChildren(node) && !items.some(hasNavigationChildren);
|
|
4599
4666
|
items.forEach((item, index) => {
|
|
4600
|
-
|
|
4667
|
+
const itemGated = gated || Boolean(navigationGateReason(item));
|
|
4668
|
+
if (soleContent && apiItems[0] === item) collect(item, tab, itemGated, otherLanguage, hidden);
|
|
4601
4669
|
else if (apiItems.includes(item)) {
|
|
4602
4670
|
const label = uniqueSiblingLabel(
|
|
4603
4671
|
`${tab}: ${labelFor(item, `Menu item ${index + 1}`)}`,
|
|
4604
4672
|
`${specSource(item.openapi) ?? ""}|${specSource(item.asyncapi) ?? ""}`
|
|
4605
4673
|
);
|
|
4606
4674
|
const icon = iconName(item.icon);
|
|
4607
|
-
collect(item, label, { parentTab: tab, ...icon ? { icon } : {}, ...item.hidden === true ? { hidden: true } : {} });
|
|
4675
|
+
collect(item, label, itemGated, otherLanguage, hidden, { parentTab: tab, ...icon ? { icon } : {}, ...item.hidden === true ? { hidden: true } : {} });
|
|
4608
4676
|
}
|
|
4609
|
-
visitChildren(item, tabLabel);
|
|
4677
|
+
visitChildren(item, tabLabel, itemGated, otherLanguage, hidden);
|
|
4610
4678
|
});
|
|
4611
4679
|
}
|
|
4612
4680
|
}
|
|
4613
|
-
function visit(node, tabLabel) {
|
|
4614
|
-
|
|
4615
|
-
|
|
4681
|
+
function visit(node, tabLabel, outerGated, otherLanguage, hidden) {
|
|
4682
|
+
const gated = outerGated || Boolean(navigationGateReason(node));
|
|
4683
|
+
collect(node, tabLabel, gated, otherLanguage, hidden);
|
|
4684
|
+
visitChildren(node, tabLabel, gated, otherLanguage, hidden);
|
|
4616
4685
|
}
|
|
4617
|
-
visit(navigation, void 0);
|
|
4686
|
+
visit(navigation, void 0, false, false, false);
|
|
4618
4687
|
return references;
|
|
4619
4688
|
}
|
|
4620
4689
|
function insertApiTab(tabs, tab, parentTab) {
|
|
@@ -4637,6 +4706,17 @@ function normalizePageRef(value, pathPrefix = "") {
|
|
|
4637
4706
|
ref = ref.replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
4638
4707
|
return pageIdFromReference(ref, true);
|
|
4639
4708
|
}
|
|
4709
|
+
var UNBOUND_OPERATIONS = { bound: false, gated: false, hidden: false, level: 0 };
|
|
4710
|
+
function descendOperationScope(scope, node, nested) {
|
|
4711
|
+
const binds = bindsSpec(node);
|
|
4712
|
+
return {
|
|
4713
|
+
bound: !nested && scope.bound || binds,
|
|
4714
|
+
gated: scope.gated || Boolean(navigationGateReason(node)),
|
|
4715
|
+
hidden: scope.hidden || !nested && node.hidden === true,
|
|
4716
|
+
// A container that binds a spec roots a tree of its own, where it is the first level when it is a group (as `operationGroups` reads it).
|
|
4717
|
+
level: binds ? typeof node.group === "string" ? 1 : 0 : !nested && scope.bound && hasGroupShape(node) ? scope.level + 1 : scope.level
|
|
4718
|
+
};
|
|
4719
|
+
}
|
|
4640
4720
|
var SWEEP_DEPTH_LIMIT = 256;
|
|
4641
4721
|
function collectGatedSourceRefs(node, gate, out, limits, depth = 0) {
|
|
4642
4722
|
if (depth > SWEEP_DEPTH_LIMIT) {
|
|
@@ -4670,15 +4750,24 @@ function collectReferenceAccess(node, access, pathPrefix, out, depth = 0) {
|
|
|
4670
4750
|
}
|
|
4671
4751
|
for (const value of Object.values(object2)) collectReferenceAccess(value, nested, pathPrefix, out, depth + 1);
|
|
4672
4752
|
}
|
|
4673
|
-
function withContainerAccess(object2, context, walk2) {
|
|
4753
|
+
function withContainerAccess(object2, context, walk2, nested = false) {
|
|
4674
4754
|
const outer = context.access;
|
|
4675
4755
|
context.access = descendNavigationAccess(outer, navigationAccess(object2));
|
|
4676
4756
|
try {
|
|
4677
|
-
return walk2
|
|
4757
|
+
return withOperationScope(object2, context, nested, walk2);
|
|
4678
4758
|
} finally {
|
|
4679
4759
|
context.access = outer;
|
|
4680
4760
|
}
|
|
4681
4761
|
}
|
|
4762
|
+
function withOperationScope(object2, context, nested, walk2) {
|
|
4763
|
+
const outer = context.operations;
|
|
4764
|
+
context.operations = descendOperationScope(outer, object2, nested);
|
|
4765
|
+
try {
|
|
4766
|
+
return walk2();
|
|
4767
|
+
} finally {
|
|
4768
|
+
context.operations = outer;
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4682
4771
|
function warnOnce(context, key, message) {
|
|
4683
4772
|
if (context.warningKeys.has(key)) return;
|
|
4684
4773
|
context.warningKeys.add(key);
|
|
@@ -4716,11 +4805,32 @@ var OPENAPI_OPERATION_REF = /^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+\S/;
|
|
|
4716
4805
|
function convertPage(value, context) {
|
|
4717
4806
|
if (typeof value === "string") {
|
|
4718
4807
|
if (OPENAPI_OPERATION_REF.test(value)) {
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4808
|
+
const { bound, gated, hidden, level } = context.operations;
|
|
4809
|
+
if (!context.operationGroups || !bound) {
|
|
4810
|
+
warnOnce(
|
|
4811
|
+
context,
|
|
4812
|
+
"openapi-operation-list",
|
|
4813
|
+
'Navigation lists individual OpenAPI operations (e.g. "GET /users") that were not added to the API navigation: an operation list is carried over only from a tab or group that names an openapi spec in a migrated docs.json. The operations themselves still have pages in the API reference.'
|
|
4814
|
+
);
|
|
4815
|
+
} else if (gated) {
|
|
4816
|
+
warnOnce(
|
|
4817
|
+
context,
|
|
4818
|
+
"openapi-operation-list-gated",
|
|
4819
|
+
'Navigation lists individual OpenAPI operations (e.g. "GET /users") under access-restricted groups, so they were not added to the API navigation. The operations themselves still have pages in the API reference.'
|
|
4820
|
+
);
|
|
4821
|
+
} else if (context.otherLanguage) {
|
|
4822
|
+
warnOnce(
|
|
4823
|
+
context,
|
|
4824
|
+
"openapi-operation-list-language",
|
|
4825
|
+
'Navigation lists individual OpenAPI operations (e.g. "GET /users") under a language other than the default, so they were not added to the API navigation: the API tab follows the default language. The operations themselves still have pages in the API reference.'
|
|
4826
|
+
);
|
|
4827
|
+
} else if (!hidden && level > API_GROUPS_MAX_DEPTH) {
|
|
4828
|
+
warnOnce(
|
|
4829
|
+
context,
|
|
4830
|
+
"openapi-operation-list-depth",
|
|
4831
|
+
`Navigation lists individual OpenAPI operations (e.g. "GET /users") in groups nested more than ${API_GROUPS_MAX_DEPTH} levels deep, so they were not added to the API navigation: the API navigation follows no deeper groups. The operations themselves still have pages in the API reference.`
|
|
4832
|
+
);
|
|
4833
|
+
}
|
|
4724
4834
|
return null;
|
|
4725
4835
|
}
|
|
4726
4836
|
return registerReference(value, context);
|
|
@@ -4831,10 +4941,10 @@ function convertMenuItems(items, context) {
|
|
|
4831
4941
|
`Mintlify menu item "${label}" has an openapi/asyncapi value without a "source", so its API reference was not migrated.`
|
|
4832
4942
|
);
|
|
4833
4943
|
}
|
|
4834
|
-
const children = convertNavigationValues([
|
|
4944
|
+
const children = withOperationScope(item, context, true, () => convertNavigationValues([
|
|
4835
4945
|
...Array.isArray(item.groups) ? item.groups : [],
|
|
4836
4946
|
...Array.isArray(item.pages) ? item.pages : []
|
|
4837
|
-
], context);
|
|
4947
|
+
], context));
|
|
4838
4948
|
if (typeof item.href === "string" && item.href) {
|
|
4839
4949
|
warnOnce(
|
|
4840
4950
|
context,
|
|
@@ -4894,7 +5004,7 @@ function convertContainerToTabs(containerValue, context, fallbackTab, trace, dep
|
|
|
4894
5004
|
...object2.hidden === true ? { hidden: true } : {}
|
|
4895
5005
|
}];
|
|
4896
5006
|
}
|
|
4897
|
-
const nested = withContainerAccess(object2, context, () => convertContainerToTabs(object2, context, tab, trace, depth + 1));
|
|
5007
|
+
const nested = withContainerAccess(object2, context, () => convertContainerToTabs(object2, context, tab, trace, depth + 1), true);
|
|
4898
5008
|
if (nested.length > 0) {
|
|
4899
5009
|
if (nested.length === 1) {
|
|
4900
5010
|
return [{
|
|
@@ -5118,6 +5228,8 @@ function projectedCompatibleConfig(config, warnings) {
|
|
|
5118
5228
|
return {
|
|
5119
5229
|
// Mintlify pages show a group eyebrow above the title and no breadcrumb trail.
|
|
5120
5230
|
breadcrumbs: false,
|
|
5231
|
+
// `styling.eyebrows: "breadcrumbs"` shows the whole group path; "section" (the default) shows the nearest group.
|
|
5232
|
+
...objectValue2(config.styling)?.eyebrows === "breadcrumbs" ? { eyebrow: "path" } : {},
|
|
5121
5233
|
...projectedTheme(config.theme) ? { theme: projectedTheme(config.theme) } : {},
|
|
5122
5234
|
...projectedContextual.length > 0 ? { contextual: { options: projectedContextual } } : {},
|
|
5123
5235
|
...projectedColorsValue ? { colors: projectedColorsValue } : {},
|
|
@@ -5263,6 +5375,16 @@ function mintlifyAllVersionPrefixes(config) {
|
|
|
5263
5375
|
collect(navigation);
|
|
5264
5376
|
return prefixes;
|
|
5265
5377
|
}
|
|
5378
|
+
function withLanguageCode(language) {
|
|
5379
|
+
try {
|
|
5380
|
+
return { ...language, language: Intl.getCanonicalLocales(String(language.language ?? language.locale ?? "en"))[0] };
|
|
5381
|
+
} catch {
|
|
5382
|
+
return void 0;
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
function pickDefaultLanguage(languages) {
|
|
5386
|
+
return languages.find((entry) => entry.default === true) ?? languages[0];
|
|
5387
|
+
}
|
|
5266
5388
|
function projectMintlifyNavigation(config, options = {}) {
|
|
5267
5389
|
const warnings = [];
|
|
5268
5390
|
const references = [];
|
|
@@ -5273,6 +5395,8 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
5273
5395
|
const gatedNodes = /* @__PURE__ */ new Map();
|
|
5274
5396
|
const navigation = objectValue2(config.navigation) ?? config;
|
|
5275
5397
|
const rootAccess = descendNavigationAccess(OPEN_NAVIGATION_ACCESS, navigationAccess(navigation));
|
|
5398
|
+
const rootOperations = descendOperationScope(UNBOUND_OPERATIONS, navigation, true);
|
|
5399
|
+
const operationGroups2 = options.operationGroups === true;
|
|
5276
5400
|
const limits = { tooDeep: false };
|
|
5277
5401
|
collectGatedSourceRefs(navigation, void 0, gatedNodes, limits);
|
|
5278
5402
|
if (limits.tooDeep) {
|
|
@@ -5284,20 +5408,16 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
5284
5408
|
const languages = Array.isArray(navigation.languages) ? navigation.languages.flatMap((value) => {
|
|
5285
5409
|
const language = objectValue2(value);
|
|
5286
5410
|
if (!language) return [];
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
} catch {
|
|
5291
|
-
warnings.push({ code: "unsupported-config", message: "A navigation language with an invalid locale code was skipped." });
|
|
5292
|
-
return [];
|
|
5293
|
-
}
|
|
5411
|
+
const withCode = withLanguageCode(language);
|
|
5412
|
+
if (!withCode) warnings.push({ code: "unsupported-config", message: "A navigation language with an invalid locale code was skipped." });
|
|
5413
|
+
return withCode ? [withCode] : [];
|
|
5294
5414
|
}) : [];
|
|
5415
|
+
const defaultLanguage = pickDefaultLanguage(languages);
|
|
5295
5416
|
let tabs = [];
|
|
5296
5417
|
let i18n;
|
|
5297
5418
|
const localizedNavigation = {};
|
|
5298
5419
|
const projectionTrace = {};
|
|
5299
|
-
if (
|
|
5300
|
-
const defaultLanguage = languages.find((entry) => entry.default === true) ?? languages[0];
|
|
5420
|
+
if (defaultLanguage) {
|
|
5301
5421
|
const defaultLocale = String(defaultLanguage.language);
|
|
5302
5422
|
const locales = languages.map((entry) => {
|
|
5303
5423
|
const code = String(entry.language);
|
|
@@ -5323,6 +5443,9 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
5323
5443
|
warningKeys,
|
|
5324
5444
|
appearances,
|
|
5325
5445
|
access: descendNavigationAccess(rootAccess, navigationAccess(language)),
|
|
5446
|
+
operations: descendOperationScope(rootOperations, language, true),
|
|
5447
|
+
operationGroups: operationGroups2,
|
|
5448
|
+
otherLanguage: language !== defaultLanguage,
|
|
5326
5449
|
gatedSourceRefs,
|
|
5327
5450
|
gatedNodes,
|
|
5328
5451
|
limits
|
|
@@ -5342,9 +5465,10 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
5342
5465
|
}
|
|
5343
5466
|
if (Object.keys(localizedNavigation).length > 0) i18n.navigation = localizedNavigation;
|
|
5344
5467
|
} else {
|
|
5345
|
-
const context = { references, seenReferences, warnings, warningKeys, appearances, access: rootAccess, gatedSourceRefs, gatedNodes, limits, pathPrefix: options.pathPrefix, resolveSourceRef: options.resolveSourceRef };
|
|
5468
|
+
const context = { references, seenReferences, warnings, warningKeys, appearances, access: rootAccess, operations: rootOperations, operationGroups: operationGroups2, gatedSourceRefs, gatedNodes, limits, pathPrefix: options.pathPrefix, resolveSourceRef: options.resolveSourceRef };
|
|
5346
5469
|
tabs = convertContainerToTabs(navigation, context, "Documentation", projectionTrace);
|
|
5347
5470
|
if (tabs.length === 0 && Array.isArray(config.navigation)) {
|
|
5471
|
+
context.operations = UNBOUND_OPERATIONS;
|
|
5348
5472
|
const children = convertNavigationValues(config.navigation, context);
|
|
5349
5473
|
if (children.length > 0) {
|
|
5350
5474
|
const hasRootPages = children.some((page) => typeof page === "string");
|
|
@@ -5361,7 +5485,7 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
5361
5485
|
message: "Mintlify navigation could not be projected; generated navigation will be used."
|
|
5362
5486
|
});
|
|
5363
5487
|
}
|
|
5364
|
-
const versionOwner =
|
|
5488
|
+
const versionOwner = defaultLanguage ?? navigation;
|
|
5365
5489
|
const globalNavigation = objectValue2(versionOwner.global) ?? objectValue2(navigation.global);
|
|
5366
5490
|
const shortcuts = Array.isArray(globalNavigation?.anchors) ? globalNavigation.anchors.flatMap((value) => {
|
|
5367
5491
|
const anchor = objectValue2(value);
|
|
@@ -7354,13 +7478,13 @@ function refKeepsIn(ref, spec, specs) {
|
|
|
7354
7478
|
const { unique, sources } = resolvedSources(ref, specs);
|
|
7355
7479
|
return unique && sources[0] === spec.sourcePath;
|
|
7356
7480
|
}
|
|
7357
|
-
function
|
|
7481
|
+
function operationKey2(operation) {
|
|
7358
7482
|
const [first, ...rest] = operation.trim().split(/\s+/);
|
|
7359
7483
|
return `${first.toLowerCase()} ${rest.join(" ")}`;
|
|
7360
7484
|
}
|
|
7361
7485
|
function keysFor(refs, names) {
|
|
7362
7486
|
return [...new Set(
|
|
7363
|
-
refs.filter(names).map((ref) => splitOpenApiRef(ref)?.operation).filter((op) => !!op).map(
|
|
7487
|
+
refs.filter(names).map((ref) => splitOpenApiRef(ref)?.operation).filter((op) => !!op).map(operationKey2)
|
|
7364
7488
|
)];
|
|
7365
7489
|
}
|
|
7366
7490
|
function withheldOperationKeys(spec, withheldRefs, keptRefs, specs = [spec]) {
|
|
@@ -10830,14 +10954,28 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, n
|
|
|
10830
10954
|
delete mount.operations;
|
|
10831
10955
|
}
|
|
10832
10956
|
};
|
|
10957
|
+
const listsNothing = (reference) => !reference.groups && !reference.operationsWithheld && !reference.otherLanguage;
|
|
10958
|
+
const groupTargets = /* @__PURE__ */ new Map();
|
|
10833
10959
|
const specs = [];
|
|
10834
10960
|
for (const reference of references) {
|
|
10835
10961
|
const dedupeKey = `${reference.kind}:${reference.value}:${reference.tabLabel ?? ""}`;
|
|
10836
10962
|
if (seen.has(dedupeKey)) {
|
|
10837
10963
|
mountDirectory(remoteByKey.get(dedupeKey), reference);
|
|
10964
|
+
const shared = groupTargets.get(dedupeKey);
|
|
10965
|
+
if (shared && !shared.unlisted) {
|
|
10966
|
+
if (reference.operationsWithheld) shared.withheld = true;
|
|
10967
|
+
if (reference.groups) {
|
|
10968
|
+
const merged = mergeApiGroups(shared.target.groups, reference.groups);
|
|
10969
|
+
if (merged) shared.target.groups = merged;
|
|
10970
|
+
} else if (listsNothing(reference)) {
|
|
10971
|
+
shared.unlisted = true;
|
|
10972
|
+
delete shared.target.groups;
|
|
10973
|
+
}
|
|
10974
|
+
}
|
|
10838
10975
|
continue;
|
|
10839
10976
|
}
|
|
10840
10977
|
seen.add(dedupeKey);
|
|
10978
|
+
const groups = mergeApiGroups(void 0, reference.groups);
|
|
10841
10979
|
const tabSuffix = reference.tabLabel ? ` (tab "${reference.tabLabel}")` : "";
|
|
10842
10980
|
if (reference.kind === "asyncapi") {
|
|
10843
10981
|
warnings.push({
|
|
@@ -10847,10 +10985,11 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, n
|
|
|
10847
10985
|
continue;
|
|
10848
10986
|
}
|
|
10849
10987
|
if (/^https:\/\//i.test(reference.value)) {
|
|
10850
|
-
const remote = { url: reference.value, ...reference.tabLabel ? { tabLabel: reference.tabLabel } : {}, ...reference.parentTab ? { parentTab: reference.parentTab } : {}, ...reference.icon ? { icon: reference.icon } : {}, ...reference.hidden ? { hidden: true } : {} };
|
|
10988
|
+
const remote = { url: reference.value, ...reference.tabLabel ? { tabLabel: reference.tabLabel } : {}, ...reference.parentTab ? { parentTab: reference.parentTab } : {}, ...reference.icon ? { icon: reference.icon } : {}, ...reference.hidden ? { hidden: true } : {}, ...groups ? { groups } : {} };
|
|
10851
10989
|
mountDirectory(remote, reference);
|
|
10852
10990
|
remoteSpecs.push(remote);
|
|
10853
10991
|
remoteByKey.set(dedupeKey, remote);
|
|
10992
|
+
groupTargets.set(dedupeKey, { target: remote, unlisted: listsNothing(reference), withheld: reference.operationsWithheld === true });
|
|
10854
10993
|
warnings.push({
|
|
10855
10994
|
code: "unsupported-config",
|
|
10856
10995
|
message: `The remote OpenAPI spec "${reference.value}"${tabSuffix} requires a network download before this import is complete.`,
|
|
@@ -10879,7 +11018,7 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, n
|
|
|
10879
11018
|
}
|
|
10880
11019
|
const content = readSpecFile(match.absolutePath, match.relativePath, warnings);
|
|
10881
11020
|
if (!content) continue;
|
|
10882
|
-
|
|
11021
|
+
const spec = {
|
|
10883
11022
|
filename: basename3(match.relativePath),
|
|
10884
11023
|
content,
|
|
10885
11024
|
tabLabel: reference.tabLabel,
|
|
@@ -10888,8 +11027,14 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, n
|
|
|
10888
11027
|
hidden: reference.hidden,
|
|
10889
11028
|
...reference.pageOnly ? { pageOnly: true } : {},
|
|
10890
11029
|
sourcePath: match.relativePath,
|
|
10891
|
-
directory: reference.directory
|
|
10892
|
-
|
|
11030
|
+
directory: reference.directory,
|
|
11031
|
+
...groups ? { groups } : {}
|
|
11032
|
+
};
|
|
11033
|
+
specs.push(spec);
|
|
11034
|
+
groupTargets.set(dedupeKey, { target: spec, unlisted: listsNothing(reference), withheld: reference.operationsWithheld === true });
|
|
11035
|
+
}
|
|
11036
|
+
for (const { target: target2, unlisted, withheld } of groupTargets.values()) {
|
|
11037
|
+
if (withheld && !unlisted && !target2.groups) target2.navigation = false;
|
|
10893
11038
|
}
|
|
10894
11039
|
for (const spec of specs) spec.filename = nameSpec(spec);
|
|
10895
11040
|
return specs;
|
|
@@ -12255,15 +12400,18 @@ function injectOpenApiSpecs(config, specs, warnings) {
|
|
|
12255
12400
|
// An API-only tab needs generated endpoint navigation; an authored
|
|
12256
12401
|
// page tab keeps its own groups alongside the bound spec, unless
|
|
12257
12402
|
// Mintlify generated the endpoint pages (the renderer appends the
|
|
12258
|
-
// endpoint groups after the authored ones).
|
|
12259
|
-
|
|
12403
|
+
// endpoint groups after the authored ones) or docs.json lists the
|
|
12404
|
+
// operations itself (those authored groups are the API navigation).
|
|
12405
|
+
// A spec whose listed operations are all withheld has no endpoint navigation either way.
|
|
12406
|
+
...spec.navigation === false || (apiTab.groups?.length || apiTab.pages?.length) && !spec.generatedNavigation && !spec.groups ? { navigation: false } : {},
|
|
12407
|
+
...spec.groups ? { groups: spec.groups } : {}
|
|
12260
12408
|
};
|
|
12261
12409
|
} else {
|
|
12262
12410
|
insertApiTab(tabs, {
|
|
12263
12411
|
tab: spec.tabLabel ?? "API Reference",
|
|
12264
12412
|
...spec.icon ? { icon: spec.icon } : {},
|
|
12265
12413
|
...spec.hidden ? { hidden: true } : {},
|
|
12266
|
-
api: { source: specAssetPath(spec.filename) }
|
|
12414
|
+
api: { source: specAssetPath(spec.filename), ...spec.navigation === false ? { navigation: false } : {}, ...spec.groups ? { groups: spec.groups } : {} }
|
|
12267
12415
|
}, spec.parentTab);
|
|
12268
12416
|
}
|
|
12269
12417
|
}
|
|
@@ -12348,6 +12496,8 @@ function migrateRepository(options) {
|
|
|
12348
12496
|
defaultVersionPrefixes = mintlifyDefaultVersionPrefixes(config);
|
|
12349
12497
|
allVersionPrefixes = mintlifyAllVersionPrefixes(config);
|
|
12350
12498
|
const projected = projectMintlifyNavigation(config, {
|
|
12499
|
+
// injectOpenApiSpecs / hydrateRemoteApiSpecs bind the authored operation lists to their API tabs.
|
|
12500
|
+
operationGroups: true,
|
|
12351
12501
|
resolveSourceRef: (repo) => {
|
|
12352
12502
|
const ref = activeSourceRefs.find((candidate) => candidate.repo.toLowerCase() === repo.toLowerCase());
|
|
12353
12503
|
if (!ref) return null;
|
|
@@ -13489,7 +13639,7 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13489
13639
|
const result = excludeWithheldOperations(spec, withheldSpecRefs, keptSpecRefs, allSpecs);
|
|
13490
13640
|
spec.content = result.content;
|
|
13491
13641
|
withheldOperations.set(spec, result.excluded);
|
|
13492
|
-
result.excluded.forEach((operation) => withheldKeys.add(
|
|
13642
|
+
result.excluded.forEach((operation) => withheldKeys.add(operationKey2(operation)));
|
|
13493
13643
|
}
|
|
13494
13644
|
const resolvedSpecs = allSpecs.filter((spec) => !spec.pageOnly);
|
|
13495
13645
|
const pageOnlySpecs = allSpecs.filter((spec) => spec.pageOnly);
|
|
@@ -13531,7 +13681,7 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13531
13681
|
const fallbackSpec = { sourcePath: fallback.relativePath, filename, content: fallbackContent };
|
|
13532
13682
|
({ kept: keptSpecRefs, unowned: unownedOperations } = keptOperations(fallback.relativePath));
|
|
13533
13683
|
const pruned = excludeWithheldOperations(fallbackSpec, withheldSpecRefs, keptSpecRefs, [fallbackSpec, ...allSpecs]);
|
|
13534
|
-
pruned.excluded.forEach((operation) => withheldKeys.add(
|
|
13684
|
+
pruned.excluded.forEach((operation) => withheldKeys.add(operationKey2(operation)));
|
|
13535
13685
|
if (!assets.some((asset) => asset.path === specAssetPath(filename))) {
|
|
13536
13686
|
assets.push(specAsset(filename, pruned.content));
|
|
13537
13687
|
}
|
|
@@ -13549,7 +13699,7 @@ ${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
|
13549
13699
|
docsConfig = injectOpenApiSpecs(docsConfig, pageOnlySpecs, warnings);
|
|
13550
13700
|
pageSpecs = [...pageSpecs, ...pageOnlySpecs];
|
|
13551
13701
|
}
|
|
13552
|
-
const notKeptPublic = [...new Set(unownedOperations.filter((operation) => withheldKeys.has(
|
|
13702
|
+
const notKeptPublic = [...new Set(unownedOperations.filter((operation) => withheldKeys.has(operationKey2(operation))))];
|
|
13553
13703
|
if (notKeptPublic.length > 0) {
|
|
13554
13704
|
warnings.push({
|
|
13555
13705
|
code: "gated-page",
|
|
@@ -15962,8 +16112,9 @@ async function hydrateRemoteApiSpecs(bundle, fetcher) {
|
|
|
15962
16112
|
if (ref?.specRef === reference.url) page.openapi = withSpecRef(ref, `openapi/${filename}`);
|
|
15963
16113
|
}
|
|
15964
16114
|
const tab = reference.tabLabel ? tabs.find((candidate) => candidate.tab === reference.tabLabel) : tabs.find((candidate) => candidate.tab.toLowerCase().includes("api"));
|
|
15965
|
-
|
|
15966
|
-
|
|
16115
|
+
const api = { source: `openapi/${filename}`, ...reference.navigation === false ? { navigation: false } : {}, ...reference.groups ? { groups: reference.groups } : {} };
|
|
16116
|
+
if (tab) tab.api = api;
|
|
16117
|
+
else insertApiTab(tabs, { tab: reference.tabLabel ?? "API Reference", ...reference.icon ? { icon: reference.icon } : {}, ...reference.hidden ? { hidden: true } : {}, api }, reference.parentTab);
|
|
15967
16118
|
bound.push({ reference, filename, content: body, operations, tabName: tab?.tab ?? reference.tabLabel ?? "API Reference" });
|
|
15968
16119
|
} catch (error) {
|
|
15969
16120
|
warnings.push({
|