jamdesk 1.1.199 → 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
|
/**
|
|
@@ -348,6 +348,82 @@ export async function resolveOpenApiMetaDescription(
|
|
|
348
348
|
return null;
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
/**
|
|
352
|
+
* The description used for `<meta>`, the OG/Twitter tags and the OG card
|
|
353
|
+
* image: authored frontmatter first, then the OpenAPI operation, then the
|
|
354
|
+
* page's first prose paragraph.
|
|
355
|
+
*
|
|
356
|
+
* Shared by `buildDocMetadata` and `renderDocPage` so the meta tags and the
|
|
357
|
+
* JSON-LD `image` resolve the SAME text. They used to disagree: only the
|
|
358
|
+
* metadata path resolved a description, so on pages with none authored the
|
|
359
|
+
* JSON-LD card URL omitted `description=` while og:image carried it, and the
|
|
360
|
+
* two URLs rendered two genuinely different cards — two OG renders and two CDN
|
|
361
|
+
* entries per page.
|
|
362
|
+
*
|
|
363
|
+
* The gate stays on the RAW value, not frontmatterText(): YAML hands back a
|
|
364
|
+
* Date for `description: 2026-01-01` and a number for `description: 404`.
|
|
365
|
+
* Both are truthy and must keep suppressing the fallbacks exactly as before.
|
|
366
|
+
*
|
|
367
|
+
* NEVER write the result back onto `data`. `parseFrontmatter` is gray-matter,
|
|
368
|
+
* whose cache returns a shallow copy that SHARES one `data` object per content
|
|
369
|
+
* string (gray-matter/index.js:39, `Object.assign({}, cached)`), so an
|
|
370
|
+
* assignment reached `renderDocPage`'s own parse of the same page and rendered
|
|
371
|
+
* as the page SUBTITLE: the operation description twice on every spec-backed
|
|
372
|
+
* page, and the first paragraph twice on every description-less prose page.
|
|
373
|
+
* The cache is module-global, so it persisted across requests rather than
|
|
374
|
+
* being a within-request ordering race. (#7 follow-up.)
|
|
375
|
+
*/
|
|
376
|
+
export async function resolveDerivedDescription(
|
|
377
|
+
config: DocsConfig,
|
|
378
|
+
data: FrontmatterData,
|
|
379
|
+
content: string,
|
|
380
|
+
projectSlug: string | null | undefined,
|
|
381
|
+
): Promise<string | undefined> {
|
|
382
|
+
let metaDescription: string | undefined = data.description;
|
|
383
|
+
if (metaDescription) return metaDescription;
|
|
384
|
+
|
|
385
|
+
if (typeof data.openapi === 'string' && data.openapi && config.api?.openapi) {
|
|
386
|
+
// Source the description from the OpenAPI operation before falling back to
|
|
387
|
+
// first-paragraph extraction (which is empty for prose-less API pages).
|
|
388
|
+
// The spec loader REUSES the render path's cached loaders — the ISR module
|
|
389
|
+
// cache (`r2:${slug}:${specPath}`, 10-min TTL) that `renderDocPage`
|
|
390
|
+
// populates when it renders the page's <ApiEndpoint>, or the static
|
|
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
|
|
397
|
+
// metadata runs on EVERY page view (every human + crawler). Mirrors the
|
|
398
|
+
// `useIsr`/`projectDir` derivation in `renderDocPage`'s OpenAPI branch.
|
|
399
|
+
const specPaths = collectLocalSpecPaths(config.api.openapi);
|
|
400
|
+
const useIsr = isIsrMode() && !!projectSlug;
|
|
401
|
+
const projectDir = useIsr ? null : getContentDir();
|
|
402
|
+
const loadSpecForMeta = async (sp: string): Promise<unknown | null> => {
|
|
403
|
+
try {
|
|
404
|
+
if (useIsr && projectSlug) {
|
|
405
|
+
const { resolveOpenApiSpec } = await import('@/lib/openapi-isr');
|
|
406
|
+
return await resolveOpenApiSpec(projectSlug, sp);
|
|
407
|
+
}
|
|
408
|
+
if (projectDir) {
|
|
409
|
+
const { api } = await getCachedSpec(sp, projectDir);
|
|
410
|
+
return api;
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
} catch {
|
|
414
|
+
// Any load/parse error degrades to generateAutoDescription below.
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
const sourced = await resolveOpenApiMetaDescription(
|
|
419
|
+
data.openapi, specPaths, loadSpecForMeta,
|
|
420
|
+
);
|
|
421
|
+
if (sourced) metaDescription = sourced;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return metaDescription || generateAutoDescription(content);
|
|
425
|
+
}
|
|
426
|
+
|
|
351
427
|
export async function buildDocMetadata(input: RenderInput): Promise<Metadata> {
|
|
352
428
|
const { slug: slugInput, projectSlug, hostAtDocs, docsPrefix, requestHeaders } = input;
|
|
353
429
|
const linkPrefix = docsPrefix ?? (hostAtDocs ? '/docs' : '');
|
|
@@ -413,59 +489,9 @@ export async function buildDocMetadata(input: RenderInput): Promise<Metadata> {
|
|
|
413
489
|
const parsed = parseFrontmatter(fileContents);
|
|
414
490
|
const data = parsed.data as FrontmatterData;
|
|
415
491
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
// here reached `renderDocPage`'s own parse of the same page and rendered as
|
|
420
|
-
// the page SUBTITLE: the operation description twice on every spec-backed
|
|
421
|
-
// page, and the first paragraph twice on every description-less prose page.
|
|
422
|
-
// The cache is module-global, so it persisted across requests rather than
|
|
423
|
-
// being a within-request ordering race. (#7 follow-up.)
|
|
424
|
-
//
|
|
425
|
-
// The gate stays on the RAW value, not frontmatterText(): YAML hands back a
|
|
426
|
-
// Date for `description: 2026-01-01` and a number for `description: 404`.
|
|
427
|
-
// Both are truthy and must keep suppressing the fallbacks exactly as before.
|
|
428
|
-
let metaDescription: string | undefined = data.description;
|
|
429
|
-
if (!metaDescription) {
|
|
430
|
-
if (typeof data.openapi === 'string' && data.openapi && config.api?.openapi) {
|
|
431
|
-
// Source the description from the OpenAPI operation before falling back to
|
|
432
|
-
// first-paragraph extraction (which is empty for prose-less API pages).
|
|
433
|
-
// The spec loader REUSES the render path's cached loaders — the ISR module
|
|
434
|
-
// cache (`r2:${slug}:${specPath}`, 10-min TTL) that `renderDocPage`
|
|
435
|
-
// populates when it renders the page's <ApiEndpoint>, or the static
|
|
436
|
-
// `getCachedSpec`. So the spec is the same warm parse the render already
|
|
437
|
-
// did: the marginal cost of this lookup is a cache hit, never an uncached
|
|
438
|
-
// fetch+parse. That matters because docs routes are force-dynamic, so this
|
|
439
|
-
// metadata runs on EVERY page view (every human + crawler). Mirrors the
|
|
440
|
-
// `useIsr`/`projectDir` derivation in `renderDocPage`'s OpenAPI branch.
|
|
441
|
-
const specPaths = collectLocalSpecPaths(config.api.openapi);
|
|
442
|
-
const useIsr = isIsrMode() && !!projectSlug;
|
|
443
|
-
const projectDir = useIsr ? null : getContentDir();
|
|
444
|
-
const loadSpecForMeta = async (sp: string): Promise<unknown | null> => {
|
|
445
|
-
try {
|
|
446
|
-
if (useIsr && projectSlug) {
|
|
447
|
-
const { resolveOpenApiSpec } = await import('@/lib/openapi-isr');
|
|
448
|
-
return await resolveOpenApiSpec(projectSlug, sp);
|
|
449
|
-
}
|
|
450
|
-
if (projectDir) {
|
|
451
|
-
const { api } = await getCachedSpec(sp, projectDir);
|
|
452
|
-
return api;
|
|
453
|
-
}
|
|
454
|
-
return null;
|
|
455
|
-
} catch {
|
|
456
|
-
// Any load/parse error degrades to generateAutoDescription below.
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
const sourced = await resolveOpenApiMetaDescription(
|
|
461
|
-
data.openapi, specPaths, loadSpecForMeta,
|
|
462
|
-
);
|
|
463
|
-
if (sourced) metaDescription = sourced;
|
|
464
|
-
}
|
|
465
|
-
if (!metaDescription) {
|
|
466
|
-
metaDescription = generateAutoDescription(parsed.content);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
492
|
+
const metaDescription = await resolveDerivedDescription(
|
|
493
|
+
config, data, parsed.content, projectSlug,
|
|
494
|
+
);
|
|
469
495
|
|
|
470
496
|
// buildSeoMetadata reads `frontmatter.description` for og:, twitter: and the
|
|
471
497
|
// OG card, so it must see the RESOLVED text — via a copy, never by mutating
|
|
@@ -611,7 +637,15 @@ export async function renderDocPage(input: RenderInput): Promise<ReactElement> {
|
|
|
611
637
|
|
|
612
638
|
const baseUrl = resolveBaseUrl(requestHeaders, projectSlug, hostAtDocs);
|
|
613
639
|
const faqPairs = extractFaqPairs(rawContent);
|
|
614
|
-
|
|
640
|
+
// The same resolution buildDocMetadata performs, so og:image and the JSON-LD
|
|
641
|
+
// `image` point at ONE card. Passed as a copy — never a write onto the shared
|
|
642
|
+
// `data`, which is what caused #7 in the first place.
|
|
643
|
+
const ogDescription = await resolveDerivedDescription(
|
|
644
|
+
config, data, rawContent, projectSlug,
|
|
645
|
+
);
|
|
646
|
+
const ogImageUrl = buildPageOgImageUrl(
|
|
647
|
+
config, { ...data, description: ogDescription }, baseUrl, linkPrefix,
|
|
648
|
+
);
|
|
615
649
|
// See frontmatterText: a YAML Date title throws when React renders it as a
|
|
616
650
|
// child below, so every consumer in this function reads the coerced value.
|
|
617
651
|
const titleText = frontmatterText(data.title);
|
package/vendored/lib/seo.ts
CHANGED
|
@@ -879,7 +879,7 @@ const MAX_DESCRIPTION_LENGTH = 155;
|
|
|
879
879
|
/** Patterns that indicate a paragraph is not prose (headings, components, images, comments, etc). */
|
|
880
880
|
const NON_PROSE_PATTERNS = [
|
|
881
881
|
/^#{1,6}\s/, // headings
|
|
882
|
-
|
|
882
|
+
/^<\/?[A-Z]/, // MDX components, opening AND closing (`</ResponseExample>`)
|
|
883
883
|
/^!\[/, // images
|
|
884
884
|
/^<!--/, // HTML comments
|
|
885
885
|
/^>/, // blockquotes
|
|
@@ -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",
|