@wildo-ai/saas-website 1.1.1
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 +34 -0
- package/dist/esm/.builder.pid +9 -0
- package/dist/esm/astro/blog-post-bridge.d.ts +324 -0
- package/dist/esm/astro/blog-post-bridge.d.ts.map +1 -0
- package/dist/esm/astro/blog-post-bridge.js +57 -0
- package/dist/esm/astro/blog-post-bridge.js.map +1 -0
- package/dist/esm/astro/blog-post-head.renderer.d.ts +129 -0
- package/dist/esm/astro/blog-post-head.renderer.d.ts.map +1 -0
- package/dist/esm/astro/blog-post-head.renderer.js +139 -0
- package/dist/esm/astro/blog-post-head.renderer.js.map +1 -0
- package/dist/esm/astro/bridge-runtime.d.ts +224 -0
- package/dist/esm/astro/bridge-runtime.d.ts.map +1 -0
- package/dist/esm/astro/bridge-runtime.js +30 -0
- package/dist/esm/astro/bridge-runtime.js.map +1 -0
- package/dist/esm/astro/collect-expected-label-keys.d.ts +65 -0
- package/dist/esm/astro/collect-expected-label-keys.d.ts.map +1 -0
- package/dist/esm/astro/collect-expected-label-keys.js +101 -0
- package/dist/esm/astro/collect-expected-label-keys.js.map +1 -0
- package/dist/esm/astro/headers-renderer.d.ts +32 -0
- package/dist/esm/astro/headers-renderer.d.ts.map +1 -0
- package/dist/esm/astro/headers-renderer.js +64 -0
- package/dist/esm/astro/headers-renderer.js.map +1 -0
- package/dist/esm/astro/i18n-routing.helper.d.ts +91 -0
- package/dist/esm/astro/i18n-routing.helper.d.ts.map +1 -0
- package/dist/esm/astro/i18n-routing.helper.js +23 -0
- package/dist/esm/astro/i18n-routing.helper.js.map +1 -0
- package/dist/esm/astro/internal/html-escape.d.ts +31 -0
- package/dist/esm/astro/internal/html-escape.d.ts.map +1 -0
- package/dist/esm/astro/internal/html-escape.js +40 -0
- package/dist/esm/astro/internal/html-escape.js.map +1 -0
- package/dist/esm/astro/label-pack-loader.d.ts +134 -0
- package/dist/esm/astro/label-pack-loader.d.ts.map +1 -0
- package/dist/esm/astro/label-pack-loader.js +298 -0
- package/dist/esm/astro/label-pack-loader.js.map +1 -0
- package/dist/esm/astro/llms-txt-server.d.ts +28 -0
- package/dist/esm/astro/llms-txt-server.d.ts.map +1 -0
- package/dist/esm/astro/llms-txt-server.js +33 -0
- package/dist/esm/astro/llms-txt-server.js.map +1 -0
- package/dist/esm/astro/pick-labels-for-page.d.ts +63 -0
- package/dist/esm/astro/pick-labels-for-page.d.ts.map +1 -0
- package/dist/esm/astro/pick-labels-for-page.js +73 -0
- package/dist/esm/astro/pick-labels-for-page.js.map +1 -0
- package/dist/esm/astro/robots-renderer.d.ts +85 -0
- package/dist/esm/astro/robots-renderer.d.ts.map +1 -0
- package/dist/esm/astro/robots-renderer.js +157 -0
- package/dist/esm/astro/robots-renderer.js.map +1 -0
- package/dist/esm/astro/sitemap-coverage.d.ts +101 -0
- package/dist/esm/astro/sitemap-coverage.d.ts.map +1 -0
- package/dist/esm/astro/sitemap-coverage.js +67 -0
- package/dist/esm/astro/sitemap-coverage.js.map +1 -0
- package/dist/esm/astro/structured-data.renderer.d.ts +21 -0
- package/dist/esm/astro/structured-data.renderer.d.ts.map +1 -0
- package/dist/esm/astro/structured-data.renderer.js +142 -0
- package/dist/esm/astro/structured-data.renderer.js.map +1 -0
- package/dist/esm/astro/website-page-head.renderer.d.ts +93 -0
- package/dist/esm/astro/website-page-head.renderer.d.ts.map +1 -0
- package/dist/esm/astro/website-page-head.renderer.js +184 -0
- package/dist/esm/astro/website-page-head.renderer.js.map +1 -0
- package/dist/esm/astro/website-page-runtime.helper.d.ts +112 -0
- package/dist/esm/astro/website-page-runtime.helper.d.ts.map +1 -0
- package/dist/esm/astro/website-page-runtime.helper.js +60 -0
- package/dist/esm/astro/website-page-runtime.helper.js.map +1 -0
- package/dist/esm/astro/website-site-context.d.ts +126 -0
- package/dist/esm/astro/website-site-context.d.ts.map +1 -0
- package/dist/esm/astro/website-site-context.js +37 -0
- package/dist/esm/astro/website-site-context.js.map +1 -0
- package/dist/esm/astro-island.d.ts +56 -0
- package/dist/esm/astro-island.d.ts.map +1 -0
- package/dist/esm/astro-island.js +61 -0
- package/dist/esm/astro-island.js.map +1 -0
- package/dist/esm/astro.d.ts +151 -0
- package/dist/esm/astro.d.ts.map +1 -0
- package/dist/esm/astro.js +151 -0
- package/dist/esm/astro.js.map +1 -0
- package/dist/esm/companion-exports.d.ts +38 -0
- package/dist/esm/companion-exports.d.ts.map +1 -0
- package/dist/esm/companion-exports.js +38 -0
- package/dist/esm/companion-exports.js.map +1 -0
- package/dist/esm/components/low-level/WebsiteButton.d.ts +73 -0
- package/dist/esm/components/low-level/WebsiteButton.d.ts.map +1 -0
- package/dist/esm/components/low-level/WebsiteButton.js +68 -0
- package/dist/esm/components/low-level/WebsiteButton.js.map +1 -0
- package/dist/esm/components/low-level/WebsiteHeading.d.ts +78 -0
- package/dist/esm/components/low-level/WebsiteHeading.d.ts.map +1 -0
- package/dist/esm/components/low-level/WebsiteHeading.js +41 -0
- package/dist/esm/components/low-level/WebsiteHeading.js.map +1 -0
- package/dist/esm/components/low-level/WebsiteImage.d.ts +73 -0
- package/dist/esm/components/low-level/WebsiteImage.d.ts.map +1 -0
- package/dist/esm/components/low-level/WebsiteImage.js +21 -0
- package/dist/esm/components/low-level/WebsiteImage.js.map +1 -0
- package/dist/esm/components/low-level/WebsiteInternalButton.d.ts +90 -0
- package/dist/esm/components/low-level/WebsiteInternalButton.d.ts.map +1 -0
- package/dist/esm/components/low-level/WebsiteInternalButton.js +47 -0
- package/dist/esm/components/low-level/WebsiteInternalButton.js.map +1 -0
- package/dist/esm/components/low-level/WebsiteInternalLink.d.ts +99 -0
- package/dist/esm/components/low-level/WebsiteInternalLink.d.ts.map +1 -0
- package/dist/esm/components/low-level/WebsiteInternalLink.js +68 -0
- package/dist/esm/components/low-level/WebsiteInternalLink.js.map +1 -0
- package/dist/esm/components/low-level/WebsiteLink.d.ts +50 -0
- package/dist/esm/components/low-level/WebsiteLink.d.ts.map +1 -0
- package/dist/esm/components/low-level/WebsiteLink.js +22 -0
- package/dist/esm/components/low-level/WebsiteLink.js.map +1 -0
- package/dist/esm/components/low-level/WebsiteText.d.ts +57 -0
- package/dist/esm/components/low-level/WebsiteText.d.ts.map +1 -0
- package/dist/esm/components/low-level/WebsiteText.js +57 -0
- package/dist/esm/components/low-level/WebsiteText.js.map +1 -0
- package/dist/esm/config/define-website-config.d.ts +83 -0
- package/dist/esm/config/define-website-config.d.ts.map +1 -0
- package/dist/esm/config/define-website-config.js +126 -0
- package/dist/esm/config/define-website-config.js.map +1 -0
- package/dist/esm/config/index.d.ts +38 -0
- package/dist/esm/config/index.d.ts.map +1 -0
- package/dist/esm/config/index.js +38 -0
- package/dist/esm/config/index.js.map +1 -0
- package/dist/esm/config/load-website-config.d.ts +136 -0
- package/dist/esm/config/load-website-config.d.ts.map +1 -0
- package/dist/esm/config/load-website-config.js +177 -0
- package/dist/esm/config/load-website-config.js.map +1 -0
- package/dist/esm/config/wildo-website-config.schemas.d.ts +251 -0
- package/dist/esm/config/wildo-website-config.schemas.d.ts.map +1 -0
- package/dist/esm/config/wildo-website-config.schemas.js +302 -0
- package/dist/esm/config/wildo-website-config.schemas.js.map +1 -0
- package/dist/esm/config-loader.d.ts +35 -0
- package/dist/esm/config-loader.d.ts.map +1 -0
- package/dist/esm/config-loader.js +35 -0
- package/dist/esm/config-loader.js.map +1 -0
- package/dist/esm/core/anonymous-session/InboundContactForm.d.ts +38 -0
- package/dist/esm/core/anonymous-session/InboundContactForm.d.ts.map +1 -0
- package/dist/esm/core/anonymous-session/InboundContactForm.js +77 -0
- package/dist/esm/core/anonymous-session/InboundContactForm.js.map +1 -0
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts +41 -0
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts.map +1 -0
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js +58 -0
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js.map +1 -0
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts +81 -0
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts.map +1 -0
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.js +177 -0
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.js.map +1 -0
- package/dist/esm/core/contexts/WebsitePageContext.d.ts +40 -0
- package/dist/esm/core/contexts/WebsitePageContext.d.ts.map +1 -0
- package/dist/esm/core/contexts/WebsitePageContext.js +8 -0
- package/dist/esm/core/contexts/WebsitePageContext.js.map +1 -0
- package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts +162 -0
- package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts.map +1 -0
- package/dist/esm/core/contexts/WebsiteRuntimeContext.js +22 -0
- package/dist/esm/core/contexts/WebsiteRuntimeContext.js.map +1 -0
- package/dist/esm/core/contexts/WebsiteSectionContext.d.ts +47 -0
- package/dist/esm/core/contexts/WebsiteSectionContext.d.ts.map +1 -0
- package/dist/esm/core/contexts/WebsiteSectionContext.js +8 -0
- package/dist/esm/core/contexts/WebsiteSectionContext.js.map +1 -0
- package/dist/esm/core/contexts/useWebsitePage.d.ts +13 -0
- package/dist/esm/core/contexts/useWebsitePage.d.ts.map +1 -0
- package/dist/esm/core/contexts/useWebsitePage.js +22 -0
- package/dist/esm/core/contexts/useWebsitePage.js.map +1 -0
- package/dist/esm/core/contexts/useWebsiteRuntime.d.ts +19 -0
- package/dist/esm/core/contexts/useWebsiteRuntime.d.ts.map +1 -0
- package/dist/esm/core/contexts/useWebsiteRuntime.js +28 -0
- package/dist/esm/core/contexts/useWebsiteRuntime.js.map +1 -0
- package/dist/esm/core/contexts/useWebsiteSection.d.ts +12 -0
- package/dist/esm/core/contexts/useWebsiteSection.d.ts.map +1 -0
- package/dist/esm/core/contexts/useWebsiteSection.js +22 -0
- package/dist/esm/core/contexts/useWebsiteSection.js.map +1 -0
- package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts +17 -0
- package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts.map +1 -0
- package/dist/esm/core/external-providers/frontend-provider-registry.website.js +23 -0
- package/dist/esm/core/external-providers/frontend-provider-registry.website.js.map +1 -0
- package/dist/esm/core/factories/define-website-page-manifest.d.ts +70 -0
- package/dist/esm/core/factories/define-website-page-manifest.d.ts.map +1 -0
- package/dist/esm/core/factories/define-website-page-manifest.js +51 -0
- package/dist/esm/core/factories/define-website-page-manifest.js.map +1 -0
- package/dist/esm/core/factories/define-website-section.d.ts +101 -0
- package/dist/esm/core/factories/define-website-section.d.ts.map +1 -0
- package/dist/esm/core/factories/define-website-section.js +71 -0
- package/dist/esm/core/factories/define-website-section.js.map +1 -0
- package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts +23 -0
- package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts.map +1 -0
- package/dist/esm/core/hooks/useWebsiteDesignTokens.js +25 -0
- package/dist/esm/core/hooks/useWebsiteDesignTokens.js.map +1 -0
- package/dist/esm/core/hooks/useWebsiteLabel.d.ts +68 -0
- package/dist/esm/core/hooks/useWebsiteLabel.d.ts.map +1 -0
- package/dist/esm/core/hooks/useWebsiteLabel.js +103 -0
- package/dist/esm/core/hooks/useWebsiteLabel.js.map +1 -0
- package/dist/esm/core/layouts/WebsitePageLayout.d.ts +87 -0
- package/dist/esm/core/layouts/WebsitePageLayout.d.ts.map +1 -0
- package/dist/esm/core/layouts/WebsitePageLayout.js +170 -0
- package/dist/esm/core/layouts/WebsitePageLayout.js.map +1 -0
- package/dist/esm/core/layouts/WebsiteSection.d.ts +91 -0
- package/dist/esm/core/layouts/WebsiteSection.d.ts.map +1 -0
- package/dist/esm/core/layouts/WebsiteSection.js +26 -0
- package/dist/esm/core/layouts/WebsiteSection.js.map +1 -0
- package/dist/esm/core/routing/internal-routing.utils.d.ts +65 -0
- package/dist/esm/core/routing/internal-routing.utils.d.ts.map +1 -0
- package/dist/esm/core/routing/internal-routing.utils.js +14 -0
- package/dist/esm/core/routing/internal-routing.utils.js.map +1 -0
- package/dist/esm/index.d.ts +129 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +129 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/mdx/BlogPost.d.ts +193 -0
- package/dist/esm/mdx/BlogPost.d.ts.map +1 -0
- package/dist/esm/mdx/BlogPost.js +161 -0
- package/dist/esm/mdx/BlogPost.js.map +1 -0
- package/dist/esm/mdx/blog-post-collection.config.d.ts +139 -0
- package/dist/esm/mdx/blog-post-collection.config.d.ts.map +1 -0
- package/dist/esm/mdx/blog-post-collection.config.js +27 -0
- package/dist/esm/mdx/blog-post-collection.config.js.map +1 -0
- package/dist/esm/mdx/blog-post-frontmatter-source.d.ts +12 -0
- package/dist/esm/mdx/blog-post-frontmatter-source.d.ts.map +1 -0
- package/dist/esm/mdx/blog-post-frontmatter-source.js +13 -0
- package/dist/esm/mdx/blog-post-frontmatter-source.js.map +1 -0
- package/dist/esm/mdx/index.d.ts +22 -0
- package/dist/esm/mdx/index.d.ts.map +1 -0
- package/dist/esm/mdx/index.js +22 -0
- package/dist/esm/mdx/index.js.map +1 -0
- package/dist/esm/mdx/website-mdx-provider.d.ts +90 -0
- package/dist/esm/mdx/website-mdx-provider.d.ts.map +1 -0
- package/dist/esm/mdx/website-mdx-provider.js +8 -0
- package/dist/esm/mdx/website-mdx-provider.js.map +1 -0
- package/dist/esm/mdx.d.ts +38 -0
- package/dist/esm/mdx.d.ts.map +1 -0
- package/dist/esm/mdx.js +38 -0
- package/dist/esm/mdx.js.map +1 -0
- package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts +75 -0
- package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.js +85 -0
- package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.js.map +1 -0
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts +120 -0
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js +152 -0
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js.map +1 -0
- package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts +73 -0
- package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/label-keys/website-label-key.schemas.js +76 -0
- package/dist/esm/schemas/label-keys/website-label-key.schemas.js.map +1 -0
- package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts +209 -0
- package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.js +218 -0
- package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.js.map +1 -0
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts +431 -0
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js +438 -0
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js.map +1 -0
- package/dist/esm/schemas/refs/page-ref.schemas.d.ts +40 -0
- package/dist/esm/schemas/refs/page-ref.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/refs/page-ref.schemas.js +43 -0
- package/dist/esm/schemas/refs/page-ref.schemas.js.map +1 -0
- package/dist/esm/schemas/refs/section-ref.schemas.d.ts +41 -0
- package/dist/esm/schemas/refs/section-ref.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/refs/section-ref.schemas.js +44 -0
- package/dist/esm/schemas/refs/section-ref.schemas.js.map +1 -0
- package/dist/esm/schemas/sections/website-section-category.shared.d.ts +63 -0
- package/dist/esm/schemas/sections/website-section-category.shared.d.ts.map +1 -0
- package/dist/esm/schemas/sections/website-section-category.shared.js +64 -0
- package/dist/esm/schemas/sections/website-section-category.shared.js.map +1 -0
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts +114 -0
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js +69 -0
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js.map +1 -0
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts +100 -0
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts.map +1 -0
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js +355 -0
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js.map +1 -0
- package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts +58 -0
- package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js +2 -0
- package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js.map +1 -0
- package/dist/esm/schemas/validators/label-pack.validator.d.ts +144 -0
- package/dist/esm/schemas/validators/label-pack.validator.d.ts.map +1 -0
- package/dist/esm/schemas/validators/label-pack.validator.js +100 -0
- package/dist/esm/schemas/validators/label-pack.validator.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +143 -0
- package/src/__tests__/bundle-isolation.test.ts +607 -0
- package/src/astro/__tests__/blog-post-bridge.test.tsx +288 -0
- package/src/astro/__tests__/blog-post-head.renderer.test.ts +249 -0
- package/src/astro/__tests__/bridge-runtime.test.tsx +198 -0
- package/src/astro/__tests__/collect-expected-label-keys.test.ts +69 -0
- package/src/astro/__tests__/headers-renderer.test.ts +143 -0
- package/src/astro/__tests__/i18n-routing.helper.test.ts +116 -0
- package/src/astro/__tests__/label-pack-loader.test.ts +305 -0
- package/src/astro/__tests__/llms-txt-server.test.ts +127 -0
- package/src/astro/__tests__/pick-labels-for-page.test.ts +60 -0
- package/src/astro/__tests__/robots-renderer.test.ts +228 -0
- package/src/astro/__tests__/sitemap-coverage.test.ts +230 -0
- package/src/astro/__tests__/structured-data.renderer.test.ts +172 -0
- package/src/astro/__tests__/website-page-head.renderer.test.ts +424 -0
- package/src/astro/__tests__/website-page-runtime.helper.test.ts +157 -0
- package/src/astro/__tests__/website-site-context.test.ts +121 -0
- package/src/astro/blog-post-bridge.tsx +404 -0
- package/src/astro/blog-post-head.renderer.ts +293 -0
- package/src/astro/bridge-runtime.tsx +270 -0
- package/src/astro/collect-expected-label-keys.ts +113 -0
- package/src/astro/headers-renderer.ts +82 -0
- package/src/astro/i18n-routing.helper.ts +116 -0
- package/src/astro/internal/html-escape.ts +41 -0
- package/src/astro/label-pack-loader.ts +385 -0
- package/src/astro/llms-txt-server.ts +56 -0
- package/src/astro/pick-labels-for-page.ts +85 -0
- package/src/astro/robots-renderer.ts +171 -0
- package/src/astro/sitemap-coverage.ts +186 -0
- package/src/astro/structured-data.renderer.ts +171 -0
- package/src/astro/website-page-head.renderer.ts +279 -0
- package/src/astro/website-page-runtime.helper.ts +188 -0
- package/src/astro/website-site-context.ts +136 -0
- package/src/astro-island.ts +61 -0
- package/src/astro.ts +151 -0
- package/src/companion-exports.ts +38 -0
- package/src/components/low-level/WebsiteButton.tsx +118 -0
- package/src/components/low-level/WebsiteHeading.tsx +96 -0
- package/src/components/low-level/WebsiteImage.tsx +96 -0
- package/src/components/low-level/WebsiteInternalButton.tsx +172 -0
- package/src/components/low-level/WebsiteInternalLink.tsx +193 -0
- package/src/components/low-level/WebsiteLink.tsx +71 -0
- package/src/components/low-level/WebsiteText.tsx +75 -0
- package/src/components/low-level/__tests__/WebsiteInternalButton.test.tsx +169 -0
- package/src/components/low-level/__tests__/WebsiteInternalLink.test.tsx +202 -0
- package/src/components/low-level/__tests__/primitives.test.tsx +141 -0
- package/src/config/__tests__/define-website-config.test.ts +231 -0
- package/src/config/__tests__/load-website-config.test.ts +215 -0
- package/src/config/define-website-config.ts +134 -0
- package/src/config/index.ts +38 -0
- package/src/config/load-website-config.ts +229 -0
- package/src/config/wildo-website-config.schemas.ts +322 -0
- package/src/config-loader.ts +35 -0
- package/src/core/__tests__/WebsitePageLayout.test.tsx +184 -0
- package/src/core/__tests__/WebsiteSection.test.tsx +85 -0
- package/src/core/__tests__/contexts.test.tsx +133 -0
- package/src/core/__tests__/core-boundary.test.ts +85 -0
- package/src/core/__tests__/define-website-page-manifest.test.tsx +111 -0
- package/src/core/__tests__/define-website-section.test.tsx +89 -0
- package/src/core/__tests__/useWebsiteDesignTokens.test.tsx +41 -0
- package/src/core/__tests__/useWebsiteLabel.test.tsx +117 -0
- package/src/core/anonymous-session/InboundContactForm.tsx +157 -0
- package/src/core/anonymous-session/__tests__/inbound-contact-form.schema.test.ts +44 -0
- package/src/core/anonymous-session/inbound-contact-form.schema.ts +62 -0
- package/src/core/anonymous-session/website-anonymous-session-client.ts +194 -0
- package/src/core/contexts/WebsitePageContext.tsx +52 -0
- package/src/core/contexts/WebsiteRuntimeContext.tsx +175 -0
- package/src/core/contexts/WebsiteSectionContext.tsx +59 -0
- package/src/core/contexts/useWebsitePage.ts +28 -0
- package/src/core/contexts/useWebsiteRuntime.ts +34 -0
- package/src/core/contexts/useWebsiteSection.ts +28 -0
- package/src/core/external-providers/frontend-provider-registry.website.ts +62 -0
- package/src/core/factories/define-website-page-manifest.ts +102 -0
- package/src/core/factories/define-website-section.ts +128 -0
- package/src/core/hooks/useWebsiteDesignTokens.ts +26 -0
- package/src/core/hooks/useWebsiteLabel.ts +121 -0
- package/src/core/layouts/WebsitePageLayout.tsx +311 -0
- package/src/core/layouts/WebsiteSection.tsx +139 -0
- package/src/core/routing/__tests__/internal-routing.utils.test.ts +134 -0
- package/src/core/routing/internal-routing.utils.ts +77 -0
- package/src/index.ts +132 -0
- package/src/mdx/BlogPost.tsx +296 -0
- package/src/mdx/__tests__/BlogPost.test.tsx +218 -0
- package/src/mdx/__tests__/blog-post-collection.config.test.ts +127 -0
- package/src/mdx/__tests__/website-mdx-provider.test.tsx +82 -0
- package/src/mdx/blog-post-collection.config.ts +177 -0
- package/src/mdx/blog-post-frontmatter-source.ts +31 -0
- package/src/mdx/index.ts +39 -0
- package/src/mdx/website-mdx-provider.tsx +95 -0
- package/src/mdx.ts +38 -0
- package/src/schemas/__tests__/blog-frontmatter.test.ts +50 -0
- package/src/schemas/__tests__/companion-exports-react-free.test.ts +51 -0
- package/src/schemas/__tests__/design-tokens.test.ts +163 -0
- package/src/schemas/__tests__/label-key.test.ts +46 -0
- package/src/schemas/__tests__/label-pack-validator.test.ts +146 -0
- package/src/schemas/__tests__/page-manifest.test.ts +66 -0
- package/src/schemas/__tests__/refs.test.ts +37 -0
- package/src/schemas/__tests__/root-config.test.ts +292 -0
- package/src/schemas/__tests__/section-definition.test.ts +47 -0
- package/src/schemas/__tests__/structured-data-reconciliation.test.ts +543 -0
- package/src/schemas/blog/blog-post-frontmatter.shared.schemas.ts +111 -0
- package/src/schemas/design-tokens/website-design-tokens.shared.schemas.ts +166 -0
- package/src/schemas/label-keys/website-label-key.schemas.ts +88 -0
- package/src/schemas/manifests/website-page-manifest.shared.schemas.ts +266 -0
- package/src/schemas/manifests/website-root-config.shared.schemas.ts +504 -0
- package/src/schemas/refs/page-ref.schemas.ts +49 -0
- package/src/schemas/refs/section-ref.schemas.ts +50 -0
- package/src/schemas/sections/website-section-category.shared.ts +65 -0
- package/src/schemas/sections/website-section-definition.shared.schemas.ts +120 -0
- package/src/schemas/structured-data/structured-data-reconciliation.shared.ts +579 -0
- package/src/schemas/structured-data/website-structured-data.shared.schemas.ts +62 -0
- package/src/schemas/validators/label-pack.validator.ts +201 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wildo-package @wildo-ai/saas-website
|
|
3
|
+
*
|
|
4
|
+
* SaaS marketing/landing website engine — schemas + helpers + layout
|
|
5
|
+
* placeholders + Astro/MDX adapters + SEO/AEO/GEO builder library.
|
|
6
|
+
*
|
|
7
|
+
* Per `saas-website-slice.md` D6/D15: this engine ships **NO concrete
|
|
8
|
+
* business sections** (no Hero, no CTA, no Pricing). It ships the
|
|
9
|
+
* structural and helper machinery; consumers author every business
|
|
10
|
+
* section themselves via the `defineWebsiteSection` factory and the
|
|
11
|
+
* accompanying SKILLs in `framework-knowledge`.
|
|
12
|
+
*
|
|
13
|
+
* @wildo-boundary
|
|
14
|
+
* - This barrel MAY re-export from any subdirectory of `src/`.
|
|
15
|
+
* - Subdirectories MUST observe their own boundary contracts (see
|
|
16
|
+
* each subdirectory's own `index.ts` `@wildo-boundary` block):
|
|
17
|
+
* - `schemas/` : NO React, NO Astro, NO `~wildo-frontend-lib/**`
|
|
18
|
+
* - `seo/` : NO React, NO Astro
|
|
19
|
+
* - `core/` : MAY use React; NO `~wildo-frontend-lib/**` (Q17=B)
|
|
20
|
+
* - `astro/` : MAY use Astro; NO React component runtime imports
|
|
21
|
+
* - `mdx/` : MAY use `@mdx-js/*`
|
|
22
|
+
* - `integration/` : MAY use React; MAY import the
|
|
23
|
+
* `@wildo-ai/saas-models/public-runtime` SUBPATH,
|
|
24
|
+
* the `@wildo-ai/presets-components-models/public-runtime`
|
|
25
|
+
* SUBPATH, `@wildo-ai/zod-decorators`,
|
|
26
|
+
* plus the SSR / Astro-middleware tier
|
|
27
|
+
* (`axios`, `jsonwebtoken`, `socket.io-client`,
|
|
28
|
+
* `reflect-metadata`) — D12 revised
|
|
29
|
+
* 2026-04-19 (second same-day loosening);
|
|
30
|
+
* the ROOT barrels of `@wildo-ai/saas-models`
|
|
31
|
+
* and `@wildo-ai/presets-components-models`
|
|
32
|
+
* are FORBIDDEN (they boot the decorator
|
|
33
|
+
* framework / pull the heavy registry);
|
|
34
|
+
* the saas-app DI / backend stack
|
|
35
|
+
* (`inversify`, `mongoose`, `express`,
|
|
36
|
+
* `pino`, `helmet`, `argon2`, `bcrypt`,
|
|
37
|
+
* the `@wildo-ai/saas-frontend-lib` /
|
|
38
|
+
* `@wildo-ai/saas-backend-lib` /
|
|
39
|
+
* `@wildo-ai/external-connectors-models`
|
|
40
|
+
* root + subpath surfaces) is FORBIDDEN;
|
|
41
|
+
* MUST NOT import `~wildo-frontend-lib/**`
|
|
42
|
+
* (Q17=B / D16)
|
|
43
|
+
*
|
|
44
|
+
* D12 (client-bundle import discipline, all `core/`/`schemas/`/`integration/`
|
|
45
|
+
* files): the entire saas-website source tree ships into a browser bundle
|
|
46
|
+
* (Astro SSG + React islands). Every shared model atom MUST come from a
|
|
47
|
+
* side-effect-free `/public-runtime` subpath:
|
|
48
|
+
* - `@wildo-ai/saas-models/public-runtime` is the primary door
|
|
49
|
+
* (saas-models-public-runtime.md K-6 — consumers see one path).
|
|
50
|
+
* - `@wildo-ai/presets-components-models/public-runtime` is the
|
|
51
|
+
* internal seam for symbols not (yet) re-exported by saas-models.
|
|
52
|
+
* - `@wildo-ai/zod-decorators` is allowed to import (first revision
|
|
53
|
+
* 2026-04-19). The package itself is side-effect-free; only an
|
|
54
|
+
* explicit `initZodDecorators()` call mutates `z.ZodType.prototype`,
|
|
55
|
+
* which neither saas-website nor the saas-models /public-runtime
|
|
56
|
+
* barrel does.
|
|
57
|
+
* - `axios`, `jsonwebtoken`, `socket.io-client`, `reflect-metadata` are
|
|
58
|
+
* allowed for SSR / Astro-middleware code paths (second same-day
|
|
59
|
+
* revision 2026-04-19). The website engine needs them to provide
|
|
60
|
+
* middleware utilities (HTTP proxying to the SaaS backend, JWT
|
|
61
|
+
* verification of cross-frontend handoff codes, real-time push,
|
|
62
|
+
* decorator-metadata reads). They are SSR-tier — discipline
|
|
63
|
+
* (and a future strict/loose entry split for React islands vs
|
|
64
|
+
* middleware) keeps them out of the React-island bundle.
|
|
65
|
+
*
|
|
66
|
+
* The ROOT barrel of `@wildo-ai/saas-models` is forbidden everywhere in
|
|
67
|
+
* this package — it calls `initZodDecorators()` at module load and pulls
|
|
68
|
+
* `@wildo-ai/external-connectors-models` + the full schema graph into
|
|
69
|
+
* the bundle. Same forbid applies to the root barrel of
|
|
70
|
+
* `@wildo-ai/presets-components-models`, the saas-app DI runtime
|
|
71
|
+
* (`inversify`, `@wildo-ai/saas-frontend-lib`, `@wildo-ai/saas-backend-lib`),
|
|
72
|
+
* and pure-backend libs (`mongoose`, `express`, `pino`, `helmet`,
|
|
73
|
+
* `argon2`, `bcrypt`). Enforced by `src/__tests__/bundle-isolation.test.ts`.
|
|
74
|
+
*
|
|
75
|
+
* These boundaries are enforced by review + the bundle-isolation test
|
|
76
|
+
* above until Phase 8 promotes them to a programmatic ESLint check
|
|
77
|
+
* (cf. `saas-website-slice.md`
|
|
78
|
+
* §"DEFERRED to Phase 8 — boundary lint mechanism design").
|
|
79
|
+
*
|
|
80
|
+
* Phase 1+2 status: schemas + runtime contexts/hooks + layout
|
|
81
|
+
* primitives + L1 placeholders + factories are all exported. Phase 5
|
|
82
|
+
* adds the Astro adapter; Phase 5.7 the `integration/` layer.
|
|
83
|
+
*
|
|
84
|
+
* D18 (L1 boundary, Phase 2): the L1 primitives (`WebsiteHeading`,
|
|
85
|
+
* `WebsiteText`, `WebsiteButton`, `WebsiteLink`, `WebsiteImage`) are
|
|
86
|
+
* self-contained placeholders — they do NOT consume
|
|
87
|
+
* `ComponentRegistryService` from `saas-frontend-lib` and do NOT
|
|
88
|
+
* re-export `saas-frontend-lib` primitives. Consumers override at the
|
|
89
|
+
* L1 boundary by barrel-re-exporting their preferred styled variant
|
|
90
|
+
* from their app's `app/website/primitives/<Primitive>.tsx`. Phase 6
|
|
91
|
+
* dogfood will revisit and decide whether to promote a registry-based
|
|
92
|
+
* override mechanism.
|
|
93
|
+
*/
|
|
94
|
+
export * from './schemas/refs/page-ref.schemas.js';
|
|
95
|
+
export * from './schemas/refs/section-ref.schemas.js';
|
|
96
|
+
export * from './schemas/label-keys/website-label-key.schemas.js';
|
|
97
|
+
export * from './schemas/sections/website-section-category.shared.js';
|
|
98
|
+
export * from './schemas/sections/website-section-definition.shared.schemas.js';
|
|
99
|
+
export * from './schemas/structured-data/website-structured-data.shared.schemas.js';
|
|
100
|
+
export * from './schemas/structured-data/structured-data-reconciliation.shared.js';
|
|
101
|
+
export * from './schemas/design-tokens/website-design-tokens.shared.schemas.js';
|
|
102
|
+
export * from './schemas/manifests/website-page-manifest.shared.schemas.js';
|
|
103
|
+
export * from './schemas/manifests/website-root-config.shared.schemas.js';
|
|
104
|
+
export * from './schemas/blog/blog-post-frontmatter.shared.schemas.js';
|
|
105
|
+
export * from './schemas/validators/label-pack.validator.js';
|
|
106
|
+
export * from './core/contexts/WebsiteRuntimeContext.js';
|
|
107
|
+
export * from './core/contexts/useWebsiteRuntime.js';
|
|
108
|
+
export * from './core/contexts/WebsitePageContext.js';
|
|
109
|
+
export * from './core/contexts/useWebsitePage.js';
|
|
110
|
+
export * from './core/contexts/WebsiteSectionContext.js';
|
|
111
|
+
export * from './core/contexts/useWebsiteSection.js';
|
|
112
|
+
export * from './core/hooks/useWebsiteLabel.js';
|
|
113
|
+
export * from './core/hooks/useWebsiteDesignTokens.js';
|
|
114
|
+
export * from './core/factories/define-website-section.js';
|
|
115
|
+
export * from './core/factories/define-website-page-manifest.js';
|
|
116
|
+
export * from './core/layouts/WebsitePageLayout.js';
|
|
117
|
+
export * from './core/layouts/WebsiteSection.js';
|
|
118
|
+
export * from './core/anonymous-session/website-anonymous-session-client.js';
|
|
119
|
+
export * from './core/anonymous-session/inbound-contact-form.schema.js';
|
|
120
|
+
export * from './core/anonymous-session/InboundContactForm.js';
|
|
121
|
+
export * from './components/low-level/WebsiteHeading.js';
|
|
122
|
+
export * from './components/low-level/WebsiteText.js';
|
|
123
|
+
export * from './components/low-level/WebsiteLink.js';
|
|
124
|
+
export * from './components/low-level/WebsiteInternalLink.js';
|
|
125
|
+
export * from './core/routing/internal-routing.utils.js';
|
|
126
|
+
export * from './components/low-level/WebsiteButton.js';
|
|
127
|
+
export * from './components/low-level/WebsiteInternalButton.js';
|
|
128
|
+
export * from './components/low-level/WebsiteImage.js';
|
|
129
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FG;AAEH,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,oDAAoD,CAAC;AACnE,cAAc,8DAA8D,CAAC;AAC7E,cAAc,kEAAkE,CAAC;AACjF,cAAc,iEAAiE,CAAC;AAChF,cAAc,8DAA8D,CAAC;AAC7E,cAAc,0DAA0D,CAAC;AACzE,cAAc,wDAAwD,CAAC;AACvE,cAAc,qDAAqD,CAAC;AACpE,cAAc,2CAA2C,CAAC;AAE1D,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,cAAc,mCAAmC,CAAC;AAClD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAE9C,cAAc,2DAA2D,CAAC;AAC1E,cAAc,sDAAsD,CAAC;AACrE,cAAc,6CAA6C,CAAC;AAE5D,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,uCAAuC,CAAC;AACtD,cAAc,sCAAsC,CAAC;AACrD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,qCAAqC,CAAC","sourcesContent":["/**\n * @wildo-package @wildo-ai/saas-website\n *\n * SaaS marketing/landing website engine — schemas + helpers + layout\n * placeholders + Astro/MDX adapters + SEO/AEO/GEO builder library.\n *\n * Per `saas-website-slice.md` D6/D15: this engine ships **NO concrete\n * business sections** (no Hero, no CTA, no Pricing). It ships the\n * structural and helper machinery; consumers author every business\n * section themselves via the `defineWebsiteSection` factory and the\n * accompanying SKILLs in `framework-knowledge`.\n *\n * @wildo-boundary\n * - This barrel MAY re-export from any subdirectory of `src/`.\n * - Subdirectories MUST observe their own boundary contracts (see\n * each subdirectory's own `index.ts` `@wildo-boundary` block):\n * - `schemas/` : NO React, NO Astro, NO `~wildo-frontend-lib/**`\n * - `seo/` : NO React, NO Astro\n * - `core/` : MAY use React; NO `~wildo-frontend-lib/**` (Q17=B)\n * - `astro/` : MAY use Astro; NO React component runtime imports\n * - `mdx/` : MAY use `@mdx-js/*`\n * - `integration/` : MAY use React; MAY import the\n * `@wildo-ai/saas-models/public-runtime` SUBPATH,\n * the `@wildo-ai/presets-components-models/public-runtime`\n * SUBPATH, `@wildo-ai/zod-decorators`,\n * plus the SSR / Astro-middleware tier\n * (`axios`, `jsonwebtoken`, `socket.io-client`,\n * `reflect-metadata`) — D12 revised\n * 2026-04-19 (second same-day loosening);\n * the ROOT barrels of `@wildo-ai/saas-models`\n * and `@wildo-ai/presets-components-models`\n * are FORBIDDEN (they boot the decorator\n * framework / pull the heavy registry);\n * the saas-app DI / backend stack\n * (`inversify`, `mongoose`, `express`,\n * `pino`, `helmet`, `argon2`, `bcrypt`,\n * the `@wildo-ai/saas-frontend-lib` /\n * `@wildo-ai/saas-backend-lib` /\n * `@wildo-ai/external-connectors-models`\n * root + subpath surfaces) is FORBIDDEN;\n * MUST NOT import `~wildo-frontend-lib/**`\n * (Q17=B / D16)\n *\n * D12 (client-bundle import discipline, all `core/`/`schemas/`/`integration/`\n * files): the entire saas-website source tree ships into a browser bundle\n * (Astro SSG + React islands). Every shared model atom MUST come from a\n * side-effect-free `/public-runtime` subpath:\n * - `@wildo-ai/saas-models/public-runtime` is the primary door\n * (saas-models-public-runtime.md K-6 — consumers see one path).\n * - `@wildo-ai/presets-components-models/public-runtime` is the\n * internal seam for symbols not (yet) re-exported by saas-models.\n * - `@wildo-ai/zod-decorators` is allowed to import (first revision\n * 2026-04-19). The package itself is side-effect-free; only an\n * explicit `initZodDecorators()` call mutates `z.ZodType.prototype`,\n * which neither saas-website nor the saas-models /public-runtime\n * barrel does.\n * - `axios`, `jsonwebtoken`, `socket.io-client`, `reflect-metadata` are\n * allowed for SSR / Astro-middleware code paths (second same-day\n * revision 2026-04-19). The website engine needs them to provide\n * middleware utilities (HTTP proxying to the SaaS backend, JWT\n * verification of cross-frontend handoff codes, real-time push,\n * decorator-metadata reads). They are SSR-tier — discipline\n * (and a future strict/loose entry split for React islands vs\n * middleware) keeps them out of the React-island bundle.\n *\n * The ROOT barrel of `@wildo-ai/saas-models` is forbidden everywhere in\n * this package — it calls `initZodDecorators()` at module load and pulls\n * `@wildo-ai/external-connectors-models` + the full schema graph into\n * the bundle. Same forbid applies to the root barrel of\n * `@wildo-ai/presets-components-models`, the saas-app DI runtime\n * (`inversify`, `@wildo-ai/saas-frontend-lib`, `@wildo-ai/saas-backend-lib`),\n * and pure-backend libs (`mongoose`, `express`, `pino`, `helmet`,\n * `argon2`, `bcrypt`). Enforced by `src/__tests__/bundle-isolation.test.ts`.\n *\n * These boundaries are enforced by review + the bundle-isolation test\n * above until Phase 8 promotes them to a programmatic ESLint check\n * (cf. `saas-website-slice.md`\n * §\"DEFERRED to Phase 8 — boundary lint mechanism design\").\n *\n * Phase 1+2 status: schemas + runtime contexts/hooks + layout\n * primitives + L1 placeholders + factories are all exported. Phase 5\n * adds the Astro adapter; Phase 5.7 the `integration/` layer.\n *\n * D18 (L1 boundary, Phase 2): the L1 primitives (`WebsiteHeading`,\n * `WebsiteText`, `WebsiteButton`, `WebsiteLink`, `WebsiteImage`) are\n * self-contained placeholders — they do NOT consume\n * `ComponentRegistryService` from `saas-frontend-lib` and do NOT\n * re-export `saas-frontend-lib` primitives. Consumers override at the\n * L1 boundary by barrel-re-exporting their preferred styled variant\n * from their app's `app/website/primitives/<Primitive>.tsx`. Phase 6\n * dogfood will revisit and decide whether to promote a registry-based\n * override mechanism.\n */\n\nexport * from './schemas/refs/page-ref.schemas';\nexport * from './schemas/refs/section-ref.schemas';\nexport * from './schemas/label-keys/website-label-key.schemas';\nexport * from './schemas/sections/website-section-category.shared';\nexport * from './schemas/sections/website-section-definition.shared.schemas';\nexport * from './schemas/structured-data/website-structured-data.shared.schemas';\nexport * from './schemas/structured-data/structured-data-reconciliation.shared';\nexport * from './schemas/design-tokens/website-design-tokens.shared.schemas';\nexport * from './schemas/manifests/website-page-manifest.shared.schemas';\nexport * from './schemas/manifests/website-root-config.shared.schemas';\nexport * from './schemas/blog/blog-post-frontmatter.shared.schemas';\nexport * from './schemas/validators/label-pack.validator';\n\nexport * from './core/contexts/WebsiteRuntimeContext';\nexport * from './core/contexts/useWebsiteRuntime';\nexport * from './core/contexts/WebsitePageContext';\nexport * from './core/contexts/useWebsitePage';\nexport * from './core/contexts/WebsiteSectionContext';\nexport * from './core/contexts/useWebsiteSection';\nexport * from './core/hooks/useWebsiteLabel';\nexport * from './core/hooks/useWebsiteDesignTokens';\nexport * from './core/factories/define-website-section';\nexport * from './core/factories/define-website-page-manifest';\nexport * from './core/layouts/WebsitePageLayout';\nexport * from './core/layouts/WebsiteSection';\n\nexport * from './core/anonymous-session/website-anonymous-session-client';\nexport * from './core/anonymous-session/inbound-contact-form.schema';\nexport * from './core/anonymous-session/InboundContactForm';\n\nexport * from './components/low-level/WebsiteHeading';\nexport * from './components/low-level/WebsiteText';\nexport * from './components/low-level/WebsiteLink';\nexport * from './components/low-level/WebsiteInternalLink';\nexport * from './core/routing/internal-routing.utils';\nexport * from './components/low-level/WebsiteButton';\nexport * from './components/low-level/WebsiteInternalButton';\nexport * from './components/low-level/WebsiteImage';\n"]}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import type { WebsitePageRef } from '../schemas/refs/page-ref.schemas';
|
|
3
|
+
import { type WebsiteMdxComponentsMap } from './website-mdx-provider';
|
|
4
|
+
/**
|
|
5
|
+
* @wildo_source:part:start saas.website.blog.post-renderer facet:layer:mdx facet:family:website
|
|
6
|
+
*
|
|
7
|
+
* `<BlogPost>` — engine-owned per-post page renderer for the marketing
|
|
8
|
+
* blog adapter.
|
|
9
|
+
*
|
|
10
|
+
* # What it does, end-to-end
|
|
11
|
+
*
|
|
12
|
+
* 1. **Strict frontmatter parse.** Reads the raw `entry.data` Astro
|
|
13
|
+
* hands the page (passthrough at the content-collection layer per
|
|
14
|
+
* Path Z, see `blog-post-collection.config.ts`) and runs it
|
|
15
|
+
* through `BlogPostFrontmatterSchema` (Zod 4) — branded `slug` /
|
|
16
|
+
* `authorRef` / `tags`, ISO-8601 `publishedAt`, optional
|
|
17
|
+
* `coverImageUrl`. A failure throws synchronously with a
|
|
18
|
+
* `[blog-post] (source="…")` message; Astro halts the build and
|
|
19
|
+
* points the operator to the offending MDX file.
|
|
20
|
+
* 2. **Renders `<WebsitePageLayout>` with a stable blog-post page
|
|
21
|
+
* ref.** The framework reserves `'blog-post'` as the
|
|
22
|
+
* `WebsitePageRef` for every blog post (see `BLOG_POST_PAGE_REF`
|
|
23
|
+
* below for the rationale). All blog posts share one page ref
|
|
24
|
+
* because the SECTION ref under that page is what's
|
|
25
|
+
* blog-author-controlled (see point 3) — the page-level chrome,
|
|
26
|
+
* label scope, and analytics signal are the same across the entire
|
|
27
|
+
* blog surface.
|
|
28
|
+
* 3. **Wraps the MDX body in `<WebsiteMdxProvider>`.** The consumer
|
|
29
|
+
* passes a `mdxComponents` map (typically containing one or two
|
|
30
|
+
* business-section refs they want available inside post bodies —
|
|
31
|
+
* `<CTASection ref="post-cta" />`, etc.). The provider scopes the
|
|
32
|
+
* map for the duration of the render and ONLY the duration of the
|
|
33
|
+
* render, so two blog posts on the same site can ship different
|
|
34
|
+
* provider configs without leaking.
|
|
35
|
+
* 4. **Emits a hero block (title / summary / publish date / author
|
|
36
|
+
* handle / cover image)** from the parsed frontmatter, then yields
|
|
37
|
+
* to the MDX body. Hero markup is intentionally minimal — the
|
|
38
|
+
* consumer styles it via `WebsiteDesignTokens` custom properties.
|
|
39
|
+
* No CSS file ships from this component; everything is inline
|
|
40
|
+
* style + design-token references.
|
|
41
|
+
*
|
|
42
|
+
* # Why a blog-specific page renderer (vs. the generic page bridge)
|
|
43
|
+
*
|
|
44
|
+
* The `createWebsitePageBridgeIsland` factory takes a `PageComponent`
|
|
45
|
+
* — a React component the page author writes. A blog post has NO React
|
|
46
|
+
* component; the MDX file IS the component (compiled by `@astrojs/mdx`
|
|
47
|
+
* to a default-export React tree). Reusing the page-bridge factory
|
|
48
|
+
* would force the consumer to write a ten-line wrapper per post that
|
|
49
|
+
* imports the MDX module, calls `<MdxBody />`, and re-passes the
|
|
50
|
+
* frontmatter — a cold violation of the "every blog post is one MDX
|
|
51
|
+
* file, period" principle. `<BlogPost>` is the framework's
|
|
52
|
+
* single ten-line wrapper, written once.
|
|
53
|
+
*
|
|
54
|
+
* # Why no per-post `pageRef` / per-section `sectionRef` knobs
|
|
55
|
+
*
|
|
56
|
+
* Two reasons:
|
|
57
|
+
*
|
|
58
|
+
* - **Label-key scope semantics.** A blog post's body has no
|
|
59
|
+
* framework-routed labels (the prose IS in the MDX). The section
|
|
60
|
+
* refs that DO need labels are those an MDX file embeds via the
|
|
61
|
+
* `mdxComponents` map — and those sections were already registered
|
|
62
|
+
* in the consumer's `WEBSITE_SECTIONS` registry with their
|
|
63
|
+
* own `expectedLabelKeys`. Letting blog authors pass arbitrary
|
|
64
|
+
* `sectionRef`s would create unbounded label-key surface area that
|
|
65
|
+
* `assertLabelPacksValid` cannot validate at build start.
|
|
66
|
+
* - **Analytics + chrome consistency.** Marketing teams treat "the
|
|
67
|
+
* blog" as one funnel. A single `pageRef='blog-post'` keeps the
|
|
68
|
+
* `data-website-page-ref` attribute on the layout root constant
|
|
69
|
+
* across every post, so the analytics ingest sees one blog-post
|
|
70
|
+
* page event regardless of slug. Per-post overrides would fragment
|
|
71
|
+
* this without buying any operator value.
|
|
72
|
+
*
|
|
73
|
+
* # Hydration + bundle boundary
|
|
74
|
+
*
|
|
75
|
+
* This component is NOT itself a Bridge factory — it's plain React
|
|
76
|
+
* meant to be rendered inside an Astro `.astro` page. The consumer's
|
|
77
|
+
* per-post Astro template (typically `src/pages/[locale]/blog/[slug].astro`)
|
|
78
|
+
* imports `<BlogPost>` directly and renders it; the surrounding
|
|
79
|
+
* `<WebsitePageBridgeIsland>` provides the runtime context. The MDX
|
|
80
|
+
* runtime (`@mdx-js/react`) is React-only and pulls into the React
|
|
81
|
+
* island bundle — bundle-isolation tests walk `mdx.ts` as their own
|
|
82
|
+
* entry to keep the browser-facing closure auditable.
|
|
83
|
+
*
|
|
84
|
+
* Authoring example:
|
|
85
|
+
*
|
|
86
|
+
* ```astro
|
|
87
|
+
* ---
|
|
88
|
+
* // src/pages/blog/[slug].astro
|
|
89
|
+
* import { getCollection, getEntry } from 'astro:content';
|
|
90
|
+
* import { BlogPost } from '@wildo-ai/saas-website/mdx';
|
|
91
|
+
* import { BLOG_POST_MDX_COMPONENTS } from '@/blog/mdx-components';
|
|
92
|
+
*
|
|
93
|
+
* export async function getStaticPaths() {
|
|
94
|
+
* const posts = await getCollection('blog');
|
|
95
|
+
* return posts
|
|
96
|
+
* .filter((p) => p.data.locale === 'en')
|
|
97
|
+
* .map((p) => ({ params: { slug: p.data.slug }, props: { entry: p } }));
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* const { entry } = Astro.props;
|
|
101
|
+
* const { Content } = await entry.render();
|
|
102
|
+
* ---
|
|
103
|
+
* <BlogPost
|
|
104
|
+
* rawFrontmatter={entry.data}
|
|
105
|
+
* source={entry.id}
|
|
106
|
+
* mdxComponents={BLOG_POST_MDX_COMPONENTS}
|
|
107
|
+
* client:load
|
|
108
|
+
* >
|
|
109
|
+
* <Content />
|
|
110
|
+
* </BlogPost>
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
/**
|
|
114
|
+
* Reserved `WebsitePageRef` for every blog post page. Exported so
|
|
115
|
+
* consumers can register a matching `BLOG_POST_PAGE_MANIFEST` in
|
|
116
|
+
* their site root config (the page manifest carries the navigation
|
|
117
|
+
* + label-pack scope; the renderer carries the structural layout).
|
|
118
|
+
*
|
|
119
|
+
* Branded as a plain string literal (cast through `unknown`) because:
|
|
120
|
+
* - The `WebsitePageRef` brand is enforced via Zod's `.brand<>()`,
|
|
121
|
+
* and parsing a string literal at module load time would require
|
|
122
|
+
* dragging the Zod runtime into module init — gratuitous when the
|
|
123
|
+
* ref is a compile-time constant.
|
|
124
|
+
* - `WebsitePageLayout` re-parses any incoming `pageRef` string
|
|
125
|
+
* defensively (see `WebsitePageLayoutProps.pageRef` JSDoc), so the
|
|
126
|
+
* brand is re-asserted at render time anyway.
|
|
127
|
+
*/
|
|
128
|
+
export declare const BLOG_POST_PAGE_REF: WebsitePageRef;
|
|
129
|
+
export interface BlogPostProps {
|
|
130
|
+
/**
|
|
131
|
+
* Raw frontmatter object Astro hands the page from `entry.data` (or
|
|
132
|
+
* equivalent). The renderer validates this through
|
|
133
|
+
* `BlogPostFrontmatterSchema` synchronously — invalid shapes throw
|
|
134
|
+
* with a `[blog-post]`-prefixed message. Pass `entry.data` directly;
|
|
135
|
+
* do NOT pre-parse on the consumer side (the framework owns the
|
|
136
|
+
* validation surface so a future schema change lands in ONE place).
|
|
137
|
+
*/
|
|
138
|
+
rawFrontmatter: unknown;
|
|
139
|
+
/**
|
|
140
|
+
* Diagnostic source identifier echoed into validation errors —
|
|
141
|
+
* typically `entry.id` from Astro's content collection (`'en/why-context.mdx'`
|
|
142
|
+
* or similar). Optional but strongly recommended; without it,
|
|
143
|
+
* `parseBlogPostFrontmatter` throws with a generic
|
|
144
|
+
* `[blog-post] Frontmatter failed …` message that does not name the
|
|
145
|
+
* offending file.
|
|
146
|
+
*/
|
|
147
|
+
source?: string;
|
|
148
|
+
/**
|
|
149
|
+
* MDX-tag → React-component map made available inside the post
|
|
150
|
+
* body via `<WebsiteMdxProvider>`. Typically a small object literal
|
|
151
|
+
* exporting one or two business sections the consumer wants
|
|
152
|
+
* available everywhere in the blog surface
|
|
153
|
+
* (`<CTASection ref="post-cta" />`, `<NewsletterSignup />`, etc.).
|
|
154
|
+
*
|
|
155
|
+
* Empty maps are valid — a post body using only standard markdown
|
|
156
|
+
* (no embedded React) needs no overrides.
|
|
157
|
+
*/
|
|
158
|
+
mdxComponents?: WebsiteMdxComponentsMap;
|
|
159
|
+
/**
|
|
160
|
+
* Per-page chrome overrides forwarded straight into
|
|
161
|
+
* `<WebsitePageLayout>`. `null` suppresses the slot entirely;
|
|
162
|
+
* `undefined` (the default) inherits the runtime navigation
|
|
163
|
+
* config's header/footer components. Most blog posts leave both
|
|
164
|
+
* unset to inherit the site-wide chrome.
|
|
165
|
+
*/
|
|
166
|
+
header?: ReactNode | null;
|
|
167
|
+
footer?: ReactNode | null;
|
|
168
|
+
/**
|
|
169
|
+
* The compiled MDX body — typically `<Content />` from Astro's
|
|
170
|
+
* `await entry.render()` call. Rendered inside the
|
|
171
|
+
* `<WebsiteMdxProvider>` boundary so its inline `<TagName />`
|
|
172
|
+
* references resolve against `mdxComponents`.
|
|
173
|
+
*/
|
|
174
|
+
children: ReactNode;
|
|
175
|
+
}
|
|
176
|
+
export declare function BlogPost(props: BlogPostProps): ReactNode;
|
|
177
|
+
export declare namespace BlogPost {
|
|
178
|
+
var displayName: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Type alias re-export so consumers writing `BLOG_POST_MDX_COMPONENTS:
|
|
182
|
+
* BlogPostMdxComponents = { … }` have a stable name (the underlying
|
|
183
|
+
* type is the same `WebsiteMdxComponentsMap` from the provider).
|
|
184
|
+
*/
|
|
185
|
+
export type BlogPostMdxComponents = WebsiteMdxComponentsMap;
|
|
186
|
+
/**
|
|
187
|
+
* Convenience type — the Astro `ComponentType` consumers write inline
|
|
188
|
+
* inside a `mdxComponents` map. Re-exported so consumer authoring
|
|
189
|
+
* doesn't need to import from `react` directly.
|
|
190
|
+
*/
|
|
191
|
+
export type BlogPostMdxComponent = ComponentType<any>;
|
|
192
|
+
/** @wildo_source:part:end saas.website.blog.post-renderer */
|
|
193
|
+
//# sourceMappingURL=BlogPost.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlogPost.d.ts","sourceRoot":"","sources":["../../../../src/mdx/BlogPost.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAMvE,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4GG;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAyD,CAAC;AAE3F,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAWD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAiFxD;yBAjFe,QAAQ;;;AAoFxB;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;AACtD,6DAA6D"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { WebsitePageLayout } from '../core/layouts/WebsitePageLayout.js';
|
|
3
|
+
import { parseBlogPostFrontmatter, } from './blog-post-collection.config.js';
|
|
4
|
+
import { WebsiteMdxProvider, } from './website-mdx-provider.js';
|
|
5
|
+
/**
|
|
6
|
+
* @wildo_source:part:start saas.website.blog.post-renderer facet:layer:mdx facet:family:website
|
|
7
|
+
*
|
|
8
|
+
* `<BlogPost>` — engine-owned per-post page renderer for the marketing
|
|
9
|
+
* blog adapter.
|
|
10
|
+
*
|
|
11
|
+
* # What it does, end-to-end
|
|
12
|
+
*
|
|
13
|
+
* 1. **Strict frontmatter parse.** Reads the raw `entry.data` Astro
|
|
14
|
+
* hands the page (passthrough at the content-collection layer per
|
|
15
|
+
* Path Z, see `blog-post-collection.config.ts`) and runs it
|
|
16
|
+
* through `BlogPostFrontmatterSchema` (Zod 4) — branded `slug` /
|
|
17
|
+
* `authorRef` / `tags`, ISO-8601 `publishedAt`, optional
|
|
18
|
+
* `coverImageUrl`. A failure throws synchronously with a
|
|
19
|
+
* `[blog-post] (source="…")` message; Astro halts the build and
|
|
20
|
+
* points the operator to the offending MDX file.
|
|
21
|
+
* 2. **Renders `<WebsitePageLayout>` with a stable blog-post page
|
|
22
|
+
* ref.** The framework reserves `'blog-post'` as the
|
|
23
|
+
* `WebsitePageRef` for every blog post (see `BLOG_POST_PAGE_REF`
|
|
24
|
+
* below for the rationale). All blog posts share one page ref
|
|
25
|
+
* because the SECTION ref under that page is what's
|
|
26
|
+
* blog-author-controlled (see point 3) — the page-level chrome,
|
|
27
|
+
* label scope, and analytics signal are the same across the entire
|
|
28
|
+
* blog surface.
|
|
29
|
+
* 3. **Wraps the MDX body in `<WebsiteMdxProvider>`.** The consumer
|
|
30
|
+
* passes a `mdxComponents` map (typically containing one or two
|
|
31
|
+
* business-section refs they want available inside post bodies —
|
|
32
|
+
* `<CTASection ref="post-cta" />`, etc.). The provider scopes the
|
|
33
|
+
* map for the duration of the render and ONLY the duration of the
|
|
34
|
+
* render, so two blog posts on the same site can ship different
|
|
35
|
+
* provider configs without leaking.
|
|
36
|
+
* 4. **Emits a hero block (title / summary / publish date / author
|
|
37
|
+
* handle / cover image)** from the parsed frontmatter, then yields
|
|
38
|
+
* to the MDX body. Hero markup is intentionally minimal — the
|
|
39
|
+
* consumer styles it via `WebsiteDesignTokens` custom properties.
|
|
40
|
+
* No CSS file ships from this component; everything is inline
|
|
41
|
+
* style + design-token references.
|
|
42
|
+
*
|
|
43
|
+
* # Why a blog-specific page renderer (vs. the generic page bridge)
|
|
44
|
+
*
|
|
45
|
+
* The `createWebsitePageBridgeIsland` factory takes a `PageComponent`
|
|
46
|
+
* — a React component the page author writes. A blog post has NO React
|
|
47
|
+
* component; the MDX file IS the component (compiled by `@astrojs/mdx`
|
|
48
|
+
* to a default-export React tree). Reusing the page-bridge factory
|
|
49
|
+
* would force the consumer to write a ten-line wrapper per post that
|
|
50
|
+
* imports the MDX module, calls `<MdxBody />`, and re-passes the
|
|
51
|
+
* frontmatter — a cold violation of the "every blog post is one MDX
|
|
52
|
+
* file, period" principle. `<BlogPost>` is the framework's
|
|
53
|
+
* single ten-line wrapper, written once.
|
|
54
|
+
*
|
|
55
|
+
* # Why no per-post `pageRef` / per-section `sectionRef` knobs
|
|
56
|
+
*
|
|
57
|
+
* Two reasons:
|
|
58
|
+
*
|
|
59
|
+
* - **Label-key scope semantics.** A blog post's body has no
|
|
60
|
+
* framework-routed labels (the prose IS in the MDX). The section
|
|
61
|
+
* refs that DO need labels are those an MDX file embeds via the
|
|
62
|
+
* `mdxComponents` map — and those sections were already registered
|
|
63
|
+
* in the consumer's `WEBSITE_SECTIONS` registry with their
|
|
64
|
+
* own `expectedLabelKeys`. Letting blog authors pass arbitrary
|
|
65
|
+
* `sectionRef`s would create unbounded label-key surface area that
|
|
66
|
+
* `assertLabelPacksValid` cannot validate at build start.
|
|
67
|
+
* - **Analytics + chrome consistency.** Marketing teams treat "the
|
|
68
|
+
* blog" as one funnel. A single `pageRef='blog-post'` keeps the
|
|
69
|
+
* `data-website-page-ref` attribute on the layout root constant
|
|
70
|
+
* across every post, so the analytics ingest sees one blog-post
|
|
71
|
+
* page event regardless of slug. Per-post overrides would fragment
|
|
72
|
+
* this without buying any operator value.
|
|
73
|
+
*
|
|
74
|
+
* # Hydration + bundle boundary
|
|
75
|
+
*
|
|
76
|
+
* This component is NOT itself a Bridge factory — it's plain React
|
|
77
|
+
* meant to be rendered inside an Astro `.astro` page. The consumer's
|
|
78
|
+
* per-post Astro template (typically `src/pages/[locale]/blog/[slug].astro`)
|
|
79
|
+
* imports `<BlogPost>` directly and renders it; the surrounding
|
|
80
|
+
* `<WebsitePageBridgeIsland>` provides the runtime context. The MDX
|
|
81
|
+
* runtime (`@mdx-js/react`) is React-only and pulls into the React
|
|
82
|
+
* island bundle — bundle-isolation tests walk `mdx.ts` as their own
|
|
83
|
+
* entry to keep the browser-facing closure auditable.
|
|
84
|
+
*
|
|
85
|
+
* Authoring example:
|
|
86
|
+
*
|
|
87
|
+
* ```astro
|
|
88
|
+
* ---
|
|
89
|
+
* // src/pages/blog/[slug].astro
|
|
90
|
+
* import { getCollection, getEntry } from 'astro:content';
|
|
91
|
+
* import { BlogPost } from '@wildo-ai/saas-website/mdx';
|
|
92
|
+
* import { BLOG_POST_MDX_COMPONENTS } from '@/blog/mdx-components';
|
|
93
|
+
*
|
|
94
|
+
* export async function getStaticPaths() {
|
|
95
|
+
* const posts = await getCollection('blog');
|
|
96
|
+
* return posts
|
|
97
|
+
* .filter((p) => p.data.locale === 'en')
|
|
98
|
+
* .map((p) => ({ params: { slug: p.data.slug }, props: { entry: p } }));
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* const { entry } = Astro.props;
|
|
102
|
+
* const { Content } = await entry.render();
|
|
103
|
+
* ---
|
|
104
|
+
* <BlogPost
|
|
105
|
+
* rawFrontmatter={entry.data}
|
|
106
|
+
* source={entry.id}
|
|
107
|
+
* mdxComponents={BLOG_POST_MDX_COMPONENTS}
|
|
108
|
+
* client:load
|
|
109
|
+
* >
|
|
110
|
+
* <Content />
|
|
111
|
+
* </BlogPost>
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Reserved `WebsitePageRef` for every blog post page. Exported so
|
|
116
|
+
* consumers can register a matching `BLOG_POST_PAGE_MANIFEST` in
|
|
117
|
+
* their site root config (the page manifest carries the navigation
|
|
118
|
+
* + label-pack scope; the renderer carries the structural layout).
|
|
119
|
+
*
|
|
120
|
+
* Branded as a plain string literal (cast through `unknown`) because:
|
|
121
|
+
* - The `WebsitePageRef` brand is enforced via Zod's `.brand<>()`,
|
|
122
|
+
* and parsing a string literal at module load time would require
|
|
123
|
+
* dragging the Zod runtime into module init — gratuitous when the
|
|
124
|
+
* ref is a compile-time constant.
|
|
125
|
+
* - `WebsitePageLayout` re-parses any incoming `pageRef` string
|
|
126
|
+
* defensively (see `WebsitePageLayoutProps.pageRef` JSDoc), so the
|
|
127
|
+
* brand is re-asserted at render time anyway.
|
|
128
|
+
*/
|
|
129
|
+
export const BLOG_POST_PAGE_REF = 'blog-post';
|
|
130
|
+
/**
|
|
131
|
+
* Empty default for `mdxComponents` — defined at module scope (NOT
|
|
132
|
+
* inline in the destructure default) so the object identity is
|
|
133
|
+
* stable across re-renders. Stable identity matters here because
|
|
134
|
+
* `<MDXProvider>`'s internal context value is `useMemo`-derived from
|
|
135
|
+
* the `components` prop; inline `{}` would invalidate every render.
|
|
136
|
+
*/
|
|
137
|
+
const EMPTY_MDX_COMPONENTS = Object.freeze({});
|
|
138
|
+
export function BlogPost(props) {
|
|
139
|
+
const { rawFrontmatter, source, mdxComponents = EMPTY_MDX_COMPONENTS, header, footer, children, } = props;
|
|
140
|
+
/**
|
|
141
|
+
* Build the diagnostic context once per render. We do not memoize
|
|
142
|
+
* (one object literal per render is cheaper than the `useMemo`
|
|
143
|
+
* accounting), but we also do not pass `source: undefined`
|
|
144
|
+
* unconditionally because `parseBlogPostFrontmatter` switches on
|
|
145
|
+
* `ctx.source !== undefined` for the error-suffix; passing `{}`
|
|
146
|
+
* when undefined keeps the suffix absent in that case.
|
|
147
|
+
*/
|
|
148
|
+
const parseCtx = source !== undefined ? { source } : {};
|
|
149
|
+
const frontmatter = parseBlogPostFrontmatter(rawFrontmatter, parseCtx);
|
|
150
|
+
/**
|
|
151
|
+
* The hero markup intentionally uses semantic HTML5 elements
|
|
152
|
+
* (`<header>`, `<h1>`, `<time>`, `<address>`) rather than raw
|
|
153
|
+
* `<div>`s so screen readers and crawlers see the post structure
|
|
154
|
+
* without consumer styling intervention. The styling story is:
|
|
155
|
+
* design tokens via the surrounding `WebsiteRuntimeContext` +
|
|
156
|
+
* the consumer's stylesheet — NO CSS ships from this file.
|
|
157
|
+
*/
|
|
158
|
+
return (_jsx(WebsitePageLayout, { pageRef: BLOG_POST_PAGE_REF, header: header, footer: footer, children: _jsxs("article", { "data-website-blog-post-slug": frontmatter.slug, children: [_jsxs("header", { children: [frontmatter.coverImageUrl !== undefined ? (_jsx("img", { src: frontmatter.coverImageUrl, alt: "", loading: "eager", "data-website-blog-post-cover": true })) : null, _jsx("h1", { children: frontmatter.title }), _jsx("p", { "data-website-blog-post-summary": true, children: frontmatter.summary }), _jsxs("div", { "data-website-blog-post-meta": true, children: [_jsx("time", { dateTime: frontmatter.publishedAt, children: frontmatter.publishedAt }), ' · ', _jsx("address", { "data-website-blog-post-author": true, children: frontmatter.authorRef })] }), frontmatter.tags.length > 0 ? (_jsx("ul", { "data-website-blog-post-tags": true, children: frontmatter.tags.map((tag) => (_jsx("li", { children: tag }, tag))) })) : null] }), _jsx(WebsiteMdxProvider, { components: mdxComponents, children: children })] }) }));
|
|
159
|
+
}
|
|
160
|
+
BlogPost.displayName = 'BlogPost';
|
|
161
|
+
//# sourceMappingURL=BlogPost.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlogPost.js","sourceRoot":"","sources":["../../../../src/mdx/BlogPost.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAGtE,OAAO,EACL,wBAAwB,GAEzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,kBAAkB,GAEnB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4GG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAmB,WAAwC,CAAC;AAkD3F;;;;;;GAMG;AACH,MAAM,oBAAoB,GAA4B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAExE,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,EACJ,cAAc,EACd,MAAM,EACN,aAAa,GAAG,oBAAoB,EACpC,MAAM,EACN,MAAM,EACN,QAAQ,GACT,GAAG,KAAK,CAAC;IAEV;;;;;;;OAOG;IACH,MAAM,QAAQ,GACZ,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,MAAM,WAAW,GAAG,wBAAwB,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IAEvE;;;;;;;OAOG;IACH,OAAO,CACL,KAAC,iBAAiB,IAChB,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,YAEd,kDAAsC,WAAW,CAAC,IAAyB,aACzE,6BACG,WAAW,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,CACzC,cACE,GAAG,EAAE,WAAW,CAAC,aAAa,EAC9B,GAAG,EAAC,EAAE,EACN,OAAO,EAAC,OAAO,yCAEf,CACH,CAAC,CAAC,CAAC,IAAI,EACR,uBAAK,WAAW,CAAC,KAAK,GAAM,EAC5B,8DAAmC,WAAW,CAAC,OAAO,GAAK,EAS3D,+DACE,eAAM,QAAQ,EAAE,WAAW,CAAC,WAAW,YACpC,WAAW,CAAC,WAAW,GACnB,EACN,KAAK,EACN,mEACG,WAAW,CAAC,SAA8B,GACnC,IACN,EACL,WAAW,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAC7B,4DACG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAC7B,uBACG,GAAwB,IADlB,GAAwB,CAE5B,CACN,CAAC,GACC,CACN,CAAC,CAAC,CAAC,IAAI,IACD,EACT,KAAC,kBAAkB,IAAC,UAAU,EAAE,aAAa,YAC1C,QAAQ,GACU,IACb,GACQ,CACrB,CAAC;AACJ,CAAC;AACD,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC","sourcesContent":["import type { ComponentType, ReactNode } from 'react';\n\nimport { WebsitePageLayout } from '../core/layouts/WebsitePageLayout';\nimport type { WebsitePageRef } from '../schemas/refs/page-ref.schemas';\n\nimport {\n parseBlogPostFrontmatter,\n type ParseBlogPostFrontmatterContext,\n} from './blog-post-collection.config';\nimport {\n WebsiteMdxProvider,\n type WebsiteMdxComponentsMap,\n} from './website-mdx-provider';\n\n/**\n * @wildo_source:part:start saas.website.blog.post-renderer facet:layer:mdx facet:family:website\n *\n * `<BlogPost>` — engine-owned per-post page renderer for the marketing\n * blog adapter.\n *\n * # What it does, end-to-end\n *\n * 1. **Strict frontmatter parse.** Reads the raw `entry.data` Astro\n * hands the page (passthrough at the content-collection layer per\n * Path Z, see `blog-post-collection.config.ts`) and runs it\n * through `BlogPostFrontmatterSchema` (Zod 4) — branded `slug` /\n * `authorRef` / `tags`, ISO-8601 `publishedAt`, optional\n * `coverImageUrl`. A failure throws synchronously with a\n * `[blog-post] (source=\"…\")` message; Astro halts the build and\n * points the operator to the offending MDX file.\n * 2. **Renders `<WebsitePageLayout>` with a stable blog-post page\n * ref.** The framework reserves `'blog-post'` as the\n * `WebsitePageRef` for every blog post (see `BLOG_POST_PAGE_REF`\n * below for the rationale). All blog posts share one page ref\n * because the SECTION ref under that page is what's\n * blog-author-controlled (see point 3) — the page-level chrome,\n * label scope, and analytics signal are the same across the entire\n * blog surface.\n * 3. **Wraps the MDX body in `<WebsiteMdxProvider>`.** The consumer\n * passes a `mdxComponents` map (typically containing one or two\n * business-section refs they want available inside post bodies —\n * `<CTASection ref=\"post-cta\" />`, etc.). The provider scopes the\n * map for the duration of the render and ONLY the duration of the\n * render, so two blog posts on the same site can ship different\n * provider configs without leaking.\n * 4. **Emits a hero block (title / summary / publish date / author\n * handle / cover image)** from the parsed frontmatter, then yields\n * to the MDX body. Hero markup is intentionally minimal — the\n * consumer styles it via `WebsiteDesignTokens` custom properties.\n * No CSS file ships from this component; everything is inline\n * style + design-token references.\n *\n * # Why a blog-specific page renderer (vs. the generic page bridge)\n *\n * The `createWebsitePageBridgeIsland` factory takes a `PageComponent`\n * — a React component the page author writes. A blog post has NO React\n * component; the MDX file IS the component (compiled by `@astrojs/mdx`\n * to a default-export React tree). Reusing the page-bridge factory\n * would force the consumer to write a ten-line wrapper per post that\n * imports the MDX module, calls `<MdxBody />`, and re-passes the\n * frontmatter — a cold violation of the \"every blog post is one MDX\n * file, period\" principle. `<BlogPost>` is the framework's\n * single ten-line wrapper, written once.\n *\n * # Why no per-post `pageRef` / per-section `sectionRef` knobs\n *\n * Two reasons:\n *\n * - **Label-key scope semantics.** A blog post's body has no\n * framework-routed labels (the prose IS in the MDX). The section\n * refs that DO need labels are those an MDX file embeds via the\n * `mdxComponents` map — and those sections were already registered\n * in the consumer's `WEBSITE_SECTIONS` registry with their\n * own `expectedLabelKeys`. Letting blog authors pass arbitrary\n * `sectionRef`s would create unbounded label-key surface area that\n * `assertLabelPacksValid` cannot validate at build start.\n * - **Analytics + chrome consistency.** Marketing teams treat \"the\n * blog\" as one funnel. A single `pageRef='blog-post'` keeps the\n * `data-website-page-ref` attribute on the layout root constant\n * across every post, so the analytics ingest sees one blog-post\n * page event regardless of slug. Per-post overrides would fragment\n * this without buying any operator value.\n *\n * # Hydration + bundle boundary\n *\n * This component is NOT itself a Bridge factory — it's plain React\n * meant to be rendered inside an Astro `.astro` page. The consumer's\n * per-post Astro template (typically `src/pages/[locale]/blog/[slug].astro`)\n * imports `<BlogPost>` directly and renders it; the surrounding\n * `<WebsitePageBridgeIsland>` provides the runtime context. The MDX\n * runtime (`@mdx-js/react`) is React-only and pulls into the React\n * island bundle — bundle-isolation tests walk `mdx.ts` as their own\n * entry to keep the browser-facing closure auditable.\n *\n * Authoring example:\n *\n * ```astro\n * ---\n * // src/pages/blog/[slug].astro\n * import { getCollection, getEntry } from 'astro:content';\n * import { BlogPost } from '@wildo-ai/saas-website/mdx';\n * import { BLOG_POST_MDX_COMPONENTS } from '@/blog/mdx-components';\n *\n * export async function getStaticPaths() {\n * const posts = await getCollection('blog');\n * return posts\n * .filter((p) => p.data.locale === 'en')\n * .map((p) => ({ params: { slug: p.data.slug }, props: { entry: p } }));\n * }\n *\n * const { entry } = Astro.props;\n * const { Content } = await entry.render();\n * ---\n * <BlogPost\n * rawFrontmatter={entry.data}\n * source={entry.id}\n * mdxComponents={BLOG_POST_MDX_COMPONENTS}\n * client:load\n * >\n * <Content />\n * </BlogPost>\n * ```\n */\n\n/**\n * Reserved `WebsitePageRef` for every blog post page. Exported so\n * consumers can register a matching `BLOG_POST_PAGE_MANIFEST` in\n * their site root config (the page manifest carries the navigation\n * + label-pack scope; the renderer carries the structural layout).\n *\n * Branded as a plain string literal (cast through `unknown`) because:\n * - The `WebsitePageRef` brand is enforced via Zod's `.brand<>()`,\n * and parsing a string literal at module load time would require\n * dragging the Zod runtime into module init — gratuitous when the\n * ref is a compile-time constant.\n * - `WebsitePageLayout` re-parses any incoming `pageRef` string\n * defensively (see `WebsitePageLayoutProps.pageRef` JSDoc), so the\n * brand is re-asserted at render time anyway.\n */\nexport const BLOG_POST_PAGE_REF: WebsitePageRef = 'blog-post' as unknown as WebsitePageRef;\n\nexport interface BlogPostProps {\n /**\n * Raw frontmatter object Astro hands the page from `entry.data` (or\n * equivalent). The renderer validates this through\n * `BlogPostFrontmatterSchema` synchronously — invalid shapes throw\n * with a `[blog-post]`-prefixed message. Pass `entry.data` directly;\n * do NOT pre-parse on the consumer side (the framework owns the\n * validation surface so a future schema change lands in ONE place).\n */\n rawFrontmatter: unknown;\n /**\n * Diagnostic source identifier echoed into validation errors —\n * typically `entry.id` from Astro's content collection (`'en/why-context.mdx'`\n * or similar). Optional but strongly recommended; without it,\n * `parseBlogPostFrontmatter` throws with a generic\n * `[blog-post] Frontmatter failed …` message that does not name the\n * offending file.\n */\n source?: string;\n /**\n * MDX-tag → React-component map made available inside the post\n * body via `<WebsiteMdxProvider>`. Typically a small object literal\n * exporting one or two business sections the consumer wants\n * available everywhere in the blog surface\n * (`<CTASection ref=\"post-cta\" />`, `<NewsletterSignup />`, etc.).\n *\n * Empty maps are valid — a post body using only standard markdown\n * (no embedded React) needs no overrides.\n */\n mdxComponents?: WebsiteMdxComponentsMap;\n /**\n * Per-page chrome overrides forwarded straight into\n * `<WebsitePageLayout>`. `null` suppresses the slot entirely;\n * `undefined` (the default) inherits the runtime navigation\n * config's header/footer components. Most blog posts leave both\n * unset to inherit the site-wide chrome.\n */\n header?: ReactNode | null;\n footer?: ReactNode | null;\n /**\n * The compiled MDX body — typically `<Content />` from Astro's\n * `await entry.render()` call. Rendered inside the\n * `<WebsiteMdxProvider>` boundary so its inline `<TagName />`\n * references resolve against `mdxComponents`.\n */\n children: ReactNode;\n}\n\n/**\n * Empty default for `mdxComponents` — defined at module scope (NOT\n * inline in the destructure default) so the object identity is\n * stable across re-renders. Stable identity matters here because\n * `<MDXProvider>`'s internal context value is `useMemo`-derived from\n * the `components` prop; inline `{}` would invalidate every render.\n */\nconst EMPTY_MDX_COMPONENTS: WebsiteMdxComponentsMap = Object.freeze({});\n\nexport function BlogPost(props: BlogPostProps): ReactNode {\n const {\n rawFrontmatter,\n source,\n mdxComponents = EMPTY_MDX_COMPONENTS,\n header,\n footer,\n children,\n } = props;\n\n /**\n * Build the diagnostic context once per render. We do not memoize\n * (one object literal per render is cheaper than the `useMemo`\n * accounting), but we also do not pass `source: undefined`\n * unconditionally because `parseBlogPostFrontmatter` switches on\n * `ctx.source !== undefined` for the error-suffix; passing `{}`\n * when undefined keeps the suffix absent in that case.\n */\n const parseCtx: ParseBlogPostFrontmatterContext =\n source !== undefined ? { source } : {};\n const frontmatter = parseBlogPostFrontmatter(rawFrontmatter, parseCtx);\n\n /**\n * The hero markup intentionally uses semantic HTML5 elements\n * (`<header>`, `<h1>`, `<time>`, `<address>`) rather than raw\n * `<div>`s so screen readers and crawlers see the post structure\n * without consumer styling intervention. The styling story is:\n * design tokens via the surrounding `WebsiteRuntimeContext` +\n * the consumer's stylesheet — NO CSS ships from this file.\n */\n return (\n <WebsitePageLayout\n pageRef={BLOG_POST_PAGE_REF}\n header={header}\n footer={footer}\n >\n <article data-website-blog-post-slug={frontmatter.slug as unknown as string}>\n <header>\n {frontmatter.coverImageUrl !== undefined ? (\n <img\n src={frontmatter.coverImageUrl}\n alt=\"\"\n loading=\"eager\"\n data-website-blog-post-cover\n />\n ) : null}\n <h1>{frontmatter.title}</h1>\n <p data-website-blog-post-summary>{frontmatter.summary}</p>\n {/*\n * Meta line is a `<div>`, not a `<p>` — `<address>` is a flow-\n * content element and cannot be a descendant of `<p>` per HTML5\n * (browsers auto-close the `<p>` on `<address>`, which produces\n * a hydration mismatch in React 19). The `<div>` keeps the\n * semantic `<time>` + `<address>` landmarks intact for screen\n * readers + crawlers without violating the parsing rules.\n */}\n <div data-website-blog-post-meta>\n <time dateTime={frontmatter.publishedAt}>\n {frontmatter.publishedAt}\n </time>\n {' · '}\n <address data-website-blog-post-author>\n {frontmatter.authorRef as unknown as string}\n </address>\n </div>\n {frontmatter.tags.length > 0 ? (\n <ul data-website-blog-post-tags>\n {frontmatter.tags.map((tag) => (\n <li key={tag as unknown as string}>\n {tag as unknown as string}\n </li>\n ))}\n </ul>\n ) : null}\n </header>\n <WebsiteMdxProvider components={mdxComponents}>\n {children}\n </WebsiteMdxProvider>\n </article>\n </WebsitePageLayout>\n );\n}\nBlogPost.displayName = 'BlogPost';\n\n/**\n * Type alias re-export so consumers writing `BLOG_POST_MDX_COMPONENTS:\n * BlogPostMdxComponents = { … }` have a stable name (the underlying\n * type is the same `WebsiteMdxComponentsMap` from the provider).\n */\nexport type BlogPostMdxComponents = WebsiteMdxComponentsMap;\n\n/**\n * Convenience type — the Astro `ComponentType` consumers write inline\n * inside a `mdxComponents` map. Re-exported so consumer authoring\n * doesn't need to import from `react` directly.\n */\nexport type BlogPostMdxComponent = ComponentType<any>;\n/** @wildo_source:part:end saas.website.blog.post-renderer */\n"]}
|