zudoku 0.85.0 → 0.86.0
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/cli/cli.js +1422 -275
- package/dist/cli/worker.js +25 -1
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +116 -4
- package/dist/declarations/lib/util/contentNegotiation.d.ts +3 -0
- package/dist/declarations/lib/util/markdown-representation.d.ts +11 -0
- package/dist/flat-config.d.ts +20 -0
- package/docs/configuration/api-reference.md +45 -0
- package/docs/configuration/docs.md +56 -1
- package/docs/configuration/llms.md +39 -4
- package/docs/configuration/navigation.mdx +4 -4
- package/docs/deploy/apache-nginx.md +20 -6
- package/docs/deploy/vercel.md +64 -21
- package/docs/deployment.md +12 -0
- package/package.json +1 -1
- package/src/app/entry.server.tsx +94 -10
- package/src/config/validators/ZudokuConfig.ts +51 -0
- package/src/lib/util/contentNegotiation.ts +265 -0
- package/src/lib/util/markdown-representation.ts +113 -0
- package/src/types.d.ts +5 -0
- package/src/vite/api/SchemaManager.ts +67 -2
- package/src/vite/api/agent-quality.ts +286 -0
- package/src/vite/api/openapi-publication.ts +162 -0
- package/src/vite/build.ts +19 -3
- package/src/vite/llms.ts +66 -15
- package/src/vite/output.ts +179 -101
- package/src/vite/plugin-api.ts +23 -26
- package/src/vite/plugin-markdown-export.ts +166 -58
- package/src/vite/prerender/prerender.ts +26 -11
- package/src/vite/prerender/utils.ts +11 -0
- package/src/vite/vercel-markdown-middleware.ts +375 -0
package/dist/cli/cli.js
CHANGED
|
@@ -53,13 +53,90 @@ var init_joinUrl = __esm({
|
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
// src/lib/util/markdown.ts
|
|
57
|
+
var getMarkdownPathname;
|
|
58
|
+
var init_markdown = __esm({
|
|
59
|
+
"src/lib/util/markdown.ts"() {
|
|
60
|
+
getMarkdownPathname = (pathname) => pathname === "/" ? "/index" : pathname;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// src/lib/util/markdown-representation.ts
|
|
65
|
+
var encodePathSegment, encodeDocumentationRoutePath, resolveDocumentationRoutePath, getMarkdownRepresentationPath, getMarkdownAlternateLink, appendLinkHeader, getMarkdownNotFound;
|
|
66
|
+
var init_markdown_representation = __esm({
|
|
67
|
+
"src/lib/util/markdown-representation.ts"() {
|
|
68
|
+
init_joinUrl();
|
|
69
|
+
init_markdown();
|
|
70
|
+
encodePathSegment = (segment) => {
|
|
71
|
+
try {
|
|
72
|
+
return encodeURIComponent(decodeURIComponent(segment));
|
|
73
|
+
} catch {
|
|
74
|
+
return encodeURIComponent(segment);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
encodeDocumentationRoutePath = (routePath) => {
|
|
78
|
+
const encodedPath = routePath.split("/").filter(Boolean).map(encodePathSegment).join("/");
|
|
79
|
+
return encodedPath ? `/${encodedPath}` : "/";
|
|
80
|
+
};
|
|
81
|
+
resolveDocumentationRoutePath = (requestUrl, basePath) => {
|
|
82
|
+
const pathname = new URL(requestUrl, "http://localhost").pathname;
|
|
83
|
+
const normalizedBasePath = encodeDocumentationRoutePath(joinUrl(basePath));
|
|
84
|
+
if (normalizedBasePath !== "/" && pathname !== normalizedBasePath && !pathname.startsWith(`${normalizedBasePath}/`)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const relativePath = normalizedBasePath === "/" ? pathname : pathname.slice(normalizedBasePath.length) || "/";
|
|
88
|
+
return encodeDocumentationRoutePath(relativePath);
|
|
89
|
+
};
|
|
90
|
+
getMarkdownRepresentationPath = (routePath, basePath) => joinUrl(
|
|
91
|
+
encodeDocumentationRoutePath(joinUrl(basePath)),
|
|
92
|
+
`${getMarkdownPathname(encodeDocumentationRoutePath(routePath))}.md`
|
|
93
|
+
);
|
|
94
|
+
getMarkdownAlternateLink = (routePath, basePath) => `<${getMarkdownRepresentationPath(routePath, basePath)}>; rel="alternate"; type="text/markdown"`;
|
|
95
|
+
appendLinkHeader = (currentValue, linkValue) => {
|
|
96
|
+
const normalizedValue = Array.isArray(currentValue) ? currentValue.join(", ") : currentValue?.toString().trim();
|
|
97
|
+
return normalizedValue ? `${normalizedValue}, ${linkValue}` : linkValue;
|
|
98
|
+
};
|
|
99
|
+
getMarkdownNotFound = ({
|
|
100
|
+
basePath,
|
|
101
|
+
includeLlmsTxt,
|
|
102
|
+
markdownRoutePaths,
|
|
103
|
+
sitemapOutDir
|
|
104
|
+
}) => {
|
|
105
|
+
const encodedBasePath = encodeDocumentationRoutePath(joinUrl(basePath));
|
|
106
|
+
const markdownEntryRoute = markdownRoutePaths.includes("/") ? "/" : markdownRoutePaths[0];
|
|
107
|
+
const links = [
|
|
108
|
+
`[Documentation home](${encodedBasePath})`,
|
|
109
|
+
...markdownEntryRoute ? [
|
|
110
|
+
`[Markdown documentation index](${getMarkdownRepresentationPath(markdownEntryRoute, basePath)})`
|
|
111
|
+
] : [],
|
|
112
|
+
...includeLlmsTxt ? [`[Agent documentation index](${joinUrl(encodedBasePath, "llms.txt")})`] : [],
|
|
113
|
+
...sitemapOutDir !== void 0 ? [
|
|
114
|
+
`[Sitemap](${joinUrl(
|
|
115
|
+
encodedBasePath,
|
|
116
|
+
encodeDocumentationRoutePath(sitemapOutDir),
|
|
117
|
+
"sitemap.xml"
|
|
118
|
+
)})`
|
|
119
|
+
] : []
|
|
120
|
+
];
|
|
121
|
+
return [
|
|
122
|
+
"# Page not found",
|
|
123
|
+
"",
|
|
124
|
+
"The requested documentation page does not exist. Try one of these entry points:",
|
|
125
|
+
"",
|
|
126
|
+
...links.map((link) => `- ${link}`),
|
|
127
|
+
""
|
|
128
|
+
].join("\n");
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
56
133
|
// src/vite/llms.ts
|
|
57
134
|
var llms_exports = {};
|
|
58
135
|
__export(llms_exports, {
|
|
59
136
|
generateLlmsTxtFiles: () => generateLlmsTxtFiles
|
|
60
137
|
});
|
|
61
138
|
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
62
|
-
import
|
|
139
|
+
import path22 from "node:path";
|
|
63
140
|
import colors6 from "picocolors";
|
|
64
141
|
async function generateLlmsTxtFiles({
|
|
65
142
|
markdownFileInfos,
|
|
@@ -69,32 +146,44 @@ async function generateLlmsTxtFiles({
|
|
|
69
146
|
siteName,
|
|
70
147
|
llmsTxt,
|
|
71
148
|
llmsTxtFull,
|
|
149
|
+
title: configuredTitle,
|
|
150
|
+
description: configuredDescription,
|
|
151
|
+
instructions,
|
|
72
152
|
redirectUrls
|
|
73
153
|
}) {
|
|
74
154
|
const nonRedirectUrls = outputUrls.filter((url) => !redirectUrls.has(url));
|
|
75
155
|
const baseUrl = basePath ?? "";
|
|
76
|
-
const title = siteName ??
|
|
156
|
+
const title = toSingleLine(configuredTitle ?? siteName ?? DEFAULT_TITLE);
|
|
157
|
+
const description = toSingleLine(
|
|
158
|
+
configuredDescription ?? DEFAULT_DESCRIPTION
|
|
159
|
+
);
|
|
160
|
+
const formattedInstructions = instructions ? formatInstructions(instructions) : void 0;
|
|
77
161
|
const markdownMap = new Map(
|
|
78
162
|
markdownFileInfos.map((info) => [info.routePath, info])
|
|
79
163
|
);
|
|
80
164
|
if (llmsTxt) {
|
|
81
|
-
const
|
|
82
|
-
llmsTxtParts.push(`# ${title}
|
|
83
|
-
`);
|
|
84
|
-
llmsTxtParts.push("> Documentation files for Large Language Models\n");
|
|
85
|
-
llmsTxtParts.push("## Documentation\n");
|
|
165
|
+
const documentationLinks = [];
|
|
86
166
|
for (const url of nonRedirectUrls) {
|
|
87
167
|
if (/(400|404|500)$/.test(url)) continue;
|
|
88
168
|
const mdInfo = markdownMap.get(url);
|
|
89
169
|
if (mdInfo) {
|
|
90
|
-
const mdUrl =
|
|
91
|
-
const linkTitle = mdInfo.title ?? url;
|
|
92
|
-
const
|
|
93
|
-
|
|
170
|
+
const mdUrl = getMarkdownRepresentationPath(url, basePath);
|
|
171
|
+
const linkTitle = escapeMarkdownLinkLabel(mdInfo.title ?? url);
|
|
172
|
+
const linkDescription = mdInfo.description ? `: ${toSingleLine(mdInfo.description)}` : "";
|
|
173
|
+
documentationLinks.push(`- [${linkTitle}](${mdUrl})${linkDescription}`);
|
|
94
174
|
}
|
|
95
175
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
176
|
+
const llmsTxtParts = [
|
|
177
|
+
`# ${title}`,
|
|
178
|
+
`> ${description}`,
|
|
179
|
+
...formattedInstructions ? [formattedInstructions] : [],
|
|
180
|
+
...documentationLinks.length > 0 ? ["## Documentation", documentationLinks.join("\n")] : []
|
|
181
|
+
];
|
|
182
|
+
await writeFile5(
|
|
183
|
+
path22.join(baseOutputDir, "llms.txt"),
|
|
184
|
+
llmsTxtParts.join("\n\n"),
|
|
185
|
+
"utf-8"
|
|
186
|
+
);
|
|
98
187
|
console.log(colors6.blue("\u2713 generated llms.txt"));
|
|
99
188
|
}
|
|
100
189
|
if (llmsTxtFull) {
|
|
@@ -112,24 +201,35 @@ async function generateLlmsTxtFiles({
|
|
|
112
201
|
llmsFullParts.push(`${info.description}
|
|
113
202
|
`);
|
|
114
203
|
}
|
|
115
|
-
llmsFullParts.push(
|
|
116
|
-
`)
|
|
204
|
+
llmsFullParts.push(
|
|
205
|
+
`URL: ${joinUrl(baseUrl, encodeDocumentationRoutePath(info.routePath))}
|
|
206
|
+
`
|
|
207
|
+
);
|
|
117
208
|
llmsFullParts.push(`
|
|
118
209
|
${info.content}
|
|
119
210
|
`);
|
|
120
211
|
}
|
|
121
212
|
const llmsFull = llmsFullParts.join("\n");
|
|
122
213
|
await writeFile5(
|
|
123
|
-
|
|
214
|
+
path22.join(baseOutputDir, "llms-full.txt"),
|
|
124
215
|
llmsFull,
|
|
125
216
|
"utf-8"
|
|
126
217
|
);
|
|
127
218
|
console.log(colors6.blue("\u2713 generated llms-full.txt"));
|
|
128
219
|
}
|
|
129
220
|
}
|
|
221
|
+
var DEFAULT_TITLE, DEFAULT_DESCRIPTION, toSingleLine, escapeMarkdownLinkLabel, formatInstructions;
|
|
130
222
|
var init_llms = __esm({
|
|
131
223
|
"src/vite/llms.ts"() {
|
|
132
224
|
init_joinUrl();
|
|
225
|
+
init_markdown_representation();
|
|
226
|
+
DEFAULT_TITLE = "Documentation";
|
|
227
|
+
DEFAULT_DESCRIPTION = "Documentation files for Large Language Models";
|
|
228
|
+
toSingleLine = (value) => value.replaceAll(/\s+/g, " ").trim();
|
|
229
|
+
escapeMarkdownLinkLabel = (value) => toSingleLine(value).replaceAll(/([\\[\]])/g, "\\$1");
|
|
230
|
+
formatInstructions = (value) => value.trim().replaceAll("\r\n", "\n").split("\n").map(
|
|
231
|
+
(line) => line.replace(/^( {0,3})(#{1,6})(?=\s|$)/, "$1\\$2").replace(/^( {0,3})([=-]{2,})\s*$/, "$1\\$2")
|
|
232
|
+
).join("\n");
|
|
133
233
|
}
|
|
134
234
|
});
|
|
135
235
|
|
|
@@ -138,12 +238,12 @@ import { hideBin } from "yargs/helpers";
|
|
|
138
238
|
import yargs from "yargs/yargs";
|
|
139
239
|
|
|
140
240
|
// src/cli/build/handler.ts
|
|
141
|
-
import
|
|
241
|
+
import path27 from "node:path";
|
|
142
242
|
|
|
143
243
|
// src/vite/build.ts
|
|
144
|
-
import { existsSync as
|
|
145
|
-
import { mkdir as mkdir6, readFile as readFile5, rename as rename2, rm as
|
|
146
|
-
import
|
|
244
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
245
|
+
import { mkdir as mkdir6, readFile as readFile5, rename as rename2, rm as rm4, writeFile as writeFile6 } from "node:fs/promises";
|
|
246
|
+
import path25 from "node:path";
|
|
147
247
|
import { build as esbuild } from "esbuild";
|
|
148
248
|
import { createBuilder } from "vite";
|
|
149
249
|
|
|
@@ -320,7 +420,7 @@ var ZudokuError = class extends Error {
|
|
|
320
420
|
|
|
321
421
|
// src/config/file-exists.ts
|
|
322
422
|
import { stat } from "node:fs/promises";
|
|
323
|
-
var fileExists = (
|
|
423
|
+
var fileExists = (path32) => stat(path32).then(() => true).catch(() => false);
|
|
324
424
|
|
|
325
425
|
// src/config/plugin-versions.ts
|
|
326
426
|
import { readFile } from "node:fs/promises";
|
|
@@ -3067,8 +3167,8 @@ import { createNanoEvents } from "nanoevents";
|
|
|
3067
3167
|
|
|
3068
3168
|
// src/lib/util/url.ts
|
|
3069
3169
|
import { matchPath } from "react-router";
|
|
3070
|
-
var matchesProtectedPattern = (pattern,
|
|
3071
|
-
var matchesAnyProtectedPattern = (patterns,
|
|
3170
|
+
var matchesProtectedPattern = (pattern, path32) => matchPath({ path: pattern, end: true }, path32) != null;
|
|
3171
|
+
var matchesAnyProtectedPattern = (patterns, path32) => patterns.some((p) => matchesProtectedPattern(p, path32));
|
|
3072
3172
|
|
|
3073
3173
|
// src/lib/core/ZudokuContext.ts
|
|
3074
3174
|
var normalizeProtectedRoutes = (val) => {
|
|
@@ -3170,6 +3270,16 @@ var ApiConfigSchema = z7.object({
|
|
|
3170
3270
|
categories: z7.array(ApiCatalogCategorySchema),
|
|
3171
3271
|
options: ApiOptionsSchema
|
|
3172
3272
|
}).partial();
|
|
3273
|
+
var OpenApiPublicationSchema = z7.object({
|
|
3274
|
+
path: z7.string().regex(
|
|
3275
|
+
/^\/(?!\/)(?:[A-Za-z0-9._~-]+\/)*[A-Za-z0-9._~-]+\.(?:json|ya?ml)$/i,
|
|
3276
|
+
"OpenAPI publication path must be a root-relative .json, .yaml, or .yml path without traversal, query, or fragment components"
|
|
3277
|
+
).refine(
|
|
3278
|
+
(value) => value.split("/").every((segment) => segment !== "." && segment !== ".."),
|
|
3279
|
+
"OpenAPI publication path must not contain traversal segments"
|
|
3280
|
+
),
|
|
3281
|
+
agentQuality: z7.boolean().optional()
|
|
3282
|
+
});
|
|
3173
3283
|
var VersionConfigSchema = z7.object({
|
|
3174
3284
|
path: z7.string(),
|
|
3175
3285
|
input: z7.string(),
|
|
@@ -3187,6 +3297,7 @@ var ApiSchema = z7.discriminatedUnion("type", [
|
|
|
3187
3297
|
z7.string(),
|
|
3188
3298
|
z7.array(z7.union([z7.string(), VersionConfigSchema]))
|
|
3189
3299
|
]),
|
|
3300
|
+
publish: OpenApiPublicationSchema.optional(),
|
|
3190
3301
|
...ApiConfigSchema.shape
|
|
3191
3302
|
}),
|
|
3192
3303
|
z7.object({
|
|
@@ -3301,6 +3412,13 @@ var LlmsConfigSchema = z7.object({
|
|
|
3301
3412
|
),
|
|
3302
3413
|
includeProtected: z7.boolean().default(false).describe(
|
|
3303
3414
|
"When enabled, includes content from protected routes in the generated .md files and llms.txt files. By default, protected routes are excluded."
|
|
3415
|
+
),
|
|
3416
|
+
title: z7.string().trim().min(1).optional().describe(
|
|
3417
|
+
"The project or site name rendered as the llms.txt H1. Defaults to the configured site title."
|
|
3418
|
+
),
|
|
3419
|
+
description: z7.string().trim().min(1).optional().describe("A short project summary rendered as the llms.txt blockquote."),
|
|
3420
|
+
instructions: z7.string().trim().min(1).optional().describe(
|
|
3421
|
+
"Guidance for agents, such as when to use the documentation and how to interpret it. Rendered as introductory prose before the link sections."
|
|
3304
3422
|
)
|
|
3305
3423
|
}).partial().prefault({});
|
|
3306
3424
|
var DocsConfigSchema = z7.object({
|
|
@@ -3308,6 +3426,9 @@ var DocsConfigSchema = z7.object({
|
|
|
3308
3426
|
publishMarkdown: z7.boolean().default(true).describe(
|
|
3309
3427
|
"When enabled, generates .md files for each document during build. Access documents at their URL path with .md extension (e.g., /foo/hello.md). Markdown files are generated without frontmatter."
|
|
3310
3428
|
),
|
|
3429
|
+
contentNegotiation: z7.boolean().optional().describe(
|
|
3430
|
+
"When enabled, serves generated Markdown at canonical document URLs when Accept prefers text/markdown and adds Vary: Accept. Defaults to the value of publishMarkdown."
|
|
3431
|
+
),
|
|
3311
3432
|
defaultOptions: z7.object({
|
|
3312
3433
|
toc: z7.boolean(),
|
|
3313
3434
|
copyPage: z7.boolean().optional(),
|
|
@@ -3321,7 +3442,10 @@ var DocsConfigSchema = z7.object({
|
|
|
3321
3442
|
}).optional()
|
|
3322
3443
|
}).partial().optional(),
|
|
3323
3444
|
llms: LlmsConfigSchema
|
|
3324
|
-
}).
|
|
3445
|
+
}).transform((docs) => ({
|
|
3446
|
+
...docs,
|
|
3447
|
+
contentNegotiation: docs.publishMarkdown && (docs.contentNegotiation ?? true)
|
|
3448
|
+
})).prefault({});
|
|
3325
3449
|
var Redirect = z7.object({
|
|
3326
3450
|
from: z7.string(),
|
|
3327
3451
|
to: z7.string()
|
|
@@ -3956,7 +4080,7 @@ var getIssuer = async (config) => {
|
|
|
3956
4080
|
init_joinUrl();
|
|
3957
4081
|
|
|
3958
4082
|
// src/vite/config.ts
|
|
3959
|
-
import
|
|
4083
|
+
import path18 from "node:path";
|
|
3960
4084
|
import dotenv from "dotenv";
|
|
3961
4085
|
import colors4 from "picocolors";
|
|
3962
4086
|
import {
|
|
@@ -4175,8 +4299,8 @@ var viteApiKeysPlugin = () => {
|
|
|
4175
4299
|
var plugin_api_keys_default = viteApiKeysPlugin;
|
|
4176
4300
|
|
|
4177
4301
|
// src/vite/plugin-api.ts
|
|
4178
|
-
import
|
|
4179
|
-
import
|
|
4302
|
+
import fs3 from "node:fs/promises";
|
|
4303
|
+
import path13 from "node:path";
|
|
4180
4304
|
import { deepEqual as deepEqual2 } from "fast-equals";
|
|
4181
4305
|
import { runnerImport as runnerImport3 } from "vite";
|
|
4182
4306
|
import { parse as parseYaml } from "yaml";
|
|
@@ -4328,14 +4452,14 @@ var flattenAllOf = (schema2) => {
|
|
|
4328
4452
|
};
|
|
4329
4453
|
|
|
4330
4454
|
// src/lib/util/traverse.ts
|
|
4331
|
-
var traverse = (specification, transform,
|
|
4332
|
-
const transformed = transform(specification,
|
|
4455
|
+
var traverse = (specification, transform, path32 = []) => {
|
|
4456
|
+
const transformed = transform(specification, path32);
|
|
4333
4457
|
if (typeof transformed !== "object" || transformed === null) {
|
|
4334
4458
|
return transformed;
|
|
4335
4459
|
}
|
|
4336
4460
|
const result = Array.isArray(transformed) ? [] : {};
|
|
4337
4461
|
for (const [key, value] of Object.entries(transformed)) {
|
|
4338
|
-
const currentPath = [...
|
|
4462
|
+
const currentPath = [...path32, key];
|
|
4339
4463
|
if (Array.isArray(value)) {
|
|
4340
4464
|
result[key] = value.map(
|
|
4341
4465
|
(item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
|
|
@@ -4363,9 +4487,9 @@ var resolveLocalRef = (schema2, ref) => {
|
|
|
4363
4487
|
if (schemaCache?.has(ref)) {
|
|
4364
4488
|
return schemaCache.get(ref);
|
|
4365
4489
|
}
|
|
4366
|
-
const
|
|
4490
|
+
const path32 = ref.split("/").slice(1);
|
|
4367
4491
|
let current = schema2;
|
|
4368
|
-
for (const segment of
|
|
4492
|
+
for (const segment of path32) {
|
|
4369
4493
|
if (!current || typeof current !== "object") {
|
|
4370
4494
|
current = null;
|
|
4371
4495
|
}
|
|
@@ -4384,7 +4508,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
4384
4508
|
}
|
|
4385
4509
|
const cloned = structuredClone(schema2);
|
|
4386
4510
|
const visited = /* @__PURE__ */ new Set();
|
|
4387
|
-
const resolve = async (current,
|
|
4511
|
+
const resolve = async (current, path32) => {
|
|
4388
4512
|
if (isIndexableObject(current)) {
|
|
4389
4513
|
if (visited.has(current)) {
|
|
4390
4514
|
return CIRCULAR_REF;
|
|
@@ -4392,7 +4516,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
4392
4516
|
visited.add(current);
|
|
4393
4517
|
if (Array.isArray(current)) {
|
|
4394
4518
|
for (let index = 0; index < current.length; index++) {
|
|
4395
|
-
current[index] = await resolve(current[index], `${
|
|
4519
|
+
current[index] = await resolve(current[index], `${path32}/${index}`);
|
|
4396
4520
|
}
|
|
4397
4521
|
} else {
|
|
4398
4522
|
if ("$ref" in current && typeof current.$ref === "string") {
|
|
@@ -4403,13 +4527,13 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
4403
4527
|
for (const resolver of resolvers) {
|
|
4404
4528
|
const resolved = await resolver($ref);
|
|
4405
4529
|
if (resolved) {
|
|
4406
|
-
result2 = await resolve(resolved,
|
|
4530
|
+
result2 = await resolve(resolved, path32);
|
|
4407
4531
|
break;
|
|
4408
4532
|
}
|
|
4409
4533
|
}
|
|
4410
4534
|
if (result2 === void 0) {
|
|
4411
4535
|
const resolved = await resolveLocalRef(cloned, $ref);
|
|
4412
|
-
result2 = await resolve(resolved,
|
|
4536
|
+
result2 = await resolve(resolved, path32);
|
|
4413
4537
|
}
|
|
4414
4538
|
if (hasSiblings) {
|
|
4415
4539
|
if (result2 === CIRCULAR_REF) {
|
|
@@ -4422,7 +4546,7 @@ var dereference = async (schema2, resolvers = []) => {
|
|
|
4422
4546
|
return result2;
|
|
4423
4547
|
}
|
|
4424
4548
|
for (const key in current) {
|
|
4425
|
-
current[key] = await resolve(current[key], `${
|
|
4549
|
+
current[key] = await resolve(current[key], `${path32}/${key}`);
|
|
4426
4550
|
}
|
|
4427
4551
|
}
|
|
4428
4552
|
visited.delete(current);
|
|
@@ -4461,9 +4585,9 @@ var upgradeSchema = (schema2) => {
|
|
|
4461
4585
|
}
|
|
4462
4586
|
return sub;
|
|
4463
4587
|
});
|
|
4464
|
-
schema2 = traverse(schema2, (sub,
|
|
4588
|
+
schema2 = traverse(schema2, (sub, path32) => {
|
|
4465
4589
|
if (sub.example !== void 0) {
|
|
4466
|
-
if (isSchemaPath(
|
|
4590
|
+
if (isSchemaPath(path32 ?? [])) {
|
|
4467
4591
|
sub.examples = [sub.example];
|
|
4468
4592
|
} else {
|
|
4469
4593
|
sub.examples = {
|
|
@@ -4476,11 +4600,11 @@ var upgradeSchema = (schema2) => {
|
|
|
4476
4600
|
}
|
|
4477
4601
|
return sub;
|
|
4478
4602
|
});
|
|
4479
|
-
schema2 = traverse(schema2, (schema3,
|
|
4603
|
+
schema2 = traverse(schema2, (schema3, path32) => {
|
|
4480
4604
|
if (schema3.type === "object" && schema3.properties !== void 0) {
|
|
4481
|
-
const parentPath =
|
|
4605
|
+
const parentPath = path32?.slice(0, -1);
|
|
4482
4606
|
const isMultipart = parentPath?.some((segment, index) => {
|
|
4483
|
-
return segment === "content" &&
|
|
4607
|
+
return segment === "content" && path32?.[index + 1] === "multipart/form-data";
|
|
4484
4608
|
});
|
|
4485
4609
|
if (isMultipart) {
|
|
4486
4610
|
const entries = Object.entries(schema3.properties);
|
|
@@ -4494,8 +4618,8 @@ var upgradeSchema = (schema2) => {
|
|
|
4494
4618
|
}
|
|
4495
4619
|
return schema3;
|
|
4496
4620
|
});
|
|
4497
|
-
schema2 = traverse(schema2, (schema3,
|
|
4498
|
-
if (
|
|
4621
|
+
schema2 = traverse(schema2, (schema3, path32) => {
|
|
4622
|
+
if (path32?.includes("content") && path32.includes("application/octet-stream")) {
|
|
4499
4623
|
return {};
|
|
4500
4624
|
}
|
|
4501
4625
|
if (schema3.type === "string" && schema3.format === "binary") {
|
|
@@ -4521,11 +4645,11 @@ var upgradeSchema = (schema2) => {
|
|
|
4521
4645
|
}
|
|
4522
4646
|
return sub;
|
|
4523
4647
|
});
|
|
4524
|
-
schema2 = traverse(schema2, (schema3,
|
|
4648
|
+
schema2 = traverse(schema2, (schema3, path32) => {
|
|
4525
4649
|
if (schema3.type === "string" && schema3.format === "byte") {
|
|
4526
|
-
const parentPath =
|
|
4650
|
+
const parentPath = path32?.slice(0, -1);
|
|
4527
4651
|
const contentMediaType = parentPath?.find(
|
|
4528
|
-
(_, index) =>
|
|
4652
|
+
(_, index) => path32?.[index - 1] === "content"
|
|
4529
4653
|
);
|
|
4530
4654
|
return {
|
|
4531
4655
|
type: "string",
|
|
@@ -4537,7 +4661,7 @@ var upgradeSchema = (schema2) => {
|
|
|
4537
4661
|
});
|
|
4538
4662
|
return schema2;
|
|
4539
4663
|
};
|
|
4540
|
-
function isSchemaPath(
|
|
4664
|
+
function isSchemaPath(path32) {
|
|
4541
4665
|
const schemaLocations = [
|
|
4542
4666
|
["components", "schemas"],
|
|
4543
4667
|
"properties",
|
|
@@ -4550,10 +4674,10 @@ function isSchemaPath(path31) {
|
|
|
4550
4674
|
];
|
|
4551
4675
|
return schemaLocations.some((location) => {
|
|
4552
4676
|
if (Array.isArray(location)) {
|
|
4553
|
-
return location.every((segment, index) =>
|
|
4677
|
+
return location.every((segment, index) => path32[index] === segment);
|
|
4554
4678
|
}
|
|
4555
|
-
return
|
|
4556
|
-
}) ||
|
|
4679
|
+
return path32.includes(location);
|
|
4680
|
+
}) || path32.includes("schema") || path32.some((segment) => segment.endsWith("Schema"));
|
|
4557
4681
|
}
|
|
4558
4682
|
|
|
4559
4683
|
// src/lib/oas/parser/index.ts
|
|
@@ -4635,13 +4759,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
|
|
|
4635
4759
|
"anyOf",
|
|
4636
4760
|
"oneOf"
|
|
4637
4761
|
]);
|
|
4638
|
-
var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(),
|
|
4762
|
+
var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path32 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
|
|
4639
4763
|
if (obj === null || typeof obj !== "object") return obj;
|
|
4640
4764
|
const refPath = obj.__$ref;
|
|
4641
4765
|
const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
|
|
4642
4766
|
if (isCircular) {
|
|
4643
4767
|
if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
|
|
4644
|
-
const circularProp =
|
|
4768
|
+
const circularProp = path32.find((p) => !OPENAPI_PROPS.has(p)) || path32[0];
|
|
4645
4769
|
return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
|
|
4646
4770
|
}
|
|
4647
4771
|
if (refs.has(obj)) return refs.get(obj);
|
|
@@ -4651,7 +4775,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
|
|
|
4651
4775
|
value,
|
|
4652
4776
|
currentPath,
|
|
4653
4777
|
refs,
|
|
4654
|
-
[...
|
|
4778
|
+
[...path32, key],
|
|
4655
4779
|
currentRefPaths
|
|
4656
4780
|
);
|
|
4657
4781
|
const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
|
|
@@ -4689,7 +4813,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
|
|
|
4689
4813
|
var getAllTags = (schema2) => {
|
|
4690
4814
|
const rootTags = schema2.tags ?? [];
|
|
4691
4815
|
const operations = Object.values(schema2.paths ?? {}).flatMap(
|
|
4692
|
-
(
|
|
4816
|
+
(path32) => HttpMethods.map((k) => path32?.[k]).filter((op) => op != null)
|
|
4693
4817
|
);
|
|
4694
4818
|
const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
|
|
4695
4819
|
const hasUntaggedOperations = operations.some(
|
|
@@ -4744,7 +4868,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
|
|
|
4744
4868
|
var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
|
|
4745
4869
|
var getAllOperations = (paths) => {
|
|
4746
4870
|
const operations = Object.entries(paths ?? {}).flatMap(
|
|
4747
|
-
([
|
|
4871
|
+
([path32, value]) => HttpMethods.flatMap((method) => {
|
|
4748
4872
|
if (!value?.[method]) return [];
|
|
4749
4873
|
const operation = value[method];
|
|
4750
4874
|
const pathParameters = value.parameters ?? [];
|
|
@@ -4764,7 +4888,7 @@ var getAllOperations = (paths) => {
|
|
|
4764
4888
|
return {
|
|
4765
4889
|
...operation,
|
|
4766
4890
|
method,
|
|
4767
|
-
path:
|
|
4891
|
+
path: path32,
|
|
4768
4892
|
parameters,
|
|
4769
4893
|
servers,
|
|
4770
4894
|
tags: operation.tags ?? []
|
|
@@ -5267,8 +5391,8 @@ var Schema = builder.objectRef("Schema").implement({
|
|
|
5267
5391
|
}),
|
|
5268
5392
|
paths: t.field({
|
|
5269
5393
|
type: [PathItem],
|
|
5270
|
-
resolve: (root) => Object.entries(root.paths ?? {}).map(([
|
|
5271
|
-
path:
|
|
5394
|
+
resolve: (root) => Object.entries(root.paths ?? {}).map(([path32, value]) => ({
|
|
5395
|
+
path: path32,
|
|
5272
5396
|
// biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
|
|
5273
5397
|
methods: Object.keys(value)
|
|
5274
5398
|
}))
|
|
@@ -5398,9 +5522,97 @@ var countMcpServers = (schema2) => operationsOf(schema2).filter(
|
|
|
5398
5522
|
// src/lib/util/ensureArray.ts
|
|
5399
5523
|
var ensureArray = (value) => Array.isArray(value) ? value : [value];
|
|
5400
5524
|
|
|
5401
|
-
// src/vite/api/
|
|
5525
|
+
// src/vite/api/openapi-publication.ts
|
|
5402
5526
|
import fs from "node:fs/promises";
|
|
5403
5527
|
import path8 from "node:path";
|
|
5528
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
5529
|
+
var getExtension = (urlPath) => path8.posix.extname(urlPath).toLowerCase();
|
|
5530
|
+
var getOpenApiMediaType = (urlPath) => getExtension(urlPath) === ".json" ? "application/json" : "application/yaml";
|
|
5531
|
+
var createOpenApiPublication = ({
|
|
5532
|
+
apiPath,
|
|
5533
|
+
urlPath,
|
|
5534
|
+
schema: schema2
|
|
5535
|
+
}) => {
|
|
5536
|
+
const mediaType = getOpenApiMediaType(urlPath);
|
|
5537
|
+
const content = mediaType === "application/json" ? `${JSON.stringify(schema2, null, 2)}
|
|
5538
|
+
` : stringifyYaml(schema2);
|
|
5539
|
+
return { apiPath, urlPath, content, mediaType };
|
|
5540
|
+
};
|
|
5541
|
+
var getRequestPathname = (requestUrl) => {
|
|
5542
|
+
try {
|
|
5543
|
+
return new URL(requestUrl, "http://zudoku.local").pathname;
|
|
5544
|
+
} catch {
|
|
5545
|
+
return void 0;
|
|
5546
|
+
}
|
|
5547
|
+
};
|
|
5548
|
+
var findOpenApiPublication = (requestUrl, publications) => {
|
|
5549
|
+
const pathname = getRequestPathname(requestUrl);
|
|
5550
|
+
return publications.find((publication) => publication.urlPath === pathname);
|
|
5551
|
+
};
|
|
5552
|
+
var createOpenApiDevMiddleware = ({
|
|
5553
|
+
getPublications,
|
|
5554
|
+
getDownloadPathMap
|
|
5555
|
+
}) => async (req, res, next) => {
|
|
5556
|
+
if (!req.url || req.method !== "GET" && req.method !== "HEAD") {
|
|
5557
|
+
return next();
|
|
5558
|
+
}
|
|
5559
|
+
const publication = findOpenApiPublication(req.url, getPublications());
|
|
5560
|
+
if (publication) {
|
|
5561
|
+
res.setHeader("Content-Type", `${publication.mediaType}; charset=utf-8`);
|
|
5562
|
+
return res.end(req.method === "HEAD" ? void 0 : publication.content);
|
|
5563
|
+
}
|
|
5564
|
+
const requestPathname = getRequestPathname(req.url);
|
|
5565
|
+
if (!requestPathname) return next();
|
|
5566
|
+
if (!requestPathname.toLowerCase().endsWith(".json") && !requestPathname.toLowerCase().endsWith(".yaml") && !requestPathname.toLowerCase().endsWith(".yml")) {
|
|
5567
|
+
return next();
|
|
5568
|
+
}
|
|
5569
|
+
const inputPath = getDownloadPathMap().get(requestPathname);
|
|
5570
|
+
if (!inputPath) return next();
|
|
5571
|
+
const content = await fs.readFile(inputPath, "utf-8");
|
|
5572
|
+
const mediaType = getOpenApiMediaType(inputPath);
|
|
5573
|
+
res.setHeader("Content-Type", `${mediaType}; charset=utf-8`);
|
|
5574
|
+
return res.end(req.method === "HEAD" ? void 0 : content);
|
|
5575
|
+
};
|
|
5576
|
+
var resolveOutputPath = (outputDir, urlPath) => {
|
|
5577
|
+
const outputRoot = path8.resolve(outputDir);
|
|
5578
|
+
const relativePath = urlPath.replace(/^\/+/, "");
|
|
5579
|
+
const outputPath = path8.resolve(outputRoot, relativePath);
|
|
5580
|
+
if (!relativePath || relativePath.includes("\\") || relativePath.split("/").some((segment) => segment === "..") || !outputPath.startsWith(`${outputRoot}${path8.sep}`) && outputPath !== outputRoot) {
|
|
5581
|
+
throw new Error(`Unsafe OpenAPI publication output path: ${urlPath}`);
|
|
5582
|
+
}
|
|
5583
|
+
return outputPath;
|
|
5584
|
+
};
|
|
5585
|
+
var writeOpenApiPublications = async (outputDir, publications) => {
|
|
5586
|
+
const outputs = publications.map((publication) => ({
|
|
5587
|
+
publication,
|
|
5588
|
+
outputPath: resolveOutputPath(outputDir, publication.urlPath)
|
|
5589
|
+
}));
|
|
5590
|
+
await Promise.all(
|
|
5591
|
+
outputs.map(async ({ publication, outputPath }) => {
|
|
5592
|
+
try {
|
|
5593
|
+
await fs.lstat(outputPath);
|
|
5594
|
+
} catch (error) {
|
|
5595
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
5596
|
+
return;
|
|
5597
|
+
}
|
|
5598
|
+
throw error;
|
|
5599
|
+
}
|
|
5600
|
+
throw new Error(
|
|
5601
|
+
`Cannot publish OpenAPI for API "${publication.apiPath}" at "${publication.urlPath}" because a build artifact already exists at "${outputPath}". Choose another publish.path or remove the conflicting public/build artifact.`
|
|
5602
|
+
);
|
|
5603
|
+
})
|
|
5604
|
+
);
|
|
5605
|
+
await Promise.all(
|
|
5606
|
+
outputs.map(async ({ publication, outputPath }) => {
|
|
5607
|
+
await fs.mkdir(path8.dirname(outputPath), { recursive: true });
|
|
5608
|
+
await fs.writeFile(outputPath, publication.content, "utf-8");
|
|
5609
|
+
})
|
|
5610
|
+
);
|
|
5611
|
+
};
|
|
5612
|
+
|
|
5613
|
+
// src/vite/api/SchemaManager.ts
|
|
5614
|
+
import fs2 from "node:fs/promises";
|
|
5615
|
+
import path9 from "node:path";
|
|
5404
5616
|
import { $RefParser as $RefParser2 } from "@apidevtools/json-schema-ref-parser";
|
|
5405
5617
|
import { upgrade } from "@scalar/openapi-parser";
|
|
5406
5618
|
import { deepEqual } from "fast-equals";
|
|
@@ -5448,9 +5660,179 @@ var flattenAllOfProcessor = async ({ schema: schema2, file }) => {
|
|
|
5448
5660
|
// src/vite/api/SchemaManager.ts
|
|
5449
5661
|
init_joinUrl();
|
|
5450
5662
|
|
|
5663
|
+
// src/vite/api/agent-quality.ts
|
|
5664
|
+
var HTTP_METHODS2 = [
|
|
5665
|
+
"get",
|
|
5666
|
+
"put",
|
|
5667
|
+
"post",
|
|
5668
|
+
"delete",
|
|
5669
|
+
"options",
|
|
5670
|
+
"head",
|
|
5671
|
+
"patch",
|
|
5672
|
+
"trace"
|
|
5673
|
+
];
|
|
5674
|
+
var REQUEST_BODY_METHODS = /* @__PURE__ */ new Set(["post", "put", "patch"]);
|
|
5675
|
+
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5676
|
+
var resolveLocalRef2 = (document, value) => {
|
|
5677
|
+
if (!isObject(value) || typeof value.$ref !== "string") return value;
|
|
5678
|
+
if (!value.$ref.startsWith("#/")) return void 0;
|
|
5679
|
+
return value.$ref.slice(2).split("/").map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~")).reduce((current, part) => {
|
|
5680
|
+
if (!isObject(current)) return void 0;
|
|
5681
|
+
return current[part];
|
|
5682
|
+
}, document);
|
|
5683
|
+
};
|
|
5684
|
+
var schemaIsTyped = (document, schema2) => {
|
|
5685
|
+
const resolved = resolveLocalRef2(document, schema2);
|
|
5686
|
+
if (typeof resolved === "boolean") return true;
|
|
5687
|
+
if (!isObject(resolved)) return false;
|
|
5688
|
+
return typeof resolved.type === "string" || Array.isArray(resolved.type) || "enum" in resolved || "const" in resolved || "properties" in resolved || "items" in resolved || "oneOf" in resolved || "anyOf" in resolved || "allOf" in resolved;
|
|
5689
|
+
};
|
|
5690
|
+
var contentHasTypedSchema = (document, content) => isObject(content) && Object.values(content).some(
|
|
5691
|
+
(mediaType) => isObject(mediaType) && schemaIsTyped(document, mediaType.schema)
|
|
5692
|
+
);
|
|
5693
|
+
var parameterIsTyped = (document, parameter) => {
|
|
5694
|
+
const resolved = resolveLocalRef2(document, parameter);
|
|
5695
|
+
return isObject(resolved) && (schemaIsTyped(document, resolved.schema) || contentHasTypedSchema(document, resolved.content));
|
|
5696
|
+
};
|
|
5697
|
+
var responseHasTypedSchema = (document, response) => {
|
|
5698
|
+
const resolved = resolveLocalRef2(document, response);
|
|
5699
|
+
return isObject(resolved) && contentHasTypedSchema(document, resolved.content);
|
|
5700
|
+
};
|
|
5701
|
+
var requestBodyHasTypedSchema = (document, requestBody) => {
|
|
5702
|
+
const resolved = resolveLocalRef2(document, requestBody);
|
|
5703
|
+
return isObject(resolved) && contentHasTypedSchema(document, resolved.content);
|
|
5704
|
+
};
|
|
5705
|
+
var getParameterName = (document, parameter) => {
|
|
5706
|
+
const resolved = resolveLocalRef2(document, parameter);
|
|
5707
|
+
return isObject(resolved) && typeof resolved.name === "string" ? resolved.name : "<unknown>";
|
|
5708
|
+
};
|
|
5709
|
+
var getParameterIdentity = (document, parameter) => {
|
|
5710
|
+
const resolved = resolveLocalRef2(document, parameter);
|
|
5711
|
+
if (!isObject(resolved) || typeof resolved.name !== "string" || typeof resolved.in !== "string") {
|
|
5712
|
+
return void 0;
|
|
5713
|
+
}
|
|
5714
|
+
return JSON.stringify([resolved.name, resolved.in]);
|
|
5715
|
+
};
|
|
5716
|
+
var mergeParameters = (document, pathParameters, operationParameters) => {
|
|
5717
|
+
const merged = [];
|
|
5718
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
5719
|
+
for (const parameter of [
|
|
5720
|
+
...Array.isArray(pathParameters) ? pathParameters : [],
|
|
5721
|
+
...Array.isArray(operationParameters) ? operationParameters : []
|
|
5722
|
+
]) {
|
|
5723
|
+
const identity = getParameterIdentity(document, parameter);
|
|
5724
|
+
if (identity === void 0) {
|
|
5725
|
+
merged.push(parameter);
|
|
5726
|
+
continue;
|
|
5727
|
+
}
|
|
5728
|
+
const existingIndex = indexes.get(identity);
|
|
5729
|
+
if (existingIndex === void 0) {
|
|
5730
|
+
indexes.set(identity, merged.length);
|
|
5731
|
+
merged.push(parameter);
|
|
5732
|
+
} else {
|
|
5733
|
+
merged[existingIndex] = parameter;
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5736
|
+
return merged;
|
|
5737
|
+
};
|
|
5738
|
+
var responseCannotHaveContent = (method, status) => method === "head" || /^1(?:\d{2}|xx)$/i.test(status) || ["204", "205", "304"].includes(status);
|
|
5739
|
+
var auditOpenApiAgentQuality = (document) => {
|
|
5740
|
+
const issues = [];
|
|
5741
|
+
const operationIds = /* @__PURE__ */ new Map();
|
|
5742
|
+
if (!isObject(document.paths)) return issues;
|
|
5743
|
+
for (const [route, rawPathItem] of Object.entries(document.paths)) {
|
|
5744
|
+
const pathItem = resolveLocalRef2(document, rawPathItem);
|
|
5745
|
+
if (!isObject(pathItem)) continue;
|
|
5746
|
+
for (const method of HTTP_METHODS2) {
|
|
5747
|
+
const operation = pathItem[method];
|
|
5748
|
+
if (!isObject(operation)) continue;
|
|
5749
|
+
const location = `${method.toUpperCase()} ${route}`;
|
|
5750
|
+
if (typeof operation.operationId !== "string" || operation.operationId.trim().length === 0) {
|
|
5751
|
+
issues.push({
|
|
5752
|
+
code: "missing-operation-id",
|
|
5753
|
+
location,
|
|
5754
|
+
message: "Operation is missing a unique operationId."
|
|
5755
|
+
});
|
|
5756
|
+
} else {
|
|
5757
|
+
const locations = operationIds.get(operation.operationId) ?? [];
|
|
5758
|
+
locations.push(location);
|
|
5759
|
+
operationIds.set(operation.operationId, locations);
|
|
5760
|
+
}
|
|
5761
|
+
if (typeof operation.description !== "string" || operation.description.trim().length === 0) {
|
|
5762
|
+
issues.push({
|
|
5763
|
+
code: "missing-description",
|
|
5764
|
+
location,
|
|
5765
|
+
message: "Operation is missing a description."
|
|
5766
|
+
});
|
|
5767
|
+
}
|
|
5768
|
+
const parameters = mergeParameters(
|
|
5769
|
+
document,
|
|
5770
|
+
pathItem.parameters,
|
|
5771
|
+
operation.parameters
|
|
5772
|
+
);
|
|
5773
|
+
for (const parameter of parameters) {
|
|
5774
|
+
if (parameterIsTyped(document, parameter)) continue;
|
|
5775
|
+
issues.push({
|
|
5776
|
+
code: "untyped-parameter",
|
|
5777
|
+
location,
|
|
5778
|
+
message: `Parameter "${getParameterName(document, parameter)}" is missing a typed schema.`
|
|
5779
|
+
});
|
|
5780
|
+
}
|
|
5781
|
+
if (operation.requestBody === void 0) {
|
|
5782
|
+
if (REQUEST_BODY_METHODS.has(method)) {
|
|
5783
|
+
issues.push({
|
|
5784
|
+
code: "missing-request-body",
|
|
5785
|
+
location,
|
|
5786
|
+
message: "Write operation is missing a requestBody schema."
|
|
5787
|
+
});
|
|
5788
|
+
}
|
|
5789
|
+
} else if (!requestBodyHasTypedSchema(document, operation.requestBody)) {
|
|
5790
|
+
issues.push({
|
|
5791
|
+
code: "untyped-request-body",
|
|
5792
|
+
location,
|
|
5793
|
+
message: "Request body is missing a typed schema."
|
|
5794
|
+
});
|
|
5795
|
+
}
|
|
5796
|
+
const responses = operation.responses;
|
|
5797
|
+
if (!isObject(responses) || Object.keys(responses).length === 0) {
|
|
5798
|
+
issues.push({
|
|
5799
|
+
code: "missing-response-schema",
|
|
5800
|
+
location,
|
|
5801
|
+
message: "Operation has no response schemas."
|
|
5802
|
+
});
|
|
5803
|
+
continue;
|
|
5804
|
+
}
|
|
5805
|
+
for (const [status, response] of Object.entries(responses)) {
|
|
5806
|
+
if (responseCannotHaveContent(method, status)) continue;
|
|
5807
|
+
if (responseHasTypedSchema(document, response)) continue;
|
|
5808
|
+
issues.push({
|
|
5809
|
+
code: "missing-response-schema",
|
|
5810
|
+
location,
|
|
5811
|
+
message: `Response ${status} is missing a typed schema.`
|
|
5812
|
+
});
|
|
5813
|
+
}
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
for (const [operationId, locations] of operationIds) {
|
|
5817
|
+
if (locations.length < 2) continue;
|
|
5818
|
+
issues.push({
|
|
5819
|
+
code: "duplicate-operation-id",
|
|
5820
|
+
location: locations.join(", "),
|
|
5821
|
+
message: `operationId "${operationId}" is used by multiple operations.`
|
|
5822
|
+
});
|
|
5823
|
+
}
|
|
5824
|
+
return issues;
|
|
5825
|
+
};
|
|
5826
|
+
var formatAgentQualityReport = (apiPath, issues) => [
|
|
5827
|
+
`Agent-quality audit for API "${apiPath}" found ${issues.length} issue${issues.length === 1 ? "" : "s"}:`,
|
|
5828
|
+
...issues.map(
|
|
5829
|
+
(issue) => `- [${issue.code}] ${issue.location}: ${issue.message}`
|
|
5830
|
+
)
|
|
5831
|
+
].join("\n");
|
|
5832
|
+
|
|
5451
5833
|
// src/vite/api/schema-codegen.ts
|
|
5452
5834
|
var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
5453
|
-
var getSegmentsFromPath = (
|
|
5835
|
+
var getSegmentsFromPath = (path32) => path32.split("/").slice(1).map(unescapeJsonPointer);
|
|
5454
5836
|
var createLocalRefMap = (obj) => {
|
|
5455
5837
|
const refMap = /* @__PURE__ */ new Map();
|
|
5456
5838
|
const siblingsMap = /* @__PURE__ */ new Map();
|
|
@@ -5481,16 +5863,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
|
|
|
5481
5863
|
/"__refMap\+Siblings:(.*?)"/g,
|
|
5482
5864
|
(_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
|
|
5483
5865
|
);
|
|
5484
|
-
var lookup = (schema2,
|
|
5485
|
-
const parts = getSegmentsFromPath(
|
|
5866
|
+
var lookup = (schema2, path32, filePath) => {
|
|
5867
|
+
const parts = getSegmentsFromPath(path32);
|
|
5486
5868
|
let val = schema2;
|
|
5487
5869
|
for (const part of parts) {
|
|
5488
5870
|
while (val.$ref?.startsWith("#/")) {
|
|
5489
|
-
val = val.$ref ===
|
|
5871
|
+
val = val.$ref === path32 ? val : lookup(schema2, val.$ref, filePath);
|
|
5490
5872
|
}
|
|
5491
5873
|
if (val[part] === void 0) {
|
|
5492
5874
|
throw new Error(
|
|
5493
|
-
`Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${
|
|
5875
|
+
`Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path32}`
|
|
5494
5876
|
);
|
|
5495
5877
|
}
|
|
5496
5878
|
val = val[part];
|
|
@@ -5573,6 +5955,7 @@ var SchemaManager = class {
|
|
|
5573
5955
|
processors;
|
|
5574
5956
|
processedSchemas = {};
|
|
5575
5957
|
referencedBy = /* @__PURE__ */ new Map();
|
|
5958
|
+
publishedSchemas;
|
|
5576
5959
|
config;
|
|
5577
5960
|
constructor({
|
|
5578
5961
|
storeDir,
|
|
@@ -5594,18 +5977,18 @@ var SchemaManager = class {
|
|
|
5594
5977
|
];
|
|
5595
5978
|
}
|
|
5596
5979
|
getPathForFile = (input, params) => {
|
|
5597
|
-
const filePath =
|
|
5980
|
+
const filePath = path9.resolve(this.config.__meta.rootDir, input);
|
|
5598
5981
|
const apis = ensureArray(this.config.apis ?? []);
|
|
5599
5982
|
for (const apiConfig of apis) {
|
|
5600
|
-
if (
|
|
5983
|
+
if (apiConfig?.type !== "file" || !apiConfig.path) continue;
|
|
5601
5984
|
const match = normalizeInputs(apiConfig.input).some(
|
|
5602
|
-
(i) =>
|
|
5985
|
+
(i) => path9.resolve(this.config.__meta.rootDir, i.input) === filePath && deepEqual(i.params, params)
|
|
5603
5986
|
);
|
|
5604
5987
|
if (match) return apiConfig.path;
|
|
5605
5988
|
}
|
|
5606
5989
|
};
|
|
5607
5990
|
processSchema = async (input) => {
|
|
5608
|
-
const filePath =
|
|
5991
|
+
const filePath = path9.resolve(this.config.__meta.rootDir, input.input);
|
|
5609
5992
|
const params = input.params;
|
|
5610
5993
|
const configuredPath = this.getPathForFile(input.input, params);
|
|
5611
5994
|
if (!configuredPath) {
|
|
@@ -5640,15 +6023,15 @@ var SchemaManager = class {
|
|
|
5640
6023
|
const processedTime = Date.now();
|
|
5641
6024
|
const code = generateCode(processedSchema, filePath);
|
|
5642
6025
|
const prefixPath = slugify(configuredPath);
|
|
5643
|
-
const processedFilePath =
|
|
6026
|
+
const processedFilePath = path9.posix.join(
|
|
5644
6027
|
this.storeDir,
|
|
5645
|
-
`${prefixPath}-${
|
|
6028
|
+
`${prefixPath}-${path9.basename(filePath)}${paramsSuffix(params)}.js`
|
|
5646
6029
|
);
|
|
5647
6030
|
const importKey = processedFilePath;
|
|
5648
|
-
await
|
|
6031
|
+
await fs2.writeFile(processedFilePath, code);
|
|
5649
6032
|
const processedJsonPath = paramsSuffix(params) ? processedFilePath.replace(/\.js$/, ".json") : "";
|
|
5650
6033
|
if (processedJsonPath) {
|
|
5651
|
-
await
|
|
6034
|
+
await fs2.writeFile(
|
|
5652
6035
|
processedJsonPath,
|
|
5653
6036
|
JSON.stringify(processedSchema, null, 2)
|
|
5654
6037
|
);
|
|
@@ -5664,7 +6047,7 @@ var SchemaManager = class {
|
|
|
5664
6047
|
const schemaVersion = processedSchema.info.version ?? FALLBACK_VERSION;
|
|
5665
6048
|
const versionPath = existingSchema?.path && existingSchema.path.length > 0 ? existingSchema.path : paramsPath(params) || schemaVersion;
|
|
5666
6049
|
const config = ensureArray(this.config.apis ?? []).find(
|
|
5667
|
-
(c) => c.path === configuredPath
|
|
6050
|
+
(c) => c.type === "file" && c.path === configuredPath
|
|
5668
6051
|
);
|
|
5669
6052
|
const processed = {
|
|
5670
6053
|
schema: processedSchema,
|
|
@@ -5686,16 +6069,25 @@ var SchemaManager = class {
|
|
|
5686
6069
|
};
|
|
5687
6070
|
if (index > -1) {
|
|
5688
6071
|
schemas[index] = processed;
|
|
6072
|
+
this.publishedSchemas = void 0;
|
|
5689
6073
|
} else {
|
|
5690
6074
|
throw new Error(
|
|
5691
6075
|
`Schema with input path ${filePath} was not pre-initialized for ${configuredPath}.`
|
|
5692
6076
|
);
|
|
5693
6077
|
}
|
|
6078
|
+
if (index === 0 && config?.type === "file" && config.publish?.agentQuality) {
|
|
6079
|
+
const issues = auditOpenApiAgentQuality(processedSchema);
|
|
6080
|
+
if (issues.length > 0) {
|
|
6081
|
+
console.warn(
|
|
6082
|
+
`[zudoku] ${formatAgentQualityReport(configuredPath, issues)}`
|
|
6083
|
+
);
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
5694
6086
|
return processed;
|
|
5695
6087
|
};
|
|
5696
6088
|
getAllTrackedFiles = () => Array.from(this.referencedBy.keys());
|
|
5697
6089
|
getFilesToReprocess = (changedFile) => {
|
|
5698
|
-
const resolvedPath =
|
|
6090
|
+
const resolvedPath = path9.resolve(this.config.__meta.rootDir, changedFile);
|
|
5699
6091
|
const referencedBy = this.referencedBy.get(resolvedPath);
|
|
5700
6092
|
if (!referencedBy) return [];
|
|
5701
6093
|
const filesToProcess = referencedBy.size === 0 ? [resolvedPath] : Array.from(referencedBy);
|
|
@@ -5714,6 +6106,7 @@ var SchemaManager = class {
|
|
|
5714
6106
|
processAllSchemas = async () => {
|
|
5715
6107
|
this.referencedBy.clear();
|
|
5716
6108
|
this.processedSchemas = {};
|
|
6109
|
+
this.publishedSchemas = void 0;
|
|
5717
6110
|
const apis = ensureArray(this.config.apis ?? []);
|
|
5718
6111
|
for (const apiConfig of apis) {
|
|
5719
6112
|
if (apiConfig.type !== "file" || !apiConfig.path) continue;
|
|
@@ -5724,7 +6117,7 @@ var SchemaManager = class {
|
|
|
5724
6117
|
version: "",
|
|
5725
6118
|
path: input.path ?? "",
|
|
5726
6119
|
label: input.label,
|
|
5727
|
-
inputPath:
|
|
6120
|
+
inputPath: path9.resolve(this.config.__meta.rootDir, input.input),
|
|
5728
6121
|
params: input.params,
|
|
5729
6122
|
importKey: "",
|
|
5730
6123
|
downloadUrl: "",
|
|
@@ -5743,10 +6136,39 @@ var SchemaManager = class {
|
|
|
5743
6136
|
);
|
|
5744
6137
|
}
|
|
5745
6138
|
}
|
|
6139
|
+
this.getPublishedSchemas();
|
|
5746
6140
|
};
|
|
5747
|
-
getLatestSchema = (
|
|
5748
|
-
getSchemasForPath = (
|
|
6141
|
+
getLatestSchema = (path32) => this.processedSchemas[path32]?.at(0);
|
|
6142
|
+
getSchemasForPath = (path32) => this.processedSchemas[path32];
|
|
5749
6143
|
getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
|
|
6144
|
+
getPublishedSchemas = () => {
|
|
6145
|
+
if (this.publishedSchemas) return this.publishedSchemas;
|
|
6146
|
+
const publications = /* @__PURE__ */ new Map();
|
|
6147
|
+
for (const apiConfig of ensureArray(this.config.apis ?? [])) {
|
|
6148
|
+
if (apiConfig.type !== "file" || !apiConfig.path || !apiConfig.publish) {
|
|
6149
|
+
continue;
|
|
6150
|
+
}
|
|
6151
|
+
const primarySchema = this.getLatestSchema(apiConfig.path);
|
|
6152
|
+
if (!primarySchema) continue;
|
|
6153
|
+
const urlPath = joinUrl(this.config.basePath, apiConfig.publish.path);
|
|
6154
|
+
const existing = publications.get(urlPath);
|
|
6155
|
+
if (existing) {
|
|
6156
|
+
throw new Error(
|
|
6157
|
+
`OpenAPI publication path "${urlPath}" is configured by both "${existing.apiPath}" and "${apiConfig.path}". Configure a unique path for each published API.`
|
|
6158
|
+
);
|
|
6159
|
+
}
|
|
6160
|
+
publications.set(
|
|
6161
|
+
urlPath,
|
|
6162
|
+
createOpenApiPublication({
|
|
6163
|
+
apiPath: apiConfig.path,
|
|
6164
|
+
urlPath,
|
|
6165
|
+
schema: primarySchema.schema
|
|
6166
|
+
})
|
|
6167
|
+
);
|
|
6168
|
+
}
|
|
6169
|
+
this.publishedSchemas = Array.from(publications.values());
|
|
6170
|
+
return this.publishedSchemas;
|
|
6171
|
+
};
|
|
5750
6172
|
getUrlToFilePathMap = () => {
|
|
5751
6173
|
const map = /* @__PURE__ */ new Map();
|
|
5752
6174
|
const apis = ensureArray(this.config.apis ?? []);
|
|
@@ -5767,7 +6189,7 @@ var SchemaManager = class {
|
|
|
5767
6189
|
};
|
|
5768
6190
|
createSchemaPath = (inputPath, versionPath, apiPath, params, config) => {
|
|
5769
6191
|
const suffix = paramsSuffix(params);
|
|
5770
|
-
const extension = suffix ? ".json" :
|
|
6192
|
+
const extension = suffix ? ".json" : path9.extname(inputPath);
|
|
5771
6193
|
const fileName = config?.schemaDownload?.fileName ?? this.config.defaults?.apis?.schemaDownload?.fileName ?? "schema";
|
|
5772
6194
|
return joinUrl(
|
|
5773
6195
|
this.config.basePath,
|
|
@@ -5779,7 +6201,7 @@ var SchemaManager = class {
|
|
|
5779
6201
|
};
|
|
5780
6202
|
|
|
5781
6203
|
// src/vite/plugin-config-reload.ts
|
|
5782
|
-
import
|
|
6204
|
+
import path12 from "node:path";
|
|
5783
6205
|
import colors3 from "picocolors";
|
|
5784
6206
|
|
|
5785
6207
|
// src/vite/plugin-navigation.ts
|
|
@@ -5787,7 +6209,7 @@ import { stringify as stringify3 } from "javascript-stringify";
|
|
|
5787
6209
|
import { isElement } from "react-is";
|
|
5788
6210
|
|
|
5789
6211
|
// src/config/validators/NavigationSchema.ts
|
|
5790
|
-
import
|
|
6212
|
+
import path10 from "node:path";
|
|
5791
6213
|
import { glob } from "glob";
|
|
5792
6214
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
5793
6215
|
import { mdxFromMarkdown } from "mdast-util-mdx";
|
|
@@ -5837,7 +6259,7 @@ var extractRichH1 = (content) => {
|
|
|
5837
6259
|
}
|
|
5838
6260
|
};
|
|
5839
6261
|
var isNavigationItem = (item) => item !== void 0;
|
|
5840
|
-
var toPosixPath = (filePath) => filePath.split(
|
|
6262
|
+
var toPosixPath = (filePath) => filePath.split(path10.win32.sep).join(path10.posix.sep);
|
|
5841
6263
|
var NavigationResolver = class {
|
|
5842
6264
|
rootDir;
|
|
5843
6265
|
globPatterns;
|
|
@@ -5970,13 +6392,13 @@ var NavigationResolver = class {
|
|
|
5970
6392
|
|
|
5971
6393
|
// src/vite/debug.ts
|
|
5972
6394
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
5973
|
-
import
|
|
6395
|
+
import path11 from "node:path";
|
|
5974
6396
|
async function writePluginDebugCode(rootDir, pluginName, code, extension = "js") {
|
|
5975
6397
|
if (process.env.ZUDOKU_BUILD_DEBUG) {
|
|
5976
|
-
const debugDir =
|
|
6398
|
+
const debugDir = path11.join(rootDir, "dist", "debug");
|
|
5977
6399
|
await mkdir(debugDir, { recursive: true });
|
|
5978
6400
|
await writeFile(
|
|
5979
|
-
|
|
6401
|
+
path11.join(debugDir, `${pluginName}.${extension}`),
|
|
5980
6402
|
typeof code === "string" ? code : code.join("\n")
|
|
5981
6403
|
);
|
|
5982
6404
|
}
|
|
@@ -6087,7 +6509,7 @@ var viteConfigReloadPlugin = () => ({
|
|
|
6087
6509
|
});
|
|
6088
6510
|
logger.info(
|
|
6089
6511
|
colors3.blue(
|
|
6090
|
-
`Config ${
|
|
6512
|
+
`Config ${path12.basename(currentConfig.__meta.configPath)} changed. Reloading...`
|
|
6091
6513
|
),
|
|
6092
6514
|
{ timestamp: true }
|
|
6093
6515
|
);
|
|
@@ -6097,6 +6519,7 @@ var viteConfigReloadPlugin = () => ({
|
|
|
6097
6519
|
|
|
6098
6520
|
// src/vite/plugin-api.ts
|
|
6099
6521
|
var PROCESSED_STORE_SUBPATH = "node_modules/.zudoku/processed";
|
|
6522
|
+
var schemaConfigurationChanged = (current, next) => current.basePath !== next.basePath || !deepEqual2(current.apis, next.apis);
|
|
6100
6523
|
var warn = (message) => {
|
|
6101
6524
|
console.warn(`[zudoku] ${message}`);
|
|
6102
6525
|
};
|
|
@@ -6121,11 +6544,11 @@ var viteApiPlugin = async () => {
|
|
|
6121
6544
|
const resolvedVirtualModuleId4 = `\0${virtualModuleId4}`;
|
|
6122
6545
|
const initialConfig = getCurrentConfig();
|
|
6123
6546
|
const zuploProcessors = ZuploEnv.isZuplo ? await runnerImport3(
|
|
6124
|
-
|
|
6547
|
+
path13.resolve(getZudokuRootDir(), "src/zuplo/with-zuplo-processors.ts")
|
|
6125
6548
|
).then((m) => m.module.default(initialConfig.__meta.rootDir)) : [];
|
|
6126
6549
|
const buildConfig = await getBuildConfig();
|
|
6127
6550
|
const buildProcessors = buildConfig?.processors ?? [];
|
|
6128
|
-
const tmpStoreDir =
|
|
6551
|
+
const tmpStoreDir = path13.posix.join(
|
|
6129
6552
|
initialConfig.__meta.rootDir,
|
|
6130
6553
|
PROCESSED_STORE_SUBPATH
|
|
6131
6554
|
);
|
|
@@ -6135,8 +6558,8 @@ var viteApiPlugin = async () => {
|
|
|
6135
6558
|
config: initialConfig,
|
|
6136
6559
|
processors
|
|
6137
6560
|
});
|
|
6138
|
-
await
|
|
6139
|
-
await
|
|
6561
|
+
await fs3.rm(tmpStoreDir, { recursive: true, force: true });
|
|
6562
|
+
await fs3.mkdir(tmpStoreDir, { recursive: true });
|
|
6140
6563
|
await schemaManager.processAllSchemas();
|
|
6141
6564
|
return {
|
|
6142
6565
|
name: "zudoku-api-plugins",
|
|
@@ -6144,19 +6567,12 @@ var viteApiPlugin = async () => {
|
|
|
6144
6567
|
schemaManager.getAllTrackedFiles().forEach((file) => this.addWatchFile(file));
|
|
6145
6568
|
},
|
|
6146
6569
|
configureServer(server) {
|
|
6147
|
-
server.middlewares.use(
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
}
|
|
6152
|
-
|
|
6153
|
-
const inputPath = pathMap.get(req.url);
|
|
6154
|
-
if (!inputPath) return next();
|
|
6155
|
-
const content = await fs2.readFile(inputPath, "utf-8");
|
|
6156
|
-
const mimeType = path12.extname(inputPath).toLowerCase() === ".json" ? "application/json" : "application/x-yaml";
|
|
6157
|
-
res.setHeader("Content-Type", `${mimeType}; charset=utf-8`);
|
|
6158
|
-
return res.end(content);
|
|
6159
|
-
});
|
|
6570
|
+
server.middlewares.use(
|
|
6571
|
+
createOpenApiDevMiddleware({
|
|
6572
|
+
getPublications: () => schemaManager.getPublishedSchemas(),
|
|
6573
|
+
getDownloadPathMap: () => schemaManager.getUrlToFilePathMap()
|
|
6574
|
+
})
|
|
6575
|
+
);
|
|
6160
6576
|
server.watcher.on("change", async (id) => {
|
|
6161
6577
|
const mainFiles = schemaManager.getFilesToReprocess(id);
|
|
6162
6578
|
if (mainFiles.length === 0) return;
|
|
@@ -6193,7 +6609,7 @@ var viteApiPlugin = async () => {
|
|
|
6193
6609
|
async load(id) {
|
|
6194
6610
|
if (id !== resolvedVirtualModuleId4) return;
|
|
6195
6611
|
const config = getCurrentConfig();
|
|
6196
|
-
if (
|
|
6612
|
+
if (schemaConfigurationChanged(schemaManager.config, config)) {
|
|
6197
6613
|
schemaManager.config = config;
|
|
6198
6614
|
await schemaManager.processAllSchemas();
|
|
6199
6615
|
schemaManager.getAllTrackedFiles().forEach((file) => this.addWatchFile(file));
|
|
@@ -6382,11 +6798,15 @@ var viteApiPlugin = async () => {
|
|
|
6382
6798
|
const pathMap = schemaManager.getUrlToFilePathMap();
|
|
6383
6799
|
if (process.env.NODE_ENV !== "production") return;
|
|
6384
6800
|
for (const [urlPath, inputPath] of pathMap) {
|
|
6385
|
-
const content = await
|
|
6386
|
-
const outputPath =
|
|
6387
|
-
await
|
|
6388
|
-
await
|
|
6801
|
+
const content = await fs3.readFile(inputPath, "utf-8");
|
|
6802
|
+
const outputPath = path13.join(config.__meta.rootDir, "dist", urlPath);
|
|
6803
|
+
await fs3.mkdir(path13.dirname(outputPath), { recursive: true });
|
|
6804
|
+
await fs3.writeFile(outputPath, content, "utf-8");
|
|
6389
6805
|
}
|
|
6806
|
+
await writeOpenApiPublications(
|
|
6807
|
+
path13.join(config.__meta.rootDir, "dist"),
|
|
6808
|
+
schemaManager.getPublishedSchemas()
|
|
6809
|
+
);
|
|
6390
6810
|
}
|
|
6391
6811
|
};
|
|
6392
6812
|
};
|
|
@@ -6549,7 +6969,7 @@ var viteDocMetadataPlugin = () => ({
|
|
|
6549
6969
|
});
|
|
6550
6970
|
|
|
6551
6971
|
// src/vite/plugin-docs.ts
|
|
6552
|
-
import
|
|
6972
|
+
import path14 from "node:path";
|
|
6553
6973
|
import { glob as glob3 } from "glob";
|
|
6554
6974
|
import globParent from "glob-parent";
|
|
6555
6975
|
|
|
@@ -6616,7 +7036,7 @@ var globMarkdownFiles = async (config, options = { absolute: false }) => {
|
|
|
6616
7036
|
if (process.env.NODE_ENV !== "development") {
|
|
6617
7037
|
const draftStatuses = await Promise.all(
|
|
6618
7038
|
globbedFiles.map(async (file) => {
|
|
6619
|
-
const absolutePath =
|
|
7039
|
+
const absolutePath = path14.resolve(config.__meta.rootDir, file);
|
|
6620
7040
|
const { data } = await readFrontmatter(absolutePath);
|
|
6621
7041
|
return { file, isDraft: data.draft === true };
|
|
6622
7042
|
})
|
|
@@ -6629,9 +7049,9 @@ var globMarkdownFiles = async (config, options = { absolute: false }) => {
|
|
|
6629
7049
|
if (draftFiles.has(file)) {
|
|
6630
7050
|
continue;
|
|
6631
7051
|
}
|
|
6632
|
-
const relativePath =
|
|
7052
|
+
const relativePath = path14.posix.relative(parent, file);
|
|
6633
7053
|
const routePath = ensureLeadingSlash(relativePath.replace(/\.mdx?$/, ""));
|
|
6634
|
-
const filePath = options.absolute ?
|
|
7054
|
+
const filePath = options.absolute ? path14.resolve(config.__meta.rootDir, file) : file;
|
|
6635
7055
|
fileMapping[routePath] = filePath;
|
|
6636
7056
|
}
|
|
6637
7057
|
}
|
|
@@ -6715,8 +7135,168 @@ var plugin_docs_default = viteDocsPlugin;
|
|
|
6715
7135
|
|
|
6716
7136
|
// src/vite/plugin-markdown-export.ts
|
|
6717
7137
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
6718
|
-
import
|
|
7138
|
+
import path15 from "node:path";
|
|
7139
|
+
|
|
7140
|
+
// src/lib/util/contentNegotiation.ts
|
|
7141
|
+
var representations = [
|
|
7142
|
+
{
|
|
7143
|
+
contentType: "text/html",
|
|
7144
|
+
type: "text",
|
|
7145
|
+
subtype: "html",
|
|
7146
|
+
parameters: { charset: "utf-8" },
|
|
7147
|
+
serverOrder: 0
|
|
7148
|
+
},
|
|
7149
|
+
{
|
|
7150
|
+
contentType: "text/markdown",
|
|
7151
|
+
type: "text",
|
|
7152
|
+
subtype: "markdown",
|
|
7153
|
+
parameters: { charset: "utf-8" },
|
|
7154
|
+
serverOrder: 1
|
|
7155
|
+
}
|
|
7156
|
+
];
|
|
7157
|
+
var tokenPattern = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
7158
|
+
var mediaRangePattern = /^([!#$%&'*+\-.^_`|~0-9A-Za-z]+|\*)\/([!#$%&'*+\-.^_`|~0-9A-Za-z]+|\*)$/;
|
|
7159
|
+
var qualityPattern = /^(?:0(?:\.[0-9]{0,3})?|1(?:\.0{0,3})?)$/;
|
|
7160
|
+
var splitOutsideQuotes = (value, delimiter) => {
|
|
7161
|
+
const parts = [];
|
|
7162
|
+
let start = 0;
|
|
7163
|
+
let quoted = false;
|
|
7164
|
+
let escaped = false;
|
|
7165
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
7166
|
+
const character = value[index];
|
|
7167
|
+
if (escaped) {
|
|
7168
|
+
escaped = false;
|
|
7169
|
+
continue;
|
|
7170
|
+
}
|
|
7171
|
+
if (quoted && character === "\\") {
|
|
7172
|
+
escaped = true;
|
|
7173
|
+
continue;
|
|
7174
|
+
}
|
|
7175
|
+
if (character === '"') {
|
|
7176
|
+
quoted = !quoted;
|
|
7177
|
+
continue;
|
|
7178
|
+
}
|
|
7179
|
+
if (!quoted && character === delimiter) {
|
|
7180
|
+
parts.push(value.slice(start, index));
|
|
7181
|
+
start = index + 1;
|
|
7182
|
+
}
|
|
7183
|
+
}
|
|
7184
|
+
parts.push(value.slice(start));
|
|
7185
|
+
return parts;
|
|
7186
|
+
};
|
|
7187
|
+
var parseParameterValue = (value) => {
|
|
7188
|
+
const trimmedValue = value.trim();
|
|
7189
|
+
if (tokenPattern.test(trimmedValue)) {
|
|
7190
|
+
return trimmedValue.toLowerCase();
|
|
7191
|
+
}
|
|
7192
|
+
if (!trimmedValue.startsWith('"') || !trimmedValue.endsWith('"')) {
|
|
7193
|
+
return void 0;
|
|
7194
|
+
}
|
|
7195
|
+
const innerValue = trimmedValue.slice(1, -1);
|
|
7196
|
+
return innerValue.replace(/\\(.)/g, "$1").toLowerCase();
|
|
7197
|
+
};
|
|
7198
|
+
var parseMediaRange = (value, order) => {
|
|
7199
|
+
const [rawMediaRange, ...rawParameters] = splitOutsideQuotes(value, ";");
|
|
7200
|
+
const mediaRangeMatch = rawMediaRange?.trim().toLowerCase().match(mediaRangePattern);
|
|
7201
|
+
if (!mediaRangeMatch) {
|
|
7202
|
+
return void 0;
|
|
7203
|
+
}
|
|
7204
|
+
const [, type, subtype] = mediaRangeMatch;
|
|
7205
|
+
if (!type || !subtype || type === "*" && subtype !== "*") {
|
|
7206
|
+
return void 0;
|
|
7207
|
+
}
|
|
7208
|
+
const parameters = /* @__PURE__ */ new Map();
|
|
7209
|
+
let quality = 1;
|
|
7210
|
+
let foundQuality = false;
|
|
7211
|
+
for (const rawParameter of rawParameters) {
|
|
7212
|
+
const separatorIndex = rawParameter.indexOf("=");
|
|
7213
|
+
if (separatorIndex === -1) {
|
|
7214
|
+
return void 0;
|
|
7215
|
+
}
|
|
7216
|
+
const name = rawParameter.slice(0, separatorIndex).trim().toLowerCase();
|
|
7217
|
+
const rawValue = rawParameter.slice(separatorIndex + 1).trim();
|
|
7218
|
+
if (!tokenPattern.test(name)) {
|
|
7219
|
+
return void 0;
|
|
7220
|
+
}
|
|
7221
|
+
if (name === "q") {
|
|
7222
|
+
if (foundQuality || !qualityPattern.test(rawValue)) {
|
|
7223
|
+
return void 0;
|
|
7224
|
+
}
|
|
7225
|
+
quality = Number(rawValue);
|
|
7226
|
+
foundQuality = true;
|
|
7227
|
+
continue;
|
|
7228
|
+
}
|
|
7229
|
+
const parameterValue = parseParameterValue(rawValue);
|
|
7230
|
+
if (parameterValue === void 0 || parameters.has(name)) {
|
|
7231
|
+
return void 0;
|
|
7232
|
+
}
|
|
7233
|
+
parameters.set(name, parameterValue);
|
|
7234
|
+
}
|
|
7235
|
+
return { type, subtype, parameters, quality, order };
|
|
7236
|
+
};
|
|
7237
|
+
var getSpecificity = (range) => {
|
|
7238
|
+
if (range.type === "*") {
|
|
7239
|
+
return 0;
|
|
7240
|
+
}
|
|
7241
|
+
if (range.subtype === "*") {
|
|
7242
|
+
return 1;
|
|
7243
|
+
}
|
|
7244
|
+
return 2;
|
|
7245
|
+
};
|
|
7246
|
+
var matchesRepresentation = (range, representation) => {
|
|
7247
|
+
if (range.type !== "*" && range.type !== representation.type) {
|
|
7248
|
+
return false;
|
|
7249
|
+
}
|
|
7250
|
+
if (range.subtype !== "*" && range.subtype !== representation.subtype) {
|
|
7251
|
+
return false;
|
|
7252
|
+
}
|
|
7253
|
+
return [...range.parameters].every(
|
|
7254
|
+
([name, value]) => representation.parameters[name]?.toLowerCase() === value
|
|
7255
|
+
);
|
|
7256
|
+
};
|
|
7257
|
+
var compareRangeMatches = (left, right) => right.specificity - left.specificity || right.parameterCount - left.parameterCount || right.quality - left.quality || left.rangeOrder - right.rangeOrder;
|
|
7258
|
+
var getRepresentationMatch = (ranges, representation) => ranges.filter((range) => matchesRepresentation(range, representation)).map((range) => ({
|
|
7259
|
+
contentType: representation.contentType,
|
|
7260
|
+
quality: range.quality,
|
|
7261
|
+
specificity: getSpecificity(range),
|
|
7262
|
+
parameterCount: range.parameters.size,
|
|
7263
|
+
rangeOrder: range.order,
|
|
7264
|
+
serverOrder: representation.serverOrder
|
|
7265
|
+
})).sort(compareRangeMatches)[0];
|
|
7266
|
+
var compareRepresentations = (left, right) => right.quality - left.quality || right.specificity - left.specificity || right.parameterCount - left.parameterCount || left.rangeOrder - right.rangeOrder || left.serverOrder - right.serverOrder;
|
|
7267
|
+
var negotiateContentType = (acceptHeader) => {
|
|
7268
|
+
if (!acceptHeader?.trim()) {
|
|
7269
|
+
return "text/html";
|
|
7270
|
+
}
|
|
7271
|
+
const ranges = splitOutsideQuotes(acceptHeader, ",").map((value, order) => parseMediaRange(value.trim(), order)).filter((range) => range !== void 0);
|
|
7272
|
+
const match = representations.map((representation) => getRepresentationMatch(ranges, representation)).filter((result) => result !== void 0).filter((result) => result.quality > 0).sort(compareRepresentations)[0];
|
|
7273
|
+
return match?.contentType ?? null;
|
|
7274
|
+
};
|
|
7275
|
+
var addAcceptToVary = (varyHeader) => {
|
|
7276
|
+
const fields = (varyHeader ?? "").split(",").map((field) => field.trim()).filter(Boolean);
|
|
7277
|
+
if (fields.some((field) => field === "*")) {
|
|
7278
|
+
return "*";
|
|
7279
|
+
}
|
|
7280
|
+
const seenFields = /* @__PURE__ */ new Set();
|
|
7281
|
+
const uniqueFields = fields.filter((field) => {
|
|
7282
|
+
const normalizedField = field.toLowerCase();
|
|
7283
|
+
if (seenFields.has(normalizedField)) {
|
|
7284
|
+
return false;
|
|
7285
|
+
}
|
|
7286
|
+
seenFields.add(normalizedField);
|
|
7287
|
+
return true;
|
|
7288
|
+
});
|
|
7289
|
+
if (!seenFields.has("accept")) {
|
|
7290
|
+
uniqueFields.push("Accept");
|
|
7291
|
+
}
|
|
7292
|
+
return uniqueFields.join(", ");
|
|
7293
|
+
};
|
|
7294
|
+
|
|
7295
|
+
// src/vite/plugin-markdown-export.ts
|
|
6719
7296
|
init_joinUrl();
|
|
7297
|
+
init_markdown_representation();
|
|
7298
|
+
var MARKDOWN_FILES_MODULE_ID = "virtual:zudoku-markdown-files";
|
|
7299
|
+
var RESOLVED_MARKDOWN_FILES_MODULE_ID = `\0${MARKDOWN_FILES_MODULE_ID}`;
|
|
6720
7300
|
var processMarkdownFile = async (filePath) => {
|
|
6721
7301
|
const { content: markdownContent, data: frontmatter } = await readFrontmatter(filePath);
|
|
6722
7302
|
let finalMarkdown = markdownContent;
|
|
@@ -6732,19 +7312,97 @@ ${markdownContent}`;
|
|
|
6732
7312
|
};
|
|
6733
7313
|
var getMarkdownOutputPath = (distDir, routePath) => {
|
|
6734
7314
|
const segments = routePath === "/" ? ["index"] : routePath.split("/").filter(Boolean);
|
|
6735
|
-
return `${
|
|
7315
|
+
return `${path15.join(distDir, ...segments)}.md`;
|
|
6736
7316
|
};
|
|
6737
|
-
var
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
7317
|
+
var writeMarkdownInfo = async (markdownInfoPath, markdownFileInfos) => {
|
|
7318
|
+
await mkdir2(path15.dirname(markdownInfoPath), { recursive: true });
|
|
7319
|
+
await writeFile2(
|
|
7320
|
+
markdownInfoPath,
|
|
7321
|
+
JSON.stringify(markdownFileInfos, null, 2),
|
|
7322
|
+
"utf-8"
|
|
6741
7323
|
);
|
|
7324
|
+
};
|
|
7325
|
+
var resolveMarkdownRoutePath = (requestUrl, basePath) => {
|
|
7326
|
+
const pathname = requestUrl.split(/[?#]/)[0]?.replace(/\.mdx?$/, "");
|
|
7327
|
+
if (!pathname) return;
|
|
7328
|
+
const routePath = resolveDocumentationRoutePath(pathname, basePath);
|
|
7329
|
+
if (!routePath) return;
|
|
6742
7330
|
if (routePath === "/index") {
|
|
6743
7331
|
return "/";
|
|
6744
7332
|
}
|
|
6745
7333
|
return routePath;
|
|
6746
7334
|
};
|
|
6747
7335
|
var needsMdFiles = (config) => config.docs.publishMarkdown || config.docs.llms.llmsTxt || config.docs.llms.llmsTxtFull;
|
|
7336
|
+
var isContentNegotiationEnabled = (config) => config.docs.publishMarkdown && config.docs.contentNegotiation;
|
|
7337
|
+
var resolveMarkdownFiles = async (config) => {
|
|
7338
|
+
const files = await resolveCustomNavigationPaths(
|
|
7339
|
+
config,
|
|
7340
|
+
await globMarkdownFiles(config, { absolute: true })
|
|
7341
|
+
);
|
|
7342
|
+
if (config.docs.llms.includeProtected || !config.protectedRoutes) {
|
|
7343
|
+
return files;
|
|
7344
|
+
}
|
|
7345
|
+
const patterns = Object.keys(config.protectedRoutes);
|
|
7346
|
+
return Object.fromEntries(
|
|
7347
|
+
Object.entries(files).filter(
|
|
7348
|
+
([routePath]) => !matchesAnyProtectedPattern(patterns, routePath)
|
|
7349
|
+
)
|
|
7350
|
+
);
|
|
7351
|
+
};
|
|
7352
|
+
var loadMarkdownContents = async (markdownFiles) => Object.fromEntries(
|
|
7353
|
+
await Promise.all(
|
|
7354
|
+
Object.entries(markdownFiles).map(async ([routePath, filePath]) => [
|
|
7355
|
+
encodeDocumentationRoutePath(routePath),
|
|
7356
|
+
(await processMarkdownFile(filePath)).content
|
|
7357
|
+
])
|
|
7358
|
+
)
|
|
7359
|
+
);
|
|
7360
|
+
var createMarkdownDevMiddleware = ({
|
|
7361
|
+
config,
|
|
7362
|
+
getMarkdownFiles
|
|
7363
|
+
}) => async (req, res, next) => {
|
|
7364
|
+
if (!req.url || req.method !== "GET" && req.method !== "HEAD") {
|
|
7365
|
+
return next();
|
|
7366
|
+
}
|
|
7367
|
+
const basePath = joinUrl(config.basePath);
|
|
7368
|
+
const pathname = req.url.split(/[?#]/)[0] ?? req.url;
|
|
7369
|
+
const isExplicitMarkdownRequest = pathname.endsWith(".md");
|
|
7370
|
+
const routePath = isExplicitMarkdownRequest ? resolveMarkdownRoutePath(req.url, basePath) : resolveDocumentationRoutePath(req.url, basePath);
|
|
7371
|
+
if (!routePath) return next();
|
|
7372
|
+
const filePath = Object.entries(getMarkdownFiles()).find(
|
|
7373
|
+
([configuredRoutePath]) => encodeDocumentationRoutePath(configuredRoutePath) === routePath
|
|
7374
|
+
)?.[1];
|
|
7375
|
+
if (!filePath) return next();
|
|
7376
|
+
if (!isExplicitMarkdownRequest && isContentNegotiationEnabled(config)) {
|
|
7377
|
+
const negotiatedType = negotiateContentType(req.headers.accept);
|
|
7378
|
+
res.setHeader("Vary", addAcceptToVary(res.getHeader("Vary")?.toString()));
|
|
7379
|
+
res.setHeader(
|
|
7380
|
+
"Link",
|
|
7381
|
+
appendLinkHeader(
|
|
7382
|
+
res.getHeader("Link"),
|
|
7383
|
+
getMarkdownAlternateLink(routePath, config.basePath)
|
|
7384
|
+
)
|
|
7385
|
+
);
|
|
7386
|
+
if (negotiatedType === null) {
|
|
7387
|
+
res.statusCode = 406;
|
|
7388
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
7389
|
+
return res.end(req.method === "HEAD" ? void 0 : "Not Acceptable");
|
|
7390
|
+
}
|
|
7391
|
+
if (negotiatedType === "text/html") {
|
|
7392
|
+
return next();
|
|
7393
|
+
}
|
|
7394
|
+
} else if (!isExplicitMarkdownRequest) {
|
|
7395
|
+
return next();
|
|
7396
|
+
}
|
|
7397
|
+
try {
|
|
7398
|
+
const { content } = await processMarkdownFile(filePath);
|
|
7399
|
+
res.setHeader("Content-Type", "text/markdown; charset=utf-8");
|
|
7400
|
+
return res.end(req.method === "HEAD" ? void 0 : content);
|
|
7401
|
+
} catch (error) {
|
|
7402
|
+
console.warn(`Failed to serve markdown for ${routePath}:`, error);
|
|
7403
|
+
return next();
|
|
7404
|
+
}
|
|
7405
|
+
};
|
|
6748
7406
|
var viteMarkdownExportPlugin = () => {
|
|
6749
7407
|
let markdownFiles = {};
|
|
6750
7408
|
let markdownFileInfos = [];
|
|
@@ -6753,58 +7411,40 @@ var viteMarkdownExportPlugin = () => {
|
|
|
6753
7411
|
applyToEnvironment(env) {
|
|
6754
7412
|
return env.name === "ssr";
|
|
6755
7413
|
},
|
|
7414
|
+
resolveId(id) {
|
|
7415
|
+
if (id === MARKDOWN_FILES_MODULE_ID) {
|
|
7416
|
+
return RESOLVED_MARKDOWN_FILES_MODULE_ID;
|
|
7417
|
+
}
|
|
7418
|
+
},
|
|
7419
|
+
async load(id) {
|
|
7420
|
+
if (id === RESOLVED_MARKDOWN_FILES_MODULE_ID) {
|
|
7421
|
+
return `export default ${JSON.stringify(await loadMarkdownContents(markdownFiles))};`;
|
|
7422
|
+
}
|
|
7423
|
+
},
|
|
6756
7424
|
async buildStart() {
|
|
6757
7425
|
const config = getCurrentConfig();
|
|
6758
7426
|
if (config.__meta.mode === "standalone" || !needsMdFiles(config)) {
|
|
6759
7427
|
return;
|
|
6760
7428
|
}
|
|
6761
|
-
markdownFiles = await
|
|
6762
|
-
config,
|
|
6763
|
-
await globMarkdownFiles(config, { absolute: true })
|
|
6764
|
-
);
|
|
6765
|
-
if (!config.docs.llms.includeProtected) {
|
|
6766
|
-
const protectedRoutes = config.protectedRoutes;
|
|
6767
|
-
if (protectedRoutes) {
|
|
6768
|
-
const patterns = Object.keys(protectedRoutes);
|
|
6769
|
-
for (const routePath of Object.keys(markdownFiles)) {
|
|
6770
|
-
if (matchesAnyProtectedPattern(patterns, routePath)) {
|
|
6771
|
-
delete markdownFiles[routePath];
|
|
6772
|
-
}
|
|
6773
|
-
}
|
|
6774
|
-
}
|
|
6775
|
-
}
|
|
7429
|
+
markdownFiles = await resolveMarkdownFiles(config);
|
|
6776
7430
|
},
|
|
6777
7431
|
async configureServer(server) {
|
|
6778
7432
|
const config = getCurrentConfig();
|
|
6779
7433
|
if (!needsMdFiles(config)) return;
|
|
6780
|
-
markdownFiles = await
|
|
6781
|
-
|
|
6782
|
-
|
|
7434
|
+
markdownFiles = await resolveMarkdownFiles(config);
|
|
7435
|
+
server.middlewares.use(
|
|
7436
|
+
createMarkdownDevMiddleware({
|
|
7437
|
+
config,
|
|
7438
|
+
getMarkdownFiles: () => markdownFiles
|
|
7439
|
+
})
|
|
6783
7440
|
);
|
|
6784
|
-
server.middlewares.use(async (req, res, next) => {
|
|
6785
|
-
if (req.method !== "GET" || !req.url?.endsWith(".md")) {
|
|
6786
|
-
return next();
|
|
6787
|
-
}
|
|
6788
|
-
const basePath = joinUrl(config.basePath);
|
|
6789
|
-
const routePath = resolveMarkdownRoutePath(req.url, basePath);
|
|
6790
|
-
const filePath = markdownFiles[routePath];
|
|
6791
|
-
if (!filePath) return next();
|
|
6792
|
-
try {
|
|
6793
|
-
const { content } = await processMarkdownFile(filePath);
|
|
6794
|
-
res.setHeader("Content-Type", "text/markdown; charset=utf-8");
|
|
6795
|
-
res.end(content);
|
|
6796
|
-
} catch (error) {
|
|
6797
|
-
console.warn(`Failed to serve markdown for ${routePath}:`, error);
|
|
6798
|
-
return next();
|
|
6799
|
-
}
|
|
6800
|
-
});
|
|
6801
7441
|
},
|
|
6802
7442
|
async closeBundle() {
|
|
6803
7443
|
const config = getCurrentConfig();
|
|
6804
|
-
if (process.env.NODE_ENV !== "production" ||
|
|
7444
|
+
if (process.env.NODE_ENV !== "production" || !needsMdFiles(config)) {
|
|
6805
7445
|
return;
|
|
6806
7446
|
}
|
|
6807
|
-
const distDir =
|
|
7447
|
+
const distDir = path15.join(
|
|
6808
7448
|
config.__meta.rootDir,
|
|
6809
7449
|
"dist",
|
|
6810
7450
|
config.basePath ?? ""
|
|
@@ -6825,22 +7465,18 @@ var viteMarkdownExportPlugin = () => {
|
|
|
6825
7465
|
content: finalMarkdown
|
|
6826
7466
|
});
|
|
6827
7467
|
const outputPath = getMarkdownOutputPath(distDir, routePath);
|
|
6828
|
-
await mkdir2(
|
|
7468
|
+
await mkdir2(path15.dirname(outputPath), { recursive: true });
|
|
6829
7469
|
await writeFile2(outputPath, finalMarkdown, "utf-8");
|
|
6830
7470
|
} catch (error) {
|
|
6831
7471
|
console.warn(`Failed to export markdown for ${routePath}:`, error);
|
|
6832
7472
|
}
|
|
6833
7473
|
}
|
|
6834
|
-
if (config.docs.llms.llmsTxt || config.docs.llms.llmsTxtFull) {
|
|
6835
|
-
const markdownInfoPath =
|
|
7474
|
+
if (config.docs.llms.llmsTxt || config.docs.llms.llmsTxtFull || isContentNegotiationEnabled(config)) {
|
|
7475
|
+
const markdownInfoPath = path15.join(
|
|
6836
7476
|
config.__meta.rootDir,
|
|
6837
7477
|
"node_modules/.zudoku/markdown-info.json"
|
|
6838
7478
|
);
|
|
6839
|
-
await
|
|
6840
|
-
markdownInfoPath,
|
|
6841
|
-
JSON.stringify(markdownFileInfos, null, 2),
|
|
6842
|
-
"utf-8"
|
|
6843
|
-
);
|
|
7479
|
+
await writeMarkdownInfo(markdownInfoPath, markdownFileInfos);
|
|
6844
7480
|
}
|
|
6845
7481
|
}
|
|
6846
7482
|
};
|
|
@@ -6984,9 +7620,9 @@ var remarkCodeTabs = () => (tree) => {
|
|
|
6984
7620
|
};
|
|
6985
7621
|
|
|
6986
7622
|
// src/vite/mdx/remark-inject-filepath.ts
|
|
6987
|
-
import
|
|
7623
|
+
import path16 from "node:path";
|
|
6988
7624
|
var remarkInjectFilepath = (rootDir) => (tree, vfile) => {
|
|
6989
|
-
const relativePath =
|
|
7625
|
+
const relativePath = path16.relative(rootDir, vfile.path).split(path16.sep).join(path16.posix.sep);
|
|
6990
7626
|
tree.children.unshift(exportMdxjsConst("__filepath", relativePath));
|
|
6991
7627
|
};
|
|
6992
7628
|
|
|
@@ -7073,7 +7709,7 @@ var remarkLastModified = () => {
|
|
|
7073
7709
|
};
|
|
7074
7710
|
|
|
7075
7711
|
// src/vite/mdx/remark-link-rewrite.ts
|
|
7076
|
-
import
|
|
7712
|
+
import path17 from "node:path";
|
|
7077
7713
|
import { visit as visit5 } from "unist-util-visit";
|
|
7078
7714
|
var markdownExtension = /\.mdx?$/;
|
|
7079
7715
|
var resolveToRoute = (url, filePath, routesByFile) => {
|
|
@@ -7081,7 +7717,7 @@ var resolveToRoute = (url, filePath, routesByFile) => {
|
|
|
7081
7717
|
if (!markdownExtension.test(pathname) || pathname.startsWith("/")) {
|
|
7082
7718
|
return void 0;
|
|
7083
7719
|
}
|
|
7084
|
-
const targetFile =
|
|
7720
|
+
const targetFile = path17.resolve(path17.dirname(filePath), pathname).replace(markdownExtension, "");
|
|
7085
7721
|
const route = routesByFile.get(targetFile);
|
|
7086
7722
|
if (route === void 0) return void 0;
|
|
7087
7723
|
return `${route}${suffix}`;
|
|
@@ -7096,11 +7732,11 @@ var remarkLinkRewrite = (basePath = "", routesByFile = /* @__PURE__ */ new Map()
|
|
|
7096
7732
|
node.url = resolved;
|
|
7097
7733
|
return;
|
|
7098
7734
|
}
|
|
7099
|
-
const base =
|
|
7735
|
+
const base = path17.posix.join(basePath);
|
|
7100
7736
|
if (basePath && node.url.startsWith(base)) {
|
|
7101
7737
|
node.url = node.url.slice(base.length);
|
|
7102
7738
|
} else if (!node.url.startsWith("/") && !node.url.startsWith("#")) {
|
|
7103
|
-
node.url =
|
|
7739
|
+
node.url = path17.posix.join("..", node.url);
|
|
7104
7740
|
}
|
|
7105
7741
|
node.url = node.url.replace(/\.mdx?(#.*)?$/, "$1");
|
|
7106
7742
|
});
|
|
@@ -7609,8 +8245,8 @@ var protectedAnnotatorPlugin = () => ({
|
|
|
7609
8245
|
});
|
|
7610
8246
|
|
|
7611
8247
|
// src/vite/config.ts
|
|
7612
|
-
var getAppClientEntryPath = () =>
|
|
7613
|
-
var getAppServerEntryPath = () =>
|
|
8248
|
+
var getAppClientEntryPath = () => path18.posix.join(getZudokuRootDir(), "src/app/entry.client.tsx");
|
|
8249
|
+
var getAppServerEntryPath = () => path18.posix.join(getZudokuRootDir(), "src/app/entry.server.tsx");
|
|
7614
8250
|
var hasLoggedCdnInfo = false;
|
|
7615
8251
|
var MEDIA_REGEX = /\.(a?png|jpe?g|gif|bmp|svg|webp|tiff|ico|webm|ogg|mp3|wav|m4a|avif|mp4)/i;
|
|
7616
8252
|
var defineEnvVars = (vars) => Object.fromEntries(
|
|
@@ -7644,7 +8280,7 @@ async function getViteConfig(dir, configEnv, options = {}) {
|
|
|
7644
8280
|
);
|
|
7645
8281
|
if (ZuploEnv.isZuplo) {
|
|
7646
8282
|
dotenv.config({
|
|
7647
|
-
path:
|
|
8283
|
+
path: path18.resolve(config.__meta.rootDir, "../.env.zuplo"),
|
|
7648
8284
|
quiet: true
|
|
7649
8285
|
});
|
|
7650
8286
|
}
|
|
@@ -7704,7 +8340,7 @@ async function getViteConfig(dir, configEnv, options = {}) {
|
|
|
7704
8340
|
sourcemap: true,
|
|
7705
8341
|
target: "es2022",
|
|
7706
8342
|
chunkSizeWarningLimit: 1500,
|
|
7707
|
-
outDir:
|
|
8343
|
+
outDir: path18.resolve(path18.join(dir, "dist", config.basePath ?? "")),
|
|
7708
8344
|
emptyOutDir: false,
|
|
7709
8345
|
rolldownOptions: {
|
|
7710
8346
|
external: [joinUrl(config.basePath, "/pagefind/pagefind.js")],
|
|
@@ -7734,8 +8370,8 @@ async function getViteConfig(dir, configEnv, options = {}) {
|
|
|
7734
8370
|
external: ["@shikijs/themes", "@shikijs/langs"]
|
|
7735
8371
|
},
|
|
7736
8372
|
build: {
|
|
7737
|
-
outDir:
|
|
7738
|
-
|
|
8373
|
+
outDir: path18.resolve(
|
|
8374
|
+
path18.join(dir, "dist", config.basePath ?? "", "server")
|
|
7739
8375
|
),
|
|
7740
8376
|
copyPublicDir: false,
|
|
7741
8377
|
rolldownOptions: {
|
|
@@ -7750,7 +8386,7 @@ async function getViteConfig(dir, configEnv, options = {}) {
|
|
|
7750
8386
|
if (filename.startsWith(`${PROTECTED_CHUNK_DIR}/`)) {
|
|
7751
8387
|
return joinUrl(config.basePath, `/${filename}`);
|
|
7752
8388
|
}
|
|
7753
|
-
if (cdnUrl?.base && [".js", ".css"].includes(
|
|
8389
|
+
if (cdnUrl?.base && [".js", ".css"].includes(path18.extname(filename))) {
|
|
7754
8390
|
return joinUrl(cdnUrl.base, filename);
|
|
7755
8391
|
}
|
|
7756
8392
|
if (cdnUrl?.media && MEDIA_REGEX.test(filename)) {
|
|
@@ -7760,7 +8396,7 @@ async function getViteConfig(dir, configEnv, options = {}) {
|
|
|
7760
8396
|
}
|
|
7761
8397
|
},
|
|
7762
8398
|
optimizeDeps: {
|
|
7763
|
-
entries: [
|
|
8399
|
+
entries: [path18.posix.join(getZudokuRootDir(), "src/{app,lib}/**")],
|
|
7764
8400
|
exclude: ["zudoku"],
|
|
7765
8401
|
include: [
|
|
7766
8402
|
"@mdx-js/react",
|
|
@@ -7857,10 +8493,10 @@ ${cssLinks}
|
|
|
7857
8493
|
|
|
7858
8494
|
// src/vite/manifest.ts
|
|
7859
8495
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
7860
|
-
import
|
|
8496
|
+
import path19 from "node:path";
|
|
7861
8497
|
var writeManifest = async (distDir, config) => {
|
|
7862
8498
|
await writeFile3(
|
|
7863
|
-
|
|
8499
|
+
path19.join(distDir, MANIFEST_FILENAME),
|
|
7864
8500
|
`${JSON.stringify(buildManifest(config), null, 2)}
|
|
7865
8501
|
`,
|
|
7866
8502
|
"utf-8"
|
|
@@ -7869,17 +8505,442 @@ var writeManifest = async (distDir, config) => {
|
|
|
7869
8505
|
|
|
7870
8506
|
// src/vite/output.ts
|
|
7871
8507
|
import assert from "node:assert";
|
|
7872
|
-
import {
|
|
7873
|
-
import
|
|
8508
|
+
import { existsSync } from "node:fs";
|
|
8509
|
+
import { cp, mkdir as mkdir3, readdir, rm, writeFile as writeFile4 } from "node:fs/promises";
|
|
8510
|
+
import path20 from "node:path";
|
|
7874
8511
|
init_joinUrl();
|
|
8512
|
+
|
|
8513
|
+
// src/vite/vercel-markdown-middleware.ts
|
|
8514
|
+
var normalizePath2 = (value) => {
|
|
8515
|
+
const pathname = value.split("/").filter(Boolean).map((segment) => {
|
|
8516
|
+
try {
|
|
8517
|
+
return encodeURIComponent(decodeURIComponent(segment));
|
|
8518
|
+
} catch {
|
|
8519
|
+
return encodeURIComponent(segment);
|
|
8520
|
+
}
|
|
8521
|
+
}).join("/");
|
|
8522
|
+
return pathname ? `/${pathname}` : "/";
|
|
8523
|
+
};
|
|
8524
|
+
var resolveRoutePath = (basePath, routePath) => {
|
|
8525
|
+
const normalizedRoutePath = normalizePath2(routePath);
|
|
8526
|
+
if (normalizedRoutePath === "/") {
|
|
8527
|
+
return basePath;
|
|
8528
|
+
}
|
|
8529
|
+
return normalizePath2(`${basePath}/${normalizedRoutePath}`);
|
|
8530
|
+
};
|
|
8531
|
+
var resolveMarkdownPath = (basePath, routePath) => {
|
|
8532
|
+
const normalizedRoutePath = normalizePath2(routePath);
|
|
8533
|
+
if (normalizedRoutePath === "/") {
|
|
8534
|
+
return normalizePath2(`${basePath}/index.md`);
|
|
8535
|
+
}
|
|
8536
|
+
return `${resolveRoutePath(basePath, routePath)}.md`;
|
|
8537
|
+
};
|
|
8538
|
+
var generateVercelMarkdownMiddleware = ({
|
|
8539
|
+
basePath = "/",
|
|
8540
|
+
knownCanonicalRoutePaths,
|
|
8541
|
+
markdownCanonicalRoutePaths,
|
|
8542
|
+
markdownNotFoundBody,
|
|
8543
|
+
passthroughPaths = []
|
|
8544
|
+
}) => {
|
|
8545
|
+
const normalizedBasePath = normalizePath2(basePath);
|
|
8546
|
+
const knownRoutes = new Set(
|
|
8547
|
+
knownCanonicalRoutePaths.map(
|
|
8548
|
+
(routePath) => resolveRoutePath(normalizedBasePath, routePath)
|
|
8549
|
+
)
|
|
8550
|
+
);
|
|
8551
|
+
const markdownRoutes = new Map(
|
|
8552
|
+
markdownCanonicalRoutePaths.map((routePath) => [
|
|
8553
|
+
resolveRoutePath(normalizedBasePath, routePath),
|
|
8554
|
+
resolveMarkdownPath(normalizedBasePath, routePath)
|
|
8555
|
+
])
|
|
8556
|
+
);
|
|
8557
|
+
const normalizedPassthroughPaths = passthroughPaths.map(normalizePath2);
|
|
8558
|
+
for (const routePath of markdownRoutes.keys()) {
|
|
8559
|
+
if (!knownRoutes.has(routePath)) {
|
|
8560
|
+
throw new Error(
|
|
8561
|
+
`Markdown route "${routePath}" is not present in knownCanonicalRoutePaths`
|
|
8562
|
+
);
|
|
8563
|
+
}
|
|
8564
|
+
}
|
|
8565
|
+
return `
|
|
8566
|
+
const BASE_PATH = ${JSON.stringify(normalizedBasePath)};
|
|
8567
|
+
const KNOWN_ROUTES = new Set(${JSON.stringify([...knownRoutes])});
|
|
8568
|
+
const MARKDOWN_ROUTES = new Map(${JSON.stringify([...markdownRoutes])});
|
|
8569
|
+
const PASSTHROUGH_PATHS = new Set(${JSON.stringify(normalizedPassthroughPaths)});
|
|
8570
|
+
const MARKDOWN_NOT_FOUND_BODY = ${JSON.stringify(markdownNotFoundBody)};
|
|
8571
|
+
const NEGOTIATED_VARY = "Accept, Accept-Encoding";
|
|
8572
|
+
|
|
8573
|
+
const REPRESENTATIONS = [
|
|
8574
|
+
{
|
|
8575
|
+
contentType: "text/html",
|
|
8576
|
+
type: "text",
|
|
8577
|
+
subtype: "html",
|
|
8578
|
+
parameters: { charset: "utf-8" },
|
|
8579
|
+
serverOrder: 0,
|
|
8580
|
+
},
|
|
8581
|
+
{
|
|
8582
|
+
contentType: "text/markdown",
|
|
8583
|
+
type: "text",
|
|
8584
|
+
subtype: "markdown",
|
|
8585
|
+
parameters: { charset: "utf-8" },
|
|
8586
|
+
serverOrder: 1,
|
|
8587
|
+
},
|
|
8588
|
+
];
|
|
8589
|
+
|
|
8590
|
+
const TOKEN_PATTERN = /^[!#$%&'*+\\-.^_\`|~0-9A-Za-z]+$/;
|
|
8591
|
+
const MEDIA_RANGE_PATTERN =
|
|
8592
|
+
/^([!#$%&'*+\\-.^_\`|~0-9A-Za-z]+|\\*)\\/([!#$%&'*+\\-.^_\`|~0-9A-Za-z]+|\\*)$/;
|
|
8593
|
+
const QUALITY_PATTERN = /^(?:0(?:\\.[0-9]{0,3})?|1(?:\\.0{0,3})?)$/;
|
|
8594
|
+
|
|
8595
|
+
const splitOutsideQuotes = (value, delimiter) => {
|
|
8596
|
+
const parts = [];
|
|
8597
|
+
let start = 0;
|
|
8598
|
+
let quoted = false;
|
|
8599
|
+
let escaped = false;
|
|
8600
|
+
|
|
8601
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
8602
|
+
const character = value[index];
|
|
8603
|
+
if (escaped) {
|
|
8604
|
+
escaped = false;
|
|
8605
|
+
continue;
|
|
8606
|
+
}
|
|
8607
|
+
if (quoted && character === "\\\\") {
|
|
8608
|
+
escaped = true;
|
|
8609
|
+
continue;
|
|
8610
|
+
}
|
|
8611
|
+
if (character === '"') {
|
|
8612
|
+
quoted = !quoted;
|
|
8613
|
+
continue;
|
|
8614
|
+
}
|
|
8615
|
+
if (!quoted && character === delimiter) {
|
|
8616
|
+
parts.push(value.slice(start, index));
|
|
8617
|
+
start = index + 1;
|
|
8618
|
+
}
|
|
8619
|
+
}
|
|
8620
|
+
|
|
8621
|
+
parts.push(value.slice(start));
|
|
8622
|
+
return parts;
|
|
8623
|
+
};
|
|
8624
|
+
|
|
8625
|
+
const parseParameterValue = (value) => {
|
|
8626
|
+
const trimmedValue = value.trim();
|
|
8627
|
+
if (TOKEN_PATTERN.test(trimmedValue)) {
|
|
8628
|
+
return trimmedValue.toLowerCase();
|
|
8629
|
+
}
|
|
8630
|
+
if (!trimmedValue.startsWith('"') || !trimmedValue.endsWith('"')) {
|
|
8631
|
+
return undefined;
|
|
8632
|
+
}
|
|
8633
|
+
return trimmedValue.slice(1, -1).replace(/\\\\(.)/g, "$1").toLowerCase();
|
|
8634
|
+
};
|
|
8635
|
+
|
|
8636
|
+
const parseMediaRange = (value, order) => {
|
|
8637
|
+
const [rawMediaRange, ...rawParameters] = splitOutsideQuotes(value, ";");
|
|
8638
|
+
const mediaRangeMatch = rawMediaRange
|
|
8639
|
+
?.trim()
|
|
8640
|
+
.toLowerCase()
|
|
8641
|
+
.match(MEDIA_RANGE_PATTERN);
|
|
8642
|
+
if (!mediaRangeMatch) return undefined;
|
|
8643
|
+
|
|
8644
|
+
const [, type, subtype] = mediaRangeMatch;
|
|
8645
|
+
if (!type || !subtype || (type === "*" && subtype !== "*")) {
|
|
8646
|
+
return undefined;
|
|
8647
|
+
}
|
|
8648
|
+
|
|
8649
|
+
const parameters = new Map();
|
|
8650
|
+
let quality = 1;
|
|
8651
|
+
let foundQuality = false;
|
|
8652
|
+
|
|
8653
|
+
for (const rawParameter of rawParameters) {
|
|
8654
|
+
const separatorIndex = rawParameter.indexOf("=");
|
|
8655
|
+
if (separatorIndex === -1) return undefined;
|
|
8656
|
+
|
|
8657
|
+
const name = rawParameter.slice(0, separatorIndex).trim().toLowerCase();
|
|
8658
|
+
const rawValue = rawParameter.slice(separatorIndex + 1).trim();
|
|
8659
|
+
if (!TOKEN_PATTERN.test(name)) return undefined;
|
|
8660
|
+
|
|
8661
|
+
if (name === "q") {
|
|
8662
|
+
if (foundQuality || !QUALITY_PATTERN.test(rawValue)) return undefined;
|
|
8663
|
+
quality = Number(rawValue);
|
|
8664
|
+
foundQuality = true;
|
|
8665
|
+
continue;
|
|
8666
|
+
}
|
|
8667
|
+
|
|
8668
|
+
// RFC 9110 no longer defines accept extensions. Every non-q parameter is
|
|
8669
|
+
// a media type parameter and participates in representation matching.
|
|
8670
|
+
const parameterValue = parseParameterValue(rawValue);
|
|
8671
|
+
if (parameterValue === undefined || parameters.has(name)) return undefined;
|
|
8672
|
+
parameters.set(name, parameterValue);
|
|
8673
|
+
}
|
|
8674
|
+
|
|
8675
|
+
return { type, subtype, parameters, quality, order };
|
|
8676
|
+
};
|
|
8677
|
+
|
|
8678
|
+
const getSpecificity = (range) => {
|
|
8679
|
+
if (range.type === "*") return 0;
|
|
8680
|
+
if (range.subtype === "*") return 1;
|
|
8681
|
+
return 2;
|
|
8682
|
+
};
|
|
8683
|
+
|
|
8684
|
+
const matchesRepresentation = (range, representation) => {
|
|
8685
|
+
if (range.type !== "*" && range.type !== representation.type) return false;
|
|
8686
|
+
if (range.subtype !== "*" && range.subtype !== representation.subtype) {
|
|
8687
|
+
return false;
|
|
8688
|
+
}
|
|
8689
|
+
return [...range.parameters].every(
|
|
8690
|
+
([name, value]) => representation.parameters[name]?.toLowerCase() === value,
|
|
8691
|
+
);
|
|
8692
|
+
};
|
|
8693
|
+
|
|
8694
|
+
const compareRangeMatches = (left, right) =>
|
|
8695
|
+
right.specificity - left.specificity ||
|
|
8696
|
+
right.parameterCount - left.parameterCount ||
|
|
8697
|
+
right.quality - left.quality ||
|
|
8698
|
+
left.rangeOrder - right.rangeOrder;
|
|
8699
|
+
|
|
8700
|
+
const getRepresentationMatch = (ranges, representation) =>
|
|
8701
|
+
ranges
|
|
8702
|
+
.filter((range) => matchesRepresentation(range, representation))
|
|
8703
|
+
.map((range) => ({
|
|
8704
|
+
contentType: representation.contentType,
|
|
8705
|
+
quality: range.quality,
|
|
8706
|
+
specificity: getSpecificity(range),
|
|
8707
|
+
parameterCount: range.parameters.size,
|
|
8708
|
+
rangeOrder: range.order,
|
|
8709
|
+
serverOrder: representation.serverOrder,
|
|
8710
|
+
}))
|
|
8711
|
+
.sort(compareRangeMatches)[0];
|
|
8712
|
+
|
|
8713
|
+
const compareRepresentations = (left, right) =>
|
|
8714
|
+
right.quality - left.quality ||
|
|
8715
|
+
right.specificity - left.specificity ||
|
|
8716
|
+
right.parameterCount - left.parameterCount ||
|
|
8717
|
+
left.rangeOrder - right.rangeOrder ||
|
|
8718
|
+
left.serverOrder - right.serverOrder;
|
|
8719
|
+
|
|
8720
|
+
const negotiateContentType = (acceptHeader) => {
|
|
8721
|
+
if (!acceptHeader?.trim()) return "text/html";
|
|
8722
|
+
|
|
8723
|
+
const ranges = splitOutsideQuotes(acceptHeader, ",")
|
|
8724
|
+
.map((value, order) => parseMediaRange(value.trim(), order))
|
|
8725
|
+
.filter((range) => range !== undefined);
|
|
8726
|
+
const match = REPRESENTATIONS
|
|
8727
|
+
.map((representation) => getRepresentationMatch(ranges, representation))
|
|
8728
|
+
.filter((result) => result !== undefined)
|
|
8729
|
+
.filter((result) => result.quality > 0)
|
|
8730
|
+
.sort(compareRepresentations)[0];
|
|
8731
|
+
return match?.contentType ?? null;
|
|
8732
|
+
};
|
|
8733
|
+
|
|
8734
|
+
const normalizeRequestPath = (pathname) => {
|
|
8735
|
+
const normalized = pathname
|
|
8736
|
+
.split("/")
|
|
8737
|
+
.filter(Boolean)
|
|
8738
|
+
.map((segment) => {
|
|
8739
|
+
try {
|
|
8740
|
+
return encodeURIComponent(decodeURIComponent(segment));
|
|
8741
|
+
} catch {
|
|
8742
|
+
return encodeURIComponent(segment);
|
|
8743
|
+
}
|
|
8744
|
+
})
|
|
8745
|
+
.join("/");
|
|
8746
|
+
return normalized ? "/" + normalized : "/";
|
|
8747
|
+
};
|
|
8748
|
+
|
|
8749
|
+
const isWithinBasePath = (pathname) =>
|
|
8750
|
+
BASE_PATH === "/" || pathname === BASE_PATH || pathname.startsWith(BASE_PATH + "/");
|
|
8751
|
+
|
|
8752
|
+
const isExtensionlessPath = (pathname) => {
|
|
8753
|
+
const lastSegment = pathname.split("/").filter(Boolean).at(-1);
|
|
8754
|
+
return lastSegment === undefined || !lastSegment.includes(".");
|
|
8755
|
+
};
|
|
8756
|
+
|
|
8757
|
+
const isMarkdownPath = (pathname) =>
|
|
8758
|
+
pathname.endsWith(".md") || pathname.endsWith(".mdx");
|
|
8759
|
+
|
|
8760
|
+
const getAlternateLink = (markdownPath) =>
|
|
8761
|
+
"<" + markdownPath + '>; rel="alternate"; type="text/markdown"';
|
|
8762
|
+
|
|
8763
|
+
const continueRequest = (headers = undefined) => {
|
|
8764
|
+
const response = new Response(null);
|
|
8765
|
+
response.headers.set("x-middleware-next", "1");
|
|
8766
|
+
if (headers) {
|
|
8767
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
8768
|
+
response.headers.set(name, value);
|
|
8769
|
+
}
|
|
8770
|
+
}
|
|
8771
|
+
return response;
|
|
8772
|
+
};
|
|
8773
|
+
|
|
8774
|
+
const negotiatedHeaders = (markdownPath) => ({
|
|
8775
|
+
Vary: NEGOTIATED_VARY,
|
|
8776
|
+
Link: getAlternateLink(markdownPath),
|
|
8777
|
+
});
|
|
8778
|
+
|
|
8779
|
+
export default function middleware(request) {
|
|
8780
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
8781
|
+
return continueRequest();
|
|
8782
|
+
}
|
|
8783
|
+
|
|
8784
|
+
const requestUrl = new URL(request.url);
|
|
8785
|
+
const pathname = normalizeRequestPath(requestUrl.pathname);
|
|
8786
|
+
const markdownPath = MARKDOWN_ROUTES.get(pathname);
|
|
8787
|
+
|
|
8788
|
+
if (markdownPath) {
|
|
8789
|
+
const negotiatedType = negotiateContentType(request.headers.get("Accept"));
|
|
8790
|
+
const headers = negotiatedHeaders(markdownPath);
|
|
8791
|
+
|
|
8792
|
+
if (negotiatedType === null) {
|
|
8793
|
+
return new Response(request.method === "HEAD" ? null : "Not Acceptable", {
|
|
8794
|
+
status: 406,
|
|
8795
|
+
headers: {
|
|
8796
|
+
...headers,
|
|
8797
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
8798
|
+
},
|
|
8799
|
+
});
|
|
8800
|
+
}
|
|
8801
|
+
|
|
8802
|
+
if (negotiatedType === "text/html") {
|
|
8803
|
+
return continueRequest(headers);
|
|
8804
|
+
}
|
|
8805
|
+
|
|
8806
|
+
requestUrl.pathname = markdownPath;
|
|
8807
|
+
return new Response(null, {
|
|
8808
|
+
headers: {
|
|
8809
|
+
...headers,
|
|
8810
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
8811
|
+
"x-middleware-rewrite": requestUrl.toString(),
|
|
8812
|
+
},
|
|
8813
|
+
});
|
|
8814
|
+
}
|
|
8815
|
+
|
|
8816
|
+
if (KNOWN_ROUTES.has(pathname) || PASSTHROUGH_PATHS.has(pathname)) {
|
|
8817
|
+
return continueRequest();
|
|
8818
|
+
}
|
|
8819
|
+
|
|
8820
|
+
if (isWithinBasePath(pathname) && isMarkdownPath(pathname)) {
|
|
8821
|
+
return new Response(
|
|
8822
|
+
request.method === "HEAD" ? null : MARKDOWN_NOT_FOUND_BODY,
|
|
8823
|
+
{
|
|
8824
|
+
status: 404,
|
|
8825
|
+
headers: { "Content-Type": "text/markdown; charset=utf-8" },
|
|
8826
|
+
},
|
|
8827
|
+
);
|
|
8828
|
+
}
|
|
8829
|
+
|
|
8830
|
+
if (!isWithinBasePath(pathname) || !isExtensionlessPath(pathname)) {
|
|
8831
|
+
return continueRequest();
|
|
8832
|
+
}
|
|
8833
|
+
|
|
8834
|
+
const negotiatedType = negotiateContentType(request.headers.get("Accept"));
|
|
8835
|
+
if (negotiatedType === null) {
|
|
8836
|
+
return new Response(request.method === "HEAD" ? null : "Not Acceptable", {
|
|
8837
|
+
status: 406,
|
|
8838
|
+
headers: {
|
|
8839
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
8840
|
+
Vary: NEGOTIATED_VARY,
|
|
8841
|
+
},
|
|
8842
|
+
});
|
|
8843
|
+
}
|
|
8844
|
+
|
|
8845
|
+
if (negotiatedType === "text/html") {
|
|
8846
|
+
return continueRequest({ Vary: NEGOTIATED_VARY });
|
|
8847
|
+
}
|
|
8848
|
+
|
|
8849
|
+
return new Response(
|
|
8850
|
+
request.method === "HEAD" ? null : MARKDOWN_NOT_FOUND_BODY,
|
|
8851
|
+
{
|
|
8852
|
+
status: 404,
|
|
8853
|
+
headers: {
|
|
8854
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
8855
|
+
Vary: NEGOTIATED_VARY,
|
|
8856
|
+
},
|
|
8857
|
+
},
|
|
8858
|
+
);
|
|
8859
|
+
}
|
|
8860
|
+
`.trimStart();
|
|
8861
|
+
};
|
|
8862
|
+
|
|
8863
|
+
// src/vite/output.ts
|
|
7875
8864
|
var pkgJson = getZudokuPackageJson();
|
|
8865
|
+
var MARKDOWN_MIDDLEWARE_PATH = "zudoku-markdown";
|
|
8866
|
+
var HTML_EXTENSION = ".html";
|
|
8867
|
+
var CLEAN_URL_ROUTES = [
|
|
8868
|
+
{
|
|
8869
|
+
src: "^/(?:(.+)/)?index(?:\\.html)?/?$",
|
|
8870
|
+
headers: { Location: "/$1" },
|
|
8871
|
+
status: 308
|
|
8872
|
+
},
|
|
8873
|
+
{
|
|
8874
|
+
src: "^/(.*)\\.html/?$",
|
|
8875
|
+
headers: { Location: "/$1" },
|
|
8876
|
+
status: 308
|
|
8877
|
+
}
|
|
8878
|
+
];
|
|
8879
|
+
var listStaticFiles = async (rootDir, currentDir = rootDir) => {
|
|
8880
|
+
if (!existsSync(currentDir)) return [];
|
|
8881
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
8882
|
+
const files = await Promise.all(
|
|
8883
|
+
entries.map(async (entry) => {
|
|
8884
|
+
const entryPath = path20.join(currentDir, entry.name);
|
|
8885
|
+
if (entry.isDirectory()) {
|
|
8886
|
+
return listStaticFiles(rootDir, entryPath);
|
|
8887
|
+
}
|
|
8888
|
+
if (!entry.isFile()) return [];
|
|
8889
|
+
return [path20.relative(rootDir, entryPath).split(path20.sep).join("/")];
|
|
8890
|
+
})
|
|
8891
|
+
);
|
|
8892
|
+
return files.flat().sort();
|
|
8893
|
+
};
|
|
8894
|
+
var getCleanPath = (filename) => {
|
|
8895
|
+
const pathWithoutExtension = filename.slice(0, -HTML_EXTENSION.length);
|
|
8896
|
+
if (pathWithoutExtension === "index") return "";
|
|
8897
|
+
if (pathWithoutExtension.endsWith("/index")) {
|
|
8898
|
+
return pathWithoutExtension.slice(0, -"/index".length);
|
|
8899
|
+
}
|
|
8900
|
+
return pathWithoutExtension;
|
|
8901
|
+
};
|
|
8902
|
+
var getCleanUrlOverrides = (staticHtmlFiles) => {
|
|
8903
|
+
const sourceByCleanPath = /* @__PURE__ */ new Map();
|
|
8904
|
+
return Object.fromEntries(
|
|
8905
|
+
staticHtmlFiles.map((filename) => {
|
|
8906
|
+
const cleanPath = getCleanPath(filename);
|
|
8907
|
+
const existingSource = sourceByCleanPath.get(cleanPath);
|
|
8908
|
+
invariant(
|
|
8909
|
+
!existingSource || existingSource === filename,
|
|
8910
|
+
`Cannot generate the Vercel clean URL "${cleanPath}" for both "${existingSource}" and "${filename}"`
|
|
8911
|
+
);
|
|
8912
|
+
sourceByCleanPath.set(cleanPath, filename);
|
|
8913
|
+
return [filename, { path: cleanPath }];
|
|
8914
|
+
})
|
|
8915
|
+
);
|
|
8916
|
+
};
|
|
8917
|
+
var cleanVercelOutput = async (dir) => {
|
|
8918
|
+
if (!process.env.VERCEL) return;
|
|
8919
|
+
await rm(path20.join(dir, ".vercel/output"), {
|
|
8920
|
+
recursive: true,
|
|
8921
|
+
force: true
|
|
8922
|
+
});
|
|
8923
|
+
};
|
|
7876
8924
|
function generateOutput({
|
|
7877
8925
|
config,
|
|
7878
8926
|
redirects,
|
|
7879
|
-
rewrites = []
|
|
8927
|
+
rewrites = [],
|
|
8928
|
+
markdownNegotiation,
|
|
8929
|
+
staticHtmlFiles = []
|
|
7880
8930
|
}) {
|
|
7881
8931
|
const routes = [];
|
|
7882
|
-
|
|
8932
|
+
if (staticHtmlFiles.length > 0) {
|
|
8933
|
+
routes.push(...CLEAN_URL_ROUTES);
|
|
8934
|
+
}
|
|
8935
|
+
const uniqueRedirects = [
|
|
8936
|
+
...new Map(
|
|
8937
|
+
redirects.map((redirect) => [
|
|
8938
|
+
JSON.stringify([redirect.from, redirect.to]),
|
|
8939
|
+
redirect
|
|
8940
|
+
])
|
|
8941
|
+
).values()
|
|
8942
|
+
];
|
|
8943
|
+
for (const redirect of uniqueRedirects) {
|
|
7883
8944
|
routes.push({
|
|
7884
8945
|
src: redirect.from,
|
|
7885
8946
|
dest: redirect.to,
|
|
@@ -7904,6 +8965,19 @@ function generateOutput({
|
|
|
7904
8965
|
continue: true
|
|
7905
8966
|
});
|
|
7906
8967
|
}
|
|
8968
|
+
if (markdownNegotiation) {
|
|
8969
|
+
const basePath = joinUrl(config.basePath);
|
|
8970
|
+
const escapedBasePath = basePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8971
|
+
routes.push({
|
|
8972
|
+
// Keep the path matcher broad because valid documentation slugs can end
|
|
8973
|
+
// in dotted versions such as `/api/1.0.0`. The middleware itself
|
|
8974
|
+
// cheaply passes through assets and non-document routes.
|
|
8975
|
+
src: basePath === "/" ? "/(.*)" : `${escapedBasePath}(?:/(.*))?`,
|
|
8976
|
+
methods: ["GET", "HEAD"],
|
|
8977
|
+
middlewarePath: MARKDOWN_MIDDLEWARE_PATH,
|
|
8978
|
+
continue: true
|
|
8979
|
+
});
|
|
8980
|
+
}
|
|
7907
8981
|
if (rewrites.length > 0) {
|
|
7908
8982
|
routes.push({ handle: "filesystem" });
|
|
7909
8983
|
for (const rewrite of rewrites) {
|
|
@@ -7913,40 +8987,82 @@ function generateOutput({
|
|
|
7913
8987
|
});
|
|
7914
8988
|
}
|
|
7915
8989
|
}
|
|
8990
|
+
const overrides = getCleanUrlOverrides(staticHtmlFiles);
|
|
7916
8991
|
const output = {
|
|
7917
8992
|
version: 3,
|
|
7918
8993
|
framework: {
|
|
7919
8994
|
version: pkgJson.version
|
|
7920
8995
|
},
|
|
7921
|
-
routes
|
|
8996
|
+
routes,
|
|
8997
|
+
...Object.keys(overrides).length > 0 && { overrides }
|
|
7922
8998
|
};
|
|
7923
8999
|
return output;
|
|
7924
9000
|
}
|
|
7925
9001
|
async function writeOutput(dir, {
|
|
7926
9002
|
config,
|
|
7927
9003
|
redirects,
|
|
7928
|
-
rewrites
|
|
9004
|
+
rewrites,
|
|
9005
|
+
markdownNegotiation
|
|
7929
9006
|
}) {
|
|
7930
|
-
const
|
|
7931
|
-
const
|
|
9007
|
+
const vercelMarkdownNegotiation = process.env.VERCEL ? markdownNegotiation : void 0;
|
|
9008
|
+
const staticFiles = process.env.VERCEL ? await listStaticFiles(path20.join(dir, ".vercel/output/static")) : [];
|
|
9009
|
+
const staticHtmlFiles = staticFiles.filter(
|
|
9010
|
+
(filename) => filename.endsWith(HTML_EXTENSION)
|
|
9011
|
+
);
|
|
9012
|
+
const staticPassthroughPaths = staticFiles.filter((filename) => {
|
|
9013
|
+
const basename = path20.posix.basename(filename);
|
|
9014
|
+
return !basename.includes(".") || basename.endsWith(".md") || basename.endsWith(".mdx");
|
|
9015
|
+
}).map((filename) => `/${filename}`);
|
|
9016
|
+
const output = generateOutput({
|
|
9017
|
+
config,
|
|
9018
|
+
redirects,
|
|
9019
|
+
rewrites,
|
|
9020
|
+
markdownNegotiation: vercelMarkdownNegotiation,
|
|
9021
|
+
staticHtmlFiles
|
|
9022
|
+
});
|
|
9023
|
+
const outputDir = process.env.VERCEL ? path20.join(dir, ".vercel/output") : path20.join(dir, "dist/.output");
|
|
7932
9024
|
await mkdir3(outputDir, { recursive: true });
|
|
7933
|
-
const outputFile =
|
|
9025
|
+
const outputFile = path20.join(outputDir, "config.json");
|
|
7934
9026
|
await writeFile4(outputFile, JSON.stringify(output, null, 2), "utf-8");
|
|
7935
9027
|
if (process.env.VERCEL) {
|
|
9028
|
+
if (vercelMarkdownNegotiation) {
|
|
9029
|
+
const functionDir = path20.join(
|
|
9030
|
+
outputDir,
|
|
9031
|
+
"functions",
|
|
9032
|
+
`${MARKDOWN_MIDDLEWARE_PATH}.func`
|
|
9033
|
+
);
|
|
9034
|
+
await mkdir3(functionDir, { recursive: true });
|
|
9035
|
+
await Promise.all([
|
|
9036
|
+
writeFile4(
|
|
9037
|
+
path20.join(functionDir, "index.js"),
|
|
9038
|
+
generateVercelMarkdownMiddleware({
|
|
9039
|
+
basePath: config.basePath,
|
|
9040
|
+
...vercelMarkdownNegotiation,
|
|
9041
|
+
passthroughPaths: staticPassthroughPaths
|
|
9042
|
+
}),
|
|
9043
|
+
"utf-8"
|
|
9044
|
+
),
|
|
9045
|
+
writeFile4(
|
|
9046
|
+
path20.join(functionDir, ".vc-config.json"),
|
|
9047
|
+
JSON.stringify({ runtime: "edge", entrypoint: "index.js" }, null, 2),
|
|
9048
|
+
"utf-8"
|
|
9049
|
+
)
|
|
9050
|
+
]);
|
|
9051
|
+
}
|
|
7936
9052
|
console.log("Wrote Vercel output to", outputDir);
|
|
7937
9053
|
}
|
|
7938
9054
|
}
|
|
7939
9055
|
|
|
7940
9056
|
// src/vite/prerender/prerender.ts
|
|
7941
9057
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
7942
|
-
import { readFile as readFile3, rm } from "node:fs/promises";
|
|
9058
|
+
import { readFile as readFile3, rm as rm2 } from "node:fs/promises";
|
|
7943
9059
|
import os from "node:os";
|
|
7944
|
-
import
|
|
9060
|
+
import path23 from "node:path";
|
|
7945
9061
|
import { pathToFileURL } from "node:url";
|
|
7946
9062
|
import { createIndex } from "pagefind";
|
|
7947
9063
|
import colors7 from "picocolors";
|
|
7948
9064
|
import PiscinaImport from "piscina";
|
|
7949
|
-
|
|
9065
|
+
init_markdown_representation();
|
|
7950
9066
|
|
|
7951
9067
|
// src/vite/reporter.ts
|
|
7952
9068
|
function writeLine(output) {
|
|
@@ -7979,9 +9095,9 @@ function throttle(fn) {
|
|
|
7979
9095
|
|
|
7980
9096
|
// src/vite/sitemap.ts
|
|
7981
9097
|
init_joinUrl();
|
|
7982
|
-
import { createWriteStream, existsSync } from "node:fs";
|
|
9098
|
+
import { createWriteStream, existsSync as existsSync2 } from "node:fs";
|
|
7983
9099
|
import { mkdir as mkdir4 } from "node:fs/promises";
|
|
7984
|
-
import
|
|
9100
|
+
import path21 from "node:path";
|
|
7985
9101
|
import colors5 from "picocolors";
|
|
7986
9102
|
import { SitemapStream } from "sitemap";
|
|
7987
9103
|
async function generateSitemap({
|
|
@@ -7995,11 +9111,11 @@ async function generateSitemap({
|
|
|
7995
9111
|
return;
|
|
7996
9112
|
}
|
|
7997
9113
|
const sitemap = new SitemapStream({ hostname: config.siteUrl });
|
|
7998
|
-
const outputDir =
|
|
7999
|
-
if (!
|
|
9114
|
+
const outputDir = path21.resolve(baseOutputDir, config.outDir ?? "");
|
|
9115
|
+
if (!existsSync2(outputDir)) {
|
|
8000
9116
|
await mkdir4(outputDir, { recursive: true });
|
|
8001
9117
|
}
|
|
8002
|
-
const sitemapOutputPath =
|
|
9118
|
+
const sitemapOutputPath = path21.join(outputDir, "sitemap.xml");
|
|
8003
9119
|
const writeStream = createWriteStream(sitemapOutputPath);
|
|
8004
9120
|
sitemap.pipe(writeStream);
|
|
8005
9121
|
let lastmod;
|
|
@@ -8029,17 +9145,17 @@ async function generateSitemap({
|
|
|
8029
9145
|
|
|
8030
9146
|
// src/vite/prerender/utils.ts
|
|
8031
9147
|
init_joinUrl();
|
|
8032
|
-
var
|
|
8033
|
-
const segments =
|
|
9148
|
+
var resolveRoutePath2 = (path32) => {
|
|
9149
|
+
const segments = path32.split("/");
|
|
8034
9150
|
if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
|
|
8035
9151
|
return void 0;
|
|
8036
9152
|
}
|
|
8037
9153
|
return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
|
|
8038
9154
|
};
|
|
8039
|
-
var isSkipped = (
|
|
9155
|
+
var isSkipped = (path32) => path32.includes("*") || /^\d+$/.test(path32);
|
|
8040
9156
|
var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
|
|
8041
9157
|
if (route.path && isSkipped(route.path)) return [];
|
|
8042
|
-
const routePath = route.path ?
|
|
9158
|
+
const routePath = route.path ? resolveRoutePath2(route.path) : void 0;
|
|
8043
9159
|
if (route.path && !routePath) return [];
|
|
8044
9160
|
const fullPath = routePath ? routePath.startsWith("/") ? routePath : joinUrl(parentPath, routePath) : parentPath;
|
|
8045
9161
|
const hasStrippedParams = route.path?.split("/").some((s) => s.startsWith(":")) ?? false;
|
|
@@ -8065,6 +9181,12 @@ var collectRewrites = (resolved) => resolved.flatMap((r) => [
|
|
|
8065
9181
|
...collectRewrites(r.children)
|
|
8066
9182
|
]);
|
|
8067
9183
|
var routesToPaths = (routes) => collectPaths(resolveRoutes(routes), "");
|
|
9184
|
+
var routesToPrerenderPaths = (routes, redirects = []) => Array.from(
|
|
9185
|
+
/* @__PURE__ */ new Set([
|
|
9186
|
+
...routesToPaths(routes),
|
|
9187
|
+
...redirects.map(({ from }) => joinUrl(from))
|
|
9188
|
+
])
|
|
9189
|
+
);
|
|
8068
9190
|
var routesToRewrites = (routes) => collectRewrites(resolveRoutes(routes));
|
|
8069
9191
|
var selectPagesToIndex = (pages, paths) => {
|
|
8070
9192
|
const withUrl = pages.flatMap(({ indexStatusCode, html }, i) => {
|
|
@@ -8095,12 +9217,12 @@ var prerender = async ({
|
|
|
8095
9217
|
serverConfigFilename,
|
|
8096
9218
|
writeRedirects = true
|
|
8097
9219
|
}) => {
|
|
8098
|
-
const distDir =
|
|
9220
|
+
const distDir = path23.join(dir, "dist", basePath);
|
|
8099
9221
|
const serverConfigPath = pathToFileURL(
|
|
8100
|
-
|
|
9222
|
+
path23.join(distDir, "server", serverConfigFilename)
|
|
8101
9223
|
).href;
|
|
8102
9224
|
const entryServerPath = pathToFileURL(
|
|
8103
|
-
|
|
9225
|
+
path23.join(distDir, "server/entry.server.js")
|
|
8104
9226
|
).href;
|
|
8105
9227
|
const rawConfig = await import(serverConfigPath).then((m) => m.default);
|
|
8106
9228
|
const config = validateConfig(await runPluginTransformConfig(rawConfig));
|
|
@@ -8108,13 +9230,8 @@ var prerender = async ({
|
|
|
8108
9230
|
const module = await import(entryServerPath);
|
|
8109
9231
|
const getRoutes = module.getRoutesByConfig;
|
|
8110
9232
|
const routes = getRoutes(config);
|
|
8111
|
-
const paths =
|
|
9233
|
+
const paths = routesToPrerenderPaths(routes, config.redirects);
|
|
8112
9234
|
const rewrites = routesToRewrites(routes);
|
|
8113
|
-
if (config.redirects) {
|
|
8114
|
-
for (const r of config.redirects) {
|
|
8115
|
-
paths.push(joinUrl(r.from));
|
|
8116
|
-
}
|
|
8117
|
-
}
|
|
8118
9235
|
const { maxThreads, maxOldGenerationSizeMb } = getWorkerScaling(
|
|
8119
9236
|
buildConfig?.prerender?.workers
|
|
8120
9237
|
);
|
|
@@ -8219,7 +9336,7 @@ var prerender = async ({
|
|
|
8219
9336
|
}
|
|
8220
9337
|
if (isTTY()) writeLine("");
|
|
8221
9338
|
const { outputPath } = await pagefindIndex.writeFiles({
|
|
8222
|
-
outputPath:
|
|
9339
|
+
outputPath: path23.join(distDir, "pagefind")
|
|
8223
9340
|
});
|
|
8224
9341
|
if (outputPath) {
|
|
8225
9342
|
const duration = (performance.now() - pagefindStart) / 1e3;
|
|
@@ -8239,7 +9356,7 @@ var prerender = async ({
|
|
|
8239
9356
|
redirectUrls
|
|
8240
9357
|
});
|
|
8241
9358
|
const llmsConfig = config.docs.llms;
|
|
8242
|
-
const markdownInfoPath =
|
|
9359
|
+
const markdownInfoPath = path23.join(
|
|
8243
9360
|
dir,
|
|
8244
9361
|
"node_modules/.zudoku/markdown-info.json"
|
|
8245
9362
|
);
|
|
@@ -8258,22 +9375,38 @@ var prerender = async ({
|
|
|
8258
9375
|
siteName: config.site?.title,
|
|
8259
9376
|
llmsTxt: llmsConfig.llmsTxt,
|
|
8260
9377
|
llmsTxtFull: llmsConfig.llmsTxtFull,
|
|
9378
|
+
title: llmsConfig.title,
|
|
9379
|
+
description: llmsConfig.description,
|
|
9380
|
+
instructions: llmsConfig.instructions,
|
|
8261
9381
|
redirectUrls
|
|
8262
9382
|
});
|
|
8263
9383
|
}
|
|
9384
|
+
const contentNegotiationEnabled = config.docs.publishMarkdown && config.docs.contentNegotiation;
|
|
9385
|
+
const markdownNotFound = contentNegotiationEnabled ? getMarkdownNotFound({
|
|
9386
|
+
basePath: config.basePath,
|
|
9387
|
+
includeLlmsTxt: llmsConfig.llmsTxt ?? false,
|
|
9388
|
+
markdownRoutePaths: markdownFileInfos.map((info) => info.routePath),
|
|
9389
|
+
sitemapOutDir: config.sitemap ? config.sitemap.outDir ?? "" : void 0
|
|
9390
|
+
}) : void 0;
|
|
8264
9391
|
if (!config.docs.publishMarkdown) {
|
|
8265
9392
|
await Promise.all(
|
|
8266
9393
|
markdownFileInfos.map((info) => {
|
|
8267
9394
|
const outputPath = getMarkdownOutputPath(distDir, info.routePath);
|
|
8268
|
-
if (!
|
|
9395
|
+
if (!path23.resolve(outputPath).startsWith(path23.resolve(distDir))) {
|
|
8269
9396
|
return;
|
|
8270
9397
|
}
|
|
8271
|
-
return
|
|
9398
|
+
return rm2(outputPath).catch(() => {
|
|
8272
9399
|
});
|
|
8273
9400
|
})
|
|
8274
9401
|
);
|
|
8275
9402
|
}
|
|
8276
|
-
return {
|
|
9403
|
+
return {
|
|
9404
|
+
workerResults,
|
|
9405
|
+
rewrites,
|
|
9406
|
+
knownRoutes: paths,
|
|
9407
|
+
markdownRoutes: markdownFileInfos.map((info) => info.routePath),
|
|
9408
|
+
markdownNotFound
|
|
9409
|
+
};
|
|
8277
9410
|
};
|
|
8278
9411
|
var getWorkerScaling = (workersOverride) => {
|
|
8279
9412
|
const PER_WORKER_HEAP_LIMIT_MB = 4096;
|
|
@@ -8323,8 +9456,8 @@ var getContainerMemoryLimitMb = () => {
|
|
|
8323
9456
|
};
|
|
8324
9457
|
|
|
8325
9458
|
// src/vite/protected/build.ts
|
|
8326
|
-
import { mkdir as mkdir5, readdir, readFile as readFile4, rename, rm as
|
|
8327
|
-
import
|
|
9459
|
+
import { mkdir as mkdir5, readdir as readdir2, readFile as readFile4, rename, rm as rm3 } from "node:fs/promises";
|
|
9460
|
+
import path24 from "node:path";
|
|
8328
9461
|
init_joinUrl();
|
|
8329
9462
|
var assertProtectedPatternsCovered = (config) => {
|
|
8330
9463
|
const { patterns } = getProtectedSourceMatcher(config);
|
|
@@ -8346,11 +9479,11 @@ var findProtectedLeaks = (output) => {
|
|
|
8346
9479
|
const visited = /* @__PURE__ */ new Set();
|
|
8347
9480
|
const stack = [{ fileName: entry.fileName, path: [entry.fileName] }];
|
|
8348
9481
|
while (stack.length > 0) {
|
|
8349
|
-
const { fileName, path:
|
|
9482
|
+
const { fileName, path: path32 } = stack.pop();
|
|
8350
9483
|
if (visited.has(fileName)) continue;
|
|
8351
9484
|
visited.add(fileName);
|
|
8352
9485
|
for (const imp of byFileName.get(fileName)?.imports ?? []) {
|
|
8353
|
-
const next = [...
|
|
9486
|
+
const next = [...path32, imp];
|
|
8354
9487
|
if (isProtected(imp)) {
|
|
8355
9488
|
leaks.push(next.join(" -> "));
|
|
8356
9489
|
continue;
|
|
@@ -8381,27 +9514,27 @@ This eagerly pulls gated content into the public bundle. Check that nothing in n
|
|
|
8381
9514
|
);
|
|
8382
9515
|
};
|
|
8383
9516
|
var moveProtectedChunks = async (clientOutDir, serverOutDir) => {
|
|
8384
|
-
const srcDir =
|
|
8385
|
-
const files = await
|
|
9517
|
+
const srcDir = path24.join(clientOutDir, PROTECTED_CHUNK_DIR);
|
|
9518
|
+
const files = await readdir2(srcDir).catch((err) => {
|
|
8386
9519
|
if (err.code === "ENOENT") return null;
|
|
8387
9520
|
throw err;
|
|
8388
9521
|
});
|
|
8389
9522
|
if (!files) return;
|
|
8390
|
-
const destDir =
|
|
9523
|
+
const destDir = path24.join(serverOutDir, PROTECTED_CHUNK_DIR);
|
|
8391
9524
|
await mkdir5(destDir, { recursive: true });
|
|
8392
9525
|
await Promise.all(
|
|
8393
9526
|
files.map(
|
|
8394
|
-
(file) => rename(
|
|
9527
|
+
(file) => rename(path24.join(srcDir, file), path24.join(destDir, file))
|
|
8395
9528
|
)
|
|
8396
9529
|
);
|
|
8397
|
-
const leftover = await
|
|
9530
|
+
const leftover = await readdir2(srcDir).catch(() => []);
|
|
8398
9531
|
if (leftover.length > 0) {
|
|
8399
9532
|
throw new Error(
|
|
8400
9533
|
`moveProtectedChunks left ${leftover.length} file(s) in ${srcDir}: ${leftover.join(", ")}.
|
|
8401
9534
|
These would be served publicly. Aborting build.`
|
|
8402
9535
|
);
|
|
8403
9536
|
}
|
|
8404
|
-
await
|
|
9537
|
+
await rm3(srcDir, { recursive: true, force: true });
|
|
8405
9538
|
};
|
|
8406
9539
|
var assertCloudflareWranglerGatesProtected = async (dir, config) => {
|
|
8407
9540
|
const { enabled } = getProtectedSourceMatcher(config);
|
|
@@ -8409,7 +9542,7 @@ var assertCloudflareWranglerGatesProtected = async (dir, config) => {
|
|
|
8409
9542
|
const protectedPrefix = `${joinUrl(config.basePath, PROTECTED_CHUNK_DIR)}/`;
|
|
8410
9543
|
const candidates = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
|
|
8411
9544
|
for (const name of candidates) {
|
|
8412
|
-
const file = await readFile4(
|
|
9545
|
+
const file = await readFile4(path24.join(dir, name), "utf-8").catch(
|
|
8413
9546
|
() => void 0
|
|
8414
9547
|
);
|
|
8415
9548
|
if (file === void 0) continue;
|
|
@@ -8437,8 +9570,11 @@ async function runBuild(options) {
|
|
|
8437
9570
|
const builder2 = await createBuilder(viteConfig);
|
|
8438
9571
|
invariant(builder2.environments.client, "Client environment is missing");
|
|
8439
9572
|
invariant(builder2.environments.ssr, "SSR environment is missing");
|
|
8440
|
-
const distDir =
|
|
8441
|
-
await
|
|
9573
|
+
const distDir = path25.resolve(path25.join(dir, "dist"));
|
|
9574
|
+
await Promise.all([
|
|
9575
|
+
rm4(distDir, { recursive: true, force: true }),
|
|
9576
|
+
cleanVercelOutput(dir)
|
|
9577
|
+
]);
|
|
8442
9578
|
const [clientResult, serverResult] = await Promise.all([
|
|
8443
9579
|
builder2.build(builder2.environments.client),
|
|
8444
9580
|
builder2.build(builder2.environments.ssr)
|
|
@@ -8487,13 +9623,13 @@ async function runBuild(options) {
|
|
|
8487
9623
|
await assertCloudflareWranglerGatesProtected(dir, config);
|
|
8488
9624
|
}
|
|
8489
9625
|
await writeFile6(
|
|
8490
|
-
|
|
9626
|
+
path25.join(distDir, "package.json"),
|
|
8491
9627
|
`${JSON.stringify({ type: "module" }, null, 2)}
|
|
8492
9628
|
`,
|
|
8493
9629
|
"utf-8"
|
|
8494
9630
|
);
|
|
8495
9631
|
await writeManifest(distDir, config);
|
|
8496
|
-
await
|
|
9632
|
+
await rm4(path25.join(clientOutDir, "index.html"), { force: true });
|
|
8497
9633
|
} else {
|
|
8498
9634
|
await runPrerender({
|
|
8499
9635
|
dir,
|
|
@@ -8517,43 +9653,54 @@ var runPrerender = async (options) => {
|
|
|
8517
9653
|
const issuer = await getIssuer(config);
|
|
8518
9654
|
const serverConfigFilename = findServerConfigFilename(serverResult);
|
|
8519
9655
|
try {
|
|
8520
|
-
const {
|
|
9656
|
+
const {
|
|
9657
|
+
workerResults,
|
|
9658
|
+
rewrites,
|
|
9659
|
+
knownRoutes,
|
|
9660
|
+
markdownRoutes,
|
|
9661
|
+
markdownNotFound
|
|
9662
|
+
} = await prerender({
|
|
8521
9663
|
html,
|
|
8522
9664
|
dir,
|
|
8523
9665
|
basePath: config.basePath,
|
|
8524
9666
|
serverConfigFilename,
|
|
8525
9667
|
writeRedirects: process.env.VERCEL === void 0
|
|
8526
9668
|
});
|
|
8527
|
-
const indexHtml =
|
|
9669
|
+
const indexHtml = path25.join(clientOutDir, "index.html");
|
|
8528
9670
|
if (!workerResults.find((r) => r.outputPath === indexHtml)) {
|
|
8529
9671
|
await writeFile6(indexHtml, html, "utf-8");
|
|
8530
9672
|
}
|
|
8531
9673
|
const statusPages = workerResults.flatMap(
|
|
8532
|
-
(r) => /^(400|404|500)\.html$/.test(
|
|
9674
|
+
(r) => /^(400|404|500)\.html$/.test(path25.basename(r.outputPath)) ? r.outputPath : []
|
|
8533
9675
|
);
|
|
8534
9676
|
for (const statusPage of statusPages) {
|
|
8535
9677
|
await rename2(
|
|
8536
9678
|
statusPage,
|
|
8537
|
-
|
|
9679
|
+
path25.join(dir, DIST_DIR, path25.basename(statusPage))
|
|
8538
9680
|
);
|
|
8539
9681
|
}
|
|
8540
|
-
await
|
|
9682
|
+
await rm4(serverOutDir, { recursive: true, force: true });
|
|
8541
9683
|
if (process.env.VERCEL) {
|
|
8542
|
-
await mkdir6(
|
|
9684
|
+
await mkdir6(path25.join(dir, ".vercel/output/static"), { recursive: true });
|
|
8543
9685
|
await rename2(
|
|
8544
|
-
|
|
8545
|
-
|
|
9686
|
+
path25.join(dir, DIST_DIR),
|
|
9687
|
+
path25.join(dir, ".vercel/output/static")
|
|
8546
9688
|
);
|
|
8547
9689
|
}
|
|
8548
9690
|
await writeOutput(dir, {
|
|
8549
9691
|
config,
|
|
8550
9692
|
redirects: workerResults.flatMap((r) => r.redirect ?? []),
|
|
8551
|
-
rewrites
|
|
9693
|
+
rewrites,
|
|
9694
|
+
markdownNegotiation: markdownNotFound ? {
|
|
9695
|
+
knownCanonicalRoutePaths: knownRoutes,
|
|
9696
|
+
markdownCanonicalRoutePaths: markdownRoutes,
|
|
9697
|
+
markdownNotFoundBody: markdownNotFound
|
|
9698
|
+
} : void 0
|
|
8552
9699
|
});
|
|
8553
9700
|
if (ZuploEnv.isZuplo && issuer) {
|
|
8554
9701
|
const provider = config.authentication?.type;
|
|
8555
9702
|
await writeFile6(
|
|
8556
|
-
|
|
9703
|
+
path25.join(dir, DIST_DIR, ".output/zuplo.json"),
|
|
8557
9704
|
JSON.stringify({ issuer, provider }, null, 2),
|
|
8558
9705
|
"utf-8"
|
|
8559
9706
|
);
|
|
@@ -8565,8 +9712,8 @@ var runPrerender = async (options) => {
|
|
|
8565
9712
|
};
|
|
8566
9713
|
var findUserEntry = (dir) => {
|
|
8567
9714
|
for (const ext of ["ts", "tsx", "js", "mjs"]) {
|
|
8568
|
-
const candidate =
|
|
8569
|
-
if (
|
|
9715
|
+
const candidate = path25.join(dir, `zudoku.server.${ext}`);
|
|
9716
|
+
if (existsSync3(candidate)) return candidate;
|
|
8570
9717
|
}
|
|
8571
9718
|
};
|
|
8572
9719
|
var bundleSSREntry = async (options) => {
|
|
@@ -8576,15 +9723,15 @@ var bundleSSREntry = async (options) => {
|
|
|
8576
9723
|
let entryPoint = userEntry;
|
|
8577
9724
|
let tempEntryPath;
|
|
8578
9725
|
if (!entryPoint) {
|
|
8579
|
-
tempEntryPath =
|
|
9726
|
+
tempEntryPath = path25.join(dir, "__ssr-entry.ts");
|
|
8580
9727
|
const templateContent = await readFile5(
|
|
8581
|
-
|
|
9728
|
+
path25.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
|
|
8582
9729
|
"utf-8"
|
|
8583
9730
|
);
|
|
8584
9731
|
await writeFile6(tempEntryPath, templateContent, "utf-8");
|
|
8585
9732
|
entryPoint = tempEntryPath;
|
|
8586
9733
|
}
|
|
8587
|
-
const frameworkPath =
|
|
9734
|
+
const frameworkPath = path25.join(serverOutDir, "entry.server.js");
|
|
8588
9735
|
try {
|
|
8589
9736
|
await esbuild({
|
|
8590
9737
|
entryPoints: [entryPoint],
|
|
@@ -8592,9 +9739,9 @@ var bundleSSREntry = async (options) => {
|
|
|
8592
9739
|
platform: ["node", "lambda"].includes(adapter) ? "node" : "neutral",
|
|
8593
9740
|
target: "es2022",
|
|
8594
9741
|
format: "esm",
|
|
8595
|
-
outfile:
|
|
9742
|
+
outfile: path25.join(serverOutDir, "entry.js"),
|
|
8596
9743
|
external: ["./zudoku.config.js"],
|
|
8597
|
-
nodePaths: [
|
|
9744
|
+
nodePaths: [path25.join(packageRoot, "node_modules")],
|
|
8598
9745
|
banner: { js: "// Bundled SSR entry" },
|
|
8599
9746
|
define: {
|
|
8600
9747
|
__ZUDOKU_TEMPLATE__: JSON.stringify(html)
|
|
@@ -8611,15 +9758,15 @@ var bundleSSREntry = async (options) => {
|
|
|
8611
9758
|
]
|
|
8612
9759
|
});
|
|
8613
9760
|
await Promise.all([
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
9761
|
+
rm4(frameworkPath, { force: true }),
|
|
9762
|
+
rm4(`${frameworkPath}.map`, { force: true }),
|
|
9763
|
+
rm4(path25.join(serverOutDir, "assets"), {
|
|
8617
9764
|
recursive: true,
|
|
8618
9765
|
force: true
|
|
8619
9766
|
})
|
|
8620
9767
|
]);
|
|
8621
9768
|
} finally {
|
|
8622
|
-
if (tempEntryPath) await
|
|
9769
|
+
if (tempEntryPath) await rm4(tempEntryPath, { force: true });
|
|
8623
9770
|
}
|
|
8624
9771
|
};
|
|
8625
9772
|
|
|
@@ -8633,11 +9780,11 @@ function printWarningToConsole(message) {
|
|
|
8633
9780
|
}
|
|
8634
9781
|
|
|
8635
9782
|
// src/cli/preview/handler.ts
|
|
8636
|
-
import
|
|
9783
|
+
import path26 from "node:path";
|
|
8637
9784
|
import { preview as vitePreview } from "vite";
|
|
8638
9785
|
var DEFAULT_PREVIEW_PORT = 4e3;
|
|
8639
9786
|
async function preview(argv) {
|
|
8640
|
-
const dir =
|
|
9787
|
+
const dir = path26.resolve(process.cwd(), argv.dir);
|
|
8641
9788
|
const viteConfig = await getViteConfig(dir, {
|
|
8642
9789
|
command: "serve",
|
|
8643
9790
|
mode: "production",
|
|
@@ -8675,7 +9822,7 @@ async function build(argv) {
|
|
|
8675
9822
|
printDiagnosticsToConsole(`Starting Zudoku build v${packageJson2.version}`);
|
|
8676
9823
|
printDiagnosticsToConsole("");
|
|
8677
9824
|
printDiagnosticsToConsole("");
|
|
8678
|
-
const dir =
|
|
9825
|
+
const dir = path27.resolve(process.cwd(), argv.dir);
|
|
8679
9826
|
try {
|
|
8680
9827
|
await runBuild({
|
|
8681
9828
|
dir,
|
|
@@ -8834,13 +9981,13 @@ var build_default = {
|
|
|
8834
9981
|
|
|
8835
9982
|
// src/cli/dev/handler.ts
|
|
8836
9983
|
init_joinUrl();
|
|
8837
|
-
import
|
|
9984
|
+
import path30 from "node:path";
|
|
8838
9985
|
|
|
8839
9986
|
// src/vite/dev-server.ts
|
|
8840
|
-
import
|
|
9987
|
+
import fs4 from "node:fs/promises";
|
|
8841
9988
|
import http from "node:http";
|
|
8842
9989
|
import https from "node:https";
|
|
8843
|
-
import
|
|
9990
|
+
import path29 from "node:path";
|
|
8844
9991
|
import { stripVTControlCharacters } from "node:util";
|
|
8845
9992
|
import { createHttpTerminator } from "http-terminator";
|
|
8846
9993
|
import {
|
|
@@ -8872,7 +10019,7 @@ async function findAvailablePort(startPort) {
|
|
|
8872
10019
|
init_joinUrl();
|
|
8873
10020
|
|
|
8874
10021
|
// src/vite/pagefind-dev-index.ts
|
|
8875
|
-
import
|
|
10022
|
+
import path28 from "node:path";
|
|
8876
10023
|
import { createIndex as createIndex2 } from "pagefind";
|
|
8877
10024
|
import { isRunnableDevEnvironment } from "vite";
|
|
8878
10025
|
init_joinUrl();
|
|
@@ -8926,7 +10073,7 @@ async function* buildPagefindDevIndex(vite, config) {
|
|
|
8926
10073
|
path: urlPath
|
|
8927
10074
|
};
|
|
8928
10075
|
}
|
|
8929
|
-
const outputPath =
|
|
10076
|
+
const outputPath = path28.join(vite.config.publicDir, "pagefind");
|
|
8930
10077
|
await pagefindIndex.writeFiles({ outputPath });
|
|
8931
10078
|
yield { type: "complete", success: true, indexed };
|
|
8932
10079
|
}
|
|
@@ -8946,9 +10093,9 @@ var DevServer = class {
|
|
|
8946
10093
|
this.protocol = "https";
|
|
8947
10094
|
const { dir } = this.#options;
|
|
8948
10095
|
const [key, cert, ca] = await Promise.all([
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
config.https.ca ?
|
|
10096
|
+
fs4.readFile(path29.resolve(dir, config.https.key)),
|
|
10097
|
+
fs4.readFile(path29.resolve(dir, config.https.cert)),
|
|
10098
|
+
config.https.ca ? fs4.readFile(path29.resolve(dir, config.https.ca)) : void 0
|
|
8952
10099
|
]);
|
|
8953
10100
|
return https.createServer({ key, cert, ca });
|
|
8954
10101
|
}
|
|
@@ -8976,7 +10123,7 @@ var DevServer = class {
|
|
|
8976
10123
|
// built-in transform middleware which would treat the path as a static asset.
|
|
8977
10124
|
name: "zudoku:entry-client",
|
|
8978
10125
|
configureServer(server2) {
|
|
8979
|
-
const entryPath =
|
|
10126
|
+
const entryPath = path29.posix.join(
|
|
8980
10127
|
server2.config.base,
|
|
8981
10128
|
"/__z/entry.client.tsx"
|
|
8982
10129
|
);
|
|
@@ -9088,14 +10235,14 @@ var DevServer = class {
|
|
|
9088
10235
|
`Server-side rendering ${this.#options.ssr ? "enabled" : "disabled"}`
|
|
9089
10236
|
);
|
|
9090
10237
|
if (config.search?.type === "pagefind") {
|
|
9091
|
-
const pagefindPath =
|
|
10238
|
+
const pagefindPath = path29.join(
|
|
9092
10239
|
vite.config.publicDir,
|
|
9093
10240
|
"pagefind/pagefind.js"
|
|
9094
10241
|
);
|
|
9095
|
-
const exists = await
|
|
10242
|
+
const exists = await fs4.stat(pagefindPath).catch(() => false);
|
|
9096
10243
|
if (!exists) {
|
|
9097
|
-
await
|
|
9098
|
-
await
|
|
10244
|
+
await fs4.mkdir(path29.dirname(pagefindPath), { recursive: true });
|
|
10245
|
+
await fs4.writeFile(pagefindPath, 'throw new Error("NOT_BUILT_YET");');
|
|
9099
10246
|
}
|
|
9100
10247
|
}
|
|
9101
10248
|
vite.middlewares.use(async (req, res) => {
|
|
@@ -9196,7 +10343,7 @@ var DevServer = class {
|
|
|
9196
10343
|
async function dev(argv) {
|
|
9197
10344
|
const packageJson2 = getZudokuPackageJson();
|
|
9198
10345
|
process.env.NODE_ENV = "development";
|
|
9199
|
-
const dir =
|
|
10346
|
+
const dir = path30.resolve(process.cwd(), argv.dir);
|
|
9200
10347
|
const server = new DevServer({
|
|
9201
10348
|
dir,
|
|
9202
10349
|
argPort: argv.port,
|
|
@@ -9301,7 +10448,7 @@ var previewCommand = {
|
|
|
9301
10448
|
var preview_default = previewCommand;
|
|
9302
10449
|
|
|
9303
10450
|
// src/cli/common/outdated.ts
|
|
9304
|
-
import { existsSync as
|
|
10451
|
+
import { existsSync as existsSync4, mkdirSync } from "node:fs";
|
|
9305
10452
|
import { readFile as readFile6, writeFile as writeFile7 } from "node:fs/promises";
|
|
9306
10453
|
import { join } from "node:path";
|
|
9307
10454
|
import colors10 from "picocolors";
|
|
@@ -9352,12 +10499,12 @@ function box(message, {
|
|
|
9352
10499
|
|
|
9353
10500
|
// src/cli/common/xdg/lib.ts
|
|
9354
10501
|
import { homedir } from "node:os";
|
|
9355
|
-
import
|
|
10502
|
+
import path31 from "node:path";
|
|
9356
10503
|
function defineDirectoryWithFallback(xdgName, fallback) {
|
|
9357
10504
|
if (process.env[xdgName]) {
|
|
9358
10505
|
return process.env[xdgName];
|
|
9359
10506
|
} else {
|
|
9360
|
-
return
|
|
10507
|
+
return path31.join(homedir(), fallback);
|
|
9361
10508
|
}
|
|
9362
10509
|
}
|
|
9363
10510
|
var XDG_CONFIG_HOME = defineDirectoryWithFallback(
|
|
@@ -9372,15 +10519,15 @@ var XDG_STATE_HOME = defineDirectoryWithFallback(
|
|
|
9372
10519
|
"XDG_DATA_HOME",
|
|
9373
10520
|
".local/state"
|
|
9374
10521
|
);
|
|
9375
|
-
var ZUDOKU_XDG_CONFIG_HOME =
|
|
10522
|
+
var ZUDOKU_XDG_CONFIG_HOME = path31.join(
|
|
9376
10523
|
XDG_CONFIG_HOME,
|
|
9377
10524
|
CLI_XDG_FOLDER_NAME
|
|
9378
10525
|
);
|
|
9379
|
-
var ZUDOKU_XDG_DATA_HOME =
|
|
10526
|
+
var ZUDOKU_XDG_DATA_HOME = path31.join(
|
|
9380
10527
|
XDG_DATA_HOME,
|
|
9381
10528
|
CLI_XDG_FOLDER_NAME
|
|
9382
10529
|
);
|
|
9383
|
-
var ZUDOKU_XDG_STATE_HOME =
|
|
10530
|
+
var ZUDOKU_XDG_STATE_HOME = path31.join(
|
|
9384
10531
|
XDG_STATE_HOME,
|
|
9385
10532
|
CLI_XDG_FOLDER_NAME
|
|
9386
10533
|
);
|
|
@@ -9422,12 +10569,12 @@ async function getLatestVersion() {
|
|
|
9422
10569
|
return void 0;
|
|
9423
10570
|
}
|
|
9424
10571
|
async function getVersionCheckInfo() {
|
|
9425
|
-
if (!
|
|
10572
|
+
if (!existsSync4(ZUDOKU_XDG_STATE_HOME)) {
|
|
9426
10573
|
mkdirSync(ZUDOKU_XDG_STATE_HOME, { recursive: true });
|
|
9427
10574
|
}
|
|
9428
10575
|
const versionCheckPath = join(ZUDOKU_XDG_STATE_HOME, VERSION_CHECK_FILE);
|
|
9429
10576
|
let versionCheckInfo;
|
|
9430
|
-
if (
|
|
10577
|
+
if (existsSync4(versionCheckPath)) {
|
|
9431
10578
|
try {
|
|
9432
10579
|
versionCheckInfo = await readFile6(versionCheckPath, "utf-8").then(
|
|
9433
10580
|
JSON.parse
|