@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/astro.config.mjs +3 -3
- package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +8 -3
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +12 -14
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +18 -10
- package/apps/storefront/src/layouts/Layout.astro +100 -20
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
- package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
- package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/apps/adminService.ts +10 -0
- package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +264 -10
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/blog/collection.ts +114 -17
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +76 -9
- package/apps/storefront/src/lib/blog/types.ts +9 -9
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +12 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +18 -6
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
- package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
- package/apps/storefront/src/lib/newsletter/send.ts +3 -3
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/seo/alternates.ts +1 -1
- package/apps/storefront/src/lib/social/notify.ts +4 -4
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +203 -69
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +9 -4
- package/apps/storefront/src/middleware/index.ts +273 -51
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +16 -2
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
- package/apps/storefront/src/pages/blog/[slug].astro +5 -5
- package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
- package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
- package/apps/storefront/src/pages/blog/index.astro +2 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
- package/apps/storefront/src/pages/cockpit.astro +61 -1362
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +42 -31
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +8 -3
- package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +6 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +6 -2
- package/apps/storefront/src/pages/style-guide/index.astro +19 -5
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
- package/apps/storefront/src/pages/the-build/index.astro +2 -2
- package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
- package/apps/storefront/src/styles/admin.css +27 -1
- package/apps/storefront/src/themes/tenants/index.ts +19 -7
- package/apps/storefront/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +41 -37
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-runtime.ts +32 -1
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +16 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +5 -5
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +136 -9
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/build/copy-tenant-assets.mjs +12 -5
- package/scripts/dev/ai-edit.mjs +2 -1
- package/scripts/dev/file-browser.mjs +2 -2
- package/scripts/dev/file-writer.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/tenant-source-root.mjs +3 -2
- package/scripts/tot-dev.mjs +5 -4
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS4-only consent/correlation broker for deterministic local fixtures.
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately not a shared runtime, browser hook, storage adapter, or
|
|
5
|
+
* destination integration. It composes the already-frozen WS4 contracts at a
|
|
6
|
+
* trusted server boundary and permits delivery only through LocalFakeDispatchCore.
|
|
7
|
+
* A caller supplies atomic correlation redemption and current consent; production
|
|
8
|
+
* persistence, lifecycle, and destination authority remain outside this namespace.
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
resolveCheckoutCorrelation,
|
|
12
|
+
type ConsentSnapshotReference,
|
|
13
|
+
type CorrelationOutcome,
|
|
14
|
+
} from "../correlation/contract.js";
|
|
15
|
+
import {
|
|
16
|
+
LocalFakeDispatchCore,
|
|
17
|
+
type LocalDispatchResult,
|
|
18
|
+
type RedactedDispatchDiagnostic,
|
|
19
|
+
} from "../dispatch/core.js";
|
|
20
|
+
import {
|
|
21
|
+
validateRegulatedCommerceIntent,
|
|
22
|
+
type RegulatedCommerceIntent,
|
|
23
|
+
} from "../events/contract.js";
|
|
24
|
+
|
|
25
|
+
export type LocalCommerceBrokerResult =
|
|
26
|
+
| {
|
|
27
|
+
readonly outcome: "delivered" | "deduplicated" | "outage";
|
|
28
|
+
readonly correlation: "attributed";
|
|
29
|
+
readonly diagnostic: RedactedDispatchDiagnostic;
|
|
30
|
+
}
|
|
31
|
+
| {
|
|
32
|
+
readonly outcome: "suppressed";
|
|
33
|
+
readonly correlation: Exclude<CorrelationOutcome, "attributed">;
|
|
34
|
+
readonly diagnostic: RedactedBrokerDiagnostic;
|
|
35
|
+
}
|
|
36
|
+
| {
|
|
37
|
+
readonly outcome: "rejected";
|
|
38
|
+
readonly reason: "invalid-server-intent";
|
|
39
|
+
readonly diagnostic: RedactedBrokerDiagnostic;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** No identifier, consent snapshot id, money, receipt, or payload may escape here. */
|
|
43
|
+
export interface RedactedBrokerDiagnostic {
|
|
44
|
+
readonly status: "suppressed" | "rejected";
|
|
45
|
+
readonly source?: "verified_server_order";
|
|
46
|
+
readonly correlation?: CorrelationOutcome;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface LocalCommerceBrokerInput {
|
|
50
|
+
/** Server-authoritative semantic intent; browser-sourced intents are refused. */
|
|
51
|
+
readonly intent: unknown;
|
|
52
|
+
/** Untrusted, opaque checkout material. It is never retained by this broker. */
|
|
53
|
+
readonly correlationRecord: unknown;
|
|
54
|
+
/** Trusted consent state sampled at this server boundary. */
|
|
55
|
+
readonly currentConsent: ConsentSnapshotReference;
|
|
56
|
+
/** Trusted server wall clock in epoch milliseconds. */
|
|
57
|
+
readonly now: number;
|
|
58
|
+
/** Atomic one-time correlation redemption supplied by the owning persistence layer. */
|
|
59
|
+
readonly consumeOnce: (correlationId: string) => boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function redacted(
|
|
63
|
+
status: RedactedBrokerDiagnostic["status"],
|
|
64
|
+
correlation?: CorrelationOutcome,
|
|
65
|
+
source?: "verified_server_order",
|
|
66
|
+
): RedactedBrokerDiagnostic {
|
|
67
|
+
return { status, ...(source ? { source } : {}), ...(correlation ? { correlation } : {}) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function unattributedOutcomeForCurrentConsent(currentConsent: ConsentSnapshotReference): Exclude<CorrelationOutcome, "attributed"> {
|
|
71
|
+
return currentConsent.state === "withdrawn"
|
|
72
|
+
? "unattributed-current-consent-withdrawn"
|
|
73
|
+
: "unattributed-current-consent-denied";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function dispatchResult(result: LocalDispatchResult): LocalCommerceBrokerResult {
|
|
77
|
+
if (result.outcome === "delivered" || result.outcome === "deduplicated" || result.outcome === "outage") {
|
|
78
|
+
return { outcome: result.outcome, correlation: "attributed", diagnostic: result.diagnostic };
|
|
79
|
+
}
|
|
80
|
+
// An attributed, contract-valid server intent must never be suppressed or rejected
|
|
81
|
+
// by this server-only fake dispatcher. Preserve fail-closed behavior if violated.
|
|
82
|
+
return {
|
|
83
|
+
outcome: "suppressed",
|
|
84
|
+
correlation: "unattributed-malformed",
|
|
85
|
+
diagnostic: redacted("suppressed", "unattributed-malformed", "verified_server_order"),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* In-memory deterministic composition for local fixtures. `accepted` lets an outage
|
|
91
|
+
* retry reuse the already-spent correlation without re-consuming it; every retry
|
|
92
|
+
* still checks current consent before it reaches the fake dispatcher.
|
|
93
|
+
*/
|
|
94
|
+
export class LocalConsentDedupeBroker {
|
|
95
|
+
private readonly accepted = new Map<string, RegulatedCommerceIntent>();
|
|
96
|
+
|
|
97
|
+
constructor(private readonly dispatcher: LocalFakeDispatchCore) {
|
|
98
|
+
if (!(dispatcher instanceof LocalFakeDispatchCore)) {
|
|
99
|
+
throw new Error("local commerce broker requires LocalFakeDispatchCore");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
dispatch(input: LocalCommerceBrokerInput): LocalCommerceBrokerResult {
|
|
104
|
+
const parsed = validateRegulatedCommerceIntent(input.intent);
|
|
105
|
+
if (!parsed.ok || parsed.value.source !== "verified_server_order") {
|
|
106
|
+
return { outcome: "rejected", reason: "invalid-server-intent", diagnostic: redacted("rejected") };
|
|
107
|
+
}
|
|
108
|
+
const serverIntent = parsed.value;
|
|
109
|
+
const prior = this.accepted.get(serverIntent.id);
|
|
110
|
+
if (prior) {
|
|
111
|
+
if (input.currentConsent.state !== "granted") {
|
|
112
|
+
const correlation = unattributedOutcomeForCurrentConsent(input.currentConsent);
|
|
113
|
+
return { outcome: "suppressed", correlation, diagnostic: redacted("suppressed", correlation, "verified_server_order") };
|
|
114
|
+
}
|
|
115
|
+
return dispatchResult(this.dispatcher.dispatch(prior));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const resolution = resolveCheckoutCorrelation({
|
|
119
|
+
record: input.correlationRecord,
|
|
120
|
+
currentConsent: input.currentConsent,
|
|
121
|
+
now: input.now,
|
|
122
|
+
consumeOnce: input.consumeOnce,
|
|
123
|
+
});
|
|
124
|
+
if (resolution.outcome !== "attributed") {
|
|
125
|
+
const correlation = resolution.outcome as Exclude<CorrelationOutcome, "attributed">;
|
|
126
|
+
return {
|
|
127
|
+
outcome: "suppressed",
|
|
128
|
+
correlation,
|
|
129
|
+
diagnostic: redacted("suppressed", correlation, "verified_server_order"),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const joined = validateRegulatedCommerceIntent({
|
|
134
|
+
...serverIntent,
|
|
135
|
+
attribution: { state: "attributed", correlationId: resolution.correlationId },
|
|
136
|
+
consent: { state: "granted", snapshotId: resolution.currentConsentSnapshotId },
|
|
137
|
+
});
|
|
138
|
+
if (!joined.ok) {
|
|
139
|
+
return { outcome: "rejected", reason: "invalid-server-intent", diagnostic: redacted("rejected", undefined, "verified_server_order") };
|
|
140
|
+
}
|
|
141
|
+
this.accepted.set(joined.value.id, joined.value);
|
|
142
|
+
return dispatchResult(this.dispatcher.dispatch(joined.value));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS4's isolated checkout consent/correlation contract.
|
|
3
|
+
*
|
|
4
|
+
* This namespace deliberately contains no runtime wiring, storage, browser globals,
|
|
5
|
+
* or destination code. It defines the small, opaque record a future approved adapter
|
|
6
|
+
* may carry between checkout and a trusted server resolver. The record is never an
|
|
7
|
+
* authorization capability: it contains no token, secret, purchaser identifier, or
|
|
8
|
+
* raw consent payload. A resolver must still use its own one-time redemption store.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const CHECKOUT_CORRELATION_SCHEMA_VERSION = "1" as const;
|
|
12
|
+
|
|
13
|
+
export const CONSENT_STATES = ["granted", "denied", "withdrawn", "not-collected"] as const;
|
|
14
|
+
export type ConsentState = (typeof CONSENT_STATES)[number];
|
|
15
|
+
|
|
16
|
+
export const CORRELATION_OUTCOMES = [
|
|
17
|
+
"attributed",
|
|
18
|
+
"unattributed-missing",
|
|
19
|
+
"unattributed-malformed",
|
|
20
|
+
"unattributed-issued-without-consent",
|
|
21
|
+
"unattributed-current-consent-denied",
|
|
22
|
+
"unattributed-current-consent-withdrawn",
|
|
23
|
+
"unattributed-expired",
|
|
24
|
+
"unattributed-replayed",
|
|
25
|
+
] as const;
|
|
26
|
+
export type CorrelationOutcome = (typeof CORRELATION_OUTCOMES)[number];
|
|
27
|
+
|
|
28
|
+
/** An opaque, non-secret identifier. Its format intentionally cannot carry PII. */
|
|
29
|
+
export interface ConsentSnapshotReference {
|
|
30
|
+
state: ConsentState;
|
|
31
|
+
snapshotId: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The sole browser-safe checkout material. `correlationId` is an opaque thread
|
|
36
|
+
* identifier, not a bearer credential; a trusted server independently enforces its
|
|
37
|
+
* one-time redemption. No raw consent choices or customer data may be added here.
|
|
38
|
+
*/
|
|
39
|
+
export interface CheckoutCorrelationRecord {
|
|
40
|
+
schemaVersion: typeof CHECKOUT_CORRELATION_SCHEMA_VERSION;
|
|
41
|
+
correlationId: string;
|
|
42
|
+
issuedAt: string;
|
|
43
|
+
expiresAt: string;
|
|
44
|
+
consent: ConsentSnapshotReference;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type CorrelationParseResult =
|
|
48
|
+
| { ok: true; value: CheckoutCorrelationRecord }
|
|
49
|
+
| { ok: false; reason: "missing" | "malformed" };
|
|
50
|
+
|
|
51
|
+
export interface ResolveCheckoutCorrelationInput {
|
|
52
|
+
/** Browser-provided material; always treated as untrusted. */
|
|
53
|
+
record: unknown;
|
|
54
|
+
/** Trusted server wall clock in epoch milliseconds. */
|
|
55
|
+
now: number;
|
|
56
|
+
/** The current trusted consent state, read at resolution time. */
|
|
57
|
+
currentConsent: ConsentSnapshotReference;
|
|
58
|
+
/**
|
|
59
|
+
* Atomically mark the opaque id as spent. This MUST return false for an id already
|
|
60
|
+
* spent. It is intentionally injected so this contract neither owns nor reaches
|
|
61
|
+
* into the WS3 runtime/storage boundary.
|
|
62
|
+
*/
|
|
63
|
+
consumeOnce: (correlationId: string) => boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CorrelationResolution {
|
|
67
|
+
outcome: CorrelationOutcome;
|
|
68
|
+
/** Present only for an attributed record and suitable only for a trusted join. */
|
|
69
|
+
correlationId?: string;
|
|
70
|
+
/** The issued and checked consent references support minimised audit evidence. */
|
|
71
|
+
issuedConsentSnapshotId?: string;
|
|
72
|
+
currentConsentSnapshotId?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const RECORD_KEYS = ["schemaVersion", "correlationId", "issuedAt", "expiresAt", "consent"] as const;
|
|
76
|
+
const CONSENT_KEYS = ["state", "snapshotId"] as const;
|
|
77
|
+
const OPAQUE_ID = /^[a-z][a-z0-9_-]{15,79}$/;
|
|
78
|
+
const SENSITIVE_IDENTIFIER_FRAGMENT = /(?:email|phone|address|name|token|secret|password|cookie|session|bearer|jwt)/i;
|
|
79
|
+
|
|
80
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
81
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isExactObject(value: unknown, keys: readonly string[]): value is Record<string, unknown> {
|
|
85
|
+
if (!isPlainObject(value)) return false;
|
|
86
|
+
const actual = Object.keys(value);
|
|
87
|
+
return actual.length === keys.length && actual.every((key) => keys.includes(key));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isOpaqueId(value: unknown): value is string {
|
|
91
|
+
return typeof value === "string" && OPAQUE_ID.test(value) && !SENSITIVE_IDENTIFIER_FRAGMENT.test(value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isIsoInstant(value: unknown): value is string {
|
|
95
|
+
if (typeof value !== "string") return false;
|
|
96
|
+
const ms = Date.parse(value);
|
|
97
|
+
return Number.isFinite(ms) && new Date(ms).toISOString() === value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parseConsentReference(value: unknown): ConsentSnapshotReference | null {
|
|
101
|
+
if (!isExactObject(value, CONSENT_KEYS)) return null;
|
|
102
|
+
if (typeof value.state !== "string" || !(CONSENT_STATES as readonly string[]).includes(value.state)) return null;
|
|
103
|
+
if (!isOpaqueId(value.snapshotId)) return null;
|
|
104
|
+
return { state: value.state as ConsentState, snapshotId: value.snapshotId };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Validate browser material as a closed, PII-free contract. Unknown fields—including
|
|
109
|
+
* `email`, `name`, `address`, `token`, or `secret`—are rejected rather than silently
|
|
110
|
+
* carried forward. Prototype-bearing objects are rejected too.
|
|
111
|
+
*/
|
|
112
|
+
export function parseCheckoutCorrelationRecord(value: unknown): CorrelationParseResult {
|
|
113
|
+
if (value === null || value === undefined) return { ok: false, reason: "missing" };
|
|
114
|
+
if (!isExactObject(value, RECORD_KEYS)) return { ok: false, reason: "malformed" };
|
|
115
|
+
if (value.schemaVersion !== CHECKOUT_CORRELATION_SCHEMA_VERSION) return { ok: false, reason: "malformed" };
|
|
116
|
+
if (!isOpaqueId(value.correlationId) || !isIsoInstant(value.issuedAt) || !isIsoInstant(value.expiresAt)) {
|
|
117
|
+
return { ok: false, reason: "malformed" };
|
|
118
|
+
}
|
|
119
|
+
const issuedAt = Date.parse(value.issuedAt);
|
|
120
|
+
const expiresAt = Date.parse(value.expiresAt);
|
|
121
|
+
if (expiresAt <= issuedAt) return { ok: false, reason: "malformed" };
|
|
122
|
+
const consent = parseConsentReference(value.consent);
|
|
123
|
+
if (!consent) return { ok: false, reason: "malformed" };
|
|
124
|
+
return {
|
|
125
|
+
ok: true,
|
|
126
|
+
value: {
|
|
127
|
+
schemaVersion: CHECKOUT_CORRELATION_SCHEMA_VERSION,
|
|
128
|
+
correlationId: value.correlationId,
|
|
129
|
+
issuedAt: value.issuedAt,
|
|
130
|
+
expiresAt: value.expiresAt,
|
|
131
|
+
consent,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Construct a validated browser record from already-opaque values. This does not mint
|
|
138
|
+
* an id and therefore cannot accidentally become a credential issuer or runtime seam.
|
|
139
|
+
*/
|
|
140
|
+
export function createCheckoutCorrelationRecord(value: CheckoutCorrelationRecord): CorrelationParseResult {
|
|
141
|
+
return parseCheckoutCorrelationRecord(value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resolve a record at the trusted server boundary. Issuance consent is immutable:
|
|
146
|
+
* granting consent later cannot retroactively attribute a denied/not-collected record.
|
|
147
|
+
* Current denial or withdrawal also wins, which makes consent updates fail closed.
|
|
148
|
+
*/
|
|
149
|
+
export function resolveCheckoutCorrelation(input: ResolveCheckoutCorrelationInput): CorrelationResolution {
|
|
150
|
+
const parsed = parseCheckoutCorrelationRecord(input.record);
|
|
151
|
+
if (!parsed.ok) {
|
|
152
|
+
return { outcome: parsed.reason === "missing" ? "unattributed-missing" : "unattributed-malformed" };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const record = parsed.value;
|
|
156
|
+
if (record.consent.state !== "granted") {
|
|
157
|
+
return { outcome: "unattributed-issued-without-consent" };
|
|
158
|
+
}
|
|
159
|
+
if (input.currentConsent.state === "withdrawn") {
|
|
160
|
+
return { outcome: "unattributed-current-consent-withdrawn" };
|
|
161
|
+
}
|
|
162
|
+
if (input.currentConsent.state !== "granted") {
|
|
163
|
+
return { outcome: "unattributed-current-consent-denied" };
|
|
164
|
+
}
|
|
165
|
+
if (!Number.isFinite(input.now) || input.now >= Date.parse(record.expiresAt)) {
|
|
166
|
+
return { outcome: "unattributed-expired" };
|
|
167
|
+
}
|
|
168
|
+
if (!input.consumeOnce(record.correlationId)) {
|
|
169
|
+
return { outcome: "unattributed-replayed" };
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
outcome: "attributed",
|
|
173
|
+
correlationId: record.correlationId,
|
|
174
|
+
issuedConsentSnapshotId: record.consent.snapshotId,
|
|
175
|
+
currentConsentSnapshotId: input.currentConsent.snapshotId,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS4-only bridge from the deterministic local dispatcher to the redacted admin
|
|
3
|
+
* diagnostics read model. It neither dispatches nor retains any raw event data.
|
|
4
|
+
*/
|
|
5
|
+
import { type LocalDispatchResult } from "../dispatch/core.js";
|
|
6
|
+
import {
|
|
7
|
+
projectCommerceDiagnostics,
|
|
8
|
+
type RedactedCommerceDiagnostics,
|
|
9
|
+
} from "./read-model.js";
|
|
10
|
+
|
|
11
|
+
function localOutcome(result: LocalDispatchResult): { outcome: "not_attempted" | "delivered" | "duplicate" | "outage" | "rejected"; error?: "invalid_intent" | "invalid_channel" | "local_outage" } {
|
|
12
|
+
if (result.outcome === "delivered") return { outcome: "delivered" };
|
|
13
|
+
if (result.outcome === "deduplicated") return { outcome: "duplicate" };
|
|
14
|
+
if (result.outcome === "outage") return { outcome: "outage", error: "local_outage" };
|
|
15
|
+
if (result.outcome === "rejected") {
|
|
16
|
+
return { outcome: "rejected", error: result.reason === "invalid-intent" ? "invalid_intent" : "invalid_channel" };
|
|
17
|
+
}
|
|
18
|
+
return { outcome: "not_attempted" };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Converts a local-only dispatch result to categories safe for a future admin surface. */
|
|
22
|
+
export function projectLocalDispatchDiagnostics(intent: unknown, result: LocalDispatchResult): RedactedCommerceDiagnostics {
|
|
23
|
+
return projectCommerceDiagnostics({ intent, delivery: localOutcome(result) });
|
|
24
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** WS4-only redacted diagnostics projection; no route, storage, or runtime wiring. */
|
|
2
|
+
import {
|
|
3
|
+
validateRegulatedCommerceIntent,
|
|
4
|
+
type CommerceSource,
|
|
5
|
+
type ConsentState,
|
|
6
|
+
type RegulatedCommerceEventKind,
|
|
7
|
+
} from "../events/contract.js";
|
|
8
|
+
|
|
9
|
+
export const COMMERCE_DIAGNOSTICS_SCHEMA_VERSION = "1" as const;
|
|
10
|
+
export type AdminDeliveryState = "not_attempted" | "delivered" | "duplicate" | "outage" | "rejected";
|
|
11
|
+
export type AdminDedupeState = "not_observed" | "accepted" | "duplicate";
|
|
12
|
+
export type AdminErrorClass = "none" | "invalid_intent" | "invalid_channel" | "local_outage";
|
|
13
|
+
|
|
14
|
+
export interface CommerceDiagnosticsInput {
|
|
15
|
+
intent: unknown;
|
|
16
|
+
/** A closed local outcome summary, never a receipt or raw destination response. */
|
|
17
|
+
delivery?: { outcome: AdminDeliveryState; error?: AdminErrorClass };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RedactedCommerceDiagnostics {
|
|
21
|
+
schemaVersion: typeof COMMERCE_DIAGNOSTICS_SCHEMA_VERSION;
|
|
22
|
+
validIntent: boolean;
|
|
23
|
+
source: CommerceSource | "unknown";
|
|
24
|
+
kind: RegulatedCommerceEventKind | "unknown";
|
|
25
|
+
consent: ConsentState | "unknown";
|
|
26
|
+
attribution: "attributed" | "unattributed" | "unknown";
|
|
27
|
+
fields: readonly ("event_kind" | "source" | "consent" | "attribution" | "money_present")[];
|
|
28
|
+
delivery: AdminDeliveryState;
|
|
29
|
+
dedupe: AdminDedupeState;
|
|
30
|
+
error: AdminErrorClass;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const FIELDS = ["event_kind", "source", "consent", "attribution", "money_present"] as const;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Project the semantic intent and local outcome into a deliberately redacted view.
|
|
37
|
+
* IDs, provenance, correlation/consent references, money values, receipts, destinations,
|
|
38
|
+
* payloads, URLs, credentials, and raw errors are structurally absent from this model.
|
|
39
|
+
*/
|
|
40
|
+
export function projectCommerceDiagnostics(input: CommerceDiagnosticsInput): RedactedCommerceDiagnostics {
|
|
41
|
+
const parsed = validateRegulatedCommerceIntent(input.intent);
|
|
42
|
+
const delivery = input.delivery?.outcome ?? "not_attempted";
|
|
43
|
+
const error = input.delivery?.error ?? (parsed.ok ? "none" : "invalid_intent");
|
|
44
|
+
if (!parsed.ok) return {
|
|
45
|
+
schemaVersion: COMMERCE_DIAGNOSTICS_SCHEMA_VERSION,
|
|
46
|
+
validIntent: false,
|
|
47
|
+
source: "unknown",
|
|
48
|
+
kind: "unknown",
|
|
49
|
+
consent: "unknown",
|
|
50
|
+
attribution: "unknown",
|
|
51
|
+
fields: [...FIELDS],
|
|
52
|
+
delivery: delivery === "not_attempted" ? "rejected" : delivery,
|
|
53
|
+
dedupe: "not_observed",
|
|
54
|
+
error,
|
|
55
|
+
};
|
|
56
|
+
const intent = parsed.value;
|
|
57
|
+
return {
|
|
58
|
+
schemaVersion: COMMERCE_DIAGNOSTICS_SCHEMA_VERSION,
|
|
59
|
+
validIntent: true,
|
|
60
|
+
source: intent.source,
|
|
61
|
+
kind: intent.kind,
|
|
62
|
+
consent: intent.consent.state,
|
|
63
|
+
attribution: intent.attribution.state === "attributed" ? "attributed" : "unattributed",
|
|
64
|
+
fields: [...FIELDS],
|
|
65
|
+
delivery,
|
|
66
|
+
dedupe: delivery === "delivered" ? "accepted" : delivery === "duplicate" ? "duplicate" : "not_observed",
|
|
67
|
+
error,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS4-only deterministic dispatch policy. This core can target only the local fake
|
|
3
|
+
* destination; an approved future adapter must be introduced at the separately
|
|
4
|
+
* gated runtime boundary.
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
validateRegulatedCommerceIntent,
|
|
8
|
+
type CommerceSource,
|
|
9
|
+
type RegulatedCommerceIntent,
|
|
10
|
+
} from "../events/contract.js";
|
|
11
|
+
import {
|
|
12
|
+
DeterministicFakeDestination,
|
|
13
|
+
type LocalFakeChannel,
|
|
14
|
+
type LocalFakeDeliveryResult,
|
|
15
|
+
} from "../testing/fake-destination.js";
|
|
16
|
+
|
|
17
|
+
export const LOCAL_DISPATCH_POLICIES = ["browser", "server", "dual"] as const;
|
|
18
|
+
export type LocalDispatchPolicy = (typeof LOCAL_DISPATCH_POLICIES)[number];
|
|
19
|
+
|
|
20
|
+
export interface LocalDispatchCoreConfig {
|
|
21
|
+
policy: LocalDispatchPolicy;
|
|
22
|
+
/** Additional local attempts after an `outage`; never schedules or performs I/O. */
|
|
23
|
+
maxOutageRetries: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type RedactedDispatchStatus = "delivered" | "deduplicated" | "outage" | "rejected" | "suppressed";
|
|
27
|
+
export interface RedactedDispatchAttempt {
|
|
28
|
+
channel: LocalFakeChannel;
|
|
29
|
+
outcome: LocalFakeDeliveryResult["outcome"];
|
|
30
|
+
attempts: number;
|
|
31
|
+
}
|
|
32
|
+
export interface RedactedDispatchDiagnostic {
|
|
33
|
+
policy: LocalDispatchPolicy;
|
|
34
|
+
status: RedactedDispatchStatus;
|
|
35
|
+
source?: CommerceSource;
|
|
36
|
+
attempts: readonly RedactedDispatchAttempt[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type LocalDispatchResult =
|
|
40
|
+
| { outcome: "delivered" | "deduplicated" | "outage"; diagnostic: RedactedDispatchDiagnostic }
|
|
41
|
+
| { outcome: "rejected"; reason: "invalid-intent" | "policy-source-mismatch"; diagnostic: RedactedDispatchDiagnostic }
|
|
42
|
+
| { outcome: "suppressed"; reason: "consent-not-granted"; diagnostic: RedactedDispatchDiagnostic };
|
|
43
|
+
|
|
44
|
+
function isConfig(config: LocalDispatchCoreConfig): boolean {
|
|
45
|
+
return (
|
|
46
|
+
(LOCAL_DISPATCH_POLICIES as readonly string[]).includes(config.policy) &&
|
|
47
|
+
Number.isSafeInteger(config.maxOutageRetries) &&
|
|
48
|
+
config.maxOutageRetries >= 0 &&
|
|
49
|
+
config.maxOutageRetries <= 10
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function channelsFor(policy: LocalDispatchPolicy): readonly LocalFakeChannel[] {
|
|
54
|
+
if (policy === "browser") return ["browser"];
|
|
55
|
+
if (policy === "server") return ["server"];
|
|
56
|
+
return ["browser", "server"];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function policyAllows(policy: LocalDispatchPolicy, source: CommerceSource): boolean {
|
|
60
|
+
return policy === "dual" ||
|
|
61
|
+
(policy === "browser" && source === "accepted_browser_operation") ||
|
|
62
|
+
(policy === "server" && source === "verified_server_order");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function emptyDiagnostic(policy: LocalDispatchPolicy, status: RedactedDispatchStatus, source?: CommerceSource): RedactedDispatchDiagnostic {
|
|
66
|
+
return { policy, status, ...(source ? { source } : {}), attempts: [] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A deterministic, local-only policy core. The runtime check deliberately excludes
|
|
71
|
+
* structurally similar objects so this class cannot be pointed at a real destination.
|
|
72
|
+
*/
|
|
73
|
+
export class LocalFakeDispatchCore {
|
|
74
|
+
private readonly diagnosticLog: RedactedDispatchDiagnostic[] = [];
|
|
75
|
+
|
|
76
|
+
constructor(
|
|
77
|
+
private readonly config: LocalDispatchCoreConfig,
|
|
78
|
+
private readonly destination: DeterministicFakeDestination,
|
|
79
|
+
) {
|
|
80
|
+
if (!isConfig(config)) throw new Error("invalid local dispatch core config");
|
|
81
|
+
if (!(destination instanceof DeterministicFakeDestination)) throw new Error("local dispatch core requires deterministic fake destination");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
dispatch(intentInput: unknown): LocalDispatchResult {
|
|
85
|
+
const parsed = validateRegulatedCommerceIntent(intentInput);
|
|
86
|
+
if (!parsed.ok) return this.reject("invalid-intent");
|
|
87
|
+
const intent = parsed.value;
|
|
88
|
+
if (intent.consent.state !== "granted") return this.suppress(intent);
|
|
89
|
+
if (!policyAllows(this.config.policy, intent.source)) return this.reject("policy-source-mismatch", intent.source);
|
|
90
|
+
|
|
91
|
+
const attempts = channelsFor(this.config.policy).map((channel) => this.deliverWithLocalRetries(channel, intent));
|
|
92
|
+
const outcomes = attempts.map(({ outcome }) => outcome);
|
|
93
|
+
const status: RedactedDispatchStatus = outcomes.includes("delivered")
|
|
94
|
+
? "delivered"
|
|
95
|
+
: outcomes.every((outcome) => outcome === "duplicate")
|
|
96
|
+
? "deduplicated"
|
|
97
|
+
: outcomes.some((outcome) => outcome === "outage")
|
|
98
|
+
? "outage"
|
|
99
|
+
: "rejected";
|
|
100
|
+
const diagnostic = { policy: this.config.policy, status, source: intent.source, attempts };
|
|
101
|
+
this.diagnosticLog.push(diagnostic);
|
|
102
|
+
return { outcome: status as "delivered" | "deduplicated" | "outage", diagnostic };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Defensive, redacted history: no event id, provenance, money, consent id, or receipt data. */
|
|
106
|
+
diagnostics(): readonly RedactedDispatchDiagnostic[] {
|
|
107
|
+
return this.diagnosticLog.map((entry) => ({
|
|
108
|
+
...entry,
|
|
109
|
+
attempts: entry.attempts.map((attempt) => ({ ...attempt })),
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private reject(reason: "invalid-intent" | "policy-source-mismatch", source?: CommerceSource): LocalDispatchResult {
|
|
114
|
+
const diagnostic = emptyDiagnostic(this.config.policy, "rejected", source);
|
|
115
|
+
this.diagnosticLog.push(diagnostic);
|
|
116
|
+
return { outcome: "rejected", reason, diagnostic };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private suppress(intent: RegulatedCommerceIntent): LocalDispatchResult {
|
|
120
|
+
const diagnostic = emptyDiagnostic(this.config.policy, "suppressed", intent.source);
|
|
121
|
+
this.diagnosticLog.push(diagnostic);
|
|
122
|
+
return { outcome: "suppressed", reason: "consent-not-granted", diagnostic };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private deliverWithLocalRetries(channel: LocalFakeChannel, intent: RegulatedCommerceIntent): RedactedDispatchAttempt {
|
|
126
|
+
let result = this.destination.deliver({ channel, intent });
|
|
127
|
+
let attempts = 1;
|
|
128
|
+
while (result.outcome === "outage" && attempts <= this.config.maxOutageRetries) {
|
|
129
|
+
result = this.destination.deliver({ channel, intent });
|
|
130
|
+
attempts += 1;
|
|
131
|
+
}
|
|
132
|
+
return { channel, outcome: result.outcome, attempts };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/** Adapter-neutral WS4 measurement intent; protected runtime wiring is deferred. */
|
|
2
|
+
export const REGULATED_COMMERCE_INTENT_SCHEMA_VERSION = "1" as const;
|
|
3
|
+
|
|
4
|
+
export const REGULATED_COMMERCE_EVENT_KINDS = [
|
|
5
|
+
"page_viewed", "product_viewed", "cart_changed", "checkout_handoff",
|
|
6
|
+
"purchase_recorded", "refund_recorded", "cancel_recorded",
|
|
7
|
+
] as const;
|
|
8
|
+
export type RegulatedCommerceEventKind = (typeof REGULATED_COMMERCE_EVENT_KINDS)[number];
|
|
9
|
+
export type CommerceSource = "accepted_browser_operation" | "verified_server_order";
|
|
10
|
+
export type AttributionState = "attributed" | "unattributed_missing" | "unattributed_expired" | "unattributed_denied";
|
|
11
|
+
export type ConsentState = "granted" | "denied" | "not_collected" | "withdrawn";
|
|
12
|
+
|
|
13
|
+
export interface CommerceMoney { readonly minor: string; readonly currency: string; }
|
|
14
|
+
export interface CommerceAttribution { readonly state: AttributionState; readonly correlationId?: string; }
|
|
15
|
+
export interface CommerceConsent { readonly state: ConsentState; readonly snapshotId?: string; }
|
|
16
|
+
export interface RegulatedCommerceIntent {
|
|
17
|
+
readonly schemaVersion: typeof REGULATED_COMMERCE_INTENT_SCHEMA_VERSION;
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly kind: RegulatedCommerceEventKind;
|
|
20
|
+
readonly occurredAt: string;
|
|
21
|
+
readonly source: CommerceSource;
|
|
22
|
+
readonly provenanceId: string;
|
|
23
|
+
readonly money?: CommerceMoney;
|
|
24
|
+
readonly attribution: CommerceAttribution;
|
|
25
|
+
readonly consent: CommerceConsent;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ContractViolation {
|
|
29
|
+
readonly path: string;
|
|
30
|
+
readonly code: "invalid_type" | "invalid_value" | "unknown_field" | "unsupported_version";
|
|
31
|
+
}
|
|
32
|
+
export type ContractResult =
|
|
33
|
+
| { readonly ok: true; readonly value: RegulatedCommerceIntent }
|
|
34
|
+
| { readonly ok: false; readonly violations: readonly ContractViolation[] };
|
|
35
|
+
|
|
36
|
+
const KINDS = new Set<string>(REGULATED_COMMERCE_EVENT_KINDS);
|
|
37
|
+
const BROWSER = new Set<RegulatedCommerceEventKind>(["page_viewed", "product_viewed", "cart_changed", "checkout_handoff"]);
|
|
38
|
+
const SERVER = new Set<RegulatedCommerceEventKind>(["purchase_recorded", "refund_recorded", "cancel_recorded"]);
|
|
39
|
+
const ID = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{7,127}$/;
|
|
40
|
+
const CURRENCY = /^[A-Z]{3}$/;
|
|
41
|
+
const MINOR = /^(?:0|[1-9][0-9]{0,17})$/;
|
|
42
|
+
|
|
43
|
+
function object(value: unknown): Record<string, unknown> | null {
|
|
44
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
45
|
+
const prototype = Object.getPrototypeOf(value);
|
|
46
|
+
return prototype === Object.prototype || prototype === null ? value as Record<string, unknown> : null;
|
|
47
|
+
}
|
|
48
|
+
function closed(value: Record<string, unknown>, allowed: readonly string[], path: string, violations: ContractViolation[]): void {
|
|
49
|
+
for (const key of Object.keys(value)) if (!allowed.includes(key)) violations.push({ path: path ? `${path}.${key}` : key, code: "unknown_field" });
|
|
50
|
+
}
|
|
51
|
+
function id(value: unknown, path: string, violations: ContractViolation[], pattern = ID): string | undefined {
|
|
52
|
+
if (typeof value !== "string") { violations.push({ path, code: "invalid_type" }); return undefined; }
|
|
53
|
+
if (!pattern.test(value)) { violations.push({ path, code: "invalid_value" }); return undefined; }
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
function iso(value: unknown, path: string, violations: ContractViolation[]): string | undefined {
|
|
57
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value))) { violations.push({ path, code: typeof value === "string" ? "invalid_value" : "invalid_type" }); return undefined; }
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function validateRegulatedCommerceIntent(input: unknown): ContractResult {
|
|
62
|
+
const violations: ContractViolation[] = [];
|
|
63
|
+
const root = object(input);
|
|
64
|
+
if (!root) return { ok: false, violations: [{ path: "", code: "invalid_type" }] };
|
|
65
|
+
closed(root, ["schemaVersion", "id", "kind", "occurredAt", "source", "provenanceId", "money", "attribution", "consent"], "", violations);
|
|
66
|
+
if (root.schemaVersion !== REGULATED_COMMERCE_INTENT_SCHEMA_VERSION) violations.push({ path: "schemaVersion", code: "unsupported_version" });
|
|
67
|
+
const eventId = id(root.id, "id", violations);
|
|
68
|
+
const occurredAt = iso(root.occurredAt, "occurredAt", violations);
|
|
69
|
+
const provenanceId = id(root.provenanceId, "provenanceId", violations);
|
|
70
|
+
const kind = typeof root.kind === "string" && KINDS.has(root.kind) ? root.kind as RegulatedCommerceEventKind : (violations.push({ path: "kind", code: typeof root.kind === "string" ? "invalid_value" : "invalid_type" }), undefined);
|
|
71
|
+
const source = root.source === "accepted_browser_operation" || root.source === "verified_server_order" ? root.source : (violations.push({ path: "source", code: typeof root.source === "string" ? "invalid_value" : "invalid_type" }), undefined);
|
|
72
|
+
if (kind && source && ((BROWSER.has(kind) && source !== "accepted_browser_operation") || (SERVER.has(kind) && source !== "verified_server_order"))) violations.push({ path: "source", code: "invalid_value" });
|
|
73
|
+
|
|
74
|
+
const rawAttribution = object(root.attribution);
|
|
75
|
+
if (!rawAttribution) violations.push({ path: "attribution", code: "invalid_type" });
|
|
76
|
+
else {
|
|
77
|
+
closed(rawAttribution, ["state", "correlationId"], "attribution", violations);
|
|
78
|
+
if (!["attributed", "unattributed_missing", "unattributed_expired", "unattributed_denied"].includes(String(rawAttribution.state))) violations.push({ path: "attribution.state", code: "invalid_value" });
|
|
79
|
+
if (rawAttribution.state === "attributed") id(rawAttribution.correlationId, "attribution.correlationId", violations);
|
|
80
|
+
if (rawAttribution.state !== "attributed" && rawAttribution.correlationId !== undefined) violations.push({ path: "attribution.correlationId", code: "invalid_value" });
|
|
81
|
+
}
|
|
82
|
+
const rawConsent = object(root.consent);
|
|
83
|
+
if (!rawConsent) violations.push({ path: "consent", code: "invalid_type" });
|
|
84
|
+
else {
|
|
85
|
+
closed(rawConsent, ["state", "snapshotId"], "consent", violations);
|
|
86
|
+
if (!["granted", "denied", "not_collected", "withdrawn"].includes(String(rawConsent.state))) violations.push({ path: "consent.state", code: "invalid_value" });
|
|
87
|
+
if (rawConsent.snapshotId !== undefined) id(rawConsent.snapshotId, "consent.snapshotId", violations);
|
|
88
|
+
}
|
|
89
|
+
let money: CommerceMoney | undefined;
|
|
90
|
+
if (root.money !== undefined) {
|
|
91
|
+
const rawMoney = object(root.money);
|
|
92
|
+
if (!rawMoney) violations.push({ path: "money", code: "invalid_type" });
|
|
93
|
+
else {
|
|
94
|
+
closed(rawMoney, ["minor", "currency"], "money", violations);
|
|
95
|
+
const minor = id(rawMoney.minor, "money.minor", violations, MINOR);
|
|
96
|
+
const currency = id(rawMoney.currency, "money.currency", violations, CURRENCY);
|
|
97
|
+
if (minor && currency) money = { minor, currency };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (kind && SERVER.has(kind) && !money) violations.push({ path: "money", code: "invalid_value" });
|
|
101
|
+
if (violations.length || !eventId || !kind || !occurredAt || !source || !provenanceId || !rawAttribution || !rawConsent) return { ok: false, violations };
|
|
102
|
+
return { ok: true, value: {
|
|
103
|
+
schemaVersion: REGULATED_COMMERCE_INTENT_SCHEMA_VERSION, id: eventId, kind, occurredAt, source, provenanceId, money,
|
|
104
|
+
attribution: {
|
|
105
|
+
state: rawAttribution.state as AttributionState,
|
|
106
|
+
...(typeof rawAttribution.correlationId === "string" ? { correlationId: rawAttribution.correlationId } : {}),
|
|
107
|
+
},
|
|
108
|
+
consent: {
|
|
109
|
+
state: rawConsent.state as ConsentState,
|
|
110
|
+
...(typeof rawConsent.snapshotId === "string" ? { snapshotId: rawConsent.snapshotId } : {}),
|
|
111
|
+
},
|
|
112
|
+
} };
|
|
113
|
+
}
|