@wildo-ai/saas-website 1.1.5 → 1.1.7
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/dist/esm/astro/blog-post-bridge.d.ts +9 -0
- package/dist/esm/astro/blog-post-bridge.d.ts.map +1 -1
- package/dist/esm/astro/blog-post-bridge.js +10 -5
- package/dist/esm/astro/blog-post-bridge.js.map +1 -1
- package/dist/esm/astro/blog-post-head.renderer.d.ts.map +1 -1
- package/dist/esm/astro/bridge-runtime.d.ts +25 -6
- package/dist/esm/astro/bridge-runtime.d.ts.map +1 -1
- package/dist/esm/astro/bridge-runtime.js +10 -6
- package/dist/esm/astro/bridge-runtime.js.map +1 -1
- package/dist/esm/astro/collect-expected-label-keys.d.ts.map +1 -1
- package/dist/esm/astro/headers-renderer.d.ts.map +1 -1
- package/dist/esm/astro/i18n-routing.helper.d.ts.map +1 -1
- package/dist/esm/astro/label-pack-loader.d.ts +4 -4
- package/dist/esm/astro/label-pack-loader.d.ts.map +1 -1
- package/dist/esm/astro/label-pack-loader.js +9 -4
- package/dist/esm/astro/label-pack-loader.js.map +1 -1
- package/dist/esm/astro/llms-txt-server.d.ts.map +1 -1
- package/dist/esm/astro/pick-labels-for-page.d.ts.map +1 -1
- package/dist/esm/astro/robots-renderer.d.ts +9 -8
- package/dist/esm/astro/robots-renderer.d.ts.map +1 -1
- package/dist/esm/astro/robots-renderer.js +15 -15
- package/dist/esm/astro/robots-renderer.js.map +1 -1
- package/dist/esm/astro/sitemap-coverage.d.ts.map +1 -1
- package/dist/esm/astro/structured-data.renderer.d.ts.map +1 -1
- package/dist/esm/astro/website-page-head.renderer.d.ts +1 -1
- package/dist/esm/astro/website-page-head.renderer.d.ts.map +1 -1
- package/dist/esm/astro/website-page-head.renderer.js +2 -1
- package/dist/esm/astro/website-page-head.renderer.js.map +1 -1
- package/dist/esm/astro/website-page-runtime.helper.d.ts.map +1 -1
- package/dist/esm/astro/website-site-context.d.ts +5 -2
- package/dist/esm/astro/website-site-context.d.ts.map +1 -1
- package/dist/esm/astro/website-site-context.js.map +1 -1
- package/dist/esm/astro-island.d.ts.map +1 -1
- package/dist/esm/astro.d.ts.map +1 -1
- package/dist/esm/companion-exports.d.ts +8 -15
- package/dist/esm/companion-exports.d.ts.map +1 -1
- package/dist/esm/companion-exports.js +8 -15
- package/dist/esm/companion-exports.js.map +1 -1
- package/dist/esm/components/low-level/WebsiteInternalButton.d.ts.map +1 -1
- package/dist/esm/components/low-level/WebsiteInternalLink.d.ts.map +1 -1
- package/dist/esm/config/define-website-config.d.ts +3 -2
- package/dist/esm/config/define-website-config.d.ts.map +1 -1
- package/dist/esm/config/define-website-config.js +10 -10
- package/dist/esm/config/define-website-config.js.map +1 -1
- package/dist/esm/config/index.d.ts.map +1 -1
- package/dist/esm/config/load-website-config.d.ts +6 -0
- package/dist/esm/config/load-website-config.d.ts.map +1 -1
- package/dist/esm/config/load-website-config.js +18 -9
- package/dist/esm/config/load-website-config.js.map +1 -1
- package/dist/esm/config/load-website-pricing-catalog.d.ts +13 -0
- package/dist/esm/config/load-website-pricing-catalog.d.ts.map +1 -0
- package/dist/esm/config/load-website-pricing-catalog.js +34 -0
- package/dist/esm/config/load-website-pricing-catalog.js.map +1 -0
- package/dist/esm/config/load-website-site-context.d.ts +53 -0
- package/dist/esm/config/load-website-site-context.d.ts.map +1 -0
- package/dist/esm/config/load-website-site-context.js +98 -0
- package/dist/esm/config/load-website-site-context.js.map +1 -0
- package/dist/esm/config/wildo-website-config.schemas.d.ts.map +1 -1
- package/dist/esm/config-loader.d.ts +2 -0
- package/dist/esm/config-loader.d.ts.map +1 -1
- package/dist/esm/config-loader.js +2 -0
- package/dist/esm/config-loader.js.map +1 -1
- package/dist/esm/core/anonymous-session/InboundContactForm.d.ts +26 -6
- package/dist/esm/core/anonymous-session/InboundContactForm.d.ts.map +1 -1
- package/dist/esm/core/anonymous-session/InboundContactForm.js +51 -33
- package/dist/esm/core/anonymous-session/InboundContactForm.js.map +1 -1
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts +11 -0
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts.map +1 -1
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js +15 -4
- package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js.map +1 -1
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts +14 -2
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts.map +1 -1
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.js +34 -4
- package/dist/esm/core/anonymous-session/website-anonymous-session-client.js.map +1 -1
- package/dist/esm/core/anonymous-session/website-submission-challenge.d.ts +36 -0
- package/dist/esm/core/anonymous-session/website-submission-challenge.d.ts.map +1 -0
- package/dist/esm/core/anonymous-session/website-submission-challenge.js +69 -0
- package/dist/esm/core/anonymous-session/website-submission-challenge.js.map +1 -0
- package/dist/esm/core/consent/WebsiteConsentBanner.d.ts.map +1 -1
- package/dist/esm/core/consent/WebsiteConsentBanner.js +11 -10
- package/dist/esm/core/consent/WebsiteConsentBanner.js.map +1 -1
- package/dist/esm/core/consent/WebsiteConsentContext.d.ts.map +1 -1
- package/dist/esm/core/consent/WebsiteConsentGate.d.ts.map +1 -1
- package/dist/esm/core/consent/useWebsiteConsent.d.ts.map +1 -1
- package/dist/esm/core/consent/website-consent-storage.d.ts.map +1 -1
- package/dist/esm/core/contexts/WebsitePageContext.d.ts.map +1 -1
- package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts +25 -5
- package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts.map +1 -1
- package/dist/esm/core/contexts/WebsiteRuntimeContext.js.map +1 -1
- package/dist/esm/core/contexts/WebsiteSectionContext.d.ts.map +1 -1
- package/dist/esm/core/contexts/useWebsitePage.d.ts.map +1 -1
- package/dist/esm/core/contexts/useWebsiteRuntime.d.ts +18 -0
- package/dist/esm/core/contexts/useWebsiteRuntime.d.ts.map +1 -1
- package/dist/esm/core/contexts/useWebsiteRuntime.js +28 -2
- package/dist/esm/core/contexts/useWebsiteRuntime.js.map +1 -1
- package/dist/esm/core/contexts/useWebsiteSection.d.ts.map +1 -1
- package/dist/esm/core/external-providers/WebsiteProviderComponent.d.ts +57 -0
- package/dist/esm/core/external-providers/WebsiteProviderComponent.d.ts.map +1 -0
- package/dist/esm/core/external-providers/WebsiteProviderComponent.js +56 -0
- package/dist/esm/core/external-providers/WebsiteProviderComponent.js.map +1 -0
- package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts.map +1 -1
- package/dist/esm/core/external-providers/provider-component-registry.website.d.ts +63 -0
- package/dist/esm/core/external-providers/provider-component-registry.website.d.ts.map +1 -0
- package/dist/esm/core/external-providers/provider-component-registry.website.js +68 -0
- package/dist/esm/core/external-providers/provider-component-registry.website.js.map +1 -0
- package/dist/esm/core/external-providers/useWebsiteProviderComponent.d.ts +36 -0
- package/dist/esm/core/external-providers/useWebsiteProviderComponent.d.ts.map +1 -0
- package/dist/esm/core/external-providers/useWebsiteProviderComponent.js +59 -0
- package/dist/esm/core/external-providers/useWebsiteProviderComponent.js.map +1 -0
- package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts +15 -5
- package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts.map +1 -1
- package/dist/esm/core/external-providers/useWebsiteProviderScripts.js +39 -24
- package/dist/esm/core/external-providers/useWebsiteProviderScripts.js.map +1 -1
- package/dist/esm/core/external-providers/website-provider-sdk-activations.d.ts +11 -0
- package/dist/esm/core/external-providers/website-provider-sdk-activations.d.ts.map +1 -0
- package/dist/esm/core/external-providers/website-provider-sdk-activations.js +14 -0
- package/dist/esm/core/external-providers/website-provider-sdk-activations.js.map +1 -0
- package/dist/esm/core/factories/define-website-page-manifest.d.ts.map +1 -1
- package/dist/esm/core/factories/define-website-section.d.ts +3 -3
- package/dist/esm/core/factories/define-website-section.d.ts.map +1 -1
- package/dist/esm/core/factories/define-website-section.js +3 -3
- package/dist/esm/core/factories/define-website-section.js.map +1 -1
- package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts.map +1 -1
- package/dist/esm/core/hooks/useWebsiteLabel.d.ts.map +1 -1
- package/dist/esm/core/layouts/WebsitePageLayout.d.ts.map +1 -1
- package/dist/esm/core/layouts/WebsitePageLayout.js +10 -7
- package/dist/esm/core/layouts/WebsitePageLayout.js.map +1 -1
- package/dist/esm/core/layouts/WebsiteSection.d.ts.map +1 -1
- package/dist/esm/core/routing/internal-routing.utils.d.ts.map +1 -1
- package/dist/esm/index.d.ts +30 -65
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +30 -65
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/mdx/BlogPost.d.ts.map +1 -1
- package/dist/esm/mdx/blog-post-collection.config.d.ts.map +1 -1
- package/dist/esm/mdx/blog-post-frontmatter-source.d.ts.map +1 -1
- package/dist/esm/mdx/index.d.ts.map +1 -1
- package/dist/esm/mdx.d.ts.map +1 -1
- package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts +35 -6
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js +39 -5
- package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js.map +1 -1
- package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.d.ts +32 -0
- package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.js +36 -0
- package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.js.map +1 -0
- package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts +9 -8
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js +8 -7
- package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js.map +1 -1
- package/dist/esm/schemas/refs/page-ref.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/refs/section-ref.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/sections/website-section-category.shared.d.ts.map +1 -1
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts +14 -7
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js +14 -7
- package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js.map +1 -1
- package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.d.ts +57 -0
- package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.d.ts.map +1 -0
- package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.js +76 -0
- package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.js.map +1 -0
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts +23 -0
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts.map +1 -1
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js +20 -2
- package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js.map +1 -1
- package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts +7 -5
- package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts.map +1 -1
- package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js.map +1 -1
- package/dist/esm/schemas/validators/label-pack.validator.d.ts.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/__tests__/bundle-isolation.test.ts +19 -22
- package/src/astro/__tests__/blog-post-bridge.test.tsx +18 -2
- package/src/astro/__tests__/bridge-runtime.test.tsx +21 -3
- package/src/astro/__tests__/label-pack-loader.test.ts +7 -4
- package/src/astro/blog-post-bridge.tsx +19 -5
- package/src/astro/bridge-runtime.tsx +35 -11
- package/src/astro/label-pack-loader.ts +8 -4
- package/src/astro/robots-renderer.ts +15 -15
- package/src/astro/website-page-head.renderer.ts +2 -2
- package/src/astro/website-site-context.ts +5 -2
- package/src/companion-exports.ts +8 -15
- package/src/config/__tests__/website-build-origins.test.ts +52 -0
- package/src/config/define-website-config.ts +10 -10
- package/src/config/load-website-config.ts +25 -14
- package/src/config/load-website-pricing-catalog.ts +52 -0
- package/src/config/load-website-site-context.ts +145 -0
- package/src/config-loader.ts +2 -0
- package/src/core/__tests__/contexts.test.tsx +20 -1
- package/src/core/__tests__/website-consent.test.tsx +2 -1
- package/src/core/anonymous-session/InboundContactForm.tsx +100 -45
- package/src/core/anonymous-session/__tests__/InboundContactForm.labels.test.tsx +108 -0
- package/src/core/anonymous-session/__tests__/InboundContactForm.submission-challenge.test.tsx +139 -0
- package/src/core/anonymous-session/inbound-contact-form.schema.ts +16 -4
- package/src/core/anonymous-session/website-anonymous-session-client.ts +39 -4
- package/src/core/anonymous-session/website-submission-challenge.ts +99 -0
- package/src/core/consent/WebsiteConsentBanner.tsx +11 -10
- package/src/core/contexts/WebsiteRuntimeContext.tsx +25 -5
- package/src/core/contexts/useWebsiteRuntime.ts +33 -2
- package/src/core/external-providers/WebsiteProviderComponent.tsx +153 -0
- package/src/core/external-providers/__tests__/frontend-provider-registry.website.test.ts +4 -0
- package/src/core/external-providers/__tests__/website-provider-component.test.tsx +238 -0
- package/src/core/external-providers/__tests__/website-provider-sdks.test.tsx +72 -0
- package/src/core/external-providers/provider-component-registry.website.ts +115 -0
- package/src/core/external-providers/useWebsiteProviderComponent.ts +76 -0
- package/src/core/external-providers/useWebsiteProviderScripts.ts +56 -37
- package/src/core/external-providers/website-provider-sdk-activations.ts +23 -0
- package/src/core/factories/define-website-section.ts +3 -3
- package/src/core/layouts/WebsitePageLayout.tsx +11 -6
- package/src/index.ts +31 -65
- package/src/schemas/__tests__/design-token-css-property.test.ts +72 -0
- package/src/schemas/__tests__/pricing-offer-structured-data.test.ts +122 -0
- package/src/schemas/__tests__/structured-data-reconciliation.test.ts +35 -0
- package/src/schemas/design-tokens/website-design-tokens.shared.schemas.ts +45 -5
- package/src/schemas/label-keys/__tests__/scaffolded-consent-label-pack.parity.test.ts +12 -0
- package/src/schemas/label-keys/website-provider-component-label-keys.schemas.ts +40 -0
- package/src/schemas/manifests/website-root-config.shared.schemas.ts +10 -9
- package/src/schemas/sections/website-section-definition.shared.schemas.ts +14 -7
- package/src/schemas/structured-data/pricing-offer-structured-data.shared.ts +144 -0
- package/src/schemas/structured-data/structured-data-reconciliation.shared.ts +46 -1
- package/src/schemas/structured-data/website-structured-data.shared.schemas.ts +7 -5
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ComponentType, FunctionComponent } from 'react';
|
|
2
2
|
import type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';
|
|
3
3
|
import type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
|
|
4
|
+
import type { ProviderComponentLoaderContribution } from '@wildo-ai/external-connectors-public/website';
|
|
4
5
|
import type { GeneratedWebsiteProviderModules } from '../core/external-providers/frontend-provider-registry.website';
|
|
5
6
|
import { BLOG_POST_PAGE_REF } from '../mdx/BlogPost';
|
|
6
7
|
import type { WebsiteMdxComponentsMap } from '../mdx/website-mdx-provider';
|
|
@@ -219,6 +220,14 @@ export interface CreateWebsiteBlogPostBridgeIslandDeps {
|
|
|
219
220
|
* Config sync emits a stable map, including an empty map when no providers are declared.
|
|
220
221
|
*/
|
|
221
222
|
frontendProviderModules?: GeneratedWebsiteProviderModules;
|
|
223
|
+
/**
|
|
224
|
+
* Loaders for provider components an ENGINE provider module declares but cannot import (#1703) —
|
|
225
|
+
* the website's facet-library door, the equivalent of the application's
|
|
226
|
+
* `contributeProviderComponentLoaders`. A provider PACKAGE that ships its own component needs
|
|
227
|
+
* nothing here: its module's `components[capability].load` is used. A DEPS entry because a loader
|
|
228
|
+
* is a function, and island props are serialized.
|
|
229
|
+
*/
|
|
230
|
+
providerComponentLoaders?: ReadonlyArray<ProviderComponentLoaderContribution>;
|
|
222
231
|
/**
|
|
223
232
|
* Per-slug map of compiled MDX bodies (each entry is a React
|
|
224
233
|
* component — the `.default` export of the MDX module). Typically
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blog-post-bridge.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"blog-post-bridge.d.ts","sourceRoot":"","sources":["../../../src/astro/blog-post-bridge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAa,MAAM,OAAO,CAAC;AAEzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAO9E,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,8CAA8C,CAAC;AACxG,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAY,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+DAA+D,CAAC;AACzG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iDAAiD,CAAC;AACvF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yDAAyD,CAAC;AACvG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuMG;AAEH;;;GAGG;AACH,MAAM,WAAW,qCAAqC;IACpD;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,+BAA+B,CAAC;IAC1D;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,aAAa,CAAC,mCAAmC,CAAC,CAAC;IAC9E;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D;;;;;OAKG;IACH,UAAU,EAAE,uBAAuB,CAAC;IACpC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,aAAa,CAAC;IACvC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,uBAAuB,CAAC;CACzC;AAED;;;;GAIG;AACH,MAAM,WAAW,gCAAgC;IAC/C;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;;OAIG;IACH,aAAa,EAAE,iBAAiB,CAAC;IACjC,2DAA2D;IAC3D,SAAS,EAAE,SAAS,CAAC;IACrB,iEAAiE;IACjE,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;OAKG;IACH,yBAAyB,EAAE,eAAe,CAAC;IAC3C,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C;0FACsF;IACtF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,qCAAqC,GAC1C,iBAAiB,CAAC,gCAAgC,CAAC,CA4ErD;AAED;;;;;;GAMG;AACH,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC9B,sDAAsD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { WebsiteRuntimeContextProvider, } from '../core/contexts/WebsiteRuntimeContext.js';
|
|
3
|
-
import {
|
|
3
|
+
import { useWebsiteProviderRegistries } from '../core/external-providers/provider-component-registry.website.js';
|
|
4
4
|
import { BlogPost, BLOG_POST_PAGE_REF } from '../mdx/BlogPost.js';
|
|
5
5
|
const EMPTY_MDX_COMPONENTS = Object.freeze({});
|
|
6
6
|
/**
|
|
@@ -32,6 +32,13 @@ export function createWebsiteBlogPostBridgeIsland(deps) {
|
|
|
32
32
|
`Verify the consumer's import.meta.glob('...content/blog/<locale>/*.mdx') ` +
|
|
33
33
|
`pattern matches the source file naming.`);
|
|
34
34
|
}
|
|
35
|
+
// After the missing-slug throw on purpose: that throw ends the render, so it never changes the
|
|
36
|
+
// hook order of a render that completes, and the build-time error stays reachable.
|
|
37
|
+
const { frontendProviderRegistry, providerComponentRegistry } = useWebsiteProviderRegistries({
|
|
38
|
+
frontendProviders,
|
|
39
|
+
frontendProviderModules: deps.frontendProviderModules,
|
|
40
|
+
providerComponentLoaders: deps.providerComponentLoaders,
|
|
41
|
+
});
|
|
35
42
|
const runtimeValue = {
|
|
36
43
|
locale,
|
|
37
44
|
defaultLocale,
|
|
@@ -41,10 +48,8 @@ export function createWebsiteBlogPostBridgeIsland(deps) {
|
|
|
41
48
|
skipToMainContentLabelKey,
|
|
42
49
|
appOrigin,
|
|
43
50
|
...(deps.consentBannerComponent !== undefined ? { consentBannerComponent: deps.consentBannerComponent } : {}),
|
|
44
|
-
frontendProviderRegistry
|
|
45
|
-
|
|
46
|
-
modules: deps.frontendProviderModules,
|
|
47
|
-
}),
|
|
51
|
+
frontendProviderRegistry,
|
|
52
|
+
providerComponentRegistry,
|
|
48
53
|
};
|
|
49
54
|
return (_jsx(WebsiteRuntimeContextProvider, { value: runtimeValue, children: _jsx(BlogPost, { rawFrontmatter: rawFrontmatter, source: source, mdxComponents: mdxComponents, children: _jsx(MdxBody, {}) }) }));
|
|
50
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blog-post-bridge.js","sourceRoot":"","sources":["../../../../src/astro/blog-post-bridge.tsx"],"names":[],"mappings":";AAKA,OAAO,EACL,6BAA6B,GAE9B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qCAAqC,EAAE,MAAM,+DAA+D,CAAC;AAEtH,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAiU/D,MAAM,oBAAoB,GAA4B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,UAAU,iCAAiC,CAC/C,IAA2C;IAE3C,MAAM,EACJ,aAAa,EACb,UAAU,EACV,aAAa,GAAG,oBAAoB,GACrC,GAAG,IAAI,CAAC;IAET,SAAS,2BAA2B,CAAC,EACnC,IAAI,EACJ,cAAc,EACd,MAAM,EACN,MAAM,EACN,aAAa,EACb,SAAS,EACT,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,SAAS,GACwB;QACjC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B;;;;;;;eAOG;YACH,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC7C,MAAM,KAAK,GACT,SAAS,CAAC,MAAM,IAAI,CAAC;gBACnB,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;gBACtB,CAAC,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,SAAS,CAAC,MAAM,SAAS,CAAC;YAC3E,MAAM,IAAI,KAAK,CACb,uDAAuD,IAAI,KAAK;gBAC9D,sDAAsD,KAAK,IAAI;gBAC/D,2EAA2E;gBAC3E,yCAAyC,CAC5C,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAA+B;YAC/C,MAAM;YACN,aAAa;YACb,SAAS;YACT,YAAY;YACZ,UAAU;YACV,yBAAyB;YACzB,SAAS;YACT,GAAG,CAAC,IAAI,CAAC,sBAAsB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7G,wBAAwB,EAAE,qCAAqC,CAAC;gBAC9D,OAAO,EAAE,iBAAiB;gBAC1B,OAAO,EAAE,IAAI,CAAC,uBAAuB;aACtC,CAAC;SACH,CAAC;QACF,OAAO,CACL,KAAC,6BAA6B,IAAC,KAAK,EAAE,YAAY,YAChD,KAAC,QAAQ,IACP,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,YAE5B,KAAC,OAAO,KAAG,GACF,GACmB,CACjC,CAAC;IACJ,CAAC;IACD,2BAA2B,CAAC,WAAW,GAAG,6BAA6B,CAAC;IAExE,OAAO,2BAA2B,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,OAAO,EAAE,kBAAkB,EAAE,CAAC","sourcesContent":["import type { ComponentType, FunctionComponent, ReactNode } from 'react';\n\nimport type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';\nimport type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n\nimport {\n WebsiteRuntimeContextProvider,\n type WebsiteRuntimeContextValue,\n} from '../core/contexts/WebsiteRuntimeContext';\nimport { createFrontendWebsiteProviderRegistry } from '../core/external-providers/frontend-provider-registry.website';\nimport type { GeneratedWebsiteProviderModules } from '../core/external-providers/frontend-provider-registry.website';\nimport { BlogPost, BLOG_POST_PAGE_REF } from '../mdx/BlogPost';\nimport type { WebsiteMdxComponentsMap } from '../mdx/website-mdx-provider';\nimport type { WebsiteDesignTokens } from '../schemas/design-tokens/website-design-tokens.shared.schemas';\nimport type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';\nimport type { WebsiteNavigationConfig } from '../schemas/manifests/website-root-config.shared.schemas';\nimport type { LabelPack } from '../schemas/validators/label-pack.validator';\n\n/**\n * @wildo_source:part:start saas.website.blog.bridge facet:layer:astro facet:family:website\n *\n * `createWebsiteBlogPostBridgeIsland(deps)` — factory that returns the\n * React island a consumer's per-locale `*.bridge.tsx` module exports for\n * mounting blog post pages. **Sibling** of `createWebsitePageBridgeIsland`\n * but adapted to the dynamic-routing constraint of the marketing-blog\n * adapter (Phase 7, D-6 = Option A).\n *\n * # Why blog posts cannot reuse the page bridge factory\n *\n * Three structural differences between marketing pages and blog posts\n * forced this dedicated factory shape:\n *\n * 1. **Per-slug body resolution.** A marketing page (`landing`,\n * `pricing`, etc.) has ONE `PageComponent` known at bridge-module\n * load time — the page bridge closes over it. A blog post's body\n * is the compiled MDX module, which varies per slug. Closing the\n * MDX body inside the factory at module load time would force one\n * bridge file per post (defeats Q16=B \"one MDX file per post\").\n * Instead, the factory closes over a `slugToMdxBody: Record<string,\n * ComponentType>` map — typically built from\n * `import.meta.glob('./content/blog/<locale>/*.mdx', { eager: true })`\n * on the consumer side — and the island accepts the active `slug`\n * as a primitive (JSON-serializable) prop.\n *\n * 2. **Layout already wrapped by `<BlogPost>`.** The page bridge wraps\n * its `PageComponent` in `<WebsitePageLayout pageRef={…}>`. The\n * `<BlogPost>` renderer ALREADY wraps its tree in\n * `<WebsitePageLayout pageRef={BLOG_POST_PAGE_REF}>` (the framework\n * reserves the page ref for the entire blog surface — see\n * `BLOG_POST_PAGE_REF` JSDoc in `BlogPost.tsx`). Reusing the page\n * bridge would double-wrap the layout. This factory instead\n * provides the runtime context and yields directly to `<BlogPost>`,\n * which owns the layout boundary itself.\n *\n * 3. **Astro's React-children context-propagation gap.** Astro\n * renders React component children INDEPENDENTLY of the parent\n * component when the boundary is `.astro` ↔ `.tsx`, even on the\n * server side. This means\n * `<BlogPost client:load><Content /></BlogPost>` written in an\n * `.astro` file would NOT propagate the `<WebsiteMdxProvider>`\n * context to the React components embedded inside `<Content />`\n * (the embedded `<CTASection />` would render WITHOUT the engine's\n * MDX provider scope; the `mdxComponents` map would be ignored).\n * Confirmed via the Astro docs, `@astrojs/react` integration\n * readme, and the documented \"Bridging two JSX runtimes\" gotcha.\n *\n * The fix: the entire blog post tree (BlogPost + MDX body +\n * embedded section components) lives inside ONE React island. The\n * MDX module is imported as a React component via Astro's\n * `import.meta.glob`, and the bridge renders it as JSX children\n * of `<BlogPost>` from INSIDE the island — no `.astro` ↔ `.tsx`\n * boundary is crossed during the body render.\n *\n * # Why hydration is required (vs. pure SSR)\n *\n * The `WebsiteRuntimeContextProvider` + `<BlogPost>` tree could in\n * theory render on the server only (no `client:*` directive). But:\n *\n * - Embedded sections (`<CTASection />`) typically have their own\n * interactivity (analytics handlers, A/B variant flippers, etc.)\n * that need React event listeners to attach client-side.\n * - The framework's `useWebsiteLabel(...)` hook chain assumes a live\n * React render tree — a pure-SSR tree would freeze label resolution\n * at build time, but any future client-driven label override (e.g.\n * a debug-mode label inspector) would silently break.\n *\n * The factory therefore returns an island that the consumer's `.astro`\n * mounts with `client:load` (or `client:idle` for posts known to be\n * non-interactive). The hydration cost is bounded by the per-locale MDX\n * glob — see \"Bundle scaling cliff\" below.\n *\n * # Bundle scaling cliff (deferred trade-off)\n *\n * `import.meta.glob({ eager: true })` materializes EVERY post's\n * compiled JS into the per-locale React-island bundle (~5–20 KB per\n * post depending on length and embedded section count). For the\n * Phase 7 dogfood (≤10 posts) this is fine and matches the documented\n * Astro+MDX pattern. Past ~50 posts this becomes a real budget concern.\n * The factory's API is forward-compatible with `{ eager: false }`\n * (lazy loaders): swap the consumer's glob to lazy mode and the\n * `slugToMdxBody[slug]` lookup returns a `() => Promise<{ default:\n * ComponentType }>`; wrap in `React.lazy()` + `<Suspense>` and the\n * factory call site stays unchanged. The migration is a consumer-side\n * change, not an engine API break.\n *\n * # Authoring pattern\n *\n * **Per-locale bridge file** (consumer side, ONE per locale):\n *\n * ```tsx\n * // examples/<app>/website/src/pages-impl/blog-post-en.bridge.tsx\n * import type { ComponentType } from 'react';\n *\n * import { createWebsiteBlogPostBridgeIsland }\n * from '@wildo-ai/saas-website/mdx';\n * import { BLOG_POST_MDX_COMPONENTS }\n * from './blog-post.mdx-components';\n * import { WEBSITE_ROOT_CONFIG } from '../website-root.config';\n *\n * // Static analyzable glob — Vite resolves at build time. The glob\n * // pattern MUST be a single string literal (no concatenation, no\n * // variable interpolation); Vite parses it AST-side. The `as` cast\n * // narrows the unknown record to the per-MDX-file shape Astro's mdx\n * // integration emits (default export = the compiled React component).\n * const rawModules = import.meta.glob('./content/blog/en/*.mdx', {\n * eager: true,\n * }) as Record<string, { default: ComponentType }>;\n *\n * const slugToMdxBody: Record<string, ComponentType> = {};\n * for (const [path, mod] of Object.entries(rawModules)) {\n * // './content/blog/en/why-context.mdx' -> 'why-context'\n * const slug = path.split('/').pop()!.replace(/\\.mdx$/, '');\n * slugToMdxBody[slug] = mod.default;\n * }\n *\n * export const BlogPostEnBridgeIsland =\n * createWebsiteBlogPostBridgeIsland({\n * slugToMdxBody,\n * navigation: WEBSITE_ROOT_CONFIG.navigation,\n * mdxComponents: BLOG_POST_MDX_COMPONENTS,\n * });\n * ```\n *\n * **Per-locale `[slug].astro` shell** (consumer side):\n *\n * ```astro\n * ---\n * import { getCollection } from 'astro:content';\n * import {\n * loadWebsitePageRuntime,\n * renderBlogPostHeadHtml,\n * renderWebsiteDesignTokensCss,\n * } from '@wildo-ai/saas-website/astro';\n * import { parseBlogPostFrontmatter } from '@wildo-ai/saas-website/mdx';\n * import { BlogPostEnBridgeIsland } from '@/pages-impl/blog-post-en.bridge';\n * import { BLOG_POST_PAGE_MANIFEST } from '@/pages-impl/blog-post.page';\n * import { WEBSITE_SITE_CONTEXT } from '@/website-site-context';\n *\n * export async function getStaticPaths() {\n * const posts = await getCollection('blog');\n * const defaultLocale = WEBSITE_SITE_CONTEXT.websiteConfig.defaultLocale;\n * return posts\n * .filter((p) => p.data.locale === defaultLocale)\n * .map((p) => ({ params: { slug: p.data.slug }, props: { entry: p } }));\n * }\n * const { entry } = Astro.props;\n *\n * const runtime = loadWebsitePageRuntime({\n * manifest: BLOG_POST_PAGE_MANIFEST,\n * siteContext: WEBSITE_SITE_CONTEXT,\n * labelPacksDir: './src/i18n',\n * astroLocale: Astro.currentLocale,\n * });\n * const frontmatter = parseBlogPostFrontmatter(entry.data, { source: entry.id });\n * const headHtml = renderBlogPostHeadHtml({\n * siteContext: WEBSITE_SITE_CONTEXT,\n * locale: runtime.locale,\n * routePath: `/blog/${frontmatter.slug}`,\n * frontmatter,\n * });\n * const designTokensCss = renderWebsiteDesignTokensCss(\n * WEBSITE_SITE_CONTEXT.rootConfig.designTokens,\n * );\n * ---\n * <!doctype html>\n * <html lang={runtime.locale}>\n * <head>\n * <meta charset=\"UTF-8\" />\n * <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n * <Fragment set:html={headHtml} />\n * <style set:html={designTokensCss}></style>\n * </head>\n * <body>\n * <BlogPostEnBridgeIsland\n * client:load\n * slug={frontmatter.slug}\n * rawFrontmatter={entry.data}\n * source={entry.id}\n * locale={runtime.locale}\n * defaultLocale={runtime.defaultLocale}\n * labelPack={runtime.labelPack}\n * designTokens={WEBSITE_SITE_CONTEXT.rootConfig.designTokens}\n * skipToMainContentLabelKey={\n * WEBSITE_SITE_CONTEXT.rootConfig.skipToMainContentLabelKey\n * }\n * />\n * </body>\n * </html>\n * ```\n *\n * # Boundary\n *\n * Strictly client-bundle-safe (re-exported from `mdx.ts` — NOT from\n * `astro-island.ts`, because this module hard-imports the MDX adapter\n * whose `@mdx-js/react` peer is optional; walked by the\n * bundle-isolation test). MUST stay clear of `node:*` and SSR-tier\n * specifiers.\n */\n\n/**\n * Module-load-time dependencies — closed over inside the returned\n * island. NEVER cross the hydration boundary as serialized props.\n */\nexport interface CreateWebsiteBlogPostBridgeIslandDeps {\n /**\n * Browser CODE imported by the consumer bridge, closed over at module load.\n * Never pass executable modules through Astro island props: those are serialized.\n * Config sync emits a stable map, including an empty map when no providers are declared.\n */\n frontendProviderModules?: GeneratedWebsiteProviderModules;\n /**\n * Per-slug map of compiled MDX bodies (each entry is a React\n * component — the `.default` export of the MDX module). Typically\n * built from `import.meta.glob('./content/blog/<locale>/*.mdx',\n * { eager: true })` with the path-key normalized to the slug. Closed\n * over at module-load time so the entire MDX body graph for the\n * locale lands inside ONE React island bundle (D-6 Option A) — this\n * is the structural fix for Astro's React-children context-propagation\n * gap (see file preamble).\n *\n * The map is read by `slugToMdxBody[props.slug]` at island render\n * time. A missing slug throws a clear framework-owned error (rather than\n * rendering an empty body silently); the consumer's `getStaticPaths`\n * is responsible for not emitting routes for slugs absent from the\n * map.\n *\n * **Why `ComponentType<any>` and not a stricter generic**: matches\n * the upstream `@mdx-js/react` convention (`MDXContent` from\n * `mdx/types` is parameterized over an open `MDXProps` shape). MDX\n * compilation hands the body a compiler-managed props object that\n * the type system cannot narrow at the framework boundary; using\n * `<any>` here keeps the consumer's\n * `import.meta.glob(...) as Record<string, { default: ComponentType }>`\n * cast assignable without an additional wrap/unwrap. Same posture\n * as `WebsiteMdxComponentsMap` (intentional symmetry).\n */\n slugToMdxBody: Readonly<Record<string, ComponentType<any>>>;\n /**\n * Site-wide navigation slot bindings (header / footer components).\n * Closed over here for the same reason as the page bridge: both\n * fields are `ComponentType` refs that cannot survive Astro's\n * serialization boundary.\n */\n navigation: WebsiteNavigationConfig;\n /**\n * Optional replacement for the framework's visitor consent panel (#534). Closed over for the same\n * reason as `navigation`. See `WebsiteRuntimeContextValue.consentBannerComponent`.\n */\n consentBannerComponent?: ComponentType;\n /**\n * MDX-tag → React-component map forwarded into `<BlogPost>` and\n * scoped via `<WebsiteMdxProvider>`. Typically a small object\n * literal exporting the consumer-authored business sections that\n * blog posts can embed (`<CTASection />`, etc.). Closed over here\n * (rather than passed as an island prop) because the values are\n * `ComponentType` refs.\n */\n mdxComponents?: WebsiteMdxComponentsMap;\n}\n\n/**\n * Hydration-boundary props — what the consumer's `.astro` page passes\n * to `<BlogPostEnBridgeIsland client:load ...{primitiveProps}/>`.\n * Every field is JSON-serializable.\n */\nexport interface WebsiteBlogPostBridgeIslandProps {\n /**\n * Slug of the post being rendered. Used by the island to look up\n * the matching MDX body in `deps.slugToMdxBody`. Carried as a plain\n * string (not the branded `BlogPostSlug`) because Astro's\n * serialization boundary unwraps brands.\n */\n slug: string;\n /**\n * Raw frontmatter object Astro hands the page from `entry.data`.\n * Forwarded straight to `<BlogPost>` for strict Zod-4 validation.\n * The bridge does NOT re-validate (single validation surface, owned\n * by the renderer).\n */\n rawFrontmatter: unknown;\n /**\n * Diagnostic source identifier echoed into validation errors —\n * typically `entry.id` from Astro's content collection. Optional\n * but recommended for actionable error messages.\n */\n source?: string;\n /** The locale Astro resolved (with default-locale fallback). */\n locale: AvailableLanguage;\n /**\n * The site's default locale. Required for `WebsiteInternalLink`\n * locale-prefix logic (mirrors the page bridge's `defaultLocale`\n * island prop).\n */\n defaultLocale: AvailableLanguage;\n /** Already-loaded + already-shape-validated label pack. */\n labelPack: LabelPack;\n /** Design-token bundle from `WebsiteRootConfig.designTokens`. */\n designTokens: WebsiteDesignTokens;\n /**\n * Fully-composed label key the framework reads to render the\n * \"Skip to main content\" affordance. Mirrors\n * `WebsiteRootConfig.skipToMainContentLabelKey`. Carried as a\n * primitive island prop for the same reason as the page bridge.\n */\n skipToMainContentLabelKey: WebsiteLabelKey;\n /** Optional website-facing public providers materialized at build time. */\n frontendProviders?: FrontendProvidersBlock;\n /** Product origin for navigation and call-to-action components inside the blog island.\n * Supplied by the Astro page from websiteConfig, just as on ordinary page bridges. */\n appOrigin?: string;\n}\n\nconst EMPTY_MDX_COMPONENTS: WebsiteMdxComponentsMap = Object.freeze({});\n\n/**\n * Returns a FUNCTION component, not the wider `ComponentType` union. The island IS a function\n * component — Astro hydrates nothing else here — and the union is not callable, so declaring it\n * cost callers the ability to invoke the island directly (which the bridge suites do, to observe\n * the synchronous throw for an unregistered slug without routing it through React's reconciler\n * error path). `FunctionComponent` is assignable to `ComponentType`, so no consumer narrows.\n */\nexport function createWebsiteBlogPostBridgeIsland(\n deps: CreateWebsiteBlogPostBridgeIslandDeps,\n): FunctionComponent<WebsiteBlogPostBridgeIslandProps> {\n const {\n slugToMdxBody,\n navigation,\n mdxComponents = EMPTY_MDX_COMPONENTS,\n } = deps;\n\n function WebsiteBlogPostBridgeIsland({\n slug,\n rawFrontmatter,\n source,\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n skipToMainContentLabelKey,\n frontendProviders,\n appOrigin,\n }: WebsiteBlogPostBridgeIslandProps): ReactNode {\n const MdxBody = slugToMdxBody[slug];\n if (MdxBody === undefined) {\n /**\n * Loud failure — the consumer's `getStaticPaths` is supposed to\n * only emit routes for slugs present in the MDX glob. A missing\n * lookup means the glob and the collection are out of sync; the\n * operator needs an actionable error string with the offending\n * slug + the keys that ARE present (truncated to keep the build\n * log readable).\n */\n const knownKeys = Object.keys(slugToMdxBody);\n const known =\n knownKeys.length <= 5\n ? knownKeys.join(', ')\n : `${knownKeys.slice(0, 5).join(', ')}, … (${knownKeys.length} total)`;\n throw new Error(\n `[blog-post-bridge] No MDX body registered for slug=\"${slug}\". ` +\n `The bridge factory's slugToMdxBody map only knows: ${known}. ` +\n `Verify the consumer's import.meta.glob('...content/blog/<locale>/*.mdx') ` +\n `pattern matches the source file naming.`,\n );\n }\n\n const runtimeValue: WebsiteRuntimeContextValue = {\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n navigation,\n skipToMainContentLabelKey,\n appOrigin,\n ...(deps.consentBannerComponent !== undefined ? { consentBannerComponent: deps.consentBannerComponent } : {}),\n frontendProviderRegistry: createFrontendWebsiteProviderRegistry({\n entries: frontendProviders,\n modules: deps.frontendProviderModules,\n }),\n };\n return (\n <WebsiteRuntimeContextProvider value={runtimeValue}>\n <BlogPost\n rawFrontmatter={rawFrontmatter}\n source={source}\n mdxComponents={mdxComponents}\n >\n <MdxBody />\n </BlogPost>\n </WebsiteRuntimeContextProvider>\n );\n }\n WebsiteBlogPostBridgeIsland.displayName = 'WebsiteBlogPostBridgeIsland';\n\n return WebsiteBlogPostBridgeIsland;\n}\n\n/**\n * Re-export of the framework-reserved blog-post page ref. Consumers\n * authoring `BLOG_POST_PAGE_MANIFEST` need this constant to satisfy\n * the manifest's `ref` field; re-exporting it from the bridge module\n * means the consumer's per-locale bridge file can grab both symbols\n * from the one `@wildo-ai/saas-website/mdx` import it already has.\n */\nexport { BLOG_POST_PAGE_REF };\n/** @wildo_source:part:end saas.website.blog.bridge */\n"]}
|
|
1
|
+
{"version":3,"file":"blog-post-bridge.js","sourceRoot":"","sources":["../../../src/astro/blog-post-bridge.tsx"],"names":[],"mappings":";AAKA,OAAO,EACL,6BAA6B,GAE9B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,4BAA4B,EAAE,MAAM,gEAAgE,CAAC;AAG9G,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAyU/D,MAAM,oBAAoB,GAA4B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAExE;;;;;;GAMG;AACH,MAAM,UAAU,iCAAiC,CAC/C,IAA2C;IAE3C,MAAM,EACJ,aAAa,EACb,UAAU,EACV,aAAa,GAAG,oBAAoB,GACrC,GAAG,IAAI,CAAC;IAET,SAAS,2BAA2B,CAAC,EACnC,IAAI,EACJ,cAAc,EACd,MAAM,EACN,MAAM,EACN,aAAa,EACb,SAAS,EACT,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,SAAS,GACwB;QACjC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B;;;;;;;eAOG;YACH,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC7C,MAAM,KAAK,GACT,SAAS,CAAC,MAAM,IAAI,CAAC;gBACnB,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;gBACtB,CAAC,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,SAAS,CAAC,MAAM,SAAS,CAAC;YAC3E,MAAM,IAAI,KAAK,CACb,uDAAuD,IAAI,KAAK;gBAC9D,sDAAsD,KAAK,IAAI;gBAC/D,2EAA2E;gBAC3E,yCAAyC,CAC5C,CAAC;QACJ,CAAC;QAED,+FAA+F;QAC/F,mFAAmF;QACnF,MAAM,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,GAAG,4BAA4B,CAAC;YAC3F,iBAAiB;YACjB,uBAAuB,EAAE,IAAI,CAAC,uBAAuB;YACrD,wBAAwB,EAAE,IAAI,CAAC,wBAAwB;SACxD,CAAC,CAAC;QACH,MAAM,YAAY,GAA+B;YAC/C,MAAM;YACN,aAAa;YACb,SAAS;YACT,YAAY;YACZ,UAAU;YACV,yBAAyB;YACzB,SAAS;YACT,GAAG,CAAC,IAAI,CAAC,sBAAsB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7G,wBAAwB;YACxB,yBAAyB;SAC1B,CAAC;QACF,OAAO,CACL,KAAC,6BAA6B,IAAC,KAAK,EAAE,YAAY,YAChD,KAAC,QAAQ,IACP,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,YAE5B,KAAC,OAAO,KAAG,GACF,GACmB,CACjC,CAAC;IACJ,CAAC;IACD,2BAA2B,CAAC,WAAW,GAAG,6BAA6B,CAAC;IAExE,OAAO,2BAA2B,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,OAAO,EAAE,kBAAkB,EAAE,CAAC","sourcesContent":["import type { ComponentType, FunctionComponent, ReactNode } from 'react';\n\nimport type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';\nimport type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n\nimport {\n WebsiteRuntimeContextProvider,\n type WebsiteRuntimeContextValue,\n} from '../core/contexts/WebsiteRuntimeContext';\nimport { useWebsiteProviderRegistries } from '../core/external-providers/provider-component-registry.website';\nimport type { ProviderComponentLoaderContribution } from '@wildo-ai/external-connectors-public/website';\nimport type { GeneratedWebsiteProviderModules } from '../core/external-providers/frontend-provider-registry.website';\nimport { BlogPost, BLOG_POST_PAGE_REF } from '../mdx/BlogPost';\nimport type { WebsiteMdxComponentsMap } from '../mdx/website-mdx-provider';\nimport type { WebsiteDesignTokens } from '../schemas/design-tokens/website-design-tokens.shared.schemas';\nimport type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';\nimport type { WebsiteNavigationConfig } from '../schemas/manifests/website-root-config.shared.schemas';\nimport type { LabelPack } from '../schemas/validators/label-pack.validator';\n\n/**\n * @wildo_source:part:start saas.website.blog.bridge facet:layer:astro facet:family:website\n *\n * `createWebsiteBlogPostBridgeIsland(deps)` — factory that returns the\n * React island a consumer's per-locale `*.bridge.tsx` module exports for\n * mounting blog post pages. **Sibling** of `createWebsitePageBridgeIsland`\n * but adapted to the dynamic-routing constraint of the marketing-blog\n * adapter (Phase 7, D-6 = Option A).\n *\n * # Why blog posts cannot reuse the page bridge factory\n *\n * Three structural differences between marketing pages and blog posts\n * forced this dedicated factory shape:\n *\n * 1. **Per-slug body resolution.** A marketing page (`landing`,\n * `pricing`, etc.) has ONE `PageComponent` known at bridge-module\n * load time — the page bridge closes over it. A blog post's body\n * is the compiled MDX module, which varies per slug. Closing the\n * MDX body inside the factory at module load time would force one\n * bridge file per post (defeats Q16=B \"one MDX file per post\").\n * Instead, the factory closes over a `slugToMdxBody: Record<string,\n * ComponentType>` map — typically built from\n * `import.meta.glob('./content/blog/<locale>/*.mdx', { eager: true })`\n * on the consumer side — and the island accepts the active `slug`\n * as a primitive (JSON-serializable) prop.\n *\n * 2. **Layout already wrapped by `<BlogPost>`.** The page bridge wraps\n * its `PageComponent` in `<WebsitePageLayout pageRef={…}>`. The\n * `<BlogPost>` renderer ALREADY wraps its tree in\n * `<WebsitePageLayout pageRef={BLOG_POST_PAGE_REF}>` (the framework\n * reserves the page ref for the entire blog surface — see\n * `BLOG_POST_PAGE_REF` JSDoc in `BlogPost.tsx`). Reusing the page\n * bridge would double-wrap the layout. This factory instead\n * provides the runtime context and yields directly to `<BlogPost>`,\n * which owns the layout boundary itself.\n *\n * 3. **Astro's React-children context-propagation gap.** Astro\n * renders React component children INDEPENDENTLY of the parent\n * component when the boundary is `.astro` ↔ `.tsx`, even on the\n * server side. This means\n * `<BlogPost client:load><Content /></BlogPost>` written in an\n * `.astro` file would NOT propagate the `<WebsiteMdxProvider>`\n * context to the React components embedded inside `<Content />`\n * (the embedded `<CTASection />` would render WITHOUT the engine's\n * MDX provider scope; the `mdxComponents` map would be ignored).\n * Confirmed via the Astro docs, `@astrojs/react` integration\n * readme, and the documented \"Bridging two JSX runtimes\" gotcha.\n *\n * The fix: the entire blog post tree (BlogPost + MDX body +\n * embedded section components) lives inside ONE React island. The\n * MDX module is imported as a React component via Astro's\n * `import.meta.glob`, and the bridge renders it as JSX children\n * of `<BlogPost>` from INSIDE the island — no `.astro` ↔ `.tsx`\n * boundary is crossed during the body render.\n *\n * # Why hydration is required (vs. pure SSR)\n *\n * The `WebsiteRuntimeContextProvider` + `<BlogPost>` tree could in\n * theory render on the server only (no `client:*` directive). But:\n *\n * - Embedded sections (`<CTASection />`) typically have their own\n * interactivity (analytics handlers, A/B variant flippers, etc.)\n * that need React event listeners to attach client-side.\n * - The framework's `useWebsiteLabel(...)` hook chain assumes a live\n * React render tree — a pure-SSR tree would freeze label resolution\n * at build time, but any future client-driven label override (e.g.\n * a debug-mode label inspector) would silently break.\n *\n * The factory therefore returns an island that the consumer's `.astro`\n * mounts with `client:load` (or `client:idle` for posts known to be\n * non-interactive). The hydration cost is bounded by the per-locale MDX\n * glob — see \"Bundle scaling cliff\" below.\n *\n * # Bundle scaling cliff (deferred trade-off)\n *\n * `import.meta.glob({ eager: true })` materializes EVERY post's\n * compiled JS into the per-locale React-island bundle (~5–20 KB per\n * post depending on length and embedded section count). For the\n * Phase 7 dogfood (≤10 posts) this is fine and matches the documented\n * Astro+MDX pattern. Past ~50 posts this becomes a real budget concern.\n * The factory's API is forward-compatible with `{ eager: false }`\n * (lazy loaders): swap the consumer's glob to lazy mode and the\n * `slugToMdxBody[slug]` lookup returns a `() => Promise<{ default:\n * ComponentType }>`; wrap in `React.lazy()` + `<Suspense>` and the\n * factory call site stays unchanged. The migration is a consumer-side\n * change, not an engine API break.\n *\n * # Authoring pattern\n *\n * **Per-locale bridge file** (consumer side, ONE per locale):\n *\n * ```tsx\n * // examples/<app>/website/src/pages-impl/blog-post-en.bridge.tsx\n * import type { ComponentType } from 'react';\n *\n * import { createWebsiteBlogPostBridgeIsland }\n * from '@wildo-ai/saas-website/mdx';\n * import { BLOG_POST_MDX_COMPONENTS }\n * from './blog-post.mdx-components';\n * import { WEBSITE_ROOT_CONFIG } from '../website-root.config';\n *\n * // Static analyzable glob — Vite resolves at build time. The glob\n * // pattern MUST be a single string literal (no concatenation, no\n * // variable interpolation); Vite parses it AST-side. The `as` cast\n * // narrows the unknown record to the per-MDX-file shape Astro's mdx\n * // integration emits (default export = the compiled React component).\n * const rawModules = import.meta.glob('./content/blog/en/*.mdx', {\n * eager: true,\n * }) as Record<string, { default: ComponentType }>;\n *\n * const slugToMdxBody: Record<string, ComponentType> = {};\n * for (const [path, mod] of Object.entries(rawModules)) {\n * // './content/blog/en/why-context.mdx' -> 'why-context'\n * const slug = path.split('/').pop()!.replace(/\\.mdx$/, '');\n * slugToMdxBody[slug] = mod.default;\n * }\n *\n * export const BlogPostEnBridgeIsland =\n * createWebsiteBlogPostBridgeIsland({\n * slugToMdxBody,\n * navigation: WEBSITE_ROOT_CONFIG.navigation,\n * mdxComponents: BLOG_POST_MDX_COMPONENTS,\n * });\n * ```\n *\n * **Per-locale `[slug].astro` shell** (consumer side):\n *\n * ```astro\n * ---\n * import { getCollection } from 'astro:content';\n * import {\n * loadWebsitePageRuntime,\n * renderBlogPostHeadHtml,\n * renderWebsiteDesignTokensCss,\n * } from '@wildo-ai/saas-website/astro';\n * import { parseBlogPostFrontmatter } from '@wildo-ai/saas-website/mdx';\n * import { BlogPostEnBridgeIsland } from '@/pages-impl/blog-post-en.bridge';\n * import { BLOG_POST_PAGE_MANIFEST } from '@/pages-impl/blog-post.page';\n * import { WEBSITE_SITE_CONTEXT } from '@/website-site-context';\n *\n * export async function getStaticPaths() {\n * const posts = await getCollection('blog');\n * const defaultLocale = WEBSITE_SITE_CONTEXT.websiteConfig.defaultLocale;\n * return posts\n * .filter((p) => p.data.locale === defaultLocale)\n * .map((p) => ({ params: { slug: p.data.slug }, props: { entry: p } }));\n * }\n * const { entry } = Astro.props;\n *\n * const runtime = loadWebsitePageRuntime({\n * manifest: BLOG_POST_PAGE_MANIFEST,\n * siteContext: WEBSITE_SITE_CONTEXT,\n * labelPacksDir: './src/i18n',\n * astroLocale: Astro.currentLocale,\n * });\n * const frontmatter = parseBlogPostFrontmatter(entry.data, { source: entry.id });\n * const headHtml = renderBlogPostHeadHtml({\n * siteContext: WEBSITE_SITE_CONTEXT,\n * locale: runtime.locale,\n * routePath: `/blog/${frontmatter.slug}`,\n * frontmatter,\n * });\n * const designTokensCss = renderWebsiteDesignTokensCss(\n * WEBSITE_SITE_CONTEXT.rootConfig.designTokens,\n * );\n * ---\n * <!doctype html>\n * <html lang={runtime.locale}>\n * <head>\n * <meta charset=\"UTF-8\" />\n * <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\n * <Fragment set:html={headHtml} />\n * <style set:html={designTokensCss}></style>\n * </head>\n * <body>\n * <BlogPostEnBridgeIsland\n * client:load\n * slug={frontmatter.slug}\n * rawFrontmatter={entry.data}\n * source={entry.id}\n * locale={runtime.locale}\n * defaultLocale={runtime.defaultLocale}\n * labelPack={runtime.labelPack}\n * designTokens={WEBSITE_SITE_CONTEXT.rootConfig.designTokens}\n * skipToMainContentLabelKey={\n * WEBSITE_SITE_CONTEXT.rootConfig.skipToMainContentLabelKey\n * }\n * />\n * </body>\n * </html>\n * ```\n *\n * # Boundary\n *\n * Strictly client-bundle-safe (re-exported from `mdx.ts` — NOT from\n * `astro-island.ts`, because this module hard-imports the MDX adapter\n * whose `@mdx-js/react` peer is optional; walked by the\n * bundle-isolation test). MUST stay clear of `node:*` and SSR-tier\n * specifiers.\n */\n\n/**\n * Module-load-time dependencies — closed over inside the returned\n * island. NEVER cross the hydration boundary as serialized props.\n */\nexport interface CreateWebsiteBlogPostBridgeIslandDeps {\n /**\n * Browser CODE imported by the consumer bridge, closed over at module load.\n * Never pass executable modules through Astro island props: those are serialized.\n * Config sync emits a stable map, including an empty map when no providers are declared.\n */\n frontendProviderModules?: GeneratedWebsiteProviderModules;\n /**\n * Loaders for provider components an ENGINE provider module declares but cannot import (#1703) —\n * the website's facet-library door, the equivalent of the application's\n * `contributeProviderComponentLoaders`. A provider PACKAGE that ships its own component needs\n * nothing here: its module's `components[capability].load` is used. A DEPS entry because a loader\n * is a function, and island props are serialized.\n */\n providerComponentLoaders?: ReadonlyArray<ProviderComponentLoaderContribution>;\n /**\n * Per-slug map of compiled MDX bodies (each entry is a React\n * component — the `.default` export of the MDX module). Typically\n * built from `import.meta.glob('./content/blog/<locale>/*.mdx',\n * { eager: true })` with the path-key normalized to the slug. Closed\n * over at module-load time so the entire MDX body graph for the\n * locale lands inside ONE React island bundle (D-6 Option A) — this\n * is the structural fix for Astro's React-children context-propagation\n * gap (see file preamble).\n *\n * The map is read by `slugToMdxBody[props.slug]` at island render\n * time. A missing slug throws a clear framework-owned error (rather than\n * rendering an empty body silently); the consumer's `getStaticPaths`\n * is responsible for not emitting routes for slugs absent from the\n * map.\n *\n * **Why `ComponentType<any>` and not a stricter generic**: matches\n * the upstream `@mdx-js/react` convention (`MDXContent` from\n * `mdx/types` is parameterized over an open `MDXProps` shape). MDX\n * compilation hands the body a compiler-managed props object that\n * the type system cannot narrow at the framework boundary; using\n * `<any>` here keeps the consumer's\n * `import.meta.glob(...) as Record<string, { default: ComponentType }>`\n * cast assignable without an additional wrap/unwrap. Same posture\n * as `WebsiteMdxComponentsMap` (intentional symmetry).\n */\n slugToMdxBody: Readonly<Record<string, ComponentType<any>>>;\n /**\n * Site-wide navigation slot bindings (header / footer components).\n * Closed over here for the same reason as the page bridge: both\n * fields are `ComponentType` refs that cannot survive Astro's\n * serialization boundary.\n */\n navigation: WebsiteNavigationConfig;\n /**\n * Optional replacement for the framework's visitor consent panel (#534). Closed over for the same\n * reason as `navigation`. See `WebsiteRuntimeContextValue.consentBannerComponent`.\n */\n consentBannerComponent?: ComponentType;\n /**\n * MDX-tag → React-component map forwarded into `<BlogPost>` and\n * scoped via `<WebsiteMdxProvider>`. Typically a small object\n * literal exporting the consumer-authored business sections that\n * blog posts can embed (`<CTASection />`, etc.). Closed over here\n * (rather than passed as an island prop) because the values are\n * `ComponentType` refs.\n */\n mdxComponents?: WebsiteMdxComponentsMap;\n}\n\n/**\n * Hydration-boundary props — what the consumer's `.astro` page passes\n * to `<BlogPostEnBridgeIsland client:load ...{primitiveProps}/>`.\n * Every field is JSON-serializable.\n */\nexport interface WebsiteBlogPostBridgeIslandProps {\n /**\n * Slug of the post being rendered. Used by the island to look up\n * the matching MDX body in `deps.slugToMdxBody`. Carried as a plain\n * string (not the branded `BlogPostSlug`) because Astro's\n * serialization boundary unwraps brands.\n */\n slug: string;\n /**\n * Raw frontmatter object Astro hands the page from `entry.data`.\n * Forwarded straight to `<BlogPost>` for strict Zod-4 validation.\n * The bridge does NOT re-validate (single validation surface, owned\n * by the renderer).\n */\n rawFrontmatter: unknown;\n /**\n * Diagnostic source identifier echoed into validation errors —\n * typically `entry.id` from Astro's content collection. Optional\n * but recommended for actionable error messages.\n */\n source?: string;\n /** The locale Astro resolved (with default-locale fallback). */\n locale: AvailableLanguage;\n /**\n * The site's default locale. Required for `WebsiteInternalLink`\n * locale-prefix logic (mirrors the page bridge's `defaultLocale`\n * island prop).\n */\n defaultLocale: AvailableLanguage;\n /** Already-loaded + already-shape-validated label pack. */\n labelPack: LabelPack;\n /** Design-token bundle from `WebsiteRootConfig.designTokens`. */\n designTokens: WebsiteDesignTokens;\n /**\n * Fully-composed label key the framework reads to render the\n * \"Skip to main content\" affordance. Mirrors\n * `WebsiteRootConfig.skipToMainContentLabelKey`. Carried as a\n * primitive island prop for the same reason as the page bridge.\n */\n skipToMainContentLabelKey: WebsiteLabelKey;\n /** Optional website-facing public providers materialized at build time. */\n frontendProviders?: FrontendProvidersBlock;\n /** Product origin for navigation and call-to-action components inside the blog island.\n * Supplied by the Astro page from websiteConfig, just as on ordinary page bridges. */\n appOrigin?: string;\n}\n\nconst EMPTY_MDX_COMPONENTS: WebsiteMdxComponentsMap = Object.freeze({});\n\n/**\n * Returns a FUNCTION component, not the wider `ComponentType` union. The island IS a function\n * component — Astro hydrates nothing else here — and the union is not callable, so declaring it\n * cost callers the ability to invoke the island directly (which the bridge suites do, to observe\n * the synchronous throw for an unregistered slug without routing it through React's reconciler\n * error path). `FunctionComponent` is assignable to `ComponentType`, so no consumer narrows.\n */\nexport function createWebsiteBlogPostBridgeIsland(\n deps: CreateWebsiteBlogPostBridgeIslandDeps,\n): FunctionComponent<WebsiteBlogPostBridgeIslandProps> {\n const {\n slugToMdxBody,\n navigation,\n mdxComponents = EMPTY_MDX_COMPONENTS,\n } = deps;\n\n function WebsiteBlogPostBridgeIsland({\n slug,\n rawFrontmatter,\n source,\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n skipToMainContentLabelKey,\n frontendProviders,\n appOrigin,\n }: WebsiteBlogPostBridgeIslandProps): ReactNode {\n const MdxBody = slugToMdxBody[slug];\n if (MdxBody === undefined) {\n /**\n * Loud failure — the consumer's `getStaticPaths` is supposed to\n * only emit routes for slugs present in the MDX glob. A missing\n * lookup means the glob and the collection are out of sync; the\n * operator needs an actionable error string with the offending\n * slug + the keys that ARE present (truncated to keep the build\n * log readable).\n */\n const knownKeys = Object.keys(slugToMdxBody);\n const known =\n knownKeys.length <= 5\n ? knownKeys.join(', ')\n : `${knownKeys.slice(0, 5).join(', ')}, … (${knownKeys.length} total)`;\n throw new Error(\n `[blog-post-bridge] No MDX body registered for slug=\"${slug}\". ` +\n `The bridge factory's slugToMdxBody map only knows: ${known}. ` +\n `Verify the consumer's import.meta.glob('...content/blog/<locale>/*.mdx') ` +\n `pattern matches the source file naming.`,\n );\n }\n\n // After the missing-slug throw on purpose: that throw ends the render, so it never changes the\n // hook order of a render that completes, and the build-time error stays reachable.\n const { frontendProviderRegistry, providerComponentRegistry } = useWebsiteProviderRegistries({\n frontendProviders,\n frontendProviderModules: deps.frontendProviderModules,\n providerComponentLoaders: deps.providerComponentLoaders,\n });\n const runtimeValue: WebsiteRuntimeContextValue = {\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n navigation,\n skipToMainContentLabelKey,\n appOrigin,\n ...(deps.consentBannerComponent !== undefined ? { consentBannerComponent: deps.consentBannerComponent } : {}),\n frontendProviderRegistry,\n providerComponentRegistry,\n };\n return (\n <WebsiteRuntimeContextProvider value={runtimeValue}>\n <BlogPost\n rawFrontmatter={rawFrontmatter}\n source={source}\n mdxComponents={mdxComponents}\n >\n <MdxBody />\n </BlogPost>\n </WebsiteRuntimeContextProvider>\n );\n }\n WebsiteBlogPostBridgeIsland.displayName = 'WebsiteBlogPostBridgeIsland';\n\n return WebsiteBlogPostBridgeIsland;\n}\n\n/**\n * Re-export of the framework-reserved blog-post page ref. Consumers\n * authoring `BLOG_POST_PAGE_MANIFEST` need this constant to satisfy\n * the manifest's `ref` field; re-exporting it from the bridge module\n * means the consumer's per-locale bridge file can grab both symbols\n * from the one `@wildo-ai/saas-website/mdx` import it already has.\n */\nexport { BLOG_POST_PAGE_REF };\n/** @wildo_source:part:end saas.website.blog.bridge */\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blog-post-head.renderer.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"blog-post-head.renderer.d.ts","sourceRoot":"","sources":["../../../src/astro/blog-post-head.renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAG9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sDAAsD,CAAC;AAChG,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,mEAAmE,CAAC;AAItH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAEH,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;;;OASG;IACH,WAAW,EAAE,kBAAkB,CAAC;IAChC;;;;OAIG;IACH,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,WAAW,EAAE,mBAAmB,CAAC;IACjC;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,yCAAyC;IACxD,WAAW,EAAE,mBAAmB,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,wBAAgB,kCAAkC,CAChD,IAAI,EAAE,yCAAyC,GAC9C,4BAA4B,CAuB9B;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,6BAA6B,GAAG,MAAM,CAkIlF;AACD,6DAA6D"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ComponentType, FunctionComponent } from 'react';
|
|
2
|
-
import type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';
|
|
2
|
+
import type { FrontendProvidersBlock, PublicPricingCatalog } from '@wildo-ai/saas-models/public-runtime';
|
|
3
3
|
import type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';
|
|
4
|
+
import type { ProviderComponentLoaderContribution } from '@wildo-ai/external-connectors-public/website';
|
|
4
5
|
import type { GeneratedWebsiteProviderModules } from '../core/external-providers/frontend-provider-registry.website';
|
|
5
6
|
import type { WebsiteConsentRecorder } from '../core/consent/WebsiteConsentContext';
|
|
6
7
|
import type { WebsiteDesignTokens } from '../schemas/design-tokens/website-design-tokens.shared.schemas';
|
|
@@ -62,22 +63,22 @@ import type { LabelPack } from '../schemas/validators/label-pack.validator';
|
|
|
62
63
|
* } from '@wildo-ai/saas-website/astro';
|
|
63
64
|
* import { LandingBridgeIsland } from '../pages-impl/landing.bridge';
|
|
64
65
|
* import { LANDING_PAGE_MANIFEST } from '../pages-impl/landing.page';
|
|
65
|
-
* import {
|
|
66
|
+
* import { WEBSITE_SITE_CONTEXT } from '../website-site-context';
|
|
66
67
|
*
|
|
67
68
|
* const runtime = loadWebsitePageRuntime({
|
|
68
69
|
* manifest: LANDING_PAGE_MANIFEST,
|
|
69
|
-
*
|
|
70
|
+
* siteContext: WEBSITE_SITE_CONTEXT,
|
|
70
71
|
* labelPacksDir: './src/i18n',
|
|
71
72
|
* astroLocale: Astro.currentLocale,
|
|
72
73
|
* });
|
|
73
74
|
* const headHtml = renderWebsitePageHeadHtml({
|
|
74
75
|
* manifest: LANDING_PAGE_MANIFEST,
|
|
75
|
-
*
|
|
76
|
+
* siteContext: WEBSITE_SITE_CONTEXT,
|
|
76
77
|
* locale: runtime.locale,
|
|
77
78
|
* labelPack: runtime.labelPack,
|
|
78
79
|
* });
|
|
79
80
|
* const designTokensCss = renderWebsiteDesignTokensCss(
|
|
80
|
-
*
|
|
81
|
+
* WEBSITE_SITE_CONTEXT.rootConfig.designTokens,
|
|
81
82
|
* );
|
|
82
83
|
* ---
|
|
83
84
|
* <!doctype html>
|
|
@@ -90,8 +91,11 @@ import type { LabelPack } from '../schemas/validators/label-pack.validator';
|
|
|
90
91
|
* <LandingBridgeIsland
|
|
91
92
|
* client:idle
|
|
92
93
|
* locale={runtime.locale}
|
|
94
|
+
* defaultLocale={runtime.defaultLocale}
|
|
95
|
+
* appOrigin={WEBSITE_SITE_CONTEXT.websiteConfig.appOrigin}
|
|
96
|
+
* apiOrigin={WEBSITE_SITE_CONTEXT.websiteConfig.apiOrigin}
|
|
93
97
|
* labelPack={runtime.labelPack}
|
|
94
|
-
* designTokens={
|
|
98
|
+
* designTokens={WEBSITE_SITE_CONTEXT.rootConfig.designTokens}
|
|
95
99
|
* />
|
|
96
100
|
* </body>
|
|
97
101
|
* </html>
|
|
@@ -141,6 +145,14 @@ export interface CreateWebsitePageBridgeIslandDeps {
|
|
|
141
145
|
* Config sync emits a stable map, including an empty map when no providers are declared.
|
|
142
146
|
*/
|
|
143
147
|
frontendProviderModules?: GeneratedWebsiteProviderModules;
|
|
148
|
+
/**
|
|
149
|
+
* Loaders for provider components an ENGINE provider module declares but cannot import (#1703) —
|
|
150
|
+
* the website's facet-library door, the equivalent of the application's
|
|
151
|
+
* `contributeProviderComponentLoaders`. A provider PACKAGE that ships its own component needs
|
|
152
|
+
* nothing here: its module's `components[capability].load` is used. A DEPS entry because a loader
|
|
153
|
+
* is a function, and island props are serialized.
|
|
154
|
+
*/
|
|
155
|
+
providerComponentLoaders?: ReadonlyArray<ProviderComponentLoaderContribution>;
|
|
144
156
|
/**
|
|
145
157
|
* The page's React component. Typically the same value that was
|
|
146
158
|
* passed to `defineWebsitePageManifest({ pageComponent })` — the
|
|
@@ -258,6 +270,13 @@ export interface WebsitePageBridgeIslandProps {
|
|
|
258
270
|
* the consumer's job and the whole point of removing the fallback.
|
|
259
271
|
*/
|
|
260
272
|
appOrigin?: string;
|
|
273
|
+
/**
|
|
274
|
+
* The application's public pricing catalogue (#1835), forwarded into
|
|
275
|
+
* `WebsiteRuntimeContext.pricingCatalog` for `useWebsitePricingCatalog()`. Plain JSON-safe data
|
|
276
|
+
* (amounts travel as decimal strings, never `bigint`), loaded in the `.astro` frontmatter with
|
|
277
|
+
* `loadWebsitePricingCatalog()`. Passed only by pages with a pricing section.
|
|
278
|
+
*/
|
|
279
|
+
pricingCatalog?: PublicPricingCatalog;
|
|
261
280
|
}
|
|
262
281
|
/**
|
|
263
282
|
* Returns a FUNCTION component, not the wider `ComponentType` union. The island IS a function
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-runtime.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"bridge-runtime.d.ts","sourceRoot":"","sources":["../../../src/astro/bridge-runtime.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAa,MAAM,OAAO,CAAC;AAEzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AACzG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAO9E,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,8CAA8C,CAAC;AACxG,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+DAA+D,CAAC;AAErH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AACpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+DAA+D,CAAC;AACzG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iDAAiD,CAAC;AACvF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yDAAyD,CAAC;AACvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4HG;AAEH;;;GAGG;AACH,MAAM,WAAW,iCAAiC;IAChD;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,+BAA+B,CAAC;IAC1D;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,aAAa,CAAC,mCAAmC,CAAC,CAAC;IAC9E;;;;;;;;;;;;;;OAcG;IACH,aAAa,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,sBAAsB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,sBAAsB,CAAC;IACvE;;;;OAIG;IACH,OAAO,EAAE,cAAc,CAAC;IACxB;;;;;OAKG;IACH,UAAU,EAAE,uBAAuB,CAAC;IACpC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,aAAa,CAAC;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C,gEAAgE;IAChE,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;;;;;;;OASG;IACH,aAAa,EAAE,iBAAiB,CAAC;IACjC,2DAA2D;IAC3D,SAAS,EAAE,SAAS,CAAC;IACrB,iEAAiE;IACjE,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;;;;;;OAUG;IACH,yBAAyB,EAAE,eAAe,CAAC;IAC3C,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,iCAAiC,GACtC,iBAAiB,CAAC,4BAA4B,CAAC,CAuDjD;AACD,uDAAuD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { WebsiteRuntimeContextProvider, } from '../core/contexts/WebsiteRuntimeContext.js';
|
|
4
|
-
import {
|
|
4
|
+
import { useWebsiteProviderRegistries } from '../core/external-providers/provider-component-registry.website.js';
|
|
5
5
|
import { WebsitePageLayout } from '../core/layouts/WebsitePageLayout.js';
|
|
6
6
|
/**
|
|
7
7
|
* Returns a FUNCTION component, not the wider `ComponentType` union. The island IS a function
|
|
@@ -12,7 +12,7 @@ import { WebsitePageLayout } from '../core/layouts/WebsitePageLayout.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export function createWebsitePageBridgeIsland(deps) {
|
|
14
14
|
const { PageComponent, pageRef, navigation } = deps;
|
|
15
|
-
function WebsitePageBridgeIsland({ locale, defaultLocale, labelPack, designTokens, skipToMainContentLabelKey, frontendProviders, apiOrigin, appOrigin, }) {
|
|
15
|
+
function WebsitePageBridgeIsland({ locale, defaultLocale, labelPack, designTokens, skipToMainContentLabelKey, frontendProviders, apiOrigin, appOrigin, pricingCatalog, }) {
|
|
16
16
|
/*
|
|
17
17
|
* Both must be present: the factory is build-time wiring and `apiOrigin` is per-environment, and
|
|
18
18
|
* a page that declares no capture section legitimately has neither. Memoised on `apiOrigin` so a
|
|
@@ -21,6 +21,11 @@ export function createWebsitePageBridgeIsland(deps) {
|
|
|
21
21
|
const consentRecorder = useMemo(() => (deps.consentRecorderFactory !== undefined && apiOrigin !== undefined
|
|
22
22
|
? deps.consentRecorderFactory(apiOrigin)
|
|
23
23
|
: undefined), [apiOrigin]);
|
|
24
|
+
const { frontendProviderRegistry, providerComponentRegistry } = useWebsiteProviderRegistries({
|
|
25
|
+
frontendProviders,
|
|
26
|
+
frontendProviderModules: deps.frontendProviderModules,
|
|
27
|
+
providerComponentLoaders: deps.providerComponentLoaders,
|
|
28
|
+
});
|
|
24
29
|
const runtimeValue = {
|
|
25
30
|
locale,
|
|
26
31
|
defaultLocale,
|
|
@@ -28,13 +33,12 @@ export function createWebsitePageBridgeIsland(deps) {
|
|
|
28
33
|
designTokens,
|
|
29
34
|
navigation,
|
|
30
35
|
skipToMainContentLabelKey,
|
|
31
|
-
frontendProviderRegistry
|
|
32
|
-
|
|
33
|
-
modules: deps.frontendProviderModules,
|
|
34
|
-
}),
|
|
36
|
+
frontendProviderRegistry,
|
|
37
|
+
providerComponentRegistry,
|
|
35
38
|
...(deps.consentBannerComponent !== undefined ? { consentBannerComponent: deps.consentBannerComponent } : {}),
|
|
36
39
|
apiOrigin,
|
|
37
40
|
appOrigin,
|
|
41
|
+
...(pricingCatalog !== undefined ? { pricingCatalog } : {}),
|
|
38
42
|
};
|
|
39
43
|
return (_jsx(WebsiteRuntimeContextProvider, { value: runtimeValue, children: _jsx(WebsitePageLayout, { pageRef: pageRef, ...(consentRecorder !== undefined ? { consentRecorder } : {}), children: _jsx(PageComponent, {}) }) }));
|
|
40
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-runtime.js","sourceRoot":"","sources":["../../../../src/astro/bridge-runtime.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAMhC,OAAO,EACL,6BAA6B,GAE9B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qCAAqC,EAAE,MAAM,+DAA+D,CAAC;AAEtH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAsQtE;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAAuC;IAEvC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAEpD,SAAS,uBAAuB,CAAC,EAC/B,MAAM,EACN,aAAa,EACb,SAAS,EACT,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,SAAS,EACT,SAAS,GACoB;QAC7B;;;;WAIG;QACH,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,sBAAsB,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS;YACzE,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC;YACxC,CAAC,CAAC,SAAS,CAAC,EACd,CAAC,SAAS,CAAC,CACZ,CAAC;QACF,MAAM,YAAY,GAA+B;YAC/C,MAAM;YACN,aAAa;YACb,SAAS;YACT,YAAY;YACZ,UAAU;YACV,yBAAyB;YACzB,wBAAwB,EAAE,qCAAqC,CAAC;gBAC9D,OAAO,EAAE,iBAAiB;gBAC1B,OAAO,EAAE,IAAI,CAAC,uBAAuB;aACtC,CAAC;YACF,GAAG,CAAC,IAAI,CAAC,sBAAsB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7G,SAAS;YACT,SAAS;SACV,CAAC;QACF,OAAO,CACL,KAAC,6BAA6B,IAAC,KAAK,EAAE,YAAY,YAChD,KAAC,iBAAiB,IAAC,OAAO,EAAE,OAAO,KAAM,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YACjG,KAAC,aAAa,KAAG,GACC,GACU,CACjC,CAAC;IACJ,CAAC;IACD,uBAAuB,CAAC,WAAW,GAAG,yBAAyB,CAAC;IAEhE,OAAO,uBAAuB,CAAC;AACjC,CAAC","sourcesContent":["import { useMemo } from 'react';\nimport type { ComponentType, FunctionComponent, ReactNode } from 'react';\n\nimport type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';\nimport type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n\nimport {\n WebsiteRuntimeContextProvider,\n type WebsiteRuntimeContextValue,\n} from '../core/contexts/WebsiteRuntimeContext';\nimport { createFrontendWebsiteProviderRegistry } from '../core/external-providers/frontend-provider-registry.website';\nimport type { GeneratedWebsiteProviderModules } from '../core/external-providers/frontend-provider-registry.website';\nimport { WebsitePageLayout } from '../core/layouts/WebsitePageLayout';\nimport type { WebsiteConsentRecorder } from '../core/consent/WebsiteConsentContext';\nimport type { WebsiteDesignTokens } from '../schemas/design-tokens/website-design-tokens.shared.schemas';\nimport type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';\nimport type { WebsiteNavigationConfig } from '../schemas/manifests/website-root-config.shared.schemas';\nimport type { WebsitePageRef } from '../schemas/refs/page-ref.schemas';\nimport type { LabelPack } from '../schemas/validators/label-pack.validator';\n\n/**\n * @wildo_source:part:start saas.website.astro.bridge facet:layer:astro facet:family:website\n *\n * `createWebsitePageBridgeIsland(deps)` — factory that returns the\n * React island a consumer's per-page `.astro` file mounts with a\n * `client:idle` (or `client:load`) directive.\n *\n * **The hard architectural constraint this design solves**: Astro\n * serializes React-island props via `devalue`-style JSON to transfer\n * them across the SSR → client-hydration boundary. Function values —\n * including `ComponentType` references — are NOT serializable. Any\n * design that tried to pass `pageComponent`, `headerComponent`, or\n * `footerComponent` AS island props would render correctly server-side\n * (the function ref is in-process during SSR) and then mismatch on\n * first hydration (the client receives `undefined`/broken placeholders\n * for the function-typed fields). The Vitest suite would not catch\n * this because Vitest renders React in a single process — there is no\n * serialization boundary in unit tests.\n *\n * **The factory pattern fixes the constraint by closing over** the\n * `pageComponent`, `pageRef`, and `navigation` (header / footer\n * component refs) at MODULE LOAD TIME on the consumer side. The\n * returned island accepts only PRIMITIVE-VALUED props (`locale`,\n * `labelPack`, `designTokens`) — all JSON-serializable, all safe to\n * cross the hydration boundary. The component refs never appear in\n * the prop signature; they live in the lexical closure that the\n * client-side bundle includes naturally as part of the island's\n * compiled module.\n *\n * **Authoring pattern (per-page bridge module)**:\n *\n * ```tsx\n * // consumer's src/pages-impl/landing.bridge.tsx\n * import { createWebsitePageBridgeIsland } from '@wildo-ai/saas-website/astro-island';\n * import { LandingPage, LANDING_PAGE_MANIFEST } from './landing.page';\n * import { WEBSITE_ROOT_CONFIG } from '../website-root.config';\n *\n * export const LandingBridgeIsland = createWebsitePageBridgeIsland({\n * PageComponent: LandingPage,\n * pageRef: LANDING_PAGE_MANIFEST.ref,\n * navigation: WEBSITE_ROOT_CONFIG.navigation,\n * });\n * ```\n *\n * ```astro\n * ---\n * // consumer's src/pages/index.astro\n * import {\n * loadWebsitePageRuntime,\n * renderWebsitePageHeadHtml,\n * renderWebsiteDesignTokensCss,\n * } from '@wildo-ai/saas-website/astro';\n * import { LandingBridgeIsland } from '../pages-impl/landing.bridge';\n * import { LANDING_PAGE_MANIFEST } from '../pages-impl/landing.page';\n * import { WEBSITE_ROOT_CONFIG } from '../website-root.config';\n *\n * const runtime = loadWebsitePageRuntime({\n * manifest: LANDING_PAGE_MANIFEST,\n * rootConfig: WEBSITE_ROOT_CONFIG,\n * labelPacksDir: './src/i18n',\n * astroLocale: Astro.currentLocale,\n * });\n * const headHtml = renderWebsitePageHeadHtml({\n * manifest: LANDING_PAGE_MANIFEST,\n * rootConfig: WEBSITE_ROOT_CONFIG,\n * locale: runtime.locale,\n * labelPack: runtime.labelPack,\n * });\n * const designTokensCss = renderWebsiteDesignTokensCss(\n * WEBSITE_ROOT_CONFIG.designTokens,\n * );\n * ---\n * <!doctype html>\n * <html lang={runtime.locale}>\n * <head>\n * <Fragment set:html={headHtml} />\n * <style set:html={designTokensCss}></style>\n * </head>\n * <body>\n * <LandingBridgeIsland\n * client:idle\n * locale={runtime.locale}\n * labelPack={runtime.labelPack}\n * designTokens={WEBSITE_ROOT_CONFIG.designTokens}\n * />\n * </body>\n * </html>\n * ```\n *\n * **Why a per-page bridge file** (rather than constructing the island\n * inline in the consumer's `.astro` frontmatter):\n *\n * Astro's `client:*` directive requires the JSX tag to reference a\n * statically-resolvable module export at compile time — that is how\n * Astro knows which module to bundle for client-side hydration. A\n * factory call inside `.astro` frontmatter (`const Island =\n * createWebsitePageBridgeIsland({...}); <Island client:idle/>`) does\n * NOT round-trip through Astro's module-graph analyzer: Astro would\n * see a runtime-constructed component reference and fail to bundle\n * the closure for the client. The per-page bridge file gives Astro a\n * static export to bind the directive to. The cost is one ~6-line\n * file per page; the win is the only architecture that is safe\n * against the H-2 hydration-mismatch failure mode AND uses Astro's\n * native bundling pipeline.\n *\n * **Why `ComponentType` refs in `Deps` and not in `Props`**: this is\n * the entire point of the redesign. `Deps` is consumed at module load\n * time on the consumer side — the closure traps the function refs\n * inside the returned component. `Props` is consumed at island-mount\n * time and crosses Astro's serialization boundary. Keeping the two\n * concerns lexically separate makes the boundary visible in the type\n * signature.\n *\n * **Boundary** (D12 client-bundle discipline): this module ships into\n * the React-island bundle. It MUST stay clear of `node:*` imports,\n * fs / path helpers, and the SSR-tier specifiers\n * (`axios`, `jsonwebtoken`, …). The bundle-isolation test catches\n * regressions both globally (closure-wide allow-list) AND with the\n * targeted Phase-5 negative test that asserts `node:fs` / `node:path`\n * reach this island closure ONLY through `astro.ts`, never through\n * `index.ts` or `companion-exports.ts`.\n */\n\n/**\n * Module-load-time dependencies — closed over inside the returned\n * island. NEVER cross the hydration boundary as serialized props.\n */\nexport interface CreateWebsitePageBridgeIslandDeps {\n /**\n * Browser CODE imported by the consumer bridge, closed over at module load.\n * Never pass executable modules through Astro island props: those are serialized.\n * Config sync emits a stable map, including an empty map when no providers are declared.\n */\n frontendProviderModules?: GeneratedWebsiteProviderModules;\n /**\n * The page's React component. Typically the same value that was\n * passed to `defineWebsitePageManifest({ pageComponent })` — the\n * authoring convention re-imports it here to keep the closure\n * explicit and the per-page bridge file self-contained.\n *\n * Typed as `ComponentType<any>` to mirror\n * `WebsitePageManifest.pageComponent` and dodge React's `ComponentType`\n * contravariance: a narrower `ComponentType<TProps>` returned by\n * `defineWebsitePageManifest<TProps>` would not be assignable to a\n * `ComponentType<unknown>` slot, blocking every per-page bridge\n * call site. The component is invoked with no props at the\n * `<PageComponent />` JSX call site below, so widening here is\n * runtime-safe.\n */\n PageComponent: ComponentType<any>;\n /**\n * Builds the recorder that persists a VISITOR's consent decision server-side, from the island's\n * runtime `apiOrigin` (#1258).\n *\n * A DEPS entry rather than an island prop, for the reason this whole interface exists: it is a\n * function, and island props are serialized. A factory rather than an instance, because the\n * `apiOrigin` it needs is resolved per environment by the `.astro` page and only reaches the island\n * as a prop.\n *\n * The engine cannot build this itself — a client needs the application's own\n * `frontendServiceName`, which the engine has no way to know. The application already constructs\n * one for its capture forms; passing THAT factory here is what makes a visitor's consent decision,\n * their captured lead and their draft note share a single `uuidRef` and transpose together.\n *\n * Optional. Omitted, a decision lives in the visitor's browser and nowhere else — the behaviour\n * before this existed, and correct for a site with no anonymous-session wiring.\n */\n consentRecorderFactory?: (apiOrigin: string) => WebsiteConsentRecorder;\n /**\n * The active page's ref. Carried as a primitive (string-branded)\n * value so the inner `WebsitePageLayout` can register the\n * `WebsitePageContext` without re-resolving it from the manifest.\n */\n pageRef: WebsitePageRef;\n /**\n * Site-wide navigation slot bindings (header / footer components).\n * Closed over here rather than passed as island props because both\n * fields are `ComponentType` refs that cannot survive Astro's\n * serialization boundary.\n */\n navigation: WebsiteNavigationConfig;\n /**\n * Optional replacement for the framework's visitor consent panel (#534). Closed over for the same\n * reason as `navigation`. See `WebsiteRuntimeContextValue.consentBannerComponent`.\n */\n consentBannerComponent?: ComponentType;\n}\n\n/**\n * Hydration-boundary props — what the consumer's `.astro` page passes\n * to `<LandingBridgeIsland client:idle ...{primitiveProps}/>`. Every\n * field is JSON-serializable.\n */\nexport interface WebsitePageBridgeIslandProps {\n /** The locale Astro resolved (with default-locale fallback). */\n locale: AvailableLanguage;\n /**\n * The site's default locale. Carried as an island prop (not closed\n * over in `Deps`) because it is JSON-primitive and consumers\n * already pass `runtime.locale` from the per-locale Astro page\n * frontmatter — passing the matching `runtime.defaultLocale` from\n * the same `loadWebsitePageRuntime(...)` return value keeps the\n * authoring story symmetric. Required for `WebsiteInternalLink`\n * (Phase 6 fix H-β) which needs both locales to compute the\n * correct prefix.\n */\n defaultLocale: AvailableLanguage;\n /** Already-loaded + already-shape-validated label pack. */\n labelPack: LabelPack;\n /** Design-token bundle from `WebsiteRootConfig.designTokens`. */\n designTokens: WebsiteDesignTokens;\n /**\n * Fully-composed label key the framework reads to render the\n * \"Skip to main content\" affordance (Phase 6 third audit fix M-α).\n * Mirrors `WebsiteRootConfig.skipToMainContentLabelKey`. Carried as\n * a JSON-serializable string prop (NOT closed over in `Deps`)\n * because it is a primitive value the consumer already has on the\n * root config in the `.astro` frontmatter — passing it as an island\n * prop keeps the per-page bridge module dependency-free of the\n * site root config (the bridge stays page-local; only the per-page\n * `.astro` page knows about `WEBSITE_ROOT_CONFIG`).\n */\n skipToMainContentLabelKey: WebsiteLabelKey;\n /** Optional website-facing public providers materialized at build time. */\n frontendProviders?: FrontendProvidersBlock;\n /**\n * Optional backend API origin (no trailing slash) forwarded into\n * `WebsiteRuntimeContext.apiOrigin` for anonymous-capture sections\n * (lead / draft-note forms) to construct their\n * `WebsiteAnonymousSessionClient`. A JSON-serializable string, so it\n * crosses the hydration boundary as an island prop; the `.astro` page\n * resolves the per-environment value from\n * `WildoMarketingWebsiteConfig.apiOrigin`. Omitted on pages with no\n * capture section.\n */\n apiOrigin?: string;\n /**\n * The product origin this site's self-serve CTAs link to (\"Start free\",\n * \"Get started\"), forwarded into `WebsiteRuntimeContext.appOrigin` (#493).\n *\n * An island prop for the same reason `apiOrigin` above is one: the `.astro`\n * page resolves it from `WildoMarketingWebsiteConfig.appOrigin`, which is\n * Node-loaded and invisible to a client island. Before this, a site\n * resolved the product origin a SECOND time from a public build-time\n * environment variable — one host authored twice with no mechanical link,\n * and a silent fallback to localhost when the variable was unset.\n *\n * Optional because a page with no CTA needs none; a page that renders one\n * without passing this should fail LOUDLY rather than fall back, which is\n * the consumer's job and the whole point of removing the fallback.\n */\n appOrigin?: string;\n}\n\n/**\n * Returns a FUNCTION component, not the wider `ComponentType` union. The island IS a function\n * component — Astro hydrates nothing else here — and the union is not callable, so the wider\n * declaration cost callers the ability to invoke the island directly. Mirrors\n * `createWebsiteBlogPostBridgeIsland`, whose suite depends on that call. `FunctionComponent` is\n * assignable to `ComponentType`, so no consumer narrows.\n */\nexport function createWebsitePageBridgeIsland(\n deps: CreateWebsitePageBridgeIslandDeps,\n): FunctionComponent<WebsitePageBridgeIslandProps> {\n const { PageComponent, pageRef, navigation } = deps;\n\n function WebsitePageBridgeIsland({\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n skipToMainContentLabelKey,\n frontendProviders,\n apiOrigin,\n appOrigin,\n }: WebsitePageBridgeIslandProps): ReactNode {\n /*\n * Both must be present: the factory is build-time wiring and `apiOrigin` is per-environment, and\n * a page that declares no capture section legitimately has neither. Memoised on `apiOrigin` so a\n * re-render cannot hand the provider a new identity and re-post a decision nobody retook.\n */\n const consentRecorder = useMemo(\n () => (deps.consentRecorderFactory !== undefined && apiOrigin !== undefined\n ? deps.consentRecorderFactory(apiOrigin)\n : undefined),\n [apiOrigin],\n );\n const runtimeValue: WebsiteRuntimeContextValue = {\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n navigation,\n skipToMainContentLabelKey,\n frontendProviderRegistry: createFrontendWebsiteProviderRegistry({\n entries: frontendProviders,\n modules: deps.frontendProviderModules,\n }),\n ...(deps.consentBannerComponent !== undefined ? { consentBannerComponent: deps.consentBannerComponent } : {}),\n apiOrigin,\n appOrigin,\n };\n return (\n <WebsiteRuntimeContextProvider value={runtimeValue}>\n <WebsitePageLayout pageRef={pageRef} {...(consentRecorder !== undefined ? { consentRecorder } : {})}>\n <PageComponent />\n </WebsitePageLayout>\n </WebsiteRuntimeContextProvider>\n );\n }\n WebsitePageBridgeIsland.displayName = 'WebsitePageBridgeIsland';\n\n return WebsitePageBridgeIsland;\n}\n/** @wildo_source:part:end saas.website.astro.bridge */\n"]}
|
|
1
|
+
{"version":3,"file":"bridge-runtime.js","sourceRoot":"","sources":["../../../src/astro/bridge-runtime.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAMhC,OAAO,EACL,6BAA6B,GAE9B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,4BAA4B,EAAE,MAAM,gEAAgE,CAAC;AAG9G,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAwRtE;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAAuC;IAEvC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAEpD,SAAS,uBAAuB,CAAC,EAC/B,MAAM,EACN,aAAa,EACb,SAAS,EACT,YAAY,EACZ,yBAAyB,EACzB,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,cAAc,GACe;QAC7B;;;;WAIG;QACH,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,sBAAsB,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS;YACzE,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC;YACxC,CAAC,CAAC,SAAS,CAAC,EACd,CAAC,SAAS,CAAC,CACZ,CAAC;QACF,MAAM,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,GAAG,4BAA4B,CAAC;YAC3F,iBAAiB;YACjB,uBAAuB,EAAE,IAAI,CAAC,uBAAuB;YACrD,wBAAwB,EAAE,IAAI,CAAC,wBAAwB;SACxD,CAAC,CAAC;QACH,MAAM,YAAY,GAA+B;YAC/C,MAAM;YACN,aAAa;YACb,SAAS;YACT,YAAY;YACZ,UAAU;YACV,yBAAyB;YACzB,wBAAwB;YACxB,yBAAyB;YACzB,GAAG,CAAC,IAAI,CAAC,sBAAsB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7G,SAAS;YACT,SAAS;YACT,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5D,CAAC;QACF,OAAO,CACL,KAAC,6BAA6B,IAAC,KAAK,EAAE,YAAY,YAChD,KAAC,iBAAiB,IAAC,OAAO,EAAE,OAAO,KAAM,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YACjG,KAAC,aAAa,KAAG,GACC,GACU,CACjC,CAAC;IACJ,CAAC;IACD,uBAAuB,CAAC,WAAW,GAAG,yBAAyB,CAAC;IAEhE,OAAO,uBAAuB,CAAC;AACjC,CAAC","sourcesContent":["import { useMemo } from 'react';\nimport type { ComponentType, FunctionComponent, ReactNode } from 'react';\n\nimport type { FrontendProvidersBlock, PublicPricingCatalog } from '@wildo-ai/saas-models/public-runtime';\nimport type { AvailableLanguage } from '@wildo-ai/saas-models/public-runtime';\n\nimport {\n WebsiteRuntimeContextProvider,\n type WebsiteRuntimeContextValue,\n} from '../core/contexts/WebsiteRuntimeContext';\nimport { useWebsiteProviderRegistries } from '../core/external-providers/provider-component-registry.website';\nimport type { ProviderComponentLoaderContribution } from '@wildo-ai/external-connectors-public/website';\nimport type { GeneratedWebsiteProviderModules } from '../core/external-providers/frontend-provider-registry.website';\nimport { WebsitePageLayout } from '../core/layouts/WebsitePageLayout';\nimport type { WebsiteConsentRecorder } from '../core/consent/WebsiteConsentContext';\nimport type { WebsiteDesignTokens } from '../schemas/design-tokens/website-design-tokens.shared.schemas';\nimport type { WebsiteLabelKey } from '../schemas/label-keys/website-label-key.schemas';\nimport type { WebsiteNavigationConfig } from '../schemas/manifests/website-root-config.shared.schemas';\nimport type { WebsitePageRef } from '../schemas/refs/page-ref.schemas';\nimport type { LabelPack } from '../schemas/validators/label-pack.validator';\n\n/**\n * @wildo_source:part:start saas.website.astro.bridge facet:layer:astro facet:family:website\n *\n * `createWebsitePageBridgeIsland(deps)` — factory that returns the\n * React island a consumer's per-page `.astro` file mounts with a\n * `client:idle` (or `client:load`) directive.\n *\n * **The hard architectural constraint this design solves**: Astro\n * serializes React-island props via `devalue`-style JSON to transfer\n * them across the SSR → client-hydration boundary. Function values —\n * including `ComponentType` references — are NOT serializable. Any\n * design that tried to pass `pageComponent`, `headerComponent`, or\n * `footerComponent` AS island props would render correctly server-side\n * (the function ref is in-process during SSR) and then mismatch on\n * first hydration (the client receives `undefined`/broken placeholders\n * for the function-typed fields). The Vitest suite would not catch\n * this because Vitest renders React in a single process — there is no\n * serialization boundary in unit tests.\n *\n * **The factory pattern fixes the constraint by closing over** the\n * `pageComponent`, `pageRef`, and `navigation` (header / footer\n * component refs) at MODULE LOAD TIME on the consumer side. The\n * returned island accepts only PRIMITIVE-VALUED props (`locale`,\n * `labelPack`, `designTokens`) — all JSON-serializable, all safe to\n * cross the hydration boundary. The component refs never appear in\n * the prop signature; they live in the lexical closure that the\n * client-side bundle includes naturally as part of the island's\n * compiled module.\n *\n * **Authoring pattern (per-page bridge module)**:\n *\n * ```tsx\n * // consumer's src/pages-impl/landing.bridge.tsx\n * import { createWebsitePageBridgeIsland } from '@wildo-ai/saas-website/astro-island';\n * import { LandingPage, LANDING_PAGE_MANIFEST } from './landing.page';\n * import { WEBSITE_ROOT_CONFIG } from '../website-root.config';\n *\n * export const LandingBridgeIsland = createWebsitePageBridgeIsland({\n * PageComponent: LandingPage,\n * pageRef: LANDING_PAGE_MANIFEST.ref,\n * navigation: WEBSITE_ROOT_CONFIG.navigation,\n * });\n * ```\n *\n * ```astro\n * ---\n * // consumer's src/pages/index.astro\n * import {\n * loadWebsitePageRuntime,\n * renderWebsitePageHeadHtml,\n * renderWebsiteDesignTokensCss,\n * } from '@wildo-ai/saas-website/astro';\n * import { LandingBridgeIsland } from '../pages-impl/landing.bridge';\n * import { LANDING_PAGE_MANIFEST } from '../pages-impl/landing.page';\n * import { WEBSITE_SITE_CONTEXT } from '../website-site-context';\n *\n * const runtime = loadWebsitePageRuntime({\n * manifest: LANDING_PAGE_MANIFEST,\n * siteContext: WEBSITE_SITE_CONTEXT,\n * labelPacksDir: './src/i18n',\n * astroLocale: Astro.currentLocale,\n * });\n * const headHtml = renderWebsitePageHeadHtml({\n * manifest: LANDING_PAGE_MANIFEST,\n * siteContext: WEBSITE_SITE_CONTEXT,\n * locale: runtime.locale,\n * labelPack: runtime.labelPack,\n * });\n * const designTokensCss = renderWebsiteDesignTokensCss(\n * WEBSITE_SITE_CONTEXT.rootConfig.designTokens,\n * );\n * ---\n * <!doctype html>\n * <html lang={runtime.locale}>\n * <head>\n * <Fragment set:html={headHtml} />\n * <style set:html={designTokensCss}></style>\n * </head>\n * <body>\n * <LandingBridgeIsland\n * client:idle\n * locale={runtime.locale}\n * defaultLocale={runtime.defaultLocale}\n * appOrigin={WEBSITE_SITE_CONTEXT.websiteConfig.appOrigin}\n * apiOrigin={WEBSITE_SITE_CONTEXT.websiteConfig.apiOrigin}\n * labelPack={runtime.labelPack}\n * designTokens={WEBSITE_SITE_CONTEXT.rootConfig.designTokens}\n * />\n * </body>\n * </html>\n * ```\n *\n * **Why a per-page bridge file** (rather than constructing the island\n * inline in the consumer's `.astro` frontmatter):\n *\n * Astro's `client:*` directive requires the JSX tag to reference a\n * statically-resolvable module export at compile time — that is how\n * Astro knows which module to bundle for client-side hydration. A\n * factory call inside `.astro` frontmatter (`const Island =\n * createWebsitePageBridgeIsland({...}); <Island client:idle/>`) does\n * NOT round-trip through Astro's module-graph analyzer: Astro would\n * see a runtime-constructed component reference and fail to bundle\n * the closure for the client. The per-page bridge file gives Astro a\n * static export to bind the directive to. The cost is one ~6-line\n * file per page; the win is the only architecture that is safe\n * against the H-2 hydration-mismatch failure mode AND uses Astro's\n * native bundling pipeline.\n *\n * **Why `ComponentType` refs in `Deps` and not in `Props`**: this is\n * the entire point of the redesign. `Deps` is consumed at module load\n * time on the consumer side — the closure traps the function refs\n * inside the returned component. `Props` is consumed at island-mount\n * time and crosses Astro's serialization boundary. Keeping the two\n * concerns lexically separate makes the boundary visible in the type\n * signature.\n *\n * **Boundary** (D12 client-bundle discipline): this module ships into\n * the React-island bundle. It MUST stay clear of `node:*` imports,\n * fs / path helpers, and the SSR-tier specifiers\n * (`axios`, `jsonwebtoken`, …). The bundle-isolation test catches\n * regressions both globally (closure-wide allow-list) AND with the\n * targeted Phase-5 negative test that asserts `node:fs` / `node:path`\n * reach this island closure ONLY through `astro.ts`, never through\n * `index.ts` or `companion-exports.ts`.\n */\n\n/**\n * Module-load-time dependencies — closed over inside the returned\n * island. NEVER cross the hydration boundary as serialized props.\n */\nexport interface CreateWebsitePageBridgeIslandDeps {\n /**\n * Browser CODE imported by the consumer bridge, closed over at module load.\n * Never pass executable modules through Astro island props: those are serialized.\n * Config sync emits a stable map, including an empty map when no providers are declared.\n */\n frontendProviderModules?: GeneratedWebsiteProviderModules;\n /**\n * Loaders for provider components an ENGINE provider module declares but cannot import (#1703) —\n * the website's facet-library door, the equivalent of the application's\n * `contributeProviderComponentLoaders`. A provider PACKAGE that ships its own component needs\n * nothing here: its module's `components[capability].load` is used. A DEPS entry because a loader\n * is a function, and island props are serialized.\n */\n providerComponentLoaders?: ReadonlyArray<ProviderComponentLoaderContribution>;\n /**\n * The page's React component. Typically the same value that was\n * passed to `defineWebsitePageManifest({ pageComponent })` — the\n * authoring convention re-imports it here to keep the closure\n * explicit and the per-page bridge file self-contained.\n *\n * Typed as `ComponentType<any>` to mirror\n * `WebsitePageManifest.pageComponent` and dodge React's `ComponentType`\n * contravariance: a narrower `ComponentType<TProps>` returned by\n * `defineWebsitePageManifest<TProps>` would not be assignable to a\n * `ComponentType<unknown>` slot, blocking every per-page bridge\n * call site. The component is invoked with no props at the\n * `<PageComponent />` JSX call site below, so widening here is\n * runtime-safe.\n */\n PageComponent: ComponentType<any>;\n /**\n * Builds the recorder that persists a VISITOR's consent decision server-side, from the island's\n * runtime `apiOrigin` (#1258).\n *\n * A DEPS entry rather than an island prop, for the reason this whole interface exists: it is a\n * function, and island props are serialized. A factory rather than an instance, because the\n * `apiOrigin` it needs is resolved per environment by the `.astro` page and only reaches the island\n * as a prop.\n *\n * The engine cannot build this itself — a client needs the application's own\n * `frontendServiceName`, which the engine has no way to know. The application already constructs\n * one for its capture forms; passing THAT factory here is what makes a visitor's consent decision,\n * their captured lead and their draft note share a single `uuidRef` and transpose together.\n *\n * Optional. Omitted, a decision lives in the visitor's browser and nowhere else — the behaviour\n * before this existed, and correct for a site with no anonymous-session wiring.\n */\n consentRecorderFactory?: (apiOrigin: string) => WebsiteConsentRecorder;\n /**\n * The active page's ref. Carried as a primitive (string-branded)\n * value so the inner `WebsitePageLayout` can register the\n * `WebsitePageContext` without re-resolving it from the manifest.\n */\n pageRef: WebsitePageRef;\n /**\n * Site-wide navigation slot bindings (header / footer components).\n * Closed over here rather than passed as island props because both\n * fields are `ComponentType` refs that cannot survive Astro's\n * serialization boundary.\n */\n navigation: WebsiteNavigationConfig;\n /**\n * Optional replacement for the framework's visitor consent panel (#534). Closed over for the same\n * reason as `navigation`. See `WebsiteRuntimeContextValue.consentBannerComponent`.\n */\n consentBannerComponent?: ComponentType;\n}\n\n/**\n * Hydration-boundary props — what the consumer's `.astro` page passes\n * to `<LandingBridgeIsland client:idle ...{primitiveProps}/>`. Every\n * field is JSON-serializable.\n */\nexport interface WebsitePageBridgeIslandProps {\n /** The locale Astro resolved (with default-locale fallback). */\n locale: AvailableLanguage;\n /**\n * The site's default locale. Carried as an island prop (not closed\n * over in `Deps`) because it is JSON-primitive and consumers\n * already pass `runtime.locale` from the per-locale Astro page\n * frontmatter — passing the matching `runtime.defaultLocale` from\n * the same `loadWebsitePageRuntime(...)` return value keeps the\n * authoring story symmetric. Required for `WebsiteInternalLink`\n * (Phase 6 fix H-β) which needs both locales to compute the\n * correct prefix.\n */\n defaultLocale: AvailableLanguage;\n /** Already-loaded + already-shape-validated label pack. */\n labelPack: LabelPack;\n /** Design-token bundle from `WebsiteRootConfig.designTokens`. */\n designTokens: WebsiteDesignTokens;\n /**\n * Fully-composed label key the framework reads to render the\n * \"Skip to main content\" affordance (Phase 6 third audit fix M-α).\n * Mirrors `WebsiteRootConfig.skipToMainContentLabelKey`. Carried as\n * a JSON-serializable string prop (NOT closed over in `Deps`)\n * because it is a primitive value the consumer already has on the\n * root config in the `.astro` frontmatter — passing it as an island\n * prop keeps the per-page bridge module dependency-free of the\n * site root config (the bridge stays page-local; only the per-page\n * `.astro` page knows about `WEBSITE_ROOT_CONFIG`).\n */\n skipToMainContentLabelKey: WebsiteLabelKey;\n /** Optional website-facing public providers materialized at build time. */\n frontendProviders?: FrontendProvidersBlock;\n /**\n * Optional backend API origin (no trailing slash) forwarded into\n * `WebsiteRuntimeContext.apiOrigin` for anonymous-capture sections\n * (lead / draft-note forms) to construct their\n * `WebsiteAnonymousSessionClient`. A JSON-serializable string, so it\n * crosses the hydration boundary as an island prop; the `.astro` page\n * resolves the per-environment value from\n * `WildoMarketingWebsiteConfig.apiOrigin`. Omitted on pages with no\n * capture section.\n */\n apiOrigin?: string;\n /**\n * The product origin this site's self-serve CTAs link to (\"Start free\",\n * \"Get started\"), forwarded into `WebsiteRuntimeContext.appOrigin` (#493).\n *\n * An island prop for the same reason `apiOrigin` above is one: the `.astro`\n * page resolves it from `WildoMarketingWebsiteConfig.appOrigin`, which is\n * Node-loaded and invisible to a client island. Before this, a site\n * resolved the product origin a SECOND time from a public build-time\n * environment variable — one host authored twice with no mechanical link,\n * and a silent fallback to localhost when the variable was unset.\n *\n * Optional because a page with no CTA needs none; a page that renders one\n * without passing this should fail LOUDLY rather than fall back, which is\n * the consumer's job and the whole point of removing the fallback.\n */\n appOrigin?: string;\n /**\n * The application's public pricing catalogue (#1835), forwarded into\n * `WebsiteRuntimeContext.pricingCatalog` for `useWebsitePricingCatalog()`. Plain JSON-safe data\n * (amounts travel as decimal strings, never `bigint`), loaded in the `.astro` frontmatter with\n * `loadWebsitePricingCatalog()`. Passed only by pages with a pricing section.\n */\n pricingCatalog?: PublicPricingCatalog;\n}\n\n/**\n * Returns a FUNCTION component, not the wider `ComponentType` union. The island IS a function\n * component — Astro hydrates nothing else here — and the union is not callable, so the wider\n * declaration cost callers the ability to invoke the island directly. Mirrors\n * `createWebsiteBlogPostBridgeIsland`, whose suite depends on that call. `FunctionComponent` is\n * assignable to `ComponentType`, so no consumer narrows.\n */\nexport function createWebsitePageBridgeIsland(\n deps: CreateWebsitePageBridgeIslandDeps,\n): FunctionComponent<WebsitePageBridgeIslandProps> {\n const { PageComponent, pageRef, navigation } = deps;\n\n function WebsitePageBridgeIsland({\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n skipToMainContentLabelKey,\n frontendProviders,\n apiOrigin,\n appOrigin,\n pricingCatalog,\n }: WebsitePageBridgeIslandProps): ReactNode {\n /*\n * Both must be present: the factory is build-time wiring and `apiOrigin` is per-environment, and\n * a page that declares no capture section legitimately has neither. Memoised on `apiOrigin` so a\n * re-render cannot hand the provider a new identity and re-post a decision nobody retook.\n */\n const consentRecorder = useMemo(\n () => (deps.consentRecorderFactory !== undefined && apiOrigin !== undefined\n ? deps.consentRecorderFactory(apiOrigin)\n : undefined),\n [apiOrigin],\n );\n const { frontendProviderRegistry, providerComponentRegistry } = useWebsiteProviderRegistries({\n frontendProviders,\n frontendProviderModules: deps.frontendProviderModules,\n providerComponentLoaders: deps.providerComponentLoaders,\n });\n const runtimeValue: WebsiteRuntimeContextValue = {\n locale,\n defaultLocale,\n labelPack,\n designTokens,\n navigation,\n skipToMainContentLabelKey,\n frontendProviderRegistry,\n providerComponentRegistry,\n ...(deps.consentBannerComponent !== undefined ? { consentBannerComponent: deps.consentBannerComponent } : {}),\n apiOrigin,\n appOrigin,\n ...(pricingCatalog !== undefined ? { pricingCatalog } : {}),\n };\n return (\n <WebsiteRuntimeContextProvider value={runtimeValue}>\n <WebsitePageLayout pageRef={pageRef} {...(consentRecorder !== undefined ? { consentRecorder } : {})}>\n <PageComponent />\n </WebsitePageLayout>\n </WebsiteRuntimeContextProvider>\n );\n }\n WebsitePageBridgeIsland.displayName = 'WebsitePageBridgeIsland';\n\n return WebsitePageBridgeIsland;\n}\n/** @wildo_source:part:end saas.website.astro.bridge */\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect-expected-label-keys.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"collect-expected-label-keys.d.ts","sourceRoot":"","sources":["../../../src/astro/collect-expected-label-keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+DAA+D,CAAC;AAG9G,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,4CAA4C,CAAC;AAEhG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,SAAS,wBAAwB,EAAE,GAC5C,6BAA6B,CAsB/B;AAuBD,oEAAoE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers-renderer.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"headers-renderer.d.ts","sourceRoot":"","sources":["../../../src/astro/headers-renderer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAIjE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,kBAAkB,GAAG,MAAM,CAapF;AACD,iEAAiE;AAEjE;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,WAAW,EAAE,kBAAkB,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,MAAM,CAgBT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n-routing.helper.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"i18n-routing.helper.d.ts","sourceRoot":"","sources":["../../../src/astro/i18n-routing.helper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE;QACP;;;;;;;WAOG;QACH,mBAAmB,EAAE,OAAO,CAAC;QAC7B;;;;WAIG;QACH,uBAAuB,EAAE,OAAO,CAAC;KAClC,CAAC;CACH;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,kBAAkB,GAAG,eAAe,CAsBrF;AACD,4DAA4D"}
|
|
@@ -6,10 +6,10 @@ import type { WebsiteSiteContext } from './website-site-context';
|
|
|
6
6
|
*
|
|
7
7
|
* `label-pack-loader.ts` — synchronous, fs-backed loader for
|
|
8
8
|
* marketing-site label packs. Consumed by:
|
|
9
|
-
* -
|
|
10
|
-
* locale's pack
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
9
|
+
* - each page's `.astro` frontmatter, through `loadWebsitePageRuntime`,
|
|
10
|
+
* which reads the active locale's pack during SSR / pre-render,
|
|
11
|
+
* - `wildo website audit`, which validates every locale pack against
|
|
12
|
+
* every manifest and section through `loadAndValidateAllLabelPacks`,
|
|
13
13
|
* - any build-time hook that wants to fail fast on a missing or
|
|
14
14
|
* malformed pack.
|
|
15
15
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label-pack-loader.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"label-pack-loader.d.ts","sourceRoot":"","sources":["../../../src/astro/label-pack-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAO9E,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,SAAS,EACd,KAAK,yBAAyB,EAC/B,MAAM,4CAA4C,CAAC;AAEpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,SAAS,CA4BpF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,kBAAkB,GAC9B,GAAG,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAMnC;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,yBAAyB,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,kBAAkB,EAC/B,wBAAwB,EAAE,6BAA6B,GACtD,SAAS,yBAAyB,EAAE,CAmBtC;AAsCD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,kBAAkB,EAC/B,wBAAwB,EAAE,6BAA6B,GACtD,IAAI,CA6DN;AA+ED,6DAA6D"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { WEBSITE_CONSENT_LABEL_KEYS } from '../schemas/label-keys/website-consent-label-keys.schemas.js';
|
|
4
|
+
import { WEBSITE_PROVIDER_COMPONENT_LABEL_KEYS } from '../schemas/label-keys/website-provider-component-label-keys.schemas.js';
|
|
4
5
|
import { validateLabelPackAgainstManifest, } from '../schemas/validators/label-pack.validator.js';
|
|
5
6
|
/**
|
|
6
7
|
* @wildo_source:part:start saas.website.astro.label-loader facet:layer:astro facet:family:website
|
|
7
8
|
*
|
|
8
9
|
* `label-pack-loader.ts` — synchronous, fs-backed loader for
|
|
9
10
|
* marketing-site label packs. Consumed by:
|
|
10
|
-
* -
|
|
11
|
-
* locale's pack
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
11
|
+
* - each page's `.astro` frontmatter, through `loadWebsitePageRuntime`,
|
|
12
|
+
* which reads the active locale's pack during SSR / pre-render,
|
|
13
|
+
* - `wildo website audit`, which validates every locale pack against
|
|
14
|
+
* every manifest and section through `loadAndValidateAllLabelPacks`,
|
|
14
15
|
* - any build-time hook that wants to fail fast on a missing or
|
|
15
16
|
* malformed pack.
|
|
16
17
|
*
|
|
@@ -163,6 +164,10 @@ function composeEffectiveChromeLabelKeys(rootConfig) {
|
|
|
163
164
|
// that turns on analytics can never ship it with an unlabelled way to refuse.
|
|
164
165
|
for (const key of WEBSITE_CONSENT_LABEL_KEYS)
|
|
165
166
|
set.add(key);
|
|
167
|
+
// So is the provider-component slot's refusal wording (#1703): a site placing an address lookup must not
|
|
168
|
+
// ship its consent-required or unavailable state as a raw key.
|
|
169
|
+
for (const key of WEBSITE_PROVIDER_COMPONENT_LABEL_KEYS)
|
|
170
|
+
set.add(key);
|
|
166
171
|
return Array.from(set);
|
|
167
172
|
}
|
|
168
173
|
/**
|