@mintlify/common 1.0.1023 → 1.0.1024

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.
Files changed (38) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +1 -0
  3. package/dist/navigation/decoratedNavigationConfig.d.ts +2 -0
  4. package/dist/navigation/decoratedNavigationConfig.js +204 -0
  5. package/dist/navigation/filterDivisions.d.ts +65 -0
  6. package/dist/navigation/filterDivisions.js +697 -0
  7. package/dist/navigation/filterDivisions.test.d.ts +1 -0
  8. package/dist/navigation/filterDivisions.test.js +670 -0
  9. package/dist/navigation/getScopedNavForPath.test.d.ts +1 -0
  10. package/dist/navigation/getScopedNavForPath.test.js +135 -0
  11. package/dist/navigation/getVersionOrLanguageFromPath.d.ts +12 -0
  12. package/dist/navigation/getVersionOrLanguageFromPath.js +71 -0
  13. package/dist/navigation/getVersionOrLanguageFromPath.test.d.ts +1 -0
  14. package/dist/navigation/getVersionOrLanguageFromPath.test.js +133 -0
  15. package/dist/navigation/index.d.ts +5 -0
  16. package/dist/navigation/index.js +5 -0
  17. package/dist/navigation/isCommonPage.d.ts +8 -0
  18. package/dist/navigation/isCommonPage.js +36 -0
  19. package/dist/navigation/isCommonPage.test.d.ts +1 -0
  20. package/dist/navigation/isCommonPage.test.js +56 -0
  21. package/dist/navigation/pathsDifferByOneSegment.test.d.ts +1 -0
  22. package/dist/navigation/pathsDifferByOneSegment.test.js +91 -0
  23. package/dist/navigation/scopeNavToPath.d.ts +18 -0
  24. package/dist/navigation/scopeNavToPath.js +455 -0
  25. package/dist/navigation/scopeNavToPath.test.d.ts +1 -0
  26. package/dist/navigation/scopeNavToPath.test.js +117 -0
  27. package/dist/navigation/scopeOffPathDivisions.test.d.ts +1 -0
  28. package/dist/navigation/scopeOffPathDivisions.test.js +852 -0
  29. package/dist/navigation/scopeProductNav.test.d.ts +1 -0
  30. package/dist/navigation/scopeProductNav.test.js +288 -0
  31. package/dist/navigation/updateFirstHrefInDivision.d.ts +29 -0
  32. package/dist/navigation/updateFirstHrefInDivision.js +383 -0
  33. package/dist/paths/isEqualIgnoringLeadingSlash.d.ts +1 -0
  34. package/dist/paths/isEqualIgnoringLeadingSlash.js +21 -0
  35. package/dist/paths/isEqualIgnoringLeadingSlash.test.d.ts +1 -0
  36. package/dist/paths/isEqualIgnoringLeadingSlash.test.js +43 -0
  37. package/dist/tsconfig.build.tsbuildinfo +1 -1
  38. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,288 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { isEqualIgnoringLeadingSlash } from '../paths/isEqualIgnoringLeadingSlash.js';
3
+ import { filterDivisions } from './filterDivisions.js';
4
+ import { getAllPathsInDecoratedNav } from './getAllPathsInDecoratedNav.js';
5
+ import { getScopedNavForPath } from './scopeNavToPath.js';
6
+ const productVersionNav = {
7
+ products: [
8
+ {
9
+ product: 'Database',
10
+ versions: [
11
+ {
12
+ version: 'v2',
13
+ default: true,
14
+ tabs: [
15
+ {
16
+ tab: 'Docs',
17
+ groups: [
18
+ {
19
+ group: 'Get started',
20
+ pages: [
21
+ { title: 'Intro', href: '/db/v2/intro' },
22
+ { title: 'Only in v2', href: '/db/v2/only' },
23
+ ],
24
+ },
25
+ ],
26
+ },
27
+ {
28
+ tab: 'Reference',
29
+ pages: [{ title: 'SQL v2', href: '/db/v2/sql' }],
30
+ },
31
+ ],
32
+ },
33
+ {
34
+ version: 'v1',
35
+ tabs: [
36
+ {
37
+ tab: 'Docs',
38
+ groups: [
39
+ {
40
+ group: 'Get started',
41
+ pages: [{ title: 'Intro', href: '/db/v1/intro' }],
42
+ },
43
+ {
44
+ group: 'Legacy',
45
+ pages: [{ title: 'Migrate', href: '/db/v1/migrate' }],
46
+ },
47
+ ],
48
+ },
49
+ ],
50
+ },
51
+ ],
52
+ },
53
+ {
54
+ product: 'Cloud',
55
+ languages: [
56
+ {
57
+ language: 'en',
58
+ default: true,
59
+ tabs: [
60
+ { tab: 'Guides', pages: [{ title: 'Cloud intro', href: '/cloud/en/intro' }] },
61
+ { tab: 'API', pages: [{ title: 'Cloud API', href: '/cloud/en/api' }] },
62
+ ],
63
+ },
64
+ {
65
+ language: 'es',
66
+ tabs: [{ tab: 'Guides', pages: [{ title: 'Cloud intro es', href: '/cloud/es/intro' }] }],
67
+ },
68
+ ],
69
+ },
70
+ {
71
+ product: 'Releases',
72
+ pages: [{ title: 'Notes', href: '/releases/notes' }],
73
+ },
74
+ ],
75
+ };
76
+ const sharedPathProductNav = {
77
+ products: [
78
+ {
79
+ product: 'Database',
80
+ versions: [
81
+ {
82
+ version: 'v2',
83
+ default: true,
84
+ pages: [
85
+ { title: 'Shared setup', href: '/shared/setup' },
86
+ { title: 'Deploy v2', href: '/db/v2/deploy' },
87
+ ],
88
+ },
89
+ {
90
+ version: 'v1',
91
+ pages: [
92
+ { title: 'Shared setup', href: '/shared/setup' },
93
+ { title: 'Migrate v1', href: '/db/v1/migrate' },
94
+ ],
95
+ },
96
+ ],
97
+ },
98
+ {
99
+ product: 'Cloud',
100
+ pages: [
101
+ { title: 'Shared setup', href: '/shared/setup' },
102
+ { title: 'Cloud intro', href: '/cloud/intro' },
103
+ ],
104
+ },
105
+ ],
106
+ };
107
+ const nestedProductNav = {
108
+ languages: [
109
+ {
110
+ language: 'en',
111
+ default: true,
112
+ products: [
113
+ {
114
+ product: 'Database',
115
+ versions: [
116
+ {
117
+ version: 'v2',
118
+ default: true,
119
+ pages: [
120
+ { title: 'Shared setup', href: '/en/shared/setup' },
121
+ { title: 'Deploy v2', href: '/en/db/v2/deploy' },
122
+ ],
123
+ },
124
+ {
125
+ version: 'v1',
126
+ pages: [
127
+ { title: 'Shared setup', href: '/en/shared/setup' },
128
+ { title: 'Migrate v1', href: '/en/db/v1/migrate' },
129
+ ],
130
+ },
131
+ ],
132
+ },
133
+ {
134
+ product: 'Cloud',
135
+ pages: [
136
+ { title: 'Shared setup', href: '/en/shared/setup' },
137
+ { title: 'Cloud intro', href: '/en/cloud/intro' },
138
+ ],
139
+ },
140
+ ],
141
+ },
142
+ {
143
+ language: 'es',
144
+ products: [
145
+ {
146
+ product: 'Database',
147
+ pages: [{ title: 'Intro es', href: '/es/db/intro' }],
148
+ },
149
+ ],
150
+ },
151
+ ],
152
+ };
153
+ function allProductNamesOf(nav) {
154
+ const names = new Set();
155
+ const walk = (node) => {
156
+ if (Array.isArray(node)) {
157
+ node.forEach(walk);
158
+ }
159
+ else if (node && typeof node === 'object') {
160
+ const record = node;
161
+ if (typeof record.product === 'string')
162
+ names.add(record.product);
163
+ Object.values(record).forEach(walk);
164
+ }
165
+ };
166
+ walk(nav);
167
+ return [...names];
168
+ }
169
+ function sessionProductsOf(nav) {
170
+ return [undefined, ...allProductNamesOf(nav)];
171
+ }
172
+ function filterWith(decoratedNav, currentPath, currentVersion, currentProduct) {
173
+ return filterDivisions({
174
+ currentPath,
175
+ decoratedNav,
176
+ userGroups: new Set(),
177
+ currentVersion,
178
+ currentLanguage: undefined,
179
+ currentProduct,
180
+ shouldUseDivisionMatch: true,
181
+ cache: { dropdownCache: new Map() },
182
+ });
183
+ }
184
+ function divisionSummary(result) {
185
+ return {
186
+ tabs: result.tabs.map(({ name, href, isActive }) => ({ name, href, isActive })),
187
+ anchors: result.anchors.map(({ name, href, isActive }) => ({ name, href, isActive })),
188
+ versions: result.versions.map(({ name, isActive }) => ({ name, isActive })),
189
+ languages: result.languages.map(({ name, isActive }) => ({ name, isActive })),
190
+ products: result.products.map(({ name, isActive }) => ({ name, isActive })),
191
+ };
192
+ }
193
+ function mapHrefs(map) {
194
+ return new Map([...map.entries()].map(([key, value]) => [key, value.href]));
195
+ }
196
+ function collectHrefs(node, hrefs = []) {
197
+ if (Array.isArray(node)) {
198
+ node.forEach((entry) => collectHrefs(entry, hrefs));
199
+ }
200
+ else if (node && typeof node === 'object') {
201
+ const record = node;
202
+ if (typeof record.href === 'string')
203
+ hrefs.push(record.href);
204
+ Object.values(record).forEach((value) => collectHrefs(value, hrefs));
205
+ }
206
+ return hrefs;
207
+ }
208
+ function productsContaining(nav, currentPath) {
209
+ const names = new Set();
210
+ const walk = (node) => {
211
+ if (Array.isArray(node)) {
212
+ node.forEach(walk);
213
+ }
214
+ else if (node && typeof node === 'object') {
215
+ const record = node;
216
+ if (typeof record.product === 'string' &&
217
+ collectHrefs(record).some((href) => isEqualIgnoringLeadingSlash(href, currentPath))) {
218
+ names.add(record.product);
219
+ }
220
+ Object.values(record).forEach(walk);
221
+ }
222
+ };
223
+ walk(nav);
224
+ return names.size;
225
+ }
226
+ function assertInvariantForEveryPathAndProduct(nav) {
227
+ const paths = getAllPathsInDecoratedNav(nav);
228
+ expect(paths.length).toBeGreaterThan(0);
229
+ for (const path of paths) {
230
+ const currentPath = `/${path.replace(/^\//, '')}`;
231
+ const scoped = getScopedNavForPath({ decoratedNav: nav, currentPath });
232
+ const ambiguous = productsContaining(nav, currentPath) > 1;
233
+ for (const sessionProduct of sessionProductsOf(nav)) {
234
+ const label = `${currentPath} (product: ${sessionProduct})`;
235
+ const full = filterWith(nav, currentPath, scoped.currentVersion, sessionProduct);
236
+ const rescoped = filterWith(scoped.scopedNav, currentPath, scoped.currentVersion, sessionProduct);
237
+ expect(rescoped.groupsOrPages, label).toEqual(full.groupsOrPages);
238
+ expect(divisionSummary(rescoped), label).toEqual(divisionSummary(full));
239
+ expect(mapHrefs(rescoped.firstHrefInVersion), label).toEqual(mapHrefs(full.firstHrefInVersion));
240
+ expect(mapHrefs(rescoped.firstHrefInLanguage), label).toEqual(mapHrefs(full.firstHrefInLanguage));
241
+ expect(mapHrefs(scoped.firstHrefInVersion), label).toEqual(ambiguous ? new Map() : mapHrefs(full.firstHrefInVersion));
242
+ expect(mapHrefs(scoped.firstHrefInLanguage), label).toEqual(ambiguous ? new Map() : mapHrefs(full.firstHrefInLanguage));
243
+ }
244
+ }
245
+ }
246
+ describe('scoped nav on product navigations', () => {
247
+ it('filters identically for every path and session product in a product+version nav', () => {
248
+ assertInvariantForEveryPathAndProduct(productVersionNav);
249
+ });
250
+ it('filters identically when the same path lives in multiple products', () => {
251
+ assertInvariantForEveryPathAndProduct(sharedPathProductNav);
252
+ });
253
+ it('filters identically for products nested under languages', () => {
254
+ assertInvariantForEveryPathAndProduct(nestedProductNav);
255
+ });
256
+ it('handles ambiguity for products nested under languages', () => {
257
+ const scoped = getScopedNavForPath({
258
+ decoratedNav: nestedProductNav,
259
+ currentPath: '/en/shared/setup',
260
+ });
261
+ expect(scoped.firstHrefInVersion.size).toBe(0);
262
+ const serialized = JSON.stringify(scoped.scopedNav);
263
+ expect(serialized).toContain('/en/db/v1/migrate');
264
+ expect(serialized).toContain('/en/db/v2/deploy');
265
+ });
266
+ it('keeps version divisions whole on product-ambiguous paths instead of baking stubs', () => {
267
+ const scoped = getScopedNavForPath({
268
+ decoratedNav: sharedPathProductNav,
269
+ currentPath: '/shared/setup',
270
+ });
271
+ expect(scoped.firstHrefInVersion.size).toBe(0);
272
+ const serialized = JSON.stringify(scoped.scopedNav);
273
+ expect(serialized).toContain('/db/v1/migrate');
274
+ expect(serialized).toContain('/db/v2/deploy');
275
+ });
276
+ it('bakes equivalent-page targets into off-path version stubs inside a product', () => {
277
+ var _a;
278
+ const scoped = getScopedNavForPath({
279
+ decoratedNav: productVersionNav,
280
+ currentPath: '/db/v2/intro',
281
+ });
282
+ expect(scoped.currentVersion).toBe('v2');
283
+ expect((_a = scoped.firstHrefInVersion.get('v1')) === null || _a === void 0 ? void 0 : _a.href).toBe('/db/v1/intro');
284
+ const serialized = JSON.stringify(scoped.scopedNav);
285
+ expect(serialized).not.toContain('/db/v1/migrate');
286
+ expect(serialized).toContain('/cloud/en/intro');
287
+ });
288
+ });
@@ -0,0 +1,29 @@
1
+ import { DecoratedNavigationConfig, DecoratedPageOrGroupConfig, DecoratedGroupConfig } from '@mintlify/validation';
2
+ import { NavigationDivisions } from './filterDivisions.js';
3
+ export declare enum DivisionMatchLevel {
4
+ EXACT = 0,
5
+ PATH = 1,
6
+ GROUP = 2,
7
+ DIVISION = 3,
8
+ NONE = 4
9
+ }
10
+ export type DivisionMatch = {
11
+ href: string;
12
+ matchLevel: DivisionMatchLevel;
13
+ };
14
+ export declare function updateFirstHrefInDivision({ type, currentPath, entry, parentGroups, nearestDivisionValue, isActiveGroup, inActiveDivision, currentDivisionValue, allDivisionValues, firstHrefInDivision, navigationDivisions, isHiddenPage, activeGroupNames, }: {
15
+ type: 'version' | 'language';
16
+ currentPath: string;
17
+ entry: DecoratedNavigationConfig | DecoratedPageOrGroupConfig;
18
+ parentGroups: DecoratedGroupConfig[];
19
+ nearestDivisionValue: string | undefined;
20
+ isActiveGroup: boolean;
21
+ inActiveDivision: boolean;
22
+ currentDivisionValue: string | undefined;
23
+ allDivisionValues: string[] | undefined;
24
+ firstHrefInDivision: Map<string | undefined, DivisionMatch>;
25
+ navigationDivisions: NavigationDivisions;
26
+ isHiddenPage: boolean;
27
+ activeGroupNames?: Set<string>;
28
+ }): void;
29
+ export declare function pathsDifferByOneSegment(a: string, b: string, type: 'version' | 'language'): boolean;