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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/apps/storefront/astro.config.mjs +3 -3
  2. package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +8 -3
  3. package/apps/storefront/env.d.ts +54 -5
  4. package/apps/storefront/integrations/materialize-guard.mjs +12 -14
  5. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  6. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  7. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  8. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  9. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  10. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  11. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  12. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  13. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  14. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  15. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  16. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  17. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  18. package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
  19. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  20. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  21. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  22. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  23. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  24. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  25. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  26. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  27. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  28. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  29. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  30. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  31. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  32. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  33. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  34. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  35. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  36. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  37. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  38. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  39. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  40. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  41. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  42. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  43. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  44. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  45. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  46. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  47. package/apps/storefront/src/components/Img.astro +1 -1
  48. package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
  49. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  50. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
  51. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  52. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  53. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  54. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  55. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  56. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  57. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  58. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  59. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  60. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  61. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  62. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  63. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  64. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  65. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  66. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  67. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  68. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  69. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  70. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  71. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  72. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  73. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  74. package/apps/storefront/src/config/resolver.ts +18 -10
  75. package/apps/storefront/src/layouts/Layout.astro +100 -20
  76. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  77. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
  78. package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
  79. package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
  80. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  81. package/apps/storefront/src/lib/apps/adminService.ts +10 -0
  82. package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
  83. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  84. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  85. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  86. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  87. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  88. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  89. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  90. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  91. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  92. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  93. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  94. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  95. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
  96. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
  97. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  98. package/apps/storefront/src/lib/apps/registryService.ts +264 -10
  99. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  100. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  101. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  102. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  103. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  104. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  105. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  106. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  107. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  108. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  109. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  110. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  111. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  112. package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
  113. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  114. package/apps/storefront/src/lib/blog/collection.ts +114 -17
  115. package/apps/storefront/src/lib/blog/markdown.ts +1 -1
  116. package/apps/storefront/src/lib/blog/provider.ts +76 -9
  117. package/apps/storefront/src/lib/blog/types.ts +9 -9
  118. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  119. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  120. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  121. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  122. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  123. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  124. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  125. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  126. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  127. package/apps/storefront/src/lib/content/index.ts +5 -5
  128. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  129. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  130. package/apps/storefront/src/lib/d1/catalog.ts +12 -0
  131. package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
  132. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  133. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  134. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  135. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  136. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  137. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  138. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  139. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  140. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  141. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  142. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  143. package/apps/storefront/src/lib/edgeCache.ts +18 -6
  144. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  145. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  146. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  147. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  148. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  149. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  150. package/apps/storefront/src/lib/membership/model.ts +1 -2
  151. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
  152. package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
  153. package/apps/storefront/src/lib/newsletter/send.ts +3 -3
  154. package/apps/storefront/src/lib/previewReload.ts +57 -1
  155. package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
  156. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  157. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  158. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  159. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  160. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  161. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  162. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  163. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  164. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  165. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  166. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  167. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  168. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  169. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  170. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  171. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  172. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  173. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  174. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  175. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  176. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  177. package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
  178. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  179. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  180. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  181. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  182. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  183. package/apps/storefront/src/lib/seo/alternates.ts +1 -1
  184. package/apps/storefront/src/lib/social/notify.ts +4 -4
  185. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  186. package/apps/storefront/src/lib/storyblok/provider.ts +203 -69
  187. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  188. package/apps/storefront/src/lib/the-build/provider.ts +9 -4
  189. package/apps/storefront/src/middleware/index.ts +273 -51
  190. package/apps/storefront/src/pages/[...slug].astro +9 -4
  191. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  192. package/apps/storefront/src/pages/admin/select.astro +96 -0
  193. package/apps/storefront/src/pages/admin.astro +16 -2
  194. package/apps/storefront/src/pages/api/activity.ts +8 -8
  195. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  196. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  197. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  198. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  199. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  200. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  201. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  202. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  203. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  204. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  205. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  206. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  207. package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
  208. package/apps/storefront/src/pages/blog/[slug].astro +5 -5
  209. package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
  210. package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
  211. package/apps/storefront/src/pages/blog/index.astro +2 -2
  212. package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
  213. package/apps/storefront/src/pages/cockpit.astro +61 -1362
  214. package/apps/storefront/src/pages/index.astro +10 -5
  215. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  216. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  217. package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
  218. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +42 -31
  219. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +8 -3
  220. package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +6 -2
  221. package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +6 -2
  222. package/apps/storefront/src/pages/style-guide/index.astro +19 -5
  223. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  224. package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
  225. package/apps/storefront/src/pages/the-build/index.astro +2 -2
  226. package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
  227. package/apps/storefront/src/styles/admin.css +27 -1
  228. package/apps/storefront/src/themes/tenants/index.ts +19 -7
  229. package/apps/storefront/tsconfig.json +1 -1
  230. package/package.json +1 -1
  231. package/packages/public-runtime/package.json +2 -1
  232. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  233. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  234. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  235. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  236. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  237. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  238. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  239. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  240. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  241. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  242. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  243. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  244. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  245. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  246. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  247. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  248. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  249. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  250. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  251. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  252. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  253. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  254. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  255. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  256. package/packages/public-runtime/src/activity/catalog.ts +41 -37
  257. package/packages/public-runtime/src/activity/event.ts +6 -6
  258. package/packages/public-runtime/src/activity/index.ts +2 -2
  259. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  260. package/packages/public-runtime/src/candidate-index.ts +42 -8
  261. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  262. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  263. package/packages/public-runtime/src/checkout-style.ts +7 -7
  264. package/packages/public-runtime/src/checkout.ts +3 -3
  265. package/packages/public-runtime/src/chrome.ts +777 -0
  266. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  267. package/packages/public-runtime/src/csp.ts +99 -0
  268. package/packages/public-runtime/src/customization-runtime.ts +32 -1
  269. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  270. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  271. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  272. package/packages/public-runtime/src/evidence.ts +288 -0
  273. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  274. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  275. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  276. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  277. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  278. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  279. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  280. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  281. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  282. package/packages/public-runtime/src/index.ts +16 -1
  283. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  284. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  285. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  286. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  287. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  288. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  289. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  290. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  291. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  292. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  293. package/packages/public-runtime/src/integration/csp.ts +105 -0
  294. package/packages/public-runtime/src/integration/digest.ts +43 -0
  295. package/packages/public-runtime/src/integration/environment.ts +228 -0
  296. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  297. package/packages/public-runtime/src/integration/events.ts +701 -0
  298. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  299. package/packages/public-runtime/src/integration/index.ts +90 -0
  300. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  301. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  302. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  303. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  304. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  305. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  306. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  307. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  308. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  309. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  310. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  311. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  312. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  313. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  314. package/packages/public-runtime/src/integration/observability.ts +451 -0
  315. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  316. package/packages/public-runtime/src/integration/phases.ts +182 -0
  317. package/packages/public-runtime/src/integration/plan.ts +112 -0
  318. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  319. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  320. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  321. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  322. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  323. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  324. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  325. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  326. package/packages/public-runtime/src/integration/result.ts +239 -0
  327. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  328. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  329. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  330. package/packages/public-runtime/src/integration/version.ts +92 -0
  331. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  332. package/packages/public-runtime/src/membership.ts +2 -2
  333. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  334. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  335. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  336. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  337. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  338. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  339. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  340. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  341. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  342. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  343. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  344. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  345. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  346. package/packages/public-runtime/src/static-bundle.ts +5 -5
  347. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  348. package/packages/public-runtime/src/tenant.ts +136 -9
  349. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  350. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  351. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  352. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  353. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  354. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  355. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  356. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  357. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  358. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  359. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  360. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  361. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  362. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  363. package/scripts/build/copy-tenant-assets.mjs +12 -5
  364. package/scripts/dev/ai-edit.mjs +2 -1
  365. package/scripts/dev/file-browser.mjs +2 -2
  366. package/scripts/dev/file-writer.mjs +2 -2
  367. package/scripts/dev/integration-guardrails.mjs +498 -0
  368. package/scripts/dev/safe-path.mjs +2 -2
  369. package/scripts/dev/tenant-source-root.mjs +3 -2
  370. package/scripts/tot-dev.mjs +5 -4
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Closed policy for the evidence-rejected G2 account-interest gap.
3
+ *
4
+ * This is deliberately a first-party policy seam, not a G2 client: it has no
5
+ * endpoint, vendor identifier, browser loader, DOM input, mutable configuration,
6
+ * or side effect. A future owned destination may consume only an already-emitted,
7
+ * validated semantic action through this policy.
8
+ */
9
+ import type { CommerceEvent } from "./events.js";
10
+ import {
11
+ APPROVED_MARKETING_ACTION_NAMES,
12
+ type ApprovedMarketingActionName,
13
+ } from "./events.js";
14
+ import {
15
+ emitManagedActionActivated,
16
+ type ManagedActionActivationInput,
17
+ type ManagedAnalyticsEmitResult,
18
+ } from "./managed-analytics.js";
19
+ import type { CommerceEventBroker } from "./commerce-broker.js";
20
+
21
+ /** Only these two actions are distinct G2 conversion candidates; never aggregate them. */
22
+ export const G2_CONVERSION_ACTION_NAMES = [
23
+ "user_clicked_contact_sales_mktg",
24
+ "user_clicked_create_account_mktg",
25
+ ] as const satisfies readonly ApprovedMarketingActionName[];
26
+
27
+ /** These actions remain first-party product/service telemetry and are never G2 eligible. */
28
+ export const INTERNAL_ONLY_ACTION_NAMES = [
29
+ "user_clicked_login_mktg",
30
+ "user_clicked_contact_support_consumer_mktg",
31
+ "user_clicked_contact_support_partner_mktg",
32
+ ] as const satisfies readonly ApprovedMarketingActionName[];
33
+
34
+ export type G2ActionDisposition =
35
+ | "g2-conversion-eligible"
36
+ | "internal-only"
37
+ | "withheld-marketing-consent";
38
+
39
+ export interface G2ActionBoundaryDecision {
40
+ readonly actionName: ApprovedMarketingActionName;
41
+ readonly disposition: G2ActionDisposition;
42
+ }
43
+
44
+ const G2_CONVERSION_ACTION_SET: ReadonlySet<string> = new Set(G2_CONVERSION_ACTION_NAMES);
45
+ const INTERNAL_ONLY_ACTION_SET: ReadonlySet<string> = new Set(INTERNAL_ONLY_ACTION_NAMES);
46
+
47
+ function approvedActionName(value: unknown): ApprovedMarketingActionName | undefined {
48
+ return (APPROVED_MARKETING_ACTION_NAMES as readonly string[]).includes(value as string)
49
+ ? value as ApprovedMarketingActionName
50
+ : undefined;
51
+ }
52
+
53
+ /**
54
+ * Classify an already-validated first-party action event for any future G2
55
+ * destination. Invalid/unrelated events are deliberately not classifiable.
56
+ *
57
+ * Marketing consent is a separate destination-purpose gate. The underlying
58
+ * first-party action event is already analytics-consent and broker-dedupe gated.
59
+ */
60
+ export function decideG2ActionBoundary(event: CommerceEvent): G2ActionBoundaryDecision | undefined {
61
+ if (event.type !== "storefront.action.activated.v1") return undefined;
62
+ if (Object.keys(event.data).length !== 1) return undefined;
63
+ const actionName = approvedActionName(event.data["name"]);
64
+ if (!actionName) return undefined;
65
+
66
+ if (INTERNAL_ONLY_ACTION_SET.has(actionName)) {
67
+ return { actionName, disposition: "internal-only" };
68
+ }
69
+ if (!G2_CONVERSION_ACTION_SET.has(actionName)) return undefined;
70
+ if (!event.consent.eligiblePurposes.includes("marketing")) {
71
+ return { actionName, disposition: "withheld-marketing-consent" };
72
+ }
73
+ return { actionName, disposition: "g2-conversion-eligible" };
74
+ }
75
+
76
+ export interface ConfirmedManagedActionInput extends ManagedActionActivationInput {
77
+ /** The platform handler must establish success; clicks and attempted submits are rejected. */
78
+ confirmedSuccess: true;
79
+ }
80
+
81
+ export type ConfirmedManagedActionResult = ManagedAnalyticsEmitResult | {
82
+ ok: true;
83
+ emitted: false;
84
+ skipReason: "not-confirmed-success";
85
+ deliveries: [];
86
+ };
87
+
88
+ /**
89
+ * The only first-party collection entry point intended for the five G2-gap
90
+ * actions. It does not deliver to G2; it simply refuses non-success activity
91
+ * before passing the closed `{ name }` event to the existing consented,
92
+ * idempotent CommerceEventBroker.
93
+ */
94
+ export function emitConfirmedManagedActionActivated(
95
+ broker: CommerceEventBroker,
96
+ input: Omit<ManagedActionActivationInput, "name"> & {
97
+ name: ApprovedMarketingActionName;
98
+ confirmedSuccess: boolean;
99
+ },
100
+ ): ConfirmedManagedActionResult {
101
+ if (input.confirmedSuccess !== true) {
102
+ return { ok: true, emitted: false, skipReason: "not-confirmed-success", deliveries: [] };
103
+ }
104
+ const { confirmedSuccess: _confirmedSuccess, ...activation } = input;
105
+ return emitManagedActionActivated(broker, activation);
106
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @tot/public-runtime — the versioned PUBLIC browser-integration contracts.
3
+ *
4
+ * The renderer-safe, semver-versioned surface (ADR 0006) that 35+ downstream
5
+ * cards build on. It is the single shared contract for both tenant-local
6
+ * configuration and managed apps (ADR 0014 C1 `integration-schema-shared`):
7
+ * promotion changes ownership and packaging, never the browser contract.
8
+ *
9
+ * Every contract here is CLOSED and FAIL-CLOSED — unknown fields, unknown
10
+ * enum members, malformed shapes, duplicates, and incompatible versions are
11
+ * rejected, never ignored or assumed safe — and none is secret-bearing (charter
12
+ * invariant 3, ADR 0018 nonce non-serialization).
13
+ *
14
+ * Charter/ADR traceability for the six source-card concerns:
15
+ * - environment → ./environment.ts (E1 / ADR 0017)
16
+ * - global hook + slots → ./phases.ts + ./manifest.ts (S1/S2, ADR 0016)
17
+ * - CSP → ./csp.ts (E2 / ADR 0018, 0019)
18
+ * - lifecycle → ./lifecycle.ts + navigation event (invariant 9)
19
+ * - migration → ./manifest.ts configurationMigrations + ./version.ts
20
+ * - authority/assurance → ./phases.ts tiers + ./assurance.ts (I1, G1/G2, 0023)
21
+ *
22
+ * Contract families:
23
+ * - version.ts — semver contract version + per-family schemaVersion pins
24
+ * - result.ts — the closed/fail-closed validation core
25
+ * - secrets.ts — the no-secret-in-the-browser guard
26
+ * - environment.ts — the universal four-stage browser environment
27
+ * - phases.ts — reserved phases, authority tiers, the contribution DAG
28
+ * - csp.ts — exact-origin CSP source contract
29
+ * - csp-compiler.ts — version-scoped compiler: curated profiles + advanced origins
30
+ * - manifest.ts — the shared integration declaration (tenant-local + app)
31
+ * - plan.ts — the server-computed effective plan + source references
32
+ * - resolve.ts — the pure resolver that merges declarations into a plan
33
+ * - render-phases.ts — the one fixed-phase render engine both mounts consume
34
+ * - lifecycle.ts — the exactly-once MPA lifecycle contract
35
+ * - lifecycle-runtime.ts — the browser runtime: exactly-once init, update/dispose,
36
+ * error isolation, and the reserved completed-navigation event seam
37
+ * - lifecycle-boot.ts — the guarded document.deferred boot binding for `window`
38
+ * - events.ts — the canonical semantic commerce + reserved navigation events
39
+ * - events-catalog.ts — per-field owner/purpose/sensitivity + retention classes
40
+ * - commerce-broker.ts — the platform-owned client emitter/subscription API:
41
+ * server-owned context stamped not caller-set, one-event-per-action dedupe,
42
+ * consent-filtered per-subscriber delivery, subscriber-fault isolation,
43
+ * and typed iframe-consumer messages
44
+ * - assurance.ts — compliance assurance metadata (non-waivable floor)
45
+ * - assurance-decision.ts — the enforcement layer over an assurance record
46
+ */
47
+ export * from "./version.js";
48
+ export * from "./result.js";
49
+ export { stableStringify as stableIntegrationStringify } from "./digest.js";
50
+ export * from "./secrets.js";
51
+ export * from "./environment.js";
52
+ export * from "./phases.js";
53
+ export * from "./csp.js";
54
+ export * from "./csp-compiler.js";
55
+ export * from "./manifest.js";
56
+ export * from "./plan.js";
57
+ export * from "./resolve.js";
58
+ export * from "./platform-adapters.js";
59
+ export * from "./native-amplitude.js";
60
+ export * from "./native-ga4.js";
61
+ export * from "./platform-adapter-runtime.js";
62
+ export * from "./render-phases.js";
63
+ export * from "./interim-bridge.js";
64
+ export * from "./consent-broker.js";
65
+ export * from "./consent-cmp-demand.js";
66
+ export * from "./consent-config.js";
67
+ export * from "./consent-preview.js";
68
+ export * from "./candidate-evidence.js";
69
+ export * from "./lifecycle.js";
70
+ export * from "./lifecycle-runtime.js";
71
+ export * from "./lifecycle-boot.js";
72
+ export * from "./events.js";
73
+ export * from "./events-catalog.js";
74
+ export * from "./commerce-broker.js";
75
+ export * from "./managed-analytics.js";
76
+ export * from "./managed-analytics-bridge.js";
77
+ export * from "./g2-action-boundary.js";
78
+ export * from "./correlation-token.js";
79
+ export * from "./order-conversion.js";
80
+ export * from "./receipt-measurement.js";
81
+ export * from "./provider-checkout-extension.js";
82
+ export * from "./assurance.js";
83
+ export * from "./assurance-decision.js";
84
+ export * from "./tamper-evidence.js";
85
+ export * from "./tamper-recovery.js";
86
+ export * from "./observability.js";
87
+ export * from "./regulated-parent-cert.js";
88
+ export * from "./promotion-gate.js";
89
+ export * from "./managed-analytics-api-v1.js";
90
+ export * from "./managed-analytics-api-v1.schemas.js";
@@ -0,0 +1,138 @@
1
+ /**
2
+ * The supported interim MARKETING bridge (ext-kernel-07, WS1 roadmap "smallest
3
+ * supported interim bridge").
4
+ *
5
+ * This is the one seam that makes the whole browser-integration kernel observable
6
+ * end-to-end WITHOUT a page edit or a vendor branch: a tenant-local declaration's
7
+ * exact-version entry module flows through the resolver → the effective plan
8
+ * ({@link resolveEffectivePlan}) → the render-phase boot descriptor
9
+ * ({@link BootDescriptorIntegration}) → the lifecycle runtime, which dynamically
10
+ * imports and inits it — consent-gated, at a pinned version, in all four
11
+ * environments. Removing the declaration removes the code, the network, and the CSP
12
+ * with it (the plan carries no vendor runtime, and `planRevision` moves), so a
13
+ * rollback leaves no stale effect.
14
+ *
15
+ * It is deliberately narrow (ADR 0016/0023 Q6): the bridge is enabled ONLY on
16
+ * eligible unregulated-marketing tenants. A regulated tenant is REFUSED with an
17
+ * actionable explanation until a capability-specific certification exists — the
18
+ * regulated shared-runtime floor is non-waivable, and this bridge never opens an
19
+ * escape hatch around it. Promote-time enforcement still runs through the full
20
+ * fail-closed gate (`verifyIntegrationPromotable`); this module is the render-time
21
+ * tenant-eligibility decision that precedes it.
22
+ */
23
+ import type { BrowserEnvironment } from "./environment.js";
24
+ import type { EffectivePlan } from "./plan.js";
25
+ import type { ContractViolation } from "./result.js";
26
+ import type { ConsentSnapshot } from "./consent-broker.js";
27
+ import { isPurposeEligible } from "./consent-broker.js";
28
+ import type { BootDescriptorIntegration } from "./render-phases.js";
29
+ import type { AssuranceSurface } from "./assurance.js";
30
+ import { resolveEffectivePlan, type IntegrationDeclarationInput } from "./resolve.js";
31
+ import type { ObligationTenant } from "../compliance-obligations.js";
32
+ import { isRegulated, resolveObligationSurface } from "../compliance-obligations.js";
33
+ import {
34
+ isRegulatedParentCertified,
35
+ type RegulatedParentCertification,
36
+ } from "./regulated-parent-cert.js";
37
+
38
+ /**
39
+ * Whether an integration's declared consent dependency permits loading its entry
40
+ * under `snapshot`. An `essential-only` entry always loads; any other entry loads
41
+ * only once EVERY declared purpose is eligible — so under the default-deny snapshot
42
+ * (only `essential`) a non-essential loader is held back until consent is granted.
43
+ * This is the predicate the lifecycle runtime's consent gate uses.
44
+ */
45
+ export function integrationConsentEligible(
46
+ integration: Pick<BootDescriptorIntegration, "consent">,
47
+ snapshot: ConsentSnapshot,
48
+ ): boolean {
49
+ if (integration.consent.loadWhen === "essential-only") return true;
50
+ return integration.consent.purposes.every((purpose) => isPurposeEligible(snapshot, purpose));
51
+ }
52
+
53
+ /** Why the interim bridge refused to enable for a tenant — actionable, non-secret. */
54
+ export interface InterimBridgeRefusal {
55
+ code: "regulated-tenant-not-certified";
56
+ /** The tenant's resolved compliance-authority surface. */
57
+ surface: AssuranceSurface;
58
+ /** An operator-facing explanation of the refusal and what unblocks it. */
59
+ message: string;
60
+ }
61
+
62
+ /**
63
+ * The interim bridge result: a resolved plan for an eligible tenant, an actionable
64
+ * refusal for a regulated one, or the resolver's fail-closed violations for an
65
+ * invalid declaration set. `reason` discriminates the two failure shapes.
66
+ */
67
+ export type InterimBridgeResult =
68
+ | { ok: true; plan: EffectivePlan }
69
+ | { ok: false; reason: "regulated-refused"; refusal: InterimBridgeRefusal }
70
+ | { ok: false; reason: "invalid-plan"; violations: ContractViolation[] };
71
+
72
+ /** Everything the bridge needs to decide eligibility and resolve the plan. */
73
+ export interface InterimBridgeInput {
74
+ tenantPublicId: string;
75
+ /** The tenant's authoritative config — the ONLY source of its regulated classification. */
76
+ tenant: ObligationTenant;
77
+ environment: BrowserEnvironment;
78
+ declarations: readonly IntegrationDeclarationInput[];
79
+ /**
80
+ * G2: the regulated elevated-parent certification artifact for a regulated tenant.
81
+ * The bridge NEVER accepts a raw "certified" boolean — it runs
82
+ * {@link decideRegulatedParentCert} itself (via {@link isRegulatedParentCertified}),
83
+ * so `certifiedImmutable` has a SINGLE ORIGIN: a regulated tenant is enabled ONLY
84
+ * when the cert clears every clause (pinned, undrifted from `servedDigest`, every
85
+ * server-derived obligation proven). Absent/failed ⇒ the decision fails closed and
86
+ * the tenant stays refused. This is the WS2 forward seam, not a per-tenant bypass.
87
+ */
88
+ regulatedCertification?: RegulatedParentCertification | null;
89
+ /**
90
+ * The content digest of the artifact currently resolved to serve — the drift anchor
91
+ * the certification decision compares against its pinned digest. When it differs, the
92
+ * served code has drifted and the certification no longer holds (fail closed).
93
+ */
94
+ servedDigest?: string;
95
+ }
96
+
97
+ /**
98
+ * Resolve the interim bridge's effective plan for a tenant, refusing a regulated one
99
+ * until certified. On an eligible tenant it delegates to the pure, hot-path
100
+ * {@link resolveEffectivePlan} (which fail-closes on an invalid/partial declaration
101
+ * set), so an eligible tenant's plan is exactly the kernel plan every mount serves —
102
+ * the bridge adds only the tenant-eligibility gate, not a second plan shape.
103
+ */
104
+ export function resolveInterimBridgePlan(input: InterimBridgeInput): InterimBridgeResult {
105
+ const surface = resolveObligationSurface(input.tenant);
106
+
107
+ // The certification is the SINGLE ORIGIN of `certifiedImmutable`: derive it from the
108
+ // decision (never a raw external flag), so a regulated tenant is enabled only when the
109
+ // cert actually clears every clause. Absent/failed ⇒ false ⇒ refused.
110
+ const regulatedCertified = isRegulatedParentCertified(input.regulatedCertification, {
111
+ tenant: input.tenant,
112
+ servedDigest: input.servedDigest,
113
+ });
114
+
115
+ if (isRegulated(input.tenant) && !regulatedCertified) {
116
+ return {
117
+ ok: false,
118
+ reason: "regulated-refused",
119
+ refusal: {
120
+ code: "regulated-tenant-not-certified",
121
+ surface,
122
+ message:
123
+ `The interim marketing bridge is not enabled for regulated tenants (resolved surface ` +
124
+ `"${surface}"). A regulated parent tier requires an immutable, version-pinned, ` +
125
+ `capability-specific certification — a mere acknowledgement cannot waive the ` +
126
+ `non-waivable regulated shared-runtime floor. Complete WS2 certification to enable it.`,
127
+ },
128
+ };
129
+ }
130
+
131
+ const result = resolveEffectivePlan({
132
+ tenantPublicId: input.tenantPublicId,
133
+ environment: input.environment,
134
+ declarations: input.declarations,
135
+ });
136
+ if (!result.ok) return { ok: false, reason: "invalid-plan", violations: result.violations };
137
+ return { ok: true, plan: result.value };
138
+ }
@@ -0,0 +1,317 @@
1
+ /**
2
+ * The thin browser binding that boots the {@link IntegrationLifecycle} from the
3
+ * document.deferred phase (ext-kernel-04 emits the `document.deferred` boundary; this is
4
+ * what its deferred script invokes). It is deliberately small and structural: it reads
5
+ * the renderer-published boot descriptor global, guarantees ONE lifecycle per document
6
+ * with a window-level singleton (so a nested layout that re-runs the boot island cannot
7
+ * double-init), wires same-document navigations to the reserved completed-navigation
8
+ * event, disposes on document teardown, and is a guarded no-op off the browser.
9
+ *
10
+ * The entry module for each integration is the same-origin, exact-version asset the
11
+ * boot descriptor carries (`integration.entry.url`): the default loader dynamically
12
+ * imports exactly that URL into the PARENT document and checks it exports the lifecycle
13
+ * contract, so no page edit or vendor branch is needed — the URL flows through the plan.
14
+ * Reviewed `platform-adapter` entries are admitted first through the closed runtime
15
+ * registry, which verifies their exact origin/tier/app/version/URL/format marker and
16
+ * constructs bundled platform code with host-owned capabilities. Every other entry goes
17
+ * through the authority-tier loader: it loads ONLY an `elevated-parent` entry (whose
18
+ * honest, review-only contract IS arbitrary parent-document JavaScript) and REFUSES every
19
+ * other tier. A `sandbox`-tier entry —
20
+ * whose contract is a null-origin iframe reachable only through the typed postMessage
21
+ * broker — is refused here and held back until the iframe host that actually isolates
22
+ * it ships, so third-party code labeled "sandboxed" is never executed with the parent
23
+ * authority its label does not grant. Loading is also consent-gated: the binding reads
24
+ * the renderer-inlined default-deny consent snapshot and holds a non-essential entry
25
+ * back until its declared purposes are eligible. With an environment-only plan there
26
+ * are no integrations, so the runtime boots inert and ready.
27
+ */
28
+ import { IntegrationLifecycle, type IntegrationModuleLoader } from "./lifecycle-runtime.js";
29
+ import type { LifecycleObserver } from "./lifecycle-runtime.js";
30
+ import type { IntegrationLifecycleModule } from "./lifecycle.js";
31
+ import type { BootDescriptorIntegration, IntegrationBootDescriptor } from "./render-phases.js";
32
+ import { INTEGRATION_PLAN_GLOBAL, INTEGRATION_CONSENT_GLOBAL } from "./render-phases.js";
33
+ import { parseConsentSnapshot, defaultConsentSnapshot, type ConsentSnapshot } from "./consent-broker.js";
34
+ import { integrationConsentEligible } from "./interim-bridge.js";
35
+ import type { NavigationCompletedEvent, NavigationKind } from "./events.js";
36
+ import { CommerceEventBroker } from "./commerce-broker.js";
37
+ import { validateBrowserEnvironment, BROWSER_ENVIRONMENT_GLOBAL, type BrowserEnvironment } from "./environment.js";
38
+ import { makePlatformAdapterRuntimeLoader } from "./platform-adapter-runtime.js";
39
+
40
+ /** The window property holding the single per-document lifecycle (idempotent boot guard). */
41
+ export const LIFECYCLE_SINGLETON_GLOBAL = "__TOT_INTEGRATION_LIFECYCLE__";
42
+ /**
43
+ * An optional window-published loader the entry-asset layer may install to OVERRIDE the
44
+ * fail-closed same-origin default. It is the seam the null-origin sandboxed-iframe host
45
+ * (the tracked F3 follow-up) installs to actually isolate a `sandbox`-tier entry; until
46
+ * that host ships the seam is unused and the default loader refuses the sandbox tier.
47
+ */
48
+ export const LIFECYCLE_MODULE_LOADER_GLOBAL = "__TOT_INTEGRATION_MODULE_LOADER__";
49
+ /** An optional window-published sink for the completed-navigation event. */
50
+ export const LIFECYCLE_NAVIGATION_SINK_GLOBAL = "__TOT_INTEGRATION_NAVIGATION__";
51
+
52
+ /** The narrow slice of `window` the binding depends on — structural, for edge-safe typing. */
53
+ export interface LifecycleWindow {
54
+ location: { pathname: string; search: string; hash: string; href: string };
55
+ addEventListener(type: string, listener: (event: unknown) => void): void;
56
+ fetch?: typeof fetch;
57
+ [key: string]: unknown;
58
+ }
59
+
60
+ export interface BootLifecycleOptions {
61
+ loadModule?: IntegrationModuleLoader;
62
+ /** Consent gate override; defaults to the renderer-inlined default-deny snapshot. */
63
+ isEligible?: (integration: BootDescriptorIntegration) => boolean;
64
+ emitNavigation?: (event: NavigationCompletedEvent) => void;
65
+ observe?: LifecycleObserver;
66
+ /**
67
+ * The four-stage browser environment handed to every integration's `init`/`update`
68
+ * context; defaults to the renderer-published `__TOT_INTEGRATION_ENV__` global
69
+ * (fail closed to the descriptor-derived fallback if absent/tampered).
70
+ */
71
+ environment?: BrowserEnvironment;
72
+ /**
73
+ * Reads the current consent snapshot handed to every integration's `init`/`update`
74
+ * context; defaults to the renderer-published `__TOT_INTEGRATION_CONSENT__` global
75
+ * (fail closed to the default-deny snapshot).
76
+ */
77
+ getConsentSnapshot?: () => ConsentSnapshot;
78
+ /**
79
+ * The commerce-event broker whose scoped, receive-only subscribe handle is placed on
80
+ * each integration's lifecycle context; defaults to one constructed from the boot
81
+ * descriptor's server-derived trusted context (tenant/environment/surface) and the
82
+ * resolved consent snapshot source, so every envelope is server-stamped and
83
+ * consent-filtered exactly like the platform path. Injectable for tests.
84
+ */
85
+ commerceBroker?: CommerceEventBroker;
86
+ /** Host-owned fetch capability for reviewed first-party platform adapters. */
87
+ fetchCapability?: typeof fetch;
88
+ /** Entry import capability; injectable for tests and non-browser hosts. */
89
+ importModule?: (url: string) => Promise<unknown>;
90
+ }
91
+
92
+ /**
93
+ * Narrow the namespace of a dynamically-imported entry module to the lifecycle
94
+ * contract: an `init()` on the module namespace, or on its default export. Anything
95
+ * else is not a valid integration entry (fail closed — the caller rejects the load).
96
+ */
97
+ function asLifecycleModule(ns: unknown): IntegrationLifecycleModule | null {
98
+ const candidate = (ns as { default?: unknown })?.default ?? ns;
99
+ if (candidate && typeof (candidate as { init?: unknown }).init === "function") {
100
+ return candidate as IntegrationLifecycleModule;
101
+ }
102
+ return null;
103
+ }
104
+
105
+ /**
106
+ * The one authority tier the same-origin (parent-realm) loader may run: an
107
+ * `elevated-parent` entry's honest contract IS arbitrary parent-document JavaScript, so
108
+ * importing it into the parent grants exactly what its (review-only) label already
109
+ * declares. Every other tier — above all `sandbox`, whose contract is a null-origin
110
+ * iframe reachable only through the typed postMessage broker — is refused here, so its
111
+ * isolation is never silently downgraded to full parent access.
112
+ */
113
+ const PARENT_REALM_TIER = "elevated-parent" as const;
114
+
115
+ /**
116
+ * Build the same-origin entry-module loader: dynamically import an integration's
117
+ * declared, exact-version `entry.url` into the PARENT document and return its lifecycle
118
+ * module. Vendor-neutral — it imports whatever same-origin URL the plan resolved, never
119
+ * a hardcoded vendor path. `importModule` is injected so the loader is testable without
120
+ * a live network; it defaults to a native dynamic `import()`.
121
+ *
122
+ * FAIL-CLOSED BY TIER (design-partner finding F3): a parent-realm import gives the entry
123
+ * full parent-document authority, so the loader refuses any tier but `elevated-parent`
124
+ * BEFORE importing — a `sandbox`-tier entry throws without ever being fetched or
125
+ * evaluated, so code labeled "sandboxed" cannot run in the parent while its isolating
126
+ * iframe host is still the tracked follow-up. The throw is caught by the runtime's
127
+ * per-integration load isolation: the entry never becomes active, the page is unaffected,
128
+ * and every other integration still boots.
129
+ */
130
+ export function makeSameOriginEntryLoader(
131
+ importModule: (url: string) => Promise<unknown> = (url) => import(/* @vite-ignore */ url),
132
+ ): IntegrationModuleLoader {
133
+ return async (integration) => {
134
+ if (integration.executionTier !== PARENT_REALM_TIER) {
135
+ throw new Error(
136
+ `Refusing to load "${integration.appId}@${integration.version}" (tier ` +
137
+ `"${integration.executionTier}") into the parent document: the same-origin loader ` +
138
+ `only runs the "${PARENT_REALM_TIER}" tier. A "sandbox"-tier entry must run inside a ` +
139
+ `null-origin iframe reachable only through the typed postMessage broker; that ` +
140
+ `isolating host is not yet wired, so the entry is held fail-closed rather than run ` +
141
+ `with parent-document authority its tier does not grant.`,
142
+ );
143
+ }
144
+ const ns = await importModule(integration.entry.url);
145
+ const mod = asLifecycleModule(ns);
146
+ if (!mod) {
147
+ throw new Error(
148
+ `Entry module "${integration.entry.url}" for "${integration.appId}@${integration.version}" ` +
149
+ `does not export the integration lifecycle contract (init()).`,
150
+ );
151
+ }
152
+ return mod;
153
+ };
154
+ }
155
+
156
+ /** The default same-origin entry loader, over a native dynamic import. */
157
+ export const sameOriginEntryLoader: IntegrationModuleLoader = makeSameOriginEntryLoader();
158
+
159
+ /**
160
+ * Construct the per-document commerce-event broker from the boot descriptor's
161
+ * server-derived trusted context (tenant/environment/surface) and the resolved consent
162
+ * snapshot source — so every envelope the broker stamps and every per-subscriber
163
+ * consent filter matches the platform path exactly. The broker backs the receive-only
164
+ * subscribe handle each sandbox entry gets; it never hands emit power to a vendor.
165
+ */
166
+ function makeDocumentCommerceBroker(
167
+ descriptor: IntegrationBootDescriptor,
168
+ getConsentSnapshot: () => ConsentSnapshot,
169
+ ): CommerceEventBroker {
170
+ return new CommerceEventBroker({
171
+ trusted: {
172
+ tenant: descriptor.tenantPublicId,
173
+ environment: descriptor.environment,
174
+ siteSurface: descriptor.siteSurface,
175
+ },
176
+ source: `//${descriptor.tenantPublicId}`,
177
+ consentSnapshot: getConsentSnapshot,
178
+ });
179
+ }
180
+
181
+ function readDescriptor(win: LifecycleWindow): IntegrationBootDescriptor | null {
182
+ const descriptor = win[INTEGRATION_PLAN_GLOBAL];
183
+ if (!descriptor || typeof descriptor !== "object") return null;
184
+ return descriptor as IntegrationBootDescriptor;
185
+ }
186
+
187
+ /**
188
+ * Read the current consent snapshot off the renderer-inlined global. A missing or
189
+ * tampered snapshot falls back to the default-deny snapshot (fail closed). This is
190
+ * the ONE read both the consent gate and the `init`/`update` context use, so they
191
+ * can never disagree about the current snapshot.
192
+ */
193
+ function readConsentSnapshot(win: LifecycleWindow): ConsentSnapshot {
194
+ return parseConsentSnapshot(win[INTEGRATION_CONSENT_GLOBAL]) ?? defaultConsentSnapshot();
195
+ }
196
+
197
+ /**
198
+ * Read the renderer-published four-stage browser environment off `__TOT_INTEGRATION_ENV__`.
199
+ * `undefined` when the global is absent or fails {@link validateBrowserEnvironment} (fail
200
+ * closed) — the caller falls back to the descriptor-derived default.
201
+ */
202
+ function readBrowserEnvironment(win: LifecycleWindow): BrowserEnvironment | undefined {
203
+ const result = validateBrowserEnvironment(win[BROWSER_ENVIRONMENT_GLOBAL]);
204
+ return result.ok ? result.value : undefined;
205
+ }
206
+
207
+ /**
208
+ * The default consent gate: eligible iff the integration's declared purposes are all
209
+ * eligible under the renderer-inlined consent snapshot. A missing or tampered snapshot
210
+ * global falls back to the default-deny snapshot, so a non-essential entry stays gated
211
+ * (fail closed). A CMP/native banner that later grants a purpose updates this global and
212
+ * calls `lifecycle.update("consent-changed")`, which loads the newly-eligible entry.
213
+ */
214
+ function defaultConsentGate(
215
+ win: LifecycleWindow,
216
+ ): (integration: BootDescriptorIntegration) => boolean {
217
+ return (integration) => integrationConsentEligible(integration, readConsentSnapshot(win));
218
+ }
219
+
220
+ /**
221
+ * Boot the lifecycle for `win` exactly once. A second call returns the same instance
222
+ * (nested-layout safety). Returns `null` when there is no boot descriptor to act on.
223
+ */
224
+ export function bootIntegrationLifecycle(
225
+ win: LifecycleWindow,
226
+ options: BootLifecycleOptions = {},
227
+ ): IntegrationLifecycle | null {
228
+ const existing = win[LIFECYCLE_SINGLETON_GLOBAL];
229
+ if (existing instanceof IntegrationLifecycle) return existing;
230
+
231
+ const descriptor = readDescriptor(win);
232
+ if (!descriptor) return null;
233
+
234
+ const hostFetch = options.fetchCapability ??
235
+ (typeof win.fetch === "function" ? win.fetch.bind(win) : undefined);
236
+ const fallbackLoader = options.importModule
237
+ ? makeSameOriginEntryLoader(options.importModule)
238
+ : sameOriginEntryLoader;
239
+ const ordinaryLoader =
240
+ options.loadModule ??
241
+ (win[LIFECYCLE_MODULE_LOADER_GLOBAL] as IntegrationModuleLoader | undefined) ??
242
+ fallbackLoader;
243
+ // Platform authority is resolved before any caller/window override: exact registry
244
+ // matches run; unknown/mismatched platform claims fail closed without delegation.
245
+ const loadModule = makePlatformAdapterRuntimeLoader({
246
+ fetchCapability: hostFetch,
247
+ importModule: options.importModule,
248
+ fallback: ordinaryLoader,
249
+ });
250
+
251
+ const isEligible = options.isEligible ?? defaultConsentGate(win);
252
+
253
+ const emitNavigation =
254
+ options.emitNavigation ??
255
+ (win[LIFECYCLE_NAVIGATION_SINK_GLOBAL] as ((e: NavigationCompletedEvent) => void) | undefined);
256
+
257
+ const environment = options.environment ?? readBrowserEnvironment(win);
258
+ const getConsentSnapshot = options.getConsentSnapshot ?? (() => readConsentSnapshot(win));
259
+ const commerceBroker =
260
+ options.commerceBroker ?? makeDocumentCommerceBroker(descriptor, getConsentSnapshot);
261
+
262
+ const lifecycle = new IntegrationLifecycle({
263
+ descriptor,
264
+ loadModule,
265
+ isEligible,
266
+ emitNavigation,
267
+ observe: options.observe,
268
+ environment,
269
+ getConsentSnapshot,
270
+ commerceBroker,
271
+ });
272
+ win[LIFECYCLE_SINGLETON_GLOBAL] = lifecycle;
273
+
274
+ wireNavigation(win, lifecycle);
275
+ wireTeardown(win, lifecycle);
276
+ void lifecycle.init();
277
+ return lifecycle;
278
+ }
279
+
280
+ /**
281
+ * Wire same-document navigations to the reserved completed-navigation event. In a
282
+ * traditional MPA a real document load re-inits by construction, so only the two
283
+ * navigations that DON'T reload the document — hash changes and history back/forward —
284
+ * need a completed-navigation signal, and neither re-initializes integrations.
285
+ */
286
+ function wireNavigation(win: LifecycleWindow, lifecycle: IntegrationLifecycle): void {
287
+ let lastPath = win.location.pathname + win.location.hash;
288
+ win.addEventListener("hashchange", () => {
289
+ const to = win.location.pathname + win.location.hash;
290
+ lifecycle.notifyNavigationCompleted({ from: lastPath, to, kind: "hash" });
291
+ lastPath = to;
292
+ });
293
+ win.addEventListener("popstate", () => {
294
+ const to = win.location.pathname + win.location.hash;
295
+ const kind: NavigationKind = "back-forward";
296
+ lifecycle.notifyNavigationCompleted({ from: lastPath, to, kind });
297
+ lastPath = to;
298
+ });
299
+ }
300
+
301
+ /** Dispose the lifecycle when the document is torn down, so integrations clean up once. */
302
+ function wireTeardown(win: LifecycleWindow, lifecycle: IntegrationLifecycle): void {
303
+ win.addEventListener("pagehide", () => {
304
+ void lifecycle.dispose();
305
+ });
306
+ }
307
+
308
+ /**
309
+ * The document.deferred entrypoint: boot from the ambient `window`. A guarded no-op when
310
+ * there is no browser (SSR, the edge Worker bundle, tests that import the module).
311
+ */
312
+ export function bootIntegrationLifecycleFromDocument(
313
+ options: BootLifecycleOptions = {},
314
+ ): IntegrationLifecycle | null {
315
+ if (typeof window === "undefined") return null;
316
+ return bootIntegrationLifecycle(window as unknown as LifecycleWindow, options);
317
+ }