jamdesk 1.1.51 → 1.1.52

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jamdesk",
3
- "version": "1.1.51",
3
+ "version": "1.1.52",
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",
@@ -1,43 +1,11 @@
1
- // Owns the unlock-mode HTML shell that was previously embedded as a
2
- // headers()-gated short-circuit inside RootLayout. Reading headers() here
3
- // only forces THIS sub-tree dynamic the docs catch-all is unaffected
4
- // (it reads its own headers and is already force-dynamic).
5
- import { headers } from 'next/headers';
6
- import { isRTLLanguage, resolveLanguageWithFallback } from '@/lib/language-utils';
7
- import { getLanguageFromRequest } from '@/lib/page-isr-helpers';
8
-
9
- export default async function UnlockLayout({
1
+ // Passthrough root layout owns the unlock html/head/body branch via
2
+ // the `x-jd-unlock-mode` header. This file exists only so the (unlock)
3
+ // route group remains a valid App Router segment; rendering happens in
4
+ // `app/layout.tsx`.
5
+ export default function UnlockLayout({
10
6
  children,
11
7
  }: {
12
8
  children: React.ReactNode;
13
9
  }) {
14
- const headersList = await headers();
15
- // Unlock mode short-circuits before we fetch project config, so we can't
16
- // consult `config.navigation.languages`. The middleware already extracts
17
- // language from the original `from` path (proxy.ts), so the header is
18
- // our only signal — fall back to "en".
19
- const lang = resolveLanguageWithFallback(
20
- getLanguageFromRequest(headersList),
21
- undefined,
22
- );
23
- const dir = isRTLLanguage(lang) ? 'rtl' : undefined;
24
- return (
25
- <html lang={lang} dir={dir}>
26
- <head>
27
- <meta name="viewport" content="width=device-width, initial-scale=1" />
28
- <meta name="robots" content="noindex, nofollow" />
29
- </head>
30
- <body
31
- style={{
32
- margin: 0,
33
- minHeight: '100vh',
34
- fontFamily:
35
- 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
36
- backgroundColor: '#f7f7f8',
37
- }}
38
- >
39
- {children}
40
- </body>
41
- </html>
42
- );
10
+ return <>{children}</>;
43
11
  }
@@ -1,16 +1,152 @@
1
- // Bare root layout must NOT read any dynamic API. Sub-tree layouts
2
- // (app/(unlock)/layout.tsx and the catch-all app/[[...slug]]/layout.tsx)
3
- // own all per-project chrome and request-aware concerns.
4
- //
5
- // Why bare: any `headers()`, `cookies()`, or `searchParams` read here
6
- // would force the entire app dynamic and defeat Vercel edge caching for
7
- // docs pages. See docs/plans/archive/2026-04-20-docs-nav-perf.md for the
8
- // postmortem that proved both prior approaches fail until this layout is
9
- // bare.
10
- export default function RootLayout({
1
+ // Root layout owns the docs html/head/body shell. Reading `headers()`
2
+ // here forces every docs page dynamic — that is the same baseline the
3
+ // codebase had pre-route-group-split. Putting chrome in root keeps
4
+ // ThemeProvider/LayoutWrapper/etc. mounted across soft navigations:
5
+ // Turbopack's per-page Fast Refresh recompile cycle was tearing them
6
+ // down when chrome lived in the catch-all `[[...slug]]/layout.tsx`,
7
+ // producing the visible flash + next-themes script-tag warnings.
8
+ import type { Metadata } from 'next';
9
+ import { headers } from 'next/headers';
10
+ import './globals.css';
11
+ import { getDocsConfig } from '@/lib/docs';
12
+ import { getDocsConfig as getIsrDocsConfig } from '@/lib/docs-isr';
13
+ import {
14
+ isIsrMode,
15
+ getProjectFromRequest,
16
+ getHostAtDocs,
17
+ getLanguageFromRequest,
18
+ } from '@/lib/page-isr-helpers';
19
+ import { isRTLLanguage, resolveLanguageWithFallback } from '@/lib/language-utils';
20
+ import type { DocsConfig } from '@/lib/docs-types';
21
+ import { buildSiteTitle, getFaviconPath, FALLBACK_METADATA } from '@/lib/seo';
22
+ import { fetchCustomCss, fetchCustomJs } from '@/lib/r2-content';
23
+ import { DocsChrome, getLocalFileContent } from '@/lib/layout-helpers';
24
+
25
+ export async function generateMetadata(): Promise<Metadata> {
26
+ let config: DocsConfig;
27
+ if (isIsrMode()) {
28
+ const headersList = await headers();
29
+ const projectSlug = getProjectFromRequest(headersList);
30
+ if (projectSlug) {
31
+ try {
32
+ config = await getIsrDocsConfig(projectSlug);
33
+ } catch {
34
+ return FALLBACK_METADATA;
35
+ }
36
+ } else {
37
+ return FALLBACK_METADATA;
38
+ }
39
+ } else {
40
+ config = getDocsConfig();
41
+ }
42
+
43
+ const faviconPath = getFaviconPath(config.favicon, config.assetVersion);
44
+ return {
45
+ title: {
46
+ template: `%s — ${config.name}`,
47
+ default: buildSiteTitle(config.name),
48
+ },
49
+ description: config.description || `Documentation for ${config.name}`,
50
+ ...(faviconPath && { icons: { icon: faviconPath } }),
51
+ };
52
+ }
53
+
54
+ export default async function RootLayout({
11
55
  children,
12
56
  }: {
13
57
  children: React.ReactNode;
14
58
  }) {
15
- return children as React.ReactElement;
59
+ const headersList = await headers();
60
+
61
+ // Unlock-mode short-circuit: middleware sets x-jd-unlock-mode when
62
+ // rewriting to /jd/unlock. Skip docs chrome — the unlock page renders
63
+ // its own minimal shell via app/(unlock)/jd/unlock/page.tsx.
64
+ if (headersList.get('x-jd-unlock-mode') === '1') {
65
+ const unlockLang = resolveLanguageWithFallback(
66
+ getLanguageFromRequest(headersList),
67
+ undefined,
68
+ );
69
+ const unlockDir = isRTLLanguage(unlockLang) ? 'rtl' : undefined;
70
+ return (
71
+ <html lang={unlockLang} dir={unlockDir}>
72
+ <head>
73
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
74
+ <meta name="robots" content="noindex, nofollow" />
75
+ </head>
76
+ <body
77
+ style={{
78
+ margin: 0,
79
+ minHeight: '100vh',
80
+ fontFamily:
81
+ 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif',
82
+ backgroundColor: '#f7f7f8',
83
+ }}
84
+ >
85
+ {children}
86
+ </body>
87
+ </html>
88
+ );
89
+ }
90
+
91
+ let config: DocsConfig;
92
+ let resolvedProjectSlug: string | null = null;
93
+ if (isIsrMode()) {
94
+ const projectSlug = getProjectFromRequest(headersList);
95
+ resolvedProjectSlug = projectSlug;
96
+ const hostAtDocs = getHostAtDocs(headersList);
97
+ if (projectSlug) {
98
+ try {
99
+ config = await getIsrDocsConfig(projectSlug);
100
+ config = { ...config, hostAtDocs };
101
+ } catch {
102
+ config = {
103
+ name: 'Documentation',
104
+ theme: 'jam',
105
+ colors: { primary: '#0ea5e9' },
106
+ navigation: { groups: [] },
107
+ hostAtDocs,
108
+ };
109
+ }
110
+ } else {
111
+ config = getDocsConfig();
112
+ }
113
+ } else {
114
+ config = getDocsConfig();
115
+ if (process.env.HOST_AT_DOCS === 'true') {
116
+ config = { ...config, hostAtDocs: true };
117
+ }
118
+ }
119
+
120
+ const isIsr = isIsrMode();
121
+ let customCss: string | null = null;
122
+ let customJs: string | null = null;
123
+ if (isIsr && resolvedProjectSlug) {
124
+ if (config._hasCustomCss) {
125
+ customCss = await fetchCustomCss(resolvedProjectSlug);
126
+ }
127
+ if (config._hasCustomJs) {
128
+ customJs = await fetchCustomJs(resolvedProjectSlug);
129
+ }
130
+ } else {
131
+ customCss = getLocalFileContent('custom.css');
132
+ customJs = getLocalFileContent('custom.js');
133
+ }
134
+
135
+ const projectDefaultLang = resolveLanguageWithFallback(null, config.navigation?.languages);
136
+ const lang = getLanguageFromRequest(headersList) ?? projectDefaultLang;
137
+ const dir = isRTLLanguage(lang) ? 'rtl' : undefined;
138
+
139
+ return (
140
+ <DocsChrome
141
+ config={config}
142
+ resolvedProjectSlug={resolvedProjectSlug ?? ''}
143
+ lang={lang}
144
+ dir={dir}
145
+ projectDefaultLang={projectDefaultLang}
146
+ customCss={customCss}
147
+ customJs={customJs}
148
+ >
149
+ {children}
150
+ </DocsChrome>
151
+ );
16
152
  }
@@ -2965,9 +2965,9 @@
2965
2965
  }
2966
2966
  },
2967
2967
  "node_modules/dompurify": {
2968
- "version": "3.4.1",
2969
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.1.tgz",
2970
- "integrity": "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==",
2968
+ "version": "3.4.2",
2969
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.2.tgz",
2970
+ "integrity": "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==",
2971
2971
  "license": "(MPL-2.0 OR Apache-2.0)",
2972
2972
  "optionalDependencies": {
2973
2973
  "@types/trusted-types": "^2.0.7"
@@ -1,119 +0,0 @@
1
- // Sole layout for the docs ISR catch-all route. Reads the project slug
2
- // from middleware-set headers (`x-project-slug`/`x-host-at-docs`), which
3
- // forces every docs page dynamic — see `builder/CLAUDE.md` "Performance
4
- // & Caching" for why edge-caching the docs HTML is currently not viable.
5
- import type { Metadata } from 'next';
6
- import { headers } from 'next/headers';
7
- import '../globals.css';
8
- import { getDocsConfig } from '@/lib/docs';
9
- import { getDocsConfig as getIsrDocsConfig } from '@/lib/docs-isr';
10
- import {
11
- isIsrMode,
12
- getProjectFromRequest,
13
- getHostAtDocs,
14
- getLanguageFromRequest,
15
- } from '@/lib/page-isr-helpers';
16
- import { isRTLLanguage, resolveLanguageWithFallback } from '@/lib/language-utils';
17
- import type { DocsConfig } from '@/lib/docs-types';
18
- import { buildSiteTitle, getFaviconPath, FALLBACK_METADATA } from '@/lib/seo';
19
- import { fetchCustomCss, fetchCustomJs } from '@/lib/r2-content';
20
- import { DocsChrome, getLocalFileContent } from '@/lib/layout-helpers';
21
-
22
- export async function generateMetadata(): Promise<Metadata> {
23
- let config: DocsConfig;
24
- if (isIsrMode()) {
25
- const headersList = await headers();
26
- const projectSlug = getProjectFromRequest(headersList);
27
- if (projectSlug) {
28
- try {
29
- config = await getIsrDocsConfig(projectSlug);
30
- } catch {
31
- return FALLBACK_METADATA;
32
- }
33
- } else {
34
- return FALLBACK_METADATA;
35
- }
36
- } else {
37
- config = getDocsConfig();
38
- }
39
-
40
- const faviconPath = getFaviconPath(config.favicon, config.assetVersion);
41
- return {
42
- title: {
43
- template: `%s — ${config.name}`,
44
- default: buildSiteTitle(config.name),
45
- },
46
- description: config.description || `Documentation for ${config.name}`,
47
- ...(faviconPath && { icons: { icon: faviconPath } }),
48
- };
49
- }
50
-
51
- export default async function LegacyLayout({
52
- children,
53
- }: {
54
- children: React.ReactNode;
55
- }) {
56
- const headersList = await headers();
57
-
58
- let config: DocsConfig;
59
- let resolvedProjectSlug: string | null = null;
60
- if (isIsrMode()) {
61
- const projectSlug = getProjectFromRequest(headersList);
62
- resolvedProjectSlug = projectSlug;
63
- const hostAtDocs = getHostAtDocs(headersList);
64
- if (projectSlug) {
65
- try {
66
- config = await getIsrDocsConfig(projectSlug);
67
- config = { ...config, hostAtDocs };
68
- } catch {
69
- config = {
70
- name: 'Documentation',
71
- theme: 'jam',
72
- colors: { primary: '#0ea5e9' },
73
- navigation: { groups: [] },
74
- hostAtDocs,
75
- };
76
- }
77
- } else {
78
- config = getDocsConfig();
79
- }
80
- } else {
81
- config = getDocsConfig();
82
- if (process.env.HOST_AT_DOCS === 'true') {
83
- config = { ...config, hostAtDocs: true };
84
- }
85
- }
86
-
87
- const isIsr = isIsrMode();
88
- let customCss: string | null = null;
89
- let customJs: string | null = null;
90
- if (isIsr && resolvedProjectSlug) {
91
- if (config._hasCustomCss) {
92
- customCss = await fetchCustomCss(resolvedProjectSlug);
93
- }
94
- if (config._hasCustomJs) {
95
- customJs = await fetchCustomJs(resolvedProjectSlug);
96
- }
97
- } else {
98
- customCss = getLocalFileContent('custom.css');
99
- customJs = getLocalFileContent('custom.js');
100
- }
101
-
102
- const projectDefaultLang = resolveLanguageWithFallback(null, config.navigation?.languages);
103
- const lang = getLanguageFromRequest(headersList) ?? projectDefaultLang;
104
- const dir = isRTLLanguage(lang) ? 'rtl' : undefined;
105
-
106
- return (
107
- <DocsChrome
108
- config={config}
109
- resolvedProjectSlug={resolvedProjectSlug ?? ''}
110
- lang={lang}
111
- dir={dir}
112
- projectDefaultLang={projectDefaultLang}
113
- customCss={customCss}
114
- customJs={customJs}
115
- >
116
- {children}
117
- </DocsChrome>
118
- );
119
- }