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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/apps/storefront/astro.config.mjs +3 -3
  2. package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +8 -3
  3. package/apps/storefront/env.d.ts +54 -5
  4. package/apps/storefront/integrations/materialize-guard.mjs +12 -14
  5. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  6. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  7. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  8. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  9. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  10. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  11. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  12. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  13. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  14. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  15. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  16. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  17. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  18. package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
  19. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  20. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  21. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  22. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  23. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  24. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  25. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  26. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  27. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  28. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  29. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  30. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  31. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  32. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  33. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  34. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  35. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  36. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  37. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  38. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  39. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  40. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  41. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  42. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  43. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  44. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  45. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  46. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  47. package/apps/storefront/src/components/Img.astro +1 -1
  48. package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
  49. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  50. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
  51. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  52. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  53. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  54. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  55. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  56. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  57. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  58. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  59. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  60. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  61. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  62. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  63. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  64. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  65. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  66. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  67. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  68. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  69. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  70. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  71. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  72. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  73. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  74. package/apps/storefront/src/config/resolver.ts +18 -10
  75. package/apps/storefront/src/layouts/Layout.astro +100 -20
  76. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  77. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
  78. package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
  79. package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
  80. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  81. package/apps/storefront/src/lib/apps/adminService.ts +10 -0
  82. package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
  83. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  84. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  85. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  86. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  87. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  88. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  89. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  90. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  91. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  92. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  93. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  94. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  95. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
  96. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
  97. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  98. package/apps/storefront/src/lib/apps/registryService.ts +264 -10
  99. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  100. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  101. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  102. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  103. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  104. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  105. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  106. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  107. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  108. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  109. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  110. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  111. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  112. package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
  113. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  114. package/apps/storefront/src/lib/blog/collection.ts +114 -17
  115. package/apps/storefront/src/lib/blog/markdown.ts +1 -1
  116. package/apps/storefront/src/lib/blog/provider.ts +76 -9
  117. package/apps/storefront/src/lib/blog/types.ts +9 -9
  118. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  119. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  120. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  121. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  122. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  123. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  124. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  125. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  126. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  127. package/apps/storefront/src/lib/content/index.ts +5 -5
  128. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  129. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  130. package/apps/storefront/src/lib/d1/catalog.ts +12 -0
  131. package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
  132. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  133. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  134. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  135. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  136. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  137. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  138. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  139. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  140. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  141. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  142. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  143. package/apps/storefront/src/lib/edgeCache.ts +18 -6
  144. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  145. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  146. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  147. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  148. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  149. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  150. package/apps/storefront/src/lib/membership/model.ts +1 -2
  151. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
  152. package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
  153. package/apps/storefront/src/lib/newsletter/send.ts +3 -3
  154. package/apps/storefront/src/lib/previewReload.ts +57 -1
  155. package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
  156. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  157. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  158. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  159. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  160. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  161. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  162. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  163. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  164. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  165. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  166. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  167. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  168. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  169. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  170. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  171. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  172. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  173. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  174. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  175. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  176. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  177. package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
  178. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  179. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  180. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  181. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  182. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  183. package/apps/storefront/src/lib/seo/alternates.ts +1 -1
  184. package/apps/storefront/src/lib/social/notify.ts +4 -4
  185. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  186. package/apps/storefront/src/lib/storyblok/provider.ts +203 -69
  187. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  188. package/apps/storefront/src/lib/the-build/provider.ts +9 -4
  189. package/apps/storefront/src/middleware/index.ts +273 -51
  190. package/apps/storefront/src/pages/[...slug].astro +9 -4
  191. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  192. package/apps/storefront/src/pages/admin/select.astro +96 -0
  193. package/apps/storefront/src/pages/admin.astro +16 -2
  194. package/apps/storefront/src/pages/api/activity.ts +8 -8
  195. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  196. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  197. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  198. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  199. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  200. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  201. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  202. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  203. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  204. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  205. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  206. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  207. package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
  208. package/apps/storefront/src/pages/blog/[slug].astro +5 -5
  209. package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
  210. package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
  211. package/apps/storefront/src/pages/blog/index.astro +2 -2
  212. package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
  213. package/apps/storefront/src/pages/cockpit.astro +61 -1362
  214. package/apps/storefront/src/pages/index.astro +10 -5
  215. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  216. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  217. package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
  218. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +42 -31
  219. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +8 -3
  220. package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +6 -2
  221. package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +6 -2
  222. package/apps/storefront/src/pages/style-guide/index.astro +19 -5
  223. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  224. package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
  225. package/apps/storefront/src/pages/the-build/index.astro +2 -2
  226. package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
  227. package/apps/storefront/src/styles/admin.css +27 -1
  228. package/apps/storefront/src/themes/tenants/index.ts +19 -7
  229. package/apps/storefront/tsconfig.json +1 -1
  230. package/package.json +1 -1
  231. package/packages/public-runtime/package.json +2 -1
  232. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  233. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  234. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  235. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  236. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  237. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  238. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  239. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  240. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  241. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  242. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  243. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  244. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  245. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  246. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  247. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  248. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  249. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  250. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  251. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  252. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  253. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  254. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  255. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  256. package/packages/public-runtime/src/activity/catalog.ts +41 -37
  257. package/packages/public-runtime/src/activity/event.ts +6 -6
  258. package/packages/public-runtime/src/activity/index.ts +2 -2
  259. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  260. package/packages/public-runtime/src/candidate-index.ts +42 -8
  261. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  262. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  263. package/packages/public-runtime/src/checkout-style.ts +7 -7
  264. package/packages/public-runtime/src/checkout.ts +3 -3
  265. package/packages/public-runtime/src/chrome.ts +777 -0
  266. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  267. package/packages/public-runtime/src/csp.ts +99 -0
  268. package/packages/public-runtime/src/customization-runtime.ts +32 -1
  269. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  270. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  271. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  272. package/packages/public-runtime/src/evidence.ts +288 -0
  273. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  274. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  275. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  276. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  277. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  278. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  279. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  280. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  281. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  282. package/packages/public-runtime/src/index.ts +16 -1
  283. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  284. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  285. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  286. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  287. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  288. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  289. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  290. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  291. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  292. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  293. package/packages/public-runtime/src/integration/csp.ts +105 -0
  294. package/packages/public-runtime/src/integration/digest.ts +43 -0
  295. package/packages/public-runtime/src/integration/environment.ts +228 -0
  296. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  297. package/packages/public-runtime/src/integration/events.ts +701 -0
  298. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  299. package/packages/public-runtime/src/integration/index.ts +90 -0
  300. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  301. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  302. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  303. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  304. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  305. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  306. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  307. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  308. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  309. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  310. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  311. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  312. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  313. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  314. package/packages/public-runtime/src/integration/observability.ts +451 -0
  315. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  316. package/packages/public-runtime/src/integration/phases.ts +182 -0
  317. package/packages/public-runtime/src/integration/plan.ts +112 -0
  318. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  319. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  320. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  321. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  322. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  323. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  324. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  325. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  326. package/packages/public-runtime/src/integration/result.ts +239 -0
  327. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  328. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  329. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  330. package/packages/public-runtime/src/integration/version.ts +92 -0
  331. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  332. package/packages/public-runtime/src/membership.ts +2 -2
  333. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  334. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  335. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  336. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  337. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  338. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  339. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  340. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  341. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  342. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  343. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  344. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  345. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  346. package/packages/public-runtime/src/static-bundle.ts +5 -5
  347. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  348. package/packages/public-runtime/src/tenant.ts +136 -9
  349. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  350. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  351. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  352. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  353. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  354. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  355. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  356. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  357. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  358. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  359. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  360. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  361. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  362. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  363. package/scripts/build/copy-tenant-assets.mjs +12 -5
  364. package/scripts/dev/ai-edit.mjs +2 -1
  365. package/scripts/dev/file-browser.mjs +2 -2
  366. package/scripts/dev/file-writer.mjs +2 -2
  367. package/scripts/dev/integration-guardrails.mjs +498 -0
  368. package/scripts/dev/safe-path.mjs +2 -2
  369. package/scripts/dev/tenant-source-root.mjs +3 -2
  370. package/scripts/tot-dev.mjs +5 -4
@@ -0,0 +1,75 @@
1
+ /**
2
+ * mergeSupportEscalation — the B3 escalation seam, sibling to
3
+ * {@link mergeHealthBanner} (top-of-tab summary) and {@link mergeRowFindings}
4
+ * (per-row chip/CTA). All three reduce the SAME `/api/changes` body through the
5
+ * SAME pure analyzer (`lib/preview/mergeDoctor.ts`); this one asks the analyzer's
6
+ * own {@link supportContext} whether ANY unresolved PLATFORM-owned fault remains
7
+ * (a retry didn't clear it) and, if so, pre-fills the store's EXISTING support
8
+ * channel — the `success@tokenoftrust.com` mailto the Publish tab already uses for
9
+ * its paywall upsell — with that structured payload.
10
+ *
11
+ * `supportContext()` is the ownership gate, and we TRUST it: it returns null for a
12
+ * fully developer-fixable queue (only `ownership:"platform"` findings escalate), so
13
+ * a free-text "I'm stuck" is impossible when the person could have self-served —
14
+ * `show` is FALSE in exactly that case. No new fetch, no new analysis pass, no
15
+ * re-implemented ownership check.
16
+ *
17
+ * One subtlety the sibling reducers sidestep by post-filtering (which we can't,
18
+ * since we must trust `supportContext` whole): a MISSING branches read makes the
19
+ * analyzer emit a platform-owned `BRANCH_REPORT_UNAVAILABLE`. That is an artifact
20
+ * of not analyzing the branch plane here, NOT a real fault, so when no
21
+ * `branchesBody` is supplied we present the branch report as empty-but-loaded (not
22
+ * "unavailable") — its absence must never manufacture an escalation.
23
+ */
24
+ import {
25
+ analyzeMerge,
26
+ supportContext,
27
+ type ChangesInput,
28
+ type BranchesInput,
29
+ } from "@/lib/preview/mergeDoctor";
30
+
31
+ /** The support inbox — the SAME channel AdminPublishTab's paywall upsell mails. */
32
+ export const SUPPORT_EMAIL = "success@tokenoftrust.com";
33
+
34
+ export interface MergeSupportEscalation {
35
+ /** Offer escalation? True iff an unresolved platform-owned fault remains. */
36
+ show: boolean;
37
+ /** The pre-filled structured report (supportContext()); "" when `show` is false. */
38
+ body: string;
39
+ /** The escalation email subject; "" when `show` is false. */
40
+ subject: string;
41
+ /** Ready-to-use `mailto:` with subject + body pre-filled; "" when `show` is false. */
42
+ mailtoHref: string;
43
+ }
44
+
45
+ const HIDDEN: MergeSupportEscalation = { show: false, body: "", subject: "", mailtoHref: "" };
46
+
47
+ /**
48
+ * Reduce a `/api/changes` body (the payload the tab already holds) to the
49
+ * platform-fault escalation state. `branchesBody` is optional; branch-plane
50
+ * platform faults (e.g. an unreachable branch classification) are also
51
+ * platform-owned, so passing it lets those escalate too — omit it safely when the
52
+ * branches panel hasn't loaded, exactly as the sibling reducers do.
53
+ */
54
+ export function mergeSupportEscalation(
55
+ changesBody: ChangesInput["body"],
56
+ opts: { branchesBody?: BranchesInput["body"]; nowMs?: number } = {},
57
+ ): MergeSupportEscalation {
58
+ const result = analyzeMerge({
59
+ changes: { status: 200, body: changesBody ?? {} },
60
+ branches: opts.branchesBody
61
+ ? { status: 200, body: opts.branchesBody }
62
+ : { status: 200, body: { report: { rows: [] } } },
63
+ tenant: changesBody?.owner ?? null,
64
+ nowMs: opts.nowMs,
65
+ });
66
+
67
+ const body = supportContext(result);
68
+ if (body === null) return HIDDEN;
69
+
70
+ const subject = "Publish issue";
71
+ const mailtoHref = `mailto:${SUPPORT_EMAIL}?subject=${encodeURIComponent(
72
+ subject,
73
+ )}&body=${encodeURIComponent(body)}`;
74
+ return { show: true, body, subject, mailtoHref };
75
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Publish-button hard-block + stale-build indicator (Bug 3, publish-safety half).
3
+ *
4
+ * PURE + dep-free so the branching is unit-testable in `node` — the AdminPublishTab
5
+ * `<script is:inline>` glue that toggles the real button/DOM is a faithful mirror of
6
+ * this contract (the repo's documented split: logic in a tested module, rendered output
7
+ * verified on preview).
8
+ *
9
+ * The incident class: a ship PLAN is computed from the aggregate read-model, but the
10
+ * most recent aggregate REBUILD can fail INDEPENDENTLY (a red queue state, or tot-mcp
11
+ * `forward_failed`) AFTER a plan was cached — leaving a stale "shippable" plan that
12
+ * would publish a build the rebuild never actually produced. So Publish is HARD-BLOCKED
13
+ * whenever the aggregate is red (`queueState === "red"`), regardless of what the plan
14
+ * says — belt-and-suspenders over the plan's own gate — and the operator is told their
15
+ * live store is serving the last good (stale) build while the rebuild is broken.
16
+ */
17
+
18
+ export interface PublishGuardInput {
19
+ /** The aggregate's current queue state (`green`/`red`/`integrating`/`empty`/…). */
20
+ queueState: string | null | undefined;
21
+ /** A Publish POST is in flight (never re-enable across a mid-publish poll). */
22
+ aggregatePublishing: boolean;
23
+ /** Viewer holds ship capability. */
24
+ canShip: boolean;
25
+ /** The plan resolved to a shippable view (`Boolean(currentPlan)`). */
26
+ shippable: boolean;
27
+ /** The shippable plan is behind a paywall the viewer hasn't cleared. */
28
+ paywalled: boolean;
29
+ /** The reviewed build is already live. */
30
+ alreadyShipped: boolean;
31
+ /** Staged changes not yet published (drives the stale-build wording). */
32
+ aheadCount: number;
33
+ /**
34
+ * Human label for WHEN the currently-serving live build was produced (already
35
+ * formatted by the caller; null when unknown). Used only in the stale indicator.
36
+ */
37
+ servingBuildAtLabel: string | null;
38
+ }
39
+
40
+ export interface PublishGuardView {
41
+ /** Whether the Publish button must be disabled. */
42
+ disabled: boolean;
43
+ /** The Publish button's title/tooltip — the honest reason. */
44
+ title: string;
45
+ /**
46
+ * Non-null ⇒ render a "serving stale build" indicator with this text (the live
47
+ * store is serving an older build because the latest rebuild failed). Null ⇒ none.
48
+ */
49
+ staleIndicator: string | null;
50
+ }
51
+
52
+ /** Compute the Publish button's disabled/title/stale state from the aggregate + plan facts. */
53
+ export function computePublishGuard(input: PublishGuardInput): PublishGuardView {
54
+ const rebuildFailed = input.queueState === "red";
55
+
56
+ const disabled =
57
+ input.aggregatePublishing ||
58
+ !input.canShip ||
59
+ !input.shippable ||
60
+ input.paywalled ||
61
+ input.alreadyShipped ||
62
+ // HARD-BLOCK: a red aggregate means the latest rebuild failed — never publish it,
63
+ // even if a stale plan still reads shippable.
64
+ rebuildFailed;
65
+
66
+ const title = !input.canShip
67
+ ? "Requires an owner or admin sign-in"
68
+ : rebuildFailed
69
+ ? "The latest preview rebuild failed — retry the build before publishing. Your live store is unchanged."
70
+ : !input.shippable
71
+ ? "Your next release isn't ready to publish yet"
72
+ : input.paywalled
73
+ ? "Publishing to your live store is part of the paid plan"
74
+ : input.alreadyShipped
75
+ ? "This reviewed build is already live"
76
+ : "Publish the exact build you reviewed to your live store";
77
+
78
+ // The stale indicator is worth showing only when a failed rebuild is actually
79
+ // holding NEW work back from live (there are staged changes ahead).
80
+ const staleIndicator =
81
+ rebuildFailed && input.aheadCount > 0
82
+ ? "Your live store is serving the last good build" +
83
+ (input.servingBuildAtLabel ? " from " + input.servingBuildAtLabel : "") +
84
+ " — the latest rebuild failed, so your newest changes aren't live yet. Retry the build to finish."
85
+ : null;
86
+
87
+ return { disabled, title, staleIndicator };
88
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Pure presentational helpers for the admin Publish surface — sha/digest/age
3
+ * formatting and the small DOM-node builders (`chip`, `cell`, `subtle`,
4
+ * `linesNode`, `changeSummaryItem`). Extracted from the `adminPublishTab.ts`
5
+ * client island so the formatting boundary is a single, independently-testable
6
+ * module rather than inlined in the 3k-line controller. No closure state, no I/O —
7
+ * every function is a pure input → string/Node mapping.
8
+ *
9
+ * The DOM builders run in the browser (the processed client `<script>`); they touch
10
+ * `document` only when called, never at module load.
11
+ */
12
+
13
+ /** First 8 chars of a sha, or an em-dash placeholder. */
14
+ export const shortSha = (sha?: string | null): string => (sha ? String(sha).slice(0, 8) : "—");
15
+
16
+ /** `sha256:` + first 12 chars + ellipsis, or an em-dash placeholder. */
17
+ export const shortDigest = (digest?: string | null): string => {
18
+ const d = String(digest || "").replace(/^sha256:/, "");
19
+ return d ? "sha256:" + d.slice(0, 12) + "…" : "—";
20
+ };
21
+
22
+ /** A coarse relative-age label ("just now" / "5m ago" / "3h ago" / "2d ago"). */
23
+ export const age = (iso?: string | null): string => {
24
+ if (!iso) return "—";
25
+ const ms = Date.now() - new Date(iso).getTime();
26
+ if (!Number.isFinite(ms) || ms < 0) return "—";
27
+ const mins = Math.floor(ms / 60000);
28
+ if (mins < 1) return "just now";
29
+ if (mins < 60) return mins + "m ago";
30
+ const hours = Math.floor(mins / 60);
31
+ if (hours < 48) return hours + "h ago";
32
+ return Math.floor(hours / 24) + "d ago";
33
+ };
34
+
35
+ /**
36
+ * A "+N −M" lines-changed node (added green, removed red) as ONE element, so a meta
37
+ * "·" separator sits before the pair rather than between the numbers. Either count
38
+ * may be absent.
39
+ */
40
+ export const linesNode = (added?: number | null, removed?: number | null): HTMLSpanElement => {
41
+ const wrap = document.createElement("span");
42
+ wrap.className = "agg-lines";
43
+ if (typeof added === "number") {
44
+ const a = document.createElement("span");
45
+ a.className = "agg-add";
46
+ a.textContent = "+" + added;
47
+ wrap.append(a);
48
+ }
49
+ if (typeof removed === "number") {
50
+ const d = document.createElement("span");
51
+ d.className = "agg-del";
52
+ d.textContent = (typeof added === "number" ? " " : "") + "−" + removed;
53
+ wrap.append(d);
54
+ }
55
+ return wrap;
56
+ };
57
+
58
+ /**
59
+ * A status chip carrying its state WORD (never a bare colored dot) plus an optional
60
+ * tone class and a leading state `glyph` (✓ / ⚠ / ✕ / ●). The glyph is decorative
61
+ * (`aria-hidden`) — the word is the real signal — so a status chip never leans on
62
+ * colour alone: it reads as icon + text for a colour-blind or monochrome viewer.
63
+ */
64
+ export const chip = (
65
+ text?: string | null,
66
+ tone?: string | null,
67
+ glyph?: string | null,
68
+ ): HTMLSpanElement => {
69
+ const el = document.createElement("span");
70
+ el.className = "chip" + (tone ? " " + tone : "");
71
+ if (glyph) {
72
+ const g = document.createElement("span");
73
+ g.className = "chip-glyph";
74
+ g.setAttribute("aria-hidden", "true");
75
+ g.textContent = glyph;
76
+ el.append(g);
77
+ }
78
+ el.append(document.createTextNode(text ?? ""));
79
+ return el;
80
+ };
81
+
82
+ /** A `<td>` composed from string/Node children (null/undefined children skipped). */
83
+ export const cell = (...children: Array<Node | string | null | undefined>): HTMLTableCellElement => {
84
+ const td = document.createElement("td");
85
+ for (const child of children) {
86
+ if (child == null) continue;
87
+ td.append(typeof child === "string" ? document.createTextNode(child) : child);
88
+ }
89
+ return td;
90
+ };
91
+
92
+ /** A muted inline `<span class="subtle">`. */
93
+ export const subtle = (text?: string | null): HTMLSpanElement => {
94
+ const el = document.createElement("span");
95
+ el.className = "subtle";
96
+ el.textContent = text ?? "";
97
+ return el;
98
+ };
99
+
100
+ /** The shape `changeSummaryItem` reads — every enrichment field is optional. */
101
+ export interface ChangeSummaryLike {
102
+ prNumber?: number | null;
103
+ changeId?: string | null;
104
+ title?: string | null;
105
+ description?: string | null;
106
+ summary?: string | null;
107
+ headSha?: string | null;
108
+ fileCount?: number | null;
109
+ filesChanged?: number | null;
110
+ linesAdded?: number | null;
111
+ linesRemoved?: number | null;
112
+ }
113
+
114
+ /**
115
+ * One included-change line item (PR # + human title, a one-line description, and
116
+ * meta: sha / files / lines). Shared by the aggregate plan's "Included changes" and
117
+ * the published-versions "what shipped" list, so both read the same way. A row
118
+ * degrades to just "PR #N · <sha>" when the enrichment fields are absent.
119
+ */
120
+ export const changeSummaryItem = (ch: ChangeSummaryLike): HTMLLIElement => {
121
+ const li = document.createElement("li");
122
+ li.className = "agg-included-item";
123
+
124
+ const top = document.createElement("div");
125
+ top.className = "agg-included-top";
126
+ const prEl = document.createElement("span");
127
+ prEl.className = "agg-included-pr";
128
+ prEl.textContent = ch.prNumber != null ? "PR #" + ch.prNumber : ch.changeId ?? "";
129
+ top.append(prEl);
130
+ if (ch.title) {
131
+ const t = document.createElement("span");
132
+ t.className = "agg-included-title";
133
+ t.textContent = ch.title;
134
+ top.append(t);
135
+ }
136
+ li.append(top);
137
+
138
+ const description = ch.description || ch.summary;
139
+ if (description) {
140
+ const desc = document.createElement("p");
141
+ desc.className = "agg-included-desc";
142
+ desc.textContent = description;
143
+ li.append(desc);
144
+ }
145
+
146
+ const meta = document.createElement("div");
147
+ meta.className = "agg-included-meta";
148
+ if (ch.headSha) {
149
+ const sha = document.createElement("span");
150
+ sha.className = "publish-mono sha-token";
151
+ sha.textContent = shortSha(ch.headSha);
152
+ meta.append(sha);
153
+ }
154
+ const fileCount = ch.fileCount != null ? ch.fileCount : ch.filesChanged;
155
+ if (fileCount != null) {
156
+ const f = document.createElement("span");
157
+ f.textContent = fileCount + (fileCount === 1 ? " file" : " files");
158
+ meta.append(f);
159
+ }
160
+ if (typeof ch.linesAdded === "number" || typeof ch.linesRemoved === "number") {
161
+ meta.append(linesNode(ch.linesAdded, ch.linesRemoved));
162
+ }
163
+ if (meta.childNodes.length) li.append(meta);
164
+ return li;
165
+ };
@@ -0,0 +1,177 @@
1
+ /**
2
+ * publishHero — the pure state machine behind the Publish tab's single "next
3
+ * action" hero. It reduces the SAME `/api/changes` read the queue + aggregate
4
+ * already consume into ONE sentence + ONE primary call-to-action describing
5
+ * exactly what the operator should do next.
6
+ *
7
+ * It is the single top-of-tab state element: the two former standalone banners
8
+ * are folded in here, not rendered separately —
9
+ * - B1 (merge-doctor health) arrives as {@link PublishHeroInput.health}
10
+ * (`mergeHealthBanner` over the same payload) and becomes the hero's
11
+ * blocker/warn state + the "Review the queue" action.
12
+ * - B3 (platform-fault escalation) arrives as {@link PublishHeroInput.escalation}
13
+ * (`mergeSupportEscalation`) and becomes the hero's most-urgent state with the
14
+ * pre-filled "Report to support" mailto as its action.
15
+ * The DOM glue therefore toggles ONE element, never three.
16
+ *
17
+ * Precedence is ordered most-urgent → calm so the ONE sentence is always the most
18
+ * important true thing: a platform fault outranks a merge conflict, a merge
19
+ * conflict outranks a ready release, a ready release outranks unreviewed changes,
20
+ * and an empty queue rests calm. Total and side-effect-free.
21
+ */
22
+
23
+ /** Tone drives the icon + non-color styling (never color alone). */
24
+ export type PublishHeroTone = "blocker" | "warn" | "attention" | "ready" | "calm";
25
+
26
+ /** The single primary action the hero offers. */
27
+ export type PublishHeroCtaKind =
28
+ | "publish"
29
+ | "review-queue"
30
+ | "report-support"
31
+ | "goto-release"
32
+ | "none";
33
+
34
+ export interface PublishHeroCta {
35
+ kind: PublishHeroCtaKind;
36
+ /** Button/link text; "" when kind is "none". */
37
+ label: string;
38
+ /** A ready-to-use href — set only for "report-support" (the pre-filled mailto). */
39
+ href?: string;
40
+ }
41
+
42
+ export interface PublishHeroView {
43
+ tone: PublishHeroTone;
44
+ /** The ONE sentence describing the current state. */
45
+ headline: string;
46
+ /** Optional supporting subtext under the headline; "" when none. */
47
+ detail: string;
48
+ cta: PublishHeroCta;
49
+ /**
50
+ * True for the two blocker states — the DOM adds an explicit "Action needed"
51
+ * label so urgency is conveyed by text + icon, not color alone (a11y).
52
+ */
53
+ urgent: boolean;
54
+ }
55
+
56
+ export interface PublishHeroInput {
57
+ /** Number of open changes in the review queue (`payload.changes.length`). */
58
+ changeCount: number;
59
+ /** The B1 merge-doctor health reduction over the same payload. */
60
+ health: { show: boolean; text: string; tone: "blocker" | "warn" };
61
+ /** The B3 platform-fault escalation reduction over the same payload. */
62
+ escalation: { show: boolean; mailtoHref: string };
63
+ /** The b09 ship plan (`payload.plan`), or null when no store is bound. */
64
+ plan:
65
+ | { ok?: boolean; alreadyShipped?: boolean; paywall?: { allowed?: boolean } | null }
66
+ | null;
67
+ /** Staged changes ahead of the live pointer (`aggregate.includedPrs.length`). */
68
+ aheadCount: number;
69
+ /** The staged aggregate sha equals the live version id — nothing to publish. */
70
+ matchesLive: boolean;
71
+ /** Whether this viewer holds ship capability (owner/admin/ship-on-behalf). */
72
+ canShip: boolean;
73
+ /** Public static hostname also advanced by this publish, when configured. */
74
+ publicSiteHost?: string | null;
75
+ }
76
+
77
+ const plural = (n: number, one: string, many: string): string => (n === 1 ? one : many);
78
+
79
+ export function publishHeroState(input: PublishHeroInput): PublishHeroView {
80
+ const { changeCount, health, escalation, plan, canShip } = input;
81
+
82
+ // 1. Platform fault (absorbs B3). The rarest, most severe state — a retry
83
+ // didn't clear it, so the primary action is the pre-filled support report.
84
+ if (escalation.show) {
85
+ return {
86
+ tone: "blocker",
87
+ headline: "Some of this is on our end.",
88
+ detail:
89
+ "A retry didn't clear it. Send us the pre-filled report and we'll take it from here.",
90
+ cta: { kind: "report-support", label: "Report to support", href: escalation.mailtoHref },
91
+ urgent: true,
92
+ };
93
+ }
94
+
95
+ // 2. A developer-actionable blocker (absorbs B1's red state) — a merge conflict
96
+ // or similar that stops the release until it's resolved in the queue.
97
+ if (health.show && health.tone === "blocker") {
98
+ return {
99
+ tone: "blocker",
100
+ headline: health.text,
101
+ detail: "Resolve these in the change queue to keep your release moving.",
102
+ cta: { kind: "review-queue", label: "Review the queue" },
103
+ urgent: true,
104
+ };
105
+ }
106
+
107
+ // A reviewed, staged release exists when the plan is ok and hasn't shipped yet.
108
+ const releaseStaged = Boolean(plan && plan.ok && !plan.alreadyShipped);
109
+ const paywalled = Boolean(plan && plan.paywall && plan.paywall.allowed === false);
110
+
111
+ // 3. A release is reviewed and ready — the finish line. Publishing it is the
112
+ // highest-value next action, so it leads over unreviewed queue items.
113
+ if (releaseStaged && canShip && !paywalled) {
114
+ const staged = input.aheadCount;
115
+ return {
116
+ tone: "ready",
117
+ headline: "Your release is reviewed and ready to publish.",
118
+ detail: input.publicSiteHost
119
+ ? `${staged || "The reviewed release"} ${staged ? plural(staged, "change is", "changes are") : "is"} staged. Publishing updates the hosted store and automatically republishes ${input.publicSiteHost}.`
120
+ : staged
121
+ ? `${staged} ${plural(staged, "change is", "changes are")} staged and waiting to go live.`
122
+ : "",
123
+ cta: {
124
+ kind: "publish",
125
+ label: input.publicSiteHost ? "Publish to both live sites" : "Publish live",
126
+ },
127
+ urgent: false,
128
+ };
129
+ }
130
+
131
+ // 4. A reviewed release is staged, but this viewer can't one-click publish it
132
+ // (read-only sign-in, or the paywall gates go-live). Point them at the
133
+ // release, where the existing gate explains why the button is held.
134
+ if (releaseStaged) {
135
+ return {
136
+ tone: "ready",
137
+ headline: "Your release is reviewed and ready to publish.",
138
+ detail: paywalled
139
+ ? "Publishing to your live store is part of the paid plan."
140
+ : "Going live needs an owner or admin sign-in for this store.",
141
+ cta: { kind: "goto-release", label: "Go to the release" },
142
+ urgent: false,
143
+ };
144
+ }
145
+
146
+ // 5. A developer-actionable warning (absorbs B1's amber state) — e.g. a change
147
+ // still needs a build. Actionable, but not a hard blocker.
148
+ if (health.show) {
149
+ return {
150
+ tone: "warn",
151
+ headline: health.text,
152
+ detail: "Handle these in the change queue below.",
153
+ cta: { kind: "review-queue", label: "Review the queue" },
154
+ urgent: false,
155
+ };
156
+ }
157
+
158
+ // 6. Changes are waiting for review — the ordinary "you have work" state.
159
+ if (changeCount > 0) {
160
+ return {
161
+ tone: "attention",
162
+ headline: `${changeCount} ${plural(changeCount, "change is", "changes are")} ready to review.`,
163
+ detail: "Review them and add them to your next release.",
164
+ cta: { kind: "review-queue", label: "Review changes" },
165
+ urgent: false,
166
+ };
167
+ }
168
+
169
+ // 7. Nothing pending — calm, no alarm.
170
+ return {
171
+ tone: "calm",
172
+ headline: "You're all caught up.",
173
+ detail: "Nothing is waiting to review or publish.",
174
+ cta: { kind: "none", label: "" },
175
+ urgent: false,
176
+ };
177
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Pure state machine: collapse a change's internal/mechanical facts onto the ONE
3
+ * merchant-facing state it presents (contract §2). This is the single authority
4
+ * for the internal→merchant mapping — no surface re-derives it. It is total and
5
+ * side-effect-free, so the whole mapping is exhaustively unit-testable independent
6
+ * of any UI or transport.
7
+ *
8
+ * The mapping absorbs all retry/reconcile churn into "Preparing" and routes every
9
+ * unrecoverable-by-merchant condition to "Needs attention" (a dead end for the
10
+ * merchant, handed to a developer). See the acceptance table's `mapsFromInternal`.
11
+ */
12
+ import type { InternalChangeStatus, MerchantChangeStateId } from "./types.js";
13
+
14
+ /**
15
+ * The facts about one change that determine its merchant state. Derived from a
16
+ * `/api/changes` row by the view-model builder; kept explicit (not the raw row)
17
+ * so the precedence logic is the readable contract, not scattered field-poking.
18
+ */
19
+ export interface ChangeStateFacts {
20
+ /** Internal pipeline status (`queued`/`reconciling`/`ready`/`blocked`/`not-built`/…). */
21
+ status: InternalChangeStatus;
22
+ /** Evidence gate: checks passed (`evidence.promotable`). */
23
+ promotable: boolean;
24
+ /** Forge-settled mergeability against the current preview tip. */
25
+ mergeable: boolean;
26
+ /** At least one evidence check reported `fail`. */
27
+ hasFailingChecks: boolean;
28
+ /** The server could confirm this row's live state against the forge. `false` ⇒ "State unknown". */
29
+ liveStateConfirmed: boolean;
30
+ /** This change is staged into the next release (in the preview aggregate). */
31
+ inNextRelease: boolean;
32
+ /** This change's release is live — it has shipped. */
33
+ published: boolean;
34
+ }
35
+
36
+ /**
37
+ * Resolve the single merchant state. Precedence is load-bearing and ordered so a
38
+ * terminal/definite fact wins over a transient one:
39
+ *
40
+ * published > in next release > needs attention > ready to add > preparing
41
+ *
42
+ * A staged change follows the release (it never independently degrades to "Needs
43
+ * attention" — the RELEASE does), which is why `inNextRelease` outranks the
44
+ * attention checks.
45
+ */
46
+ export function resolveChangeState(f: ChangeStateFacts): MerchantChangeStateId {
47
+ if (f.published) return "published";
48
+ if (f.inNextRelease) return "in-next-release";
49
+
50
+ // Needs attention — the merchant dead-end (handed to a developer). Any of:
51
+ // - "State unknown": the forge listing failed, the stored label is unverifiable;
52
+ // - blocked;
53
+ // - evidence failed (a check came back `fail`);
54
+ // - a forge-settled merge conflict on an otherwise-ready change.
55
+ if (!f.liveStateConfirmed) return "needs-attention";
56
+ if (f.status === "blocked") return "needs-attention";
57
+ if (f.hasFailingChecks) return "needs-attention";
58
+ if (f.status === "ready" && f.promotable && !f.mergeable) return "needs-attention";
59
+
60
+ // Ready to add — checks passed, cleanly mergeable, not yet staged.
61
+ if (f.status === "ready" && f.promotable && f.mergeable) return "ready-to-add";
62
+
63
+ // Everything else is still being built/checked automatically: queued,
64
+ // reconciling, building, not-built, or ready-but-not-yet-promotable.
65
+ return "preparing";
66
+ }