@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.
- package/README.md +137 -0
- package/bin/cli.js +408 -0
- package/dist/index.cjs +20 -0
- package/dist/index.d.cts +475 -0
- package/dist/index.d.ts +475 -0
- package/dist/index.js +20 -0
- package/docs/_dir.yml +2 -0
- package/docs/config.md +75 -0
- package/docs/data-sources.md +88 -0
- package/docs/deploy.md +38 -0
- package/docs/features.md +51 -0
- package/docs/getting-started.md +42 -0
- package/docs/index.md +34 -0
- package/docs/markdown.md +154 -0
- package/docs/theming.md +53 -0
- package/package.json +142 -0
- package/scripts/generate-docs-index.ts +111 -0
- package/scripts/github-pull.ts +124 -0
- package/src/adapters/config/index.ts +1 -0
- package/src/adapters/config/node-config-loader.ts +36 -0
- package/src/adapters/fs/fs-docs-loader.ts +31 -0
- package/src/adapters/fs/index.ts +12 -0
- package/src/adapters/fs/memory-fs.ts +60 -0
- package/src/adapters/fs/node-fs.ts +116 -0
- package/src/adapters/git/index.ts +164 -0
- package/src/adapters/github/github-pull.ts +340 -0
- package/src/adapters/index.ts +6 -0
- package/src/adapters/velite.ts +112 -0
- package/src/adapters/vite/docs-watcher.ts +36 -0
- package/src/adapters/vite/index.ts +2 -0
- package/src/adapters/vite/polling-watcher.ts +89 -0
- package/src/code-block-enhancements/index.ts +3 -0
- package/src/config/index.ts +15 -0
- package/src/content/index.ts +11 -0
- package/src/content-validation/index.ts +5 -0
- package/src/index.ts +174 -0
- package/src/live-editor/index.ts +7 -0
- package/src/modules/analytics/application/plausible-adapter.ts +103 -0
- package/src/modules/analytics/application/umami-adapter.ts +109 -0
- package/src/modules/analytics/domain/events/index.ts +37 -0
- package/src/modules/analytics/domain/models/index.ts +35 -0
- package/src/modules/analytics/domain/operations/analytics-operations.ts +67 -0
- package/src/modules/analytics/domain/validators/index.ts +50 -0
- package/src/modules/analytics/index.ts +9 -0
- package/src/modules/analytics/ports/analytics-port.ts +17 -0
- package/src/modules/analytics/types/analytics.ts +16 -0
- package/src/modules/api-docs/application/type-docs-adapter.ts +34 -0
- package/src/modules/api-docs/domain/events/index.ts +42 -0
- package/src/modules/api-docs/domain/models/index.ts +40 -0
- package/src/modules/api-docs/domain/operations/api-docs-operations.ts +59 -0
- package/src/modules/api-docs/domain/validators/index.ts +56 -0
- package/src/modules/api-docs/index.ts +8 -0
- package/src/modules/api-docs/ports/api-docs-port.ts +21 -0
- package/src/modules/api-docs/types/api-docs.ts +15 -0
- package/src/modules/api-reference/openapi.ts +166 -0
- package/src/modules/api-reference/orpc.ts +94 -0
- package/src/modules/code-block-enhancements/application/usecases/index.ts +39 -0
- package/src/modules/code-block-enhancements/domain/code-operations.ts +72 -0
- package/src/modules/code-block-enhancements/domain/events/index.ts +58 -0
- package/src/modules/code-block-enhancements/domain/index.ts +11 -0
- package/src/modules/code-block-enhancements/domain/models/index.ts +36 -0
- package/src/modules/code-block-enhancements/domain/validators/index.ts +50 -0
- package/src/modules/code-block-enhancements/index.ts +10 -0
- package/src/modules/code-block-enhancements/ports/index.ts +18 -0
- package/src/modules/code-block-enhancements/types/index.ts +37 -0
- package/src/modules/code-execution/application/sandbox-adapter.ts +44 -0
- package/src/modules/code-execution/domain/events/index.ts +58 -0
- package/src/modules/code-execution/domain/models/index.ts +33 -0
- package/src/modules/code-execution/domain/operations/execution-operations.ts +60 -0
- package/src/modules/code-execution/domain/validators/index.ts +50 -0
- package/src/modules/code-execution/index.ts +8 -0
- package/src/modules/code-execution/ports/execution-port.ts +15 -0
- package/src/modules/code-execution/types/execution.ts +15 -0
- package/src/modules/comments/application/giscus-adapter.ts +55 -0
- package/src/modules/comments/domain/events/index.ts +55 -0
- package/src/modules/comments/domain/models/index.ts +30 -0
- package/src/modules/comments/domain/operations/comment-operations.ts +46 -0
- package/src/modules/comments/domain/validators/index.ts +50 -0
- package/src/modules/comments/index.ts +8 -0
- package/src/modules/comments/ports/comment-port.ts +18 -0
- package/src/modules/comments/types/comments.ts +19 -0
- package/src/modules/config/application/usecases/index.ts +1 -0
- package/src/modules/config/application/usecases/loadConfig.ts +38 -0
- package/src/modules/config/application/workflows/index.ts +1 -0
- package/src/modules/config/application/workflows/validateConfig.ts +14 -0
- package/src/modules/config/domain/events/index.ts +50 -0
- package/src/modules/config/domain/models/index.ts +9 -0
- package/src/modules/config/domain/operations/defaults.ts +74 -0
- package/src/modules/config/domain/operations/index.ts +2 -0
- package/src/modules/config/domain/operations/resolve.ts +40 -0
- package/src/modules/config/domain/validators/index.ts +152 -0
- package/src/modules/config/index.ts +23 -0
- package/src/modules/config/ports/index.ts +15 -0
- package/src/modules/config/types/index.ts +113 -0
- package/src/modules/config/types/public.ts +19 -0
- package/src/modules/content/application/cache/index.ts +6 -0
- package/src/modules/content/application/cache/parse-cache.ts +6 -0
- package/src/modules/content/application/cache/parseCache.ts +56 -0
- package/src/modules/content/application/usecases/index.ts +2 -0
- package/src/modules/content/application/usecases/load-doc-page.ts +1 -0
- package/src/modules/content/application/usecases/loadDocPage.ts +111 -0
- package/src/modules/content/application/usecases/scanDocsDir.ts +105 -0
- package/src/modules/content/application/workflows/index.ts +1 -0
- package/src/modules/content/application/workflows/processDocs.ts +51 -0
- package/src/modules/content/domain/events/index.ts +61 -0
- package/src/modules/content/domain/models/index.ts +13 -0
- package/src/modules/content/domain/operations/build-page.ts +146 -0
- package/src/modules/content/domain/operations/buildPage.ts +93 -0
- package/src/modules/content/domain/operations/index.ts +17 -0
- package/src/modules/content/domain/operations/parse.ts +241 -0
- package/src/modules/content/domain/validators/frontmatter.ts +82 -0
- package/src/modules/content/domain/validators/index.ts +6 -0
- package/src/modules/content/index.ts +27 -0
- package/src/modules/content/ports/index.ts +45 -0
- package/src/modules/content/ports/velite.ts +134 -0
- package/src/modules/content/types/index.ts +76 -0
- package/src/modules/content/types/public.ts +1 -0
- package/src/modules/content-linting/application/builtin-linting-adapter.ts +82 -0
- package/src/modules/content-linting/domain/events/index.ts +58 -0
- package/src/modules/content-linting/domain/models/index.ts +46 -0
- package/src/modules/content-linting/domain/operations/linting-operations.ts +82 -0
- package/src/modules/content-linting/domain/validators/index.ts +50 -0
- package/src/modules/content-linting/index.ts +8 -0
- package/src/modules/content-linting/ports/linting-port.ts +21 -0
- package/src/modules/content-linting/types/linting.ts +18 -0
- package/src/modules/content-validation/application/usecases/index.ts +18 -0
- package/src/modules/content-validation/domain/events/index.ts +7 -0
- package/src/modules/content-validation/domain/index.ts +16 -0
- package/src/modules/content-validation/domain/models/index.ts +33 -0
- package/src/modules/content-validation/domain/operations/index.ts +157 -0
- package/src/modules/content-validation/domain/types/index.ts +17 -0
- package/src/modules/content-validation/domain/validators/index.ts +8 -0
- package/src/modules/content-validation/index.ts +9 -0
- package/src/modules/content-validation/ports/index.ts +15 -0
- package/src/modules/content-validation/types/index.ts +5 -0
- package/src/modules/export/application/puppeteer-export-adapter.ts +63 -0
- package/src/modules/export/domain/events/index.ts +55 -0
- package/src/modules/export/domain/models/index.ts +38 -0
- package/src/modules/export/domain/operations/export-operations.ts +56 -0
- package/src/modules/export/domain/validators/index.ts +52 -0
- package/src/modules/export/index.ts +8 -0
- package/src/modules/export/ports/export-port.ts +15 -0
- package/src/modules/export/types/export.ts +15 -0
- package/src/modules/external-search/application/algolia-adapter.ts +182 -0
- package/src/modules/external-search/application/meilisearch-adapter.ts +161 -0
- package/src/modules/external-search/domain/events/index.ts +42 -0
- package/src/modules/external-search/domain/models/index.ts +42 -0
- package/src/modules/external-search/domain/operations/search-operations.ts +53 -0
- package/src/modules/external-search/domain/validators/index.ts +55 -0
- package/src/modules/external-search/index.ts +9 -0
- package/src/modules/external-search/ports/external-search-port.ts +23 -0
- package/src/modules/external-search/types/search.ts +19 -0
- package/src/modules/git-history/application/simple-git-adapter.ts +46 -0
- package/src/modules/git-history/domain/events/index.ts +34 -0
- package/src/modules/git-history/domain/models/index.ts +25 -0
- package/src/modules/git-history/domain/operations/git-history-operations.ts +56 -0
- package/src/modules/git-history/domain/validators/index.ts +52 -0
- package/src/modules/git-history/index.ts +8 -0
- package/src/modules/git-history/ports/git-history-port.ts +21 -0
- package/src/modules/git-history/types/git-history.ts +15 -0
- package/src/modules/i18n/application/fs-locale-loader.ts +39 -0
- package/src/modules/i18n/domain/events/index.ts +34 -0
- package/src/modules/i18n/domain/models/index.ts +34 -0
- package/src/modules/i18n/domain/operations/locale-operations.ts +74 -0
- package/src/modules/i18n/domain/validators/index.ts +52 -0
- package/src/modules/i18n/index.ts +8 -0
- package/src/modules/i18n/ports/locale-loader-port.ts +18 -0
- package/src/modules/i18n/types/locale.ts +17 -0
- package/src/modules/image-optimization/application/sharp-adapter.ts +58 -0
- package/src/modules/image-optimization/domain/events/index.ts +39 -0
- package/src/modules/image-optimization/domain/models/index.ts +27 -0
- package/src/modules/image-optimization/domain/operations/image-operations.ts +65 -0
- package/src/modules/image-optimization/domain/validators/index.ts +52 -0
- package/src/modules/image-optimization/index.ts +8 -0
- package/src/modules/image-optimization/ports/image-optimizer-port.ts +15 -0
- package/src/modules/image-optimization/types/image.ts +17 -0
- package/src/modules/live-editor/application/usecases/index.ts +60 -0
- package/src/modules/live-editor/domain/events/index.ts +37 -0
- package/src/modules/live-editor/domain/gitOperations.ts +110 -0
- package/src/modules/live-editor/domain/index.ts +15 -0
- package/src/modules/live-editor/domain/models/index.ts +21 -0
- package/src/modules/live-editor/domain/validators/index.ts +55 -0
- package/src/modules/live-editor/index.ts +14 -0
- package/src/modules/live-editor/ports/index.ts +49 -0
- package/src/modules/live-editor/types/index.ts +80 -0
- package/src/modules/live-preview/application/vite-preview-adapter.ts +20 -0
- package/src/modules/live-preview/domain/operations/preview-operations.ts +56 -0
- package/src/modules/live-preview/index.ts +8 -0
- package/src/modules/live-preview/ports/preview-port.ts +20 -0
- package/src/modules/live-preview/types/preview.ts +16 -0
- package/src/modules/mdx/application/mdx-js-adapter.ts +38 -0
- package/src/modules/mdx/domain/operations/parse-mdx.ts +43 -0
- package/src/modules/mdx/index.ts +8 -0
- package/src/modules/mdx/ports/mdx-parser-port.ts +15 -0
- package/src/modules/mdx/types/mdx.ts +14 -0
- package/src/modules/navigation/application/usecases/buildNav.ts +15 -0
- package/src/modules/navigation/application/usecases/buildSidebar.ts +45 -0
- package/src/modules/navigation/application/usecases/index.ts +2 -0
- package/src/modules/navigation/domain/events/index.ts +30 -0
- package/src/modules/navigation/domain/models/index.ts +7 -0
- package/src/modules/navigation/domain/operations/group.ts +79 -0
- package/src/modules/navigation/domain/operations/index.ts +10 -0
- package/src/modules/navigation/domain/operations/sort.ts +27 -0
- package/src/modules/navigation/domain/shared/utils/array.ts +39 -0
- package/src/modules/navigation/domain/validators/index.ts +88 -0
- package/src/modules/navigation/index.ts +15 -0
- package/src/modules/navigation/ports/index.ts +9 -0
- package/src/modules/navigation/types/index.ts +38 -0
- package/src/modules/navigation/types/public.ts +9 -0
- package/src/modules/openapi/application/swagger-parser-adapter.ts +178 -0
- package/src/modules/openapi/domain/operations/parse-openapi.ts +46 -0
- package/src/modules/openapi/index.ts +8 -0
- package/src/modules/openapi/ports/openapi-parser-port.ts +15 -0
- package/src/modules/openapi/types/openapi.ts +37 -0
- package/src/modules/plugin/application/usecases/build-virtual-modules.ts +53 -0
- package/src/modules/plugin/application/usecases/index.ts +1 -0
- package/src/modules/plugin/application/workflows/build-docs.ts +79 -0
- package/src/modules/plugin/application/workflows/index.ts +1 -0
- package/src/modules/plugin/domain/events/index.ts +62 -0
- package/src/modules/plugin/domain/models/index.ts +1 -0
- package/src/modules/plugin/domain/operations/build-virtual.ts +76 -0
- package/src/modules/plugin/domain/operations/index.ts +11 -0
- package/src/modules/plugin/domain/validators/index.ts +30 -0
- package/src/modules/plugin/index.ts +14 -0
- package/src/modules/plugin/ports/index.ts +21 -0
- package/src/modules/plugin/types/index.ts +27 -0
- package/src/modules/plugin/types/public.ts +1 -0
- package/src/modules/search/application/usecases/index.ts +1 -0
- package/src/modules/search/application/usecases/indexPages.ts +15 -0
- package/src/modules/search/domain/events/index.ts +32 -0
- package/src/modules/search/domain/models/index.ts +1 -0
- package/src/modules/search/domain/operations/build-index.ts +6 -0
- package/src/modules/search/domain/operations/buildIndex.ts +70 -0
- package/src/modules/search/domain/operations/index.ts +7 -0
- package/src/modules/search/domain/operations/query.ts +186 -0
- package/src/modules/search/domain/validators/index.ts +32 -0
- package/src/modules/search/index.ts +9 -0
- package/src/modules/search/types/index.ts +22 -0
- package/src/modules/search/types/public.ts +1 -0
- package/src/modules/search-analytics/application/local-storage-adapter.ts +68 -0
- package/src/modules/search-analytics/domain/operations/search-analytics-operations.ts +74 -0
- package/src/modules/search-analytics/index.ts +8 -0
- package/src/modules/search-analytics/ports/search-analytics-port.ts +20 -0
- package/src/modules/search-analytics/types/search-analytics.ts +16 -0
- package/src/modules/search-highlighting/application/dom-highlighting-adapter.ts +36 -0
- package/src/modules/search-highlighting/domain/operations/highlighting-operations.ts +72 -0
- package/src/modules/search-highlighting/index.ts +8 -0
- package/src/modules/search-highlighting/ports/highlighting-port.ts +14 -0
- package/src/modules/search-highlighting/types/highlighting.ts +15 -0
- package/src/modules/seo-optimization/application/usecases/index.ts +18 -0
- package/src/modules/seo-optimization/domain/index.ts +12 -0
- package/src/modules/seo-optimization/domain/seoOperations.ts +92 -0
- package/src/modules/seo-optimization/index.ts +8 -0
- package/src/modules/seo-optimization/ports/index.ts +18 -0
- package/src/modules/seo-optimization/types/index.ts +41 -0
- package/src/modules/theme/application/css-theme-adapter.ts +50 -0
- package/src/modules/theme/domain/operations/theme-operations.ts +68 -0
- package/src/modules/theme/index.ts +8 -0
- package/src/modules/theme/ports/theme-port.ts +8 -0
- package/src/modules/theme/types/theme.ts +12 -0
- package/src/modules/toc/application/markdown-toc-adapter.ts +106 -0
- package/src/modules/toc/domain/operations/toc-operations.ts +92 -0
- package/src/modules/toc/index.ts +8 -0
- package/src/modules/toc/ports/toc-port.ts +21 -0
- package/src/modules/toc/types/toc.ts +17 -0
- package/src/modules/toc-generator/application/usecases/index.ts +30 -0
- package/src/modules/toc-generator/domain/index.ts +12 -0
- package/src/modules/toc-generator/domain/tocOperations.ts +104 -0
- package/src/modules/toc-generator/index.ts +9 -0
- package/src/modules/toc-generator/ports/index.ts +20 -0
- package/src/modules/toc-generator/types/index.ts +50 -0
- package/src/modules/version-control/domain/events/index.ts +39 -0
- package/src/modules/version-control/domain/models/index.ts +25 -0
- package/src/modules/version-control/domain/validators/index.ts +50 -0
- package/src/modules/versioning/application/fs-version-loader.ts +44 -0
- package/src/modules/versioning/domain/operations/versioning-operations.ts +80 -0
- package/src/modules/versioning/index.ts +8 -0
- package/src/modules/versioning/ports/version-loader-port.ts +18 -0
- package/src/modules/versioning/types/versioning.ts +17 -0
- package/src/modules/workflow/domain/events/index.ts +55 -0
- package/src/modules/workflow/domain/models/index.ts +40 -0
- package/src/modules/workflow/domain/validators/index.ts +50 -0
- package/src/modules/yaml-parser/application/js-yaml-adapter.ts +34 -0
- package/src/modules/yaml-parser/domain/operations/parse-yaml.ts +58 -0
- package/src/modules/yaml-parser/index.ts +7 -0
- package/src/modules/yaml-parser/ports/yaml-parser-port.ts +18 -0
- package/src/navigation/index.ts +9 -0
- package/src/plugin/index.ts +4 -0
- package/src/presentation/index.ts +1 -0
- package/src/presentation/live-editor/index.ts +22 -0
- package/src/presentation/live-editor/live-editor-component.ts +219 -0
- package/src/presentation/live-editor/virtual-module.ts +16 -0
- package/src/presentation/solid/CodeBlock.tsx +120 -0
- package/src/presentation/solid/Toc.tsx +58 -0
- package/src/presentation/solid/index.ts +11 -0
- package/src/presentation/vite-plugin/docs-plugin.ts +212 -0
- package/src/presentation/vite-plugin/index.ts +9 -0
- package/src/presentation/vite-plugin/transform-markdown.ts +26 -0
- package/src/runtime/analytics.ts +59 -0
- package/src/runtime/api-diff.ts +115 -0
- package/src/runtime/app.tsx +32 -0
- package/src/runtime/components/AccentPicker.tsx +114 -0
- package/src/runtime/components/ApiPlayground.tsx +332 -0
- package/src/runtime/components/AskAiDialog.tsx +127 -0
- package/src/runtime/components/BackToTop.tsx +38 -0
- package/src/runtime/components/Banner.tsx +75 -0
- package/src/runtime/components/Breadcrumbs.tsx +42 -0
- package/src/runtime/components/CollectionDropdown.tsx +85 -0
- package/src/runtime/components/CustomizeDrawer.tsx +195 -0
- package/src/runtime/components/DocMarkdown.tsx +579 -0
- package/src/runtime/components/DocPrevNext.tsx +54 -0
- package/src/runtime/components/DocToc.tsx +147 -0
- package/src/runtime/components/DocsDropdown.tsx +184 -0
- package/src/runtime/components/Footer.tsx +52 -0
- package/src/runtime/components/GitHubAuth.tsx +82 -0
- package/src/runtime/components/GitHubStats.tsx +73 -0
- package/src/runtime/components/Head.tsx +225 -0
- package/src/runtime/components/LocaleDropdown.tsx +75 -0
- package/src/runtime/components/MobileBottomNav.tsx +111 -0
- package/src/runtime/components/NotFound.tsx +139 -0
- package/src/runtime/components/PageActions.tsx +144 -0
- package/src/runtime/components/RelatedDocs.tsx +50 -0
- package/src/runtime/components/ScrollProgress.tsx +32 -0
- package/src/runtime/components/SearchPalette.tsx +333 -0
- package/src/runtime/components/ShowcaseCard.tsx +87 -0
- package/src/runtime/components/SidebarNav.tsx +237 -0
- package/src/runtime/components/Skeleton.tsx +39 -0
- package/src/runtime/components/ThemeToggle.tsx +26 -0
- package/src/runtime/components/TopNav.tsx +270 -0
- package/src/runtime/components/VersionDropdown.tsx +189 -0
- package/src/runtime/components/markdown/components.ts +280 -0
- package/src/runtime/config.ts +225 -0
- package/src/runtime/content.ts +451 -0
- package/src/runtime/context.tsx +30 -0
- package/src/runtime/data.ts +93 -0
- package/src/runtime/github.ts +226 -0
- package/src/runtime/icons.ts +32 -0
- package/src/runtime/index.ts +153 -0
- package/src/runtime/layouts/DocsLayout.tsx +144 -0
- package/src/runtime/markdown-content.css +996 -0
- package/src/runtime/pages/AbTestPage.tsx +88 -0
- package/src/runtime/pages/AnalyticsPage.tsx +90 -0
- package/src/runtime/pages/ApiDiffPage.tsx +86 -0
- package/src/runtime/pages/ApiEndpointPage.tsx +248 -0
- package/src/runtime/pages/BuilderPage.tsx +150 -0
- package/src/runtime/pages/ChangelogPage.tsx +210 -0
- package/src/runtime/pages/CollectionPage.tsx +140 -0
- package/src/runtime/pages/CommunityPage.tsx +384 -0
- package/src/runtime/pages/CreatePage.tsx +448 -0
- package/src/runtime/pages/DocPage.tsx +193 -0
- package/src/runtime/pages/EditPage.tsx +201 -0
- package/src/runtime/pages/HomePage.tsx +187 -0
- package/src/runtime/pages/IssuesPage.tsx +140 -0
- package/src/runtime/pages/OAuthCallbackPage.tsx +80 -0
- package/src/runtime/pages/PluginsPage.tsx +249 -0
- package/src/runtime/pages/ShowcasePage.tsx +202 -0
- package/src/runtime/pwa.ts +39 -0
- package/src/runtime/router.tsx +192 -0
- package/src/runtime/seo.ts +192 -0
- package/src/runtime/theme.css +175 -0
- package/src/runtime/theme.ts +224 -0
- package/src/runtime/types.ts +217 -0
- package/src/search/index.ts +6 -0
- package/src/seo-optimization/index.ts +4 -0
- package/src/shared/constants/index.ts +26 -0
- package/src/shared/errors/index.ts +195 -0
- package/src/shared/index.ts +27 -0
- package/src/shared/types/brand.ts +14 -0
- package/src/shared/types/client.d.ts +45 -0
- package/src/shared/types/either.ts +38 -0
- package/src/shared/types/index.ts +42 -0
- package/src/shared/types/option.ts +52 -0
- package/src/shared/types/result.ts +81 -0
- package/src/shared/utils/array.ts +97 -0
- package/src/shared/utils/index.ts +46 -0
- package/src/shared/utils/object.ts +47 -0
- package/src/shared/utils/string.ts +74 -0
- package/src/toc-generator/index.ts +1 -0
- package/src/types/js-yaml.d.ts +42 -0
- package/src/worker.ts +462 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure external search operations.
|
|
3
|
+
* No I/O. No exceptions thrown. Returns Result types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Result } from "@create-docs/shared/types";
|
|
7
|
+
import { err, ok } from "@create-docs/shared/types";
|
|
8
|
+
import type {
|
|
9
|
+
ExternalSearchConfig,
|
|
10
|
+
ExternalSearchResult,
|
|
11
|
+
} from "../../types/search";
|
|
12
|
+
|
|
13
|
+
export type ExternalSearchError = {
|
|
14
|
+
readonly kind: "external-search-error";
|
|
15
|
+
readonly message: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Search external index with query.
|
|
20
|
+
*/
|
|
21
|
+
export const searchExternal = (
|
|
22
|
+
_query: string,
|
|
23
|
+
_config: ExternalSearchConfig,
|
|
24
|
+
): Result<readonly ExternalSearchResult[], ExternalSearchError> => {
|
|
25
|
+
try {
|
|
26
|
+
// This will be implemented via adapter
|
|
27
|
+
// For now, return empty array
|
|
28
|
+
return ok([]);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return err({
|
|
31
|
+
kind: "external-search-error",
|
|
32
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Index documents to external search.
|
|
39
|
+
*/
|
|
40
|
+
export const indexToExternal = (
|
|
41
|
+
_docs: readonly unknown[],
|
|
42
|
+
_config: ExternalSearchConfig,
|
|
43
|
+
): Result<void, ExternalSearchError> => {
|
|
44
|
+
try {
|
|
45
|
+
// This will be implemented via adapter
|
|
46
|
+
return ok(undefined);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return err({
|
|
49
|
+
kind: "external-search-error",
|
|
50
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// External Search Domain Schemas - Arktype validation schemas
|
|
2
|
+
// Following /follow-arktype workflow for type-safe runtime validation
|
|
3
|
+
|
|
4
|
+
import { type Type, type } from "arktype";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Search Endpoint Schema
|
|
8
|
+
*/
|
|
9
|
+
export const searchEndpointSchema: Type = type("string >= 1").narrow(
|
|
10
|
+
(endpoint: string) =>
|
|
11
|
+
endpoint.startsWith("http://") || endpoint.startsWith("https://"),
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export type SearchEndpoint = typeof searchEndpointSchema.infer;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Search URL Schema
|
|
18
|
+
*/
|
|
19
|
+
export const searchUrlSchema: Type = type("string >= 1").narrow(
|
|
20
|
+
(url: string) => url.startsWith("http://") || url.startsWith("https://"),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export type SearchUrl = typeof searchUrlSchema.infer;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Validation errors
|
|
27
|
+
*/
|
|
28
|
+
export class ValidationError extends Error {
|
|
29
|
+
constructor(message: string) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = "ValidationError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate search endpoint using Arktype
|
|
37
|
+
*/
|
|
38
|
+
export const validateSearchEndpoint = (endpoint: unknown): SearchEndpoint => {
|
|
39
|
+
const result = searchEndpointSchema(endpoint);
|
|
40
|
+
if (result instanceof type.errors) {
|
|
41
|
+
throw new ValidationError("Search endpoint must be a valid URL");
|
|
42
|
+
}
|
|
43
|
+
return result as SearchEndpoint;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Validate search URL using Arktype
|
|
48
|
+
*/
|
|
49
|
+
export const validateSearchUrl = (url: unknown): SearchUrl => {
|
|
50
|
+
const result = searchUrlSchema(url);
|
|
51
|
+
if (result instanceof type.errors) {
|
|
52
|
+
throw new ValidationError("Search URL must be a valid URL");
|
|
53
|
+
}
|
|
54
|
+
return result as SearchUrl;
|
|
55
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API for the external-search module.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { createAlgoliaAdapter } from "./application/algolia-adapter";
|
|
6
|
+
export { createMeilisearchAdapter } from "./application/meilisearch-adapter";
|
|
7
|
+
export * from "./domain/operations/search-operations";
|
|
8
|
+
export * from "./ports/external-search-port";
|
|
9
|
+
export * from "./types/search";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port for external search operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type ExternalSearchPort = {
|
|
6
|
+
/**
|
|
7
|
+
* Search external index.
|
|
8
|
+
*/
|
|
9
|
+
readonly search: (
|
|
10
|
+
query: string,
|
|
11
|
+
config: unknown,
|
|
12
|
+
) => Promise<readonly unknown[]>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Index documents to external search.
|
|
16
|
+
*/
|
|
17
|
+
readonly index: (docs: readonly unknown[], config: unknown) => Promise<void>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Clear external index.
|
|
21
|
+
*/
|
|
22
|
+
readonly clear: (config: unknown) => Promise<void>;
|
|
23
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External search types for Algolia/MeiliSearch integration.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type ExternalSearchConfig = {
|
|
6
|
+
readonly provider: "algolia" | "meilisearch" | "local";
|
|
7
|
+
readonly appId?: string;
|
|
8
|
+
readonly apiKey?: string;
|
|
9
|
+
readonly indexName?: string;
|
|
10
|
+
readonly host?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ExternalSearchResult = {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly title: string;
|
|
16
|
+
readonly description?: string;
|
|
17
|
+
readonly url: string;
|
|
18
|
+
readonly score?: number;
|
|
19
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple-git adapter implementation.
|
|
3
|
+
* Provides git history using simple-git library.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ioError, pluginError } from "@create-docs/shared/errors";
|
|
7
|
+
import { simpleGit } from "simple-git";
|
|
8
|
+
import type { GitHistoryPort } from "../ports/git-history-port";
|
|
9
|
+
import type { GitCommit } from "../types/git-history";
|
|
10
|
+
|
|
11
|
+
export const createSimpleGitAdapter = (): GitHistoryPort => {
|
|
12
|
+
const git = simpleGit();
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
getLog: async (filePath: string, config?: unknown) => {
|
|
16
|
+
try {
|
|
17
|
+
const maxCommits =
|
|
18
|
+
config && typeof config === "object" && "maxCommits" in config
|
|
19
|
+
? (config as { maxCommits: number }).maxCommits
|
|
20
|
+
: 10;
|
|
21
|
+
|
|
22
|
+
const log = await git.log({ file: filePath, maxCount: maxCommits });
|
|
23
|
+
|
|
24
|
+
return log.all.map((commit) => ({
|
|
25
|
+
hash: commit.hash,
|
|
26
|
+
author: commit.author_name,
|
|
27
|
+
date: commit.date,
|
|
28
|
+
message: commit.message,
|
|
29
|
+
})) as GitCommit[];
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw pluginError("Git log failed", {
|
|
32
|
+
context: { filePath, config, error: String(error) },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
getFileAtCommit: async (filePath: string, commit: string) => {
|
|
38
|
+
try {
|
|
39
|
+
const content = await git.show([`${commit}:${filePath}`]);
|
|
40
|
+
return content;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw ioError(filePath, "Git show failed", error);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain events for git history
|
|
3
|
+
*/
|
|
4
|
+
export interface HistoryLoadedEvent {
|
|
5
|
+
readonly type: "HISTORY_LOADED";
|
|
6
|
+
readonly timestamp: number;
|
|
7
|
+
readonly commitCount: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CommitSelectedEvent {
|
|
11
|
+
readonly type: "COMMIT_SELECTED";
|
|
12
|
+
readonly timestamp: number;
|
|
13
|
+
readonly commitHash: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type GitHistoryDomainEvent = HistoryLoadedEvent | CommitSelectedEvent;
|
|
17
|
+
|
|
18
|
+
export const createHistoryLoadedEvent = (
|
|
19
|
+
commitCount: number,
|
|
20
|
+
now: number = Date.now(),
|
|
21
|
+
): HistoryLoadedEvent => ({
|
|
22
|
+
type: "HISTORY_LOADED",
|
|
23
|
+
timestamp: now,
|
|
24
|
+
commitCount,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const createCommitSelectedEvent = (
|
|
28
|
+
commitHash: string,
|
|
29
|
+
now: number = Date.now(),
|
|
30
|
+
): CommitSelectedEvent => ({
|
|
31
|
+
type: "COMMIT_SELECTED",
|
|
32
|
+
timestamp: now,
|
|
33
|
+
commitHash,
|
|
34
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain models for git history
|
|
3
|
+
*/
|
|
4
|
+
export interface GitCommit {
|
|
5
|
+
readonly hash: string;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
readonly author: string;
|
|
8
|
+
readonly timestamp: number;
|
|
9
|
+
readonly branch: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const createGitCommit = (
|
|
13
|
+
hash: string,
|
|
14
|
+
message: string,
|
|
15
|
+
author: string,
|
|
16
|
+
branch: string,
|
|
17
|
+
options?: { timestamp?: number },
|
|
18
|
+
now: number = Date.now(),
|
|
19
|
+
): GitCommit => ({
|
|
20
|
+
hash,
|
|
21
|
+
message,
|
|
22
|
+
author,
|
|
23
|
+
branch,
|
|
24
|
+
timestamp: options?.timestamp ?? now,
|
|
25
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure git history operations.
|
|
3
|
+
* No I/O. No exceptions thrown. Returns Result types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Result } from "@create-docs/shared/types";
|
|
7
|
+
import { err, ok } from "@create-docs/shared/types";
|
|
8
|
+
import type { GitCommit } from "../../types/git-history";
|
|
9
|
+
|
|
10
|
+
export type GitHistoryError = {
|
|
11
|
+
readonly kind: "git-history-error";
|
|
12
|
+
readonly message: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Filter commits by date range.
|
|
17
|
+
*/
|
|
18
|
+
export const filterCommitsByDate = (
|
|
19
|
+
commits: readonly GitCommit[],
|
|
20
|
+
since: string,
|
|
21
|
+
until: string,
|
|
22
|
+
): Result<readonly GitCommit[], GitHistoryError> => {
|
|
23
|
+
try {
|
|
24
|
+
const sinceDate = new Date(since);
|
|
25
|
+
const untilDate = new Date(until);
|
|
26
|
+
|
|
27
|
+
const filtered = commits.filter((commit) => {
|
|
28
|
+
const commitDate = new Date(commit.date);
|
|
29
|
+
return commitDate >= sinceDate && commitDate <= untilDate;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return ok(filtered);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return err({
|
|
35
|
+
kind: "git-history-error",
|
|
36
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Limit commits to max count.
|
|
43
|
+
*/
|
|
44
|
+
export const limitCommits = (
|
|
45
|
+
commits: readonly GitCommit[],
|
|
46
|
+
max: number,
|
|
47
|
+
): readonly GitCommit[] => {
|
|
48
|
+
return commits.slice(0, max);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Format commit for display.
|
|
53
|
+
*/
|
|
54
|
+
export const formatCommit = (commit: GitCommit): string => {
|
|
55
|
+
return `${commit.hash.slice(0, 7)} - ${commit.message} (${commit.author})`;
|
|
56
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Git History Domain Schemas - Arktype validation schemas
|
|
2
|
+
// Following /follow-arktype workflow for type-safe runtime validation
|
|
3
|
+
|
|
4
|
+
import { type Type, type } from "arktype";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Commit Hash Schema
|
|
8
|
+
*/
|
|
9
|
+
export const commitHashSchema: Type = type(/^[a-f0-9]{40}$/);
|
|
10
|
+
|
|
11
|
+
export type CommitHash = typeof commitHashSchema.infer;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Branch Name Schema
|
|
15
|
+
*/
|
|
16
|
+
export const branchNameSchema: Type = type("string >= 1");
|
|
17
|
+
|
|
18
|
+
export type BranchName = typeof branchNameSchema.infer;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Validation errors
|
|
22
|
+
*/
|
|
23
|
+
export class ValidationError extends Error {
|
|
24
|
+
constructor(message: string) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "ValidationError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate commit hash using Arktype
|
|
32
|
+
*/
|
|
33
|
+
export const validateCommitHash = (hash: unknown): CommitHash => {
|
|
34
|
+
const result = commitHashSchema(hash);
|
|
35
|
+
if (result instanceof type.errors) {
|
|
36
|
+
throw new ValidationError(
|
|
37
|
+
"Commit hash must be a valid 40-character hex string",
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return result as CommitHash;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validate branch name using Arktype
|
|
45
|
+
*/
|
|
46
|
+
export const validateBranchName = (name: unknown): BranchName => {
|
|
47
|
+
const result = branchNameSchema(name);
|
|
48
|
+
if (result instanceof type.errors) {
|
|
49
|
+
throw new ValidationError("Branch name must be a non-empty string");
|
|
50
|
+
}
|
|
51
|
+
return result as BranchName;
|
|
52
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API for the git-history module.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { createSimpleGitAdapter } from "./application/simple-git-adapter";
|
|
6
|
+
export * from "./domain/operations/git-history-operations";
|
|
7
|
+
export * from "./ports/git-history-port";
|
|
8
|
+
export * from "./types/git-history";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port for git history operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type GitHistoryPort = {
|
|
6
|
+
/**
|
|
7
|
+
* Get git log for a file.
|
|
8
|
+
*/
|
|
9
|
+
readonly getLog: (
|
|
10
|
+
filePath: string,
|
|
11
|
+
config: unknown,
|
|
12
|
+
) => Promise<readonly unknown[]>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get file content at specific commit.
|
|
16
|
+
*/
|
|
17
|
+
readonly getFileAtCommit: (
|
|
18
|
+
filePath: string,
|
|
19
|
+
commit: string,
|
|
20
|
+
) => Promise<string>;
|
|
21
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git history types for version tracking.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type GitCommit = {
|
|
6
|
+
readonly hash: string;
|
|
7
|
+
readonly author: string;
|
|
8
|
+
readonly date: string;
|
|
9
|
+
readonly message: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type GitHistoryConfig = {
|
|
13
|
+
readonly enabled: boolean;
|
|
14
|
+
readonly maxCommits: number;
|
|
15
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system adapter for locale loading.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readdir } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import type { LocaleLoaderPort } from "../ports/locale-loader-port";
|
|
8
|
+
|
|
9
|
+
export const createFsLocaleLoader = (): LocaleLoaderPort => ({
|
|
10
|
+
scanLocales: async (docsDir: string) => {
|
|
11
|
+
try {
|
|
12
|
+
const entries = await readdir(docsDir, { withFileTypes: true });
|
|
13
|
+
return entries
|
|
14
|
+
.filter((entry) => entry.isDirectory())
|
|
15
|
+
.map((entry) => entry.name);
|
|
16
|
+
} catch {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
loadLocaleDocs: async (locale: string, docsDir: string) => {
|
|
22
|
+
try {
|
|
23
|
+
const localeDir = join(docsDir, locale);
|
|
24
|
+
const entries = await readdir(localeDir, {
|
|
25
|
+
withFileTypes: true,
|
|
26
|
+
recursive: true,
|
|
27
|
+
});
|
|
28
|
+
return entries
|
|
29
|
+
.filter(
|
|
30
|
+
(entry) =>
|
|
31
|
+
entry.isFile() &&
|
|
32
|
+
(entry.name.endsWith(".md") || entry.name.endsWith(".mdoc")),
|
|
33
|
+
)
|
|
34
|
+
.map((entry) => join(entry.parentPath, entry.name));
|
|
35
|
+
} catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain events for i18n
|
|
3
|
+
*/
|
|
4
|
+
export interface LocaleChangedEvent {
|
|
5
|
+
readonly type: "LOCALE_CHANGED";
|
|
6
|
+
readonly timestamp: number;
|
|
7
|
+
readonly localeCode: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TranslationLoadedEvent {
|
|
11
|
+
readonly type: "TRANSLATION_LOADED";
|
|
12
|
+
readonly timestamp: number;
|
|
13
|
+
readonly language: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type I18nDomainEvent = LocaleChangedEvent | TranslationLoadedEvent;
|
|
17
|
+
|
|
18
|
+
export const createLocaleChangedEvent = (
|
|
19
|
+
localeCode: string,
|
|
20
|
+
now: number = Date.now(),
|
|
21
|
+
): LocaleChangedEvent => ({
|
|
22
|
+
type: "LOCALE_CHANGED",
|
|
23
|
+
timestamp: now,
|
|
24
|
+
localeCode,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const createTranslationLoadedEvent = (
|
|
28
|
+
language: string,
|
|
29
|
+
now: number = Date.now(),
|
|
30
|
+
): TranslationLoadedEvent => ({
|
|
31
|
+
type: "TRANSLATION_LOADED",
|
|
32
|
+
timestamp: now,
|
|
33
|
+
language,
|
|
34
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain models for i18n
|
|
3
|
+
*/
|
|
4
|
+
export interface Translation {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly value: string;
|
|
7
|
+
readonly language: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Locale {
|
|
11
|
+
readonly code: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly rtl: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const createTranslation = (
|
|
17
|
+
key: string,
|
|
18
|
+
value: string,
|
|
19
|
+
language: string,
|
|
20
|
+
): Translation => ({
|
|
21
|
+
key,
|
|
22
|
+
value,
|
|
23
|
+
language,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const createLocale = (
|
|
27
|
+
code: string,
|
|
28
|
+
name: string,
|
|
29
|
+
rtl: boolean = false,
|
|
30
|
+
): Locale => ({
|
|
31
|
+
code,
|
|
32
|
+
name,
|
|
33
|
+
rtl,
|
|
34
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure locale operations.
|
|
3
|
+
* No I/O. No exceptions thrown.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
Locale,
|
|
8
|
+
LocaleConfig,
|
|
9
|
+
LocalizedDocPage,
|
|
10
|
+
} from "../../types/locale";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extract locale from a slug path.
|
|
14
|
+
* Example: "en/getting-started/introduction" -> "en"
|
|
15
|
+
*/
|
|
16
|
+
export const extractLocaleFromSlug = (
|
|
17
|
+
slug: string,
|
|
18
|
+
locales: readonly Locale[],
|
|
19
|
+
): Locale | null => {
|
|
20
|
+
const parts = slug.split("/");
|
|
21
|
+
const firstPart = parts[0];
|
|
22
|
+
if (firstPart && locales.includes(firstPart)) {
|
|
23
|
+
return firstPart;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Remove locale prefix from a slug.
|
|
30
|
+
* Example: "en/getting-started/introduction" -> "getting-started/introduction"
|
|
31
|
+
*/
|
|
32
|
+
export const removeLocaleFromSlug = (slug: string, locale: Locale): string => {
|
|
33
|
+
return slug.startsWith(`${locale}/`) ? slug.slice(locale.length + 1) : slug;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Add locale prefix to a slug.
|
|
38
|
+
* Example: "getting-started/introduction" + "en" -> "en/getting-started/introduction"
|
|
39
|
+
*/
|
|
40
|
+
export const addLocaleToSlug = (slug: string, locale: Locale): string => {
|
|
41
|
+
return `${locale}/${slug}`;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Group doc pages by locale.
|
|
46
|
+
*/
|
|
47
|
+
export const groupDocsByLocale = (
|
|
48
|
+
pages: readonly LocalizedDocPage[],
|
|
49
|
+
): Readonly<Record<Locale, readonly LocalizedDocPage[]>> => {
|
|
50
|
+
const groups: Record<string, LocalizedDocPage[]> = {};
|
|
51
|
+
for (const page of pages) {
|
|
52
|
+
const { locale } = page;
|
|
53
|
+
if (!groups[locale]) {
|
|
54
|
+
groups[locale] = [];
|
|
55
|
+
}
|
|
56
|
+
groups[locale].push(page);
|
|
57
|
+
}
|
|
58
|
+
return groups;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Validate locale config.
|
|
63
|
+
*/
|
|
64
|
+
export const validateLocaleConfig = (config: LocaleConfig): boolean => {
|
|
65
|
+
if (!config.defaultLocale || !config.locales.includes(config.defaultLocale)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
for (const locale of config.locales) {
|
|
69
|
+
if (!config.localePaths[locale]) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// I18n Domain Schemas - Arktype validation schemas
|
|
2
|
+
// Following /follow-arktype workflow for type-safe runtime validation
|
|
3
|
+
|
|
4
|
+
import { type Type, type } from "arktype";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Locale Code Schema
|
|
8
|
+
*/
|
|
9
|
+
export const localeCodeSchema: Type = type("string").narrow(
|
|
10
|
+
(code: string) => code.length === 2,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export type LocaleCode = typeof localeCodeSchema.infer;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Translation Key Schema
|
|
17
|
+
*/
|
|
18
|
+
export const translationKeySchema: Type = type("string >= 1");
|
|
19
|
+
|
|
20
|
+
export type TranslationKey = typeof translationKeySchema.infer;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validation errors
|
|
24
|
+
*/
|
|
25
|
+
export class ValidationError extends Error {
|
|
26
|
+
constructor(message: string) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "ValidationError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validate locale code using Arktype
|
|
34
|
+
*/
|
|
35
|
+
export const validateLocaleCode = (code: unknown): LocaleCode => {
|
|
36
|
+
const result = localeCodeSchema(code);
|
|
37
|
+
if (result instanceof type.errors) {
|
|
38
|
+
throw new ValidationError("Locale code must be a 2-character string");
|
|
39
|
+
}
|
|
40
|
+
return result as LocaleCode;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validate translation key using Arktype
|
|
45
|
+
*/
|
|
46
|
+
export const validateTranslationKey = (key: unknown): TranslationKey => {
|
|
47
|
+
const result = translationKeySchema(key);
|
|
48
|
+
if (result instanceof type.errors) {
|
|
49
|
+
throw new ValidationError("Translation key must be a non-empty string");
|
|
50
|
+
}
|
|
51
|
+
return result as TranslationKey;
|
|
52
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port for locale loading operations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type LocaleLoaderPort = {
|
|
6
|
+
/**
|
|
7
|
+
* Scan docs directory for locale-specific folders.
|
|
8
|
+
*/
|
|
9
|
+
readonly scanLocales: (docsDir: string) => Promise<readonly string[]>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Load docs for a specific locale.
|
|
13
|
+
*/
|
|
14
|
+
readonly loadLocaleDocs: (
|
|
15
|
+
locale: string,
|
|
16
|
+
docsDir: string,
|
|
17
|
+
) => Promise<readonly string[]>;
|
|
18
|
+
};
|