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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/apps/storefront/astro.config.mjs +3 -3
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +12 -14
  4. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  5. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  6. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  7. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  8. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  9. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  10. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  11. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  12. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  13. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  14. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  15. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  16. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  17. package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
  18. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  19. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  20. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  21. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  22. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  23. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  24. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  25. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  26. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  27. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  28. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  29. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  30. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  31. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  32. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  33. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  34. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  35. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  36. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  37. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  38. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  39. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  40. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  41. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  42. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  43. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  44. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  45. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  46. package/apps/storefront/src/components/Img.astro +1 -1
  47. package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
  48. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  49. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
  50. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  51. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  52. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  53. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  54. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  55. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  56. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  57. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  58. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  59. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  60. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  61. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  62. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  63. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  64. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  65. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  66. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  67. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  68. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  69. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  70. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  71. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  72. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  73. package/apps/storefront/src/config/resolver.ts +4 -4
  74. package/apps/storefront/src/layouts/Layout.astro +100 -20
  75. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  76. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
  77. package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
  78. package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
  79. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  80. package/apps/storefront/src/lib/apps/adminService.ts +10 -0
  81. package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
  82. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  83. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  84. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  85. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  86. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  87. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  88. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  89. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  90. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  91. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  92. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  93. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  94. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
  95. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
  96. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  97. package/apps/storefront/src/lib/apps/registryService.ts +264 -10
  98. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  99. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  100. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  101. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  102. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  103. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  104. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  105. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  106. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  107. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  108. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  109. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  110. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  111. package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
  112. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  113. package/apps/storefront/src/lib/blog/collection.ts +97 -10
  114. package/apps/storefront/src/lib/blog/markdown.ts +1 -1
  115. package/apps/storefront/src/lib/blog/provider.ts +57 -3
  116. package/apps/storefront/src/lib/blog/types.ts +9 -9
  117. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  118. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  119. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  120. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  121. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  122. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  123. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  124. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  125. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  126. package/apps/storefront/src/lib/content/index.ts +5 -5
  127. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  128. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  129. package/apps/storefront/src/lib/d1/catalog.ts +12 -0
  130. package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
  131. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  132. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  133. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  134. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  135. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  136. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  137. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  138. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  139. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  140. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  141. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  142. package/apps/storefront/src/lib/edgeCache.ts +18 -6
  143. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  144. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  145. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  146. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  147. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  148. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  149. package/apps/storefront/src/lib/membership/model.ts +1 -2
  150. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
  151. package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
  152. package/apps/storefront/src/lib/newsletter/send.ts +3 -3
  153. package/apps/storefront/src/lib/previewReload.ts +57 -1
  154. package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
  155. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  156. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  157. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  158. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  159. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  160. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  161. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  162. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  163. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  164. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  165. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  166. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  167. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  168. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  169. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  170. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  171. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  172. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  173. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  174. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  175. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  176. package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
  177. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  178. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  179. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  180. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  181. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  182. package/apps/storefront/src/lib/seo/alternates.ts +1 -1
  183. package/apps/storefront/src/lib/social/notify.ts +4 -4
  184. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  185. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  186. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  187. package/apps/storefront/src/lib/the-build/provider.ts +9 -4
  188. package/apps/storefront/src/middleware/index.ts +273 -51
  189. package/apps/storefront/src/pages/[...slug].astro +9 -4
  190. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  191. package/apps/storefront/src/pages/admin/select.astro +96 -0
  192. package/apps/storefront/src/pages/admin.astro +16 -2
  193. package/apps/storefront/src/pages/api/activity.ts +8 -8
  194. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  195. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  196. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  197. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  198. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  199. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  200. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  201. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  202. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  203. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  204. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  205. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  206. package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
  207. package/apps/storefront/src/pages/blog/[slug].astro +5 -5
  208. package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
  209. package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
  210. package/apps/storefront/src/pages/blog/index.astro +2 -2
  211. package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
  212. package/apps/storefront/src/pages/cockpit.astro +61 -1362
  213. package/apps/storefront/src/pages/index.astro +10 -5
  214. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  215. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  216. package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
  217. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  218. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  219. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  220. package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
  221. package/apps/storefront/src/pages/the-build/index.astro +2 -2
  222. package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
  223. package/apps/storefront/src/styles/admin.css +27 -1
  224. package/apps/storefront/tsconfig.json +1 -1
  225. package/package.json +1 -1
  226. package/packages/public-runtime/package.json +2 -1
  227. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  228. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  229. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  230. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  231. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  232. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  233. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  234. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  235. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  236. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  237. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  238. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  239. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  240. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  241. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  242. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  243. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  244. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  245. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  246. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  247. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  248. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  249. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  250. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  251. package/packages/public-runtime/src/activity/catalog.ts +41 -37
  252. package/packages/public-runtime/src/activity/event.ts +6 -6
  253. package/packages/public-runtime/src/activity/index.ts +2 -2
  254. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  255. package/packages/public-runtime/src/candidate-index.ts +42 -8
  256. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  257. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  258. package/packages/public-runtime/src/checkout-style.ts +7 -7
  259. package/packages/public-runtime/src/checkout.ts +3 -3
  260. package/packages/public-runtime/src/chrome.ts +777 -0
  261. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  262. package/packages/public-runtime/src/csp.ts +99 -0
  263. package/packages/public-runtime/src/customization-runtime.ts +32 -1
  264. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  265. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  266. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  267. package/packages/public-runtime/src/evidence.ts +288 -0
  268. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  269. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  270. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  271. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  272. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  273. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  274. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  275. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  276. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  277. package/packages/public-runtime/src/index.ts +16 -1
  278. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  279. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  280. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  281. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  282. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  283. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  284. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  285. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  286. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  287. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  288. package/packages/public-runtime/src/integration/csp.ts +105 -0
  289. package/packages/public-runtime/src/integration/digest.ts +43 -0
  290. package/packages/public-runtime/src/integration/environment.ts +228 -0
  291. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  292. package/packages/public-runtime/src/integration/events.ts +701 -0
  293. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  294. package/packages/public-runtime/src/integration/index.ts +90 -0
  295. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  296. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  297. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  298. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  299. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  300. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  301. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  302. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  303. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  304. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  305. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  306. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  307. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  308. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  309. package/packages/public-runtime/src/integration/observability.ts +451 -0
  310. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  311. package/packages/public-runtime/src/integration/phases.ts +182 -0
  312. package/packages/public-runtime/src/integration/plan.ts +112 -0
  313. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  314. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  315. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  316. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  317. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  318. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  319. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  320. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  321. package/packages/public-runtime/src/integration/result.ts +239 -0
  322. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  323. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  324. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  325. package/packages/public-runtime/src/integration/version.ts +92 -0
  326. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  327. package/packages/public-runtime/src/membership.ts +2 -2
  328. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  329. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  330. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  331. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  332. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  333. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  334. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  335. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  336. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  337. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  338. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  339. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  340. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  341. package/packages/public-runtime/src/static-bundle.ts +5 -5
  342. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  343. package/packages/public-runtime/src/tenant.ts +45 -9
  344. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  345. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  346. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  347. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  348. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  349. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  350. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  351. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  352. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  353. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  354. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  355. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  356. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  357. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  358. package/scripts/dev/file-browser.mjs +2 -2
  359. package/scripts/dev/file-writer.mjs +2 -2
  360. package/scripts/dev/integration-guardrails.mjs +498 -0
  361. package/scripts/dev/safe-path.mjs +2 -2
  362. package/scripts/dev/tenant-source-root.mjs +1 -1
  363. package/scripts/tot-dev.mjs +2 -2
@@ -1,4 +1,15 @@
1
1
  import type { AuthorBio, BlogPost, BlogPostSummary, PostStatus } from "./types.js";
2
+ import type { ContentProvider } from "../storyblok/provider.js";
3
+
4
+ type ArtifactSource = Pick<ContentProvider, "readArtifact" | "listArtifactPaths">;
5
+ function canReadArtifacts(value: ArtifactSource | undefined): value is ArtifactSource {
6
+ return typeof value?.listArtifactPaths === "function" && typeof value.readArtifact === "function";
7
+ }
8
+ export interface CollectionReadOptions {
9
+ includeUnpublished?: boolean;
10
+ now?: string;
11
+ content?: ArtifactSource;
12
+ }
2
13
 
3
14
  /** A blog collection = a route base + a per-tenant content dir. */
4
15
  export interface Collection {
@@ -147,9 +158,22 @@ function toSummary(p: BlogPost): BlogPostSummary {
147
158
  * the same glob the index is built from (no per-tenant config needed). Sorted for
148
159
  * a stable admin ordering. A tenant with no blog content returns `[]`.
149
160
  */
150
- export function listCollections(tenantId: string): string[] {
161
+ export async function listCollections(
162
+ tenantId: string,
163
+ content?: ArtifactSource,
164
+ ): Promise<string[]> {
151
165
  const marker = `/tenants/${tenantId}/content/`;
152
166
  const collections = new Set<string>();
167
+ const runtimePaths = canReadArtifacts(content)
168
+ ? await content.listArtifactPaths("")
169
+ : null;
170
+ if (runtimePaths !== null) {
171
+ for (const path of runtimePaths) {
172
+ const match = /^([^/]+)\/posts\/[^/]+\.json$/.exec(path);
173
+ if (match?.[1] && SEGMENT_RE.test(match[1])) collections.add(match[1]);
174
+ }
175
+ return [...collections].sort();
176
+ }
153
177
  for (const key of Object.keys(postGlobs)) {
154
178
  const at = key.indexOf(marker);
155
179
  if (at < 0) continue;
@@ -168,12 +192,33 @@ export function listCollections(tenantId: string): string[] {
168
192
  export async function listPosts(
169
193
  tenantId: string,
170
194
  collection: string,
171
- opts?: { includeUnpublished?: boolean; now?: string },
195
+ opts?: CollectionReadOptions,
172
196
  ): Promise<BlogPostSummary[]> {
173
197
  if (!SEGMENT_RE.test(collection)) return [];
174
198
  const prefix = postsPrefix(tenantId, collection);
175
- const keys = Object.keys(postGlobs).filter((key) => key.includes(prefix));
176
- const posts = (await Promise.all(keys.map((key) => postGlobs[key]!()))).map(normalize);
199
+ const runtimeContent = canReadArtifacts(opts?.content) ? opts.content : null;
200
+ const runtimePaths = runtimeContent
201
+ ? await runtimeContent.listArtifactPaths(`${collection}/posts/`)
202
+ : null;
203
+ const posts = runtimePaths !== null
204
+ ? (await Promise.all(
205
+ runtimePaths
206
+ .filter((path) => path.endsWith(".json"))
207
+ .map(async (path) => {
208
+ const raw = await runtimeContent?.readArtifact(path);
209
+ if (!raw) return null;
210
+ try {
211
+ return normalize(JSON.parse(raw) as BlogPost);
212
+ } catch {
213
+ return null;
214
+ }
215
+ }),
216
+ )).filter((post): post is BlogPost => post !== null)
217
+ : (await Promise.all(
218
+ Object.keys(postGlobs)
219
+ .filter((key) => key.includes(prefix))
220
+ .map((key) => postGlobs[key]!()),
221
+ )).map(normalize);
177
222
  const includeUnpublished = opts?.includeUnpublished ?? false;
178
223
  const now = opts?.now ?? new Date().toISOString();
179
224
  return posts
@@ -191,9 +236,23 @@ export async function getPost(
191
236
  tenantId: string,
192
237
  collection: string,
193
238
  slug: string,
194
- opts?: { includeUnpublished?: boolean; now?: string },
239
+ opts?: CollectionReadOptions,
195
240
  ): Promise<BlogPost | null> {
196
241
  if (!SEGMENT_RE.test(collection) || !SLUG_RE.test(slug)) return null;
242
+ if (canReadArtifacts(opts?.content)) {
243
+ const paths = await opts.content.listArtifactPaths(`${collection}/posts/`);
244
+ const raw = await opts.content.readArtifact(`${collection}/posts/${slug}.json`);
245
+ if (raw) {
246
+ try {
247
+ const post = normalize(JSON.parse(raw) as BlogPost);
248
+ if (!(opts.includeUnpublished ?? false) && !isVisibleOnLive(post, opts.now)) return null;
249
+ return post;
250
+ } catch {
251
+ return null;
252
+ }
253
+ }
254
+ if (paths !== null) return null;
255
+ }
197
256
  const key = Object.keys(postGlobs).find(
198
257
  (candidate) =>
199
258
  candidate.includes(postsPrefix(tenantId, collection)) && candidate.endsWith(`/${slug}.json`),
@@ -215,7 +274,7 @@ export async function listTranslations(
215
274
  tenantId: string,
216
275
  collection: string,
217
276
  translationGroupId: string,
218
- opts?: { includeUnpublished?: boolean; now?: string },
277
+ opts?: CollectionReadOptions,
219
278
  ): Promise<BlogPostSummary[]> {
220
279
  if (!translationGroupId) return [];
221
280
  const posts = await listPosts(tenantId, collection, opts);
@@ -233,7 +292,7 @@ export async function getTranslations(
233
292
  tenantId: string,
234
293
  collection: string,
235
294
  slug: string,
236
- opts?: { includeUnpublished?: boolean; now?: string },
295
+ opts?: CollectionReadOptions,
237
296
  ): Promise<BlogPostSummary[]> {
238
297
  const post = await getPost(tenantId, collection, slug, opts);
239
298
  if (!post) return [];
@@ -252,9 +311,10 @@ export async function relatedPosts(
252
311
  collection: string,
253
312
  slug: string,
254
313
  n = 3,
314
+ content?: ArtifactSource,
255
315
  ): Promise<BlogPostSummary[]> {
256
- const pool = (await listPosts(tenantId, collection)).filter((p) => p.slug !== slug);
257
- const current = await getPost(tenantId, collection, slug);
316
+ const pool = (await listPosts(tenantId, collection, { content })).filter((p) => p.slug !== slug);
317
+ const current = await getPost(tenantId, collection, slug, { content });
258
318
  const category = current?.categories[0];
259
319
  const result = category ? pool.filter((p) => p.categories.includes(category)) : [];
260
320
  for (const p of pool) {
@@ -268,9 +328,10 @@ export async function resolveRedirect(
268
328
  tenantId: string,
269
329
  collection: string,
270
330
  slug: string,
331
+ content?: ArtifactSource,
271
332
  ): Promise<string | null> {
272
333
  if (!SEGMENT_RE.test(collection)) return null;
273
- const redirects = await getRedirects(tenantId, collection);
334
+ const redirects = await getRedirects(tenantId, collection, content);
274
335
  return redirects[slug] ?? null;
275
336
  }
276
337
 
@@ -282,8 +343,21 @@ export async function resolveRedirect(
282
343
  export async function getRedirects(
283
344
  tenantId: string,
284
345
  collection: string,
346
+ content?: ArtifactSource,
285
347
  ): Promise<Record<string, string>> {
286
348
  if (!SEGMENT_RE.test(collection)) return {};
349
+ if (canReadArtifacts(content)) {
350
+ const paths = await content.listArtifactPaths(`${collection}/`);
351
+ const raw = await content.readArtifact(`${collection}/redirects.json`);
352
+ if (raw) {
353
+ try {
354
+ return JSON.parse(raw) as Record<string, string>;
355
+ } catch {
356
+ return {};
357
+ }
358
+ }
359
+ if (paths !== null) return {};
360
+ }
287
361
  const key = Object.keys(redirectGlobs).find(
288
362
  (candidate) =>
289
363
  candidate.includes(`/tenants/${tenantId}/content/${collection}/`) &&
@@ -301,8 +375,21 @@ export async function getRedirects(
301
375
  export async function getAuthors(
302
376
  tenantId: string,
303
377
  collection: string,
378
+ content?: ArtifactSource,
304
379
  ): Promise<Record<string, AuthorBio>> {
305
380
  if (!SEGMENT_RE.test(collection)) return {};
381
+ if (canReadArtifacts(content)) {
382
+ const paths = await content.listArtifactPaths(`${collection}/`);
383
+ const raw = await content.readArtifact(`${collection}/authors.json`);
384
+ if (raw) {
385
+ try {
386
+ return JSON.parse(raw) as Record<string, AuthorBio>;
387
+ } catch {
388
+ return {};
389
+ }
390
+ }
391
+ if (paths !== null) return {};
392
+ }
306
393
  const key = Object.keys(authorsGlobs).find(
307
394
  (candidate) =>
308
395
  candidate.includes(`/tenants/${tenantId}/content/${collection}/`) &&
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Minimal, dependency-free Markdown -> sanitized HTML for the admin blog editor
3
- * (B3.4 of docs/plans/2026-08-21-blog-platform-workstream.md). The storefront
3
+ * (see docs/plans/2026-08-21-blog-platform-workstream.md). The storefront
4
4
  * ships no markdown library (see the "No new deps" convention in §1), and the
5
5
  * migration package's `sanitize-html` is a node-only devtool — so authoring from
6
6
  * the admin gets this small hand-rolled renderer instead.
@@ -4,6 +4,13 @@
4
4
  // collection; switching `/blog` onto it is a later migration once order is
5
5
  // encoded as data (see B0.1 §"/blog migration caution").
6
6
  import type { BlogPost, BlogPostSummary } from "./types.js";
7
+ import type { ContentProvider } from "../storyblok/provider.js";
8
+
9
+ type ArtifactSource = Pick<ContentProvider, "readArtifact" | "listArtifactPaths">;
10
+
11
+ function canReadArtifacts(value: ArtifactSource | undefined): value is ArtifactSource {
12
+ return typeof value?.listArtifactPaths === "function" && typeof value.readArtifact === "function";
13
+ }
7
14
 
8
15
  const indexGlobs = import.meta.glob<BlogPostSummary[]>(
9
16
  "../../../../../tenants/*/content/blog/index.json",
@@ -23,19 +30,66 @@ function tenantKey(keys: string[], tenantId: string, suffix: string): string | u
23
30
  return keys.find((key) => key.includes(segment) && key.endsWith(suffix));
24
31
  }
25
32
 
26
- export async function listBlogPosts(tenantId: string): Promise<BlogPostSummary[]> {
33
+ export async function listBlogPosts(
34
+ tenantId: string,
35
+ source?: ArtifactSource,
36
+ ): Promise<BlogPostSummary[]> {
37
+ if (canReadArtifacts(source)) {
38
+ const paths = await source.listArtifactPaths("blog/");
39
+ const raw = await source.readArtifact("blog/index.json");
40
+ if (raw) {
41
+ try {
42
+ return JSON.parse(raw) as BlogPostSummary[];
43
+ } catch {
44
+ return [];
45
+ }
46
+ }
47
+ if (paths !== null) return [];
48
+ }
27
49
  const key = tenantKey(Object.keys(indexGlobs), tenantId, "/index.json")
28
50
  ?? Object.keys(indexGlobs).find((candidate) => candidate.includes(`/tenants/${tenantId}/content/blog/index.json`));
29
51
  return key ? indexGlobs[key]!() : [];
30
52
  }
31
53
 
32
- export async function getBlogPost(tenantId: string, slug: string): Promise<BlogPost | null> {
54
+ export async function getBlogPost(
55
+ tenantId: string,
56
+ slug: string,
57
+ source?: ArtifactSource,
58
+ ): Promise<BlogPost | null> {
33
59
  if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) return null;
60
+ if (canReadArtifacts(source)) {
61
+ const paths = await source.listArtifactPaths("blog/posts/");
62
+ const raw = await source.readArtifact(`blog/posts/${slug}.json`);
63
+ if (raw) {
64
+ try {
65
+ return JSON.parse(raw) as BlogPost;
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+ if (paths !== null) return null;
71
+ }
34
72
  const key = tenantKey(Object.keys(postGlobs), tenantId, `/posts/${slug}.json`);
35
73
  return key ? postGlobs[key]!() : null;
36
74
  }
37
75
 
38
- export async function resolveBlogRedirect(tenantId: string, slug: string): Promise<string | null> {
76
+ export async function resolveBlogRedirect(
77
+ tenantId: string,
78
+ slug: string,
79
+ source?: ArtifactSource,
80
+ ): Promise<string | null> {
81
+ if (canReadArtifacts(source)) {
82
+ const paths = await source.listArtifactPaths("blog/");
83
+ const raw = await source.readArtifact("blog/redirects.json");
84
+ if (raw) {
85
+ try {
86
+ return (JSON.parse(raw) as Record<string, string>)[slug] ?? null;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+ if (paths !== null) return null;
92
+ }
39
93
  const key = tenantKey(Object.keys(redirectGlobs), tenantId, "/redirects.json")
40
94
  ?? Object.keys(redirectGlobs).find((candidate) => candidate.includes(`/tenants/${tenantId}/content/blog/redirects.json`));
41
95
  if (!key) return null;
@@ -5,7 +5,7 @@ export interface BlogImage {
5
5
  height?: number;
6
6
  }
7
7
 
8
- export type PostStatus = "draft" | "published" | "unlisted" | "scheduled"; // "scheduled" added in B3.1
8
+ export type PostStatus = "draft" | "published" | "unlisted" | "scheduled";
9
9
 
10
10
  /**
11
11
  * Per-post entitlement gate — who may read the body. Orthogonal to `PostStatus`
@@ -29,17 +29,17 @@ export interface BlogPostSummary {
29
29
  tags: string[];
30
30
  featuredImage?: BlogImage;
31
31
  status: PostStatus; // seeded/legacy posts without this field default to "published"
32
- scheduledAt?: string; // ISO date; when status === "scheduled", the post goes live once this instant has passed (B3.1)
33
- canonicalUrl?: string; // syndication override (B1.6)
34
- inLanguage?: string; // default "en" (B1.6)
35
- access?: BlogAccess; // entitlement gate; default "public". Orthogonal to `status` (B4.0)
36
- translationGroupId?: string; // stable id shared by every language variant of one article; absent = singleton (I1)
32
+ scheduledAt?: string; // ISO date; when status === "scheduled", the post goes live once this instant has passed
33
+ canonicalUrl?: string; // syndication override
34
+ inLanguage?: string; // default "en"
35
+ access?: BlogAccess; // entitlement gate; default "public". Orthogonal to `status`
36
+ translationGroupId?: string; // stable id shared by every language variant of one article; absent = singleton
37
37
  }
38
38
 
39
39
  export interface BlogPost extends BlogPostSummary {
40
40
  oldSlugs: string[];
41
41
  contentHtml: string;
42
- // Markdown source for the admin editor (B3.4). When present, `contentHtml` is
42
+ // Markdown source for the admin editor. When present, `contentHtml` is
43
43
  // its rendered output; migrated/legacy posts may carry only `contentHtml`.
44
44
  bodyMarkdown?: string;
45
45
  }
@@ -55,6 +55,6 @@ export interface AuthorBio {
55
55
  title?: string; // role/title, e.g. "Founder"
56
56
  avatar?: string; // image path (site-relative or absolute)
57
57
  url?: string; // personal/site URL
58
- sameAs?: string[]; // social profile URLs — E-E-A-T signal (feeds Person.sameAs, B1.6)
59
- twitter?: string; // handle, e.g. "@ada" (feeds twitter:creator, B1.6)
58
+ sameAs?: string[]; // social profile URLs — E-E-A-T signal (feeds Person.sameAs)
59
+ twitter?: string; // handle, e.g. "@ada" (feeds twitter:creator)
60
60
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "assignments": {
3
+ "/contact/contact-sales": { "headerVariant": "minimal" },
4
+ "/contact/contact-sales/thank-you": { "headerVariant": "minimal" }
5
+ }
6
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "lang": "en",
3
+ "head": {
4
+ "favicon": "/tenants/tokenoftrust.com/favicon.png",
5
+ "themeStylesheets": ["/tenants/tokenoftrust.com/themes/tot-navy.css"]
6
+ },
7
+ "sharedMarketingCss": true,
8
+ "header": {
9
+ "variant": "primary",
10
+ "brand": {
11
+ "href": "/",
12
+ "label": "Token of Trust",
13
+ "logo": {
14
+ "src": "/tenants/tokenoftrust.com/logo-wordmark.png",
15
+ "alt": "Token of Trust",
16
+ "width": 180,
17
+ "height": 32
18
+ }
19
+ },
20
+ "nav": [
21
+ { "id": "nav-platform", "label": "Platform", "href": "/#platform" },
22
+ { "id": "nav-industries", "label": "Industries", "href": "/#industries" },
23
+ { "id": "nav-resources", "label": "Resources", "href": "https://help.tokenoftrust.com", "external": true },
24
+ { "id": "nav-pricing", "label": "Pricing", "href": "https://tokenoftrust.com/pricing/", "external": true },
25
+ { "id": "nav-login", "label": "Login", "href": "https://app.tokenoftrust.com", "external": true }
26
+ ],
27
+ "ctas": [
28
+ { "id": "cta-book-demo", "label": "Book Demo", "href": "https://tokenoftrust.com/contact/", "variant": "secondary" },
29
+ { "id": "cta-start-free", "label": "Start For Free", "href": "https://app.tokenoftrust.com", "variant": "primary" }
30
+ ]
31
+ },
32
+ "footer": {
33
+ "variant": "default",
34
+ "brand": {
35
+ "text": "Customer verification and compliance for regulated ecommerce. 200+ countries. We will never sell your data."
36
+ },
37
+ "columns": [
38
+ {
39
+ "title": "Platform",
40
+ "links": [
41
+ { "id": "ftr-platform-idv", "label": "Identity Verification", "href": "/#platform" },
42
+ { "id": "ftr-platform-age", "label": "Age Assurance", "href": "/#platform" },
43
+ { "id": "ftr-platform-fraud", "label": "Fraud Prevention", "href": "/#platform" },
44
+ { "id": "ftr-platform-evidence", "label": "Compliance Evidence", "href": "/#platform" },
45
+ { "id": "ftr-platform-aml", "label": "AML · PACT Act · Tax", "href": "/#platform" }
46
+ ]
47
+ },
48
+ {
49
+ "title": "Industries",
50
+ "links": [
51
+ { "id": "ftr-industries-alcohol", "label": "Alcohol", "href": "/#industries" },
52
+ { "id": "ftr-industries-vape", "label": "Tobacco & Vape", "href": "/#industries" },
53
+ { "id": "ftr-industries-age-restricted", "label": "Age-Restricted Goods", "href": "/#industries" },
54
+ { "id": "ftr-industries-marketplaces", "label": "Marketplaces", "href": "/#industries" }
55
+ ]
56
+ },
57
+ {
58
+ "title": "Company",
59
+ "links": [
60
+ { "id": "ftr-company-pricing", "label": "Pricing", "href": "https://tokenoftrust.com/pricing/", "external": true },
61
+ { "id": "ftr-company-contact", "label": "Contact Sales", "href": "https://tokenoftrust.com/contact/", "external": true },
62
+ { "id": "ftr-company-resources", "label": "Resources", "href": "https://help.tokenoftrust.com", "external": true },
63
+ { "id": "ftr-company-login", "label": "Login", "href": "https://app.tokenoftrust.com", "external": true }
64
+ ]
65
+ },
66
+ {
67
+ "title": "Legal",
68
+ "links": [
69
+ { "id": "ftr-legal-privacy", "label": "Privacy Policy", "href": "https://tokenoftrust.com/privacy/", "external": true },
70
+ { "id": "ftr-legal-terms", "label": "Terms", "href": "https://tokenoftrust.com/terms/", "external": true }
71
+ ]
72
+ }
73
+ ],
74
+ "social": [],
75
+ "bottom": {
76
+ "note": "GDPR · CCPA · PIPEDA compliant. 256-bit SSL. We will never sell your data."
77
+ }
78
+ }
79
+ }