jamdesk 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/unit/deploy-templates.test.js +6 -1
- package/dist/__tests__/unit/deploy-templates.test.js.map +1 -1
- package/dist/commands/deploy/templates.d.ts.map +1 -1
- package/dist/commands/deploy/templates.js +7 -2
- package/dist/commands/deploy/templates.js.map +1 -1
- package/dist/lib/deps.d.ts.map +1 -1
- package/dist/lib/deps.js +3 -1
- package/dist/lib/deps.js.map +1 -1
- package/package.json +2 -2
- package/vendored/app/[[...slug]]/page.tsx +5 -2
- package/vendored/components/mdx/ApiPage.tsx +18 -0
- package/vendored/components/mdx/OpenApiEndpoint.tsx +3 -3
- package/vendored/components/navigation/Breadcrumb.tsx +63 -3
- package/vendored/components/navigation/Sidebar.tsx +61 -38
- package/vendored/components/snippets/generated/CodeLink.tsx +25 -0
- package/vendored/components/snippets/generated/HeaderAPI.tsx +44 -0
- package/vendored/components/snippets/generated/PlansAvailable.tsx +53 -0
- package/vendored/components/snippets/generated/SnippetIntro.tsx +43 -0
- package/vendored/components/ui/CodePanel.tsx +7 -0
- package/vendored/lib/crypto-helpers.ts +25 -0
- package/vendored/lib/enhance-navigation.ts +175 -0
- package/vendored/lib/isr-build-executor.ts +16 -0
- package/vendored/lib/openapi-isr.ts +16 -24
- package/vendored/lib/r2-content.ts +5 -0
- package/vendored/lib/revalidation-trigger.ts +21 -0
|
@@ -52,10 +52,15 @@ describe('generateWorkerCode', () => {
|
|
|
52
52
|
const code = generateWorkerCode(testConfig);
|
|
53
53
|
expect(code).toContain('cacheEverything: true');
|
|
54
54
|
});
|
|
55
|
-
it('does not cache server errors', () => {
|
|
55
|
+
it('does not cache redirects or server errors', () => {
|
|
56
56
|
const code = generateWorkerCode(testConfig);
|
|
57
|
+
expect(code).toContain('"300-399": 0');
|
|
57
58
|
expect(code).toContain('"500-599": 0');
|
|
58
59
|
});
|
|
60
|
+
it('passes redirects through to browser', () => {
|
|
61
|
+
const code = generateWorkerCode(testConfig);
|
|
62
|
+
expect(code).toContain('redirect: "manual"');
|
|
63
|
+
});
|
|
59
64
|
});
|
|
60
65
|
describe('generateWranglerConfig', () => {
|
|
61
66
|
it('generates valid TOML with catch-all route', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-templates.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/deploy-templates.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"deploy-templates.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/deploy-templates.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/templates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy/templates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CA4EnE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAuBvE;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAqBpE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAsCD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAexD"}
|
|
@@ -56,18 +56,23 @@ export default {
|
|
|
56
56
|
// Custom header for domain verification (Vercel strips standard forwarding headers)
|
|
57
57
|
headers.set("X-Jamdesk-Forwarded-Host", url.hostname);
|
|
58
58
|
|
|
59
|
+
// Don't follow redirects — let the browser handle them so the URL updates.
|
|
60
|
+
// Without this, redirects happen internally and the browser URL doesn't change,
|
|
61
|
+
// which causes the sidebar to mis-highlight the active page.
|
|
59
62
|
const proxyRequest = new Request(proxyUrl, {
|
|
60
63
|
method: request.method,
|
|
61
|
-
headers
|
|
64
|
+
headers,
|
|
62
65
|
body: request.body,
|
|
66
|
+
redirect: "manual",
|
|
63
67
|
});
|
|
64
68
|
|
|
65
69
|
// Cache all content types at Cloudflare edge (CF doesn't cache HTML by default).
|
|
66
70
|
// Cache duration is controlled by upstream Cache-Control headers.
|
|
71
|
+
// Never cache redirects or errors — they must always hit origin.
|
|
67
72
|
return fetch(proxyRequest, {
|
|
68
73
|
cf: {
|
|
69
74
|
cacheEverything: true,
|
|
70
|
-
cacheTtlByStatus: { "500-599": 0 },
|
|
75
|
+
cacheTtlByStatus: { "300-399": 0, "500-599": 0 },
|
|
71
76
|
},
|
|
72
77
|
});
|
|
73
78
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/commands/deploy/templates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,UAAU,kBAAkB,CAAC,MAAwB;IACzD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IACpC,MAAM,WAAW,GAAG,GAAG,IAAI,cAAc,CAAC;IAE1C,OAAO;;;;aAII,UAAU,mCAAmC,WAAW;;;;;;wBAM7C,WAAW;;;;KAI9B,UAAU;;;;;;;;sBAQO,UAAU;6BACH,UAAU,6BAA6B,UAAU
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/commands/deploy/templates.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/deps.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/lib/deps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/lib/deps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyGH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAqF1E;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
package/dist/lib/deps.js
CHANGED
|
@@ -57,6 +57,8 @@ const REQUIRED_DEPS = {
|
|
|
57
57
|
'katex': '^0.16.28',
|
|
58
58
|
// Diagrams
|
|
59
59
|
'mermaid': '^11.12.2',
|
|
60
|
+
// YAML parsing (for OpenAPI specs)
|
|
61
|
+
'js-yaml': '^4.1.1',
|
|
60
62
|
// Schema validation
|
|
61
63
|
'ajv': '^8.17.1',
|
|
62
64
|
// MDX compilation (for validate command)
|
|
@@ -76,7 +78,7 @@ const REQUIRED_DEPS = {
|
|
|
76
78
|
// Script dependencies (for vendored scripts)
|
|
77
79
|
'fs-extra': '^11.3.3',
|
|
78
80
|
'json5': '^2.2.3',
|
|
79
|
-
'glob': '^13.0.
|
|
81
|
+
'glob': '^13.0.6',
|
|
80
82
|
// TypeScript (needed for Next.js to avoid auto-install breaking symlink)
|
|
81
83
|
'typescript': '^5.3.3',
|
|
82
84
|
'@types/node': '^25.2.0',
|
package/dist/lib/deps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/lib/deps.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/lib/deps.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"commander": "^14.0.3",
|
|
100
100
|
"fastest-levenshtein": "^1.0.16",
|
|
101
101
|
"fs-extra": "^11.2.0",
|
|
102
|
-
"glob": "^13.0.
|
|
102
|
+
"glob": "^13.0.6",
|
|
103
103
|
"gray-matter": "^4.0.3",
|
|
104
104
|
"json5": "^2.2.3",
|
|
105
105
|
"ora": "^9.3.0"
|
|
@@ -610,9 +610,12 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
610
610
|
/>
|
|
611
611
|
)}
|
|
612
612
|
|
|
613
|
-
{/* Additional MDX content
|
|
613
|
+
{/* Additional MDX content — strip <ResponseExample> on OpenAPI pages
|
|
614
|
+
(auto-generated ResponseExamplePanel already handles responses) */}
|
|
614
615
|
<MDXRemote
|
|
615
|
-
source={
|
|
616
|
+
source={openApiEndpointData
|
|
617
|
+
? content.replace(/<ResponseExample>[\s\S]*?<\/ResponseExample>/g, '')
|
|
618
|
+
: content}
|
|
616
619
|
components={AllComponentsWithInline}
|
|
617
620
|
options={{
|
|
618
621
|
// Keep expression props (e.g. cols={2}) compatible under next-mdx-remote v6.
|
|
@@ -361,6 +361,24 @@ export function ApiPageWrapper({ children }: ApiPageWrapperProps) {
|
|
|
361
361
|
return () => window.removeEventListener('resize', handleResize);
|
|
362
362
|
}, [isDesktop, mounted, applyPanelHeights]);
|
|
363
363
|
|
|
364
|
+
// Recalculate sidebar panel heights when a CodePanel switches tabs
|
|
365
|
+
useEffect(() => {
|
|
366
|
+
if (!sidebarRef.current || !isDesktop || !mounted) return;
|
|
367
|
+
|
|
368
|
+
const handleTabChange = (e: Event) => {
|
|
369
|
+
const panel = (e.target as Element).closest('[data-code-panel]');
|
|
370
|
+
if (panel) {
|
|
371
|
+
// Invalidate cached measurement — content changed
|
|
372
|
+
panelDataRef.current.delete(panel);
|
|
373
|
+
requestAnimationFrame(() => applyPanelHeights());
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const sidebar = sidebarRef.current;
|
|
378
|
+
sidebar.addEventListener('codepanel-tab-change', handleTabChange);
|
|
379
|
+
return () => sidebar.removeEventListener('codepanel-tab-change', handleTabChange);
|
|
380
|
+
}, [isDesktop, mounted, applyPanelHeights]);
|
|
381
|
+
|
|
364
382
|
return (
|
|
365
383
|
<div className="flex h-full">
|
|
366
384
|
{/* Main content area with independent scroll */}
|
|
@@ -877,10 +877,10 @@ function CodeExamplesSection({ examples }: { examples: CodeExamples }) {
|
|
|
877
877
|
* Renders full endpoint documentation from OpenAPI spec data.
|
|
878
878
|
* Composes existing ParamField, ResponseField, and CodeGroup components.
|
|
879
879
|
*/
|
|
880
|
-
export function OpenApiEndpoint({
|
|
881
|
-
endpoint,
|
|
880
|
+
export function OpenApiEndpoint({
|
|
881
|
+
endpoint,
|
|
882
882
|
codeExamples,
|
|
883
|
-
children
|
|
883
|
+
children
|
|
884
884
|
}: OpenApiEndpointProps) {
|
|
885
885
|
const {
|
|
886
886
|
method,
|
|
@@ -174,17 +174,77 @@ function findNavigationPath(config: DocsConfig, targetSlug: string): BreadcrumbI
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
|
-
*
|
|
177
|
+
* Find the page's navigation title from docs.json config.
|
|
178
|
+
* Falls back to formatting the last slug segment.
|
|
178
179
|
*/
|
|
179
180
|
function getPageTitle(slug: string[], config?: DocsConfig): string {
|
|
180
|
-
|
|
181
|
-
|
|
181
|
+
if (config) {
|
|
182
|
+
const targetSlug = slug.join('/');
|
|
183
|
+
const title = findPageNavTitle(config, targetSlug);
|
|
184
|
+
if (title) return title;
|
|
185
|
+
}
|
|
182
186
|
const lastSegment = slug[slug.length - 1];
|
|
183
187
|
return lastSegment
|
|
184
188
|
.replace(/-/g, ' ')
|
|
185
189
|
.replace(/\b\w/g, (l) => l.toUpperCase());
|
|
186
190
|
}
|
|
187
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Search navigation for the page's title (from enhanced nav or page object).
|
|
194
|
+
*/
|
|
195
|
+
function findPageNavTitle(config: DocsConfig, targetSlug: string): string | null {
|
|
196
|
+
const nav = config.navigation;
|
|
197
|
+
|
|
198
|
+
function searchPages(pages: (NavigationPage | GroupConfig)[]): string | null {
|
|
199
|
+
for (const item of pages) {
|
|
200
|
+
if (typeof item === 'object' && 'group' in item) {
|
|
201
|
+
const groupItem = item as GroupConfig;
|
|
202
|
+
if (groupItem.pages) {
|
|
203
|
+
const found = searchPages(groupItem.pages);
|
|
204
|
+
if (found) return found;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
const { path, title } = normalizeNavPage(item as NavigationPage);
|
|
208
|
+
if (path === targetSlug) return title;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function searchGroups(groups: GroupConfig[]): string | null {
|
|
215
|
+
for (const group of groups) {
|
|
216
|
+
if (group.pages) {
|
|
217
|
+
const found = searchPages(group.pages);
|
|
218
|
+
if (found) return found;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Search all navigation structures
|
|
225
|
+
if (nav.languages) {
|
|
226
|
+
for (const lang of nav.languages) {
|
|
227
|
+
if (lang.tabs) {
|
|
228
|
+
for (const tab of lang.tabs) {
|
|
229
|
+
if (tab.groups) { const r = searchGroups(tab.groups); if (r) return r; }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (nav.anchors) {
|
|
235
|
+
for (const anchor of nav.anchors) {
|
|
236
|
+
if (anchor.groups) { const r = searchGroups(anchor.groups); if (r) return r; }
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (nav.tabs) {
|
|
240
|
+
for (const tab of nav.tabs) {
|
|
241
|
+
if (tab.groups) { const r = searchGroups(tab.groups); if (r) return r; }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (nav.groups) { const r = searchGroups(nav.groups); if (r) return r; }
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
188
248
|
export function Breadcrumb({ slug, config }: BreadcrumbProps) {
|
|
189
249
|
const linkPrefix = useLinkPrefix();
|
|
190
250
|
const homeLink = config ? `${linkPrefix}/${getFirstPage(config)}` : `${linkPrefix}/introduction`;
|
|
@@ -53,10 +53,11 @@ interface NavPageProps {
|
|
|
53
53
|
pathname: string | null;
|
|
54
54
|
layout: LayoutVariant;
|
|
55
55
|
onPrefetch: (path: string) => void;
|
|
56
|
+
onNavigate: (href: string) => void;
|
|
56
57
|
linkPrefix?: string; // e.g., '/docs' when hostAtDocs is true
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
export const NavPage = React.memo(function NavPage({ page, pathname, layout, onPrefetch, linkPrefix = '' }: NavPageProps) {
|
|
60
|
+
export const NavPage = React.memo(function NavPage({ page, pathname, layout, onPrefetch, onNavigate, linkPrefix = '' }: NavPageProps) {
|
|
60
61
|
const href = `${linkPrefix}/${page.path}`;
|
|
61
62
|
const isActive = pathname === href;
|
|
62
63
|
const colors = page.method ? methodColors[page.method] : null;
|
|
@@ -67,6 +68,7 @@ export const NavPage = React.memo(function NavPage({ page, pathname, layout, onP
|
|
|
67
68
|
href={href}
|
|
68
69
|
prefetch={false}
|
|
69
70
|
onMouseEnter={() => onPrefetch(page.path)}
|
|
71
|
+
onClick={() => onNavigate(href)}
|
|
70
72
|
className={`flex items-center gap-2 ${layout === 'header-logo' ? 'pr-3' : 'px-3'} py-1.5 rounded-lg text-sm transition-colors ${
|
|
71
73
|
isActive
|
|
72
74
|
? 'text-[var(--color-primary)] nav-active'
|
|
@@ -92,6 +94,30 @@ export const NavPage = React.memo(function NavPage({ page, pathname, layout, onP
|
|
|
92
94
|
);
|
|
93
95
|
});
|
|
94
96
|
|
|
97
|
+
/** Find the first page path in a group, searching items, pages, and nested groups */
|
|
98
|
+
function findFirstPageInGroups(groups: ResolvedGroup[]): string | null {
|
|
99
|
+
for (const group of groups) {
|
|
100
|
+
// Prefer ordered items array (includes both pages and nested groups in order)
|
|
101
|
+
if (group.items) {
|
|
102
|
+
for (const item of group.items) {
|
|
103
|
+
if (item.type === 'page') return item.page.path;
|
|
104
|
+
if (item.type === 'group') {
|
|
105
|
+
const nested = findFirstPageInGroups([item.group]);
|
|
106
|
+
if (nested) return nested;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
// Legacy format: pages + nested arrays
|
|
111
|
+
if (group.pages.length > 0) return group.pages[0].path;
|
|
112
|
+
if (group.nested) {
|
|
113
|
+
const nested = findFirstPageInGroups(group.nested);
|
|
114
|
+
if (nested) return nested;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
95
121
|
/** Find all groups in the path from root to the page containing currentPath */
|
|
96
122
|
function findGroupsContainingPath(groups: ResolvedGroup[], currentPath: string, parentNames: string[] = []): Set<string> {
|
|
97
123
|
const result = new Set<string>();
|
|
@@ -121,8 +147,9 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
121
147
|
const sidebarRef = useRef<HTMLElement>(null);
|
|
122
148
|
const linkPrefix = useLinkPrefix();
|
|
123
149
|
|
|
124
|
-
// Optimistic
|
|
125
|
-
|
|
150
|
+
// Optimistic navigation: immediately highlight clicked link while page loads.
|
|
151
|
+
// Strips hash fragments so anchor links (e.g. /page#section) match pathname.
|
|
152
|
+
const handleNavigate = useCallback((url: string) => {
|
|
126
153
|
setPendingPathname(url.split('#')[0]);
|
|
127
154
|
}, []);
|
|
128
155
|
|
|
@@ -148,26 +175,43 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
148
175
|
// In sidebar-logo layout, logo and search are in sidebar
|
|
149
176
|
const showLogoInSidebar = layout === 'sidebar-logo';
|
|
150
177
|
const showSearchInSidebar = layout === 'sidebar-logo';
|
|
151
|
-
|
|
178
|
+
|
|
152
179
|
// Logo configuration
|
|
153
180
|
const logoConfig = normalizeLogo(config.logo, config.assetVersion);
|
|
154
181
|
const logoHref = logoConfig?.href || '/';
|
|
155
182
|
const logoSrc = logoConfig ? (isDark && logoConfig.dark ? logoConfig.dark : logoConfig.light) : null;
|
|
156
183
|
const showLogoImage = logoSrc && !logoError;
|
|
157
184
|
|
|
158
|
-
// Resolve navigation using the
|
|
185
|
+
// Resolve navigation structure (tab/anchor selection) using the raw pathname.
|
|
186
|
+
// Uses effectivePathname (not activePathname) because navigation resolution
|
|
187
|
+
// needs the actual URL for correct tab detection — activePathname is only for
|
|
188
|
+
// highlight matching within the resolved groups.
|
|
159
189
|
const resolvedNav = useMemo(() => {
|
|
160
|
-
return resolveNavigation(config, effectivePathname || '/
|
|
161
|
-
}, [config, effectivePathname]);
|
|
190
|
+
return resolveNavigation(config, effectivePathname || pathname || '/');
|
|
191
|
+
}, [config, effectivePathname, pathname]);
|
|
192
|
+
|
|
193
|
+
// Normalize bare root path to first page path for active state highlighting.
|
|
194
|
+
// When an upstream proxy (e.g., Cloudflare Worker) follows the server redirect
|
|
195
|
+
// from /docs → /docs/introduction internally, the browser URL stays at /docs.
|
|
196
|
+
// This causes usePathname() to return "/docs" which doesn't match any nav link.
|
|
197
|
+
// Map the bare root to the first page so the sidebar highlights correctly.
|
|
198
|
+
const activePathname = useMemo(() => {
|
|
199
|
+
if (!effectivePathname) return effectivePathname;
|
|
200
|
+
const rootPath = linkPrefix || '/';
|
|
201
|
+
if (effectivePathname !== rootPath) return effectivePathname;
|
|
202
|
+
|
|
203
|
+
const firstPage = findFirstPageInGroups(resolvedNav.groups);
|
|
204
|
+
return firstPage ? `${linkPrefix}/${firstPage}` : effectivePathname;
|
|
205
|
+
}, [effectivePathname, linkPrefix, resolvedNav.groups]);
|
|
162
206
|
|
|
163
207
|
// Track if this is the initial mount (for resetting expanded state on refresh)
|
|
164
208
|
const isInitialMount = useRef(true);
|
|
165
209
|
|
|
166
210
|
// Memoize the groups that should be expanded for the current page
|
|
167
211
|
const groupsForCurrentPage = useMemo(() => {
|
|
168
|
-
const currentPath =
|
|
212
|
+
const currentPath = activePathname?.replace(/^\/(?:docs\/)?/, '') || '';
|
|
169
213
|
return findGroupsContainingPath(resolvedNav.groups, currentPath);
|
|
170
|
-
}, [resolvedNav.groups,
|
|
214
|
+
}, [resolvedNav.groups, activePathname]);
|
|
171
215
|
|
|
172
216
|
// Expand groups containing current page
|
|
173
217
|
// On initial mount: reset to only current page's groups
|
|
@@ -239,7 +283,7 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
239
283
|
});
|
|
240
284
|
|
|
241
285
|
return () => cancelAnimationFrame(rafId);
|
|
242
|
-
}, [
|
|
286
|
+
}, [activePathname]);
|
|
243
287
|
|
|
244
288
|
// Prevent body scroll when sidebar is open on mobile
|
|
245
289
|
useEffect(() => {
|
|
@@ -253,16 +297,6 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
253
297
|
};
|
|
254
298
|
}, [isOpen]);
|
|
255
299
|
|
|
256
|
-
// Find the first page in a group (including nested groups)
|
|
257
|
-
function findFirstPageInGroup(g: ResolvedGroup): string | null {
|
|
258
|
-
if (g.pages.length > 0) return g.pages[0].path;
|
|
259
|
-
for (const nested of g.nested || []) {
|
|
260
|
-
const nestedFirst = findFirstPageInGroup(nested);
|
|
261
|
-
if (nestedFirst) return nestedFirst;
|
|
262
|
-
}
|
|
263
|
-
return null;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
300
|
// Toggle group expansion; if expanding, navigate to first page
|
|
267
301
|
const handleGroupClick = useCallback((group: ResolvedGroup) => {
|
|
268
302
|
setExpandedGroups(prev => {
|
|
@@ -270,7 +304,7 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
270
304
|
if (prev.has(group.name)) {
|
|
271
305
|
newSet.delete(group.name);
|
|
272
306
|
} else {
|
|
273
|
-
const firstPagePath =
|
|
307
|
+
const firstPagePath = findFirstPageInGroups([group]);
|
|
274
308
|
if (firstPagePath) router.push(`${linkPrefix}/${firstPagePath}`);
|
|
275
309
|
newSet.add(group.name);
|
|
276
310
|
}
|
|
@@ -344,9 +378,10 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
344
378
|
<NavPage
|
|
345
379
|
key={p.page.path}
|
|
346
380
|
page={p.page}
|
|
347
|
-
pathname={
|
|
381
|
+
pathname={activePathname}
|
|
348
382
|
layout={layout}
|
|
349
383
|
onPrefetch={handlePrefetch}
|
|
384
|
+
onNavigate={handleNavigate}
|
|
350
385
|
linkPrefix={linkPrefix}
|
|
351
386
|
/>
|
|
352
387
|
))}
|
|
@@ -382,9 +417,10 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
382
417
|
<NavPage
|
|
383
418
|
key={page.path}
|
|
384
419
|
page={page}
|
|
385
|
-
pathname={
|
|
420
|
+
pathname={activePathname}
|
|
386
421
|
layout={layout}
|
|
387
422
|
onPrefetch={handlePrefetch}
|
|
423
|
+
onNavigate={handleNavigate}
|
|
388
424
|
linkPrefix={linkPrefix}
|
|
389
425
|
/>
|
|
390
426
|
))}
|
|
@@ -429,20 +465,7 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
429
465
|
);
|
|
430
466
|
}
|
|
431
467
|
|
|
432
|
-
|
|
433
|
-
const findFirstPage = (groups: ResolvedGroup[]): string | null => {
|
|
434
|
-
for (const group of groups) {
|
|
435
|
-
if (group.pages.length > 0) {
|
|
436
|
-
return group.pages[0].path;
|
|
437
|
-
}
|
|
438
|
-
if (group.nested) {
|
|
439
|
-
const nestedFirst = findFirstPage(group.nested);
|
|
440
|
-
if (nestedFirst) return nestedFirst;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
return null;
|
|
444
|
-
};
|
|
445
|
-
const firstPagePath = findFirstPage(anchor.groups);
|
|
468
|
+
const firstPagePath = findFirstPageInGroups(anchor.groups);
|
|
446
469
|
|
|
447
470
|
return (
|
|
448
471
|
<button
|
|
@@ -719,7 +742,7 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
719
742
|
</aside>
|
|
720
743
|
|
|
721
744
|
{/* Search Modal - shown when search is opened from sidebar */}
|
|
722
|
-
<SearchModal isOpen={isSearchOpen} onClose={() => setIsSearchOpen(false)} onNavigate={
|
|
745
|
+
<SearchModal isOpen={isSearchOpen} onClose={() => setIsSearchOpen(false)} onNavigate={handleNavigate} />
|
|
723
746
|
</>
|
|
724
747
|
);
|
|
725
748
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Auto-generated file - do not edit manually
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
// Import built-in MDX components that snippets can use
|
|
7
|
+
import { Note, Info, Warning, Tip, Check, Danger, Callout } from '@/components/mdx/Callouts';
|
|
8
|
+
import { Card } from '@/components/mdx/Card';
|
|
9
|
+
import { CardGroup } from '@/components/mdx/CardGroup';
|
|
10
|
+
import { ParamField } from '@/components/mdx/ParamField';
|
|
11
|
+
import { ResponseField } from '@/components/mdx/ResponseField';
|
|
12
|
+
import { Accordion, AccordionGroup } from '@/components/mdx/Accordion';
|
|
13
|
+
import { CodeGroup } from '@/components/mdx/CodeGroup';
|
|
14
|
+
import { Steps, Step } from '@/components/mdx/Steps';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const CodeLink = ({ title, href }: any) => {
|
|
18
|
+
return (
|
|
19
|
+
<Card title={`${title}`} href={`${href}`} horizontal icon="code">
|
|
20
|
+
{" "}
|
|
21
|
+
</Card>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default CodeLink;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Auto-generated file - do not edit manually
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
// Import built-in MDX components that snippets can use
|
|
7
|
+
import { Note, Info, Warning, Tip, Check, Danger, Callout } from '@/components/mdx/Callouts';
|
|
8
|
+
import { Card } from '@/components/mdx/Card';
|
|
9
|
+
import { CardGroup } from '@/components/mdx/CardGroup';
|
|
10
|
+
import { ParamField } from '@/components/mdx/ParamField';
|
|
11
|
+
import { ResponseField } from '@/components/mdx/ResponseField';
|
|
12
|
+
import { Accordion, AccordionGroup } from '@/components/mdx/Accordion';
|
|
13
|
+
import { CodeGroup } from '@/components/mdx/CodeGroup';
|
|
14
|
+
import { Steps, Step } from '@/components/mdx/Steps';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const HeaderAPI = ({ noProfileKey, profileKeyRequired }: any) => (
|
|
18
|
+
<>
|
|
19
|
+
<ParamField header="Authorization" type="string" required>
|
|
20
|
+
<a href="/apis/overview#authorization">API Key</a> of the Primary Profile.
|
|
21
|
+
<br />
|
|
22
|
+
<br />
|
|
23
|
+
Format: <code>Authorization: Bearer API_KEY</code>
|
|
24
|
+
</ParamField>
|
|
25
|
+
{!noProfileKey &&
|
|
26
|
+
(profileKeyRequired ? (
|
|
27
|
+
<ParamField header="Profile-Key" type="string" required>
|
|
28
|
+
<a href="/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
|
|
29
|
+
<br />
|
|
30
|
+
<br />
|
|
31
|
+
Format: <code>Profile-Key: PROFILE_KEY</code>
|
|
32
|
+
</ParamField>
|
|
33
|
+
) : (
|
|
34
|
+
<ParamField header="Profile-Key" type="string">
|
|
35
|
+
<a href="/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
|
|
36
|
+
<br />
|
|
37
|
+
<br />
|
|
38
|
+
Format: <code>Profile-Key: PROFILE_KEY</code>
|
|
39
|
+
</ParamField>
|
|
40
|
+
))}
|
|
41
|
+
</>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export default HeaderAPI;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Auto-generated file - do not edit manually
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
// Import built-in MDX components that snippets can use
|
|
7
|
+
import { Note, Info, Warning, Tip, Check, Danger, Callout } from '@/components/mdx/Callouts';
|
|
8
|
+
import { Card } from '@/components/mdx/Card';
|
|
9
|
+
import { CardGroup } from '@/components/mdx/CardGroup';
|
|
10
|
+
import { ParamField } from '@/components/mdx/ParamField';
|
|
11
|
+
import { ResponseField } from '@/components/mdx/ResponseField';
|
|
12
|
+
import { Accordion, AccordionGroup } from '@/components/mdx/Accordion';
|
|
13
|
+
import { CodeGroup } from '@/components/mdx/CodeGroup';
|
|
14
|
+
import { Steps, Step } from '@/components/mdx/Steps';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const PlansAvailable = ({ plans, maxPackRequired }: any) => {
|
|
18
|
+
let displayPlans = plans;
|
|
19
|
+
|
|
20
|
+
if (plans.length === 1) {
|
|
21
|
+
const lowerCasePlan = plans[0].toLowerCase();
|
|
22
|
+
if (lowerCasePlan === "basic") {
|
|
23
|
+
displayPlans = ["Basic", "Premium", "Business", "Enterprise"];
|
|
24
|
+
} else if (lowerCasePlan === "business") {
|
|
25
|
+
displayPlans = ["Business", "Enterprise"];
|
|
26
|
+
} else if (lowerCasePlan === "premium") {
|
|
27
|
+
displayPlans = ["Premium", "Business", "Enterprise"];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
|
|
33
|
+
<Note>
|
|
34
|
+
Available on {displayPlans.length === 1 ? "the " : ""}
|
|
35
|
+
{displayPlans.join(", ").replace(/\b\w/g, (l) => l.toUpperCase())}{" "}
|
|
36
|
+
{displayPlans.length > 1 ? "plans" : "plan"}.
|
|
37
|
+
|
|
38
|
+
{maxPackRequired && (
|
|
39
|
+
|
|
40
|
+
<a href="https://www.acme.com/docs/additional/maxpack"
|
|
41
|
+
className="flex items-center mt-2 cursor-pointer"
|
|
42
|
+
>
|
|
43
|
+
<span className="px-1.5 py-0.5 rounded text-sm" style={{backgroundColor: '#C264B6', color: 'white', fontSize: '12px'}}>
|
|
44
|
+
Max Pack required
|
|
45
|
+
</span>
|
|
46
|
+
</a>
|
|
47
|
+
)}
|
|
48
|
+
</Note>
|
|
49
|
+
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default PlansAvailable;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Auto-generated file - do not edit manually
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
// Import built-in MDX components that snippets can use
|
|
7
|
+
import { Note, Info, Warning, Tip, Check, Danger, Callout } from '@/components/mdx/Callouts';
|
|
8
|
+
import { Card } from '@/components/mdx/Card';
|
|
9
|
+
import { CardGroup } from '@/components/mdx/CardGroup';
|
|
10
|
+
import { ParamField } from '@/components/mdx/ParamField';
|
|
11
|
+
import { ResponseField } from '@/components/mdx/ResponseField';
|
|
12
|
+
import { Accordion, AccordionGroup } from '@/components/mdx/Accordion';
|
|
13
|
+
import { CodeGroup } from '@/components/mdx/CodeGroup';
|
|
14
|
+
import { Steps, Step } from '@/components/mdx/Steps';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// Helper component for rendering plain MDX snippets
|
|
18
|
+
// Content is from project snippets (controlled source), not user input
|
|
19
|
+
const PlainMdxSnippet = ({ content }: { content: string }) => {
|
|
20
|
+
const formattedContent = content
|
|
21
|
+
.split('\n\n')
|
|
22
|
+
.map((paragraph) => {
|
|
23
|
+
let html = paragraph.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
24
|
+
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
|
25
|
+
html = html.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
|
|
26
|
+
return html;
|
|
27
|
+
})
|
|
28
|
+
.filter(p => p.trim());
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="snippet-content">
|
|
32
|
+
{formattedContent.map((p, i) => (
|
|
33
|
+
<p key={i} dangerouslySetInnerHTML={{ __html: p }} />
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const SnippetIntro = () => {
|
|
40
|
+
return <PlainMdxSnippet content={"One of the core principles of software development is DRY (Don't Repeat\nYourself). This is a principle that apply to documentation as\nwell. If you find yourself repeating the same content in multiple places, you\nshould consider creating a custom snippet to keep your content in sync."} />;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default SnippetIntro;
|
|
@@ -121,6 +121,7 @@ export function CodePanel({
|
|
|
121
121
|
const [scrollRatio, setScrollRatio] = useState(0); // 0 to 1, for custom scrollbar position
|
|
122
122
|
const [thumbWidth, setThumbWidth] = useState(30); // Percentage width of thumb
|
|
123
123
|
const [isDragging, setIsDragging] = useState(false);
|
|
124
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
124
125
|
const tabsRef = useRef<HTMLDivElement>(null);
|
|
125
126
|
const trackRef = useRef<HTMLDivElement>(null);
|
|
126
127
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
@@ -149,6 +150,11 @@ export function CodePanel({
|
|
|
149
150
|
}
|
|
150
151
|
};
|
|
151
152
|
|
|
153
|
+
// Notify parent (ApiPage) after tab content re-renders so sidebar heights recalculate
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
panelRef.current?.dispatchEvent(new CustomEvent('codepanel-tab-change', { bubbles: true }));
|
|
156
|
+
}, [activeTab]);
|
|
157
|
+
|
|
152
158
|
// Check for overflow and scroll position
|
|
153
159
|
useEffect(() => {
|
|
154
160
|
const checkOverflow = () => {
|
|
@@ -246,6 +252,7 @@ export function CodePanel({
|
|
|
246
252
|
|
|
247
253
|
return (
|
|
248
254
|
<div
|
|
255
|
+
ref={panelRef}
|
|
249
256
|
className={`rounded-xl overflow-hidden not-prose w-full flex flex-col ${className}`}
|
|
250
257
|
style={{ border: `0.5px solid ${codePanelColors.border}`, boxShadow: 'var(--shadow-lg)' }}
|
|
251
258
|
data-code-panel={panelType || 'inline'}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// DO NOT EDIT — this file is auto-synced from shared/. Edit the source in shared/ and run ./scripts/sync-shared.sh
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared Cryptographic Helpers
|
|
5
|
+
*
|
|
6
|
+
* Constant-time comparison using HMAC to avoid length-leak timing attacks.
|
|
7
|
+
*
|
|
8
|
+
* SYNC TARGET: This file is the source of truth.
|
|
9
|
+
* Synced to: builder/build-service/lib, proxy/lib
|
|
10
|
+
*/
|
|
11
|
+
import { timingSafeEqual, createHmac } from 'crypto';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Constant-time string comparison using HMAC.
|
|
15
|
+
*
|
|
16
|
+
* Unlike naive timingSafeEqual (which requires equal-length buffers and
|
|
17
|
+
* leaks length via early return), this HMACs both values first so
|
|
18
|
+
* comparisons are always on fixed-length 32-byte digests.
|
|
19
|
+
*/
|
|
20
|
+
export function secretsEqual(a: string, b: string): boolean {
|
|
21
|
+
const key = 'jamdesk-secret-comparison';
|
|
22
|
+
const hmacA = createHmac('sha256', key).update(a).digest();
|
|
23
|
+
const hmacB = createHmac('sha256', key).update(b).digest();
|
|
24
|
+
return timingSafeEqual(hmacA, hmacB);
|
|
25
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Enhancement
|
|
3
|
+
*
|
|
4
|
+
* Enhances docs.json navigation entries with metadata from MDX frontmatter:
|
|
5
|
+
* - Sidebar titles (sidebarTitle > title)
|
|
6
|
+
* - HTTP method badges (from openapi: or api: frontmatter)
|
|
7
|
+
* - Icons and tags
|
|
8
|
+
*
|
|
9
|
+
* Port of scripts/enhance-navigation.cjs for use in the production build pipeline.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
DocsConfig,
|
|
14
|
+
NavigationPage,
|
|
15
|
+
NavigationPageObject,
|
|
16
|
+
GroupConfig,
|
|
17
|
+
} from './docs-types.js';
|
|
18
|
+
|
|
19
|
+
type HttpMethod = NonNullable<NavigationPageObject['method']>;
|
|
20
|
+
|
|
21
|
+
export interface PageInfo {
|
|
22
|
+
path: string;
|
|
23
|
+
frontmatter: Record<string, unknown>;
|
|
24
|
+
content: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const VALID_METHODS: readonly string[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parse HTTP method from `api:` frontmatter field.
|
|
31
|
+
* Example: "POST /analytics/post" -> "POST"
|
|
32
|
+
*/
|
|
33
|
+
export function parseApiMethod(apiField: unknown): HttpMethod | null {
|
|
34
|
+
if (!apiField || typeof apiField !== 'string') return null;
|
|
35
|
+
const trimmed = apiField.trim().toUpperCase();
|
|
36
|
+
for (const method of VALID_METHODS) {
|
|
37
|
+
if (trimmed.startsWith(method)) return method as HttpMethod;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Parse HTTP method from `openapi:` frontmatter field.
|
|
44
|
+
* Example: "/openapi/spec.yml GET /api/v1/users" -> "GET"
|
|
45
|
+
*/
|
|
46
|
+
export function parseOpenApiMethod(openapiField: unknown): HttpMethod | null {
|
|
47
|
+
if (!openapiField || typeof openapiField !== 'string') return null;
|
|
48
|
+
const parts = openapiField.trim().split(/\s+/);
|
|
49
|
+
for (const part of parts) {
|
|
50
|
+
const upper = part.toUpperCase();
|
|
51
|
+
if (VALID_METHODS.includes(upper)) return upper as HttpMethod;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildFrontmatterMap(
|
|
57
|
+
pageInfos: PageInfo[],
|
|
58
|
+
): Map<string, Record<string, unknown>> {
|
|
59
|
+
const map = new Map<string, Record<string, unknown>>();
|
|
60
|
+
for (const info of pageInfos) {
|
|
61
|
+
const pagePath = info.path.replace(/\.mdx?$/, '');
|
|
62
|
+
map.set(pagePath, info.frontmatter);
|
|
63
|
+
}
|
|
64
|
+
return map;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Enhance a single page entry with frontmatter data.
|
|
69
|
+
* Preserves existing explicit values — only fills in missing ones.
|
|
70
|
+
*/
|
|
71
|
+
function enhancePage(
|
|
72
|
+
page: NavigationPage,
|
|
73
|
+
map: Map<string, Record<string, unknown>>,
|
|
74
|
+
): NavigationPage {
|
|
75
|
+
const pagePath = typeof page === 'string' ? page : page.page;
|
|
76
|
+
const existing: Partial<NavigationPageObject> =
|
|
77
|
+
typeof page === 'object' ? page : {};
|
|
78
|
+
|
|
79
|
+
const fm = map.get(pagePath);
|
|
80
|
+
if (!fm) return page;
|
|
81
|
+
|
|
82
|
+
// sidebarTitle takes priority over title for sidebar display
|
|
83
|
+
const title =
|
|
84
|
+
existing.title ||
|
|
85
|
+
(fm.sidebarTitle as string | undefined) ||
|
|
86
|
+
(fm.title as string | undefined);
|
|
87
|
+
const method =
|
|
88
|
+
existing.method || parseApiMethod(fm.api) || parseOpenApiMethod(fm.openapi);
|
|
89
|
+
const icon = existing.icon || (fm.icon as string | undefined);
|
|
90
|
+
const tag = existing.tag || (fm.tag as string | undefined);
|
|
91
|
+
|
|
92
|
+
if (title || method || icon || tag) {
|
|
93
|
+
return {
|
|
94
|
+
page: pagePath,
|
|
95
|
+
...(title && { title }),
|
|
96
|
+
...(method && { method }),
|
|
97
|
+
...(icon && { icon }),
|
|
98
|
+
...(tag && { tag }),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return page;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Recursively enhance all pages/groups in a navigation array.
|
|
107
|
+
*/
|
|
108
|
+
function enhancePages(
|
|
109
|
+
pages: (NavigationPage | GroupConfig)[],
|
|
110
|
+
map: Map<string, Record<string, unknown>>,
|
|
111
|
+
): (NavigationPage | GroupConfig)[] {
|
|
112
|
+
return pages.map((item) => {
|
|
113
|
+
if (typeof item === 'object' && 'group' in item) {
|
|
114
|
+
return enhanceNavNode(
|
|
115
|
+
item as unknown as Record<string, unknown>,
|
|
116
|
+
map,
|
|
117
|
+
) as unknown as GroupConfig;
|
|
118
|
+
}
|
|
119
|
+
return enhancePage(item as NavigationPage, map);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Navigation keys whose children are sub-nodes (recurse with enhanceNavNode). */
|
|
124
|
+
const RECURSE_KEYS = [
|
|
125
|
+
'groups', 'tabs', 'anchors', 'dropdowns',
|
|
126
|
+
'products', 'versions', 'languages', 'menu',
|
|
127
|
+
] as const;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generic recursive walker — enhances any navigation node that has
|
|
131
|
+
* pages, groups, tabs, anchors, dropdowns, products, versions,
|
|
132
|
+
* languages, or menu keys.
|
|
133
|
+
*/
|
|
134
|
+
function enhanceNavNode(
|
|
135
|
+
node: Record<string, unknown>,
|
|
136
|
+
map: Map<string, Record<string, unknown>>,
|
|
137
|
+
): Record<string, unknown> {
|
|
138
|
+
const result = { ...node };
|
|
139
|
+
|
|
140
|
+
// Pages contain leaf page entries (strings or objects) mixed with nested groups
|
|
141
|
+
if (Array.isArray(node.pages)) {
|
|
142
|
+
result.pages = enhancePages(node.pages, map);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// All other array keys contain sub-nodes that need recursive enhancement
|
|
146
|
+
for (const key of RECURSE_KEYS) {
|
|
147
|
+
if (Array.isArray(node[key])) {
|
|
148
|
+
result[key] = (node[key] as Record<string, unknown>[]).map((child) =>
|
|
149
|
+
enhanceNavNode(child, map),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Enhance docs.json navigation with frontmatter metadata.
|
|
159
|
+
* Returns a new config — does NOT mutate the input.
|
|
160
|
+
*/
|
|
161
|
+
export function enhanceConfigNavigation(
|
|
162
|
+
config: DocsConfig,
|
|
163
|
+
pageInfos: PageInfo[],
|
|
164
|
+
): DocsConfig {
|
|
165
|
+
const nav = config.navigation;
|
|
166
|
+
if (!nav || Object.keys(nav).length === 0) return config;
|
|
167
|
+
|
|
168
|
+
const map = buildFrontmatterMap(pageInfos);
|
|
169
|
+
const enhanced = enhanceNavNode(
|
|
170
|
+
nav as Record<string, unknown>,
|
|
171
|
+
map,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return { ...config, navigation: enhanced as DocsConfig['navigation'] };
|
|
175
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* This module provides testable helper functions used by build.ts.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import fs from 'fs';
|
|
10
11
|
import glob from 'fast-glob';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -82,6 +83,19 @@ export async function collectSnippetFiles(projectDir: string): Promise<string[]>
|
|
|
82
83
|
return files;
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
/** Collect OpenAPI spec files from project directory. Returns paths relative to openapi/. */
|
|
87
|
+
export async function collectOpenApiFiles(projectDir: string): Promise<string[]> {
|
|
88
|
+
const openapiDir = `${projectDir}/openapi`;
|
|
89
|
+
if (!fs.existsSync(openapiDir)) return [];
|
|
90
|
+
|
|
91
|
+
const files = await glob('**/*.{yaml,yml,json}', {
|
|
92
|
+
cwd: openapiDir,
|
|
93
|
+
ignore: ['node_modules/**'],
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return files;
|
|
97
|
+
}
|
|
98
|
+
|
|
85
99
|
/**
|
|
86
100
|
* Options for getting project base URL.
|
|
87
101
|
*/
|
|
@@ -137,6 +151,8 @@ export interface IsrBuildResult {
|
|
|
137
151
|
assetCount: number;
|
|
138
152
|
/** Number of snippets uploaded */
|
|
139
153
|
snippetCount: number;
|
|
154
|
+
/** Number of OpenAPI spec files uploaded */
|
|
155
|
+
openapiCount: number;
|
|
140
156
|
/** Paths that were revalidated */
|
|
141
157
|
revalidatedPaths: string[];
|
|
142
158
|
/** Build duration in milliseconds */
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* with caching for ISR mode rendering.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import yaml from 'js-yaml';
|
|
9
|
+
import SwaggerParser from '@apidevtools/swagger-parser';
|
|
10
|
+
import { fetchOpenApiFile } from './r2-content';
|
|
9
11
|
|
|
10
12
|
interface OpenApiSpec {
|
|
11
13
|
openapi?: string;
|
|
@@ -19,12 +21,7 @@ interface OpenApiSpec {
|
|
|
19
21
|
const specCache = new Map<string, { spec: OpenApiSpec; timestamp: number }>();
|
|
20
22
|
const CACHE_TTL = 600_000; // 10 minutes
|
|
21
23
|
|
|
22
|
-
/**
|
|
23
|
-
* Fetch OpenAPI spec from external URL.
|
|
24
|
-
*
|
|
25
|
-
* @param url - Full URL to OpenAPI spec
|
|
26
|
-
* @returns Parsed OpenAPI specification
|
|
27
|
-
*/
|
|
24
|
+
/** Fetch OpenAPI spec from external URL. */
|
|
28
25
|
export async function fetchOpenApiSpec(url: string): Promise<OpenApiSpec> {
|
|
29
26
|
// Check cache
|
|
30
27
|
const cached = specCache.get(url);
|
|
@@ -51,9 +48,8 @@ export async function fetchOpenApiSpec(url: string): Promise<OpenApiSpec> {
|
|
|
51
48
|
/**
|
|
52
49
|
* Fetch OpenAPI spec from R2 (project-local spec file).
|
|
53
50
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* @returns Parsed OpenAPI specification
|
|
51
|
+
* Handles both YAML and JSON formats, and resolves all internal $ref
|
|
52
|
+
* references (matching the static mode behavior of SwaggerParser.validate).
|
|
57
53
|
*/
|
|
58
54
|
export async function fetchOpenApiSpecFromR2(
|
|
59
55
|
projectSlug: string,
|
|
@@ -67,8 +63,14 @@ export async function fetchOpenApiSpecFromR2(
|
|
|
67
63
|
return cached.spec;
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
const content = await
|
|
71
|
-
|
|
66
|
+
const content = await fetchOpenApiFile(projectSlug, specPath);
|
|
67
|
+
|
|
68
|
+
// Parse YAML or JSON into a plain object
|
|
69
|
+
const isYaml = /\.ya?ml$/i.test(specPath);
|
|
70
|
+
const raw = isYaml ? yaml.load(content) : JSON.parse(content);
|
|
71
|
+
|
|
72
|
+
// Dereference all $ref pointers (matching static mode's SwaggerParser.validate behavior)
|
|
73
|
+
const spec = await SwaggerParser.dereference(raw as any) as unknown as OpenApiSpec;
|
|
72
74
|
|
|
73
75
|
// Cache it
|
|
74
76
|
specCache.set(cacheKey, { spec, timestamp: Date.now() });
|
|
@@ -79,10 +81,6 @@ export async function fetchOpenApiSpecFromR2(
|
|
|
79
81
|
/**
|
|
80
82
|
* Resolve OpenAPI spec from config reference.
|
|
81
83
|
* Handles both URLs and local file references.
|
|
82
|
-
*
|
|
83
|
-
* @param projectSlug - Project identifier
|
|
84
|
-
* @param specRef - URL or local path to spec
|
|
85
|
-
* @returns Parsed OpenAPI specification
|
|
86
84
|
*/
|
|
87
85
|
export async function resolveOpenApiSpec(
|
|
88
86
|
projectSlug: string,
|
|
@@ -96,11 +94,7 @@ export async function resolveOpenApiSpec(
|
|
|
96
94
|
return fetchOpenApiSpecFromR2(projectSlug, specRef);
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
/**
|
|
100
|
-
* Clear OpenAPI cache.
|
|
101
|
-
*
|
|
102
|
-
* @param projectSlug - Optional project to clear. If not provided, clears all.
|
|
103
|
-
*/
|
|
97
|
+
/** Clear OpenAPI cache. If projectSlug is provided, clears only that project. */
|
|
104
98
|
export function clearOpenApiCache(projectSlug?: string): void {
|
|
105
99
|
if (projectSlug) {
|
|
106
100
|
for (const key of specCache.keys()) {
|
|
@@ -113,9 +107,7 @@ export function clearOpenApiCache(projectSlug?: string): void {
|
|
|
113
107
|
}
|
|
114
108
|
}
|
|
115
109
|
|
|
116
|
-
/**
|
|
117
|
-
* Get the current size of the OpenAPI spec cache.
|
|
118
|
-
*/
|
|
110
|
+
/** Get the current size of the OpenAPI spec cache. */
|
|
119
111
|
export function getOpenApiCacheSize(): number {
|
|
120
112
|
return specCache.size;
|
|
121
113
|
}
|
|
@@ -36,6 +36,10 @@ export async function fetchSnippet(_projectSlug: string, _snippetPath: string):
|
|
|
36
36
|
throw new Error('R2 snippet fetching not available in dev mode');
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export async function fetchOpenApiFile(_projectSlug: string, _specPath: string): Promise<string> {
|
|
40
|
+
throw new Error('R2 OpenAPI fetching not available in dev mode');
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
export async function listAllPaths(_projectSlug: string): Promise<string[]> { return []; }
|
|
40
44
|
|
|
41
45
|
export interface Manifest {
|
|
@@ -44,6 +48,7 @@ export interface Manifest {
|
|
|
44
48
|
projectSlug?: string;
|
|
45
49
|
files: Record<string, { hash: string; size?: number }>;
|
|
46
50
|
snippets?: Record<string, { hash: string }>;
|
|
51
|
+
openapi?: Record<string, { hash: string }>;
|
|
47
52
|
configHash?: string;
|
|
48
53
|
}
|
|
49
54
|
|
|
@@ -99,6 +99,7 @@ async function revalidateProxy(secret: string, projectSlug: string): Promise<voi
|
|
|
99
99
|
|
|
100
100
|
export interface Manifest {
|
|
101
101
|
files: Record<string, { hash: string }>;
|
|
102
|
+
openapi?: Record<string, { hash: string }>;
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/** Compare manifests and return paths that changed (without .mdx extension). */
|
|
@@ -128,9 +129,29 @@ export function getChangedPaths(
|
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
// When only OpenAPI specs changed (no MDX changes), revalidate all pages
|
|
133
|
+
// because we can't know which pages reference the changed spec
|
|
134
|
+
if (changed.length === 0 && hasOpenapiChanges(oldManifest, newManifest)) {
|
|
135
|
+
return Object.keys(newManifest.files).map(p => p.replace('.mdx', ''));
|
|
136
|
+
}
|
|
137
|
+
|
|
131
138
|
return changed;
|
|
132
139
|
}
|
|
133
140
|
|
|
141
|
+
/** Check if any OpenAPI spec hashes changed between manifests. */
|
|
142
|
+
function hasOpenapiChanges(oldManifest: Manifest, newManifest: Manifest): boolean {
|
|
143
|
+
const oldSpecs = oldManifest.openapi || {};
|
|
144
|
+
const newSpecs = newManifest.openapi || {};
|
|
145
|
+
|
|
146
|
+
for (const [path, info] of Object.entries(newSpecs)) {
|
|
147
|
+
if (!oldSpecs[path] || oldSpecs[path].hash !== info.hash) return true;
|
|
148
|
+
}
|
|
149
|
+
for (const path of Object.keys(oldSpecs)) {
|
|
150
|
+
if (!(path in newSpecs)) return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
134
155
|
/** Trigger revalidation for changed files between manifests. */
|
|
135
156
|
export async function triggerRevalidationForChanges(
|
|
136
157
|
projectSlug: string,
|