jamdesk 1.1.200 → 1.1.201
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.
|
|
3
|
+
"version": "1.1.201",
|
|
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",
|
|
@@ -17,32 +17,58 @@ interface OpenApiSpec {
|
|
|
17
17
|
[key: string]: unknown;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// Cache for OpenAPI specs (longer TTL since specs change infrequently)
|
|
21
|
-
|
|
20
|
+
// Cache for OpenAPI specs (longer TTL since specs change infrequently).
|
|
21
|
+
//
|
|
22
|
+
// Stores the in-flight PROMISE rather than the resolved value, and stores it
|
|
23
|
+
// BEFORE awaiting. Next.js resolves `generateMetadata` and renders the page
|
|
24
|
+
// component for the same request, and both paths resolve the same spec
|
|
25
|
+
// (render-doc-page.tsx buildDocMetadata + renderDocPage). With a value cache
|
|
26
|
+
// populated only after the await, both missed and both paid a full R2 fetch
|
|
27
|
+
// AND a full SwaggerParser.dereference — the dereference being the expensive
|
|
28
|
+
// half. Caching the promise makes the second caller join the first.
|
|
29
|
+
const specCache = new Map<string, { spec: Promise<OpenApiSpec>; timestamp: number }>();
|
|
22
30
|
const CACHE_TTL = 600_000; // 10 minutes
|
|
23
31
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Memoize one spec load on `cacheKey` for CACHE_TTL, de-duplicating concurrent
|
|
34
|
+
* callers. A rejected load is evicted instead of being served for the rest of
|
|
35
|
+
* the TTL, so one transient R2 failure cannot pin a project's specs broken for
|
|
36
|
+
* ten minutes.
|
|
37
|
+
*/
|
|
38
|
+
function cachedSpec(
|
|
39
|
+
cacheKey: string,
|
|
40
|
+
produce: () => Promise<OpenApiSpec>,
|
|
41
|
+
): Promise<OpenApiSpec> {
|
|
42
|
+
const cached = specCache.get(cacheKey);
|
|
28
43
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
29
44
|
return cached.spec;
|
|
30
45
|
}
|
|
31
46
|
|
|
32
|
-
const
|
|
33
|
-
|
|
47
|
+
const spec = produce();
|
|
48
|
+
specCache.set(cacheKey, { spec, timestamp: Date.now() });
|
|
49
|
+
// Evict on failure. The identity check means a later successful entry is
|
|
50
|
+
// never deleted by an earlier failure resolving after it. Attaching a
|
|
51
|
+
// handler here also keeps this bookkeeping from surfacing as an unhandled
|
|
52
|
+
// rejection; the caller still sees the throw from its own await.
|
|
53
|
+
spec.catch(() => {
|
|
54
|
+
if (specCache.get(cacheKey)?.spec === spec) specCache.delete(cacheKey);
|
|
34
55
|
});
|
|
56
|
+
return spec;
|
|
57
|
+
}
|
|
35
58
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
/** Fetch OpenAPI spec from external URL. */
|
|
60
|
+
export function fetchOpenApiSpec(url: string): Promise<OpenApiSpec> {
|
|
61
|
+
return cachedSpec(url, async () => {
|
|
62
|
+
const response = await fetch(url, {
|
|
63
|
+
headers: { Accept: 'application/json' },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
throw new Error(`Failed to fetch OpenAPI spec: ${response.status}`);
|
|
68
|
+
}
|
|
44
69
|
|
|
45
|
-
|
|
70
|
+
return (await response.json()) as OpenApiSpec;
|
|
71
|
+
});
|
|
46
72
|
}
|
|
47
73
|
|
|
48
74
|
/**
|
|
@@ -51,34 +77,23 @@ export async function fetchOpenApiSpec(url: string): Promise<OpenApiSpec> {
|
|
|
51
77
|
* Handles both YAML and JSON formats, and resolves all internal $ref
|
|
52
78
|
* references (matching the static mode behavior of SwaggerParser.validate).
|
|
53
79
|
*/
|
|
54
|
-
export
|
|
80
|
+
export function fetchOpenApiSpecFromR2(
|
|
55
81
|
projectSlug: string,
|
|
56
82
|
specPath: string
|
|
57
83
|
): Promise<OpenApiSpec> {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// Dereference all $ref pointers (matching static mode's SwaggerParser.validate behavior).
|
|
73
|
-
// SwaggerParser.dereference accepts a loose Document type that does not line up with
|
|
74
|
-
// our internal OpenApiSpec; intermediate `any` cast is required at this library boundary.
|
|
75
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- SwaggerParser type interop (see CLAUDE.md memory)
|
|
76
|
-
const spec = await SwaggerParser.dereference(raw as any) as unknown as OpenApiSpec;
|
|
77
|
-
|
|
78
|
-
// Cache it
|
|
79
|
-
specCache.set(cacheKey, { spec, timestamp: Date.now() });
|
|
80
|
-
|
|
81
|
-
return spec;
|
|
84
|
+
return cachedSpec(`r2:${projectSlug}:${specPath}`, async () => {
|
|
85
|
+
const content = await fetchOpenApiFile(projectSlug, specPath);
|
|
86
|
+
|
|
87
|
+
// Parse YAML or JSON into a plain object
|
|
88
|
+
const isYaml = /\.ya?ml$/i.test(specPath);
|
|
89
|
+
const raw = isYaml ? yamlLoad(content) : JSON.parse(content);
|
|
90
|
+
|
|
91
|
+
// Dereference all $ref pointers (matching static mode's SwaggerParser.validate behavior).
|
|
92
|
+
// SwaggerParser.dereference accepts a loose Document type that does not line up with
|
|
93
|
+
// our internal OpenApiSpec; intermediate `any` cast is required at this library boundary.
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- SwaggerParser type interop (see CLAUDE.md memory)
|
|
95
|
+
return await SwaggerParser.dereference(raw as any) as unknown as OpenApiSpec;
|
|
96
|
+
});
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
/**
|
|
@@ -388,9 +388,12 @@ export async function resolveDerivedDescription(
|
|
|
388
388
|
// The spec loader REUSES the render path's cached loaders — the ISR module
|
|
389
389
|
// cache (`r2:${slug}:${specPath}`, 10-min TTL) that `renderDocPage`
|
|
390
390
|
// populates when it renders the page's <ApiEndpoint>, or the static
|
|
391
|
-
// `getCachedSpec`.
|
|
392
|
-
//
|
|
393
|
-
//
|
|
391
|
+
// `getCachedSpec`. That cache stores the in-flight PROMISE, so this lookup
|
|
392
|
+
// joins the render's load rather than starting a second one — Next.js runs
|
|
393
|
+
// `generateMetadata` and the page component for the same request, and a
|
|
394
|
+
// value-cache populated only after the await let BOTH miss and both pay a
|
|
395
|
+
// full R2 fetch plus a full SwaggerParser.dereference. See openapi-isr.ts
|
|
396
|
+
// `cachedSpec`. That matters because docs routes are force-dynamic, so this
|
|
394
397
|
// metadata runs on EVERY page view (every human + crawler). Mirrors the
|
|
395
398
|
// `useIsr`/`projectDir` derivation in `renderDocPage`'s OpenAPI branch.
|
|
396
399
|
const specPaths = collectLocalSpecPaths(config.api.openapi);
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"node_modules/@alloc/quick-lru": {
|
|
71
|
-
"version": "5.
|
|
72
|
-
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.
|
|
73
|
-
"integrity": "sha512-
|
|
71
|
+
"version": "5.3.0",
|
|
72
|
+
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz",
|
|
73
|
+
"integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==",
|
|
74
74
|
"license": "MIT",
|
|
75
75
|
"engines": {
|
|
76
76
|
"node": ">=10"
|
|
@@ -2290,12 +2290,12 @@
|
|
|
2290
2290
|
}
|
|
2291
2291
|
},
|
|
2292
2292
|
"node_modules/commander": {
|
|
2293
|
-
"version": "
|
|
2294
|
-
"resolved": "https://registry.npmjs.org/commander/-/commander-
|
|
2295
|
-
"integrity": "sha512-
|
|
2293
|
+
"version": "15.0.0",
|
|
2294
|
+
"resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",
|
|
2295
|
+
"integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",
|
|
2296
2296
|
"license": "MIT",
|
|
2297
2297
|
"engines": {
|
|
2298
|
-
"node": ">=
|
|
2298
|
+
"node": ">=22.12.0"
|
|
2299
2299
|
}
|
|
2300
2300
|
},
|
|
2301
2301
|
"node_modules/cose-base": {
|
|
@@ -3763,16 +3763,16 @@
|
|
|
3763
3763
|
}
|
|
3764
3764
|
},
|
|
3765
3765
|
"node_modules/katex": {
|
|
3766
|
-
"version": "0.18.
|
|
3767
|
-
"resolved": "https://registry.npmjs.org/katex/-/katex-0.18.
|
|
3768
|
-
"integrity": "sha512-
|
|
3766
|
+
"version": "0.18.5",
|
|
3767
|
+
"resolved": "https://registry.npmjs.org/katex/-/katex-0.18.5.tgz",
|
|
3768
|
+
"integrity": "sha512-1FU5H3RjGJVj6GT9fMjf2uMIXmPQp232aXS3UEeQzf0dxefHg/CKMvNB3DuOC46LFG/E5PNxrE8dOwB782FDGg==",
|
|
3769
3769
|
"funding": [
|
|
3770
3770
|
"https://opencollective.com/katex",
|
|
3771
3771
|
"https://github.com/sponsors/katex"
|
|
3772
3772
|
],
|
|
3773
3773
|
"license": "MIT",
|
|
3774
3774
|
"dependencies": {
|
|
3775
|
-
"commander": "^
|
|
3775
|
+
"commander": "^15.0.0"
|
|
3776
3776
|
},
|
|
3777
3777
|
"bin": {
|
|
3778
3778
|
"katex": "cli.js"
|
|
@@ -4475,6 +4475,15 @@
|
|
|
4475
4475
|
"uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0"
|
|
4476
4476
|
}
|
|
4477
4477
|
},
|
|
4478
|
+
"node_modules/mermaid/node_modules/commander": {
|
|
4479
|
+
"version": "8.3.0",
|
|
4480
|
+
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
|
4481
|
+
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
|
4482
|
+
"license": "MIT",
|
|
4483
|
+
"engines": {
|
|
4484
|
+
"node": ">= 12"
|
|
4485
|
+
}
|
|
4486
|
+
},
|
|
4478
4487
|
"node_modules/mermaid/node_modules/katex": {
|
|
4479
4488
|
"version": "0.16.47",
|
|
4480
4489
|
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
|
|
@@ -4700,6 +4709,15 @@
|
|
|
4700
4709
|
"url": "https://opencollective.com/unified"
|
|
4701
4710
|
}
|
|
4702
4711
|
},
|
|
4712
|
+
"node_modules/micromark-extension-math/node_modules/commander": {
|
|
4713
|
+
"version": "8.3.0",
|
|
4714
|
+
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
|
4715
|
+
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
|
4716
|
+
"license": "MIT",
|
|
4717
|
+
"engines": {
|
|
4718
|
+
"node": ">= 12"
|
|
4719
|
+
}
|
|
4720
|
+
},
|
|
4703
4721
|
"node_modules/micromark-extension-math/node_modules/katex": {
|
|
4704
4722
|
"version": "0.16.47",
|
|
4705
4723
|
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
|
|
@@ -5786,6 +5804,15 @@
|
|
|
5786
5804
|
"url": "https://opencollective.com/unified"
|
|
5787
5805
|
}
|
|
5788
5806
|
},
|
|
5807
|
+
"node_modules/rehype-katex/node_modules/commander": {
|
|
5808
|
+
"version": "8.3.0",
|
|
5809
|
+
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
|
5810
|
+
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
|
5811
|
+
"license": "MIT",
|
|
5812
|
+
"engines": {
|
|
5813
|
+
"node": ">= 12"
|
|
5814
|
+
}
|
|
5815
|
+
},
|
|
5789
5816
|
"node_modules/rehype-katex/node_modules/katex": {
|
|
5790
5817
|
"version": "0.16.47",
|
|
5791
5818
|
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
|