@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,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client bootstrap that arms the protected-surface tamper sentinel on the live
|
|
3
|
+
* storefront. It maps the storefront's existing protected-surface DOM (compliance
|
|
4
|
+
* notices, the age-gate host, cart/checkout controls) to the policy-04
|
|
5
|
+
* `PROTECTED_SURFACES` markers the sentinel keys on, wires the browser recovery
|
|
6
|
+
* sinks (a durable `sendBeacon` to the server receiver + an operator console
|
|
7
|
+
* alert), and starts observing.
|
|
8
|
+
*
|
|
9
|
+
* This is a DETECTIVE control, mounted unconditionally — it never prevents a
|
|
10
|
+
* mutation, it recovers from one. It is defensive by construction: absent globals,
|
|
11
|
+
* a server (non-browser) context, or a missing surface each degrade to a no-op
|
|
12
|
+
* rather than throwing on the page.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
createTamperSentinel,
|
|
16
|
+
PROTECTED_SURFACE_ATTR,
|
|
17
|
+
type TamperSink,
|
|
18
|
+
} from "./tamper-sentinel";
|
|
19
|
+
import type {
|
|
20
|
+
ProtectedSurface,
|
|
21
|
+
TamperAttribution,
|
|
22
|
+
TamperEvidenceBeacon,
|
|
23
|
+
IntegrationEnvironment,
|
|
24
|
+
IntegrationSiteSurface,
|
|
25
|
+
} from "@tot/public-runtime";
|
|
26
|
+
import { INTEGRATION_PLAN_GLOBAL } from "@tot/public-runtime";
|
|
27
|
+
|
|
28
|
+
/** Where the browser posts a tamper beacon; the server receiver records + suspends. */
|
|
29
|
+
export const TAMPER_BEACON_ENDPOINT = "/api/compliance/tamper";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Existing storefront DOM conventions per protected surface. The sentinel keys on
|
|
33
|
+
* `data-tot-protected-surface`; this bridges the surfaces already rendered by the
|
|
34
|
+
* compliance components + commerce chrome onto that single marker, so no component
|
|
35
|
+
* has to be re-plumbed to be guarded.
|
|
36
|
+
*/
|
|
37
|
+
const SURFACE_SELECTORS: Record<ProtectedSurface, readonly string[]> = {
|
|
38
|
+
"age-gate": ["[data-tot-compliance]", '[data-compliance="age-gate"]'],
|
|
39
|
+
"compliance-warning": [
|
|
40
|
+
'[data-compliance="nicotine-warning"]',
|
|
41
|
+
'[data-compliance="prop65"]',
|
|
42
|
+
'[data-compliance="pact-act"]',
|
|
43
|
+
'[data-compliance="purchase-limit"]',
|
|
44
|
+
'[data-compliance="adult-signature"]',
|
|
45
|
+
],
|
|
46
|
+
"shipping-jurisdiction-notice": [
|
|
47
|
+
'[data-compliance="shipping-restriction"]',
|
|
48
|
+
'[data-compliance="jurisdiction"]',
|
|
49
|
+
],
|
|
50
|
+
"cart-preflight": ["[data-cart-preflight]"],
|
|
51
|
+
"checkout-controls": ["[data-checkout-controls]"],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** Stamp the sentinel marker onto every element matching a known protected-surface selector. */
|
|
55
|
+
export function stampProtectedSurfaces(doc: Document): void {
|
|
56
|
+
for (const surface of Object.keys(SURFACE_SELECTORS) as ProtectedSurface[]) {
|
|
57
|
+
for (const selector of SURFACE_SELECTORS[surface]) {
|
|
58
|
+
for (const el of Array.from(doc.querySelectorAll(selector))) {
|
|
59
|
+
if (!el.hasAttribute(PROTECTED_SURFACE_ATTR)) {
|
|
60
|
+
el.setAttribute(PROTECTED_SURFACE_ATTR, surface);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface BootPlan {
|
|
68
|
+
environment?: string;
|
|
69
|
+
tenantPublicId?: string;
|
|
70
|
+
integrations?: Array<{ origin?: string; appId?: string; version?: string }>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function readPlan(win: Window): BootPlan | null {
|
|
74
|
+
const raw = (win as unknown as Record<string, unknown>)[INTEGRATION_PLAN_GLOBAL];
|
|
75
|
+
return raw && typeof raw === "object" ? (raw as BootPlan) : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function integrationsFrom(plan: BootPlan): TamperAttribution[] {
|
|
79
|
+
const out: TamperAttribution[] = [];
|
|
80
|
+
for (const i of plan.integrations ?? []) {
|
|
81
|
+
if (i.appId && i.version && i.origin) {
|
|
82
|
+
out.push({ appId: i.appId, version: i.version, origin: i.origin });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function beaconSink(win: Window): TamperSink {
|
|
89
|
+
const post = (beacon: TamperEvidenceBeacon): void => {
|
|
90
|
+
try {
|
|
91
|
+
const body = JSON.stringify(beacon);
|
|
92
|
+
const nav = win.navigator as Navigator & {
|
|
93
|
+
sendBeacon?: (url: string, data?: BodyInit) => boolean;
|
|
94
|
+
};
|
|
95
|
+
if (typeof nav.sendBeacon === "function") {
|
|
96
|
+
nav.sendBeacon(TAMPER_BEACON_ENDPOINT, new Blob([body], { type: "application/json" }));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
void fetch(TAMPER_BEACON_ENDPOINT, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: { "content-type": "application/json" },
|
|
102
|
+
body,
|
|
103
|
+
keepalive: true,
|
|
104
|
+
}).catch(() => {});
|
|
105
|
+
} catch {
|
|
106
|
+
/* recovery already happened in the DOM; a failed beacon must never throw */
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
emitEvidence: post,
|
|
111
|
+
alert: (beacon) => {
|
|
112
|
+
// Operator-visible, secret-free line. The durable record is the posted beacon.
|
|
113
|
+
// eslint-disable-next-line no-console
|
|
114
|
+
console.warn(
|
|
115
|
+
`[tamper] detective recovery: surface=${beacon.surface} kind=${beacon.kind} recovered=${beacon.recovered} app=${beacon.attribution?.appId ?? "unattributable"}`,
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
// The server receiver resolves + executes the suspend from the posted beacon;
|
|
119
|
+
// the browser cannot suspend an install itself.
|
|
120
|
+
suspend: () => {},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Arm the sentinel for this page. No-ops (returns `null`) outside a browser or when
|
|
126
|
+
* no integration plan is present (nothing third-party can have tampered).
|
|
127
|
+
*/
|
|
128
|
+
export function bootTamperSentinel(win: Window = globalThis as unknown as Window) {
|
|
129
|
+
if (typeof win === "undefined" || typeof win.document === "undefined") return null;
|
|
130
|
+
const plan = readPlan(win);
|
|
131
|
+
if (!plan || !plan.tenantPublicId || !plan.environment) return null;
|
|
132
|
+
|
|
133
|
+
const doc = win.document;
|
|
134
|
+
stampProtectedSurfaces(doc);
|
|
135
|
+
|
|
136
|
+
const siteSurface =
|
|
137
|
+
(doc.documentElement?.getAttribute("data-tot-site-surface") as IntegrationSiteSurface | null) ??
|
|
138
|
+
"storefront";
|
|
139
|
+
|
|
140
|
+
const sentinel = createTamperSentinel({
|
|
141
|
+
document: doc,
|
|
142
|
+
context: {
|
|
143
|
+
tenant: plan.tenantPublicId,
|
|
144
|
+
environment: plan.environment as IntegrationEnvironment,
|
|
145
|
+
siteSurface,
|
|
146
|
+
integrations: integrationsFrom(plan),
|
|
147
|
+
},
|
|
148
|
+
sink: beaconSink(win),
|
|
149
|
+
});
|
|
150
|
+
sentinel.arm();
|
|
151
|
+
return sentinel;
|
|
152
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protected-surface tamper sentinel — a DETECTIVE runtime control (D-R7,
|
|
3
|
+
* `integration-protected-tamper-recovery`).
|
|
4
|
+
*
|
|
5
|
+
* Full-parent third-party code shares the document with the storefront, so its DOM
|
|
6
|
+
* reach cannot be enforceably narrowed (WS2 security model). This sentinel does NOT
|
|
7
|
+
* prevent mutation and never claims to. It DETECTS mutation of the platform-owned
|
|
8
|
+
* protected surfaces (policy-04 `PROTECTED_SURFACES` — age gates, nicotine/Prop 65
|
|
9
|
+
* warnings, shipping/jurisdiction notices, cart preflight, checkout controls),
|
|
10
|
+
* RESTORES the safe state it captured at arm time, EMITS a durable evidence beacon,
|
|
11
|
+
* ALERTS operations, and — when the mutation is attributable to a single
|
|
12
|
+
* integration — requests that integration be SUSPENDED.
|
|
13
|
+
*
|
|
14
|
+
* Surfaces are keyed by the `data-tot-protected-surface="<surface>"` marker whose
|
|
15
|
+
* value is one of `PROTECTED_SURFACES`. Each marked element captured at
|
|
16
|
+
* {@link createTamperSentinel} `arm()` becomes the AUTHORITATIVE node: the sentinel
|
|
17
|
+
* holds its identity, its pristine subtree, and its position, and reconciles the
|
|
18
|
+
* live DOM back to that baseline on every sweep. Because it restores the SAME node
|
|
19
|
+
* object it captured, a clone-replace that strips event listeners is recovered by
|
|
20
|
+
* reinstating the original (listener-bearing) node.
|
|
21
|
+
*
|
|
22
|
+
* Recovery is idempotent: rapid re-mutation just re-triggers a sweep that restores
|
|
23
|
+
* again. The sentinel disconnects its own observer while restoring and drains the
|
|
24
|
+
* records it caused, so its repair never feeds back as a new detection.
|
|
25
|
+
*/
|
|
26
|
+
import {
|
|
27
|
+
buildTamperEvidenceBeacon,
|
|
28
|
+
attributeSoleSuspect,
|
|
29
|
+
PROTECTED_SURFACES,
|
|
30
|
+
type ProtectedSurface,
|
|
31
|
+
type TamperKind,
|
|
32
|
+
type TamperAttribution,
|
|
33
|
+
type TamperEvidenceBeacon,
|
|
34
|
+
type IntegrationEnvironment,
|
|
35
|
+
type IntegrationSiteSurface,
|
|
36
|
+
} from "@tot/public-runtime";
|
|
37
|
+
|
|
38
|
+
/** The DOM marker a protected surface stamps; its value is a `ProtectedSurface`. */
|
|
39
|
+
export const PROTECTED_SURFACE_ATTR = "data-tot-protected-surface";
|
|
40
|
+
|
|
41
|
+
/** The marker an integration's own injected node carries, enabling positive attribution. */
|
|
42
|
+
export const INTEGRATION_MARKER_ATTR = "data-tot-app";
|
|
43
|
+
|
|
44
|
+
const PROTECTED_SURFACE_SET: ReadonlySet<string> = new Set(PROTECTED_SURFACES);
|
|
45
|
+
|
|
46
|
+
/** The recovery actions a sentinel drives, injected so the engine stays pure/testable. */
|
|
47
|
+
export interface TamperSink {
|
|
48
|
+
/** Persist the durable evidence beacon (e.g. `navigator.sendBeacon` to the server). */
|
|
49
|
+
emitEvidence(beacon: TamperEvidenceBeacon): void;
|
|
50
|
+
/** Raise an operational alert for the detected+recovered tamper. */
|
|
51
|
+
alert(beacon: TamperEvidenceBeacon): void;
|
|
52
|
+
/** Suspend the attributable integration. Only called when attribution is resolved. */
|
|
53
|
+
suspend(attribution: TamperAttribution, beacon: TamperEvidenceBeacon): void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The tenant/environment scope + active integrations a sentinel stamps onto every beacon. */
|
|
57
|
+
export interface TamperSentinelContext {
|
|
58
|
+
tenant: string;
|
|
59
|
+
environment: IntegrationEnvironment;
|
|
60
|
+
siteSurface: IntegrationSiteSurface;
|
|
61
|
+
/** The non-platform integrations active on the page, used for attribution. */
|
|
62
|
+
integrations: readonly TamperAttribution[];
|
|
63
|
+
/** ISO-clock seam for deterministic tests. */
|
|
64
|
+
now?: () => string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface TamperSentinelOptions {
|
|
68
|
+
document: Document;
|
|
69
|
+
context: TamperSentinelContext;
|
|
70
|
+
sink: TamperSink;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface TamperSentinel {
|
|
74
|
+
/** Capture the pristine baseline of every marked surface and start observing. */
|
|
75
|
+
arm(): void;
|
|
76
|
+
/** Stop observing. Baselines are retained; `sweep()` still works. */
|
|
77
|
+
disarm(): void;
|
|
78
|
+
/** Reconcile every guarded surface back to baseline once, synchronously. */
|
|
79
|
+
sweep(): void;
|
|
80
|
+
/** The number of surfaces currently guarded (surfaces present at `arm()`). */
|
|
81
|
+
readonly guardCount: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface Guard {
|
|
85
|
+
surface: ProtectedSurface;
|
|
86
|
+
/** The authoritative captured node; its identity is preserved across recovery. */
|
|
87
|
+
node: Element;
|
|
88
|
+
parent: Node | null;
|
|
89
|
+
/** The sibling the node sat before, so it is reinserted at its exact position. */
|
|
90
|
+
anchor: ChildNode | null;
|
|
91
|
+
baselineInnerRaw: string;
|
|
92
|
+
baselineInner: string;
|
|
93
|
+
baselineText: string;
|
|
94
|
+
baselineOverlayCount: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function normWhitespace(html: string): string {
|
|
98
|
+
return html.replace(/>\s+</g, "><").replace(/\s+/g, " ").trim();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normText(text: string | null): string {
|
|
102
|
+
return (text ?? "").replace(/\s+/g, " ").trim();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** A descendant styled to cover the surface (inline positioned + a stacking/inset hint). */
|
|
106
|
+
function isCoveringElement(el: Element): boolean {
|
|
107
|
+
const style = el.getAttribute("style");
|
|
108
|
+
if (!style) return false;
|
|
109
|
+
const s = style.toLowerCase();
|
|
110
|
+
const positioned = /position\s*:\s*(absolute|fixed)/.test(s);
|
|
111
|
+
const stacks = /z-index\s*:/.test(s) || /inset\s*:/.test(s) || /top\s*:/.test(s);
|
|
112
|
+
return positioned && stacks;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function coveringCount(el: Element): number {
|
|
116
|
+
let n = 0;
|
|
117
|
+
for (const child of Array.from(el.querySelectorAll("*"))) {
|
|
118
|
+
if (isCoveringElement(child)) n++;
|
|
119
|
+
}
|
|
120
|
+
return n;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function isHidden(el: Element): boolean {
|
|
124
|
+
if (el.hasAttribute("hidden")) return true;
|
|
125
|
+
if (el.getAttribute("aria-hidden") === "true") return true;
|
|
126
|
+
const anyEl = el as unknown as { style?: CSSStyleDeclaration };
|
|
127
|
+
const style = anyEl.style;
|
|
128
|
+
if (style) {
|
|
129
|
+
if (style.display === "none") return true;
|
|
130
|
+
if (style.visibility === "hidden") return true;
|
|
131
|
+
if (style.opacity === "0") return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function unhide(el: Element): void {
|
|
137
|
+
el.removeAttribute("hidden");
|
|
138
|
+
if (el.getAttribute("aria-hidden") === "true") el.removeAttribute("aria-hidden");
|
|
139
|
+
const anyEl = el as unknown as { style?: CSSStyleDeclaration };
|
|
140
|
+
const style = anyEl.style;
|
|
141
|
+
if (style) {
|
|
142
|
+
style.removeProperty("display");
|
|
143
|
+
style.removeProperty("visibility");
|
|
144
|
+
style.removeProperty("opacity");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function createTamperSentinel(options: TamperSentinelOptions): TamperSentinel {
|
|
149
|
+
const { document, context, sink } = options;
|
|
150
|
+
const doc = document;
|
|
151
|
+
const guards: Guard[] = [];
|
|
152
|
+
let observer: MutationObserver | null = null;
|
|
153
|
+
// Re-entrancy guard: our own restoration mutates the DOM, which the observer
|
|
154
|
+
// would re-deliver. The guard makes a sweep atomic so recovery never feeds back
|
|
155
|
+
// into an unbounded observe→restore→observe storm.
|
|
156
|
+
let sweeping = false;
|
|
157
|
+
|
|
158
|
+
const now = (): string => context.now?.() ?? new Date().toISOString();
|
|
159
|
+
|
|
160
|
+
function captureGuards(): void {
|
|
161
|
+
guards.length = 0;
|
|
162
|
+
for (const el of Array.from(doc.querySelectorAll(`[${PROTECTED_SURFACE_ATTR}]`))) {
|
|
163
|
+
const surface = el.getAttribute(PROTECTED_SURFACE_ATTR) ?? "";
|
|
164
|
+
if (!PROTECTED_SURFACE_SET.has(surface)) continue;
|
|
165
|
+
guards.push({
|
|
166
|
+
surface: surface as ProtectedSurface,
|
|
167
|
+
node: el,
|
|
168
|
+
parent: el.parentNode,
|
|
169
|
+
anchor: el.nextSibling,
|
|
170
|
+
baselineInnerRaw: el.innerHTML,
|
|
171
|
+
baselineInner: normWhitespace(el.innerHTML),
|
|
172
|
+
baselineText: normText(el.textContent),
|
|
173
|
+
baselineOverlayCount: coveringCount(el),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function siblingImpostor(g: Guard): Element | null {
|
|
179
|
+
for (const el of Array.from(doc.querySelectorAll(`[${PROTECTED_SURFACE_ATTR}="${g.surface}"]`))) {
|
|
180
|
+
if (el !== g.node && el.isConnected) return el;
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function inspect(g: Guard): TamperKind | null {
|
|
186
|
+
if (!g.node.isConnected) {
|
|
187
|
+
const impostor = siblingImpostor(g);
|
|
188
|
+
if (impostor) {
|
|
189
|
+
const sameMarkup =
|
|
190
|
+
normWhitespace(impostor.innerHTML) === g.baselineInner &&
|
|
191
|
+
normText(impostor.textContent) === g.baselineText;
|
|
192
|
+
return sameMarkup ? "listener-stripped" : "displaced";
|
|
193
|
+
}
|
|
194
|
+
return "removed";
|
|
195
|
+
}
|
|
196
|
+
if (isHidden(g.node)) return "hidden";
|
|
197
|
+
if (coveringCount(g.node) > g.baselineOverlayCount) return "overlaid";
|
|
198
|
+
if (normText(g.node.textContent) !== g.baselineText) return "text-altered";
|
|
199
|
+
if (normWhitespace(g.node.innerHTML) !== g.baselineInner) return "subtree-mutated";
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Read a positively-identifying integration appId from the tampering node, if any. */
|
|
204
|
+
function injectedAppId(g: Guard, kind: TamperKind): string | null {
|
|
205
|
+
if (kind === "removed" || kind === "displaced" || kind === "listener-stripped") {
|
|
206
|
+
const impostor = siblingImpostor(g);
|
|
207
|
+
const id = impostor?.getAttribute(INTEGRATION_MARKER_ATTR);
|
|
208
|
+
if (id) return id;
|
|
209
|
+
}
|
|
210
|
+
if (g.node.isConnected) {
|
|
211
|
+
for (const child of Array.from(g.node.querySelectorAll(`[${INTEGRATION_MARKER_ATTR}]`))) {
|
|
212
|
+
const id = child.getAttribute(INTEGRATION_MARKER_ATTR);
|
|
213
|
+
if (id) return id;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function resolveAttribution(appId: string | null): TamperAttribution | null {
|
|
220
|
+
if (appId) {
|
|
221
|
+
const match = context.integrations.find((i) => i.appId === appId);
|
|
222
|
+
if (match) return match;
|
|
223
|
+
}
|
|
224
|
+
return attributeSoleSuspect(context.integrations);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function removeImpostors(g: Guard): void {
|
|
228
|
+
for (const el of Array.from(doc.querySelectorAll(`[${PROTECTED_SURFACE_ATTR}="${g.surface}"]`))) {
|
|
229
|
+
if (el !== g.node && el.isConnected) el.remove();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function reinsert(g: Guard): void {
|
|
234
|
+
if (g.node.isConnected) return;
|
|
235
|
+
if (g.parent && (g.parent as Node & { isConnected: boolean }).isConnected) {
|
|
236
|
+
if (g.anchor && g.anchor.parentNode === g.parent) {
|
|
237
|
+
g.parent.insertBefore(g.node, g.anchor);
|
|
238
|
+
} else {
|
|
239
|
+
g.parent.appendChild(g.node);
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
doc.body?.appendChild(g.node);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function restore(g: Guard): void {
|
|
247
|
+
removeImpostors(g);
|
|
248
|
+
reinsert(g);
|
|
249
|
+
unhide(g.node);
|
|
250
|
+
if (normWhitespace(g.node.innerHTML) !== g.baselineInner) {
|
|
251
|
+
// `baselineInnerRaw` is the platform's OWN pristine subtree, snapshotted at
|
|
252
|
+
// arm() before any third-party code executes — restoring it is exactly the
|
|
253
|
+
// safe-state recovery this control exists for, not injection of external HTML.
|
|
254
|
+
g.node.innerHTML = g.baselineInnerRaw;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function reportAndRecover(g: Guard): void {
|
|
259
|
+
const kind = inspect(g);
|
|
260
|
+
if (!kind) return;
|
|
261
|
+
const appId = injectedAppId(g, kind);
|
|
262
|
+
restore(g);
|
|
263
|
+
const recovered = inspect(g) === null;
|
|
264
|
+
const attribution = resolveAttribution(appId);
|
|
265
|
+
const beacon = buildTamperEvidenceBeacon({
|
|
266
|
+
tenant: context.tenant,
|
|
267
|
+
environment: context.environment,
|
|
268
|
+
siteSurface: context.siteSurface,
|
|
269
|
+
surface: g.surface,
|
|
270
|
+
kind,
|
|
271
|
+
detectedAt: now(),
|
|
272
|
+
recovered,
|
|
273
|
+
attribution,
|
|
274
|
+
});
|
|
275
|
+
sink.emitEvidence(beacon);
|
|
276
|
+
sink.alert(beacon);
|
|
277
|
+
if (attribution) sink.suspend(attribution, beacon);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function sweep(): void {
|
|
281
|
+
if (sweeping) return;
|
|
282
|
+
sweeping = true;
|
|
283
|
+
observer?.disconnect();
|
|
284
|
+
try {
|
|
285
|
+
for (const g of guards) reportAndRecover(g);
|
|
286
|
+
} finally {
|
|
287
|
+
// Drain the mutations our own recovery caused so they aren't re-detected.
|
|
288
|
+
observer?.takeRecords?.();
|
|
289
|
+
startObserving();
|
|
290
|
+
sweeping = false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function startObserving(): void {
|
|
295
|
+
if (!observer) return;
|
|
296
|
+
const root = doc.documentElement ?? doc.body;
|
|
297
|
+
if (!root) return;
|
|
298
|
+
observer.observe(root, {
|
|
299
|
+
subtree: true,
|
|
300
|
+
childList: true,
|
|
301
|
+
attributes: true,
|
|
302
|
+
characterData: true,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function arm(): void {
|
|
307
|
+
captureGuards();
|
|
308
|
+
if (typeof MutationObserver !== "undefined") {
|
|
309
|
+
observer = new MutationObserver(() => sweep());
|
|
310
|
+
startObserving();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function disarm(): void {
|
|
315
|
+
observer?.disconnect();
|
|
316
|
+
observer = null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
arm,
|
|
321
|
+
disarm,
|
|
322
|
+
sweep,
|
|
323
|
+
get guardCount() {
|
|
324
|
+
return guards.length;
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Structured content contracts
|
|
2
|
+
* Structured content contracts — ONE import surface for the
|
|
3
3
|
* data-driven framework widgets, so tenant content (JSON) and the .astro
|
|
4
4
|
* components share a single typed vocabulary and a tenant never copies HTML/CSS.
|
|
5
5
|
*
|
|
6
|
-
* New
|
|
7
|
-
* trust strip. Re-exported here alongside the tier/comparison contracts
|
|
8
|
-
*
|
|
6
|
+
* New contracts: rich text, callouts, needs-review, prose/policy sections,
|
|
7
|
+
* trust strip. Re-exported here alongside the tier/comparison contracts already
|
|
8
|
+
* defined in lib/membership/model, so a membership page and a policy page
|
|
9
9
|
* import from the same place. (The <BrandGrid> `Brand` contract lives with its
|
|
10
10
|
* component in components/commerce/BrandGrid.astro.)
|
|
11
11
|
*/
|
|
@@ -15,7 +15,7 @@ export * from "./needsReview.js";
|
|
|
15
15
|
export * from "./prose.js";
|
|
16
16
|
export * from "./trustStrip.js";
|
|
17
17
|
|
|
18
|
-
// Reused tier contracts
|
|
18
|
+
// Reused tier contracts — surfaced here so all structured widgets share
|
|
19
19
|
// one contract entry point.
|
|
20
20
|
export type {
|
|
21
21
|
Tier,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Apply a unified diff to a source string — the missing half of the content_edit wire
|
|
3
3
|
* contract. tot-mcp's `content_edit` returns per-file unified DIFFS (`--- a/…` /
|
|
4
4
|
* `+++ b/…` / `@@` hunks), not updated contents; to materialize a draft artifact
|
|
5
|
-
*
|
|
5
|
+
* the storefront must re-apply the diff to the
|
|
6
6
|
* exact content it sent.
|
|
7
7
|
*
|
|
8
8
|
* STRICT + FAIL-CLOSED by design: every hunk's context and deletion lines must match
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Route helper for `/api/content-edit/ai-edit` — mirrors `@/lib/change/route.ts`'s
|
|
2
|
+
* Route helper for `/api/content-edit/ai-edit` — mirrors `@/lib/change/route/route.ts`'s
|
|
3
3
|
* `getChangeClient`: build the env-configured client, or a 503 Response when
|
|
4
4
|
* the MCP integration isn't configured (missing `MCP_BASE_URL`/credentials).
|
|
5
5
|
*/
|
|
@@ -27,6 +27,13 @@ export interface Queryable {
|
|
|
27
27
|
first<T = Record<string, unknown>>(sql: string, ...params: unknown[]): Promise<T | null>;
|
|
28
28
|
run(sql: string, ...params: unknown[]): Promise<void>;
|
|
29
29
|
exec(sql: string): Promise<void>;
|
|
30
|
+
/** D1 executes a batch sequentially inside one implicit transaction. */
|
|
31
|
+
batch?(statements: readonly QueryStatement[]): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface QueryStatement {
|
|
35
|
+
sql: string;
|
|
36
|
+
params?: readonly unknown[];
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
/** The slice of the Cloudflare D1Database we use (kept local to avoid a types dep). */
|
|
@@ -39,6 +46,7 @@ export interface D1Like {
|
|
|
39
46
|
};
|
|
40
47
|
};
|
|
41
48
|
exec(sql: string): Promise<unknown>;
|
|
49
|
+
batch?(statements: unknown[]): Promise<unknown[]>;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
/** Adapt a Cloudflare D1Database to the Queryable seam. */
|
|
@@ -54,6 +62,10 @@ export function fromD1(db: D1Like): Queryable {
|
|
|
54
62
|
exec: async (sql: string) => {
|
|
55
63
|
await db.exec(sql);
|
|
56
64
|
},
|
|
65
|
+
batch: async (statements) => {
|
|
66
|
+
if (!db.batch) throw new Error("D1 batch is unavailable");
|
|
67
|
+
await db.batch(statements.map(({ sql, params = [] }) => db.prepare(sql).bind(...params)));
|
|
68
|
+
},
|
|
57
69
|
};
|
|
58
70
|
}
|
|
59
71
|
|