@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,451 @@
1
+ /**
2
+ * Per-integration observability + performance budgets (ext-policy-08).
3
+ *
4
+ * Third-party integrations run inside the kernel-05 lifecycle runtime and the
5
+ * commerce-01 event broker, both of which already isolate a faulting
6
+ * integration from the page and from every other integration. This module is
7
+ * the durable, ATTRIBUTED signal those isolation points feed: a closed,
8
+ * fail-closed, secret-free contract that names WHICH integration (appId +
9
+ * version + origin) produced a performance measurement, a CSP violation, an
10
+ * initialization failure, or a delivery failure — tenant/environment/
11
+ * siteSurface scoped, exactly like {@link import("./tamper-evidence.js").TamperEvidenceBeacon}.
12
+ *
13
+ * Every manifest already DECLARES a per-integration {@link PerformanceBudget}
14
+ * (`browser.performanceBudget`, ext-policy-01). This module is the RUNTIME
15
+ * side of that declaration: {@link evaluatePerformanceBudget} compares a
16
+ * measured {@link PerformanceSignal} against the declared budget and flags an
17
+ * integration that exceeds it. Budget evaluation is fail-closed: an
18
+ * integration with no resolvable budget is flagged over-budget rather than
19
+ * silently passed ({@link evaluatePerformanceBudgets}).
20
+ *
21
+ * No PII: the schema is closed by construction (no free-form user data field
22
+ * exists to leak) and every signal is scanned with {@link findForbiddenFields}
23
+ * (secrets.ts) as defense in depth, exactly like every other contract here.
24
+ */
25
+ import type { ContractResult } from "./result.js";
26
+ import {
27
+ Violations,
28
+ reqObject,
29
+ childObject,
30
+ reqEnum,
31
+ reqLiteral,
32
+ reqString,
33
+ reqInt,
34
+ reqIsoTimestamp,
35
+ rejectUnknownKeys,
36
+ joinPath,
37
+ } from "./result.js";
38
+ import { OBSERVABILITY_SIGNAL_SCHEMA_VERSION } from "./version.js";
39
+ import { INTEGRATION_ENVIRONMENTS, INTEGRATION_SITE_SURFACES } from "./environment.js";
40
+ import type { IntegrationEnvironment, IntegrationSiteSurface } from "./environment.js";
41
+ import { findForbiddenFields } from "./secrets.js";
42
+ import type { PerformanceBudget } from "./manifest.js";
43
+ import type { LifecycleObservation } from "./lifecycle-runtime.js";
44
+ import type { SubscriberDeliveryOutcome } from "./commerce-broker.js";
45
+
46
+ /**
47
+ * The observability signal taxonomy. Every one is a fact ATTRIBUTABLE to one
48
+ * integration's own code, entry, or destination — never anonymous DOM reach
49
+ * (that is {@link import("./tamper-evidence.js").TamperEvidenceBeacon}'s domain).
50
+ */
51
+ export const OBSERVABILITY_SIGNAL_KINDS = [
52
+ "performance",
53
+ "csp-violation",
54
+ "initialization-failure",
55
+ "delivery-failure",
56
+ ] as const;
57
+ export type ObservabilitySignalKind = (typeof OBSERVABILITY_SIGNAL_KINDS)[number];
58
+
59
+ /** The exact integration a signal is attributed to — never a guess. */
60
+ export interface IntegrationAttribution {
61
+ appId: string;
62
+ version: string;
63
+ origin: string;
64
+ }
65
+
66
+ /** Stable key for grouping/looking up signals or budgets by integration identity. */
67
+ export function attributionKey(attribution: IntegrationAttribution): string {
68
+ return `${attribution.appId}@${attribution.version}`;
69
+ }
70
+
71
+ // ── Per-kind measured data ──────────────────────────────────────────────────
72
+
73
+ /**
74
+ * Measured timing/transfer facts for one integration's load+init. The three
75
+ * budget-bearing fields ({@link PERFORMANCE_BUDGET_DIMENSIONS}) map 1:1 onto
76
+ * the manifest's declared {@link PerformanceBudget}; `loadMs`/`initMs` are
77
+ * diagnostic timing with no declared ceiling.
78
+ */
79
+ export interface PerformanceSignalData {
80
+ /** Milliseconds from module-fetch start to the loader resolving (kernel-05 `load` stage). */
81
+ loadMs: number;
82
+ /** Milliseconds from a resolved module to `init()` resolving (kernel-05 `init` stage). */
83
+ initMs: number;
84
+ /** Bytes transferred for the entry module and anything it fetched during load+init. */
85
+ transferredBytes: number;
86
+ /** Network requests beyond the entry module observed during load+init. */
87
+ additionalRequests: number;
88
+ /** Main-thread time attributable to this integration's load+init, ms. */
89
+ mainThreadMilliseconds: number;
90
+ }
91
+
92
+ export const CSP_VIOLATION_DISPOSITIONS = ["enforce", "report"] as const;
93
+ export type CspViolationDisposition = (typeof CSP_VIOLATION_DISPOSITIONS)[number];
94
+
95
+ export interface CspViolationSignalData {
96
+ /** The violated directive, e.g. `"script-src"`. */
97
+ directive: string;
98
+ /** The blocked origin only — never a full URL (a query string is where a token lives). */
99
+ blockedOrigin: string;
100
+ disposition: CspViolationDisposition;
101
+ }
102
+
103
+ /** The lifecycle stages a failure can be attributed to before an integration ever runs. */
104
+ export const INITIALIZATION_FAILURE_STAGES = ["load", "init"] as const;
105
+ export type InitializationFailureStage = (typeof INITIALIZATION_FAILURE_STAGES)[number];
106
+
107
+ export interface InitializationFailureSignalData {
108
+ stage: InitializationFailureStage;
109
+ /** A message only — never a stack or thrown value (kernel-05's own convention). */
110
+ message: string;
111
+ }
112
+
113
+ export interface DeliveryFailureSignalData {
114
+ /** The commerce-broker subscription id that failed to receive the event. */
115
+ subscriberId: string;
116
+ /** A message only — never a stack or thrown value. */
117
+ message: string;
118
+ }
119
+
120
+ // ── The closed, discriminated signal contract ───────────────────────────────
121
+
122
+ interface ObservabilitySignalBase {
123
+ schemaVersion: typeof OBSERVABILITY_SIGNAL_SCHEMA_VERSION;
124
+ tenant: string;
125
+ environment: IntegrationEnvironment;
126
+ siteSurface: IntegrationSiteSurface;
127
+ attribution: IntegrationAttribution;
128
+ /** ISO-8601 instant the signal was observed. */
129
+ observedAt: string;
130
+ }
131
+
132
+ export interface PerformanceSignal extends ObservabilitySignalBase {
133
+ kind: "performance";
134
+ data: PerformanceSignalData;
135
+ }
136
+
137
+ export interface CspViolationSignal extends ObservabilitySignalBase {
138
+ kind: "csp-violation";
139
+ data: CspViolationSignalData;
140
+ }
141
+
142
+ export interface InitializationFailureSignal extends ObservabilitySignalBase {
143
+ kind: "initialization-failure";
144
+ data: InitializationFailureSignalData;
145
+ }
146
+
147
+ export interface DeliveryFailureSignal extends ObservabilitySignalBase {
148
+ kind: "delivery-failure";
149
+ data: DeliveryFailureSignalData;
150
+ }
151
+
152
+ /** The closed, versioned, secret-free per-integration observability signal. */
153
+ export type IntegrationObservabilitySignal =
154
+ | PerformanceSignal
155
+ | CspViolationSignal
156
+ | InitializationFailureSignal
157
+ | DeliveryFailureSignal;
158
+
159
+ /** The fields a caller supplies to {@link buildObservabilitySignal}. */
160
+ export type ObservabilitySignalInput = Omit<IntegrationObservabilitySignal, "schemaVersion">;
161
+
162
+ /** Stamp the fixed `schemaVersion` — the only constructor, so a signal is never built stale. */
163
+ export function buildObservabilitySignal(
164
+ input: ObservabilitySignalInput,
165
+ ): IntegrationObservabilitySignal {
166
+ return { schemaVersion: OBSERVABILITY_SIGNAL_SCHEMA_VERSION, ...input } as IntegrationObservabilitySignal;
167
+ }
168
+
169
+ /**
170
+ * Reduce a raw CSP `blocked-uri` to the bare origin a signal may carry. A
171
+ * scheme keyword (`"inline"`, `"eval"`, `"data"`, `"blob"`, `"self"`) passes
172
+ * through unchanged; a URL is reduced to `scheme://host[:port]` with the
173
+ * path/query/fragment dropped — the query string is where a candidate/preview
174
+ * token would live (the same reduction {@link import("./lifecycle-runtime.js").sanitizeNavigationUrl} applies to navigation).
175
+ */
176
+ export function sanitizeBlockedUri(raw: string): string {
177
+ try {
178
+ const url = new URL(raw);
179
+ return `${url.protocol}//${url.host}`;
180
+ } catch {
181
+ return raw.split("?")[0]!.split("#")[0]!;
182
+ }
183
+ }
184
+
185
+ function reqNonNegativeInt(
186
+ obj: Record<string, unknown>,
187
+ key: string,
188
+ v: Violations,
189
+ path: string,
190
+ ): number | undefined {
191
+ const value = reqInt(obj, key, v, path);
192
+ if (value !== undefined && value < 0) {
193
+ v.add(joinPath(path, key), "format", `"${key}" must be non-negative.`);
194
+ return undefined;
195
+ }
196
+ return value;
197
+ }
198
+
199
+ function reqNonNegativeNumber(
200
+ obj: Record<string, unknown>,
201
+ key: string,
202
+ v: Violations,
203
+ path: string,
204
+ ): number | undefined {
205
+ const value = obj[key];
206
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
207
+ v.add(joinPath(path, key), "type", `Expected a non-negative number at "${key}".`);
208
+ return undefined;
209
+ }
210
+ return value;
211
+ }
212
+
213
+ /** No query string or fragment — that is where a preview/candidate token would ride. */
214
+ function reqNoQueryOrFragment(value: string, v: Violations, path: string): void {
215
+ if (value.includes("?") || value.includes("#")) {
216
+ v.add(path, "format", `"${path}" must not carry a query string or fragment.`);
217
+ }
218
+ }
219
+
220
+ function validateAttribution(
221
+ value: unknown,
222
+ v: Violations,
223
+ path: string,
224
+ ): IntegrationAttribution | undefined {
225
+ const obj = reqObject(value, v, path);
226
+ if (!obj) return undefined;
227
+ rejectUnknownKeys(obj, ["appId", "version", "origin"], v, path);
228
+ const appId = reqString(obj, "appId", v, path);
229
+ const version = reqString(obj, "version", v, path);
230
+ const origin = reqString(obj, "origin", v, path);
231
+ if (appId === undefined || version === undefined || origin === undefined) return undefined;
232
+ return { appId, version, origin };
233
+ }
234
+
235
+ function validateSignalData(
236
+ kind: ObservabilitySignalKind,
237
+ data: Record<string, unknown>,
238
+ v: Violations,
239
+ ): void {
240
+ switch (kind) {
241
+ case "performance":
242
+ rejectUnknownKeys(
243
+ data,
244
+ ["loadMs", "initMs", "transferredBytes", "additionalRequests", "mainThreadMilliseconds"],
245
+ v,
246
+ "data",
247
+ );
248
+ reqNonNegativeNumber(data, "loadMs", v, "data");
249
+ reqNonNegativeNumber(data, "initMs", v, "data");
250
+ reqNonNegativeInt(data, "transferredBytes", v, "data");
251
+ reqNonNegativeInt(data, "additionalRequests", v, "data");
252
+ reqNonNegativeNumber(data, "mainThreadMilliseconds", v, "data");
253
+ return;
254
+ case "csp-violation": {
255
+ rejectUnknownKeys(data, ["directive", "blockedOrigin", "disposition"], v, "data");
256
+ reqString(data, "directive", v, "data");
257
+ const blockedOrigin = reqString(data, "blockedOrigin", v, "data");
258
+ if (blockedOrigin !== undefined) {
259
+ reqNoQueryOrFragment(blockedOrigin, v, "data.blockedOrigin");
260
+ }
261
+ reqEnum(data, "disposition", CSP_VIOLATION_DISPOSITIONS, v, "data");
262
+ return;
263
+ }
264
+ case "initialization-failure":
265
+ rejectUnknownKeys(data, ["stage", "message"], v, "data");
266
+ reqEnum(data, "stage", INITIALIZATION_FAILURE_STAGES, v, "data");
267
+ reqString(data, "message", v, "data");
268
+ return;
269
+ case "delivery-failure":
270
+ rejectUnknownKeys(data, ["subscriberId", "message"], v, "data");
271
+ reqString(data, "subscriberId", v, "data");
272
+ reqString(data, "message", v, "data");
273
+ return;
274
+ }
275
+ }
276
+
277
+ /**
278
+ * The single fail-CLOSED authority for "is this a well-formed observability
279
+ * signal?". Rejects an unknown schema version, an unknown kind, a malformed
280
+ * attribution, a kind/data mismatch, or any secret-bearing field.
281
+ */
282
+ export function validateObservabilitySignal(
283
+ input: unknown,
284
+ ): ContractResult<IntegrationObservabilitySignal> {
285
+ const v = new Violations();
286
+ const obj = reqObject(input, v, "");
287
+ if (!obj) return { ok: false, violations: v.items };
288
+
289
+ rejectUnknownKeys(
290
+ obj,
291
+ ["schemaVersion", "kind", "tenant", "environment", "siteSurface", "attribution", "observedAt", "data"],
292
+ v,
293
+ "",
294
+ );
295
+
296
+ reqLiteral(obj, "schemaVersion", OBSERVABILITY_SIGNAL_SCHEMA_VERSION, v, "");
297
+ const kind = reqEnum(obj, "kind", OBSERVABILITY_SIGNAL_KINDS, v, "");
298
+ reqString(obj, "tenant", v, "");
299
+ reqEnum(obj, "environment", INTEGRATION_ENVIRONMENTS, v, "");
300
+ reqEnum(obj, "siteSurface", INTEGRATION_SITE_SURFACES, v, "");
301
+ reqIsoTimestamp(obj, "observedAt", v, "");
302
+ validateAttribution(obj["attribution"], v, "attribution");
303
+
304
+ const data = childObject(obj, "data", v, "");
305
+ if (kind && data) validateSignalData(kind, data, v);
306
+
307
+ for (const violation of findForbiddenFields(obj)) v.items.push(violation);
308
+
309
+ if (!v.ok) return { ok: false, violations: v.items };
310
+ return { ok: true, value: input as IntegrationObservabilitySignal };
311
+ }
312
+
313
+ // ── Bridges from the sibling isolation points (kernel-05, commerce-01) ─────
314
+
315
+ /** {@link IntegrationAttribution} from the boot descriptor's per-integration identity. */
316
+ export function attributionFromBootIntegration(integration: {
317
+ appId: string;
318
+ version: string;
319
+ origin: string;
320
+ }): IntegrationAttribution {
321
+ return { appId: integration.appId, version: integration.version, origin: integration.origin };
322
+ }
323
+
324
+ /** Everything an observation needs to become a signal, beyond the observation itself. */
325
+ export interface ObservabilitySignalContext {
326
+ tenant: string;
327
+ environment: IntegrationEnvironment;
328
+ siteSurface: IntegrationSiteSurface;
329
+ /** ISO-8601 timestamp source; defaults to wall clock. */
330
+ now?: () => string;
331
+ }
332
+
333
+ function nowOf(context: ObservabilitySignalContext): string {
334
+ return context.now ? context.now() : new Date().toISOString();
335
+ }
336
+
337
+ /**
338
+ * Turn a failed kernel-05 {@link LifecycleObservation} into an attributed
339
+ * `initialization-failure` signal. `null` for anything that is not a load/init
340
+ * failure — a successful stage, an update/dispose, or a consent `gate` deferral
341
+ * is not an initialization failure.
342
+ */
343
+ export function initializationFailureFromObservation(
344
+ observation: LifecycleObservation,
345
+ context: ObservabilitySignalContext,
346
+ ): InitializationFailureSignal | null {
347
+ if (observation.ok) return null;
348
+ if (observation.stage !== "load" && observation.stage !== "init") return null;
349
+ return buildObservabilitySignal({
350
+ kind: "initialization-failure",
351
+ tenant: context.tenant,
352
+ environment: context.environment,
353
+ siteSurface: context.siteSurface,
354
+ attribution: attributionFromBootIntegration(observation.integration),
355
+ observedAt: nowOf(context),
356
+ data: { stage: observation.stage, message: observation.error ?? "unknown error" },
357
+ }) as InitializationFailureSignal;
358
+ }
359
+
360
+ /**
361
+ * Turn a failed commerce-broker {@link SubscriberDeliveryOutcome} into an
362
+ * attributed `delivery-failure` signal. `null` when delivery succeeded or was
363
+ * a policy-correct skip (`out-of-scope` / `consent-ineligible`) — only a
364
+ * throwing subscriber (`reason: "threw"`) is a genuine delivery failure.
365
+ * `attribution` is supplied by the caller, which owns the subscriber-id →
366
+ * integration mapping (a subscriber id is not itself an appId/version/origin).
367
+ */
368
+ export function deliveryFailureFromOutcome(
369
+ outcome: SubscriberDeliveryOutcome,
370
+ attribution: IntegrationAttribution,
371
+ context: ObservabilitySignalContext,
372
+ ): DeliveryFailureSignal | null {
373
+ if (outcome.delivered || outcome.reason !== "threw") return null;
374
+ return buildObservabilitySignal({
375
+ kind: "delivery-failure",
376
+ tenant: context.tenant,
377
+ environment: context.environment,
378
+ siteSurface: context.siteSurface,
379
+ attribution,
380
+ observedAt: nowOf(context),
381
+ data: { subscriberId: outcome.subscriberId, message: outcome.error ?? "unknown error" },
382
+ }) as DeliveryFailureSignal;
383
+ }
384
+
385
+ // ── Budgets: the runtime side of the manifest's declared PerformanceBudget ──
386
+
387
+ /** The three declared budget dimensions a {@link PerformanceSignal} is evaluated against. */
388
+ export const PERFORMANCE_BUDGET_DIMENSIONS = [
389
+ "transferredBytes",
390
+ "additionalRequests",
391
+ "mainThreadMilliseconds",
392
+ ] as const;
393
+ export type PerformanceBudgetDimension = (typeof PERFORMANCE_BUDGET_DIMENSIONS)[number];
394
+
395
+ const BUDGET_CEILING_FIELD: Record<
396
+ PerformanceBudgetDimension,
397
+ "maxTransferredBytes" | "maxAdditionalRequests" | "maxMainThreadMilliseconds"
398
+ > = {
399
+ transferredBytes: "maxTransferredBytes",
400
+ additionalRequests: "maxAdditionalRequests",
401
+ mainThreadMilliseconds: "maxMainThreadMilliseconds",
402
+ };
403
+
404
+ export interface PerformanceBudgetVerdict {
405
+ attribution: IntegrationAttribution;
406
+ overBudget: boolean;
407
+ /** Every dimension that exceeded its declared ceiling; empty ⇔ `overBudget: false`. */
408
+ exceeded: PerformanceBudgetDimension[];
409
+ }
410
+
411
+ /**
412
+ * Compare one measured {@link PerformanceSignal} against its integration's
413
+ * declared {@link PerformanceBudget}. Every dimension is checked (no
414
+ * short-circuit), so `exceeded` is complete.
415
+ */
416
+ export function evaluatePerformanceBudget(
417
+ signal: PerformanceSignal,
418
+ budget: PerformanceBudget,
419
+ ): PerformanceBudgetVerdict {
420
+ const exceeded: PerformanceBudgetDimension[] = [];
421
+ for (const dimension of PERFORMANCE_BUDGET_DIMENSIONS) {
422
+ if (signal.data[dimension] > budget[BUDGET_CEILING_FIELD[dimension]]) {
423
+ exceeded.push(dimension);
424
+ }
425
+ }
426
+ return { attribution: signal.attribution, overBudget: exceeded.length > 0, exceeded };
427
+ }
428
+
429
+ /**
430
+ * Evaluate a batch of performance signals. FAIL-CLOSED: an integration whose
431
+ * budget cannot be resolved (`budgetOf` returns `undefined` — an absent or
432
+ * unresolvable manifest) is flagged over-budget on every dimension rather than
433
+ * silently passed, so a missing declaration can never read as a clean bill of
434
+ * health.
435
+ */
436
+ export function evaluatePerformanceBudgets(
437
+ signals: readonly PerformanceSignal[],
438
+ budgetOf: (attribution: IntegrationAttribution) => PerformanceBudget | undefined,
439
+ ): PerformanceBudgetVerdict[] {
440
+ return signals.map((signal) => {
441
+ const budget = budgetOf(signal.attribution);
442
+ if (!budget) {
443
+ return {
444
+ attribution: signal.attribution,
445
+ overBudget: true,
446
+ exceeded: [...PERFORMANCE_BUDGET_DIMENSIONS],
447
+ };
448
+ }
449
+ return evaluatePerformanceBudget(signal, budget);
450
+ });
451
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Reserved render phases, authority tiers, and the contribution dependency DAG
3
+ * (ADR 0014 S1/S2 `integration-phases-fixed-dag`, I1 `integration-authority-
4
+ * four-tiers`, ADR 0016).
5
+ *
6
+ * Phases are a fixed, ordered set — there is no arbitrary selector or generic
7
+ * injection slot. Dependencies form a DAG within that order; cycles, unresolved
8
+ * references, duplicate edges, and any edge that would let a later-phase
9
+ * contribution outrank an earlier compliance phase are rejected.
10
+ */
11
+ import type { Violations } from "./result.js";
12
+ import { assertUnique } from "./result.js";
13
+
14
+ /** Reserved phases in fixed execution order. */
15
+ export const RESERVED_PHASES = [
16
+ "platform.environment", // renderer-owned environment block; not vendor-addressable
17
+ "consent.defaults", // compliance/consent baseline — cannot be outranked
18
+ "head.early", // early-head loader
19
+ "body.start", // body-start fallback markup
20
+ "document.deferred", // deferred global entry
21
+ ] as const;
22
+ export type ReservedPhase = (typeof RESERVED_PHASES)[number];
23
+
24
+ /** Phases a vendor entry or contribution may target. `platform.environment` is renderer-owned. */
25
+ export const VENDOR_ADDRESSABLE_PHASES = [
26
+ "consent.defaults",
27
+ "head.early",
28
+ "body.start",
29
+ "document.deferred",
30
+ ] as const;
31
+ export type VendorAddressablePhase = (typeof VENDOR_ADDRESSABLE_PHASES)[number];
32
+
33
+ /** Compliance-critical phases that always precede vendor code and cannot be outranked. */
34
+ export const COMPLIANCE_PHASES: readonly ReservedPhase[] = [
35
+ "platform.environment",
36
+ "consent.defaults",
37
+ ];
38
+
39
+ export function isReservedPhase(value: unknown): value is ReservedPhase {
40
+ return (
41
+ typeof value === "string" &&
42
+ (RESERVED_PHASES as readonly string[]).includes(value)
43
+ );
44
+ }
45
+
46
+ export function phaseRank(phase: ReservedPhase): number {
47
+ return RESERVED_PHASES.indexOf(phase);
48
+ }
49
+
50
+ /** Four authority tiers; declared DOM intent never masquerades as isolation (ADR 0016). */
51
+ export const EXECUTION_TIERS = [
52
+ "platform-adapter",
53
+ "sandbox",
54
+ "server",
55
+ "elevated-parent",
56
+ ] as const;
57
+ export type ExecutionTier = (typeof EXECUTION_TIERS)[number];
58
+
59
+ /** Contribution kinds. Only curated platform templates — no arbitrary markup slot. */
60
+ export const CONTRIBUTION_KINDS = ["platform-template"] as const;
61
+ export type ContributionKind = (typeof CONTRIBUTION_KINDS)[number];
62
+
63
+ /** Entry asset formats. Module-only keeps the strict CSP grammar (ADR 0019). */
64
+ export const ENTRY_FORMATS = ["module"] as const;
65
+ export type EntryFormat = (typeof ENTRY_FORMATS)[number];
66
+
67
+ /** A node in the contribution DAG: the implicit `entry` plus each contribution id. */
68
+ export interface DagNode {
69
+ id: string;
70
+ phase: ReservedPhase;
71
+ }
72
+
73
+ /** `dependent` runs after `prerequisite`. */
74
+ export interface DagEdge {
75
+ dependent: string;
76
+ prerequisite: string;
77
+ }
78
+
79
+ /**
80
+ * Fail-closed validation of the contribution DAG: unique ids, resolvable edge
81
+ * endpoints, no duplicate edges, no cycles, and no phase inversion (a dependent
82
+ * may not sit in an earlier phase than a prerequisite it runs after, which is
83
+ * how "compliance phases cannot be outranked" is enforced by construction).
84
+ */
85
+ export function validateContributionDag(
86
+ nodes: readonly DagNode[],
87
+ edges: readonly DagEdge[],
88
+ v: Violations,
89
+ path: string,
90
+ ): void {
91
+ assertUnique(
92
+ nodes.map((n) => n.id),
93
+ v,
94
+ `${path}.contributions`,
95
+ "contribution id",
96
+ );
97
+
98
+ const byId = new Map(nodes.map((n) => [n.id, n]));
99
+ const adjacency = new Map<string, string[]>();
100
+ const seenEdges = new Set<string>();
101
+
102
+ edges.forEach((edge, i) => {
103
+ const edgePath = `${path}.dependencies[${i}]`;
104
+ const dependent = byId.get(edge.dependent);
105
+ const prerequisite = byId.get(edge.prerequisite);
106
+ if (!dependent) {
107
+ v.add(edgePath, "unresolved_ref", `Unknown contribution "${edge.dependent}".`);
108
+ }
109
+ if (!prerequisite) {
110
+ v.add(edgePath, "unresolved_ref", `Unknown prerequisite "${edge.prerequisite}".`);
111
+ }
112
+ const key = `${edge.dependent}<-${edge.prerequisite}`;
113
+ if (seenEdges.has(key)) {
114
+ v.add(edgePath, "duplicate", "Duplicate dependency edge; duplicates are rejected.");
115
+ }
116
+ seenEdges.add(key);
117
+ if (dependent && prerequisite) {
118
+ if (phaseRank(dependent.phase) < phaseRank(prerequisite.phase)) {
119
+ v.add(
120
+ edgePath,
121
+ "phase_inversion",
122
+ `"${edge.dependent}" (${dependent.phase}) cannot run after later-phase ` +
123
+ `"${edge.prerequisite}" (${prerequisite.phase}); an earlier or compliance ` +
124
+ `phase cannot be outranked.`,
125
+ );
126
+ }
127
+ const list = adjacency.get(edge.dependent);
128
+ if (list) list.push(edge.prerequisite);
129
+ else adjacency.set(edge.dependent, [edge.prerequisite]);
130
+ }
131
+ });
132
+
133
+ const cycle = findCycle(
134
+ nodes.map((n) => n.id),
135
+ adjacency,
136
+ );
137
+ if (cycle) {
138
+ v.add(
139
+ `${path}.dependencies`,
140
+ "cycle",
141
+ `Dependency cycle: ${cycle.join(" -> ")}. The contribution graph must be acyclic.`,
142
+ );
143
+ }
144
+ }
145
+
146
+ function findCycle(
147
+ ids: readonly string[],
148
+ adjacency: Map<string, string[]>,
149
+ ): string[] | null {
150
+ const WHITE = 0;
151
+ const GRAY = 1;
152
+ const BLACK = 2;
153
+ const color = new Map<string, number>(ids.map((id) => [id, WHITE]));
154
+ const stack: string[] = [];
155
+
156
+ const visit = (node: string): string[] | null => {
157
+ color.set(node, GRAY);
158
+ stack.push(node);
159
+ for (const next of adjacency.get(node) ?? []) {
160
+ const state = color.get(next) ?? WHITE;
161
+ if (state === GRAY) {
162
+ const from = stack.indexOf(next);
163
+ return [...stack.slice(from), next];
164
+ }
165
+ if (state === WHITE) {
166
+ const found = visit(next);
167
+ if (found) return found;
168
+ }
169
+ }
170
+ stack.pop();
171
+ color.set(node, BLACK);
172
+ return null;
173
+ };
174
+
175
+ for (const id of ids) {
176
+ if ((color.get(id) ?? WHITE) === WHITE) {
177
+ const found = visit(id);
178
+ if (found) return found;
179
+ }
180
+ }
181
+ return null;
182
+ }