@docubook/flame 1.4.4 → 1.5.1

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 (53) hide show
  1. package/.docu/lib/build.deno.js +11 -0
  2. package/.docu/lib/build.impl-7KJ4ZTAJ.js +12 -0
  3. package/.docu/lib/build.node.js +10 -0
  4. package/.docu/lib/chunk-7ZEUL6PR.js +383 -0
  5. package/.docu/lib/chunk-AI7QAMMZ.js +2410 -0
  6. package/.docu/lib/chunk-E4OIJWCU.js +368 -0
  7. package/.docu/lib/chunk-IR5TVJOV.js +79 -0
  8. package/.docu/lib/chunk-J5NMYSBJ.js +59 -0
  9. package/.docu/lib/chunk-PTRZ2S2C.js +298 -0
  10. package/.docu/lib/chunk-RE4NGTMT.js +185 -0
  11. package/.docu/lib/chunk-TE52TIEW.js +92 -0
  12. package/.docu/lib/clean.js +32 -0
  13. package/.docu/lib/deploy.deno.js +13 -0
  14. package/.docu/lib/deploy.node.js +10 -0
  15. package/.docu/lib/preview.deno.js +10 -0
  16. package/.docu/lib/preview.node.js +10 -0
  17. package/.docu/lib/server.deno.js +11 -0
  18. package/.docu/lib/server.node.js +11 -0
  19. package/.docu/node/build.deno.ts +7 -0
  20. package/.docu/node/build.impl.ts +416 -0
  21. package/.docu/node/build.node.ts +3 -0
  22. package/.docu/node/deploy.deno.ts +11 -0
  23. package/.docu/node/deploy.node.ts +6 -0
  24. package/.docu/node/deploy.shared.ts +85 -0
  25. package/.docu/node/deploy.ts +11 -0
  26. package/.docu/node/escapeHtml.ts +18 -0
  27. package/.docu/node/git.ts +79 -0
  28. package/.docu/node/html.shared.ts +110 -0
  29. package/.docu/node/hydrate.node.ts +276 -0
  30. package/.docu/node/hydrate.ts +17 -31
  31. package/.docu/node/mdx.ts +1 -1
  32. package/.docu/node/paths.ts +24 -0
  33. package/.docu/node/plugin-builder.ts +6 -2
  34. package/.docu/node/plugin.ts +11 -2
  35. package/.docu/node/preview.deno.ts +4 -0
  36. package/.docu/node/preview.impl.ts +96 -0
  37. package/.docu/node/preview.node.ts +4 -0
  38. package/.docu/node/security.ts +5 -0
  39. package/.docu/node/server-routes.ts +4 -4
  40. package/.docu/node/server.deno.ts +4 -0
  41. package/.docu/node/server.impl.ts +184 -0
  42. package/.docu/node/server.node.ts +4 -0
  43. package/.docu/styles/globals.css +20 -5
  44. package/README.md +57 -506
  45. package/bin/cli.js +99 -14
  46. package/bin/compile-lib.mjs +67 -0
  47. package/package.json +9 -7
  48. package/template/docs/getting-started/configuration.mdx +18 -0
  49. package/template/docs/getting-started/overview.mdx +50 -0
  50. package/template/docs/guide/deployment.mdx +27 -0
  51. package/template/docs/guide/routing.mdx +25 -0
  52. package/template/docs/index.mdx +8 -205
  53. package/template/docu.json +32 -1
@@ -0,0 +1,298 @@
1
+ import {
2
+ loadDocuConfig
3
+ } from "./chunk-J5NMYSBJ.js";
4
+
5
+ // package.json
6
+ var package_default = {
7
+ name: "@docubook/flame",
8
+ version: "1.5.1",
9
+ description: "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
10
+ type: "module",
11
+ bin: {
12
+ flame: "./bin/cli.js"
13
+ },
14
+ files: [
15
+ "bin",
16
+ "template",
17
+ "docu.schema.json",
18
+ ".docu",
19
+ "!.docu/__tests__",
20
+ "!.docu/dist",
21
+ "!.docu/build-cache.json",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ publishConfig: {
26
+ registry: "https://registry.npmjs.org/",
27
+ access: "public"
28
+ },
29
+ engines: {
30
+ node: ">=20.11",
31
+ bun: ">=1.1.0"
32
+ },
33
+ scripts: {
34
+ dev: "bun .docu/node/server.ts",
35
+ build: "NODE_ENV=production bun .docu/node/build.ts",
36
+ clean: "bun .docu/node/clean.ts",
37
+ preview: "bun .docu/node/preview.ts",
38
+ deploy: "bun .docu/node/deploy.ts",
39
+ "compile:lib": "node bin/compile-lib.mjs",
40
+ test: "vitest run",
41
+ lint: "eslint .",
42
+ prepublishOnly: "bun .docu/node/clean.ts && eslint . && vitest run && node bin/compile-lib.mjs"
43
+ },
44
+ keywords: [
45
+ "docubook",
46
+ "documentation",
47
+ "static-site",
48
+ "daisyui",
49
+ "bun",
50
+ "docs framework",
51
+ "react",
52
+ "react-dom"
53
+ ],
54
+ homepage: "https://github.com/DocuBook/docubook/tree/main/packages/flame",
55
+ repository: {
56
+ type: "git",
57
+ url: "git+https://github.com/DocuBook/docubook.git",
58
+ directory: "packages/flame"
59
+ },
60
+ author: "wildan.nrs",
61
+ "author-url": "https://wildan.dev",
62
+ license: "MIT",
63
+ dependencies: {
64
+ "@docubook/core": "workspace:^",
65
+ "@docubook/mdx-content": "workspace:^",
66
+ "@docubook/runt": "workspace:^",
67
+ "@docubook/themes-colors": "workspace:^",
68
+ "@docubook/ui-react": "workspace:^",
69
+ "@tailwindcss/cli": "4.3.0",
70
+ "@tailwindcss/typography": "0.5.16",
71
+ "bun-plugin-tailwind": "^0.1.2",
72
+ daisyui: "^5.5.19",
73
+ esbuild: "^0.28.1",
74
+ "lucide-react": "^1.14.0",
75
+ react: "^19.2.7",
76
+ "react-dom": "^19.2.7",
77
+ unified: "^11.0.0"
78
+ },
79
+ peerDependencies: {
80
+ "@sentry/bun": "^10.0.0"
81
+ },
82
+ peerDependenciesMeta: {
83
+ "@sentry/bun": {
84
+ optional: true
85
+ }
86
+ },
87
+ devDependencies: {
88
+ "@eslint/js": "^10.0.1",
89
+ "@types/node": "^22.0.0",
90
+ "@types/react": "^19.0.0",
91
+ "@types/react-dom": "^19.0.0",
92
+ "bun-types": "^1.3.0",
93
+ eslint: "^10.3.0",
94
+ "eslint-plugin-perfectionist": "^5.9.0",
95
+ "eslint-plugin-react": "^7.37.5",
96
+ "eslint-plugin-react-hooks": "^7.1.1",
97
+ tailwindcss: "^4.3.0",
98
+ typescript: "^5.9.3",
99
+ "typescript-eslint": "^8.59.2"
100
+ }
101
+ };
102
+
103
+ // .docu/node/logger.ts
104
+ var docuConfig = loadDocuConfig();
105
+ var isCI = !!(process.env.CI || process.env.NO_COLOR || !process.stdout.isTTY);
106
+ var LOG_FORMAT = process.env.LOG_FORMAT || "pretty";
107
+ var isJSON = LOG_FORMAT === "json";
108
+ var LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
109
+ var currentLevel = LEVELS[process.env.LOG_LEVEL || "info"] ?? LEVELS.info;
110
+ function shouldLog(level) {
111
+ return LEVELS[level] >= currentLevel;
112
+ }
113
+ function jsonLog(level, msg, meta) {
114
+ if (!shouldLog(level)) return;
115
+ const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...meta, level, msg };
116
+ const out = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
117
+ out(JSON.stringify(entry));
118
+ }
119
+ function guard(level, msg, meta) {
120
+ if (isJSON) {
121
+ jsonLog(level, msg, meta);
122
+ return true;
123
+ }
124
+ return !shouldLog(level);
125
+ }
126
+ var c = isCI || isJSON ? {
127
+ reset: "",
128
+ bold: "",
129
+ dim: "",
130
+ green: "",
131
+ cyan: "",
132
+ magenta: "",
133
+ yellow: "",
134
+ red: "",
135
+ white: "",
136
+ gray: ""
137
+ } : {
138
+ reset: "\x1B[0m",
139
+ bold: "\x1B[1m",
140
+ dim: "\x1B[2m",
141
+ green: "\x1B[32m",
142
+ cyan: "\x1B[36m",
143
+ magenta: "\x1B[35m",
144
+ yellow: "\x1B[33m",
145
+ red: "\x1B[31m",
146
+ white: "\x1B[37m",
147
+ gray: "\x1B[90m"
148
+ };
149
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
150
+ var Spinner = class {
151
+ frame = 0;
152
+ message = "";
153
+ timer = null;
154
+ info(finalMsg) {
155
+ if (this.timer) clearInterval(this.timer);
156
+ this.timer = null;
157
+ if (isCI) {
158
+ console.log(`\u2139 ${finalMsg}`);
159
+ } else {
160
+ process.stdout.write(`\r\x1B[K${c.cyan}\u2139${c.reset} ${finalMsg}
161
+ `);
162
+ process.stdout.write("\x1B[?25h");
163
+ }
164
+ }
165
+ start(msg) {
166
+ this.message = msg;
167
+ if (isCI) {
168
+ console.log(`\u2026 ${msg}`);
169
+ return;
170
+ }
171
+ this.frame = 0;
172
+ process.stdout.write("\x1B[?25l");
173
+ this.render();
174
+ this.timer = setInterval(() => this.render(), 80);
175
+ }
176
+ stop(finalMsg) {
177
+ if (this.timer) clearInterval(this.timer);
178
+ this.timer = null;
179
+ if (isCI) {
180
+ console.log(`\u2713 ${finalMsg}`);
181
+ return;
182
+ }
183
+ process.stdout.write(`\r\x1B[K${c.green}\u2713${c.reset} ${finalMsg}
184
+ `);
185
+ process.stdout.write("\x1B[?25h");
186
+ }
187
+ render() {
188
+ const f = SPINNER_FRAMES[this.frame % SPINNER_FRAMES.length];
189
+ process.stdout.write(`\r${c.cyan}${f}${c.reset} ${this.message}`);
190
+ this.frame++;
191
+ }
192
+ };
193
+ var MAX_ROUTE_LINES = 5;
194
+ var routeLineCount = 0;
195
+ var routeTruncated = false;
196
+ function printRouteTree(routes, prefix = "", isRoot = true) {
197
+ for (let i = 0; i < routes.length; i++) {
198
+ if (routeTruncated) return;
199
+ if (routeLineCount >= MAX_ROUTE_LINES) {
200
+ routeTruncated = true;
201
+ process.stdout.write(`${prefix}${c.dim}...${c.reset}
202
+ `);
203
+ return;
204
+ }
205
+ const route = routes[i];
206
+ const last = i === routes.length - 1;
207
+ const connector = isRoot ? "" : last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
208
+ const childPrefix = isRoot ? "" : last ? " " : "\u2502 ";
209
+ const path = route.noLink ? `${c.dim}${route.href}${c.reset}` : `${c.white}${route.href}${c.reset}`;
210
+ const label = route.noLink ? `${c.gray}(group)${c.reset}` : "";
211
+ process.stdout.write(`${prefix}${connector}${path} ${label}
212
+ `);
213
+ routeLineCount++;
214
+ if (route.items?.length) {
215
+ printRouteTree(route.items, prefix + childPrefix, false);
216
+ }
217
+ }
218
+ }
219
+ var logger = {
220
+ spinner: new Spinner(),
221
+ buildStart() {
222
+ if (guard("info", "build_start", { package: "@docubook/flame", version: package_default.version })) return;
223
+ console.log(
224
+ `
225
+ ${c.bold}${c.cyan} \u{1F525} DocuBook Flame${c.reset} ${c.dim}v${package_default.version}${c.reset}
226
+ `
227
+ );
228
+ },
229
+ bundleStart() {
230
+ if (guard("info", "bundle_start")) return;
231
+ this.spinner.start("Building client bundle...");
232
+ },
233
+ bundleDone(ms) {
234
+ if (guard("info", "bundle_done", { duration_ms: ms })) return;
235
+ this.spinner.stop(`Client bundle compiled ${c.dim}(${ms}ms)${c.reset}`);
236
+ },
237
+ indexStart() {
238
+ if (guard("info", "index_start")) return;
239
+ this.spinner.start("Generating search index...");
240
+ },
241
+ indexDone(records, ms) {
242
+ if (guard("info", "index_done", { records, duration_ms: ms })) return;
243
+ this.spinner.stop(`Search index generated ${c.dim}(${records} records, ${ms}ms)${c.reset}`);
244
+ },
245
+ routes() {
246
+ if (guard("debug", "routes")) return;
247
+ const routes = docuConfig.routes;
248
+ if (!routes?.length) return;
249
+ console.log(`
250
+ ${c.bold} Routes${c.reset} ${c.dim}(${countRoutes(routes)} pages)${c.reset}
251
+ `);
252
+ routeLineCount = 0;
253
+ routeTruncated = false;
254
+ printRouteTree(routes, " ");
255
+ },
256
+ ready(port, hmr = false) {
257
+ if (guard("info", "server_ready", { port, hmr })) return;
258
+ console.log(
259
+ `
260
+ ${c.green}${c.bold} \u2713 Ready${c.reset} in ${c.bold}http://localhost:${port}/docs/${c.reset}`
261
+ );
262
+ if (hmr) console.log(`${c.dim} HMR enabled \u2014 watching docs/ for changes${c.reset}`);
263
+ console.log("");
264
+ },
265
+ request(method, pathname, status, duration_ms) {
266
+ if (guard("info", "http_request", { method, path: pathname, status, duration_ms })) return;
267
+ const color = status >= 500 ? c.red : status >= 400 ? c.gray : c.green;
268
+ const ts = (/* @__PURE__ */ new Date()).toLocaleTimeString();
269
+ const dur = duration_ms != null ? ` ${c.dim}${duration_ms}ms${c.reset}` : "";
270
+ console.log(
271
+ `${c.dim} ${ts}${c.reset} ${method} ${pathname} ${color}${status}${c.reset}${dur}`
272
+ );
273
+ },
274
+ debug(msg, meta) {
275
+ if (guard("debug", msg, meta)) return;
276
+ console.log(`${c.gray} [debug] ${msg}${c.reset}`);
277
+ },
278
+ warn(msg, meta) {
279
+ if (guard("warn", msg, meta)) return;
280
+ console.warn(`${c.yellow} \u26A0 ${msg}${c.reset}`);
281
+ },
282
+ error(msg, meta) {
283
+ if (guard("error", msg, meta)) return;
284
+ console.error(`${c.red} \u2716 ${msg}${c.reset}`);
285
+ }
286
+ };
287
+ function countRoutes(routes) {
288
+ let count = 0;
289
+ for (const r of routes) {
290
+ if (!r.noLink) count++;
291
+ if (r.items) count += countRoutes(r.items);
292
+ }
293
+ return count;
294
+ }
295
+
296
+ export {
297
+ logger
298
+ };
@@ -0,0 +1,185 @@
1
+ // .docu/node/security.ts
2
+ import { resolve } from "node:path";
3
+ import { realpathSync } from "node:fs";
4
+ var SECURITY_HEADERS = {
5
+ "Strict-Transport-Security": "max-age=63072000; includeSubDomains; preload",
6
+ "X-Frame-Options": "DENY",
7
+ "X-Content-Type-Options": "nosniff",
8
+ "Referrer-Policy": "strict-origin-when-cross-origin",
9
+ "Permissions-Policy": "camera=(), microphone=(), geolocation=()"
10
+ };
11
+ function generateNonce() {
12
+ return crypto.randomUUID();
13
+ }
14
+ function cspHeader(nonce, allowEval = false) {
15
+ const scriptSrc = allowEval ? `script-src 'self' 'nonce-${nonce}' 'unsafe-eval'` : `script-src 'self' 'nonce-${nonce}'`;
16
+ return [
17
+ "default-src 'self'",
18
+ scriptSrc,
19
+ "style-src 'self' 'unsafe-inline'",
20
+ "img-src 'self' https: data:",
21
+ "font-src 'self' data:",
22
+ "connect-src 'self' https:",
23
+ "frame-src https://www.youtube-nocookie.com",
24
+ "frame-ancestors 'none'"
25
+ ].join("; ");
26
+ }
27
+ function isPathSafe(pathname, baseDir) {
28
+ const decoded = decodeURIComponent(pathname);
29
+ const resolved = resolve(baseDir, decoded.slice(1));
30
+ const baseDirSlash = baseDir.endsWith("/") ? baseDir : baseDir + "/";
31
+ if (!(resolved === baseDir || resolved.startsWith(baseDirSlash))) {
32
+ return false;
33
+ }
34
+ if (resolved === baseDir) return true;
35
+ try {
36
+ const real = realpathSync(resolved);
37
+ const realBase = realpathSync(baseDir);
38
+ const realBaseDirSlash = realBase.endsWith("/") ? realBase : realBase + "/";
39
+ return real.startsWith(realBaseDirSlash);
40
+ } catch (err) {
41
+ const nodeErr = err;
42
+ if (nodeErr.code === "ENOENT") {
43
+ return true;
44
+ }
45
+ console.error(
46
+ `[security] isPathSafe error for pathname="${pathname}": ${nodeErr.message} (code=${nodeErr.code})`
47
+ );
48
+ return false;
49
+ }
50
+ }
51
+ function isSlugSafe(slug, docsDir) {
52
+ const resolved = resolve(docsDir, slug);
53
+ const docsDirSlash = docsDir.endsWith("/") ? docsDir : docsDir + "/";
54
+ if (!(resolved === docsDir || resolved.startsWith(docsDirSlash))) {
55
+ return false;
56
+ }
57
+ if (resolved === docsDir) return true;
58
+ try {
59
+ const real = realpathSync(resolved);
60
+ const realDocsDir = realpathSync(docsDir);
61
+ const realDocsDirSlash = realDocsDir.endsWith("/") ? realDocsDir : realDocsDir + "/";
62
+ return real.startsWith(realDocsDirSlash);
63
+ } catch (err) {
64
+ const nodeErr = err;
65
+ if (nodeErr.code === "ENOENT") {
66
+ return true;
67
+ }
68
+ console.error(
69
+ `[security] isSlugSafe error for slug="${slug}": ${nodeErr.message} (code=${nodeErr.code})`
70
+ );
71
+ return false;
72
+ }
73
+ }
74
+ function normalizeImporterPath(importer) {
75
+ return resolve(importer).replace(/\\/g, "/");
76
+ }
77
+ function injectNonce(html, nonce) {
78
+ return html.replace(/<script\b(?![^>]*\bsrc\s*=)([^>]*)>/gi, (match) => {
79
+ if (/nonce\s*=/i.test(match)) {
80
+ return match.replace(/nonce="[^"]*"/i, `nonce="${nonce}"`);
81
+ }
82
+ return match.replace(/>$/, ` nonce="${nonce}">`);
83
+ });
84
+ }
85
+ function wrapPluginResponse(pluginResponse, allowEval = false) {
86
+ const securedHeaders = new Headers(pluginResponse.headers);
87
+ for (const [key, value] of Object.entries(SECURITY_HEADERS)) {
88
+ if (!securedHeaders.has(key)) {
89
+ securedHeaders.set(key, value);
90
+ }
91
+ }
92
+ const contentType = securedHeaders.get("Content-Type") || "";
93
+ if (contentType.includes("text/html") && !securedHeaders.has("Content-Security-Policy")) {
94
+ securedHeaders.set("Content-Security-Policy", cspHeader(generateNonce(), allowEval));
95
+ }
96
+ return new Response(pluginResponse.body, {
97
+ status: pluginResponse.status,
98
+ statusText: pluginResponse.statusText,
99
+ headers: securedHeaders
100
+ });
101
+ }
102
+ function htmlResponse(html, nonce, status = 200, allowEval = false) {
103
+ return new Response(html, {
104
+ status,
105
+ headers: {
106
+ "Content-Type": "text/html",
107
+ ...SECURITY_HEADERS,
108
+ "Content-Security-Policy": cspHeader(nonce, allowEval)
109
+ }
110
+ });
111
+ }
112
+
113
+ // .docu/node/utils.ts
114
+ import { cn, parseDate, formatDate, formatDate2 } from "@docubook/core";
115
+ import { readdir, stat } from "node:fs/promises";
116
+ import { join } from "node:path";
117
+ async function scanMdxFiles(dir, baseDir = "") {
118
+ const files = [];
119
+ const entries = await readdir(dir, { withFileTypes: true });
120
+ for (const entry of entries) {
121
+ const fullPath = join(dir, entry.name);
122
+ const relativePath = baseDir ? `${baseDir}/${entry.name}` : entry.name;
123
+ if (entry.isDirectory()) {
124
+ if (entry.name === "assets" || entry.name.startsWith(".")) continue;
125
+ files.push(...await scanMdxFiles(fullPath, relativePath));
126
+ } else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
127
+ if (entry.name === "index.mdx" && !baseDir) continue;
128
+ const stats = await stat(fullPath);
129
+ let path = relativePath.replace(/\.(mdx|md)$/, "");
130
+ if (/\/index$/.test(path)) {
131
+ path = path.replace(/\/index$/, "");
132
+ }
133
+ files.push({ path, absPath: fullPath, mtime: stats.mtimeMs });
134
+ }
135
+ }
136
+ return files;
137
+ }
138
+ function isExternalUrl(url) {
139
+ return /^(https?:\/\/|\/\/)/.test(url);
140
+ }
141
+ function docsHtmlHref(path) {
142
+ return `${path}.html`;
143
+ }
144
+ function stripDocsHtmlSuffix(pathname) {
145
+ if (pathname === "/docs.html") return "/docs";
146
+ if (pathname.startsWith("/docs/") && pathname.endsWith(".html"))
147
+ return pathname.slice(0, -".html".length);
148
+ return pathname;
149
+ }
150
+ var MIME_TYPES = {
151
+ html: "text/html",
152
+ css: "text/css",
153
+ js: "application/javascript",
154
+ json: "application/json",
155
+ png: "image/png",
156
+ jpg: "image/jpeg",
157
+ jpeg: "image/jpeg",
158
+ svg: "image/svg+xml",
159
+ ico: "image/x-icon",
160
+ woff: "font/woff",
161
+ woff2: "font/woff2"
162
+ };
163
+ function getContentType(pathname) {
164
+ const ext = pathname.split(".").pop()?.toLowerCase();
165
+ return MIME_TYPES[ext || ""] || "application/octet-stream";
166
+ }
167
+
168
+ export {
169
+ SECURITY_HEADERS,
170
+ generateNonce,
171
+ cspHeader,
172
+ isPathSafe,
173
+ isSlugSafe,
174
+ normalizeImporterPath,
175
+ injectNonce,
176
+ wrapPluginResponse,
177
+ htmlResponse,
178
+ scanMdxFiles,
179
+ isExternalUrl,
180
+ docsHtmlHref,
181
+ stripDocsHtmlSuffix,
182
+ getContentType,
183
+ cn,
184
+ formatDate2
185
+ };
@@ -0,0 +1,92 @@
1
+ import {
2
+ SECURITY_HEADERS,
3
+ cspHeader,
4
+ generateNonce,
5
+ getContentType,
6
+ injectNonce
7
+ } from "./chunk-RE4NGTMT.js";
8
+ import {
9
+ logger
10
+ } from "./chunk-PTRZ2S2C.js";
11
+ import {
12
+ DIST_DIR
13
+ } from "./chunk-J5NMYSBJ.js";
14
+
15
+ // .docu/node/preview.impl.ts
16
+ import { existsSync, statSync, readFileSync } from "node:fs";
17
+ import { readFile } from "node:fs/promises";
18
+ import { resolve } from "node:path";
19
+ function resolveFile(pathname) {
20
+ const path = pathname.slice(1);
21
+ const isWithinDist = (p) => p === DIST_DIR || p.startsWith(DIST_DIR + "/");
22
+ if (!path.includes(".")) {
23
+ const withIndex = resolve(DIST_DIR, path, "index.html");
24
+ if (isWithinDist(withIndex) && existsSync(withIndex)) return withIndex;
25
+ const withHtml = resolve(DIST_DIR, path + ".html");
26
+ if (isWithinDist(withHtml) && existsSync(withHtml)) return withHtml;
27
+ }
28
+ const exact = resolve(DIST_DIR, path);
29
+ if (!isWithinDist(exact)) return null;
30
+ try {
31
+ if (statSync(exact).isFile()) return exact;
32
+ } catch (err) {
33
+ if (err.code !== "ENOENT") throw err;
34
+ }
35
+ return null;
36
+ }
37
+ async function runPreview(adapter) {
38
+ const PORT = parseInt(process.env.PORT || "4173", 10);
39
+ logger.buildStart();
40
+ if (!existsSync(DIST_DIR)) {
41
+ logger.spinner.start("Checking build output...");
42
+ logger.spinner.info("dist not found. Run \x1B[1mflame build\x1B[0m first.");
43
+ process.exit(0);
44
+ }
45
+ const notFoundPath = resolve(DIST_DIR, "404.html");
46
+ const handle = await adapter.serve(
47
+ async (req) => {
48
+ const url = new URL(req.url);
49
+ const pathname = decodeURIComponent(url.pathname);
50
+ const filePath = resolveFile(pathname);
51
+ if (filePath) {
52
+ const contentType = getContentType(filePath);
53
+ if (contentType === "text/html") {
54
+ const nonce = generateNonce();
55
+ const html = await readFile(filePath, "utf-8");
56
+ const modified = injectNonce(html, nonce);
57
+ return new Response(modified, {
58
+ headers: {
59
+ "Content-Type": "text/html",
60
+ ...SECURITY_HEADERS,
61
+ "Content-Security-Policy": cspHeader(nonce, true)
62
+ }
63
+ });
64
+ }
65
+ return new Response(readFileSync(filePath), {
66
+ headers: { "Content-Type": contentType }
67
+ });
68
+ }
69
+ if (existsSync(notFoundPath)) {
70
+ const nonce = generateNonce();
71
+ const html = await readFile(notFoundPath, "utf-8");
72
+ const modified = injectNonce(html, nonce);
73
+ return new Response(modified, {
74
+ status: 404,
75
+ headers: {
76
+ "Content-Type": "text/html",
77
+ ...SECURITY_HEADERS,
78
+ "Content-Security-Policy": cspHeader(nonce, true)
79
+ }
80
+ });
81
+ }
82
+ return new Response("404 - Not Found", { status: 404 });
83
+ },
84
+ { port: PORT }
85
+ );
86
+ logger.ready(handle.port);
87
+ return handle;
88
+ }
89
+
90
+ export {
91
+ runPreview
92
+ };
@@ -0,0 +1,32 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-PTRZ2S2C.js";
4
+ import "./chunk-J5NMYSBJ.js";
5
+
6
+ // .docu/node/clean.ts
7
+ import { rm } from "node:fs/promises";
8
+ import { existsSync } from "node:fs";
9
+ var DIST = ".docu/dist";
10
+ var CACHE = ".docu/build-cache.json";
11
+ async function clean() {
12
+ logger.buildStart();
13
+ logger.spinner.start("Cleaning build artifacts...");
14
+ let removed = 0;
15
+ if (existsSync(DIST)) {
16
+ await rm(DIST, { recursive: true, force: true });
17
+ removed++;
18
+ }
19
+ if (existsSync(CACHE)) {
20
+ await rm(CACHE, { force: true });
21
+ removed++;
22
+ }
23
+ if (removed === 0) {
24
+ logger.spinner.info("Nothing to clean");
25
+ } else {
26
+ logger.spinner.stop(`Cleaned ${removed} item(s)`);
27
+ }
28
+ }
29
+ clean().catch((err) => {
30
+ console.error("Clean failed:", err);
31
+ process.exit(1);
32
+ });
@@ -0,0 +1,13 @@
1
+ import {
2
+ runDeploy
3
+ } from "./chunk-IR5TVJOV.js";
4
+ import "./chunk-J5NMYSBJ.js";
5
+
6
+ // .docu/node/deploy.deno.ts
7
+ try {
8
+ await runDeploy();
9
+ } catch (err) {
10
+ console.error("Deploy failed:", err);
11
+ process.exit(1);
12
+ }
13
+ process.exit(0);
@@ -0,0 +1,10 @@
1
+ import {
2
+ runDeploy
3
+ } from "./chunk-IR5TVJOV.js";
4
+ import "./chunk-J5NMYSBJ.js";
5
+
6
+ // .docu/node/deploy.node.ts
7
+ runDeploy().catch((err) => {
8
+ console.error("Deploy failed:", err);
9
+ process.exit(1);
10
+ });
@@ -0,0 +1,10 @@
1
+ import {
2
+ runPreview
3
+ } from "./chunk-TE52TIEW.js";
4
+ import "./chunk-RE4NGTMT.js";
5
+ import "./chunk-PTRZ2S2C.js";
6
+ import "./chunk-J5NMYSBJ.js";
7
+
8
+ // .docu/node/preview.deno.ts
9
+ import { denoAdapter } from "@docubook/runt";
10
+ await runPreview(denoAdapter);
@@ -0,0 +1,10 @@
1
+ import {
2
+ runPreview
3
+ } from "./chunk-TE52TIEW.js";
4
+ import "./chunk-RE4NGTMT.js";
5
+ import "./chunk-PTRZ2S2C.js";
6
+ import "./chunk-J5NMYSBJ.js";
7
+
8
+ // .docu/node/preview.node.ts
9
+ import { nodeAdapter } from "@docubook/runt";
10
+ await runPreview(nodeAdapter);
@@ -0,0 +1,11 @@
1
+ import {
2
+ runServer
3
+ } from "./chunk-7ZEUL6PR.js";
4
+ import "./chunk-AI7QAMMZ.js";
5
+ import "./chunk-RE4NGTMT.js";
6
+ import "./chunk-PTRZ2S2C.js";
7
+ import "./chunk-J5NMYSBJ.js";
8
+
9
+ // .docu/node/server.deno.ts
10
+ import { denoAdapter } from "@docubook/runt";
11
+ await runServer(denoAdapter);
@@ -0,0 +1,11 @@
1
+ import {
2
+ runServer
3
+ } from "./chunk-7ZEUL6PR.js";
4
+ import "./chunk-AI7QAMMZ.js";
5
+ import "./chunk-RE4NGTMT.js";
6
+ import "./chunk-PTRZ2S2C.js";
7
+ import "./chunk-J5NMYSBJ.js";
8
+
9
+ // .docu/node/server.node.ts
10
+ import { nodeAdapter } from "@docubook/runt";
11
+ await runServer(nodeAdapter);
@@ -0,0 +1,7 @@
1
+ import { runBuildCli } from "./build.impl";
2
+
3
+ await runBuildCli();
4
+ // Deno resolves react-dom/server to server.browser.js (its `deno` export
5
+ // condition), which opens a module-scope MessageChannel that keeps the event
6
+ // loop alive after the build completes — force a clean exit.
7
+ process.exit(0);