@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,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wildo_source:part:start engine.saas-website.config facet:layer:engine facet:family:marketing-website
|
|
3
|
+
*
|
|
4
|
+
* Per-service marketing-website configuration block, authored in
|
|
5
|
+
* the website service folder as `wildo.website.config.ts`.
|
|
6
|
+
*
|
|
7
|
+
* # What this file's loaded value is used for TODAY
|
|
8
|
+
*
|
|
9
|
+
* This schema is the single source of truth for the marketing-site routing
|
|
10
|
+
* and origin surface. Every consumer reads here:
|
|
11
|
+
*
|
|
12
|
+
* - `defaultLocale` + `locales` — read by `buildAstroI18nConfig(...)`,
|
|
13
|
+
* `assertLabelPacksValid(...)`, `loadWebsitePageRuntime(...)` and
|
|
14
|
+
* `renderWebsitePageHeadHtml(...)` via the shared `WebsiteSiteContext`.
|
|
15
|
+
* - `publicOrigin` — read by the head renderer to compose absolute
|
|
16
|
+
* `<link rel="canonical">`, `og:url`, hreflang `href` URLs and
|
|
17
|
+
* the structured-data `mainEntityOfPage`. The previous
|
|
18
|
+
* `WebsiteRootConfig.seo.siteOrigin` was deleted.
|
|
19
|
+
* - `appOrigin` — has NO runtime consumer today. The
|
|
20
|
+
* marketing → app handoff link surface that will read it does
|
|
21
|
+
* not exist yet. Authored eagerly so the link-surface PR is a
|
|
22
|
+
* single framework-owned move; until then the field is exercised
|
|
23
|
+
* only by the schema parser.
|
|
24
|
+
* - `csp?` — read by the website-local `sync-nginx-csp` build step,
|
|
25
|
+
* which merges the app-static posture with `infra.csp?.<serviceKey>`,
|
|
26
|
+
* emits the generated artifact consumed by Astro pages, and rewrites
|
|
27
|
+
* the checked-in nginx marker file used by the website Docker image.
|
|
28
|
+
*
|
|
29
|
+
* Future companion subprocesses that introspect the marketing
|
|
30
|
+
* surface (locale-coverage audits, label-pack validation against
|
|
31
|
+
* authored locales, SEO-link sanity checks) will also read here —
|
|
32
|
+
* none exist today.
|
|
33
|
+
*
|
|
34
|
+
* # Why this is per-service website configuration
|
|
35
|
+
*
|
|
36
|
+
* Co-locating the schema with the engine package that owns its semantics
|
|
37
|
+
* keeps three concerns separate from application-root service declarations:
|
|
38
|
+
*
|
|
39
|
+
* 1. **Right reuse axis.** Marketing-site content (locales, SEO origins,
|
|
40
|
+
* future label-pack overrides, marketing-only CTAs) is conceptually
|
|
41
|
+
* different from saas-app-level service declarations (`path`,
|
|
42
|
+
* `serviceName`, `frontendType`, `sessionStorage`). The marketing
|
|
43
|
+
* config travels with the website service folder, NOT with the
|
|
44
|
+
* SaaS app root. Two unrelated lifecycles, two different review
|
|
45
|
+
* audiences.
|
|
46
|
+
* 2. **Engine boundary.** `platform-config-lib` no longer needs to
|
|
47
|
+
* depend on `@wildo-ai/presets-components-models` (the home of
|
|
48
|
+
* `AvailableLanguage`) just to type the marketing-site locale
|
|
49
|
+
* enum. Adding a new website primitive (a new SEO field, a new
|
|
50
|
+
* locale-pack id) belongs with the website package and its public
|
|
51
|
+
* configuration contract.
|
|
52
|
+
* 3. **Disambiguator hazard.** The previous root-level
|
|
53
|
+
* `marketingWebsite?` block silently assumed "one STATIC_WEBSITE
|
|
54
|
+
* service per SaaS app" — but nothing in the schema enforced it.
|
|
55
|
+
* Two STATIC_WEBSITE services would have silently shared one
|
|
56
|
+
* config block with no obvious "which one wins?" answer. The
|
|
57
|
+
* application configuration rejects more than one STATIC_WEBSITE (and
|
|
58
|
+
* TECH_DOC) service, so the per-service config-file lookup is
|
|
59
|
+
* deterministic by construction.
|
|
60
|
+
*
|
|
61
|
+
* # Schema decisions encoded here
|
|
62
|
+
*
|
|
63
|
+
* - `z.strictObject` so authoring an unknown key surfaces as a parse
|
|
64
|
+
* error at `defineWebsiteConfig` time instead of being silently
|
|
65
|
+
* dropped (same posture as `WildoSaasConfigSchema` and
|
|
66
|
+
* `WildoTechnicalDocConfigSchema`).
|
|
67
|
+
*
|
|
68
|
+
* - **No `enabled` field.** The gate is "is there a `wildo.website.config.ts`
|
|
69
|
+
* in a `frontendType: STATIC_WEBSITE` service folder?" — file
|
|
70
|
+
* presence under a STATIC_WEBSITE service IS the enable signal.
|
|
71
|
+
* The Astro build / dev companion / future audit CLI requires the
|
|
72
|
+
* file when it walks a STATIC_WEBSITE service folder, so a
|
|
73
|
+
* half-authored config simply cannot exist (the loader fails
|
|
74
|
+
* closed). Removing the boolean closes a foot-gun: an authored
|
|
75
|
+
* `enabled: false` block was previously a silent way to keep the
|
|
76
|
+
* config "warm" without shipping anything, and consumers had to
|
|
77
|
+
* remember to read the flag in addition to the file presence — a
|
|
78
|
+
* future read-the-config / forget-the-flag bug waiting to happen.
|
|
79
|
+
* A separate `services.<key>.enabled` pattern (orthogonal to
|
|
80
|
+
* marketing-website) is the right place for any "temporarily
|
|
81
|
+
* disable this service" story (deferred — see DASHBOARD).
|
|
82
|
+
*
|
|
83
|
+
* - URL fields are `z.url()` (Zod 4 native validator) so we get
|
|
84
|
+
* protocol + host parsing for free, matching the rest of the
|
|
85
|
+
* codebase.
|
|
86
|
+
*
|
|
87
|
+
* - `csp?` is typed against `WildoCspConfigSchema` from
|
|
88
|
+
* `@wildo-ai/platform-config-lib`, mirroring the docs service's
|
|
89
|
+
* `wildo.tech-doc.config.ts` contract. The parsed value is consumed by
|
|
90
|
+
* the shared `resolveCspPolicy()` / `emitCspArtifacts()` generator flow.
|
|
91
|
+
*
|
|
92
|
+
* - `defaultLocale ∈ locales` cross-check via `.superRefine` —
|
|
93
|
+
* keeping `defaultLocale` as a top-level field is product-explicit
|
|
94
|
+
* ("which locale URL is the canonical one") and decouples it from
|
|
95
|
+
* list ordering. Locale-path uniqueness AND locale-value
|
|
96
|
+
* uniqueness are also enforced — duplicate paths would silently
|
|
97
|
+
* overwrite each other in the Astro build and corrupt the sitemap;
|
|
98
|
+
* duplicate locales would produce duplicate hreflang entries.
|
|
99
|
+
*
|
|
100
|
+
* - Locale primitive is `AvailableLanguage`. The schema imports it
|
|
101
|
+
* from `@wildo-ai/presets-components-models/public-runtime` (the
|
|
102
|
+
* side-effect-free public-runtime seam), but **consumers authoring
|
|
103
|
+
* `wildo.website.config.ts` must import it from
|
|
104
|
+
* `@wildo-ai/saas-models/public-runtime`**. Both paths produce identical
|
|
105
|
+
* values; saas-models simply re-exports the presets atom. Picking
|
|
106
|
+
* one path consistently across consumer files prevents drift in
|
|
107
|
+
* docs / examples / search results. Values authored in
|
|
108
|
+
* `wildo.website.config.ts` are the lowercase BCP-47 strings
|
|
109
|
+
* (`'en'`, `'fr'`), NOT the UPPERCASE TS enum member names
|
|
110
|
+
* (`EN`, `FR`). `z.enum(AvailableLanguage)` parses against the
|
|
111
|
+
* enum VALUES, which is what we want.
|
|
112
|
+
*/
|
|
113
|
+
import { z } from 'zod';
|
|
114
|
+
import { AvailableLanguage } from '@wildo-ai/presets-components-models/public-runtime';
|
|
115
|
+
/**
|
|
116
|
+
* Per-locale configuration. Each authored locale gets one entry.
|
|
117
|
+
*
|
|
118
|
+
* `path` is the locale URL prefix segment (e.g. `'en'`, `'fr'`) — the
|
|
119
|
+
* Astro builder maps `/en/...` and `/fr/...` paths via this value. We
|
|
120
|
+
* do NOT auto-derive `path` from the locale string because some
|
|
121
|
+
* deployments may want a flat root for the default locale (`/...`
|
|
122
|
+
* instead of `/en/...`); keeping it explicit per locale leaves that
|
|
123
|
+
* door open without a config-shape change.
|
|
124
|
+
*/
|
|
125
|
+
export declare const WebsiteLocaleConfigSchema: z.ZodObject<{
|
|
126
|
+
locale: z.ZodEnum<typeof AvailableLanguage>;
|
|
127
|
+
path: z.ZodString;
|
|
128
|
+
}, z.core.$strict>;
|
|
129
|
+
export type WebsiteLocaleConfig = z.infer<typeof WebsiteLocaleConfigSchema>;
|
|
130
|
+
/**
|
|
131
|
+
* Per-service marketing-website configuration block.
|
|
132
|
+
*
|
|
133
|
+
* Authored as `export default defineWebsiteConfig({...})` in
|
|
134
|
+
* `<website-service-root>/wildo.website.config.ts`. The Astro
|
|
135
|
+
* build loads this file once at bootstrap via jiti and composes
|
|
136
|
+
* a `WebsiteSiteContext`; helpers consume that composed context rather than
|
|
137
|
+
* this raw value.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
|
|
142
|
+
* import { defineWebsiteConfig } from '@wildo-ai/saas-website';
|
|
143
|
+
*
|
|
144
|
+
* export default defineWebsiteConfig({
|
|
145
|
+
* publicOrigin: 'https://example.com',
|
|
146
|
+
* appOrigin: 'https://app.example.com',
|
|
147
|
+
* defaultLocale: AvailableLanguage.EN,
|
|
148
|
+
* locales: [
|
|
149
|
+
* { locale: AvailableLanguage.EN, path: 'en' },
|
|
150
|
+
* { locale: AvailableLanguage.FR, path: 'fr' },
|
|
151
|
+
* ],
|
|
152
|
+
* csp: { enabled: true, reportOnly: true },
|
|
153
|
+
* });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare const WildoMarketingWebsiteConfigSchema: z.ZodObject<{
|
|
157
|
+
/**
|
|
158
|
+
* Public origin of the marketing site (apex / www) — canonical
|
|
159
|
+
* input the Astro head renderer emits as absolute URLs
|
|
160
|
+
* (`<link rel="canonical">`, `og:url`, hreflang `href`,
|
|
161
|
+
* sitemap base, structured-data `mainEntityOfPage`).
|
|
162
|
+
*
|
|
163
|
+
* MUST NOT end with `/` — the head renderer composes
|
|
164
|
+
* `${publicOrigin}${routePath}` and a trailing slash would emit
|
|
165
|
+
* `https://example.com//`. Surface the constraint loudly here
|
|
166
|
+
* rather than silently corrupting URLs at build time.
|
|
167
|
+
*/
|
|
168
|
+
publicOrigin: z.ZodURL;
|
|
169
|
+
/**
|
|
170
|
+
* Origin of the SaaS application the marketing site links into
|
|
171
|
+
* (signup / login / pricing → checkout).
|
|
172
|
+
*
|
|
173
|
+
* **Today**: NO runtime consumer. The cross-origin handoff
|
|
174
|
+
* surface that will read it does not exist yet. Authored
|
|
175
|
+
* eagerly so the link-surface PR is a single framework-owned move
|
|
176
|
+
* — until then the field is exercised only by the schema
|
|
177
|
+
* parser.
|
|
178
|
+
*
|
|
179
|
+
* Distinct from the general `WildoSaasConfig.services.<name>`
|
|
180
|
+
* URL surface because the marketing site is a separate Astro
|
|
181
|
+
* project that does not load the full SaaS app graph at build
|
|
182
|
+
* time.
|
|
183
|
+
*/
|
|
184
|
+
appOrigin: z.ZodURL;
|
|
185
|
+
/**
|
|
186
|
+
* Backend API origin the marketing site POSTs to for anonymous
|
|
187
|
+
* captures (lead / draft-note forms) — e.g.
|
|
188
|
+
* `https://api.example.com`. Threaded through
|
|
189
|
+
* `WebsiteRuntimeContext.apiOrigin` into the React island that
|
|
190
|
+
* constructs the `WebsiteAnonymousSessionClient`.
|
|
191
|
+
*
|
|
192
|
+
* OPTIONAL because a pure brochure site with no capture forms never
|
|
193
|
+
* talks to an API — only sites that host an anonymous-capture
|
|
194
|
+
* section need it. When such a section renders without a resolved
|
|
195
|
+
* `apiOrigin`, the section surfaces a loud misconfiguration rather
|
|
196
|
+
* than silently POSTing nowhere.
|
|
197
|
+
*
|
|
198
|
+
* MUST NOT end with `/` — the anon client composes
|
|
199
|
+
* `${apiOrigin}${MAIN_API_BASE_PATH}${path}` and a trailing slash
|
|
200
|
+
* would emit a doubled `//` in the request URL. (The client also
|
|
201
|
+
* defensively strips trailing slashes, but the refine surfaces the
|
|
202
|
+
* mistake at authoring time.)
|
|
203
|
+
*
|
|
204
|
+
* Distinct from `appOrigin` (the human-facing app the marketing CTAs
|
|
205
|
+
* link into) and `publicOrigin` (the marketing site's own canonical
|
|
206
|
+
* origin): this is the machine-to-machine API host.
|
|
207
|
+
*/
|
|
208
|
+
apiOrigin: z.ZodOptional<z.ZodURL>;
|
|
209
|
+
/**
|
|
210
|
+
* Optional Content-Security-Policy override for the marketing site.
|
|
211
|
+
* Consumed by the website-local `sync-nginx-csp` step, which emits
|
|
212
|
+
* the generated artifact for Astro page injection and rewrites the
|
|
213
|
+
* website nginx marker file from the same resolved output.
|
|
214
|
+
*/
|
|
215
|
+
csp: z.ZodOptional<z.ZodObject<{
|
|
216
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
217
|
+
reportOnly: z.ZodDefault<z.ZodBoolean>;
|
|
218
|
+
mergeStrategy: z.ZodDefault<z.ZodEnum<typeof import("@wildo-ai/platform-config-lib").CspMergeStrategy>>;
|
|
219
|
+
directives: z.ZodDefault<z.ZodObject<Record<"default-src" | "script-src" | "style-src" | "img-src" | "connect-src" | "font-src" | "frame-src" | "frame-ancestors" | "object-src" | "form-action" | "base-uri" | "manifest-src" | "media-src" | "worker-src", z.ZodOptional<z.ZodArray<z.ZodString>>>, z.core.$strict>>;
|
|
220
|
+
reportUri: z.ZodOptional<z.ZodString>;
|
|
221
|
+
}, z.core.$strict>>;
|
|
222
|
+
/**
|
|
223
|
+
* Default locale the marketing site serves for unmatched URLs
|
|
224
|
+
* and as the canonical hreflang signal. Must appear in
|
|
225
|
+
* `locales` below (cross-check enforced by the schema-level
|
|
226
|
+
* `.superRefine`).
|
|
227
|
+
*
|
|
228
|
+
* Read by `buildAstroI18nConfig(...)` (drives Astro's own
|
|
229
|
+
* `i18n.defaultLocale`) and by the head renderer (drives
|
|
230
|
+
* `<html lang="...">`, `og:locale`, hreflang `x-default`).
|
|
231
|
+
*/
|
|
232
|
+
defaultLocale: z.ZodEnum<typeof AvailableLanguage>;
|
|
233
|
+
/**
|
|
234
|
+
* Ordered list of locales the marketing site builds. Order is
|
|
235
|
+
* preserved (used by the language switcher rendering and by
|
|
236
|
+
* the sitemap emitter — when the sitemap surface lands).
|
|
237
|
+
*
|
|
238
|
+
* Read by `buildAstroI18nConfig(...)` (Astro `i18n.locales`),
|
|
239
|
+
* by `assertLabelPacksValid(...)` (one label pack per locale),
|
|
240
|
+
* by `loadWebsitePageRuntime(...)` (per-page locale resolution),
|
|
241
|
+
* and by the head renderer (hreflang alternates).
|
|
242
|
+
*/
|
|
243
|
+
locales: z.ZodArray<z.ZodObject<{
|
|
244
|
+
locale: z.ZodEnum<typeof AvailableLanguage>;
|
|
245
|
+
path: z.ZodString;
|
|
246
|
+
}, z.core.$strict>>;
|
|
247
|
+
}, z.core.$strict>;
|
|
248
|
+
export type WildoMarketingWebsiteConfig = z.infer<typeof WildoMarketingWebsiteConfigSchema>;
|
|
249
|
+
export type WildoMarketingWebsiteConfigInput = z.input<typeof WildoMarketingWebsiteConfigSchema>;
|
|
250
|
+
/** @wildo_source:part:end engine.saas-website.config */
|
|
251
|
+
//# sourceMappingURL=wildo-website-config.schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wildo-website-config.schemas.d.ts","sourceRoot":"","sources":["../../../../src/config/wildo-website-config.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AAEvF;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB;;;kBASpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,iCAAiC;IAE1C;;;;;;;;;;OAUG;;IASH;;;;;;;;;;;;;;OAcG;;IAGH;;;;;;;;;;;;;;;;;;;;;;OAsBG;;IAWH;;;;;OAKG;;;;;;;;IAGH;;;;;;;;;OASG;;IAGH;;;;;;;;;OASG;;;;;kBAqDH,CAAC;AACL,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,gCAAgC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AACjG,wDAAwD"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wildo_source:part:start engine.saas-website.config facet:layer:engine facet:family:marketing-website
|
|
3
|
+
*
|
|
4
|
+
* Per-service marketing-website configuration block, authored in
|
|
5
|
+
* the website service folder as `wildo.website.config.ts`.
|
|
6
|
+
*
|
|
7
|
+
* # What this file's loaded value is used for TODAY
|
|
8
|
+
*
|
|
9
|
+
* This schema is the single source of truth for the marketing-site routing
|
|
10
|
+
* and origin surface. Every consumer reads here:
|
|
11
|
+
*
|
|
12
|
+
* - `defaultLocale` + `locales` — read by `buildAstroI18nConfig(...)`,
|
|
13
|
+
* `assertLabelPacksValid(...)`, `loadWebsitePageRuntime(...)` and
|
|
14
|
+
* `renderWebsitePageHeadHtml(...)` via the shared `WebsiteSiteContext`.
|
|
15
|
+
* - `publicOrigin` — read by the head renderer to compose absolute
|
|
16
|
+
* `<link rel="canonical">`, `og:url`, hreflang `href` URLs and
|
|
17
|
+
* the structured-data `mainEntityOfPage`. The previous
|
|
18
|
+
* `WebsiteRootConfig.seo.siteOrigin` was deleted.
|
|
19
|
+
* - `appOrigin` — has NO runtime consumer today. The
|
|
20
|
+
* marketing → app handoff link surface that will read it does
|
|
21
|
+
* not exist yet. Authored eagerly so the link-surface PR is a
|
|
22
|
+
* single framework-owned move; until then the field is exercised
|
|
23
|
+
* only by the schema parser.
|
|
24
|
+
* - `csp?` — read by the website-local `sync-nginx-csp` build step,
|
|
25
|
+
* which merges the app-static posture with `infra.csp?.<serviceKey>`,
|
|
26
|
+
* emits the generated artifact consumed by Astro pages, and rewrites
|
|
27
|
+
* the checked-in nginx marker file used by the website Docker image.
|
|
28
|
+
*
|
|
29
|
+
* Future companion subprocesses that introspect the marketing
|
|
30
|
+
* surface (locale-coverage audits, label-pack validation against
|
|
31
|
+
* authored locales, SEO-link sanity checks) will also read here —
|
|
32
|
+
* none exist today.
|
|
33
|
+
*
|
|
34
|
+
* # Why this is per-service website configuration
|
|
35
|
+
*
|
|
36
|
+
* Co-locating the schema with the engine package that owns its semantics
|
|
37
|
+
* keeps three concerns separate from application-root service declarations:
|
|
38
|
+
*
|
|
39
|
+
* 1. **Right reuse axis.** Marketing-site content (locales, SEO origins,
|
|
40
|
+
* future label-pack overrides, marketing-only CTAs) is conceptually
|
|
41
|
+
* different from saas-app-level service declarations (`path`,
|
|
42
|
+
* `serviceName`, `frontendType`, `sessionStorage`). The marketing
|
|
43
|
+
* config travels with the website service folder, NOT with the
|
|
44
|
+
* SaaS app root. Two unrelated lifecycles, two different review
|
|
45
|
+
* audiences.
|
|
46
|
+
* 2. **Engine boundary.** `platform-config-lib` no longer needs to
|
|
47
|
+
* depend on `@wildo-ai/presets-components-models` (the home of
|
|
48
|
+
* `AvailableLanguage`) just to type the marketing-site locale
|
|
49
|
+
* enum. Adding a new website primitive (a new SEO field, a new
|
|
50
|
+
* locale-pack id) belongs with the website package and its public
|
|
51
|
+
* configuration contract.
|
|
52
|
+
* 3. **Disambiguator hazard.** The previous root-level
|
|
53
|
+
* `marketingWebsite?` block silently assumed "one STATIC_WEBSITE
|
|
54
|
+
* service per SaaS app" — but nothing in the schema enforced it.
|
|
55
|
+
* Two STATIC_WEBSITE services would have silently shared one
|
|
56
|
+
* config block with no obvious "which one wins?" answer. The
|
|
57
|
+
* application configuration rejects more than one STATIC_WEBSITE (and
|
|
58
|
+
* TECH_DOC) service, so the per-service config-file lookup is
|
|
59
|
+
* deterministic by construction.
|
|
60
|
+
*
|
|
61
|
+
* # Schema decisions encoded here
|
|
62
|
+
*
|
|
63
|
+
* - `z.strictObject` so authoring an unknown key surfaces as a parse
|
|
64
|
+
* error at `defineWebsiteConfig` time instead of being silently
|
|
65
|
+
* dropped (same posture as `WildoSaasConfigSchema` and
|
|
66
|
+
* `WildoTechnicalDocConfigSchema`).
|
|
67
|
+
*
|
|
68
|
+
* - **No `enabled` field.** The gate is "is there a `wildo.website.config.ts`
|
|
69
|
+
* in a `frontendType: STATIC_WEBSITE` service folder?" — file
|
|
70
|
+
* presence under a STATIC_WEBSITE service IS the enable signal.
|
|
71
|
+
* The Astro build / dev companion / future audit CLI requires the
|
|
72
|
+
* file when it walks a STATIC_WEBSITE service folder, so a
|
|
73
|
+
* half-authored config simply cannot exist (the loader fails
|
|
74
|
+
* closed). Removing the boolean closes a foot-gun: an authored
|
|
75
|
+
* `enabled: false` block was previously a silent way to keep the
|
|
76
|
+
* config "warm" without shipping anything, and consumers had to
|
|
77
|
+
* remember to read the flag in addition to the file presence — a
|
|
78
|
+
* future read-the-config / forget-the-flag bug waiting to happen.
|
|
79
|
+
* A separate `services.<key>.enabled` pattern (orthogonal to
|
|
80
|
+
* marketing-website) is the right place for any "temporarily
|
|
81
|
+
* disable this service" story (deferred — see DASHBOARD).
|
|
82
|
+
*
|
|
83
|
+
* - URL fields are `z.url()` (Zod 4 native validator) so we get
|
|
84
|
+
* protocol + host parsing for free, matching the rest of the
|
|
85
|
+
* codebase.
|
|
86
|
+
*
|
|
87
|
+
* - `csp?` is typed against `WildoCspConfigSchema` from
|
|
88
|
+
* `@wildo-ai/platform-config-lib`, mirroring the docs service's
|
|
89
|
+
* `wildo.tech-doc.config.ts` contract. The parsed value is consumed by
|
|
90
|
+
* the shared `resolveCspPolicy()` / `emitCspArtifacts()` generator flow.
|
|
91
|
+
*
|
|
92
|
+
* - `defaultLocale ∈ locales` cross-check via `.superRefine` —
|
|
93
|
+
* keeping `defaultLocale` as a top-level field is product-explicit
|
|
94
|
+
* ("which locale URL is the canonical one") and decouples it from
|
|
95
|
+
* list ordering. Locale-path uniqueness AND locale-value
|
|
96
|
+
* uniqueness are also enforced — duplicate paths would silently
|
|
97
|
+
* overwrite each other in the Astro build and corrupt the sitemap;
|
|
98
|
+
* duplicate locales would produce duplicate hreflang entries.
|
|
99
|
+
*
|
|
100
|
+
* - Locale primitive is `AvailableLanguage`. The schema imports it
|
|
101
|
+
* from `@wildo-ai/presets-components-models/public-runtime` (the
|
|
102
|
+
* side-effect-free public-runtime seam), but **consumers authoring
|
|
103
|
+
* `wildo.website.config.ts` must import it from
|
|
104
|
+
* `@wildo-ai/saas-models/public-runtime`**. Both paths produce identical
|
|
105
|
+
* values; saas-models simply re-exports the presets atom. Picking
|
|
106
|
+
* one path consistently across consumer files prevents drift in
|
|
107
|
+
* docs / examples / search results. Values authored in
|
|
108
|
+
* `wildo.website.config.ts` are the lowercase BCP-47 strings
|
|
109
|
+
* (`'en'`, `'fr'`), NOT the UPPERCASE TS enum member names
|
|
110
|
+
* (`EN`, `FR`). `z.enum(AvailableLanguage)` parses against the
|
|
111
|
+
* enum VALUES, which is what we want.
|
|
112
|
+
*/
|
|
113
|
+
import { WildoCspConfigSchema } from '@wildo-ai/platform-config-lib';
|
|
114
|
+
import { z } from 'zod';
|
|
115
|
+
import { AvailableLanguage } from '@wildo-ai/presets-components-models/public-runtime';
|
|
116
|
+
/**
|
|
117
|
+
* Per-locale configuration. Each authored locale gets one entry.
|
|
118
|
+
*
|
|
119
|
+
* `path` is the locale URL prefix segment (e.g. `'en'`, `'fr'`) — the
|
|
120
|
+
* Astro builder maps `/en/...` and `/fr/...` paths via this value. We
|
|
121
|
+
* do NOT auto-derive `path` from the locale string because some
|
|
122
|
+
* deployments may want a flat root for the default locale (`/...`
|
|
123
|
+
* instead of `/en/...`); keeping it explicit per locale leaves that
|
|
124
|
+
* door open without a config-shape change.
|
|
125
|
+
*/
|
|
126
|
+
export const WebsiteLocaleConfigSchema = z.strictObject({
|
|
127
|
+
locale: z.enum(AvailableLanguage),
|
|
128
|
+
path: z
|
|
129
|
+
.string()
|
|
130
|
+
.min(1)
|
|
131
|
+
.regex(/^[a-z0-9-]+$/, 'locale path must be lowercase alphanumeric with hyphens (becomes a URL segment)'),
|
|
132
|
+
});
|
|
133
|
+
/**
|
|
134
|
+
* Per-service marketing-website configuration block.
|
|
135
|
+
*
|
|
136
|
+
* Authored as `export default defineWebsiteConfig({...})` in
|
|
137
|
+
* `<website-service-root>/wildo.website.config.ts`. The Astro
|
|
138
|
+
* build loads this file once at bootstrap via jiti and composes
|
|
139
|
+
* a `WebsiteSiteContext`; helpers consume that composed context rather than
|
|
140
|
+
* this raw value.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
|
|
145
|
+
* import { defineWebsiteConfig } from '@wildo-ai/saas-website';
|
|
146
|
+
*
|
|
147
|
+
* export default defineWebsiteConfig({
|
|
148
|
+
* publicOrigin: 'https://example.com',
|
|
149
|
+
* appOrigin: 'https://app.example.com',
|
|
150
|
+
* defaultLocale: AvailableLanguage.EN,
|
|
151
|
+
* locales: [
|
|
152
|
+
* { locale: AvailableLanguage.EN, path: 'en' },
|
|
153
|
+
* { locale: AvailableLanguage.FR, path: 'fr' },
|
|
154
|
+
* ],
|
|
155
|
+
* csp: { enabled: true, reportOnly: true },
|
|
156
|
+
* });
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export const WildoMarketingWebsiteConfigSchema = z
|
|
160
|
+
.strictObject({
|
|
161
|
+
/**
|
|
162
|
+
* Public origin of the marketing site (apex / www) — canonical
|
|
163
|
+
* input the Astro head renderer emits as absolute URLs
|
|
164
|
+
* (`<link rel="canonical">`, `og:url`, hreflang `href`,
|
|
165
|
+
* sitemap base, structured-data `mainEntityOfPage`).
|
|
166
|
+
*
|
|
167
|
+
* MUST NOT end with `/` — the head renderer composes
|
|
168
|
+
* `${publicOrigin}${routePath}` and a trailing slash would emit
|
|
169
|
+
* `https://example.com//`. Surface the constraint loudly here
|
|
170
|
+
* rather than silently corrupting URLs at build time.
|
|
171
|
+
*/
|
|
172
|
+
publicOrigin: z
|
|
173
|
+
.url()
|
|
174
|
+
.refine((value) => !value.endsWith('/'), {
|
|
175
|
+
error: 'WildoMarketingWebsiteConfig.publicOrigin must NOT end with "/" (the head ' +
|
|
176
|
+
'renderer concatenates publicOrigin + routePath; trailing slash would emit "//")',
|
|
177
|
+
}),
|
|
178
|
+
/**
|
|
179
|
+
* Origin of the SaaS application the marketing site links into
|
|
180
|
+
* (signup / login / pricing → checkout).
|
|
181
|
+
*
|
|
182
|
+
* **Today**: NO runtime consumer. The cross-origin handoff
|
|
183
|
+
* surface that will read it does not exist yet. Authored
|
|
184
|
+
* eagerly so the link-surface PR is a single framework-owned move
|
|
185
|
+
* — until then the field is exercised only by the schema
|
|
186
|
+
* parser.
|
|
187
|
+
*
|
|
188
|
+
* Distinct from the general `WildoSaasConfig.services.<name>`
|
|
189
|
+
* URL surface because the marketing site is a separate Astro
|
|
190
|
+
* project that does not load the full SaaS app graph at build
|
|
191
|
+
* time.
|
|
192
|
+
*/
|
|
193
|
+
appOrigin: z.url(),
|
|
194
|
+
/**
|
|
195
|
+
* Backend API origin the marketing site POSTs to for anonymous
|
|
196
|
+
* captures (lead / draft-note forms) — e.g.
|
|
197
|
+
* `https://api.example.com`. Threaded through
|
|
198
|
+
* `WebsiteRuntimeContext.apiOrigin` into the React island that
|
|
199
|
+
* constructs the `WebsiteAnonymousSessionClient`.
|
|
200
|
+
*
|
|
201
|
+
* OPTIONAL because a pure brochure site with no capture forms never
|
|
202
|
+
* talks to an API — only sites that host an anonymous-capture
|
|
203
|
+
* section need it. When such a section renders without a resolved
|
|
204
|
+
* `apiOrigin`, the section surfaces a loud misconfiguration rather
|
|
205
|
+
* than silently POSTing nowhere.
|
|
206
|
+
*
|
|
207
|
+
* MUST NOT end with `/` — the anon client composes
|
|
208
|
+
* `${apiOrigin}${MAIN_API_BASE_PATH}${path}` and a trailing slash
|
|
209
|
+
* would emit a doubled `//` in the request URL. (The client also
|
|
210
|
+
* defensively strips trailing slashes, but the refine surfaces the
|
|
211
|
+
* mistake at authoring time.)
|
|
212
|
+
*
|
|
213
|
+
* Distinct from `appOrigin` (the human-facing app the marketing CTAs
|
|
214
|
+
* link into) and `publicOrigin` (the marketing site's own canonical
|
|
215
|
+
* origin): this is the machine-to-machine API host.
|
|
216
|
+
*/
|
|
217
|
+
apiOrigin: z
|
|
218
|
+
.url()
|
|
219
|
+
.refine((value) => !value.endsWith('/'), {
|
|
220
|
+
error: 'WildoMarketingWebsiteConfig.apiOrigin must NOT end with "/" (the anonymous ' +
|
|
221
|
+
'client concatenates apiOrigin + MAIN_API_BASE_PATH + path; a trailing slash ' +
|
|
222
|
+
'would emit "//")',
|
|
223
|
+
})
|
|
224
|
+
.optional(),
|
|
225
|
+
/**
|
|
226
|
+
* Optional Content-Security-Policy override for the marketing site.
|
|
227
|
+
* Consumed by the website-local `sync-nginx-csp` step, which emits
|
|
228
|
+
* the generated artifact for Astro page injection and rewrites the
|
|
229
|
+
* website nginx marker file from the same resolved output.
|
|
230
|
+
*/
|
|
231
|
+
csp: WildoCspConfigSchema.optional(),
|
|
232
|
+
/**
|
|
233
|
+
* Default locale the marketing site serves for unmatched URLs
|
|
234
|
+
* and as the canonical hreflang signal. Must appear in
|
|
235
|
+
* `locales` below (cross-check enforced by the schema-level
|
|
236
|
+
* `.superRefine`).
|
|
237
|
+
*
|
|
238
|
+
* Read by `buildAstroI18nConfig(...)` (drives Astro's own
|
|
239
|
+
* `i18n.defaultLocale`) and by the head renderer (drives
|
|
240
|
+
* `<html lang="...">`, `og:locale`, hreflang `x-default`).
|
|
241
|
+
*/
|
|
242
|
+
defaultLocale: z.enum(AvailableLanguage),
|
|
243
|
+
/**
|
|
244
|
+
* Ordered list of locales the marketing site builds. Order is
|
|
245
|
+
* preserved (used by the language switcher rendering and by
|
|
246
|
+
* the sitemap emitter — when the sitemap surface lands).
|
|
247
|
+
*
|
|
248
|
+
* Read by `buildAstroI18nConfig(...)` (Astro `i18n.locales`),
|
|
249
|
+
* by `assertLabelPacksValid(...)` (one label pack per locale),
|
|
250
|
+
* by `loadWebsitePageRuntime(...)` (per-page locale resolution),
|
|
251
|
+
* and by the head renderer (hreflang alternates).
|
|
252
|
+
*/
|
|
253
|
+
locales: z.array(WebsiteLocaleConfigSchema).min(1),
|
|
254
|
+
})
|
|
255
|
+
.superRefine((config, ctx) => {
|
|
256
|
+
const declaredLocales = new Set(config.locales.map((entry) => entry.locale));
|
|
257
|
+
if (!declaredLocales.has(config.defaultLocale)) {
|
|
258
|
+
ctx.addIssue({
|
|
259
|
+
code: 'custom',
|
|
260
|
+
path: ['defaultLocale'],
|
|
261
|
+
message: `defaultLocale '${config.defaultLocale}' is not declared in 'locales' ` +
|
|
262
|
+
`(declared: [${[...declaredLocales].join(', ')}]). The default locale ` +
|
|
263
|
+
`must be one of the locales the site actually builds.`,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
// Locale-path uniqueness: two entries collapsing to the same URL
|
|
267
|
+
// segment would silently overwrite each other in the Astro build
|
|
268
|
+
// and corrupt the sitemap. Fail closed at parse time.
|
|
269
|
+
const pathOwners = new Map();
|
|
270
|
+
for (const entry of config.locales) {
|
|
271
|
+
const previous = pathOwners.get(entry.path);
|
|
272
|
+
if (previous !== undefined) {
|
|
273
|
+
ctx.addIssue({
|
|
274
|
+
code: 'custom',
|
|
275
|
+
path: ['locales'],
|
|
276
|
+
message: `Locale path '${entry.path}' is declared by both '${previous}' and ` +
|
|
277
|
+
`'${entry.locale}'. Each locale must have a unique URL path segment.`,
|
|
278
|
+
});
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
pathOwners.set(entry.path, entry.locale);
|
|
282
|
+
}
|
|
283
|
+
// Locale-value uniqueness: same locale appearing twice would
|
|
284
|
+
// produce duplicate hreflang entries and ambiguous default-locale
|
|
285
|
+
// resolution.
|
|
286
|
+
const localeOwners = new Map();
|
|
287
|
+
for (const entry of config.locales) {
|
|
288
|
+
const previousPath = localeOwners.get(entry.locale);
|
|
289
|
+
if (previousPath !== undefined) {
|
|
290
|
+
ctx.addIssue({
|
|
291
|
+
code: 'custom',
|
|
292
|
+
path: ['locales'],
|
|
293
|
+
message: `Locale '${entry.locale}' is declared twice (paths '${previousPath}' ` +
|
|
294
|
+
`and '${entry.path}'). Each locale must appear at most once.`,
|
|
295
|
+
});
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
localeOwners.set(entry.locale, entry.path);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
/** @wildo_source:part:end engine.saas-website.config */
|
|
302
|
+
//# sourceMappingURL=wildo-website-config.schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wildo-website-config.schemas.js","sourceRoot":"","sources":["../../../../src/config/wildo-website-config.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,MAAM,oDAAoD,CAAC;AAEvF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,YAAY,CAAC;IACtD,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACjC,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,KAAK,CACJ,cAAc,EACd,iFAAiF,CAClF;CACJ,CAAC,CAAC;AAGH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC;KAC/C,YAAY,CAAC;IACZ;;;;;;;;;;OAUG;IACH,YAAY,EAAE,CAAC;SACZ,GAAG,EAAE;SACL,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACvC,KAAK,EACH,2EAA2E;YAC3E,iFAAiF;KACpF,CAAC;IAEJ;;;;;;;;;;;;;;OAcG;IACH,SAAS,EAAE,CAAC,CAAC,GAAG,EAAE;IAElB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAS,EAAE,CAAC;SACT,GAAG,EAAE;SACL,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACvC,KAAK,EACH,6EAA6E;YAC7E,8EAA8E;YAC9E,kBAAkB;KACrB,CAAC;SACD,QAAQ,EAAE;IAEb;;;;;OAKG;IACH,GAAG,EAAE,oBAAoB,CAAC,QAAQ,EAAE;IAEpC;;;;;;;;;OASG;IACH,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;IAExC;;;;;;;;;OASG;IACH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CACnD,CAAC;KACD,WAAW,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IAC3B,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7E,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,eAAe,CAAC;YACvB,OAAO,EACL,kBAAkB,MAAM,CAAC,aAAa,iCAAiC;gBACvE,eAAe,CAAC,GAAG,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB;gBACvE,sDAAsD;SACzD,CAAC,CAAC;IACL,CAAC;IAED,iEAAiE;IACjE,iEAAiE;IACjE,sDAAsD;IACtD,MAAM,UAAU,GAAG,IAAI,GAAG,EAA6B,CAAC;IACxD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,OAAO,EACL,gBAAgB,KAAK,CAAC,IAAI,0BAA0B,QAAQ,QAAQ;oBACpE,IAAI,KAAK,CAAC,MAAM,qDAAqD;aACxE,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,6DAA6D;IAC7D,kEAAkE;IAClE,cAAc;IACd,MAAM,YAAY,GAAG,IAAI,GAAG,EAA6B,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,OAAO,EACL,WAAW,KAAK,CAAC,MAAM,+BAA+B,YAAY,IAAI;oBACtE,QAAQ,KAAK,CAAC,IAAI,2CAA2C;aAChE,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC,CAAC,CAAC;AAGL,wDAAwD","sourcesContent":["/**\n * @wildo_source:part:start engine.saas-website.config facet:layer:engine facet:family:marketing-website\n *\n * Per-service marketing-website configuration block, authored in\n * the website service folder as `wildo.website.config.ts`.\n *\n * # What this file's loaded value is used for TODAY\n *\n * This schema is the single source of truth for the marketing-site routing\n * and origin surface. Every consumer reads here:\n *\n * - `defaultLocale` + `locales` — read by `buildAstroI18nConfig(...)`,\n * `assertLabelPacksValid(...)`, `loadWebsitePageRuntime(...)` and\n * `renderWebsitePageHeadHtml(...)` via the shared `WebsiteSiteContext`.\n * - `publicOrigin` — read by the head renderer to compose absolute\n * `<link rel=\"canonical\">`, `og:url`, hreflang `href` URLs and\n * the structured-data `mainEntityOfPage`. The previous\n * `WebsiteRootConfig.seo.siteOrigin` was deleted.\n * - `appOrigin` — has NO runtime consumer today. The\n * marketing → app handoff link surface that will read it does\n * not exist yet. Authored eagerly so the link-surface PR is a\n * single framework-owned move; until then the field is exercised\n * only by the schema parser.\n * - `csp?` — read by the website-local `sync-nginx-csp` build step,\n * which merges the app-static posture with `infra.csp?.<serviceKey>`,\n * emits the generated artifact consumed by Astro pages, and rewrites\n * the checked-in nginx marker file used by the website Docker image.\n *\n * Future companion subprocesses that introspect the marketing\n * surface (locale-coverage audits, label-pack validation against\n * authored locales, SEO-link sanity checks) will also read here —\n * none exist today.\n *\n * # Why this is per-service website configuration\n *\n * Co-locating the schema with the engine package that owns its semantics\n * keeps three concerns separate from application-root service declarations:\n *\n * 1. **Right reuse axis.** Marketing-site content (locales, SEO origins,\n * future label-pack overrides, marketing-only CTAs) is conceptually\n * different from saas-app-level service declarations (`path`,\n * `serviceName`, `frontendType`, `sessionStorage`). The marketing\n * config travels with the website service folder, NOT with the\n * SaaS app root. Two unrelated lifecycles, two different review\n * audiences.\n * 2. **Engine boundary.** `platform-config-lib` no longer needs to\n * depend on `@wildo-ai/presets-components-models` (the home of\n * `AvailableLanguage`) just to type the marketing-site locale\n * enum. Adding a new website primitive (a new SEO field, a new\n * locale-pack id) belongs with the website package and its public\n * configuration contract.\n * 3. **Disambiguator hazard.** The previous root-level\n * `marketingWebsite?` block silently assumed \"one STATIC_WEBSITE\n * service per SaaS app\" — but nothing in the schema enforced it.\n * Two STATIC_WEBSITE services would have silently shared one\n * config block with no obvious \"which one wins?\" answer. The\n * application configuration rejects more than one STATIC_WEBSITE (and\n * TECH_DOC) service, so the per-service config-file lookup is\n * deterministic by construction.\n *\n * # Schema decisions encoded here\n *\n * - `z.strictObject` so authoring an unknown key surfaces as a parse\n * error at `defineWebsiteConfig` time instead of being silently\n * dropped (same posture as `WildoSaasConfigSchema` and\n * `WildoTechnicalDocConfigSchema`).\n *\n * - **No `enabled` field.** The gate is \"is there a `wildo.website.config.ts`\n * in a `frontendType: STATIC_WEBSITE` service folder?\" — file\n * presence under a STATIC_WEBSITE service IS the enable signal.\n * The Astro build / dev companion / future audit CLI requires the\n * file when it walks a STATIC_WEBSITE service folder, so a\n * half-authored config simply cannot exist (the loader fails\n * closed). Removing the boolean closes a foot-gun: an authored\n * `enabled: false` block was previously a silent way to keep the\n * config \"warm\" without shipping anything, and consumers had to\n * remember to read the flag in addition to the file presence — a\n * future read-the-config / forget-the-flag bug waiting to happen.\n * A separate `services.<key>.enabled` pattern (orthogonal to\n * marketing-website) is the right place for any \"temporarily\n * disable this service\" story (deferred — see DASHBOARD).\n *\n * - URL fields are `z.url()` (Zod 4 native validator) so we get\n * protocol + host parsing for free, matching the rest of the\n * codebase.\n *\n * - `csp?` is typed against `WildoCspConfigSchema` from\n * `@wildo-ai/platform-config-lib`, mirroring the docs service's\n * `wildo.tech-doc.config.ts` contract. The parsed value is consumed by\n * the shared `resolveCspPolicy()` / `emitCspArtifacts()` generator flow.\n *\n * - `defaultLocale ∈ locales` cross-check via `.superRefine` —\n * keeping `defaultLocale` as a top-level field is product-explicit\n * (\"which locale URL is the canonical one\") and decouples it from\n * list ordering. Locale-path uniqueness AND locale-value\n * uniqueness are also enforced — duplicate paths would silently\n * overwrite each other in the Astro build and corrupt the sitemap;\n * duplicate locales would produce duplicate hreflang entries.\n *\n * - Locale primitive is `AvailableLanguage`. The schema imports it\n * from `@wildo-ai/presets-components-models/public-runtime` (the\n * side-effect-free public-runtime seam), but **consumers authoring\n * `wildo.website.config.ts` must import it from\n * `@wildo-ai/saas-models/public-runtime`**. Both paths produce identical\n * values; saas-models simply re-exports the presets atom. Picking\n * one path consistently across consumer files prevents drift in\n * docs / examples / search results. Values authored in\n * `wildo.website.config.ts` are the lowercase BCP-47 strings\n * (`'en'`, `'fr'`), NOT the UPPERCASE TS enum member names\n * (`EN`, `FR`). `z.enum(AvailableLanguage)` parses against the\n * enum VALUES, which is what we want.\n */\n\nimport { WildoCspConfigSchema } from '@wildo-ai/platform-config-lib';\nimport { z } from 'zod';\nimport { AvailableLanguage } from '@wildo-ai/presets-components-models/public-runtime';\n\n/**\n * Per-locale configuration. Each authored locale gets one entry.\n *\n * `path` is the locale URL prefix segment (e.g. `'en'`, `'fr'`) — the\n * Astro builder maps `/en/...` and `/fr/...` paths via this value. We\n * do NOT auto-derive `path` from the locale string because some\n * deployments may want a flat root for the default locale (`/...`\n * instead of `/en/...`); keeping it explicit per locale leaves that\n * door open without a config-shape change.\n */\nexport const WebsiteLocaleConfigSchema = z.strictObject({\n locale: z.enum(AvailableLanguage),\n path: z\n .string()\n .min(1)\n .regex(\n /^[a-z0-9-]+$/,\n 'locale path must be lowercase alphanumeric with hyphens (becomes a URL segment)',\n ),\n});\nexport type WebsiteLocaleConfig = z.infer<typeof WebsiteLocaleConfigSchema>;\n\n/**\n * Per-service marketing-website configuration block.\n *\n * Authored as `export default defineWebsiteConfig({...})` in\n * `<website-service-root>/wildo.website.config.ts`. The Astro\n * build loads this file once at bootstrap via jiti and composes\n * a `WebsiteSiteContext`; helpers consume that composed context rather than\n * this raw value.\n *\n * @example\n * ```ts\n * import { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n * import { defineWebsiteConfig } from '@wildo-ai/saas-website';\n *\n * export default defineWebsiteConfig({\n * publicOrigin: 'https://example.com',\n * appOrigin: 'https://app.example.com',\n * defaultLocale: AvailableLanguage.EN,\n * locales: [\n * { locale: AvailableLanguage.EN, path: 'en' },\n * { locale: AvailableLanguage.FR, path: 'fr' },\n * ],\n * csp: { enabled: true, reportOnly: true },\n * });\n * ```\n */\nexport const WildoMarketingWebsiteConfigSchema = z\n .strictObject({\n /**\n * Public origin of the marketing site (apex / www) — canonical\n * input the Astro head renderer emits as absolute URLs\n * (`<link rel=\"canonical\">`, `og:url`, hreflang `href`,\n * sitemap base, structured-data `mainEntityOfPage`).\n *\n * MUST NOT end with `/` — the head renderer composes\n * `${publicOrigin}${routePath}` and a trailing slash would emit\n * `https://example.com//`. Surface the constraint loudly here\n * rather than silently corrupting URLs at build time.\n */\n publicOrigin: z\n .url()\n .refine((value) => !value.endsWith('/'), {\n error:\n 'WildoMarketingWebsiteConfig.publicOrigin must NOT end with \"/\" (the head ' +\n 'renderer concatenates publicOrigin + routePath; trailing slash would emit \"//\")',\n }),\n\n /**\n * Origin of the SaaS application the marketing site links into\n * (signup / login / pricing → checkout).\n *\n * **Today**: NO runtime consumer. The cross-origin handoff\n * surface that will read it does not exist yet. Authored\n * eagerly so the link-surface PR is a single framework-owned move\n * — until then the field is exercised only by the schema\n * parser.\n *\n * Distinct from the general `WildoSaasConfig.services.<name>`\n * URL surface because the marketing site is a separate Astro\n * project that does not load the full SaaS app graph at build\n * time.\n */\n appOrigin: z.url(),\n\n /**\n * Backend API origin the marketing site POSTs to for anonymous\n * captures (lead / draft-note forms) — e.g.\n * `https://api.example.com`. Threaded through\n * `WebsiteRuntimeContext.apiOrigin` into the React island that\n * constructs the `WebsiteAnonymousSessionClient`.\n *\n * OPTIONAL because a pure brochure site with no capture forms never\n * talks to an API — only sites that host an anonymous-capture\n * section need it. When such a section renders without a resolved\n * `apiOrigin`, the section surfaces a loud misconfiguration rather\n * than silently POSTing nowhere.\n *\n * MUST NOT end with `/` — the anon client composes\n * `${apiOrigin}${MAIN_API_BASE_PATH}${path}` and a trailing slash\n * would emit a doubled `//` in the request URL. (The client also\n * defensively strips trailing slashes, but the refine surfaces the\n * mistake at authoring time.)\n *\n * Distinct from `appOrigin` (the human-facing app the marketing CTAs\n * link into) and `publicOrigin` (the marketing site's own canonical\n * origin): this is the machine-to-machine API host.\n */\n apiOrigin: z\n .url()\n .refine((value) => !value.endsWith('/'), {\n error:\n 'WildoMarketingWebsiteConfig.apiOrigin must NOT end with \"/\" (the anonymous ' +\n 'client concatenates apiOrigin + MAIN_API_BASE_PATH + path; a trailing slash ' +\n 'would emit \"//\")',\n })\n .optional(),\n\n /**\n * Optional Content-Security-Policy override for the marketing site.\n * Consumed by the website-local `sync-nginx-csp` step, which emits\n * the generated artifact for Astro page injection and rewrites the\n * website nginx marker file from the same resolved output.\n */\n csp: WildoCspConfigSchema.optional(),\n\n /**\n * Default locale the marketing site serves for unmatched URLs\n * and as the canonical hreflang signal. Must appear in\n * `locales` below (cross-check enforced by the schema-level\n * `.superRefine`).\n *\n * Read by `buildAstroI18nConfig(...)` (drives Astro's own\n * `i18n.defaultLocale`) and by the head renderer (drives\n * `<html lang=\"...\">`, `og:locale`, hreflang `x-default`).\n */\n defaultLocale: z.enum(AvailableLanguage),\n\n /**\n * Ordered list of locales the marketing site builds. Order is\n * preserved (used by the language switcher rendering and by\n * the sitemap emitter — when the sitemap surface lands).\n *\n * Read by `buildAstroI18nConfig(...)` (Astro `i18n.locales`),\n * by `assertLabelPacksValid(...)` (one label pack per locale),\n * by `loadWebsitePageRuntime(...)` (per-page locale resolution),\n * and by the head renderer (hreflang alternates).\n */\n locales: z.array(WebsiteLocaleConfigSchema).min(1),\n })\n .superRefine((config, ctx) => {\n const declaredLocales = new Set(config.locales.map((entry) => entry.locale));\n if (!declaredLocales.has(config.defaultLocale)) {\n ctx.addIssue({\n code: 'custom',\n path: ['defaultLocale'],\n message:\n `defaultLocale '${config.defaultLocale}' is not declared in 'locales' ` +\n `(declared: [${[...declaredLocales].join(', ')}]). The default locale ` +\n `must be one of the locales the site actually builds.`,\n });\n }\n\n // Locale-path uniqueness: two entries collapsing to the same URL\n // segment would silently overwrite each other in the Astro build\n // and corrupt the sitemap. Fail closed at parse time.\n const pathOwners = new Map<string, AvailableLanguage>();\n for (const entry of config.locales) {\n const previous = pathOwners.get(entry.path);\n if (previous !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['locales'],\n message:\n `Locale path '${entry.path}' is declared by both '${previous}' and ` +\n `'${entry.locale}'. Each locale must have a unique URL path segment.`,\n });\n continue;\n }\n pathOwners.set(entry.path, entry.locale);\n }\n\n // Locale-value uniqueness: same locale appearing twice would\n // produce duplicate hreflang entries and ambiguous default-locale\n // resolution.\n const localeOwners = new Map<AvailableLanguage, string>();\n for (const entry of config.locales) {\n const previousPath = localeOwners.get(entry.locale);\n if (previousPath !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['locales'],\n message:\n `Locale '${entry.locale}' is declared twice (paths '${previousPath}' ` +\n `and '${entry.path}'). Each locale must appear at most once.`,\n });\n continue;\n }\n localeOwners.set(entry.locale, entry.path);\n }\n });\nexport type WildoMarketingWebsiteConfig = z.infer<typeof WildoMarketingWebsiteConfigSchema>;\nexport type WildoMarketingWebsiteConfigInput = z.input<typeof WildoMarketingWebsiteConfigSchema>;\n/** @wildo_source:part:end engine.saas-website.config */\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wildo-package @wildo-ai/saas-website (Node-only config-loader entry)
|
|
3
|
+
*
|
|
4
|
+
* Dedicated subpath for the per-service `wildo.website.config.ts`
|
|
5
|
+
* loader (`saas-website-slice.md` Step 7.5 — mirrors the
|
|
6
|
+
* `saas-technical-doc.md` Step 7.5 ship). Consumers:
|
|
7
|
+
*
|
|
8
|
+
* - `examples/wonder-todos/website/astro.config.ts` — wires the
|
|
9
|
+
* loader into the Astro build's `i18n` config.
|
|
10
|
+
* - Future companion subprocesses that introspect the marketing
|
|
11
|
+
* surface (locale coverage audits, SEO-link sanity checks).
|
|
12
|
+
*
|
|
13
|
+
* @wildo-boundary
|
|
14
|
+
* This entry is on the **Node-only** side of the bundle boundary.
|
|
15
|
+
* It re-exports `loadWebsiteConfig` (which transitively pulls
|
|
16
|
+
* `loadConfig` from `@wildo-ai/platform-config-lib` — jiti-backed,
|
|
17
|
+
* uses `node:fs` / `node:path` / `node:crypto`) and is therefore
|
|
18
|
+
* carved into its own package subpath so it cannot leak into the
|
|
19
|
+
* browser-side React-island bundle.
|
|
20
|
+
*
|
|
21
|
+
* Enforced by `engine/saas-website/src/__tests__/bundle-isolation.test.ts`:
|
|
22
|
+
* - This file IS in `ENTRY_FILES` (so the closure walker covers it).
|
|
23
|
+
* - `@wildo-ai/platform-config-lib` is in `ALLOWED_BARE_SPECIFIERS`
|
|
24
|
+
* AND `SSR_ONLY_BARE_SPECIFIERS`, which together prove the
|
|
25
|
+
* specifier reaches the closure ONLY through this entry — never
|
|
26
|
+
* through `index.ts`, `companion-exports.ts`, or `astro-island.ts`.
|
|
27
|
+
*
|
|
28
|
+
* Re-exporting `loadWebsiteConfig` from `index.ts` would (a) drag
|
|
29
|
+
* `@wildo-ai/platform-config-lib`'s heavy graph into the marketing-
|
|
30
|
+
* site browser bundle and (b) trip the SSR-tier negative test
|
|
31
|
+
* above — both fail-loud signals that this subpath separation
|
|
32
|
+
* exists for a reason.
|
|
33
|
+
*/
|
|
34
|
+
export * from './config/load-website-config';
|
|
35
|
+
//# sourceMappingURL=config-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/config-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,cAAc,8BAA8B,CAAC"}
|