blodemd 0.0.5 → 0.0.7
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/README.md +2 -2
- package/dev-server/app/[[...slug]]/page.tsx +139 -0
- package/dev-server/app/blodemd-dev/invalidate/route.ts +12 -0
- package/dev-server/app/blodemd-dev/static/[...path]/route.ts +32 -0
- package/dev-server/app/blodemd-dev/version/route.ts +14 -0
- package/dev-server/app/blodemd-internal/proxy/route.ts +86 -0
- package/dev-server/app/error.tsx +24 -0
- package/dev-server/app/favicon.ico +0 -0
- package/dev-server/app/globals.css +4 -0
- package/dev-server/app/layout.tsx +38 -0
- package/dev-server/app/not-found.tsx +18 -0
- package/dev-server/app/search/route.ts +17 -0
- package/dev-server/components/dev-reload-script.tsx +86 -0
- package/dev-server/components/providers.tsx +15 -0
- package/dev-server/lib/dev-state.ts +8 -0
- package/dev-server/lib/local-content-source.ts +103 -0
- package/dev-server/lib/local-runtime.tsx +558 -0
- package/dev-server/next-env.d.ts +5 -0
- package/dev-server/next.config.js +46 -0
- package/dev-server/package.json +57 -0
- package/dev-server/postcss.config.mjs +7 -0
- package/dev-server/public/glide-variable.woff2 +0 -0
- package/dev-server/tsconfig.json +50 -0
- package/dist/cli.mjs +311 -86
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +457 -0
- package/docs/components/api/api-playground.tsx +295 -0
- package/docs/components/api/api-reference.tsx +121 -0
- package/docs/components/content/collection-index.tsx +114 -0
- package/docs/components/docs/contextual-menu.tsx +406 -0
- package/docs/components/docs/copy-page-menu.tsx +255 -0
- package/docs/components/docs/doc-header.tsx +210 -0
- package/docs/components/docs/doc-shell.tsx +313 -0
- package/docs/components/docs/doc-sidebar.tsx +211 -0
- package/docs/components/docs/doc-toc.tsx +45 -0
- package/docs/components/docs/mobile-nav.tsx +205 -0
- package/docs/components/icons/doc-icon.tsx +96 -0
- package/docs/components/mdx/accordion.tsx +83 -0
- package/docs/components/mdx/badge.tsx +79 -0
- package/docs/components/mdx/callout.tsx +88 -0
- package/docs/components/mdx/card.tsx +110 -0
- package/docs/components/mdx/code-block.tsx +75 -0
- package/docs/components/mdx/code-group.tsx +94 -0
- package/docs/components/mdx/color.tsx +87 -0
- package/docs/components/mdx/columns.tsx +25 -0
- package/docs/components/mdx/expandable.tsx +45 -0
- package/docs/components/mdx/field-layout.tsx +77 -0
- package/docs/components/mdx/frame.tsx +23 -0
- package/docs/components/mdx/get-text-content.ts +18 -0
- package/docs/components/mdx/icon.tsx +12 -0
- package/docs/components/mdx/index.tsx +107 -0
- package/docs/components/mdx/installer.tsx +20 -0
- package/docs/components/mdx/panel.tsx +11 -0
- package/docs/components/mdx/param-field.tsx +56 -0
- package/docs/components/mdx/preview.tsx +36 -0
- package/docs/components/mdx/prompt.tsx +63 -0
- package/docs/components/mdx/request-example.tsx +27 -0
- package/docs/components/mdx/response-field.tsx +42 -0
- package/docs/components/mdx/steps.tsx +92 -0
- package/docs/components/mdx/tabs.tsx +88 -0
- package/docs/components/mdx/tile.tsx +43 -0
- package/docs/components/mdx/tooltip.tsx +71 -0
- package/docs/components/mdx/tree.tsx +120 -0
- package/docs/components/mdx/type-table.tsx +71 -0
- package/docs/components/mdx/update.tsx +44 -0
- package/docs/components/mdx/video.tsx +12 -0
- package/docs/components/mdx/view.tsx +66 -0
- package/docs/components/providers.tsx +15 -0
- package/docs/components/ui/breadcrumb.tsx +92 -0
- package/docs/components/ui/button.tsx +90 -0
- package/docs/components/ui/card.tsx +92 -0
- package/docs/components/ui/command.tsx +139 -0
- package/docs/components/ui/dialog.tsx +97 -0
- package/docs/components/ui/field.tsx +237 -0
- package/docs/components/ui/input.tsx +105 -0
- package/docs/components/ui/label.tsx +22 -0
- package/docs/components/ui/popover.tsx +72 -0
- package/docs/components/ui/search.tsx +384 -0
- package/docs/components/ui/separator.tsx +26 -0
- package/docs/components/ui/sheet.tsx +104 -0
- package/docs/components/ui/sidebar.tsx +433 -0
- package/docs/components/ui/theme-toggle.tsx +62 -0
- package/docs/components/ui/tooltip.tsx +53 -0
- package/docs/lib/contextual-options.ts +193 -0
- package/docs/lib/docs-collection.ts +22 -0
- package/docs/lib/mdx.ts +87 -0
- package/docs/lib/navigation.ts +288 -0
- package/docs/lib/openapi.ts +158 -0
- package/docs/lib/routes.ts +44 -0
- package/docs/lib/server-cache.ts +83 -0
- package/docs/lib/shiki.ts +40 -0
- package/docs/lib/theme.ts +29 -0
- package/docs/lib/toc.ts +2 -0
- package/docs/lib/utils.ts +5 -0
- package/package.json +43 -6
- package/packages/@repo/common/dist/index.d.ts +9 -0
- package/packages/@repo/common/dist/index.d.ts.map +1 -0
- package/packages/@repo/common/dist/index.js +42 -0
- package/packages/@repo/common/package.json +34 -0
- package/packages/@repo/common/src/index.ts +51 -0
- package/packages/@repo/contracts/dist/api-key.d.ts +30 -0
- package/packages/@repo/contracts/dist/api-key.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/api-key.js +20 -0
- package/packages/@repo/contracts/dist/dates.d.ts +4 -0
- package/packages/@repo/contracts/dist/dates.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/dates.js +2 -0
- package/packages/@repo/contracts/dist/deployment.d.ts +71 -0
- package/packages/@repo/contracts/dist/deployment.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/deployment.js +46 -0
- package/packages/@repo/contracts/dist/domain.d.ts +94 -0
- package/packages/@repo/contracts/dist/domain.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/domain.js +36 -0
- package/packages/@repo/contracts/dist/ids.d.ts +14 -0
- package/packages/@repo/contracts/dist/ids.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/ids.js +10 -0
- package/packages/@repo/contracts/dist/index.d.ts +10 -0
- package/packages/@repo/contracts/dist/index.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/index.js +11 -0
- package/packages/@repo/contracts/dist/pagination.d.ts +23 -0
- package/packages/@repo/contracts/dist/pagination.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/pagination.js +15 -0
- package/packages/@repo/contracts/dist/project.d.ts +25 -0
- package/packages/@repo/contracts/dist/project.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/project.js +23 -0
- package/packages/@repo/contracts/dist/tenant.d.ts +111 -0
- package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/tenant.js +56 -0
- package/packages/@repo/contracts/dist/user.d.ts +9 -0
- package/packages/@repo/contracts/dist/user.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/user.js +9 -0
- package/packages/@repo/contracts/package.json +37 -0
- package/packages/@repo/contracts/src/api-key.ts +27 -0
- package/packages/@repo/contracts/src/dates.ts +4 -0
- package/packages/@repo/contracts/src/deployment.ts +73 -0
- package/packages/@repo/contracts/src/domain.ts +51 -0
- package/packages/@repo/contracts/src/ids.ts +22 -0
- package/packages/@repo/contracts/src/index.ts +11 -0
- package/packages/@repo/contracts/src/pagination.ts +21 -0
- package/packages/@repo/contracts/src/project.ts +30 -0
- package/packages/@repo/contracts/src/tenant.ts +92 -0
- package/packages/@repo/contracts/src/user.ts +12 -0
- package/packages/@repo/models/dist/docs-config.d.ts +985 -0
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -0
- package/packages/@repo/models/dist/docs-config.js +548 -0
- package/packages/@repo/models/dist/index.d.ts +3 -0
- package/packages/@repo/models/dist/index.d.ts.map +1 -0
- package/packages/@repo/models/dist/index.js +3 -0
- package/packages/@repo/models/dist/tenant.d.ts +25 -0
- package/packages/@repo/models/dist/tenant.d.ts.map +1 -0
- package/packages/@repo/models/dist/tenant.js +1 -0
- package/packages/@repo/models/package.json +37 -0
- package/packages/@repo/models/src/docs-config.ts +648 -0
- package/packages/@repo/models/src/index.ts +3 -0
- package/packages/@repo/models/src/tenant.ts +29 -0
- package/packages/@repo/prebuild/dist/index.d.ts +2 -0
- package/packages/@repo/prebuild/dist/index.d.ts.map +1 -0
- package/packages/@repo/prebuild/dist/index.js +2 -0
- package/packages/@repo/prebuild/dist/openapi.d.ts +43 -0
- package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -0
- package/packages/@repo/prebuild/dist/openapi.js +58 -0
- package/packages/@repo/prebuild/package.json +39 -0
- package/packages/@repo/prebuild/src/index.ts +2 -0
- package/packages/@repo/prebuild/src/openapi.ts +116 -0
- package/packages/@repo/previewing/dist/blob-source.d.ts +16 -0
- package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/blob-source.js +110 -0
- package/packages/@repo/previewing/dist/content-source.d.ts +12 -0
- package/packages/@repo/previewing/dist/content-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/content-source.js +1 -0
- package/packages/@repo/previewing/dist/fs-source.d.ts +11 -0
- package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/fs-source.js +72 -0
- package/packages/@repo/previewing/dist/index.d.ts +120 -0
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/index.js +984 -0
- package/packages/@repo/previewing/package.json +41 -0
- package/packages/@repo/previewing/src/blob-source.ts +167 -0
- package/packages/@repo/previewing/src/content-source.ts +12 -0
- package/packages/@repo/previewing/src/fs-source.ts +104 -0
- package/packages/@repo/previewing/src/index.ts +1490 -0
- package/packages/@repo/validation/dist/index.d.ts +12 -0
- package/packages/@repo/validation/dist/index.d.ts.map +1 -0
- package/packages/@repo/validation/dist/index.js +30 -0
- package/packages/@repo/validation/package.json +37 -0
- package/packages/@repo/validation/src/index.ts +59 -0
- package/packages/@repo/validation/src/mintlify-docs-schema.json +5016 -0
- package/scripts/prepare-package.mjs +39 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"declarationMap": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"incremental": false,
|
|
8
|
+
"isolatedModules": true,
|
|
9
|
+
"jsx": "react-jsx",
|
|
10
|
+
"lib": [
|
|
11
|
+
"es2022",
|
|
12
|
+
"DOM",
|
|
13
|
+
"DOM.Iterable"
|
|
14
|
+
],
|
|
15
|
+
"module": "ESNext",
|
|
16
|
+
"moduleDetection": "force",
|
|
17
|
+
"moduleResolution": "Bundler",
|
|
18
|
+
"noEmit": true,
|
|
19
|
+
"noUncheckedIndexedAccess": true,
|
|
20
|
+
"paths": {
|
|
21
|
+
"@/*": [
|
|
22
|
+
"../docs/*"
|
|
23
|
+
],
|
|
24
|
+
"@dev/*": [
|
|
25
|
+
"./*"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"plugins": [
|
|
29
|
+
{
|
|
30
|
+
"name": "next"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"resolveJsonModule": true,
|
|
34
|
+
"skipLibCheck": true,
|
|
35
|
+
"strict": true,
|
|
36
|
+
"strictNullChecks": true,
|
|
37
|
+
"target": "ES2022"
|
|
38
|
+
},
|
|
39
|
+
"exclude": [
|
|
40
|
+
"node_modules"
|
|
41
|
+
],
|
|
42
|
+
"include": [
|
|
43
|
+
"**/*.ts",
|
|
44
|
+
"**/*.tsx",
|
|
45
|
+
"next-env.d.ts",
|
|
46
|
+
"next.config.js",
|
|
47
|
+
".next/types/**/*.ts",
|
|
48
|
+
".next/dev/types/**/*.ts"
|
|
49
|
+
]
|
|
50
|
+
}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
4
|
import fs, { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
5
|
import path, { join } from "node:path";
|
|
@@ -8,12 +9,14 @@ import { Command } from "commander";
|
|
|
8
9
|
import open from "open";
|
|
9
10
|
import { homedir } from "node:os";
|
|
10
11
|
import { once } from "node:events";
|
|
12
|
+
import { createServer } from "node:net";
|
|
11
13
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
12
14
|
import { fileURLToPath } from "node:url";
|
|
13
15
|
import { createFsSource, loadSiteConfig } from "@repo/previewing";
|
|
14
16
|
import { watch } from "chokidar";
|
|
15
|
-
import { createServer } from "node:http";
|
|
17
|
+
import { createServer as createServer$1 } from "node:http";
|
|
16
18
|
import { createHash, randomBytes } from "node:crypto";
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
17
20
|
//#region src/constants.ts
|
|
18
21
|
const CLI_NAME = "blodemd";
|
|
19
22
|
const OAUTH_CLIENT_ID = "6b5f9860-fe96-4a83-b1ad-266260523c91";
|
|
@@ -288,9 +291,20 @@ const resolveTokenStatus = (token) => {
|
|
|
288
291
|
};
|
|
289
292
|
};
|
|
290
293
|
//#endregion
|
|
294
|
+
//#region src/site-config.ts
|
|
295
|
+
const CONFIG_FILE$2 = "docs.json";
|
|
296
|
+
const loadValidatedSiteConfig = async (root) => {
|
|
297
|
+
const result = await loadSiteConfig(createFsSource(root));
|
|
298
|
+
if (!result.ok) throw new CliError(result.errors.join("\n"), EXIT_CODES.VALIDATION, `Make sure ${CONFIG_FILE$2} exists and is valid JSON.`);
|
|
299
|
+
return {
|
|
300
|
+
config: result.config,
|
|
301
|
+
warnings: result.warnings
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
//#endregion
|
|
291
305
|
//#region src/dev/resolve-root.ts
|
|
292
306
|
const CONFIG_FILE$1 = "docs.json";
|
|
293
|
-
const fileExists$
|
|
307
|
+
const fileExists$1 = async (filePath) => {
|
|
294
308
|
try {
|
|
295
309
|
await fs.access(filePath);
|
|
296
310
|
return true;
|
|
@@ -298,21 +312,17 @@ const fileExists$2 = async (filePath) => {
|
|
|
298
312
|
return false;
|
|
299
313
|
}
|
|
300
314
|
};
|
|
301
|
-
const resolveDocsRoot
|
|
315
|
+
const resolveDocsRoot = async (dir) => {
|
|
302
316
|
if (dir) return path.resolve(process.cwd(), dir);
|
|
303
317
|
const candidates = [
|
|
304
318
|
process.cwd(),
|
|
305
319
|
path.join(process.cwd(), "docs"),
|
|
306
320
|
path.join(process.cwd(), "apps/docs")
|
|
307
321
|
];
|
|
308
|
-
for (const candidate of candidates) if (await fileExists$
|
|
322
|
+
for (const candidate of candidates) if (await fileExists$1(path.join(candidate, CONFIG_FILE$1))) return candidate;
|
|
309
323
|
return process.cwd();
|
|
310
324
|
};
|
|
311
|
-
const validateDocsRoot = async (root) =>
|
|
312
|
-
const result = await loadSiteConfig(createFsSource(root));
|
|
313
|
-
if (!result.ok) throw new CliError(result.errors.join("\n"), EXIT_CODES.VALIDATION, `Make sure ${CONFIG_FILE$1} exists and is valid JSON.`);
|
|
314
|
-
return result;
|
|
315
|
-
};
|
|
325
|
+
const validateDocsRoot = async (root) => await loadValidatedSiteConfig(root);
|
|
316
326
|
//#endregion
|
|
317
327
|
//#region src/dev/watcher.ts
|
|
318
328
|
const INVALIDATE_ENDPOINT = "/blodemd-dev/invalidate";
|
|
@@ -375,12 +385,20 @@ const createDevWatcher = ({ port, root }) => {
|
|
|
375
385
|
//#region src/dev/command.ts
|
|
376
386
|
const DEV_READY_ENDPOINT = "/blodemd-dev/version";
|
|
377
387
|
const DEV_READY_TIMEOUT_MS = 45e3;
|
|
388
|
+
const DEV_PORT_SCAN_LIMIT = 10;
|
|
389
|
+
const DEV_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
390
|
+
const LOCALHOST = "127.0.0.1";
|
|
391
|
+
const RUNTIME_EXCLUDE_DIRS = new Set([
|
|
392
|
+
".next",
|
|
393
|
+
".turbo",
|
|
394
|
+
"node_modules"
|
|
395
|
+
]);
|
|
378
396
|
const parsePositiveInteger$1 = (value, label) => {
|
|
379
397
|
const parsed = Number.parseInt(value, 10);
|
|
380
398
|
if (!Number.isInteger(parsed) || parsed <= 0) throw new CliError(`${label} must be a positive integer.`, EXIT_CODES.VALIDATION);
|
|
381
399
|
return parsed;
|
|
382
400
|
};
|
|
383
|
-
const fileExists
|
|
401
|
+
const fileExists = async (filePath) => {
|
|
384
402
|
try {
|
|
385
403
|
await fs.access(filePath);
|
|
386
404
|
return true;
|
|
@@ -388,11 +406,149 @@ const fileExists$1 = async (filePath) => {
|
|
|
388
406
|
return false;
|
|
389
407
|
}
|
|
390
408
|
};
|
|
409
|
+
const probePortAvailability = async (port) => {
|
|
410
|
+
const server = createServer();
|
|
411
|
+
const listening = (async () => {
|
|
412
|
+
await once(server, "listening");
|
|
413
|
+
return { kind: "listening" };
|
|
414
|
+
})();
|
|
415
|
+
const errored = (async () => {
|
|
416
|
+
const [error] = await once(server, "error");
|
|
417
|
+
return {
|
|
418
|
+
error,
|
|
419
|
+
kind: "error"
|
|
420
|
+
};
|
|
421
|
+
})();
|
|
422
|
+
server.listen({
|
|
423
|
+
exclusive: true,
|
|
424
|
+
host: LOCALHOST,
|
|
425
|
+
port
|
|
426
|
+
});
|
|
427
|
+
const outcome = await Promise.race([listening, errored]);
|
|
428
|
+
if (outcome.kind === "error") {
|
|
429
|
+
if (outcome.error.code === "EADDRINUSE" || outcome.error.code === "EACCES") return false;
|
|
430
|
+
throw outcome.error;
|
|
431
|
+
}
|
|
432
|
+
server.close();
|
|
433
|
+
await once(server, "close");
|
|
434
|
+
return true;
|
|
435
|
+
};
|
|
436
|
+
const resolveDevPort = async (requestedPort, probePort = probePortAvailability) => {
|
|
437
|
+
for (let offset = 0; offset < DEV_PORT_SCAN_LIMIT; offset += 1) {
|
|
438
|
+
const candidate = requestedPort + offset;
|
|
439
|
+
if (candidate > 65535) break;
|
|
440
|
+
if (await probePort(candidate)) return candidate;
|
|
441
|
+
}
|
|
442
|
+
throw new CliError(`No available port found within ${DEV_PORT_SCAN_LIMIT} attempts starting at ${requestedPort}.`, EXIT_CODES.ERROR, "Close the process using the port or pass a different --port value.");
|
|
443
|
+
};
|
|
444
|
+
const shutdownChildProcess = async (child, timeoutMs = DEV_SHUTDOWN_TIMEOUT_MS) => {
|
|
445
|
+
if (child.exitCode !== null) return;
|
|
446
|
+
const timer = setTimeout(() => {
|
|
447
|
+
if (child.exitCode === null) child.kill("SIGKILL");
|
|
448
|
+
}, timeoutMs);
|
|
449
|
+
const exitPromise = once(child, "exit");
|
|
450
|
+
try {
|
|
451
|
+
child.kill("SIGTERM");
|
|
452
|
+
} catch (error) {
|
|
453
|
+
clearTimeout(timer);
|
|
454
|
+
if (error.code === "ESRCH") return;
|
|
455
|
+
throw error;
|
|
456
|
+
}
|
|
457
|
+
await exitPromise.finally(() => {
|
|
458
|
+
clearTimeout(timer);
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* Derive the CLI npm package root from the running script path.
|
|
463
|
+
* The CLI entry point is at `<pkg-root>/dist/cli.mjs`.
|
|
464
|
+
*/
|
|
465
|
+
const resolveCliPackageRoot = (cliFilePath) => path.dirname(path.dirname(cliFilePath));
|
|
466
|
+
const copyStandaloneTree = async (sourceDir, targetDir) => {
|
|
467
|
+
await fs.cp(sourceDir, targetDir, {
|
|
468
|
+
filter: (source) => {
|
|
469
|
+
const relative = path.relative(sourceDir, source);
|
|
470
|
+
if (!relative) return true;
|
|
471
|
+
const topSegment = relative.split(path.sep)[0] ?? "";
|
|
472
|
+
return !RUNTIME_EXCLUDE_DIRS.has(topSegment);
|
|
473
|
+
},
|
|
474
|
+
recursive: true
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
const isStandaloneCliInstall = async (cliPackageRoot) => {
|
|
478
|
+
try {
|
|
479
|
+
return (await fs.realpath(cliPackageRoot)).split(path.sep).includes("node_modules");
|
|
480
|
+
} catch {
|
|
481
|
+
return cliPackageRoot.split(path.sep).includes("node_modules");
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
const materializeStandaloneRuntime = async (cliPackageRoot) => {
|
|
485
|
+
const runtimeRoot = path.join(CONFIG_DIR, "standalone-runtime");
|
|
486
|
+
await fs.rm(runtimeRoot, {
|
|
487
|
+
force: true,
|
|
488
|
+
recursive: true
|
|
489
|
+
});
|
|
490
|
+
await fs.mkdir(runtimeRoot, { recursive: true });
|
|
491
|
+
for (const dir of [
|
|
492
|
+
"dev-server",
|
|
493
|
+
"docs",
|
|
494
|
+
"packages"
|
|
495
|
+
]) await copyStandaloneTree(path.join(cliPackageRoot, dir), path.join(runtimeRoot, dir));
|
|
496
|
+
await fs.symlink(path.join(cliPackageRoot, "node_modules"), path.join(runtimeRoot, "node_modules"), process.platform === "win32" ? "junction" : "dir");
|
|
497
|
+
await fs.writeFile(path.join(runtimeRoot, "dev-server", "package.json"), `${JSON.stringify({
|
|
498
|
+
dependencies: {
|
|
499
|
+
next: "16.2.1",
|
|
500
|
+
react: "^19.2.0",
|
|
501
|
+
"react-dom": "^19.2.0"
|
|
502
|
+
},
|
|
503
|
+
devDependencies: {
|
|
504
|
+
"@types/node": "^22.19.15",
|
|
505
|
+
"@types/react": "19.2.14",
|
|
506
|
+
"@types/react-dom": "19.2.3",
|
|
507
|
+
typescript: "6.0.2"
|
|
508
|
+
},
|
|
509
|
+
name: "blodemd-dev-server",
|
|
510
|
+
private: true,
|
|
511
|
+
type: "module"
|
|
512
|
+
}, null, 2)}\n`);
|
|
513
|
+
return {
|
|
514
|
+
devServerDir: path.join(runtimeRoot, "dev-server"),
|
|
515
|
+
packagesDir: path.join(runtimeRoot, "packages")
|
|
516
|
+
};
|
|
517
|
+
};
|
|
518
|
+
/**
|
|
519
|
+
* Check if a shipped dev-server exists alongside the CLI (npm-installed mode).
|
|
520
|
+
* Verifies both the dev-server directory AND that `next` is resolvable
|
|
521
|
+
* (it's a dependency when npm-installed, but not in the monorepo).
|
|
522
|
+
*/
|
|
523
|
+
const findStandaloneDevServer = async (cliPackageRoot) => {
|
|
524
|
+
const devServerDir = path.join(cliPackageRoot, "dev-server");
|
|
525
|
+
if (!await fileExists(path.join(devServerDir, "next.config.js"))) return null;
|
|
526
|
+
if (!await isStandaloneCliInstall(cliPackageRoot)) return null;
|
|
527
|
+
try {
|
|
528
|
+
createRequire(path.join(cliPackageRoot, "package.json")).resolve("next/package.json");
|
|
529
|
+
} catch {
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
const runtime = await materializeStandaloneRuntime(cliPackageRoot);
|
|
533
|
+
return {
|
|
534
|
+
devServerDir: runtime.devServerDir,
|
|
535
|
+
mode: "standalone",
|
|
536
|
+
nextPackageRoot: cliPackageRoot,
|
|
537
|
+
packagesDir: runtime.packagesDir
|
|
538
|
+
};
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* Resolve the `next` CLI binary from the blodemd package's own dependencies.
|
|
542
|
+
*/
|
|
543
|
+
const resolveNextBin = (cliPackageRoot) => {
|
|
544
|
+
const nextPkgPath = createRequire(path.join(cliPackageRoot, "package.json")).resolve("next/package.json");
|
|
545
|
+
return path.join(path.dirname(nextPkgPath), "dist", "bin", "next");
|
|
546
|
+
};
|
|
391
547
|
const findMonorepoRoot = async (start) => {
|
|
392
548
|
let current = start;
|
|
393
549
|
while (true) {
|
|
394
550
|
const packageJsonPath = path.join(current, "package.json");
|
|
395
|
-
if (await fileExists
|
|
551
|
+
if (await fileExists(packageJsonPath)) {
|
|
396
552
|
const raw = await fs.readFile(packageJsonPath, "utf8");
|
|
397
553
|
const workspaces = JSON.parse(raw).workspaces ?? [];
|
|
398
554
|
if (workspaces.includes("apps/*") && workspaces.includes("packages/*")) return current;
|
|
@@ -401,7 +557,48 @@ const findMonorepoRoot = async (start) => {
|
|
|
401
557
|
if (parent === current) break;
|
|
402
558
|
current = parent;
|
|
403
559
|
}
|
|
404
|
-
throw new CliError("Could not locate the blodemd
|
|
560
|
+
throw new CliError("Could not locate the blodemd dev server.", EXIT_CODES.ERROR, "Make sure blodemd is installed correctly (npm i blodemd).");
|
|
561
|
+
};
|
|
562
|
+
const resolveDevServer = async (cliFilePath) => {
|
|
563
|
+
const standalone = await findStandaloneDevServer(resolveCliPackageRoot(cliFilePath));
|
|
564
|
+
if (standalone) return standalone;
|
|
565
|
+
return {
|
|
566
|
+
mode: "monorepo",
|
|
567
|
+
repoRoot: await findMonorepoRoot(path.dirname(cliFilePath))
|
|
568
|
+
};
|
|
569
|
+
};
|
|
570
|
+
const spawnDevServer = (server, { root, port }) => {
|
|
571
|
+
if (server.mode === "standalone") {
|
|
572
|
+
const nextBin = resolveNextBin(server.nextPackageRoot);
|
|
573
|
+
return spawn(process.execPath, [
|
|
574
|
+
nextBin,
|
|
575
|
+
"dev",
|
|
576
|
+
"--webpack"
|
|
577
|
+
], {
|
|
578
|
+
cwd: server.devServerDir,
|
|
579
|
+
env: {
|
|
580
|
+
...process.env,
|
|
581
|
+
BLODEMD_PACKAGES_DIR: server.packagesDir,
|
|
582
|
+
DOCS_ROOT: root,
|
|
583
|
+
NODE_PATH: [server.packagesDir, process.env.NODE_PATH].filter(Boolean).join(path.delimiter),
|
|
584
|
+
PORT: String(port)
|
|
585
|
+
},
|
|
586
|
+
stdio: "inherit"
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
return spawn(process.platform === "win32" ? "npm.cmd" : "npm", [
|
|
590
|
+
"run",
|
|
591
|
+
"dev",
|
|
592
|
+
"--workspace=dev-server"
|
|
593
|
+
], {
|
|
594
|
+
cwd: server.repoRoot,
|
|
595
|
+
env: {
|
|
596
|
+
...process.env,
|
|
597
|
+
DOCS_ROOT: root,
|
|
598
|
+
PORT: String(port)
|
|
599
|
+
},
|
|
600
|
+
stdio: "inherit"
|
|
601
|
+
});
|
|
405
602
|
};
|
|
406
603
|
const waitForServer = async ({ child, port }) => {
|
|
407
604
|
const url = `http://localhost:${port}${DEV_READY_ENDPOINT}`;
|
|
@@ -418,29 +615,18 @@ const waitForServer = async ({ child, port }) => {
|
|
|
418
615
|
}
|
|
419
616
|
throw new CliError("Timed out waiting for the local dev server to start.", EXIT_CODES.ERROR);
|
|
420
617
|
};
|
|
421
|
-
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
422
618
|
const devCommand = async ({ dir, openBrowser, port: portValue }) => {
|
|
423
619
|
intro(chalk.bold("blodemd dev"));
|
|
424
620
|
try {
|
|
425
|
-
const
|
|
426
|
-
const root = await resolveDocsRoot
|
|
621
|
+
const resolvedPort = await resolveDevPort(parsePositiveInteger$1(portValue, "Port"));
|
|
622
|
+
const root = await resolveDocsRoot(dir);
|
|
427
623
|
await validateDocsRoot(root);
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
const localUrl = `http://localhost:${port}`;
|
|
624
|
+
const server = await resolveDevServer(fileURLToPath(import.meta.url));
|
|
625
|
+
const localUrl = `http://localhost:${resolvedPort}`;
|
|
431
626
|
log.info(`Docs root: ${chalk.cyan(root)}`);
|
|
432
|
-
const child =
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
"--workspace=dev-server"
|
|
436
|
-
], {
|
|
437
|
-
cwd: repoRoot,
|
|
438
|
-
env: {
|
|
439
|
-
...process.env,
|
|
440
|
-
DOCS_ROOT: root,
|
|
441
|
-
PORT: String(port)
|
|
442
|
-
},
|
|
443
|
-
stdio: "inherit"
|
|
627
|
+
const child = spawnDevServer(server, {
|
|
628
|
+
port: resolvedPort,
|
|
629
|
+
root
|
|
444
630
|
});
|
|
445
631
|
let watcher = null;
|
|
446
632
|
let shuttingDown = false;
|
|
@@ -451,35 +637,28 @@ const devCommand = async ({ dir, openBrowser, port: portValue }) => {
|
|
|
451
637
|
await watcher.close();
|
|
452
638
|
watcher = null;
|
|
453
639
|
}
|
|
454
|
-
|
|
640
|
+
await shutdownChildProcess(child);
|
|
455
641
|
};
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
};
|
|
459
|
-
process.once("SIGINT", onSignal);
|
|
460
|
-
process.once("SIGTERM", onSignal);
|
|
642
|
+
process.once("SIGINT", closeAll);
|
|
643
|
+
process.once("SIGTERM", closeAll);
|
|
461
644
|
try {
|
|
462
645
|
await waitForServer({
|
|
463
646
|
child,
|
|
464
|
-
port
|
|
647
|
+
port: resolvedPort
|
|
465
648
|
});
|
|
466
649
|
watcher = await createDevWatcher({
|
|
467
|
-
port,
|
|
650
|
+
port: resolvedPort,
|
|
468
651
|
root
|
|
469
652
|
});
|
|
470
653
|
log.success(`Dev server running at ${chalk.cyan(localUrl)}`);
|
|
471
654
|
if (openBrowser) await open(localUrl);
|
|
472
655
|
const [code, signal] = await once(child, "exit");
|
|
473
|
-
await closeAll();
|
|
474
|
-
process.removeListener("SIGINT", onSignal);
|
|
475
|
-
process.removeListener("SIGTERM", onSignal);
|
|
476
656
|
if (shuttingDown || signal === "SIGINT" || signal === "SIGTERM") return;
|
|
477
657
|
if (code !== 0) throw new CliError(`The local dev server exited with code ${code ?? "unknown"}.`, EXIT_CODES.ERROR);
|
|
478
|
-
}
|
|
658
|
+
} finally {
|
|
479
659
|
await closeAll();
|
|
480
|
-
process.removeListener("SIGINT",
|
|
481
|
-
process.removeListener("SIGTERM",
|
|
482
|
-
throw error;
|
|
660
|
+
process.removeListener("SIGINT", closeAll);
|
|
661
|
+
process.removeListener("SIGTERM", closeAll);
|
|
483
662
|
}
|
|
484
663
|
} catch (error) {
|
|
485
664
|
const cliError = toCliError(error);
|
|
@@ -511,7 +690,7 @@ const waitForOAuthCode = (options) => {
|
|
|
511
690
|
});
|
|
512
691
|
for (const socket of sockets) socket.destroy();
|
|
513
692
|
};
|
|
514
|
-
const httpServer = createServer((request, response) => {
|
|
693
|
+
const httpServer = createServer$1((request, response) => {
|
|
515
694
|
if (!request.url) {
|
|
516
695
|
response.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
517
696
|
response.end(errorHtml("Missing request URL"));
|
|
@@ -568,6 +747,53 @@ const createOAuthState = () => randomBytes(24).toString("hex");
|
|
|
568
747
|
const createCodeVerifier = () => randomBytes(64).toString("base64url");
|
|
569
748
|
const createCodeChallenge = (verifier) => createHash("sha256").update(verifier).digest().toString("base64url");
|
|
570
749
|
//#endregion
|
|
750
|
+
//#region src/runtime.ts
|
|
751
|
+
const MIN_SUPPORTED_NODE_VERSION = [
|
|
752
|
+
20,
|
|
753
|
+
17,
|
|
754
|
+
0
|
|
755
|
+
];
|
|
756
|
+
const MAX_SUPPORTED_NODE_MAJOR = 25;
|
|
757
|
+
const SUPPORTED_NODE_RANGE = ">=20.17.0 <25";
|
|
758
|
+
const parseVersion = (input) => {
|
|
759
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(input.trim());
|
|
760
|
+
if (!match) return null;
|
|
761
|
+
const [, majorText = "", minorText = "", patchText = ""] = match;
|
|
762
|
+
if (!majorText || !minorText || !patchText) return null;
|
|
763
|
+
const major = Number.parseInt(majorText, 10);
|
|
764
|
+
const minor = Number.parseInt(minorText, 10);
|
|
765
|
+
const patch = Number.parseInt(patchText, 10);
|
|
766
|
+
if ([
|
|
767
|
+
major,
|
|
768
|
+
minor,
|
|
769
|
+
patch
|
|
770
|
+
].some((value) => Number.isNaN(value))) return null;
|
|
771
|
+
return [
|
|
772
|
+
major,
|
|
773
|
+
minor,
|
|
774
|
+
patch
|
|
775
|
+
];
|
|
776
|
+
};
|
|
777
|
+
const isSupportedNodeVersion = (version) => {
|
|
778
|
+
const parsed = parseVersion(version);
|
|
779
|
+
if (!parsed) return false;
|
|
780
|
+
const [major, minor, patch] = parsed;
|
|
781
|
+
const [minMajor, minMinor, minPatch] = MIN_SUPPORTED_NODE_VERSION;
|
|
782
|
+
if (major >= MAX_SUPPORTED_NODE_MAJOR) return false;
|
|
783
|
+
if (major !== minMajor) return major > minMajor;
|
|
784
|
+
if (minor !== minMinor) return minor > minMinor;
|
|
785
|
+
return patch >= minPatch;
|
|
786
|
+
};
|
|
787
|
+
const assertSupportedNodeVersion = (version = process.versions.node) => {
|
|
788
|
+
if (isSupportedNodeVersion(version)) return;
|
|
789
|
+
throw new CliError(`blodemd requires Node.js ${SUPPORTED_NODE_RANGE}. Current version: ${version}.`, EXIT_CODES.VALIDATION, "Install a supported Node.js version and try again.");
|
|
790
|
+
};
|
|
791
|
+
const readCliVersion = (moduleUrl) => {
|
|
792
|
+
const moduleDir = path.dirname(fileURLToPath(moduleUrl));
|
|
793
|
+
const raw = readFileSync(path.resolve(moduleDir, "..", "package.json"), "utf8");
|
|
794
|
+
return JSON.parse(raw).version ?? "0.0.0";
|
|
795
|
+
};
|
|
796
|
+
//#endregion
|
|
571
797
|
//#region src/cli.ts
|
|
572
798
|
const CONFIG_FILE = "docs.json";
|
|
573
799
|
const TEXT_CONTENT_TYPES = {
|
|
@@ -587,31 +813,6 @@ const ensureFile = async (filePath, content) => {
|
|
|
587
813
|
await fs.writeFile(filePath, content, { flag: "wx" });
|
|
588
814
|
} catch {}
|
|
589
815
|
};
|
|
590
|
-
const fileExists = async (filePath) => {
|
|
591
|
-
try {
|
|
592
|
-
await fs.access(filePath);
|
|
593
|
-
return true;
|
|
594
|
-
} catch {
|
|
595
|
-
return false;
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
const readConfig = async (root) => {
|
|
599
|
-
const raw = await fs.readFile(path.join(root, CONFIG_FILE), "utf8");
|
|
600
|
-
return {
|
|
601
|
-
name: JSON.parse(raw).name,
|
|
602
|
-
raw
|
|
603
|
-
};
|
|
604
|
-
};
|
|
605
|
-
const resolveDocsRoot = async (dir) => {
|
|
606
|
-
if (dir) return path.resolve(process.cwd(), dir);
|
|
607
|
-
const candidates = [
|
|
608
|
-
process.cwd(),
|
|
609
|
-
path.join(process.cwd(), "docs"),
|
|
610
|
-
path.join(process.cwd(), "apps/docs")
|
|
611
|
-
];
|
|
612
|
-
for (const candidate of candidates) if (await fileExists(path.join(candidate, CONFIG_FILE))) return candidate;
|
|
613
|
-
return process.cwd();
|
|
614
|
-
};
|
|
615
816
|
const readGitValue = (gitArgs) => {
|
|
616
817
|
const result = spawnSync("git", gitArgs, {
|
|
617
818
|
encoding: "utf8",
|
|
@@ -718,26 +919,48 @@ const autoCreateProject = async (project, apiUrl, headers) => {
|
|
|
718
919
|
log.info(`API key for CI: ${chalk.dim(createResult.token)}`);
|
|
719
920
|
return true;
|
|
720
921
|
};
|
|
922
|
+
const MAX_BATCH_BYTES = 4 * 1024 * 1024;
|
|
721
923
|
const uploadFiles = async (files, root, apiPath, deploymentId, headers, s) => {
|
|
722
924
|
s.start(`Uploading ${files.length} files`);
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
925
|
+
const items = await Promise.all(files.map(async (filePath) => {
|
|
926
|
+
return {
|
|
927
|
+
contentBase64: (await fs.readFile(filePath)).toString("base64"),
|
|
928
|
+
contentType: getContentType(filePath),
|
|
929
|
+
path: normalizeRelativePath(root, filePath)
|
|
930
|
+
};
|
|
931
|
+
}));
|
|
932
|
+
const batches = [];
|
|
933
|
+
let current = [];
|
|
934
|
+
let currentBytes = 0;
|
|
935
|
+
for (const item of items) {
|
|
936
|
+
const itemBytes = item.contentBase64.length + item.path.length + 64;
|
|
937
|
+
if (current.length > 0 && currentBytes + itemBytes > MAX_BATCH_BYTES) {
|
|
938
|
+
batches.push(current);
|
|
939
|
+
current = [];
|
|
940
|
+
currentBytes = 0;
|
|
941
|
+
}
|
|
942
|
+
current.push(item);
|
|
943
|
+
currentBytes += itemBytes;
|
|
944
|
+
}
|
|
945
|
+
if (current.length > 0) batches.push(current);
|
|
946
|
+
let uploaded = 0;
|
|
947
|
+
for (const batch of batches) {
|
|
948
|
+
await requestJson(apiPath(`/${deploymentId}/files/batch`), {
|
|
949
|
+
body: JSON.stringify({ files: batch }),
|
|
732
950
|
headers,
|
|
733
951
|
method: "POST"
|
|
734
|
-
},
|
|
735
|
-
|
|
952
|
+
}, "Failed to upload files");
|
|
953
|
+
uploaded += batch.length;
|
|
954
|
+
s.message(`Uploading files (${uploaded}/${files.length})`);
|
|
736
955
|
}
|
|
737
956
|
s.stop(`Uploaded ${chalk.cyan(String(files.length))} files`);
|
|
738
957
|
};
|
|
739
958
|
const program = new Command();
|
|
740
|
-
|
|
959
|
+
const cliVersion = readCliVersion(import.meta.url);
|
|
960
|
+
program.name("blodemd").description("Blode.md CLI").version(cliVersion);
|
|
961
|
+
program.hook("preAction", () => {
|
|
962
|
+
assertSupportedNodeVersion();
|
|
963
|
+
});
|
|
741
964
|
program.command("login").description("Authenticate with Blode.md").option("--token", "Paste an API key instead of using browser login").option("--port <port>", "Loopback callback port", String(DEFAULT_OAUTH_CALLBACK_PORT)).option("--timeout <seconds>", "OAuth timeout in seconds", String(180)).option("--no-open", "Print URL instead of opening the browser").action(async (options) => {
|
|
742
965
|
intro(chalk.bold("blodemd login"));
|
|
743
966
|
try {
|
|
@@ -847,7 +1070,7 @@ program.command("init").description("Scaffold a docs folder").argument("[dir]",
|
|
|
847
1070
|
const root = path.resolve(process.cwd(), dir);
|
|
848
1071
|
await fs.mkdir(root, { recursive: true });
|
|
849
1072
|
await ensureFile(path.join(root, CONFIG_FILE), `${JSON.stringify({
|
|
850
|
-
$schema: "https://
|
|
1073
|
+
$schema: "https://docs.blode.md/docs.json",
|
|
851
1074
|
colors: { primary: "#0D9373" },
|
|
852
1075
|
name: "my-project",
|
|
853
1076
|
navigation: { groups: [{
|
|
@@ -867,7 +1090,8 @@ program.command("init").description("Scaffold a docs folder").argument("[dir]",
|
|
|
867
1090
|
program.command("validate").description("Validate docs.json").argument("[dir]", "docs directory").action(async (dir) => {
|
|
868
1091
|
intro(chalk.bold("blodemd validate"));
|
|
869
1092
|
try {
|
|
870
|
-
await
|
|
1093
|
+
const { warnings } = await loadValidatedSiteConfig(await resolveDocsRoot(dir));
|
|
1094
|
+
for (const warning of warnings) log.warn(warning);
|
|
871
1095
|
log.success(`${chalk.cyan(CONFIG_FILE)} is valid.`);
|
|
872
1096
|
log.info("Done");
|
|
873
1097
|
} catch (error) {
|
|
@@ -880,8 +1104,9 @@ program.command("push").description("Deploy docs").argument("[dir]", "docs direc
|
|
|
880
1104
|
try {
|
|
881
1105
|
const root = await resolveDocsRoot(dir);
|
|
882
1106
|
s.start("Validating configuration");
|
|
883
|
-
const config = await
|
|
1107
|
+
const { config, warnings } = await loadValidatedSiteConfig(root);
|
|
884
1108
|
s.stop("Configuration valid");
|
|
1109
|
+
for (const warning of warnings) log.warn(warning);
|
|
885
1110
|
const { project, apiUrl, authToken, branch, commitMessage } = await resolvePushConfig(config, options);
|
|
886
1111
|
s.start("Collecting files");
|
|
887
1112
|
const files = await collectFiles(root);
|