@wildo-ai/saas-website 1.1.4 → 1.1.6
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 +23 -20
- package/dist/esm/astro/blog-post-bridge.d.ts.map +1 -1
- package/dist/esm/astro/blog-post-bridge.js +13 -6
- 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 +55 -26
- package/dist/esm/astro/bridge-runtime.d.ts.map +1 -1
- package/dist/esm/astro/bridge-runtime.js +21 -7
- 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 +14 -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 +20 -10
- 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 +10 -5
- package/dist/esm/config/wildo-website-config.schemas.d.ts.map +1 -1
- package/dist/esm/config/wildo-website-config.schemas.js +10 -5
- package/dist/esm/config/wildo-website-config.schemas.js.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 +14 -3
- 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 +28 -13
- 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 +24 -1
- 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 +46 -3
- 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 +6 -0
- package/dist/esm/core/consent/WebsiteConsentBanner.d.ts.map +1 -0
- package/dist/esm/core/consent/WebsiteConsentBanner.js +129 -0
- package/dist/esm/core/consent/WebsiteConsentBanner.js.map +1 -0
- package/dist/esm/core/consent/WebsiteConsentContext.d.ts +85 -0
- package/dist/esm/core/consent/WebsiteConsentContext.d.ts.map +1 -0
- package/dist/esm/core/consent/WebsiteConsentContext.js +89 -0
- package/dist/esm/core/consent/WebsiteConsentContext.js.map +1 -0
- package/dist/esm/core/consent/WebsiteConsentGate.d.ts +27 -0
- package/dist/esm/core/consent/WebsiteConsentGate.d.ts.map +1 -0
- package/dist/esm/core/consent/WebsiteConsentGate.js +9 -0
- package/dist/esm/core/consent/WebsiteConsentGate.js.map +1 -0
- package/dist/esm/core/consent/useWebsiteConsent.d.ts +10 -0
- package/dist/esm/core/consent/useWebsiteConsent.d.ts.map +1 -0
- package/dist/esm/core/consent/useWebsiteConsent.js +17 -0
- package/dist/esm/core/consent/useWebsiteConsent.js.map +1 -0
- package/dist/esm/core/consent/website-consent-storage.d.ts +22 -0
- package/dist/esm/core/consent/website-consent-storage.d.ts.map +1 -0
- package/dist/esm/core/consent/website-consent-storage.js +102 -0
- package/dist/esm/core/consent/website-consent-storage.js.map +1 -0
- package/dist/esm/core/contexts/WebsitePageContext.d.ts.map +1 -1
- package/dist/esm/core/contexts/WebsitePageContext.js.map +1 -1
- package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts +33 -6
- 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/WebsiteSectionContext.js.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 +1 -2
- package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts.map +1 -1
- package/dist/esm/core/external-providers/frontend-provider-registry.website.js.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 +43 -10
- package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts.map +1 -1
- package/dist/esm/core/external-providers/useWebsiteProviderScripts.js +92 -29
- 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 +21 -1
- package/dist/esm/core/layouts/WebsitePageLayout.d.ts.map +1 -1
- package/dist/esm/core/layouts/WebsitePageLayout.js +39 -8
- 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 +36 -65
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +36 -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 +61 -0
- package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.d.ts.map +1 -0
- package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.js +73 -0
- package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.js.map +1 -0
- 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 +6 -5
- 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-category.shared.js.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 +6 -29
- package/src/__tests__/bundle-isolation.test.ts +19 -22
- package/src/astro/__tests__/blog-post-bridge.test.tsx +58 -1
- package/src/astro/__tests__/bridge-runtime.test.tsx +44 -1
- package/src/astro/__tests__/label-pack-loader.test.ts +29 -1
- package/src/astro/blog-post-bridge.tsx +36 -26
- package/src/astro/bridge-runtime.tsx +79 -33
- package/src/astro/label-pack-loader.ts +12 -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 +27 -15
- package/src/config/load-website-pricing-catalog.ts +52 -0
- package/src/config/load-website-site-context.ts +145 -0
- package/src/config/wildo-website-config.schemas.ts +10 -5
- package/src/config-loader.ts +2 -0
- package/src/core/__tests__/WebsitePageLayout.test.tsx +1 -1
- package/src/core/__tests__/contexts.test.tsx +20 -1
- package/src/core/__tests__/website-consent.test.tsx +312 -0
- 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/__tests__/inbound-contact-form.schema.test.ts +11 -4
- package/src/core/anonymous-session/inbound-contact-form.schema.ts +30 -13
- package/src/core/anonymous-session/website-anonymous-session-client.ts +52 -3
- package/src/core/anonymous-session/website-submission-challenge.ts +99 -0
- package/src/core/consent/WebsiteConsentBanner.tsx +223 -0
- package/src/core/consent/WebsiteConsentContext.tsx +164 -0
- package/src/core/consent/WebsiteConsentGate.tsx +34 -0
- package/src/core/consent/useWebsiteConsent.ts +18 -0
- package/src/core/consent/website-consent-storage.ts +116 -0
- package/src/core/contexts/WebsiteRuntimeContext.tsx +33 -6
- 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/frontend-provider-registry.website.ts +1 -2
- 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 +114 -40
- 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 +75 -7
- package/src/index.ts +38 -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 +83 -0
- package/src/schemas/label-keys/website-consent-label-keys.schemas.ts +78 -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
- package/dist/esm/core/external-providers/provider-scripts.website.d.ts +0 -68
- package/dist/esm/core/external-providers/provider-scripts.website.d.ts.map +0 -1
- package/dist/esm/core/external-providers/provider-scripts.website.js +0 -99
- package/dist/esm/core/external-providers/provider-scripts.website.js.map +0 -1
- package/src/core/external-providers/__tests__/provider-scripts.website.test.ts +0 -146
- package/src/core/external-providers/provider-scripts.website.ts +0 -126
|
@@ -19,10 +19,36 @@ export function useWebsiteRuntime() {
|
|
|
19
19
|
const value = useContext(WebsiteRuntimeContext);
|
|
20
20
|
if (value === null) {
|
|
21
21
|
throw new Error('[useWebsiteRuntime] must be called inside <WebsiteRuntimeContextProvider>. ' +
|
|
22
|
-
'In Astro pages this is provided by
|
|
23
|
-
'
|
|
22
|
+
'In Astro pages this is provided by the page\'s bridge island (`createWebsitePageBridgeIsland` ' +
|
|
23
|
+
'from @wildo-ai/saas-website/astro-island) — make sure the page renders its bridge instead of ' +
|
|
24
|
+
'rendering React components directly.');
|
|
24
25
|
}
|
|
25
26
|
return value;
|
|
26
27
|
}
|
|
27
28
|
/** @wildo_source:part:end saas.website.use-runtime.hook */
|
|
29
|
+
/**
|
|
30
|
+
* @wildo_source:part:start saas.website.use-pricing-catalog.hook facet:layer:core facet:family:website facet:audience:app-developer
|
|
31
|
+
*
|
|
32
|
+
* `useWebsitePricingCatalog()` — the application's public pricing catalogue, for a pricing section.
|
|
33
|
+
*
|
|
34
|
+
* The plans, their prices, which one is recommended, how much of each price may be stated, and which
|
|
35
|
+
* are announced but not yet on sale — all derived at build time from the billing catalogue the
|
|
36
|
+
* backend sells from. A pricing section renders from THIS and never keeps its own copy of a price:
|
|
37
|
+
* a copy is a price that will disagree with the one the application charges.
|
|
38
|
+
*
|
|
39
|
+
* Throws when the page did not pass the catalogue to its bridge island: a pricing section with no
|
|
40
|
+
* plans would silently state that the application sells nothing. The page's `.astro` frontmatter
|
|
41
|
+
* loads it with `loadWebsitePricingCatalog()` from `@wildo-ai/saas-website/config-loader` and passes
|
|
42
|
+
* it as the island's `pricingCatalog` prop.
|
|
43
|
+
*/
|
|
44
|
+
export function useWebsitePricingCatalog() {
|
|
45
|
+
const { pricingCatalog } = useWebsiteRuntime();
|
|
46
|
+
if (pricingCatalog === undefined) {
|
|
47
|
+
throw new Error('[useWebsitePricingCatalog] this page did not pass a pricing catalogue to its bridge island. In the page\'s '
|
|
48
|
+
+ '`.astro` frontmatter, load it with `await loadWebsitePricingCatalog()` (the @wildo-ai/saas-website/config-loader '
|
|
49
|
+
+ 'subpath) and pass `pricingCatalog={pricingCatalog}` to the island.');
|
|
50
|
+
}
|
|
51
|
+
return pricingCatalog;
|
|
52
|
+
}
|
|
53
|
+
/** @wildo_source:part:end saas.website.use-pricing-catalog.hook */
|
|
28
54
|
//# sourceMappingURL=useWebsiteRuntime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebsiteRuntime.js","sourceRoot":"","sources":["../../../../../src/core/contexts/useWebsiteRuntime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useWebsiteRuntime.js","sourceRoot":"","sources":["../../../../../src/core/contexts/useWebsiteRuntime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAInC,OAAO,EACL,qBAAqB,GAEtB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,qBAAqB,CAAC,CAAC;IAChD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,6EAA6E;YAC3E,gGAAgG;YAChG,+FAA+F;YAC/F,sCAAsC,CACzC,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AACD,2DAA2D;AAE3D;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,wBAAwB;IACtC,MAAM,EAAE,cAAc,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAC/C,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,6GAA6G;cACzG,mHAAmH;cACnH,oEAAoE,CACzE,CAAC;IACJ,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC;AACD,mEAAmE","sourcesContent":["import { useContext } from 'react';\n\nimport type { PublicPricingCatalog } from '@wildo-ai/saas-models/public-runtime';\n\nimport {\n WebsiteRuntimeContext,\n type WebsiteRuntimeContextValue,\n} from './WebsiteRuntimeContext';\n\n/**\n * @wildo_source:part:start saas.website.use-runtime.hook facet:layer:core facet:family:website\n *\n * `useWebsiteRuntime()` — read access to the\n * `WebsiteRuntimeContextValue`. Throws if invoked outside of a\n * `<WebsiteRuntimeContextProvider>` so a missing host adapter is\n * detected at the first hook call (fail-loud).\n *\n * Section authors usually do NOT call this directly — prefer the\n * higher-level hooks (`useWebsiteLabel`, `useWebsiteLabelByKey`,\n * `useWebsiteDesignTokens`) which expose only the slice they need.\n * Direct access is reserved for layout primitives and locale-aware\n * formatters that legitimately need the active locale or the raw\n * label pack.\n */\nexport function useWebsiteRuntime(): WebsiteRuntimeContextValue {\n const value = useContext(WebsiteRuntimeContext);\n if (value === null) {\n throw new Error(\n '[useWebsiteRuntime] must be called inside <WebsiteRuntimeContextProvider>. ' +\n 'In Astro pages this is provided by the page\\'s bridge island (`createWebsitePageBridgeIsland` ' +\n 'from @wildo-ai/saas-website/astro-island) — make sure the page renders its bridge instead of ' +\n 'rendering React components directly.',\n );\n }\n return value;\n}\n/** @wildo_source:part:end saas.website.use-runtime.hook */\n\n/**\n * @wildo_source:part:start saas.website.use-pricing-catalog.hook facet:layer:core facet:family:website facet:audience:app-developer\n *\n * `useWebsitePricingCatalog()` — the application's public pricing catalogue, for a pricing section.\n *\n * The plans, their prices, which one is recommended, how much of each price may be stated, and which\n * are announced but not yet on sale — all derived at build time from the billing catalogue the\n * backend sells from. A pricing section renders from THIS and never keeps its own copy of a price:\n * a copy is a price that will disagree with the one the application charges.\n *\n * Throws when the page did not pass the catalogue to its bridge island: a pricing section with no\n * plans would silently state that the application sells nothing. The page's `.astro` frontmatter\n * loads it with `loadWebsitePricingCatalog()` from `@wildo-ai/saas-website/config-loader` and passes\n * it as the island's `pricingCatalog` prop.\n */\nexport function useWebsitePricingCatalog(): PublicPricingCatalog {\n const { pricingCatalog } = useWebsiteRuntime();\n if (pricingCatalog === undefined) {\n throw new Error(\n '[useWebsitePricingCatalog] this page did not pass a pricing catalogue to its bridge island. In the page\\'s '\n + '`.astro` frontmatter, load it with `await loadWebsitePricingCatalog()` (the @wildo-ai/saas-website/config-loader '\n + 'subpath) and pass `pricingCatalog={pricingCatalog}` to the island.',\n );\n }\n return pricingCatalog;\n}\n/** @wildo_source:part:end saas.website.use-pricing-catalog.hook */\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebsiteSection.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"useWebsiteSection.d.ts","sourceRoot":"","sources":["../../../../../src/core/contexts/useWebsiteSection.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,0BAA0B,CAW9D;AACD,2DAA2D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { ConsentPurpose, ProviderCapability } from '@wildo-ai/saas-models/public-runtime';
|
|
3
|
+
/**
|
|
4
|
+
* @wildo_source:part:start saas.website.provider-component.slot facet:layer:core facet:family:website
|
|
5
|
+
*
|
|
6
|
+
* `<WebsiteProviderComponent>` — places ONE provider's component on a website page (#1703): an
|
|
7
|
+
* address lookup on a contact form, a vendor widget on a marketing page. The component comes from the
|
|
8
|
+
* provider's own `components[capability]` declaration, the same one the application reads, through
|
|
9
|
+
* the website's provider-component registry.
|
|
10
|
+
*
|
|
11
|
+
* What it guarantees, so a page does not have to:
|
|
12
|
+
*
|
|
13
|
+
* - **Nothing of the vendor reaches the static HTML.** The component loads from an effect, which never
|
|
14
|
+
* runs during the Astro server render, and the registry itself refuses to load where there is no
|
|
15
|
+
* browser document. The server renders only the reserved box.
|
|
16
|
+
* - **No layout shift.** `reservedMinHeight` applies to the box in EVERY state — the server
|
|
17
|
+
* placeholder, loading, the refusals and the loaded component — so the page does not jump when the
|
|
18
|
+
* component arrives. Set it to the component's rendered height.
|
|
19
|
+
* - **The vendor's scripts load with the component**, not on every page: the layout injects at startup
|
|
20
|
+
* only the scripts of providers that contribute no component.
|
|
21
|
+
* - **Consent before download.** A provider resting on a purpose the visitor has not granted downloads
|
|
22
|
+
* nothing; the box states that the feature is off, names the purposes it rests on, and offers the
|
|
23
|
+
* consent panel (`openSettings`). Granting there loads the component in place.
|
|
24
|
+
* - **Announced, and operable by keyboard.** The box holds a `role="status"` live region from the first
|
|
25
|
+
* render, so a refusal is announced when it appears; the consent button sits outside it, so it is not
|
|
26
|
+
* read out as part of the message, and is a real button.
|
|
27
|
+
* - **A failure says so.** A component that could not load renders the site's `unavailable` wording,
|
|
28
|
+
* never a blank. A provider that offers no component here (left off in this environment) renders the
|
|
29
|
+
* empty box, since there is nothing to retry.
|
|
30
|
+
*
|
|
31
|
+
* The words come from the site's label pack: `website.chrome.provider-component.consent-required` and
|
|
32
|
+
* `.unavailable` are framework-owned keys every pack must carry, and the purpose names and the button
|
|
33
|
+
* reuse the consent panel's keys. Pass `renderConsentRequired` to word the refusal in the page's own
|
|
34
|
+
* chrome instead; it receives the missing purposes and the action that opens the panel, and then owns
|
|
35
|
+
* its own announcement.
|
|
36
|
+
*
|
|
37
|
+
* It does not choose a provider. Pass the ref the page chose — `useWebsiteProviderComponentChoices`
|
|
38
|
+
* lists the candidates — or `undefined` while nothing is chosen.
|
|
39
|
+
*/
|
|
40
|
+
export interface WebsiteProviderComponentProps<TProps extends object> {
|
|
41
|
+
/** The capability the component fills, e.g. `FRONTEND_ADDRESS_LOOKUP`. */
|
|
42
|
+
capability: ProviderCapability;
|
|
43
|
+
/** The provider the page chose, or `undefined` while nothing is chosen. */
|
|
44
|
+
providerRef: string | undefined;
|
|
45
|
+
/** The props the loaded component receives — the capability's slot contract. */
|
|
46
|
+
componentProps: TProps;
|
|
47
|
+
/** CSS length reserved for the box in every state, so the page does not shift when the component loads. */
|
|
48
|
+
reservedMinHeight?: string;
|
|
49
|
+
/** Replaces the default consent-required state. */
|
|
50
|
+
renderConsentRequired?: (input: {
|
|
51
|
+
missingPurposes: readonly ConsentPurpose[];
|
|
52
|
+
openConsentSettings: () => void;
|
|
53
|
+
}) => ReactNode;
|
|
54
|
+
}
|
|
55
|
+
export declare function WebsiteProviderComponent<TProps extends object>({ capability, providerRef, componentProps, reservedMinHeight, renderConsentRequired, }: WebsiteProviderComponentProps<TProps>): ReactNode;
|
|
56
|
+
/** @wildo_source:part:end saas.website.provider-component.slot */
|
|
57
|
+
//# sourceMappingURL=WebsiteProviderComponent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebsiteProviderComponent.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-providers/WebsiteProviderComponent.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9C,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AA6D/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,WAAW,6BAA6B,CAAC,MAAM,SAAS,MAAM;IAClE,0EAA0E;IAC1E,UAAU,EAAE,kBAAkB,CAAC;IAC/B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,gFAAgF;IAChF,cAAc,EAAE,MAAM,CAAC;IACvB,2GAA2G;IAC3G,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mDAAmD;IACnD,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;QAAC,mBAAmB,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,SAAS,CAAC;CAC/H;AAED,wBAAgB,wBAAwB,CAAC,MAAM,SAAS,MAAM,EAAE,EAC9D,UAAU,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,qBAAqB,GACtB,EAAE,6BAA6B,CAAC,MAAM,CAAC,GAAG,SAAS,CAgCnD;AACD,kEAAkE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ProviderComponentStatus } from '@wildo-ai/external-connectors-public/website';
|
|
3
|
+
import { websiteDesignTokenCssVar } from '../../schemas/design-tokens/website-design-tokens.shared.schemas.js';
|
|
4
|
+
import { WebsiteConsentLabelSlot, websiteConsentLabelKey, websiteConsentPurposeLabelKey, } from '../../schemas/label-keys/website-consent-label-keys.schemas.js';
|
|
5
|
+
import { WebsiteProviderComponentLabelSlot, websiteProviderComponentLabelKey, } from '../../schemas/label-keys/website-provider-component-label-keys.schemas.js';
|
|
6
|
+
import { useWebsiteConsent } from '../consent/useWebsiteConsent.js';
|
|
7
|
+
import { useWebsiteLabelByKey } from '../hooks/useWebsiteLabel.js';
|
|
8
|
+
import { useWebsiteProviderComponent } from './useWebsiteProviderComponent.js';
|
|
9
|
+
const STATE_STYLE = {
|
|
10
|
+
display: 'flex',
|
|
11
|
+
flexDirection: 'column',
|
|
12
|
+
gap: '0.5rem',
|
|
13
|
+
padding: '0.75rem',
|
|
14
|
+
border: `1px solid ${websiteDesignTokenCssVar('colors', 'border', '#ccc')}`,
|
|
15
|
+
borderRadius: websiteDesignTokenCssVar('radii', 'md', '0.375rem'),
|
|
16
|
+
};
|
|
17
|
+
const BUTTON_STYLE = {
|
|
18
|
+
alignSelf: 'flex-start',
|
|
19
|
+
padding: '0.5rem 1rem',
|
|
20
|
+
borderRadius: websiteDesignTokenCssVar('radii', 'md', '0.375rem'),
|
|
21
|
+
border: `1px solid ${websiteDesignTokenCssVar('colors', 'border', '#ccc')}`,
|
|
22
|
+
background: websiteDesignTokenCssVar('colors', 'background', '#fff'),
|
|
23
|
+
color: websiteDesignTokenCssVar('colors', 'foreground', '#000'),
|
|
24
|
+
cursor: 'pointer',
|
|
25
|
+
};
|
|
26
|
+
function PurposeName({ purpose }) {
|
|
27
|
+
return _jsx("li", { "data-website-provider-component-purpose": purpose, children: useWebsiteLabelByKey(websiteConsentPurposeLabelKey(purpose)) });
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The words announced for a refusal. Rendered INTO the always-present live region, so a screen reader
|
|
31
|
+
* announces the change when it happens; a region inserted already filled is often not announced at all.
|
|
32
|
+
*/
|
|
33
|
+
function RefusalStatement({ status, missingPurposes }) {
|
|
34
|
+
const consentRequired = useWebsiteLabelByKey(websiteProviderComponentLabelKey(WebsiteProviderComponentLabelSlot.CONSENT_REQUIRED));
|
|
35
|
+
const unavailable = useWebsiteLabelByKey(websiteProviderComponentLabelKey(WebsiteProviderComponentLabelSlot.UNAVAILABLE));
|
|
36
|
+
if (status === ProviderComponentStatus.UNAVAILABLE)
|
|
37
|
+
return _jsx("p", { style: { margin: 0 }, children: unavailable });
|
|
38
|
+
return (_jsxs(_Fragment, { children: [_jsx("p", { style: { margin: 0 }, children: consentRequired }), _jsx("ul", { style: { margin: 0 }, children: missingPurposes.map((purpose) => _jsx(PurposeName, { purpose: purpose }, purpose)) })] }));
|
|
39
|
+
}
|
|
40
|
+
function ManageConsentButton({ onClick }) {
|
|
41
|
+
return _jsx("button", { type: "button", style: BUTTON_STYLE, onClick: onClick, children: useWebsiteLabelByKey(websiteConsentLabelKey(WebsiteConsentLabelSlot.MANAGE)) });
|
|
42
|
+
}
|
|
43
|
+
export function WebsiteProviderComponent({ capability, providerRef, componentProps, reservedMinHeight, renderConsentRequired, }) {
|
|
44
|
+
const consent = useWebsiteConsent();
|
|
45
|
+
const { status, Component, missingConsentPurposes } = useWebsiteProviderComponent(capability, providerRef);
|
|
46
|
+
const isReady = status === ProviderComponentStatus.READY && Component !== null;
|
|
47
|
+
const isConsentRequired = status === ProviderComponentStatus.CONSENT_REQUIRED;
|
|
48
|
+
const isUnavailable = status === ProviderComponentStatus.UNAVAILABLE;
|
|
49
|
+
const useDefaultRefusal = isUnavailable || (isConsentRequired && renderConsentRequired === undefined);
|
|
50
|
+
// The registry cannot know a capability's props; the page states them through `componentProps`.
|
|
51
|
+
const Loaded = isReady ? Component : null;
|
|
52
|
+
return (_jsxs("div", { "data-website-provider-component": capability, "data-website-provider-component-state": status, "aria-busy": status === ProviderComponentStatus.LOADING ? true : undefined, style: reservedMinHeight !== undefined ? { minHeight: reservedMinHeight } : undefined, children: [Loaded !== null ? _jsx(Loaded, { ...componentProps }) : null, isConsentRequired && renderConsentRequired !== undefined
|
|
53
|
+
? renderConsentRequired({ missingPurposes: missingConsentPurposes, openConsentSettings: consent.openSettings })
|
|
54
|
+
: null, Loaded === null ? (_jsxs("div", { style: useDefaultRefusal ? STATE_STYLE : undefined, children: [_jsx("div", { role: "status", children: useDefaultRefusal ? _jsx(RefusalStatement, { status: status, missingPurposes: missingConsentPurposes }) : null }), isConsentRequired && renderConsentRequired === undefined ? _jsx(ManageConsentButton, { onClick: consent.openSettings }) : null] })) : null] }));
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=WebsiteProviderComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebsiteProviderComponent.js","sourceRoot":"","sources":["../../../../../src/core/external-providers/WebsiteProviderComponent.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAGvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,kEAAkE,CAAC;AAC5G,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,6BAA6B,GAC9B,MAAM,6DAA6D,CAAC;AACrE,OAAO,EACL,iCAAiC,EACjC,gCAAgC,GACjC,MAAM,wEAAwE,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAE5E,MAAM,WAAW,GAAwB;IACvC,OAAO,EAAE,MAAM;IACf,aAAa,EAAE,QAAQ;IACvB,GAAG,EAAE,QAAQ;IACb,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,aAAa,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE;IAC3E,YAAY,EAAE,wBAAwB,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC;CAClE,CAAC;AAEF,MAAM,YAAY,GAAwB;IACxC,SAAS,EAAE,YAAY;IACvB,OAAO,EAAE,aAAa;IACtB,YAAY,EAAE,wBAAwB,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC;IACjE,MAAM,EAAE,aAAa,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE;IAC3E,UAAU,EAAE,wBAAwB,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC;IACpE,KAAK,EAAE,wBAAwB,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC;IAC/D,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF,SAAS,WAAW,CAAC,EAAE,OAAO,EAA+B;IAC3D,OAAO,wDAA6C,OAAO,YAAG,oBAAoB,CAAC,6BAA6B,CAAC,OAAO,CAAC,CAAC,GAAM,CAAC;AACnI,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,EAAE,MAAM,EAAE,eAAe,EAAmF;IACpI,MAAM,eAAe,GAAG,oBAAoB,CAAC,gCAAgC,CAAC,iCAAiC,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACnI,MAAM,WAAW,GAAG,oBAAoB,CAAC,gCAAgC,CAAC,iCAAiC,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1H,IAAI,MAAM,KAAK,uBAAuB,CAAC,WAAW;QAAE,OAAO,YAAG,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,YAAG,WAAW,GAAK,CAAC;IACtG,OAAO,CACL,8BACE,YAAG,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,YAAG,eAAe,GAAK,EAC9C,aAAI,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,YACrB,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAC,WAAW,IAAe,OAAO,EAAE,OAAO,IAAzB,OAAO,CAAsB,CAAC,GAC/E,IACJ,CACJ,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,EAAE,OAAO,EAA2B;IAC/D,OAAO,iBAAQ,IAAI,EAAC,QAAQ,EAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,YAAG,oBAAoB,CAAC,sBAAsB,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,GAAU,CAAC;AAC9J,CAAC;AAoDD,MAAM,UAAU,wBAAwB,CAAwB,EAC9D,UAAU,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,qBAAqB,GACiB;IACtC,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,sBAAsB,EAAE,GAAG,2BAA2B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAE3G,MAAM,OAAO,GAAG,MAAM,KAAK,uBAAuB,CAAC,KAAK,IAAI,SAAS,KAAK,IAAI,CAAC;IAC/E,MAAM,iBAAiB,GAAG,MAAM,KAAK,uBAAuB,CAAC,gBAAgB,CAAC;IAC9E,MAAM,aAAa,GAAG,MAAM,KAAK,uBAAuB,CAAC,WAAW,CAAC;IACrE,MAAM,iBAAiB,GAAG,aAAa,IAAI,CAAC,iBAAiB,IAAI,qBAAqB,KAAK,SAAS,CAAC,CAAC;IACtG,gGAAgG;IAChG,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAE,SAAoD,CAAC,CAAC,CAAC,IAAI,CAAC;IAEtF,OAAO,CACL,kDACmC,UAAU,2CACJ,MAAM,eAClC,MAAM,KAAK,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EACxE,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,SAAS,aAEpF,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,KAAC,MAAM,OAAK,cAAc,GAAI,CAAC,CAAC,CAAC,IAAI,EACvD,iBAAiB,IAAI,qBAAqB,KAAK,SAAS;gBACvD,CAAC,CAAC,qBAAqB,CAAC,EAAE,eAAe,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;gBAC/G,CAAC,CAAC,IAAI,EACP,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CACjB,eAAK,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,aACrD,cAAK,IAAI,EAAC,QAAQ,YACf,iBAAiB,CAAC,CAAC,CAAC,KAAC,gBAAgB,IAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,sBAAsB,GAAI,CAAC,CAAC,CAAC,IAAI,GACrG,EACL,iBAAiB,IAAI,qBAAqB,KAAK,SAAS,CAAC,CAAC,CAAC,KAAC,mBAAmB,IAAC,OAAO,EAAE,OAAO,CAAC,YAAY,GAAI,CAAC,CAAC,CAAC,IAAI,IACrH,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC","sourcesContent":["import React, { type ReactNode } from 'react';\nimport { ProviderComponentStatus } from '@wildo-ai/external-connectors-public/website';\nimport type { ConsentPurpose, ProviderCapability } from '@wildo-ai/saas-models/public-runtime';\n\nimport { websiteDesignTokenCssVar } from '../../schemas/design-tokens/website-design-tokens.shared.schemas';\nimport {\n WebsiteConsentLabelSlot,\n websiteConsentLabelKey,\n websiteConsentPurposeLabelKey,\n} from '../../schemas/label-keys/website-consent-label-keys.schemas';\nimport {\n WebsiteProviderComponentLabelSlot,\n websiteProviderComponentLabelKey,\n} from '../../schemas/label-keys/website-provider-component-label-keys.schemas';\nimport { useWebsiteConsent } from '../consent/useWebsiteConsent';\nimport { useWebsiteLabelByKey } from '../hooks/useWebsiteLabel';\nimport { useWebsiteProviderComponent } from './useWebsiteProviderComponent';\n\nconst STATE_STYLE: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n padding: '0.75rem',\n border: `1px solid ${websiteDesignTokenCssVar('colors', 'border', '#ccc')}`,\n borderRadius: websiteDesignTokenCssVar('radii', 'md', '0.375rem'),\n};\n\nconst BUTTON_STYLE: React.CSSProperties = {\n alignSelf: 'flex-start',\n padding: '0.5rem 1rem',\n borderRadius: websiteDesignTokenCssVar('radii', 'md', '0.375rem'),\n border: `1px solid ${websiteDesignTokenCssVar('colors', 'border', '#ccc')}`,\n background: websiteDesignTokenCssVar('colors', 'background', '#fff'),\n color: websiteDesignTokenCssVar('colors', 'foreground', '#000'),\n cursor: 'pointer',\n};\n\nfunction PurposeName({ purpose }: { purpose: ConsentPurpose }): ReactNode {\n return <li data-website-provider-component-purpose={purpose}>{useWebsiteLabelByKey(websiteConsentPurposeLabelKey(purpose))}</li>;\n}\n\n/**\n * The words announced for a refusal. Rendered INTO the always-present live region, so a screen reader\n * announces the change when it happens; a region inserted already filled is often not announced at all.\n */\nfunction RefusalStatement({ status, missingPurposes }: { status: ProviderComponentStatus; missingPurposes: readonly ConsentPurpose[] }): ReactNode {\n const consentRequired = useWebsiteLabelByKey(websiteProviderComponentLabelKey(WebsiteProviderComponentLabelSlot.CONSENT_REQUIRED));\n const unavailable = useWebsiteLabelByKey(websiteProviderComponentLabelKey(WebsiteProviderComponentLabelSlot.UNAVAILABLE));\n if (status === ProviderComponentStatus.UNAVAILABLE) return <p style={{ margin: 0 }}>{unavailable}</p>;\n return (\n <>\n <p style={{ margin: 0 }}>{consentRequired}</p>\n <ul style={{ margin: 0 }}>\n {missingPurposes.map((purpose) => <PurposeName key={purpose} purpose={purpose} />)}\n </ul>\n </>\n );\n}\n\nfunction ManageConsentButton({ onClick }: { onClick: () => void }): ReactNode {\n return <button type=\"button\" style={BUTTON_STYLE} onClick={onClick}>{useWebsiteLabelByKey(websiteConsentLabelKey(WebsiteConsentLabelSlot.MANAGE))}</button>;\n}\n\n/**\n * @wildo_source:part:start saas.website.provider-component.slot facet:layer:core facet:family:website\n *\n * `<WebsiteProviderComponent>` — places ONE provider's component on a website page (#1703): an\n * address lookup on a contact form, a vendor widget on a marketing page. The component comes from the\n * provider's own `components[capability]` declaration, the same one the application reads, through\n * the website's provider-component registry.\n *\n * What it guarantees, so a page does not have to:\n *\n * - **Nothing of the vendor reaches the static HTML.** The component loads from an effect, which never\n * runs during the Astro server render, and the registry itself refuses to load where there is no\n * browser document. The server renders only the reserved box.\n * - **No layout shift.** `reservedMinHeight` applies to the box in EVERY state — the server\n * placeholder, loading, the refusals and the loaded component — so the page does not jump when the\n * component arrives. Set it to the component's rendered height.\n * - **The vendor's scripts load with the component**, not on every page: the layout injects at startup\n * only the scripts of providers that contribute no component.\n * - **Consent before download.** A provider resting on a purpose the visitor has not granted downloads\n * nothing; the box states that the feature is off, names the purposes it rests on, and offers the\n * consent panel (`openSettings`). Granting there loads the component in place.\n * - **Announced, and operable by keyboard.** The box holds a `role=\"status\"` live region from the first\n * render, so a refusal is announced when it appears; the consent button sits outside it, so it is not\n * read out as part of the message, and is a real button.\n * - **A failure says so.** A component that could not load renders the site's `unavailable` wording,\n * never a blank. A provider that offers no component here (left off in this environment) renders the\n * empty box, since there is nothing to retry.\n *\n * The words come from the site's label pack: `website.chrome.provider-component.consent-required` and\n * `.unavailable` are framework-owned keys every pack must carry, and the purpose names and the button\n * reuse the consent panel's keys. Pass `renderConsentRequired` to word the refusal in the page's own\n * chrome instead; it receives the missing purposes and the action that opens the panel, and then owns\n * its own announcement.\n *\n * It does not choose a provider. Pass the ref the page chose — `useWebsiteProviderComponentChoices`\n * lists the candidates — or `undefined` while nothing is chosen.\n */\nexport interface WebsiteProviderComponentProps<TProps extends object> {\n /** The capability the component fills, e.g. `FRONTEND_ADDRESS_LOOKUP`. */\n capability: ProviderCapability;\n /** The provider the page chose, or `undefined` while nothing is chosen. */\n providerRef: string | undefined;\n /** The props the loaded component receives — the capability's slot contract. */\n componentProps: TProps;\n /** CSS length reserved for the box in every state, so the page does not shift when the component loads. */\n reservedMinHeight?: string;\n /** Replaces the default consent-required state. */\n renderConsentRequired?: (input: { missingPurposes: readonly ConsentPurpose[]; openConsentSettings: () => void }) => ReactNode;\n}\n\nexport function WebsiteProviderComponent<TProps extends object>({\n capability,\n providerRef,\n componentProps,\n reservedMinHeight,\n renderConsentRequired,\n}: WebsiteProviderComponentProps<TProps>): ReactNode {\n const consent = useWebsiteConsent();\n const { status, Component, missingConsentPurposes } = useWebsiteProviderComponent(capability, providerRef);\n\n const isReady = status === ProviderComponentStatus.READY && Component !== null;\n const isConsentRequired = status === ProviderComponentStatus.CONSENT_REQUIRED;\n const isUnavailable = status === ProviderComponentStatus.UNAVAILABLE;\n const useDefaultRefusal = isUnavailable || (isConsentRequired && renderConsentRequired === undefined);\n // The registry cannot know a capability's props; the page states them through `componentProps`.\n const Loaded = isReady ? (Component as unknown as React.ComponentType<TProps>) : null;\n\n return (\n <div\n data-website-provider-component={capability}\n data-website-provider-component-state={status}\n aria-busy={status === ProviderComponentStatus.LOADING ? true : undefined}\n style={reservedMinHeight !== undefined ? { minHeight: reservedMinHeight } : undefined}\n >\n {Loaded !== null ? <Loaded {...componentProps} /> : null}\n {isConsentRequired && renderConsentRequired !== undefined\n ? renderConsentRequired({ missingPurposes: missingConsentPurposes, openConsentSettings: consent.openSettings })\n : null}\n {Loaded === null ? (\n <div style={useDefaultRefusal ? STATE_STYLE : undefined}>\n <div role=\"status\">\n {useDefaultRefusal ? <RefusalStatement status={status} missingPurposes={missingConsentPurposes} /> : null}\n </div>\n {isConsentRequired && renderConsentRequired === undefined ? <ManageConsentButton onClick={consent.openSettings} /> : null}\n </div>\n ) : null}\n </div>\n );\n}\n/** @wildo_source:part:end saas.website.provider-component.slot */\n"]}
|
|
@@ -28,8 +28,7 @@ export interface CreateFrontendWebsiteProviderRegistryInput {
|
|
|
28
28
|
readonly entries?: FrontendProvidersBlock;
|
|
29
29
|
/**
|
|
30
30
|
* The CODE channel. Absent is the ordinary case for a website that declares
|
|
31
|
-
* no
|
|
32
|
-
* so there is nothing to import and nothing to pass.
|
|
31
|
+
* no providers. Config sync still writes an empty map so startup imports remain stable.
|
|
33
32
|
*/
|
|
34
33
|
readonly modules?: GeneratedWebsiteProviderModules;
|
|
35
34
|
readonly onDiagnostic?: (diagnostic: FrontendProviderHydrationDiagnostic) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend-provider-registry.website.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"frontend-provider-registry.website.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-providers/frontend-provider-registry.website.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,8CAA8C,CAAC;AAExG,YAAY,EACV,6BAA6B,EAC7B,0BAA0B,EAC1B,+BAA+B,EAC/B,+BAA+B,GAChC,MAAM,8CAA8C,CAAC;AAEtD,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,8CAA8C,CAAC;AAEpG,MAAM,WAAW,0CAA0C;IACzD,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,sBAAsB,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,+BAA+B,CAAC;IACnD,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,mCAAmC,KAAK,IAAI,CAAC;CACnF;AAED;;GAEG;AACH,wBAAgB,qCAAqC,CACnD,KAAK,CAAC,EAAE,0CAA0C,GACjD,+BAA+B,CAMjC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontend-provider-registry.website.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"frontend-provider-registry.website.js","sourceRoot":"","sources":["../../../../../src/core/external-providers/frontend-provider-registry.website.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EACL,uCAAuC,GAExC,MAAM,8CAA8C,CAAC;AAwBtD;;GAEG;AACH,MAAM,UAAU,qCAAqC,CACnD,KAAkD;IAElD,OAAO,uCAAuC,CAAC;QAC7C,OAAO,EAAE,KAAK,EAAE,OAAO;QACvB,OAAO,EAAE,KAAK,EAAE,OAAO;QACvB,YAAY,EAAE,KAAK,EAAE,YAAY;KAClC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * The website's provider registry — now composed from BOTH channels.\n *\n * It used to build itself by casting each served `FrontendProviderEntry` to a\n * module type that was an alias of that entry, which made the cast true and\n * useless: the registry could never hold anything a JSON payload could not\n * carry, so a provider's SDK loader had nowhere to arrive.\n *\n * - **DATA** — the materialized frontend-services block for THIS website\n * service. Authoritative for which providers are on, in this environment.\n * - **CODE** — `generatedFrontendProviderModules` from the website's own\n * `src/generated/frontend-providers.generated.ts`, written by\n * `wildo config sync` and handed in by the Astro bridge.\n *\n * The types are now the engine's own website-facet types rather than local\n * aliases, so a provider module authored against\n * `@wildo-ai/external-connectors-public/website` is the same type this registry\n * holds. Re-exported under the same names so every consumer's import is\n * unchanged.\n */\n\nimport {\n composeHydratedFrontendWebsiteProviders,\n type GeneratedWebsiteProviderModules,\n} from '@wildo-ai/external-connectors-public/website';\nimport type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';\nimport type { FrontendProviderHydrationDiagnostic } from '@wildo-ai/external-connectors-public/website';\n\nexport type {\n FrontendWebsiteProviderModule,\n FrontendWebsiteProviderRef,\n FrontendWebsiteProviderRegistry,\n GeneratedWebsiteProviderModules,\n} from '@wildo-ai/external-connectors-public/website';\n\nimport type { FrontendWebsiteProviderRegistry } from '@wildo-ai/external-connectors-public/website';\n\nexport interface CreateFrontendWebsiteProviderRegistryInput {\n /** The DATA channel: the materialized block for this website service. */\n readonly entries?: FrontendProvidersBlock;\n /**\n * The CODE channel. Absent is the ordinary case for a website that declares\n * no providers. Config sync still writes an empty map so startup imports remain stable.\n */\n readonly modules?: GeneratedWebsiteProviderModules;\n readonly onDiagnostic?: (diagnostic: FrontendProviderHydrationDiagnostic) => void;\n}\n\n/**\n * Website-side public-provider registry, hydrated from both channels.\n */\nexport function createFrontendWebsiteProviderRegistry(\n input?: CreateFrontendWebsiteProviderRegistryInput,\n): FrontendWebsiteProviderRegistry {\n return composeHydratedFrontendWebsiteProviders({\n entries: input?.entries,\n modules: input?.modules,\n onDiagnostic: input?.onDiagnostic,\n });\n}\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The website's provider-component registry (#1703): the SAME registry the application builds, from
|
|
3
|
+
* the SAME `components` declaration, over the website's live provider modules.
|
|
4
|
+
*
|
|
5
|
+
* A provider declares its components once, on its frontend module. Before this, the website facet
|
|
6
|
+
* type accepted `components` and nothing on the website read it, so a contact form could not host an
|
|
7
|
+
* address lookup (#561) and a marketing page could not host any provider component at all. The
|
|
8
|
+
* registry is `createProviderComponentRegistry` from `@wildo-ai/external-connectors-public/website`
|
|
9
|
+
* — keyed `capability:providerRef`, consent-gated, script-before-component and browser-only — and
|
|
10
|
+
* this file only feeds it the website's two inputs:
|
|
11
|
+
*
|
|
12
|
+
* - the HYDRATED website provider registry (both channels joined, so a provider not live for this
|
|
13
|
+
* website service contributes no component);
|
|
14
|
+
* - the loaders the site's facet library contributes for components an engine provider module
|
|
15
|
+
* declares but cannot import (the module lives in a renderer-free package), handed in through the
|
|
16
|
+
* page bridge's `providerComponentLoaders` rather than through module-level state, because the
|
|
17
|
+
* bridge is the website's one construction site and can carry it explicitly.
|
|
18
|
+
*
|
|
19
|
+
* Nothing is loaded here. A component loads when a `WebsiteProviderComponent` slot asks for it, from
|
|
20
|
+
* an effect — never during the Astro server render.
|
|
21
|
+
*/
|
|
22
|
+
import { type ComponentType } from 'react';
|
|
23
|
+
import { type ProviderComponentDiagnostic, type ProviderComponentLoaderContribution, type ProviderComponentRegistry } from '@wildo-ai/external-connectors-public/website';
|
|
24
|
+
import type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';
|
|
25
|
+
import { type FrontendWebsiteProviderRegistry, type GeneratedWebsiteProviderModules } from './frontend-provider-registry.website';
|
|
26
|
+
/** A component type the website's registry hands back once loaded. */
|
|
27
|
+
export type WebsiteProviderComponentType = ComponentType<never>;
|
|
28
|
+
/** The website's provider-component registry: the shared registry, holding React components. */
|
|
29
|
+
export type WebsiteProviderComponentRegistry = ProviderComponentRegistry<WebsiteProviderComponentType>;
|
|
30
|
+
export interface CreateWebsiteProviderComponentRegistryInput {
|
|
31
|
+
/** The hydrated website provider registry; absent on a site with no providers. */
|
|
32
|
+
readonly providerRegistry: FrontendWebsiteProviderRegistry | undefined;
|
|
33
|
+
/** Loaders the site's facet library contributes for engine-declared components. */
|
|
34
|
+
readonly providerComponentLoaders?: Iterable<ProviderComponentLoaderContribution>;
|
|
35
|
+
/**
|
|
36
|
+
* Where a registration or load problem is reported. Defaults to `console.warn`: the website runtime
|
|
37
|
+
* has no logger service, and a provider component that cannot be offered is a developer-facing fact
|
|
38
|
+
* that must not be silent.
|
|
39
|
+
*/
|
|
40
|
+
readonly onDiagnostic?: (diagnostic: ProviderComponentDiagnostic) => void;
|
|
41
|
+
}
|
|
42
|
+
/** Builds the website's provider-component registry from its live provider modules. */
|
|
43
|
+
export declare function createWebsiteProviderComponentRegistry(input: CreateWebsiteProviderComponentRegistryInput): WebsiteProviderComponentRegistry;
|
|
44
|
+
/** What a page bridge builds once per island: the hydrated provider registry and its component registry. */
|
|
45
|
+
export interface WebsiteProviderRegistries {
|
|
46
|
+
readonly frontendProviderRegistry: FrontendWebsiteProviderRegistry;
|
|
47
|
+
readonly providerComponentRegistry: WebsiteProviderComponentRegistry;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Builds both registries for a page bridge, MEMOISED on their inputs.
|
|
51
|
+
*
|
|
52
|
+
* Memoised because the component registry caches each loaded component: a registry rebuilt on every
|
|
53
|
+
* render would forget what it loaded, hand every slot a new registry identity, and make each slot's
|
|
54
|
+
* load effect run again — a download loop. The inputs are an island prop (`frontendProviders`, fixed
|
|
55
|
+
* for the island's life) and two module-load-time bridge dependencies, so in practice this builds
|
|
56
|
+
* once per island.
|
|
57
|
+
*/
|
|
58
|
+
export declare function useWebsiteProviderRegistries(input: {
|
|
59
|
+
readonly frontendProviders: FrontendProvidersBlock | undefined;
|
|
60
|
+
readonly frontendProviderModules: GeneratedWebsiteProviderModules | undefined;
|
|
61
|
+
readonly providerComponentLoaders: Iterable<ProviderComponentLoaderContribution> | undefined;
|
|
62
|
+
}): WebsiteProviderRegistries;
|
|
63
|
+
//# sourceMappingURL=provider-component-registry.website.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-component-registry.website.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-providers/provider-component-registry.website.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,mCAAmC,EACxC,KAAK,yBAAyB,EAC/B,MAAM,8CAA8C,CAAC;AAEtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAEnF,OAAO,EAEL,KAAK,+BAA+B,EACpC,KAAK,+BAA+B,EACrC,MAAM,sCAAsC,CAAC;AAE9C,sEAAsE;AACtE,MAAM,MAAM,4BAA4B,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;AAEhE,gGAAgG;AAChG,MAAM,MAAM,gCAAgC,GAAG,yBAAyB,CAAC,4BAA4B,CAAC,CAAC;AAEvG,MAAM,WAAW,2CAA2C;IAC1D,kFAAkF;IAClF,QAAQ,CAAC,gBAAgB,EAAE,+BAA+B,GAAG,SAAS,CAAC;IACvE,mFAAmF;IACnF,QAAQ,CAAC,wBAAwB,CAAC,EAAE,QAAQ,CAAC,mCAAmC,CAAC,CAAC;IAClF;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,2BAA2B,KAAK,IAAI,CAAC;CAC3E;AAWD,uFAAuF;AACvF,wBAAgB,sCAAsC,CACpD,KAAK,EAAE,2CAA2C,GACjD,gCAAgC,CAOlC;AAED,4GAA4G;AAC5G,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,wBAAwB,EAAE,+BAA+B,CAAC;IACnE,QAAQ,CAAC,yBAAyB,EAAE,gCAAgC,CAAC;CACtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC/D,QAAQ,CAAC,uBAAuB,EAAE,+BAA+B,GAAG,SAAS,CAAC;IAC9E,QAAQ,CAAC,wBAAwB,EAAE,QAAQ,CAAC,mCAAmC,CAAC,GAAG,SAAS,CAAC;CAC9F,GAAG,yBAAyB,CAe5B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The website's provider-component registry (#1703): the SAME registry the application builds, from
|
|
3
|
+
* the SAME `components` declaration, over the website's live provider modules.
|
|
4
|
+
*
|
|
5
|
+
* A provider declares its components once, on its frontend module. Before this, the website facet
|
|
6
|
+
* type accepted `components` and nothing on the website read it, so a contact form could not host an
|
|
7
|
+
* address lookup (#561) and a marketing page could not host any provider component at all. The
|
|
8
|
+
* registry is `createProviderComponentRegistry` from `@wildo-ai/external-connectors-public/website`
|
|
9
|
+
* — keyed `capability:providerRef`, consent-gated, script-before-component and browser-only — and
|
|
10
|
+
* this file only feeds it the website's two inputs:
|
|
11
|
+
*
|
|
12
|
+
* - the HYDRATED website provider registry (both channels joined, so a provider not live for this
|
|
13
|
+
* website service contributes no component);
|
|
14
|
+
* - the loaders the site's facet library contributes for components an engine provider module
|
|
15
|
+
* declares but cannot import (the module lives in a renderer-free package), handed in through the
|
|
16
|
+
* page bridge's `providerComponentLoaders` rather than through module-level state, because the
|
|
17
|
+
* bridge is the website's one construction site and can carry it explicitly.
|
|
18
|
+
*
|
|
19
|
+
* Nothing is loaded here. A component loads when a `WebsiteProviderComponent` slot asks for it, from
|
|
20
|
+
* an effect — never during the Astro server render.
|
|
21
|
+
*/
|
|
22
|
+
import { useMemo } from 'react';
|
|
23
|
+
import { collectProviderComponentRegistrations, createProviderComponentRegistry, indexProviderComponentLoaderContributions, } from '@wildo-ai/external-connectors-public/website';
|
|
24
|
+
import { createFrontendWebsiteProviderRegistry, } from './frontend-provider-registry.website.js';
|
|
25
|
+
function warnProviderComponentDiagnostic(diagnostic) {
|
|
26
|
+
// Reported in the BROWSER only. The registry is built during the server render or prerender too, once per
|
|
27
|
+
// page, and a declaration nothing can load would otherwise fill the build log with one warning per page
|
|
28
|
+
// for a fact the first page already stated. The browser is where somebody can see and act on it.
|
|
29
|
+
if (typeof document === 'undefined')
|
|
30
|
+
return;
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.warn(`[wildo-provider-component] ${diagnostic.kind}: ${diagnostic.message}`);
|
|
33
|
+
}
|
|
34
|
+
/** Builds the website's provider-component registry from its live provider modules. */
|
|
35
|
+
export function createWebsiteProviderComponentRegistry(input) {
|
|
36
|
+
const onDiagnostic = input.onDiagnostic ?? warnProviderComponentDiagnostic;
|
|
37
|
+
const registrations = collectProviderComponentRegistrations(input.providerRegistry?.modules.values() ?? [], {
|
|
38
|
+
contributedLoaders: indexProviderComponentLoaderContributions(input.providerComponentLoaders),
|
|
39
|
+
onDiagnostic,
|
|
40
|
+
});
|
|
41
|
+
return createProviderComponentRegistry({ registrations, onDiagnostic });
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Builds both registries for a page bridge, MEMOISED on their inputs.
|
|
45
|
+
*
|
|
46
|
+
* Memoised because the component registry caches each loaded component: a registry rebuilt on every
|
|
47
|
+
* render would forget what it loaded, hand every slot a new registry identity, and make each slot's
|
|
48
|
+
* load effect run again — a download loop. The inputs are an island prop (`frontendProviders`, fixed
|
|
49
|
+
* for the island's life) and two module-load-time bridge dependencies, so in practice this builds
|
|
50
|
+
* once per island.
|
|
51
|
+
*/
|
|
52
|
+
export function useWebsiteProviderRegistries(input) {
|
|
53
|
+
const { frontendProviders, frontendProviderModules, providerComponentLoaders } = input;
|
|
54
|
+
return useMemo(() => {
|
|
55
|
+
const frontendProviderRegistry = createFrontendWebsiteProviderRegistry({
|
|
56
|
+
entries: frontendProviders,
|
|
57
|
+
modules: frontendProviderModules,
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
frontendProviderRegistry,
|
|
61
|
+
providerComponentRegistry: createWebsiteProviderComponentRegistry({
|
|
62
|
+
providerRegistry: frontendProviderRegistry,
|
|
63
|
+
...(providerComponentLoaders !== undefined ? { providerComponentLoaders } : {}),
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
}, [frontendProviders, frontendProviderModules, providerComponentLoaders]);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=provider-component-registry.website.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-component-registry.website.js","sourceRoot":"","sources":["../../../../../src/core/external-providers/provider-component-registry.website.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,OAAO,EAAsB,MAAM,OAAO,CAAC;AACpD,OAAO,EACL,qCAAqC,EACrC,+BAA+B,EAC/B,yCAAyC,GAI1C,MAAM,8CAA8C,CAAC;AAItD,OAAO,EACL,qCAAqC,GAGtC,MAAM,sCAAsC,CAAC;AAqB9C,SAAS,+BAA+B,CAAC,UAAuC;IAC9E,0GAA0G;IAC1G,wGAAwG;IACxG,iGAAiG;IACjG,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC5C,sCAAsC;IACtC,OAAO,CAAC,IAAI,CAAC,8BAA8B,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,sCAAsC,CACpD,KAAkD;IAElD,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,+BAA+B,CAAC;IAC3E,MAAM,aAAa,GAAG,qCAAqC,CAAC,KAAK,CAAC,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QAC1G,kBAAkB,EAAE,yCAAyC,CAAC,KAAK,CAAC,wBAAwB,CAAC;QAC7F,YAAY;KACb,CAAC,CAAC;IACH,OAAO,+BAA+B,CAA+B,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC,CAAC;AACxG,CAAC;AAQD;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAI5C;IACC,MAAM,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,GAAG,KAAK,CAAC;IACvF,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,wBAAwB,GAAG,qCAAqC,CAAC;YACrE,OAAO,EAAE,iBAAiB;YAC1B,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAC;QACH,OAAO;YACL,wBAAwB;YACxB,yBAAyB,EAAE,sCAAsC,CAAC;gBAChE,gBAAgB,EAAE,wBAAwB;gBAC1C,GAAG,CAAC,wBAAwB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,wBAAwB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChF,CAAC;SACH,CAAC;IACJ,CAAC,EAAE,CAAC,iBAAiB,EAAE,uBAAuB,EAAE,wBAAwB,CAAC,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["/**\n * The website's provider-component registry (#1703): the SAME registry the application builds, from\n * the SAME `components` declaration, over the website's live provider modules.\n *\n * A provider declares its components once, on its frontend module. Before this, the website facet\n * type accepted `components` and nothing on the website read it, so a contact form could not host an\n * address lookup (#561) and a marketing page could not host any provider component at all. The\n * registry is `createProviderComponentRegistry` from `@wildo-ai/external-connectors-public/website`\n * — keyed `capability:providerRef`, consent-gated, script-before-component and browser-only — and\n * this file only feeds it the website's two inputs:\n *\n * - the HYDRATED website provider registry (both channels joined, so a provider not live for this\n * website service contributes no component);\n * - the loaders the site's facet library contributes for components an engine provider module\n * declares but cannot import (the module lives in a renderer-free package), handed in through the\n * page bridge's `providerComponentLoaders` rather than through module-level state, because the\n * bridge is the website's one construction site and can carry it explicitly.\n *\n * Nothing is loaded here. A component loads when a `WebsiteProviderComponent` slot asks for it, from\n * an effect — never during the Astro server render.\n */\n\nimport { useMemo, type ComponentType } from 'react';\nimport {\n collectProviderComponentRegistrations,\n createProviderComponentRegistry,\n indexProviderComponentLoaderContributions,\n type ProviderComponentDiagnostic,\n type ProviderComponentLoaderContribution,\n type ProviderComponentRegistry,\n} from '@wildo-ai/external-connectors-public/website';\n\nimport type { FrontendProvidersBlock } from '@wildo-ai/saas-models/public-runtime';\n\nimport {\n createFrontendWebsiteProviderRegistry,\n type FrontendWebsiteProviderRegistry,\n type GeneratedWebsiteProviderModules,\n} from './frontend-provider-registry.website';\n\n/** A component type the website's registry hands back once loaded. */\nexport type WebsiteProviderComponentType = ComponentType<never>;\n\n/** The website's provider-component registry: the shared registry, holding React components. */\nexport type WebsiteProviderComponentRegistry = ProviderComponentRegistry<WebsiteProviderComponentType>;\n\nexport interface CreateWebsiteProviderComponentRegistryInput {\n /** The hydrated website provider registry; absent on a site with no providers. */\n readonly providerRegistry: FrontendWebsiteProviderRegistry | undefined;\n /** Loaders the site's facet library contributes for engine-declared components. */\n readonly providerComponentLoaders?: Iterable<ProviderComponentLoaderContribution>;\n /**\n * Where a registration or load problem is reported. Defaults to `console.warn`: the website runtime\n * has no logger service, and a provider component that cannot be offered is a developer-facing fact\n * that must not be silent.\n */\n readonly onDiagnostic?: (diagnostic: ProviderComponentDiagnostic) => void;\n}\n\nfunction warnProviderComponentDiagnostic(diagnostic: ProviderComponentDiagnostic): void {\n // Reported in the BROWSER only. The registry is built during the server render or prerender too, once per\n // page, and a declaration nothing can load would otherwise fill the build log with one warning per page\n // for a fact the first page already stated. The browser is where somebody can see and act on it.\n if (typeof document === 'undefined') return;\n // eslint-disable-next-line no-console\n console.warn(`[wildo-provider-component] ${diagnostic.kind}: ${diagnostic.message}`);\n}\n\n/** Builds the website's provider-component registry from its live provider modules. */\nexport function createWebsiteProviderComponentRegistry(\n input: CreateWebsiteProviderComponentRegistryInput,\n): WebsiteProviderComponentRegistry {\n const onDiagnostic = input.onDiagnostic ?? warnProviderComponentDiagnostic;\n const registrations = collectProviderComponentRegistrations(input.providerRegistry?.modules.values() ?? [], {\n contributedLoaders: indexProviderComponentLoaderContributions(input.providerComponentLoaders),\n onDiagnostic,\n });\n return createProviderComponentRegistry<WebsiteProviderComponentType>({ registrations, onDiagnostic });\n}\n\n/** What a page bridge builds once per island: the hydrated provider registry and its component registry. */\nexport interface WebsiteProviderRegistries {\n readonly frontendProviderRegistry: FrontendWebsiteProviderRegistry;\n readonly providerComponentRegistry: WebsiteProviderComponentRegistry;\n}\n\n/**\n * Builds both registries for a page bridge, MEMOISED on their inputs.\n *\n * Memoised because the component registry caches each loaded component: a registry rebuilt on every\n * render would forget what it loaded, hand every slot a new registry identity, and make each slot's\n * load effect run again — a download loop. The inputs are an island prop (`frontendProviders`, fixed\n * for the island's life) and two module-load-time bridge dependencies, so in practice this builds\n * once per island.\n */\nexport function useWebsiteProviderRegistries(input: {\n readonly frontendProviders: FrontendProvidersBlock | undefined;\n readonly frontendProviderModules: GeneratedWebsiteProviderModules | undefined;\n readonly providerComponentLoaders: Iterable<ProviderComponentLoaderContribution> | undefined;\n}): WebsiteProviderRegistries {\n const { frontendProviders, frontendProviderModules, providerComponentLoaders } = input;\n return useMemo(() => {\n const frontendProviderRegistry = createFrontendWebsiteProviderRegistry({\n entries: frontendProviders,\n modules: frontendProviderModules,\n });\n return {\n frontendProviderRegistry,\n providerComponentRegistry: createWebsiteProviderComponentRegistry({\n providerRegistry: frontendProviderRegistry,\n ...(providerComponentLoaders !== undefined ? { providerComponentLoaders } : {}),\n }),\n };\n }, [frontendProviders, frontendProviderModules, providerComponentLoaders]);\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading provider-contributed components on the website (#1703) — the website's counterpart of the
|
|
3
|
+
* application's `useProviderComponentChoices` / `useProviderComponent`, over the same registry and
|
|
4
|
+
* the same status vocabulary (`ProviderComponentStatus`).
|
|
5
|
+
*
|
|
6
|
+
* Two differences from the application, both about WHO answers:
|
|
7
|
+
*
|
|
8
|
+
* - the consent grant is the VISITOR's decision from `WebsiteConsentContext`, not a signed-in
|
|
9
|
+
* person's stored preference;
|
|
10
|
+
* - nothing starts before that decision has been read from storage (`isResolved`). The server render
|
|
11
|
+
* and the first client render read no storage, and asking the registry then would report a
|
|
12
|
+
* consent-required state to a visitor who already agreed, flashing the refusal on every page.
|
|
13
|
+
*
|
|
14
|
+
* Neither hook picks a provider: when several providers offer a component for one capability,
|
|
15
|
+
* choosing is the page's job.
|
|
16
|
+
*/
|
|
17
|
+
import { type ProviderComponentLoadState } from '@wildo-ai/external-connectors-public/website';
|
|
18
|
+
import type { ProviderCapability } from '@wildo-ai/saas-models/public-runtime';
|
|
19
|
+
import type { WebsiteProviderComponentType } from './provider-component-registry.website';
|
|
20
|
+
/** What `useWebsiteProviderComponent` returns: the shared load state, holding a React component. */
|
|
21
|
+
export type WebsiteProviderComponentState = ProviderComponentLoadState<WebsiteProviderComponentType>;
|
|
22
|
+
/**
|
|
23
|
+
* The providers that are BOTH live for this website service and carry a component for this
|
|
24
|
+
* capability, in the order the service declared them. Synchronous, so a page can render its choice
|
|
25
|
+
* on the first frame.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useWebsiteProviderComponentChoices(capability: ProviderCapability): ReadonlyArray<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Loads one provider's component for a capability, once the visitor's consent decision is known.
|
|
30
|
+
*
|
|
31
|
+
* Pass `undefined` for the ref while nothing is chosen. The load runs in an effect, so it never runs
|
|
32
|
+
* during the Astro server render; `CONSENT_REQUIRED` means nothing of the provider was downloaded,
|
|
33
|
+
* and granting the missing purposes through the consent panel loads it without a re-mount.
|
|
34
|
+
*/
|
|
35
|
+
export declare function useWebsiteProviderComponent(capability: ProviderCapability, providerRef: string | undefined): WebsiteProviderComponentState;
|
|
36
|
+
//# sourceMappingURL=useWebsiteProviderComponent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWebsiteProviderComponent.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-providers/useWebsiteProviderComponent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAI/E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAE1F,oGAAoG;AACpG,MAAM,MAAM,6BAA6B,GAAG,0BAA0B,CAAC,4BAA4B,CAAC,CAAC;AAErG;;;;GAIG;AACH,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,kBAAkB,GAAG,aAAa,CAAC,MAAM,CAAC,CAGxG;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,6BAA6B,CAuB/B"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading provider-contributed components on the website (#1703) — the website's counterpart of the
|
|
3
|
+
* application's `useProviderComponentChoices` / `useProviderComponent`, over the same registry and
|
|
4
|
+
* the same status vocabulary (`ProviderComponentStatus`).
|
|
5
|
+
*
|
|
6
|
+
* Two differences from the application, both about WHO answers:
|
|
7
|
+
*
|
|
8
|
+
* - the consent grant is the VISITOR's decision from `WebsiteConsentContext`, not a signed-in
|
|
9
|
+
* person's stored preference;
|
|
10
|
+
* - nothing starts before that decision has been read from storage (`isResolved`). The server render
|
|
11
|
+
* and the first client render read no storage, and asking the registry then would report a
|
|
12
|
+
* consent-required state to a visitor who already agreed, flashing the refusal on every page.
|
|
13
|
+
*
|
|
14
|
+
* Neither hook picks a provider: when several providers offer a component for one capability,
|
|
15
|
+
* choosing is the page's job.
|
|
16
|
+
*/
|
|
17
|
+
import { useEffect, useState } from 'react';
|
|
18
|
+
import { idleProviderComponentLoadState, startProviderComponentLoad, } from '@wildo-ai/external-connectors-public/website';
|
|
19
|
+
import { useWebsiteConsent } from '../consent/useWebsiteConsent.js';
|
|
20
|
+
import { useWebsiteRuntime } from '../contexts/useWebsiteRuntime.js';
|
|
21
|
+
/**
|
|
22
|
+
* The providers that are BOTH live for this website service and carry a component for this
|
|
23
|
+
* capability, in the order the service declared them. Synchronous, so a page can render its choice
|
|
24
|
+
* on the first frame.
|
|
25
|
+
*/
|
|
26
|
+
export function useWebsiteProviderComponentChoices(capability) {
|
|
27
|
+
const { providerComponentRegistry } = useWebsiteRuntime();
|
|
28
|
+
return providerComponentRegistry?.listProviders(capability) ?? [];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Loads one provider's component for a capability, once the visitor's consent decision is known.
|
|
32
|
+
*
|
|
33
|
+
* Pass `undefined` for the ref while nothing is chosen. The load runs in an effect, so it never runs
|
|
34
|
+
* during the Astro server render; `CONSENT_REQUIRED` means nothing of the provider was downloaded,
|
|
35
|
+
* and granting the missing purposes through the consent panel loads it without a re-mount.
|
|
36
|
+
*/
|
|
37
|
+
export function useWebsiteProviderComponent(capability, providerRef) {
|
|
38
|
+
const { providerComponentRegistry } = useWebsiteRuntime();
|
|
39
|
+
const consent = useWebsiteConsent();
|
|
40
|
+
const grantedKey = [...consent.grantedPurposes].sort().join(',');
|
|
41
|
+
const [state, setState] = useState(() => idleProviderComponentLoadState());
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!consent.isResolved) {
|
|
44
|
+
setState(idleProviderComponentLoadState());
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return startProviderComponentLoad({
|
|
48
|
+
registry: providerComponentRegistry,
|
|
49
|
+
capability,
|
|
50
|
+
providerRef,
|
|
51
|
+
grantedConsentPurposes: consent.grantedPurposes,
|
|
52
|
+
onState: setState,
|
|
53
|
+
});
|
|
54
|
+
// `grantedPurposes` is read through `grantedKey`: keyed by the granted SET, not the array's identity.
|
|
55
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
56
|
+
}, [capability, providerRef, providerComponentRegistry, consent.isResolved, grantedKey]);
|
|
57
|
+
return state;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=useWebsiteProviderComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWebsiteProviderComponent.js","sourceRoot":"","sources":["../../../../../src/core/external-providers/useWebsiteProviderComponent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,GAE3B,MAAM,8CAA8C,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAMlE;;;;GAIG;AACH,MAAM,UAAU,kCAAkC,CAAC,UAA8B;IAC/E,MAAM,EAAE,yBAAyB,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAC1D,OAAO,yBAAyB,EAAE,aAAa,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,UAA8B,EAC9B,WAA+B;IAE/B,MAAM,EAAE,yBAAyB,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAC1D,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgC,GAAG,EAAE,CAAC,8BAA8B,EAAgC,CAAC,CAAC;IAExI,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YACxB,QAAQ,CAAC,8BAA8B,EAAgC,CAAC,CAAC;YACzE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,0BAA0B,CAAC;YAChC,QAAQ,EAAE,yBAAyB;YACnC,UAAU;YACV,WAAW;YACX,sBAAsB,EAAE,OAAO,CAAC,eAAe;YAC/C,OAAO,EAAE,QAAQ;SAClB,CAAC,CAAC;QACH,sGAAsG;QACtG,uDAAuD;IACzD,CAAC,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,yBAAyB,EAAE,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;IAEzF,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * Reading provider-contributed components on the website (#1703) — the website's counterpart of the\n * application's `useProviderComponentChoices` / `useProviderComponent`, over the same registry and\n * the same status vocabulary (`ProviderComponentStatus`).\n *\n * Two differences from the application, both about WHO answers:\n *\n * - the consent grant is the VISITOR's decision from `WebsiteConsentContext`, not a signed-in\n * person's stored preference;\n * - nothing starts before that decision has been read from storage (`isResolved`). The server render\n * and the first client render read no storage, and asking the registry then would report a\n * consent-required state to a visitor who already agreed, flashing the refusal on every page.\n *\n * Neither hook picks a provider: when several providers offer a component for one capability,\n * choosing is the page's job.\n */\n\nimport { useEffect, useState } from 'react';\nimport {\n idleProviderComponentLoadState,\n startProviderComponentLoad,\n type ProviderComponentLoadState,\n} from '@wildo-ai/external-connectors-public/website';\nimport type { ProviderCapability } from '@wildo-ai/saas-models/public-runtime';\n\nimport { useWebsiteConsent } from '../consent/useWebsiteConsent';\nimport { useWebsiteRuntime } from '../contexts/useWebsiteRuntime';\nimport type { WebsiteProviderComponentType } from './provider-component-registry.website';\n\n/** What `useWebsiteProviderComponent` returns: the shared load state, holding a React component. */\nexport type WebsiteProviderComponentState = ProviderComponentLoadState<WebsiteProviderComponentType>;\n\n/**\n * The providers that are BOTH live for this website service and carry a component for this\n * capability, in the order the service declared them. Synchronous, so a page can render its choice\n * on the first frame.\n */\nexport function useWebsiteProviderComponentChoices(capability: ProviderCapability): ReadonlyArray<string> {\n const { providerComponentRegistry } = useWebsiteRuntime();\n return providerComponentRegistry?.listProviders(capability) ?? [];\n}\n\n/**\n * Loads one provider's component for a capability, once the visitor's consent decision is known.\n *\n * Pass `undefined` for the ref while nothing is chosen. The load runs in an effect, so it never runs\n * during the Astro server render; `CONSENT_REQUIRED` means nothing of the provider was downloaded,\n * and granting the missing purposes through the consent panel loads it without a re-mount.\n */\nexport function useWebsiteProviderComponent(\n capability: ProviderCapability,\n providerRef: string | undefined,\n): WebsiteProviderComponentState {\n const { providerComponentRegistry } = useWebsiteRuntime();\n const consent = useWebsiteConsent();\n const grantedKey = [...consent.grantedPurposes].sort().join(',');\n const [state, setState] = useState<WebsiteProviderComponentState>(() => idleProviderComponentLoadState<WebsiteProviderComponentType>());\n\n useEffect(() => {\n if (!consent.isResolved) {\n setState(idleProviderComponentLoadState<WebsiteProviderComponentType>());\n return undefined;\n }\n return startProviderComponentLoad({\n registry: providerComponentRegistry,\n capability,\n providerRef,\n grantedConsentPurposes: consent.grantedPurposes,\n onState: setState,\n });\n // `grantedPurposes` is read through `grantedKey`: keyed by the granted SET, not the array's identity.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [capability, providerRef, providerComponentRegistry, consent.isResolved, grantedKey]);\n\n return state;\n}\n"]}
|
|
@@ -12,17 +12,43 @@
|
|
|
12
12
|
* does not exist would throw, and starting a vendor SDK there would run a live
|
|
13
13
|
* analytics client inside a build.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
15
|
+
* Both come from `@wildo-ai/external-connectors-public/website`. The script
|
|
16
|
+
* injector lives there rather than here so the SaaS application, the technical
|
|
17
|
+
* doc and this site run ONE copy of it: a second copy of a DOM mutation
|
|
18
|
+
* eventually disagrees about idempotence, which is how a vendor tag gets loaded
|
|
19
|
+
* twice. It first shipped here alone, and that is how the application came to
|
|
20
|
+
* declare its Drive Picker's Google API script and never inject it.
|
|
21
|
+
*
|
|
22
|
+
* ## Client-side injection, and the alternative that was rejected
|
|
23
|
+
*
|
|
24
|
+
* This is an Astro static site, so the "obvious" home for a third-party tag is
|
|
25
|
+
* the built HTML `<head>`. That was rejected for now: the set of live providers
|
|
26
|
+
* is per SERVICE and per ENVIRONMENT, resolved from the materialized artifact,
|
|
27
|
+
* while the built HTML is one artifact shared across deploys of the same build.
|
|
28
|
+
* Baking the tags in would make the build environment-specific — the same class
|
|
29
|
+
* of coupling the DATA channel exists to avoid. Injecting from the client keeps
|
|
30
|
+
* one build correct everywhere, at the cost of the tag arriving after hydration.
|
|
31
|
+
* Revisit if a provider ever needs to run before first paint.
|
|
23
32
|
*/
|
|
33
|
+
import { type ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';
|
|
24
34
|
import type { FrontendWebsiteProviderRegistry } from './frontend-provider-registry.website';
|
|
25
|
-
|
|
35
|
+
import type { WebsiteProviderComponentRegistry } from './provider-component-registry.website';
|
|
36
|
+
import type { WebsiteProviderSdkActivation } from './website-provider-sdk-activations';
|
|
37
|
+
/**
|
|
38
|
+
* Injects the `<script>` tags of the live providers that rest on NO consent purpose and render no component.
|
|
39
|
+
*
|
|
40
|
+
* No consent input, deliberately (#1704): an executed `<script>` cannot be stopped, so a provider that rests on a
|
|
41
|
+
* purpose must ship as a stoppable SDK (started on the grant and stopped on withdrawal by
|
|
42
|
+
* {@link useWebsiteProviderSdks}); a consent-based module declaring `scripts` is refused at projection and skipped by
|
|
43
|
+
* the collector. No grant could change what this injects.
|
|
44
|
+
*/
|
|
45
|
+
export declare function useWebsiteProviderScripts(registry: FrontendWebsiteProviderRegistry | undefined,
|
|
46
|
+
/**
|
|
47
|
+
* The website's provider-component registry (#1703). A provider with a registered component loads its
|
|
48
|
+
* scripts WITH that component, when a page first shows it — so an address lookup's vendor loader is
|
|
49
|
+
* not downloaded on every page of the site, only on the one with the form. This hook injects the rest.
|
|
50
|
+
*/
|
|
51
|
+
componentRegistry?: WebsiteProviderComponentRegistry): void;
|
|
26
52
|
/**
|
|
27
53
|
* Loads and starts the npm SDK of every live provider that declares one.
|
|
28
54
|
*
|
|
@@ -30,5 +56,12 @@ export declare function useWebsiteProviderScripts(registry: FrontendWebsiteProvi
|
|
|
30
56
|
* offers a teardown — an analytics client that cannot be stopped cannot honour
|
|
31
57
|
* a consent withdrawal, and accumulates one instance per view transition.
|
|
32
58
|
*/
|
|
33
|
-
export declare function useWebsiteProviderSdks(registry: FrontendWebsiteProviderRegistry | undefined
|
|
59
|
+
export declare function useWebsiteProviderSdks(registry: FrontendWebsiteProviderRegistry | undefined,
|
|
60
|
+
/**
|
|
61
|
+
* The purposes the visitor granted (#534). Providers are activated in TWO groups on purpose: those that
|
|
62
|
+
* rest on no purpose (error monitoring, captcha) are started once and never restarted by a consent
|
|
63
|
+
* change, while the consent-based ones start when their purpose is granted and are DEACTIVATED when it
|
|
64
|
+
* is withdrawn — which is the only way a withdrawal stops a running analytics client.
|
|
65
|
+
*/
|
|
66
|
+
grantedConsentPurposes: readonly ConsentPurpose[]): ReadonlyArray<WebsiteProviderSdkActivation>;
|
|
34
67
|
//# sourceMappingURL=useWebsiteProviderScripts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebsiteProviderScripts.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"useWebsiteProviderScripts.d.ts","sourceRoot":"","sources":["../../../../../src/core/external-providers/useWebsiteProviderScripts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAUH,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAC5F,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC;AAC5F,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,uCAAuC,CAAC;AAC9F,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAUvF;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,+BAA+B,GAAG,SAAS;AACrD;;;;GAIG;AACH,iBAAiB,CAAC,EAAE,gCAAgC,GACnD,IAAI,CAcN;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,+BAA+B,GAAG,SAAS;AACrD;;;;;GAKG;AACH,sBAAsB,EAAE,SAAS,cAAc,EAAE,GAChD,aAAa,CAAC,4BAA4B,CAAC,CAM7C"}
|