@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.
Files changed (225) hide show
  1. package/dist/esm/astro/blog-post-bridge.d.ts +9 -0
  2. package/dist/esm/astro/blog-post-bridge.d.ts.map +1 -1
  3. package/dist/esm/astro/blog-post-bridge.js +10 -5
  4. package/dist/esm/astro/blog-post-bridge.js.map +1 -1
  5. package/dist/esm/astro/blog-post-head.renderer.d.ts.map +1 -1
  6. package/dist/esm/astro/bridge-runtime.d.ts +25 -6
  7. package/dist/esm/astro/bridge-runtime.d.ts.map +1 -1
  8. package/dist/esm/astro/bridge-runtime.js +10 -6
  9. package/dist/esm/astro/bridge-runtime.js.map +1 -1
  10. package/dist/esm/astro/collect-expected-label-keys.d.ts.map +1 -1
  11. package/dist/esm/astro/headers-renderer.d.ts.map +1 -1
  12. package/dist/esm/astro/i18n-routing.helper.d.ts.map +1 -1
  13. package/dist/esm/astro/label-pack-loader.d.ts +4 -4
  14. package/dist/esm/astro/label-pack-loader.d.ts.map +1 -1
  15. package/dist/esm/astro/label-pack-loader.js +9 -4
  16. package/dist/esm/astro/label-pack-loader.js.map +1 -1
  17. package/dist/esm/astro/llms-txt-server.d.ts.map +1 -1
  18. package/dist/esm/astro/pick-labels-for-page.d.ts.map +1 -1
  19. package/dist/esm/astro/robots-renderer.d.ts +9 -8
  20. package/dist/esm/astro/robots-renderer.d.ts.map +1 -1
  21. package/dist/esm/astro/robots-renderer.js +15 -15
  22. package/dist/esm/astro/robots-renderer.js.map +1 -1
  23. package/dist/esm/astro/sitemap-coverage.d.ts.map +1 -1
  24. package/dist/esm/astro/structured-data.renderer.d.ts.map +1 -1
  25. package/dist/esm/astro/website-page-head.renderer.d.ts +1 -1
  26. package/dist/esm/astro/website-page-head.renderer.d.ts.map +1 -1
  27. package/dist/esm/astro/website-page-head.renderer.js +2 -1
  28. package/dist/esm/astro/website-page-head.renderer.js.map +1 -1
  29. package/dist/esm/astro/website-page-runtime.helper.d.ts.map +1 -1
  30. package/dist/esm/astro/website-site-context.d.ts +5 -2
  31. package/dist/esm/astro/website-site-context.d.ts.map +1 -1
  32. package/dist/esm/astro/website-site-context.js.map +1 -1
  33. package/dist/esm/astro-island.d.ts.map +1 -1
  34. package/dist/esm/astro.d.ts.map +1 -1
  35. package/dist/esm/companion-exports.d.ts +8 -15
  36. package/dist/esm/companion-exports.d.ts.map +1 -1
  37. package/dist/esm/companion-exports.js +8 -15
  38. package/dist/esm/companion-exports.js.map +1 -1
  39. package/dist/esm/components/low-level/WebsiteInternalButton.d.ts.map +1 -1
  40. package/dist/esm/components/low-level/WebsiteInternalLink.d.ts.map +1 -1
  41. package/dist/esm/config/define-website-config.d.ts +3 -2
  42. package/dist/esm/config/define-website-config.d.ts.map +1 -1
  43. package/dist/esm/config/define-website-config.js +10 -10
  44. package/dist/esm/config/define-website-config.js.map +1 -1
  45. package/dist/esm/config/index.d.ts.map +1 -1
  46. package/dist/esm/config/load-website-config.d.ts +6 -0
  47. package/dist/esm/config/load-website-config.d.ts.map +1 -1
  48. package/dist/esm/config/load-website-config.js +18 -9
  49. package/dist/esm/config/load-website-config.js.map +1 -1
  50. package/dist/esm/config/load-website-pricing-catalog.d.ts +13 -0
  51. package/dist/esm/config/load-website-pricing-catalog.d.ts.map +1 -0
  52. package/dist/esm/config/load-website-pricing-catalog.js +34 -0
  53. package/dist/esm/config/load-website-pricing-catalog.js.map +1 -0
  54. package/dist/esm/config/load-website-site-context.d.ts +53 -0
  55. package/dist/esm/config/load-website-site-context.d.ts.map +1 -0
  56. package/dist/esm/config/load-website-site-context.js +98 -0
  57. package/dist/esm/config/load-website-site-context.js.map +1 -0
  58. package/dist/esm/config/wildo-website-config.schemas.d.ts.map +1 -1
  59. package/dist/esm/config-loader.d.ts +2 -0
  60. package/dist/esm/config-loader.d.ts.map +1 -1
  61. package/dist/esm/config-loader.js +2 -0
  62. package/dist/esm/config-loader.js.map +1 -1
  63. package/dist/esm/core/anonymous-session/InboundContactForm.d.ts +26 -6
  64. package/dist/esm/core/anonymous-session/InboundContactForm.d.ts.map +1 -1
  65. package/dist/esm/core/anonymous-session/InboundContactForm.js +51 -33
  66. package/dist/esm/core/anonymous-session/InboundContactForm.js.map +1 -1
  67. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts +11 -0
  68. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.d.ts.map +1 -1
  69. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js +15 -4
  70. package/dist/esm/core/anonymous-session/inbound-contact-form.schema.js.map +1 -1
  71. package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts +14 -2
  72. package/dist/esm/core/anonymous-session/website-anonymous-session-client.d.ts.map +1 -1
  73. package/dist/esm/core/anonymous-session/website-anonymous-session-client.js +34 -4
  74. package/dist/esm/core/anonymous-session/website-anonymous-session-client.js.map +1 -1
  75. package/dist/esm/core/anonymous-session/website-submission-challenge.d.ts +36 -0
  76. package/dist/esm/core/anonymous-session/website-submission-challenge.d.ts.map +1 -0
  77. package/dist/esm/core/anonymous-session/website-submission-challenge.js +69 -0
  78. package/dist/esm/core/anonymous-session/website-submission-challenge.js.map +1 -0
  79. package/dist/esm/core/consent/WebsiteConsentBanner.d.ts.map +1 -1
  80. package/dist/esm/core/consent/WebsiteConsentBanner.js +11 -10
  81. package/dist/esm/core/consent/WebsiteConsentBanner.js.map +1 -1
  82. package/dist/esm/core/consent/WebsiteConsentContext.d.ts.map +1 -1
  83. package/dist/esm/core/consent/WebsiteConsentGate.d.ts.map +1 -1
  84. package/dist/esm/core/consent/useWebsiteConsent.d.ts.map +1 -1
  85. package/dist/esm/core/consent/website-consent-storage.d.ts.map +1 -1
  86. package/dist/esm/core/contexts/WebsitePageContext.d.ts.map +1 -1
  87. package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts +25 -5
  88. package/dist/esm/core/contexts/WebsiteRuntimeContext.d.ts.map +1 -1
  89. package/dist/esm/core/contexts/WebsiteRuntimeContext.js.map +1 -1
  90. package/dist/esm/core/contexts/WebsiteSectionContext.d.ts.map +1 -1
  91. package/dist/esm/core/contexts/useWebsitePage.d.ts.map +1 -1
  92. package/dist/esm/core/contexts/useWebsiteRuntime.d.ts +18 -0
  93. package/dist/esm/core/contexts/useWebsiteRuntime.d.ts.map +1 -1
  94. package/dist/esm/core/contexts/useWebsiteRuntime.js +28 -2
  95. package/dist/esm/core/contexts/useWebsiteRuntime.js.map +1 -1
  96. package/dist/esm/core/contexts/useWebsiteSection.d.ts.map +1 -1
  97. package/dist/esm/core/external-providers/WebsiteProviderComponent.d.ts +57 -0
  98. package/dist/esm/core/external-providers/WebsiteProviderComponent.d.ts.map +1 -0
  99. package/dist/esm/core/external-providers/WebsiteProviderComponent.js +56 -0
  100. package/dist/esm/core/external-providers/WebsiteProviderComponent.js.map +1 -0
  101. package/dist/esm/core/external-providers/frontend-provider-registry.website.d.ts.map +1 -1
  102. package/dist/esm/core/external-providers/provider-component-registry.website.d.ts +63 -0
  103. package/dist/esm/core/external-providers/provider-component-registry.website.d.ts.map +1 -0
  104. package/dist/esm/core/external-providers/provider-component-registry.website.js +68 -0
  105. package/dist/esm/core/external-providers/provider-component-registry.website.js.map +1 -0
  106. package/dist/esm/core/external-providers/useWebsiteProviderComponent.d.ts +36 -0
  107. package/dist/esm/core/external-providers/useWebsiteProviderComponent.d.ts.map +1 -0
  108. package/dist/esm/core/external-providers/useWebsiteProviderComponent.js +59 -0
  109. package/dist/esm/core/external-providers/useWebsiteProviderComponent.js.map +1 -0
  110. package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts +15 -5
  111. package/dist/esm/core/external-providers/useWebsiteProviderScripts.d.ts.map +1 -1
  112. package/dist/esm/core/external-providers/useWebsiteProviderScripts.js +39 -24
  113. package/dist/esm/core/external-providers/useWebsiteProviderScripts.js.map +1 -1
  114. package/dist/esm/core/external-providers/website-provider-sdk-activations.d.ts +11 -0
  115. package/dist/esm/core/external-providers/website-provider-sdk-activations.d.ts.map +1 -0
  116. package/dist/esm/core/external-providers/website-provider-sdk-activations.js +14 -0
  117. package/dist/esm/core/external-providers/website-provider-sdk-activations.js.map +1 -0
  118. package/dist/esm/core/factories/define-website-page-manifest.d.ts.map +1 -1
  119. package/dist/esm/core/factories/define-website-section.d.ts +3 -3
  120. package/dist/esm/core/factories/define-website-section.d.ts.map +1 -1
  121. package/dist/esm/core/factories/define-website-section.js +3 -3
  122. package/dist/esm/core/factories/define-website-section.js.map +1 -1
  123. package/dist/esm/core/hooks/useWebsiteDesignTokens.d.ts.map +1 -1
  124. package/dist/esm/core/hooks/useWebsiteLabel.d.ts.map +1 -1
  125. package/dist/esm/core/layouts/WebsitePageLayout.d.ts.map +1 -1
  126. package/dist/esm/core/layouts/WebsitePageLayout.js +10 -7
  127. package/dist/esm/core/layouts/WebsitePageLayout.js.map +1 -1
  128. package/dist/esm/core/layouts/WebsiteSection.d.ts.map +1 -1
  129. package/dist/esm/core/routing/internal-routing.utils.d.ts.map +1 -1
  130. package/dist/esm/index.d.ts +30 -65
  131. package/dist/esm/index.d.ts.map +1 -1
  132. package/dist/esm/index.js +30 -65
  133. package/dist/esm/index.js.map +1 -1
  134. package/dist/esm/mdx/BlogPost.d.ts.map +1 -1
  135. package/dist/esm/mdx/blog-post-collection.config.d.ts.map +1 -1
  136. package/dist/esm/mdx/blog-post-frontmatter-source.d.ts.map +1 -1
  137. package/dist/esm/mdx/index.d.ts.map +1 -1
  138. package/dist/esm/mdx.d.ts.map +1 -1
  139. package/dist/esm/schemas/blog/blog-post-frontmatter.shared.schemas.d.ts.map +1 -1
  140. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts +35 -6
  141. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.d.ts.map +1 -1
  142. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js +39 -5
  143. package/dist/esm/schemas/design-tokens/website-design-tokens.shared.schemas.js.map +1 -1
  144. package/dist/esm/schemas/label-keys/website-consent-label-keys.schemas.d.ts.map +1 -1
  145. package/dist/esm/schemas/label-keys/website-label-key.schemas.d.ts.map +1 -1
  146. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.d.ts +32 -0
  147. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.d.ts.map +1 -0
  148. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.js +36 -0
  149. package/dist/esm/schemas/label-keys/website-provider-component-label-keys.schemas.js.map +1 -0
  150. package/dist/esm/schemas/manifests/website-page-manifest.shared.schemas.d.ts.map +1 -1
  151. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts +9 -8
  152. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.d.ts.map +1 -1
  153. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js +8 -7
  154. package/dist/esm/schemas/manifests/website-root-config.shared.schemas.js.map +1 -1
  155. package/dist/esm/schemas/refs/page-ref.schemas.d.ts.map +1 -1
  156. package/dist/esm/schemas/refs/section-ref.schemas.d.ts.map +1 -1
  157. package/dist/esm/schemas/sections/website-section-category.shared.d.ts.map +1 -1
  158. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts +14 -7
  159. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.d.ts.map +1 -1
  160. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js +14 -7
  161. package/dist/esm/schemas/sections/website-section-definition.shared.schemas.js.map +1 -1
  162. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.d.ts +57 -0
  163. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.d.ts.map +1 -0
  164. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.js +76 -0
  165. package/dist/esm/schemas/structured-data/pricing-offer-structured-data.shared.js.map +1 -0
  166. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts +23 -0
  167. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.d.ts.map +1 -1
  168. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js +20 -2
  169. package/dist/esm/schemas/structured-data/structured-data-reconciliation.shared.js.map +1 -1
  170. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts +7 -5
  171. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.d.ts.map +1 -1
  172. package/dist/esm/schemas/structured-data/website-structured-data.shared.schemas.js.map +1 -1
  173. package/dist/esm/schemas/validators/label-pack.validator.d.ts.map +1 -1
  174. package/dist/tsconfig.build.tsbuildinfo +1 -1
  175. package/package.json +5 -5
  176. package/src/__tests__/bundle-isolation.test.ts +19 -22
  177. package/src/astro/__tests__/blog-post-bridge.test.tsx +18 -2
  178. package/src/astro/__tests__/bridge-runtime.test.tsx +21 -3
  179. package/src/astro/__tests__/label-pack-loader.test.ts +7 -4
  180. package/src/astro/blog-post-bridge.tsx +19 -5
  181. package/src/astro/bridge-runtime.tsx +35 -11
  182. package/src/astro/label-pack-loader.ts +8 -4
  183. package/src/astro/robots-renderer.ts +15 -15
  184. package/src/astro/website-page-head.renderer.ts +2 -2
  185. package/src/astro/website-site-context.ts +5 -2
  186. package/src/companion-exports.ts +8 -15
  187. package/src/config/__tests__/website-build-origins.test.ts +52 -0
  188. package/src/config/define-website-config.ts +10 -10
  189. package/src/config/load-website-config.ts +25 -14
  190. package/src/config/load-website-pricing-catalog.ts +52 -0
  191. package/src/config/load-website-site-context.ts +145 -0
  192. package/src/config-loader.ts +2 -0
  193. package/src/core/__tests__/contexts.test.tsx +20 -1
  194. package/src/core/__tests__/website-consent.test.tsx +2 -1
  195. package/src/core/anonymous-session/InboundContactForm.tsx +100 -45
  196. package/src/core/anonymous-session/__tests__/InboundContactForm.labels.test.tsx +108 -0
  197. package/src/core/anonymous-session/__tests__/InboundContactForm.submission-challenge.test.tsx +139 -0
  198. package/src/core/anonymous-session/inbound-contact-form.schema.ts +16 -4
  199. package/src/core/anonymous-session/website-anonymous-session-client.ts +39 -4
  200. package/src/core/anonymous-session/website-submission-challenge.ts +99 -0
  201. package/src/core/consent/WebsiteConsentBanner.tsx +11 -10
  202. package/src/core/contexts/WebsiteRuntimeContext.tsx +25 -5
  203. package/src/core/contexts/useWebsiteRuntime.ts +33 -2
  204. package/src/core/external-providers/WebsiteProviderComponent.tsx +153 -0
  205. package/src/core/external-providers/__tests__/frontend-provider-registry.website.test.ts +4 -0
  206. package/src/core/external-providers/__tests__/website-provider-component.test.tsx +238 -0
  207. package/src/core/external-providers/__tests__/website-provider-sdks.test.tsx +72 -0
  208. package/src/core/external-providers/provider-component-registry.website.ts +115 -0
  209. package/src/core/external-providers/useWebsiteProviderComponent.ts +76 -0
  210. package/src/core/external-providers/useWebsiteProviderScripts.ts +56 -37
  211. package/src/core/external-providers/website-provider-sdk-activations.ts +23 -0
  212. package/src/core/factories/define-website-section.ts +3 -3
  213. package/src/core/layouts/WebsitePageLayout.tsx +11 -6
  214. package/src/index.ts +31 -65
  215. package/src/schemas/__tests__/design-token-css-property.test.ts +72 -0
  216. package/src/schemas/__tests__/pricing-offer-structured-data.test.ts +122 -0
  217. package/src/schemas/__tests__/structured-data-reconciliation.test.ts +35 -0
  218. package/src/schemas/design-tokens/website-design-tokens.shared.schemas.ts +45 -5
  219. package/src/schemas/label-keys/__tests__/scaffolded-consent-label-pack.parity.test.ts +12 -0
  220. package/src/schemas/label-keys/website-provider-component-label-keys.schemas.ts +40 -0
  221. package/src/schemas/manifests/website-root-config.shared.schemas.ts +10 -9
  222. package/src/schemas/sections/website-section-definition.shared.schemas.ts +14 -7
  223. package/src/schemas/structured-data/pricing-offer-structured-data.shared.ts +144 -0
  224. package/src/schemas/structured-data/structured-data-reconciliation.shared.ts +46 -1
  225. package/src/schemas/structured-data/website-structured-data.shared.schemas.ts +7 -5
@@ -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"]}
@@ -32,13 +32,23 @@
32
32
  */
33
33
  import { type ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';
34
34
  import type { FrontendWebsiteProviderRegistry } from './frontend-provider-registry.website';
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
+ */
35
45
  export declare function useWebsiteProviderScripts(registry: FrontendWebsiteProviderRegistry | undefined,
36
46
  /**
37
- * The purposes the visitor granted (#534). A provider resting on an ungranted purpose injects nothing;
38
- * granting it later injects its script then. A withdrawal cannot remove an executed script, so it takes
39
- * effect on the next page load.
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.
40
50
  */
41
- grantedConsentPurposes: readonly ConsentPurpose[]): void;
51
+ componentRegistry?: WebsiteProviderComponentRegistry): void;
42
52
  /**
43
53
  * Loads and starts the npm SDK of every live provider that declares one.
44
54
  *
@@ -53,5 +63,5 @@ export declare function useWebsiteProviderSdks(registry: FrontendWebsiteProvider
53
63
  * change, while the consent-based ones start when their purpose is granted and are DEACTIVATED when it
54
64
  * is withdrawn — which is the only way a withdrawal stops a running analytics client.
55
65
  */
56
- grantedConsentPurposes: readonly ConsentPurpose[]): void;
66
+ grantedConsentPurposes: readonly ConsentPurpose[]): ReadonlyArray<WebsiteProviderSdkActivation>;
57
67
  //# sourceMappingURL=useWebsiteProviderScripts.d.ts.map
@@ -1 +1 @@
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;AAU5F,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,+BAA+B,GAAG,SAAS;AACrD;;;;GAIG;AACH,sBAAsB,EAAE,SAAS,cAAc,EAAE,GAChD,IAAI,CAeN;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,+BAA+B,GAAG,SAAS;AACrD;;;;;GAKG;AACH,sBAAsB,EAAE,SAAS,cAAc,EAAE,GAChD,IAAI,CAGN"}
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"}
@@ -30,8 +30,8 @@
30
30
  * one build correct everywhere, at the cost of the tag arriving after hydration.
31
31
  * Revisit if a provider ever needs to run before first paint.
32
32
  */
33
- import { useEffect } from 'react';
34
- import { activateFrontendProviderSdks, collectDeclaredProviderScripts, consentPurposesRequiredByProvider, injectProviderScripts, } from '@wildo-ai/external-connectors-public/website';
33
+ import { useEffect, useMemo, useRef, useState } from 'react';
34
+ import { activateFrontendProviderSdks, collectProviderScriptsWithoutComponents, consentPurposesRequiredByProvider, createFrontendProviderSdkActivationLane, injectProviderScripts, } from '@wildo-ai/external-connectors-public/website';
35
35
  import { AppFrontendType } from '@wildo-ai/saas-models/public-runtime';
36
36
  /**
37
37
  * A stable key for a granted-purpose set, so an effect re-runs when the SET changes and not when a new
@@ -40,18 +40,26 @@ import { AppFrontendType } from '@wildo-ai/saas-models/public-runtime';
40
40
  function grantedKey(grantedConsentPurposes) {
41
41
  return [...grantedConsentPurposes].sort().join(',');
42
42
  }
43
+ /**
44
+ * Injects the `<script>` tags of the live providers that rest on NO consent purpose and render no component.
45
+ *
46
+ * No consent input, deliberately (#1704): an executed `<script>` cannot be stopped, so a provider that rests on a
47
+ * purpose must ship as a stoppable SDK (started on the grant and stopped on withdrawal by
48
+ * {@link useWebsiteProviderSdks}); a consent-based module declaring `scripts` is refused at projection and skipped by
49
+ * the collector. No grant could change what this injects.
50
+ */
43
51
  export function useWebsiteProviderScripts(registry,
44
52
  /**
45
- * The purposes the visitor granted (#534). A provider resting on an ungranted purpose injects nothing;
46
- * granting it later injects its script then. A withdrawal cannot remove an executed script, so it takes
47
- * effect on the next page load.
53
+ * The website's provider-component registry (#1703). A provider with a registered component loads its
54
+ * scripts WITH that component, when a page first shows it — so an address lookup's vendor loader is
55
+ * not downloaded on every page of the site, only on the one with the form. This hook injects the rest.
48
56
  */
49
- grantedConsentPurposes) {
50
- const key = grantedKey(grantedConsentPurposes);
57
+ componentRegistry) {
51
58
  useEffect(() => {
52
59
  if (typeof document === 'undefined')
53
60
  return;
54
- const scripts = collectDeclaredProviderScripts(registry?.modules.values(), grantedConsentPurposes);
61
+ const withComponents = [...(componentRegistry?.providersWithComponents() ?? [])].map((providerRef) => ({ providerRef }));
62
+ const scripts = collectProviderScriptsWithoutComponents(registry?.modules.values() ?? [], withComponents);
55
63
  if (scripts.length === 0)
56
64
  return;
57
65
  injectProviderScripts(scripts, document);
@@ -59,8 +67,7 @@ grantedConsentPurposes) {
59
67
  // work immediately; removing the element undoes neither, so a cleanup that
60
68
  // removed it would only make the next mount inject a SECOND copy of an
61
69
  // already-running script. Idempotence by `src` is the real guard.
62
- // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed by the granted SET, see grantedKey.
63
- }, [registry, key]);
70
+ }, [registry, componentRegistry]);
64
71
  }
65
72
  /**
66
73
  * Loads and starts the npm SDK of every live provider that declares one.
@@ -77,10 +84,14 @@ export function useWebsiteProviderSdks(registry,
77
84
  * is withdrawn — which is the only way a withdrawal stops a running analytics client.
78
85
  */
79
86
  grantedConsentPurposes) {
80
- useProviderSdkGroup(registry, false, []);
81
- useProviderSdkGroup(registry, true, grantedConsentPurposes);
87
+ const consentFree = useProviderSdkGroup(registry, false, []);
88
+ const consentBased = useProviderSdkGroup(registry, true, grantedConsentPurposes);
89
+ // The providers RUNNING on this page, both groups, published by the page layout (#1735): a form reads the captcha
90
+ // vendor's handle from here. Emptied the moment a group stops, so nothing holds a handle past a withdrawal.
91
+ return useMemo(() => [...consentFree, ...consentBased], [consentFree, consentBased]);
82
92
  }
83
93
  function useProviderSdkGroup(registry, consentBased, grantedConsentPurposes) {
94
+ const [running, setRunning] = useState([]);
84
95
  // Keyed by the grants this group's providers actually rest on, so changing an unrelated purpose does
85
96
  // not stop and restart a running analytics client.
86
97
  const relevant = registry === undefined
@@ -90,15 +101,18 @@ function useProviderSdkGroup(registry, consentBased, grantedConsentPurposes) {
90
101
  .flatMap((module) => consentPurposesRequiredByProvider(module))
91
102
  .filter((purpose) => grantedConsentPurposes.includes(purpose));
92
103
  const key = grantedKey(relevant);
104
+ // One lane per group for the page's lifetime: a start waits for the previous stop.
105
+ const lane = useRef(createFrontendProviderSdkActivationLane());
93
106
  useEffect(() => {
94
107
  if (registry === undefined)
95
108
  return;
96
109
  const modules = [...registry.modules.values()].filter((module) => (consentPurposesRequiredByProvider(module).length > 0) === consentBased);
97
110
  if (modules.length === 0)
98
111
  return;
99
- let unmounted = false;
100
- let activated;
101
- void activateFrontendProviderSdks({
112
+ // Through the group's lane, so a start never overlaps the previous stop of the same page-global vendor (#1704): a
113
+ // visitor who grants, withdraws and grants again while the bundle downloads must not have the first run's stop
114
+ // switch off the second run's client. The stop also covers an unmount mid-download.
115
+ const stop = lane.current.start(() => activateFrontendProviderSdks({
102
116
  surface: AppFrontendType.STATIC_WEBSITE,
103
117
  modules,
104
118
  grantedConsentPurposes,
@@ -108,19 +122,20 @@ function useProviderSdkGroup(registry, consentBased, grantedConsentPurposes) {
108
122
  // eslint-disable-next-line no-console
109
123
  console.warn(`[wildo-provider-sdk] ${diagnostic.kind}: ${diagnostic.message}`);
110
124
  },
111
- }).then((result) => {
112
- activated = result;
113
- // Activation is asynchronous and unmount is not: if the surface went away
114
- // while a vendor bundle was still downloading, stop what we just started
115
- // rather than leaving a client nobody can reach.
116
- if (unmounted)
117
- void result.deactivateAll();
125
+ }), (result) => {
126
+ setRunning(modules.flatMap((module) => {
127
+ const activation = result.activations.get(module.metadata.ref);
128
+ return activation === undefined
129
+ ? []
130
+ : [{ providerRef: module.metadata.ref, providerCapabilities: [...module.providerCapabilities], activation }];
131
+ }));
118
132
  });
119
133
  return () => {
120
- unmounted = true;
121
- void activated?.deactivateAll();
134
+ setRunning([]);
135
+ stop();
122
136
  };
123
137
  // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed by the granted SET, see grantedKey.
124
138
  }, [registry, consentBased, key]);
139
+ return running;
125
140
  }
126
141
  //# sourceMappingURL=useWebsiteProviderScripts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWebsiteProviderScripts.js","sourceRoot":"","sources":["../../../../../src/core/external-providers/useWebsiteProviderScripts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The website's executable-surface hooks: load what the live providers declared.\n *\n * Two mechanisms, one per kind of vendor. `useWebsiteProviderScripts` adds the\n * `<script>` tags a provider declared — the case a dynamic `import()` cannot\n * serve, and the dominant one on a marketing site. `useWebsiteProviderSdks`\n * activates the npm SDK of any provider that ships one, which reaches this\n * bundle through the CODE channel.\n *\n * Both are effect-only, and that is not incidental: this is an Astro site, so\n * page modules execute in Node at build time. Injecting into a document that\n * does not exist would throw, and starting a vendor SDK there would run a live\n * analytics client inside a build.\n *\n * Both come from `@wildo-ai/external-connectors-public/website`. The script\n * injector lives there rather than here so the SaaS application, the technical\n * doc and this site run ONE copy of it: a second copy of a DOM mutation\n * eventually disagrees about idempotence, which is how a vendor tag gets loaded\n * twice. It first shipped here alone, and that is how the application came to\n * declare its Drive Picker's Google API script and never inject it.\n *\n * ## Client-side injection, and the alternative that was rejected\n *\n * This is an Astro static site, so the \"obvious\" home for a third-party tag is\n * the built HTML `<head>`. That was rejected for now: the set of live providers\n * is per SERVICE and per ENVIRONMENT, resolved from the materialized artifact,\n * while the built HTML is one artifact shared across deploys of the same build.\n * Baking the tags in would make the build environment-specific — the same class\n * of coupling the DATA channel exists to avoid. Injecting from the client keeps\n * one build correct everywhere, at the cost of the tag arriving after hydration.\n * Revisit if a provider ever needs to run before first paint.\n */\n\nimport { useEffect } from 'react';\nimport {\n activateFrontendProviderSdks,\n collectDeclaredProviderScripts,\n consentPurposesRequiredByProvider,\n injectProviderScripts,\n type ActivatedFrontendProviderSdks,\n} from '@wildo-ai/external-connectors-public/website';\nimport { AppFrontendType, type ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';\nimport type { FrontendWebsiteProviderRegistry } from './frontend-provider-registry.website';\n\n/**\n * A stable key for a granted-purpose set, so an effect re-runs when the SET changes and not when a new\n * array with the same members is handed in.\n */\nfunction grantedKey(grantedConsentPurposes: readonly ConsentPurpose[]): string {\n return [...grantedConsentPurposes].sort().join(',');\n}\n\nexport function useWebsiteProviderScripts(\n registry: FrontendWebsiteProviderRegistry | undefined,\n /**\n * The purposes the visitor granted (#534). A provider resting on an ungranted purpose injects nothing;\n * granting it later injects its script then. A withdrawal cannot remove an executed script, so it takes\n * effect on the next page load.\n */\n grantedConsentPurposes: readonly ConsentPurpose[],\n): void {\n const key = grantedKey(grantedConsentPurposes);\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n const scripts = collectDeclaredProviderScripts(registry?.modules.values(), grantedConsentPurposes);\n if (scripts.length === 0) return;\n\n injectProviderScripts(scripts, document);\n // Deliberately no teardown. A vendor tag installs a global and often starts\n // work immediately; removing the element undoes neither, so a cleanup that\n // removed it would only make the next mount inject a SECOND copy of an\n // already-running script. Idempotence by `src` is the real guard.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed by the granted SET, see grantedKey.\n }, [registry, key]);\n}\n\n/**\n * Loads and starts the npm SDK of every live provider that declares one.\n *\n * Unlike a script tag, an SDK activation IS undone on unmount where the vendor\n * offers a teardown — an analytics client that cannot be stopped cannot honour\n * a consent withdrawal, and accumulates one instance per view transition.\n */\nexport function useWebsiteProviderSdks(\n registry: FrontendWebsiteProviderRegistry | undefined,\n /**\n * The purposes the visitor granted (#534). Providers are activated in TWO groups on purpose: those that\n * rest on no purpose (error monitoring, captcha) are started once and never restarted by a consent\n * change, while the consent-based ones start when their purpose is granted and are DEACTIVATED when it\n * is withdrawn — which is the only way a withdrawal stops a running analytics client.\n */\n grantedConsentPurposes: readonly ConsentPurpose[],\n): void {\n useProviderSdkGroup(registry, false, []);\n useProviderSdkGroup(registry, true, grantedConsentPurposes);\n}\n\nfunction useProviderSdkGroup(\n registry: FrontendWebsiteProviderRegistry | undefined,\n consentBased: boolean,\n grantedConsentPurposes: readonly ConsentPurpose[],\n): void {\n // Keyed by the grants this group's providers actually rest on, so changing an unrelated purpose does\n // not stop and restart a running analytics client.\n const relevant = registry === undefined\n ? []\n : [...registry.modules.values()]\n .filter((module) => (consentPurposesRequiredByProvider(module).length > 0) === consentBased)\n .flatMap((module) => consentPurposesRequiredByProvider(module))\n .filter((purpose) => grantedConsentPurposes.includes(purpose));\n const key = grantedKey(relevant);\n useEffect(() => {\n if (registry === undefined) return;\n const modules = [...registry.modules.values()].filter(\n (module) => (consentPurposesRequiredByProvider(module).length > 0) === consentBased,\n );\n if (modules.length === 0) return;\n\n let unmounted = false;\n let activated: ActivatedFrontendProviderSdks | undefined;\n\n void activateFrontendProviderSdks({\n surface: AppFrontendType.STATIC_WEBSITE,\n modules,\n grantedConsentPurposes,\n onDiagnostic: (diagnostic) => {\n // The website runtime has no logger service, and a provider that cannot\n // start is a developer-facing fact that must not be silent.\n // eslint-disable-next-line no-console\n console.warn(`[wildo-provider-sdk] ${diagnostic.kind}: ${diagnostic.message}`);\n },\n }).then((result) => {\n activated = result;\n // Activation is asynchronous and unmount is not: if the surface went away\n // while a vendor bundle was still downloading, stop what we just started\n // rather than leaving a client nobody can reach.\n if (unmounted) void result.deactivateAll();\n });\n\n return () => {\n unmounted = true;\n void activated?.deactivateAll();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed by the granted SET, see grantedKey.\n }, [registry, consentBased, key]);\n}\n"]}
1
+ {"version":3,"file":"useWebsiteProviderScripts.js","sourceRoot":"","sources":["../../../../src/core/external-providers/useWebsiteProviderScripts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The website's executable-surface hooks: load what the live providers declared.\n *\n * Two mechanisms, one per kind of vendor. `useWebsiteProviderScripts` adds the\n * `<script>` tags a provider declared — the case a dynamic `import()` cannot\n * serve, and the dominant one on a marketing site. `useWebsiteProviderSdks`\n * activates the npm SDK of any provider that ships one, which reaches this\n * bundle through the CODE channel.\n *\n * Both are effect-only, and that is not incidental: this is an Astro site, so\n * page modules execute in Node at build time. Injecting into a document that\n * does not exist would throw, and starting a vendor SDK there would run a live\n * analytics client inside a build.\n *\n * Both come from `@wildo-ai/external-connectors-public/website`. The script\n * injector lives there rather than here so the SaaS application, the technical\n * doc and this site run ONE copy of it: a second copy of a DOM mutation\n * eventually disagrees about idempotence, which is how a vendor tag gets loaded\n * twice. It first shipped here alone, and that is how the application came to\n * declare its Drive Picker's Google API script and never inject it.\n *\n * ## Client-side injection, and the alternative that was rejected\n *\n * This is an Astro static site, so the \"obvious\" home for a third-party tag is\n * the built HTML `<head>`. That was rejected for now: the set of live providers\n * is per SERVICE and per ENVIRONMENT, resolved from the materialized artifact,\n * while the built HTML is one artifact shared across deploys of the same build.\n * Baking the tags in would make the build environment-specific — the same class\n * of coupling the DATA channel exists to avoid. Injecting from the client keeps\n * one build correct everywhere, at the cost of the tag arriving after hydration.\n * Revisit if a provider ever needs to run before first paint.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n activateFrontendProviderSdks,\n collectProviderScriptsWithoutComponents,\n consentPurposesRequiredByProvider,\n createFrontendProviderSdkActivationLane,\n injectProviderScripts,\n} from '@wildo-ai/external-connectors-public/website';\nimport { AppFrontendType, type ConsentPurpose } from '@wildo-ai/saas-models/public-runtime';\nimport type { FrontendWebsiteProviderRegistry } from './frontend-provider-registry.website';\nimport type { WebsiteProviderComponentRegistry } from './provider-component-registry.website';\nimport type { WebsiteProviderSdkActivation } from './website-provider-sdk-activations';\n\n/**\n * A stable key for a granted-purpose set, so an effect re-runs when the SET changes and not when a new\n * array with the same members is handed in.\n */\nfunction grantedKey(grantedConsentPurposes: readonly ConsentPurpose[]): string {\n return [...grantedConsentPurposes].sort().join(',');\n}\n\n/**\n * Injects the `<script>` tags of the live providers that rest on NO consent purpose and render no component.\n *\n * No consent input, deliberately (#1704): an executed `<script>` cannot be stopped, so a provider that rests on a\n * purpose must ship as a stoppable SDK (started on the grant and stopped on withdrawal by\n * {@link useWebsiteProviderSdks}); a consent-based module declaring `scripts` is refused at projection and skipped by\n * the collector. No grant could change what this injects.\n */\nexport function useWebsiteProviderScripts(\n registry: FrontendWebsiteProviderRegistry | undefined,\n /**\n * The website's provider-component registry (#1703). A provider with a registered component loads its\n * scripts WITH that component, when a page first shows it — so an address lookup's vendor loader is\n * not downloaded on every page of the site, only on the one with the form. This hook injects the rest.\n */\n componentRegistry?: WebsiteProviderComponentRegistry,\n): void {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n const withComponents = [...(componentRegistry?.providersWithComponents() ?? [])].map((providerRef) => ({ providerRef }));\n const scripts = collectProviderScriptsWithoutComponents(registry?.modules.values() ?? [], withComponents);\n if (scripts.length === 0) return;\n\n injectProviderScripts(scripts, document);\n // Deliberately no teardown. A vendor tag installs a global and often starts\n // work immediately; removing the element undoes neither, so a cleanup that\n // removed it would only make the next mount inject a SECOND copy of an\n // already-running script. Idempotence by `src` is the real guard.\n }, [registry, componentRegistry]);\n}\n\n/**\n * Loads and starts the npm SDK of every live provider that declares one.\n *\n * Unlike a script tag, an SDK activation IS undone on unmount where the vendor\n * offers a teardown — an analytics client that cannot be stopped cannot honour\n * a consent withdrawal, and accumulates one instance per view transition.\n */\nexport function useWebsiteProviderSdks(\n registry: FrontendWebsiteProviderRegistry | undefined,\n /**\n * The purposes the visitor granted (#534). Providers are activated in TWO groups on purpose: those that\n * rest on no purpose (error monitoring, captcha) are started once and never restarted by a consent\n * change, while the consent-based ones start when their purpose is granted and are DEACTIVATED when it\n * is withdrawn — which is the only way a withdrawal stops a running analytics client.\n */\n grantedConsentPurposes: readonly ConsentPurpose[],\n): ReadonlyArray<WebsiteProviderSdkActivation> {\n const consentFree = useProviderSdkGroup(registry, false, []);\n const consentBased = useProviderSdkGroup(registry, true, grantedConsentPurposes);\n // The providers RUNNING on this page, both groups, published by the page layout (#1735): a form reads the captcha\n // vendor's handle from here. Emptied the moment a group stops, so nothing holds a handle past a withdrawal.\n return useMemo(() => [...consentFree, ...consentBased], [consentFree, consentBased]);\n}\n\nfunction useProviderSdkGroup(\n registry: FrontendWebsiteProviderRegistry | undefined,\n consentBased: boolean,\n grantedConsentPurposes: readonly ConsentPurpose[],\n): ReadonlyArray<WebsiteProviderSdkActivation> {\n const [running, setRunning] = useState<ReadonlyArray<WebsiteProviderSdkActivation>>([]);\n // Keyed by the grants this group's providers actually rest on, so changing an unrelated purpose does\n // not stop and restart a running analytics client.\n const relevant = registry === undefined\n ? []\n : [...registry.modules.values()]\n .filter((module) => (consentPurposesRequiredByProvider(module).length > 0) === consentBased)\n .flatMap((module) => consentPurposesRequiredByProvider(module))\n .filter((purpose) => grantedConsentPurposes.includes(purpose));\n const key = grantedKey(relevant);\n // One lane per group for the page's lifetime: a start waits for the previous stop.\n const lane = useRef(createFrontendProviderSdkActivationLane());\n useEffect(() => {\n if (registry === undefined) return;\n const modules = [...registry.modules.values()].filter(\n (module) => (consentPurposesRequiredByProvider(module).length > 0) === consentBased,\n );\n if (modules.length === 0) return;\n\n // Through the group's lane, so a start never overlaps the previous stop of the same page-global vendor (#1704): a\n // visitor who grants, withdraws and grants again while the bundle downloads must not have the first run's stop\n // switch off the second run's client. The stop also covers an unmount mid-download.\n const stop = lane.current.start(\n () => activateFrontendProviderSdks({\n surface: AppFrontendType.STATIC_WEBSITE,\n modules,\n grantedConsentPurposes,\n onDiagnostic: (diagnostic) => {\n // The website runtime has no logger service, and a provider that cannot\n // start is a developer-facing fact that must not be silent.\n // eslint-disable-next-line no-console\n console.warn(`[wildo-provider-sdk] ${diagnostic.kind}: ${diagnostic.message}`);\n },\n }),\n (result) => {\n setRunning(modules.flatMap((module) => {\n const activation = result.activations.get(module.metadata.ref);\n return activation === undefined\n ? []\n : [{ providerRef: module.metadata.ref, providerCapabilities: [...module.providerCapabilities], activation }];\n }));\n },\n );\n return () => {\n setRunning([]);\n stop();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed by the granted SET, see grantedKey.\n }, [registry, consentBased, key]);\n return running;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { ProviderFrontendSdkActivation } from '@wildo-ai/external-connectors-public/website';
2
+ /** One running provider: its ref, the capabilities it serves, and its activation (whose `handle` is the vendor's client). */
3
+ export interface WebsiteProviderSdkActivation {
4
+ readonly providerRef: string;
5
+ readonly providerCapabilities: ReadonlyArray<string>;
6
+ readonly activation: ProviderFrontendSdkActivation;
7
+ }
8
+ export declare const WebsiteProviderSdkActivationsContext: import("react").Context<readonly WebsiteProviderSdkActivation[]>;
9
+ /** The provider SDKs running on this page, in declaration order. Empty outside a page layout and before any start. */
10
+ export declare function useWebsiteProviderSdkActivations(): ReadonlyArray<WebsiteProviderSdkActivation>;
11
+ //# sourceMappingURL=website-provider-sdk-activations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"website-provider-sdk-activations.d.ts","sourceRoot":"","sources":["../../../../src/core/external-providers/website-provider-sdk-activations.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAElG,6HAA6H;AAC7H,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,oBAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACrD,QAAQ,CAAC,UAAU,EAAE,6BAA6B,CAAC;CACpD;AAED,eAAO,MAAM,oCAAoC,kEAAiE,CAAC;AAEnH,sHAAsH;AACtH,wBAAgB,gCAAgC,IAAI,aAAa,CAAC,4BAA4B,CAAC,CAE9F"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The provider SDKs RUNNING on this website page (#1735), published by the page layout.
3
+ *
4
+ * A section reaches a running vendor through here rather than through a global — the contact form asks the consented
5
+ * captcha vendor for a token this way. The list is emptied the moment a provider is stopped (a consent withdrawal, a
6
+ * navigation), so a consumer that reads it at the moment it needs a handle never holds one past a withdrawal.
7
+ */
8
+ import { createContext, useContext } from 'react';
9
+ export const WebsiteProviderSdkActivationsContext = createContext([]);
10
+ /** The provider SDKs running on this page, in declaration order. Empty outside a page layout and before any start. */
11
+ export function useWebsiteProviderSdkActivations() {
12
+ return useContext(WebsiteProviderSdkActivationsContext);
13
+ }
14
+ //# sourceMappingURL=website-provider-sdk-activations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"website-provider-sdk-activations.js","sourceRoot":"","sources":["../../../../src/core/external-providers/website-provider-sdk-activations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAUlD,MAAM,CAAC,MAAM,oCAAoC,GAAG,aAAa,CAA8C,EAAE,CAAC,CAAC;AAEnH,sHAAsH;AACtH,MAAM,UAAU,gCAAgC;IAC9C,OAAO,UAAU,CAAC,oCAAoC,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["/**\n * The provider SDKs RUNNING on this website page (#1735), published by the page layout.\n *\n * A section reaches a running vendor through here rather than through a global — the contact form asks the consented\n * captcha vendor for a token this way. The list is emptied the moment a provider is stopped (a consent withdrawal, a\n * navigation), so a consumer that reads it at the moment it needs a handle never holds one past a withdrawal.\n */\nimport { createContext, useContext } from 'react';\nimport type { ProviderFrontendSdkActivation } from '@wildo-ai/external-connectors-public/website';\n\n/** One running provider: its ref, the capabilities it serves, and its activation (whose `handle` is the vendor's client). */\nexport interface WebsiteProviderSdkActivation {\n readonly providerRef: string;\n readonly providerCapabilities: ReadonlyArray<string>;\n readonly activation: ProviderFrontendSdkActivation;\n}\n\nexport const WebsiteProviderSdkActivationsContext = createContext<ReadonlyArray<WebsiteProviderSdkActivation>>([]);\n\n/** The provider SDKs running on this page, in declaration order. Empty outside a page layout and before any start. */\nexport function useWebsiteProviderSdkActivations(): ReadonlyArray<WebsiteProviderSdkActivation> {\n return useContext(WebsiteProviderSdkActivationsContext);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"define-website-page-manifest.d.ts","sourceRoot":"","sources":["../../../../../src/core/factories/define-website-page-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,4BAA4B,EAC7B,MAAM,8DAA8D,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAEhF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,8BAA8B,CAAC,MAAM;IACpD,GAAG,EAAE,cAAc,GAAG,MAAM,CAAC;IAC7B,SAAS,EAAE,oBAAoB,GAAG,MAAM,CAAC;IACzC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,WAAW,EAAE,SAAS,CAAC,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC;IACrD,aAAa,EAAE,wBAAwB,CAAC;IACxC,SAAS,CAAC,EAAE,4BAA4B,CAAC;IACzC,GAAG,CAAC,EAAE,oBAAoB,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB,CAAC,MAAM,CAC9C,SAAQ,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC;IAClD,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACtE,KAAK,EAAE,8BAA8B,CAAC,MAAM,CAAC,GAC5C,wBAAwB,CAAC,MAAM,CAAC,CAmBlC;AACD,yEAAyE"}
1
+ {"version":3,"file":"define-website-page-manifest.d.ts","sourceRoot":"","sources":["../../../../src/core/factories/define-website-page-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,4BAA4B,EAC7B,MAAM,8DAA8D,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAEhF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,8BAA8B,CAAC,MAAM;IACpD,GAAG,EAAE,cAAc,GAAG,MAAM,CAAC;IAC7B,SAAS,EAAE,oBAAoB,GAAG,MAAM,CAAC;IACzC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,WAAW,EAAE,SAAS,CAAC,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC;IACrD,aAAa,EAAE,wBAAwB,CAAC;IACxC,SAAS,CAAC,EAAE,4BAA4B,CAAC;IACzC,GAAG,CAAC,EAAE,oBAAoB,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB,CAAC,MAAM,CAC9C,SAAQ,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC;IAClD,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACtE,KAAK,EAAE,8BAA8B,CAAC,MAAM,CAAC,GAC5C,wBAAwB,CAAC,MAAM,CAAC,CAmBlC;AACD,yEAAyE"}
@@ -65,9 +65,9 @@ export interface TypedWebsiteSectionDefinition<TProps> extends Omit<WebsiteSecti
65
65
  * **Why a factory instead of constructing the literal directly**:
66
66
  * - Consumers don't have to remember to parse refs / keys
67
67
  * themselves before assembling the literal.
68
- * - The schema parse provides a single audited validation chokepoint
69
- * so `wildo website audit` (Phase 7) can re-validate definitions
70
- * without re-entering the React graph.
68
+ * - The schema parse provides a single audited validation chokepoint,
69
+ * and the parsed `expectedLabelKeys` are what `wildo website audit`
70
+ * checks every locale pack against, without re-entering the React graph.
71
71
  * - The factory is the documented authoring API the
72
72
  * `website_factory_authoring` skill targets — keeping the
73
73
  * authoring surface narrow lets the framework evolve the
@@ -1 +1 @@
1
- {"version":3,"file":"define-website-section.d.ts","sourceRoot":"","sources":["../../../../../src/core/factories/define-website-section.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kEAAkE,CAAC;AACjH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AACrG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oDAAoD,CAAC;AAC1F,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sEAAsE,CAAC;AAExH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,yBAAyB,CAAC,MAAM;IAC/C,UAAU,EAAE,iBAAiB,GAAG,MAAM,CAAC;IACvC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,iBAAiB,EAAE,SAAS,CAAC,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC;IACzD,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,2BAA2B,CAAC;CACvD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,6BAA6B,CAAC,MAAM,CACnD,SAAQ,IAAI,CAAC,wBAAwB,EAAE,WAAW,GAAG,mBAAmB,CAAC;IACzE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,2BAA2B,CAAC;CACvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EACzC,KAAK,EAAE,yBAAyB,CAAC,MAAM,CAAC,GACvC,6BAA6B,CAAC,MAAM,CAAC,CAoBvC;AACD,mEAAmE"}
1
+ {"version":3,"file":"define-website-section.d.ts","sourceRoot":"","sources":["../../../../src/core/factories/define-website-section.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kEAAkE,CAAC;AACjH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AACrG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oDAAoD,CAAC;AAC1F,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sEAAsE,CAAC;AAExH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,yBAAyB,CAAC,MAAM;IAC/C,UAAU,EAAE,iBAAiB,GAAG,MAAM,CAAC;IACvC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,iBAAiB,EAAE,SAAS,CAAC,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC;IACzD,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,2BAA2B,CAAC;CACvD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,6BAA6B,CAAC,MAAM,CACnD,SAAQ,IAAI,CAAC,wBAAwB,EAAE,WAAW,GAAG,mBAAmB,CAAC;IACzE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,2BAA2B,CAAC;CACvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EACzC,KAAK,EAAE,yBAAyB,CAAC,MAAM,CAAC,GACvC,6BAA6B,CAAC,MAAM,CAAC,CAoBvC;AACD,mEAAmE"}
@@ -15,9 +15,9 @@ import { WebsiteSectionDefinitionSchema } from '../../schemas/sections/website-s
15
15
  * **Why a factory instead of constructing the literal directly**:
16
16
  * - Consumers don't have to remember to parse refs / keys
17
17
  * themselves before assembling the literal.
18
- * - The schema parse provides a single audited validation chokepoint
19
- * so `wildo website audit` (Phase 7) can re-validate definitions
20
- * without re-entering the React graph.
18
+ * - The schema parse provides a single audited validation chokepoint,
19
+ * and the parsed `expectedLabelKeys` are what `wildo website audit`
20
+ * checks every locale pack against, without re-entering the React graph.
21
21
  * - The factory is the documented authoring API the
22
22
  * `website_factory_authoring` skill targets — keeping the
23
23
  * authoring surface narrow lets the framework evolve the