@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.0

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 (363) hide show
  1. package/apps/storefront/astro.config.mjs +3 -3
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +12 -14
  4. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  5. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  6. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  7. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  8. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  9. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  10. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  11. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  12. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  13. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  14. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  15. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  16. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  17. package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
  18. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  19. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  20. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  21. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  22. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  23. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  24. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  25. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  26. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  27. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  28. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  29. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  30. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  31. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  32. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  33. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  34. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  35. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  36. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  37. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  38. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  39. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  40. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  41. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  42. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  43. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  44. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  45. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  46. package/apps/storefront/src/components/Img.astro +1 -1
  47. package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
  48. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  49. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
  50. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  51. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  52. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  53. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  54. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  55. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  56. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  57. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  58. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  59. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  60. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  61. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  62. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  63. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  64. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  65. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  66. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  67. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  68. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  69. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  70. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  71. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  72. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  73. package/apps/storefront/src/config/resolver.ts +4 -4
  74. package/apps/storefront/src/layouts/Layout.astro +100 -20
  75. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  76. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
  77. package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
  78. package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
  79. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  80. package/apps/storefront/src/lib/apps/adminService.ts +10 -0
  81. package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
  82. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  83. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  84. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  85. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  86. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  87. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  88. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  89. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  90. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  91. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  92. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  93. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  94. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
  95. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
  96. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  97. package/apps/storefront/src/lib/apps/registryService.ts +264 -10
  98. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  99. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  100. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  101. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  102. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  103. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  104. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  105. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  106. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  107. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  108. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  109. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  110. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  111. package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
  112. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  113. package/apps/storefront/src/lib/blog/collection.ts +97 -10
  114. package/apps/storefront/src/lib/blog/markdown.ts +1 -1
  115. package/apps/storefront/src/lib/blog/provider.ts +57 -3
  116. package/apps/storefront/src/lib/blog/types.ts +9 -9
  117. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  118. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  119. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  120. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  121. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  122. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  123. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  124. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  125. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  126. package/apps/storefront/src/lib/content/index.ts +5 -5
  127. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  128. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  129. package/apps/storefront/src/lib/d1/catalog.ts +12 -0
  130. package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
  131. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  132. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  133. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  134. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  135. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  136. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  137. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  138. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  139. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  140. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  141. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  142. package/apps/storefront/src/lib/edgeCache.ts +18 -6
  143. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  144. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  145. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  146. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  147. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  148. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  149. package/apps/storefront/src/lib/membership/model.ts +1 -2
  150. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
  151. package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
  152. package/apps/storefront/src/lib/newsletter/send.ts +3 -3
  153. package/apps/storefront/src/lib/previewReload.ts +57 -1
  154. package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
  155. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  156. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  157. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  158. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  159. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  160. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  161. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  162. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  163. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  164. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  165. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  166. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  167. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  168. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  169. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  170. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  171. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  172. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  173. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  174. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  175. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  176. package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
  177. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  178. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  179. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  180. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  181. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  182. package/apps/storefront/src/lib/seo/alternates.ts +1 -1
  183. package/apps/storefront/src/lib/social/notify.ts +4 -4
  184. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  185. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  186. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  187. package/apps/storefront/src/lib/the-build/provider.ts +9 -4
  188. package/apps/storefront/src/middleware/index.ts +273 -51
  189. package/apps/storefront/src/pages/[...slug].astro +9 -4
  190. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  191. package/apps/storefront/src/pages/admin/select.astro +96 -0
  192. package/apps/storefront/src/pages/admin.astro +16 -2
  193. package/apps/storefront/src/pages/api/activity.ts +8 -8
  194. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  195. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  196. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  197. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  198. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  199. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  200. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  201. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  202. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  203. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  204. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  205. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  206. package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
  207. package/apps/storefront/src/pages/blog/[slug].astro +5 -5
  208. package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
  209. package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
  210. package/apps/storefront/src/pages/blog/index.astro +2 -2
  211. package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
  212. package/apps/storefront/src/pages/cockpit.astro +61 -1362
  213. package/apps/storefront/src/pages/index.astro +10 -5
  214. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  215. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  216. package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
  217. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  218. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  219. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  220. package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
  221. package/apps/storefront/src/pages/the-build/index.astro +2 -2
  222. package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
  223. package/apps/storefront/src/styles/admin.css +27 -1
  224. package/apps/storefront/tsconfig.json +1 -1
  225. package/package.json +1 -1
  226. package/packages/public-runtime/package.json +2 -1
  227. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  228. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  229. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  230. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  231. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  232. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  233. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  234. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  235. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  236. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  237. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  238. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  239. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  240. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  241. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  242. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  243. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  244. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  245. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  246. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  247. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  248. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  249. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  250. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  251. package/packages/public-runtime/src/activity/catalog.ts +41 -37
  252. package/packages/public-runtime/src/activity/event.ts +6 -6
  253. package/packages/public-runtime/src/activity/index.ts +2 -2
  254. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  255. package/packages/public-runtime/src/candidate-index.ts +42 -8
  256. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  257. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  258. package/packages/public-runtime/src/checkout-style.ts +7 -7
  259. package/packages/public-runtime/src/checkout.ts +3 -3
  260. package/packages/public-runtime/src/chrome.ts +777 -0
  261. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  262. package/packages/public-runtime/src/csp.ts +99 -0
  263. package/packages/public-runtime/src/customization-runtime.ts +32 -1
  264. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  265. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  266. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  267. package/packages/public-runtime/src/evidence.ts +288 -0
  268. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  269. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  270. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  271. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  272. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  273. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  274. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  275. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  276. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  277. package/packages/public-runtime/src/index.ts +16 -1
  278. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  279. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  280. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  281. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  282. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  283. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  284. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  285. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  286. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  287. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  288. package/packages/public-runtime/src/integration/csp.ts +105 -0
  289. package/packages/public-runtime/src/integration/digest.ts +43 -0
  290. package/packages/public-runtime/src/integration/environment.ts +228 -0
  291. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  292. package/packages/public-runtime/src/integration/events.ts +701 -0
  293. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  294. package/packages/public-runtime/src/integration/index.ts +90 -0
  295. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  296. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  297. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  298. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  299. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  300. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  301. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  302. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  303. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  304. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  305. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  306. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  307. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  308. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  309. package/packages/public-runtime/src/integration/observability.ts +451 -0
  310. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  311. package/packages/public-runtime/src/integration/phases.ts +182 -0
  312. package/packages/public-runtime/src/integration/plan.ts +112 -0
  313. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  314. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  315. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  316. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  317. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  318. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  319. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  320. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  321. package/packages/public-runtime/src/integration/result.ts +239 -0
  322. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  323. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  324. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  325. package/packages/public-runtime/src/integration/version.ts +92 -0
  326. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  327. package/packages/public-runtime/src/membership.ts +2 -2
  328. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  329. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  330. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  331. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  332. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  333. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  334. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  335. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  336. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  337. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  338. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  339. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  340. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  341. package/packages/public-runtime/src/static-bundle.ts +5 -5
  342. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  343. package/packages/public-runtime/src/tenant.ts +45 -9
  344. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  345. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  346. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  347. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  348. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  349. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  350. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  351. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  352. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  353. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  354. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  355. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  356. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  357. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  358. package/scripts/dev/file-browser.mjs +2 -2
  359. package/scripts/dev/file-writer.mjs +2 -2
  360. package/scripts/dev/integration-guardrails.mjs +498 -0
  361. package/scripts/dev/safe-path.mjs +2 -2
  362. package/scripts/dev/tenant-source-root.mjs +1 -1
  363. package/scripts/tot-dev.mjs +2 -2
@@ -0,0 +1,497 @@
1
+ /**
2
+ * The platform-owned browser commerce event broker (ADR 0014 M2
3
+ * `integration-commerce-data-minimized`, M3 `integration-events-dual-delivery`;
4
+ * ext-commerce-02). It is the single client-side seam between the storefront's own
5
+ * page/product/cart/handoff signals and the scoped subscribers — apps, sandboxed
6
+ * widget iframes, elevated-parent code — that want to measure them.
7
+ *
8
+ * It owns three properties that its callers do NOT get to override:
9
+ *
10
+ * - SERVER-OWNED CONTEXT IS STAMPED, NEVER SUPPLIED. A caller hands the broker only
11
+ * an anonymous, allowlisted `data` payload plus the logical action's identity
12
+ * ({@link CommerceEmitInput}); the broker stamps `storefront.tenant`,
13
+ * `environment`, `siteSurface`, `emitter: "browser"`, and the current consent
14
+ * snapshot from the trusted execution context it was constructed with. Every
15
+ * emitted envelope is then run through {@link validateCommerceEvent}, so the
16
+ * closed field catalog rejects any PII, payment, order, or unknown field — a
17
+ * privileged parent with DOM authority gains NO extra server-held data by that
18
+ * authority, and a browser emitter can never author a server-authoritative
19
+ * conversion (`forged_authority`).
20
+ *
21
+ * - ONE EVENT PER LOGICAL ACTION. The broker deduplicates on the envelope's
22
+ * {@link eventDedupeKey} (`id`): a second emit of the same logical id — a
23
+ * double-fired DOM handler, a replay — collapses to a no-op, so a subscriber
24
+ * registered once observes each logical action exactly once.
25
+ *
26
+ * - SUBSCRIBER FAULTS ARE ISOLATED. Subscriptions are keyed by a stable id, so a
27
+ * re-subscribe REPLACES rather than stacks (no duplicate listeners after a
28
+ * consent/config update). Each subscriber is delivered inside its own try/catch
29
+ * (the kernel-05 lifecycle isolation model), so a throwing subscriber cannot
30
+ * break the cart, navigation, or any other subscriber, and the emit call returns
31
+ * normally to its caller.
32
+ *
33
+ * Delivery is consent-filtered per subscriber: a subscriber declares the
34
+ * {@link ConsentPurpose} its destination depends on, and the broker withholds the
35
+ * event unless that purpose is eligible under the current snapshot
36
+ * ({@link isPurposeEligible}) — the same predicate the server destination filter and
37
+ * the browser destination loader share. A sandboxed iframe consumer receives the
38
+ * validated {@link CommerceEvent} as a TYPED, channel-tagged postMessage, never a raw
39
+ * ad-hoc message.
40
+ *
41
+ * DOM-free and dependency-injected (trusted context, a consent snapshot source, a
42
+ * clock), so it is edge-safe and fully testable in node; an iframe sink is the only
43
+ * browser-touching surface and is supplied by the caller.
44
+ */
45
+ import type {
46
+ CommerceEvent,
47
+ CommerceEventType,
48
+ EventCorrelation,
49
+ TrustedEventContext,
50
+ } from "./events.js";
51
+ import {
52
+ EVENT_SOURCE_OF_TRUTH,
53
+ PURCHASE_COMPLETED_EVENT_TYPE,
54
+ eventDedupeKey,
55
+ validateCommerceEvent,
56
+ } from "./events.js";
57
+ import type { ConsentSnapshot } from "./consent-broker.js";
58
+ import { isPurposeEligible, toEventConsentSnapshot } from "./consent-broker.js";
59
+ import type { ConsentPurpose } from "../integration-capabilities.js";
60
+ import { COMMERCE_EVENT_STOREFRONT_SCHEMA_VERSION } from "./version.js";
61
+ import type { ContractViolation } from "./result.js";
62
+ import { safeManagedAnalyticsDiagnosticId } from "./managed-analytics-value-safety.js";
63
+ import {
64
+ validateTenantActionRegistry,
65
+ type TenantActionRegistry,
66
+ } from "./managed-analytics.js";
67
+
68
+ /** The channel tag every broker→iframe message carries; a consumer keys its parse on it. */
69
+ export const COMMERCE_EVENT_MESSAGE_CHANNEL = "tot.commerce-event.v1" as const;
70
+
71
+ /** The typed envelope the broker posts to an iframe consumer — never a bare event. */
72
+ export interface CommerceEventMessage {
73
+ channel: typeof COMMERCE_EVENT_MESSAGE_CHANNEL;
74
+ event: CommerceEvent;
75
+ }
76
+
77
+ /**
78
+ * What a caller supplies to emit one logical browser commerce action. It carries the
79
+ * anonymous, allowlisted `data` and the action's identity ONLY — never the
80
+ * server-owned `storefront.*` context, which the broker stamps from its trusted
81
+ * construction context so it cannot be caller-forged.
82
+ */
83
+ export interface CommerceEmitInput {
84
+ /** A browser source-of-truth event type; a server-authoritative type is refused. */
85
+ type: CommerceEventType;
86
+ /** The opaque logical event id — the dedupe key. The same logical action reuses it. */
87
+ logicalId: string;
88
+ /** Opaque in-event subject (e.g. an anonymous cart/item-set handle). */
89
+ subject: string;
90
+ /** The anonymous, field-allowlisted payload for this type. Validated on emit. */
91
+ data: Record<string, unknown>;
92
+ /** The bounded-lifetime attribution thread this action belongs to. */
93
+ correlation: EventCorrelation;
94
+ /** Optional per-emitter monotonic counter for out-of-order detection. */
95
+ sequence?: number;
96
+ }
97
+
98
+ /** The minimal postMessage surface a real sandboxed iframe `contentWindow` satisfies. */
99
+ export interface CommerceEventPostTarget {
100
+ postMessage(message: CommerceEventMessage, targetOrigin: string): void;
101
+ }
102
+
103
+ /**
104
+ * Where a subscriber's events go:
105
+ * - `callback` — an in-page handler invoked with the validated event.
106
+ * - `iframe` — a sandboxed consumer the broker posts a TYPED, channel-tagged
107
+ * message to at a fixed `targetOrigin`, so the frame reads a validated event and
108
+ * never a raw one.
109
+ */
110
+ export type CommerceEventSink =
111
+ | { kind: "callback"; handler: (event: CommerceEvent) => void }
112
+ | { kind: "iframe"; frame: CommerceEventPostTarget; targetOrigin: string };
113
+
114
+ /**
115
+ * One scoped subscription. `id` is its stable identity: subscribing again with the
116
+ * same id REPLACES the prior registration (no duplicate listeners). `types` is the
117
+ * app/topic scope; omitting it subscribes to every browser event type. `consentPurpose`
118
+ * is the destination's required purpose; omitting it means essential-only (always
119
+ * eligible).
120
+ */
121
+ export interface CommerceEventSubscription {
122
+ id: string;
123
+ sink: CommerceEventSink;
124
+ types?: readonly CommerceEventType[];
125
+ consentPurpose?: ConsentPurpose;
126
+ }
127
+
128
+ /** Why a subscriber did not receive an emitted event (delivery is scoped + fail-soft). */
129
+ export type SubscriberSkipReason = "out-of-scope" | "consent-ineligible" | "threw";
130
+
131
+ /** The per-subscriber outcome of one emit — a throwing sink is recorded, never propagated. */
132
+ export interface SubscriberDeliveryOutcome {
133
+ subscriberId: string;
134
+ delivered: boolean;
135
+ reason?: SubscriberSkipReason;
136
+ /** Stable diagnostic code; subscriber exception text and stacks are never copied. */
137
+ error?: "subscriber_threw";
138
+ }
139
+
140
+ /** Why an emit delivered to nobody without being an error (the envelope was fine). */
141
+ export type EmitSkipReason = "duplicate" | "not-browser-authored";
142
+
143
+ /** The result of one emit. `ok: false` carries the envelope's contract violations. */
144
+ export type CommerceEmitResult =
145
+ | {
146
+ ok: true;
147
+ event: CommerceEvent;
148
+ /** False when the logical id was already emitted (deduplicated) — no re-fire. */
149
+ emitted: boolean;
150
+ skipReason?: EmitSkipReason;
151
+ deliveries: SubscriberDeliveryOutcome[];
152
+ }
153
+ | { ok: false; violations: ContractViolation[] };
154
+
155
+ /** A structured, non-sensitive delivery signal for observability — never a payload or stack. */
156
+ export interface CommerceBrokerObservation {
157
+ type: CommerceEventType;
158
+ logicalId: string;
159
+ subscriberId: string;
160
+ ok: boolean;
161
+ /** Stable diagnostic code; subscriber exception text and stacks are never observed. */
162
+ error?: "subscriber_threw";
163
+ }
164
+
165
+ export type CommerceBrokerObserver = (observation: CommerceBrokerObservation) => void;
166
+
167
+ export interface CommerceEventBrokerOptions {
168
+ /** The trusted tenant/environment/surface stamped onto every envelope; never caller-set. */
169
+ trusted: TrustedEventContext;
170
+ /** The event `source` URN for this surface; server-owned, stamped from trusted context. */
171
+ source: string;
172
+ /** Reads the current consent snapshot at emit time (the neutral broker's snapshot). */
173
+ consentSnapshot: () => ConsentSnapshot;
174
+ /** Server-loaded explicit action registry for this tenant; absent/invalid fails action emission closed. */
175
+ tenantActionRegistry?: unknown;
176
+ /** ISO-8601 timestamp source; defaults to wall clock. */
177
+ now?: () => string;
178
+ /** Optional observer for delivery diagnostics (isolation faults surface here, not to callers). */
179
+ observe?: CommerceBrokerObserver;
180
+ }
181
+
182
+ const MANAGED_ANALYTICS_EVENT_TYPES: readonly CommerceEventType[] = [
183
+ "storefront.page.viewed.v2",
184
+ "storefront.action.activated.v1",
185
+ ];
186
+
187
+ function isManagedAnalyticsEvent(type: CommerceEventType): boolean {
188
+ return MANAGED_ANALYTICS_EVENT_TYPES.includes(type);
189
+ }
190
+
191
+ export class CommerceEventBroker {
192
+ private readonly subscriptions = new Map<string, CommerceEventSubscription>();
193
+ private readonly emittedIds = new Set<string>();
194
+ private readonly opts: CommerceEventBrokerOptions;
195
+ private readonly tenantActionRegistry: TenantActionRegistry | null;
196
+ private readonly tenantActionRegistryViolations: ContractViolation[];
197
+
198
+ constructor(options: CommerceEventBrokerOptions) {
199
+ this.opts = options;
200
+ if (options.tenantActionRegistry === undefined) {
201
+ this.tenantActionRegistry = null;
202
+ this.tenantActionRegistryViolations = [];
203
+ } else {
204
+ const registry = validateTenantActionRegistry(options.tenantActionRegistry);
205
+ this.tenantActionRegistry = registry.ok ? registry.value : null;
206
+ this.tenantActionRegistryViolations = registry.ok ? [] : registry.violations;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Register (or REPLACE) a scoped subscription. Keying on `subscription.id` means a
212
+ * re-subscribe — e.g. after a consent change re-derives a destination — swaps the
213
+ * registration in place rather than stacking a second listener, so one logical
214
+ * action still fires the subscriber exactly once. Returns an unsubscribe function.
215
+ */
216
+ subscribe(subscription: CommerceEventSubscription): () => void {
217
+ this.subscriptions.set(subscription.id, subscription);
218
+ return () => {
219
+ const current = this.subscriptions.get(subscription.id);
220
+ // Only remove if this exact registration is still the live one; a later
221
+ // re-subscribe under the same id owns the slot and must not be torn down here.
222
+ if (current === subscription) this.subscriptions.delete(subscription.id);
223
+ };
224
+ }
225
+
226
+ /** Whether a subscriber id currently holds a registration. */
227
+ hasSubscriber(id: string): boolean {
228
+ return this.subscriptions.has(id);
229
+ }
230
+
231
+ /** Read destination eligibility from the broker's current trusted consent snapshot. */
232
+ isConsentEligible(purpose: ConsentPurpose): boolean {
233
+ return isPurposeEligible(this.opts.consentSnapshot(), purpose);
234
+ }
235
+
236
+ /**
237
+ * Emit one logical browser commerce action. The broker builds the envelope from the
238
+ * trusted context (the caller supplies no server-owned field), validates it against
239
+ * the closed contract, deduplicates on its logical id, and fans it out to every
240
+ * in-scope, consent-eligible subscriber inside per-subscriber isolation.
241
+ *
242
+ * A failed validation delivers to nobody and returns the violations. A duplicate
243
+ * logical id returns `emitted: false`. A subscriber that throws is recorded in its
244
+ * outcome and observed — never rethrown — so the caller (cart, navigation) is
245
+ * unaffected.
246
+ */
247
+ emit(input: CommerceEmitInput): CommerceEmitResult {
248
+ // Fail fast and clearly on a server-authoritative type before building an envelope
249
+ // the validator would reject anyway; the browser can never author a conversion.
250
+ if (EVENT_SOURCE_OF_TRUTH[input.type] !== "browser") {
251
+ return {
252
+ ok: false,
253
+ violations: [
254
+ {
255
+ path: "type",
256
+ code: "forged_authority",
257
+ message: `"${input.type}" is server-authoritative; the browser broker cannot emit it.`,
258
+ },
259
+ ],
260
+ };
261
+ }
262
+
263
+ const snapshot = this.opts.consentSnapshot();
264
+ const envelope: CommerceEvent = {
265
+ specversion: "1.0",
266
+ id: input.logicalId,
267
+ source: this.opts.source,
268
+ type: input.type,
269
+ subject: input.subject,
270
+ time: this.now(),
271
+ datacontenttype: "application/json",
272
+ storefront: {
273
+ schemaVersion: COMMERCE_EVENT_STOREFRONT_SCHEMA_VERSION,
274
+ tenant: this.opts.trusted.tenant,
275
+ environment: this.opts.trusted.environment,
276
+ siteSurface: this.opts.trusted.siteSurface,
277
+ emitter: "browser",
278
+ ...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
279
+ },
280
+ correlation: input.correlation,
281
+ consent: toEventConsentSnapshot(snapshot),
282
+ data: input.data,
283
+ };
284
+
285
+ const validation = validateCommerceEvent(envelope);
286
+ if (!validation.ok) return { ok: false, violations: validation.violations };
287
+ const event = validation.value;
288
+
289
+ if (event.type === "storefront.action.activated.v1") {
290
+ if (!this.tenantActionRegistry) {
291
+ return {
292
+ ok: false,
293
+ violations:
294
+ this.tenantActionRegistryViolations.length > 0
295
+ ? this.tenantActionRegistryViolations
296
+ : [
297
+ {
298
+ path: "data.name",
299
+ code: "unregistered_action",
300
+ message: "Managed actions require a valid tenant action registry.",
301
+ },
302
+ ],
303
+ };
304
+ }
305
+ const actionName = event.data["name"] as string;
306
+ if (!this.tenantActionRegistry.actions.some((action) => action.name === actionName)) {
307
+ return {
308
+ ok: false,
309
+ violations: [
310
+ {
311
+ path: "data.name",
312
+ code: "unregistered_action",
313
+ message: "Action is not present in the tenant's explicit action registry.",
314
+ },
315
+ ],
316
+ };
317
+ }
318
+ }
319
+
320
+ if (isManagedAnalyticsEvent(event.type) && !isPurposeEligible(snapshot, "analytics")) {
321
+ return {
322
+ ok: false,
323
+ violations: [
324
+ {
325
+ path: "consent.eligiblePurposes",
326
+ code: "consent_ineligible",
327
+ message: "Managed analytics events require analytics consent before creation or delivery.",
328
+ },
329
+ ],
330
+ };
331
+ }
332
+
333
+ const key = eventDedupeKey(event);
334
+ if (this.emittedIds.has(key)) {
335
+ return { ok: true, event, emitted: false, skipReason: "duplicate", deliveries: [] };
336
+ }
337
+ this.emittedIds.add(key);
338
+
339
+ const deliveries: SubscriberDeliveryOutcome[] = [];
340
+ for (const subscription of this.subscriptions.values()) {
341
+ deliveries.push(this.deliver(subscription, event, snapshot));
342
+ }
343
+ return { ok: true, event, emitted: true, deliveries };
344
+ }
345
+
346
+ private deliver(
347
+ subscription: CommerceEventSubscription,
348
+ event: CommerceEvent,
349
+ snapshot: ConsentSnapshot,
350
+ ): SubscriberDeliveryOutcome {
351
+ if (subscription.types && !subscription.types.includes(event.type)) {
352
+ return { subscriberId: subscription.id, delivered: false, reason: "out-of-scope" };
353
+ }
354
+ if (
355
+ isManagedAnalyticsEvent(event.type) &&
356
+ subscription.consentPurpose !== "analytics"
357
+ ) {
358
+ return { subscriberId: subscription.id, delivered: false, reason: "consent-ineligible" };
359
+ }
360
+ // essential is always eligible, so an undeclared purpose always passes.
361
+ const purpose = subscription.consentPurpose ?? "essential";
362
+ if (!isPurposeEligible(snapshot, purpose)) {
363
+ return { subscriberId: subscription.id, delivered: false, reason: "consent-ineligible" };
364
+ }
365
+
366
+ try {
367
+ this.dispatch(subscription.sink, event);
368
+ this.observe({
369
+ type: event.type,
370
+ logicalId: event.id,
371
+ subscriberId: subscription.id,
372
+ ok: true,
373
+ });
374
+ return { subscriberId: subscription.id, delivered: true };
375
+ } catch {
376
+ this.observe({
377
+ type: event.type,
378
+ logicalId: event.id,
379
+ subscriberId: subscription.id,
380
+ ok: false,
381
+ error: "subscriber_threw",
382
+ });
383
+ return {
384
+ subscriberId: subscription.id,
385
+ delivered: false,
386
+ reason: "threw",
387
+ error: "subscriber_threw",
388
+ };
389
+ }
390
+ }
391
+
392
+ private dispatch(sink: CommerceEventSink, event: CommerceEvent): void {
393
+ if (sink.kind === "callback") {
394
+ sink.handler(event);
395
+ return;
396
+ }
397
+ // A sandboxed consumer gets a TYPED, channel-tagged, already-validated envelope.
398
+ sink.frame.postMessage({ channel: COMMERCE_EVENT_MESSAGE_CHANNEL, event }, sink.targetOrigin);
399
+ }
400
+
401
+ private now(): string {
402
+ return this.opts.now ? this.opts.now() : new Date().toISOString();
403
+ }
404
+
405
+ private observe(observation: CommerceBrokerObservation): void {
406
+ try {
407
+ this.opts.observe?.(Object.freeze({
408
+ ...observation,
409
+ logicalId: safeManagedAnalyticsDiagnosticId(observation.logicalId),
410
+ subscriberId: safeManagedAnalyticsDiagnosticId(observation.subscriberId),
411
+ }));
412
+ } catch {
413
+ // Observability is fail-soft: a broken collaborator never changes delivery.
414
+ }
415
+ }
416
+ }
417
+
418
+ /**
419
+ * The iframe-consumer side of the channel: read a received postMessage payload
420
+ * FAIL-CLOSED into a validated {@link CommerceEvent}. A payload without the exact
421
+ * broker channel tag, or one whose event does not pass {@link validateCommerceEvent},
422
+ * yields `null` — a consumer never acts on a raw, forged, or off-channel message.
423
+ */
424
+ export function parseCommerceEventMessage(data: unknown): CommerceEvent | null {
425
+ if (typeof data !== "object" || data === null) return null;
426
+ const record = data as Record<string, unknown>;
427
+ if (record["channel"] !== COMMERCE_EVENT_MESSAGE_CHANNEL) return null;
428
+ const result = validateCommerceEvent(record["event"]);
429
+ return result.ok ? result.value : null;
430
+ }
431
+
432
+ /**
433
+ * The anonymous, minimized post-purchase summary a browser subscriber may receive
434
+ * (ADR 0014 M2 `integration-commerce-data-minimized`): currency, total value, item
435
+ * count, and anonymous line items. It carries NO order id, customer identity, payment
436
+ * data, or signed-cart material — the authoritative financial record lives only on the
437
+ * server verified-order path. The broker's closed field catalog re-enforces this on emit.
438
+ */
439
+ export interface PurchaseCompletedSummary {
440
+ currency: string;
441
+ /** A non-negative decimal money string, e.g. `"42.50"`. */
442
+ value: string;
443
+ itemCount: number;
444
+ items: readonly {
445
+ productId: string;
446
+ variantId?: string;
447
+ quantity: number;
448
+ unitPrice: string;
449
+ }[];
450
+ }
451
+
452
+ /**
453
+ * The ONE named seam a post-purchase third-party trigger flows through: emit the
454
+ * browser-deliverable `storefront.purchase.completed.v1` signal (design-partner finding
455
+ * F1/F2 wire). It rides the broker's existing guarantees verbatim — server-owned context
456
+ * stamped not caller-set, one-event-per-logical-action dedupe, per-subscriber consent
457
+ * filtering, and subscriber-fault isolation — so a scoped sandbox subscriber (via the
458
+ * lifecycle context's receive-only subscribe handle) reacts to a completed purchase
459
+ * exactly once. The caller supplies only the anonymous summary and the logical action
460
+ * identity; it can never set a server-owned field or author a verified-provider order.
461
+ *
462
+ * NOTE ON THE LIVE TRIGGER (honest gap): the storefront's checkout AND receipt are
463
+ * provider-hosted and cross-domain (Foxy), so there is no storefront-rendered
464
+ * confirmation page in THIS origin to call this seam from today. The subscribe/validate
465
+ * path is fully wired and this emit seam is real and tested; the remaining work is the
466
+ * cross-domain bridge that carries the provider receipt signal back to a storefront-origin
467
+ * document which then calls this — tracked as an explicit follow-up, not faked here.
468
+ */
469
+ export function emitPurchaseCompleted(
470
+ broker: CommerceEventBroker,
471
+ input: {
472
+ /** The opaque logical event id — the dedupe key. The same completed purchase reuses it. */
473
+ logicalId: string;
474
+ /** Opaque in-event subject (e.g. an anonymous purchase/session handle) — never an order id. */
475
+ subject: string;
476
+ /** The bounded-lifetime attribution thread this purchase belongs to. */
477
+ correlation: EventCorrelation;
478
+ /** The anonymous, PII-free purchase summary. */
479
+ summary: PurchaseCompletedSummary;
480
+ /** Optional per-emitter monotonic counter for out-of-order detection. */
481
+ sequence?: number;
482
+ },
483
+ ): CommerceEmitResult {
484
+ return broker.emit({
485
+ type: PURCHASE_COMPLETED_EVENT_TYPE,
486
+ logicalId: input.logicalId,
487
+ subject: input.subject,
488
+ correlation: input.correlation,
489
+ data: {
490
+ currency: input.summary.currency,
491
+ value: input.summary.value,
492
+ itemCount: input.summary.itemCount,
493
+ items: input.summary.items.map((item) => ({ ...item })),
494
+ },
495
+ ...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
496
+ });
497
+ }