@raystack/chronicle 0.1.0-canary.1f5227c

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 (124) hide show
  1. package/bin/chronicle.js +2 -0
  2. package/dist/cli/index.js +543 -0
  3. package/package.json +68 -0
  4. package/src/cli/__tests__/config.test.ts +25 -0
  5. package/src/cli/__tests__/scaffold.test.ts +10 -0
  6. package/src/cli/commands/build.ts +52 -0
  7. package/src/cli/commands/dev.ts +21 -0
  8. package/src/cli/commands/init.ts +154 -0
  9. package/src/cli/commands/serve.ts +55 -0
  10. package/src/cli/commands/start.ts +24 -0
  11. package/src/cli/index.ts +21 -0
  12. package/src/cli/utils/config.ts +43 -0
  13. package/src/cli/utils/index.ts +2 -0
  14. package/src/cli/utils/resolve.ts +6 -0
  15. package/src/cli/utils/scaffold.ts +20 -0
  16. package/src/components/api/code-snippets.module.css +7 -0
  17. package/src/components/api/code-snippets.tsx +76 -0
  18. package/src/components/api/endpoint-page.module.css +58 -0
  19. package/src/components/api/endpoint-page.tsx +283 -0
  20. package/src/components/api/field-row.module.css +126 -0
  21. package/src/components/api/field-row.tsx +204 -0
  22. package/src/components/api/field-section.module.css +24 -0
  23. package/src/components/api/field-section.tsx +100 -0
  24. package/src/components/api/index.ts +8 -0
  25. package/src/components/api/json-editor.module.css +9 -0
  26. package/src/components/api/json-editor.tsx +61 -0
  27. package/src/components/api/key-value-editor.module.css +13 -0
  28. package/src/components/api/key-value-editor.tsx +62 -0
  29. package/src/components/api/method-badge.module.css +4 -0
  30. package/src/components/api/method-badge.tsx +29 -0
  31. package/src/components/api/response-panel.module.css +8 -0
  32. package/src/components/api/response-panel.tsx +44 -0
  33. package/src/components/common/breadcrumb.tsx +3 -0
  34. package/src/components/common/button.tsx +3 -0
  35. package/src/components/common/callout.module.css +7 -0
  36. package/src/components/common/callout.tsx +27 -0
  37. package/src/components/common/code-block.tsx +3 -0
  38. package/src/components/common/dialog.tsx +3 -0
  39. package/src/components/common/index.ts +10 -0
  40. package/src/components/common/input-field.tsx +3 -0
  41. package/src/components/common/sidebar.tsx +3 -0
  42. package/src/components/common/switch.tsx +3 -0
  43. package/src/components/common/table.tsx +3 -0
  44. package/src/components/common/tabs.tsx +3 -0
  45. package/src/components/mdx/code.module.css +42 -0
  46. package/src/components/mdx/code.tsx +36 -0
  47. package/src/components/mdx/details.module.css +14 -0
  48. package/src/components/mdx/details.tsx +17 -0
  49. package/src/components/mdx/image.tsx +24 -0
  50. package/src/components/mdx/index.tsx +35 -0
  51. package/src/components/mdx/link.tsx +37 -0
  52. package/src/components/mdx/mermaid.module.css +9 -0
  53. package/src/components/mdx/mermaid.tsx +37 -0
  54. package/src/components/mdx/paragraph.module.css +8 -0
  55. package/src/components/mdx/paragraph.tsx +19 -0
  56. package/src/components/mdx/table.tsx +40 -0
  57. package/src/components/ui/breadcrumbs.tsx +72 -0
  58. package/src/components/ui/client-theme-switcher.tsx +18 -0
  59. package/src/components/ui/footer.module.css +27 -0
  60. package/src/components/ui/footer.tsx +31 -0
  61. package/src/components/ui/search.module.css +111 -0
  62. package/src/components/ui/search.tsx +174 -0
  63. package/src/lib/api-routes.ts +120 -0
  64. package/src/lib/config.ts +56 -0
  65. package/src/lib/head.tsx +45 -0
  66. package/src/lib/index.ts +2 -0
  67. package/src/lib/openapi.ts +188 -0
  68. package/src/lib/page-context.tsx +95 -0
  69. package/src/lib/remark-unused-directives.ts +30 -0
  70. package/src/lib/schema.ts +99 -0
  71. package/src/lib/snippet-generators.ts +87 -0
  72. package/src/lib/source.ts +138 -0
  73. package/src/pages/ApiLayout.module.css +22 -0
  74. package/src/pages/ApiLayout.tsx +29 -0
  75. package/src/pages/ApiPage.tsx +68 -0
  76. package/src/pages/DocsLayout.tsx +18 -0
  77. package/src/pages/DocsPage.tsx +43 -0
  78. package/src/pages/NotFound.tsx +10 -0
  79. package/src/pages/__tests__/head.test.tsx +57 -0
  80. package/src/server/App.tsx +59 -0
  81. package/src/server/__tests__/entry-server.test.tsx +35 -0
  82. package/src/server/__tests__/handlers.test.ts +77 -0
  83. package/src/server/__tests__/og.test.ts +23 -0
  84. package/src/server/__tests__/router.test.ts +72 -0
  85. package/src/server/__tests__/vite-config.test.ts +25 -0
  86. package/src/server/dev.ts +156 -0
  87. package/src/server/entry-client.tsx +74 -0
  88. package/src/server/entry-prod.ts +127 -0
  89. package/src/server/entry-server.tsx +35 -0
  90. package/src/server/handlers/apis-proxy.ts +52 -0
  91. package/src/server/handlers/health.ts +3 -0
  92. package/src/server/handlers/llms.ts +58 -0
  93. package/src/server/handlers/og.ts +87 -0
  94. package/src/server/handlers/robots.ts +11 -0
  95. package/src/server/handlers/search.ts +140 -0
  96. package/src/server/handlers/sitemap.ts +39 -0
  97. package/src/server/handlers/specs.ts +9 -0
  98. package/src/server/index.html +12 -0
  99. package/src/server/prod.ts +18 -0
  100. package/src/server/router.ts +42 -0
  101. package/src/server/vite-config.ts +71 -0
  102. package/src/themes/default/Layout.module.css +81 -0
  103. package/src/themes/default/Layout.tsx +132 -0
  104. package/src/themes/default/Page.module.css +102 -0
  105. package/src/themes/default/Page.tsx +21 -0
  106. package/src/themes/default/Toc.module.css +48 -0
  107. package/src/themes/default/Toc.tsx +66 -0
  108. package/src/themes/default/font.ts +4 -0
  109. package/src/themes/default/index.ts +13 -0
  110. package/src/themes/paper/ChapterNav.module.css +71 -0
  111. package/src/themes/paper/ChapterNav.tsx +95 -0
  112. package/src/themes/paper/Layout.module.css +33 -0
  113. package/src/themes/paper/Layout.tsx +25 -0
  114. package/src/themes/paper/Page.module.css +174 -0
  115. package/src/themes/paper/Page.tsx +106 -0
  116. package/src/themes/paper/ReadingProgress.module.css +132 -0
  117. package/src/themes/paper/ReadingProgress.tsx +294 -0
  118. package/src/themes/paper/index.ts +8 -0
  119. package/src/themes/registry.ts +14 -0
  120. package/src/types/config.ts +80 -0
  121. package/src/types/content.ts +36 -0
  122. package/src/types/index.ts +3 -0
  123. package/src/types/theme.ts +22 -0
  124. package/tsconfig.json +29 -0
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli/index.js'
@@ -0,0 +1,543 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
18
+
19
+ // src/server/vite-config.ts
20
+ var exports_vite_config = {};
21
+ __export(exports_vite_config, {
22
+ createViteConfig: () => createViteConfig
23
+ });
24
+ import path5 from "path";
25
+ import react from "@vitejs/plugin-react";
26
+ import mdx from "@mdx-js/rollup";
27
+ import remarkDirective from "remark-directive";
28
+ import remarkGfm from "remark-gfm";
29
+ import remarkFrontmatter from "remark-frontmatter";
30
+ import remarkMdxFrontmatter from "remark-mdx-frontmatter";
31
+ import rehypeShiki from "@shikijs/rehype";
32
+ async function createViteConfig(options) {
33
+ const { root, contentDir, isDev = false } = options;
34
+ return {
35
+ root,
36
+ configFile: false,
37
+ resolve: {
38
+ alias: {
39
+ "@": path5.resolve(root, "src"),
40
+ "@content": contentDir
41
+ },
42
+ dedupe: ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"]
43
+ },
44
+ server: {
45
+ fs: {
46
+ allow: [root, contentDir]
47
+ }
48
+ },
49
+ plugins: [
50
+ mdx({
51
+ remarkPlugins: [
52
+ remarkFrontmatter,
53
+ remarkMdxFrontmatter,
54
+ remarkGfm,
55
+ remarkDirective
56
+ ],
57
+ rehypePlugins: [
58
+ [rehypeShiki, { themes: { light: "github-light", dark: "github-dark" } }]
59
+ ],
60
+ mdExtensions: [".md"],
61
+ mdxExtensions: [".mdx"]
62
+ }),
63
+ react()
64
+ ],
65
+ define: {
66
+ "process.env.CHRONICLE_CONTENT_DIR": JSON.stringify(contentDir),
67
+ "process.env.CHRONICLE_PROJECT_ROOT": JSON.stringify(root)
68
+ },
69
+ css: {
70
+ modules: {
71
+ localsConvention: "camelCase"
72
+ }
73
+ },
74
+ ssr: {
75
+ noExternal: ["@raystack/apsara"]
76
+ },
77
+ build: {
78
+ rolldownOptions: {
79
+ input: isDev ? undefined : {
80
+ client: path5.resolve(root, "src/server/index.html")
81
+ }
82
+ }
83
+ }
84
+ };
85
+ }
86
+ var init_vite_config = () => {};
87
+
88
+ // src/server/dev.ts
89
+ var exports_dev = {};
90
+ __export(exports_dev, {
91
+ startDevServer: () => startDevServer
92
+ });
93
+ import { createServer as createViteServer } from "vite";
94
+ import { createServer } from "http";
95
+ import fsPromises from "fs/promises";
96
+ import { createReadStream } from "fs";
97
+ import path6 from "path";
98
+ import chalk3 from "chalk";
99
+ async function startDevServer(options) {
100
+ const { port, root, contentDir } = options;
101
+ const viteConfig = await createViteConfig({ root, contentDir, isDev: true });
102
+ const vite = await createViteServer({
103
+ ...viteConfig,
104
+ server: { middlewareMode: true },
105
+ appType: "custom"
106
+ });
107
+ const templatePath = path6.resolve(root, "src/server/index.html");
108
+ const server = createServer(async (req, res) => {
109
+ const url = req.url || "/";
110
+ try {
111
+ if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules/")) {
112
+ vite.middlewares(req, res, () => {
113
+ res.statusCode = 404;
114
+ res.end();
115
+ });
116
+ return;
117
+ }
118
+ const contentFile = path6.join(contentDir, decodeURIComponent(url.split("?")[0]));
119
+ if (!url.endsWith(".md") && !url.endsWith(".mdx")) {
120
+ try {
121
+ const stat = await fsPromises.stat(contentFile);
122
+ if (stat.isFile()) {
123
+ const ext = path6.extname(contentFile).toLowerCase();
124
+ const mimeTypes = {
125
+ ".png": "image/png",
126
+ ".jpg": "image/jpeg",
127
+ ".jpeg": "image/jpeg",
128
+ ".gif": "image/gif",
129
+ ".svg": "image/svg+xml",
130
+ ".webp": "image/webp",
131
+ ".ico": "image/x-icon",
132
+ ".pdf": "application/pdf",
133
+ ".json": "application/json",
134
+ ".yaml": "text/yaml",
135
+ ".yml": "text/yaml",
136
+ ".txt": "text/plain"
137
+ };
138
+ res.setHeader("Content-Type", mimeTypes[ext] || "application/octet-stream");
139
+ createReadStream(contentFile).pipe(res);
140
+ return;
141
+ }
142
+ } catch {}
143
+ }
144
+ if (/\.(js|ts|tsx|css|map)(\?|$)/.test(url)) {
145
+ vite.middlewares(req, res, () => {
146
+ res.statusCode = 404;
147
+ res.end();
148
+ });
149
+ return;
150
+ }
151
+ const { matchRoute } = await vite.ssrLoadModule(path6.resolve(root, "src/server/router.ts"));
152
+ const routeHandler = matchRoute(new URL(url, `http://localhost:${port}`).href);
153
+ if (routeHandler) {
154
+ const request = new Request(new URL(url, `http://localhost:${port}`));
155
+ const response = await routeHandler(request);
156
+ res.statusCode = response.status;
157
+ response.headers.forEach((value, key) => res.setHeader(key, value));
158
+ const body = await response.text();
159
+ res.end(body);
160
+ return;
161
+ }
162
+ const pathname = new URL(url, `http://localhost:${port}`).pathname;
163
+ const slug = pathname === "/" ? [] : pathname.slice(1).split("/").filter(Boolean);
164
+ const source = await vite.ssrLoadModule(path6.resolve(root, "src/lib/source.ts"));
165
+ const { mdxComponents } = await vite.ssrLoadModule(path6.resolve(root, "src/components/mdx/index.tsx"));
166
+ const { loadConfig } = await vite.ssrLoadModule(path6.resolve(root, "src/lib/config.ts"));
167
+ const config = loadConfig();
168
+ const { loadApiSpecs } = await vite.ssrLoadModule(path6.resolve(root, "src/lib/openapi.ts"));
169
+ const apiSpecs = config.api?.length ? loadApiSpecs(config.api) : [];
170
+ const [tree, sourcePage] = await Promise.all([
171
+ source.buildPageTree(),
172
+ source.getPage(slug)
173
+ ]);
174
+ let pageData = null;
175
+ let embeddedData = { config, tree, slug, frontmatter: null, filePath: null };
176
+ if (sourcePage) {
177
+ const component = await source.loadPageComponent(sourcePage);
178
+ const React = await import("react");
179
+ const MDXBody = component;
180
+ pageData = {
181
+ slug,
182
+ frontmatter: sourcePage.frontmatter,
183
+ content: MDXBody ? React.createElement(MDXBody, { components: mdxComponents }) : null
184
+ };
185
+ embeddedData.frontmatter = sourcePage.frontmatter;
186
+ embeddedData.filePath = sourcePage.filePath;
187
+ }
188
+ let template = await fsPromises.readFile(templatePath, "utf-8");
189
+ template = await vite.transformIndexHtml(url, template);
190
+ const dataScript = `<script>window.__PAGE_DATA__ = ${JSON.stringify(embeddedData)}</script>`;
191
+ template = template.replace("<!--head-outlet-->", `<!--head-outlet-->${dataScript}`);
192
+ const { render } = await vite.ssrLoadModule(path6.resolve(root, "src/server/entry-server.tsx"));
193
+ const html = render(url, { config, tree, page: pageData, apiSpecs });
194
+ const finalHtml = template.replace("<!--ssr-outlet-->", html);
195
+ res.setHeader("Content-Type", "text/html");
196
+ res.statusCode = 200;
197
+ res.end(finalHtml);
198
+ } catch (e) {
199
+ vite.ssrFixStacktrace(e);
200
+ console.error(e);
201
+ res.statusCode = 500;
202
+ res.end(e.message);
203
+ }
204
+ });
205
+ server.listen(port, () => {
206
+ console.log(chalk3.cyan(`
207
+ Chronicle dev server running at:`));
208
+ console.log(chalk3.green(` http://localhost:${port}
209
+ `));
210
+ });
211
+ const shutdown = () => {
212
+ vite.close();
213
+ server.close();
214
+ process.exit(0);
215
+ };
216
+ process.on("SIGINT", shutdown);
217
+ process.on("SIGTERM", shutdown);
218
+ return { server, vite };
219
+ }
220
+ var init_dev = __esm(() => {
221
+ init_vite_config();
222
+ });
223
+
224
+ // src/server/prod.ts
225
+ var exports_prod = {};
226
+ __export(exports_prod, {
227
+ startProdServer: () => startProdServer
228
+ });
229
+ import path8 from "path";
230
+ import chalk6 from "chalk";
231
+ async function startProdServer(options) {
232
+ const { port, distDir } = options;
233
+ const serverEntry = path8.resolve(distDir, "server/entry-prod.js");
234
+ const { startServer } = await import(serverEntry);
235
+ console.log(chalk6.cyan("Starting production server..."));
236
+ return startServer({ port, distDir });
237
+ }
238
+ var init_prod = () => {};
239
+
240
+ // src/cli/index.ts
241
+ import { Command as Command6 } from "commander";
242
+
243
+ // src/cli/commands/init.ts
244
+ import { Command } from "commander";
245
+ import { execSync } from "child_process";
246
+ import fs2 from "fs";
247
+ import path3 from "path";
248
+ import chalk from "chalk";
249
+ import { stringify } from "yaml";
250
+
251
+ // src/cli/utils/scaffold.ts
252
+ import fs from "fs";
253
+ import path2 from "path";
254
+
255
+ // src/cli/utils/resolve.ts
256
+ import path from "path";
257
+ import { fileURLToPath } from "url";
258
+ var PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
259
+
260
+ // src/cli/utils/scaffold.ts
261
+ function detectPackageManager() {
262
+ if (process.env.npm_config_user_agent) {
263
+ return process.env.npm_config_user_agent.split("/")[0];
264
+ }
265
+ const cwd = process.cwd();
266
+ if (fs.existsSync(path2.join(cwd, "bun.lock")) || fs.existsSync(path2.join(cwd, "bun.lockb")))
267
+ return "bun";
268
+ if (fs.existsSync(path2.join(cwd, "pnpm-lock.yaml")))
269
+ return "pnpm";
270
+ if (fs.existsSync(path2.join(cwd, "yarn.lock")))
271
+ return "yarn";
272
+ return "npm";
273
+ }
274
+ function getChronicleVersion() {
275
+ const pkgPath = path2.join(PACKAGE_ROOT, "package.json");
276
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
277
+ return pkg.version;
278
+ }
279
+
280
+ // src/cli/commands/init.ts
281
+ function createConfig() {
282
+ return {
283
+ title: "My Documentation",
284
+ description: "Documentation powered by Chronicle",
285
+ theme: { name: "default" },
286
+ search: { enabled: true, placeholder: "Search documentation..." }
287
+ };
288
+ }
289
+ function createPackageJson(name) {
290
+ return {
291
+ name,
292
+ private: true,
293
+ type: "module",
294
+ scripts: {
295
+ dev: "chronicle dev",
296
+ build: "chronicle build",
297
+ start: "chronicle start"
298
+ },
299
+ dependencies: {
300
+ "@raystack/chronicle": `^${getChronicleVersion()}`
301
+ },
302
+ devDependencies: {
303
+ "@raystack/tools-config": "0.56.0",
304
+ "openapi-types": "^12.1.3",
305
+ typescript: "5.9.3",
306
+ "@types/react": "^19.2.10",
307
+ "@types/node": "^25.1.0"
308
+ }
309
+ };
310
+ }
311
+ var sampleMdx = `---
312
+ title: Welcome
313
+ description: Getting started with your documentation
314
+ order: 1
315
+ ---
316
+
317
+ # Welcome
318
+
319
+ This is your documentation home page.
320
+ `;
321
+ var initCommand = new Command("init").description("Initialize a new Chronicle project").option("-c, --content <path>", "Content directory name", "content").action((options) => {
322
+ const projectDir = process.cwd();
323
+ const dirName = path3.basename(projectDir) || "docs";
324
+ const contentDir = path3.join(projectDir, options.content);
325
+ if (!fs2.existsSync(contentDir)) {
326
+ fs2.mkdirSync(contentDir, { recursive: true });
327
+ console.log(chalk.green("✓"), "Created", contentDir);
328
+ }
329
+ const packageJsonPath = path3.join(projectDir, "package.json");
330
+ if (!fs2.existsSync(packageJsonPath)) {
331
+ fs2.writeFileSync(packageJsonPath, JSON.stringify(createPackageJson(dirName), null, 2) + `
332
+ `);
333
+ console.log(chalk.green("✓"), "Created", packageJsonPath);
334
+ } else {
335
+ const existing = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
336
+ const template = createPackageJson(dirName);
337
+ let updated = false;
338
+ if (existing.type !== "module") {
339
+ existing.type = "module";
340
+ updated = true;
341
+ }
342
+ if (!existing.scripts)
343
+ existing.scripts = {};
344
+ for (const [key, value] of Object.entries(template.scripts)) {
345
+ if (!existing.scripts[key]) {
346
+ existing.scripts[key] = value;
347
+ updated = true;
348
+ }
349
+ }
350
+ if (!existing.dependencies)
351
+ existing.dependencies = {};
352
+ for (const [key, value] of Object.entries(template.dependencies)) {
353
+ if (!existing.dependencies[key]) {
354
+ existing.dependencies[key] = value;
355
+ updated = true;
356
+ }
357
+ }
358
+ if (!existing.devDependencies)
359
+ existing.devDependencies = {};
360
+ for (const [key, value] of Object.entries(template.devDependencies)) {
361
+ if (!existing.devDependencies[key]) {
362
+ existing.devDependencies[key] = value;
363
+ updated = true;
364
+ }
365
+ }
366
+ if (updated) {
367
+ fs2.writeFileSync(packageJsonPath, JSON.stringify(existing, null, 2) + `
368
+ `);
369
+ console.log(chalk.green("✓"), "Updated", packageJsonPath);
370
+ } else {
371
+ console.log(chalk.yellow("⚠"), packageJsonPath, "already has all required entries");
372
+ }
373
+ }
374
+ const configPath = path3.join(projectDir, "chronicle.yaml");
375
+ if (!fs2.existsSync(configPath)) {
376
+ fs2.writeFileSync(configPath, stringify(createConfig()));
377
+ console.log(chalk.green("✓"), "Created", configPath);
378
+ } else {
379
+ console.log(chalk.yellow("⚠"), configPath, "already exists");
380
+ }
381
+ const contentFiles = fs2.readdirSync(contentDir);
382
+ if (contentFiles.length === 0) {
383
+ const indexPath = path3.join(contentDir, "index.mdx");
384
+ fs2.writeFileSync(indexPath, sampleMdx);
385
+ console.log(chalk.green("✓"), "Created", indexPath);
386
+ }
387
+ const gitignorePath = path3.join(projectDir, ".gitignore");
388
+ const gitignoreEntries = ["node_modules", "dist"];
389
+ if (fs2.existsSync(gitignorePath)) {
390
+ const existing = fs2.readFileSync(gitignorePath, "utf-8");
391
+ const missing = gitignoreEntries.filter((e) => !existing.includes(e));
392
+ if (missing.length > 0) {
393
+ fs2.appendFileSync(gitignorePath, `
394
+ ${missing.join(`
395
+ `)}
396
+ `);
397
+ console.log(chalk.green("✓"), "Added", missing.join(", "), "to .gitignore");
398
+ }
399
+ } else {
400
+ fs2.writeFileSync(gitignorePath, `${gitignoreEntries.join(`
401
+ `)}
402
+ `);
403
+ console.log(chalk.green("✓"), "Created .gitignore");
404
+ }
405
+ const pm = detectPackageManager();
406
+ console.log(chalk.cyan(`
407
+ Installing dependencies with ${pm}...`));
408
+ execSync(`${pm} install`, { cwd: projectDir, stdio: "inherit" });
409
+ const runCmd = pm === "npm" ? "npx" : pm === "bun" ? "bunx" : `${pm} dlx`;
410
+ console.log(chalk.green(`
411
+ ✓ Chronicle initialized!`));
412
+ console.log(`
413
+ Run`, chalk.cyan(`${runCmd} chronicle dev`), "to start development server");
414
+ });
415
+
416
+ // src/cli/commands/dev.ts
417
+ import { Command as Command2 } from "commander";
418
+ import chalk4 from "chalk";
419
+
420
+ // src/cli/utils/config.ts
421
+ import path4 from "path";
422
+ import { parse } from "yaml";
423
+ import chalk2 from "chalk";
424
+ function resolveContentDir(contentFlag) {
425
+ if (contentFlag)
426
+ return path4.resolve(contentFlag);
427
+ if (process.env.CHRONICLE_CONTENT_DIR)
428
+ return path4.resolve(process.env.CHRONICLE_CONTENT_DIR);
429
+ return path4.resolve("content");
430
+ }
431
+
432
+ // src/cli/commands/dev.ts
433
+ var devCommand = new Command2("dev").description("Start development server").option("-p, --port <port>", "Port number", "3000").option("-c, --content <path>", "Content directory").action(async (options) => {
434
+ const contentDir = resolveContentDir(options.content);
435
+ const port = parseInt(options.port, 10);
436
+ process.env.CHRONICLE_PROJECT_ROOT = process.cwd();
437
+ process.env.CHRONICLE_CONTENT_DIR = contentDir;
438
+ console.log(chalk4.cyan("Starting dev server..."));
439
+ const { startDevServer: startDevServer2 } = await Promise.resolve().then(() => (init_dev(), exports_dev));
440
+ await startDevServer2({ port, root: PACKAGE_ROOT, contentDir });
441
+ });
442
+
443
+ // src/cli/commands/build.ts
444
+ import { Command as Command3 } from "commander";
445
+ import path7 from "path";
446
+ import chalk5 from "chalk";
447
+ var buildCommand = new Command3("build").description("Build for production").option("-c, --content <path>", "Content directory").option("-o, --outDir <path>", "Output directory", "dist").action(async (options) => {
448
+ const contentDir = resolveContentDir(options.content);
449
+ const outDir = path7.resolve(options.outDir);
450
+ process.env.CHRONICLE_PROJECT_ROOT = process.cwd();
451
+ process.env.CHRONICLE_CONTENT_DIR = contentDir;
452
+ console.log(chalk5.cyan("Building for production..."));
453
+ const { build } = await import("vite");
454
+ const { createViteConfig: createViteConfig2 } = await Promise.resolve().then(() => (init_vite_config(), exports_vite_config));
455
+ const baseConfig = await createViteConfig2({ root: PACKAGE_ROOT, contentDir });
456
+ console.log(chalk5.gray("Building client..."));
457
+ await build({
458
+ ...baseConfig,
459
+ build: {
460
+ outDir: path7.join(outDir, "client"),
461
+ ssrManifest: true,
462
+ rolldownOptions: {
463
+ input: path7.resolve(PACKAGE_ROOT, "src/server/index.html")
464
+ }
465
+ }
466
+ });
467
+ console.log(chalk5.gray("Building server..."));
468
+ await build({
469
+ ...baseConfig,
470
+ ssr: {
471
+ noExternal: true
472
+ },
473
+ build: {
474
+ outDir: path7.join(outDir, "server"),
475
+ ssr: path7.resolve(PACKAGE_ROOT, "src/server/entry-prod.ts")
476
+ }
477
+ });
478
+ console.log(chalk5.green("Build complete →"), outDir);
479
+ });
480
+
481
+ // src/cli/commands/start.ts
482
+ import { Command as Command4 } from "commander";
483
+ import path9 from "path";
484
+ import chalk7 from "chalk";
485
+ var startCommand = new Command4("start").description("Start production server").option("-p, --port <port>", "Port number", "3000").option("-c, --content <path>", "Content directory").option("-d, --dist <path>", "Dist directory", "dist").action(async (options) => {
486
+ const contentDir = resolveContentDir(options.content);
487
+ const port = parseInt(options.port, 10);
488
+ const distDir = path9.resolve(options.dist);
489
+ process.env.CHRONICLE_PROJECT_ROOT = process.cwd();
490
+ process.env.CHRONICLE_CONTENT_DIR = contentDir;
491
+ console.log(chalk7.cyan("Starting production server..."));
492
+ const { startProdServer: startProdServer2 } = await Promise.resolve().then(() => (init_prod(), exports_prod));
493
+ await startProdServer2({ port, root: PACKAGE_ROOT, distDir });
494
+ });
495
+
496
+ // src/cli/commands/serve.ts
497
+ import { Command as Command5 } from "commander";
498
+ import path10 from "path";
499
+ import chalk8 from "chalk";
500
+ var serveCommand = new Command5("serve").description("Build and start production server").option("-p, --port <port>", "Port number", "3000").option("-c, --content <path>", "Content directory").option("-o, --outDir <path>", "Output directory", "dist").action(async (options) => {
501
+ const contentDir = resolveContentDir(options.content);
502
+ const port = parseInt(options.port, 10);
503
+ const outDir = path10.resolve(options.outDir);
504
+ process.env.CHRONICLE_PROJECT_ROOT = process.cwd();
505
+ process.env.CHRONICLE_CONTENT_DIR = contentDir;
506
+ console.log(chalk8.cyan("Building for production..."));
507
+ const { build } = await import("vite");
508
+ const { createViteConfig: createViteConfig2 } = await Promise.resolve().then(() => (init_vite_config(), exports_vite_config));
509
+ const baseConfig = await createViteConfig2({ root: PACKAGE_ROOT, contentDir });
510
+ await build({
511
+ ...baseConfig,
512
+ build: {
513
+ outDir: path10.join(outDir, "client"),
514
+ ssrManifest: true,
515
+ rolldownOptions: {
516
+ input: path10.resolve(PACKAGE_ROOT, "src/server/index.html")
517
+ }
518
+ }
519
+ });
520
+ await build({
521
+ ...baseConfig,
522
+ ssr: {
523
+ noExternal: true
524
+ },
525
+ build: {
526
+ outDir: path10.join(outDir, "server"),
527
+ ssr: path10.resolve(PACKAGE_ROOT, "src/server/entry-prod.ts")
528
+ }
529
+ });
530
+ console.log(chalk8.cyan("Starting production server..."));
531
+ const { startProdServer: startProdServer2 } = await Promise.resolve().then(() => (init_prod(), exports_prod));
532
+ await startProdServer2({ port, root: PACKAGE_ROOT, distDir: outDir });
533
+ });
534
+
535
+ // src/cli/index.ts
536
+ var program = new Command6;
537
+ program.name("chronicle").description("Config-driven documentation framework").version("0.1.0");
538
+ program.addCommand(initCommand);
539
+ program.addCommand(devCommand);
540
+ program.addCommand(buildCommand);
541
+ program.addCommand(startCommand);
542
+ program.addCommand(serveCommand);
543
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@raystack/chronicle",
3
+ "version": "0.1.0-canary.1f5227c",
4
+ "description": "Config-driven documentation framework",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "files": [
8
+ "bin",
9
+ "dist",
10
+ "src",
11
+ "templates",
12
+ "tsconfig.json"
13
+ ],
14
+ "bin": {
15
+ "chronicle": "./bin/chronicle.js"
16
+ },
17
+ "scripts": {
18
+ "build:cli": "bun build-cli.ts"
19
+ },
20
+ "devDependencies": {
21
+ "@biomejs/biome": "^2.3.13",
22
+ "@raystack/tools-config": "0.56.0",
23
+ "@types/lodash": "^4.17.23",
24
+ "@types/mdx": "^2.0.13",
25
+ "@types/node": "^25.1.0",
26
+ "@types/react": "^19.2.10",
27
+ "@types/react-dom": "^19.2.3",
28
+ "@types/semver": "^7.7.1",
29
+ "semver": "^7.7.4",
30
+ "typescript": "5.9.3"
31
+ },
32
+ "dependencies": {
33
+ "@codemirror/lang-json": "^6.0.2",
34
+ "@codemirror/state": "^6.5.4",
35
+ "@codemirror/theme-one-dark": "^6.1.3",
36
+ "@codemirror/view": "^6.39.14",
37
+ "@heroicons/react": "^2.2.0",
38
+ "@mdx-js/rollup": "^3.1.1",
39
+ "@raystack/apsara": "^0.56.0",
40
+ "@vitejs/plugin-react": "^6.0.1",
41
+ "chalk": "^5.6.2",
42
+ "class-variance-authority": "^0.7.1",
43
+ "codemirror": "^6.0.2",
44
+ "commander": "^14.0.2",
45
+ "glob": "^11.0.0",
46
+ "gray-matter": "^4.0.3",
47
+ "lodash": "^4.17.23",
48
+ "mermaid": "^11.13.0",
49
+ "openapi-types": "^12.1.3",
50
+ "react": "^19.0.0",
51
+ "react-device-detect": "^2.2.3",
52
+ "react-dom": "^19.0.0",
53
+ "react-router-dom": "^7.13.1",
54
+ "remark-directive": "^4.0.0",
55
+ "remark-gfm": "^4.0.1",
56
+ "@shikijs/rehype": "^4.0.2",
57
+ "remark-frontmatter": "^5.0.0",
58
+ "remark-mdx-frontmatter": "^5.2.0",
59
+ "satori": "^0.25.0",
60
+ "sirv": "^3.0.1",
61
+ "slugify": "^1.6.6",
62
+ "unified": "^11.0.5",
63
+ "unist-util-visit": "^5.1.0",
64
+ "minisearch": "^7.2.0",
65
+ "vite": "^8.0.0",
66
+ "yaml": "^2.8.2"
67
+ }
68
+ }
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { resolveContentDir } from '../utils/config'
3
+
4
+ describe('resolveContentDir', () => {
5
+ it('returns flag value when provided', () => {
6
+ const result = resolveContentDir('/custom/path')
7
+ expect(result).toBe('/custom/path')
8
+ })
9
+
10
+ it('returns env var when set', () => {
11
+ const original = process.env.CHRONICLE_CONTENT_DIR
12
+ process.env.CHRONICLE_CONTENT_DIR = '/env/content'
13
+ const result = resolveContentDir()
14
+ expect(result).toContain('env/content')
15
+ process.env.CHRONICLE_CONTENT_DIR = original
16
+ })
17
+
18
+ it('defaults to content directory', () => {
19
+ const original = process.env.CHRONICLE_CONTENT_DIR
20
+ delete process.env.CHRONICLE_CONTENT_DIR
21
+ const result = resolveContentDir()
22
+ expect(result).toContain('content')
23
+ process.env.CHRONICLE_CONTENT_DIR = original
24
+ })
25
+ })
@@ -0,0 +1,10 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { detectPackageManager } from '../utils/scaffold'
3
+
4
+ describe('detectPackageManager', () => {
5
+ it('returns a string', () => {
6
+ const result = detectPackageManager()
7
+ expect(typeof result).toBe('string')
8
+ expect(['npm', 'bun', 'pnpm', 'yarn']).toContain(result)
9
+ })
10
+ })