@tokenoftrust/storefront-runner 1.4.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/apps/storefront/astro.config.mjs +17 -1
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +54 -0
  4. package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
  5. package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
  6. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  7. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  8. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  9. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  10. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  11. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  12. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  13. package/apps/storefront/migrations-apps/meta/0002_snapshot.json +1160 -0
  14. package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -0
  15. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  16. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  17. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  18. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  19. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  20. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  21. package/apps/storefront/migrations-apps/meta/_journal.json +56 -0
  22. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  23. package/apps/storefront/public/js/dashboard-team.js +38 -2
  24. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  25. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  26. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  27. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  28. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  29. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  30. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  31. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  32. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  33. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  34. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  35. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  36. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  37. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  38. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  39. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  40. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  41. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  42. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  43. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  44. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  45. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  46. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  47. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  48. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  49. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  50. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  51. package/apps/storefront/src/components/Img.astro +1 -1
  52. package/apps/storefront/src/components/Seo.astro +65 -1
  53. package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
  54. package/apps/storefront/src/components/admin/AdminBlogTab.astro +1435 -0
  55. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
  56. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  57. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1980 -2564
  58. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  59. package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
  60. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  61. package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
  62. package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
  63. package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
  64. package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
  65. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  66. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  67. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  68. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  69. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  70. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  71. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  72. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  73. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  74. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  75. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  76. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  77. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  78. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  79. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  80. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  81. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  82. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  83. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  84. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  85. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  86. package/apps/storefront/src/config/resolver.ts +4 -4
  87. package/apps/storefront/src/layouts/Layout.astro +146 -21
  88. package/apps/storefront/src/lib/activity/alerts.ts +17 -19
  89. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  90. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +89 -9
  91. package/apps/storefront/src/lib/activity/deployVersion.ts +20 -27
  92. package/apps/storefront/src/lib/activity/ingest.ts +8 -8
  93. package/apps/storefront/src/lib/activity/ingestAuth.ts +1 -1
  94. package/apps/storefront/src/lib/activity/killSwitch.ts +6 -6
  95. package/apps/storefront/src/lib/activity/query.ts +9 -9
  96. package/apps/storefront/src/lib/activity/recordActivity.ts +11 -15
  97. package/apps/storefront/src/lib/activity/store.ts +4 -6
  98. package/apps/storefront/src/lib/activity/uiActor.ts +12 -12
  99. package/apps/storefront/src/lib/activity/workerCommit.ts +6 -6
  100. package/apps/storefront/src/lib/admin/adminShell.ts +292 -0
  101. package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
  102. package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
  103. package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
  104. package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
  105. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  106. package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
  107. package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
  108. package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
  109. package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
  110. package/apps/storefront/src/lib/apps/adminService.ts +14 -4
  111. package/apps/storefront/src/lib/apps/adminSession.ts +54 -45
  112. package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
  113. package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
  114. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  115. package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
  116. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  117. package/apps/storefront/src/lib/apps/credentials.ts +2 -2
  118. package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
  119. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  120. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  121. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  122. package/apps/storefront/src/lib/apps/healthAggregate.ts +5 -5
  123. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  124. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  125. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  126. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  127. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  128. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  129. package/apps/storefront/src/lib/apps/orders/attributionService.ts +2 -2
  130. package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
  131. package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
  132. package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
  133. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  134. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +9 -1
  135. package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
  136. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +7 -7
  137. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  138. package/apps/storefront/src/lib/apps/registryService.ts +270 -16
  139. package/apps/storefront/src/lib/apps/scopes.ts +2 -2
  140. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  141. package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
  142. package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
  143. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  144. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  145. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  146. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  147. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  148. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  149. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  150. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  151. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  152. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  153. package/apps/storefront/src/lib/apps/widgets/eligibility.ts +2 -2
  154. package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
  155. package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
  156. package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
  157. package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
  158. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  159. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  160. package/apps/storefront/src/lib/auth/adminEntry.ts +11 -11
  161. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  162. package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
  163. package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
  164. package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +2 -4
  165. package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
  166. package/apps/storefront/src/lib/auth/session.ts +7 -8
  167. package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
  168. package/apps/storefront/src/lib/basePath.ts +9 -2
  169. package/apps/storefront/src/lib/blog/access.ts +29 -0
  170. package/apps/storefront/src/lib/blog/collection.ts +400 -0
  171. package/apps/storefront/src/lib/blog/markdown.ts +174 -0
  172. package/apps/storefront/src/lib/blog/provider.ts +62 -3
  173. package/apps/storefront/src/lib/blog/reactions.ts +87 -0
  174. package/apps/storefront/src/lib/blog/rss.ts +92 -0
  175. package/apps/storefront/src/lib/blog/teaser.ts +83 -0
  176. package/apps/storefront/src/lib/blog/types.ts +34 -0
  177. package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
  178. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  179. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  180. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  181. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  182. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  183. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  184. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  185. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  186. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  187. package/apps/storefront/src/lib/content/index.ts +5 -5
  188. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  189. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  190. package/apps/storefront/src/lib/d1/catalog.ts +14 -2
  191. package/apps/storefront/src/lib/d1/schema-apps.ts +372 -17
  192. package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
  193. package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
  194. package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
  195. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  196. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  197. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  198. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  199. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  200. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  201. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  202. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  203. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  204. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  205. package/apps/storefront/src/lib/dev/cockpitStore.ts +1 -9
  206. package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
  207. package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
  208. package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
  209. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  210. package/apps/storefront/src/lib/edgeCache.ts +34 -5
  211. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  212. package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
  213. package/apps/storefront/src/lib/i18n.ts +25 -0
  214. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  215. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  216. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  217. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  218. package/apps/storefront/src/lib/jsonld.ts +118 -0
  219. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  220. package/apps/storefront/src/lib/membership/model.ts +1 -2
  221. package/apps/storefront/src/lib/monitoring/manifest.ts +11 -11
  222. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
  223. package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
  224. package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
  225. package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
  226. package/apps/storefront/src/lib/newsletter/send.ts +265 -0
  227. package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
  228. package/apps/storefront/src/lib/previewReload.ts +57 -1
  229. package/apps/storefront/src/lib/publish/adminPublishTab.ts +4625 -0
  230. package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
  231. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  232. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  233. package/apps/storefront/src/lib/publish/dispatchHealth.ts +5 -7
  234. package/apps/storefront/src/lib/publish/domainState.ts +22 -13
  235. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  236. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  237. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  238. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  239. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  240. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  241. package/apps/storefront/src/lib/publish/panelPolling.ts +78 -0
  242. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  243. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  244. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  245. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  246. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  247. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  248. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  249. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  250. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  251. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  252. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  253. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  254. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  255. package/apps/storefront/src/lib/publish/shipWorkspace.ts +121 -51
  256. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  257. package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -0
  258. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  259. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  260. package/apps/storefront/src/lib/rate/kvWindow.ts +53 -0
  261. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  262. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  263. package/apps/storefront/src/lib/search/index.ts +2 -2
  264. package/apps/storefront/src/lib/seo/alternates.ts +42 -0
  265. package/apps/storefront/src/lib/seo/meta.ts +65 -0
  266. package/apps/storefront/src/lib/social/golive.ts +212 -0
  267. package/apps/storefront/src/lib/social/notify.ts +32 -0
  268. package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
  269. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  270. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  271. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  272. package/apps/storefront/src/lib/the-build/provider.ts +32 -0
  273. package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
  274. package/apps/storefront/src/lib/the-build/toc.ts +95 -0
  275. package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
  276. package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
  277. package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
  278. package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
  279. package/apps/storefront/src/middleware/index.ts +385 -52
  280. package/apps/storefront/src/pages/[...slug].astro +9 -4
  281. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  282. package/apps/storefront/src/pages/admin/select.astro +96 -0
  283. package/apps/storefront/src/pages/admin.astro +25 -1313
  284. package/apps/storefront/src/pages/api/activity.ts +8 -8
  285. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  286. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  287. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  288. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  289. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  290. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  291. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  292. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  293. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  294. package/apps/storefront/src/pages/api/blog/[collection]/[slug]/react.ts +104 -0
  295. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  296. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  297. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  298. package/apps/storefront/src/pages/api/newsletter/[collection]/subscribe.ts +146 -0
  299. package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
  300. package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
  301. package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
  302. package/apps/storefront/src/pages/api/rum/vitals.ts +20 -2
  303. package/apps/storefront/src/pages/blog/[slug].astro +99 -19
  304. package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
  305. package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
  306. package/apps/storefront/src/pages/blog/index.astro +4 -2
  307. package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
  308. package/apps/storefront/src/pages/cockpit.astro +77 -1353
  309. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
  310. package/apps/storefront/src/pages/index.astro +10 -5
  311. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  312. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  313. package/apps/storefront/src/pages/search.astro +1 -1
  314. package/apps/storefront/src/pages/sitemap.xml.ts +21 -1
  315. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  316. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  317. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  318. package/apps/storefront/src/pages/the-build/[slug].astro +129 -0
  319. package/apps/storefront/src/pages/the-build/index.astro +146 -0
  320. package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
  321. package/apps/storefront/src/styles/admin.css +1106 -0
  322. package/apps/storefront/tsconfig.json +6 -1
  323. package/package.json +1 -1
  324. package/packages/public-runtime/package.json +3 -1
  325. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  326. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  327. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  328. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  329. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  330. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  331. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  332. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  333. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  334. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  335. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  336. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  337. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  338. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  339. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  340. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  341. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  342. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  343. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  344. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  345. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  346. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  347. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  348. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  349. package/packages/public-runtime/src/activity/catalog.ts +89 -33
  350. package/packages/public-runtime/src/activity/event.ts +6 -6
  351. package/packages/public-runtime/src/activity/index.ts +2 -2
  352. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  353. package/packages/public-runtime/src/candidate-index.ts +42 -8
  354. package/packages/public-runtime/src/catalog-d1.ts +1 -1
  355. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  356. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  357. package/packages/public-runtime/src/checkout-style.ts +7 -7
  358. package/packages/public-runtime/src/checkout.ts +3 -3
  359. package/packages/public-runtime/src/chrome.ts +777 -0
  360. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  361. package/packages/public-runtime/src/csp.ts +99 -0
  362. package/packages/public-runtime/src/customization-preview.ts +25 -2
  363. package/packages/public-runtime/src/customization-runtime.ts +156 -3
  364. package/packages/public-runtime/src/customization-versioning.ts +10 -0
  365. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  366. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  367. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  368. package/packages/public-runtime/src/evidence.ts +288 -0
  369. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  370. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  371. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  372. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  373. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  374. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  375. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  376. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  377. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  378. package/packages/public-runtime/src/index.ts +17 -1
  379. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  380. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  381. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  382. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  383. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  384. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  385. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  386. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  387. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  388. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  389. package/packages/public-runtime/src/integration/csp.ts +105 -0
  390. package/packages/public-runtime/src/integration/digest.ts +43 -0
  391. package/packages/public-runtime/src/integration/environment.ts +228 -0
  392. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  393. package/packages/public-runtime/src/integration/events.ts +701 -0
  394. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  395. package/packages/public-runtime/src/integration/index.ts +90 -0
  396. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  397. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  398. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  399. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  400. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  401. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  402. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  403. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  404. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  405. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  406. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  407. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  408. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  409. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  410. package/packages/public-runtime/src/integration/observability.ts +451 -0
  411. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  412. package/packages/public-runtime/src/integration/phases.ts +182 -0
  413. package/packages/public-runtime/src/integration/plan.ts +112 -0
  414. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  415. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  416. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  417. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  418. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  419. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  420. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  421. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  422. package/packages/public-runtime/src/integration/result.ts +239 -0
  423. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  424. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  425. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  426. package/packages/public-runtime/src/integration/version.ts +92 -0
  427. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  428. package/packages/public-runtime/src/membership.ts +2 -2
  429. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  430. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  431. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  432. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  433. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  434. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  435. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  436. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  437. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  438. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  439. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  440. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  441. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  442. package/packages/public-runtime/src/static-bundle.ts +357 -0
  443. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  444. package/packages/public-runtime/src/tenant.ts +81 -17
  445. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  446. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  447. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  448. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  449. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  450. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  451. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  452. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  453. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  454. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  455. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  456. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  457. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  458. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  459. package/scripts/build/copy-tenant-assets.mjs +14 -4
  460. package/scripts/dev/ai-edit.mjs +1 -1
  461. package/scripts/dev/checkout-watch.mjs +2 -2
  462. package/scripts/dev/file-browser.mjs +4 -6
  463. package/scripts/dev/file-writer.mjs +3 -3
  464. package/scripts/dev/git-status.mjs +2 -2
  465. package/scripts/dev/integration-guardrails.mjs +498 -0
  466. package/scripts/dev/locate-handler.mjs +3 -3
  467. package/scripts/dev/port-check.mjs +3 -3
  468. package/scripts/dev/publish.mjs +5 -9
  469. package/scripts/dev/safe-path.mjs +2 -2
  470. package/scripts/dev/shot.mjs +1 -1
  471. package/scripts/dev/tenant-source-root.mjs +1 -1
  472. package/scripts/dev/unified-diff.mjs +8 -3
  473. package/scripts/tot-dev.mjs +8 -11
  474. package/apps/storefront/src/lib/dev/vcBinding.ts +0 -80
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * POST /api/activity — the ingest endpoint of the operational activity telemetry
3
- * spine (card D4). Accepts one `ActivityEvent` (or a batch array) as JSON from
4
- * the emitter cohorts — the CLI (D3) and the UI (D6), both posting the wire shape
3
+ * spine. Accepts one `ActivityEvent` (or a batch array) as JSON from
4
+ * the emitter cohorts — the CLI and the UI, both posting the wire shape
5
5
  * of `packages/public-runtime/src/activity/event.ts` — authenticates the caller,
6
6
  * re-redacts server-side, and fans each accepted event out to the sink(s) its
7
7
  * catalog entry declares. See DECISIONS.md §8 for the store decision.
@@ -18,7 +18,7 @@
18
18
  * DEFENSE IN DEPTH — every event is rebuilt through `createActivityEvent()`
19
19
  * (`ingestEvents`), re-applying the default-deny allowlist + canary scan even
20
20
  * though emitters already redacted. A value-canary trip is self-telemetried as
21
- * `system.ingest.rejected` so a leak attempt is visible (D6/D7 alert on it).
21
+ * `system.ingest.rejected` so a leak attempt is visible for alerting.
22
22
  *
23
23
  * OFF THE CRITICAL PATH — auth + parse + validate + redact are cheap synchronous
24
24
  * CPU; the sink WRITES (D1 + Analytics Engine) run past the response via
@@ -50,7 +50,7 @@ function json(body: unknown, status: number): Response {
50
50
  }
51
51
 
52
52
  export async function POST(context: APIContext): Promise<Response> {
53
- // Kill switch (D8): telemetry off ⇒ accept-and-drop. Fail-open toward the EMITTER —
53
+ // Kill switch: telemetry off ⇒ accept-and-drop. Fail-open toward the EMITTER —
54
54
  // a 202 (never a 4xx/5xx) so a CLI/UI poster never errors, retries, or is delayed by
55
55
  // the switch; nothing is validated, stored, or fanned out.
56
56
  if (isActivityDisabled()) {
@@ -96,8 +96,8 @@ export async function POST(context: APIContext): Promise<Response> {
96
96
 
97
97
  // Self-telemetry: a value-canary trip during server-side redaction means an
98
98
  // emitter leaked (or tried to leak) a secret/PII into the payload. Emit a
99
- // `system.ingest.rejected` event (itself routed to logs+analytics) so D6/D7 can
100
- // alert — the fact of the trip, the action, never the offending value. Rejected
99
+ // `system.ingest.rejected` event (itself routed to logs+analytics) so it can be
100
+ // alerted on — the fact of the trip, the action, never the offending value. Rejected
101
101
  // events get the same self-telemetry so a spoof/unknown-action attempt is
102
102
  // visible. Built here (synchronously) but fanned out off-path below.
103
103
  const selfTelemetry = [
@@ -127,9 +127,9 @@ export async function POST(context: APIContext): Promise<Response> {
127
127
 
128
128
  // Fan every accepted event + the self-telemetry out to its sinks, PAST the
129
129
  // response. Best-effort per sink (never throws). The adapter's per-request
130
- // ExecutionContext lives at `locals.runtime.ctx`; runAfterResponse floats the
130
+ // ExecutionContext lives at `locals.cfContext`; runAfterResponse floats the
131
131
  // promise in dev/Node when it is absent.
132
- const cfContext = (context.locals as { runtime?: { ctx?: CfExecutionContextLike } })?.runtime?.ctx ?? null;
132
+ const cfContext = (context.locals as { cfContext?: CfExecutionContextLike })?.cfContext ?? null;
133
133
  const work = Promise.all([...accepted.map((a) => a.event), ...selfTelemetry].map((e) => fanOutActivity(e, sinks)));
134
134
  runAfterResponse(work, cfContext);
135
135
 
@@ -25,9 +25,7 @@
25
25
  * ops-timeline; never a URL/query value.
26
26
  */
27
27
  import type { APIContext } from "astro";
28
- import { readViewerSession } from "@/lib/auth/route";
29
- import { resolveAdminEntry } from "@/lib/auth/adminEntry";
30
- import { resolveAdminTenantMode } from "@/lib/adminTenantEnvelope";
28
+ import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
31
29
  import { readD1 } from "@/lib/env";
32
30
  import { fromD1 } from "@/lib/d1/catalog";
33
31
  import { checkActivityAlerts } from "@/lib/activity/alerts";
@@ -46,24 +44,11 @@ function json(body: unknown, status = 200): Response {
46
44
 
47
45
  export async function GET(context: APIContext): Promise<Response> {
48
46
  const { tenant } = context.locals;
49
- const nowSeconds = Math.floor(Date.now() / 1000);
50
47
 
51
- const viewerSession = await readViewerSession(context);
52
- if (!viewerSession) return json({ ok: false, reason: "sign_in_required" }, 401);
53
-
54
- const adminEntry = resolveAdminEntry({
55
- record: viewerSession,
56
- hostResource: tenant.appDomain,
57
- nowSeconds,
58
- });
59
- const { isStaff } = resolveAdminTenantMode({
60
- session: viewerSession,
61
- adminAppTenant: tenant.appDomain,
62
- requestedTarget: null,
63
- nowSeconds,
64
- });
65
- const authorized = adminEntry.isOwnerViewer || isStaff;
66
- if (!authorized) return json({ ok: false, reason: "not_authorized" }, 403);
48
+ // Owner/admin-of-tenant or ToT-staff only — same bar as /admin/ops-timeline,
49
+ // fail-closed and bound to the Host-resolved tenant.
50
+ const access = await requireAdminApiEntry(context, { requireOwner: true });
51
+ if (!access.ok) return json({ ok: false, reason: access.reason }, access.status);
67
52
 
68
53
  const d1 = await readD1("STOREFRONT_APPS_DB");
69
54
  if (!d1) return json({ ok: true, dbAvailable: false, alerts: [] });
@@ -1,13 +1,14 @@
1
1
  /**
2
- * `GET /api/admin/orders/:id` — one order's full detail for the Orders tab (U2).
2
+ * `GET /api/admin/orders/:id` — one order's full detail for the Orders tab.
3
3
  *
4
4
  * Server-side proxy to checkout `GET /commerce/orders/:orderId` for the
5
5
  * resolved tenant + env. Bearer stays server-side; response is the CheckoutOrderDetail
6
- * DTO. `verificationDetail` is empty until U4 fills it — consumers tolerate that.
6
+ * DTO. `verificationDetail` is currently empty — consumers tolerate that.
7
7
  * Read-only (scope-locks).
8
8
  */
9
9
  import type { APIContext } from "astro";
10
10
  import { readEnv } from "@/lib/env";
11
+ import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
11
12
  import {
12
13
  createCheckoutOrderClient,
13
14
  orderApiBaseUrl,
@@ -31,6 +32,10 @@ function jsonError(code: string, status: number): Response {
31
32
 
32
33
  export async function GET(context: APIContext): Promise<Response> {
33
34
  const { tenant, customizationEnv } = context.locals;
35
+
36
+ const access = await requireAdminApiEntry(context);
37
+ if (!access.ok) return jsonError(access.reason, access.status);
38
+
34
39
  const orderId = context.params.id;
35
40
  if (!orderId) return jsonError("order_not_found", 404);
36
41
 
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `GET /api/admin/orders` — internal admin route backing the Orders tab (U2).
2
+ * `GET /api/admin/orders` — internal admin route backing the Orders tab.
3
3
  *
4
4
  * Server-side proxy to the checkout order READ API for the resolved tenant +
5
5
  * environment. The per-tenant bearer key stays on the Worker (never in the
6
6
  * response); the browser only ever sees order DTO JSON. Read-only (scope-locks).
7
- * U3 consumes this and maps the DTO → OrdersViewModel.
7
+ * The admin UI consumes this and maps the DTO → OrdersViewModel.
8
8
  *
9
9
  * Access control matches the rest of the admin surface (owned by the admin
10
10
  * host/approver layer) — same posture as `api/evidence.json.ts`; no new gate here.
@@ -12,6 +12,7 @@
12
12
  import type { APIContext } from "astro";
13
13
  import type { CheckoutOrderListFilters } from "@tot/public-runtime";
14
14
  import { readEnv } from "@/lib/env";
15
+ import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
15
16
  import {
16
17
  createCheckoutOrderClient,
17
18
  orderApiBaseUrl,
@@ -36,6 +37,11 @@ function jsonError(code: string, status: number): Response {
36
37
  export async function GET(context: APIContext): Promise<Response> {
37
38
  const { tenant, customizationEnv } = context.locals;
38
39
 
40
+ // Fail closed: require a signed-in member/owner of THIS tenant, bound to the
41
+ // Host-resolved appDomain — never trusting the conditional login gate for auth.
42
+ const access = await requireAdminApiEntry(context);
43
+ if (!access.ok) return jsonError(access.reason, access.status);
44
+
39
45
  const bearer = await readEnv("FOXY_ORDER_API_KEY");
40
46
  if (!bearer) {
41
47
  // Worker secret not provisioned for this env — a config error, not the
@@ -0,0 +1,174 @@
1
+ /**
2
+ * POST /api/apps/admin/gtm-migration-install — the review->install surface of
3
+ * the GTM migration assistant. It takes a reviewed draft's `draftId`, LOADS the
4
+ * stored draft server-side, and converts it into a DISABLED desired
5
+ * managed-analytics install + draft revision, strictly through WS1's pinned
6
+ * PUBLIC contract. It returns the resulting disabled install/revision + a
7
+ * correlation id.
8
+ *
9
+ * Auth mirrors the read-only analyze route (`gtm-migration.ts`): the tenant is
10
+ * `auth.tenantId` from the resolved operator session — NEVER the request body.
11
+ *
12
+ * SECURITY BOUNDARY (a reviewer enforces this):
13
+ * - LOAD-BY-ID ONLY: the body carries a `draftId` (+ optional
14
+ * `targetInstallId` and its original `expectedRevision`), never a draft
15
+ * object. The stable mutation identity comes from the required
16
+ * `Idempotency-Key` header. A body that tries to smuggle draft data
17
+ * (`proposedConfiguration`/`proposedAdapterId`/`findings`/`draft`/inline
18
+ * `configuration`) is rejected — the stored draft is loaded from WS1 and
19
+ * re-validated against the pinned validator; untrusted draft data never gets
20
+ * past review.
21
+ * - DISABLED-ONLY: the produced install is `status:"disabled"` with a
22
+ * `status:"draft"` revision. Any attempt to force enable/publish/activate in
23
+ * the body is refused. This route imports NO registry/D1/R2/materializer/
24
+ * lifecycle dependency and writes NOTHING to storefront's own stores — every
25
+ * effect goes through the pinned WS1 client, which exposes no enable op.
26
+ */
27
+ import type { APIContext } from "astro";
28
+ import {
29
+ validateGetIntegrationInstallRequest,
30
+ validateGetMigrationDraftRequest,
31
+ validateMutationRequest,
32
+ } from "@tot/public-runtime";
33
+ import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
34
+ import { json } from "@/lib/apps/adminService.js";
35
+ import {
36
+ reviewInstallDraft,
37
+ type ReviewInstallResult,
38
+ type Ws1ManagedAnalyticsClient,
39
+ } from "@/lib/apps/gtmMigrationInstall.js";
40
+ import { resolveWs1ManagedAnalyticsClient } from "@/lib/apps/ws1ManagedAnalyticsClient.js";
41
+
42
+ export const prerender = false;
43
+
44
+ /** Fields that would smuggle inline draft data past review — a body may carry only ids. */
45
+ const DRAFT_DATA_FIELDS = ["draft", "proposedConfiguration", "proposedAdapterId", "findings", "configuration"] as const;
46
+ /** Fields that would try to enable/publish/activate — this path produces a disabled draft only. */
47
+ const ENABLE_FIELDS = ["status", "enabled", "enable", "publish", "publishes", "activate"] as const;
48
+
49
+ interface ParsedRequest {
50
+ readonly draftId: string;
51
+ readonly targetInstallId?: string;
52
+ readonly expectedRevision?: number;
53
+ }
54
+
55
+ /** Parse ids plus optional revise CAS state, and NOTHING that could enable or resubmit a draft. */
56
+ function parseRequest(body: unknown): ParsedRequest | { error: string } {
57
+ if (!body || typeof body !== "object") return { error: "A JSON object body with a draftId is required." };
58
+ const o = body as Record<string, unknown>;
59
+
60
+ for (const field of DRAFT_DATA_FIELDS) {
61
+ if (field in o) {
62
+ return { error: "Submit a draftId only; the reviewed draft is loaded server-side and cannot be supplied inline." };
63
+ }
64
+ }
65
+ for (const field of ENABLE_FIELDS) {
66
+ if (field in o) {
67
+ return { error: "This path produces a disabled draft only; it cannot enable, publish, or activate." };
68
+ }
69
+ }
70
+
71
+ const rawDraftId = o.draftId;
72
+ if (typeof rawDraftId !== "string" || !rawDraftId.trim()) return { error: "A draftId is required." };
73
+ const draftCheck = validateGetMigrationDraftRequest({
74
+ apiVersion: "v1",
75
+ query: { draftId: rawDraftId },
76
+ });
77
+ if (!draftCheck.ok) {
78
+ return { error: "draftId must be a valid opaque identifier." };
79
+ }
80
+ const draftId = draftCheck.value.query.draftId;
81
+
82
+ let targetInstallId: string | undefined;
83
+ if ("targetInstallId" in o) {
84
+ const t = o.targetInstallId;
85
+ if (typeof t !== "string" || !t.trim()) return { error: "targetInstallId must be a non-empty string when present." };
86
+ const installCheck = validateGetIntegrationInstallRequest({
87
+ apiVersion: "v1",
88
+ query: { installId: t },
89
+ });
90
+ if (!installCheck.ok) {
91
+ return { error: "targetInstallId must be a valid opaque identifier." };
92
+ }
93
+ targetInstallId = installCheck.value.query.installId;
94
+ }
95
+ let expectedRevision: number | undefined;
96
+ if ("expectedRevision" in o) {
97
+ if (!Number.isInteger(o.expectedRevision) || (o.expectedRevision as number) < 0) {
98
+ return { error: "expectedRevision must be a non-negative integer when present." };
99
+ }
100
+ expectedRevision = o.expectedRevision as number;
101
+ }
102
+ if (targetInstallId !== undefined && expectedRevision === undefined) {
103
+ return { error: "expectedRevision is required when targetInstallId is present so retries preserve the original CAS request." };
104
+ }
105
+ if (targetInstallId === undefined && expectedRevision !== undefined) {
106
+ return { error: "expectedRevision is only accepted when targetInstallId is present." };
107
+ }
108
+ return { draftId, targetInstallId, expectedRevision };
109
+ }
110
+
111
+ export async function POST(context: APIContext): Promise<Response> {
112
+ const url = new URL(context.request.url);
113
+ const requestedTenant = url.searchParams.get("tenant");
114
+ const auth = await resolveOperatorSession(context, requestedTenant);
115
+ if (!auth.ok) return json({ error: auth.message }, auth.status);
116
+
117
+ const correlationId = crypto.randomUUID();
118
+
119
+ let body: unknown;
120
+ try {
121
+ body = await context.request.json();
122
+ } catch {
123
+ return json({ ok: false, correlationId, error: "The request body must be JSON." }, 400);
124
+ }
125
+
126
+ const parsed = parseRequest(body);
127
+ if ("error" in parsed) return json({ ok: false, correlationId, error: parsed.error }, 400);
128
+
129
+ const idempotencyKey = context.request.headers.get("idempotency-key")?.trim();
130
+ const keyCheck = validateMutationRequest({
131
+ apiVersion: "v1",
132
+ idempotencyKey,
133
+ expectedRevision: 0,
134
+ body: {},
135
+ });
136
+ if (!keyCheck.ok) {
137
+ return json({ ok: false, correlationId, error: "A valid Idempotency-Key header is required." }, 400);
138
+ }
139
+
140
+ let client: Ws1ManagedAnalyticsClient | null;
141
+ try {
142
+ client = await resolveWs1ManagedAnalyticsClient(context, auth.tenantId);
143
+ } catch {
144
+ return json({ ok: false, correlationId, error: "Managed analytics service resolution failed safely.", code: "internal_error" }, 502);
145
+ }
146
+ if (!client) return json({ ok: false, correlationId, error: "Managed analytics service is not configured." }, 503);
147
+
148
+ let result: ReviewInstallResult;
149
+ try {
150
+ result = await reviewInstallDraft(client, {
151
+ draftId: parsed.draftId,
152
+ targetInstallId: parsed.targetInstallId,
153
+ expectedRevision: parsed.expectedRevision,
154
+ idempotencyKey: keyCheck.value.idempotencyKey,
155
+ correlationId,
156
+ });
157
+ } catch {
158
+ return json({ ok: false, correlationId, error: "Managed analytics review failed safely.", code: "internal_error" }, 502);
159
+ }
160
+
161
+ if (!result.ok) {
162
+ return json({ ok: false, correlationId, error: result.error, code: result.code }, result.status);
163
+ }
164
+ return json(
165
+ {
166
+ ok: true,
167
+ correlationId,
168
+ install: result.install,
169
+ revision: result.revision,
170
+ idempotencyReplayed: result.idempotencyReplayed,
171
+ },
172
+ 200,
173
+ );
174
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * POST /api/apps/admin/gtm-migration — the READ-ONLY "Migrate from GTM" analyze
3
+ * surface. Accepts an uploaded GTM container export (a JSON request body, or a
4
+ * `multipart/form-data` file part), runs the SAFE `@tot/public-runtime` pipeline
5
+ * (parse -> graph -> classify) over it, and returns a REDACTED report: object
6
+ * accounting, the disposition summary, per-object dispositions, the dependency
7
+ * graph, unresolved findings, and recommendations. `?format=bundle` returns the
8
+ * same report as a downloadable JSON attachment with a provenance stamp.
9
+ *
10
+ * Auth mirrors the read-only `integrations` route: the tenant is `auth.tenantId`
11
+ * from the resolved operator session — NEVER the request body — and any tenant
12
+ * membership admits (analyze is a diagnostic read, not a manage action).
13
+ *
14
+ * READ-ONLY BOUNDARY (a reviewer enforces this): this route writes NOTHING,
15
+ * builds no integration or draft, publishes nothing, activates nothing, and
16
+ * emits no markup/script/network-to-third-parties/event-listener. It imports
17
+ * only the operator-session gate, the JSON responder, and the pure, inert
18
+ * pipeline + report builder — no registry, D1, R2, or lifecycle dependency is
19
+ * imported or reachable. It parses inert data and returns a report. Ids from a
20
+ * foreign container are reflected back only; they are never used to look
21
+ * anything up about this tenant.
22
+ */
23
+ import type { APIContext } from "astro";
24
+ import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
25
+ import { json } from "@/lib/apps/adminService.js";
26
+ import { analyzeGtmExport } from "@/lib/apps/gtmMigrationReport.js";
27
+
28
+ export const prerender = false;
29
+
30
+ /** The multipart field name the upload UI posts the export file under. */
31
+ const FILE_FIELD = "file";
32
+
33
+ /** Read the uploaded export text from a JSON body or a multipart file part. */
34
+ async function readUploadText(request: Request): Promise<string | { error: string }> {
35
+ const contentType = (request.headers.get("content-type") ?? "").toLowerCase();
36
+ if (contentType.includes("multipart/form-data")) {
37
+ let form: FormData;
38
+ try {
39
+ form = await request.formData();
40
+ } catch {
41
+ return { error: "could not read the uploaded form" };
42
+ }
43
+ const part = form.get(FILE_FIELD);
44
+ if (part == null) return { error: `no "${FILE_FIELD}" part in the upload` };
45
+ if (typeof part === "string") return part;
46
+ return await part.text();
47
+ }
48
+ // Any other content type: the raw request body IS the export document.
49
+ return await request.text();
50
+ }
51
+
52
+ export async function POST(context: APIContext): Promise<Response> {
53
+ const url = new URL(context.request.url);
54
+ const requestedTenant = url.searchParams.get("tenant");
55
+ const auth = await resolveOperatorSession(context, requestedTenant);
56
+ if (!auth.ok) return json({ error: auth.message }, auth.status);
57
+
58
+ const upload = await readUploadText(context.request);
59
+ const correlationId = crypto.randomUUID();
60
+ const generatedAt = new Date().toISOString();
61
+
62
+ if (typeof upload !== "string") {
63
+ return json({ ok: false, correlationId, error: upload.error, violations: [] }, 400);
64
+ }
65
+ if (upload.trim() === "") {
66
+ return json(
67
+ { ok: false, correlationId, error: "The upload was empty.", violations: [] },
68
+ 400,
69
+ );
70
+ }
71
+
72
+ const result = analyzeGtmExport(upload, { tenantId: auth.tenantId, correlationId, generatedAt });
73
+
74
+ if (!result.ok) {
75
+ // Structured parse violations + a stable, safe error and the correlation id.
76
+ return json(
77
+ { ok: false, correlationId, error: result.error, violations: result.violations },
78
+ 422,
79
+ );
80
+ }
81
+
82
+ const wantsBundle = url.searchParams.get("format") === "bundle";
83
+ const responseBody = { ok: true, correlationId, report: result.report };
84
+ if (!wantsBundle) return json(responseBody, 200);
85
+
86
+ // Support-bundle download: the same redacted report as a JSON attachment.
87
+ const filename = `gtm-migration-report-${correlationId}.json`;
88
+ return new Response(JSON.stringify(responseBody, null, 2), {
89
+ status: 200,
90
+ headers: {
91
+ "content-type": "application/json",
92
+ "cache-control": "no-store",
93
+ "content-disposition": `attachment; filename="${filename}"`,
94
+ },
95
+ });
96
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * POST /api/apps/admin/integration-lifecycle — execute an integration-config
3
+ * lifecycle write action (ext-admin-07): `preview` a candidate (its effective
4
+ * code/config/CSP/consent/assurance diff), `publish` it to live THROUGH the
5
+ * policy-06 promotion gate, or `rollback` live to a known-good revision. The
6
+ * install-status actions (`suspend` / `resume` / `uninstall`) keep their own
7
+ * registry-backed routes; this route owns the three that ride the
8
+ * candidate/change-control substrate.
9
+ *
10
+ * Authorization mirrors every mutating admin route: `resolveOperatorSession`
11
+ * resolves the tenant from the host/console session (never the body), and
12
+ * `requireManageRole` denies a read-only console `developer` — so a developer or
13
+ * vendor never holds tenant publish authority. `requireOwnedInstall` rejects a
14
+ * foreign-tenant `installId` with the same 404 a missing one gets.
15
+ *
16
+ * The pure {@link handleLifecycleRequest} decides and persists ONLY on success —
17
+ * a blocked publish leaves the ACTUAL last-good state untouched. The lifecycle
18
+ * state + candidate-evidence + gate-input materialization is the control plane's
19
+ * job (the same materialization ext-admin-02's read model resolves per install);
20
+ * until an install is materialized the controller answers a 409, never a
21
+ * fabricated success.
22
+ */
23
+ import type { APIContext } from "astro";
24
+ import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
25
+ import {
26
+ getRegistryService,
27
+ requireOwnedInstall,
28
+ requireManageRole,
29
+ parseInstallId,
30
+ json,
31
+ } from "@/lib/apps/adminService.js";
32
+ import {
33
+ handleLifecycleRequest,
34
+ type LifecycleDeps,
35
+ type LifecycleWriteAction,
36
+ } from "@/lib/apps/integrationLifecycleController.js";
37
+ import { parseEnvironment, type IntegrationEnvironment } from "@tot/public-runtime";
38
+ import { loadTenantLocalLifecycleState } from "@/lib/apps/tenantIntegrationMaterialization.js";
39
+ import { resolveAdminTenant } from "@/config/adminTenantLookup";
40
+
41
+ export const prerender = false;
42
+
43
+ const WRITE_ACTIONS: ReadonlySet<string> = new Set(["preview", "publish", "rollback"]);
44
+
45
+ /**
46
+ * Build the control-plane materialization seam for one tenant's installs. A tenant's
47
+ * OWN (tenant-local) declaration is materialized through the interim bridge (ext-kernel-09):
48
+ * `load` returns its REAL live-by-config lifecycle state instead of the former blanket
49
+ * `null`, so a declared install is no longer reported `not_materialized`. It carries no
50
+ * separate candidate/promotion gate — an on-by-default declaration is already effective —
51
+ * so `resolveCandidateEvidence`/`resolveGate` stay null and a `preview`/`publish` refuses
52
+ * fail-closed (no candidate) rather than fabricating one. A managed-app install with a
53
+ * persisted candidate lifecycle remains the control plane's separate concern.
54
+ */
55
+ function integrationLifecycleDeps(_context: APIContext, tenantId: string, stage: IntegrationEnvironment): LifecycleDeps {
56
+ return {
57
+ load: async (installId) => {
58
+ // Best-effort: a resolver hiccup answers "unmaterialized" (fail-closed 409),
59
+ // never a 500 — an install with no materialized declaration is null either way.
60
+ try {
61
+ const tenant = await resolveAdminTenant(tenantId);
62
+ if (!tenant || tenant.tenant_id !== tenantId) return null;
63
+ return loadTenantLocalLifecycleState(tenant, installId, stage);
64
+ } catch (err) {
65
+ console.error(`[integration-lifecycle] materialization skipped for ${tenantId}: ${String(err)}`);
66
+ return null;
67
+ }
68
+ },
69
+ save: async () => {},
70
+ resolveCandidateEvidence: async () => null,
71
+ resolveGate: async () => null,
72
+ };
73
+ }
74
+
75
+ export async function POST(context: APIContext): Promise<Response> {
76
+ const requestUrl = new URL(context.request.url);
77
+ const requestedTenant = requestUrl.searchParams.get("tenant");
78
+ const auth = await resolveOperatorSession(context, requestedTenant);
79
+ if (!auth.ok) return json({ error: auth.message }, auth.status);
80
+ const manageDenied = requireManageRole(auth);
81
+ if (manageDenied) return manageDenied;
82
+
83
+ let body: unknown;
84
+ try {
85
+ body = await context.request.json();
86
+ } catch {
87
+ return json({ error: "invalid JSON body" }, 400);
88
+ }
89
+
90
+ const action = (body as { action?: unknown }).action;
91
+ if (typeof action !== "string" || !WRITE_ACTIONS.has(action)) {
92
+ return json({ error: "action must be one of: preview, publish, rollback" }, 400);
93
+ }
94
+ const installId = parseInstallId(body);
95
+ if (!installId) return json({ error: "installId is required" }, 400);
96
+
97
+ const registry = await getRegistryService();
98
+ if (registry instanceof Response) return registry;
99
+
100
+ const owned = await requireOwnedInstall(registry, installId, auth.tenantId);
101
+ if (owned instanceof Response) return owned;
102
+
103
+ const { expectedLiveRevision, targetRevision } = body as {
104
+ expectedLiveRevision?: string | null;
105
+ targetRevision?: string;
106
+ };
107
+
108
+ const stage: IntegrationEnvironment = parseEnvironment(requestUrl.searchParams.get("stage")) ?? "live";
109
+ const deps = integrationLifecycleDeps(context, auth.tenantId, stage);
110
+ const result = await handleLifecycleRequest(deps, {
111
+ action: action as LifecycleWriteAction,
112
+ installId,
113
+ actor: auth.actor,
114
+ ...(expectedLiveRevision !== undefined ? { expectedLiveRevision } : {}),
115
+ ...(typeof targetRevision === "string" ? { targetRevision } : {}),
116
+ });
117
+ return json(result.body, result.status);
118
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * GET /api/apps/admin/integrations — the effective integration admin read model
3
+ * (ext-admin-02): the tenant-scoped data behind ext-admin-01's `/admin#integrations`
4
+ * IA. Combines each install's desired config, resolved effective plan, compiled CSP,
5
+ * consent, assurance, candidate, and rollback availability into ONE safe view.
6
+ *
7
+ * Auth mirrors `list.ts` exactly: the `?tenant=` query param is only ever a CANDIDATE
8
+ * into `resolveOperatorSession` — the tenant actually used is `auth.tenantId` from the
9
+ * resolved session, never the raw param — so another tenant, or a vendor with no
10
+ * membership, is denied (401/403) before any read model is assembled. Read-only: any
11
+ * membership role admits. The pure {@link assembleIntegrationReadModel} is the second,
12
+ * defense-in-depth gate: it re-asserts every materialized input belongs to
13
+ * `auth.tenantId` and redacts secrets, so a mis-plumbed control plane cannot leak.
14
+ *
15
+ * The control-plane materialization of effective plans, candidate evidence, and
16
+ * assurance records is resolved per install where present; an install with none yet
17
+ * surfaces honestly as `configuring` (no effective authority, no rollback), never a
18
+ * fabricated live state.
19
+ */
20
+ import type { APIContext } from "astro";
21
+ import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
22
+ import { getRegistryService, json } from "@/lib/apps/adminService.js";
23
+ import { parseEnvironment, type IntegrationEnvironment } from "@tot/public-runtime";
24
+ import {
25
+ assembleIntegrationReadModel,
26
+ type IntegrationReadModelInputItem,
27
+ } from "@/lib/apps/integrationReadModel.js";
28
+ import { tenantLocalReadModelItems } from "@/lib/apps/tenantIntegrationMaterialization.js";
29
+ import { resolveAdminTenant } from "@/config/adminTenantLookup";
30
+
31
+ export const prerender = false;
32
+
33
+ /** A merchant-legible purpose line for an install, never an internal adapter name. */
34
+ function purposeFor(appId: string): string {
35
+ return `Integration: ${appId}`;
36
+ }
37
+
38
+ export async function GET(context: APIContext): Promise<Response> {
39
+ const url = new URL(context.request.url);
40
+ const requestedTenant = url.searchParams.get("tenant");
41
+ const auth = await resolveOperatorSession(context, requestedTenant);
42
+ if (!auth.ok) return json({ error: auth.message }, auth.status);
43
+
44
+ const registry = await getRegistryService();
45
+ if (registry instanceof Response) return registry;
46
+
47
+ const stage: IntegrationEnvironment = parseEnvironment(url.searchParams.get("stage")) ?? "staged";
48
+
49
+ const installs = await registry.listByTenant(auth.tenantId);
50
+ const items: IntegrationReadModelInputItem[] = installs
51
+ .filter((install) => install.status !== "uninstalled")
52
+ .map((install) => ({
53
+ install: {
54
+ installId: install.installId,
55
+ appId: install.appId,
56
+ appVersion: install.appVersion,
57
+ tenantId: install.tenantId,
58
+ status: install.status,
59
+ },
60
+ purpose: purposeFor(install.appId),
61
+ // The desired config is the tenant's own input; when the manifest snapshot is not
62
+ // a structured object it contributes nothing — never a fabricated value.
63
+ desiredConfig: {},
64
+ }));
65
+
66
+ // ext-kernel-09: a tenant's OWN (tenant-local) declarations are materialized through
67
+ // the interim bridge and appended with their REAL effective plan + compiled CSP, so a
68
+ // declared integration surfaces effective state — not `configuring`. Resolved off the
69
+ // auth-bound tenant config only (the tenant id is `auth.tenantId`, never the param).
70
+ // Best-effort + additive: a resolver hiccup degrades to the managed-app view rather
71
+ // than failing the whole read model closed.
72
+ try {
73
+ const tenantConfig = await resolveAdminTenant(auth.tenantId);
74
+ if (tenantConfig && tenantConfig.tenant_id === auth.tenantId) {
75
+ items.push(...tenantLocalReadModelItems(tenantConfig, stage));
76
+ }
77
+ } catch (err) {
78
+ console.error(`[admin-integrations] tenant-local materialization skipped for ${auth.tenantId}: ${String(err)}`);
79
+ }
80
+
81
+ const result = assembleIntegrationReadModel({ tenantPublicId: auth.tenantId, stage, items });
82
+ if (!result.ok) {
83
+ // A cross-tenant input or surviving secret is a server-side integrity failure, not
84
+ // something to echo back field-by-field.
85
+ return json({ error: "integration read model failed integrity checks" }, 500);
86
+ }
87
+
88
+ return json({ readModel: result.value });
89
+ }