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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/apps/storefront/astro.config.mjs +3 -3
  2. package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +8 -3
  3. package/apps/storefront/env.d.ts +54 -5
  4. package/apps/storefront/integrations/materialize-guard.mjs +12 -14
  5. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  6. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  7. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  8. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  9. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  10. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  11. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  12. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  13. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  14. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  15. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  16. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  17. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  18. package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
  19. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  20. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  21. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  22. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  23. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  24. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  25. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  26. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  27. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  28. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  29. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  30. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  31. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  32. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  33. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  34. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  35. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  36. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  37. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  38. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  39. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  40. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  41. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  42. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  43. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  44. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  45. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  46. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  47. package/apps/storefront/src/components/Img.astro +1 -1
  48. package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
  49. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  50. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
  51. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  52. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  53. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  54. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  55. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  56. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  57. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  58. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  59. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  60. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  61. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  62. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  63. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  64. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  65. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  66. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  67. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  68. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  69. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  70. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  71. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  72. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  73. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  74. package/apps/storefront/src/config/resolver.ts +18 -10
  75. package/apps/storefront/src/layouts/Layout.astro +100 -20
  76. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  77. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
  78. package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
  79. package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
  80. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  81. package/apps/storefront/src/lib/apps/adminService.ts +10 -0
  82. package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
  83. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  84. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  85. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  86. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  87. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  88. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  89. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  90. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  91. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  92. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  93. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  94. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  95. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
  96. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
  97. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  98. package/apps/storefront/src/lib/apps/registryService.ts +264 -10
  99. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  100. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  101. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  102. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  103. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  104. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  105. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  106. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  107. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  108. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  109. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  110. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  111. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  112. package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
  113. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  114. package/apps/storefront/src/lib/blog/collection.ts +114 -17
  115. package/apps/storefront/src/lib/blog/markdown.ts +1 -1
  116. package/apps/storefront/src/lib/blog/provider.ts +76 -9
  117. package/apps/storefront/src/lib/blog/types.ts +9 -9
  118. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  119. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  120. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  121. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  122. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  123. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  124. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  125. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  126. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  127. package/apps/storefront/src/lib/content/index.ts +5 -5
  128. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  129. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  130. package/apps/storefront/src/lib/d1/catalog.ts +12 -0
  131. package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
  132. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  133. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  134. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  135. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  136. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  137. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  138. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  139. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  140. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  141. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  142. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  143. package/apps/storefront/src/lib/edgeCache.ts +18 -6
  144. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  145. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  146. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  147. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  148. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  149. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  150. package/apps/storefront/src/lib/membership/model.ts +1 -2
  151. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
  152. package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
  153. package/apps/storefront/src/lib/newsletter/send.ts +3 -3
  154. package/apps/storefront/src/lib/previewReload.ts +57 -1
  155. package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
  156. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  157. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  158. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  159. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  160. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  161. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  162. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  163. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  164. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  165. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  166. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  167. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  168. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  169. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  170. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  171. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  172. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  173. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  174. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  175. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  176. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  177. package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
  178. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  179. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  180. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  181. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  182. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  183. package/apps/storefront/src/lib/seo/alternates.ts +1 -1
  184. package/apps/storefront/src/lib/social/notify.ts +4 -4
  185. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  186. package/apps/storefront/src/lib/storyblok/provider.ts +203 -69
  187. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  188. package/apps/storefront/src/lib/the-build/provider.ts +9 -4
  189. package/apps/storefront/src/middleware/index.ts +273 -51
  190. package/apps/storefront/src/pages/[...slug].astro +9 -4
  191. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  192. package/apps/storefront/src/pages/admin/select.astro +96 -0
  193. package/apps/storefront/src/pages/admin.astro +16 -2
  194. package/apps/storefront/src/pages/api/activity.ts +8 -8
  195. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  196. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  197. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  198. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  199. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  200. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  201. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  202. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  203. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  204. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  205. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  206. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  207. package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
  208. package/apps/storefront/src/pages/blog/[slug].astro +5 -5
  209. package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
  210. package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
  211. package/apps/storefront/src/pages/blog/index.astro +2 -2
  212. package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
  213. package/apps/storefront/src/pages/cockpit.astro +61 -1362
  214. package/apps/storefront/src/pages/index.astro +10 -5
  215. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  216. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  217. package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
  218. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +42 -31
  219. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +8 -3
  220. package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +6 -2
  221. package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +6 -2
  222. package/apps/storefront/src/pages/style-guide/index.astro +19 -5
  223. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  224. package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
  225. package/apps/storefront/src/pages/the-build/index.astro +2 -2
  226. package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
  227. package/apps/storefront/src/styles/admin.css +27 -1
  228. package/apps/storefront/src/themes/tenants/index.ts +19 -7
  229. package/apps/storefront/tsconfig.json +1 -1
  230. package/package.json +1 -1
  231. package/packages/public-runtime/package.json +2 -1
  232. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  233. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  234. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  235. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  236. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  237. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  238. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  239. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  240. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  241. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  242. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  243. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  244. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  245. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  246. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  247. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  248. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  249. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  250. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  251. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  252. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  253. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  254. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  255. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  256. package/packages/public-runtime/src/activity/catalog.ts +41 -37
  257. package/packages/public-runtime/src/activity/event.ts +6 -6
  258. package/packages/public-runtime/src/activity/index.ts +2 -2
  259. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  260. package/packages/public-runtime/src/candidate-index.ts +42 -8
  261. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  262. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  263. package/packages/public-runtime/src/checkout-style.ts +7 -7
  264. package/packages/public-runtime/src/checkout.ts +3 -3
  265. package/packages/public-runtime/src/chrome.ts +777 -0
  266. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  267. package/packages/public-runtime/src/csp.ts +99 -0
  268. package/packages/public-runtime/src/customization-runtime.ts +32 -1
  269. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  270. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  271. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  272. package/packages/public-runtime/src/evidence.ts +288 -0
  273. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  274. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  275. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  276. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  277. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  278. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  279. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  280. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  281. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  282. package/packages/public-runtime/src/index.ts +16 -1
  283. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  284. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  285. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  286. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  287. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  288. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  289. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  290. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  291. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  292. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  293. package/packages/public-runtime/src/integration/csp.ts +105 -0
  294. package/packages/public-runtime/src/integration/digest.ts +43 -0
  295. package/packages/public-runtime/src/integration/environment.ts +228 -0
  296. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  297. package/packages/public-runtime/src/integration/events.ts +701 -0
  298. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  299. package/packages/public-runtime/src/integration/index.ts +90 -0
  300. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  301. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  302. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  303. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  304. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  305. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  306. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  307. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  308. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  309. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  310. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  311. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  312. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  313. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  314. package/packages/public-runtime/src/integration/observability.ts +451 -0
  315. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  316. package/packages/public-runtime/src/integration/phases.ts +182 -0
  317. package/packages/public-runtime/src/integration/plan.ts +112 -0
  318. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  319. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  320. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  321. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  322. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  323. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  324. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  325. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  326. package/packages/public-runtime/src/integration/result.ts +239 -0
  327. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  328. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  329. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  330. package/packages/public-runtime/src/integration/version.ts +92 -0
  331. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  332. package/packages/public-runtime/src/membership.ts +2 -2
  333. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  334. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  335. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  336. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  337. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  338. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  339. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  340. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  341. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  342. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  343. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  344. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  345. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  346. package/packages/public-runtime/src/static-bundle.ts +5 -5
  347. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  348. package/packages/public-runtime/src/tenant.ts +136 -9
  349. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  350. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  351. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  352. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  353. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  354. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  355. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  356. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  357. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  358. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  359. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  360. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  361. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  362. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  363. package/scripts/build/copy-tenant-assets.mjs +12 -5
  364. package/scripts/dev/ai-edit.mjs +2 -1
  365. package/scripts/dev/file-browser.mjs +2 -2
  366. package/scripts/dev/file-writer.mjs +2 -2
  367. package/scripts/dev/integration-guardrails.mjs +498 -0
  368. package/scripts/dev/safe-path.mjs +2 -2
  369. package/scripts/dev/tenant-source-root.mjs +3 -2
  370. package/scripts/tot-dev.mjs +5 -4
@@ -23,18 +23,29 @@ import {
23
23
  receiveForwardedOrder,
24
24
  type OrderForwardInput,
25
25
  } from "@/lib/apps/orders/orderForwardReceiver.js";
26
+ import { joinForwardedOrderConversion } from "@/lib/apps/orders/orderConversionD1.js";
27
+ import { D1VerifiedProviderMeasurementStore } from "@/lib/apps/orders/verifiedProviderMeasurement.js";
26
28
  import type { OrderLineItem, OrderStatus } from "@/lib/apps/orders/ordersStore.js";
27
29
  import { recordActivity } from "@/lib/activity/recordActivity";
28
30
 
29
31
  export const prerender = false;
30
32
 
31
33
  const SIGNATURE_HEADER = "x-order-forward-signature";
32
- const ORDER_STATUSES: readonly OrderStatus[] = ["created", "fulfilled", "cancelled"];
34
+ const ORDER_STATUSES: readonly OrderStatus[] = ["created", "fulfilled", "refunded", "cancelled"];
33
35
 
34
36
  function json(body: unknown, status: number): Response {
35
37
  return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
36
38
  }
37
39
 
40
+ /** Coarse, non-PII conversion-join diagnostic (never the order id or correlation token). */
41
+ function logConversionDiag(fields: Record<string, unknown>): void {
42
+ try {
43
+ console.warn("[diag:order-conversion]", JSON.stringify(fields));
44
+ } catch {
45
+ /* diagnostics must never break the caller */
46
+ }
47
+ }
48
+
38
49
  // --- HMAC verification -----------------------------------------------------
39
50
  // Same shape as `@tot/private-controlplane`'s `verifyGiteaSignature` (hex
40
51
  // HMAC-SHA256 over the raw body, verified via `crypto.subtle.verify` so a
@@ -85,6 +96,10 @@ interface OrderForwardBody {
85
96
  lineItems?: unknown;
86
97
  rawCustomerId?: unknown;
87
98
  createdAt?: unknown;
99
+ providerFields?: unknown;
100
+ providerEventId?: unknown;
101
+ occurredAt?: unknown;
102
+ amountMinor?: unknown;
88
103
  }
89
104
 
90
105
  function parseLineItems(value: unknown): OrderLineItem[] | string {
@@ -128,7 +143,16 @@ function parseOrderForwardInput(body: OrderForwardBody): OrderForwardInput | str
128
143
  return "rawCustomerId must be a string";
129
144
  }
130
145
  if (body.createdAt !== undefined && typeof body.createdAt !== "string") return "createdAt must be a string";
146
+ if (
147
+ body.providerFields !== undefined &&
148
+ (typeof body.providerFields !== "object" || body.providerFields === null || Array.isArray(body.providerFields))
149
+ ) {
150
+ return "providerFields must be an object";
151
+ }
131
152
 
153
+ if (body.providerEventId !== undefined && (typeof body.providerEventId !== "string" || !body.providerEventId.trim())) return "providerEventId must be a non-empty string";
154
+ if (body.occurredAt !== undefined && typeof body.occurredAt !== "string") return "occurredAt must be a string";
155
+ if (body.amountMinor !== undefined && (typeof body.amountMinor !== "number" || !Number.isSafeInteger(body.amountMinor) || body.amountMinor < 0)) return "amountMinor must be a non-negative safe integer";
132
156
  return {
133
157
  tenantId: body.tenantId,
134
158
  externalOrderId: body.externalOrderId,
@@ -140,6 +164,10 @@ function parseOrderForwardInput(body: OrderForwardBody): OrderForwardInput | str
140
164
  lineItems,
141
165
  rawCustomerId: body.rawCustomerId as string | undefined,
142
166
  createdAt: body.createdAt as string | undefined,
167
+ providerEventId: body.providerEventId as string | undefined,
168
+ occurredAt: body.occurredAt as string | undefined,
169
+ amountMinor: body.amountMinor as number | undefined,
170
+ providerFields: body.providerFields as Record<string, unknown> | undefined,
143
171
  };
144
172
  }
145
173
 
@@ -168,7 +196,51 @@ export async function POST(context: APIContext): Promise<Response> {
168
196
  const d1 = await readD1("STOREFRONT_APPS_DB");
169
197
  if (!d1) return json({ error: "orders store not configured" }, 503);
170
198
 
171
- const result = await receiveForwardedOrder(fromD1(d1), input);
199
+ const db = fromD1(d1);
200
+ const result = await receiveForwardedOrder(db, input);
201
+
202
+
203
+ // This local-only outbox runs only after raw-body HMAC verification and the
204
+ // trusted order receiver. Its failure is fail-soft: commerce still ACKs.
205
+ try {
206
+ const measurement = await new D1VerifiedProviderMeasurementStore(db).record(input);
207
+ if (measurement.outcome === "recorded") {
208
+ logConversionDiag({ tenantId: input.tenantId, env: input.env, regulatedMeasurement: "recorded" });
209
+ }
210
+ } catch (cause) {
211
+ logConversionDiag({
212
+ tenantId: input.tenantId,
213
+ env: input.env,
214
+ regulatedMeasurement: "outbox-unavailable",
215
+ error: cause instanceof Error ? cause.name : "unknown",
216
+ });
217
+ }
218
+
219
+ // Join the verified order to its browser handoff correlation and produce the
220
+ // authoritative server conversion event(s) — ext-commerce-05 destinations fan
221
+ // these out. The join owns idempotency + single-use replay defense; a failure
222
+ // here must never fail the order ACK, so it is fail-soft (logged, swallowed).
223
+ try {
224
+ const conversion = await joinForwardedOrderConversion(db, input, result);
225
+ if (conversion.events.length > 0) {
226
+ // Coarse, non-PII diagnostic: attribution outcome + conversion count only —
227
+ // never the order id, correlation token, or any customer field.
228
+ logConversionDiag({
229
+ tenantId: input.tenantId,
230
+ env: input.env,
231
+ conversions: conversion.events.length,
232
+ attributed: conversion.attribution.attributed,
233
+ reason: conversion.attribution.attributed ? undefined : conversion.attribution.reason,
234
+ });
235
+ }
236
+ } catch (cause) {
237
+ logConversionDiag({
238
+ tenantId: input.tenantId,
239
+ env: input.env,
240
+ error: cause instanceof Error ? cause.name : "unknown",
241
+ });
242
+ }
243
+
172
244
  // D11: HIGH-VOLUME machine action (the tot-foxycart forwarder, HMAC-authed) → analytics
173
245
  // only, never timelined. Coarse dimensions only; customer/order ids never stored.
174
246
  recordActivity({
@@ -0,0 +1,77 @@
1
+ /**
2
+ * POST /api/commerce/checkout-handoff — the checkout-correlation MINT seam
3
+ * (ext-commerce-10).
4
+ *
5
+ * The shopper's analytics consent is a CLIENT-side decision (CMP / native banner),
6
+ * but the correlation token is minted under the SERVER-only signing secret. So at the
7
+ * handoff the client posts its LIVE consent snapshot and the server mints the token,
8
+ * returning ONLY the opaque passthrough cart field to attach to the managed-cart form.
9
+ * The signing secret never leaves the server; the token carries no PII and no secret.
10
+ *
11
+ * Server-owned by construction:
12
+ * - The tenant and the {@link IntegrationEnvironment} stage come from `context.locals`
13
+ * (the renderer-owned `BrowserEnvironment`), NEVER from the request body — a client
14
+ * cannot mint a cross-tenant or cross-stage token.
15
+ * - The consent snapshot is parsed FAIL-CLOSED (`parseConsentSnapshot`): an absent,
16
+ * forged, or tampered snapshot collapses to default-deny → unattributed.
17
+ * - No store on this tenant, consent withheld, or no signing key → an explicit
18
+ * unattributed response; the handoff proceeds, checkout is never blocked.
19
+ */
20
+ import type { APIContext } from "astro";
21
+ import { parseConsentSnapshot, defaultConsentSnapshot } from "@tot/public-runtime";
22
+ import { resolveCheckoutHandoffCorrelation } from "@/lib/commerce/checkoutHandoffCorrelation";
23
+
24
+ export const prerender = false;
25
+
26
+ function json(body: unknown, status = 200): Response {
27
+ return new Response(JSON.stringify(body), {
28
+ status,
29
+ headers: {
30
+ "content-type": "application/json; charset=utf-8",
31
+ "cache-control": "no-store",
32
+ },
33
+ });
34
+ }
35
+
36
+ interface HandoffBody {
37
+ /** The shopper's serialized consent snapshot (the `__TOT_INTEGRATION_CONSENT__` global). */
38
+ consent?: unknown;
39
+ }
40
+
41
+ export async function POST(context: APIContext): Promise<Response> {
42
+ const { tenant, browserEnvironment, commerce } = context.locals;
43
+
44
+ // No managed checkout on this tenant → nothing to attribute.
45
+ if (!commerce) {
46
+ return json({ attributed: false, reason: "no-store" });
47
+ }
48
+
49
+ let body: HandoffBody;
50
+ try {
51
+ body = (await context.request.json()) as HandoffBody;
52
+ } catch {
53
+ body = {};
54
+ }
55
+
56
+ // Fail-closed: an absent/forged/tampered snapshot is default-deny (essential only).
57
+ const consent = parseConsentSnapshot(body.consent) ?? defaultConsentSnapshot();
58
+
59
+ const result = await resolveCheckoutHandoffCorrelation({
60
+ tenant: tenant.tenant_id,
61
+ // Env-consistency: the request's own resolved stage, never a client value.
62
+ environment: browserEnvironment.environment,
63
+ consent,
64
+ // Opaque per-handoff logical id the order join threads back to.
65
+ eventId: crypto.randomUUID(),
66
+ });
67
+
68
+ if (result.attributed) {
69
+ // Return ONLY the opaque passthrough field; the signing secret never leaves here.
70
+ return json({ attributed: true, cartField: result.cartField });
71
+ }
72
+ return json({ attributed: false, reason: result.reason });
73
+ }
74
+
75
+ export async function GET(): Promise<Response> {
76
+ return json({ error: "method_not_allowed" }, 405);
77
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * POST /api/compliance/tamper — the server receiver for protected-surface tamper
3
+ * beacons (D-R7, `integration-protected-tamper-recovery`).
4
+ *
5
+ * The browser sentinel restores safe state IN the document and posts a
6
+ * `TamperEvidenceBeacon` here. This route validates it fail-closed, records the
7
+ * durable evidence + operator alert, and — when the beacon is attributable to a
8
+ * single active install — SUSPENDS that install. It is a DETECTIVE control: the
9
+ * suspension is after-the-fact recovery, never prevention.
10
+ *
11
+ * TENANT SCOPE is server-owned: the install lookup and suspend are ALWAYS keyed on
12
+ * `locals.tenant` (middleware-resolved from the request host), never on the
13
+ * body-supplied `tenant` — a page cannot ask to suspend another tenant's app. The
14
+ * beacon body carries no secret (the contract rejects one), and the response is a
15
+ * write-path ack the emitter ignores, so a failed post never breaks the page.
16
+ */
17
+ import type { APIContext } from "astro";
18
+ import { getRegistryService, json } from "@/lib/apps/adminService.js";
19
+ import type { AppInstall } from "@/lib/apps/registryService.js";
20
+ import { RegistryError } from "@/lib/apps/registryService.js";
21
+ import { planTamperRecovery, type AttributableInstall } from "@tot/public-runtime";
22
+
23
+ export const prerender = false;
24
+
25
+ const SYSTEM_ACTOR = "system:tamper-sentinel";
26
+
27
+ function toAttributable(install: AppInstall): AttributableInstall {
28
+ return {
29
+ installId: install.installId,
30
+ appId: install.appId,
31
+ appVersion: install.appVersion,
32
+ status: install.status,
33
+ };
34
+ }
35
+
36
+ export async function POST(context: APIContext): Promise<Response> {
37
+ const tenantId = context.locals.tenant?.tenant_id;
38
+ // No resolved tenant ⇒ nothing third-party could be scoped to; accept-and-drop so
39
+ // the emitter never errors or retries.
40
+ if (!tenantId) return json({ ok: true, accepted: false }, 202);
41
+
42
+ let body: unknown;
43
+ try {
44
+ body = await context.request.json();
45
+ } catch {
46
+ return json({ ok: false, error: "invalid JSON body" }, 400);
47
+ }
48
+
49
+ // Resolve the tenant's installs (empty when the registry binding isn't configured
50
+ // in this runtime — the beacon is still validated + recorded, just not actioned).
51
+ const registry = await getRegistryService();
52
+ const installs: AttributableInstall[] =
53
+ registry instanceof Response
54
+ ? []
55
+ : (await registry.listByTenant(tenantId)).map(toAttributable);
56
+
57
+ const plan = planTamperRecovery(body, installs, {
58
+ recordId: crypto.randomUUID(),
59
+ receivedAt: new Date().toISOString(),
60
+ });
61
+ if (!plan.ok) return json({ ok: false, error: "invalid tamper beacon" }, 400);
62
+
63
+ const { record, suspend } = plan.plan;
64
+ // Durable, secret-free evidence line for operations. The beacon itself is the record.
65
+ // eslint-disable-next-line no-console
66
+ console.warn(
67
+ `[tamper] tenant=${tenantId} record=${record.recordId} surface=${record.beacon.surface} kind=${record.beacon.kind} recovered=${record.beacon.recovered} app=${record.beacon.attribution?.appId ?? "unattributable"}`,
68
+ );
69
+
70
+ let suspended = false;
71
+ if (suspend && !(registry instanceof Response)) {
72
+ try {
73
+ await registry.suspend(suspend.installId, SYSTEM_ACTOR);
74
+ suspended = true;
75
+ } catch (err) {
76
+ // An already-suspended/uninstalled install (invalid_state) is a benign
77
+ // no-op under rapid re-mutation; anything else is swallowed so the write
78
+ // path never errors the emitter. The audit row from a successful suspend is
79
+ // the durable trail.
80
+ if (!(err instanceof RegistryError)) {
81
+ // eslint-disable-next-line no-console
82
+ console.error(`[tamper] suspend failed for ${suspend.installId}`, err);
83
+ }
84
+ }
85
+ }
86
+
87
+ return json({ ok: true, accepted: true, recovered: record.beacon.recovered, suspended }, 202);
88
+ }
@@ -0,0 +1,108 @@
1
+ /** Published managed analytics v1 RPC transport: basePath + client method name. */
2
+ import type { APIContext } from "astro";
3
+ import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
4
+ import { getManagedAnalyticsService, json, requireManageRole } from "@/lib/apps/adminService.js";
5
+ import {
6
+ validateCreateIntegrationInstallRequest, validateCreateMigrationDraftRequest,
7
+ validateDeleteIntegrationInstallRequest, validateDeleteMigrationDraftRequest,
8
+ validateGetAdapterDefinitionRequest, validateGetIntegrationInstallRequest,
9
+ validateGetIntegrationRevisionRequest, validateGetMigrationDraftRequest,
10
+ validateListAdapterDefinitionsRequest, validateListIntegrationInstallsRequest,
11
+ validateListIntegrationRevisionsRequest, validateReviseIntegrationInstallRequest,
12
+ validateSetIntegrationInstallStatusRequest, validateUpdateMigrationDraftRequest,
13
+ type CreateIntegrationInstallRequest, type CreateMigrationDraftRequest,
14
+ type DeleteIntegrationInstallRequest, type DeleteMigrationDraftRequest,
15
+ type GetAdapterDefinitionRequest, type GetIntegrationInstallRequest,
16
+ type GetIntegrationRevisionRequest, type GetMigrationDraftRequest,
17
+ type ListAdapterDefinitionsRequest, type ListIntegrationInstallsRequest,
18
+ type ListIntegrationRevisionsRequest, type ReviseIntegrationInstallRequest,
19
+ type SetIntegrationInstallStatusRequest, type UpdateMigrationDraftRequest,
20
+ } from "@tot/public-runtime";
21
+ import { MANAGED_ANALYTICS_READ_OPERATIONS, managedAnalyticsOpaqueId, sanitizeManagedAnalyticsOutbound, type ManagedAnalyticsOperation } from "@/lib/apps/managedAnalyticsService.js";
22
+
23
+ export const prerender = false;
24
+ type Validator = (input: unknown) => { ok: true; value: unknown } | { ok: false; violations: Array<{ path: string; code: string; message: string }> };
25
+
26
+ // Null-prototype + own-property lookup: `constructor`/prototype names can never
27
+ // enter dispatch, even if a runtime supplies them as a dynamic path segment.
28
+ const VALIDATORS: Readonly<Record<string, Validator>> = Object.assign(Object.create(null) as Record<string, Validator>, {
29
+ listAdapterDefinitions: validateListAdapterDefinitionsRequest,
30
+ getAdapterDefinition: validateGetAdapterDefinitionRequest,
31
+ listIntegrationInstalls: validateListIntegrationInstallsRequest,
32
+ getIntegrationInstall: validateGetIntegrationInstallRequest,
33
+ listIntegrationRevisions: validateListIntegrationRevisionsRequest,
34
+ getIntegrationRevision: validateGetIntegrationRevisionRequest,
35
+ getMigrationDraft: validateGetMigrationDraftRequest,
36
+ createMigrationDraft: validateCreateMigrationDraftRequest,
37
+ updateMigrationDraft: validateUpdateMigrationDraftRequest,
38
+ deleteMigrationDraft: validateDeleteMigrationDraftRequest,
39
+ createIntegrationInstall: validateCreateIntegrationInstallRequest,
40
+ reviseIntegrationInstall: validateReviseIntegrationInstallRequest,
41
+ setIntegrationInstallStatus: validateSetIntegrationInstallStatusRequest,
42
+ deleteIntegrationInstall: validateDeleteIntegrationInstallRequest,
43
+ });
44
+
45
+ const STATUS: Record<string, number> = {
46
+ invalid_request: 400, unauthorized: 401, forbidden: 403, not_found: 404,
47
+ validation_failed: 422, unsupported_version: 400, stale_revision: 409,
48
+ idempotency_conflict: 409, adapter_deprecated: 409, conflict: 409,
49
+ internal_error: 500,
50
+ };
51
+ function response(body: unknown): Response {
52
+ const code = body && typeof body === "object" && "error" in body
53
+ ? (body as { error?: { code?: string } }).error?.code : undefined;
54
+ return json(body, code ? STATUS[code] ?? 500 : 200);
55
+ }
56
+ function routeError(code: string, message: string, details?: Array<{ path: string; code: string; message: string }>): Response {
57
+ return response({ apiVersion: "v1", requestId: managedAnalyticsOpaqueId("req"), error: { code, message, ...(details?.length ? { details } : {}) } });
58
+ }
59
+ function operationResponse(operation: ManagedAnalyticsOperation, body: unknown): Response {
60
+ const safe = sanitizeManagedAnalyticsOutbound(operation, body);
61
+ return safe === null ? routeError("internal_error", "The integration service could not complete the request.") : response(safe);
62
+ }
63
+
64
+ export async function POST(context: APIContext): Promise<Response> {
65
+ try {
66
+ const operation = context.params.operation;
67
+ if (!operation || !Object.prototype.hasOwnProperty.call(VALIDATORS, operation)) {
68
+ return routeError("invalid_request", "Unknown managed analytics v1 operation.");
69
+ }
70
+ const op = operation as ManagedAnalyticsOperation;
71
+ const url = new URL(context.request.url);
72
+ const auth = await resolveOperatorSession(context, url.searchParams.get("tenant"));
73
+ if (!auth.ok) return routeError(auth.status === 401 ? "unauthorized" : "forbidden", "The request is not authorized.");
74
+ if (!MANAGED_ANALYTICS_READ_OPERATIONS.has(op) && requireManageRole(auth)) {
75
+ return routeError("forbidden", "The request is not authorized.");
76
+ }
77
+ let raw: unknown;
78
+ try { raw = await context.request.json(); }
79
+ catch { return routeError("invalid_request", "The request body must be valid JSON."); }
80
+ const parsed = VALIDATORS[operation]!(raw);
81
+ if (!parsed.ok) {
82
+ const unsupportedVersion = parsed.violations.some(({ path, code }) => path === "apiVersion" && code === "unsupported_value");
83
+ return routeError(unsupportedVersion ? "unsupported_version" : "invalid_request", unsupportedVersion ? "The API version is not supported." : "The request failed validation.", parsed.violations);
84
+ }
85
+ const service = await getManagedAnalyticsService();
86
+ if (service instanceof Response) return routeError("internal_error", "The integration service is unavailable.");
87
+ const ctx = { tenantId: auth.tenantId, actor: auth.actor };
88
+ const request = parsed.value;
89
+ switch (op) {
90
+ case "listAdapterDefinitions": return operationResponse(op, service.listAdapterDefinitions(request as ListAdapterDefinitionsRequest));
91
+ case "getAdapterDefinition": return operationResponse(op, service.getAdapterDefinition((request as GetAdapterDefinitionRequest).query.adapterId));
92
+ case "listIntegrationInstalls": return operationResponse(op, await service.listIntegrationInstalls(ctx, request as ListIntegrationInstallsRequest));
93
+ case "getIntegrationInstall": return operationResponse(op, await service.getIntegrationInstall(ctx, (request as GetIntegrationInstallRequest).query.installId));
94
+ case "listIntegrationRevisions": { const value = request as ListIntegrationRevisionsRequest; return operationResponse(op, await service.listIntegrationRevisions(ctx, value.query.installId, value.query)); }
95
+ case "getIntegrationRevision": { const value = request as GetIntegrationRevisionRequest; return operationResponse(op, await service.getIntegrationRevision(ctx, value.query.installId, value.query.revisionId)); }
96
+ case "getMigrationDraft": return operationResponse(op, await service.getMigrationDraft(ctx, (request as GetMigrationDraftRequest).query.draftId));
97
+ case "createMigrationDraft": return operationResponse(op, await service.createMigrationDraft(ctx, request as CreateMigrationDraftRequest));
98
+ case "updateMigrationDraft": return operationResponse(op, await service.updateMigrationDraft(ctx, request as UpdateMigrationDraftRequest));
99
+ case "deleteMigrationDraft": return operationResponse(op, await service.deleteMigrationDraft(ctx, request as DeleteMigrationDraftRequest));
100
+ case "createIntegrationInstall": return operationResponse(op, await service.createIntegrationInstall(ctx, request as CreateIntegrationInstallRequest));
101
+ case "reviseIntegrationInstall": return operationResponse(op, await service.reviseIntegrationInstall(ctx, request as ReviseIntegrationInstallRequest));
102
+ case "setIntegrationInstallStatus": return operationResponse(op, await service.setIntegrationInstallStatus(ctx, request as SetIntegrationInstallStatusRequest));
103
+ case "deleteIntegrationInstall": return operationResponse(op, await service.deleteIntegrationInstall(ctx, request as DeleteIntegrationInstallRequest));
104
+ }
105
+ } catch {
106
+ return routeError("internal_error", "The integration service could not complete the request.");
107
+ }
108
+ }
@@ -60,8 +60,7 @@ export const POST: APIRoute = async (context) => {
60
60
  const kv = tenantId ? await readKv("TENANT_CACHE") : undefined;
61
61
  if (tenantId && kv) {
62
62
  const cfContext =
63
- (context.locals as { runtime?: { ctx?: CfExecutionContextLike } })?.runtime?.ctx ??
64
- null;
63
+ (context.locals as { cfContext?: CfExecutionContextLike })?.cfContext ?? null;
65
64
  runAfterResponse(recordBlogView(kv, tenantId, sample.path), cfContext);
66
65
  }
67
66
  }
@@ -11,11 +11,11 @@ import { buildAlternates } from "@/lib/seo/alternates.js";
11
11
  import { languageLabel } from "@/lib/i18n.js";
12
12
  import { breadcrumbLd, blogPostingLd } from "@/lib/jsonld.js";
13
13
 
14
- const { tenant, canonicalBase, basePath } = Astro.locals;
14
+ const { tenant, canonicalBase, basePath, content } = Astro.locals;
15
15
  const slug = Astro.params.slug ?? "";
16
- const post = await getBlogPost(tenant.tenant_id, slug);
16
+ const post = await getBlogPost(tenant.tenant_id, slug, content);
17
17
  if (!post) {
18
- const redirect = await resolveBlogRedirect(tenant.tenant_id, slug);
18
+ const redirect = await resolveBlogRedirect(tenant.tenant_id, slug, content);
19
19
  if (redirect) return Astro.redirect(`${basePath}/blog/${redirect}/`, 301);
20
20
  return new Response(null, { status: 404 });
21
21
  }
@@ -38,7 +38,7 @@ const canonical = new URL(`/blog/${post.slug}/`, canonicalBase + "/").toString()
38
38
  // (the invariant `blog/author/[author].astro` documents). Same slug + same source
39
39
  // the archive itself filters on, so the two can't disagree.
40
40
  const authorArchiveSlug = authorSlug(post.author);
41
- const authorHasArchive = (await listBlogPosts(tenant.tenant_id)).some(
41
+ const authorHasArchive = (await listBlogPosts(tenant.tenant_id, content)).some(
42
42
  (p) => authorSlug(p.author) === authorArchiveSlug,
43
43
  );
44
44
  const authorUrl = authorHasArchive
@@ -63,7 +63,7 @@ const authorHref = authorHasArchive
63
63
  // I2 — hreflang alternates + per-variant <html lang>. Singleton ⇒ [] alternates
64
64
  // and no `lang` override, so a non-translated post is byte-identical to today.
65
65
  const translations = post.translationGroupId
66
- ? await getTranslations(tenant.tenant_id, "blog", post.slug)
66
+ ? await getTranslations(tenant.tenant_id, "blog", post.slug, { content })
67
67
  : [];
68
68
  const alternates = buildAlternates(translations, (s) =>
69
69
  new URL(`/blog/${s}/`, canonicalBase + "/").toString(),
@@ -8,15 +8,15 @@ import { breadcrumbLd } from "@/lib/jsonld.js";
8
8
  // Author archive for the `/blog` collection. Lists exactly the posts whose
9
9
  // bylines link here (same source as /blog/index.astro + /blog/[slug].astro),
10
10
  // so a byline never points at a page missing its post.
11
- const { tenant, basePath, canonicalBase } = Astro.locals;
11
+ const { tenant, basePath, canonicalBase, content } = Astro.locals;
12
12
  const slug = Astro.params.author ?? "";
13
13
 
14
- const allPosts = await listBlogPosts(tenant.tenant_id);
14
+ const allPosts = await listBlogPosts(tenant.tenant_id, content);
15
15
  const posts = allPosts.filter((post) => authorSlug(post.author) === slug);
16
16
  if (posts.length === 0) return new Response(null, { status: 404 });
17
17
 
18
18
  const authorName = posts[0]!.author;
19
- const bio = (await getAuthors(tenant.tenant_id, "blog"))[slug];
19
+ const bio = (await getAuthors(tenant.tenant_id, "blog", content))[slug];
20
20
 
21
21
  const href = (path: string) => `${basePath}${path}` || "/";
22
22
  const canonical = new URL(`/blog/author/${slug}/`, canonicalBase + "/").toString();
@@ -5,12 +5,12 @@ import { listBlogPosts } from "@/lib/blog/provider.js";
5
5
  import { collectPostsByTerm } from "@/lib/blog/collection.js";
6
6
  import { breadcrumbLd } from "@/lib/jsonld.js";
7
7
 
8
- const { tenant, basePath, canonicalBase } = Astro.locals;
8
+ const { tenant, basePath, canonicalBase, content } = Astro.locals;
9
9
  const param = Astro.params.category ?? "";
10
10
 
11
11
  // Same source as the /blog index (the collection's curated list), filtered to
12
12
  // one category — so a post can never appear here that isn't already live on /blog.
13
- const all = await listBlogPosts(tenant.tenant_id);
13
+ const all = await listBlogPosts(tenant.tenant_id, content);
14
14
  const match = collectPostsByTerm(all, "categories", param);
15
15
  if (!match) return new Response(null, { status: 404 });
16
16
  const { label, posts } = match;
@@ -3,8 +3,8 @@ import Layout from "@/layouts/Layout.astro";
3
3
  import PostAccessBadge from "@/components/blog/PostAccessBadge.astro";
4
4
  import { listBlogPosts } from "@/lib/blog/provider.js";
5
5
 
6
- const { tenant, basePath, canonicalBase } = Astro.locals;
7
- const posts = await listBlogPosts(tenant.tenant_id);
6
+ const { tenant, basePath, canonicalBase, content } = Astro.locals;
7
+ const posts = await listBlogPosts(tenant.tenant_id, content);
8
8
  if (posts.length === 0) return new Response(null, { status: 404 });
9
9
 
10
10
  const href = (path: string) => `${basePath}${path}` || "/";
@@ -5,10 +5,10 @@ import { listBlogPosts } from "@/lib/blog/provider.js";
5
5
  import { collectPostsByTerm } from "@/lib/blog/collection.js";
6
6
  import { breadcrumbLd } from "@/lib/jsonld.js";
7
7
 
8
- const { tenant, basePath, canonicalBase } = Astro.locals;
8
+ const { tenant, basePath, canonicalBase, content } = Astro.locals;
9
9
  const param = Astro.params.tag ?? "";
10
10
 
11
- const all = await listBlogPosts(tenant.tenant_id);
11
+ const all = await listBlogPosts(tenant.tenant_id, content);
12
12
  const match = collectPostsByTerm(all, "tags", param);
13
13
  if (!match) return new Response(null, { status: 404 });
14
14
  const { label, posts } = match;