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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/apps/storefront/astro.config.mjs +3 -3
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +12 -14
  4. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  5. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  6. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  7. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  8. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  9. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  10. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  11. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  12. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  13. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  14. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  15. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  16. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  17. package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
  18. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  19. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  20. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  21. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  22. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  23. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  24. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  25. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  26. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  27. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  28. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  29. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  30. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  31. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  32. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  33. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  34. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  35. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  36. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  37. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  38. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  39. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  40. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  41. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  42. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  43. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  44. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  45. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  46. package/apps/storefront/src/components/Img.astro +1 -1
  47. package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
  48. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  49. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
  50. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  51. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  52. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  53. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  54. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  55. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  56. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  57. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  58. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  59. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  60. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  61. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  62. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  63. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  64. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  65. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  66. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  67. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  68. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  69. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  70. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  71. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  72. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  73. package/apps/storefront/src/config/resolver.ts +4 -4
  74. package/apps/storefront/src/layouts/Layout.astro +100 -20
  75. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  76. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
  77. package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
  78. package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
  79. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  80. package/apps/storefront/src/lib/apps/adminService.ts +10 -0
  81. package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
  82. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  83. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  84. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  85. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  86. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  87. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  88. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  89. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  90. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  91. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  92. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  93. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  94. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
  95. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
  96. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  97. package/apps/storefront/src/lib/apps/registryService.ts +264 -10
  98. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  99. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  100. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  101. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  102. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  103. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  104. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  105. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  106. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  107. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  108. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  109. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  110. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  111. package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
  112. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  113. package/apps/storefront/src/lib/blog/collection.ts +97 -10
  114. package/apps/storefront/src/lib/blog/markdown.ts +1 -1
  115. package/apps/storefront/src/lib/blog/provider.ts +57 -3
  116. package/apps/storefront/src/lib/blog/types.ts +9 -9
  117. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  118. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  119. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  120. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  121. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  122. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  123. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  124. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  125. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  126. package/apps/storefront/src/lib/content/index.ts +5 -5
  127. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  128. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  129. package/apps/storefront/src/lib/d1/catalog.ts +12 -0
  130. package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
  131. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  132. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  133. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  134. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  135. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  136. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  137. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  138. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  139. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  140. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  141. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  142. package/apps/storefront/src/lib/edgeCache.ts +18 -6
  143. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  144. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  145. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  146. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  147. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  148. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  149. package/apps/storefront/src/lib/membership/model.ts +1 -2
  150. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
  151. package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
  152. package/apps/storefront/src/lib/newsletter/send.ts +3 -3
  153. package/apps/storefront/src/lib/previewReload.ts +57 -1
  154. package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
  155. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  156. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  157. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  158. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  159. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  160. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  161. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  162. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  163. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  164. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  165. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  166. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  167. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  168. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  169. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  170. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  171. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  172. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  173. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  174. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  175. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  176. package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
  177. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  178. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  179. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  180. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  181. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  182. package/apps/storefront/src/lib/seo/alternates.ts +1 -1
  183. package/apps/storefront/src/lib/social/notify.ts +4 -4
  184. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  185. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  186. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  187. package/apps/storefront/src/lib/the-build/provider.ts +9 -4
  188. package/apps/storefront/src/middleware/index.ts +273 -51
  189. package/apps/storefront/src/pages/[...slug].astro +9 -4
  190. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  191. package/apps/storefront/src/pages/admin/select.astro +96 -0
  192. package/apps/storefront/src/pages/admin.astro +16 -2
  193. package/apps/storefront/src/pages/api/activity.ts +8 -8
  194. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  195. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  196. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  197. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  198. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  199. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  200. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  201. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  202. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  203. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  204. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  205. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  206. package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
  207. package/apps/storefront/src/pages/blog/[slug].astro +5 -5
  208. package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
  209. package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
  210. package/apps/storefront/src/pages/blog/index.astro +2 -2
  211. package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
  212. package/apps/storefront/src/pages/cockpit.astro +61 -1362
  213. package/apps/storefront/src/pages/index.astro +10 -5
  214. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  215. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  216. package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
  217. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  218. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  219. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  220. package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
  221. package/apps/storefront/src/pages/the-build/index.astro +2 -2
  222. package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
  223. package/apps/storefront/src/styles/admin.css +27 -1
  224. package/apps/storefront/tsconfig.json +1 -1
  225. package/package.json +1 -1
  226. package/packages/public-runtime/package.json +2 -1
  227. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  228. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  229. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  230. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  231. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  232. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  233. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  234. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  235. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  236. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  237. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  238. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  239. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  240. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  241. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  242. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  243. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  244. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  245. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  246. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  247. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  248. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  249. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  250. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  251. package/packages/public-runtime/src/activity/catalog.ts +41 -37
  252. package/packages/public-runtime/src/activity/event.ts +6 -6
  253. package/packages/public-runtime/src/activity/index.ts +2 -2
  254. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  255. package/packages/public-runtime/src/candidate-index.ts +42 -8
  256. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  257. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  258. package/packages/public-runtime/src/checkout-style.ts +7 -7
  259. package/packages/public-runtime/src/checkout.ts +3 -3
  260. package/packages/public-runtime/src/chrome.ts +777 -0
  261. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  262. package/packages/public-runtime/src/csp.ts +99 -0
  263. package/packages/public-runtime/src/customization-runtime.ts +32 -1
  264. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  265. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  266. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  267. package/packages/public-runtime/src/evidence.ts +288 -0
  268. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  269. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  270. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  271. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  272. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  273. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  274. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  275. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  276. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  277. package/packages/public-runtime/src/index.ts +16 -1
  278. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  279. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  280. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  281. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  282. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  283. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  284. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  285. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  286. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  287. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  288. package/packages/public-runtime/src/integration/csp.ts +105 -0
  289. package/packages/public-runtime/src/integration/digest.ts +43 -0
  290. package/packages/public-runtime/src/integration/environment.ts +228 -0
  291. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  292. package/packages/public-runtime/src/integration/events.ts +701 -0
  293. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  294. package/packages/public-runtime/src/integration/index.ts +90 -0
  295. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  296. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  297. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  298. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  299. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  300. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  301. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  302. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  303. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  304. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  305. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  306. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  307. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  308. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  309. package/packages/public-runtime/src/integration/observability.ts +451 -0
  310. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  311. package/packages/public-runtime/src/integration/phases.ts +182 -0
  312. package/packages/public-runtime/src/integration/plan.ts +112 -0
  313. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  314. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  315. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  316. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  317. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  318. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  319. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  320. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  321. package/packages/public-runtime/src/integration/result.ts +239 -0
  322. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  323. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  324. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  325. package/packages/public-runtime/src/integration/version.ts +92 -0
  326. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  327. package/packages/public-runtime/src/membership.ts +2 -2
  328. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  329. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  330. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  331. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  332. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  333. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  334. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  335. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  336. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  337. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  338. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  339. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  340. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  341. package/packages/public-runtime/src/static-bundle.ts +5 -5
  342. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  343. package/packages/public-runtime/src/tenant.ts +45 -9
  344. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  345. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  346. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  347. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  348. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  349. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  350. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  351. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  352. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  353. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  354. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  355. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  356. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  357. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  358. package/scripts/dev/file-browser.mjs +2 -2
  359. package/scripts/dev/file-writer.mjs +2 -2
  360. package/scripts/dev/integration-guardrails.mjs +498 -0
  361. package/scripts/dev/safe-path.mjs +2 -2
  362. package/scripts/dev/tenant-source-root.mjs +1 -1
  363. package/scripts/tot-dev.mjs +2 -2
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Hosted /dev activity log (hosted-dev-activity-bridge). Polls
3
+ * GET /api/dev/activity — a translated, file-level feed of what the local dev
4
+ * loop reported — and renders it newest-first. Also drives the "your edit
5
+ * landed" celebration: the first poll primes a baseline (sessionStorage, same
6
+ * survive-a-reload rationale as the local stepper's editBase), and any LATER
7
+ * event past that baseline triggers the banner + a lightweight confetti
8
+ * burst. POLL (not SSE): Workers are stateless per request, so a redeploy
9
+ * mid-session needs no reconnect logic here. Imported from a processed
10
+ * `<script>` in cockpit.astro (ADR 0013), rendered only when hostedActivity
11
+ * (never alongside the isLocalDevLoop-gated local scripts). basePath is read
12
+ * from `document.body.dataset`.
13
+ */
14
+ export function initHostedActivity(): void {
15
+ const root = document.getElementById("hostedActivity");
16
+ if (!root) return;
17
+
18
+ // The request's tenant prefix ("" host-routed, "/<tenant>" path-routed). Every
19
+ // /api/dev/* call below carries it so the activity feed + the CLI sign-in-code mint
20
+ // run in THIS store's scope (locals.tenant on the API request) — the same store this
21
+ // /<tenant>/cockpit rendered for, never the session's first membership.
22
+ const basePath = document.body.dataset.cockpitBasePath || "";
23
+
24
+ // ?reset — a TRUE reset for re-testing the onboarding loop: clear THIS browser's
25
+ // stepper progress AND the server-side KV (live-runtime record + activity buffer),
26
+ // then reload clean. Clearing the server too means a stopped local loop stops reading
27
+ // as "up" immediately instead of lingering until the runtime TTL/live-window expires.
28
+ try {
29
+ if (/[?&]reset\b/.test(location.search)) {
30
+ ["tot.hosted.steps", "tot.hosted.current", "tot.hosted.editBase"].forEach((k) => {
31
+ try { window.sessionStorage.removeItem(k); } catch (e) {}
32
+ });
33
+ let reloaded = false;
34
+ const doneReset = () => { if (reloaded) return; reloaded = true; location.replace(location.pathname); };
35
+ setTimeout(doneReset, 2000); // fallback so a hung request still lands us fresh
36
+ try { fetch(basePath + "/api/dev/activity", { method: "DELETE", cache: "no-store" }).then(doneReset, doneReset); }
37
+ catch (e) { doneReset(); }
38
+ return; // the post-clear reload re-runs this script against a fresh, empty state
39
+ }
40
+ } catch (e) {}
41
+
42
+ const tableEl: any = document.getElementById("haTable");
43
+ const bodyEl: any = document.getElementById("haBody");
44
+ const emptyEl: any = document.getElementById("haEmpty");
45
+
46
+ // Bridge strip — the rail's power source, fed by the SAME poll's `runtime` object
47
+ // (no second poller). `runtime` is { cliVersion, runnerVersion, url, aliveAt, live }
48
+ // reported by the developer's local `tot` CLI; absent until its data layer lands,
49
+ // so every read here is defensive (missing runtime -> the strip stays hidden).
50
+ const bridge: any = document.getElementById("bridge");
51
+ const bUrl: any = document.getElementById("bUrl");
52
+ const bGo: any = document.getElementById("bGo");
53
+ const bKicker = document.getElementById("bKicker");
54
+ const bHint: any = document.getElementById("bHint");
55
+ const bDiag: any = document.getElementById("bDiag");
56
+ const bTxt: any = document.getElementById("bTxt");
57
+ const bObstacle: any = document.getElementById("bObstacle");
58
+ const bObTitle = document.getElementById("bObTitle");
59
+ const bObMsg = document.getElementById("bObMsg");
60
+ const bObCmd: any = document.getElementById("bObCmd");
61
+ const bObNote = document.getElementById("bObNote");
62
+ const doneRunMeta = document.getElementById("doneRunMeta");
63
+ let sawLive = false;
64
+ function updateBridge(rt: any, obstacle: any) {
65
+ if (!bridge) return;
66
+ const live = !!(rt && rt.live);
67
+ if (live) sawLive = true;
68
+ // Obstacle lane (v2 §6): a failed `tot start` is the ONE reason the strip
69
+ // shows BEFORE the loop was ever live this page-view — the developer needs the
70
+ // fix, not a silent "watching…". The server returns `obstacle` only when there
71
+ // is no live heartbeat, so a live loop supersedes it. Fill from server copy.
72
+ if (obstacle && !live) {
73
+ bridge.hidden = false;
74
+ bridge.setAttribute("data-live", "obstacle");
75
+ if (bTxt) bTxt.hidden = true;
76
+ if (bDiag) bDiag.hidden = true;
77
+ if (bGo) bGo.hidden = true;
78
+ if (bHint) bHint.hidden = true;
79
+ if (bObstacle) bObstacle.hidden = false;
80
+ if (bObTitle) bObTitle.textContent = obstacle.title || "Setup hit a snag";
81
+ if (bObMsg) bObMsg.textContent = obstacle.message || "";
82
+ if (bObCmd) {
83
+ if (obstacle.fixCommand) { bObCmd.textContent = obstacle.fixCommand; bObCmd.hidden = false; }
84
+ else bObCmd.hidden = true;
85
+ }
86
+ if (bObNote) bObNote.textContent = obstacle.fixNote || "";
87
+ return;
88
+ }
89
+ // Back to the normal live/quiet strip: restore the pieces the obstacle state hid.
90
+ if (bObstacle) bObstacle.hidden = true;
91
+ if (bTxt) bTxt.hidden = false;
92
+ if (!rt || !rt.url) { bridge.hidden = true; return; }
93
+ // On FIRST arrival, a stale runtime record (offline, never seen live this
94
+ // page-view — e.g. left over from a prior run, still within the heartbeat TTL)
95
+ // must NOT show — let Step 1 "Get your store running" do the work. Only surface
96
+ // the strip once it's live, or after it's gone quiet HAVING been live this page-view.
97
+ if (!live && !sawLive) { bridge.hidden = true; return; }
98
+ bridge.hidden = false;
99
+ bridge.setAttribute("data-live", live ? "1" : "0");
100
+ if (bUrl) { bUrl.textContent = rt.url; bUrl.href = rt.url; }
101
+ // No point offering "Open your store" to a server that stopped answering.
102
+ if (bGo) { bGo.href = rt.url; bGo.hidden = !live; }
103
+ if (bKicker) bKicker.textContent = live ? "Live on your machine" : "Went quiet";
104
+ if (bHint) bHint.hidden = live; // recovery hint only while offline
105
+ if (doneRunMeta) doneRunMeta.textContent = rt.url; // the collapsed Step-1 row's meta
106
+ if (bDiag) {
107
+ if (rt.cliVersion) {
108
+ bDiag.textContent = "tot CLI v" + rt.cliVersion + (rt.runnerVersion ? " · runner v" + rt.runnerVersion : "");
109
+ bDiag.hidden = false;
110
+ } else {
111
+ bDiag.hidden = true;
112
+ }
113
+ }
114
+ updateEditHint(rt); // suggest the exact "open this file" command on Step 2
115
+ }
116
+
117
+ // Item 6 — once the CLI reports the developer's $EDITOR, show the EXACT command to
118
+ // open the first file to edit (e.g. `code content/home.html`). Without one, guess
119
+ // from the browser's OS (a reasonable proxy for the local dev machine in the common
120
+ // case of viewing the cockpit on the same laptop) and default to VS Code (`code`) —
121
+ // the most common case — with an alternate + AI-agent note underneath.
122
+ const editOpenCmd = document.getElementById("editOpenCmd");
123
+ const editOpenHint = document.getElementById("editOpenHint");
124
+ function platformOpenDirCmd(dirArg: string) {
125
+ const plat = String(((navigator as any).userAgentData && (navigator as any).userAgentData.platform) || navigator.platform || navigator.userAgent || "");
126
+ if (/win/i.test(plat)) return "explorer " + dirArg;
127
+ if (/mac/i.test(plat)) return "open " + dirArg;
128
+ return "xdg-open " + dirArg;
129
+ }
130
+ function updateEditHint(rt: any) {
131
+ if (!editOpenCmd) return;
132
+ const editor = rt && typeof rt.editor === "string" ? rt.editor : "";
133
+ const cwd = rt && typeof rt.cwd === "string" ? rt.cwd : "";
134
+ // Prefer the FULL path (works from any directory) when the CLI reports the
135
+ // project dir; else a project-relative path with a "run it from there" note.
136
+ const file = cwd ? (cwd.replace(/\/+$/, "") + "/content/home.html") : "content/home.html";
137
+ editOpenCmd.textContent = (editor || "code") + " " + file;
138
+ if (editOpenHint) {
139
+ if (editor) {
140
+ editOpenHint.textContent = cwd
141
+ ? "Run that anywhere to open the file — then change a line and save."
142
+ : "Run that from your project directory — then change a line and save.";
143
+ } else {
144
+ const altCmd = platformOpenDirCmd(cwd || ".");
145
+ editOpenHint.textContent = "Using VS Code? Run that. Prefer Cursor, another editor, or an AI coding " +
146
+ "agent like Claude? Same idea — open the folder (`" + altCmd + "`) or file above and edit there. " +
147
+ "Set $EDITOR and we'll show your exact command instead.";
148
+ }
149
+ }
150
+ }
151
+
152
+ const SS = window.sessionStorage;
153
+ function load(k: string, d: any) { try { const v = SS.getItem(k); return v === null ? d : JSON.parse(v); } catch (e) { return d; } }
154
+ function put(k: string, v: any) { try { SS.setItem(k, JSON.stringify(v)); } catch (e) {} }
155
+
156
+ let baseline = load("tot.hosted.editBase", null); // null = not yet primed by a first poll
157
+ let expanded = false; // activity list: last 3 by default, expand to all
158
+ // Have we seen the store go live during THIS PAGE VIEW? In-memory ONLY (not
159
+ // persisted): a fresh land or a reload starts false, so a stale/offline record can
160
+ // never greet the developer with "went quiet" — Step 1 owns that moment. The
161
+ // recovery ("went quiet") state shows only after we actually watched it live → drop.
162
+
163
+ // Guided accordion. Each step opens with its content; when its activity is detected
164
+ // it animates CLOSED to a reopenable done-bar, and a single "Next Up" panel at the
165
+ // bottom expands to tease the following step with ONE CTA that opens it.
166
+ // steps = completion flags {run, edit}
167
+ // current = the OPEN step key, or null between steps (Next Up showing)
168
+ // Both persisted so a reload restores the exact place in the flow.
169
+ const steps = load("tot.hosted.steps", { run: false, edit: false });
170
+ // Live-driven flow. current is the EXPLICITLY-opened step ("run" when Step 1 was
171
+ // reopened, "edit"/"next" for those) or null — the default view, where Step 1's
172
+ // open/minimized state is driven by whether localhost is LIVE. NOT persisted: a
173
+ // refresh lands in this default view (progress in steps still persists).
174
+ let current: string | null = null;
175
+ let liveNow = false; // is localhost live right now? set each poll from runtime.live
176
+ const bodies: any = {
177
+ run: document.getElementById("stage-run"),
178
+ edit: document.getElementById("hosted-stage-edit"),
179
+ next: document.getElementById("hosted-stage-next"),
180
+ };
181
+ // Rail station elements — the reskin's DOM (applyView targets these).
182
+ const stEls: any = {
183
+ run: document.getElementById("st-run"),
184
+ edit: document.getElementById("st-edit"),
185
+ next: document.getElementById("st-next"),
186
+ };
187
+ const segRun: any = document.getElementById("seg-run");
188
+ const segEdit: any = document.getElementById("seg-edit");
189
+ const doneRunEl: any = document.getElementById("done-run");
190
+ const doneEditEl: any = document.getElementById("done-edit");
191
+ const doneEditMeta = document.getElementById("doneEditMeta");
192
+ const lockEditEl: any = document.getElementById("lock-edit");
193
+ const lockNextEl: any = document.getElementById("lock-next");
194
+ const teaseEditEl: any = document.getElementById("tease-edit");
195
+ const teaseNextEl: any = document.getElementById("tease-next");
196
+ const quietNoteEl: any = document.getElementById("quietNote");
197
+
198
+ // Persisted state mutations funnel through here so the sessionStorage write can
199
+ // never be forgotten at a call site (progress in steps + the edit baseline are
200
+ // the only things that survive a reload).
201
+ function completeStep(key: string) {
202
+ if (steps[key]) return false; // already done — no-op
203
+ steps[key] = true; put("tot.hosted.steps", steps);
204
+ return true; // "just completed" — caller may celebrate
205
+ }
206
+ function setBaseline(v: number) { baseline = v; put("tot.hosted.editBase", baseline); }
207
+
208
+ // ============================================================================
209
+ // STATE → VIEW → DOM. The flow's behaviour is split into three layers so the
210
+ // rules live in ONE readable place and every render goes through one path:
211
+ // 1. derive — computeView(): pure, reads state (steps/current/liveNow),
212
+ // returns a view model. EVERY visibility/emphasis rule is here.
213
+ // 2. apply — applyView(): the ONLY code that mutates the rail DOM (station
214
+ // statuses, segment lit/flow, step bodies, done rows, locked
215
+ // labels, teasers, quiet note, activity).
216
+ // 3. renderSteps(): apply(compute()). Call after ANY state change — never poke
217
+ // an element directly from a handler or the poll.
218
+ // The signal-rail reskin kept layer 1's RULES verbatim (only added derived
219
+ // fields) and rewrote applyView() + the markup/CSS it targets.
220
+ // ============================================================================
221
+
222
+ // --- derive ---------------------------------------------------------------
223
+ function unlocked(step: string) {
224
+ return step === "run" || (step === "edit" && steps.run) || (step === "next" && steps.edit);
225
+ }
226
+ // Step 1 is OPEN while you still need to get the store running (localhost not live) or
227
+ // when you explicitly reopened it; it MINIMIZES to a done row once localhost is live —
228
+ // in opposition to revealing the Step-2 teaser.
229
+ function step1Open() {
230
+ return current === "run" || (current === null && !(steps.run && liveNow));
231
+ }
232
+ // The complete view model — no DOM access, so the rules can be read top to bottom:
233
+ // open which step BODY is expanded {run, edit, next}
234
+ // seg which rail segment is LIT {run, edit}
235
+ // flow do lit segments flow downward (live) boolean
236
+ // stRun/Edit/Next each station's status "open" | "done" | "next" | "locked"
237
+ // teaseEdit/Next is that step's forward teaser shown (live-gated)
238
+ // doneRun/Edit is that step's collapsed done row shown
239
+ // lockEdit/Next is that step's locked label shown
240
+ // quietNote the Step-1 "went quiet" note (re-inflated after a drop)
241
+ // showActivity is "Your activity" visible (NEVER while Step 1 is open)
242
+ function computeView() {
243
+ const s1 = step1Open();
244
+ // Forward teasers show ONLY while localhost is live — a forward CTA only makes
245
+ // sense while the loop is alive. When the loop goes quiet Step 1 re-inflates to own
246
+ // that moment, so the teaser retracts. Either is also hidden while the step it
247
+ // teases is already open.
248
+ const teaseEdit = steps.run && !steps.edit && liveNow && current !== "edit";
249
+ const teaseNext = steps.edit && liveNow && current !== "next";
250
+ return {
251
+ open: { run: s1, edit: current === "edit", next: current === "next" },
252
+ seg: { run: steps.run, edit: steps.edit },
253
+ flow: liveNow,
254
+ stRun: s1 ? "open" : "done",
255
+ stEdit: steps.edit ? "done" : (current === "edit") ? "open" : teaseEdit ? "next" : "locked",
256
+ stNext: (current === "next") ? "open" : teaseNext ? "next" : "locked",
257
+ teaseEdit: teaseEdit,
258
+ teaseNext: teaseNext,
259
+ doneRun: !s1 && steps.run,
260
+ doneEdit: steps.edit && current !== "edit",
261
+ lockEdit: !steps.run,
262
+ lockNext: !steps.edit,
263
+ quietNote: s1 && steps.run && !liveNow && sawLive,
264
+ showActivity: !s1 && steps.run,
265
+ };
266
+ }
267
+
268
+ // --- apply (targets the rail DOM; the rules live in computeView above) -----
269
+ function applyView(v: ReturnType<typeof computeView>) {
270
+ if (stEls.run) stEls.run.setAttribute("data-st", v.stRun);
271
+ if (stEls.edit) stEls.edit.setAttribute("data-st", v.stEdit);
272
+ if (stEls.next) stEls.next.setAttribute("data-st", v.stNext);
273
+ if (segRun) { segRun.setAttribute("data-lit", v.seg.run ? "1" : "0"); segRun.setAttribute("data-flow", v.flow ? "1" : "0"); }
274
+ if (segEdit) { segEdit.setAttribute("data-lit", v.seg.edit ? "1" : "0"); segEdit.setAttribute("data-flow", v.flow ? "1" : "0"); }
275
+ if (bodies.run) bodies.run.hidden = !v.open.run;
276
+ if (bodies.edit) bodies.edit.hidden = !v.open.edit;
277
+ if (bodies.next) bodies.next.hidden = !v.open.next;
278
+ if (doneRunEl) doneRunEl.hidden = !v.doneRun;
279
+ if (doneEditEl) doneEditEl.hidden = !v.doneEdit;
280
+ if (lockEditEl) lockEditEl.hidden = !v.lockEdit;
281
+ if (lockNextEl) lockNextEl.hidden = !v.lockNext;
282
+ if (teaseEditEl) teaseEditEl.hidden = !v.teaseEdit;
283
+ if (teaseNextEl) teaseNextEl.hidden = !v.teaseNext;
284
+ if (quietNoteEl) quietNoteEl.hidden = !v.quietNote;
285
+ if (root) (root as any).hidden = !v.showActivity;
286
+ }
287
+ // Single render entry point — derive, then apply. (Named renderSteps so the
288
+ // existing call sites keep working; distinct from the activity render(events).)
289
+ function renderSteps() { applyView(computeView()); }
290
+
291
+ function expandStage(el: any) {
292
+ if (!el) return;
293
+ el.hidden = false;
294
+ el.classList.add("expanding");
295
+ setTimeout(() => { if (el && el.classList) el.classList.remove("expanding"); }, 420);
296
+ }
297
+
298
+ // Open a step (from a teaser CTA or a collapsed done row) — animates it in.
299
+ function goTo(step: string) {
300
+ if (!unlocked(step)) return;
301
+ current = step;
302
+ renderSteps();
303
+ expandStage(bodies[step]);
304
+ window.scrollTo({ top: 0, behavior: "smooth" });
305
+ }
306
+
307
+ // Rail click wiring: done rows reopen their step; teaser CTAs open the teased step.
308
+ if (doneRunEl) doneRunEl.addEventListener("click", () => { goTo("run"); });
309
+ if (doneEditEl) doneEditEl.addEventListener("click", () => { goTo("edit"); });
310
+ const ctaEdit = document.getElementById("cta-edit");
311
+ if (ctaEdit) ctaEdit.addEventListener("click", () => { goTo("edit"); });
312
+ const ctaNext = document.getElementById("cta-next");
313
+ if (ctaNext) ctaNext.addEventListener("click", () => { goTo("next"); });
314
+
315
+ // Absolute clock time with seconds (e.g. "2:57:14 PM") — a fixed point, not a
316
+ // churning duration. Full date+time stays on the hover title (render()).
317
+ function rel(ts: number | string): string {
318
+ try {
319
+ return new Date(Number(ts)).toLocaleTimeString([], { hour: "numeric", minute: "2-digit", second: "2-digit" });
320
+ } catch (x) {
321
+ return "";
322
+ }
323
+ }
324
+
325
+ // Toast — reuses the shared #toast element (also driven by the copy script).
326
+ const toastEl = document.getElementById("toast");
327
+ const toastMsgEl = document.getElementById("toastMsg");
328
+ let toastT: ReturnType<typeof setTimeout>;
329
+ function toast(msg: string) {
330
+ if (!toastEl || !toastMsgEl) return;
331
+ toastMsgEl.textContent = msg;
332
+ toastEl.classList.add("show");
333
+ clearTimeout(toastT);
334
+ toastT = setTimeout(() => { toastEl.classList.remove("show"); }, 2600);
335
+ }
336
+
337
+ // Celebration — replaces the full-screen confetti. Stamps the Step-2 node (spark
338
+ // burst + stamp, via the .celebrating class) and shows a toast. Reduced-motion
339
+ // disables the burst/stamp in CSS; the toast still shows.
340
+ function celebrate(file?: string) {
341
+ const st = stEls.edit;
342
+ if (st) {
343
+ st.classList.remove("celebrating");
344
+ void st.offsetWidth; // restart the burst animation
345
+ st.classList.add("celebrating");
346
+ setTimeout(() => { if (st && st.classList) st.classList.remove("celebrating"); }, 1100);
347
+ }
348
+ if (doneEditMeta && file) doneEditMeta.textContent = file;
349
+ toast("Saved " + (file || "your file") + " — your store reloaded. That's the loop.");
350
+ }
351
+
352
+ const toggleEl: any = document.getElementById("haToggle");
353
+ const VISIBLE = 3; // show the latest 3 by default; expand for the rest
354
+ if (toggleEl) {
355
+ toggleEl.addEventListener("click", () => { expanded = !expanded; render(lastEvents); });
356
+ }
357
+ let lastEvents: any[] = [];
358
+
359
+ function render(events: any[]) {
360
+ lastEvents = events;
361
+ // Item 3 — drop the "up"/"running locally" pings; the live widget at the top
362
+ // already conveys that. Only real FILE activity belongs in the table, and each
363
+ // field is its own <td> so time / action / file never run together.
364
+ const real = (events || []).filter((e) => e && e.event !== "up");
365
+ Array.prototype.forEach.call(bodyEl.querySelectorAll(".ha-row"), (n: any) => { n.remove(); });
366
+ if (!real.length) {
367
+ emptyEl.hidden = false;
368
+ if (tableEl) tableEl.hidden = true;
369
+ if (toggleEl) toggleEl.hidden = true;
370
+ return;
371
+ }
372
+ emptyEl.hidden = true;
373
+ if (tableEl) tableEl.hidden = false;
374
+ const newestFirst = real.slice().reverse();
375
+ const shown = expanded ? newestFirst : newestFirst.slice(0, VISIBLE);
376
+ shown.forEach((e, i) => {
377
+ const tr = document.createElement("tr");
378
+ tr.className = i === 0 ? "ha-row is-new" : "ha-row"; // accent the newest row
379
+ const when = document.createElement("td");
380
+ when.className = "ha-when";
381
+ when.textContent = rel(e.at);
382
+ try { when.title = new Date(e.at).toLocaleString(); } catch (x) {} // exact time on hover
383
+ const desc = document.createElement("td");
384
+ desc.className = "ha-desc";
385
+ desc.textContent = e.description;
386
+ const file = document.createElement("td");
387
+ file.className = "ha-file";
388
+ file.textContent = e.file || ""; // the concrete file name, e.g. content/home.html
389
+ tr.appendChild(when); tr.appendChild(desc); tr.appendChild(file);
390
+ bodyEl.appendChild(tr);
391
+ });
392
+ if (toggleEl) {
393
+ if (real.length > VISIBLE) {
394
+ toggleEl.hidden = false;
395
+ toggleEl.textContent = expanded ? "Show fewer" : "Show all " + real.length;
396
+ } else {
397
+ toggleEl.hidden = true;
398
+ }
399
+ }
400
+ }
401
+
402
+ // Adaptive poll cadence + visibility pause (2026-07-15 KV-quota incident: a
403
+ // single backgrounded tab, polling flat-out every 3s with no pause, burned
404
+ // through the ENTIRE account-wide daily KV read quota by itself and took
405
+ // down real customer traffic with 500s). Two rules:
406
+ // 1. HIDDEN tab → don't poll at all. A backgrounded/minimized tab can't be
407
+ // "watching" the loop; resume (with an immediate catch-up poll) the
408
+ // instant it becomes visible again.
409
+ // 2. VISIBLE but the loop is already fully celebrated (steps.run AND
410
+ // steps.edit) → the high-stakes "is it working yet" moment has passed,
411
+ // so back off to a slow cadence. Still visible AND still mid-loop →
412
+ // keep the fast cadence (this is the one case where sub-second-feeling
413
+ // responsiveness genuinely matters — a real developer staring at the
414
+ // screen waiting to see their save land).
415
+ const FAST_MS = 3000; // actively watching for the loop to prove itself
416
+ const SLOW_MS = 20000; // loop already celebrated — just an occasional glance
417
+ let pollTimer: ReturnType<typeof setTimeout> | null = null;
418
+
419
+ function nextDelay() {
420
+ return (steps.run && steps.edit) ? SLOW_MS : FAST_MS;
421
+ }
422
+
423
+ function scheduleNext() {
424
+ if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
425
+ if (document.hidden) return; // stay fully paused; visibilitychange resumes us
426
+ pollTimer = setTimeout(poll, nextDelay());
427
+ }
428
+
429
+ function poll() {
430
+ fetch(basePath + "/api/dev/activity", { cache: "no-store" })
431
+ .then((r) => (r.ok ? r.json() : Promise.reject()))
432
+ .then((d) => {
433
+ const events = (d && d.events) || [];
434
+ const rt = d && d.runtime;
435
+ const isLive = !!(rt && rt.live);
436
+ liveNow = isLive; // drives Step 1 minimize + the Step-2 teaser visibility below
437
+ updateBridge(rt, d && d.obstacle); // same poll drives the bridge strip (+ obstacle lane)
438
+ render(events);
439
+ // b1 fix — Step-2 completion must key off the SAME events the feed shows:
440
+ // REAL file edits (event !== "up"), NOT the single newest buffer entry. The
441
+ // local loop interleaves recurring "up" liveness pings into this one ring
442
+ // buffer, so the newest event is frequently an "up" ping even right after a
443
+ // save. The old gate read events[last]: when an "up" ping landed AFTER the
444
+ // edit it (1) made lastIsEdit false so completeStep("edit") never fired, and
445
+ // (2) still advanced the baseline watermark past the edit's timestamp —
446
+ // permanently stranding Step 2 even though the save rendered in "Your
447
+ // activity". Filter to real edits (mirroring render()'s filter) and advance
448
+ // the watermark only from real edits, so a liveness ping can neither satisfy
449
+ // nor poison the gate.
450
+ const realEdits = events.filter((e: any) => e && e.event !== "up");
451
+ const lastEdit = realEdits.length ? realEdits[realEdits.length - 1] : null;
452
+ const latestEdit = lastEdit ? lastEdit.at : 0;
453
+ // Step 1 "Get running" completes once localhost reports LIVE (or any activity
454
+ // arrives). Once complete, renderSteps() minimizes Step 1 and reveals the Step-2 nudge.
455
+ if (isLive || events.length) completeStep("run");
456
+ if (baseline === null) {
457
+ setBaseline(latestEdit); // first poll primes the watermark from real edits — no edit inferred yet
458
+ } else if (latestEdit > baseline) {
459
+ setBaseline(latestEdit);
460
+ // A real FILE edit landed after the watermark → Step 2 completes (once).
461
+ // completeStep flips + persists once, returning true on that first poll only.
462
+ if (completeStep("edit")) {
463
+ celebrate(lastEdit && lastEdit.file);
464
+ if (current === "edit") current = null; // minimize Step 2 → reveal Step-3 teaser
465
+ }
466
+ }
467
+ // ALWAYS re-render so a localhost drop hides the Step-2 nudge + reopens Step 1.
468
+ renderSteps();
469
+ })
470
+ .catch(() => {})
471
+ .then(scheduleNext); // reschedule (or not, if now hidden) regardless of outcome
472
+ }
473
+
474
+ document.addEventListener("visibilitychange", () => {
475
+ if (document.hidden) {
476
+ if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
477
+ } else {
478
+ poll(); // catch up immediately; poll() reschedules itself at the end
479
+ }
480
+ });
481
+
482
+ // Item 7/10 — Step 3 "Submit for preview" opens the "coming soon" dialog (the
483
+ // compliance-reviewed preview pipeline isn't wired into this panel yet).
484
+ const submitBtn = document.getElementById("submitPreview");
485
+ const comingSoon: any = document.getElementById("comingSoon");
486
+ if (submitBtn && comingSoon) {
487
+ submitBtn.addEventListener("click", () => {
488
+ if (typeof comingSoon.showModal === "function") comingSoon.showModal();
489
+ else comingSoon.setAttribute("open", "");
490
+ });
491
+ const csClose = document.getElementById("comingSoonClose");
492
+ if (csClose) csClose.addEventListener("click", () => {
493
+ if (typeof comingSoon.close === "function") comingSoon.close();
494
+ comingSoon.removeAttribute("open");
495
+ });
496
+ }
497
+
498
+ renderSteps();
499
+ if (!document.hidden) poll(); // if the tab starts hidden, visibilitychange kicks it off later
500
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Info popover: toggle on the ⓘ button, dismiss on outside click. Imported
3
+ * from a processed `<script>` in cockpit.astro.
4
+ */
5
+ export function initInfoPopover(): void {
6
+ const info1 = document.getElementById("info1");
7
+ if (!info1) return;
8
+ const openBtn = info1.querySelector(".info-btn");
9
+ if (!openBtn) return;
10
+ openBtn.addEventListener("click", (e) => { e.stopPropagation(); info1.classList.toggle("open"); });
11
+ document.addEventListener("click", (e) => { if (!info1.contains(e.target as Node)) info1.classList.remove("open"); });
12
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Copy the "connect the MCP" command (d3-unhold-dev-step0-connect-mcp). Static
3
+ * text (no minting), so unlike the setup-command copy button (copyCommand.ts)
4
+ * this button is visible from load. Imported from a processed `<script>` in
5
+ * cockpit.astro.
6
+ */
7
+ export function initMcpCopy(): void {
8
+ const copyIcon = document.getElementById("mcpCopyIcon");
9
+ const cmdText = document.getElementById("mcpCmdText");
10
+ const toast = document.getElementById("toast");
11
+ const toastMsg = document.getElementById("toastMsg");
12
+ if (!copyIcon || !cmdText) return;
13
+ let tT: ReturnType<typeof setTimeout>;
14
+ function ping(m: string) {
15
+ if (!toast || !toastMsg) return;
16
+ toastMsg.textContent = m; toast.classList.add("show");
17
+ clearTimeout(tT); tT = setTimeout(() => { toast.classList.remove("show"); }, 1900);
18
+ }
19
+ copyIcon.addEventListener("click", () => {
20
+ const text = cmdText.textContent || "";
21
+ const done = navigator.clipboard
22
+ ? navigator.clipboard.writeText(text).then(() => true).catch(() => false)
23
+ : Promise.resolve(false);
24
+ done.then((ok) => {
25
+ if (ok) {
26
+ copyIcon.setAttribute("data-copied", "1");
27
+ setTimeout(() => { copyIcon.removeAttribute("data-copied"); }, 1600);
28
+ ping("Copied");
29
+ } else {
30
+ ping("Select the command above to copy");
31
+ }
32
+ });
33
+ });
34
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Live loop Monitor — polls the local dev server's /__tot/dev-loop-status
3
+ * and renders the loopbar: server health, watched file, and the save→reload
4
+ * timeline. Imported from a processed `<script>` in cockpit.astro (ADR 0013),
5
+ * rendered only when isLocalDevLoop (the storefront's own `astro dev`, never
6
+ * the hosted panel). Broadcasts window.__totLoop + the "tot:loop-state"
7
+ * CustomEvent so the stepper and ask widgets share this one poll instead of
8
+ * each opening their own. All dynamic values go through textContent, never
9
+ * innerHTML.
10
+ */
11
+ declare global {
12
+ interface Window {
13
+ __totLoop?: Record<string, unknown>;
14
+ }
15
+ }
16
+
17
+ export function initMonitor(): void {
18
+ const bar: any = document.getElementById("loopbar");
19
+ if (!bar) return;
20
+ const elStatus: any = document.getElementById("lbStatus");
21
+ const elWatch: any = document.getElementById("lbWatch");
22
+ const elLast: any = document.getElementById("lbLast");
23
+ const elServer: any = document.getElementById("lbServer");
24
+ const elUrl: any = document.getElementById("lbUrl");
25
+ const elFile: any = document.getElementById("lbFile");
26
+ const elSave: any = document.getElementById("lbSave");
27
+ const elReload: any = document.getElementById("lbReload");
28
+ const head = document.getElementById("lbHead");
29
+
30
+ if (head) head.addEventListener("click", () => { bar.classList.toggle("open"); });
31
+
32
+ function rel(ts: number | string): string {
33
+ const d = Math.max(0, Math.round((Date.now() - Number(ts)) / 1000));
34
+ return d < 2 ? "just now" : d < 60 ? d + "s ago" : Math.floor(d / 60) + "m ago";
35
+ }
36
+ function setTs(el: any, ts: number | undefined, emptyText: string) {
37
+ if (ts) {
38
+ el.setAttribute("data-ts", String(ts));
39
+ el.classList.remove("dim");
40
+ el.textContent = rel(ts);
41
+ } else {
42
+ el.removeAttribute("data-ts");
43
+ el.classList.add("dim");
44
+ el.textContent = emptyText;
45
+ }
46
+ }
47
+ setInterval(() => {
48
+ [elSave, elReload].forEach((el) => {
49
+ const ts = el.getAttribute("data-ts");
50
+ if (ts) el.textContent = rel(ts);
51
+ });
52
+ }, 1000);
53
+
54
+ function render(s: any) {
55
+ bar.classList.add("live");
56
+ const port = s.port || null;
57
+ const url = port ? ("localhost:" + port + (s.tenant ? "/" + s.tenant + "/" : "/")) : "—";
58
+ elServer.textContent = s.server ? ("running" + (port ? " · port " + port : "")) : "not running";
59
+ elServer.classList.toggle("dim", !s.server);
60
+ elUrl.textContent = url;
61
+ elUrl.classList.toggle("dim", !port);
62
+ elFile.textContent = s.watchedFile || "content/*.html";
63
+ elFile.classList.toggle("dim", !s.watchedFile);
64
+ elWatch.textContent = s.watchedFile || "your content";
65
+ setTs(elSave, s.lastSaveAt, "no saves yet");
66
+ setTs(elReload, s.lastReloadAt, "waiting for a save");
67
+
68
+ const saved = s.lastSaveAt || 0;
69
+ const reloaded = s.lastReloadAt || 0;
70
+ let state, status, last;
71
+ if (!saved) {
72
+ state = "running"; status = "Running"; last = "reloads on save";
73
+ } else if (reloaded >= saved) {
74
+ state = "running"; status = "Running"; last = "reloaded";
75
+ } else if (Date.now() - saved < 3000) {
76
+ state = "reloading"; status = "Reloading…"; last = "change detected";
77
+ } else {
78
+ state = "stale"; status = "Stale"; last = "save not reflected — restart?";
79
+ }
80
+ bar.setAttribute("data-state", state);
81
+ elStatus.textContent = status;
82
+ elLast.textContent = last;
83
+ // Broadcast the snapshot so other consumers (stepper, Ask) share this
84
+ // one poll instead of each opening their own. window.__totLoop holds the
85
+ // latest for listeners that attach late.
86
+ window.__totLoop = s;
87
+ window.dispatchEvent(new CustomEvent("tot:loop-state", { detail: s }));
88
+ }
89
+ function offline() {
90
+ bar.classList.add("live");
91
+ bar.setAttribute("data-state", "stale");
92
+ elStatus.textContent = "Offline";
93
+ elLast.textContent = "dev server not reachable";
94
+ elServer.textContent = "not reachable";
95
+ const off = { server: false, offline: true };
96
+ window.__totLoop = off;
97
+ window.dispatchEvent(new CustomEvent("tot:loop-state", { detail: off }));
98
+ }
99
+ function poll() {
100
+ fetch("/__tot/dev-loop-status", { cache: "no-store" })
101
+ .then((r) => (r.ok ? r.json() : Promise.reject()))
102
+ .then(render)
103
+ .catch(offline);
104
+ }
105
+ poll();
106
+ setInterval(poll, 1000);
107
+ }