@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
@@ -27,6 +27,14 @@ export const appInstalls = sqliteTable(
27
27
  webhookEndpoint: text("webhook_endpoint"),
28
28
  widgetPlacements: text("widget_placements").notNull().default("[]"),
29
29
  installActor: text("install_actor").notNull(),
30
+ // Nullable discriminator keeps the managed-analytics v1 lifecycle on the
31
+ // canonical install row instead of creating a second install authority.
32
+ managedKind: text("managed_kind"),
33
+ managedRevision: integer("managed_revision").notNull().default(0),
34
+ managedActiveRevisionId: text("managed_active_revision_id"),
35
+ // Validated non-live candidate only. Live effects remain in manifest and
36
+ // app_version_snapshots, the registry's established serving authorities.
37
+ managedCandidate: text("managed_candidate"),
30
38
  createdAt: text("created_at").notNull(),
31
39
  updatedAt: text("updated_at").notNull(),
32
40
  },
@@ -70,6 +78,23 @@ export const appAuditLog = sqliteTable(
70
78
  }),
71
79
  );
72
80
 
81
+ // Inert reviewed migration suggestions. This is deliberately NOT an install,
82
+ // live effect, revision, or publish authority; conversion requires a separate
83
+ // canonical `app_installs` mutation and drafts can never be enabled.
84
+ export const integrationMigrationDrafts = sqliteTable(
85
+ "integration_migration_drafts",
86
+ {
87
+ draftId: text("draft_id").primaryKey(),
88
+ tenantId: text("tenant_id").notNull(),
89
+ revision: integer("revision").notNull(),
90
+ state: text("state").notNull().default("open"),
91
+ record: text("record").notNull(),
92
+ createdAt: text("created_at").notNull(),
93
+ updatedAt: text("updated_at").notNull(),
94
+ },
95
+ (t) => ({ idxIntegrationMigrationDraftsTenant: index("idx_integration_migration_drafts_tenant").on(t.tenantId, t.createdAt) }),
96
+ );
97
+
73
98
  // --- Operational activity timeline ------------------------------------------
74
99
  // The low-volume / high-value LIFECYCLE cohort of the actor×action telemetry
75
100
  // spine (DECISIONS.md §8). Only actions whose catalog `spec.sinks` include
@@ -344,3 +369,254 @@ export const newsletterSends = sqliteTable(
344
369
  },
345
370
  (t) => ({ pk: primaryKey({ columns: [t.tenantId, t.collection, t.digestId] }) }),
346
371
  );
372
+
373
+ // --- Canonical publish-candidate identity -----------------------------------
374
+ // ONE canonical candidate per `(tenant_id, repository, pr_number)` — the real
375
+ // forge PR. Retries/rebuilds are NOT new candidates; they are child `attempts`
376
+ // (below). This is the durable identity spine other units resolve against; the
377
+ // KV `ReviewEnvironment` (`cust:*`) and aggregate/release models (`rel:*`) are
378
+ // read projections that reference this row's `change_id`/`pr_number`.
379
+ //
380
+ // `id` is a SYNTHETIC surrogate (`cand_<uuid>`), deliberately DISTINCT from the
381
+ // canonical key so foreign keys (attempts) never depend on the mutable natural
382
+ // key. `change_id` is the REAL forge change id — the stable branch/build
383
+ // identity (`candidate/<change_id>`) — preserved, never derived from `id`.
384
+ // `tenant_id` is the isolation boundary, written from the resolved session,
385
+ // NEVER a client body. The UNIQUE `(tenant_id, repository, pr_number)` is what
386
+ // makes "one candidate per PR" a schema invariant (upsert-by-key, not
387
+ // insert-then-dedupe); the UNIQUE `(tenant_id, change_id)` keeps the branch/
388
+ // build identity unambiguous for resolve-by-changeId callers.
389
+ export const candidates = sqliteTable(
390
+ "candidates",
391
+ {
392
+ id: text("id").primaryKey(),
393
+ tenantId: text("tenant_id").notNull(),
394
+ // The `<tenant>-<tag>` forge repository the PR lives in.
395
+ repository: text("repository").notNull(),
396
+ // The forge PR number — the third leg of the canonical key.
397
+ prNumber: integer("pr_number").notNull(),
398
+ // The real forge change id — stable branch/build identity (`candidate/<change_id>`).
399
+ changeId: text("change_id").notNull(),
400
+ // Source branch, conventionally `candidate/<change_id>` (nullable convenience mirror).
401
+ branch: text("branch"),
402
+ title: text("title"),
403
+ author: text("author"),
404
+ // Canonical candidate lifecycle: open | preparing | ready | needs-attention | accepted | rejected | closed.
405
+ status: text("status").notNull().default("open"),
406
+ // The PR head/base shas as last reconciled (nullable until first read).
407
+ headSha: text("head_sha"),
408
+ baseSha: text("base_sha"),
409
+ createdAt: text("created_at").notNull(),
410
+ updatedAt: text("updated_at").notNull(),
411
+ },
412
+ (t) => ({
413
+ uqCandidatesCanonical: uniqueIndex("uq_candidates_tenant_repo_pr").on(
414
+ t.tenantId,
415
+ t.repository,
416
+ t.prNumber,
417
+ ),
418
+ uqCandidatesChange: uniqueIndex("uq_candidates_tenant_change").on(t.tenantId, t.changeId),
419
+ idxCandidatesTenantStatus: index("idx_candidates_tenant_status").on(t.tenantId, t.status),
420
+ }),
421
+ );
422
+
423
+ // One `attempt` = one build/check/rebase EXECUTION of a candidate. Attempts are
424
+ // the child rows retries produce — they must NEVER queue as separate top-level
425
+ // candidate rows. `candidate_id` references `candidates.id` in code only (no SQL
426
+ // FK — repo convention, matches `webhook_deliveries.event_id` / `orders`).
427
+ // `tenant_id` is denormalized for tenant-scoped reads/isolation without a join.
428
+ // `attempt_number` is monotonic per candidate (1,2,3…) giving stable ordering;
429
+ // UNIQUE `(candidate_id, attempt_number)` makes a double-assigned number a loud
430
+ // failure rather than a silent duplicate. `kind`: build | check | rebase;
431
+ // `status`: pending | running | passed | failed | cancelled (mirrors
432
+ // `IntegrationRun`'s state vocabulary). `evidence` is a JSON EvidenceReport
433
+ // snapshot; `detail` a human-legible terminal-state message.
434
+ export const candidateAttempts = sqliteTable(
435
+ "candidate_attempts",
436
+ {
437
+ id: text("id").primaryKey(),
438
+ candidateId: text("candidate_id").notNull(),
439
+ tenantId: text("tenant_id").notNull(),
440
+ attemptNumber: integer("attempt_number").notNull(),
441
+ kind: text("kind").notNull(),
442
+ status: text("status").notNull().default("pending"),
443
+ // The candidate head sha this attempt executed against, or null if unknown.
444
+ headSha: text("head_sha"),
445
+ detail: text("detail"),
446
+ evidence: text("evidence"),
447
+ startedAt: text("started_at").notNull(),
448
+ // Null while pending/running; set on a terminal transition.
449
+ finishedAt: text("finished_at"),
450
+ },
451
+ (t) => ({
452
+ uqAttemptNumber: uniqueIndex("uq_candidate_attempts_candidate_number").on(
453
+ t.candidateId,
454
+ t.attemptNumber,
455
+ ),
456
+ idxAttemptsCandidate: index("idx_candidate_attempts_candidate").on(t.candidateId),
457
+ idxAttemptsTenant: index("idx_candidate_attempts_tenant").on(t.tenantId),
458
+ }),
459
+ );
460
+
461
+ // One active submit per tenant/change. This serializes identity authorization,
462
+ // the Gitea mutation, and audit finalization so a verified callback cannot
463
+ // become stale between its forge read and its KV writes. `expires_at` is
464
+ // diagnostic only; recovery is explicit so a slow live submit cannot be
465
+ // time-fenced incorrectly.
466
+ export const candidateSubmitLeases = sqliteTable(
467
+ "candidate_submit_leases",
468
+ {
469
+ tenantId: text("tenant_id").notNull(),
470
+ changeId: text("change_id").notNull(),
471
+ authorizationId: text("authorization_id").notNull(),
472
+ status: text("status").notNull().default("active"),
473
+ acquiredAt: text("acquired_at").notNull(),
474
+ expiresAt: text("expires_at").notNull(),
475
+ },
476
+ (t) => ({
477
+ pk: primaryKey({ columns: [t.tenantId, t.changeId] }),
478
+ idxExpires: index("idx_candidate_submit_leases_expires").on(t.expiresAt),
479
+ }),
480
+ );
481
+
482
+ // --- Merchant consent configuration -----------------------------------------
483
+ // APPEND-ONLY revision history of a tenant's consent configuration (the
484
+ // ext-admin-03 surface that feeds the ext-policy-03 broker). Each save is a NEW
485
+ // immutable row — the current config is `MAX(revision)` per tenant — so the table
486
+ // IS the audit trail (actor + timestamp per revision, never mutated in place) and
487
+ // a revert is a forward-only new revision that copies a prior `config` and records
488
+ // `reverted_from`. `config` is the JSON `TenantConsentConfig` (never secret-bearing
489
+ // — consent config carries no secrets). `tenant_id` is the isolation boundary,
490
+ // written from the resolved session, never a client body. UNIQUE
491
+ // `(tenant_id, revision)` makes the monotonic revision a schema invariant.
492
+ export const consentConfigRevisions = sqliteTable(
493
+ "consent_config_revisions",
494
+ {
495
+ id: text("id").primaryKey(),
496
+ tenantId: text("tenant_id").notNull(),
497
+ // Monotonic per tenant (1, 2, 3…) — the latest revision is the current config.
498
+ revision: integer("revision").notNull(),
499
+ // The JSON-serialized validated `TenantConsentConfig`.
500
+ config: text("config").notNull(),
501
+ // Opaque actor id — never email/PII (matches `app_audit_log`).
502
+ actor: text("actor").notNull(),
503
+ // Human-legible note (e.g. the reason, or "revert to revision N").
504
+ note: text("note"),
505
+ // When this revision copies an earlier one, the source revision — else null.
506
+ revertedFrom: integer("reverted_from"),
507
+ createdAt: text("created_at").notNull(),
508
+ },
509
+ (t) => ({
510
+ uqConsentConfigTenantRevision: uniqueIndex("uq_consent_config_tenant_revision").on(
511
+ t.tenantId,
512
+ t.revision,
513
+ ),
514
+ idxConsentConfigTenant: index("idx_consent_config_tenant").on(t.tenantId),
515
+ }),
516
+ );
517
+
518
+ // Demonstrated CMP demand — one row per `(tenant, adapter)` demand signal. The set
519
+ // of SELECTABLE CMP adapters is DERIVED from these rows (distinct-tenant count over
520
+ // a threshold), never a hardcoded catalog: the first CMP Storefront adapts is
521
+ // whichever real demand first proves (ADR 0014 P2 / 0025). UNIQUE
522
+ // `(tenant_id, adapter_id)` makes demand a per-tenant fact (a tenant re-asking does
523
+ // not inflate the count); the `adapter_id` index serves the per-adapter rollup.
524
+ export const consentCmpDemand = sqliteTable(
525
+ "consent_cmp_demand",
526
+ {
527
+ id: text("id").primaryKey(),
528
+ tenantId: text("tenant_id").notNull(),
529
+ adapterId: text("adapter_id").notNull(),
530
+ // Where the demand came from (a merchant request, a support ticket), for the record.
531
+ source: text("source"),
532
+ at: text("at").notNull(),
533
+ },
534
+ (t) => ({
535
+ uqConsentCmpDemandTenantAdapter: uniqueIndex("uq_consent_cmp_demand_tenant_adapter").on(
536
+ t.tenantId,
537
+ t.adapterId,
538
+ ),
539
+ idxConsentCmpDemandAdapter: index("idx_consent_cmp_demand_adapter").on(t.adapterId),
540
+ }),
541
+ );
542
+
543
+ // --- Commerce conversion attribution (ext-commerce-04) -----------------------
544
+ // The single-use ledger for the signed checkout-correlation token's `jti`
545
+ // (`packages/public-runtime/src/integration/correlation-token.ts`). The order join
546
+ // consumes a token EXACTLY once: a replay of a consumed `jti` is refused. `jti` is
547
+ // the natural key (the token's own 96-bit nonce); `expires_at` is the token's Unix-
548
+ // seconds `exp` so a consumed row can be pruned once the token is already `expired`
549
+ // (a past-`exp` token is rejected before the guard is even reached). Carries no PII
550
+ // and no secret — the `jti` is an opaque nonce, never the token or the signing key.
551
+ export const correlationReplay = sqliteTable("correlation_replay", {
552
+ jti: text("jti").primaryKey(),
553
+ expiresAt: integer("expires_at").notNull(),
554
+ consumedAt: text("consumed_at").notNull(),
555
+ });
556
+
557
+ // --- Retained managed-app version facets (ext-apps-02) -----------------------
558
+ // APPEND-ONLY retention of every activated managed-app VERSION's COMPLETE
559
+ // restorable facet set, and the atomic-rollback spine over it. One row per
560
+ // activation of an install; the CURRENT active version is `MAX(seq)` per install
561
+ // (same "the latest row is current" model as `consent_config_revisions`). The
562
+ // whole restorable facet set (code ref/digest, entry module, public config,
563
+ // contributions, granted scopes, consent declaration, CSP origins) is one JSON
564
+ // blob in `facets` — so retaining a version and rolling one back are each a
565
+ // SINGLE-ROW insert: the restore is atomic by construction (you get the whole
566
+ // prior facet set or nothing — never a partial/mixed activation). A rollback is
567
+ // forward-only: it appends a NEW activation copying a retained version's facets
568
+ // and records `restored_from_seq`, so history is never rewritten and the rollback
569
+ // is itself an audited row. `tenant_id` is the isolation boundary, denormalized
570
+ // for tenant-scoped reads (no SQL FK — repo convention). `facets` carries no
571
+ // secret material (proved by the ext-kernel-01 secret scan before insert). UNIQUE
572
+ // `(install_id, seq)` makes the monotonic sequence a schema invariant.
573
+ export const appVersionSnapshots = sqliteTable(
574
+ "app_version_snapshots",
575
+ {
576
+ id: text("id").primaryKey(),
577
+ installId: text("install_id").notNull(),
578
+ tenantId: text("tenant_id").notNull(),
579
+ // Monotonic per install (1, 2, 3…); the largest is the active version.
580
+ seq: integer("seq").notNull(),
581
+ // The app package version this activation serves (mirror of facets.appVersion).
582
+ appVersion: text("app_version").notNull(),
583
+ // The complete restorable facet set as one JSON blob (AppVersionFacets).
584
+ facets: text("facets").notNull(),
585
+ // Opaque actor id — never email/PII (matches `app_audit_log`).
586
+ actor: text("actor").notNull(),
587
+ // Human-legible note (e.g. the reason, or "rollback to version N").
588
+ note: text("note"),
589
+ // When this activation restores an earlier one, its source `seq` — else null.
590
+ restoredFromSeq: integer("restored_from_seq"),
591
+ createdAt: text("created_at").notNull(),
592
+ },
593
+ (t) => ({
594
+ uqAppVersionSnapshotsInstallSeq: uniqueIndex("uq_app_version_snapshots_install_seq").on(
595
+ t.installId,
596
+ t.seq,
597
+ ),
598
+ idxAppVersionSnapshotsInstall: index("idx_app_version_snapshots_install").on(t.installId),
599
+ }),
600
+ );
601
+
602
+ // The per-`(tenant, external_order_id)` attribution ledger. An order's attribution is
603
+ // decided ONCE (on its first `order.created`/`order.fulfilled` conversion) and reused
604
+ // for every later lifecycle conversion, so the correlation `jti` is consumed once per
605
+ // order — not once per lifecycle event — and a duplicate/replayed/out-of-order webhook
606
+ // reproduces the same outcome. `attributed` is 0/1; when attributed, `correlation_id`
607
+ // + `correlation_expires_at` hold the shared thread; when not, `reason` records why
608
+ // (missing/expired/context_mismatch/replay/…). Never customer identity — only the
609
+ // opaque correlation handle. `(tenant_id, external_order_id)` is the natural key.
610
+ export const orderAttribution = sqliteTable(
611
+ "order_attribution",
612
+ {
613
+ tenantId: text("tenant_id").notNull(),
614
+ externalOrderId: text("external_order_id").notNull(),
615
+ attributed: integer("attributed").notNull().default(0),
616
+ correlationId: text("correlation_id"),
617
+ correlationExpiresAt: text("correlation_expires_at"),
618
+ reason: text("reason"),
619
+ recordedAt: text("recorded_at").notNull(),
620
+ },
621
+ (t) => ({ pk: primaryKey({ columns: [t.tenantId, t.externalOrderId] }) }),
622
+ );
@@ -0,0 +1,49 @@
1
+ /**
2
+ * "Stuck? Ask" recovery affordance. A quiet FAB that opens a scoped Q&A
3
+ * for "why isn't the loop working" — NOT general AI editing. Static answers
4
+ * ship as markup; the two stateful answers (is it running? / save-vs-reload)
5
+ * update from the shared tot:loop-state signal + window.__totLoop (monitor.ts
6
+ * — no second poll). "Reconnect preview" reloads the page, re-establishing
7
+ * the HMR connection and re-reading loop state (recovers a stale client); a
8
+ * fully-down server is handled by the answer text (run tot start). Imported
9
+ * from a processed `<script>` in cockpit.astro (ADR 0013), rendered only when
10
+ * isLocalDevLoop.
11
+ */
12
+ export function initAsk(): void {
13
+ const fab = document.getElementById("askFab");
14
+ const panel = document.getElementById("askPanel");
15
+ if (!fab || !panel) return;
16
+ const ansRunning = document.getElementById("ans-running");
17
+ const ansChange = document.getElementById("ans-change");
18
+ const restart = document.getElementById("restartBtn");
19
+
20
+ fab.addEventListener("click", (e) => { e.stopPropagation(); panel.classList.toggle("open"); });
21
+ document.addEventListener("click", (e) => {
22
+ if (!panel.contains(e.target as Node) && e.target !== fab && !fab.contains(e.target as Node)) panel.classList.remove("open");
23
+ });
24
+ if (restart) restart.addEventListener("click", () => { location.reload(); });
25
+
26
+ function rel(ts: number | string): string {
27
+ const d = Math.max(0, Math.round((Date.now() - Number(ts)) / 1000));
28
+ return d < 2 ? "just now" : d < 60 ? d + "s ago" : Math.floor(d / 60) + "m ago";
29
+ }
30
+ function update(s: any) {
31
+ if (!s) return;
32
+ if (ansRunning) {
33
+ ansRunning.textContent = (s.offline || !s.server)
34
+ ? "Your dev server isn't reachable right now — run tot start in your terminal, then reload this page."
35
+ : "Yes — your server is running" + (s.port ? " on port " + s.port : "") + ". This page is connected to it.";
36
+ }
37
+ if (ansChange) {
38
+ if (!s.lastSaveAt) {
39
+ ansChange.textContent = "No saves seen yet. Edit a watched file (like content/home.html) and save — it'll show up here.";
40
+ } else if ((s.lastReloadAt || 0) >= s.lastSaveAt) {
41
+ ansChange.textContent = "Working: your last save (" + rel(s.lastSaveAt) + ") triggered a reload (" + rel(s.lastReloadAt) + "). If the page didn't visibly change, make sure you edited the file the page renders from.";
42
+ } else {
43
+ ansChange.textContent = "Your save (" + rel(s.lastSaveAt) + ") hasn't been reflected by a reload yet — the preview may be stale. Try Reconnect preview below.";
44
+ }
45
+ }
46
+ }
47
+ window.addEventListener("tot:loop-state", (e: any) => { update(e.detail); });
48
+ if (window.__totLoop) update(window.__totLoop);
49
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * C8 · Click-to-source capstone — the cockpit side. Runs the live-preview
3
+ * iframe (mini + full-screen dialog) and LISTENS for the framed picker's
4
+ * tot:locate postMessage. The picker (ai-widget-client.js, auto-injected into
5
+ * the same-origin preview) resolves a click to {path,line,col} via
6
+ * /__tot/locate, then — because it is framed — postMessages the result here
7
+ * instead of showing its own toast. We verify event.origin === our origin,
8
+ * then hand off to files.ts's window.__totOpenFileAt to open the source +
9
+ * drop the caret. Only content-region hits ever arrive (chrome/refused
10
+ * clicks stay silent in the frame), per decision
11
+ * click-to-source-anchors-at-content-region. Imported from a processed
12
+ * `<script>` in cockpit.astro (ADR 0013), rendered only when isLocalDevLoop.
13
+ * isDev and the hosted app domain are read from `document.body.dataset`.
14
+ */
15
+ export function initClickToSource(): void {
16
+ const isDev = document.body.dataset.cockpitIsDev === "true";
17
+ const hostedAppDomain = document.body.dataset.cockpitAppDomain || "";
18
+ const dlg: any = document.getElementById("previewDialog");
19
+ const expandBtn = document.getElementById("previewExpand");
20
+ const closeBtn = document.getElementById("previewClose");
21
+ const dlgFrame: any = document.getElementById("previewDialogFrame");
22
+ const miniFrame: any = document.getElementById("previewFrame");
23
+ const urlEl = document.getElementById("previewUrl");
24
+
25
+ if (miniFrame) {
26
+ if (isDev) {
27
+ // Point the preview at the DEV tenant (TOT_DEV_TENANT), read from the server
28
+ // via /__tot/dev-loop-status — NOT the SSR appDomain (workerd can't see
29
+ // TOT_DEV_*). Same-origin, so the auto-injected picker can postMessage back.
30
+ fetch("/__tot/dev-loop-status")
31
+ .then((r) => r.json())
32
+ .then((s) => {
33
+ const tenant = s && s.tenant;
34
+ if (!tenant) return;
35
+ const src = "/" + tenant + "/";
36
+ miniFrame.src = src;
37
+ if (urlEl) urlEl.textContent = "localhost preview · " + tenant;
38
+ })
39
+ .catch(() => { /* dev server unreachable — preview stays blank */ });
40
+ } else if (hostedAppDomain) {
41
+ // Hosted: the SSR-resolved tenant IS the signed-in developer's own store
42
+ // (cp-env's capability gate already scoped it) — same-origin, so a cp-write
43
+ // save + this reload shows the developer's own draft overlay via the
44
+ // middleware's tot_dev_draft cookie check.
45
+ miniFrame.src = "/" + hostedAppDomain + "/";
46
+ if (urlEl) urlEl.textContent = "your store · " + hostedAppDomain;
47
+ }
48
+ }
49
+
50
+ if (expandBtn && dlg) {
51
+ expandBtn.addEventListener("click", () => {
52
+ if (dlgFrame && !dlgFrame.getAttribute("src") && miniFrame) dlgFrame.src = miniFrame.getAttribute("src") || miniFrame.src;
53
+ if (typeof dlg.showModal === "function") dlg.showModal(); else dlg.setAttribute("open", "");
54
+ });
55
+ }
56
+ if (closeBtn && dlg) closeBtn.addEventListener("click", () => { dlg.close ? dlg.close() : dlg.removeAttribute("open"); });
57
+ if (dlg) dlg.addEventListener("click", (e: MouseEvent) => { if (e.target === dlg) { dlg.close ? dlg.close() : dlg.removeAttribute("open"); } });
58
+
59
+ // The one privileged inbound message: a source location from OUR OWN origin's
60
+ // framed preview. Reject any other origin, shape, or type outright.
61
+ window.addEventListener("message", (e) => {
62
+ if (e.origin !== window.location.origin) return;
63
+ const d: any = e.data;
64
+ if (!d || d.type !== "tot:locate" || typeof d.path !== "string") return;
65
+ if (typeof (window as any).__totOpenFileAt === "function") {
66
+ const opened = (window as any).__totOpenFileAt(d.path, d.line, d.col);
67
+ // If the preview was expanded full-screen, close it so the editor + caret are visible.
68
+ if (opened && dlg && dlg.open) { dlg.close ? dlg.close() : dlg.removeAttribute("open"); }
69
+ }
70
+ });
71
+ }
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Copy the one command. Two behaviours:
3
+ * · fastpath (signed-in dev): mint a single-use `tot login --code … && tot start` from
4
+ * /api/dev/cli-signin-code, reveal it (textContent, never innerHTML — it carries a
5
+ * live code), and copy the whole thing.
6
+ * · otherwise: copy the generic install-and-run command directly.
7
+ *
8
+ * Also drives rendezvous approval: after a command is generated, the
9
+ * terminal ATTACHES its own PKCE key and prints a fingerprint; the developer
10
+ * confirms it here — the load-bearing anti-interception step, since
11
+ * approving the wrong fingerprint is how a hijack is caught.
12
+ *
13
+ * Imported from a processed `<script>` in cockpit.astro. The values below are
14
+ * carried as data-attrs on #copyCmd because this module runs standalone (not
15
+ * inside the same closure as hostedActivity.ts's basePath).
16
+ */
17
+ export function initCopyCommand(): void {
18
+ const btn: any = document.getElementById("copyCmd");
19
+ const textEl = document.getElementById("cli-cmd-text");
20
+ const note = document.getElementById("cli-cmd-note");
21
+ const toast = document.getElementById("toast");
22
+ const toastMsg = document.getElementById("toastMsg");
23
+ const copyIcon: any = document.getElementById("copyIcon");
24
+ if (!btn || !textEl) return;
25
+ const generic = btn.getAttribute("data-generic") || "";
26
+ const fastpath = btn.getAttribute("data-fastpath") === "1";
27
+ // The request's tenant prefix ("" host-routed, "/<tenant>" path-routed). Carried
28
+ // as a data-attr because `basePath` is otherwise defined only inside the SEPARATE
29
+ // hostedActivity module (hostedActivity.ts) — referencing it here (the
30
+ // cli-signin-code / -status / -approve fetches below) was a ReferenceError that
31
+ // the mint's catch surfaced as the opaque "Couldn't reach the sign-in service"
32
+ // with no request ever sent.
33
+ const basePath = btn.getAttribute("data-base-path") || "";
34
+ let tT: ReturnType<typeof setTimeout>;
35
+ function ping(m: string) {
36
+ if (!toast) return;
37
+ if (toastMsg) toastMsg.textContent = m;
38
+ toast.classList.add("show");
39
+ clearTimeout(tT); tT = setTimeout(() => { toast.classList.remove("show"); }, 1900);
40
+ }
41
+ function copy(text: string): Promise<boolean> {
42
+ if (navigator.clipboard) return navigator.clipboard.writeText(text).then(() => true).catch(() => false);
43
+ return Promise.resolve(false);
44
+ }
45
+ function label(s: string) { btn.childNodes[btn.childNodes.length - 1].textContent = s; }
46
+
47
+ // Standard copy-icon affordance on the terminal box. Once the command is
48
+ // revealed, this is the obvious, always-there way to (re)copy it — the
49
+ // icon→check swap is its own inline confirmation, so copying no longer
50
+ // depends on the transient toast being noticed.
51
+ let iconT: ReturnType<typeof setTimeout>;
52
+ function flashCopied() {
53
+ if (!copyIcon) return;
54
+ copyIcon.setAttribute("data-copied", "1");
55
+ copyIcon.setAttribute("aria-label", "Copied");
56
+ copyIcon.setAttribute("title", "Copied");
57
+ clearTimeout(iconT);
58
+ iconT = setTimeout(() => {
59
+ copyIcon.removeAttribute("data-copied");
60
+ copyIcon.setAttribute("aria-label", "Copy the command");
61
+ copyIcon.setAttribute("title", "Copy the command");
62
+ }, 1600);
63
+ }
64
+ function revealIcon() {
65
+ if (copyIcon) copyIcon.hidden = false;
66
+ }
67
+ if (copyIcon) {
68
+ copyIcon.addEventListener("click", () => {
69
+ copy(textEl.textContent || "").then((ok) => {
70
+ if (ok) { flashCopied(); ping("Copied — paste & press Enter"); }
71
+ else ping("Select the command above to copy");
72
+ });
73
+ });
74
+ }
75
+
76
+ // ── Rendezvous approval: after a command is generated, the terminal ATTACHES
77
+ // its own PKCE key and prints a fingerprint; the developer confirms it HERE. This
78
+ // is the load-bearing anti-interception step — approving the wrong fingerprint is
79
+ // how a hijack is caught, so the confirm is an explicit, human decision.
80
+ const rzvRoot: any = document.getElementById("rzvApprove");
81
+ const rzvWait: any = document.getElementById("rzvWait");
82
+ const rzvConfirm: any = document.getElementById("rzvConfirm");
83
+ const rzvFp: any = document.getElementById("rzvFp");
84
+ const rzvApproveBtn: any = document.getElementById("rzvApproveBtn");
85
+ const rzvDenyBtn: any = document.getElementById("rzvDenyBtn");
86
+ const rzvDone: any = document.getElementById("rzvDone");
87
+ let rzvTimer: ReturnType<typeof setTimeout> | null = null;
88
+ let rzvCode: string | null = null;
89
+ let rzvDecided = false;
90
+ function rzvShow(el: any) { if (el) el.hidden = false; }
91
+ function rzvHide(el: any) { if (el) el.hidden = true; }
92
+ // Once the terminal has ATTACHED, the command has plainly been pasted and
93
+ // run — hide the copy affordances so the approve prompt is the page's
94
+ // single focus (DZ 2026-08-18). An EXPIRED sign-in restores the generate
95
+ // button (a fresh mint is needed); the stale command's copy icon stays gone.
96
+ function rzvHideCopyCtas() {
97
+ if (btn) btn.hidden = true;
98
+ if (copyIcon) copyIcon.hidden = true;
99
+ }
100
+ function rzvRestoreGenerate() {
101
+ if (btn) btn.hidden = false;
102
+ }
103
+ function rzvStopPoll() { if (rzvTimer) { clearTimeout(rzvTimer); rzvTimer = null; } }
104
+ function rzvFinish(msg: string) {
105
+ rzvStopPoll(); rzvDecided = true;
106
+ rzvHide(rzvWait); rzvHide(rzvConfirm);
107
+ if (rzvDone) { rzvDone.textContent = msg; rzvDone.hidden = false; }
108
+ }
109
+ async function rzvPoll(): Promise<void> {
110
+ if (rzvDecided) return;
111
+ let r: Response, b: any;
112
+ try {
113
+ r = await fetch(basePath + "/api/dev/cli-signin-status", {
114
+ method: "POST", headers: { "content-type": "application/json" },
115
+ body: JSON.stringify({ rendezvousCode: rzvCode }),
116
+ });
117
+ b = await r.json().catch(() => ({}));
118
+ } catch (e) { rzvTimer = setTimeout(rzvPoll, 2800); return; }
119
+ if (!r || !r.ok) { rzvTimer = setTimeout(rzvPoll, 3200); return; }
120
+ if (b.state === "attached" && b.fingerprint) {
121
+ rzvHideCopyCtas();
122
+ rzvHide(rzvWait); rzvShow(rzvConfirm);
123
+ if (rzvFp) { rzvFp.textContent = b.fingerprint; rzvFp.setAttribute("data-fp", b.fingerprint); }
124
+ return; // stop polling — wait for the human decision
125
+ }
126
+ if (b.state === "approved") { rzvHideCopyCtas(); rzvFinish("Signed in ✓ — your terminal is good to go."); return; }
127
+ if (b.state === "denied") { rzvHideCopyCtas(); rzvFinish("Rejected — that terminal wasn't approved. You're safe; generate a fresh command to try again."); return; }
128
+ if (b.state === "expired") { rzvRestoreGenerate(); rzvFinish("This sign-in expired. Generate a fresh setup command above."); return; }
129
+ rzvTimer = setTimeout(rzvPoll, 2500); // pending → keep watching
130
+ }
131
+ function rzvStart(code: string) {
132
+ rzvCode = code; rzvDecided = false;
133
+ if (rzvApproveBtn) rzvApproveBtn.disabled = false;
134
+ if (rzvDenyBtn) rzvDenyBtn.disabled = false;
135
+ rzvShow(rzvRoot); rzvShow(rzvWait); rzvHide(rzvConfirm); rzvHide(rzvDone);
136
+ rzvStopPoll(); rzvPoll();
137
+ }
138
+ async function rzvDecide(decision: string): Promise<void> {
139
+ if (rzvDecided) return;
140
+ const fp = rzvFp ? (rzvFp.getAttribute("data-fp") || "") : "";
141
+ if (rzvApproveBtn) rzvApproveBtn.disabled = true;
142
+ if (rzvDenyBtn) rzvDenyBtn.disabled = true;
143
+ let r: Response;
144
+ try {
145
+ r = await fetch(basePath + "/api/dev/cli-signin-approve", {
146
+ method: "POST", headers: { "content-type": "application/json" },
147
+ body: JSON.stringify({ rendezvousCode: rzvCode, fingerprint: fp, decision: decision }),
148
+ });
149
+ } catch (e) {
150
+ if (rzvApproveBtn) rzvApproveBtn.disabled = false;
151
+ if (rzvDenyBtn) rzvDenyBtn.disabled = false;
152
+ return;
153
+ }
154
+ if (decision === "deny") { rzvFinish("Rejected — that terminal wasn't approved."); return; }
155
+ if (r.ok) { rzvFinish("Signed in ✓ — your terminal is good to go."); return; }
156
+ if (rzvApproveBtn) rzvApproveBtn.disabled = false;
157
+ if (rzvDenyBtn) rzvDenyBtn.disabled = false;
158
+ const m = r.status === 403 ? "That didn't match your identity — generate a fresh command and try again."
159
+ : r.status === 409 ? "That code didn't match a waiting terminal — re-check it, or generate a fresh command."
160
+ : "Couldn't complete approval — try again.";
161
+ if (rzvDone) { rzvDone.textContent = m; rzvDone.hidden = false; }
162
+ }
163
+ if (rzvApproveBtn) rzvApproveBtn.addEventListener("click", () => { rzvDecide("approve"); });
164
+ if (rzvDenyBtn) rzvDenyBtn.addEventListener("click", () => { rzvDecide("deny"); });
165
+
166
+ if (!fastpath) {
167
+ btn.addEventListener("click", () => {
168
+ copy(generic).then((ok) => {
169
+ label(ok ? " Copied — paste & press Enter" : " Copy the command");
170
+ ping(ok ? "Copied the command" : "Select the command to copy");
171
+ setTimeout(() => { label(" Copy the command"); }, 2600);
172
+ });
173
+ });
174
+ return;
175
+ }
176
+
177
+ let busy = false;
178
+ btn.addEventListener("click", async () => {
179
+ if (busy) return;
180
+ busy = true;
181
+ label(" …"); btn.disabled = true;
182
+ let res: Response, body: any;
183
+ try {
184
+ res = await fetch(basePath + "/api/dev/cli-signin-code", {
185
+ method: "POST",
186
+ headers: { "content-type": "application/json" },
187
+ body: "{}",
188
+ });
189
+ body = await res.json().catch(() => ({}));
190
+ } catch (err) {
191
+ if (note) note.textContent = "Couldn't reach the sign-in service — try again in a moment.";
192
+ label(" Copy the command"); btn.disabled = false; busy = false;
193
+ return;
194
+ }
195
+ if (!res.ok || !body.command) {
196
+ const byStatus: Record<number, string> = {
197
+ 401: "Your session expired — reload the page and sign in again.",
198
+ 403: "This account doesn't have developer access to a store yet.",
199
+ 409: "Your store isn't set up yet — reload this page for its status.",
200
+ 502: "The sign-in service is having a moment — try again.",
201
+ 503: "Terminal sign-in isn't configured on this environment.",
202
+ };
203
+ if (note) note.textContent = byStatus[res.status] || "Couldn't mint a sign-in command — try again.";
204
+ label(" Copy the command"); btn.disabled = false; busy = false;
205
+ return;
206
+ }
207
+ // Reveal the real command (carries the non-secret rendezvous handle) as a pure
208
+ // text node, then copy it.
209
+ textEl.textContent = body.command;
210
+ textEl.classList.remove("is-placeholder");
211
+ revealIcon();
212
+ // Start watching for the terminal to attach so we can prompt for approval.
213
+ if (body.rendezvousCode) rzvStart(body.rendezvousCode);
214
+ const ok = await copy(body.command);
215
+ if (ok) flashCopied();
216
+ label(ok ? " Copied ✓" : " Select & copy");
217
+ ping(ok ? "Copied — paste & press Enter" : "Select the command above to copy");
218
+ if (note) {
219
+ note.textContent = ok
220
+ ? "Copied. Paste into your terminal and press Enter — it signs you in, runs your store, and opens your browser. Single-use, short-lived code."
221
+ : "Select the command above and copy it, then paste into your terminal and press Enter. Single-use, short-lived code.";
222
+ }
223
+ btn.disabled = false;
224
+ setTimeout(() => { label(" Copy the command"); }, 4000);
225
+ busy = false;
226
+ });
227
+ }