@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,81 @@
1
+ /**
2
+ * WS4-only deterministic verified-order ledger core. This is an in-memory
3
+ * normalization seam, not a webhook receiver, durable store, or runtime adapter.
4
+ */
5
+ import {
6
+ resolveVerifiedOrderTruth,
7
+ type AcceptedProviderEvent,
8
+ type VerifiedOrderSnapshot,
9
+ type VerifiedOrderTruthResolution,
10
+ } from "../purchase-truth/contract.js";
11
+
12
+ export const LOCAL_VERIFIED_ORDER_LEDGER_SCHEMA_VERSION = "1" as const;
13
+
14
+ function eventKey(provider: string, providerEventId: string): string {
15
+ return `${provider}\u0000${providerEventId}`;
16
+ }
17
+
18
+ function orderKey(provider: string, providerOrderId: string, tenant: string): string {
19
+ return `${provider}\u0000${providerOrderId}\u0000${tenant}`;
20
+ }
21
+
22
+ function copyEvent(value: AcceptedProviderEvent): AcceptedProviderEvent {
23
+ return { ...value };
24
+ }
25
+
26
+ function copyOrder(value: VerifiedOrderSnapshot): VerifiedOrderSnapshot {
27
+ return { ...value };
28
+ }
29
+
30
+ /**
31
+ * Deterministic, process-local ledger for contract and fixture verification. A future
32
+ * approved server owner must replace its maps with atomic durable compare-and-set;
33
+ * this class never opens a connection, reads environment state, or accepts browser data.
34
+ */
35
+ export class LocalVerifiedOrderLedger {
36
+ readonly schemaVersion = LOCAL_VERIFIED_ORDER_LEDGER_SCHEMA_VERSION;
37
+ private readonly events = new Map<string, AcceptedProviderEvent>();
38
+ private readonly orders = new Map<string, VerifiedOrderSnapshot>();
39
+
40
+ apply(record: unknown): VerifiedOrderTruthResolution {
41
+ const parsed = resolveVerifiedOrderTruth({
42
+ record,
43
+ existingEvent: this.findExistingEvent(record),
44
+ existingOrder: this.findExistingOrder(record),
45
+ });
46
+ if (parsed.outcome !== "accepted") return parsed;
47
+ this.events.set(eventKey(parsed.acceptedEvent.provider, parsed.acceptedEvent.providerEventId), copyEvent(parsed.acceptedEvent));
48
+ this.orders.set(orderKey(parsed.nextOrder.provider, parsed.nextOrder.providerOrderId, parsed.nextOrder.tenant), copyOrder(parsed.nextOrder));
49
+ return parsed;
50
+ }
51
+
52
+ /** Defensive state snapshot intended for local assertions only. */
53
+ order(provider: string, providerOrderId: string, tenant: string): VerifiedOrderSnapshot | null {
54
+ const value = this.orders.get(orderKey(provider, providerOrderId, tenant));
55
+ return value ? copyOrder(value) : null;
56
+ }
57
+
58
+ eventCount(): number {
59
+ return this.events.size;
60
+ }
61
+
62
+ orderCount(): number {
63
+ return this.orders.size;
64
+ }
65
+
66
+ private findExistingEvent(record: unknown): AcceptedProviderEvent | null {
67
+ if (!isRecord(record) || typeof record.provider !== "string" || typeof record.providerEventId !== "string") return null;
68
+ const value = this.events.get(eventKey(record.provider, record.providerEventId));
69
+ return value ? copyEvent(value) : null;
70
+ }
71
+
72
+ private findExistingOrder(record: unknown): VerifiedOrderSnapshot | null {
73
+ if (!isRecord(record) || typeof record.provider !== "string" || typeof record.providerOrderId !== "string" || typeof record.tenant !== "string") return null;
74
+ const value = this.orders.get(orderKey(record.provider, record.providerOrderId, record.tenant));
75
+ return value ? copyOrder(value) : null;
76
+ }
77
+ }
78
+
79
+ function isRecord(value: unknown): value is Record<string, unknown> {
80
+ return typeof value === "object" && value !== null && !Array.isArray(value);
81
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * WS4-only, local mapping plans for regulated commerce measurement.
3
+ *
4
+ * This module maps the closed semantic contract into inert vendor-shaped records.
5
+ * It has no endpoint, SDK, browser global, persistence, credential, or dispatch
6
+ * capability. A separately approved owner must decide whether any plan is ever
7
+ * delivered; this module is deliberately unable to do so.
8
+ */
9
+ import {
10
+ validateRegulatedCommerceIntent,
11
+ type RegulatedCommerceEventKind,
12
+ } from "../events/contract.js";
13
+
14
+ export const LOCAL_MAPPING_DELIVERY_STATES = ["delivered", "duplicate", "outage", "suppressed"] as const;
15
+ export type LocalMappingDeliveryState = (typeof LOCAL_MAPPING_DELIVERY_STATES)[number];
16
+
17
+ export type LocalMappingPlan =
18
+ | {
19
+ readonly outcome: "mapped";
20
+ readonly ga4: LocalGa4Event;
21
+ readonly googleAds?: LocalGoogleAdsConversion;
22
+ /** Explicitly records why a local conversion field is absent without suppressing GA4. */
23
+ readonly omittedFields?: readonly "google_ads_conversion"[];
24
+ }
25
+ | { readonly outcome: "omitted"; readonly reason: LocalMappingOmissionReason };
26
+
27
+ export type LocalMappingOmissionReason =
28
+ | "invalid-intent"
29
+ | "consent-not-granted"
30
+ | "duplicate"
31
+ | "outage"
32
+ | "suppressed"
33
+ | "unattributed-purchase";
34
+
35
+ /** Vendor-shaped labels only; these records are not calls to a vendor API. */
36
+ export interface LocalGa4Event {
37
+ readonly eventName: "page_view" | "view_item" | "view_cart" | "begin_checkout" | "purchase" | "refund" | "cancel";
38
+ readonly eventId: string;
39
+ readonly currency?: string;
40
+ readonly valueMinor?: string;
41
+ }
42
+
43
+ /** Eligible server purchase conversion only; no conversion identifier or secret is accepted. */
44
+ export interface LocalGoogleAdsConversion {
45
+ readonly conversionName: "purchase";
46
+ readonly eventId: string;
47
+ readonly currency: string;
48
+ readonly valueMinor: string;
49
+ }
50
+
51
+ const GA4_NAMES: Readonly<Record<RegulatedCommerceEventKind, LocalGa4Event["eventName"]>> = {
52
+ page_viewed: "page_view",
53
+ product_viewed: "view_item",
54
+ cart_changed: "view_cart",
55
+ checkout_handoff: "begin_checkout",
56
+ purchase_recorded: "purchase",
57
+ refund_recorded: "refund",
58
+ cancel_recorded: "cancel",
59
+ };
60
+
61
+ /**
62
+ * Create one inert mapping plan from one logical event after a local delivery
63
+ * decision. Browser/server/dual transport is intentionally absent: the logical
64
+ * event id is mapped once, and duplicate/outage paths never create a second plan.
65
+ */
66
+ export function mapLocalGa4AndAds(input: unknown, delivery: LocalMappingDeliveryState): LocalMappingPlan {
67
+ const parsed = validateRegulatedCommerceIntent(input);
68
+ if (!parsed.ok) return { outcome: "omitted", reason: "invalid-intent" };
69
+ if (parsed.value.consent.state !== "granted") return { outcome: "omitted", reason: "consent-not-granted" };
70
+ if (delivery !== "delivered") return { outcome: "omitted", reason: delivery };
71
+
72
+ const { kind, id, money, attribution } = parsed.value;
73
+ const ga4: LocalGa4Event = {
74
+ eventName: GA4_NAMES[kind],
75
+ eventId: id,
76
+ ...(money ? { currency: money.currency, valueMinor: money.minor } : {}),
77
+ };
78
+ if (kind !== "purchase_recorded") return { outcome: "mapped", ga4 };
79
+ if (attribution.state !== "attributed" || !money) {
80
+ return { outcome: "mapped", ga4, omittedFields: ["google_ads_conversion"] };
81
+ }
82
+ return {
83
+ outcome: "mapped",
84
+ ga4,
85
+ googleAds: { conversionName: "purchase", eventId: id, currency: money.currency, valueMinor: money.minor },
86
+ };
87
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Pure bridge from an accepted verified-provider ledger transition to the
3
+ * WS4 semantic intent. It deliberately has no transport, persistence, browser,
4
+ * or destination capability: the Storefront adapter owns its durable local outbox.
5
+ */
6
+ import {
7
+ validateRegulatedCommerceIntent,
8
+ type RegulatedCommerceIntent,
9
+ } from "../events/contract.js";
10
+ import type {
11
+ VerifiedOrderTruth,
12
+ VerifiedOrderTruthResolution,
13
+ } from "../purchase-truth/contract.js";
14
+
15
+ export interface VerifiedProviderIntentResult {
16
+ readonly intent: RegulatedCommerceIntent | null;
17
+ readonly reason: "not-accepted" | "reaffirmed" | "invalid-intent" | null;
18
+ }
19
+
20
+ /**
21
+ * Produce one server-authoritative measurement intent only for a ledger
22
+ * transition that was accepted from verified provider truth. Fulfillment is a
23
+ * reaffirmation of the original purchase and therefore never mints a duplicate.
24
+ */
25
+ export function intentFromVerifiedProviderTruth(
26
+ record: VerifiedOrderTruth,
27
+ resolution: VerifiedOrderTruthResolution,
28
+ ): VerifiedProviderIntentResult {
29
+ if (resolution.outcome !== "accepted") return { intent: null, reason: "not-accepted" };
30
+ if (resolution.effect === "reaffirmed") return { intent: null, reason: "reaffirmed" };
31
+
32
+ const kind =
33
+ resolution.effect === "purchase"
34
+ ? "purchase_recorded"
35
+ : resolution.effect === "refund"
36
+ ? "refund_recorded"
37
+ : "cancel_recorded";
38
+ const parsed = validateRegulatedCommerceIntent({
39
+ schemaVersion: "1",
40
+ id: `verified-provider:${record.provider}:${record.providerEventId}:${kind}`,
41
+ kind,
42
+ occurredAt: record.occurredAt,
43
+ source: "verified_server_order",
44
+ provenanceId: `provider-event:${record.provider}:${record.providerEventId}`,
45
+ money: { minor: String(record.amountMinor), currency: record.currency },
46
+ // Correlation/consent joining is a later broker task. Until then this is
47
+ // explicitly server-unattributed and never claims browser consent.
48
+ attribution: { state: "unattributed_missing" },
49
+ consent: { state: "not_collected" },
50
+ });
51
+ return parsed.ok ? { intent: parsed.value, reason: null } : { intent: null, reason: "invalid-intent" };
52
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * WS4's isolated verified-order truth and dedupe contract.
3
+ *
4
+ * It deliberately defines no webhook endpoint, persistence implementation, event
5
+ * emission, or destination delivery. A future approved server owner supplies trusted
6
+ * provider verification and atomic storage; browser input never enters this contract.
7
+ */
8
+
9
+ export const PURCHASE_TRUTH_SCHEMA_VERSION = "1" as const;
10
+
11
+ export const VERIFIED_ORDER_STATUSES = ["created", "fulfilled", "refunded", "cancelled"] as const;
12
+ export type VerifiedOrderStatus = (typeof VERIFIED_ORDER_STATUSES)[number];
13
+
14
+ export const ORDER_TRUTH_STATES = ["none", "purchased", "refunded", "cancelled"] as const;
15
+ export type OrderTruthState = (typeof ORDER_TRUTH_STATES)[number];
16
+
17
+ export interface VerifiedOrderTruth {
18
+ schemaVersion: typeof PURCHASE_TRUTH_SCHEMA_VERSION;
19
+ /** Fixed authority marker: only a verified provider receiver may construct this. */
20
+ authority: "verified-provider";
21
+ /** A public provider integration identifier, not a destination credential. */
22
+ provider: string;
23
+ /** Opaque provider webhook/event identity; the durable replay key scope includes provider. */
24
+ providerEventId: string;
25
+ /** Opaque provider order identity. It cannot contain customer or payment material. */
26
+ providerOrderId: string;
27
+ tenant: string;
28
+ status: VerifiedOrderStatus;
29
+ occurredAt: string;
30
+ currency: string;
31
+ /** Non-negative integer minor units, so no decimal rounding can alter truth. */
32
+ amountMinor: number;
33
+ }
34
+
35
+ export type TruthParseResult =
36
+ | { ok: true; value: VerifiedOrderTruth }
37
+ | { ok: false; reason: "missing" | "malformed" };
38
+
39
+ /** The persisted replay fact for this provider event id (provided by a future durable ledger). */
40
+ export interface AcceptedProviderEvent {
41
+ provider: string;
42
+ providerEventId: string;
43
+ fingerprint: string;
44
+ }
45
+
46
+ /** The persisted lifecycle state for this verified order (also owned by a future durable ledger). */
47
+ export interface VerifiedOrderSnapshot {
48
+ provider: string;
49
+ providerOrderId: string;
50
+ tenant: string;
51
+ state: Exclude<OrderTruthState, "none">;
52
+ }
53
+
54
+ export interface ResolveVerifiedOrderTruthInput {
55
+ /** Untrusted shape at this contract boundary; receiver verification is external. */
56
+ record: unknown;
57
+ /** Existing event replay fact, looked up atomically by provider + providerEventId. */
58
+ existingEvent: AcceptedProviderEvent | null;
59
+ /** Existing order state, looked up atomically by provider + providerOrderId + tenant. */
60
+ existingOrder: VerifiedOrderSnapshot | null;
61
+ }
62
+
63
+ export type VerifiedOrderTruthResolution =
64
+ | { outcome: "invalid"; reason: "missing" | "malformed" }
65
+ | { outcome: "duplicate"; fingerprint: string }
66
+ | { outcome: "replay-conflict"; fingerprint: string }
67
+ | { outcome: "stale-transition"; fingerprint: string; currentState: Exclude<OrderTruthState, "none"> }
68
+ | {
69
+ outcome: "accepted";
70
+ /** Purchase is emitted once; later fulfillment is a verified reaffirmation, not a duplicate purchase. */
71
+ effect: "purchase" | "refund" | "cancel" | "reaffirmed";
72
+ fingerprint: string;
73
+ acceptedEvent: AcceptedProviderEvent;
74
+ nextOrder: VerifiedOrderSnapshot;
75
+ };
76
+
77
+ const RECORD_KEYS = [
78
+ "schemaVersion",
79
+ "authority",
80
+ "provider",
81
+ "providerEventId",
82
+ "providerOrderId",
83
+ "tenant",
84
+ "status",
85
+ "occurredAt",
86
+ "currency",
87
+ "amountMinor",
88
+ ] as const;
89
+ const OPAQUE_IDENTIFIER = /^[a-z][a-z0-9_-]{2,95}$/;
90
+ const SENSITIVE_FRAGMENT = /(?:email|phone|address|name|token|secret|password|cookie|session|bearer|jwt|card|payment)/i;
91
+ const CURRENCY = /^[A-Z]{3}$/;
92
+
93
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
94
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
95
+ }
96
+
97
+ function isExactObject(value: unknown, keys: readonly string[]): value is Record<string, unknown> {
98
+ if (!isPlainObject(value)) return false;
99
+ const actual = Object.keys(value);
100
+ return actual.length === keys.length && actual.every((key) => keys.includes(key));
101
+ }
102
+
103
+ function isOpaqueIdentifier(value: unknown): value is string {
104
+ return typeof value === "string" && OPAQUE_IDENTIFIER.test(value) && !SENSITIVE_FRAGMENT.test(value);
105
+ }
106
+
107
+ function isIsoInstant(value: unknown): value is string {
108
+ if (typeof value !== "string") return false;
109
+ const ms = Date.parse(value);
110
+ return Number.isFinite(ms) && new Date(ms).toISOString() === value;
111
+ }
112
+
113
+ /**
114
+ * Parse the closed, minimised verified-order record. Unknown fields (including raw
115
+ * provider payloads, customer data, or credentials) fail closed. `authority` is an
116
+ * explicit assertion the trusted receiver must stamp, never a browser permission.
117
+ */
118
+ export function parseVerifiedOrderTruth(value: unknown): TruthParseResult {
119
+ if (value === null || value === undefined) return { ok: false, reason: "missing" };
120
+ if (!isExactObject(value, RECORD_KEYS)) return { ok: false, reason: "malformed" };
121
+ if (value.schemaVersion !== PURCHASE_TRUTH_SCHEMA_VERSION || value.authority !== "verified-provider") {
122
+ return { ok: false, reason: "malformed" };
123
+ }
124
+ if (
125
+ !isOpaqueIdentifier(value.provider) ||
126
+ !isOpaqueIdentifier(value.providerEventId) ||
127
+ !isOpaqueIdentifier(value.providerOrderId) ||
128
+ !isOpaqueIdentifier(value.tenant) ||
129
+ typeof value.status !== "string" ||
130
+ !(VERIFIED_ORDER_STATUSES as readonly string[]).includes(value.status) ||
131
+ !isIsoInstant(value.occurredAt) ||
132
+ typeof value.currency !== "string" ||
133
+ !CURRENCY.test(value.currency) ||
134
+ typeof value.amountMinor !== "number" ||
135
+ !Number.isSafeInteger(value.amountMinor) ||
136
+ value.amountMinor < 0
137
+ ) {
138
+ return { ok: false, reason: "malformed" };
139
+ }
140
+ return {
141
+ ok: true,
142
+ value: {
143
+ schemaVersion: PURCHASE_TRUTH_SCHEMA_VERSION,
144
+ authority: "verified-provider",
145
+ provider: value.provider,
146
+ providerEventId: value.providerEventId,
147
+ providerOrderId: value.providerOrderId,
148
+ tenant: value.tenant,
149
+ status: value.status as VerifiedOrderStatus,
150
+ occurredAt: value.occurredAt,
151
+ currency: value.currency,
152
+ amountMinor: value.amountMinor,
153
+ },
154
+ };
155
+ }
156
+
157
+ /** Build only a validated record; this is not a webhook verifier or credential issuer. */
158
+ export function createVerifiedOrderTruth(value: VerifiedOrderTruth): TruthParseResult {
159
+ return parseVerifiedOrderTruth(value);
160
+ }
161
+
162
+ /**
163
+ * Stable, inspectable fingerprint for compare-and-set replay storage. It is deliberately
164
+ * derived only from the closed, minimised record; raw provider payloads never influence
165
+ * idempotency. A production ledger may hash this canonical string at rest.
166
+ */
167
+ export function verifiedOrderTruthFingerprint(record: VerifiedOrderTruth): string {
168
+ return [
169
+ record.schemaVersion,
170
+ record.authority,
171
+ record.provider,
172
+ record.providerEventId,
173
+ record.providerOrderId,
174
+ record.tenant,
175
+ record.status,
176
+ record.occurredAt,
177
+ record.currency,
178
+ String(record.amountMinor),
179
+ ].join("|");
180
+ }
181
+
182
+ function existingOrderMatches(record: VerifiedOrderTruth, existing: VerifiedOrderSnapshot): boolean {
183
+ return (
184
+ existing.provider === record.provider &&
185
+ existing.providerOrderId === record.providerOrderId &&
186
+ existing.tenant === record.tenant
187
+ );
188
+ }
189
+
190
+ function nextState(status: VerifiedOrderStatus, current: OrderTruthState): Exclude<OrderTruthState, "none"> | null {
191
+ if (current === "refunded" || current === "cancelled") return null;
192
+ if (current === "none") {
193
+ return status === "created" || status === "fulfilled" ? "purchased" : status;
194
+ }
195
+ // A created order may later be fulfilled, but it remains one logical purchase.
196
+ if (current === "purchased") {
197
+ if (status === "created") return null;
198
+ if (status === "fulfilled") return "purchased";
199
+ return status;
200
+ }
201
+ return null;
202
+ }
203
+
204
+ function effectFor(status: VerifiedOrderStatus, previous: OrderTruthState): "purchase" | "refund" | "cancel" | "reaffirmed" {
205
+ if (status === "refunded") return "refund";
206
+ if (status === "cancelled") return "cancel";
207
+ return previous === "purchased" ? "reaffirmed" : "purchase";
208
+ }
209
+
210
+ /**
211
+ * Make the idempotency/replay and lifecycle decision without owning persistence. The
212
+ * caller must atomically look up and compare-and-set the returned event/order facts;
213
+ * a losing race must re-read and invoke this function again. This keeps durable server
214
+ * ledger implementation outside WS4's build-ahead contract namespace.
215
+ */
216
+ export function resolveVerifiedOrderTruth(input: ResolveVerifiedOrderTruthInput): VerifiedOrderTruthResolution {
217
+ const parsed = parseVerifiedOrderTruth(input.record);
218
+ if (!parsed.ok) return { outcome: "invalid", reason: parsed.reason };
219
+ const record = parsed.value;
220
+ const fingerprint = verifiedOrderTruthFingerprint(record);
221
+
222
+ if (input.existingEvent) {
223
+ if (
224
+ input.existingEvent.provider !== record.provider ||
225
+ input.existingEvent.providerEventId !== record.providerEventId ||
226
+ input.existingEvent.fingerprint !== fingerprint
227
+ ) {
228
+ return { outcome: "replay-conflict", fingerprint };
229
+ }
230
+ return { outcome: "duplicate", fingerprint };
231
+ }
232
+
233
+ if (input.existingOrder && !existingOrderMatches(record, input.existingOrder)) {
234
+ // A durable ledger must never hand an order state across provider/order/tenant scope.
235
+ return { outcome: "replay-conflict", fingerprint };
236
+ }
237
+ const previous = input.existingOrder?.state ?? "none";
238
+ const state = nextState(record.status, previous);
239
+ if (!state) {
240
+ // `none` always maps to a state above; retain an explicit narrowed fallback for TS.
241
+ const currentState: Exclude<OrderTruthState, "none"> = previous === "none" ? "purchased" : previous;
242
+ return { outcome: "stale-transition", fingerprint, currentState };
243
+ }
244
+
245
+ return {
246
+ outcome: "accepted",
247
+ effect: effectFor(record.status, previous),
248
+ fingerprint,
249
+ acceptedEvent: { provider: record.provider, providerEventId: record.providerEventId, fingerprint },
250
+ nextOrder: { provider: record.provider, providerOrderId: record.providerOrderId, tenant: record.tenant, state },
251
+ };
252
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * WS4-only source adapter for application operations that have already been
3
+ * accepted by their owning browser surface. It deliberately has no browser
4
+ * globals, route knowledge, persistence, destination, or dispatch authority.
5
+ */
6
+ import {
7
+ validateRegulatedCommerceIntent,
8
+ type CommerceAttribution,
9
+ type CommerceConsent,
10
+ type ContractViolation,
11
+ type RegulatedCommerceIntent,
12
+ } from "../events/contract.js";
13
+
14
+ export const ACCEPTED_BROWSER_OPERATION_KINDS = [
15
+ "page_viewed", "product_viewed", "cart_changed", "checkout_handoff",
16
+ ] as const;
17
+ export type AcceptedBrowserOperationKind = (typeof ACCEPTED_BROWSER_OPERATION_KINDS)[number];
18
+
19
+ /** The deliberately small, PII-free handoff from an accepted application operation. */
20
+ export interface AcceptedBrowserOperation {
21
+ readonly id: string;
22
+ readonly kind: AcceptedBrowserOperationKind;
23
+ readonly occurredAt: string;
24
+ readonly provenanceId: string;
25
+ readonly attribution: CommerceAttribution;
26
+ readonly consent: CommerceConsent;
27
+ }
28
+
29
+ export type AcceptedBrowserOperationResult =
30
+ | { readonly accepted: true; readonly intent: RegulatedCommerceIntent }
31
+ | { readonly accepted: false; readonly violations: readonly ContractViolation[] };
32
+
33
+ const OPERATION_KEYS = ["id", "kind", "occurredAt", "provenanceId", "attribution", "consent"] as const;
34
+
35
+ function plainExact(value: unknown): value is Record<string, unknown> {
36
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
37
+ const prototype = Object.getPrototypeOf(value);
38
+ if (prototype !== Object.prototype && prototype !== null) return false;
39
+ const keys = Object.keys(value);
40
+ return keys.length === OPERATION_KEYS.length && keys.every((key) => (OPERATION_KEYS as readonly string[]).includes(key));
41
+ }
42
+
43
+ /**
44
+ * Convert one accepted page/product/cart/handoff operation to the frozen semantic
45
+ * contract. This preserves denied or unattributed state for local dispatch policy
46
+ * to suppress later; it never upgrades consent or attribution on its own.
47
+ */
48
+ export function sourceAcceptedBrowserOperation(input: unknown): AcceptedBrowserOperationResult {
49
+ if (!plainExact(input)) return { accepted: false, violations: [{ path: "", code: "invalid_type" }] };
50
+ if (!(ACCEPTED_BROWSER_OPERATION_KINDS as readonly string[]).includes(String(input.kind))) {
51
+ return { accepted: false, violations: [{ path: "kind", code: "invalid_value" }] };
52
+ }
53
+
54
+ const parsed = validateRegulatedCommerceIntent({
55
+ schemaVersion: "1",
56
+ id: input.id,
57
+ kind: input.kind,
58
+ occurredAt: input.occurredAt,
59
+ source: "accepted_browser_operation",
60
+ provenanceId: input.provenanceId,
61
+ attribution: input.attribution,
62
+ consent: input.consent,
63
+ });
64
+ return parsed.ok
65
+ ? { accepted: true, intent: parsed.value }
66
+ : { accepted: false, violations: parsed.violations };
67
+ }
@@ -0,0 +1,93 @@
1
+ /** Deterministic, in-memory WS4 fixture destination. Never performs I/O or delivery. */
2
+ import {
3
+ validateRegulatedCommerceIntent,
4
+ type RegulatedCommerceIntent,
5
+ } from "../events/contract.js";
6
+
7
+ export const LOCAL_FAKE_DESTINATION_SCHEMA_VERSION = "1" as const;
8
+ export const LOCAL_FAKE_CHANNELS = ["browser", "server"] as const;
9
+ export type LocalFakeChannel = (typeof LOCAL_FAKE_CHANNELS)[number];
10
+
11
+ export interface LocalFakeDestinationConfig {
12
+ schemaVersion: typeof LOCAL_FAKE_DESTINATION_SCHEMA_VERSION;
13
+ /** A fixture name only; it is never a hostname, URL, credential, or real destination id. */
14
+ name: "fake-analytics" | "fake-ads";
15
+ /** Fail this many valid attempts before the first accepted local receipt. */
16
+ outageAttempts: number;
17
+ }
18
+
19
+ export interface LocalFakeReceipt {
20
+ destination: LocalFakeDestinationConfig["name"];
21
+ channel: LocalFakeChannel;
22
+ eventId: string;
23
+ attempt: number;
24
+ }
25
+
26
+ export type LocalFakeDeliveryResult =
27
+ | { outcome: "delivered"; receipt: LocalFakeReceipt }
28
+ | { outcome: "duplicate"; eventId: string }
29
+ | { outcome: "outage"; eventId: string; attempt: number }
30
+ | { outcome: "rejected"; reason: "invalid-channel" | "invalid-intent" };
31
+
32
+ export interface LocalFakeDeliveryInput {
33
+ channel: unknown;
34
+ intent: unknown;
35
+ }
36
+
37
+ function isChannel(value: unknown): value is LocalFakeChannel {
38
+ return typeof value === "string" && (LOCAL_FAKE_CHANNELS as readonly string[]).includes(value);
39
+ }
40
+
41
+ function validConfig(config: LocalFakeDestinationConfig): boolean {
42
+ return (
43
+ config.schemaVersion === LOCAL_FAKE_DESTINATION_SCHEMA_VERSION &&
44
+ (config.name === "fake-analytics" || config.name === "fake-ads") &&
45
+ Number.isSafeInteger(config.outageAttempts) &&
46
+ config.outageAttempts >= 0
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Local-only delivery fixture. A successful event id is consumed once across both
52
+ * simulated channels, modelling browser/server dual delivery without any network.
53
+ * An outage happens before consumption, so the same id is eligible for a safe retry.
54
+ */
55
+ export class DeterministicFakeDestination {
56
+ private readonly attempts = new Map<string, number>();
57
+ private readonly delivered = new Set<string>();
58
+ private readonly receiptLog: LocalFakeReceipt[] = [];
59
+
60
+ constructor(private readonly config: LocalFakeDestinationConfig) {
61
+ if (!validConfig(config)) throw new Error("invalid local fake destination config");
62
+ }
63
+
64
+ deliver(input: LocalFakeDeliveryInput): LocalFakeDeliveryResult {
65
+ if (!isChannel(input.channel)) return { outcome: "rejected", reason: "invalid-channel" };
66
+ const parsed = validateRegulatedCommerceIntent(input.intent);
67
+ if (!parsed.ok) return { outcome: "rejected", reason: "invalid-intent" };
68
+ return this.deliverValidated(input.channel, parsed.value);
69
+ }
70
+
71
+ private deliverValidated(channel: LocalFakeChannel, intent: RegulatedCommerceIntent): LocalFakeDeliveryResult {
72
+ const eventId = intent.id;
73
+ if (this.delivered.has(eventId)) return { outcome: "duplicate", eventId };
74
+ const attempt = (this.attempts.get(eventId) ?? 0) + 1;
75
+ this.attempts.set(eventId, attempt);
76
+ if (attempt <= this.config.outageAttempts) return { outcome: "outage", eventId, attempt };
77
+
78
+ const receipt: LocalFakeReceipt = { destination: this.config.name, channel, eventId, attempt };
79
+ this.delivered.add(eventId);
80
+ this.receiptLog.push(receipt);
81
+ return { outcome: "delivered", receipt };
82
+ }
83
+
84
+ /** A defensive snapshot for assertions; callers cannot mutate fixture history. */
85
+ receipts(): readonly LocalFakeReceipt[] {
86
+ return this.receiptLog.map((receipt) => ({ ...receipt }));
87
+ }
88
+
89
+ /** The number of valid attempts for an event id, including simulated outages. */
90
+ attemptCount(eventId: string): number {
91
+ return this.attempts.get(eventId) ?? 0;
92
+ }
93
+ }
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Digest-addressed static-bundle serving (T2, ADR 0012 —
2
+ * Digest-addressed static-bundle serving (ADR 0012 —
3
3
  * `docs/architecture/decisions/0012-instant-static-rollback-pointer-substrate.md`),
4
- * T6-aware (per-file content-addressed asset dedup,
4
+ * dedup-aware (per-file content-addressed asset dedup,
5
5
  * `docs/epics/proposed/storefront-instant-static-rollback.md` Addendum §A).
6
6
  *
7
7
  * Reads an already-materialized, already-staged static bundle
8
8
  * (`scripts/publish/materialize-tenant.mjs` output, staged by
9
9
  * `scripts/publish/stage-bundle.mjs` to `versions/<treeDigest>/…` PLUS the
10
- * shared `assets/<sha256>` pool, T6) straight out of R2 and serves the bytes
10
+ * shared `assets/<sha256>` pool) straight out of R2 and serves the bytes
11
11
  * VERBATIM — no re-render, no per-request CSP-nonce middleware (the bundle
12
12
  * carries one pinned nonce for its whole tree; see the ADR's "Consequences"
13
13
  * section). This is the read half: the write path (what PUTs a bundle into this
@@ -320,8 +320,8 @@ export async function serveStaticBundle(
320
320
  }
321
321
 
322
322
  const isHtml = rel.endsWith(".html");
323
- // T6 cache tier: use the manifest's `kind` classification when present.
324
- // A pre-T6 manifest (no `files` at all) falls back to the ORIGINAL
323
+ // Cache tier: use the manifest's `kind` classification when present.
324
+ // A manifest with no `files` at all falls back to the ORIGINAL
325
325
  // isHtml-based heuristic — every non-html path used to be treated as a
326
326
  // long-cacheable asset, which this preserves exactly for an old bundle
327
327
  // this Worker version might still encounter mid-rollout.