@takazudo/zudo-doc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -0
  3. package/dist/body-foot-util/body-foot-util-area.d.ts +55 -0
  4. package/dist/body-foot-util/body-foot-util-area.js +71 -0
  5. package/dist/body-foot-util/edit-link.d.ts +45 -0
  6. package/dist/body-foot-util/edit-link.js +42 -0
  7. package/dist/body-foot-util/index.d.ts +3 -0
  8. package/dist/body-foot-util/index.js +11 -0
  9. package/dist/breadcrumb/breadcrumb.d.ts +57 -0
  10. package/dist/breadcrumb/breadcrumb.js +113 -0
  11. package/dist/breadcrumb/find-path.d.ts +16 -0
  12. package/dist/breadcrumb/find-path.js +13 -0
  13. package/dist/breadcrumb/index.d.ts +4 -0
  14. package/dist/breadcrumb/index.js +7 -0
  15. package/dist/breadcrumb/types.d.ts +32 -0
  16. package/dist/breadcrumb/types.js +0 -0
  17. package/dist/code-syntax/code-block-enhancer-script.d.ts +3 -0
  18. package/dist/code-syntax/code-block-enhancer-script.js +165 -0
  19. package/dist/code-syntax/code-block-enhancer.d.ts +22 -0
  20. package/dist/code-syntax/code-block-enhancer.js +13 -0
  21. package/dist/code-syntax/index.d.ts +8 -0
  22. package/dist/code-syntax/index.js +29 -0
  23. package/dist/code-syntax/mermaid-init-script.d.ts +51 -0
  24. package/dist/code-syntax/mermaid-init-script.js +225 -0
  25. package/dist/code-syntax/mermaid-init.d.ts +48 -0
  26. package/dist/code-syntax/mermaid-init.js +14 -0
  27. package/dist/code-syntax/tabs-init-script.d.ts +3 -0
  28. package/dist/code-syntax/tabs-init-script.js +106 -0
  29. package/dist/code-syntax/tabs-init.d.ts +24 -0
  30. package/dist/code-syntax/tabs-init.js +10 -0
  31. package/dist/code-syntax/tabs.d.ts +36 -0
  32. package/dist/code-syntax/tabs.js +62 -0
  33. package/dist/content/component-map.d.ts +51 -0
  34. package/dist/content/component-map.js +29 -0
  35. package/dist/content/content-blockquote.d.ts +11 -0
  36. package/dist/content/content-blockquote.js +14 -0
  37. package/dist/content/content-code.d.ts +26 -0
  38. package/dist/content/content-code.js +39 -0
  39. package/dist/content/content-link.d.ts +12 -0
  40. package/dist/content/content-link.js +43 -0
  41. package/dist/content/content-ol.d.ts +11 -0
  42. package/dist/content/content-ol.js +15 -0
  43. package/dist/content/content-paragraph.d.ts +10 -0
  44. package/dist/content/content-paragraph.js +7 -0
  45. package/dist/content/content-strong.d.ts +11 -0
  46. package/dist/content/content-strong.js +14 -0
  47. package/dist/content/content-table.d.ts +11 -0
  48. package/dist/content/content-table.js +14 -0
  49. package/dist/content/content-ul.d.ts +11 -0
  50. package/dist/content/content-ul.js +15 -0
  51. package/dist/content/heading-h2.d.ts +14 -0
  52. package/dist/content/heading-h2.js +18 -0
  53. package/dist/content/heading-h3.d.ts +14 -0
  54. package/dist/content/heading-h3.js +18 -0
  55. package/dist/content/heading-h4.d.ts +14 -0
  56. package/dist/content/heading-h4.js +18 -0
  57. package/dist/content/index.d.ts +13 -0
  58. package/dist/content/index.js +27 -0
  59. package/dist/details/details.d.ts +26 -0
  60. package/dist/details/details.js +10 -0
  61. package/dist/details/index.d.ts +2 -0
  62. package/dist/details/index.js +4 -0
  63. package/dist/doclayout/anchors.d.ts +55 -0
  64. package/dist/doclayout/anchors.js +42 -0
  65. package/dist/doclayout/doc-layout-with-defaults.d.ts +83 -0
  66. package/dist/doclayout/doc-layout-with-defaults.js +159 -0
  67. package/dist/doclayout/doc-layout.d.ts +121 -0
  68. package/dist/doclayout/doc-layout.js +89 -0
  69. package/dist/doclayout/index.d.ts +6 -0
  70. package/dist/doclayout/index.js +17 -0
  71. package/dist/footer/footer.d.ts +42 -0
  72. package/dist/footer/footer.js +79 -0
  73. package/dist/footer/index.d.ts +3 -0
  74. package/dist/footer/index.js +4 -0
  75. package/dist/footer/types.d.ts +58 -0
  76. package/dist/footer/types.js +0 -0
  77. package/dist/head/doc-head.d.ts +33 -0
  78. package/dist/head/doc-head.js +90 -0
  79. package/dist/head/index.d.ts +5 -0
  80. package/dist/head/index.js +8 -0
  81. package/dist/head/og-tags.d.ts +25 -0
  82. package/dist/head/og-tags.js +16 -0
  83. package/dist/head/twitter-card.d.ts +21 -0
  84. package/dist/head/twitter-card.js +14 -0
  85. package/dist/head/types.d.ts +83 -0
  86. package/dist/head/types.js +0 -0
  87. package/dist/header/header.d.ts +158 -0
  88. package/dist/header/header.js +367 -0
  89. package/dist/header/index.d.ts +6 -0
  90. package/dist/header/index.js +18 -0
  91. package/dist/header/nav-active.d.ts +39 -0
  92. package/dist/header/nav-active.js +24 -0
  93. package/dist/header/nav-overflow-script.d.ts +3 -0
  94. package/dist/header/nav-overflow-script.js +169 -0
  95. package/dist/header/right-items.d.ts +38 -0
  96. package/dist/header/right-items.js +30 -0
  97. package/dist/header/types.d.ts +40 -0
  98. package/dist/header/types.js +0 -0
  99. package/dist/html-preview-wrapper/dedent.d.ts +10 -0
  100. package/dist/html-preview-wrapper/dedent.js +16 -0
  101. package/dist/html-preview-wrapper/highlighted-code.d.ts +18 -0
  102. package/dist/html-preview-wrapper/highlighted-code.js +53 -0
  103. package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +60 -0
  104. package/dist/html-preview-wrapper/html-preview-wrapper.js +36 -0
  105. package/dist/html-preview-wrapper/html-preview.d.ts +35 -0
  106. package/dist/html-preview-wrapper/html-preview.js +75 -0
  107. package/dist/html-preview-wrapper/index.d.ts +5 -0
  108. package/dist/html-preview-wrapper/index.js +12 -0
  109. package/dist/html-preview-wrapper/preflight.d.ts +13 -0
  110. package/dist/html-preview-wrapper/preflight.js +108 -0
  111. package/dist/html-preview-wrapper/preview-base.d.ts +29 -0
  112. package/dist/html-preview-wrapper/preview-base.js +127 -0
  113. package/dist/i18n-version/index.d.ts +5 -0
  114. package/dist/i18n-version/index.js +14 -0
  115. package/dist/i18n-version/language-switcher.d.ts +24 -0
  116. package/dist/i18n-version/language-switcher.js +25 -0
  117. package/dist/i18n-version/types.d.ts +43 -0
  118. package/dist/i18n-version/types.js +0 -0
  119. package/dist/i18n-version/version-banner.d.ts +30 -0
  120. package/dist/i18n-version/version-banner.js +21 -0
  121. package/dist/i18n-version/version-switcher.d.ts +112 -0
  122. package/dist/i18n-version/version-switcher.js +157 -0
  123. package/dist/index.d.ts +2 -0
  124. package/dist/index.js +0 -0
  125. package/dist/integrations/claude-resources/escape-for-mdx.d.ts +8 -0
  126. package/dist/integrations/claude-resources/escape-for-mdx.js +131 -0
  127. package/dist/integrations/claude-resources/generate.d.ts +13 -0
  128. package/dist/integrations/claude-resources/generate.js +404 -0
  129. package/dist/integrations/claude-resources/index.d.ts +46 -0
  130. package/dist/integrations/claude-resources/index.js +24 -0
  131. package/dist/integrations/doc-history/index.d.ts +137 -0
  132. package/dist/integrations/doc-history/index.js +138 -0
  133. package/dist/integrations/doc-history/pre-build.d.ts +58 -0
  134. package/dist/integrations/doc-history/pre-build.js +67 -0
  135. package/dist/integrations/llms-txt/dev-middleware.d.ts +40 -0
  136. package/dist/integrations/llms-txt/dev-middleware.js +76 -0
  137. package/dist/integrations/llms-txt/emit.d.ts +22 -0
  138. package/dist/integrations/llms-txt/emit.js +55 -0
  139. package/dist/integrations/llms-txt/generate.d.ts +26 -0
  140. package/dist/integrations/llms-txt/generate.js +36 -0
  141. package/dist/integrations/llms-txt/index.d.ts +7 -0
  142. package/dist/integrations/llms-txt/index.js +27 -0
  143. package/dist/integrations/llms-txt/load.d.ts +51 -0
  144. package/dist/integrations/llms-txt/load.js +83 -0
  145. package/dist/integrations/llms-txt/strip.d.ts +22 -0
  146. package/dist/integrations/llms-txt/strip.js +10 -0
  147. package/dist/integrations/llms-txt/types.d.ts +145 -0
  148. package/dist/integrations/llms-txt/types.js +0 -0
  149. package/dist/integrations/search-index/build-emitter.d.ts +30 -0
  150. package/dist/integrations/search-index/build-emitter.js +15 -0
  151. package/dist/integrations/search-index/collect.d.ts +12 -0
  152. package/dist/integrations/search-index/collect.js +48 -0
  153. package/dist/integrations/search-index/content-files.d.ts +32 -0
  154. package/dist/integrations/search-index/content-files.js +50 -0
  155. package/dist/integrations/search-index/dev-middleware.d.ts +16 -0
  156. package/dist/integrations/search-index/dev-middleware.js +30 -0
  157. package/dist/integrations/search-index/index.d.ts +5 -0
  158. package/dist/integrations/search-index/index.js +14 -0
  159. package/dist/integrations/search-index/types.d.ts +38 -0
  160. package/dist/integrations/search-index/types.js +6 -0
  161. package/dist/metainfo/doc-metainfo.d.ts +50 -0
  162. package/dist/metainfo/doc-metainfo.js +107 -0
  163. package/dist/metainfo/doc-tags.d.ts +66 -0
  164. package/dist/metainfo/doc-tags.js +53 -0
  165. package/dist/metainfo/frontmatter-preview.d.ts +95 -0
  166. package/dist/metainfo/frontmatter-preview.js +55 -0
  167. package/dist/metainfo/index.d.ts +4 -0
  168. package/dist/metainfo/index.js +28 -0
  169. package/dist/nav-indexing/category-nav.d.ts +28 -0
  170. package/dist/nav-indexing/category-nav.js +41 -0
  171. package/dist/nav-indexing/category-tree-nav.d.ts +27 -0
  172. package/dist/nav-indexing/category-tree-nav.js +49 -0
  173. package/dist/nav-indexing/doc-card-grid.d.ts +38 -0
  174. package/dist/nav-indexing/doc-card-grid.js +40 -0
  175. package/dist/nav-indexing/docs-sitemap.d.ts +25 -0
  176. package/dist/nav-indexing/docs-sitemap.js +40 -0
  177. package/dist/nav-indexing/index.d.ts +10 -0
  178. package/dist/nav-indexing/index.js +18 -0
  179. package/dist/nav-indexing/nav-card-grid.d.ts +31 -0
  180. package/dist/nav-indexing/nav-card-grid.js +43 -0
  181. package/dist/nav-indexing/site-tree-nav-demo.d.ts +39 -0
  182. package/dist/nav-indexing/site-tree-nav-demo.js +65 -0
  183. package/dist/nav-indexing/tag-nav.d.ts +38 -0
  184. package/dist/nav-indexing/tag-nav.js +96 -0
  185. package/dist/nav-indexing/types.d.ts +95 -0
  186. package/dist/nav-indexing/types.js +0 -0
  187. package/dist/nav-indexing/versions-page-content.d.ts +29 -0
  188. package/dist/nav-indexing/versions-page-content.js +76 -0
  189. package/dist/page-loading/index.d.ts +2 -0
  190. package/dist/page-loading/index.js +10 -0
  191. package/dist/page-loading/page-loading-overlay.d.ts +35 -0
  192. package/dist/page-loading/page-loading-overlay.js +50 -0
  193. package/dist/sidebar/index.d.ts +3 -0
  194. package/dist/sidebar/index.js +4 -0
  195. package/dist/sidebar/sidebar.d.ts +54 -0
  196. package/dist/sidebar/sidebar.js +35 -0
  197. package/dist/sidebar/types.d.ts +71 -0
  198. package/dist/sidebar/types.js +0 -0
  199. package/dist/sidebar-resizer/index.d.ts +14 -0
  200. package/dist/sidebar-resizer/index.js +165 -0
  201. package/dist/sidebar-resizer/sidebar-resizer-init.d.ts +20 -0
  202. package/dist/sidebar-resizer/sidebar-resizer-init.js +119 -0
  203. package/dist/sidebar-tree/build-tree.d.ts +43 -0
  204. package/dist/sidebar-tree/build-tree.js +127 -0
  205. package/dist/sidebar-tree/category-meta.d.ts +19 -0
  206. package/dist/sidebar-tree/category-meta.js +63 -0
  207. package/dist/sidebar-tree/index.d.ts +3 -0
  208. package/dist/sidebar-tree/index.js +13 -0
  209. package/dist/sidebar-tree/types.d.ts +120 -0
  210. package/dist/sidebar-tree/types.js +0 -0
  211. package/dist/tab-item/index.d.ts +2 -0
  212. package/dist/tab-item/index.js +5 -0
  213. package/dist/tab-item/tab-item.d.ts +38 -0
  214. package/dist/tab-item/tab-item.js +25 -0
  215. package/dist/theme/color-scheme-provider.d.ts +32 -0
  216. package/dist/theme/color-scheme-provider.js +43 -0
  217. package/dist/theme/color-tweak-export-modal.d.ts +20 -0
  218. package/dist/theme/color-tweak-export-modal.js +138 -0
  219. package/dist/theme/design-token-serde.d.ts +136 -0
  220. package/dist/theme/design-token-serde.js +254 -0
  221. package/dist/theme/design-token-tweak-panel.d.ts +11 -0
  222. package/dist/theme/design-token-tweak-panel.js +10 -0
  223. package/dist/theme/design-token-types.d.ts +19 -0
  224. package/dist/theme/design-token-types.js +6 -0
  225. package/dist/theme/iframe-bridge.d.ts +44 -0
  226. package/dist/theme/iframe-bridge.js +70 -0
  227. package/dist/theme/index.d.ts +9 -0
  228. package/dist/theme/index.js +42 -0
  229. package/dist/theme/theme-toggle.d.ts +17 -0
  230. package/dist/theme/theme-toggle.js +91 -0
  231. package/dist/toc/cx.d.ts +16 -0
  232. package/dist/toc/cx.js +22 -0
  233. package/dist/toc/index.d.ts +5 -0
  234. package/dist/toc/index.js +9 -0
  235. package/dist/toc/mobile-toc.d.ts +46 -0
  236. package/dist/toc/mobile-toc.js +88 -0
  237. package/dist/toc/smart-break.d.ts +27 -0
  238. package/dist/toc/smart-break.js +36 -0
  239. package/dist/toc/toc-title.d.ts +10 -0
  240. package/dist/toc/toc-title.js +14 -0
  241. package/dist/toc/toc.d.ts +60 -0
  242. package/dist/toc/toc.js +56 -0
  243. package/dist/toc/types.d.ts +23 -0
  244. package/dist/toc/types.js +0 -0
  245. package/dist/toc/use-active-heading.d.ts +30 -0
  246. package/dist/toc/use-active-heading.js +106 -0
  247. package/dist/transitions/index.d.ts +1 -0
  248. package/dist/transitions/index.js +12 -0
  249. package/dist/transitions/page-events.d.ts +41 -0
  250. package/dist/transitions/page-events.js +20 -0
  251. package/dist/url-normalizer/index.d.ts +1 -0
  252. package/dist/url-normalizer/index.js +6 -0
  253. package/dist/url-normalizer/normalize.d.ts +104 -0
  254. package/dist/url-normalizer/normalize.js +22 -0
  255. package/package.json +186 -0
@@ -0,0 +1,138 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join, resolve as resolvePath } from "node:path";
4
+ import {
5
+ runDocHistoryMetaStep
6
+ } from "./pre-build.js";
7
+ const DEFAULT_SERVER_PORT = 4322;
8
+ const DEFAULT_MAX_ENTRIES = 50;
9
+ const DOC_HISTORY_ROUTE_PREFIX = "/doc-history/";
10
+ const DOC_HISTORY_OUTPUT_DIRNAME = "doc-history";
11
+ const DOC_HISTORY_GENERATE_BIN = "doc-history-generate";
12
+ function docHistoryPlugin(options) {
13
+ return { name: "doc-history", options };
14
+ }
15
+ function createDocHistoryDevMiddleware(options, logger) {
16
+ const port = options.serverPort ?? DEFAULT_SERVER_PORT;
17
+ return (req, res, next) => {
18
+ const url = req.url ?? "";
19
+ if (!url.includes(DOC_HISTORY_ROUTE_PREFIX)) {
20
+ next();
21
+ return;
22
+ }
23
+ const idx = url.indexOf(DOC_HISTORY_ROUTE_PREFIX);
24
+ const proxyPath = url.slice(idx);
25
+ const proxyUrl = `http://localhost:${port}${proxyPath}`;
26
+ fetch(proxyUrl).then(async (upstream) => {
27
+ res.statusCode = upstream.status;
28
+ res.setHeader(
29
+ "Content-Type",
30
+ upstream.headers.get("content-type") ?? "application/json"
31
+ );
32
+ const body = await upstream.text();
33
+ res.end(body);
34
+ }).catch((err) => {
35
+ const msg = err instanceof Error ? err.message : String(err);
36
+ logger?.warn(
37
+ `Doc history proxy failed: ${msg}. Is the doc-history server running on port ${port}?`
38
+ );
39
+ res.statusCode = 502;
40
+ res.setHeader("Content-Type", "application/json");
41
+ res.end(
42
+ JSON.stringify({
43
+ error: `Doc history server unavailable (port ${port})`
44
+ })
45
+ );
46
+ });
47
+ };
48
+ }
49
+ async function runDocHistoryPostBuild(options, ctx) {
50
+ if (process.env.SKIP_DOC_HISTORY === "1") {
51
+ ctx.logger?.info("Skipping doc history generation (SKIP_DOC_HISTORY=1)");
52
+ return;
53
+ }
54
+ const args = buildGenerateCliArgs(options, ctx.outDir);
55
+ await spawnDocHistoryGenerate(args, ctx.logger);
56
+ }
57
+ function buildGenerateCliArgs(options, outDir) {
58
+ const historyOut = join(outDir, DOC_HISTORY_OUTPUT_DIRNAME);
59
+ const args = [
60
+ "--content-dir",
61
+ options.docsDir,
62
+ "--out-dir",
63
+ historyOut
64
+ ];
65
+ if (options.locales) {
66
+ for (const [key, locale] of Object.entries(options.locales)) {
67
+ args.push("--locale", `${key}:${locale.dir}`);
68
+ }
69
+ }
70
+ if (options.maxEntries != null) {
71
+ args.push("--max-entries", String(options.maxEntries));
72
+ }
73
+ return args;
74
+ }
75
+ function resolveDocHistoryGenerateBin() {
76
+ const localRequire = createRequire(import.meta.url);
77
+ const pkgJsonPath = localRequire.resolve(
78
+ "@takazudo/zudo-doc-history-server/package.json"
79
+ );
80
+ const pkgJson = localRequire(
81
+ "@takazudo/zudo-doc-history-server/package.json"
82
+ );
83
+ const binEntry = typeof pkgJson.bin === "string" ? pkgJson.bin : pkgJson.bin?.[DOC_HISTORY_GENERATE_BIN];
84
+ if (!binEntry) {
85
+ throw new Error(
86
+ `@takazudo/zudo-doc-history-server does not declare a '${DOC_HISTORY_GENERATE_BIN}' bin entry`
87
+ );
88
+ }
89
+ return resolvePath(dirname(pkgJsonPath), binEntry);
90
+ }
91
+ function spawnDocHistoryGenerate(args, logger) {
92
+ return new Promise((resolveSpawn, reject) => {
93
+ let cliPath;
94
+ try {
95
+ cliPath = resolveDocHistoryGenerateBin();
96
+ } catch (err) {
97
+ const msg = err instanceof Error ? err.message : String(err);
98
+ logger?.warn(
99
+ `Failed to resolve ${DOC_HISTORY_GENERATE_BIN} from @takazudo/zudo-doc-history-server: ${msg}`
100
+ );
101
+ reject(err instanceof Error ? err : new Error(msg));
102
+ return;
103
+ }
104
+ const child = spawn(process.execPath, [cliPath, ...args], {
105
+ stdio: "inherit",
106
+ shell: false
107
+ });
108
+ child.on("error", (err) => {
109
+ logger?.warn(
110
+ `Failed to launch ${DOC_HISTORY_GENERATE_BIN}: ${err.message}`
111
+ );
112
+ reject(err);
113
+ });
114
+ child.on("exit", (code, signal) => {
115
+ if (code === 0) {
116
+ resolveSpawn();
117
+ return;
118
+ }
119
+ const reason = code != null ? `exit code ${code}` : `signal ${signal ?? "unknown"}`;
120
+ reject(
121
+ new Error(`${DOC_HISTORY_GENERATE_BIN} failed (${reason})`)
122
+ );
123
+ });
124
+ });
125
+ }
126
+ export {
127
+ DEFAULT_MAX_ENTRIES,
128
+ DEFAULT_SERVER_PORT,
129
+ DOC_HISTORY_GENERATE_BIN,
130
+ DOC_HISTORY_OUTPUT_DIRNAME,
131
+ DOC_HISTORY_ROUTE_PREFIX,
132
+ buildGenerateCliArgs,
133
+ createDocHistoryDevMiddleware,
134
+ docHistoryPlugin,
135
+ resolveDocHistoryGenerateBin,
136
+ runDocHistoryMetaStep,
137
+ runDocHistoryPostBuild
138
+ };
@@ -0,0 +1,58 @@
1
+ /** A single non-default locale entry; mirrors `settings.locales[*]`. */
2
+ interface DocHistoryMetaLocaleConfig {
3
+ /** Absolute or project-relative directory holding this locale's MDX content. */
4
+ dir: string;
5
+ }
6
+ /** A versioned docs entry; mirrors `settings.versions[*]`. */
7
+ interface DocHistoryMetaVersionConfig {
8
+ /** Slug used to namespace the version (e.g. `"1.0"`). */
9
+ slug: string;
10
+ /** Default-locale content directory for this version. */
11
+ docsDir: string;
12
+ /** Optional non-default locales, keyed by locale code. */
13
+ locales?: Record<string, DocHistoryMetaLocaleConfig>;
14
+ }
15
+ /** Options accepted by the preBuild runner. */
16
+ interface RunDocHistoryMetaOptions {
17
+ /** Project root — directory containing `zfb.config.ts`. Resolves all relative paths. */
18
+ projectRoot: string;
19
+ /** Default-locale content directory (e.g. `"src/content/docs"`). */
20
+ docsDir: string;
21
+ /** Optional non-default locales, keyed by locale code. */
22
+ locales?: Record<string, DocHistoryMetaLocaleConfig>;
23
+ /**
24
+ * Optional versioned docs (e.g. legacy `1.0`). Each version produces
25
+ * its own default-locale collection plus per-locale variants.
26
+ * Currently unused by the host's emitted manifest (parity with
27
+ * `scripts/zfb-prebuild.mjs`) — accepted here so callers can pass
28
+ * versions through without ad-hoc filtering.
29
+ */
30
+ versions?: DocHistoryMetaVersionConfig[];
31
+ /** Optional logger. Falls back to `console`. */
32
+ logger?: {
33
+ info(msg: string): void;
34
+ warn?(msg: string): void;
35
+ };
36
+ }
37
+ /** A single manifest entry. */
38
+ interface DocHistoryMetaEntry {
39
+ author: string;
40
+ createdDate: string;
41
+ updatedDate: string;
42
+ }
43
+ /** Manifest shape — keyed by composedSlug. */
44
+ type DocHistoryMetaManifest = Record<string, DocHistoryMetaEntry>;
45
+ /**
46
+ * Emit `<projectRoot>/.zfb/doc-history-meta.json` from git history.
47
+ *
48
+ * Honours the `SKIP_DOC_HISTORY=1` env-var short-circuit (see header
49
+ * comment for the CI contract). Writes a byte-identical empty manifest
50
+ * (`{}\n`) when set; writes a sorted-key JSON document otherwise.
51
+ *
52
+ * Skips files with no git history (untracked / not yet committed) by
53
+ * omitting them from the manifest — the page-side wrapper treats
54
+ * undefined entries as "no SSR data".
55
+ */
56
+ declare function runDocHistoryMetaStep(options: RunDocHistoryMetaOptions): Promise<void>;
57
+
58
+ export { type DocHistoryMetaEntry, type DocHistoryMetaLocaleConfig, type DocHistoryMetaManifest, type DocHistoryMetaVersionConfig, type RunDocHistoryMetaOptions, runDocHistoryMetaStep };
@@ -0,0 +1,67 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import {
4
+ collectContentFiles,
5
+ getFileCommits,
6
+ getFirstCommit,
7
+ getCommitInfo
8
+ } from "@takazudo/zudo-doc-history-server/git-history";
9
+ const META_OUT_RELATIVE_DIR = ".zfb";
10
+ const META_OUT_FILENAME = "doc-history-meta.json";
11
+ async function runDocHistoryMetaStep(options) {
12
+ const projectRoot = path.resolve(options.projectRoot);
13
+ const zfbDir = path.join(projectRoot, META_OUT_RELATIVE_DIR);
14
+ const outPath = path.join(zfbDir, META_OUT_FILENAME);
15
+ const logger = options.logger ?? defaultLogger;
16
+ if (process.env.SKIP_DOC_HISTORY === "1") {
17
+ logger.info(
18
+ "[doc-history-meta] SKIP_DOC_HISTORY=1 \u2014 emitting empty doc-history-meta.json"
19
+ );
20
+ fs.mkdirSync(zfbDir, { recursive: true });
21
+ fs.writeFileSync(outPath, "{}\n", "utf-8");
22
+ return;
23
+ }
24
+ const dirEntries = [
25
+ [null, path.resolve(projectRoot, options.docsDir)]
26
+ ];
27
+ if (options.locales) {
28
+ for (const [code, locale] of Object.entries(options.locales)) {
29
+ dirEntries.push([code, path.resolve(projectRoot, locale.dir)]);
30
+ }
31
+ }
32
+ const meta = {};
33
+ for (const [localeKey, contentDir] of dirEntries) {
34
+ const files = collectContentFiles(contentDir);
35
+ for (const { filePath, slug } of files) {
36
+ const commits = getFileCommits(filePath, 1);
37
+ if (commits.length === 0) continue;
38
+ const newestInfo = getCommitInfo(commits[0], filePath);
39
+ const firstHash = getFirstCommit(filePath);
40
+ const oldestInfo = firstHash && firstHash !== commits[0] ? getCommitInfo(firstHash, filePath) : newestInfo;
41
+ const composedSlug = localeKey ? `${localeKey}/${slug}` : slug;
42
+ meta[composedSlug] = {
43
+ // Author comes from the FIRST (oldest) commit.
44
+ author: oldestInfo.author,
45
+ // createdDate = oldest commit; updatedDate = newest commit.
46
+ createdDate: oldestInfo.date,
47
+ updatedDate: newestInfo.date
48
+ };
49
+ }
50
+ }
51
+ fs.mkdirSync(zfbDir, { recursive: true });
52
+ fs.writeFileSync(outPath, JSON.stringify(meta, null, 2) + "\n", "utf-8");
53
+ logger.info(
54
+ `[doc-history-meta] wrote ${Object.keys(meta).length} entries \u2192 .zfb/doc-history-meta.json`
55
+ );
56
+ }
57
+ const defaultLogger = {
58
+ info(msg) {
59
+ console.log(msg);
60
+ },
61
+ warn(msg) {
62
+ console.warn(msg);
63
+ }
64
+ };
65
+ export {
66
+ runDocHistoryMetaStep
67
+ };
@@ -0,0 +1,40 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { LlmsTxtSiteMeta, LlmsTxtLocaleConfig } from './types.js';
3
+
4
+ /** Connect-style middleware signature — works as a Vite plugin middleware. */
5
+ type LlmsTxtNextFn = (err?: unknown) => void;
6
+ type LlmsTxtMiddleware = (req: IncomingMessage, res: ServerResponse, next: LlmsTxtNextFn) => void;
7
+ /** Minimal logger surface used by the middleware on generation failure. */
8
+ interface LlmsTxtMiddlewareLogger {
9
+ warn(msg: string): void;
10
+ }
11
+ /**
12
+ * Build-time + dev-time options. Mirrors {@link LlmsTxtEmitOptions}
13
+ * minus the `outDir` field (the dev middleware never writes to disk)
14
+ * so callers can hand a single options object to both `emitLlmsTxt`
15
+ * (build) and `createLlmsTxtDevMiddleware` (dev).
16
+ */
17
+ interface LlmsTxtDevMiddlewareOptions extends LlmsTxtSiteMeta {
18
+ /** URL base used when materialising per-page links. */
19
+ base: string;
20
+ /** Optional absolute site URL; see `LlmsTxtLoadOptions.siteUrl`. */
21
+ siteUrl?: string;
22
+ /** Default-locale content directory. */
23
+ defaultLocaleDir: string;
24
+ /** Additional locales (e.g. `[{ code: "ja", dir: "src/content/docs-ja" }]`). */
25
+ locales?: LlmsTxtLocaleConfig[];
26
+ }
27
+ /**
28
+ * Build a dev-server middleware that responds to GET requests for
29
+ * `/llms.txt`, `/llms-full.txt`, `/<code>/llms.txt`, and
30
+ * `/<code>/llms-full.txt`. Unknown locale prefixes fall through (the
31
+ * chain returns 404), avoiding collisions with future routes that
32
+ * might share the `/<thing>/llms.txt` shape.
33
+ *
34
+ * Every request rebuilds the entries from disk — the same pattern the
35
+ * sibling `searchIndex` middleware uses — so authoring loops surface
36
+ * content edits without a dev-server restart.
37
+ */
38
+ declare function createLlmsTxtDevMiddleware(options: LlmsTxtDevMiddlewareOptions, logger?: LlmsTxtMiddlewareLogger): LlmsTxtMiddleware;
39
+
40
+ export { type LlmsTxtDevMiddlewareOptions, type LlmsTxtMiddleware, type LlmsTxtMiddlewareLogger, type LlmsTxtNextFn, createLlmsTxtDevMiddleware };
@@ -0,0 +1,76 @@
1
+ import { generateLlmsFullTxt, generateLlmsTxt } from "./generate.js";
2
+ import { loadDocEntries } from "./load.js";
3
+ const LLMS_KIND_PATTERN = /^(?:\/(.+?))?\/(llms|llms-full)\.txt$/;
4
+ function createLlmsTxtDevMiddleware(options, logger) {
5
+ const meta = {
6
+ siteName: options.siteName,
7
+ siteDescription: options.siteDescription
8
+ };
9
+ const localeCodes = new Set((options.locales ?? []).map((l) => l.code));
10
+ const localeDirByCode = new Map(
11
+ (options.locales ?? []).map((l) => [l.code, l.dir])
12
+ );
13
+ const base = options.base ?? "";
14
+ const siteUrl = options.siteUrl || void 0;
15
+ return (req, res, next) => {
16
+ const url = req.url ?? "";
17
+ const match = matchLlmsRoute(stripBase(url, base), localeCodes);
18
+ if (!match) {
19
+ next();
20
+ return;
21
+ }
22
+ if (req.method && req.method !== "GET" && req.method !== "HEAD") {
23
+ res.statusCode = 405;
24
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
25
+ res.end("Method Not Allowed");
26
+ return;
27
+ }
28
+ try {
29
+ const contentDir = match.locale === null ? options.defaultLocaleDir : localeDirByCode.get(match.locale);
30
+ if (contentDir === void 0) {
31
+ res.statusCode = 404;
32
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
33
+ res.end("Not Found");
34
+ return;
35
+ }
36
+ const entries = loadDocEntries({
37
+ contentDir,
38
+ locale: match.locale,
39
+ base,
40
+ siteUrl
41
+ });
42
+ const body = match.kind === "llms" ? generateLlmsTxt(entries, meta) : generateLlmsFullTxt(entries, meta);
43
+ res.statusCode = 200;
44
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
45
+ res.end(body);
46
+ } catch (err) {
47
+ const msg = err instanceof Error ? err.message : String(err);
48
+ logger?.warn(`llms-txt generation failed: ${msg}`);
49
+ res.statusCode = 500;
50
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
51
+ res.end("Internal Server Error");
52
+ }
53
+ };
54
+ }
55
+ function matchLlmsRoute(url, localeCodes) {
56
+ const pathname = url.split("?")[0];
57
+ const m = pathname.match(LLMS_KIND_PATTERN);
58
+ if (!m) return null;
59
+ const prefix = m[1];
60
+ const kind = m[2];
61
+ if (prefix === void 0) return { kind, locale: null };
62
+ if (localeCodes.has(prefix)) return { kind, locale: prefix };
63
+ return null;
64
+ }
65
+ function stripBase(url, base) {
66
+ if (!base || base === "/") return url;
67
+ const normalised = base.endsWith("/") ? base.slice(0, -1) : base;
68
+ if (url.startsWith(normalised + "/")) {
69
+ return url.slice(normalised.length);
70
+ }
71
+ if (url === normalised) return "/";
72
+ return url;
73
+ }
74
+ export {
75
+ createLlmsTxtDevMiddleware
76
+ };
@@ -0,0 +1,22 @@
1
+ import { LlmsTxtEmitOptions, LlmsTxtEmitResult } from './types.js';
2
+
3
+ /**
4
+ * Build-time emitter that writes `llms.txt` and `llms-full.txt` for the
5
+ * default locale plus every additional locale.
6
+ *
7
+ * Designed as a zfb-shaped build hook: the function takes a single
8
+ * options object, performs synchronous fs writes, and returns the list
9
+ * of files it produced. Adapters wrap it for their build system —
10
+ * Astro's `astro:build:done`, a future zfb plugin's `onBuildDone`,
11
+ * a Node CLI in CI, etc.
12
+ */
13
+
14
+ /**
15
+ * Walk every configured content root, generate the two files per
16
+ * locale, and write them to `outDir` (default locale) and
17
+ * `outDir/<code>/` (additional locales). The output directory is
18
+ * created if needed; existing files are overwritten.
19
+ */
20
+ declare function emitLlmsTxt(options: LlmsTxtEmitOptions): LlmsTxtEmitResult;
21
+
22
+ export { emitLlmsTxt };
@@ -0,0 +1,55 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { generateLlmsFullTxt, generateLlmsTxt } from "./generate.js";
4
+ import { loadDocEntries } from "./load.js";
5
+ function emitLlmsTxt(options) {
6
+ const {
7
+ outDir,
8
+ base,
9
+ siteUrl,
10
+ siteName,
11
+ siteDescription,
12
+ defaultLocaleDir,
13
+ locales = [],
14
+ logger
15
+ } = options;
16
+ const meta = { siteName, siteDescription };
17
+ const written = [];
18
+ const defaultEntries = loadDocEntries({
19
+ contentDir: defaultLocaleDir,
20
+ locale: null,
21
+ base,
22
+ siteUrl
23
+ });
24
+ mkdirSync(outDir, { recursive: true });
25
+ const defaultIndexPath = join(outDir, "llms.txt");
26
+ const defaultFullPath = join(outDir, "llms-full.txt");
27
+ writeFileSync(defaultIndexPath, generateLlmsTxt(defaultEntries, meta));
28
+ writeFileSync(defaultFullPath, generateLlmsFullTxt(defaultEntries, meta));
29
+ written.push(defaultIndexPath, defaultFullPath);
30
+ logger?.info(
31
+ `Generated llms.txt and llms-full.txt (${defaultEntries.length} pages)`
32
+ );
33
+ for (const { code, dir } of locales) {
34
+ const localeEntries = loadDocEntries({
35
+ contentDir: dir,
36
+ locale: code,
37
+ base,
38
+ siteUrl
39
+ });
40
+ const localeDir = join(outDir, code);
41
+ mkdirSync(localeDir, { recursive: true });
42
+ const indexPath = join(localeDir, "llms.txt");
43
+ const fullPath = join(localeDir, "llms-full.txt");
44
+ writeFileSync(indexPath, generateLlmsTxt(localeEntries, meta));
45
+ writeFileSync(fullPath, generateLlmsFullTxt(localeEntries, meta));
46
+ written.push(indexPath, fullPath);
47
+ logger?.info(
48
+ `Generated ${code}/llms.txt and ${code}/llms-full.txt (${localeEntries.length} pages)`
49
+ );
50
+ }
51
+ return { written };
52
+ }
53
+ export {
54
+ emitLlmsTxt
55
+ };
@@ -0,0 +1,26 @@
1
+ import { LlmsDocEntry, LlmsTxtSiteMeta } from './types.js';
2
+
3
+ /**
4
+ * Pure string generators for `llms.txt` and `llms-full.txt`.
5
+ *
6
+ * Both functions are intentionally pure (no fs, no logger, no globals)
7
+ * so they can be unit-tested with fixture entries and so a future zfb
8
+ * non-HTML page (`pages/llms.txt.tsx`) can call them straight from a
9
+ * default export without a build-time hook.
10
+ *
11
+ * Output format must remain byte-identical to the legacy Astro emitter
12
+ * — the file shape is part of the project's public surface.
13
+ */
14
+
15
+ /**
16
+ * Slim index — site header followed by a markdown bullet per page with
17
+ * `[title](url): description`.
18
+ */
19
+ declare function generateLlmsTxt(entries: readonly LlmsDocEntry[], meta: LlmsTxtSiteMeta): string;
20
+ /**
21
+ * Full content concatenation — site header followed by every page's
22
+ * stripped body separated by `---` rules.
23
+ */
24
+ declare function generateLlmsFullTxt(entries: readonly LlmsDocEntry[], meta: LlmsTxtSiteMeta): string;
25
+
26
+ export { generateLlmsFullTxt, generateLlmsTxt };
@@ -0,0 +1,36 @@
1
+ function generateLlmsTxt(entries, meta) {
2
+ const lines = [];
3
+ lines.push(`# ${meta.siteName}`);
4
+ lines.push("");
5
+ lines.push(`> ${meta.siteDescription}`);
6
+ lines.push("");
7
+ lines.push("## Docs");
8
+ lines.push("");
9
+ for (const entry of entries) {
10
+ lines.push(`- [${entry.title}](${entry.url}): ${entry.description}`);
11
+ }
12
+ lines.push("");
13
+ return lines.join("\n");
14
+ }
15
+ function generateLlmsFullTxt(entries, meta) {
16
+ const lines = [];
17
+ lines.push(`# ${meta.siteName}`);
18
+ lines.push("");
19
+ lines.push(`> ${meta.siteDescription}`);
20
+ for (const entry of entries) {
21
+ lines.push("");
22
+ lines.push("---");
23
+ lines.push("");
24
+ lines.push(`# ${entry.title}`);
25
+ lines.push("");
26
+ lines.push(`> Source: ${entry.url}`);
27
+ lines.push("");
28
+ lines.push(entry.content);
29
+ }
30
+ lines.push("");
31
+ return lines.join("\n");
32
+ }
33
+ export {
34
+ generateLlmsFullTxt,
35
+ generateLlmsTxt
36
+ };
@@ -0,0 +1,7 @@
1
+ export { LlmsTxtDevMiddlewareOptions, LlmsTxtMiddleware, LlmsTxtMiddlewareLogger, LlmsTxtNextFn, createLlmsTxtDevMiddleware } from './dev-middleware.js';
2
+ export { emitLlmsTxt } from './emit.js';
3
+ export { generateLlmsFullTxt, generateLlmsTxt } from './generate.js';
4
+ export { collectMdFiles, isExcluded, loadDocEntries, parseMarkdownFile, slugToUrl } from './load.js';
5
+ export { stripImportsAndJsx, stripMarkdown } from './strip.js';
6
+ export { LlmsDocEntry, LlmsTxtEmitOptions, LlmsTxtEmitResult, LlmsTxtFrontmatter, LlmsTxtLoadOptions, LlmsTxtLocaleConfig, LlmsTxtLogger, LlmsTxtSiteMeta } from './types.js';
7
+ import 'node:http';
@@ -0,0 +1,27 @@
1
+ import { createLlmsTxtDevMiddleware } from "./dev-middleware.js";
2
+ import { emitLlmsTxt } from "./emit.js";
3
+ import {
4
+ generateLlmsFullTxt,
5
+ generateLlmsTxt
6
+ } from "./generate.js";
7
+ import {
8
+ collectMdFiles,
9
+ isExcluded,
10
+ loadDocEntries,
11
+ parseMarkdownFile,
12
+ slugToUrl
13
+ } from "./load.js";
14
+ import { stripImportsAndJsx, stripMarkdown } from "./strip.js";
15
+ export {
16
+ collectMdFiles,
17
+ createLlmsTxtDevMiddleware,
18
+ emitLlmsTxt,
19
+ generateLlmsFullTxt,
20
+ generateLlmsTxt,
21
+ isExcluded,
22
+ loadDocEntries,
23
+ parseMarkdownFile,
24
+ slugToUrl,
25
+ stripImportsAndJsx,
26
+ stripMarkdown
27
+ };
@@ -0,0 +1,51 @@
1
+ import { LlmsTxtFrontmatter, LlmsTxtLoadOptions, LlmsDocEntry } from './types.js';
2
+
3
+ /**
4
+ * Filesystem loader for llms-txt content. Walks a markdown content root,
5
+ * parses frontmatter via `gray-matter`, and emits sorted
6
+ * {@link LlmsDocEntry} records ready for the generator stage.
7
+ *
8
+ * Lives in the framework layer (no `@/...` imports) so any consumer
9
+ * project — Astro today, zfb tomorrow, fixture-driven unit tests in
10
+ * between — can drive the emitter without dragging in the legacy
11
+ * `src/utils/content-files.ts` module.
12
+ */
13
+
14
+ /**
15
+ * Walk `dir` recursively and return every `*.md` / `*.mdx` file's
16
+ * absolute path together with its docs-relative slug. Files (or whole
17
+ * directories) whose name starts with `_` are skipped, matching the
18
+ * Content Collections convention.
19
+ */
20
+ declare function collectMdFiles(dir: string): Array<{
21
+ filePath: string;
22
+ slug: string;
23
+ }>;
24
+ /**
25
+ * Compute a docs URL for a given slug + locale. Mirrors the legacy
26
+ * `slugToUrl(slug, locale, true)` call site exactly: when `siteUrl` is
27
+ * empty, the URL is path-only; when `siteUrl` is set, a fully qualified
28
+ * URL is produced.
29
+ */
30
+ declare function slugToUrl(slug: string, locale: string | null, base: string, siteUrl?: string): string;
31
+ /** Whether a given doc should be excluded from the llms.txt index. */
32
+ declare function isExcluded(data: LlmsTxtFrontmatter): boolean;
33
+ /**
34
+ * Parse a markdown file into frontmatter + body. Returns `null` when
35
+ * the file is unreadable so callers can simply skip it.
36
+ */
37
+ declare function parseMarkdownFile(filePath: string): {
38
+ data: LlmsTxtFrontmatter;
39
+ content: string;
40
+ } | null;
41
+ /**
42
+ * Build the sorted {@link LlmsDocEntry} list for a single content root.
43
+ * Excluded pages (draft / unlisted / search_exclude) are dropped.
44
+ * Entries are sorted ascending by `sidebar_position`; entries without a
45
+ * position go last (they share `Number.MAX_SAFE_INTEGER`, and
46
+ * `Array.prototype.sort` is stable in V8 so ties keep filesystem
47
+ * traversal order — same as the legacy emitter).
48
+ */
49
+ declare function loadDocEntries(options: LlmsTxtLoadOptions): LlmsDocEntry[];
50
+
51
+ export { collectMdFiles, isExcluded, loadDocEntries, parseMarkdownFile, slugToUrl };