@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,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document-scoped bridge from platform-owned semantic action boundaries to the
|
|
3
|
+
* versioned commerce-event broker.
|
|
4
|
+
*
|
|
5
|
+
* This bridge deliberately has no DOM input. Its action boundary does not accept
|
|
6
|
+
* selectors, elements, labels, text, classes, forms, destinations, analytics commands,
|
|
7
|
+
* or arbitrary event data. Platform components call {@link activate} (or publish
|
|
8
|
+
* through the injected typed source) with one approved semantic name and an opaque
|
|
9
|
+
* logical identity. The existing {@link CommerceEventBroker} remains the only authority
|
|
10
|
+
* that stamps trusted context, validates the closed event contract, applies consent,
|
|
11
|
+
* deduplicates, and isolates subscriber failures.
|
|
12
|
+
*/
|
|
13
|
+
import type { CommerceEventBroker } from "./commerce-broker.js";
|
|
14
|
+
import {
|
|
15
|
+
NAVIGATION_KINDS,
|
|
16
|
+
type ApprovedMarketingActionName,
|
|
17
|
+
type NavigationKind,
|
|
18
|
+
} from "./events.js";
|
|
19
|
+
import type { ContractViolation } from "./result.js";
|
|
20
|
+
import { resemblesManagedAnalyticsPii } from "./managed-analytics-value-safety.js";
|
|
21
|
+
import {
|
|
22
|
+
emitManagedActionActivated,
|
|
23
|
+
emitManagedPageView,
|
|
24
|
+
pageViewOwnerForNavigation,
|
|
25
|
+
type ManagedActionActivationInput,
|
|
26
|
+
type ManagedAnalyticsEmitResult,
|
|
27
|
+
type ManagedPageViewInput,
|
|
28
|
+
} from "./managed-analytics.js";
|
|
29
|
+
|
|
30
|
+
/** One explicit activation raised at a platform component's semantic handler. */
|
|
31
|
+
export interface PlatformManagedActionActivation
|
|
32
|
+
extends Omit<ManagedActionActivationInput, "name"> {
|
|
33
|
+
/** One of the program-approved v1 identifiers; labels and selectors are not accepted. */
|
|
34
|
+
name: ApprovedMarketingActionName;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Optional platform-owned activation bus. It is an injected typed seam, not a browser
|
|
39
|
+
* event or DOM-discovery surface. The returned function must remove this listener.
|
|
40
|
+
*/
|
|
41
|
+
export interface PlatformManagedActionSource {
|
|
42
|
+
subscribe(listener: (activation: PlatformManagedActionActivation) => void): () => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ManagedAnalyticsNavigationCommit extends ManagedPageViewInput {
|
|
46
|
+
kind: NavigationKind;
|
|
47
|
+
/** An aborted navigation never became a viewed resource. */
|
|
48
|
+
aborted?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type ManagedAnalyticsBridgeSkipReason =
|
|
52
|
+
| "already-initialized"
|
|
53
|
+
| "lifecycle-unchanged"
|
|
54
|
+
| "not-initialized"
|
|
55
|
+
| "not-page-view-owner"
|
|
56
|
+
| "disposed";
|
|
57
|
+
|
|
58
|
+
export interface ManagedAnalyticsBridgeSkipped {
|
|
59
|
+
ok: true;
|
|
60
|
+
emitted: false;
|
|
61
|
+
skipReason: ManagedAnalyticsBridgeSkipReason;
|
|
62
|
+
deliveries: [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type ManagedAnalyticsBridgeResult =
|
|
66
|
+
| ManagedAnalyticsEmitResult
|
|
67
|
+
| ManagedAnalyticsBridgeSkipped;
|
|
68
|
+
|
|
69
|
+
export interface ManagedAnalyticsEventBrokerBridgeOptions {
|
|
70
|
+
/** The existing versioned broker; this bridge never constructs a parallel bus. */
|
|
71
|
+
broker: CommerceEventBroker;
|
|
72
|
+
/** Optional typed source for platform-owned action handler activations. */
|
|
73
|
+
actionSource?: PlatformManagedActionSource;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type BridgeState = "idle" | "initializing" | "initialized" | "disposed";
|
|
77
|
+
|
|
78
|
+
function skipped(skipReason: ManagedAnalyticsBridgeSkipReason): ManagedAnalyticsBridgeSkipped {
|
|
79
|
+
return { ok: true, emitted: false, skipReason, deliveries: [] };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const PLATFORM_ACTION_FIELDS = ["logicalId", "name", "correlation", "sequence"] as const;
|
|
83
|
+
const DOCUMENT_PAGE_FIELDS = ["logicalId", "path", "correlation", "attribution", "sequence"] as const;
|
|
84
|
+
const NAVIGATION_FIELDS = [...DOCUMENT_PAGE_FIELDS, "kind", "aborted"] as const;
|
|
85
|
+
|
|
86
|
+
function failure(
|
|
87
|
+
path: string,
|
|
88
|
+
code: string,
|
|
89
|
+
message: string,
|
|
90
|
+
): { ok: false; violations: ContractViolation[] } {
|
|
91
|
+
return { ok: false, violations: [{ path, code, message }] };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Validate only bridge-owned top-level shape and value canaries; the broker validates the envelope. */
|
|
95
|
+
function bridgeInputViolations(
|
|
96
|
+
input: unknown,
|
|
97
|
+
allowed: readonly string[],
|
|
98
|
+
label: string,
|
|
99
|
+
): ContractViolation[] {
|
|
100
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
101
|
+
return [{ path: "(root)", code: "type", message: `${label} must be an object.` }];
|
|
102
|
+
}
|
|
103
|
+
const record = input as Record<string, unknown>;
|
|
104
|
+
if (Object.keys(record).some((key) => !allowed.includes(key))) {
|
|
105
|
+
return [{
|
|
106
|
+
path: "(root)",
|
|
107
|
+
code: "unknown_field",
|
|
108
|
+
message: `${label} contains a field outside its closed public contract.`,
|
|
109
|
+
}];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const violations: ContractViolation[] = [];
|
|
113
|
+
if (resemblesManagedAnalyticsPii(record["logicalId"])) {
|
|
114
|
+
violations.push({
|
|
115
|
+
path: "logicalId",
|
|
116
|
+
code: "pii_value",
|
|
117
|
+
message: "Logical ids must be opaque and cannot resemble customer PII.",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (resemblesManagedAnalyticsPii(record["path"])) {
|
|
121
|
+
violations.push({
|
|
122
|
+
path: "path",
|
|
123
|
+
code: "pii_value",
|
|
124
|
+
message: "Page paths cannot contain values resembling customer PII.",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const correlation = record["correlation"];
|
|
128
|
+
if (
|
|
129
|
+
typeof correlation === "object" &&
|
|
130
|
+
correlation !== null &&
|
|
131
|
+
!Array.isArray(correlation) &&
|
|
132
|
+
resemblesManagedAnalyticsPii((correlation as Record<string, unknown>)["id"])
|
|
133
|
+
) {
|
|
134
|
+
violations.push({
|
|
135
|
+
path: "correlation.id",
|
|
136
|
+
code: "pii_value",
|
|
137
|
+
message: "Correlation ids must be opaque and cannot resemble customer PII.",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return violations;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function invalidNavigationKind(): { ok: false; violations: ContractViolation[] } {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
violations: [
|
|
147
|
+
{
|
|
148
|
+
path: "kind",
|
|
149
|
+
code: "unknown_enum",
|
|
150
|
+
message: `Navigation kind must be one of: ${NAVIGATION_KINDS.join(", ")}.`,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* One bridge instance belongs to one traditional document.
|
|
158
|
+
*
|
|
159
|
+
* `initialize` emits the document-owned page view and binds the optional platform
|
|
160
|
+
* action source exactly once. Consent changes do not rebind or reinitialize it: the
|
|
161
|
+
* broker reads its current consent source at every later emission. A same-document
|
|
162
|
+
* route commit can emit its page view through {@link navigationCommitted}; document
|
|
163
|
+
* navigations remain owned by the destination document bootstrap, while hash moves
|
|
164
|
+
* and aborts are never page views. `dispose` is idempotent and terminal.
|
|
165
|
+
*/
|
|
166
|
+
export class ManagedAnalyticsEventBrokerBridge {
|
|
167
|
+
private state: BridgeState = "idle";
|
|
168
|
+
private unsubscribeActions?: () => void;
|
|
169
|
+
private readonly broker: CommerceEventBroker;
|
|
170
|
+
private readonly actionSource?: PlatformManagedActionSource;
|
|
171
|
+
|
|
172
|
+
constructor(options: ManagedAnalyticsEventBrokerBridgeOptions) {
|
|
173
|
+
this.broker = options.broker;
|
|
174
|
+
this.actionSource = options.actionSource;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
get isInitialized(): boolean {
|
|
178
|
+
return this.state === "initialized";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
get isDisposed(): boolean {
|
|
182
|
+
return this.state === "disposed";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Bind this document once and emit its bootstrap-owned page view. Repeated calls —
|
|
187
|
+
* including a mistaken consent-change re-init — neither subscribe nor emit again.
|
|
188
|
+
*/
|
|
189
|
+
initialize(page: ManagedPageViewInput): ManagedAnalyticsBridgeResult {
|
|
190
|
+
if (this.state === "disposed") return skipped("disposed");
|
|
191
|
+
if (this.state !== "idle") return skipped("already-initialized");
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const violations = bridgeInputViolations(page, DOCUMENT_PAGE_FIELDS, "Document page input");
|
|
195
|
+
if (violations.length > 0) return { ok: false, violations };
|
|
196
|
+
} catch {
|
|
197
|
+
return failure(
|
|
198
|
+
"(root)",
|
|
199
|
+
"input_access_failed",
|
|
200
|
+
"Document page input could not be read safely.",
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.state = "initializing";
|
|
205
|
+
if (this.actionSource) {
|
|
206
|
+
try {
|
|
207
|
+
const unsubscribe = this.actionSource.subscribe((activation) => {
|
|
208
|
+
try {
|
|
209
|
+
// The result is intentionally ignored: analytics never breaks the action.
|
|
210
|
+
this.activate(activation);
|
|
211
|
+
} catch {
|
|
212
|
+
// A hostile collaborator/proxy cannot escape the platform-owned listener.
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
if (typeof unsubscribe !== "function") throw new TypeError("invalid unsubscribe");
|
|
216
|
+
this.unsubscribeActions = unsubscribe;
|
|
217
|
+
} catch {
|
|
218
|
+
this.state = "idle";
|
|
219
|
+
return failure(
|
|
220
|
+
"actionSource",
|
|
221
|
+
"source_subscribe_failed",
|
|
222
|
+
"The platform action source could not be bound.",
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const result = emitManagedPageView(this.broker, page);
|
|
229
|
+
if (!result.ok) {
|
|
230
|
+
return this.rollbackInitialization() ?? result;
|
|
231
|
+
}
|
|
232
|
+
this.state = "initialized";
|
|
233
|
+
return result;
|
|
234
|
+
} catch {
|
|
235
|
+
return this.rollbackInitialization() ?? failure(
|
|
236
|
+
"initialize",
|
|
237
|
+
"bridge_initialization_failed",
|
|
238
|
+
"Managed analytics initialization failed safely.",
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Consent changes are intentionally an in-place no-op for bridge lifecycle. The
|
|
245
|
+
* broker consults its live consent snapshot on every future activation/navigation.
|
|
246
|
+
*/
|
|
247
|
+
consentUpdated(): ManagedAnalyticsBridgeResult {
|
|
248
|
+
if (this.state === "disposed") return skipped("disposed");
|
|
249
|
+
if (this.state !== "initialized") return skipped("not-initialized");
|
|
250
|
+
return skipped("lifecycle-unchanged");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Emit an explicitly named platform action through the existing broker. */
|
|
254
|
+
activate(activation: PlatformManagedActionActivation): ManagedAnalyticsBridgeResult {
|
|
255
|
+
if (this.state === "disposed") return skipped("disposed");
|
|
256
|
+
if (this.state !== "initialized") return skipped("not-initialized");
|
|
257
|
+
|
|
258
|
+
let projected: ManagedActionActivationInput;
|
|
259
|
+
try {
|
|
260
|
+
const violations = bridgeInputViolations(
|
|
261
|
+
activation,
|
|
262
|
+
PLATFORM_ACTION_FIELDS,
|
|
263
|
+
"Platform action activation",
|
|
264
|
+
);
|
|
265
|
+
if (violations.length > 0) return { ok: false, violations };
|
|
266
|
+
projected = {
|
|
267
|
+
logicalId: activation.logicalId,
|
|
268
|
+
name: activation.name,
|
|
269
|
+
correlation: activation.correlation,
|
|
270
|
+
...(activation.sequence !== undefined ? { sequence: activation.sequence } : {}),
|
|
271
|
+
};
|
|
272
|
+
} catch {
|
|
273
|
+
return failure(
|
|
274
|
+
"(root)",
|
|
275
|
+
"input_access_failed",
|
|
276
|
+
"Platform action activation could not be read safely.",
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
return emitManagedActionActivated(this.broker, projected);
|
|
282
|
+
} catch {
|
|
283
|
+
return failure(
|
|
284
|
+
"activate",
|
|
285
|
+
"action_emit_failed",
|
|
286
|
+
"Managed action emission failed safely.",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Emit the page view owned by a committed same-document route. Full-document loads
|
|
293
|
+
* are counted by the new document's `initialize`; hashes and aborts are not views.
|
|
294
|
+
*/
|
|
295
|
+
navigationCommitted(
|
|
296
|
+
navigation: ManagedAnalyticsNavigationCommit,
|
|
297
|
+
): ManagedAnalyticsBridgeResult {
|
|
298
|
+
if (this.state === "disposed") return skipped("disposed");
|
|
299
|
+
if (this.state !== "initialized") return skipped("not-initialized");
|
|
300
|
+
|
|
301
|
+
let page: ManagedPageViewInput;
|
|
302
|
+
let kind: NavigationKind;
|
|
303
|
+
let aborted: boolean | undefined;
|
|
304
|
+
try {
|
|
305
|
+
const violations = bridgeInputViolations(
|
|
306
|
+
navigation,
|
|
307
|
+
NAVIGATION_FIELDS,
|
|
308
|
+
"Navigation commit",
|
|
309
|
+
);
|
|
310
|
+
if (violations.length > 0) return { ok: false, violations };
|
|
311
|
+
if (!(NAVIGATION_KINDS as readonly unknown[]).includes(navigation.kind)) {
|
|
312
|
+
return invalidNavigationKind();
|
|
313
|
+
}
|
|
314
|
+
kind = navigation.kind;
|
|
315
|
+
aborted = navigation.aborted;
|
|
316
|
+
page = {
|
|
317
|
+
logicalId: navigation.logicalId,
|
|
318
|
+
path: navigation.path,
|
|
319
|
+
correlation: navigation.correlation,
|
|
320
|
+
...(navigation.attribution ? { attribution: navigation.attribution } : {}),
|
|
321
|
+
...(navigation.sequence !== undefined ? { sequence: navigation.sequence } : {}),
|
|
322
|
+
};
|
|
323
|
+
} catch {
|
|
324
|
+
return failure(
|
|
325
|
+
"(root)",
|
|
326
|
+
"input_access_failed",
|
|
327
|
+
"Navigation commit could not be read safely.",
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
if (pageViewOwnerForNavigation(kind, aborted) !== "navigation-commit") {
|
|
331
|
+
return skipped("not-page-view-owner");
|
|
332
|
+
}
|
|
333
|
+
try {
|
|
334
|
+
return emitManagedPageView(this.broker, page);
|
|
335
|
+
} catch {
|
|
336
|
+
return failure(
|
|
337
|
+
"navigation",
|
|
338
|
+
"page_emit_failed",
|
|
339
|
+
"Managed page emission failed safely.",
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Remove the typed platform source listener once; disposal is terminal. */
|
|
345
|
+
dispose(): ManagedAnalyticsBridgeResult {
|
|
346
|
+
if (this.state === "disposed" && !this.unsubscribeActions) return skipped("disposed");
|
|
347
|
+
this.state = "disposed";
|
|
348
|
+
const unbindFailure = this.unbindActions();
|
|
349
|
+
return unbindFailure ?? skipped("lifecycle-unchanged");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private rollbackInitialization(): ManagedAnalyticsBridgeResult | null {
|
|
353
|
+
const unbindFailure = this.unbindActions();
|
|
354
|
+
// A listener whose removal could not be proven remains owned by this bridge.
|
|
355
|
+
// Stay terminal so a corrected initialize cannot stack another listener; a
|
|
356
|
+
// later dispose may retry the retained teardown.
|
|
357
|
+
this.state = unbindFailure ? "disposed" : "idle";
|
|
358
|
+
return unbindFailure;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private unbindActions(): ManagedAnalyticsBridgeResult | null {
|
|
362
|
+
const unsubscribe = this.unsubscribeActions;
|
|
363
|
+
if (!unsubscribe) return null;
|
|
364
|
+
try {
|
|
365
|
+
unsubscribe();
|
|
366
|
+
this.unsubscribeActions = undefined;
|
|
367
|
+
return null;
|
|
368
|
+
} catch {
|
|
369
|
+
return failure(
|
|
370
|
+
"actionSource",
|
|
371
|
+
"source_unsubscribe_failed",
|
|
372
|
+
"The platform action source could not be unbound cleanly.",
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Convenience factory for hosts that prefer a function over `new`. */
|
|
379
|
+
export function createManagedAnalyticsEventBrokerBridge(
|
|
380
|
+
options: ManagedAnalyticsEventBrokerBridgeOptions,
|
|
381
|
+
): ManagedAnalyticsEventBrokerBridge {
|
|
382
|
+
return new ManagedAnalyticsEventBrokerBridge(options);
|
|
383
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Shared PII-value canary for managed browser events and their diagnostics. */
|
|
2
|
+
import { MANAGED_ANALYTICS_PII_VALUE_PATTERN } from "./managed-analytics-api-v1-safety.mjs";
|
|
3
|
+
|
|
4
|
+
const MANAGED_ANALYTICS_PII_VALUE = new RegExp(MANAGED_ANALYTICS_PII_VALUE_PATTERN);
|
|
5
|
+
|
|
6
|
+
/** True when a public string resembles an email, SSN, payment card, or phone number. */
|
|
7
|
+
export function resemblesManagedAnalyticsPii(value: unknown): boolean {
|
|
8
|
+
return typeof value === "string" && MANAGED_ANALYTICS_PII_VALUE.test(value);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Defense-in-depth for identifiers copied into structured broker observations. */
|
|
12
|
+
export function safeManagedAnalyticsDiagnosticId(value: string): string {
|
|
13
|
+
return resemblesManagedAnalyticsPii(value) ? "[redacted]" : value;
|
|
14
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storefront-managed analytics semantics.
|
|
3
|
+
*
|
|
4
|
+
* This module is deliberately DOM-free. Platform components call the named emit
|
|
5
|
+
* functions at the semantic action boundary; the runtime never discovers actions by
|
|
6
|
+
* inspecting selectors, text, classes, forms, globals, or a dataLayer. Tenant and
|
|
7
|
+
* environment are stamped by CommerceEventBroker's trusted construction context.
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
reqArray,
|
|
11
|
+
reqLiteral,
|
|
12
|
+
reqObject,
|
|
13
|
+
reqString,
|
|
14
|
+
rejectUnknownKeys,
|
|
15
|
+
Violations,
|
|
16
|
+
type ContractResult,
|
|
17
|
+
} from "./result.js";
|
|
18
|
+
import { findForbiddenFields } from "./secrets.js";
|
|
19
|
+
import type { CommerceEmitResult, CommerceEventBroker } from "./commerce-broker.js";
|
|
20
|
+
import {
|
|
21
|
+
APPROVED_MARKETING_ACTION_NAMES,
|
|
22
|
+
MARKETING_ACTION_NAME_PATTERN,
|
|
23
|
+
NAVIGATION_KINDS,
|
|
24
|
+
type ApprovedMarketingActionName,
|
|
25
|
+
type EventCorrelation,
|
|
26
|
+
type NavigationKind,
|
|
27
|
+
} from "./events.js";
|
|
28
|
+
|
|
29
|
+
export const TENANT_ACTION_REGISTRY_SCHEMA_VERSION = 1 as const;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Stable action identifiers are authored data, not labels. The `_mktg` suffix keeps
|
|
33
|
+
* marketing-program actions in a distinct namespace while allowing each tenant to
|
|
34
|
+
* register only the actions it actually renders.
|
|
35
|
+
*/
|
|
36
|
+
export const TENANT_MARKETING_ACTION_NAME_PATTERN = MARKETING_ACTION_NAME_PATTERN;
|
|
37
|
+
|
|
38
|
+
export interface TenantAnalyticsAction {
|
|
39
|
+
name: ApprovedMarketingActionName;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The explicit, server-loaded action allowlist for one tenant. */
|
|
43
|
+
export interface TenantActionRegistry {
|
|
44
|
+
schemaVersion: typeof TENANT_ACTION_REGISTRY_SCHEMA_VERSION;
|
|
45
|
+
actions: TenantAnalyticsAction[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate an authored tenant registry fail-closed. Action declarations carry only a
|
|
50
|
+
* semantic name: DOM binding details and free-form payloads are not part of the
|
|
51
|
+
* contract, so they are rejected as unknown fields.
|
|
52
|
+
*/
|
|
53
|
+
export function validateTenantActionRegistry(input: unknown): ContractResult<TenantActionRegistry> {
|
|
54
|
+
const v = new Violations();
|
|
55
|
+
const obj = reqObject(input, v, "");
|
|
56
|
+
if (!obj) return { ok: false, violations: v.items };
|
|
57
|
+
rejectUnknownKeys(obj, ["schemaVersion", "actions"], v, "");
|
|
58
|
+
reqLiteral(obj, "schemaVersion", TENANT_ACTION_REGISTRY_SCHEMA_VERSION, v, "");
|
|
59
|
+
|
|
60
|
+
const rawActions = reqArray(obj, "actions", v, "");
|
|
61
|
+
const actions: TenantAnalyticsAction[] = [];
|
|
62
|
+
if (rawActions) {
|
|
63
|
+
rawActions.forEach((raw, index) => {
|
|
64
|
+
const path = `actions[${index}]`;
|
|
65
|
+
const action = reqObject(raw, v, path);
|
|
66
|
+
if (!action) return;
|
|
67
|
+
rejectUnknownKeys(action, ["name"], v, path);
|
|
68
|
+
const name = reqString(action, "name", v, path);
|
|
69
|
+
if (name === undefined) return;
|
|
70
|
+
if (!TENANT_MARKETING_ACTION_NAME_PATTERN.test(name)) {
|
|
71
|
+
v.add(
|
|
72
|
+
`${path}.name`,
|
|
73
|
+
"format",
|
|
74
|
+
"Action name must be lowercase snake_case, end in _mktg, and contain no free-form text.",
|
|
75
|
+
);
|
|
76
|
+
} else if (!(APPROVED_MARKETING_ACTION_NAMES as readonly string[]).includes(name)) {
|
|
77
|
+
v.add(
|
|
78
|
+
`${path}.name`,
|
|
79
|
+
"unknown_enum",
|
|
80
|
+
"Action name is not in the approved v1 marketing action allowlist.",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
actions.push({ name: name as ApprovedMarketingActionName });
|
|
84
|
+
});
|
|
85
|
+
const seen = new Set<string>();
|
|
86
|
+
actions.forEach((action, index) => {
|
|
87
|
+
if (seen.has(action.name)) {
|
|
88
|
+
v.add(
|
|
89
|
+
`actions[${index}].name`,
|
|
90
|
+
"duplicate",
|
|
91
|
+
"Duplicate action name; duplicates are rejected.",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
seen.add(action.name);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const violation of findForbiddenFields(obj)) v.items.push(violation);
|
|
99
|
+
if (!v.ok) return { ok: false, violations: v.items };
|
|
100
|
+
return {
|
|
101
|
+
ok: true,
|
|
102
|
+
value: {
|
|
103
|
+
schemaVersion: TENANT_ACTION_REGISTRY_SCHEMA_VERSION,
|
|
104
|
+
actions,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** A minimized attribution object; raw URLs never cross the analytics contract. */
|
|
110
|
+
export interface ManagedPageAttribution {
|
|
111
|
+
referrer?: { origin: string };
|
|
112
|
+
utm?: {
|
|
113
|
+
source?: string;
|
|
114
|
+
medium?: string;
|
|
115
|
+
campaign?: string;
|
|
116
|
+
term?: string;
|
|
117
|
+
content?: string;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface AttributionSourceInput {
|
|
122
|
+
/** Explicit renderer/runtime input. This module never reads document.referrer. */
|
|
123
|
+
referrerUrl?: string | null;
|
|
124
|
+
/** Explicit route URL used only to read allowlisted UTM keys. */
|
|
125
|
+
routeUrl?: string | null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const UTM_QUERY_FIELDS = [
|
|
129
|
+
["utm_source", "source"],
|
|
130
|
+
["utm_medium", "medium"],
|
|
131
|
+
["utm_campaign", "campaign"],
|
|
132
|
+
["utm_term", "term"],
|
|
133
|
+
["utm_content", "content"],
|
|
134
|
+
] as const;
|
|
135
|
+
const SAFE_CAMPAIGN_TOKEN = /^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$/;
|
|
136
|
+
const PROTECTED_CAMPAIGN_TOKEN =
|
|
137
|
+
/(?:@|\d{7,}|(?:^|[._~-])(?:bearer|password|secret|token|authorization|preview)(?:$|[._~-]))/i;
|
|
138
|
+
|
|
139
|
+
function safeCampaignToken(value: string): string | undefined {
|
|
140
|
+
return SAFE_CAMPAIGN_TOKEN.test(value) && !PROTECTED_CAMPAIGN_TOKEN.test(value)
|
|
141
|
+
? value
|
|
142
|
+
: undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Reduce explicit URL inputs to an origin-only referrer and the five standard UTM
|
|
147
|
+
* tokens. Query keys outside the UTM allowlist, duplicate keys, unsafe free-form
|
|
148
|
+
* values, credentials, paths, and fragments are discarded. No browser global is read.
|
|
149
|
+
*/
|
|
150
|
+
export function minimizePageAttribution(input: AttributionSourceInput): ManagedPageAttribution | undefined {
|
|
151
|
+
const result: ManagedPageAttribution = {};
|
|
152
|
+
|
|
153
|
+
if (input.referrerUrl) {
|
|
154
|
+
try {
|
|
155
|
+
const referrer = new URL(input.referrerUrl);
|
|
156
|
+
if (
|
|
157
|
+
["http:", "https:"].includes(referrer.protocol) &&
|
|
158
|
+
referrer.username === "" &&
|
|
159
|
+
referrer.password === ""
|
|
160
|
+
) {
|
|
161
|
+
result.referrer = { origin: referrer.origin };
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
// Invalid referrer input is omitted, never forwarded raw.
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (input.routeUrl) {
|
|
169
|
+
try {
|
|
170
|
+
const route = new URL(input.routeUrl, "https://route.invalid");
|
|
171
|
+
const utm: NonNullable<ManagedPageAttribution["utm"]> = {};
|
|
172
|
+
for (const [queryName, field] of UTM_QUERY_FIELDS) {
|
|
173
|
+
const values = route.searchParams.getAll(queryName);
|
|
174
|
+
if (values.length !== 1) continue;
|
|
175
|
+
const value = safeCampaignToken(values[0] ?? "");
|
|
176
|
+
if (value !== undefined) utm[field] = value;
|
|
177
|
+
}
|
|
178
|
+
if (Object.keys(utm).length > 0) result.utm = utm;
|
|
179
|
+
} catch {
|
|
180
|
+
// Invalid route input contributes no attribution.
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return result.referrer || result.utm ? result : undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export type ManagedAnalyticsEmitResult =
|
|
188
|
+
| CommerceEmitResult
|
|
189
|
+
| {
|
|
190
|
+
ok: true;
|
|
191
|
+
emitted: false;
|
|
192
|
+
skipReason: "consent-ineligible";
|
|
193
|
+
deliveries: [];
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
function consentIneligible(): ManagedAnalyticsEmitResult {
|
|
197
|
+
return { ok: true, emitted: false, skipReason: "consent-ineligible", deliveries: [] };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface ManagedPageViewInput {
|
|
201
|
+
logicalId: string;
|
|
202
|
+
path: string;
|
|
203
|
+
correlation: EventCorrelation;
|
|
204
|
+
attribution?: ManagedPageAttribution;
|
|
205
|
+
sequence?: number;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Emit one page view at a semantic document/route commit. The path and optional
|
|
210
|
+
* minimized attribution are the complete data allowlist; title and DOM state are not
|
|
211
|
+
* accepted by this managed seam.
|
|
212
|
+
*/
|
|
213
|
+
export function emitManagedPageView(
|
|
214
|
+
broker: CommerceEventBroker,
|
|
215
|
+
input: ManagedPageViewInput,
|
|
216
|
+
): ManagedAnalyticsEmitResult {
|
|
217
|
+
if (!broker.isConsentEligible("analytics")) return consentIneligible();
|
|
218
|
+
return broker.emit({
|
|
219
|
+
type: "storefront.page.viewed.v2",
|
|
220
|
+
logicalId: input.logicalId,
|
|
221
|
+
subject: "page",
|
|
222
|
+
correlation: input.correlation,
|
|
223
|
+
data: {
|
|
224
|
+
path: input.path,
|
|
225
|
+
...(input.attribution ? { attribution: input.attribution } : {}),
|
|
226
|
+
},
|
|
227
|
+
...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface ManagedActionActivationInput {
|
|
232
|
+
logicalId: string;
|
|
233
|
+
name: string;
|
|
234
|
+
correlation: EventCorrelation;
|
|
235
|
+
sequence?: number;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Emit one explicitly registered semantic action. The caller supplies no tenant,
|
|
240
|
+
* environment, selector, label, destination, or arbitrary properties; the emitted
|
|
241
|
+
* data object is exactly `{ name }`.
|
|
242
|
+
*/
|
|
243
|
+
export function emitManagedActionActivated(
|
|
244
|
+
broker: CommerceEventBroker,
|
|
245
|
+
input: ManagedActionActivationInput,
|
|
246
|
+
): ManagedAnalyticsEmitResult {
|
|
247
|
+
if (!broker.isConsentEligible("analytics")) return consentIneligible();
|
|
248
|
+
return broker.emit({
|
|
249
|
+
type: "storefront.action.activated.v1",
|
|
250
|
+
logicalId: input.logicalId,
|
|
251
|
+
subject: "action",
|
|
252
|
+
correlation: input.correlation,
|
|
253
|
+
data: { name: input.name },
|
|
254
|
+
...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Where the page-view paired with a completed navigation is emitted. */
|
|
259
|
+
export type NavigationPageViewOwner = "document-bootstrap" | "navigation-commit" | "none";
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Page/navigation compatibility policy:
|
|
263
|
+
* - a full document navigation is counted once by the destination document bootstrap;
|
|
264
|
+
* - a committed same-document back/forward or redirect is counted at route commit;
|
|
265
|
+
* - a hash move emits the navigation lifecycle event but is not a new page view;
|
|
266
|
+
* - an aborted navigation emits neither event.
|
|
267
|
+
*/
|
|
268
|
+
export function pageViewOwnerForNavigation(
|
|
269
|
+
kind: NavigationKind,
|
|
270
|
+
aborted = false,
|
|
271
|
+
): NavigationPageViewOwner {
|
|
272
|
+
if (aborted) return "none";
|
|
273
|
+
if (!(NAVIGATION_KINDS as readonly unknown[]).includes(kind)) return "none";
|
|
274
|
+
if (kind === "hash") return "none";
|
|
275
|
+
if (kind === "document") return "document-bootstrap";
|
|
276
|
+
return "navigation-commit";
|
|
277
|
+
}
|