@tokenoftrust/storefront-runner 1.4.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/apps/storefront/astro.config.mjs +17 -1
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +54 -0
  4. package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
  5. package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
  6. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  7. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  8. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  9. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  10. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  11. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  12. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  13. package/apps/storefront/migrations-apps/meta/0002_snapshot.json +1160 -0
  14. package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -0
  15. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  16. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  17. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  18. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  19. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  20. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  21. package/apps/storefront/migrations-apps/meta/_journal.json +56 -0
  22. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  23. package/apps/storefront/public/js/dashboard-team.js +38 -2
  24. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  25. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  26. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  27. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  28. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  29. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  30. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  31. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  32. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  33. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  34. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  35. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  36. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  37. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  38. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  39. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  40. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  41. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  42. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  43. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  44. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  45. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  46. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  47. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  48. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  49. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  50. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  51. package/apps/storefront/src/components/Img.astro +1 -1
  52. package/apps/storefront/src/components/Seo.astro +65 -1
  53. package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
  54. package/apps/storefront/src/components/admin/AdminBlogTab.astro +1435 -0
  55. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
  56. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  57. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1980 -2564
  58. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  59. package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
  60. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  61. package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
  62. package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
  63. package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
  64. package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
  65. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  66. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  67. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  68. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  69. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  70. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  71. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  72. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  73. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  74. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  75. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  76. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  77. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  78. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  79. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  80. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  81. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  82. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  83. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  84. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  85. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  86. package/apps/storefront/src/config/resolver.ts +4 -4
  87. package/apps/storefront/src/layouts/Layout.astro +146 -21
  88. package/apps/storefront/src/lib/activity/alerts.ts +17 -19
  89. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  90. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +89 -9
  91. package/apps/storefront/src/lib/activity/deployVersion.ts +20 -27
  92. package/apps/storefront/src/lib/activity/ingest.ts +8 -8
  93. package/apps/storefront/src/lib/activity/ingestAuth.ts +1 -1
  94. package/apps/storefront/src/lib/activity/killSwitch.ts +6 -6
  95. package/apps/storefront/src/lib/activity/query.ts +9 -9
  96. package/apps/storefront/src/lib/activity/recordActivity.ts +11 -15
  97. package/apps/storefront/src/lib/activity/store.ts +4 -6
  98. package/apps/storefront/src/lib/activity/uiActor.ts +12 -12
  99. package/apps/storefront/src/lib/activity/workerCommit.ts +6 -6
  100. package/apps/storefront/src/lib/admin/adminShell.ts +292 -0
  101. package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
  102. package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
  103. package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
  104. package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
  105. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  106. package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
  107. package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
  108. package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
  109. package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
  110. package/apps/storefront/src/lib/apps/adminService.ts +14 -4
  111. package/apps/storefront/src/lib/apps/adminSession.ts +54 -45
  112. package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
  113. package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
  114. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  115. package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
  116. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  117. package/apps/storefront/src/lib/apps/credentials.ts +2 -2
  118. package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
  119. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  120. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  121. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  122. package/apps/storefront/src/lib/apps/healthAggregate.ts +5 -5
  123. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  124. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  125. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  126. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  127. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  128. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  129. package/apps/storefront/src/lib/apps/orders/attributionService.ts +2 -2
  130. package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
  131. package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
  132. package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
  133. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  134. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +9 -1
  135. package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
  136. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +7 -7
  137. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  138. package/apps/storefront/src/lib/apps/registryService.ts +270 -16
  139. package/apps/storefront/src/lib/apps/scopes.ts +2 -2
  140. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  141. package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
  142. package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
  143. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  144. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  145. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  146. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  147. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  148. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  149. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  150. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  151. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  152. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  153. package/apps/storefront/src/lib/apps/widgets/eligibility.ts +2 -2
  154. package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
  155. package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
  156. package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
  157. package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
  158. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  159. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  160. package/apps/storefront/src/lib/auth/adminEntry.ts +11 -11
  161. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  162. package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
  163. package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
  164. package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +2 -4
  165. package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
  166. package/apps/storefront/src/lib/auth/session.ts +7 -8
  167. package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
  168. package/apps/storefront/src/lib/basePath.ts +9 -2
  169. package/apps/storefront/src/lib/blog/access.ts +29 -0
  170. package/apps/storefront/src/lib/blog/collection.ts +400 -0
  171. package/apps/storefront/src/lib/blog/markdown.ts +174 -0
  172. package/apps/storefront/src/lib/blog/provider.ts +62 -3
  173. package/apps/storefront/src/lib/blog/reactions.ts +87 -0
  174. package/apps/storefront/src/lib/blog/rss.ts +92 -0
  175. package/apps/storefront/src/lib/blog/teaser.ts +83 -0
  176. package/apps/storefront/src/lib/blog/types.ts +34 -0
  177. package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
  178. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  179. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  180. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  181. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  182. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  183. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  184. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  185. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  186. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  187. package/apps/storefront/src/lib/content/index.ts +5 -5
  188. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  189. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  190. package/apps/storefront/src/lib/d1/catalog.ts +14 -2
  191. package/apps/storefront/src/lib/d1/schema-apps.ts +372 -17
  192. package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
  193. package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
  194. package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
  195. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  196. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  197. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  198. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  199. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  200. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  201. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  202. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  203. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  204. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  205. package/apps/storefront/src/lib/dev/cockpitStore.ts +1 -9
  206. package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
  207. package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
  208. package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
  209. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  210. package/apps/storefront/src/lib/edgeCache.ts +34 -5
  211. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  212. package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
  213. package/apps/storefront/src/lib/i18n.ts +25 -0
  214. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  215. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  216. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  217. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  218. package/apps/storefront/src/lib/jsonld.ts +118 -0
  219. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  220. package/apps/storefront/src/lib/membership/model.ts +1 -2
  221. package/apps/storefront/src/lib/monitoring/manifest.ts +11 -11
  222. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
  223. package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
  224. package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
  225. package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
  226. package/apps/storefront/src/lib/newsletter/send.ts +265 -0
  227. package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
  228. package/apps/storefront/src/lib/previewReload.ts +57 -1
  229. package/apps/storefront/src/lib/publish/adminPublishTab.ts +4625 -0
  230. package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
  231. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  232. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  233. package/apps/storefront/src/lib/publish/dispatchHealth.ts +5 -7
  234. package/apps/storefront/src/lib/publish/domainState.ts +22 -13
  235. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  236. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  237. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  238. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  239. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  240. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  241. package/apps/storefront/src/lib/publish/panelPolling.ts +78 -0
  242. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  243. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  244. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  245. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  246. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  247. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  248. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  249. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  250. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  251. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  252. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  253. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  254. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  255. package/apps/storefront/src/lib/publish/shipWorkspace.ts +121 -51
  256. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  257. package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -0
  258. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  259. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  260. package/apps/storefront/src/lib/rate/kvWindow.ts +53 -0
  261. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  262. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  263. package/apps/storefront/src/lib/search/index.ts +2 -2
  264. package/apps/storefront/src/lib/seo/alternates.ts +42 -0
  265. package/apps/storefront/src/lib/seo/meta.ts +65 -0
  266. package/apps/storefront/src/lib/social/golive.ts +212 -0
  267. package/apps/storefront/src/lib/social/notify.ts +32 -0
  268. package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
  269. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  270. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  271. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  272. package/apps/storefront/src/lib/the-build/provider.ts +32 -0
  273. package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
  274. package/apps/storefront/src/lib/the-build/toc.ts +95 -0
  275. package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
  276. package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
  277. package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
  278. package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
  279. package/apps/storefront/src/middleware/index.ts +385 -52
  280. package/apps/storefront/src/pages/[...slug].astro +9 -4
  281. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  282. package/apps/storefront/src/pages/admin/select.astro +96 -0
  283. package/apps/storefront/src/pages/admin.astro +25 -1313
  284. package/apps/storefront/src/pages/api/activity.ts +8 -8
  285. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  286. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  287. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  288. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  289. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  290. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  291. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  292. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  293. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  294. package/apps/storefront/src/pages/api/blog/[collection]/[slug]/react.ts +104 -0
  295. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  296. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  297. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  298. package/apps/storefront/src/pages/api/newsletter/[collection]/subscribe.ts +146 -0
  299. package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
  300. package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
  301. package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
  302. package/apps/storefront/src/pages/api/rum/vitals.ts +20 -2
  303. package/apps/storefront/src/pages/blog/[slug].astro +99 -19
  304. package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
  305. package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
  306. package/apps/storefront/src/pages/blog/index.astro +4 -2
  307. package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
  308. package/apps/storefront/src/pages/cockpit.astro +77 -1353
  309. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
  310. package/apps/storefront/src/pages/index.astro +10 -5
  311. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  312. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  313. package/apps/storefront/src/pages/search.astro +1 -1
  314. package/apps/storefront/src/pages/sitemap.xml.ts +21 -1
  315. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  316. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  317. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  318. package/apps/storefront/src/pages/the-build/[slug].astro +129 -0
  319. package/apps/storefront/src/pages/the-build/index.astro +146 -0
  320. package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
  321. package/apps/storefront/src/styles/admin.css +1106 -0
  322. package/apps/storefront/tsconfig.json +6 -1
  323. package/package.json +1 -1
  324. package/packages/public-runtime/package.json +3 -1
  325. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  326. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  327. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  328. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  329. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  330. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  331. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  332. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  333. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  334. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  335. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  336. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  337. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  338. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  339. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  340. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  341. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  342. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  343. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  344. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  345. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  346. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  347. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  348. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  349. package/packages/public-runtime/src/activity/catalog.ts +89 -33
  350. package/packages/public-runtime/src/activity/event.ts +6 -6
  351. package/packages/public-runtime/src/activity/index.ts +2 -2
  352. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  353. package/packages/public-runtime/src/candidate-index.ts +42 -8
  354. package/packages/public-runtime/src/catalog-d1.ts +1 -1
  355. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  356. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  357. package/packages/public-runtime/src/checkout-style.ts +7 -7
  358. package/packages/public-runtime/src/checkout.ts +3 -3
  359. package/packages/public-runtime/src/chrome.ts +777 -0
  360. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  361. package/packages/public-runtime/src/csp.ts +99 -0
  362. package/packages/public-runtime/src/customization-preview.ts +25 -2
  363. package/packages/public-runtime/src/customization-runtime.ts +156 -3
  364. package/packages/public-runtime/src/customization-versioning.ts +10 -0
  365. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  366. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  367. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  368. package/packages/public-runtime/src/evidence.ts +288 -0
  369. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  370. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  371. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  372. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  373. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  374. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  375. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  376. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  377. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  378. package/packages/public-runtime/src/index.ts +17 -1
  379. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  380. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  381. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  382. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  383. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  384. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  385. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  386. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  387. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  388. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  389. package/packages/public-runtime/src/integration/csp.ts +105 -0
  390. package/packages/public-runtime/src/integration/digest.ts +43 -0
  391. package/packages/public-runtime/src/integration/environment.ts +228 -0
  392. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  393. package/packages/public-runtime/src/integration/events.ts +701 -0
  394. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  395. package/packages/public-runtime/src/integration/index.ts +90 -0
  396. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  397. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  398. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  399. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  400. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  401. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  402. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  403. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  404. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  405. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  406. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  407. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  408. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  409. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  410. package/packages/public-runtime/src/integration/observability.ts +451 -0
  411. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  412. package/packages/public-runtime/src/integration/phases.ts +182 -0
  413. package/packages/public-runtime/src/integration/plan.ts +112 -0
  414. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  415. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  416. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  417. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  418. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  419. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  420. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  421. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  422. package/packages/public-runtime/src/integration/result.ts +239 -0
  423. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  424. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  425. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  426. package/packages/public-runtime/src/integration/version.ts +92 -0
  427. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  428. package/packages/public-runtime/src/membership.ts +2 -2
  429. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  430. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  431. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  432. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  433. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  434. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  435. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  436. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  437. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  438. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  439. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  440. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  441. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  442. package/packages/public-runtime/src/static-bundle.ts +357 -0
  443. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  444. package/packages/public-runtime/src/tenant.ts +81 -17
  445. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  446. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  447. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  448. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  449. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  450. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  451. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  452. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  453. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  454. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  455. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  456. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  457. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  458. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  459. package/scripts/build/copy-tenant-assets.mjs +14 -4
  460. package/scripts/dev/ai-edit.mjs +1 -1
  461. package/scripts/dev/checkout-watch.mjs +2 -2
  462. package/scripts/dev/file-browser.mjs +4 -6
  463. package/scripts/dev/file-writer.mjs +3 -3
  464. package/scripts/dev/git-status.mjs +2 -2
  465. package/scripts/dev/integration-guardrails.mjs +498 -0
  466. package/scripts/dev/locate-handler.mjs +3 -3
  467. package/scripts/dev/port-check.mjs +3 -3
  468. package/scripts/dev/publish.mjs +5 -9
  469. package/scripts/dev/safe-path.mjs +2 -2
  470. package/scripts/dev/shot.mjs +1 -1
  471. package/scripts/dev/tenant-source-root.mjs +1 -1
  472. package/scripts/dev/unified-diff.mjs +8 -3
  473. package/scripts/tot-dev.mjs +8 -11
  474. package/apps/storefront/src/lib/dev/vcBinding.ts +0 -80
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Drizzle schema — the source of truth for `STOREFRONT_APPS_DB` *migration
3
- * generation* (ADR-0002). Split out of `schema.ts` (which now covers only the
4
- * catalog + customization tables on `CATALOG_DB`): these tables are the
5
- * storefront-apps registry/webhooks/orders spine and belong to a DEDICATED
6
- * D1, never the catalog.
3
+ * generation*. `schema.ts` covers the catalog + customization tables on
4
+ * `CATALOG_DB`; these tables are the storefront-apps registry/webhooks/orders
5
+ * spine and belong to a DEDICATED D1, never the catalog.
7
6
  *
8
7
  * `drizzle-kit generate --config drizzle.config.apps.ts` diffs this into
9
8
  * numbered SQL under `migrations-apps/`, applied by `wrangler d1 migrations
@@ -11,8 +10,8 @@
11
10
  */
12
11
  import { sqliteTable, text, integer, real, index, uniqueIndex, primaryKey } from "drizzle-orm/sqlite-core";
13
12
 
14
- // --- Private Apps registry (PrivateApps epic, D2) ----------------------------
15
- // One install per (app, tenant, env). `installId` is the future JWT `sub`.
13
+ // --- Private Apps registry --------------------------------------------------
14
+ // One install per (app, tenant, env). `installId` is the JWT `sub`.
16
15
  // Append-only `app_audit_log` is never pruned by the service layer.
17
16
  export const appInstalls = sqliteTable(
18
17
  "app_installs",
@@ -28,6 +27,14 @@ export const appInstalls = sqliteTable(
28
27
  webhookEndpoint: text("webhook_endpoint"),
29
28
  widgetPlacements: text("widget_placements").notNull().default("[]"),
30
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"),
31
38
  createdAt: text("created_at").notNull(),
32
39
  updatedAt: text("updated_at").notNull(),
33
40
  },
@@ -71,14 +78,31 @@ export const appAuditLog = sqliteTable(
71
78
  }),
72
79
  );
73
80
 
74
- // --- Operational activity timeline (activity-telemetry epic, card D4) --------
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
+
98
+ // --- Operational activity timeline ------------------------------------------
75
99
  // The low-volume / high-value LIFECYCLE cohort of the actor×action telemetry
76
100
  // spine (DECISIONS.md §8). Only actions whose catalog `spec.sinks` include
77
101
  // "timeline" land here; the high-volume cohort goes to Cloudflare Analytics
78
102
  // Engine (binding `ACTIVITY_ANALYTICS`), and EVERY action also goes to Workers
79
103
  // Logs. Reuses the EXISTING STOREFRONT_APPS_DB (precedent: `app_audit_log`
80
104
  // above) rather than a new binding — the cohort is small (~50k rows/month at
81
- // 10× pilot) and the D5 query UI needs point queries + joins + arbitrary
105
+ // 10× pilot) and the timeline query UI needs point queries + joins + arbitrary
82
106
  // retention that Analytics Engine cannot give.
83
107
  //
84
108
  // `id` is the emitter-minted event UUID and the PRIMARY KEY, so a retried/
@@ -116,7 +140,7 @@ export const activityEvents = sqliteTable(
116
140
  ingestedAt: text("ingested_at").notNull(),
117
141
  },
118
142
  (t) => ({
119
- // The D5 timeline query: a tenant's events newest-first.
143
+ // The timeline query: a tenant's events newest-first.
120
144
  idxActivityTenantAt: index("idx_activity_tenant_at").on(t.tenantId, t.at),
121
145
  // Filter a tenant's timeline by action family (dashboards glob the prefix).
122
146
  idxActivityTenantAction: index("idx_activity_tenant_action").on(t.tenantId, t.action),
@@ -125,7 +149,7 @@ export const activityEvents = sqliteTable(
125
149
  }),
126
150
  );
127
151
 
128
- // --- Webhook event spine (PrivateApps epic, D4 Chunk A) ---------------------
152
+ // --- Webhook event spine ----------------------------------------------------
129
153
  // Immutable event log. `id` is the semantic event id we mint — distinct from
130
154
  // any per-delivery wire envelope id (see `webhookDeliveries.deliveryId` below).
131
155
  export const webhookEvents = sqliteTable("webhook_events", {
@@ -195,10 +219,10 @@ export const webhookInstallHealth = sqliteTable("webhook_install_health", {
195
219
  updatedAt: text("updated_at").notNull(),
196
220
  });
197
221
 
198
- // --- Order ingestion (PrivateApps epic, D6 Chunk A) --------------------------
222
+ // --- Order ingestion --------------------------------------------------------
199
223
  // The read-model `ordersStore.ts` diffs incoming Foxy orders against (created
200
- // → fulfilled transitions, for a later chunk's event emission) and that the
201
- // attribution route (also later) reads from for its cross-tenant ownership
224
+ // → fulfilled transitions, for event emission) and that the
225
+ // attribution route reads from for its cross-tenant ownership
202
226
  // check. `(tenantId, externalOrderId)` is the composite PK — same "no
203
227
  // synthetic id, the natural key IS the key" style as `customizationVersions`
204
228
  // (schema.ts) — no SQL FK to `appAttributions.orderId` (repo convention — code-only).
@@ -225,7 +249,7 @@ export const orders = sqliteTable(
225
249
  }),
226
250
  );
227
251
 
228
- // First idempotency ledger in the codebase (D6 Chunk A) — keyed per
252
+ // The idempotency ledger — keyed per
229
253
  // `(installId, idempotencyKey)` so a retried write (e.g. attribution POST)
230
254
  // replays the stored `responseBody` instead of re-applying the mutation.
231
255
  // `bodyHash` (sha256 of the canonical request body) lets a caller re-sending
@@ -242,9 +266,38 @@ export const idempotencyKeys = sqliteTable(
242
266
  (t) => ({ pk: primaryKey({ columns: [t.installId, t.idempotencyKey] }) }),
243
267
  );
244
268
 
245
- // App-owned attribution/commission records (`attribution:write`, D6 later
246
- // chunk owns the route). `orderId` references `orders.externalOrderId` in code
247
- // only (no SQL FK, repo convention); the route chunk enforces the cross-tenant
269
+ // --- Encrypted outbound social OAuth tokens ---------------------------------
270
+ // The ONLY retrievable secret store in this codebase: per-`(tenant, network)`
271
+ // OAuth access/refresh tokens we must REPLAY to X/LinkedIn, so unlike the
272
+ // one-way-hashed `app_credentials` above — they are stored ENCRYPTED-AT-REST,
273
+ // not hashed. `ciphertext` is AES-GCM(base64) over the JSON token payload and
274
+ // `iv` its per-write 96-bit nonce (base64); the key-encryption-key (KEK) is a
275
+ // Worker env binding (`SOCIAL_TOKEN_KEK`), NEVER a column here — mirroring how
276
+ // every other real secret in this codebase is an env binding, not row data.
277
+ // Composite PK `(tenant_id, network)` is the natural key (same "the natural key
278
+ // IS the key" style as `orders`); a rotate is an INSERT OR REPLACE overwrite.
279
+ // `expires_at` is the token's own OAuth expiry (nullable — long-lived manual
280
+ // creds have none), distinct from the row's `updated_at` bookkeeping clock.
281
+ export const socialTokens = sqliteTable(
282
+ "social_tokens",
283
+ {
284
+ tenantId: text("tenant_id").notNull(),
285
+ network: text("network").notNull(),
286
+ ciphertext: text("ciphertext").notNull(),
287
+ iv: text("iv").notNull(),
288
+ expiresAt: text("expires_at"),
289
+ createdAt: text("created_at").notNull(),
290
+ updatedAt: text("updated_at").notNull(),
291
+ },
292
+ (t) => ({
293
+ pk: primaryKey({ columns: [t.tenantId, t.network] }),
294
+ idxSocialTokensTenant: index("idx_social_tokens_tenant").on(t.tenantId),
295
+ }),
296
+ );
297
+
298
+ // App-owned attribution/commission records (`attribution:write`). `orderId`
299
+ // references `orders.externalOrderId` in code
300
+ // only (no SQL FK, repo convention); the route enforces the cross-tenant
248
301
  // ownership check the contract requires before ever writing a row here.
249
302
  export const appAttributions = sqliteTable(
250
303
  "app_attributions",
@@ -265,3 +318,305 @@ export const appAttributions = sqliteTable(
265
318
  idxAppAttributionsTenantOrder: index("idx_app_attributions_tenant_order").on(t.tenantId, t.orderId),
266
319
  }),
267
320
  );
321
+
322
+ // --- Newsletter subscribers -------------------------------------------------
323
+ // First-party audience for the blog/newsletter digest, kept in OUR D1 (not a
324
+ // vendor list) so the per-tenant isolation + GDPR data-control invariants stay
325
+ // under our control.
326
+ // `tenantId` is the isolation boundary, written from the resolved host/session
327
+ // — NEVER a client body. Keyed on `(tenant_id, collection, email)` so a tenant
328
+ // with multiple collections (e.g. `blog` + `the-build`) runs independent lists.
329
+ // `token` is an opaque random used for both confirm + one-click unsubscribe
330
+ // links; `consentIpHash` is sha256(ip+salt) — never a raw IP (PII). Timestamps
331
+ // are ISO-8601 text. `status`: pending | confirmed | unsubscribed | bounced.
332
+ export const newsletterSubscribers = sqliteTable(
333
+ "newsletter_subscribers",
334
+ {
335
+ tenantId: text("tenant_id").notNull(),
336
+ collection: text("collection").notNull(),
337
+ email: text("email").notNull(),
338
+ status: text("status").notNull().default("pending"),
339
+ token: text("token").notNull(),
340
+ consentSource: text("consent_source"),
341
+ consentIpHash: text("consent_ip_hash"),
342
+ subscribedAt: text("subscribed_at").notNull(),
343
+ confirmedAt: text("confirmed_at"),
344
+ unsubscribedAt: text("unsubscribed_at"),
345
+ lastSentAt: text("last_sent_at"),
346
+ },
347
+ (t) => ({
348
+ pk: primaryKey({ columns: [t.tenantId, t.collection, t.email] }),
349
+ idxNewsletterTenantCollStatus: index("idx_newsletter_tenant_coll_status").on(
350
+ t.tenantId,
351
+ t.collection,
352
+ t.status,
353
+ ),
354
+ }),
355
+ );
356
+
357
+ // Idempotent per-digest send log so a re-triggered digest can't double-send.
358
+ // Keyed on `(tenant_id, collection, digest_id)` (e.g. `the-build-2026-W34`);
359
+ // `postSlugs` is a JSON array of the slugs included in that digest.
360
+ export const newsletterSends = sqliteTable(
361
+ "newsletter_sends",
362
+ {
363
+ tenantId: text("tenant_id").notNull(),
364
+ collection: text("collection").notNull(),
365
+ digestId: text("digest_id").notNull(),
366
+ postSlugs: text("post_slugs").notNull().default("[]"),
367
+ recipientCount: integer("recipient_count").notNull().default(0),
368
+ sentAt: text("sent_at").notNull(),
369
+ },
370
+ (t) => ({ pk: primaryKey({ columns: [t.tenantId, t.collection, t.digestId] }) }),
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
+ );
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Opaque, SESSION-KV-backed credential that lets a developer's LOCAL dev-loop
3
3
  * process authenticate POST /api/dev/activity on the hosted Worker — the
4
- * local→hosted activity bridge (handoff
5
- * 2026-07-14-hosted-dev-activity-bridge-and-stepper).
4
+ * local→hosted activity bridge.
6
5
  *
7
6
  * Minted alongside the existing cli-signin-code paste
8
7
  * (apps/storefront/src/lib/dev/cliSignInCode.ts) and cached by the CLI in
@@ -30,8 +29,7 @@ export interface ActivityIngestClaims {
30
29
  * sign-in code was issued (see api/dev/cli-signin-code.ts). It rides in the
31
30
  * claims so every runtime/activity write the local loop makes with this token
32
31
  * is stampable with the same id — support looks a live session up by it.
33
- * Optional so tokens minted before this field existed still verify (they just
34
- * carry no trace); every freshly-minted token sets it.
32
+ * Optional: a token without it carries no trace; every freshly-minted token sets it.
35
33
  */
36
34
  traceId?: string;
37
35
  }
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Per-vendor "recent activity" feed for the hosted /dev panel — a small ring
3
3
  * buffer of file-save events reported by a developer's LOCAL dev loop (see
4
- * activityIngestToken.ts for the write-path auth). File-level only, per
5
- * decision (no per-line diff, no separate published-history tier — see
6
- * handoff 2026-07-14-hosted-dev-activity-bridge-and-stepper).
4
+ * activityIngestToken.ts for the write-path auth). File-level only: no
5
+ * per-line diff, no separate published-history tier.
7
6
  */
8
7
 
9
8
  export type ActivityEventKind = "change" | "add" | "unlink" | "up";
@@ -62,8 +61,7 @@ export async function clearActivity(kv: KVNamespace, appDomain: string): Promise
62
61
 
63
62
  /**
64
63
  * Business-readable translation of a reported file path + event, for the
65
- * activity log. File-level only — no per-line diff enrichment (Darrin,
66
- * 2026-07-14: "we can show that in step 2").
64
+ * activity log. File-level only — no per-line diff enrichment.
67
65
  */
68
66
  export function describeActivity(file: string | undefined, event: ActivityEventKind): string {
69
67
  if (event === "up") return "Your store is running locally";
@@ -3,12 +3,9 @@
3
3
  * click-to-source). Local `astro dev` serves these off dev-server-only plugins at
4
4
  * `/__tot/*` (dev-plugins/dev-file-browser.mjs, dev-write.mjs, dev-diff.mjs, ...) —
5
5
  * paths that don't exist in the deployed Cloudflare Worker. The hosted Worker instead
6
- * serves the SAME shape off `/api/dev/*` Astro API routes, landing incrementally
6
+ * serves the SAME shape off `/api/dev/*` Astro API routes
7
7
  * (cp-tree, cp-file, cp-write, cp-diff, cp-locate). One dev.astro UI, one resolver.
8
8
  *
9
- * See decision `architecture-environment-aware-cockpit`
10
- * (workstream tot-lime-cloud-dev-cockpit).
11
- *
12
9
  * `basePath` is the request's tenant prefix ("" host-routed, "/<tenant>" path-routed).
13
10
  * The HOSTED routes are tenant-scoped through the middleware's host/path resolution, so
14
11
  * a `/<tenant>/cockpit` render must call `/<tenant>/api/dev/*` for `locals.tenant` to be
@@ -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
+ }