@docubook/flame 2.0.0-beta.0 → 2.0.0-beta.2

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.
Files changed (54) hide show
  1. package/.docu/components/ScrollTo.tsx +17 -11
  2. package/.docu/components/Toc.tsx +32 -7
  3. package/.docu/lib/build.deno.js +6 -9
  4. package/.docu/lib/build.deno.js.map +1 -0
  5. package/.docu/lib/build.impl-BJsi28mD.js +377 -0
  6. package/.docu/lib/build.impl-BJsi28mD.js.map +1 -0
  7. package/.docu/lib/build.impl-yVLaa1eQ.js +2 -0
  8. package/.docu/lib/build.node.js +6 -9
  9. package/.docu/lib/build.node.js.map +1 -0
  10. package/.docu/lib/clean.js +25 -25
  11. package/.docu/lib/clean.js.map +1 -0
  12. package/.docu/lib/deno-DmEaKggj.js +20 -0
  13. package/.docu/lib/deno-DmEaKggj.js.map +1 -0
  14. package/.docu/lib/deploy.deno.js +9 -9
  15. package/.docu/lib/deploy.deno.js.map +1 -0
  16. package/.docu/lib/deploy.node.js +8 -8
  17. package/.docu/lib/deploy.node.js.map +1 -0
  18. package/.docu/lib/deploy.shared-D3UWafa6.js +295 -0
  19. package/.docu/lib/deploy.shared-D3UWafa6.js.map +1 -0
  20. package/.docu/lib/html.shared-DSn-7_6t.js +2467 -0
  21. package/.docu/lib/html.shared-DSn-7_6t.js.map +1 -0
  22. package/.docu/lib/logger-CycvLCrQ.js +312 -0
  23. package/.docu/lib/logger-CycvLCrQ.js.map +1 -0
  24. package/.docu/lib/node-DPTySdwG.js +80 -0
  25. package/.docu/lib/node-DPTySdwG.js.map +1 -0
  26. package/.docu/lib/paths-BtOIPBQ9.js +52 -0
  27. package/.docu/lib/paths-BtOIPBQ9.js.map +1 -0
  28. package/.docu/lib/preview.deno.js +7 -11
  29. package/.docu/lib/preview.deno.js.map +1 -0
  30. package/.docu/lib/preview.impl-CXJS2tQS.js +77 -0
  31. package/.docu/lib/preview.impl-CXJS2tQS.js.map +1 -0
  32. package/.docu/lib/preview.node.js +7 -11
  33. package/.docu/lib/preview.node.js.map +1 -0
  34. package/.docu/lib/server.deno.js +7 -12
  35. package/.docu/lib/server.deno.js.map +1 -0
  36. package/.docu/lib/server.impl-CJuWimfN.js +358 -0
  37. package/.docu/lib/server.impl-CJuWimfN.js.map +1 -0
  38. package/.docu/lib/server.node.js +7 -12
  39. package/.docu/lib/server.node.js.map +1 -0
  40. package/.docu/lib/utils-DA17MyQG.js +167 -0
  41. package/.docu/lib/utils-DA17MyQG.js.map +1 -0
  42. package/.docu/node/hydrate.node.ts +116 -166
  43. package/bin/compile-lib.mjs +30 -28
  44. package/package.json +6 -6
  45. package/.docu/lib/build.impl-S4DS5XPH.js +0 -12
  46. package/.docu/lib/chunk-4IQXHHPF.js +0 -62
  47. package/.docu/lib/chunk-C3RCUTUE.js +0 -470
  48. package/.docu/lib/chunk-D3QTSBR4.js +0 -434
  49. package/.docu/lib/chunk-EOK6KATZ.js +0 -191
  50. package/.docu/lib/chunk-IVM5UM44.js +0 -301
  51. package/.docu/lib/chunk-JMQI3FKV.js +0 -92
  52. package/.docu/lib/chunk-LXJLSXQI.js +0 -2691
  53. package/.docu/lib/chunk-O326MYJE.js +0 -330
  54. package/.docu/lib/chunk-UISOJ4RW.js +0 -114
@@ -1,434 +0,0 @@
1
- import {
2
- BuildPluginBuilder,
3
- DocsLayout,
4
- DocsPage,
5
- IndexPage,
6
- NotFoundPage,
7
- buildClientBundle,
8
- captureException,
9
- compileMdx,
10
- computeInlineThemeCss,
11
- errorHtml,
12
- frontmatterField,
13
- generateSearchIndex,
14
- hmrScript,
15
- htmlShell,
16
- initSentry,
17
- loadPlugins
18
- } from "./chunk-LXJLSXQI.js";
19
- import {
20
- SECURITY_HEADERS,
21
- generateNonce,
22
- getContentType,
23
- htmlResponse,
24
- isPathSafe,
25
- isSlugSafe,
26
- stripDocsHtmlSuffix,
27
- wrapPluginResponse
28
- } from "./chunk-EOK6KATZ.js";
29
- import {
30
- logger
31
- } from "./chunk-IVM5UM44.js";
32
- import {
33
- DIST_DIR,
34
- DOCS_DIR,
35
- PROJECT_ROOT,
36
- loadDocuConfig
37
- } from "./chunk-4IQXHHPF.js";
38
-
39
- // .docu/node/server.impl.ts
40
- import { watch, existsSync } from "node:fs";
41
- import { dirname, join } from "node:path";
42
- import { createRequire } from "node:module";
43
-
44
- // .docu/node/server-routes.ts
45
- import { readFile, stat } from "node:fs/promises";
46
- import { resolve } from "node:path";
47
- import { readFileSync, statSync } from "node:fs";
48
- import React from "react";
49
- import { renderToString } from "react-dom/server";
50
- function createHtmlResponse(title, description, body, status, state, depth = 0) {
51
- const nonce = generateNonce();
52
- const favicon = state.docuConfig.meta?.favicon || "/favicon.ico";
53
- const html = htmlShell({
54
- title,
55
- description,
56
- body,
57
- favicon,
58
- css: state.assetManifest.css,
59
- js: state.assetManifest.js,
60
- nonce,
61
- extraScripts: hmrScript(nonce),
62
- themeCss: state.inlineThemeCss,
63
- depth,
64
- // 404 pages can be requested at arbitrary depths (e.g. a noLink section
65
- // path typed in the address bar) — relative asset paths would resolve
66
- // against the wrong directory and break CSS/JS.
67
- absoluteAssets: status === 404
68
- });
69
- return htmlResponse(html, nonce, status, true);
70
- }
71
- var docsCache = /* @__PURE__ */ new Map();
72
- async function getDocsForSlug(slug, state) {
73
- if (!isSlugSafe(slug, DOCS_DIR)) return null;
74
- const paths = [
75
- resolve(DOCS_DIR, slug, "index.mdx"),
76
- resolve(DOCS_DIR, `${slug}.mdx`),
77
- resolve(DOCS_DIR, slug, "index.md"),
78
- resolve(DOCS_DIR, `${slug}.md`)
79
- ];
80
- const resolvedDocsDir = resolve(DOCS_DIR);
81
- let filePath = null;
82
- let raw = null;
83
- for (const p of paths) {
84
- const resolvedCandidate = resolve(p);
85
- if (resolvedCandidate !== resolvedDocsDir && !resolvedCandidate.startsWith(resolvedDocsDir + "/")) {
86
- continue;
87
- }
88
- try {
89
- raw = await readFile(resolvedCandidate, "utf-8");
90
- filePath = resolvedCandidate;
91
- break;
92
- } catch (err) {
93
- if (err.code !== "ENOENT") throw err;
94
- }
95
- }
96
- if (!filePath || !raw) return null;
97
- const relPath = filePath.replace(PROJECT_ROOT + "/", "");
98
- const mtimeMs = (await stat(filePath)).mtimeMs;
99
- const cached = docsCache.get(relPath);
100
- if (cached && cached.mtimeMs === mtimeMs) {
101
- return cached.doc;
102
- }
103
- let content = raw;
104
- if (state.builder) {
105
- const transformed = await state.builder.runOnLoad(relPath, content);
106
- if (transformed?.contents) {
107
- content = transformed.contents;
108
- }
109
- }
110
- const remarkPlugins = state.builder?.collectRemarkPlugins();
111
- const rehypePlugins = state.builder?.collectRehypePlugins();
112
- const result = await compileMdx(content, relPath, void 0, remarkPlugins, rehypePlugins);
113
- let frontmatter = result.frontmatter;
114
- if (state.builder) {
115
- frontmatter = await state.builder.runTransformFrontmatterChain(frontmatter, {
116
- slug: slug || "/",
117
- filePath: relPath,
118
- content
119
- });
120
- }
121
- const doc = {
122
- content: result.content,
123
- compiledSource: result.compiledSource,
124
- frontmatter,
125
- tocs: result.tocs,
126
- filePath: relPath,
127
- resolvedContent: content
128
- };
129
- docsCache.set(relPath, { mtimeMs, doc });
130
- return doc;
131
- }
132
- async function renderDocsServerPage(doc, slug, pathname, state) {
133
- const title = frontmatterField(doc.frontmatter, "title") || slug.join("/") || "Docs";
134
- const description = frontmatterField(doc.frontmatter, "description");
135
- const page = React.createElement(
136
- DocsLayout,
137
- { repoUrl: state.docuConfig.repo?.url, pathname },
138
- React.createElement(DocsPage, {
139
- slug,
140
- title,
141
- description,
142
- date: frontmatterField(doc.frontmatter, "date") || void 0,
143
- // Same root-relative SSR as build — see build.ts.
144
- content: renderToString(doc.content),
145
- tocs: doc.tocs,
146
- filePath: doc.filePath,
147
- repoUrl: state.docuConfig.repo?.url,
148
- compiledSource: doc.compiledSource
149
- })
150
- );
151
- const body = renderToString(page);
152
- const depth = slug.length || 1;
153
- if (state.builder) {
154
- const ctx = {
155
- slug: slug.join("/") || "/",
156
- filePath: doc.filePath,
157
- frontmatter: doc.frontmatter,
158
- content: doc.resolvedContent,
159
- config: state.docuConfig
160
- };
161
- const headExtra = state.builder.collectHead(ctx);
162
- const bodyExtra = state.builder.collectBody(ctx);
163
- const nonce = generateNonce();
164
- const favicon = state.docuConfig.meta?.favicon || "/favicon.ico";
165
- let html = htmlShell({
166
- title,
167
- description,
168
- body,
169
- favicon,
170
- css: state.assetManifest.css,
171
- js: state.assetManifest.js,
172
- nonce,
173
- extraScripts: hmrScript(nonce),
174
- themeCss: state.inlineThemeCss,
175
- headExtra,
176
- bodyExtra,
177
- depth
178
- });
179
- html = await state.builder.runTransformHtmlChain(html, ctx);
180
- return htmlResponse(html, nonce, 200, true);
181
- }
182
- return createHtmlResponse(title, description, body, 200, state, depth);
183
- }
184
- function renderPage(Component, title, description, status, state, props = {}, depth = 0) {
185
- const page = React.createElement(
186
- DocsLayout,
187
- { repoUrl: state.docuConfig.repo?.url, pathname: "/docs" },
188
- React.createElement(Component, props)
189
- );
190
- const body = renderToString(page);
191
- return createHtmlResponse(title, description, body, status, state, depth);
192
- }
193
- async function handleDocsIndex(state) {
194
- const doc = await getDocsForSlug("", state);
195
- if (!doc) return renderPage(NotFoundPage, "404 - Not Found", "", 404, state, {}, 1);
196
- return renderDocsServerPage(doc, [], "/docs", state);
197
- }
198
- async function handleDocsRoute(slug, state) {
199
- const path = slug.join("/");
200
- const doc = await getDocsForSlug(path, state);
201
- if (!doc)
202
- return renderPage(NotFoundPage, "404 - Not Found", "", 404, state, {}, slug.length || 1);
203
- return renderDocsServerPage(doc, slug, `/docs/${path}`, state);
204
- }
205
- function handleIndex(state) {
206
- const page = React.createElement(IndexPage);
207
- const body = renderToString(page);
208
- return createHtmlResponse(
209
- state.docuConfig.meta?.title || "DocuBook",
210
- state.docuConfig.meta?.description || "",
211
- body,
212
- 200,
213
- state
214
- );
215
- }
216
- function handleNotFound(state, depth = 0) {
217
- return renderPage(NotFoundPage, "404 - Not Found", "", 404, state, {}, depth);
218
- }
219
- function serveStatic(pathname) {
220
- let decoded;
221
- try {
222
- decoded = decodeURIComponent(pathname);
223
- } catch {
224
- return null;
225
- }
226
- if (!isPathSafe(pathname, DIST_DIR)) return null;
227
- const assetPath = resolve(DIST_DIR, decoded.slice(1));
228
- try {
229
- const s = statSync(assetPath);
230
- if (s.isFile()) {
231
- return new Response(readFileSync(assetPath), {
232
- headers: { "Content-Type": getContentType(pathname) }
233
- });
234
- }
235
- } catch (err) {
236
- if (err.code !== "ENOENT") throw err;
237
- }
238
- if (decoded.startsWith("/docs/assets/")) {
239
- const docsAssetsDir = resolve(DOCS_DIR, "assets");
240
- const requestedRelative = decoded.slice("/docs/assets/".length);
241
- const docsAsset = resolve(docsAssetsDir, requestedRelative);
242
- const docsAssetsDirWithSep = docsAssetsDir.endsWith("/") ? docsAssetsDir : docsAssetsDir + "/";
243
- if (docsAsset !== docsAssetsDir && !docsAsset.startsWith(docsAssetsDirWithSep)) return null;
244
- try {
245
- const s = statSync(docsAsset);
246
- if (s.isFile()) {
247
- return new Response(readFileSync(docsAsset), {
248
- headers: { "Content-Type": getContentType(pathname) }
249
- });
250
- }
251
- } catch (err) {
252
- if (err.code !== "ENOENT") throw err;
253
- }
254
- }
255
- return null;
256
- }
257
- function serverErrorResponse(error) {
258
- const msg = error instanceof Error ? error.message : "Unknown error";
259
- const st = process.env.NODE_ENV !== "production" && error instanceof Error ? error.stack : void 0;
260
- return new Response(errorHtml(msg, st), {
261
- status: 500,
262
- headers: {
263
- "Content-Type": "text/html",
264
- ...SECURITY_HEADERS,
265
- "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'"
266
- }
267
- });
268
- }
269
-
270
- // .docu/node/server.impl.ts
271
- async function runServer(adapter) {
272
- const docuConfig = loadDocuConfig();
273
- const parsedPort = parseInt(process.env.PORT ?? "3000", 10);
274
- const PORT = Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535 ? parsedPort : 3e3;
275
- logger.buildStart();
276
- await initSentry();
277
- logger.bundleStart();
278
- let t = performance.now();
279
- const assetManifest = await buildClientBundle();
280
- logger.bundleDone(Math.round(performance.now() - t));
281
- const inlineThemeCss = computeInlineThemeCss();
282
- logger.indexStart();
283
- t = performance.now();
284
- const records = await generateSearchIndex();
285
- logger.indexDone(records, Math.round(performance.now() - t));
286
- logger.routes();
287
- const pluginsConfig = docuConfig.plugins ?? [];
288
- const builder = pluginsConfig.length > 0 ? new BuildPluginBuilder(docuConfig) : null;
289
- if (builder) {
290
- const plugins = await loadPlugins(pluginsConfig);
291
- for (const plugin of plugins) {
292
- await plugin.setup(builder);
293
- }
294
- }
295
- const state = {
296
- docuConfig,
297
- assetManifest,
298
- inlineThemeCss,
299
- builder
300
- };
301
- const hmrClients = /* @__PURE__ */ new Set();
302
- const require2 = createRequire(import.meta.url);
303
- function resolveMdxContentSrc() {
304
- try {
305
- const pkgDir = dirname(require2.resolve("@docubook/markdown/package.json"));
306
- const src = join(pkgDir, "src");
307
- return existsSync(src) ? src : null;
308
- } catch {
309
- return null;
310
- }
311
- }
312
- let hmrTimeout = null;
313
- const watcher = watch(DOCS_DIR, { recursive: true }, (_event, filename) => {
314
- if (!filename || !filename.endsWith(".mdx") && !filename.endsWith(".md")) return;
315
- if (hmrTimeout) clearTimeout(hmrTimeout);
316
- hmrTimeout = setTimeout(() => {
317
- for (const client of [...hmrClients]) {
318
- try {
319
- client.enqueue(new TextEncoder().encode("data: reload\n\n"));
320
- } catch {
321
- hmrClients.delete(client);
322
- }
323
- }
324
- }, 300);
325
- });
326
- let srcHmrTimeout = null;
327
- const mdxContentSrc = resolveMdxContentSrc();
328
- const srcWatcher = mdxContentSrc ? watch(mdxContentSrc, { recursive: true }, (_event, filename) => {
329
- if (!filename || !/\.(ts|tsx|js|jsx|css)$/.test(filename)) return;
330
- if (srcHmrTimeout) clearTimeout(srcHmrTimeout);
331
- srcHmrTimeout = setTimeout(async () => {
332
- try {
333
- state.assetManifest = await buildClientBundle();
334
- logger.warn("[hmr] client bundle rebuilt (component change)");
335
- } catch (e) {
336
- logger.warn(
337
- `[hmr] bundle rebuild failed: ${e instanceof Error ? e.message : String(e)}`
338
- );
339
- }
340
- for (const client of [...hmrClients]) {
341
- try {
342
- client.enqueue(new TextEncoder().encode("data: reload\n\n"));
343
- } catch {
344
- hmrClients.delete(client);
345
- }
346
- }
347
- }, 300);
348
- }) : null;
349
- process.on("SIGINT", () => {
350
- watcher.close();
351
- srcWatcher?.close();
352
- process.exit(0);
353
- });
354
- process.on("SIGTERM", () => {
355
- watcher.close();
356
- srcWatcher?.close();
357
- process.exit(0);
358
- });
359
- let handle = null;
360
- const fetchHandler = async (req) => {
361
- const url = new URL(req.url);
362
- const pathname = stripDocsHtmlSuffix(url.pathname);
363
- const startTime = performance.now();
364
- if (builder) {
365
- const pluginResponse = await builder.runHandleRequest(req, {
366
- port: handle?.port ?? PORT,
367
- hostname: handle?.hostname ?? "localhost"
368
- });
369
- if (pluginResponse) {
370
- const securedResponse = wrapPluginResponse(pluginResponse, true);
371
- logger.request(
372
- req.method,
373
- pathname,
374
- securedResponse.status,
375
- Math.round(performance.now() - startTime)
376
- );
377
- return securedResponse;
378
- }
379
- }
380
- try {
381
- if (pathname === "/__hmr") {
382
- const stream = new ReadableStream({
383
- start(controller) {
384
- hmrClients.add(controller);
385
- controller.enqueue(new TextEncoder().encode("data: connected\n\n"));
386
- },
387
- cancel(controller) {
388
- hmrClients.delete(controller);
389
- }
390
- });
391
- return new Response(stream, {
392
- headers: {
393
- "Content-Type": "text/event-stream",
394
- "Cache-Control": "no-cache",
395
- Connection: "keep-alive"
396
- }
397
- });
398
- }
399
- if (pathname.startsWith("/assets/") || /\.\w+$/.test(pathname)) {
400
- const staticRes = serveStatic(pathname);
401
- if (staticRes) return staticRes;
402
- }
403
- let response;
404
- if (pathname === "/") {
405
- response = handleIndex(state);
406
- } else if (pathname === "/docs" || pathname === "/docs/") {
407
- response = await handleDocsIndex(state);
408
- } else if (pathname.startsWith("/docs/")) {
409
- const slug = pathname.slice("/docs/".length).split("/").filter(Boolean);
410
- response = slug.length === 0 ? await handleDocsIndex(state) : await handleDocsRoute(slug, state);
411
- } else {
412
- response = handleNotFound(state);
413
- }
414
- logger.request(
415
- req.method,
416
- pathname,
417
- response.status,
418
- Math.round(performance.now() - startTime)
419
- );
420
- return response;
421
- } catch (err) {
422
- captureException(err, { method: req.method, pathname });
423
- logger.request(req.method, pathname, 500, Math.round(performance.now() - startTime));
424
- return serverErrorResponse(err);
425
- }
426
- };
427
- handle = await adapter.serve(fetchHandler, { port: PORT, idleTimeout: 255 });
428
- logger.ready(handle.port, true);
429
- return handle;
430
- }
431
-
432
- export {
433
- runServer
434
- };
@@ -1,191 +0,0 @@
1
- // .docu/node/security.ts
2
- import { randomBytes } from "node:crypto";
3
- import { resolve } from "node:path";
4
- import { realpathSync } from "node:fs";
5
- var SECURITY_HEADERS = {
6
- "Strict-Transport-Security": "max-age=63072000; includeSubDomains; preload",
7
- "X-Frame-Options": "DENY",
8
- "X-Content-Type-Options": "nosniff",
9
- "Referrer-Policy": "strict-origin-when-cross-origin",
10
- "Permissions-Policy": "camera=(), microphone=(), geolocation=()"
11
- };
12
- function generateNonce() {
13
- return randomBytes(16).toString("base64");
14
- }
15
- function cspHeader(nonce, allowEval = false) {
16
- const scriptSrc = allowEval ? `script-src 'self' 'nonce-${nonce}' 'unsafe-eval'` : `script-src 'self' 'nonce-${nonce}'`;
17
- return [
18
- "default-src 'self'",
19
- scriptSrc,
20
- "style-src 'self' 'unsafe-inline'",
21
- "img-src 'self' https: data:",
22
- "font-src 'self' data:",
23
- "connect-src 'self' https:",
24
- "frame-src https://www.youtube-nocookie.com",
25
- "frame-ancestors 'none'"
26
- ].join("; ");
27
- }
28
- function isPathSafe(pathname, baseDir) {
29
- const decoded = decodeURIComponent(pathname);
30
- const resolved = resolve(baseDir, decoded.slice(1));
31
- const baseDirSlash = baseDir.endsWith("/") ? baseDir : baseDir + "/";
32
- if (!(resolved === baseDir || resolved.startsWith(baseDirSlash))) {
33
- return false;
34
- }
35
- if (resolved === baseDir) return true;
36
- try {
37
- const real = realpathSync(resolved);
38
- const realBase = realpathSync(baseDir);
39
- const realBaseDirSlash = realBase.endsWith("/") ? realBase : realBase + "/";
40
- return real.startsWith(realBaseDirSlash);
41
- } catch (err) {
42
- const nodeErr = err;
43
- if (nodeErr.code === "ENOENT") {
44
- return true;
45
- }
46
- console.error(
47
- `[security] isPathSafe error for pathname="${pathname}": ${nodeErr.message} (code=${nodeErr.code})`
48
- );
49
- return false;
50
- }
51
- }
52
- function isSlugSafe(slug, docsDir) {
53
- const resolved = resolve(docsDir, slug);
54
- const docsDirSlash = docsDir.endsWith("/") ? docsDir : docsDir + "/";
55
- if (!(resolved === docsDir || resolved.startsWith(docsDirSlash))) {
56
- return false;
57
- }
58
- if (resolved === docsDir) return true;
59
- try {
60
- const real = realpathSync(resolved);
61
- const realDocsDir = realpathSync(docsDir);
62
- const realDocsDirSlash = realDocsDir.endsWith("/") ? realDocsDir : realDocsDir + "/";
63
- return real.startsWith(realDocsDirSlash);
64
- } catch (err) {
65
- const nodeErr = err;
66
- if (nodeErr.code === "ENOENT") {
67
- return true;
68
- }
69
- console.error(
70
- `[security] isSlugSafe error for slug="${slug}": ${nodeErr.message} (code=${nodeErr.code})`
71
- );
72
- return false;
73
- }
74
- }
75
- function normalizeImporterPath(importer) {
76
- return resolve(importer).replace(/\\/g, "/");
77
- }
78
- function injectNonce(html, nonce) {
79
- return html.replace(/<script\b(?![^>]*\bsrc\s*=)([^>]*)>/gi, (match) => {
80
- if (/nonce\s*=/i.test(match)) {
81
- return match.replace(/nonce="[^"]*"/i, `nonce="${nonce}"`);
82
- }
83
- return match.replace(/>$/, ` nonce="${nonce}">`);
84
- });
85
- }
86
- function wrapPluginResponse(pluginResponse, allowEval = false) {
87
- const securedHeaders = new Headers(pluginResponse.headers);
88
- for (const [key, value] of Object.entries(SECURITY_HEADERS)) {
89
- if (!securedHeaders.has(key)) {
90
- securedHeaders.set(key, value);
91
- }
92
- }
93
- let body = pluginResponse.body;
94
- const contentType = securedHeaders.get("Content-Type") || "";
95
- if (contentType.includes("text/html") && !securedHeaders.has("Content-Security-Policy")) {
96
- const nonce = generateNonce();
97
- securedHeaders.set("Content-Security-Policy", cspHeader(nonce, allowEval));
98
- if (typeof body === "string") {
99
- body = injectNonce(body, nonce);
100
- }
101
- }
102
- return new Response(body, {
103
- status: pluginResponse.status,
104
- statusText: pluginResponse.statusText,
105
- headers: securedHeaders
106
- });
107
- }
108
- function htmlResponse(html, nonce, status = 200, allowEval = false) {
109
- return new Response(html, {
110
- status,
111
- headers: {
112
- "Content-Type": "text/html",
113
- ...SECURITY_HEADERS,
114
- "Content-Security-Policy": cspHeader(nonce, allowEval)
115
- }
116
- });
117
- }
118
-
119
- // .docu/node/utils.ts
120
- import { cn, parseDate, formatDate, formatDate2 } from "@docubook/core";
121
- import { readdir, stat } from "node:fs/promises";
122
- import { join } from "node:path";
123
- async function scanMdxFiles(dir, baseDir = "") {
124
- const files = [];
125
- const entries = await readdir(dir, { withFileTypes: true });
126
- for (const entry of entries) {
127
- const fullPath = join(dir, entry.name);
128
- const relativePath = baseDir ? `${baseDir}/${entry.name}` : entry.name;
129
- if (entry.isDirectory()) {
130
- if (entry.name === "assets" || entry.name.startsWith(".")) continue;
131
- files.push(...await scanMdxFiles(fullPath, relativePath));
132
- } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
133
- if (entry.name === "index.mdx" && !baseDir) continue;
134
- const stats = await stat(fullPath);
135
- let path = relativePath.replace(/\.(mdx|md)$/, "");
136
- if (/\/index$/.test(path)) {
137
- path = path.replace(/\/index$/, "");
138
- }
139
- files.push({ path, absPath: fullPath, mtime: stats.mtimeMs });
140
- }
141
- }
142
- return files;
143
- }
144
- function isExternalUrl(url) {
145
- return /^(https?:\/\/|\/\/)/.test(url);
146
- }
147
- function docsHtmlHref(path) {
148
- return `${path}.html`;
149
- }
150
- function stripDocsHtmlSuffix(pathname) {
151
- if (pathname === "/docs.html") return "/docs";
152
- if (pathname.startsWith("/docs/") && pathname.endsWith(".html"))
153
- return pathname.slice(0, -".html".length);
154
- return pathname;
155
- }
156
- var MIME_TYPES = {
157
- html: "text/html",
158
- css: "text/css",
159
- js: "application/javascript",
160
- json: "application/json",
161
- png: "image/png",
162
- jpg: "image/jpeg",
163
- jpeg: "image/jpeg",
164
- svg: "image/svg+xml",
165
- ico: "image/x-icon",
166
- woff: "font/woff",
167
- woff2: "font/woff2"
168
- };
169
- function getContentType(pathname) {
170
- const ext = pathname.split(".").pop()?.toLowerCase();
171
- return MIME_TYPES[ext || ""] || "application/octet-stream";
172
- }
173
-
174
- export {
175
- SECURITY_HEADERS,
176
- generateNonce,
177
- cspHeader,
178
- isPathSafe,
179
- isSlugSafe,
180
- normalizeImporterPath,
181
- injectNonce,
182
- wrapPluginResponse,
183
- htmlResponse,
184
- scanMdxFiles,
185
- isExternalUrl,
186
- docsHtmlHref,
187
- stripDocsHtmlSuffix,
188
- getContentType,
189
- cn,
190
- formatDate2
191
- };