jamdesk 1.1.90 → 1.1.92
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 +1 -1
- package/vendored/app/api/r2/[project]/[...path]/route.ts +14 -9
- package/vendored/app/layout.tsx +2 -2
- package/vendored/components/HtmlLangSync.tsx +3 -2
- package/vendored/components/mdx/Accordion.tsx +1 -1
- package/vendored/components/mdx/Card.tsx +1 -1
- package/vendored/components/mdx/CodeGroup.tsx +18 -23
- package/vendored/components/mdx/Color.tsx +0 -1
- package/vendored/components/mdx/Icon.tsx +1 -1
- package/vendored/components/mdx/MDXComponents.tsx +92 -66
- package/vendored/components/mdx/OpenApiEndpoint.tsx +0 -1
- package/vendored/components/mdx/ParamField.tsx +0 -1
- package/vendored/components/mdx/RequestExample.tsx +0 -1
- package/vendored/components/mdx/ResponseExample.tsx +0 -1
- package/vendored/components/mdx/Steps.tsx +12 -3
- package/vendored/components/mdx/Table.tsx +8 -2
- package/vendored/components/mdx/Tabs.tsx +1 -1
- package/vendored/components/mdx/Tree.tsx +6 -4
- package/vendored/components/navigation/Header.tsx +7 -5
- package/vendored/components/navigation/LanguageSelector.tsx +37 -10
- package/vendored/components/navigation/TableOfContents.tsx +1 -1
- package/vendored/components/navigation/TabsNav.tsx +17 -5
- package/vendored/components/search/SearchModal.tsx +41 -36
- package/vendored/components/ui/CodePanel.tsx +2 -2
- package/vendored/hooks/useChat.ts +1 -1
- package/vendored/hooks/useShikiHighlight.ts +7 -1
- package/vendored/lib/code-utils.ts +6 -2
- package/vendored/lib/health-checks.ts +2 -2
- package/vendored/lib/language-utils.ts +87 -15
- package/vendored/lib/layout-helpers.tsx +2 -1
- package/vendored/lib/mdx-inline-components.ts +1 -1
- package/vendored/lib/navigation-resolver.ts +0 -69
- package/vendored/lib/normalize-config.ts +1 -1
- package/vendored/lib/openapi/generator.ts +3 -3
- package/vendored/lib/openapi/parser.ts +14 -6
- package/vendored/lib/openapi/validator.ts +2 -2
- package/vendored/lib/openapi-isr.ts +4 -1
- package/vendored/lib/public-paths-resolver.ts +7 -6
- package/vendored/lib/redis.ts +2 -2
- package/vendored/lib/rehype-code-meta.ts +2 -2
- package/vendored/lib/render-doc-page.tsx +2 -2
- package/vendored/lib/seo.ts +22 -7
- package/vendored/lib/shiki-highlighter.ts +1 -1
- package/vendored/lib/snippet-loader-isr.ts +1 -1
- package/vendored/lib/static-artifacts.ts +37 -6
- package/vendored/lib/validate-config.ts +1 -0
- package/vendored/workspace-package-lock.json +10 -10
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Supports generating pages from navigation openapi field.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
9
|
-
import type { GeneratedPage,
|
|
8
|
+
import type { OpenAPIV3 } from 'openapi-types';
|
|
9
|
+
import type { GeneratedPage, OpenApiEndpointData } from './types';
|
|
10
10
|
import { parseEndpoint, getAllEndpoints, getSpecInfo } from './parser';
|
|
11
11
|
import { getCachedSpec } from './cache';
|
|
12
12
|
|
|
@@ -94,7 +94,7 @@ export async function generatePagesFromSpec(
|
|
|
94
94
|
|
|
95
95
|
for (const { method, path, operation, tags } of endpoints) {
|
|
96
96
|
// Skip hidden endpoints (x-hidden extension)
|
|
97
|
-
if ((operation as
|
|
97
|
+
if ((operation as { 'x-hidden'?: unknown })['x-hidden']) {
|
|
98
98
|
continue;
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -193,8 +193,9 @@ function parseServers(api: OpenAPI.Document): ServerInfo[] {
|
|
|
193
193
|
|
|
194
194
|
// Swagger 2.0
|
|
195
195
|
if ('host' in api && typeof api.host === 'string') {
|
|
196
|
-
const
|
|
197
|
-
const
|
|
196
|
+
const swagger2 = api as { schemes?: string[]; basePath?: string };
|
|
197
|
+
const scheme = swagger2.schemes?.[0] || 'https';
|
|
198
|
+
const basePath = swagger2.basePath || '';
|
|
198
199
|
return [{
|
|
199
200
|
url: `${scheme}://${api.host}${basePath}`,
|
|
200
201
|
}];
|
|
@@ -229,9 +230,16 @@ function parseSecuritySchemes(api: OpenAPI.Document): Map<string, SecurityRequir
|
|
|
229
230
|
|
|
230
231
|
// Swagger 2.0
|
|
231
232
|
if ('securityDefinitions' in api) {
|
|
232
|
-
|
|
233
|
+
type Swagger2SecurityScheme = {
|
|
234
|
+
type: SecurityRequirement['type'];
|
|
235
|
+
scheme?: string;
|
|
236
|
+
in?: SecurityRequirement['in'];
|
|
237
|
+
name?: string;
|
|
238
|
+
};
|
|
239
|
+
const defs = (api as { securityDefinitions?: Record<string, Swagger2SecurityScheme> })
|
|
240
|
+
.securityDefinitions;
|
|
233
241
|
for (const [name, scheme] of Object.entries(defs || {})) {
|
|
234
|
-
const s = scheme as
|
|
242
|
+
const s = scheme as Swagger2SecurityScheme;
|
|
235
243
|
schemes.set(name, {
|
|
236
244
|
name,
|
|
237
245
|
type: s.type,
|
|
@@ -319,7 +327,7 @@ function parseRequestBody(
|
|
|
319
327
|
content[mediaType] = {
|
|
320
328
|
schema: toJsonSchema(mediaObj.schema),
|
|
321
329
|
example: mediaObj.example,
|
|
322
|
-
examples: mediaObj.examples as
|
|
330
|
+
examples: mediaObj.examples as Record<string, { value: unknown; summary?: string }> | undefined,
|
|
323
331
|
};
|
|
324
332
|
}
|
|
325
333
|
|
|
@@ -346,7 +354,7 @@ function parseResponses(
|
|
|
346
354
|
content[mediaType] = {
|
|
347
355
|
schema: toJsonSchema(mediaObj.schema),
|
|
348
356
|
example: mediaObj.example,
|
|
349
|
-
examples: mediaObj.examples as
|
|
357
|
+
examples: mediaObj.examples as Record<string, { value: unknown; summary?: string }> | undefined,
|
|
350
358
|
};
|
|
351
359
|
}
|
|
352
360
|
|
|
@@ -41,12 +41,12 @@ function checkDuplicateOperationIds(
|
|
|
41
41
|
specPath: string
|
|
42
42
|
): OpenApiValidationError | null {
|
|
43
43
|
const seen = new Map<string, string>(); // operationId → "METHOD /path"
|
|
44
|
-
const paths = (api as
|
|
44
|
+
const paths = (api as { paths?: Record<string, unknown> }).paths || {};
|
|
45
45
|
const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'];
|
|
46
46
|
|
|
47
47
|
for (const [pathStr, pathItem] of Object.entries(paths)) {
|
|
48
48
|
for (const method of methods) {
|
|
49
|
-
const operation = (pathItem as Record<string,
|
|
49
|
+
const operation = (pathItem as Record<string, { operationId?: string } | undefined> | undefined)?.[method];
|
|
50
50
|
if (!operation?.operationId) continue;
|
|
51
51
|
|
|
52
52
|
const id = operation.operationId;
|
|
@@ -69,7 +69,10 @@ export async function fetchOpenApiSpecFromR2(
|
|
|
69
69
|
const isYaml = /\.ya?ml$/i.test(specPath);
|
|
70
70
|
const raw = isYaml ? yaml.load(content) : JSON.parse(content);
|
|
71
71
|
|
|
72
|
-
// Dereference all $ref pointers (matching static mode's SwaggerParser.validate behavior)
|
|
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)
|
|
73
76
|
const spec = await SwaggerParser.dereference(raw as any) as unknown as OpenApiSpec;
|
|
74
77
|
|
|
75
78
|
// Cache it
|
|
@@ -22,10 +22,11 @@ function collectPublicGroupPages(
|
|
|
22
22
|
if (!nav || typeof nav !== 'object') return out;
|
|
23
23
|
|
|
24
24
|
const node = nav as Record<string, unknown>;
|
|
25
|
+
const pages = node.pages;
|
|
25
26
|
|
|
26
|
-
if ('group' in node && Array.isArray(
|
|
27
|
+
if ('group' in node && Array.isArray(pages)) {
|
|
27
28
|
const isPublic = inPublicGroup || node.public === true;
|
|
28
|
-
for (const p of
|
|
29
|
+
for (const p of pages) {
|
|
29
30
|
if (typeof p === 'string') {
|
|
30
31
|
if (isPublic) out.push(`/${p}`);
|
|
31
32
|
} else {
|
|
@@ -35,14 +36,14 @@ function collectPublicGroupPages(
|
|
|
35
36
|
return out;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
if (Array.isArray(
|
|
39
|
-
for (const p of
|
|
39
|
+
if (Array.isArray(pages)) {
|
|
40
|
+
for (const p of pages) {
|
|
40
41
|
collectPublicGroupPages(p, inPublicGroup, out);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
for (const key of ['groups', 'tabs', 'anchors', 'versions', 'languages']) {
|
|
45
|
-
const arr =
|
|
46
|
+
const arr = node[key];
|
|
46
47
|
if (Array.isArray(arr)) {
|
|
47
48
|
for (const item of arr) {
|
|
48
49
|
collectPublicGroupPages(item, inPublicGroup, out);
|
|
@@ -72,7 +73,7 @@ export function resolvePublicPaths(input: Input): string[] {
|
|
|
72
73
|
set.add(path);
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
const globs =
|
|
76
|
+
const globs = input.docsConfig.auth?.password?.public;
|
|
76
77
|
if (Array.isArray(globs)) {
|
|
77
78
|
for (const g of globs) {
|
|
78
79
|
if (typeof g === 'string' && g.startsWith('/')) {
|
package/vendored/lib/redis.ts
CHANGED
|
@@ -44,9 +44,9 @@ async function upstashCommand(
|
|
|
44
44
|
headers: { Authorization: `Bearer ${kvToken}` },
|
|
45
45
|
});
|
|
46
46
|
const bodyText = await response.text();
|
|
47
|
-
let data:
|
|
47
|
+
let data: { error?: string; result?: unknown } | null = null;
|
|
48
48
|
try {
|
|
49
|
-
data = bodyText ? JSON.parse(bodyText) : null;
|
|
49
|
+
data = bodyText ? (JSON.parse(bodyText) as { error?: string; result?: unknown }) : null;
|
|
50
50
|
} catch {
|
|
51
51
|
data = { result: bodyText };
|
|
52
52
|
}
|
|
@@ -154,7 +154,7 @@ export function rehypeCodeMeta() {
|
|
|
154
154
|
|
|
155
155
|
// Store parsed data in node.data so Shiki transformers can access it
|
|
156
156
|
node.data = node.data || {};
|
|
157
|
-
(node.data as
|
|
157
|
+
(node.data as { parsedMeta?: unknown }).parsedMeta = parsed;
|
|
158
158
|
|
|
159
159
|
if (parsed.icon) {
|
|
160
160
|
node.properties['data-icon'] = parsed.icon;
|
|
@@ -182,7 +182,7 @@ export function rehypeCodeMeta() {
|
|
|
182
182
|
preNode.properties['data-title'] = parsed.title;
|
|
183
183
|
// Store in parent data as well
|
|
184
184
|
preNode.data = preNode.data || {};
|
|
185
|
-
(preNode.data as
|
|
185
|
+
(preNode.data as { parsedTitle?: string }).parsedTitle = parsed.title;
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { notFound } from 'next/navigation';
|
|
14
14
|
import { MDXRemote } from 'next-mdx-remote/rsc';
|
|
15
15
|
import type { Metadata } from 'next';
|
|
16
|
-
import type { ReactElement } from 'react';
|
|
16
|
+
import type { AnchorHTMLAttributes, ReactElement } from 'react';
|
|
17
17
|
import { MDXComponents } from '@/components/mdx/MDXComponents';
|
|
18
18
|
import { Breadcrumb } from '@/components/navigation/Breadcrumb';
|
|
19
19
|
import { TableOfContents } from '@/components/navigation/TableOfContents';
|
|
@@ -360,7 +360,7 @@ export async function renderDocPage(input: RenderInput): Promise<ReactElement> {
|
|
|
360
360
|
...snippetAliases,
|
|
361
361
|
...inlineComponents,
|
|
362
362
|
...(hostAtDocs ? {
|
|
363
|
-
a: ({ ariaLabel, href, ...props }:
|
|
363
|
+
a: ({ ariaLabel, href, ...props }: AnchorHTMLAttributes<HTMLAnchorElement> & { ariaLabel?: string }) => {
|
|
364
364
|
const needsPrefix = href?.startsWith('/') && !href.startsWith('/docs/') && href !== '/docs';
|
|
365
365
|
return (
|
|
366
366
|
<a
|
package/vendored/lib/seo.ts
CHANGED
|
@@ -8,9 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Metadata } from 'next';
|
|
11
|
-
import type { DocsConfig, Logo, LogoConfig, Favicon,
|
|
11
|
+
import type { DocsConfig, Logo, LogoConfig, Favicon, LanguageConfig, LanguageCode } from './docs-types';
|
|
12
12
|
import { transformConfigImagePath } from './docs-types';
|
|
13
|
-
import {
|
|
13
|
+
import { findHreflangAliasCollisions, transformLanguagePath, toHreflang } from './language-utils';
|
|
14
|
+
import { logger } from '../shared/logger';
|
|
15
|
+
|
|
16
|
+
// Dedupe per-process: same docs.json shape produces the same collision
|
|
17
|
+
// signature on every page render, so we'd otherwise spam logs.
|
|
18
|
+
const warnedCollisionSignatures = new Set<string>();
|
|
14
19
|
|
|
15
20
|
const HAS_DOCS_SUFFIX = /\b(?:Documentation|Docs)\s*$/i;
|
|
16
21
|
|
|
@@ -183,7 +188,7 @@ function getLanguagePagePaths(langConfig: LanguageConfig): Set<string> {
|
|
|
183
188
|
* @param languages - Array of language configurations from docs.json
|
|
184
189
|
* @returns Record of language code to URL for alternates.languages
|
|
185
190
|
*/
|
|
186
|
-
function buildHreflangAlternates(
|
|
191
|
+
export function buildHreflangAlternates(
|
|
187
192
|
baseUrl: string,
|
|
188
193
|
pagePath: string,
|
|
189
194
|
languages: LanguageConfig[]
|
|
@@ -197,14 +202,12 @@ function buildHreflangAlternates(
|
|
|
197
202
|
const defaultLang = languages.find((l) => l.default)?.language || languages[0]?.language || 'en';
|
|
198
203
|
|
|
199
204
|
// Detect current language from path
|
|
200
|
-
const currentLang = extractLanguageFromPath(pagePath) || defaultLang;
|
|
201
|
-
|
|
202
205
|
// Keep only languages whose navigation declares this page — capturing the
|
|
203
206
|
// language-relative path so the build loop and x-default branch don't have
|
|
204
207
|
// to recompute it via transformLanguagePath.
|
|
205
208
|
const supported: Array<{ lang: LanguageCode; cleanPath: string }> = [];
|
|
206
209
|
for (const langConfig of languages) {
|
|
207
|
-
const langPath = transformLanguagePath(pagePath,
|
|
210
|
+
const langPath = transformLanguagePath(pagePath, langConfig.language, defaultLang);
|
|
208
211
|
const cleanPath = langPath.replace(/^\//, '');
|
|
209
212
|
if (getLanguagePagePaths(langConfig).has(cleanPath)) {
|
|
210
213
|
supported.push({ lang: langConfig.language, cleanPath });
|
|
@@ -217,9 +220,21 @@ function buildHreflangAlternates(
|
|
|
217
220
|
return undefined;
|
|
218
221
|
}
|
|
219
222
|
|
|
223
|
+
const collisions = findHreflangAliasCollisions(supported.map((s) => s.lang));
|
|
224
|
+
for (const { tag, codes } of collisions) {
|
|
225
|
+
const signature = `${tag}:${codes.join(',')}`;
|
|
226
|
+
if (warnedCollisionSignatures.has(signature)) continue;
|
|
227
|
+
warnedCollisionSignatures.add(signature);
|
|
228
|
+
logger.warn('hreflang alias collision — last entry wins, earlier translations dropped', {
|
|
229
|
+
bcp47Tag: tag,
|
|
230
|
+
internalCodes: codes,
|
|
231
|
+
remedy: 'Remove one of the duplicate entries from navigation.languages in docs.json',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
220
235
|
const alternates: Record<string, string> = {};
|
|
221
236
|
for (const { lang, cleanPath } of supported) {
|
|
222
|
-
alternates[lang] = cleanPath ? `${baseUrl}/${cleanPath}` : baseUrl;
|
|
237
|
+
alternates[toHreflang(lang)] = cleanPath ? `${baseUrl}/${cleanPath}` : baseUrl;
|
|
223
238
|
}
|
|
224
239
|
|
|
225
240
|
// x-default points to the default language only when it has the page; absence
|
|
@@ -8,7 +8,7 @@ import { PRELOADED_LANGUAGES, SUPPORTED_THEMES, type SupportedTheme } from './sh
|
|
|
8
8
|
|
|
9
9
|
// Extend globalThis type for highlighter caching
|
|
10
10
|
declare global {
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
var __shikiHighlighterPromise: Promise<Highlighter> | undefined;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -322,7 +322,7 @@ async function compileSnippet(
|
|
|
322
322
|
try {
|
|
323
323
|
// Create function that returns the component
|
|
324
324
|
// NOTE: new Function() is intentionally used here to compile user-provided snippets
|
|
325
|
-
|
|
325
|
+
|
|
326
326
|
const createComponent = new Function(
|
|
327
327
|
'React',
|
|
328
328
|
'_jsx',
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* for ISR projects. These are uploaded to R2 alongside the MDX content.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { NavigationConfig } from './docs-types.js';
|
|
8
|
+
import type { NavigationConfig, LanguageConfig } from './docs-types.js';
|
|
9
9
|
import { RECURSE_KEYS } from './enhance-navigation.js';
|
|
10
10
|
import { filterVisibility } from './visibility-filter.js';
|
|
11
11
|
import {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resolveLocaleFromPath,
|
|
14
14
|
resolveLocaleWithLoweredSet,
|
|
15
15
|
} from './language-utils.js';
|
|
16
|
+
import { buildHreflangAlternates } from './seo.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Page metadata for artifact generation.
|
|
@@ -44,6 +45,13 @@ export interface SitemapOptions {
|
|
|
44
45
|
hostAtDocs?: boolean;
|
|
45
46
|
/** Block all crawlers - generates empty sitemap */
|
|
46
47
|
noindex?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Language configurations from docs.json navigation. When provided, the
|
|
50
|
+
* sitemap emits `<xhtml:link rel="alternate" hreflang>` siblings per URL
|
|
51
|
+
* for every language that declares the page — Google's recommended sitemap
|
|
52
|
+
* localization signal (in addition to in-page hreflang link tags).
|
|
53
|
+
*/
|
|
54
|
+
languages?: LanguageConfig[];
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
/**
|
|
@@ -53,7 +61,7 @@ export interface SitemapOptions {
|
|
|
53
61
|
* @returns XML string
|
|
54
62
|
*/
|
|
55
63
|
export function generateSitemap(options: SitemapOptions): string {
|
|
56
|
-
const { baseUrl, pages, hostAtDocs = false, noindex = false } = options;
|
|
64
|
+
const { baseUrl, pages, hostAtDocs = false, noindex = false, languages } = options;
|
|
57
65
|
|
|
58
66
|
if (noindex) {
|
|
59
67
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -62,6 +70,11 @@ export function generateSitemap(options: SitemapOptions): string {
|
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
const urlPrefix = hostAtDocs ? '/docs' : '';
|
|
73
|
+
const hreflangBaseUrl = hostAtDocs ? `${baseUrl}/docs` : baseUrl;
|
|
74
|
+
const multiLang = languages && languages.length > 1;
|
|
75
|
+
const urlsetAttrs = multiLang
|
|
76
|
+
? 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"'
|
|
77
|
+
: 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
|
|
65
78
|
|
|
66
79
|
const entries = pages
|
|
67
80
|
.filter(p => !p.noindex && !p.hidden)
|
|
@@ -70,16 +83,32 @@ export function generateSitemap(options: SitemapOptions): string {
|
|
|
70
83
|
const lastmod = p.lastModified || new Date().toISOString().split('T')[0];
|
|
71
84
|
const priority = p.path === 'introduction' ? '1.0' : '0.8';
|
|
72
85
|
|
|
86
|
+
// Per Google's spec, emit hreflang siblings only when the page has
|
|
87
|
+
// translations. buildHreflangAlternates already filters to languages
|
|
88
|
+
// whose navigation declares this page, so we won't point at 404s.
|
|
89
|
+
let hreflangLinks = '';
|
|
90
|
+
if (multiLang) {
|
|
91
|
+
const alternates = buildHreflangAlternates(hreflangBaseUrl, p.path, languages);
|
|
92
|
+
if (alternates) {
|
|
93
|
+
hreflangLinks = Object.entries(alternates)
|
|
94
|
+
.map(
|
|
95
|
+
([tag, href]) =>
|
|
96
|
+
`\n <xhtml:link rel="alternate" hreflang="${tag}" href="${href}"/>`,
|
|
97
|
+
)
|
|
98
|
+
.join('');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
73
102
|
return ` <url>
|
|
74
103
|
<loc>${url}</loc>
|
|
75
104
|
<lastmod>${lastmod}</lastmod>
|
|
76
105
|
<changefreq>weekly</changefreq>
|
|
77
|
-
<priority>${priority}</priority
|
|
106
|
+
<priority>${priority}</priority>${hreflangLinks}
|
|
78
107
|
</url>`;
|
|
79
108
|
});
|
|
80
109
|
|
|
81
110
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
82
|
-
<urlset
|
|
111
|
+
<urlset ${urlsetAttrs}>
|
|
83
112
|
${entries.join('\n')}
|
|
84
113
|
</urlset>`;
|
|
85
114
|
}
|
|
@@ -353,6 +382,8 @@ export interface GenerateAllOptions {
|
|
|
353
382
|
rssPages?: RssPageInfo[];
|
|
354
383
|
/** Pages with full content (for llms-full.txt generation) */
|
|
355
384
|
llmsFullPages?: LlmsFullPageInfo[];
|
|
385
|
+
/** Language configurations (forwarded to sitemap for hreflang siblings) */
|
|
386
|
+
languages?: LanguageConfig[];
|
|
356
387
|
}
|
|
357
388
|
|
|
358
389
|
/**
|
|
@@ -374,10 +405,10 @@ export interface GeneratedArtifacts {
|
|
|
374
405
|
*/
|
|
375
406
|
export function generateAllArtifacts(options: GenerateAllOptions): GeneratedArtifacts {
|
|
376
407
|
const {
|
|
377
|
-
baseUrl, name, description, pages, hostAtDocs, noindex, rssPages, llmsFullPages,
|
|
408
|
+
baseUrl, name, description, pages, hostAtDocs, noindex, rssPages, llmsFullPages, languages,
|
|
378
409
|
} = options;
|
|
379
410
|
|
|
380
|
-
const sitemap = generateSitemap({ baseUrl, pages, hostAtDocs, noindex });
|
|
411
|
+
const sitemap = generateSitemap({ baseUrl, pages, hostAtDocs, noindex, languages });
|
|
381
412
|
const llmsTxt = generateLlmsTxt({
|
|
382
413
|
name, description, baseUrl, pages, hostAtDocs, noindex,
|
|
383
414
|
});
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import AjvModule, { ErrorObject } from 'ajv';
|
|
13
13
|
import addFormatsModule from 'ajv-formats';
|
|
14
14
|
// ESM compatibility - ajv-formats exports differently under NodeNext resolution
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ajv-formats ESM/CJS interop default-export shape varies by bundler
|
|
15
16
|
const addFormats = (addFormatsModule as any).default || addFormatsModule;
|
|
16
17
|
// ESM compatibility - Ajv exports differently in Node.js ESM
|
|
17
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1921,12 +1921,12 @@
|
|
|
1921
1921
|
}
|
|
1922
1922
|
},
|
|
1923
1923
|
"node_modules/@types/node": {
|
|
1924
|
-
"version": "25.
|
|
1925
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.
|
|
1926
|
-
"integrity": "sha512-
|
|
1924
|
+
"version": "25.8.0",
|
|
1925
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz",
|
|
1926
|
+
"integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==",
|
|
1927
1927
|
"license": "MIT",
|
|
1928
1928
|
"dependencies": {
|
|
1929
|
-
"undici-types": "
|
|
1929
|
+
"undici-types": ">=7.24.0 <7.24.7"
|
|
1930
1930
|
}
|
|
1931
1931
|
},
|
|
1932
1932
|
"node_modules/@types/react": {
|
|
@@ -2928,9 +2928,9 @@
|
|
|
2928
2928
|
"license": "MIT"
|
|
2929
2929
|
},
|
|
2930
2930
|
"node_modules/electron-to-chromium": {
|
|
2931
|
-
"version": "1.5.
|
|
2932
|
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.
|
|
2933
|
-
"integrity": "sha512-
|
|
2931
|
+
"version": "1.5.355",
|
|
2932
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.355.tgz",
|
|
2933
|
+
"integrity": "sha512-LUPZhKzZPYSPme1jEYohpkA+ybYCJztr1quAdBd7E7h3+VOBVcKkwwtBJu41nrjawrRzfb8mtMfzWozoaK0ZIQ==",
|
|
2934
2934
|
"license": "ISC"
|
|
2935
2935
|
},
|
|
2936
2936
|
"node_modules/enhanced-resolve": {
|
|
@@ -6279,9 +6279,9 @@
|
|
|
6279
6279
|
"license": "MIT"
|
|
6280
6280
|
},
|
|
6281
6281
|
"node_modules/undici-types": {
|
|
6282
|
-
"version": "7.
|
|
6283
|
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.
|
|
6284
|
-
"integrity": "sha512-
|
|
6282
|
+
"version": "7.24.6",
|
|
6283
|
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
|
6284
|
+
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
|
6285
6285
|
"license": "MIT"
|
|
6286
6286
|
},
|
|
6287
6287
|
"node_modules/unified": {
|