@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,116 @@
1
+ /**
2
+ * Progressive stepper + staged flow. Drives Run → Edit → What's next with
3
+ * progressive disclosure: only the current stage shows, completed stages
4
+ * collapse to a done-bar, and the flow AUTO-ADVANCES on monitor.ts's REAL loop
5
+ * signal (never a timer) — Step 1 completes when the local server is live,
6
+ * Step 2 when a real save→reload is observed since the edit step began.
7
+ * Consumes the shared tot:loop-state event from monitor.ts (one poll for the
8
+ * whole page). Imported from a processed `<script>` in cockpit.astro (ADR
9
+ * 0013), rendered only when isLocalDevLoop.
10
+ *
11
+ * Progress is persisted in sessionStorage. This is REQUIRED, not a nicety:
12
+ * saving a watched file triggers Vite's global full-reload, which reloads
13
+ * THIS /dev tab too — resetting in-memory state and re-baselining the very
14
+ * save that should advance Edit→Next. Persisting done-flags + current stage +
15
+ * the edit baseline (the lastSaveAt watermark captured when the edit step
16
+ * began, BEFORE the qualifying save) lets the flow survive that reload and
17
+ * credit the save.
18
+ */
19
+ export function initStepper(): void {
20
+ const stepper: any = document.getElementById("stepper");
21
+ if (!stepper) return;
22
+ const stages: any = { run: document.getElementById("stage-run"), edit: document.getElementById("stage-edit"), next: document.getElementById("stage-next") };
23
+ const bars: any = { run: document.getElementById("bar-run"), edit: document.getElementById("bar-edit") };
24
+ const celRun = document.getElementById("cel-run");
25
+ const celEdit = document.getElementById("cel-edit");
26
+ const toNext: any = document.getElementById("toNext");
27
+ const detect = document.getElementById("detect");
28
+ const detectTxt = document.getElementById("detectTxt");
29
+ const detectEdit = document.getElementById("detect-edit");
30
+ const detectEditTxt = document.getElementById("detectEditTxt");
31
+ const barRunMeta = document.getElementById("bar-run-meta");
32
+ const barEditMeta = document.getElementById("bar-edit-meta");
33
+ const toast = document.getElementById("toast");
34
+ const toastMsg = document.getElementById("toastMsg");
35
+ let tT: ReturnType<typeof setTimeout>;
36
+ const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
37
+
38
+ const SS = window.sessionStorage;
39
+ function load(k: string, d: any) { try { const v = SS.getItem(k); return v === null ? d : JSON.parse(v); } catch (e) { return d; } }
40
+ function put(k: string, v: any) { try { SS.setItem(k, JSON.stringify(v)); } catch (e) {} }
41
+
42
+ function ping(m: string, once?: string) {
43
+ if (once && load(once, false)) return;
44
+ if (once) put(once, true);
45
+ if (!toast || !toastMsg) return;
46
+ toastMsg.textContent = m; toast.classList.add("show");
47
+ clearTimeout(tT); tT = setTimeout(() => { toast.classList.remove("show"); }, 1900);
48
+ }
49
+
50
+ const state = {
51
+ unlocked: load("tot.unlocked", { run: true, edit: false, next: false }),
52
+ done: load("tot.done", { run: false, edit: false }),
53
+ current: load("tot.current", "run"),
54
+ };
55
+ let editBase = load("tot.editBase", null);
56
+ function persist() { put("tot.unlocked", state.unlocked); put("tot.done", state.done); put("tot.current", state.current); }
57
+
58
+ function render() {
59
+ Array.prototype.forEach.call(stepper.querySelectorAll(".snode"), (n: any) => {
60
+ const s = n.getAttribute("data-go");
61
+ n.setAttribute("data-st", state.current === s ? "active" : state.done[s] ? "done" : state.unlocked[s] ? "done" : "locked");
62
+ });
63
+ Object.keys(stages).forEach((s) => { if (stages[s]) stages[s].hidden = state.current !== s; });
64
+ if (bars.run) bars.run.hidden = !(state.done.run && state.current !== "run");
65
+ if (bars.edit) bars.edit.hidden = !(state.done.edit && state.current !== "edit");
66
+ // Contextual celebrate banners reflect achieved state (survive reloads).
67
+ if (celRun && state.done.run) celRun.hidden = false;
68
+ if (celEdit && state.done.edit) celEdit.hidden = false;
69
+ if (toNext) toNext.disabled = !state.unlocked.next;
70
+ }
71
+ function goTo(s: string) {
72
+ if (!state.unlocked[s]) return;
73
+ state.current = s; persist(); render();
74
+ window.scrollTo({ top: 0, behavior: reduce ? "auto" : "smooth" });
75
+ }
76
+
77
+ function onSignal(s: any) {
78
+ if (!s || s.offline) return;
79
+
80
+ // Step 1 — local server is live.
81
+ if (s.server && !state.done.run) {
82
+ state.done.run = true; state.unlocked.edit = true;
83
+ if (editBase === null) { editBase = s.lastSaveAt || 0; put("tot.editBase", editBase); }
84
+ persist();
85
+ if (barRunMeta && s.port) barRunMeta.textContent = "localhost:" + s.port + (s.tenant ? "/" + s.tenant + "/" : "/");
86
+ if (detect) detect.setAttribute("data-listening", "false");
87
+ if (detectTxt) detectTxt.textContent = "Your store is live on this machine.";
88
+ if (state.current === "run") { goTo("edit"); } else { render(); }
89
+ ping("Your store is live", "tot.pinged.run");
90
+ }
91
+
92
+ // Step 2 — a NEW save (since the edit step began) has been reflected by a reload.
93
+ if (state.done.run && !state.done.edit && editBase !== null) {
94
+ const newSave = (s.lastSaveAt || 0) > editBase;
95
+ const reflected = (s.lastSaveAt || 0) > 0 && (s.lastReloadAt || 0) >= (s.lastSaveAt || 0);
96
+ if (newSave && !reflected && detectEditTxt) {
97
+ detectEditTxt.textContent = "Change detected — reloading your store…";
98
+ }
99
+ if (newSave && reflected) {
100
+ state.done.edit = true; state.unlocked.next = true; persist();
101
+ if (barEditMeta && s.watchedFile) barEditMeta.textContent = s.watchedFile;
102
+ if (detectEdit) detectEdit.setAttribute("data-listening", "false");
103
+ if (detectEditTxt) detectEditTxt.textContent = "Saved — your store reloaded. That's the loop.";
104
+ if (state.current === "edit") { goTo("next"); } else { render(); }
105
+ ping("Saved — your page reloaded", "tot.pinged.edit");
106
+ }
107
+ }
108
+ }
109
+
110
+ render();
111
+ Array.prototype.forEach.call(document.querySelectorAll("[data-go]"), (el: any) => {
112
+ el.addEventListener("click", () => { goTo(el.getAttribute("data-go")); });
113
+ });
114
+ window.addEventListener("tot:loop-state", (e: any) => { onSignal(e.detail); });
115
+ if (window.__totLoop) onSignal(window.__totLoop);
116
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Theme toggle: flip data-theme on <html>, seeded from the OS preference on
3
+ * first click, and PERSISTED to localStorage so a reload stays on the manual
4
+ * choice (the is:inline head seed script reads this same key before first
5
+ * paint — that one stays is:inline because it must run synchronously before
6
+ * CSS paints, the one legitimate is:inline case per ADR 0013). Clearing
7
+ * storage falls back to prefers-color-scheme again, same as a first-ever
8
+ * visit. Imported from a processed `<script>` in cockpit.astro.
9
+ */
10
+ export function initThemeToggle(): void {
11
+ const root = document.documentElement;
12
+ const btn = document.getElementById("themeBtn");
13
+ if (!btn) return;
14
+ const KEY = "tot.theme";
15
+ function store(v: string) { try { localStorage.setItem(KEY, v); } catch (e) {} }
16
+ btn.addEventListener("click", () => {
17
+ const cur = root.getAttribute("data-theme");
18
+ const next = cur === "dark" ? "light" : cur === "light" ? "dark"
19
+ : (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "dark");
20
+ root.setAttribute("data-theme", next);
21
+ store(next);
22
+ });
23
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Per-DEVELOPER "local dev runtime" latest-state — the LIVE half of the
3
- * local→hosted activity bridge (G1). Where activityStore.ts holds a ring buffer
3
+ * local→hosted activity bridge. Where activityStore.ts holds a ring buffer
4
4
  * of file-SAVE events, this holds the most recent heartbeat a developer's LOCAL
5
5
  * `tot dev`/`tot start` sent (see the CLI's dev-heartbeat.mjs). The hosted cockpit
6
6
  * reads it to show a LIVE, clickable local-dev link + the running CLI version.
@@ -15,10 +15,11 @@
15
15
  * response with Set-Cookie, and anything non-200/non-HTML. In `astro dev` there
16
16
  * is no `caches` global, so every helper is a clean no-op.
17
17
  *
18
- * Note on the CSP nonce: a stored page carries the nonce it was rendered with, in
19
- * both its inline tags and its CSP header, and is served to many users. That is
20
- * safe — the nonce is not a secret; it only has to match within one identical,
21
- * non-personalized document, which it does.
18
+ * Note on the CSP nonce (ADR 0018): a stored page is NONCE-FREE its inline tags
19
+ * and CSP header carry the nonce placeholder, not a live nonce. A fresh per-response
20
+ * nonce is stamped in at serve time (stampResponseNonce), on both the cold render and
21
+ * the warm cache hit, so no cached artifact ever holds a reusable nonce and every
22
+ * response gets its own matching nonce/CSP pair.
22
23
  */
23
24
 
24
25
  /** Synthetic internal origin for cache keys — never hits the network. */
@@ -62,12 +63,23 @@ export function pageCacheKey(
62
63
  pathname: string,
63
64
  search: string,
64
65
  version: number,
66
+ planRevision?: string,
65
67
  ): Request {
66
- return new Request(`${CACHE_ORIGIN}/${tenantId}/v${version}${pathname}${search}`);
68
+ // A stored page carries the CSP header it was rendered with, INCLUDING any
69
+ // version-scoped integration origins. Folding the resolved `planRevision` into
70
+ // the key makes a warm entry plan-scoped: a rollback/uninstall resolves a new
71
+ // revision, so the old entry is unreachable (a clean miss that re-renders
72
+ // without the removed origins) instead of surviving to TTL. Absent (no
73
+ // integration plane resolved) the key is byte-identical to the non-integration
74
+ // form, so a plain tenant pays nothing and its cache is never busted.
75
+ const planSegment = planRevision ? `/pr:${planRevision}` : "";
76
+ return new Request(
77
+ `${CACHE_ORIGIN}/${tenantId}/v${version}${planSegment}${pathname}${search}`,
78
+ );
67
79
  }
68
80
 
69
81
  /**
70
- * Cache key for a digest-addressed static-bundle response (T2, ADR 0012).
82
+ * Cache key for a digest-addressed static-bundle response (ADR 0012).
71
83
  * Keyed by `treeDigest` instead of the integer `cachever` `pageCacheKey` uses —
72
84
  * a pointer flip to a NEW digest is a different key, so it is a clean cache
73
85
  * miss (never a stale serve of the old digest); an unchanged digest keeps
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The storefront's OWN branded "your open change now conflicts" email. When the shared
3
+ * preview base advances (another change was accepted/integrated), every OTHER open
4
+ * candidate is auto-rebased onto the new base; a clean rebuild is silent, but a genuine
5
+ * same-line overlap can't be auto-combined and needs the author's call. This is the push
6
+ * notification for that case — the author is pinged the MOMENT their change actually
7
+ * conflicts with someone else's merge, not at their own accept attempt.
8
+ *
9
+ * Rendered here (our brand), transmitted by tot20 (see lib/messaging/messagesClient.ts).
10
+ * Email HTML rules: inline styles only, table-free simple layout, a visible fallback URL
11
+ * under the button.
12
+ */
13
+
14
+ /** Minimal HTML escape for untrusted interpolation (file paths, store name). */
15
+ function esc(s: string): string {
16
+ return s
17
+ .replace(/&/g, "&amp;")
18
+ .replace(/</g, "&lt;")
19
+ .replace(/>/g, "&gt;")
20
+ .replace(/"/g, "&quot;");
21
+ }
22
+
23
+ export interface CandidateConflictEmailInput {
24
+ /** Display name of the store/experience. Default "Regulated Storefront". */
25
+ storeName?: string;
26
+ /** The author's open change PR number, when known. */
27
+ prNumber?: number | null;
28
+ /** Repo-relative paths that changed on both sides and need the author's pick. */
29
+ unresolved: string[];
30
+ /** Absolute deep-link to the resolve UI (admin#publish), or null when no origin is known. */
31
+ resolveUrl?: string | null;
32
+ }
33
+
34
+ const FILE_LIMIT = 12;
35
+
36
+ export function renderCandidateConflictEmail(input: CandidateConflictEmailInput): {
37
+ subject: string;
38
+ html: string;
39
+ } {
40
+ const store = input.storeName?.trim() || "Regulated Storefront";
41
+ const label = input.prNumber != null ? `PR #${input.prNumber}` : "your change";
42
+ const subject = `Action needed: ${label} now conflicts with ${store}`;
43
+ const url = input.resolveUrl || null;
44
+
45
+ const files = input.unresolved.slice(0, FILE_LIMIT);
46
+ const overflow = input.unresolved.length - files.length;
47
+ const fileList = files.length
48
+ ? `<ul style="margin:8px 0 0;padding-left:20px;color:#33415b;font-size:14px;">${files
49
+ .map((f) => `<li style="margin:2px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">${esc(f)}</li>`)
50
+ .join("")}${overflow > 0 ? `<li style="margin:2px 0;color:#8593a8;">…and ${overflow} more file(s)</li>` : ""}</ul>`
51
+ : "";
52
+
53
+ const cta = url
54
+ ? `<div style="margin:28px 0;">
55
+ <a href="${esc(url)}" style="display:inline-block;background:#0b7a6d;color:#ffffff;text-decoration:none;font-weight:600;font-size:16px;padding:14px 28px;border-radius:999px;">Resolve it now</a>
56
+ </div>
57
+ <p style="margin:0 0 4px;font-size:13px;color:#55637a;">Or paste this link into your browser:</p>
58
+ <p style="margin:0 0 24px;font-size:13px;word-break:break-all;"><a href="${esc(url)}" style="color:#0b7a6d;">${esc(url)}</a></p>`
59
+ : "";
60
+
61
+ const html = `<!doctype html>
62
+ <html lang="en">
63
+ <head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /></head>
64
+ <body style="margin:0;padding:0;background:#f7f9fc;">
65
+ <div style="max-width:520px;margin:0 auto;padding:32px 20px;font:16px/1.6 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;color:#33415b;">
66
+ <div style="font:600 12px/1 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.12em;text-transform:uppercase;color:#0b7a6d;margin:0 0 24px;">
67
+ Token of Trust&#169; &middot; ${esc(store)}
68
+ </div>
69
+ <h1 style="font-size:22px;line-height:1.3;letter-spacing:-.02em;margin:0 0 12px;color:#0a2540;">${esc(label)} needs your input</h1>
70
+ <p style="margin:0 0 8px;color:#33415b;">The store moved on — another change was just accepted into <strong>${esc(store)}</strong>, and we tried to rebuild ${esc(label)} on top of it automatically. Most of it merged cleanly, but these files were edited on both sides and need you to pick which version wins:</p>
71
+ ${fileList}
72
+ ${cta}
73
+ <p style="margin:16px 0 0;font-size:14px;color:#33415b;">You can also resolve it from the command line — <code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">tot preview --strategy=ours</code> keeps your version, <code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">tot preview --strategy=theirs</code> takes the store's.</p>
74
+ <p style="margin:24px 0 0;font-size:12px;color:#8593a8;border-top:1px solid #e3e8ef;padding-top:16px;">Your change is safe and still open — it just stays not-mergeable until you resolve these files. Nothing was overwritten.</p>
75
+ </div>
76
+ </body>
77
+ </html>`;
78
+
79
+ return { subject, html };
80
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Storefront-side resolution of the renderer-owned four-stage browser
3
+ * environment (`@tot/public-runtime` `resolveBrowserEnvironment`). This is the ONE
4
+ * place request state is turned into the authoritative environment object; both
5
+ * document mounts (Layout.astro and `rawHtmlResponse`) serialize the result, so
6
+ * the object is identical in both by construction.
7
+ *
8
+ * The stage is derived from server facts only — `import.meta.env.DEV`, the request
9
+ * host, the resolved customization environment, and whether this is a per-change
10
+ * candidate render — never a client-supplied value. A candidate render is always
11
+ * `candidate` and never `live`; anything unresolved stays non-live.
12
+ */
13
+ import {
14
+ resolveBrowserEnvironment,
15
+ resolveEnvironmentOnlyPlan,
16
+ type BrowserEnvironment,
17
+ type EffectivePlan,
18
+ type IntegrationSiteSurface,
19
+ } from "@tot/public-runtime";
20
+ import { isAdminPath } from "@/lib/auth/loginGate";
21
+
22
+ /**
23
+ * The non-secret render revision: the commit SHA this build was cut from (baked at
24
+ * build time, the same value GET /health reports), or `"local"` when there is no
25
+ * git context (dev, vitest). Never a token or signed URL.
26
+ */
27
+ function platformRevision(): string {
28
+ return typeof __GIT_SHA__ !== "undefined" && __GIT_SHA__ ? __GIT_SHA__ : "local";
29
+ }
30
+
31
+ /** A request host is local when it is served on loopback (`astro dev` or a local built Worker). */
32
+ function isLocalHost(host: string): boolean {
33
+ return host.startsWith("localhost") || host.startsWith("127.0.0.1");
34
+ }
35
+
36
+ /**
37
+ * Map the resolved tenant + request path to a document surface. Admin/cockpit
38
+ * paths are `admin`; cart/checkout paths are `checkout`; a catalog tenant is
39
+ * `storefront`; a marketing tenant (no catalog) is `marketing`.
40
+ */
41
+ export function deriveSiteSurface(input: {
42
+ catalog: boolean;
43
+ pathname: string;
44
+ }): IntegrationSiteSurface {
45
+ // Admin + the cockpit control-plane panel (host-routed `/cockpit` or tenant-first
46
+ // `/<tenant>/cockpit`) are the `admin` surface.
47
+ if (isAdminPath(input.pathname) || /^\/(?:[^/]+\/)?cockpit(?:\/|$)/.test(input.pathname)) {
48
+ return "admin";
49
+ }
50
+ if (/^\/(checkout|cart)(\/|$)/.test(input.pathname)) return "checkout";
51
+ return input.catalog ? "storefront" : "marketing";
52
+ }
53
+
54
+ /**
55
+ * The normal (non-candidate) per-request environment object, computed once by the
56
+ * middleware. `astro dev` and a Worker served on localhost both resolve to `local`
57
+ * (their CSP-enforcement mode differs — report-only vs enforced — but that is a
58
+ * response-header concern, not part of this object).
59
+ */
60
+ export function requestBrowserEnvironment(input: {
61
+ host: string;
62
+ isPreviewHost: boolean;
63
+ customizationEnv: "live" | "test";
64
+ canonicalHost: string;
65
+ siteSurface: IntegrationSiteSurface;
66
+ }): BrowserEnvironment {
67
+ return resolveBrowserEnvironment({
68
+ dev: import.meta.env.DEV,
69
+ localHost: isLocalHost(input.host),
70
+ candidate: false,
71
+ previewHost: input.isPreviewHost,
72
+ customizationEnv: input.customizationEnv,
73
+ siteSurface: input.siteSurface,
74
+ canonicalHost: input.canonicalHost,
75
+ revision: platformRevision(),
76
+ });
77
+ }
78
+
79
+ /**
80
+ * CSP enforcement mode for this runtime — the axis that stays DISTINCT from the
81
+ * `local` stage. `astro dev` (Node) serves un-nonced Vite `<style>`/`<script>`, so a
82
+ * strict nonce policy would blank the site: it is REPORT-ONLY there (violations
83
+ * still surface). The built Worker is CSP-clean, so the strict policy is ENFORCED —
84
+ * including the built Worker run locally, which is how enforced CSP is proven.
85
+ * Report-only-dev and enforced-built-Worker are both correct; only the mode differs,
86
+ * never the four-stage environment object.
87
+ */
88
+ export type CspEnforcementMode = "report-only" | "enforced";
89
+
90
+ export function cspEnforcementMode(dev: boolean): CspEnforcementMode {
91
+ return dev ? "report-only" : "enforced";
92
+ }
93
+
94
+ /** The response header name that carries the mode: report-only in dev, enforced otherwise. */
95
+ export function cspHeaderName(
96
+ dev: boolean,
97
+ ): "Content-Security-Policy-Report-Only" | "Content-Security-Policy" {
98
+ return dev ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
99
+ }
100
+
101
+ /**
102
+ * The environment object for a per-change candidate render (the cockpit candidate
103
+ * renderer, the revision renderer, and the not-yet-built PR page). The stage is
104
+ * always `candidate` — never `live`, even when the underlying tenant is live — and
105
+ * `revision` is the candidate's own version identity (a versionId / head sha), a
106
+ * public, non-secret value.
107
+ */
108
+ export function candidateBrowserEnvironment(input: {
109
+ canonicalHost: string;
110
+ revision: string;
111
+ siteSurface?: IntegrationSiteSurface;
112
+ }): BrowserEnvironment {
113
+ return resolveBrowserEnvironment({
114
+ dev: import.meta.env.DEV,
115
+ localHost: false,
116
+ candidate: true,
117
+ previewHost: true,
118
+ customizationEnv: "test",
119
+ siteSurface: input.siteSurface ?? "marketing",
120
+ canonicalHost: input.canonicalHost,
121
+ revision: input.revision,
122
+ });
123
+ }
124
+
125
+ /**
126
+ * The resolved effective plan for a candidate render — the `candidate`-stage
127
+ * environment wrapped in an environment-only plan so `rawHtmlResponse` (the raw
128
+ * mount of the fixed-phase engine) renders the same renderer-owned phases a live
129
+ * page gets. No integration declarations are materialized on the candidate hot
130
+ * path, so this is environment-only until that wiring lands.
131
+ */
132
+ export function candidateIntegrationPlan(
133
+ input: { canonicalHost: string; revision: string; siteSurface?: IntegrationSiteSurface },
134
+ tenantPublicId: string,
135
+ ): EffectivePlan {
136
+ return resolveEnvironmentOnlyPlan(candidateBrowserEnvironment(input), tenantPublicId);
137
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Materialize active managed-analytics installs into the render plane's existing
3
+ * declaration seam. `app_installs` owns status/manifest and MAX(seq) owns the
4
+ * active public config; candidates and drafts are never read here.
5
+ */
6
+ import {
7
+ ENVIRONMENT_SCHEMA_VERSION,
8
+ buildPlatformAdapterDeclaration,
9
+ validateIntegrationManifest,
10
+ type IntegrationDeclarationInput,
11
+ } from "@tot/public-runtime";
12
+ import { AppVersionStore } from "../apps/appVersionStore.js";
13
+ import type { Queryable } from "../d1/catalog.js";
14
+
15
+ const MANAGED_KIND = "managed-analytics-v1";
16
+
17
+ interface ActiveManagedRow {
18
+ install_id: string;
19
+ app_id: string;
20
+ app_version: string;
21
+ manifest: string;
22
+ }
23
+
24
+ /**
25
+ * Return only declarations whose canonical row, current immutable activation,
26
+ * and reviewed platform-adapter rebuild agree. Malformed/stale rows fail closed
27
+ * individually; an unavailable database is handled by the caller as no effects.
28
+ */
29
+ export async function loadActiveManagedInstallDeclarations(
30
+ db: Queryable,
31
+ tenantPublicId: string,
32
+ ): Promise<IntegrationDeclarationInput[]> {
33
+ const rows = await db.all<ActiveManagedRow>(
34
+ `SELECT install_id, app_id, app_version, manifest
35
+ FROM app_installs
36
+ WHERE tenant_id = ? AND managed_kind = ? AND status = 'active'
37
+ ORDER BY install_id`,
38
+ tenantPublicId,
39
+ MANAGED_KIND,
40
+ );
41
+ const versions = new AppVersionStore(db);
42
+ const declarations: IntegrationDeclarationInput[] = [];
43
+ for (const row of rows) {
44
+ try {
45
+ const manifest = validateIntegrationManifest(JSON.parse(row.manifest) as unknown);
46
+ const current = await versions.currentVersion(row.install_id);
47
+ if (!manifest.ok || !current || current.tenantId !== tenantPublicId) continue;
48
+ if (current.appVersion !== row.app_version || current.facets.appVersion !== row.app_version) continue;
49
+ if (current.facets.digest !== manifest.value.app.digest || manifest.value.app.id !== row.app_id) continue;
50
+ const destinationId = current.facets.publicConfig["destinationId"];
51
+ if (typeof destinationId !== "string") continue;
52
+ const built = buildPlatformAdapterDeclaration({
53
+ adapterId: row.app_id,
54
+ ids: { live: destinationId },
55
+ tenantPublicId,
56
+ environment: {
57
+ schemaVersion: ENVIRONMENT_SCHEMA_VERSION,
58
+ siteSurface: "storefront",
59
+ environment: "live",
60
+ canonicalHost: tenantPublicId,
61
+ revision: `managed-live:${current.seq}`,
62
+ },
63
+ });
64
+ if (!built.ok || built.declaration.manifest.app.digest !== manifest.value.app.digest) continue;
65
+ declarations.push({ ...built.declaration, ref: `managed-install:${row.install_id}` });
66
+ } catch {
67
+ // Persisted JSON/version corruption must disable this install, never widen
68
+ // CSP or partially materialize a subscription.
69
+ }
70
+ }
71
+ return declarations;
72
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Per-response CSP-nonce stamping (ADR 0018 nonce non-serialization; ext-kernel-04).
3
+ *
4
+ * The renderer emits a nonce-free template: every platform inline tag and the CSP
5
+ * header carry {@link INTEGRATION_NONCE_PLACEHOLDER}, never a live nonce. This is
6
+ * what the edge cache stores — a nonce-free artifact. At serve time (a cold render
7
+ * OR a warm cache hit) this stamps ONE fresh per-response nonce into both the HTML
8
+ * body and the CSP header, so every response gets a fresh, matching nonce/CSP pair
9
+ * and no nonce is ever reused across responses.
10
+ */
11
+ import { INTEGRATION_NONCE_PLACEHOLDER, stampNonce } from "@tot/public-runtime";
12
+
13
+ const CSP_HEADERS = [
14
+ "content-security-policy",
15
+ "content-security-policy-report-only",
16
+ ] as const;
17
+
18
+ /** A fresh, non-guessable per-response nonce. */
19
+ export function freshNonce(): string {
20
+ return crypto.randomUUID();
21
+ }
22
+
23
+ /**
24
+ * Replace the nonce placeholder with a fresh `nonce` in a response's CSP header(s)
25
+ * and — for an HTML response — its body. Non-HTML responses keep their (streamed)
26
+ * body untouched; their CSP header is still stamped. A response with no placeholder
27
+ * (a static-bundle body, an API JSON) is returned effectively unchanged.
28
+ */
29
+ export async function stampResponseNonce(
30
+ response: Response,
31
+ nonce: string,
32
+ ): Promise<Response> {
33
+ for (const header of CSP_HEADERS) {
34
+ const value = response.headers.get(header);
35
+ if (value && value.includes(INTEGRATION_NONCE_PLACEHOLDER)) {
36
+ response.headers.set(header, stampNonce(value, nonce));
37
+ }
38
+ }
39
+
40
+ const contentType = response.headers.get("content-type") ?? "";
41
+ if (!contentType.includes("text/html")) return response;
42
+
43
+ const body = await response.text();
44
+ return new Response(stampNonce(body, nonce), {
45
+ status: response.status,
46
+ statusText: response.statusText,
47
+ headers: response.headers,
48
+ });
49
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Tenant-local integration declaration materialization (ext-kernel-09).
3
+ *
4
+ * This is the ONE production seam that turns a tenant's OWN declared, exact-version
5
+ * integrations into the already-materialized {@link IntegrationDeclarationInput} set
6
+ * the render hot path consumes. The interim marketing bridge
7
+ * ({@link resolveInterimBridgePlan}) resolves that set into the effective plan the
8
+ * fixed-phase engine serves — refusing a regulated tenant, resolving an eligible
9
+ * unregulated one — so a declared tenant boots its integration on-by-default
10
+ * (consent-gated) with no page edit and no vendor branch.
11
+ *
12
+ * A tenant with no declaration returns the empty set, so the bridge resolves an
13
+ * environment-only plan and the request is byte-identical to non-integration
14
+ * behavior (zero cost). The declarations are already materialized in tenant config
15
+ * (the manifest, the resolved public config, the set of asset paths present for the
16
+ * version); this loader only reads them, fail-closed — resolution is the bridge's
17
+ * job, materialization is config's, and the two never blur.
18
+ */
19
+ import type { IntegrationDeclarationInput, IntegrationManifest, TenantConfig } from "@tot/public-runtime";
20
+
21
+ const NO_DECLARATIONS: readonly IntegrationDeclarationInput[] = Object.freeze([]);
22
+
23
+ /**
24
+ * Read a tenant's already-materialized tenant-local declarations. Returns the empty
25
+ * set (never throws) when the tenant declares none or the field is malformed — an
26
+ * undeclared tenant must resolve to the environment-only plan, never an error.
27
+ */
28
+ export function loadTenantIntegrationDeclarations(
29
+ tenant: Pick<TenantConfig, "integrations">,
30
+ ): readonly IntegrationDeclarationInput[] {
31
+ const declared = tenant.integrations;
32
+ if (!Array.isArray(declared) || declared.length === 0) return NO_DECLARATIONS;
33
+ return declared;
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Demo tenant-local declaration — a same-origin sandbox analytics adapter.
38
+ //
39
+ // Seeded on the unregulated marketing demo/home tenants so a preview deploy
40
+ // DEMONSTRABLY materializes a real integration on-by-default: its exact-version
41
+ // entry module (committed under `public/apps/…`, so always materialized and served
42
+ // same-origin) flows the declaration → interim bridge → effective plan → boot
43
+ // descriptor → lifecycle import. Loading is consent-gated (purpose `analytics`,
44
+ // held back under the default-deny snapshot until granted), and the declared
45
+ // beacon origin reaches the strict CSP via `cspNeeds`. A regulated tenant carrying
46
+ // the same declaration is still REFUSED by the bridge.
47
+ // ---------------------------------------------------------------------------
48
+
49
+ /** The demo adapter's app id — a same-origin, sandbox-tier marketing analytics adapter. */
50
+ export const DEMO_ANALYTICS_APP_ID = "tot.demo.analytics";
51
+ /** The demo adapter's exact pinned version. */
52
+ export const DEMO_ANALYTICS_VERSION = "0.1.0";
53
+ /**
54
+ * The same-origin, exact-version entry module the lifecycle runtime dynamically
55
+ * imports. Committed under `apps/storefront/public/apps/…` so it is served verbatim
56
+ * at this root-absolute URL and is ALWAYS materialized (no R2 pointer needed).
57
+ */
58
+ export const DEMO_ANALYTICS_ENTRY_URL = `/apps/${DEMO_ANALYTICS_APP_ID}/${DEMO_ANALYTICS_VERSION}/entry.js`;
59
+ /** The beacon endpoint the demo declaration reserves in CSP (`connect-src`). */
60
+ export const DEMO_ANALYTICS_BEACON_ORIGIN = "https://demo-analytics.tokenoftrust.com";
61
+
62
+ /** The demo adapter's shared browser manifest (ext-kernel-01 v2). */
63
+ const DEMO_ANALYTICS_MANIFEST: IntegrationManifest = {
64
+ schemaVersion: 2,
65
+ app: {
66
+ id: DEMO_ANALYTICS_APP_ID,
67
+ name: "ToT Demo Analytics",
68
+ version: DEMO_ANALYTICS_VERSION,
69
+ digest: `sha256:${DEMO_ANALYTICS_APP_ID}-${DEMO_ANALYTICS_VERSION}`,
70
+ publisher: "Token of Trust",
71
+ },
72
+ compatibility: { storefrontBrowserContract: ">=2.0.0 <3.0.0", configurationSchemaVersion: 1 },
73
+ browser: {
74
+ executionTier: "sandbox",
75
+ entry: { asset: DEMO_ANALYTICS_ENTRY_URL, format: "module", phase: "document.deferred" },
76
+ contributions: [],
77
+ dependencies: [],
78
+ publicConfigSchema: {
79
+ type: "object",
80
+ additionalProperties: false,
81
+ required: ["siteId"],
82
+ properties: { siteId: { type: "string" } },
83
+ },
84
+ consent: {
85
+ purposes: ["analytics"],
86
+ loadWhen: "all-declared-purposes-eligible",
87
+ unknownState: "block",
88
+ },
89
+ // Same-origin entry loads under the strict base `'self'`; the declared beacon
90
+ // origin is the advanced exact source the compiler folds into `connect-src`.
91
+ csp: { profiles: [], sources: { "connect-src": [DEMO_ANALYTICS_BEACON_ORIGIN] } },
92
+ permissions: { events: ["storefront.page.viewed.v1"], data: [], actions: [], domIntent: "none" },
93
+ performanceBudget: {
94
+ maxTransferredBytes: 20000,
95
+ maxAdditionalRequests: 1,
96
+ maxMainThreadMilliseconds: 20,
97
+ mustBeAsyncOrDeferred: true,
98
+ },
99
+ },
100
+ server: { topics: [], secretRequirements: [] },
101
+ assurance: { requestedClassification: "baseline", regulatedTenantDefault: "disabled-until-certified" },
102
+ configurationMigrations: [],
103
+ };
104
+
105
+ /**
106
+ * The demo tenant-local declaration seeded on the unregulated marketing demo tenants.
107
+ * `materializedAssets` names the committed same-origin entry module, so the resolver's
108
+ * fail-closed materialized-asset check passes without any R2/publish step.
109
+ */
110
+ export const DEMO_ANALYTICS_DECLARATION: IntegrationDeclarationInput = {
111
+ origin: "tenant-local",
112
+ ref: "config/tenants:demo-analytics",
113
+ manifest: DEMO_ANALYTICS_MANIFEST,
114
+ publicConfig: { siteId: "tot-demo" },
115
+ materializedAssets: [DEMO_ANALYTICS_ENTRY_URL],
116
+ };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Shopper membership eligibility — the request-scoped seam that resolves whether
3
- * the current shopper is a member, and which tier (F10).
3
+ * the current shopper is a member, and which tier.
4
4
  *
5
5
  * Phase 1 storefronts have NO shopper-login/account source (member accounts are
6
6
  * a Phase-2 managed-checkout capability), so this resolves anonymous