@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.
- package/apps/storefront/astro.config.mjs +3 -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 +4 -4
- 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 +97 -10
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +57 -3
- 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 +168 -60
- 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 +31 -27
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
- 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/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 +45 -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/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 +1 -1
- package/scripts/tot-dev.mjs +2 -2
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-class native measurement adapters (ext-admin-04).
|
|
3
|
+
*
|
|
4
|
+
* These reviewed measurement destinations are expressed as
|
|
5
|
+
* PARAMETER-DRIVEN DECLARATIONS over the ONE shared browser-integration contract —
|
|
6
|
+
* never a per-vendor renderer branch. A merchant enters a public destination id
|
|
7
|
+
* (a GA4/Ads measurement id, a Meta pixel id) per environment and gets a working,
|
|
8
|
+
* consent-filtered, CSP-clean measurement integration. Each adapter is:
|
|
9
|
+
*
|
|
10
|
+
* - a {@link PlatformAdapterDefinition} — a Storefront-owned, reviewed template
|
|
11
|
+
* that fixes the vendor's exact CSP profile ({@link INTEGRATION_CSP_PROFILES}),
|
|
12
|
+
* consent purpose, render phase, performance budget, and semantic-event → vendor
|
|
13
|
+
* mapping. The tenant contributes only public config (ids), never code or origins,
|
|
14
|
+
* which is why a first-class destination needs no renderer change;
|
|
15
|
+
* - built into a tenant-scoped {@link IntegrationDeclarationInput} by
|
|
16
|
+
* {@link buildPlatformAdapterDeclaration}, resolved through the SAME pure
|
|
17
|
+
* {@link resolveEffectivePlan} every catalog entry uses — so its CSP, consent,
|
|
18
|
+
* and lifecycle are the shared ones, and removal is just dropping the declaration
|
|
19
|
+
* (planRevision-scoped: no residual markup, events, or CSP);
|
|
20
|
+
* - run at the client through {@link createAdapterDestination}, which registers ONE
|
|
21
|
+
* stable-id subscription on the platform {@link CommerceEventBroker}. The broker
|
|
22
|
+
* owns dedupe (one event per logical action) and consent filtering, so Google and
|
|
23
|
+
* Meta each measure a logical page/cart/checkout action exactly once — never a
|
|
24
|
+
* duplicate, even with multiple adapters live.
|
|
25
|
+
*
|
|
26
|
+
* NON-LIVE SAFETY — by construction, two independent guards:
|
|
27
|
+
* 1. Destination ids are per-ENVIRONMENT. {@link resolveAdapterDestinationId} returns
|
|
28
|
+
* the id for the CURRENT stage only, and {@link buildPlatformAdapterDeclaration}
|
|
29
|
+
* REFUSES a config where a non-live stage reuses the live id — so candidate/staged
|
|
30
|
+
* traffic can never reach the live property. A stage with no configured id fires
|
|
31
|
+
* nothing.
|
|
32
|
+
* 2. Real conversions fire only on `live`. A conversion-class mapping
|
|
33
|
+
* ({@link VendorEventMapping.isConversion}) is dropped on every non-live stage
|
|
34
|
+
* ({@link isLiveEnvironment}), so a candidate/staged page never fires a real
|
|
35
|
+
* conversion to the vendor, whatever id is configured.
|
|
36
|
+
*
|
|
37
|
+
* Public ids only — a GA4 measurement id and a Meta pixel id are public values that
|
|
38
|
+
* ship in page HTML; neither adapter needs a server-custody secret, so `server`
|
|
39
|
+
* carries none. If a future first-class destination needs a secret (e.g. a Meta
|
|
40
|
+
* Conversions API token), it declares it as a server `secretRequirement` NAME and the
|
|
41
|
+
* value stays in server custody — never in this browser config.
|
|
42
|
+
*/
|
|
43
|
+
import type { ContractResult, ContractViolation } from "./result.js";
|
|
44
|
+
import { Violations, isPlainObject } from "./result.js";
|
|
45
|
+
import type { CommerceEventType, CommerceEvent } from "./events.js";
|
|
46
|
+
import { APPROVED_MARKETING_ACTION_NAMES } from "./events.js";
|
|
47
|
+
import type {
|
|
48
|
+
CommerceEventBroker,
|
|
49
|
+
CommerceEventSubscription,
|
|
50
|
+
} from "./commerce-broker.js";
|
|
51
|
+
import type { ConsentPurpose } from "../integration-capabilities.js";
|
|
52
|
+
import {
|
|
53
|
+
INTEGRATION_ENVIRONMENTS,
|
|
54
|
+
isLiveEnvironment,
|
|
55
|
+
type BrowserEnvironment,
|
|
56
|
+
type IntegrationEnvironment,
|
|
57
|
+
} from "./environment.js";
|
|
58
|
+
import { validateBrowserEnvironment } from "./environment.js";
|
|
59
|
+
import {
|
|
60
|
+
BROWSER_INTEGRATION_CONTRACT_VERSION,
|
|
61
|
+
INTEGRATION_MANIFEST_SCHEMA_VERSION,
|
|
62
|
+
} from "./version.js";
|
|
63
|
+
import {
|
|
64
|
+
validateIntegrationManifest,
|
|
65
|
+
type IntegrationManifest,
|
|
66
|
+
type PerformanceBudget,
|
|
67
|
+
} from "./manifest.js";
|
|
68
|
+
import type { IntegrationCspProfile } from "./csp-compiler.js";
|
|
69
|
+
import { INTEGRATION_CSP_PROFILES } from "./csp-compiler.js";
|
|
70
|
+
import { INTEGRATION_CSP_DIRECTIVES } from "./csp.js";
|
|
71
|
+
import { digestValue } from "./digest.js";
|
|
72
|
+
import type { EffectivePlan } from "./plan.js";
|
|
73
|
+
import { resolveEffectivePlan, type IntegrationDeclarationInput } from "./resolve.js";
|
|
74
|
+
|
|
75
|
+
/** How a single semantic commerce event maps onto a vendor's own event vocabulary. */
|
|
76
|
+
export interface VendorEventMapping {
|
|
77
|
+
/** The vendor's native event name. */
|
|
78
|
+
vendorEvent: string;
|
|
79
|
+
/**
|
|
80
|
+
* Whether this is a CONVERSION-class signal (checkout/purchase intent). Conversions
|
|
81
|
+
* are dropped on every non-live stage so they never fire a real conversion.
|
|
82
|
+
*/
|
|
83
|
+
isConversion: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A Storefront-owned, reviewed first-class adapter template. It is a DECLARATION over
|
|
88
|
+
* the shared contract — it fixes the vendor's exact CSP profile, consent purpose,
|
|
89
|
+
* render phase, budget, and event mapping. The tenant supplies only public ids.
|
|
90
|
+
*/
|
|
91
|
+
export interface PlatformAdapterDefinition {
|
|
92
|
+
/** Stable adapter id — also the resolved integration `appId`/`ref`. */
|
|
93
|
+
adapterId: string;
|
|
94
|
+
/** Merchant-facing destination name. */
|
|
95
|
+
name: string;
|
|
96
|
+
/** Plain-language purpose for the inventory row. */
|
|
97
|
+
purpose: string;
|
|
98
|
+
publisher: string;
|
|
99
|
+
supportUrl: string;
|
|
100
|
+
/** The ONE platform-owned entry module every instance of this adapter points at. */
|
|
101
|
+
entryAsset: string;
|
|
102
|
+
/** The curated CSP profile slug this adapter's origins come from. */
|
|
103
|
+
cspProfile: IntegrationCspProfile;
|
|
104
|
+
/** The consent purpose the destination depends on (the broker enforces it). */
|
|
105
|
+
consentPurpose: Exclude<ConsentPurpose, "essential">;
|
|
106
|
+
/** The vendor-addressable render phase the adapter runs in. */
|
|
107
|
+
phase: "document.deferred";
|
|
108
|
+
/** The performance budget the adapter must fit — surfaced in the read model. */
|
|
109
|
+
performanceBudget: PerformanceBudget;
|
|
110
|
+
/** Human hint for the public destination-id field (e.g. "GA4 measurement id"). */
|
|
111
|
+
idLabel: string;
|
|
112
|
+
/** Validates the shape of a public destination id for this vendor. */
|
|
113
|
+
validateId(id: string): boolean;
|
|
114
|
+
/** The semantic commerce events this adapter measures, and their vendor mapping. */
|
|
115
|
+
eventMap: Readonly<Partial<Record<CommerceEventType, VendorEventMapping>>>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const GOOGLE_MEASUREMENT_ID = /^G-[A-Z0-9]{4,20}$/;
|
|
119
|
+
const META_PIXEL_ID = /^\d{10,20}$/;
|
|
120
|
+
const AMPLITUDE_PROJECT_KEY = /^(?=[a-f0-9]{32}$)(?=.*[a-f])[a-f0-9]+$/i;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Native Google Analytics 4 measurement. A single public measurement id drives
|
|
124
|
+
* the platform adapter, which sends reviewed event payloads to the curated analytics
|
|
125
|
+
* endpoints in the `google-measurement` CSP profile. It does not run a merchant-owned
|
|
126
|
+
* browser container. Depends on analytics consent (ADR 0023 Q3).
|
|
127
|
+
*/
|
|
128
|
+
export const GOOGLE_MEASUREMENT_ADAPTER: PlatformAdapterDefinition = Object.freeze({
|
|
129
|
+
adapterId: "platform.google-measurement",
|
|
130
|
+
name: "Google Analytics 4",
|
|
131
|
+
purpose: "Native Google Analytics 4 measurement.",
|
|
132
|
+
publisher: "Storefront platform adapter",
|
|
133
|
+
supportUrl: "https://developers.google.com/analytics/devguides/collection/ga4",
|
|
134
|
+
entryAsset: "/platform/google-measurement/v1/entry.mjs",
|
|
135
|
+
cspProfile: "google-measurement",
|
|
136
|
+
consentPurpose: "analytics",
|
|
137
|
+
phase: "document.deferred",
|
|
138
|
+
performanceBudget: Object.freeze({
|
|
139
|
+
maxTransferredBytes: 90_000,
|
|
140
|
+
maxAdditionalRequests: 4,
|
|
141
|
+
maxMainThreadMilliseconds: 120,
|
|
142
|
+
mustBeAsyncOrDeferred: true,
|
|
143
|
+
}),
|
|
144
|
+
idLabel: "GA4 measurement id (e.g. G-XXXXXXX)",
|
|
145
|
+
validateId: (id: string) => GOOGLE_MEASUREMENT_ID.test(id),
|
|
146
|
+
eventMap: Object.freeze({
|
|
147
|
+
"storefront.page.viewed.v1": { vendorEvent: "page_view", isConversion: false },
|
|
148
|
+
"storefront.page.viewed.v2": { vendorEvent: "page_view", isConversion: false },
|
|
149
|
+
"storefront.action.activated.v1": { vendorEvent: "approved_action", isConversion: false },
|
|
150
|
+
}),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Meta pixel. A single public pixel id drives fbq; the loader is served from
|
|
155
|
+
* connect.facebook.net and collects to facebook.com (the `meta-pixel` CSP profile).
|
|
156
|
+
* Depends on marketing consent (advertising attribution).
|
|
157
|
+
*/
|
|
158
|
+
export const META_PIXEL_ADAPTER: PlatformAdapterDefinition = Object.freeze({
|
|
159
|
+
adapterId: "platform.meta-pixel",
|
|
160
|
+
name: "Meta pixel",
|
|
161
|
+
purpose: "Meta (Facebook/Instagram) pixel measurement via fbq.",
|
|
162
|
+
publisher: "Storefront platform adapter",
|
|
163
|
+
supportUrl: "https://developers.facebook.com/docs/meta-pixel",
|
|
164
|
+
entryAsset: "platform/meta-pixel/v1/entry.mjs",
|
|
165
|
+
cspProfile: "meta-pixel",
|
|
166
|
+
consentPurpose: "marketing",
|
|
167
|
+
phase: "document.deferred",
|
|
168
|
+
performanceBudget: Object.freeze({
|
|
169
|
+
maxTransferredBytes: 70_000,
|
|
170
|
+
maxAdditionalRequests: 3,
|
|
171
|
+
maxMainThreadMilliseconds: 100,
|
|
172
|
+
mustBeAsyncOrDeferred: true,
|
|
173
|
+
}),
|
|
174
|
+
idLabel: "Meta pixel id (15–16 digits)",
|
|
175
|
+
validateId: (id: string) => META_PIXEL_ID.test(id),
|
|
176
|
+
eventMap: Object.freeze({
|
|
177
|
+
"storefront.page.viewed.v1": { vendorEvent: "PageView", isConversion: false },
|
|
178
|
+
"storefront.product.viewed.v1": { vendorEvent: "ViewContent", isConversion: false },
|
|
179
|
+
"storefront.cart.item_added.v1": { vendorEvent: "AddToCart", isConversion: false },
|
|
180
|
+
"storefront.checkout.handoff.v1": { vendorEvent: "InitiateCheckout", isConversion: true },
|
|
181
|
+
}),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Native Amplitude measurement through the reviewed direct HTTP transport. Only
|
|
186
|
+
* explicit page views and allowlisted platform action activations are subscribed;
|
|
187
|
+
* the adapter does not load or configure the Amplitude browser SDK.
|
|
188
|
+
*/
|
|
189
|
+
export const AMPLITUDE_HTTP_ADAPTER: PlatformAdapterDefinition = Object.freeze({
|
|
190
|
+
adapterId: "platform.amplitude",
|
|
191
|
+
name: "Amplitude",
|
|
192
|
+
purpose: "Native Amplitude measurement for explicit page views and approved marketing actions.",
|
|
193
|
+
publisher: "Storefront platform adapter",
|
|
194
|
+
supportUrl: "https://amplitude.com/docs/apis/analytics/http-v2",
|
|
195
|
+
entryAsset: "/platform/amplitude-http/v1/entry.mjs",
|
|
196
|
+
cspProfile: "amplitude-http",
|
|
197
|
+
consentPurpose: "analytics",
|
|
198
|
+
phase: "document.deferred",
|
|
199
|
+
performanceBudget: Object.freeze({
|
|
200
|
+
maxTransferredBytes: 8_000,
|
|
201
|
+
maxAdditionalRequests: 1,
|
|
202
|
+
maxMainThreadMilliseconds: 10,
|
|
203
|
+
mustBeAsyncOrDeferred: true,
|
|
204
|
+
}),
|
|
205
|
+
idLabel: "Amplitude project API key (32 hexadecimal characters)",
|
|
206
|
+
validateId: (id: string) => AMPLITUDE_PROJECT_KEY.test(id),
|
|
207
|
+
eventMap: Object.freeze({
|
|
208
|
+
"storefront.page.viewed.v1": { vendorEvent: "page_view", isConversion: false },
|
|
209
|
+
"storefront.page.viewed.v2": { vendorEvent: "page_view", isConversion: false },
|
|
210
|
+
"storefront.action.activated.v1": { vendorEvent: "marketing_action", isConversion: false },
|
|
211
|
+
}),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
/** The closed set of first-class platform adapters, in catalog order. */
|
|
215
|
+
export const PLATFORM_ADAPTERS: readonly PlatformAdapterDefinition[] = Object.freeze([
|
|
216
|
+
GOOGLE_MEASUREMENT_ADAPTER,
|
|
217
|
+
META_PIXEL_ADAPTER,
|
|
218
|
+
AMPLITUDE_HTTP_ADAPTER,
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
const ADAPTERS_BY_ID: ReadonlyMap<string, PlatformAdapterDefinition> = new Map(
|
|
222
|
+
PLATFORM_ADAPTERS.map((a) => [a.adapterId, a]),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
/** Look up a first-class adapter by its stable id, or undefined for an unknown id. */
|
|
226
|
+
export function getPlatformAdapter(adapterId: string): PlatformAdapterDefinition | undefined {
|
|
227
|
+
return ADAPTERS_BY_ID.get(adapterId);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** The event types an adapter measures — the subscription scope + the permissions.events set. */
|
|
231
|
+
export function adapterEventTypes(def: PlatformAdapterDefinition): CommerceEventType[] {
|
|
232
|
+
return Object.keys(def.eventMap) as CommerceEventType[];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Per-environment public destination ids ────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
/** The public-config key prefix a per-environment destination id is stored under. */
|
|
238
|
+
export const DESTINATION_ID_CONFIG_PREFIX = "destinationId";
|
|
239
|
+
|
|
240
|
+
/** The flat, primitive public-config key one environment's destination id lives at. */
|
|
241
|
+
export function destinationIdConfigKey(environment: IntegrationEnvironment): string {
|
|
242
|
+
return `${DESTINATION_ID_CONFIG_PREFIX}_${environment}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** A merchant's per-environment public destination ids. `live` is required to publish. */
|
|
246
|
+
export type DestinationIdsByEnvironment = Partial<Record<IntegrationEnvironment, string>>;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Resolve the destination id that fires for a specific stage from resolved public
|
|
250
|
+
* config — the current stage's id ONLY. A stage with no configured id returns null,
|
|
251
|
+
* so nothing fires there; because {@link buildPlatformAdapterDeclaration} refuses a
|
|
252
|
+
* non-live stage that reuses the live id, this can never return the live id off a
|
|
253
|
+
* non-live stage. This is the first non-live guard.
|
|
254
|
+
*/
|
|
255
|
+
export function resolveAdapterDestinationId(
|
|
256
|
+
publicConfig: Record<string, unknown>,
|
|
257
|
+
environment: IntegrationEnvironment,
|
|
258
|
+
): string | null {
|
|
259
|
+
const value = publicConfig[destinationIdConfigKey(environment)];
|
|
260
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ── Declaration builder ───────────────────────────────────────────────────────
|
|
264
|
+
|
|
265
|
+
/** What the admin surface supplies to configure one first-class adapter. */
|
|
266
|
+
export interface PlatformAdapterConfigInput {
|
|
267
|
+
adapterId: string;
|
|
268
|
+
/** Public destination ids per environment. `live` is required; non-live stages take SEPARATE test ids. */
|
|
269
|
+
ids: DestinationIdsByEnvironment;
|
|
270
|
+
/** The tenant this adapter is configured for (server-resolved, never browser-set). */
|
|
271
|
+
tenantPublicId: string;
|
|
272
|
+
/** The stage this build is resolved against (server-derived; a build is a candidate). */
|
|
273
|
+
environment: BrowserEnvironment;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** The consent + budget + destination summary an admin card surfaces in the read model. */
|
|
277
|
+
export interface PlatformAdapterCard {
|
|
278
|
+
adapterId: string;
|
|
279
|
+
name: string;
|
|
280
|
+
purpose: string;
|
|
281
|
+
publisher: string;
|
|
282
|
+
supportUrl: string;
|
|
283
|
+
idLabel: string;
|
|
284
|
+
/** Consent purposes the destination depends on — visible before publish. */
|
|
285
|
+
consentPurposes: ConsentPurpose[];
|
|
286
|
+
/** The performance budget the adapter must fit — visible before publish. */
|
|
287
|
+
performanceBudget: PerformanceBudget;
|
|
288
|
+
/** The exact CSP origins this adapter contributes (flattened, sorted). */
|
|
289
|
+
cspOrigins: string[];
|
|
290
|
+
/** The semantic events this adapter measures. */
|
|
291
|
+
eventTypes: CommerceEventType[];
|
|
292
|
+
/** Which stages have a configured destination id, and whether each is conversion-live. */
|
|
293
|
+
destinationBehavior: AdapterStageBehavior[];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** One stage's measurement disposition — makes the non-live safety visible in the read model. */
|
|
297
|
+
export interface AdapterStageBehavior {
|
|
298
|
+
environment: IntegrationEnvironment;
|
|
299
|
+
/** True when a destination id is configured for this stage. */
|
|
300
|
+
configured: boolean;
|
|
301
|
+
/** True only on `live`: the stage where real conversions fire. */
|
|
302
|
+
firesRealConversions: boolean;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export type PlatformAdapterBuildResult =
|
|
306
|
+
| {
|
|
307
|
+
ok: true;
|
|
308
|
+
adapterId: string;
|
|
309
|
+
declaration: IntegrationDeclarationInput;
|
|
310
|
+
/** Proof the adapter self-serves over the shared resolver with no renderer change. */
|
|
311
|
+
plan: EffectivePlan;
|
|
312
|
+
/** The read-model surface: consent purposes + budget + per-stage destination behavior. */
|
|
313
|
+
card: PlatformAdapterCard;
|
|
314
|
+
}
|
|
315
|
+
| { ok: false; reason: "unknown-adapter" | "validation-blocked"; violations: ContractViolation[] };
|
|
316
|
+
|
|
317
|
+
/** Flatten a curated CSP profile's origins across every directive (sorted, deduped). */
|
|
318
|
+
function profileOrigins(profile: IntegrationCspProfile): string[] {
|
|
319
|
+
const set = new Set<string>();
|
|
320
|
+
const sources = INTEGRATION_CSP_PROFILES[profile] ?? {};
|
|
321
|
+
for (const directive of INTEGRATION_CSP_DIRECTIVES) {
|
|
322
|
+
for (const origin of sources[directive] ?? []) set.add(origin);
|
|
323
|
+
}
|
|
324
|
+
return [...set].sort();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** The per-stage destination behavior, in canonical environment order. */
|
|
328
|
+
function stageBehavior(ids: DestinationIdsByEnvironment): AdapterStageBehavior[] {
|
|
329
|
+
return INTEGRATION_ENVIRONMENTS.map((environment) => ({
|
|
330
|
+
environment,
|
|
331
|
+
configured: typeof ids[environment] === "string" && (ids[environment] as string).length > 0,
|
|
332
|
+
firesRealConversions: isLiveEnvironment(environment) && typeof ids[environment] === "string",
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Build the read-model card for an adapter definition + a merchant's configured ids. */
|
|
337
|
+
export function buildPlatformAdapterCard(
|
|
338
|
+
def: PlatformAdapterDefinition,
|
|
339
|
+
ids: DestinationIdsByEnvironment = {},
|
|
340
|
+
): PlatformAdapterCard {
|
|
341
|
+
return {
|
|
342
|
+
adapterId: def.adapterId,
|
|
343
|
+
name: def.name,
|
|
344
|
+
purpose: def.purpose,
|
|
345
|
+
publisher: def.publisher,
|
|
346
|
+
supportUrl: def.supportUrl,
|
|
347
|
+
idLabel: def.idLabel,
|
|
348
|
+
consentPurposes: [def.consentPurpose],
|
|
349
|
+
performanceBudget: def.performanceBudget,
|
|
350
|
+
cspOrigins: profileOrigins(def.cspProfile),
|
|
351
|
+
eventTypes: adapterEventTypes(def),
|
|
352
|
+
destinationBehavior: stageBehavior(ids),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** The catalog of first-class adapters as read-model cards (no tenant ids configured). */
|
|
357
|
+
export function platformAdapterCatalog(): PlatformAdapterCard[] {
|
|
358
|
+
return PLATFORM_ADAPTERS.map((def) => buildPlatformAdapterCard(def));
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Build a tenant-scoped first-class adapter declaration from a merchant's public ids,
|
|
363
|
+
* fail-closed. An unknown adapter, a missing/invalid live id, an invalid stage id, a
|
|
364
|
+
* non-live stage that REUSES the live id, or an unresolvable environment yields
|
|
365
|
+
* violations and NO declaration. The synthesized manifest is resolved into an
|
|
366
|
+
* effective plan — proof it self-serves over the shared resolver.
|
|
367
|
+
*/
|
|
368
|
+
export function buildPlatformAdapterDeclaration(
|
|
369
|
+
input: unknown,
|
|
370
|
+
): PlatformAdapterBuildResult {
|
|
371
|
+
if (!isPlainObject(input)) {
|
|
372
|
+
return blocked("validation-blocked", [
|
|
373
|
+
{ path: "(root)", code: "type", message: "Adapter config must be an object." },
|
|
374
|
+
]);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const def = typeof input["adapterId"] === "string" ? getPlatformAdapter(input["adapterId"]) : undefined;
|
|
378
|
+
if (!def) {
|
|
379
|
+
return blocked("unknown-adapter", [
|
|
380
|
+
{
|
|
381
|
+
path: "adapterId",
|
|
382
|
+
code: "unknown_adapter",
|
|
383
|
+
message: `Unknown adapter; first-class adapters are: ${PLATFORM_ADAPTERS.map((a) => a.adapterId).join(", ")}.`,
|
|
384
|
+
},
|
|
385
|
+
]);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const v = new Violations();
|
|
389
|
+
|
|
390
|
+
const tenantPublicId =
|
|
391
|
+
typeof input["tenantPublicId"] === "string" && input["tenantPublicId"].length > 0
|
|
392
|
+
? input["tenantPublicId"]
|
|
393
|
+
: undefined;
|
|
394
|
+
if (tenantPublicId === undefined) {
|
|
395
|
+
v.add("tenantPublicId", "type", "tenantPublicId must be a non-empty string.");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const envResult = validateBrowserEnvironment(input["environment"]);
|
|
399
|
+
if (!envResult.ok) {
|
|
400
|
+
for (const violation of envResult.violations) {
|
|
401
|
+
v.add(violation.path ? `environment.${violation.path}` : "environment", violation.code, violation.message);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const ids = validateDestinationIds(input["ids"], def, v);
|
|
406
|
+
|
|
407
|
+
if (!v.ok || tenantPublicId === undefined || ids === undefined || !envResult.ok) {
|
|
408
|
+
return blocked("validation-blocked", v.items);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Public config: one flat, primitive, PUBLIC id per configured stage. These are
|
|
412
|
+
// public measurement ids (they ship in page HTML), never secret material.
|
|
413
|
+
const publicConfig: Record<string, unknown> = {};
|
|
414
|
+
const schemaProps: Record<string, { type: string }> = {};
|
|
415
|
+
const requiredKeys: string[] = [];
|
|
416
|
+
for (const environment of INTEGRATION_ENVIRONMENTS) {
|
|
417
|
+
const id = ids[environment];
|
|
418
|
+
if (id === undefined) continue;
|
|
419
|
+
const key = destinationIdConfigKey(environment);
|
|
420
|
+
publicConfig[key] = id;
|
|
421
|
+
schemaProps[key] = { type: "string" };
|
|
422
|
+
requiredKeys.push(key);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const manifest: IntegrationManifest = {
|
|
426
|
+
schemaVersion: INTEGRATION_MANIFEST_SCHEMA_VERSION,
|
|
427
|
+
app: {
|
|
428
|
+
id: def.adapterId,
|
|
429
|
+
name: def.name,
|
|
430
|
+
version: "1.0.0",
|
|
431
|
+
digest: digestValue({ adapterId: def.adapterId, tenantPublicId, publicConfig }),
|
|
432
|
+
publisher: def.publisher,
|
|
433
|
+
supportUrl: def.supportUrl,
|
|
434
|
+
},
|
|
435
|
+
compatibility: {
|
|
436
|
+
storefrontBrowserContract: `>=${BROWSER_INTEGRATION_CONTRACT_VERSION}`,
|
|
437
|
+
configurationSchemaVersion: 1,
|
|
438
|
+
},
|
|
439
|
+
browser: {
|
|
440
|
+
executionTier: "platform-adapter",
|
|
441
|
+
entry: { asset: def.entryAsset, format: "module", phase: def.phase },
|
|
442
|
+
contributions: [],
|
|
443
|
+
dependencies: [],
|
|
444
|
+
publicConfigSchema: {
|
|
445
|
+
type: "object",
|
|
446
|
+
additionalProperties: false,
|
|
447
|
+
required: requiredKeys.sort(),
|
|
448
|
+
properties: schemaProps,
|
|
449
|
+
},
|
|
450
|
+
consent: {
|
|
451
|
+
purposes: [def.consentPurpose],
|
|
452
|
+
loadWhen: "all-declared-purposes-eligible",
|
|
453
|
+
unknownState: "block",
|
|
454
|
+
},
|
|
455
|
+
csp: { profiles: [def.cspProfile], sources: {} },
|
|
456
|
+
permissions: { events: adapterEventTypes(def), data: [], actions: [], domIntent: "none" },
|
|
457
|
+
performanceBudget: def.performanceBudget,
|
|
458
|
+
},
|
|
459
|
+
// Public ids only — no server-custody secret. A future Conversions-API token would
|
|
460
|
+
// be declared here as a NAME, never carried in browser config.
|
|
461
|
+
server: { topics: [], secretRequirements: [] },
|
|
462
|
+
assurance: { requestedClassification: "baseline", regulatedTenantDefault: "disabled-until-certified" },
|
|
463
|
+
configurationMigrations: [],
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const manifestResult = validateIntegrationManifest(manifest);
|
|
467
|
+
if (!manifestResult.ok) return blocked("validation-blocked", manifestResult.violations);
|
|
468
|
+
|
|
469
|
+
const declaration: IntegrationDeclarationInput = {
|
|
470
|
+
origin: "platform-adapter",
|
|
471
|
+
ref: def.adapterId,
|
|
472
|
+
manifest,
|
|
473
|
+
publicConfig,
|
|
474
|
+
materializedAssets: [def.entryAsset],
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
const planResult = resolveEffectivePlan({
|
|
478
|
+
tenantPublicId,
|
|
479
|
+
environment: envResult.value,
|
|
480
|
+
declarations: [declaration],
|
|
481
|
+
});
|
|
482
|
+
if (!planResult.ok) return blocked("validation-blocked", planResult.violations);
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
ok: true,
|
|
486
|
+
adapterId: def.adapterId,
|
|
487
|
+
declaration,
|
|
488
|
+
plan: planResult.value,
|
|
489
|
+
card: buildPlatformAdapterCard(def, ids),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Validate the per-environment public ids. `live` is required; every provided id must
|
|
495
|
+
* match the adapter's shape; and NO non-live stage may reuse the live id — that is the
|
|
496
|
+
* config-time guard that keeps candidate/staged traffic off the live property.
|
|
497
|
+
*/
|
|
498
|
+
function validateDestinationIds(
|
|
499
|
+
value: unknown,
|
|
500
|
+
def: PlatformAdapterDefinition,
|
|
501
|
+
v: Violations,
|
|
502
|
+
): DestinationIdsByEnvironment | undefined {
|
|
503
|
+
if (!isPlainObject(value)) {
|
|
504
|
+
v.add("ids", "type", "ids must be an object of { environment: destinationId }.");
|
|
505
|
+
return undefined;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const out: DestinationIdsByEnvironment = {};
|
|
509
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
510
|
+
if (!(INTEGRATION_ENVIRONMENTS as readonly string[]).includes(key)) {
|
|
511
|
+
v.add(`ids.${key}`, "unknown_enum", `Unknown environment "${key}"; one of: ${INTEGRATION_ENVIRONMENTS.join(", ")}.`);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
515
|
+
v.add(`ids.${key}`, "type", "A destination id must be a non-empty string.");
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (!def.validateId(raw)) {
|
|
519
|
+
v.add(`ids.${key}`, "format", `"${raw}" is not a valid ${def.idLabel}.`);
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
out[key as IntegrationEnvironment] = raw;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const liveId = out["live"];
|
|
526
|
+
if (liveId === undefined) {
|
|
527
|
+
v.add("ids.live", "required", "A live destination id is required to publish this adapter.");
|
|
528
|
+
} else {
|
|
529
|
+
for (const environment of INTEGRATION_ENVIRONMENTS) {
|
|
530
|
+
if (environment === "live") continue;
|
|
531
|
+
if (out[environment] === liveId) {
|
|
532
|
+
v.add(
|
|
533
|
+
`ids.${environment}`,
|
|
534
|
+
"non_live_reuses_live_id",
|
|
535
|
+
`The "${environment}" stage reuses the live destination id; non-live traffic must never reach the live property. Use a separate test id or leave it unset.`,
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return v.ok ? out : undefined;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function blocked(
|
|
545
|
+
reason: "unknown-adapter" | "validation-blocked",
|
|
546
|
+
violations: ContractViolation[],
|
|
547
|
+
): PlatformAdapterBuildResult {
|
|
548
|
+
return { ok: false, reason, violations };
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// ── Client runtime: the broker subscription each adapter registers ─────────────
|
|
552
|
+
|
|
553
|
+
/** One mapped vendor call the adapter's entry module makes. */
|
|
554
|
+
export interface VendorCall {
|
|
555
|
+
adapterId: string;
|
|
556
|
+
/** The public destination id for the CURRENT stage this call targets. */
|
|
557
|
+
destinationId: string;
|
|
558
|
+
/** The vendor's own event name. */
|
|
559
|
+
vendorEvent: string;
|
|
560
|
+
/** The semantic event this maps from. */
|
|
561
|
+
eventType: CommerceEventType;
|
|
562
|
+
/** The logical event id — the same id the broker deduped on. */
|
|
563
|
+
logicalId: string;
|
|
564
|
+
/** The anonymous, allowlisted payload (already validated by the broker). */
|
|
565
|
+
data: Record<string, unknown>;
|
|
566
|
+
/** Whether this was a conversion-class signal (only ever true on `live`). */
|
|
567
|
+
isConversion: boolean;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/** The vendor sink the entry module injects, or a test spy. */
|
|
571
|
+
export type AdapterDispatch = (call: VendorCall) => void;
|
|
572
|
+
|
|
573
|
+
export interface CreateAdapterDestinationInput {
|
|
574
|
+
adapter: PlatformAdapterDefinition;
|
|
575
|
+
/** The resolved per-integration public config (holds the per-stage destination ids). */
|
|
576
|
+
publicConfig: Record<string, unknown>;
|
|
577
|
+
/** The resolved environment — its stage decides which id fires and whether conversions fire. */
|
|
578
|
+
environment: BrowserEnvironment;
|
|
579
|
+
/** The vendor sink. Injected so the adapter runtime is pure and node-testable. */
|
|
580
|
+
dispatch: AdapterDispatch;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Build the ONE broker subscription an adapter's entry module registers. Its `id` is
|
|
585
|
+
* the stable adapter id, so a re-subscribe (after a consent/config change) REPLACES
|
|
586
|
+
* rather than stacks — a logical action still measures exactly once. The broker fans
|
|
587
|
+
* each logical action to this subscriber exactly once and only when the adapter's
|
|
588
|
+
* consent purpose is eligible, so consent filtering and one-per-action dedupe are the
|
|
589
|
+
* broker's, shared with every other destination.
|
|
590
|
+
*
|
|
591
|
+
* The handler is the non-live enforcement point: it fires only to the CURRENT stage's
|
|
592
|
+
* destination id (never the live id off a non-live stage), and drops conversion-class
|
|
593
|
+
* events on every non-live stage — so a candidate/staged page never fires a real
|
|
594
|
+
* conversion.
|
|
595
|
+
*/
|
|
596
|
+
export function createAdapterDestination(
|
|
597
|
+
input: CreateAdapterDestinationInput,
|
|
598
|
+
): CommerceEventSubscription {
|
|
599
|
+
const { adapter, publicConfig, environment, dispatch } = input;
|
|
600
|
+
return {
|
|
601
|
+
id: adapter.adapterId,
|
|
602
|
+
consentPurpose: adapter.consentPurpose,
|
|
603
|
+
types: adapterEventTypes(adapter),
|
|
604
|
+
sink: {
|
|
605
|
+
kind: "callback",
|
|
606
|
+
handler: (event: CommerceEvent) => {
|
|
607
|
+
const call = mapCommerceEventToVendorCall(adapter, publicConfig, environment, event);
|
|
608
|
+
if (call) dispatch(call);
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Map one delivered commerce event to a vendor call, applying both non-live guards.
|
|
616
|
+
* Returns null (no vendor call) when: the current stage has no destination id; the
|
|
617
|
+
* event type is not in the adapter's map; or the mapping is a conversion on a non-live
|
|
618
|
+
* stage. Pure — exported so the mapping + non-live gating are directly testable.
|
|
619
|
+
*/
|
|
620
|
+
export function mapCommerceEventToVendorCall(
|
|
621
|
+
adapter: PlatformAdapterDefinition,
|
|
622
|
+
publicConfig: Record<string, unknown>,
|
|
623
|
+
environment: BrowserEnvironment,
|
|
624
|
+
event: CommerceEvent,
|
|
625
|
+
): VendorCall | null {
|
|
626
|
+
const destinationId = resolveAdapterDestinationId(publicConfig, environment.environment);
|
|
627
|
+
if (!destinationId) return null;
|
|
628
|
+
|
|
629
|
+
const mapping = adapter.eventMap[event.type];
|
|
630
|
+
if (!mapping) return null;
|
|
631
|
+
|
|
632
|
+
let vendorEvent = mapping.vendorEvent;
|
|
633
|
+
if (
|
|
634
|
+
adapter.adapterId === GOOGLE_MEASUREMENT_ADAPTER.adapterId &&
|
|
635
|
+
event.type === "storefront.action.activated.v1"
|
|
636
|
+
) {
|
|
637
|
+
const name = event.data["name"];
|
|
638
|
+
if (
|
|
639
|
+
typeof name !== "string" ||
|
|
640
|
+
!(APPROVED_MARKETING_ACTION_NAMES as readonly string[]).includes(name)
|
|
641
|
+
) return null;
|
|
642
|
+
vendorEvent = name;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Guard 2: real conversions fire only on live. A non-live stage never fires a real
|
|
646
|
+
// conversion, even to a configured test id.
|
|
647
|
+
if (mapping.isConversion && !isLiveEnvironment(environment.environment)) return null;
|
|
648
|
+
|
|
649
|
+
return {
|
|
650
|
+
adapterId: adapter.adapterId,
|
|
651
|
+
destinationId,
|
|
652
|
+
vendorEvent,
|
|
653
|
+
eventType: event.type,
|
|
654
|
+
logicalId: event.id,
|
|
655
|
+
data: event.data,
|
|
656
|
+
isConversion: mapping.isConversion,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Register a first-class adapter's destination on the platform commerce broker and
|
|
662
|
+
* return the unsubscribe handle. Removing the adapter (unsubscribe) withdraws its
|
|
663
|
+
* measurement with no residue — the mirror of dropping the declaration from the plan,
|
|
664
|
+
* which withdraws its CSP + entry module.
|
|
665
|
+
*/
|
|
666
|
+
export function registerAdapterDestination(
|
|
667
|
+
broker: CommerceEventBroker,
|
|
668
|
+
input: CreateAdapterDestinationInput,
|
|
669
|
+
): () => void {
|
|
670
|
+
return broker.subscribe(createAdapterDestination(input));
|
|
671
|
+
}
|