@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,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-integration observability + performance budgets (ext-policy-08).
|
|
3
|
+
*
|
|
4
|
+
* Third-party integrations run inside the kernel-05 lifecycle runtime and the
|
|
5
|
+
* commerce-01 event broker, both of which already isolate a faulting
|
|
6
|
+
* integration from the page and from every other integration. This module is
|
|
7
|
+
* the durable, ATTRIBUTED signal those isolation points feed: a closed,
|
|
8
|
+
* fail-closed, secret-free contract that names WHICH integration (appId +
|
|
9
|
+
* version + origin) produced a performance measurement, a CSP violation, an
|
|
10
|
+
* initialization failure, or a delivery failure — tenant/environment/
|
|
11
|
+
* siteSurface scoped, exactly like {@link import("./tamper-evidence.js").TamperEvidenceBeacon}.
|
|
12
|
+
*
|
|
13
|
+
* Every manifest already DECLARES a per-integration {@link PerformanceBudget}
|
|
14
|
+
* (`browser.performanceBudget`, ext-policy-01). This module is the RUNTIME
|
|
15
|
+
* side of that declaration: {@link evaluatePerformanceBudget} compares a
|
|
16
|
+
* measured {@link PerformanceSignal} against the declared budget and flags an
|
|
17
|
+
* integration that exceeds it. Budget evaluation is fail-closed: an
|
|
18
|
+
* integration with no resolvable budget is flagged over-budget rather than
|
|
19
|
+
* silently passed ({@link evaluatePerformanceBudgets}).
|
|
20
|
+
*
|
|
21
|
+
* No PII: the schema is closed by construction (no free-form user data field
|
|
22
|
+
* exists to leak) and every signal is scanned with {@link findForbiddenFields}
|
|
23
|
+
* (secrets.ts) as defense in depth, exactly like every other contract here.
|
|
24
|
+
*/
|
|
25
|
+
import type { ContractResult } from "./result.js";
|
|
26
|
+
import {
|
|
27
|
+
Violations,
|
|
28
|
+
reqObject,
|
|
29
|
+
childObject,
|
|
30
|
+
reqEnum,
|
|
31
|
+
reqLiteral,
|
|
32
|
+
reqString,
|
|
33
|
+
reqInt,
|
|
34
|
+
reqIsoTimestamp,
|
|
35
|
+
rejectUnknownKeys,
|
|
36
|
+
joinPath,
|
|
37
|
+
} from "./result.js";
|
|
38
|
+
import { OBSERVABILITY_SIGNAL_SCHEMA_VERSION } from "./version.js";
|
|
39
|
+
import { INTEGRATION_ENVIRONMENTS, INTEGRATION_SITE_SURFACES } from "./environment.js";
|
|
40
|
+
import type { IntegrationEnvironment, IntegrationSiteSurface } from "./environment.js";
|
|
41
|
+
import { findForbiddenFields } from "./secrets.js";
|
|
42
|
+
import type { PerformanceBudget } from "./manifest.js";
|
|
43
|
+
import type { LifecycleObservation } from "./lifecycle-runtime.js";
|
|
44
|
+
import type { SubscriberDeliveryOutcome } from "./commerce-broker.js";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The observability signal taxonomy. Every one is a fact ATTRIBUTABLE to one
|
|
48
|
+
* integration's own code, entry, or destination — never anonymous DOM reach
|
|
49
|
+
* (that is {@link import("./tamper-evidence.js").TamperEvidenceBeacon}'s domain).
|
|
50
|
+
*/
|
|
51
|
+
export const OBSERVABILITY_SIGNAL_KINDS = [
|
|
52
|
+
"performance",
|
|
53
|
+
"csp-violation",
|
|
54
|
+
"initialization-failure",
|
|
55
|
+
"delivery-failure",
|
|
56
|
+
] as const;
|
|
57
|
+
export type ObservabilitySignalKind = (typeof OBSERVABILITY_SIGNAL_KINDS)[number];
|
|
58
|
+
|
|
59
|
+
/** The exact integration a signal is attributed to — never a guess. */
|
|
60
|
+
export interface IntegrationAttribution {
|
|
61
|
+
appId: string;
|
|
62
|
+
version: string;
|
|
63
|
+
origin: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Stable key for grouping/looking up signals or budgets by integration identity. */
|
|
67
|
+
export function attributionKey(attribution: IntegrationAttribution): string {
|
|
68
|
+
return `${attribution.appId}@${attribution.version}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Per-kind measured data ──────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Measured timing/transfer facts for one integration's load+init. The three
|
|
75
|
+
* budget-bearing fields ({@link PERFORMANCE_BUDGET_DIMENSIONS}) map 1:1 onto
|
|
76
|
+
* the manifest's declared {@link PerformanceBudget}; `loadMs`/`initMs` are
|
|
77
|
+
* diagnostic timing with no declared ceiling.
|
|
78
|
+
*/
|
|
79
|
+
export interface PerformanceSignalData {
|
|
80
|
+
/** Milliseconds from module-fetch start to the loader resolving (kernel-05 `load` stage). */
|
|
81
|
+
loadMs: number;
|
|
82
|
+
/** Milliseconds from a resolved module to `init()` resolving (kernel-05 `init` stage). */
|
|
83
|
+
initMs: number;
|
|
84
|
+
/** Bytes transferred for the entry module and anything it fetched during load+init. */
|
|
85
|
+
transferredBytes: number;
|
|
86
|
+
/** Network requests beyond the entry module observed during load+init. */
|
|
87
|
+
additionalRequests: number;
|
|
88
|
+
/** Main-thread time attributable to this integration's load+init, ms. */
|
|
89
|
+
mainThreadMilliseconds: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const CSP_VIOLATION_DISPOSITIONS = ["enforce", "report"] as const;
|
|
93
|
+
export type CspViolationDisposition = (typeof CSP_VIOLATION_DISPOSITIONS)[number];
|
|
94
|
+
|
|
95
|
+
export interface CspViolationSignalData {
|
|
96
|
+
/** The violated directive, e.g. `"script-src"`. */
|
|
97
|
+
directive: string;
|
|
98
|
+
/** The blocked origin only — never a full URL (a query string is where a token lives). */
|
|
99
|
+
blockedOrigin: string;
|
|
100
|
+
disposition: CspViolationDisposition;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The lifecycle stages a failure can be attributed to before an integration ever runs. */
|
|
104
|
+
export const INITIALIZATION_FAILURE_STAGES = ["load", "init"] as const;
|
|
105
|
+
export type InitializationFailureStage = (typeof INITIALIZATION_FAILURE_STAGES)[number];
|
|
106
|
+
|
|
107
|
+
export interface InitializationFailureSignalData {
|
|
108
|
+
stage: InitializationFailureStage;
|
|
109
|
+
/** A message only — never a stack or thrown value (kernel-05's own convention). */
|
|
110
|
+
message: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface DeliveryFailureSignalData {
|
|
114
|
+
/** The commerce-broker subscription id that failed to receive the event. */
|
|
115
|
+
subscriberId: string;
|
|
116
|
+
/** A message only — never a stack or thrown value. */
|
|
117
|
+
message: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ── The closed, discriminated signal contract ───────────────────────────────
|
|
121
|
+
|
|
122
|
+
interface ObservabilitySignalBase {
|
|
123
|
+
schemaVersion: typeof OBSERVABILITY_SIGNAL_SCHEMA_VERSION;
|
|
124
|
+
tenant: string;
|
|
125
|
+
environment: IntegrationEnvironment;
|
|
126
|
+
siteSurface: IntegrationSiteSurface;
|
|
127
|
+
attribution: IntegrationAttribution;
|
|
128
|
+
/** ISO-8601 instant the signal was observed. */
|
|
129
|
+
observedAt: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface PerformanceSignal extends ObservabilitySignalBase {
|
|
133
|
+
kind: "performance";
|
|
134
|
+
data: PerformanceSignalData;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface CspViolationSignal extends ObservabilitySignalBase {
|
|
138
|
+
kind: "csp-violation";
|
|
139
|
+
data: CspViolationSignalData;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface InitializationFailureSignal extends ObservabilitySignalBase {
|
|
143
|
+
kind: "initialization-failure";
|
|
144
|
+
data: InitializationFailureSignalData;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface DeliveryFailureSignal extends ObservabilitySignalBase {
|
|
148
|
+
kind: "delivery-failure";
|
|
149
|
+
data: DeliveryFailureSignalData;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** The closed, versioned, secret-free per-integration observability signal. */
|
|
153
|
+
export type IntegrationObservabilitySignal =
|
|
154
|
+
| PerformanceSignal
|
|
155
|
+
| CspViolationSignal
|
|
156
|
+
| InitializationFailureSignal
|
|
157
|
+
| DeliveryFailureSignal;
|
|
158
|
+
|
|
159
|
+
/** The fields a caller supplies to {@link buildObservabilitySignal}. */
|
|
160
|
+
export type ObservabilitySignalInput = Omit<IntegrationObservabilitySignal, "schemaVersion">;
|
|
161
|
+
|
|
162
|
+
/** Stamp the fixed `schemaVersion` — the only constructor, so a signal is never built stale. */
|
|
163
|
+
export function buildObservabilitySignal(
|
|
164
|
+
input: ObservabilitySignalInput,
|
|
165
|
+
): IntegrationObservabilitySignal {
|
|
166
|
+
return { schemaVersion: OBSERVABILITY_SIGNAL_SCHEMA_VERSION, ...input } as IntegrationObservabilitySignal;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Reduce a raw CSP `blocked-uri` to the bare origin a signal may carry. A
|
|
171
|
+
* scheme keyword (`"inline"`, `"eval"`, `"data"`, `"blob"`, `"self"`) passes
|
|
172
|
+
* through unchanged; a URL is reduced to `scheme://host[:port]` with the
|
|
173
|
+
* path/query/fragment dropped — the query string is where a candidate/preview
|
|
174
|
+
* token would live (the same reduction {@link import("./lifecycle-runtime.js").sanitizeNavigationUrl} applies to navigation).
|
|
175
|
+
*/
|
|
176
|
+
export function sanitizeBlockedUri(raw: string): string {
|
|
177
|
+
try {
|
|
178
|
+
const url = new URL(raw);
|
|
179
|
+
return `${url.protocol}//${url.host}`;
|
|
180
|
+
} catch {
|
|
181
|
+
return raw.split("?")[0]!.split("#")[0]!;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function reqNonNegativeInt(
|
|
186
|
+
obj: Record<string, unknown>,
|
|
187
|
+
key: string,
|
|
188
|
+
v: Violations,
|
|
189
|
+
path: string,
|
|
190
|
+
): number | undefined {
|
|
191
|
+
const value = reqInt(obj, key, v, path);
|
|
192
|
+
if (value !== undefined && value < 0) {
|
|
193
|
+
v.add(joinPath(path, key), "format", `"${key}" must be non-negative.`);
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function reqNonNegativeNumber(
|
|
200
|
+
obj: Record<string, unknown>,
|
|
201
|
+
key: string,
|
|
202
|
+
v: Violations,
|
|
203
|
+
path: string,
|
|
204
|
+
): number | undefined {
|
|
205
|
+
const value = obj[key];
|
|
206
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
207
|
+
v.add(joinPath(path, key), "type", `Expected a non-negative number at "${key}".`);
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** No query string or fragment — that is where a preview/candidate token would ride. */
|
|
214
|
+
function reqNoQueryOrFragment(value: string, v: Violations, path: string): void {
|
|
215
|
+
if (value.includes("?") || value.includes("#")) {
|
|
216
|
+
v.add(path, "format", `"${path}" must not carry a query string or fragment.`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function validateAttribution(
|
|
221
|
+
value: unknown,
|
|
222
|
+
v: Violations,
|
|
223
|
+
path: string,
|
|
224
|
+
): IntegrationAttribution | undefined {
|
|
225
|
+
const obj = reqObject(value, v, path);
|
|
226
|
+
if (!obj) return undefined;
|
|
227
|
+
rejectUnknownKeys(obj, ["appId", "version", "origin"], v, path);
|
|
228
|
+
const appId = reqString(obj, "appId", v, path);
|
|
229
|
+
const version = reqString(obj, "version", v, path);
|
|
230
|
+
const origin = reqString(obj, "origin", v, path);
|
|
231
|
+
if (appId === undefined || version === undefined || origin === undefined) return undefined;
|
|
232
|
+
return { appId, version, origin };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function validateSignalData(
|
|
236
|
+
kind: ObservabilitySignalKind,
|
|
237
|
+
data: Record<string, unknown>,
|
|
238
|
+
v: Violations,
|
|
239
|
+
): void {
|
|
240
|
+
switch (kind) {
|
|
241
|
+
case "performance":
|
|
242
|
+
rejectUnknownKeys(
|
|
243
|
+
data,
|
|
244
|
+
["loadMs", "initMs", "transferredBytes", "additionalRequests", "mainThreadMilliseconds"],
|
|
245
|
+
v,
|
|
246
|
+
"data",
|
|
247
|
+
);
|
|
248
|
+
reqNonNegativeNumber(data, "loadMs", v, "data");
|
|
249
|
+
reqNonNegativeNumber(data, "initMs", v, "data");
|
|
250
|
+
reqNonNegativeInt(data, "transferredBytes", v, "data");
|
|
251
|
+
reqNonNegativeInt(data, "additionalRequests", v, "data");
|
|
252
|
+
reqNonNegativeNumber(data, "mainThreadMilliseconds", v, "data");
|
|
253
|
+
return;
|
|
254
|
+
case "csp-violation": {
|
|
255
|
+
rejectUnknownKeys(data, ["directive", "blockedOrigin", "disposition"], v, "data");
|
|
256
|
+
reqString(data, "directive", v, "data");
|
|
257
|
+
const blockedOrigin = reqString(data, "blockedOrigin", v, "data");
|
|
258
|
+
if (blockedOrigin !== undefined) {
|
|
259
|
+
reqNoQueryOrFragment(blockedOrigin, v, "data.blockedOrigin");
|
|
260
|
+
}
|
|
261
|
+
reqEnum(data, "disposition", CSP_VIOLATION_DISPOSITIONS, v, "data");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
case "initialization-failure":
|
|
265
|
+
rejectUnknownKeys(data, ["stage", "message"], v, "data");
|
|
266
|
+
reqEnum(data, "stage", INITIALIZATION_FAILURE_STAGES, v, "data");
|
|
267
|
+
reqString(data, "message", v, "data");
|
|
268
|
+
return;
|
|
269
|
+
case "delivery-failure":
|
|
270
|
+
rejectUnknownKeys(data, ["subscriberId", "message"], v, "data");
|
|
271
|
+
reqString(data, "subscriberId", v, "data");
|
|
272
|
+
reqString(data, "message", v, "data");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The single fail-CLOSED authority for "is this a well-formed observability
|
|
279
|
+
* signal?". Rejects an unknown schema version, an unknown kind, a malformed
|
|
280
|
+
* attribution, a kind/data mismatch, or any secret-bearing field.
|
|
281
|
+
*/
|
|
282
|
+
export function validateObservabilitySignal(
|
|
283
|
+
input: unknown,
|
|
284
|
+
): ContractResult<IntegrationObservabilitySignal> {
|
|
285
|
+
const v = new Violations();
|
|
286
|
+
const obj = reqObject(input, v, "");
|
|
287
|
+
if (!obj) return { ok: false, violations: v.items };
|
|
288
|
+
|
|
289
|
+
rejectUnknownKeys(
|
|
290
|
+
obj,
|
|
291
|
+
["schemaVersion", "kind", "tenant", "environment", "siteSurface", "attribution", "observedAt", "data"],
|
|
292
|
+
v,
|
|
293
|
+
"",
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
reqLiteral(obj, "schemaVersion", OBSERVABILITY_SIGNAL_SCHEMA_VERSION, v, "");
|
|
297
|
+
const kind = reqEnum(obj, "kind", OBSERVABILITY_SIGNAL_KINDS, v, "");
|
|
298
|
+
reqString(obj, "tenant", v, "");
|
|
299
|
+
reqEnum(obj, "environment", INTEGRATION_ENVIRONMENTS, v, "");
|
|
300
|
+
reqEnum(obj, "siteSurface", INTEGRATION_SITE_SURFACES, v, "");
|
|
301
|
+
reqIsoTimestamp(obj, "observedAt", v, "");
|
|
302
|
+
validateAttribution(obj["attribution"], v, "attribution");
|
|
303
|
+
|
|
304
|
+
const data = childObject(obj, "data", v, "");
|
|
305
|
+
if (kind && data) validateSignalData(kind, data, v);
|
|
306
|
+
|
|
307
|
+
for (const violation of findForbiddenFields(obj)) v.items.push(violation);
|
|
308
|
+
|
|
309
|
+
if (!v.ok) return { ok: false, violations: v.items };
|
|
310
|
+
return { ok: true, value: input as IntegrationObservabilitySignal };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ── Bridges from the sibling isolation points (kernel-05, commerce-01) ─────
|
|
314
|
+
|
|
315
|
+
/** {@link IntegrationAttribution} from the boot descriptor's per-integration identity. */
|
|
316
|
+
export function attributionFromBootIntegration(integration: {
|
|
317
|
+
appId: string;
|
|
318
|
+
version: string;
|
|
319
|
+
origin: string;
|
|
320
|
+
}): IntegrationAttribution {
|
|
321
|
+
return { appId: integration.appId, version: integration.version, origin: integration.origin };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Everything an observation needs to become a signal, beyond the observation itself. */
|
|
325
|
+
export interface ObservabilitySignalContext {
|
|
326
|
+
tenant: string;
|
|
327
|
+
environment: IntegrationEnvironment;
|
|
328
|
+
siteSurface: IntegrationSiteSurface;
|
|
329
|
+
/** ISO-8601 timestamp source; defaults to wall clock. */
|
|
330
|
+
now?: () => string;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function nowOf(context: ObservabilitySignalContext): string {
|
|
334
|
+
return context.now ? context.now() : new Date().toISOString();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Turn a failed kernel-05 {@link LifecycleObservation} into an attributed
|
|
339
|
+
* `initialization-failure` signal. `null` for anything that is not a load/init
|
|
340
|
+
* failure — a successful stage, an update/dispose, or a consent `gate` deferral
|
|
341
|
+
* is not an initialization failure.
|
|
342
|
+
*/
|
|
343
|
+
export function initializationFailureFromObservation(
|
|
344
|
+
observation: LifecycleObservation,
|
|
345
|
+
context: ObservabilitySignalContext,
|
|
346
|
+
): InitializationFailureSignal | null {
|
|
347
|
+
if (observation.ok) return null;
|
|
348
|
+
if (observation.stage !== "load" && observation.stage !== "init") return null;
|
|
349
|
+
return buildObservabilitySignal({
|
|
350
|
+
kind: "initialization-failure",
|
|
351
|
+
tenant: context.tenant,
|
|
352
|
+
environment: context.environment,
|
|
353
|
+
siteSurface: context.siteSurface,
|
|
354
|
+
attribution: attributionFromBootIntegration(observation.integration),
|
|
355
|
+
observedAt: nowOf(context),
|
|
356
|
+
data: { stage: observation.stage, message: observation.error ?? "unknown error" },
|
|
357
|
+
}) as InitializationFailureSignal;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Turn a failed commerce-broker {@link SubscriberDeliveryOutcome} into an
|
|
362
|
+
* attributed `delivery-failure` signal. `null` when delivery succeeded or was
|
|
363
|
+
* a policy-correct skip (`out-of-scope` / `consent-ineligible`) — only a
|
|
364
|
+
* throwing subscriber (`reason: "threw"`) is a genuine delivery failure.
|
|
365
|
+
* `attribution` is supplied by the caller, which owns the subscriber-id →
|
|
366
|
+
* integration mapping (a subscriber id is not itself an appId/version/origin).
|
|
367
|
+
*/
|
|
368
|
+
export function deliveryFailureFromOutcome(
|
|
369
|
+
outcome: SubscriberDeliveryOutcome,
|
|
370
|
+
attribution: IntegrationAttribution,
|
|
371
|
+
context: ObservabilitySignalContext,
|
|
372
|
+
): DeliveryFailureSignal | null {
|
|
373
|
+
if (outcome.delivered || outcome.reason !== "threw") return null;
|
|
374
|
+
return buildObservabilitySignal({
|
|
375
|
+
kind: "delivery-failure",
|
|
376
|
+
tenant: context.tenant,
|
|
377
|
+
environment: context.environment,
|
|
378
|
+
siteSurface: context.siteSurface,
|
|
379
|
+
attribution,
|
|
380
|
+
observedAt: nowOf(context),
|
|
381
|
+
data: { subscriberId: outcome.subscriberId, message: outcome.error ?? "unknown error" },
|
|
382
|
+
}) as DeliveryFailureSignal;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ── Budgets: the runtime side of the manifest's declared PerformanceBudget ──
|
|
386
|
+
|
|
387
|
+
/** The three declared budget dimensions a {@link PerformanceSignal} is evaluated against. */
|
|
388
|
+
export const PERFORMANCE_BUDGET_DIMENSIONS = [
|
|
389
|
+
"transferredBytes",
|
|
390
|
+
"additionalRequests",
|
|
391
|
+
"mainThreadMilliseconds",
|
|
392
|
+
] as const;
|
|
393
|
+
export type PerformanceBudgetDimension = (typeof PERFORMANCE_BUDGET_DIMENSIONS)[number];
|
|
394
|
+
|
|
395
|
+
const BUDGET_CEILING_FIELD: Record<
|
|
396
|
+
PerformanceBudgetDimension,
|
|
397
|
+
"maxTransferredBytes" | "maxAdditionalRequests" | "maxMainThreadMilliseconds"
|
|
398
|
+
> = {
|
|
399
|
+
transferredBytes: "maxTransferredBytes",
|
|
400
|
+
additionalRequests: "maxAdditionalRequests",
|
|
401
|
+
mainThreadMilliseconds: "maxMainThreadMilliseconds",
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
export interface PerformanceBudgetVerdict {
|
|
405
|
+
attribution: IntegrationAttribution;
|
|
406
|
+
overBudget: boolean;
|
|
407
|
+
/** Every dimension that exceeded its declared ceiling; empty ⇔ `overBudget: false`. */
|
|
408
|
+
exceeded: PerformanceBudgetDimension[];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Compare one measured {@link PerformanceSignal} against its integration's
|
|
413
|
+
* declared {@link PerformanceBudget}. Every dimension is checked (no
|
|
414
|
+
* short-circuit), so `exceeded` is complete.
|
|
415
|
+
*/
|
|
416
|
+
export function evaluatePerformanceBudget(
|
|
417
|
+
signal: PerformanceSignal,
|
|
418
|
+
budget: PerformanceBudget,
|
|
419
|
+
): PerformanceBudgetVerdict {
|
|
420
|
+
const exceeded: PerformanceBudgetDimension[] = [];
|
|
421
|
+
for (const dimension of PERFORMANCE_BUDGET_DIMENSIONS) {
|
|
422
|
+
if (signal.data[dimension] > budget[BUDGET_CEILING_FIELD[dimension]]) {
|
|
423
|
+
exceeded.push(dimension);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return { attribution: signal.attribution, overBudget: exceeded.length > 0, exceeded };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Evaluate a batch of performance signals. FAIL-CLOSED: an integration whose
|
|
431
|
+
* budget cannot be resolved (`budgetOf` returns `undefined` — an absent or
|
|
432
|
+
* unresolvable manifest) is flagged over-budget on every dimension rather than
|
|
433
|
+
* silently passed, so a missing declaration can never read as a clean bill of
|
|
434
|
+
* health.
|
|
435
|
+
*/
|
|
436
|
+
export function evaluatePerformanceBudgets(
|
|
437
|
+
signals: readonly PerformanceSignal[],
|
|
438
|
+
budgetOf: (attribution: IntegrationAttribution) => PerformanceBudget | undefined,
|
|
439
|
+
): PerformanceBudgetVerdict[] {
|
|
440
|
+
return signals.map((signal) => {
|
|
441
|
+
const budget = budgetOf(signal.attribution);
|
|
442
|
+
if (!budget) {
|
|
443
|
+
return {
|
|
444
|
+
attribution: signal.attribution,
|
|
445
|
+
overBudget: true,
|
|
446
|
+
exceeded: [...PERFORMANCE_BUDGET_DIMENSIONS],
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return evaluatePerformanceBudget(signal, budget);
|
|
450
|
+
});
|
|
451
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reserved render phases, authority tiers, and the contribution dependency DAG
|
|
3
|
+
* (ADR 0014 S1/S2 `integration-phases-fixed-dag`, I1 `integration-authority-
|
|
4
|
+
* four-tiers`, ADR 0016).
|
|
5
|
+
*
|
|
6
|
+
* Phases are a fixed, ordered set — there is no arbitrary selector or generic
|
|
7
|
+
* injection slot. Dependencies form a DAG within that order; cycles, unresolved
|
|
8
|
+
* references, duplicate edges, and any edge that would let a later-phase
|
|
9
|
+
* contribution outrank an earlier compliance phase are rejected.
|
|
10
|
+
*/
|
|
11
|
+
import type { Violations } from "./result.js";
|
|
12
|
+
import { assertUnique } from "./result.js";
|
|
13
|
+
|
|
14
|
+
/** Reserved phases in fixed execution order. */
|
|
15
|
+
export const RESERVED_PHASES = [
|
|
16
|
+
"platform.environment", // renderer-owned environment block; not vendor-addressable
|
|
17
|
+
"consent.defaults", // compliance/consent baseline — cannot be outranked
|
|
18
|
+
"head.early", // early-head loader
|
|
19
|
+
"body.start", // body-start fallback markup
|
|
20
|
+
"document.deferred", // deferred global entry
|
|
21
|
+
] as const;
|
|
22
|
+
export type ReservedPhase = (typeof RESERVED_PHASES)[number];
|
|
23
|
+
|
|
24
|
+
/** Phases a vendor entry or contribution may target. `platform.environment` is renderer-owned. */
|
|
25
|
+
export const VENDOR_ADDRESSABLE_PHASES = [
|
|
26
|
+
"consent.defaults",
|
|
27
|
+
"head.early",
|
|
28
|
+
"body.start",
|
|
29
|
+
"document.deferred",
|
|
30
|
+
] as const;
|
|
31
|
+
export type VendorAddressablePhase = (typeof VENDOR_ADDRESSABLE_PHASES)[number];
|
|
32
|
+
|
|
33
|
+
/** Compliance-critical phases that always precede vendor code and cannot be outranked. */
|
|
34
|
+
export const COMPLIANCE_PHASES: readonly ReservedPhase[] = [
|
|
35
|
+
"platform.environment",
|
|
36
|
+
"consent.defaults",
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export function isReservedPhase(value: unknown): value is ReservedPhase {
|
|
40
|
+
return (
|
|
41
|
+
typeof value === "string" &&
|
|
42
|
+
(RESERVED_PHASES as readonly string[]).includes(value)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function phaseRank(phase: ReservedPhase): number {
|
|
47
|
+
return RESERVED_PHASES.indexOf(phase);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Four authority tiers; declared DOM intent never masquerades as isolation (ADR 0016). */
|
|
51
|
+
export const EXECUTION_TIERS = [
|
|
52
|
+
"platform-adapter",
|
|
53
|
+
"sandbox",
|
|
54
|
+
"server",
|
|
55
|
+
"elevated-parent",
|
|
56
|
+
] as const;
|
|
57
|
+
export type ExecutionTier = (typeof EXECUTION_TIERS)[number];
|
|
58
|
+
|
|
59
|
+
/** Contribution kinds. Only curated platform templates — no arbitrary markup slot. */
|
|
60
|
+
export const CONTRIBUTION_KINDS = ["platform-template"] as const;
|
|
61
|
+
export type ContributionKind = (typeof CONTRIBUTION_KINDS)[number];
|
|
62
|
+
|
|
63
|
+
/** Entry asset formats. Module-only keeps the strict CSP grammar (ADR 0019). */
|
|
64
|
+
export const ENTRY_FORMATS = ["module"] as const;
|
|
65
|
+
export type EntryFormat = (typeof ENTRY_FORMATS)[number];
|
|
66
|
+
|
|
67
|
+
/** A node in the contribution DAG: the implicit `entry` plus each contribution id. */
|
|
68
|
+
export interface DagNode {
|
|
69
|
+
id: string;
|
|
70
|
+
phase: ReservedPhase;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** `dependent` runs after `prerequisite`. */
|
|
74
|
+
export interface DagEdge {
|
|
75
|
+
dependent: string;
|
|
76
|
+
prerequisite: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Fail-closed validation of the contribution DAG: unique ids, resolvable edge
|
|
81
|
+
* endpoints, no duplicate edges, no cycles, and no phase inversion (a dependent
|
|
82
|
+
* may not sit in an earlier phase than a prerequisite it runs after, which is
|
|
83
|
+
* how "compliance phases cannot be outranked" is enforced by construction).
|
|
84
|
+
*/
|
|
85
|
+
export function validateContributionDag(
|
|
86
|
+
nodes: readonly DagNode[],
|
|
87
|
+
edges: readonly DagEdge[],
|
|
88
|
+
v: Violations,
|
|
89
|
+
path: string,
|
|
90
|
+
): void {
|
|
91
|
+
assertUnique(
|
|
92
|
+
nodes.map((n) => n.id),
|
|
93
|
+
v,
|
|
94
|
+
`${path}.contributions`,
|
|
95
|
+
"contribution id",
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
99
|
+
const adjacency = new Map<string, string[]>();
|
|
100
|
+
const seenEdges = new Set<string>();
|
|
101
|
+
|
|
102
|
+
edges.forEach((edge, i) => {
|
|
103
|
+
const edgePath = `${path}.dependencies[${i}]`;
|
|
104
|
+
const dependent = byId.get(edge.dependent);
|
|
105
|
+
const prerequisite = byId.get(edge.prerequisite);
|
|
106
|
+
if (!dependent) {
|
|
107
|
+
v.add(edgePath, "unresolved_ref", `Unknown contribution "${edge.dependent}".`);
|
|
108
|
+
}
|
|
109
|
+
if (!prerequisite) {
|
|
110
|
+
v.add(edgePath, "unresolved_ref", `Unknown prerequisite "${edge.prerequisite}".`);
|
|
111
|
+
}
|
|
112
|
+
const key = `${edge.dependent}<-${edge.prerequisite}`;
|
|
113
|
+
if (seenEdges.has(key)) {
|
|
114
|
+
v.add(edgePath, "duplicate", "Duplicate dependency edge; duplicates are rejected.");
|
|
115
|
+
}
|
|
116
|
+
seenEdges.add(key);
|
|
117
|
+
if (dependent && prerequisite) {
|
|
118
|
+
if (phaseRank(dependent.phase) < phaseRank(prerequisite.phase)) {
|
|
119
|
+
v.add(
|
|
120
|
+
edgePath,
|
|
121
|
+
"phase_inversion",
|
|
122
|
+
`"${edge.dependent}" (${dependent.phase}) cannot run after later-phase ` +
|
|
123
|
+
`"${edge.prerequisite}" (${prerequisite.phase}); an earlier or compliance ` +
|
|
124
|
+
`phase cannot be outranked.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const list = adjacency.get(edge.dependent);
|
|
128
|
+
if (list) list.push(edge.prerequisite);
|
|
129
|
+
else adjacency.set(edge.dependent, [edge.prerequisite]);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const cycle = findCycle(
|
|
134
|
+
nodes.map((n) => n.id),
|
|
135
|
+
adjacency,
|
|
136
|
+
);
|
|
137
|
+
if (cycle) {
|
|
138
|
+
v.add(
|
|
139
|
+
`${path}.dependencies`,
|
|
140
|
+
"cycle",
|
|
141
|
+
`Dependency cycle: ${cycle.join(" -> ")}. The contribution graph must be acyclic.`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function findCycle(
|
|
147
|
+
ids: readonly string[],
|
|
148
|
+
adjacency: Map<string, string[]>,
|
|
149
|
+
): string[] | null {
|
|
150
|
+
const WHITE = 0;
|
|
151
|
+
const GRAY = 1;
|
|
152
|
+
const BLACK = 2;
|
|
153
|
+
const color = new Map<string, number>(ids.map((id) => [id, WHITE]));
|
|
154
|
+
const stack: string[] = [];
|
|
155
|
+
|
|
156
|
+
const visit = (node: string): string[] | null => {
|
|
157
|
+
color.set(node, GRAY);
|
|
158
|
+
stack.push(node);
|
|
159
|
+
for (const next of adjacency.get(node) ?? []) {
|
|
160
|
+
const state = color.get(next) ?? WHITE;
|
|
161
|
+
if (state === GRAY) {
|
|
162
|
+
const from = stack.indexOf(next);
|
|
163
|
+
return [...stack.slice(from), next];
|
|
164
|
+
}
|
|
165
|
+
if (state === WHITE) {
|
|
166
|
+
const found = visit(next);
|
|
167
|
+
if (found) return found;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
stack.pop();
|
|
171
|
+
color.set(node, BLACK);
|
|
172
|
+
return null;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
for (const id of ids) {
|
|
176
|
+
if ((color.get(id) ?? WHITE) === WHITE) {
|
|
177
|
+
const found = visit(id);
|
|
178
|
+
if (found) return found;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|