@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,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform-owned browser commerce event broker (ADR 0014 M2
|
|
3
|
+
* `integration-commerce-data-minimized`, M3 `integration-events-dual-delivery`;
|
|
4
|
+
* ext-commerce-02). It is the single client-side seam between the storefront's own
|
|
5
|
+
* page/product/cart/handoff signals and the scoped subscribers — apps, sandboxed
|
|
6
|
+
* widget iframes, elevated-parent code — that want to measure them.
|
|
7
|
+
*
|
|
8
|
+
* It owns three properties that its callers do NOT get to override:
|
|
9
|
+
*
|
|
10
|
+
* - SERVER-OWNED CONTEXT IS STAMPED, NEVER SUPPLIED. A caller hands the broker only
|
|
11
|
+
* an anonymous, allowlisted `data` payload plus the logical action's identity
|
|
12
|
+
* ({@link CommerceEmitInput}); the broker stamps `storefront.tenant`,
|
|
13
|
+
* `environment`, `siteSurface`, `emitter: "browser"`, and the current consent
|
|
14
|
+
* snapshot from the trusted execution context it was constructed with. Every
|
|
15
|
+
* emitted envelope is then run through {@link validateCommerceEvent}, so the
|
|
16
|
+
* closed field catalog rejects any PII, payment, order, or unknown field — a
|
|
17
|
+
* privileged parent with DOM authority gains NO extra server-held data by that
|
|
18
|
+
* authority, and a browser emitter can never author a server-authoritative
|
|
19
|
+
* conversion (`forged_authority`).
|
|
20
|
+
*
|
|
21
|
+
* - ONE EVENT PER LOGICAL ACTION. The broker deduplicates on the envelope's
|
|
22
|
+
* {@link eventDedupeKey} (`id`): a second emit of the same logical id — a
|
|
23
|
+
* double-fired DOM handler, a replay — collapses to a no-op, so a subscriber
|
|
24
|
+
* registered once observes each logical action exactly once.
|
|
25
|
+
*
|
|
26
|
+
* - SUBSCRIBER FAULTS ARE ISOLATED. Subscriptions are keyed by a stable id, so a
|
|
27
|
+
* re-subscribe REPLACES rather than stacks (no duplicate listeners after a
|
|
28
|
+
* consent/config update). Each subscriber is delivered inside its own try/catch
|
|
29
|
+
* (the kernel-05 lifecycle isolation model), so a throwing subscriber cannot
|
|
30
|
+
* break the cart, navigation, or any other subscriber, and the emit call returns
|
|
31
|
+
* normally to its caller.
|
|
32
|
+
*
|
|
33
|
+
* Delivery is consent-filtered per subscriber: a subscriber declares the
|
|
34
|
+
* {@link ConsentPurpose} its destination depends on, and the broker withholds the
|
|
35
|
+
* event unless that purpose is eligible under the current snapshot
|
|
36
|
+
* ({@link isPurposeEligible}) — the same predicate the server destination filter and
|
|
37
|
+
* the browser destination loader share. A sandboxed iframe consumer receives the
|
|
38
|
+
* validated {@link CommerceEvent} as a TYPED, channel-tagged postMessage, never a raw
|
|
39
|
+
* ad-hoc message.
|
|
40
|
+
*
|
|
41
|
+
* DOM-free and dependency-injected (trusted context, a consent snapshot source, a
|
|
42
|
+
* clock), so it is edge-safe and fully testable in node; an iframe sink is the only
|
|
43
|
+
* browser-touching surface and is supplied by the caller.
|
|
44
|
+
*/
|
|
45
|
+
import type {
|
|
46
|
+
CommerceEvent,
|
|
47
|
+
CommerceEventType,
|
|
48
|
+
EventCorrelation,
|
|
49
|
+
TrustedEventContext,
|
|
50
|
+
} from "./events.js";
|
|
51
|
+
import {
|
|
52
|
+
EVENT_SOURCE_OF_TRUTH,
|
|
53
|
+
PURCHASE_COMPLETED_EVENT_TYPE,
|
|
54
|
+
eventDedupeKey,
|
|
55
|
+
validateCommerceEvent,
|
|
56
|
+
} from "./events.js";
|
|
57
|
+
import type { ConsentSnapshot } from "./consent-broker.js";
|
|
58
|
+
import { isPurposeEligible, toEventConsentSnapshot } from "./consent-broker.js";
|
|
59
|
+
import type { ConsentPurpose } from "../integration-capabilities.js";
|
|
60
|
+
import { COMMERCE_EVENT_STOREFRONT_SCHEMA_VERSION } from "./version.js";
|
|
61
|
+
import type { ContractViolation } from "./result.js";
|
|
62
|
+
import { safeManagedAnalyticsDiagnosticId } from "./managed-analytics-value-safety.js";
|
|
63
|
+
import {
|
|
64
|
+
validateTenantActionRegistry,
|
|
65
|
+
type TenantActionRegistry,
|
|
66
|
+
} from "./managed-analytics.js";
|
|
67
|
+
|
|
68
|
+
/** The channel tag every broker→iframe message carries; a consumer keys its parse on it. */
|
|
69
|
+
export const COMMERCE_EVENT_MESSAGE_CHANNEL = "tot.commerce-event.v1" as const;
|
|
70
|
+
|
|
71
|
+
/** The typed envelope the broker posts to an iframe consumer — never a bare event. */
|
|
72
|
+
export interface CommerceEventMessage {
|
|
73
|
+
channel: typeof COMMERCE_EVENT_MESSAGE_CHANNEL;
|
|
74
|
+
event: CommerceEvent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* What a caller supplies to emit one logical browser commerce action. It carries the
|
|
79
|
+
* anonymous, allowlisted `data` and the action's identity ONLY — never the
|
|
80
|
+
* server-owned `storefront.*` context, which the broker stamps from its trusted
|
|
81
|
+
* construction context so it cannot be caller-forged.
|
|
82
|
+
*/
|
|
83
|
+
export interface CommerceEmitInput {
|
|
84
|
+
/** A browser source-of-truth event type; a server-authoritative type is refused. */
|
|
85
|
+
type: CommerceEventType;
|
|
86
|
+
/** The opaque logical event id — the dedupe key. The same logical action reuses it. */
|
|
87
|
+
logicalId: string;
|
|
88
|
+
/** Opaque in-event subject (e.g. an anonymous cart/item-set handle). */
|
|
89
|
+
subject: string;
|
|
90
|
+
/** The anonymous, field-allowlisted payload for this type. Validated on emit. */
|
|
91
|
+
data: Record<string, unknown>;
|
|
92
|
+
/** The bounded-lifetime attribution thread this action belongs to. */
|
|
93
|
+
correlation: EventCorrelation;
|
|
94
|
+
/** Optional per-emitter monotonic counter for out-of-order detection. */
|
|
95
|
+
sequence?: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The minimal postMessage surface a real sandboxed iframe `contentWindow` satisfies. */
|
|
99
|
+
export interface CommerceEventPostTarget {
|
|
100
|
+
postMessage(message: CommerceEventMessage, targetOrigin: string): void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Where a subscriber's events go:
|
|
105
|
+
* - `callback` — an in-page handler invoked with the validated event.
|
|
106
|
+
* - `iframe` — a sandboxed consumer the broker posts a TYPED, channel-tagged
|
|
107
|
+
* message to at a fixed `targetOrigin`, so the frame reads a validated event and
|
|
108
|
+
* never a raw one.
|
|
109
|
+
*/
|
|
110
|
+
export type CommerceEventSink =
|
|
111
|
+
| { kind: "callback"; handler: (event: CommerceEvent) => void }
|
|
112
|
+
| { kind: "iframe"; frame: CommerceEventPostTarget; targetOrigin: string };
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* One scoped subscription. `id` is its stable identity: subscribing again with the
|
|
116
|
+
* same id REPLACES the prior registration (no duplicate listeners). `types` is the
|
|
117
|
+
* app/topic scope; omitting it subscribes to every browser event type. `consentPurpose`
|
|
118
|
+
* is the destination's required purpose; omitting it means essential-only (always
|
|
119
|
+
* eligible).
|
|
120
|
+
*/
|
|
121
|
+
export interface CommerceEventSubscription {
|
|
122
|
+
id: string;
|
|
123
|
+
sink: CommerceEventSink;
|
|
124
|
+
types?: readonly CommerceEventType[];
|
|
125
|
+
consentPurpose?: ConsentPurpose;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Why a subscriber did not receive an emitted event (delivery is scoped + fail-soft). */
|
|
129
|
+
export type SubscriberSkipReason = "out-of-scope" | "consent-ineligible" | "threw";
|
|
130
|
+
|
|
131
|
+
/** The per-subscriber outcome of one emit — a throwing sink is recorded, never propagated. */
|
|
132
|
+
export interface SubscriberDeliveryOutcome {
|
|
133
|
+
subscriberId: string;
|
|
134
|
+
delivered: boolean;
|
|
135
|
+
reason?: SubscriberSkipReason;
|
|
136
|
+
/** Stable diagnostic code; subscriber exception text and stacks are never copied. */
|
|
137
|
+
error?: "subscriber_threw";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Why an emit delivered to nobody without being an error (the envelope was fine). */
|
|
141
|
+
export type EmitSkipReason = "duplicate" | "not-browser-authored";
|
|
142
|
+
|
|
143
|
+
/** The result of one emit. `ok: false` carries the envelope's contract violations. */
|
|
144
|
+
export type CommerceEmitResult =
|
|
145
|
+
| {
|
|
146
|
+
ok: true;
|
|
147
|
+
event: CommerceEvent;
|
|
148
|
+
/** False when the logical id was already emitted (deduplicated) — no re-fire. */
|
|
149
|
+
emitted: boolean;
|
|
150
|
+
skipReason?: EmitSkipReason;
|
|
151
|
+
deliveries: SubscriberDeliveryOutcome[];
|
|
152
|
+
}
|
|
153
|
+
| { ok: false; violations: ContractViolation[] };
|
|
154
|
+
|
|
155
|
+
/** A structured, non-sensitive delivery signal for observability — never a payload or stack. */
|
|
156
|
+
export interface CommerceBrokerObservation {
|
|
157
|
+
type: CommerceEventType;
|
|
158
|
+
logicalId: string;
|
|
159
|
+
subscriberId: string;
|
|
160
|
+
ok: boolean;
|
|
161
|
+
/** Stable diagnostic code; subscriber exception text and stacks are never observed. */
|
|
162
|
+
error?: "subscriber_threw";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export type CommerceBrokerObserver = (observation: CommerceBrokerObservation) => void;
|
|
166
|
+
|
|
167
|
+
export interface CommerceEventBrokerOptions {
|
|
168
|
+
/** The trusted tenant/environment/surface stamped onto every envelope; never caller-set. */
|
|
169
|
+
trusted: TrustedEventContext;
|
|
170
|
+
/** The event `source` URN for this surface; server-owned, stamped from trusted context. */
|
|
171
|
+
source: string;
|
|
172
|
+
/** Reads the current consent snapshot at emit time (the neutral broker's snapshot). */
|
|
173
|
+
consentSnapshot: () => ConsentSnapshot;
|
|
174
|
+
/** Server-loaded explicit action registry for this tenant; absent/invalid fails action emission closed. */
|
|
175
|
+
tenantActionRegistry?: unknown;
|
|
176
|
+
/** ISO-8601 timestamp source; defaults to wall clock. */
|
|
177
|
+
now?: () => string;
|
|
178
|
+
/** Optional observer for delivery diagnostics (isolation faults surface here, not to callers). */
|
|
179
|
+
observe?: CommerceBrokerObserver;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const MANAGED_ANALYTICS_EVENT_TYPES: readonly CommerceEventType[] = [
|
|
183
|
+
"storefront.page.viewed.v2",
|
|
184
|
+
"storefront.action.activated.v1",
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
function isManagedAnalyticsEvent(type: CommerceEventType): boolean {
|
|
188
|
+
return MANAGED_ANALYTICS_EVENT_TYPES.includes(type);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export class CommerceEventBroker {
|
|
192
|
+
private readonly subscriptions = new Map<string, CommerceEventSubscription>();
|
|
193
|
+
private readonly emittedIds = new Set<string>();
|
|
194
|
+
private readonly opts: CommerceEventBrokerOptions;
|
|
195
|
+
private readonly tenantActionRegistry: TenantActionRegistry | null;
|
|
196
|
+
private readonly tenantActionRegistryViolations: ContractViolation[];
|
|
197
|
+
|
|
198
|
+
constructor(options: CommerceEventBrokerOptions) {
|
|
199
|
+
this.opts = options;
|
|
200
|
+
if (options.tenantActionRegistry === undefined) {
|
|
201
|
+
this.tenantActionRegistry = null;
|
|
202
|
+
this.tenantActionRegistryViolations = [];
|
|
203
|
+
} else {
|
|
204
|
+
const registry = validateTenantActionRegistry(options.tenantActionRegistry);
|
|
205
|
+
this.tenantActionRegistry = registry.ok ? registry.value : null;
|
|
206
|
+
this.tenantActionRegistryViolations = registry.ok ? [] : registry.violations;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Register (or REPLACE) a scoped subscription. Keying on `subscription.id` means a
|
|
212
|
+
* re-subscribe — e.g. after a consent change re-derives a destination — swaps the
|
|
213
|
+
* registration in place rather than stacking a second listener, so one logical
|
|
214
|
+
* action still fires the subscriber exactly once. Returns an unsubscribe function.
|
|
215
|
+
*/
|
|
216
|
+
subscribe(subscription: CommerceEventSubscription): () => void {
|
|
217
|
+
this.subscriptions.set(subscription.id, subscription);
|
|
218
|
+
return () => {
|
|
219
|
+
const current = this.subscriptions.get(subscription.id);
|
|
220
|
+
// Only remove if this exact registration is still the live one; a later
|
|
221
|
+
// re-subscribe under the same id owns the slot and must not be torn down here.
|
|
222
|
+
if (current === subscription) this.subscriptions.delete(subscription.id);
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Whether a subscriber id currently holds a registration. */
|
|
227
|
+
hasSubscriber(id: string): boolean {
|
|
228
|
+
return this.subscriptions.has(id);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Read destination eligibility from the broker's current trusted consent snapshot. */
|
|
232
|
+
isConsentEligible(purpose: ConsentPurpose): boolean {
|
|
233
|
+
return isPurposeEligible(this.opts.consentSnapshot(), purpose);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Emit one logical browser commerce action. The broker builds the envelope from the
|
|
238
|
+
* trusted context (the caller supplies no server-owned field), validates it against
|
|
239
|
+
* the closed contract, deduplicates on its logical id, and fans it out to every
|
|
240
|
+
* in-scope, consent-eligible subscriber inside per-subscriber isolation.
|
|
241
|
+
*
|
|
242
|
+
* A failed validation delivers to nobody and returns the violations. A duplicate
|
|
243
|
+
* logical id returns `emitted: false`. A subscriber that throws is recorded in its
|
|
244
|
+
* outcome and observed — never rethrown — so the caller (cart, navigation) is
|
|
245
|
+
* unaffected.
|
|
246
|
+
*/
|
|
247
|
+
emit(input: CommerceEmitInput): CommerceEmitResult {
|
|
248
|
+
// Fail fast and clearly on a server-authoritative type before building an envelope
|
|
249
|
+
// the validator would reject anyway; the browser can never author a conversion.
|
|
250
|
+
if (EVENT_SOURCE_OF_TRUTH[input.type] !== "browser") {
|
|
251
|
+
return {
|
|
252
|
+
ok: false,
|
|
253
|
+
violations: [
|
|
254
|
+
{
|
|
255
|
+
path: "type",
|
|
256
|
+
code: "forged_authority",
|
|
257
|
+
message: `"${input.type}" is server-authoritative; the browser broker cannot emit it.`,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const snapshot = this.opts.consentSnapshot();
|
|
264
|
+
const envelope: CommerceEvent = {
|
|
265
|
+
specversion: "1.0",
|
|
266
|
+
id: input.logicalId,
|
|
267
|
+
source: this.opts.source,
|
|
268
|
+
type: input.type,
|
|
269
|
+
subject: input.subject,
|
|
270
|
+
time: this.now(),
|
|
271
|
+
datacontenttype: "application/json",
|
|
272
|
+
storefront: {
|
|
273
|
+
schemaVersion: COMMERCE_EVENT_STOREFRONT_SCHEMA_VERSION,
|
|
274
|
+
tenant: this.opts.trusted.tenant,
|
|
275
|
+
environment: this.opts.trusted.environment,
|
|
276
|
+
siteSurface: this.opts.trusted.siteSurface,
|
|
277
|
+
emitter: "browser",
|
|
278
|
+
...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
|
|
279
|
+
},
|
|
280
|
+
correlation: input.correlation,
|
|
281
|
+
consent: toEventConsentSnapshot(snapshot),
|
|
282
|
+
data: input.data,
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const validation = validateCommerceEvent(envelope);
|
|
286
|
+
if (!validation.ok) return { ok: false, violations: validation.violations };
|
|
287
|
+
const event = validation.value;
|
|
288
|
+
|
|
289
|
+
if (event.type === "storefront.action.activated.v1") {
|
|
290
|
+
if (!this.tenantActionRegistry) {
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
violations:
|
|
294
|
+
this.tenantActionRegistryViolations.length > 0
|
|
295
|
+
? this.tenantActionRegistryViolations
|
|
296
|
+
: [
|
|
297
|
+
{
|
|
298
|
+
path: "data.name",
|
|
299
|
+
code: "unregistered_action",
|
|
300
|
+
message: "Managed actions require a valid tenant action registry.",
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const actionName = event.data["name"] as string;
|
|
306
|
+
if (!this.tenantActionRegistry.actions.some((action) => action.name === actionName)) {
|
|
307
|
+
return {
|
|
308
|
+
ok: false,
|
|
309
|
+
violations: [
|
|
310
|
+
{
|
|
311
|
+
path: "data.name",
|
|
312
|
+
code: "unregistered_action",
|
|
313
|
+
message: "Action is not present in the tenant's explicit action registry.",
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (isManagedAnalyticsEvent(event.type) && !isPurposeEligible(snapshot, "analytics")) {
|
|
321
|
+
return {
|
|
322
|
+
ok: false,
|
|
323
|
+
violations: [
|
|
324
|
+
{
|
|
325
|
+
path: "consent.eligiblePurposes",
|
|
326
|
+
code: "consent_ineligible",
|
|
327
|
+
message: "Managed analytics events require analytics consent before creation or delivery.",
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const key = eventDedupeKey(event);
|
|
334
|
+
if (this.emittedIds.has(key)) {
|
|
335
|
+
return { ok: true, event, emitted: false, skipReason: "duplicate", deliveries: [] };
|
|
336
|
+
}
|
|
337
|
+
this.emittedIds.add(key);
|
|
338
|
+
|
|
339
|
+
const deliveries: SubscriberDeliveryOutcome[] = [];
|
|
340
|
+
for (const subscription of this.subscriptions.values()) {
|
|
341
|
+
deliveries.push(this.deliver(subscription, event, snapshot));
|
|
342
|
+
}
|
|
343
|
+
return { ok: true, event, emitted: true, deliveries };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private deliver(
|
|
347
|
+
subscription: CommerceEventSubscription,
|
|
348
|
+
event: CommerceEvent,
|
|
349
|
+
snapshot: ConsentSnapshot,
|
|
350
|
+
): SubscriberDeliveryOutcome {
|
|
351
|
+
if (subscription.types && !subscription.types.includes(event.type)) {
|
|
352
|
+
return { subscriberId: subscription.id, delivered: false, reason: "out-of-scope" };
|
|
353
|
+
}
|
|
354
|
+
if (
|
|
355
|
+
isManagedAnalyticsEvent(event.type) &&
|
|
356
|
+
subscription.consentPurpose !== "analytics"
|
|
357
|
+
) {
|
|
358
|
+
return { subscriberId: subscription.id, delivered: false, reason: "consent-ineligible" };
|
|
359
|
+
}
|
|
360
|
+
// essential is always eligible, so an undeclared purpose always passes.
|
|
361
|
+
const purpose = subscription.consentPurpose ?? "essential";
|
|
362
|
+
if (!isPurposeEligible(snapshot, purpose)) {
|
|
363
|
+
return { subscriberId: subscription.id, delivered: false, reason: "consent-ineligible" };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
this.dispatch(subscription.sink, event);
|
|
368
|
+
this.observe({
|
|
369
|
+
type: event.type,
|
|
370
|
+
logicalId: event.id,
|
|
371
|
+
subscriberId: subscription.id,
|
|
372
|
+
ok: true,
|
|
373
|
+
});
|
|
374
|
+
return { subscriberId: subscription.id, delivered: true };
|
|
375
|
+
} catch {
|
|
376
|
+
this.observe({
|
|
377
|
+
type: event.type,
|
|
378
|
+
logicalId: event.id,
|
|
379
|
+
subscriberId: subscription.id,
|
|
380
|
+
ok: false,
|
|
381
|
+
error: "subscriber_threw",
|
|
382
|
+
});
|
|
383
|
+
return {
|
|
384
|
+
subscriberId: subscription.id,
|
|
385
|
+
delivered: false,
|
|
386
|
+
reason: "threw",
|
|
387
|
+
error: "subscriber_threw",
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private dispatch(sink: CommerceEventSink, event: CommerceEvent): void {
|
|
393
|
+
if (sink.kind === "callback") {
|
|
394
|
+
sink.handler(event);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
// A sandboxed consumer gets a TYPED, channel-tagged, already-validated envelope.
|
|
398
|
+
sink.frame.postMessage({ channel: COMMERCE_EVENT_MESSAGE_CHANNEL, event }, sink.targetOrigin);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private now(): string {
|
|
402
|
+
return this.opts.now ? this.opts.now() : new Date().toISOString();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private observe(observation: CommerceBrokerObservation): void {
|
|
406
|
+
try {
|
|
407
|
+
this.opts.observe?.(Object.freeze({
|
|
408
|
+
...observation,
|
|
409
|
+
logicalId: safeManagedAnalyticsDiagnosticId(observation.logicalId),
|
|
410
|
+
subscriberId: safeManagedAnalyticsDiagnosticId(observation.subscriberId),
|
|
411
|
+
}));
|
|
412
|
+
} catch {
|
|
413
|
+
// Observability is fail-soft: a broken collaborator never changes delivery.
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* The iframe-consumer side of the channel: read a received postMessage payload
|
|
420
|
+
* FAIL-CLOSED into a validated {@link CommerceEvent}. A payload without the exact
|
|
421
|
+
* broker channel tag, or one whose event does not pass {@link validateCommerceEvent},
|
|
422
|
+
* yields `null` — a consumer never acts on a raw, forged, or off-channel message.
|
|
423
|
+
*/
|
|
424
|
+
export function parseCommerceEventMessage(data: unknown): CommerceEvent | null {
|
|
425
|
+
if (typeof data !== "object" || data === null) return null;
|
|
426
|
+
const record = data as Record<string, unknown>;
|
|
427
|
+
if (record["channel"] !== COMMERCE_EVENT_MESSAGE_CHANNEL) return null;
|
|
428
|
+
const result = validateCommerceEvent(record["event"]);
|
|
429
|
+
return result.ok ? result.value : null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* The anonymous, minimized post-purchase summary a browser subscriber may receive
|
|
434
|
+
* (ADR 0014 M2 `integration-commerce-data-minimized`): currency, total value, item
|
|
435
|
+
* count, and anonymous line items. It carries NO order id, customer identity, payment
|
|
436
|
+
* data, or signed-cart material — the authoritative financial record lives only on the
|
|
437
|
+
* server verified-order path. The broker's closed field catalog re-enforces this on emit.
|
|
438
|
+
*/
|
|
439
|
+
export interface PurchaseCompletedSummary {
|
|
440
|
+
currency: string;
|
|
441
|
+
/** A non-negative decimal money string, e.g. `"42.50"`. */
|
|
442
|
+
value: string;
|
|
443
|
+
itemCount: number;
|
|
444
|
+
items: readonly {
|
|
445
|
+
productId: string;
|
|
446
|
+
variantId?: string;
|
|
447
|
+
quantity: number;
|
|
448
|
+
unitPrice: string;
|
|
449
|
+
}[];
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* The ONE named seam a post-purchase third-party trigger flows through: emit the
|
|
454
|
+
* browser-deliverable `storefront.purchase.completed.v1` signal (design-partner finding
|
|
455
|
+
* F1/F2 wire). It rides the broker's existing guarantees verbatim — server-owned context
|
|
456
|
+
* stamped not caller-set, one-event-per-logical-action dedupe, per-subscriber consent
|
|
457
|
+
* filtering, and subscriber-fault isolation — so a scoped sandbox subscriber (via the
|
|
458
|
+
* lifecycle context's receive-only subscribe handle) reacts to a completed purchase
|
|
459
|
+
* exactly once. The caller supplies only the anonymous summary and the logical action
|
|
460
|
+
* identity; it can never set a server-owned field or author a verified-provider order.
|
|
461
|
+
*
|
|
462
|
+
* NOTE ON THE LIVE TRIGGER (honest gap): the storefront's checkout AND receipt are
|
|
463
|
+
* provider-hosted and cross-domain (Foxy), so there is no storefront-rendered
|
|
464
|
+
* confirmation page in THIS origin to call this seam from today. The subscribe/validate
|
|
465
|
+
* path is fully wired and this emit seam is real and tested; the remaining work is the
|
|
466
|
+
* cross-domain bridge that carries the provider receipt signal back to a storefront-origin
|
|
467
|
+
* document which then calls this — tracked as an explicit follow-up, not faked here.
|
|
468
|
+
*/
|
|
469
|
+
export function emitPurchaseCompleted(
|
|
470
|
+
broker: CommerceEventBroker,
|
|
471
|
+
input: {
|
|
472
|
+
/** The opaque logical event id — the dedupe key. The same completed purchase reuses it. */
|
|
473
|
+
logicalId: string;
|
|
474
|
+
/** Opaque in-event subject (e.g. an anonymous purchase/session handle) — never an order id. */
|
|
475
|
+
subject: string;
|
|
476
|
+
/** The bounded-lifetime attribution thread this purchase belongs to. */
|
|
477
|
+
correlation: EventCorrelation;
|
|
478
|
+
/** The anonymous, PII-free purchase summary. */
|
|
479
|
+
summary: PurchaseCompletedSummary;
|
|
480
|
+
/** Optional per-emitter monotonic counter for out-of-order detection. */
|
|
481
|
+
sequence?: number;
|
|
482
|
+
},
|
|
483
|
+
): CommerceEmitResult {
|
|
484
|
+
return broker.emit({
|
|
485
|
+
type: PURCHASE_COMPLETED_EVENT_TYPE,
|
|
486
|
+
logicalId: input.logicalId,
|
|
487
|
+
subject: input.subject,
|
|
488
|
+
correlation: input.correlation,
|
|
489
|
+
data: {
|
|
490
|
+
currency: input.summary.currency,
|
|
491
|
+
value: input.summary.value,
|
|
492
|
+
itemCount: input.summary.itemCount,
|
|
493
|
+
items: input.summary.items.map((item) => ({ ...item })),
|
|
494
|
+
},
|
|
495
|
+
...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
|
|
496
|
+
});
|
|
497
|
+
}
|