@wrikka/create-docs 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 (380) hide show
  1. package/README.md +137 -0
  2. package/bin/cli.js +408 -0
  3. package/dist/index.cjs +20 -0
  4. package/dist/index.d.cts +475 -0
  5. package/dist/index.d.ts +475 -0
  6. package/dist/index.js +20 -0
  7. package/docs/_dir.yml +2 -0
  8. package/docs/config.md +75 -0
  9. package/docs/data-sources.md +88 -0
  10. package/docs/deploy.md +38 -0
  11. package/docs/features.md +51 -0
  12. package/docs/getting-started.md +42 -0
  13. package/docs/index.md +34 -0
  14. package/docs/markdown.md +154 -0
  15. package/docs/theming.md +53 -0
  16. package/package.json +142 -0
  17. package/scripts/generate-docs-index.ts +111 -0
  18. package/scripts/github-pull.ts +124 -0
  19. package/src/adapters/config/index.ts +1 -0
  20. package/src/adapters/config/node-config-loader.ts +36 -0
  21. package/src/adapters/fs/fs-docs-loader.ts +31 -0
  22. package/src/adapters/fs/index.ts +12 -0
  23. package/src/adapters/fs/memory-fs.ts +60 -0
  24. package/src/adapters/fs/node-fs.ts +116 -0
  25. package/src/adapters/git/index.ts +164 -0
  26. package/src/adapters/github/github-pull.ts +340 -0
  27. package/src/adapters/index.ts +6 -0
  28. package/src/adapters/velite.ts +112 -0
  29. package/src/adapters/vite/docs-watcher.ts +36 -0
  30. package/src/adapters/vite/index.ts +2 -0
  31. package/src/adapters/vite/polling-watcher.ts +89 -0
  32. package/src/code-block-enhancements/index.ts +3 -0
  33. package/src/config/index.ts +15 -0
  34. package/src/content/index.ts +11 -0
  35. package/src/content-validation/index.ts +5 -0
  36. package/src/index.ts +174 -0
  37. package/src/live-editor/index.ts +7 -0
  38. package/src/modules/analytics/application/plausible-adapter.ts +103 -0
  39. package/src/modules/analytics/application/umami-adapter.ts +109 -0
  40. package/src/modules/analytics/domain/events/index.ts +37 -0
  41. package/src/modules/analytics/domain/models/index.ts +35 -0
  42. package/src/modules/analytics/domain/operations/analytics-operations.ts +67 -0
  43. package/src/modules/analytics/domain/validators/index.ts +50 -0
  44. package/src/modules/analytics/index.ts +9 -0
  45. package/src/modules/analytics/ports/analytics-port.ts +17 -0
  46. package/src/modules/analytics/types/analytics.ts +16 -0
  47. package/src/modules/api-docs/application/type-docs-adapter.ts +34 -0
  48. package/src/modules/api-docs/domain/events/index.ts +42 -0
  49. package/src/modules/api-docs/domain/models/index.ts +40 -0
  50. package/src/modules/api-docs/domain/operations/api-docs-operations.ts +59 -0
  51. package/src/modules/api-docs/domain/validators/index.ts +56 -0
  52. package/src/modules/api-docs/index.ts +8 -0
  53. package/src/modules/api-docs/ports/api-docs-port.ts +21 -0
  54. package/src/modules/api-docs/types/api-docs.ts +15 -0
  55. package/src/modules/api-reference/openapi.ts +166 -0
  56. package/src/modules/api-reference/orpc.ts +94 -0
  57. package/src/modules/code-block-enhancements/application/usecases/index.ts +39 -0
  58. package/src/modules/code-block-enhancements/domain/code-operations.ts +72 -0
  59. package/src/modules/code-block-enhancements/domain/events/index.ts +58 -0
  60. package/src/modules/code-block-enhancements/domain/index.ts +11 -0
  61. package/src/modules/code-block-enhancements/domain/models/index.ts +36 -0
  62. package/src/modules/code-block-enhancements/domain/validators/index.ts +50 -0
  63. package/src/modules/code-block-enhancements/index.ts +10 -0
  64. package/src/modules/code-block-enhancements/ports/index.ts +18 -0
  65. package/src/modules/code-block-enhancements/types/index.ts +37 -0
  66. package/src/modules/code-execution/application/sandbox-adapter.ts +44 -0
  67. package/src/modules/code-execution/domain/events/index.ts +58 -0
  68. package/src/modules/code-execution/domain/models/index.ts +33 -0
  69. package/src/modules/code-execution/domain/operations/execution-operations.ts +60 -0
  70. package/src/modules/code-execution/domain/validators/index.ts +50 -0
  71. package/src/modules/code-execution/index.ts +8 -0
  72. package/src/modules/code-execution/ports/execution-port.ts +15 -0
  73. package/src/modules/code-execution/types/execution.ts +15 -0
  74. package/src/modules/comments/application/giscus-adapter.ts +55 -0
  75. package/src/modules/comments/domain/events/index.ts +55 -0
  76. package/src/modules/comments/domain/models/index.ts +30 -0
  77. package/src/modules/comments/domain/operations/comment-operations.ts +46 -0
  78. package/src/modules/comments/domain/validators/index.ts +50 -0
  79. package/src/modules/comments/index.ts +8 -0
  80. package/src/modules/comments/ports/comment-port.ts +18 -0
  81. package/src/modules/comments/types/comments.ts +19 -0
  82. package/src/modules/config/application/usecases/index.ts +1 -0
  83. package/src/modules/config/application/usecases/loadConfig.ts +38 -0
  84. package/src/modules/config/application/workflows/index.ts +1 -0
  85. package/src/modules/config/application/workflows/validateConfig.ts +14 -0
  86. package/src/modules/config/domain/events/index.ts +50 -0
  87. package/src/modules/config/domain/models/index.ts +9 -0
  88. package/src/modules/config/domain/operations/defaults.ts +74 -0
  89. package/src/modules/config/domain/operations/index.ts +2 -0
  90. package/src/modules/config/domain/operations/resolve.ts +40 -0
  91. package/src/modules/config/domain/validators/index.ts +152 -0
  92. package/src/modules/config/index.ts +23 -0
  93. package/src/modules/config/ports/index.ts +15 -0
  94. package/src/modules/config/types/index.ts +113 -0
  95. package/src/modules/config/types/public.ts +19 -0
  96. package/src/modules/content/application/cache/index.ts +6 -0
  97. package/src/modules/content/application/cache/parse-cache.ts +6 -0
  98. package/src/modules/content/application/cache/parseCache.ts +56 -0
  99. package/src/modules/content/application/usecases/index.ts +2 -0
  100. package/src/modules/content/application/usecases/load-doc-page.ts +1 -0
  101. package/src/modules/content/application/usecases/loadDocPage.ts +111 -0
  102. package/src/modules/content/application/usecases/scanDocsDir.ts +105 -0
  103. package/src/modules/content/application/workflows/index.ts +1 -0
  104. package/src/modules/content/application/workflows/processDocs.ts +51 -0
  105. package/src/modules/content/domain/events/index.ts +61 -0
  106. package/src/modules/content/domain/models/index.ts +13 -0
  107. package/src/modules/content/domain/operations/build-page.ts +146 -0
  108. package/src/modules/content/domain/operations/buildPage.ts +93 -0
  109. package/src/modules/content/domain/operations/index.ts +17 -0
  110. package/src/modules/content/domain/operations/parse.ts +241 -0
  111. package/src/modules/content/domain/validators/frontmatter.ts +82 -0
  112. package/src/modules/content/domain/validators/index.ts +6 -0
  113. package/src/modules/content/index.ts +27 -0
  114. package/src/modules/content/ports/index.ts +45 -0
  115. package/src/modules/content/ports/velite.ts +134 -0
  116. package/src/modules/content/types/index.ts +76 -0
  117. package/src/modules/content/types/public.ts +1 -0
  118. package/src/modules/content-linting/application/builtin-linting-adapter.ts +82 -0
  119. package/src/modules/content-linting/domain/events/index.ts +58 -0
  120. package/src/modules/content-linting/domain/models/index.ts +46 -0
  121. package/src/modules/content-linting/domain/operations/linting-operations.ts +82 -0
  122. package/src/modules/content-linting/domain/validators/index.ts +50 -0
  123. package/src/modules/content-linting/index.ts +8 -0
  124. package/src/modules/content-linting/ports/linting-port.ts +21 -0
  125. package/src/modules/content-linting/types/linting.ts +18 -0
  126. package/src/modules/content-validation/application/usecases/index.ts +18 -0
  127. package/src/modules/content-validation/domain/events/index.ts +7 -0
  128. package/src/modules/content-validation/domain/index.ts +16 -0
  129. package/src/modules/content-validation/domain/models/index.ts +33 -0
  130. package/src/modules/content-validation/domain/operations/index.ts +157 -0
  131. package/src/modules/content-validation/domain/types/index.ts +17 -0
  132. package/src/modules/content-validation/domain/validators/index.ts +8 -0
  133. package/src/modules/content-validation/index.ts +9 -0
  134. package/src/modules/content-validation/ports/index.ts +15 -0
  135. package/src/modules/content-validation/types/index.ts +5 -0
  136. package/src/modules/export/application/puppeteer-export-adapter.ts +63 -0
  137. package/src/modules/export/domain/events/index.ts +55 -0
  138. package/src/modules/export/domain/models/index.ts +38 -0
  139. package/src/modules/export/domain/operations/export-operations.ts +56 -0
  140. package/src/modules/export/domain/validators/index.ts +52 -0
  141. package/src/modules/export/index.ts +8 -0
  142. package/src/modules/export/ports/export-port.ts +15 -0
  143. package/src/modules/export/types/export.ts +15 -0
  144. package/src/modules/external-search/application/algolia-adapter.ts +182 -0
  145. package/src/modules/external-search/application/meilisearch-adapter.ts +161 -0
  146. package/src/modules/external-search/domain/events/index.ts +42 -0
  147. package/src/modules/external-search/domain/models/index.ts +42 -0
  148. package/src/modules/external-search/domain/operations/search-operations.ts +53 -0
  149. package/src/modules/external-search/domain/validators/index.ts +55 -0
  150. package/src/modules/external-search/index.ts +9 -0
  151. package/src/modules/external-search/ports/external-search-port.ts +23 -0
  152. package/src/modules/external-search/types/search.ts +19 -0
  153. package/src/modules/git-history/application/simple-git-adapter.ts +46 -0
  154. package/src/modules/git-history/domain/events/index.ts +34 -0
  155. package/src/modules/git-history/domain/models/index.ts +25 -0
  156. package/src/modules/git-history/domain/operations/git-history-operations.ts +56 -0
  157. package/src/modules/git-history/domain/validators/index.ts +52 -0
  158. package/src/modules/git-history/index.ts +8 -0
  159. package/src/modules/git-history/ports/git-history-port.ts +21 -0
  160. package/src/modules/git-history/types/git-history.ts +15 -0
  161. package/src/modules/i18n/application/fs-locale-loader.ts +39 -0
  162. package/src/modules/i18n/domain/events/index.ts +34 -0
  163. package/src/modules/i18n/domain/models/index.ts +34 -0
  164. package/src/modules/i18n/domain/operations/locale-operations.ts +74 -0
  165. package/src/modules/i18n/domain/validators/index.ts +52 -0
  166. package/src/modules/i18n/index.ts +8 -0
  167. package/src/modules/i18n/ports/locale-loader-port.ts +18 -0
  168. package/src/modules/i18n/types/locale.ts +17 -0
  169. package/src/modules/image-optimization/application/sharp-adapter.ts +58 -0
  170. package/src/modules/image-optimization/domain/events/index.ts +39 -0
  171. package/src/modules/image-optimization/domain/models/index.ts +27 -0
  172. package/src/modules/image-optimization/domain/operations/image-operations.ts +65 -0
  173. package/src/modules/image-optimization/domain/validators/index.ts +52 -0
  174. package/src/modules/image-optimization/index.ts +8 -0
  175. package/src/modules/image-optimization/ports/image-optimizer-port.ts +15 -0
  176. package/src/modules/image-optimization/types/image.ts +17 -0
  177. package/src/modules/live-editor/application/usecases/index.ts +60 -0
  178. package/src/modules/live-editor/domain/events/index.ts +37 -0
  179. package/src/modules/live-editor/domain/gitOperations.ts +110 -0
  180. package/src/modules/live-editor/domain/index.ts +15 -0
  181. package/src/modules/live-editor/domain/models/index.ts +21 -0
  182. package/src/modules/live-editor/domain/validators/index.ts +55 -0
  183. package/src/modules/live-editor/index.ts +14 -0
  184. package/src/modules/live-editor/ports/index.ts +49 -0
  185. package/src/modules/live-editor/types/index.ts +80 -0
  186. package/src/modules/live-preview/application/vite-preview-adapter.ts +20 -0
  187. package/src/modules/live-preview/domain/operations/preview-operations.ts +56 -0
  188. package/src/modules/live-preview/index.ts +8 -0
  189. package/src/modules/live-preview/ports/preview-port.ts +20 -0
  190. package/src/modules/live-preview/types/preview.ts +16 -0
  191. package/src/modules/mdx/application/mdx-js-adapter.ts +38 -0
  192. package/src/modules/mdx/domain/operations/parse-mdx.ts +43 -0
  193. package/src/modules/mdx/index.ts +8 -0
  194. package/src/modules/mdx/ports/mdx-parser-port.ts +15 -0
  195. package/src/modules/mdx/types/mdx.ts +14 -0
  196. package/src/modules/navigation/application/usecases/buildNav.ts +15 -0
  197. package/src/modules/navigation/application/usecases/buildSidebar.ts +45 -0
  198. package/src/modules/navigation/application/usecases/index.ts +2 -0
  199. package/src/modules/navigation/domain/events/index.ts +30 -0
  200. package/src/modules/navigation/domain/models/index.ts +7 -0
  201. package/src/modules/navigation/domain/operations/group.ts +79 -0
  202. package/src/modules/navigation/domain/operations/index.ts +10 -0
  203. package/src/modules/navigation/domain/operations/sort.ts +27 -0
  204. package/src/modules/navigation/domain/shared/utils/array.ts +39 -0
  205. package/src/modules/navigation/domain/validators/index.ts +88 -0
  206. package/src/modules/navigation/index.ts +15 -0
  207. package/src/modules/navigation/ports/index.ts +9 -0
  208. package/src/modules/navigation/types/index.ts +38 -0
  209. package/src/modules/navigation/types/public.ts +9 -0
  210. package/src/modules/openapi/application/swagger-parser-adapter.ts +178 -0
  211. package/src/modules/openapi/domain/operations/parse-openapi.ts +46 -0
  212. package/src/modules/openapi/index.ts +8 -0
  213. package/src/modules/openapi/ports/openapi-parser-port.ts +15 -0
  214. package/src/modules/openapi/types/openapi.ts +37 -0
  215. package/src/modules/plugin/application/usecases/build-virtual-modules.ts +53 -0
  216. package/src/modules/plugin/application/usecases/index.ts +1 -0
  217. package/src/modules/plugin/application/workflows/build-docs.ts +79 -0
  218. package/src/modules/plugin/application/workflows/index.ts +1 -0
  219. package/src/modules/plugin/domain/events/index.ts +62 -0
  220. package/src/modules/plugin/domain/models/index.ts +1 -0
  221. package/src/modules/plugin/domain/operations/build-virtual.ts +76 -0
  222. package/src/modules/plugin/domain/operations/index.ts +11 -0
  223. package/src/modules/plugin/domain/validators/index.ts +30 -0
  224. package/src/modules/plugin/index.ts +14 -0
  225. package/src/modules/plugin/ports/index.ts +21 -0
  226. package/src/modules/plugin/types/index.ts +27 -0
  227. package/src/modules/plugin/types/public.ts +1 -0
  228. package/src/modules/search/application/usecases/index.ts +1 -0
  229. package/src/modules/search/application/usecases/indexPages.ts +15 -0
  230. package/src/modules/search/domain/events/index.ts +32 -0
  231. package/src/modules/search/domain/models/index.ts +1 -0
  232. package/src/modules/search/domain/operations/build-index.ts +6 -0
  233. package/src/modules/search/domain/operations/buildIndex.ts +70 -0
  234. package/src/modules/search/domain/operations/index.ts +7 -0
  235. package/src/modules/search/domain/operations/query.ts +186 -0
  236. package/src/modules/search/domain/validators/index.ts +32 -0
  237. package/src/modules/search/index.ts +9 -0
  238. package/src/modules/search/types/index.ts +22 -0
  239. package/src/modules/search/types/public.ts +1 -0
  240. package/src/modules/search-analytics/application/local-storage-adapter.ts +68 -0
  241. package/src/modules/search-analytics/domain/operations/search-analytics-operations.ts +74 -0
  242. package/src/modules/search-analytics/index.ts +8 -0
  243. package/src/modules/search-analytics/ports/search-analytics-port.ts +20 -0
  244. package/src/modules/search-analytics/types/search-analytics.ts +16 -0
  245. package/src/modules/search-highlighting/application/dom-highlighting-adapter.ts +36 -0
  246. package/src/modules/search-highlighting/domain/operations/highlighting-operations.ts +72 -0
  247. package/src/modules/search-highlighting/index.ts +8 -0
  248. package/src/modules/search-highlighting/ports/highlighting-port.ts +14 -0
  249. package/src/modules/search-highlighting/types/highlighting.ts +15 -0
  250. package/src/modules/seo-optimization/application/usecases/index.ts +18 -0
  251. package/src/modules/seo-optimization/domain/index.ts +12 -0
  252. package/src/modules/seo-optimization/domain/seoOperations.ts +92 -0
  253. package/src/modules/seo-optimization/index.ts +8 -0
  254. package/src/modules/seo-optimization/ports/index.ts +18 -0
  255. package/src/modules/seo-optimization/types/index.ts +41 -0
  256. package/src/modules/theme/application/css-theme-adapter.ts +50 -0
  257. package/src/modules/theme/domain/operations/theme-operations.ts +68 -0
  258. package/src/modules/theme/index.ts +8 -0
  259. package/src/modules/theme/ports/theme-port.ts +8 -0
  260. package/src/modules/theme/types/theme.ts +12 -0
  261. package/src/modules/toc/application/markdown-toc-adapter.ts +106 -0
  262. package/src/modules/toc/domain/operations/toc-operations.ts +92 -0
  263. package/src/modules/toc/index.ts +8 -0
  264. package/src/modules/toc/ports/toc-port.ts +21 -0
  265. package/src/modules/toc/types/toc.ts +17 -0
  266. package/src/modules/toc-generator/application/usecases/index.ts +30 -0
  267. package/src/modules/toc-generator/domain/index.ts +12 -0
  268. package/src/modules/toc-generator/domain/tocOperations.ts +104 -0
  269. package/src/modules/toc-generator/index.ts +9 -0
  270. package/src/modules/toc-generator/ports/index.ts +20 -0
  271. package/src/modules/toc-generator/types/index.ts +50 -0
  272. package/src/modules/version-control/domain/events/index.ts +39 -0
  273. package/src/modules/version-control/domain/models/index.ts +25 -0
  274. package/src/modules/version-control/domain/validators/index.ts +50 -0
  275. package/src/modules/versioning/application/fs-version-loader.ts +44 -0
  276. package/src/modules/versioning/domain/operations/versioning-operations.ts +80 -0
  277. package/src/modules/versioning/index.ts +8 -0
  278. package/src/modules/versioning/ports/version-loader-port.ts +18 -0
  279. package/src/modules/versioning/types/versioning.ts +17 -0
  280. package/src/modules/workflow/domain/events/index.ts +55 -0
  281. package/src/modules/workflow/domain/models/index.ts +40 -0
  282. package/src/modules/workflow/domain/validators/index.ts +50 -0
  283. package/src/modules/yaml-parser/application/js-yaml-adapter.ts +34 -0
  284. package/src/modules/yaml-parser/domain/operations/parse-yaml.ts +58 -0
  285. package/src/modules/yaml-parser/index.ts +7 -0
  286. package/src/modules/yaml-parser/ports/yaml-parser-port.ts +18 -0
  287. package/src/navigation/index.ts +9 -0
  288. package/src/plugin/index.ts +4 -0
  289. package/src/presentation/index.ts +1 -0
  290. package/src/presentation/live-editor/index.ts +22 -0
  291. package/src/presentation/live-editor/live-editor-component.ts +219 -0
  292. package/src/presentation/live-editor/virtual-module.ts +16 -0
  293. package/src/presentation/solid/CodeBlock.tsx +120 -0
  294. package/src/presentation/solid/Toc.tsx +58 -0
  295. package/src/presentation/solid/index.ts +11 -0
  296. package/src/presentation/vite-plugin/docs-plugin.ts +212 -0
  297. package/src/presentation/vite-plugin/index.ts +9 -0
  298. package/src/presentation/vite-plugin/transform-markdown.ts +26 -0
  299. package/src/runtime/analytics.ts +59 -0
  300. package/src/runtime/api-diff.ts +115 -0
  301. package/src/runtime/app.tsx +32 -0
  302. package/src/runtime/components/AccentPicker.tsx +114 -0
  303. package/src/runtime/components/ApiPlayground.tsx +332 -0
  304. package/src/runtime/components/AskAiDialog.tsx +127 -0
  305. package/src/runtime/components/BackToTop.tsx +38 -0
  306. package/src/runtime/components/Banner.tsx +75 -0
  307. package/src/runtime/components/Breadcrumbs.tsx +42 -0
  308. package/src/runtime/components/CollectionDropdown.tsx +85 -0
  309. package/src/runtime/components/CustomizeDrawer.tsx +195 -0
  310. package/src/runtime/components/DocMarkdown.tsx +579 -0
  311. package/src/runtime/components/DocPrevNext.tsx +54 -0
  312. package/src/runtime/components/DocToc.tsx +147 -0
  313. package/src/runtime/components/DocsDropdown.tsx +184 -0
  314. package/src/runtime/components/Footer.tsx +52 -0
  315. package/src/runtime/components/GitHubAuth.tsx +82 -0
  316. package/src/runtime/components/GitHubStats.tsx +73 -0
  317. package/src/runtime/components/Head.tsx +225 -0
  318. package/src/runtime/components/LocaleDropdown.tsx +75 -0
  319. package/src/runtime/components/MobileBottomNav.tsx +111 -0
  320. package/src/runtime/components/NotFound.tsx +139 -0
  321. package/src/runtime/components/PageActions.tsx +144 -0
  322. package/src/runtime/components/RelatedDocs.tsx +50 -0
  323. package/src/runtime/components/ScrollProgress.tsx +32 -0
  324. package/src/runtime/components/SearchPalette.tsx +333 -0
  325. package/src/runtime/components/ShowcaseCard.tsx +87 -0
  326. package/src/runtime/components/SidebarNav.tsx +237 -0
  327. package/src/runtime/components/Skeleton.tsx +39 -0
  328. package/src/runtime/components/ThemeToggle.tsx +26 -0
  329. package/src/runtime/components/TopNav.tsx +270 -0
  330. package/src/runtime/components/VersionDropdown.tsx +189 -0
  331. package/src/runtime/components/markdown/components.ts +280 -0
  332. package/src/runtime/config.ts +225 -0
  333. package/src/runtime/content.ts +451 -0
  334. package/src/runtime/context.tsx +30 -0
  335. package/src/runtime/data.ts +93 -0
  336. package/src/runtime/github.ts +226 -0
  337. package/src/runtime/icons.ts +32 -0
  338. package/src/runtime/index.ts +153 -0
  339. package/src/runtime/layouts/DocsLayout.tsx +144 -0
  340. package/src/runtime/markdown-content.css +996 -0
  341. package/src/runtime/pages/AbTestPage.tsx +88 -0
  342. package/src/runtime/pages/AnalyticsPage.tsx +90 -0
  343. package/src/runtime/pages/ApiDiffPage.tsx +86 -0
  344. package/src/runtime/pages/ApiEndpointPage.tsx +248 -0
  345. package/src/runtime/pages/BuilderPage.tsx +150 -0
  346. package/src/runtime/pages/ChangelogPage.tsx +210 -0
  347. package/src/runtime/pages/CollectionPage.tsx +140 -0
  348. package/src/runtime/pages/CommunityPage.tsx +384 -0
  349. package/src/runtime/pages/CreatePage.tsx +448 -0
  350. package/src/runtime/pages/DocPage.tsx +193 -0
  351. package/src/runtime/pages/EditPage.tsx +201 -0
  352. package/src/runtime/pages/HomePage.tsx +187 -0
  353. package/src/runtime/pages/IssuesPage.tsx +140 -0
  354. package/src/runtime/pages/OAuthCallbackPage.tsx +80 -0
  355. package/src/runtime/pages/PluginsPage.tsx +249 -0
  356. package/src/runtime/pages/ShowcasePage.tsx +202 -0
  357. package/src/runtime/pwa.ts +39 -0
  358. package/src/runtime/router.tsx +192 -0
  359. package/src/runtime/seo.ts +192 -0
  360. package/src/runtime/theme.css +175 -0
  361. package/src/runtime/theme.ts +224 -0
  362. package/src/runtime/types.ts +217 -0
  363. package/src/search/index.ts +6 -0
  364. package/src/seo-optimization/index.ts +4 -0
  365. package/src/shared/constants/index.ts +26 -0
  366. package/src/shared/errors/index.ts +195 -0
  367. package/src/shared/index.ts +27 -0
  368. package/src/shared/types/brand.ts +14 -0
  369. package/src/shared/types/client.d.ts +45 -0
  370. package/src/shared/types/either.ts +38 -0
  371. package/src/shared/types/index.ts +42 -0
  372. package/src/shared/types/option.ts +52 -0
  373. package/src/shared/types/result.ts +81 -0
  374. package/src/shared/utils/array.ts +97 -0
  375. package/src/shared/utils/index.ts +46 -0
  376. package/src/shared/utils/object.ts +47 -0
  377. package/src/shared/utils/string.ts +74 -0
  378. package/src/toc-generator/index.ts +1 -0
  379. package/src/types/js-yaml.d.ts +42 -0
  380. package/src/worker.ts +462 -0
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Pure frontmatter parser.
3
+ * No I/O. No exceptions thrown. Returns ParsedContent.
4
+ *
5
+ * Supports:
6
+ * - Full YAML 1.2 via js-yaml adapter (nested objects, dates, anchors)
7
+ * - Fallback to mini-parser for simple cases
8
+ * - Scalar values: string, number, boolean, null
9
+ * - Inline arrays: [a, b, c]
10
+ * - List arrays:
11
+ * tags:
12
+ * - a
13
+ * - b
14
+ * - Quoted strings ("value" or 'value')
15
+ */
16
+ import {
17
+ FRONTMATTER_DELIMITER,
18
+ SIDEBAR_DEFAULT_ORDER,
19
+ } from "@create-docs/shared/constants";
20
+ import { isString } from "@create-docs/shared/utils/object";
21
+ import {
22
+ isBlank,
23
+ splitLines,
24
+ stripQuotes,
25
+ stripSuffix,
26
+ } from "@create-docs/shared/utils/string";
27
+ import { parseYaml } from "../../../yaml-parser/domain/operations/parse-yaml";
28
+ import type { Frontmatter, ParsedContent, TocItem } from "../../types";
29
+
30
+ const parseScalar = (raw: string): unknown => {
31
+ const value = stripQuotes(raw);
32
+ if (value === "true") return true;
33
+ if (value === "false") return false;
34
+ if (value === "null" || value === "~") return null;
35
+ if (value === "") return "";
36
+ if (/^-?\d+$/.test(value)) return Number.parseInt(value, 10);
37
+ if (/^-?\d*\.\d+$/.test(value)) return Number.parseFloat(value);
38
+ return value;
39
+ };
40
+
41
+ const parseInlineList = (raw: string): string[] => {
42
+ const inner = raw.slice(1, -1).trim();
43
+ if (inner.length === 0) return [];
44
+ return inner
45
+ .split(",")
46
+ .map((v) => stripQuotes(v.trim()))
47
+ .filter((v) => v.length > 0);
48
+ };
49
+
50
+ /** Parse a multi-line frontmatter block (without delimiters) into a typed object. */
51
+ export const parseFrontmatterBlock = (
52
+ lines: readonly string[],
53
+ ): Frontmatter => {
54
+ const block = lines.join("\n");
55
+ const yamlResult = parseYaml(block);
56
+
57
+ if (
58
+ yamlResult.ok &&
59
+ typeof yamlResult.value === "object" &&
60
+ yamlResult.value !== null
61
+ ) {
62
+ return yamlResult.value as Frontmatter;
63
+ }
64
+
65
+ // Fallback to mini-parser for simple cases
66
+ const out: Record<string, unknown> = {};
67
+ let i = 0;
68
+ while (i < lines.length) {
69
+ const line = lines[i] ?? "";
70
+ const trimmed = line.trim();
71
+ if (trimmed.length === 0 || trimmed.startsWith("#")) {
72
+ i++;
73
+ continue;
74
+ }
75
+
76
+ const colonIdx = line.indexOf(":");
77
+ if (colonIdx < 0) {
78
+ i++;
79
+ continue;
80
+ }
81
+
82
+ const key = line.slice(0, colonIdx).trim();
83
+ const rest = line.slice(colonIdx + 1).trim();
84
+
85
+ if (rest === "" || rest === "|" || rest === ">") {
86
+ // List array starting on next line(s)
87
+ const items: string[] = [];
88
+ i++;
89
+ while (i < lines.length) {
90
+ const next = lines[i] ?? "";
91
+ if (!next.trim().startsWith("-")) break;
92
+ items.push(stripQuotes(next.trim().slice(1).trim()));
93
+ i++;
94
+ }
95
+ if (items.length > 0) out[key] = items;
96
+ continue;
97
+ }
98
+
99
+ if (rest.startsWith("[") && rest.endsWith("]")) {
100
+ out[key] = parseInlineList(rest);
101
+ } else {
102
+ out[key] = parseScalar(rest);
103
+ }
104
+ i++;
105
+ }
106
+ return out as Frontmatter;
107
+ };
108
+
109
+ /** Parse a raw markdown file's contents, separating frontmatter from body. */
110
+ export const parseFrontmatter = (raw: string): ParsedContent => {
111
+ const lines = splitLines(raw);
112
+ if (lines[0]?.trim() !== FRONTMATTER_DELIMITER) {
113
+ return { frontmatter: {}, content: raw, rawContent: raw };
114
+ }
115
+
116
+ let endIdx = -1;
117
+ for (let i = 1; i < lines.length; i++) {
118
+ if (lines[i]?.trim() === FRONTMATTER_DELIMITER) {
119
+ endIdx = i;
120
+ break;
121
+ }
122
+ }
123
+
124
+ if (endIdx === -1) {
125
+ return { frontmatter: {}, content: raw, rawContent: raw };
126
+ }
127
+
128
+ const fmLines = lines.slice(1, endIdx);
129
+ const contentLines = lines.slice(endIdx + 1);
130
+ const frontmatter = parseFrontmatterBlock(fmLines);
131
+ const content = contentLines.join("\n");
132
+
133
+ return { frontmatter, content, rawContent: raw };
134
+ };
135
+
136
+ /** Extract a title from frontmatter or fall back to slug tail. */
137
+ export const extractTitle = (
138
+ frontmatter: Frontmatter,
139
+ slug: string,
140
+ ): string => {
141
+ if (isString(frontmatter.title) && !isBlank(frontmatter.title)) {
142
+ return frontmatter.title;
143
+ }
144
+ const parts = slug.split("/");
145
+ return parts[parts.length - 1] ?? slug;
146
+ };
147
+
148
+ /** Extract a numeric order from frontmatter; default = SIDEBAR_DEFAULT_ORDER. */
149
+ export const extractOrder = (frontmatter: Frontmatter): number => {
150
+ const o = frontmatter.order;
151
+ return typeof o === "number" && Number.isFinite(o)
152
+ ? o
153
+ : SIDEBAR_DEFAULT_ORDER;
154
+ };
155
+
156
+ /** First path segment of a slug, used for grouping in sidebar. */
157
+ export const extractGroup = (slug: string): string | undefined => {
158
+ const parts = slug.split("/");
159
+ return parts.length > 1 ? parts[0] : undefined;
160
+ };
161
+
162
+ /** Generate a deterministic id from a slug. */
163
+ export const slugToId = (slug: string): string =>
164
+ slug.replace(/[/\\]/g, "-").replace(/^-/, "");
165
+
166
+ /** Strip `.md` or `.mdoc` extension from a file name. */
167
+ export const stripMarkdownExt = (fileName: string): string =>
168
+ stripSuffix(stripSuffix(fileName, ".mdoc"), ".md");
169
+
170
+ /** Determine if a file name has a supported markdown extension. */
171
+ export const isMarkdownFile = (fileName: string): boolean =>
172
+ fileName.endsWith(".md") || fileName.endsWith(".mdoc");
173
+
174
+ /** Extract a description from frontmatter, if any. */
175
+ export const extractDescription = (
176
+ frontmatter: Frontmatter,
177
+ ): string | undefined => {
178
+ const d = frontmatter.description;
179
+ return isString(d) && !isBlank(d) ? d : undefined;
180
+ };
181
+
182
+ /** Strip Markdown formatting from a body so we can index plain text. */
183
+ export const plainText = (md: string): string =>
184
+ md
185
+ .replace(/```[\s\S]*?```/g, " ") // fenced code
186
+ .replace(/~~~[\s\S]*?~~~/g, " ") // tilde code
187
+ .replace(/`[^`\n]*`/g, " ") // inline code
188
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1") // images
189
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1") // links
190
+ .replace(/^#{1,6}\s+/gm, "") // headings
191
+ .replace(/^\s*[-*+]\s+/gm, "") // bullets
192
+ .replace(/^\s*\d+\.\s+/gm, "") // ordered list
193
+ .replace(/^>\s?/gm, "") // blockquote
194
+ .replace(/[*_~]+/g, "") // emphasis
195
+ .replace(/\s+/g, " ")
196
+ .trim();
197
+
198
+ /** Count words in a plain-text string. */
199
+ export const countWords = (text: string): number => {
200
+ if (text.length === 0) return 0;
201
+ const t = text.trim();
202
+ if (t.length === 0) return 0;
203
+ return t.split(/\s+/).length;
204
+ };
205
+
206
+ /** Estimate reading time in minutes from a word count (default 220 wpm). */
207
+ export const estimateReadingTime = (wordCount: number, wpm = 220): number =>
208
+ Math.max(1, Math.ceil(wordCount / wpm));
209
+
210
+ /** Slugify a heading for an anchor id. Deduplicates across the document. */
211
+ export const slugifyHeading = (raw: string): string => {
212
+ const base = raw
213
+ .toLowerCase()
214
+ .trim()
215
+ .replace(/[`*_~]+/g, "")
216
+ .replace(/[^\p{Letter}\p{Number}\s-]/gu, "")
217
+ .replace(/\s+/g, "-")
218
+ .replace(/-+/g, "-")
219
+ .replace(/^-+|-+$/g, "");
220
+ return base.length > 0 ? base : "section";
221
+ };
222
+
223
+ /** Extract a table of contents from markdown body (h1–h6). Dedupe anchor ids. */
224
+ export const extractToc = (md: string): readonly TocItem[] => {
225
+ const lines = splitLines(md);
226
+ const seen = new Map<string, number>();
227
+ const items: TocItem[] = [];
228
+ for (const line of lines) {
229
+ const m = /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
230
+ if (!m) continue;
231
+ const depth = m[1]?.length as 1 | 2 | 3 | 4 | 5 | 6;
232
+ const text = m[2] ?? "";
233
+ if (text.length === 0) continue;
234
+ const base = slugifyHeading(text);
235
+ const count = seen.get(base) ?? 0;
236
+ seen.set(base, count + 1);
237
+ const id = count === 0 ? base : `${base}-${count}`;
238
+ items.push({ id, text, depth });
239
+ }
240
+ return items;
241
+ };
@@ -0,0 +1,82 @@
1
+ // Content Domain Schemas - Arktype validation schemas
2
+ // Following /follow-arktype workflow for type-safe runtime validation
3
+
4
+ import { BADGE_VARIANTS } from "@create-docs/shared/constants";
5
+ import { validationError } from "@create-docs/shared/errors";
6
+ import { err, ok, type Result } from "@create-docs/shared/types/result";
7
+ import { type Type, type } from "arktype";
8
+ import type { BadgeVariant, Frontmatter } from "../../types";
9
+
10
+ /**
11
+ * Badge Variant Schema
12
+ */
13
+ export const badgeVariantSchema: Type = type(
14
+ "'default' | 'primary' | 'success' | 'warning' | 'danger'",
15
+ );
16
+
17
+ export type BadgeVariantSchema = typeof badgeVariantSchema.infer;
18
+
19
+ /**
20
+ * Title Schema
21
+ */
22
+ export const titleSchema: Type = type("string").narrow(
23
+ (title: string) => title.trim().length > 0,
24
+ );
25
+
26
+ /**
27
+ * Order Schema
28
+ */
29
+ export const orderSchema: Type = type("number").narrow((order: number) =>
30
+ Number.isFinite(order),
31
+ );
32
+
33
+ export const validateBadgeVariant = (
34
+ v: unknown,
35
+ ): Result<BadgeVariant | undefined, ReturnType<typeof validationError>> => {
36
+ if (v === undefined) return ok(undefined);
37
+ const result = badgeVariantSchema(v);
38
+ if (result instanceof type.errors) {
39
+ return err(
40
+ validationError("Invalid badge variant", {
41
+ suggestions: [...BADGE_VARIANTS],
42
+ }),
43
+ );
44
+ }
45
+ return ok(v as BadgeVariant);
46
+ };
47
+
48
+ export const validateTitle = (
49
+ f: Frontmatter,
50
+ ): Result<string | undefined, ReturnType<typeof validationError>> => {
51
+ const t = f.title;
52
+ if (t === undefined) return ok(undefined);
53
+ const result = titleSchema(t);
54
+ if (result instanceof type.errors) {
55
+ return err(validationError("Title must be a non-empty string"));
56
+ }
57
+ return ok(t);
58
+ };
59
+
60
+ export const validateOrder = (
61
+ f: Frontmatter,
62
+ ): Result<number | undefined, ReturnType<typeof validationError>> => {
63
+ const o = f.order;
64
+ if (o === undefined) return ok(undefined);
65
+ const result = orderSchema(o);
66
+ if (result instanceof type.errors) {
67
+ return err(validationError("Order must be a finite number"));
68
+ }
69
+ return ok(o);
70
+ };
71
+
72
+ export const validateFrontmatter = (
73
+ f: Frontmatter,
74
+ ): Result<Frontmatter, ReturnType<typeof validationError>> => {
75
+ const titleResult = validateTitle(f);
76
+ if (!titleResult.ok) return titleResult;
77
+ const orderResult = validateOrder(f);
78
+ if (!orderResult.ok) return orderResult;
79
+ const badgeResult = validateBadgeVariant(f.badgeVariant);
80
+ if (!badgeResult.ok) return badgeResult;
81
+ return ok(f);
82
+ };
@@ -0,0 +1,6 @@
1
+ export {
2
+ validateBadgeVariant,
3
+ validateFrontmatter,
4
+ validateOrder,
5
+ validateTitle,
6
+ } from "./frontmatter";
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Public API for the content module.
3
+ *
4
+ * Re-exports types, pure operations, validators, use cases, and ports.
5
+ * Consumers (other modules, presentation) should import from here only.
6
+ */
7
+
8
+ export * from "./application/cache";
9
+ export * from "./application/usecases";
10
+ export * from "./application/workflows";
11
+ export {
12
+ contentChanged,
13
+ contentDiscovered,
14
+ contentParsed,
15
+ contentParseFailed,
16
+ contentRemoved,
17
+ } from "./domain/events";
18
+ export * from "./domain/operations";
19
+ export * from "./domain/validators";
20
+ export type {
21
+ ContentParser,
22
+ DirectoryEntry,
23
+ DirectoryScanner,
24
+ FileReader,
25
+ FileWatcher,
26
+ } from "./ports";
27
+ export * from "./types/public";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Content module ports — interfaces that the application layer depends on
3
+ * to perform I/O. Adapters provide concrete implementations.
4
+ */
5
+
6
+ import type { AppError } from "@create-docs/shared/errors";
7
+ import type { Result } from "@create-docs/shared/types/result";
8
+ import type { FilePath, ParsedContent } from "../types";
9
+
10
+ /** Reads raw file contents from disk. */
11
+ export interface FileReader {
12
+ read(filePath: FilePath): Promise<Result<string, AppError>>;
13
+ exists(filePath: FilePath): Promise<boolean>;
14
+ stat(
15
+ filePath: FilePath,
16
+ ): Promise<Result<{ mtimeMs: number; size: number }, AppError>>;
17
+ }
18
+
19
+ /** Reads directory entries, recursively. */
20
+ export interface DirectoryScanner {
21
+ list(dirPath: string): Promise<Result<readonly DirectoryEntry[], AppError>>;
22
+ }
23
+
24
+ /** A single entry returned by a directory scanner. */
25
+ export interface DirectoryEntry {
26
+ readonly name: string;
27
+ readonly isDirectory: boolean;
28
+ readonly path: string;
29
+ }
30
+
31
+ /** Parses a raw file's content into structured data. */
32
+ export interface ContentParser {
33
+ parse(raw: string): ParsedContent;
34
+ }
35
+
36
+ /** Watches a directory for file system changes. */
37
+ export interface FileWatcher {
38
+ watch(
39
+ dir: string,
40
+ handlers: {
41
+ onChange: (filePath: string) => void;
42
+ onError: (err: unknown) => void;
43
+ },
44
+ ): { close: () => void };
45
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Velite-based port implementations
3
+ *
4
+ * Implements content module ports using Velite as the underlying engine.
5
+ * This replaces comark with Velite for framework-agnostic content processing.
6
+ */
7
+
8
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import type { AppError } from "@create-docs/shared/errors";
11
+ import { ioError } from "@create-docs/shared/errors";
12
+ import type { Result } from "@create-docs/shared/types/result";
13
+ import type { FilePath, Frontmatter, ParsedContent } from "../types";
14
+ import type { DirectoryEntry } from "./index";
15
+
16
+ const parseFrontmatter = (
17
+ raw: string,
18
+ ): { frontmatter: Frontmatter; content: string } => {
19
+ const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
20
+ if (!match) {
21
+ return { frontmatter: {}, content: raw };
22
+ }
23
+
24
+ const lines = match[1].split("\n");
25
+ const frontmatter: Record<string, unknown> = {};
26
+
27
+ for (const line of lines) {
28
+ const [key, ...valueParts] = line.split(":");
29
+ if (key && valueParts.length > 0) {
30
+ const value = valueParts.join(":").trim();
31
+ frontmatter[key.trim()] = value;
32
+ }
33
+ }
34
+
35
+ return { frontmatter: frontmatter as Frontmatter, content: match[2] };
36
+ };
37
+
38
+ /**
39
+ * Velite-based content parser
40
+ * Parses markdown/MDX using YAML frontmatter and body split
41
+ */
42
+ export const veliteParse = (raw: string): ParsedContent => {
43
+ const { frontmatter, content } = parseFrontmatter(raw);
44
+ return {
45
+ frontmatter,
46
+ content,
47
+ rawContent: raw,
48
+ };
49
+ };
50
+
51
+ /**
52
+ * Velite-based directory scanner
53
+ * Scans the directory for markdown/MDX content files
54
+ */
55
+ export const veliteList = async (
56
+ dirPath: string,
57
+ ): Promise<Result<readonly DirectoryEntry[], AppError>> => {
58
+ try {
59
+ const entries = readdirSync(dirPath, { withFileTypes: true });
60
+ const value: DirectoryEntry[] = entries.map((entry) => ({
61
+ name: entry.name,
62
+ isDirectory: entry.isDirectory(),
63
+ path: join(dirPath, entry.name),
64
+ }));
65
+ return { ok: true, value };
66
+ } catch (error) {
67
+ return {
68
+ ok: false,
69
+ error: ioError(
70
+ dirPath,
71
+ error instanceof Error ? error.message : "Failed to scan directory",
72
+ error,
73
+ ),
74
+ };
75
+ }
76
+ };
77
+
78
+ /**
79
+ * Velite-based file reader
80
+ * Reads raw file contents from disk
81
+ */
82
+ export const veliteRead = async (
83
+ filePath: FilePath,
84
+ ): Promise<Result<string, AppError>> => {
85
+ try {
86
+ const value = readFileSync(filePath, "utf-8");
87
+ return { ok: true, value };
88
+ } catch (error) {
89
+ return {
90
+ ok: false,
91
+ error: ioError(
92
+ filePath,
93
+ error instanceof Error ? error.message : "Failed to read file",
94
+ error,
95
+ ),
96
+ };
97
+ }
98
+ };
99
+
100
+ export const veliteExists = async (filePath: FilePath): Promise<boolean> => {
101
+ return existsSync(filePath);
102
+ };
103
+
104
+ export const veliteStat = async (
105
+ filePath: FilePath,
106
+ ): Promise<Result<{ mtimeMs: number; size: number }, AppError>> => {
107
+ try {
108
+ const stats = statSync(filePath);
109
+ return {
110
+ ok: true,
111
+ value: { mtimeMs: stats.mtimeMs, size: stats.size },
112
+ };
113
+ } catch (error) {
114
+ return {
115
+ ok: false,
116
+ error: ioError(
117
+ filePath,
118
+ error instanceof Error ? error.message : "Failed to stat file",
119
+ error,
120
+ ),
121
+ };
122
+ }
123
+ };
124
+
125
+ /**
126
+ * Default Velite port implementations
127
+ */
128
+ export const veliteContentParser = { parse: veliteParse };
129
+ export const veliteDirectoryScanner = { list: veliteList };
130
+ export const veliteFileReader = {
131
+ read: veliteRead,
132
+ exists: veliteExists,
133
+ stat: veliteStat,
134
+ };
@@ -0,0 +1,76 @@
1
+ import type { Brand } from "@create-docs/shared/types";
2
+
3
+ export type DocSlug = Brand<string, "DocSlug">;
4
+ export type DocId = Brand<string, "DocId">;
5
+ export type FilePath = Brand<string, "FilePath">;
6
+
7
+ export const DocSlug = (s: string): DocSlug => s as DocSlug;
8
+ export const DocId = (s: string): DocId => s as DocId;
9
+ export const FilePath = (s: string): FilePath => s as FilePath;
10
+
11
+ export type BadgeVariant = "default" | "success" | "warning" | "danger";
12
+
13
+ /** A single heading extracted from page body. */
14
+ export interface TocItem {
15
+ readonly id: string;
16
+ readonly text: string;
17
+ readonly depth: 1 | 2 | 3 | 4 | 5 | 6;
18
+ }
19
+
20
+ /** Per-page metadata derived from the body (not user-authored). */
21
+ export interface PageMetadata {
22
+ readonly readingTime: number;
23
+ readonly wordCount: number;
24
+ readonly lastModified?: string;
25
+ readonly editUrl?: string;
26
+ }
27
+
28
+ /** Readonly data model for a doc page's frontmatter. */
29
+ export interface Frontmatter {
30
+ readonly title?: string;
31
+ readonly description?: string;
32
+ readonly order?: number;
33
+ readonly badge?: string;
34
+ readonly badgeVariant?: BadgeVariant;
35
+ readonly collapsible?: boolean;
36
+ readonly category?: string;
37
+ readonly author?: string;
38
+ readonly lastUpdated?: string;
39
+ readonly tags?: readonly string[];
40
+ readonly draft?: boolean;
41
+ readonly [key: string]: unknown;
42
+ }
43
+
44
+ /** Parsed, normalized body of a markdown file (frontmatter stripped). */
45
+ export interface ParsedContent {
46
+ readonly frontmatter: Frontmatter;
47
+ readonly content: string;
48
+ readonly rawContent: string;
49
+ }
50
+
51
+ /** A single doc page, ready for downstream consumption. */
52
+ export interface DocPage {
53
+ readonly id: DocId;
54
+ readonly slug: DocSlug;
55
+ readonly title: string;
56
+ readonly description?: string;
57
+ readonly frontmatter: Frontmatter;
58
+ readonly content: string;
59
+ readonly rawContent: string;
60
+ readonly filePath: FilePath;
61
+ readonly group?: string;
62
+ readonly order: number;
63
+ readonly toc: readonly TocItem[];
64
+ readonly metadata: PageMetadata;
65
+ }
66
+
67
+ /** Lightweight descriptor for a file discovered during scanning. */
68
+ export interface DocFile {
69
+ readonly id: DocId;
70
+ readonly slug: DocSlug;
71
+ readonly frontmatter: Frontmatter;
72
+ readonly rawContent: string;
73
+ readonly filePath: FilePath;
74
+ readonly group?: string;
75
+ readonly order: number;
76
+ }
@@ -0,0 +1 @@
1
+ export * from "./index";
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Built-in linting adapter implementation.
3
+ * Provides basic content linting rules.
4
+ */
5
+
6
+ import { runLintRules } from "../domain/operations/linting-operations";
7
+ import type { LintingPort } from "../ports/linting-port";
8
+ import type { LintResult, LintRule } from "../types/linting";
9
+
10
+ const DEFAULT_RULES: LintRule[] = [
11
+ {
12
+ id: "missing-title",
13
+ name: "Missing Title",
14
+ description: "Document must have a title in frontmatter",
15
+ severity: "error",
16
+ },
17
+ {
18
+ id: "missing-description",
19
+ name: "Missing Description",
20
+ description: "Document should have a description in frontmatter",
21
+ severity: "warning",
22
+ },
23
+ ];
24
+
25
+ export const createBuiltinLintingAdapter = (): LintingPort => ({
26
+ lint: async (filePath: string, rules?: readonly unknown[]) => {
27
+ try {
28
+ const response = await fetch(filePath);
29
+ if (!response.ok) {
30
+ throw new Error(`Failed to load file: ${response.statusText}`);
31
+ }
32
+ const content = await response.text();
33
+
34
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
35
+ const frontmatter = frontmatterMatch?.[1]
36
+ ? (JSON.parse(
37
+ frontmatterMatch[1].replace(/:/g, ":").replace(/'/g, '"'),
38
+ ) as Record<string, unknown>)
39
+ : {};
40
+
41
+ const lintRules = (rules as LintRule[]) || DEFAULT_RULES;
42
+ const results = runLintRules(content, frontmatter, lintRules);
43
+
44
+ if (!results.ok) {
45
+ console.error("Linting error:", results.error);
46
+ return [];
47
+ }
48
+
49
+ return results.value;
50
+ } catch (error) {
51
+ console.error("Lint error:", error);
52
+ return [];
53
+ }
54
+ },
55
+
56
+ fix: async (filePath: string, results?: readonly unknown[]) => {
57
+ try {
58
+ const response = await fetch(filePath);
59
+ if (!response.ok) {
60
+ throw new Error(`Failed to load file: ${response.statusText}`);
61
+ }
62
+ const content = await response.text();
63
+
64
+ const lintResults = results as LintResult[];
65
+ let fixedContent = content;
66
+
67
+ for (const result of lintResults) {
68
+ if (result.ruleId === "missing-title" && result.severity === "error") {
69
+ if (!fixedContent.match(/^---\n[\s\S]*?\n---/)) {
70
+ fixedContent = `---\ntitle: "Untitled"\n---\n\n${fixedContent}`;
71
+ }
72
+ }
73
+ }
74
+
75
+ if (fixedContent !== content) {
76
+ console.log(`Fixed ${lintResults.length} issues in ${filePath}`);
77
+ }
78
+ } catch (error) {
79
+ console.error("Fix error:", error);
80
+ }
81
+ },
82
+ });