jamdesk 1.0.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/LICENSE +21 -0
- package/README.md +323 -0
- package/bin/jamdesk.js +76 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.d.ts +8 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.js +165 -0
- package/dist/__tests__/integration/deprecated-components.integration.test.js.map +1 -0
- package/dist/__tests__/integration/migrate.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/migrate.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/migrate.integration.test.js +64 -0
- package/dist/__tests__/integration/migrate.integration.test.js.map +1 -0
- package/dist/__tests__/integration/prepublish.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/prepublish.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/prepublish.integration.test.js +27 -0
- package/dist/__tests__/integration/prepublish.integration.test.js.map +1 -0
- package/dist/__tests__/integration/validate.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/validate.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/validate.integration.test.js +56 -0
- package/dist/__tests__/integration/validate.integration.test.js.map +1 -0
- package/dist/__tests__/unit/deploy-templates.test.d.ts +2 -0
- package/dist/__tests__/unit/deploy-templates.test.d.ts.map +1 -0
- package/dist/__tests__/unit/deploy-templates.test.js +124 -0
- package/dist/__tests__/unit/deploy-templates.test.js.map +1 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.d.ts +2 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.js +69 -0
- package/dist/__tests__/unit/deprecated-components-sync.test.js.map +1 -0
- package/dist/__tests__/unit/deps-sync.test.d.ts +14 -0
- package/dist/__tests__/unit/deps-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/deps-sync.test.js +166 -0
- package/dist/__tests__/unit/deps-sync.test.js.map +1 -0
- package/dist/__tests__/unit/docs-config.test.d.ts +2 -0
- package/dist/__tests__/unit/docs-config.test.d.ts.map +1 -0
- package/dist/__tests__/unit/docs-config.test.js +288 -0
- package/dist/__tests__/unit/docs-config.test.js.map +1 -0
- package/dist/__tests__/unit/errors.test.d.ts +2 -0
- package/dist/__tests__/unit/errors.test.d.ts.map +1 -0
- package/dist/__tests__/unit/errors.test.js +27 -0
- package/dist/__tests__/unit/errors.test.js.map +1 -0
- package/dist/__tests__/unit/extract-hooks.test.d.ts +5 -0
- package/dist/__tests__/unit/extract-hooks.test.d.ts.map +1 -0
- package/dist/__tests__/unit/extract-hooks.test.js +205 -0
- package/dist/__tests__/unit/extract-hooks.test.js.map +1 -0
- package/dist/__tests__/unit/frontmatter-sync.test.d.ts +8 -0
- package/dist/__tests__/unit/frontmatter-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/frontmatter-sync.test.js +26 -0
- package/dist/__tests__/unit/frontmatter-sync.test.js.map +1 -0
- package/dist/__tests__/unit/mdx-validator.test.d.ts +2 -0
- package/dist/__tests__/unit/mdx-validator.test.d.ts.map +1 -0
- package/dist/__tests__/unit/mdx-validator.test.js +264 -0
- package/dist/__tests__/unit/mdx-validator.test.js.map +1 -0
- package/dist/__tests__/unit/migrate-convert.test.d.ts +2 -0
- package/dist/__tests__/unit/migrate-convert.test.d.ts.map +1 -0
- package/dist/__tests__/unit/migrate-convert.test.js +297 -0
- package/dist/__tests__/unit/migrate-convert.test.js.map +1 -0
- package/dist/__tests__/unit/migrate-detect.test.d.ts +2 -0
- package/dist/__tests__/unit/migrate-detect.test.d.ts.map +1 -0
- package/dist/__tests__/unit/migrate-detect.test.js +35 -0
- package/dist/__tests__/unit/migrate-detect.test.js.map +1 -0
- package/dist/__tests__/unit/migrate-mdx.test.d.ts +2 -0
- package/dist/__tests__/unit/migrate-mdx.test.d.ts.map +1 -0
- package/dist/__tests__/unit/migrate-mdx.test.js +158 -0
- package/dist/__tests__/unit/migrate-mdx.test.js.map +1 -0
- package/dist/__tests__/unit/openapi.test.d.ts +2 -0
- package/dist/__tests__/unit/openapi.test.d.ts.map +1 -0
- package/dist/__tests__/unit/openapi.test.js +52 -0
- package/dist/__tests__/unit/openapi.test.js.map +1 -0
- package/dist/__tests__/unit/package-config.test.d.ts +2 -0
- package/dist/__tests__/unit/package-config.test.d.ts.map +1 -0
- package/dist/__tests__/unit/package-config.test.js +63 -0
- package/dist/__tests__/unit/package-config.test.js.map +1 -0
- package/dist/__tests__/unit/port.test.d.ts +2 -0
- package/dist/__tests__/unit/port.test.d.ts.map +1 -0
- package/dist/__tests__/unit/port.test.js +20 -0
- package/dist/__tests__/unit/port.test.js.map +1 -0
- package/dist/__tests__/unit/vendored-sync.test.d.ts +14 -0
- package/dist/__tests__/unit/vendored-sync.test.d.ts.map +1 -0
- package/dist/__tests__/unit/vendored-sync.test.js +90 -0
- package/dist/__tests__/unit/vendored-sync.test.js.map +1 -0
- package/dist/commands/broken-links.d.ts +11 -0
- package/dist/commands/broken-links.d.ts.map +1 -0
- package/dist/commands/broken-links.js +95 -0
- package/dist/commands/broken-links.js.map +1 -0
- package/dist/commands/clean.d.ts +7 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +59 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/deploy/cloudflare.d.ts +12 -0
- package/dist/commands/deploy/cloudflare.d.ts.map +1 -0
- package/dist/commands/deploy/cloudflare.js +409 -0
- package/dist/commands/deploy/cloudflare.js.map +1 -0
- package/dist/commands/deploy/templates.d.ts +23 -0
- package/dist/commands/deploy/templates.d.ts.map +1 -0
- package/dist/commands/deploy/templates.js +179 -0
- package/dist/commands/deploy/templates.js.map +1 -0
- package/dist/commands/deploy/types.d.ts +19 -0
- package/dist/commands/deploy/types.d.ts.map +1 -0
- package/dist/commands/deploy/types.js +5 -0
- package/dist/commands/deploy/types.js.map +1 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +817 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +159 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +96 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/migrate/convert-mdx.d.ts +50 -0
- package/dist/commands/migrate/convert-mdx.d.ts.map +1 -0
- package/dist/commands/migrate/convert-mdx.js +108 -0
- package/dist/commands/migrate/convert-mdx.js.map +1 -0
- package/dist/commands/migrate/convert.d.ts +80 -0
- package/dist/commands/migrate/convert.d.ts.map +1 -0
- package/dist/commands/migrate/convert.js +158 -0
- package/dist/commands/migrate/convert.js.map +1 -0
- package/dist/commands/migrate/detect.d.ts +31 -0
- package/dist/commands/migrate/detect.d.ts.map +1 -0
- package/dist/commands/migrate/detect.js +62 -0
- package/dist/commands/migrate/detect.js.map +1 -0
- package/dist/commands/migrate/extract-hooks.d.ts +71 -0
- package/dist/commands/migrate/extract-hooks.d.ts.map +1 -0
- package/dist/commands/migrate/extract-hooks.js +473 -0
- package/dist/commands/migrate/extract-hooks.js.map +1 -0
- package/dist/commands/migrate/index.d.ts +17 -0
- package/dist/commands/migrate/index.d.ts.map +1 -0
- package/dist/commands/migrate/index.js +282 -0
- package/dist/commands/migrate/index.js.map +1 -0
- package/dist/commands/migrate/prompts.d.ts +22 -0
- package/dist/commands/migrate/prompts.d.ts.map +1 -0
- package/dist/commands/migrate/prompts.js +67 -0
- package/dist/commands/migrate/prompts.js.map +1 -0
- package/dist/commands/migrate/types.d.ts +22 -0
- package/dist/commands/migrate/types.d.ts.map +1 -0
- package/dist/commands/migrate/types.js +26 -0
- package/dist/commands/migrate/types.js.map +1 -0
- package/dist/commands/openapi-check.d.ts +11 -0
- package/dist/commands/openapi-check.d.ts.map +1 -0
- package/dist/commands/openapi-check.js +88 -0
- package/dist/commands/openapi-check.js.map +1 -0
- package/dist/commands/rename.d.ts +10 -0
- package/dist/commands/rename.d.ts.map +1 -0
- package/dist/commands/rename.js +125 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +57 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +12 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +163 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +334 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +18 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/deprecated-components.d.ts +72 -0
- package/dist/lib/deprecated-components.d.ts.map +1 -0
- package/dist/lib/deprecated-components.js +138 -0
- package/dist/lib/deprecated-components.js.map +1 -0
- package/dist/lib/deps.d.ts +17 -0
- package/dist/lib/deps.d.ts.map +1 -0
- package/dist/lib/deps.js +186 -0
- package/dist/lib/deps.js.map +1 -0
- package/dist/lib/docs-config.d.ts +67 -0
- package/dist/lib/docs-config.d.ts.map +1 -0
- package/dist/lib/docs-config.js +294 -0
- package/dist/lib/docs-config.js.map +1 -0
- package/dist/lib/errors.d.ts +23 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +32 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/frontmatter-utils.d.ts +25 -0
- package/dist/lib/frontmatter-utils.d.ts.map +1 -0
- package/dist/lib/frontmatter-utils.js +64 -0
- package/dist/lib/frontmatter-utils.js.map +1 -0
- package/dist/lib/mdx-validator.d.ts +27 -0
- package/dist/lib/mdx-validator.d.ts.map +1 -0
- package/dist/lib/mdx-validator.js +148 -0
- package/dist/lib/mdx-validator.js.map +1 -0
- package/dist/lib/navigation-validator.d.ts +31 -0
- package/dist/lib/navigation-validator.d.ts.map +1 -0
- package/dist/lib/navigation-validator.js +75 -0
- package/dist/lib/navigation-validator.js.map +1 -0
- package/dist/lib/normalize-config.d.ts +57 -0
- package/dist/lib/normalize-config.d.ts.map +1 -0
- package/dist/lib/normalize-config.js +63 -0
- package/dist/lib/normalize-config.js.map +1 -0
- package/dist/lib/openapi/cache.d.ts +40 -0
- package/dist/lib/openapi/cache.d.ts.map +1 -0
- package/dist/lib/openapi/cache.js +76 -0
- package/dist/lib/openapi/cache.js.map +1 -0
- package/dist/lib/openapi/errors.d.ts +36 -0
- package/dist/lib/openapi/errors.d.ts.map +1 -0
- package/dist/lib/openapi/errors.js +162 -0
- package/dist/lib/openapi/errors.js.map +1 -0
- package/dist/lib/openapi/index.d.ts +10 -0
- package/dist/lib/openapi/index.d.ts.map +1 -0
- package/dist/lib/openapi/index.js +12 -0
- package/dist/lib/openapi/index.js.map +1 -0
- package/dist/lib/openapi/types.d.ts +198 -0
- package/dist/lib/openapi/types.d.ts.map +1 -0
- package/dist/lib/openapi/types.js +8 -0
- package/dist/lib/openapi/types.js.map +1 -0
- package/dist/lib/openapi/validator.d.ts +45 -0
- package/dist/lib/openapi/validator.d.ts.map +1 -0
- package/dist/lib/openapi/validator.js +128 -0
- package/dist/lib/openapi/validator.js.map +1 -0
- package/dist/lib/openapi.d.ts +7 -0
- package/dist/lib/openapi.d.ts.map +1 -0
- package/dist/lib/openapi.js +7 -0
- package/dist/lib/openapi.js.map +1 -0
- package/dist/lib/output.d.ts +14 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +19 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/path-security.d.ts +23 -0
- package/dist/lib/path-security.d.ts.map +1 -0
- package/dist/lib/path-security.js +35 -0
- package/dist/lib/path-security.js.map +1 -0
- package/dist/lib/port.d.ts +18 -0
- package/dist/lib/port.d.ts.map +1 -0
- package/dist/lib/port.js +65 -0
- package/dist/lib/port.js.map +1 -0
- package/dist/lib/spinner.d.ts +4 -0
- package/dist/lib/spinner.d.ts.map +1 -0
- package/dist/lib/spinner.js +16 -0
- package/dist/lib/spinner.js.map +1 -0
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +49 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/utils/update-checker.d.ts +34 -0
- package/dist/utils/update-checker.d.ts.map +1 -0
- package/dist/utils/update-checker.js +142 -0
- package/dist/utils/update-checker.js.map +1 -0
- package/package.json +125 -0
- package/templates/docs.json +11 -0
- package/templates/introduction.mdx +19 -0
- package/templates/quickstart.mdx +20 -0
- package/vendored/app/[[...slug]]/error.tsx +103 -0
- package/vendored/app/[[...slug]]/page.tsx +690 -0
- package/vendored/app/api/assets/[...path]/route.ts +78 -0
- package/vendored/app/api/ev/route.ts +61 -0
- package/vendored/app/api/isr-health/route.ts +66 -0
- package/vendored/app/api/mcp/[project]/route.ts +435 -0
- package/vendored/app/api/og/route.tsx +167 -0
- package/vendored/app/api/r2/[project]/[...path]/route.ts +214 -0
- package/vendored/app/api/revalidate/route.ts +76 -0
- package/vendored/app/globals.css +37 -0
- package/vendored/app/layout.tsx +571 -0
- package/vendored/app/not-found.tsx +47 -0
- package/vendored/components/CodeBlockCopyButton.tsx +146 -0
- package/vendored/components/HeaderLinkCopy.tsx +135 -0
- package/vendored/components/errors/NotFoundContent.tsx +147 -0
- package/vendored/components/layout/LayoutWrapper.tsx +128 -0
- package/vendored/components/mdx/Accordion.tsx +91 -0
- package/vendored/components/mdx/ApiCodePanel.tsx +51 -0
- package/vendored/components/mdx/ApiEndpoint.tsx +104 -0
- package/vendored/components/mdx/ApiPage.tsx +379 -0
- package/vendored/components/mdx/Badge.tsx +169 -0
- package/vendored/components/mdx/Callouts.tsx +140 -0
- package/vendored/components/mdx/Card.tsx +214 -0
- package/vendored/components/mdx/CodeGroup.tsx +136 -0
- package/vendored/components/mdx/Color.tsx +244 -0
- package/vendored/components/mdx/Columns.tsx +37 -0
- package/vendored/components/mdx/Expandable.tsx +37 -0
- package/vendored/components/mdx/Frame.tsx +51 -0
- package/vendored/components/mdx/Icon.tsx +132 -0
- package/vendored/components/mdx/Latex.tsx +75 -0
- package/vendored/components/mdx/MDXComponents.tsx +414 -0
- package/vendored/components/mdx/Mermaid.tsx +35 -0
- package/vendored/components/mdx/MermaidInner.tsx +342 -0
- package/vendored/components/mdx/OpenApiEndpoint.tsx +971 -0
- package/vendored/components/mdx/Panel.tsx +26 -0
- package/vendored/components/mdx/PanelWrapper.tsx +100 -0
- package/vendored/components/mdx/ParamField.tsx +75 -0
- package/vendored/components/mdx/RequestExample.tsx +91 -0
- package/vendored/components/mdx/ResponseExample.tsx +145 -0
- package/vendored/components/mdx/ResponseField.tsx +109 -0
- package/vendored/components/mdx/Steps.tsx +173 -0
- package/vendored/components/mdx/Table.tsx +352 -0
- package/vendored/components/mdx/Tabs.tsx +147 -0
- package/vendored/components/mdx/Tile.tsx +127 -0
- package/vendored/components/mdx/Tooltip.tsx +111 -0
- package/vendored/components/mdx/Tree.tsx +484 -0
- package/vendored/components/mdx/Update.tsx +90 -0
- package/vendored/components/mdx/View.tsx +354 -0
- package/vendored/components/mdx/YouTube.tsx +35 -0
- package/vendored/components/mdx/ZoomableImage.tsx +83 -0
- package/vendored/components/navigation/Breadcrumb.tsx +241 -0
- package/vendored/components/navigation/DefaultLogo.tsx +81 -0
- package/vendored/components/navigation/Header.tsx +512 -0
- package/vendored/components/navigation/LanguageSelector.tsx +249 -0
- package/vendored/components/navigation/PageNavigation.tsx +174 -0
- package/vendored/components/navigation/Sidebar.tsx +713 -0
- package/vendored/components/navigation/SocialFooter.tsx +186 -0
- package/vendored/components/navigation/TableOfContents.tsx +435 -0
- package/vendored/components/navigation/TabsNav.tsx +182 -0
- package/vendored/components/search/LazySearchModal.tsx +19 -0
- package/vendored/components/search/SearchModal.tsx +573 -0
- package/vendored/components/snippets/ProjectSnippets.tsx +4 -0
- package/vendored/components/theme/ThemeProvider.tsx +31 -0
- package/vendored/components/theme/ThemeToggle.tsx +134 -0
- package/vendored/components/ui/CodePanel.tsx +517 -0
- package/vendored/components/ui/CodePanelModal.tsx +342 -0
- package/vendored/contexts/TabSyncContext.tsx +30 -0
- package/vendored/hooks/useFocusTrap.ts +42 -0
- package/vendored/hooks/useHashNavigation.ts +39 -0
- package/vendored/hooks/useShikiHighlight.ts +101 -0
- package/vendored/lib/analytics-client.ts +77 -0
- package/vendored/lib/build/cache.ts +138 -0
- package/vendored/lib/build/error-parser.ts +690 -0
- package/vendored/lib/build/estimation.ts +113 -0
- package/vendored/lib/build/index.ts +17 -0
- package/vendored/lib/build/page-file-map.ts +48 -0
- package/vendored/lib/build/r2-upload.ts +179 -0
- package/vendored/lib/cache-keys.ts +117 -0
- package/vendored/lib/code-utils.ts +42 -0
- package/vendored/lib/content-loader.ts +176 -0
- package/vendored/lib/deprecated-components.ts +185 -0
- package/vendored/lib/docs-isr.ts +180 -0
- package/vendored/lib/docs-types.ts +874 -0
- package/vendored/lib/docs.ts +203 -0
- package/vendored/lib/domain-helpers.ts +107 -0
- package/vendored/lib/email-notifier.ts +102 -0
- package/vendored/lib/email-templates/build-failure.tsx +193 -0
- package/vendored/lib/email-templates/components/base-layout.tsx +150 -0
- package/vendored/lib/email-templates/components/error-box.tsx +88 -0
- package/vendored/lib/email-templates/components/info-row.tsx +63 -0
- package/vendored/lib/email-templates/index.ts +13 -0
- package/vendored/lib/empty-polyfill.js +3 -0
- package/vendored/lib/extract-highlights.ts +124 -0
- package/vendored/lib/fonts.ts +227 -0
- package/vendored/lib/frontmatter-utils.ts +77 -0
- package/vendored/lib/fs-utils.ts +20 -0
- package/vendored/lib/git-utils.ts +87 -0
- package/vendored/lib/health-checks.ts +224 -0
- package/vendored/lib/icon-utils.ts +492 -0
- package/vendored/lib/infer-page-type.ts +14 -0
- package/vendored/lib/isr-build-executor.ts +185 -0
- package/vendored/lib/language-icons.ts +152 -0
- package/vendored/lib/language-utils.ts +338 -0
- package/vendored/lib/latex-config.ts +64 -0
- package/vendored/lib/link-prefix-context.tsx +32 -0
- package/vendored/lib/logger.ts +63 -0
- package/vendored/lib/mcp-search.ts +255 -0
- package/vendored/lib/mdx-inline-components.ts +155 -0
- package/vendored/lib/mdx.ts +100 -0
- package/vendored/lib/middleware-helpers.ts +519 -0
- package/vendored/lib/navigation-resolver.ts +621 -0
- package/vendored/lib/navigation-utils.ts +103 -0
- package/vendored/lib/normalize-config.ts +94 -0
- package/vendored/lib/openapi/cache.ts +92 -0
- package/vendored/lib/openapi/code-examples.ts +389 -0
- package/vendored/lib/openapi/errors.ts +253 -0
- package/vendored/lib/openapi/generator.ts +230 -0
- package/vendored/lib/openapi/index.ts +84 -0
- package/vendored/lib/openapi/parser.ts +474 -0
- package/vendored/lib/openapi/types.ts +232 -0
- package/vendored/lib/openapi/validator.ts +156 -0
- package/vendored/lib/openapi-isr.ts +121 -0
- package/vendored/lib/page-isr-helpers.ts +137 -0
- package/vendored/lib/path-safety.ts +130 -0
- package/vendored/lib/paths.ts +35 -0
- package/vendored/lib/preprocess-mdx.ts +951 -0
- package/vendored/lib/process-mdx-with-exports.ts +75 -0
- package/vendored/lib/project-resolver.ts +165 -0
- package/vendored/lib/r2-content.ts +60 -0
- package/vendored/lib/r2-manifest.ts +84 -0
- package/vendored/lib/recent-searches.ts +41 -0
- package/vendored/lib/recma-compound-components.ts +84 -0
- package/vendored/lib/redirect-compiler.ts +160 -0
- package/vendored/lib/redirect-matcher.ts +296 -0
- package/vendored/lib/redis.ts +23 -0
- package/vendored/lib/rehype-class-to-classname.ts +31 -0
- package/vendored/lib/rehype-code-meta.ts +275 -0
- package/vendored/lib/rehype-nozoom-to-data.ts +45 -0
- package/vendored/lib/remark-extract-exports.ts +104 -0
- package/vendored/lib/resilience.ts +260 -0
- package/vendored/lib/revalidation-helpers.ts +200 -0
- package/vendored/lib/revalidation-trigger.ts +150 -0
- package/vendored/lib/screenshot-capture.ts +229 -0
- package/vendored/lib/search-client.ts +91 -0
- package/vendored/lib/search-suggestions.ts +38 -0
- package/vendored/lib/search.ts +158 -0
- package/vendored/lib/seo.ts +264 -0
- package/vendored/lib/shiki-client.ts +131 -0
- package/vendored/lib/shiki-config.ts +289 -0
- package/vendored/lib/shiki-css-theme.ts +46 -0
- package/vendored/lib/shiki-highlighter.ts +62 -0
- package/vendored/lib/shiki-transformers.ts +337 -0
- package/vendored/lib/slack-notifier.ts +248 -0
- package/vendored/lib/snippet-compiler-isr.ts +114 -0
- package/vendored/lib/snippet-loader-isr.ts +276 -0
- package/vendored/lib/static-artifacts.ts +375 -0
- package/vendored/lib/static-file-route.ts +72 -0
- package/vendored/lib/tracking-script.ts +19 -0
- package/vendored/lib/typography-config.ts +42 -0
- package/vendored/lib/validate-config.ts +268 -0
- package/vendored/next.config.js +45 -0
- package/vendored/postcss.config.js +6 -0
- package/vendored/schema/README.md +28 -0
- package/vendored/schema/docs-schema.json +4631 -0
- package/vendored/scripts/build-project.cjs +174 -0
- package/vendored/scripts/build-search-index.cjs +347 -0
- package/vendored/scripts/compile-snippets.cjs +488 -0
- package/vendored/scripts/copy-files.cjs +295 -0
- package/vendored/scripts/dev-project.cjs +534 -0
- package/vendored/scripts/enhance-navigation.cjs +354 -0
- package/vendored/scripts/validate-links.cjs +423 -0
- package/vendored/shared/constants.ts +6 -0
- package/vendored/shared/index.ts +19 -0
- package/vendored/shared/logger.ts +62 -0
- package/vendored/shared/memory-monitor.ts +190 -0
- package/vendored/shared/navigation-validator.ts +101 -0
- package/vendored/shared/path-security.ts +39 -0
- package/vendored/shared/status-reporter.ts +199 -0
- package/vendored/shared/timer.ts +51 -0
- package/vendored/shared/types.ts +102 -0
- package/vendored/tailwind.config.ts +39 -0
- package/vendored/themes/base.css +1311 -0
- package/vendored/themes/index.ts +119 -0
- package/vendored/themes/jam/variables.css +835 -0
- package/vendored/themes/nebula/variables.css +282 -0
- package/vendored/themes/pulsar/variables.css +1009 -0
- package/vendored/themes/types.ts +89 -0
- package/vendored/tsconfig.json +48 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Helpers
|
|
3
|
+
*
|
|
4
|
+
* Testable helper functions for the Next.js middleware.
|
|
5
|
+
* Handles project resolution, security validation, and routing logic.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { log } from './logger';
|
|
9
|
+
import {
|
|
10
|
+
resolveProject,
|
|
11
|
+
resolveProjectFromHostname,
|
|
12
|
+
resolveCustomDomain,
|
|
13
|
+
getProjectConfig,
|
|
14
|
+
isSubdomain,
|
|
15
|
+
} from './project-resolver';
|
|
16
|
+
import { redis } from './redis';
|
|
17
|
+
import { getForwardedHosts, isJamdeskDomain } from './domain-helpers';
|
|
18
|
+
import { getRedirects, matchRedirect, mergeQueryStrings, isInvalidDestination } from './redirect-matcher';
|
|
19
|
+
import { ASSET_PREFIX } from './docs-types';
|
|
20
|
+
import type { NextRequest } from 'next/server';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Result of project resolution.
|
|
24
|
+
*/
|
|
25
|
+
export interface ProjectResolutionResult {
|
|
26
|
+
/** Resolved project slug, or null if not found */
|
|
27
|
+
projectSlug: string | null;
|
|
28
|
+
/** Whether to host docs at /docs path */
|
|
29
|
+
hostAtDocs: boolean;
|
|
30
|
+
/** Error message if resolution failed */
|
|
31
|
+
error?: string;
|
|
32
|
+
/** Whether to skip middleware (non-ISR mode) */
|
|
33
|
+
skip?: boolean;
|
|
34
|
+
/** Domain verification status */
|
|
35
|
+
domainStatus?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Security validation result.
|
|
40
|
+
*/
|
|
41
|
+
export interface SecurityResult {
|
|
42
|
+
/** Whether the request is allowed */
|
|
43
|
+
allowed: boolean;
|
|
44
|
+
/** Error message if blocked */
|
|
45
|
+
error?: string;
|
|
46
|
+
/** HTTP status code for blocked requests */
|
|
47
|
+
status?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Redirect check result.
|
|
52
|
+
*/
|
|
53
|
+
export interface RedirectResult {
|
|
54
|
+
/** Whether a redirect should happen */
|
|
55
|
+
shouldRedirect: boolean;
|
|
56
|
+
/** Redirect destination URL */
|
|
57
|
+
destination?: string;
|
|
58
|
+
/** HTTP status code (301 or 302) */
|
|
59
|
+
status?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Handle project resolution from hostname.
|
|
64
|
+
*
|
|
65
|
+
* Supports forwarded host headers for custom domain resolution. When a request
|
|
66
|
+
* arrives at a Jamdesk subdomain with X-Jamdesk-Forwarded-Host set to a custom
|
|
67
|
+
* domain, the custom domain's config (hostAtDocs) is used instead of the
|
|
68
|
+
* subdomain's project config. This enables generic Cloudflare Workers that
|
|
69
|
+
* don't need domain-specific logic.
|
|
70
|
+
*
|
|
71
|
+
* @param request - Next.js request object
|
|
72
|
+
* @returns Resolution result with project slug or error
|
|
73
|
+
*/
|
|
74
|
+
export async function handleProjectResolution(
|
|
75
|
+
request: NextRequest
|
|
76
|
+
): Promise<ProjectResolutionResult> {
|
|
77
|
+
const hostname = request.headers.get('host') || '';
|
|
78
|
+
|
|
79
|
+
// Skip if not in ISR mode
|
|
80
|
+
if (process.env.ISR_MODE !== 'true') {
|
|
81
|
+
return { projectSlug: null, hostAtDocs: true, skip: true };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
log('info', 'Resolving project from hostname', { hostname });
|
|
85
|
+
|
|
86
|
+
// Check if it's a Jamdesk subdomain first (fast path)
|
|
87
|
+
const isJamdesk = isSubdomain(hostname);
|
|
88
|
+
|
|
89
|
+
if (isJamdesk) {
|
|
90
|
+
const projectSlug = resolveProjectFromHostname(hostname);
|
|
91
|
+
if (projectSlug) {
|
|
92
|
+
// Check for forwarded custom domain - use its config for hostAtDocs
|
|
93
|
+
// This enables generic Cloudflare Workers that pass X-Jamdesk-Forwarded-Host
|
|
94
|
+
const forwardedHosts = getForwardedHosts(request);
|
|
95
|
+
const customForwardedHost = forwardedHosts.find(h => !isJamdeskDomain(h));
|
|
96
|
+
|
|
97
|
+
if (customForwardedHost) {
|
|
98
|
+
// Resolve hostAtDocs from custom domain config
|
|
99
|
+
const customResolution = await resolveCustomDomain(customForwardedHost);
|
|
100
|
+
log('info', 'Project resolved from subdomain with forwarded custom domain', {
|
|
101
|
+
hostname,
|
|
102
|
+
projectSlug,
|
|
103
|
+
forwardedHost: customForwardedHost,
|
|
104
|
+
hostAtDocs: customResolution.hostAtDocs,
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
projectSlug,
|
|
108
|
+
hostAtDocs: customResolution.hostAtDocs,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// No forwarded custom domain - use project config
|
|
113
|
+
const config = await getProjectConfig(projectSlug);
|
|
114
|
+
log('info', 'Project resolved from subdomain', { hostname, projectSlug });
|
|
115
|
+
return { projectSlug, hostAtDocs: config.hostAtDocs };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Custom domain - resolve via Redis
|
|
120
|
+
const resolution = await resolveCustomDomain(hostname);
|
|
121
|
+
|
|
122
|
+
if (!resolution.projectSlug) {
|
|
123
|
+
log('warn', 'Project not found for hostname', { hostname });
|
|
124
|
+
return {
|
|
125
|
+
projectSlug: null,
|
|
126
|
+
hostAtDocs: true,
|
|
127
|
+
error: 'Project not found',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check domain verification status
|
|
132
|
+
if (resolution.domainStatus && resolution.domainStatus !== 'active' && resolution.domainStatus !== 'cached') {
|
|
133
|
+
log('warn', 'Domain not active', { hostname, status: resolution.domainStatus });
|
|
134
|
+
return {
|
|
135
|
+
projectSlug: resolution.projectSlug,
|
|
136
|
+
hostAtDocs: resolution.hostAtDocs,
|
|
137
|
+
domainStatus: resolution.domainStatus,
|
|
138
|
+
error: 'Domain not verified',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
log('info', 'Project resolved from custom domain', { hostname, projectSlug: resolution.projectSlug });
|
|
143
|
+
return {
|
|
144
|
+
projectSlug: resolution.projectSlug,
|
|
145
|
+
hostAtDocs: resolution.hostAtDocs,
|
|
146
|
+
domainStatus: resolution.domainStatus,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Validate forwarded host headers for security.
|
|
152
|
+
* Prevents unauthorized proxying of content.
|
|
153
|
+
*
|
|
154
|
+
* @param request - Next.js request
|
|
155
|
+
* @param projectSlug - Expected project slug
|
|
156
|
+
* @returns Security validation result
|
|
157
|
+
*/
|
|
158
|
+
export async function validateForwardedHost(
|
|
159
|
+
request: NextRequest,
|
|
160
|
+
projectSlug: string
|
|
161
|
+
): Promise<SecurityResult> {
|
|
162
|
+
const hostname = request.headers.get('host') || '';
|
|
163
|
+
const forwardedHosts = getForwardedHosts(request);
|
|
164
|
+
|
|
165
|
+
// No forwarded hosts - allow
|
|
166
|
+
if (forwardedHosts.length === 0) {
|
|
167
|
+
return { allowed: true };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Not a Jamdesk subdomain - skip validation
|
|
171
|
+
if (!isJamdeskDomain(hostname)) {
|
|
172
|
+
return { allowed: true };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Filter to only custom domains (non-Jamdesk) that need validation
|
|
176
|
+
const customForwardedHosts = forwardedHosts.filter(h => h && !isJamdeskDomain(h));
|
|
177
|
+
|
|
178
|
+
// No custom domains to validate - allow (Jamdesk domains are trusted)
|
|
179
|
+
if (customForwardedHosts.length === 0) {
|
|
180
|
+
return { allowed: true };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// No Redis - can't validate custom forwarded hosts, fail closed for security
|
|
184
|
+
if (!redis) {
|
|
185
|
+
log('error', 'Blocked request - cannot validate forwarded host without Redis', { hostname, forwardedHosts: customForwardedHosts });
|
|
186
|
+
return {
|
|
187
|
+
allowed: false,
|
|
188
|
+
error: 'Service temporarily unavailable',
|
|
189
|
+
status: 503,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Validate each custom forwarded host
|
|
194
|
+
for (const forwardedHost of customForwardedHosts) {
|
|
195
|
+
try {
|
|
196
|
+
const [status, mappedProject] = await Promise.all([
|
|
197
|
+
redis.get<string>(`domainStatus:${forwardedHost}`),
|
|
198
|
+
redis.get<string>(`domain:${forwardedHost}`),
|
|
199
|
+
]);
|
|
200
|
+
|
|
201
|
+
// Must be active AND map to the same project
|
|
202
|
+
if (status !== 'active' || mappedProject !== projectSlug) {
|
|
203
|
+
log('warn', 'Blocked proxied request - forwarded host not verified', {
|
|
204
|
+
hostname,
|
|
205
|
+
projectSlug,
|
|
206
|
+
forwardedHost,
|
|
207
|
+
mappedProject: mappedProject || 'not-registered',
|
|
208
|
+
status: status || 'missing',
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
allowed: false,
|
|
212
|
+
error: `Domain "${forwardedHost}" is not authorized to serve this content.`,
|
|
213
|
+
status: 403,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
log('error', 'Redis lookup failed for forwarded host', { forwardedHost, error });
|
|
218
|
+
return {
|
|
219
|
+
allowed: false,
|
|
220
|
+
error: 'Service temporarily unavailable',
|
|
221
|
+
status: 503,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return { allowed: true };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check for redirects and return redirect info if matched.
|
|
231
|
+
*
|
|
232
|
+
* @param projectSlug - Project identifier
|
|
233
|
+
* @param pathname - Request pathname
|
|
234
|
+
* @param search - Query string
|
|
235
|
+
* @returns Redirect result
|
|
236
|
+
*/
|
|
237
|
+
export async function checkRedirects(
|
|
238
|
+
projectSlug: string,
|
|
239
|
+
pathname: string,
|
|
240
|
+
search: string
|
|
241
|
+
): Promise<RedirectResult> {
|
|
242
|
+
const redirects = await getRedirects(projectSlug);
|
|
243
|
+
if (!redirects?.length) {
|
|
244
|
+
return { shouldRedirect: false };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const match = matchRedirect(pathname, redirects);
|
|
248
|
+
if (!match) {
|
|
249
|
+
return { shouldRedirect: false };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const destination = mergeQueryStrings(search, match.destination);
|
|
253
|
+
|
|
254
|
+
// Final safety check: skip redirects to protected paths or with wildcards
|
|
255
|
+
if (isInvalidDestination(destination)) {
|
|
256
|
+
log('warn', 'Skipping redirect to invalid destination', {
|
|
257
|
+
projectSlug,
|
|
258
|
+
pathname,
|
|
259
|
+
destination,
|
|
260
|
+
});
|
|
261
|
+
return { shouldRedirect: false };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
shouldRedirect: true,
|
|
266
|
+
destination,
|
|
267
|
+
status: match.permanent ? 301 : 302,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Internal API routes that should skip middleware processing.
|
|
273
|
+
*
|
|
274
|
+
* IMPORTANT: When adding a new API route to app/api/, add it here too!
|
|
275
|
+
* Otherwise doc pages with similar paths will be incorrectly skipped.
|
|
276
|
+
*
|
|
277
|
+
* Note: Documentation pages under /api/* (like /api/overview, /api/users/list)
|
|
278
|
+
* should NOT be skipped - they need project context from middleware.
|
|
279
|
+
* Only skip actual internal API endpoints.
|
|
280
|
+
*
|
|
281
|
+
* Uses exact match OR prefix-with-slash to avoid false positives:
|
|
282
|
+
* - /api/og → skipped (exact match)
|
|
283
|
+
* - /api/og/ → skipped (prefix match)
|
|
284
|
+
* - /api/ogimage → NOT skipped (could be a doc page)
|
|
285
|
+
*/
|
|
286
|
+
export const INTERNAL_API_ROUTES = [
|
|
287
|
+
'/api/assets', // Asset serving from R2 (app/api/assets/[...path])
|
|
288
|
+
'/api/ev', // Analytics events (app/api/ev)
|
|
289
|
+
'/api/isr-health', // Health check endpoint (app/api/isr-health)
|
|
290
|
+
'/api/mcp', // MCP endpoint (app/api/mcp/[project])
|
|
291
|
+
'/api/og', // OG image generation (app/api/og)
|
|
292
|
+
'/api/r2', // R2 content serving (app/api/r2/[project]/[...path])
|
|
293
|
+
'/api/revalidate', // Cache revalidation (app/api/revalidate)
|
|
294
|
+
];
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Check if path is an internal API route.
|
|
298
|
+
*
|
|
299
|
+
* @param pathname - Request pathname
|
|
300
|
+
* @returns true if this is an internal API route that should skip middleware
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* isInternalApiRoute('/api/og') // true (exact match)
|
|
304
|
+
* isInternalApiRoute('/api/og/') // true (prefix + slash)
|
|
305
|
+
* isInternalApiRoute('/api/ogimage') // false (not a match)
|
|
306
|
+
* isInternalApiRoute('/api/users/list') // false (doc page)
|
|
307
|
+
*/
|
|
308
|
+
export function isInternalApiRoute(pathname: string): boolean {
|
|
309
|
+
return INTERNAL_API_ROUTES.some(route =>
|
|
310
|
+
pathname === route || pathname.startsWith(route + '/')
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* File extensions that should skip middleware.
|
|
316
|
+
* Note: Image extensions (.png, .jpg, .webp, etc.) are NOT skipped
|
|
317
|
+
* because they need to be routed through the asset API with project context.
|
|
318
|
+
*/
|
|
319
|
+
const SKIP_EXTENSIONS = [
|
|
320
|
+
'.css',
|
|
321
|
+
'.js',
|
|
322
|
+
'.woff',
|
|
323
|
+
'.woff2',
|
|
324
|
+
'.ttf',
|
|
325
|
+
'.eot',
|
|
326
|
+
'.map',
|
|
327
|
+
];
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Static asset paths bundled with the app (not project-specific).
|
|
331
|
+
* These are served directly from public/ and don't need middleware processing.
|
|
332
|
+
*/
|
|
333
|
+
const STATIC_ASSET_PREFIXES = [
|
|
334
|
+
`${ASSET_PREFIX}/branding/`, // Jamdesk branding assets
|
|
335
|
+
`${ASSET_PREFIX}/fonts/`, // Font Awesome web fonts
|
|
336
|
+
] as const;
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Check if middleware should run for this path.
|
|
340
|
+
*
|
|
341
|
+
* @param pathname - Request pathname (e.g., '/introduction')
|
|
342
|
+
* @returns true if middleware should process this request
|
|
343
|
+
*/
|
|
344
|
+
export function shouldRunMiddleware(pathname: string): boolean {
|
|
345
|
+
// Skip Next.js internals
|
|
346
|
+
if (pathname.startsWith('/_next/')) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Skip internal API routes (but NOT doc pages under /api/*)
|
|
351
|
+
if (isInternalApiRoute(pathname)) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Skip other static paths
|
|
356
|
+
if (pathname === '/favicon.ico' || pathname.startsWith('/.well-known/')) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Skip bundled static assets (branding, fonts)
|
|
361
|
+
// Note: /_jd/images/ is NOT skipped - those are project-specific from R2
|
|
362
|
+
if (STATIC_ASSET_PREFIXES.some(prefix => pathname.startsWith(prefix))) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Skip static file extensions
|
|
367
|
+
if (SKIP_EXTENSIONS.some(ext => pathname.endsWith(ext))) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Note: /robots.txt, /sitemap.xml, /llms.txt are NOT skipped
|
|
372
|
+
// They are served from R2 and need project context from middleware
|
|
373
|
+
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Check if this is an MCP request that needs routing.
|
|
379
|
+
*
|
|
380
|
+
* @param pathname - Request pathname
|
|
381
|
+
* @returns true if this is an MCP request
|
|
382
|
+
*/
|
|
383
|
+
export function isMcpRequest(pathname: string): boolean {
|
|
384
|
+
return pathname === '/_mcp' || pathname === '/docs/_mcp';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get the MCP API path for a project.
|
|
389
|
+
*
|
|
390
|
+
* @param projectSlug - Project identifier
|
|
391
|
+
* @returns MCP API route path
|
|
392
|
+
*/
|
|
393
|
+
export function getMcpApiPath(projectSlug: string): string {
|
|
394
|
+
return `/api/mcp/${projectSlug}`;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Check if path needs trailing slash normalization.
|
|
399
|
+
*
|
|
400
|
+
* @param pathname - Request pathname
|
|
401
|
+
* @returns true if trailing slash should be removed
|
|
402
|
+
*/
|
|
403
|
+
export function needsTrailingSlashRedirect(pathname: string): boolean {
|
|
404
|
+
return (
|
|
405
|
+
pathname !== '/' &&
|
|
406
|
+
pathname.endsWith('/') &&
|
|
407
|
+
!pathname.startsWith('/_next/')
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Handle hostAtDocs routing logic.
|
|
413
|
+
* Determines if path needs redirect based on hostAtDocs setting.
|
|
414
|
+
*
|
|
415
|
+
* @param pathname - Request pathname
|
|
416
|
+
* @param hostAtDocs - Whether docs are hosted at /docs
|
|
417
|
+
* @returns Redirect destination or null if no redirect needed
|
|
418
|
+
*/
|
|
419
|
+
export function getHostAtDocsRedirect(
|
|
420
|
+
pathname: string,
|
|
421
|
+
hostAtDocs: boolean
|
|
422
|
+
): string | null {
|
|
423
|
+
if (hostAtDocs) {
|
|
424
|
+
// hostAtDocs=true: Docs served at /docs/*
|
|
425
|
+
if (pathname === '/') {
|
|
426
|
+
return '/docs';
|
|
427
|
+
}
|
|
428
|
+
// Check if path is a page (not an asset)
|
|
429
|
+
const isAsset = pathname.includes('.');
|
|
430
|
+
if (!isAsset && !pathname.startsWith('/docs')) {
|
|
431
|
+
return `/docs${pathname}`;
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
// hostAtDocs=false: Docs served at root
|
|
435
|
+
// Redirect legacy /docs/* paths
|
|
436
|
+
if (pathname === '/docs' || pathname === '/docs/') {
|
|
437
|
+
return '/';
|
|
438
|
+
}
|
|
439
|
+
if (pathname.startsWith('/docs/')) {
|
|
440
|
+
return pathname.replace('/docs/', '/');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Build response headers with project context.
|
|
448
|
+
*
|
|
449
|
+
* @param projectSlug - Resolved project slug
|
|
450
|
+
* @param hostAtDocs - Whether docs are hosted at /docs
|
|
451
|
+
* @param existingHeaders - Existing request headers to clone
|
|
452
|
+
* @returns New headers with project context added
|
|
453
|
+
*/
|
|
454
|
+
export function buildProjectHeaders(
|
|
455
|
+
projectSlug: string,
|
|
456
|
+
existingHeaders: Headers,
|
|
457
|
+
hostAtDocs = true
|
|
458
|
+
): Headers {
|
|
459
|
+
const newHeaders = new Headers(existingHeaders);
|
|
460
|
+
newHeaders.set('x-project-slug', projectSlug);
|
|
461
|
+
newHeaders.set('x-host-at-docs', hostAtDocs ? 'true' : 'false');
|
|
462
|
+
return newHeaders;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Asset extensions that need to be routed to the assets API.
|
|
467
|
+
*/
|
|
468
|
+
const ASSET_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.pdf'];
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Project-specific files that need to be fetched from R2 via assets API.
|
|
472
|
+
* These files are unique per project and stored in the assets/ directory in R2.
|
|
473
|
+
*
|
|
474
|
+
* Note: search-data.json and search-index.orama.json are NOT included here
|
|
475
|
+
* because they're stored at the project root in R2, not in assets/.
|
|
476
|
+
* They have dedicated routes (app/search-data.json/route.ts) instead.
|
|
477
|
+
*/
|
|
478
|
+
const PROJECT_SPECIFIC_FILES: string[] = [
|
|
479
|
+
// Currently empty - search files have dedicated routes
|
|
480
|
+
];
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Check if this is an asset request that needs routing to the assets API.
|
|
484
|
+
*
|
|
485
|
+
* @param pathname - Request pathname
|
|
486
|
+
* @returns true if this is an asset that should be served from R2
|
|
487
|
+
*/
|
|
488
|
+
export function isAssetRequest(pathname: string): boolean {
|
|
489
|
+
// Skip bundled static assets (branding, fonts) - served directly from public/
|
|
490
|
+
if (STATIC_ASSET_PREFIXES.some(prefix => pathname.startsWith(prefix))) {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Project images under /_jd/images/ are served from R2
|
|
495
|
+
if (pathname.startsWith(`${ASSET_PREFIX}/images/`)) {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Project-specific files from R2, or files with asset extensions
|
|
500
|
+
return (
|
|
501
|
+
PROJECT_SPECIFIC_FILES.includes(pathname) ||
|
|
502
|
+
ASSET_EXTENSIONS.some(ext => pathname.endsWith(ext))
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Get the assets API path for rewriting.
|
|
508
|
+
*
|
|
509
|
+
* Strips the /_jd/ prefix since R2 stores assets without it.
|
|
510
|
+
*
|
|
511
|
+
* @param pathname - Original request pathname (e.g., '/_jd/images/logo.png')
|
|
512
|
+
* @returns Assets API path (e.g., '/api/assets/images/logo.png')
|
|
513
|
+
*/
|
|
514
|
+
export function getAssetsApiPath(pathname: string): string {
|
|
515
|
+
// Strip asset prefix and leading slash (R2 stores at images/, not _jd/images/)
|
|
516
|
+
const prefixPattern = new RegExp(`^${ASSET_PREFIX}/`);
|
|
517
|
+
const assetPath = pathname.replace(prefixPattern, '').replace(/^\//, '');
|
|
518
|
+
return `/api/assets/${assetPath}`;
|
|
519
|
+
}
|