@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/astro.config.mjs +3 -3
- package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +8 -3
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +12 -14
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +18 -10
- package/apps/storefront/src/layouts/Layout.astro +100 -20
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
- package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
- package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/apps/adminService.ts +10 -0
- package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +264 -10
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/blog/collection.ts +114 -17
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +76 -9
- package/apps/storefront/src/lib/blog/types.ts +9 -9
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +12 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +18 -6
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
- package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
- package/apps/storefront/src/lib/newsletter/send.ts +3 -3
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/seo/alternates.ts +1 -1
- package/apps/storefront/src/lib/social/notify.ts +4 -4
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +203 -69
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +9 -4
- package/apps/storefront/src/middleware/index.ts +273 -51
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +16 -2
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
- package/apps/storefront/src/pages/blog/[slug].astro +5 -5
- package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
- package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
- package/apps/storefront/src/pages/blog/index.astro +2 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
- package/apps/storefront/src/pages/cockpit.astro +61 -1362
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +42 -31
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +8 -3
- package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +6 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +6 -2
- package/apps/storefront/src/pages/style-guide/index.astro +19 -5
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
- package/apps/storefront/src/pages/the-build/index.astro +2 -2
- package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
- package/apps/storefront/src/styles/admin.css +27 -1
- package/apps/storefront/src/themes/tenants/index.ts +19 -7
- package/apps/storefront/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +41 -37
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-runtime.ts +32 -1
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +16 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +5 -5
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +136 -9
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/build/copy-tenant-assets.mjs +12 -5
- package/scripts/dev/ai-edit.mjs +2 -1
- package/scripts/dev/file-browser.mjs +2 -2
- package/scripts/dev/file-writer.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/tenant-source-root.mjs +3 -2
- package/scripts/tot-dev.mjs +5 -4
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
import { tenantDirSegment } from "@tot/public-runtime";
|
|
1
2
|
import type { AuthorBio, BlogPost, BlogPostSummary, PostStatus } from "./types.js";
|
|
3
|
+
import type { ContentProvider } from "../storyblok/provider.js";
|
|
4
|
+
|
|
5
|
+
type ArtifactSource = Pick<ContentProvider, "readArtifact" | "listArtifactPaths">;
|
|
6
|
+
function canReadArtifacts(value: ArtifactSource | undefined): value is ArtifactSource {
|
|
7
|
+
return typeof value?.listArtifactPaths === "function" && typeof value.readArtifact === "function";
|
|
8
|
+
}
|
|
9
|
+
export interface CollectionReadOptions {
|
|
10
|
+
includeUnpublished?: boolean;
|
|
11
|
+
now?: string;
|
|
12
|
+
content?: ArtifactSource;
|
|
13
|
+
}
|
|
2
14
|
|
|
3
15
|
/** A blog collection = a route base + a per-tenant content dir. */
|
|
4
16
|
export interface Collection {
|
|
@@ -10,15 +22,24 @@ export interface Collection {
|
|
|
10
22
|
// collection id are both path segments filtered at call time. The derived index
|
|
11
23
|
// is the set of `posts/*.json` files — there is no hand-maintained index.json.
|
|
12
24
|
const postGlobs = import.meta.glob<BlogPost>(
|
|
13
|
-
|
|
25
|
+
[
|
|
26
|
+
"../../../../../tenants/*/content/*/posts/*.json",
|
|
27
|
+
"../../../../../tenants/e2e/*/content/*/posts/*.json",
|
|
28
|
+
],
|
|
14
29
|
{ import: "default" },
|
|
15
30
|
);
|
|
16
31
|
const redirectGlobs = import.meta.glob<Record<string, string>>(
|
|
17
|
-
|
|
32
|
+
[
|
|
33
|
+
"../../../../../tenants/*/content/*/redirects.json",
|
|
34
|
+
"../../../../../tenants/e2e/*/content/*/redirects.json",
|
|
35
|
+
],
|
|
18
36
|
{ import: "default" },
|
|
19
37
|
);
|
|
20
38
|
const authorsGlobs = import.meta.glob<Record<string, AuthorBio>>(
|
|
21
|
-
|
|
39
|
+
[
|
|
40
|
+
"../../../../../tenants/*/content/*/authors.json",
|
|
41
|
+
"../../../../../tenants/e2e/*/content/*/authors.json",
|
|
42
|
+
],
|
|
22
43
|
{ import: "default" },
|
|
23
44
|
);
|
|
24
45
|
|
|
@@ -117,7 +138,7 @@ function normalize(raw: BlogPost): BlogPost {
|
|
|
117
138
|
}
|
|
118
139
|
|
|
119
140
|
function postsPrefix(tenantId: string, collection: string): string {
|
|
120
|
-
return
|
|
141
|
+
return `${tenantDirSegment(tenantId)}content/${collection}/posts/`;
|
|
121
142
|
}
|
|
122
143
|
|
|
123
144
|
function toSummary(p: BlogPost): BlogPostSummary {
|
|
@@ -147,9 +168,22 @@ function toSummary(p: BlogPost): BlogPostSummary {
|
|
|
147
168
|
* the same glob the index is built from (no per-tenant config needed). Sorted for
|
|
148
169
|
* a stable admin ordering. A tenant with no blog content returns `[]`.
|
|
149
170
|
*/
|
|
150
|
-
export function listCollections(
|
|
151
|
-
|
|
171
|
+
export async function listCollections(
|
|
172
|
+
tenantId: string,
|
|
173
|
+
content?: ArtifactSource,
|
|
174
|
+
): Promise<string[]> {
|
|
175
|
+
const marker = `${tenantDirSegment(tenantId)}content/`;
|
|
152
176
|
const collections = new Set<string>();
|
|
177
|
+
const runtimePaths = canReadArtifacts(content)
|
|
178
|
+
? await content.listArtifactPaths("")
|
|
179
|
+
: null;
|
|
180
|
+
if (runtimePaths !== null) {
|
|
181
|
+
for (const path of runtimePaths) {
|
|
182
|
+
const match = /^([^/]+)\/posts\/[^/]+\.json$/.exec(path);
|
|
183
|
+
if (match?.[1] && SEGMENT_RE.test(match[1])) collections.add(match[1]);
|
|
184
|
+
}
|
|
185
|
+
return [...collections].sort();
|
|
186
|
+
}
|
|
153
187
|
for (const key of Object.keys(postGlobs)) {
|
|
154
188
|
const at = key.indexOf(marker);
|
|
155
189
|
if (at < 0) continue;
|
|
@@ -168,12 +202,33 @@ export function listCollections(tenantId: string): string[] {
|
|
|
168
202
|
export async function listPosts(
|
|
169
203
|
tenantId: string,
|
|
170
204
|
collection: string,
|
|
171
|
-
opts?:
|
|
205
|
+
opts?: CollectionReadOptions,
|
|
172
206
|
): Promise<BlogPostSummary[]> {
|
|
173
207
|
if (!SEGMENT_RE.test(collection)) return [];
|
|
174
208
|
const prefix = postsPrefix(tenantId, collection);
|
|
175
|
-
const
|
|
176
|
-
const
|
|
209
|
+
const runtimeContent = canReadArtifacts(opts?.content) ? opts.content : null;
|
|
210
|
+
const runtimePaths = runtimeContent
|
|
211
|
+
? await runtimeContent.listArtifactPaths(`${collection}/posts/`)
|
|
212
|
+
: null;
|
|
213
|
+
const posts = runtimePaths !== null
|
|
214
|
+
? (await Promise.all(
|
|
215
|
+
runtimePaths
|
|
216
|
+
.filter((path) => path.endsWith(".json"))
|
|
217
|
+
.map(async (path) => {
|
|
218
|
+
const raw = await runtimeContent?.readArtifact(path);
|
|
219
|
+
if (!raw) return null;
|
|
220
|
+
try {
|
|
221
|
+
return normalize(JSON.parse(raw) as BlogPost);
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}),
|
|
226
|
+
)).filter((post): post is BlogPost => post !== null)
|
|
227
|
+
: (await Promise.all(
|
|
228
|
+
Object.keys(postGlobs)
|
|
229
|
+
.filter((key) => key.includes(prefix))
|
|
230
|
+
.map((key) => postGlobs[key]!()),
|
|
231
|
+
)).map(normalize);
|
|
177
232
|
const includeUnpublished = opts?.includeUnpublished ?? false;
|
|
178
233
|
const now = opts?.now ?? new Date().toISOString();
|
|
179
234
|
return posts
|
|
@@ -191,9 +246,23 @@ export async function getPost(
|
|
|
191
246
|
tenantId: string,
|
|
192
247
|
collection: string,
|
|
193
248
|
slug: string,
|
|
194
|
-
opts?:
|
|
249
|
+
opts?: CollectionReadOptions,
|
|
195
250
|
): Promise<BlogPost | null> {
|
|
196
251
|
if (!SEGMENT_RE.test(collection) || !SLUG_RE.test(slug)) return null;
|
|
252
|
+
if (canReadArtifacts(opts?.content)) {
|
|
253
|
+
const paths = await opts.content.listArtifactPaths(`${collection}/posts/`);
|
|
254
|
+
const raw = await opts.content.readArtifact(`${collection}/posts/${slug}.json`);
|
|
255
|
+
if (raw) {
|
|
256
|
+
try {
|
|
257
|
+
const post = normalize(JSON.parse(raw) as BlogPost);
|
|
258
|
+
if (!(opts.includeUnpublished ?? false) && !isVisibleOnLive(post, opts.now)) return null;
|
|
259
|
+
return post;
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (paths !== null) return null;
|
|
265
|
+
}
|
|
197
266
|
const key = Object.keys(postGlobs).find(
|
|
198
267
|
(candidate) =>
|
|
199
268
|
candidate.includes(postsPrefix(tenantId, collection)) && candidate.endsWith(`/${slug}.json`),
|
|
@@ -215,7 +284,7 @@ export async function listTranslations(
|
|
|
215
284
|
tenantId: string,
|
|
216
285
|
collection: string,
|
|
217
286
|
translationGroupId: string,
|
|
218
|
-
opts?:
|
|
287
|
+
opts?: CollectionReadOptions,
|
|
219
288
|
): Promise<BlogPostSummary[]> {
|
|
220
289
|
if (!translationGroupId) return [];
|
|
221
290
|
const posts = await listPosts(tenantId, collection, opts);
|
|
@@ -233,7 +302,7 @@ export async function getTranslations(
|
|
|
233
302
|
tenantId: string,
|
|
234
303
|
collection: string,
|
|
235
304
|
slug: string,
|
|
236
|
-
opts?:
|
|
305
|
+
opts?: CollectionReadOptions,
|
|
237
306
|
): Promise<BlogPostSummary[]> {
|
|
238
307
|
const post = await getPost(tenantId, collection, slug, opts);
|
|
239
308
|
if (!post) return [];
|
|
@@ -252,9 +321,10 @@ export async function relatedPosts(
|
|
|
252
321
|
collection: string,
|
|
253
322
|
slug: string,
|
|
254
323
|
n = 3,
|
|
324
|
+
content?: ArtifactSource,
|
|
255
325
|
): Promise<BlogPostSummary[]> {
|
|
256
|
-
const pool = (await listPosts(tenantId, collection)).filter((p) => p.slug !== slug);
|
|
257
|
-
const current = await getPost(tenantId, collection, slug);
|
|
326
|
+
const pool = (await listPosts(tenantId, collection, { content })).filter((p) => p.slug !== slug);
|
|
327
|
+
const current = await getPost(tenantId, collection, slug, { content });
|
|
258
328
|
const category = current?.categories[0];
|
|
259
329
|
const result = category ? pool.filter((p) => p.categories.includes(category)) : [];
|
|
260
330
|
for (const p of pool) {
|
|
@@ -268,9 +338,10 @@ export async function resolveRedirect(
|
|
|
268
338
|
tenantId: string,
|
|
269
339
|
collection: string,
|
|
270
340
|
slug: string,
|
|
341
|
+
content?: ArtifactSource,
|
|
271
342
|
): Promise<string | null> {
|
|
272
343
|
if (!SEGMENT_RE.test(collection)) return null;
|
|
273
|
-
const redirects = await getRedirects(tenantId, collection);
|
|
344
|
+
const redirects = await getRedirects(tenantId, collection, content);
|
|
274
345
|
return redirects[slug] ?? null;
|
|
275
346
|
}
|
|
276
347
|
|
|
@@ -282,11 +353,24 @@ export async function resolveRedirect(
|
|
|
282
353
|
export async function getRedirects(
|
|
283
354
|
tenantId: string,
|
|
284
355
|
collection: string,
|
|
356
|
+
content?: ArtifactSource,
|
|
285
357
|
): Promise<Record<string, string>> {
|
|
286
358
|
if (!SEGMENT_RE.test(collection)) return {};
|
|
359
|
+
if (canReadArtifacts(content)) {
|
|
360
|
+
const paths = await content.listArtifactPaths(`${collection}/`);
|
|
361
|
+
const raw = await content.readArtifact(`${collection}/redirects.json`);
|
|
362
|
+
if (raw) {
|
|
363
|
+
try {
|
|
364
|
+
return JSON.parse(raw) as Record<string, string>;
|
|
365
|
+
} catch {
|
|
366
|
+
return {};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (paths !== null) return {};
|
|
370
|
+
}
|
|
287
371
|
const key = Object.keys(redirectGlobs).find(
|
|
288
372
|
(candidate) =>
|
|
289
|
-
candidate.includes(
|
|
373
|
+
candidate.includes(`${tenantDirSegment(tenantId)}content/${collection}/`) &&
|
|
290
374
|
candidate.endsWith("/redirects.json"),
|
|
291
375
|
);
|
|
292
376
|
if (!key) return {};
|
|
@@ -301,11 +385,24 @@ export async function getRedirects(
|
|
|
301
385
|
export async function getAuthors(
|
|
302
386
|
tenantId: string,
|
|
303
387
|
collection: string,
|
|
388
|
+
content?: ArtifactSource,
|
|
304
389
|
): Promise<Record<string, AuthorBio>> {
|
|
305
390
|
if (!SEGMENT_RE.test(collection)) return {};
|
|
391
|
+
if (canReadArtifacts(content)) {
|
|
392
|
+
const paths = await content.listArtifactPaths(`${collection}/`);
|
|
393
|
+
const raw = await content.readArtifact(`${collection}/authors.json`);
|
|
394
|
+
if (raw) {
|
|
395
|
+
try {
|
|
396
|
+
return JSON.parse(raw) as Record<string, AuthorBio>;
|
|
397
|
+
} catch {
|
|
398
|
+
return {};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (paths !== null) return {};
|
|
402
|
+
}
|
|
306
403
|
const key = Object.keys(authorsGlobs).find(
|
|
307
404
|
(candidate) =>
|
|
308
|
-
candidate.includes(
|
|
405
|
+
candidate.includes(`${tenantDirSegment(tenantId)}content/${collection}/`) &&
|
|
309
406
|
candidate.endsWith("/authors.json"),
|
|
310
407
|
);
|
|
311
408
|
if (!key) return {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal, dependency-free Markdown -> sanitized HTML for the admin blog editor
|
|
3
|
-
* (
|
|
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.
|
|
@@ -3,41 +3,108 @@
|
|
|
3
3
|
// derived engine lives in ./collection.ts and powers `the-build` + any new
|
|
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
|
+
import { tenantDirSegment } from "@tot/public-runtime";
|
|
6
7
|
import type { BlogPost, BlogPostSummary } from "./types.js";
|
|
8
|
+
import type { ContentProvider } from "../storyblok/provider.js";
|
|
7
9
|
|
|
10
|
+
type ArtifactSource = Pick<ContentProvider, "readArtifact" | "listArtifactPaths">;
|
|
11
|
+
|
|
12
|
+
function canReadArtifacts(value: ArtifactSource | undefined): value is ArtifactSource {
|
|
13
|
+
return typeof value?.listArtifactPaths === "function" && typeof value.readArtifact === "function";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Both tenant tiers: real at `tenants/<id>/`, platform test one level deeper at
|
|
17
|
+
// `tenants/e2e/<id>/`. `tenantDirSegment` is the one inverse used to look a
|
|
18
|
+
// tenant's keys back up.
|
|
8
19
|
const indexGlobs = import.meta.glob<BlogPostSummary[]>(
|
|
9
|
-
|
|
20
|
+
[
|
|
21
|
+
"../../../../../tenants/*/content/blog/index.json",
|
|
22
|
+
"../../../../../tenants/e2e/*/content/blog/index.json",
|
|
23
|
+
],
|
|
10
24
|
{ import: "default" },
|
|
11
25
|
);
|
|
12
26
|
const postGlobs = import.meta.glob<BlogPost>(
|
|
13
|
-
|
|
27
|
+
[
|
|
28
|
+
"../../../../../tenants/*/content/blog/posts/*.json",
|
|
29
|
+
"../../../../../tenants/e2e/*/content/blog/posts/*.json",
|
|
30
|
+
],
|
|
14
31
|
{ import: "default" },
|
|
15
32
|
);
|
|
16
33
|
const redirectGlobs = import.meta.glob<Record<string, string>>(
|
|
17
|
-
|
|
34
|
+
[
|
|
35
|
+
"../../../../../tenants/*/content/blog/redirects.json",
|
|
36
|
+
"../../../../../tenants/e2e/*/content/blog/redirects.json",
|
|
37
|
+
],
|
|
18
38
|
{ import: "default" },
|
|
19
39
|
);
|
|
20
40
|
|
|
21
41
|
function tenantKey(keys: string[], tenantId: string, suffix: string): string | undefined {
|
|
22
|
-
const segment =
|
|
42
|
+
const segment = `${tenantDirSegment(tenantId)}content/blog/`;
|
|
23
43
|
return keys.find((key) => key.includes(segment) && key.endsWith(suffix));
|
|
24
44
|
}
|
|
25
45
|
|
|
26
|
-
export async function listBlogPosts(
|
|
46
|
+
export async function listBlogPosts(
|
|
47
|
+
tenantId: string,
|
|
48
|
+
source?: ArtifactSource,
|
|
49
|
+
): Promise<BlogPostSummary[]> {
|
|
50
|
+
if (canReadArtifacts(source)) {
|
|
51
|
+
const paths = await source.listArtifactPaths("blog/");
|
|
52
|
+
const raw = await source.readArtifact("blog/index.json");
|
|
53
|
+
if (raw) {
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(raw) as BlogPostSummary[];
|
|
56
|
+
} catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (paths !== null) return [];
|
|
61
|
+
}
|
|
27
62
|
const key = tenantKey(Object.keys(indexGlobs), tenantId, "/index.json")
|
|
28
|
-
?? Object.keys(indexGlobs).find((candidate) => candidate.includes(
|
|
63
|
+
?? Object.keys(indexGlobs).find((candidate) => candidate.includes(`${tenantDirSegment(tenantId)}content/blog/index.json`));
|
|
29
64
|
return key ? indexGlobs[key]!() : [];
|
|
30
65
|
}
|
|
31
66
|
|
|
32
|
-
export async function getBlogPost(
|
|
67
|
+
export async function getBlogPost(
|
|
68
|
+
tenantId: string,
|
|
69
|
+
slug: string,
|
|
70
|
+
source?: ArtifactSource,
|
|
71
|
+
): Promise<BlogPost | null> {
|
|
33
72
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) return null;
|
|
73
|
+
if (canReadArtifacts(source)) {
|
|
74
|
+
const paths = await source.listArtifactPaths("blog/posts/");
|
|
75
|
+
const raw = await source.readArtifact(`blog/posts/${slug}.json`);
|
|
76
|
+
if (raw) {
|
|
77
|
+
try {
|
|
78
|
+
return JSON.parse(raw) as BlogPost;
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (paths !== null) return null;
|
|
84
|
+
}
|
|
34
85
|
const key = tenantKey(Object.keys(postGlobs), tenantId, `/posts/${slug}.json`);
|
|
35
86
|
return key ? postGlobs[key]!() : null;
|
|
36
87
|
}
|
|
37
88
|
|
|
38
|
-
export async function resolveBlogRedirect(
|
|
89
|
+
export async function resolveBlogRedirect(
|
|
90
|
+
tenantId: string,
|
|
91
|
+
slug: string,
|
|
92
|
+
source?: ArtifactSource,
|
|
93
|
+
): Promise<string | null> {
|
|
94
|
+
if (canReadArtifacts(source)) {
|
|
95
|
+
const paths = await source.listArtifactPaths("blog/");
|
|
96
|
+
const raw = await source.readArtifact("blog/redirects.json");
|
|
97
|
+
if (raw) {
|
|
98
|
+
try {
|
|
99
|
+
return (JSON.parse(raw) as Record<string, string>)[slug] ?? null;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (paths !== null) return null;
|
|
105
|
+
}
|
|
39
106
|
const key = tenantKey(Object.keys(redirectGlobs), tenantId, "/redirects.json")
|
|
40
|
-
?? Object.keys(redirectGlobs).find((candidate) => candidate.includes(
|
|
107
|
+
?? Object.keys(redirectGlobs).find((candidate) => candidate.includes(`${tenantDirSegment(tenantId)}content/blog/redirects.json`));
|
|
41
108
|
if (!key) return null;
|
|
42
109
|
const redirects = await redirectGlobs[key]!();
|
|
43
110
|
return redirects[slug] ?? null;
|
|
@@ -5,7 +5,7 @@ export interface BlogImage {
|
|
|
5
5
|
height?: number;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export type PostStatus = "draft" | "published" | "unlisted" | "scheduled";
|
|
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
|
|
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
|
|
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
|
|
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
|
|
59
|
-
twitter?: string; // handle, e.g. "@ada" (feeds twitter:creator
|
|
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,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
|
+
}
|