@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
|
@@ -32,6 +32,10 @@ import {
|
|
|
32
32
|
STOREFRONT_CSP_ALLOWLIST,
|
|
33
33
|
checkoutCspAdditions,
|
|
34
34
|
appWidgetCspAdditions,
|
|
35
|
+
embedCspAdditions,
|
|
36
|
+
foldIntegrationCsp,
|
|
37
|
+
embedsArtifactPath,
|
|
38
|
+
resolveEffectiveEmbeds,
|
|
35
39
|
CSP_REPORT_GROUP,
|
|
36
40
|
CSP_REPORT_PATH,
|
|
37
41
|
readPublishedArtifact,
|
|
@@ -43,23 +47,35 @@ import {
|
|
|
43
47
|
resolveServableChannelVersion,
|
|
44
48
|
channelFromEnv,
|
|
45
49
|
serveStaticBundle,
|
|
50
|
+
resolveEnvironmentOnlyPlan,
|
|
51
|
+
resolveInterimBridgePlan,
|
|
52
|
+
compileIntegrationCspFromPlan,
|
|
53
|
+
collectDeclaredCspProfiles,
|
|
54
|
+
INTEGRATION_NONCE_PLACEHOLDER,
|
|
46
55
|
} from "@tot/public-runtime";
|
|
47
|
-
import type { CapabilityConfig, StaticBundleR2BucketLike } from "@tot/public-runtime";
|
|
56
|
+
import type { CapabilityConfig, StaticBundleR2BucketLike, BrowserEnvironment } from "@tot/public-runtime";
|
|
57
|
+
import { loadTenantIntegrationDeclarations } from "@/lib/integration/tenantDeclarations";
|
|
58
|
+
import { loadActiveManagedInstallDeclarations } from "@/lib/integration/managedInstallDeclarations";
|
|
59
|
+
import { fromD1 } from "@/lib/d1/catalog";
|
|
48
60
|
import { resolveTenantCommerce } from "@/lib/checkoutCommerce";
|
|
49
61
|
import { evaluateDemoGate, hostInGateList } from "@/lib/demoGate";
|
|
50
62
|
import {
|
|
51
63
|
evaluateLoginGate,
|
|
52
64
|
parseGateMode,
|
|
53
65
|
isAuthPath,
|
|
66
|
+
isAdminPath,
|
|
54
67
|
sessionAdmitsHost,
|
|
55
68
|
sessionHostCapability,
|
|
56
69
|
PUBLIC_HOME_TENANT_ID,
|
|
57
70
|
} from "@/lib/auth/loginGate";
|
|
71
|
+
import { resolveAdminLanding, isAdminSelectPath } from "@/lib/auth/adminLanding";
|
|
58
72
|
import { gatedHoldingResponse } from "@/lib/auth/gatePage";
|
|
59
73
|
import { readSession, KvSessionStore, SESSION_COOKIE } from "@/lib/auth/session";
|
|
60
74
|
import { isMaintenanceOn, maintenanceResponse } from "@/lib/maintenance";
|
|
61
75
|
import { HEALTH_PATH, healthResponse } from "@/lib/health";
|
|
62
76
|
import { observeDeployVersion } from "@/lib/activity/deployVersion";
|
|
77
|
+
import { requestBrowserEnvironment, deriveSiteSurface, cspHeaderName } from "@/lib/integration/browserEnvironment";
|
|
78
|
+
import { freshNonce, stampResponseNonce } from "@/lib/integration/nonceStamp";
|
|
63
79
|
import { recordActivity } from "@/lib/activity/recordActivity";
|
|
64
80
|
import { primeActivityKillSwitch } from "@/lib/activity/killSwitch";
|
|
65
81
|
import type { DeepPartial, ThemeTokens } from "@tot/public-runtime";
|
|
@@ -70,19 +86,19 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
70
86
|
const host =
|
|
71
87
|
request.headers.get("host") ?? url.host ?? "localhost";
|
|
72
88
|
|
|
73
|
-
// ---- Activity telemetry kill switch
|
|
89
|
+
// ---- Activity telemetry kill switch -----------------------------
|
|
74
90
|
// Prime the ONE `ACTIVITY_TELEMETRY_DISABLED` flag from the (async) Worker env
|
|
75
91
|
// once per isolate, BEFORE the first emit, so every telemetry chokepoint in this
|
|
76
92
|
// isolate (deploy-provenance, recordActivity, ingest) observes it. Fail-open,
|
|
77
93
|
// never throws. Off by default (unset ⇒ telemetry on).
|
|
78
94
|
await primeActivityKillSwitch();
|
|
79
95
|
|
|
80
|
-
// ---- Deploy provenance
|
|
96
|
+
// ---- Deploy provenance — worker_commit / deploy.version_observed -
|
|
81
97
|
// Once per worker isolate (cold start), emit `deploy.version_observed` carrying
|
|
82
|
-
// this build's commit SHA
|
|
83
|
-
// tip). Idempotent per isolate, best-effort, never throws — so it's safe
|
|
84
|
-
// the very front door, ahead of /health, and fires regardless of
|
|
85
|
-
// A no-op when the
|
|
98
|
+
// this build's commit SHA for downstream deploy-skew derivation (running build vs
|
|
99
|
+
// umbrella tip). Idempotent per isolate, best-effort, never throws — so it's safe
|
|
100
|
+
// here at the very front door, ahead of /health, and fires regardless of
|
|
101
|
+
// Host/tenant/gate. A no-op when the kill switch above is set.
|
|
86
102
|
observeDeployVersion();
|
|
87
103
|
|
|
88
104
|
// ---- /health — liveness + deploy-identity probe ---------------------------
|
|
@@ -178,17 +194,59 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
178
194
|
const bypassToken = await readEnv("DEMO_GATE_BYPASS_TOKEN");
|
|
179
195
|
const qaBypass =
|
|
180
196
|
!!bypassToken && request.headers.get("x-demo-gate-bypass") === bypassToken;
|
|
197
|
+
|
|
198
|
+
// Canonical admin tenant landing: an AUTHENTICATED viewer who is not a
|
|
199
|
+
// member/staff of THIS host's tenant must be routed to their OWN tenant
|
|
200
|
+
// (auto when sole) or the tenant picker (when several / none) — never bounced
|
|
201
|
+
// into a sign-in loop and never silently shown the wrong tenant. Only for
|
|
202
|
+
// admin paths, only for an authed non-member, and never on the /admin/select
|
|
203
|
+
// picker itself (whose whole purpose is to be reachable by such a viewer).
|
|
204
|
+
if (
|
|
205
|
+
isAdminPath(url.pathname) &&
|
|
206
|
+
!isAdminSelectPath(url.pathname) &&
|
|
207
|
+
!admitted &&
|
|
208
|
+
viewerRecord &&
|
|
209
|
+
!qaBypass
|
|
210
|
+
) {
|
|
211
|
+
const landing = resolveAdminLanding({
|
|
212
|
+
record: viewerRecord,
|
|
213
|
+
routedTenant: tenant.appDomain,
|
|
214
|
+
basePath,
|
|
215
|
+
intended: url.pathname + url.search,
|
|
216
|
+
nowSeconds,
|
|
217
|
+
});
|
|
218
|
+
if (landing.action === "auto" || landing.action === "select") {
|
|
219
|
+
return context.redirect(landing.location, 303);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// The /admin/select picker requires only a VALID SESSION (any authed viewer),
|
|
224
|
+
// not membership of the routed tenant — it lists the viewer's OWN tenants. So
|
|
225
|
+
// it passes the gate on the strength of the session alone.
|
|
226
|
+
const hasValidSession =
|
|
227
|
+
admitted || (isAdminSelectPath(url.pathname) && !!viewerRecord);
|
|
181
228
|
const decision = evaluateLoginGate({
|
|
182
229
|
host,
|
|
183
230
|
pathname: url.pathname,
|
|
184
231
|
search: url.search,
|
|
185
232
|
mode: gateMode,
|
|
186
233
|
gatedHosts: demoGateHosts,
|
|
187
|
-
hasValidSession
|
|
234
|
+
hasValidSession,
|
|
188
235
|
tenantExempt,
|
|
189
236
|
qaBypass,
|
|
190
237
|
});
|
|
191
238
|
if (decision.action === "redirect") {
|
|
239
|
+
// ADMIN (and any privileged surface) hard-redirects to sign-in rather than
|
|
240
|
+
// rendering the merchant-facing "Coming soon" holding page: an unauthenticated
|
|
241
|
+
// admin visitor must land on the sign-in prompt, never an ambiguous 200 that
|
|
242
|
+
// reads as "this store isn't ready." The holding page's whole audience is a
|
|
243
|
+
// public browser of a gated STOREFRONT; it is the wrong signal for /admin. The
|
|
244
|
+
// gate already decided to withhold — this only changes HOW (303 vs holding
|
|
245
|
+
// page); it never weakens the gate. `decision.location` already carries
|
|
246
|
+
// `?next=` so the admin returns to exactly where they were headed.
|
|
247
|
+
if (isAdminPath(url.pathname)) {
|
|
248
|
+
return context.redirect(decision.location, 303);
|
|
249
|
+
}
|
|
192
250
|
// Withhold content but offer sign-in (carrying the intended destination).
|
|
193
251
|
return gatedHoldingResponse(decision.location);
|
|
194
252
|
}
|
|
@@ -198,7 +256,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
198
256
|
// in: a member sees their tenants[] role; the owner of their own tenant sees
|
|
199
257
|
// the resource-scoped session's capability; a ToT-staff viewer admitted via
|
|
200
258
|
// an explicit vendor selection sees the staffRoles-scoped capability. The
|
|
201
|
-
// ship gates (
|
|
259
|
+
// ship gates (decideIsOwner/resolveShipPrincipal) read this capability,
|
|
202
260
|
// so binding it here keeps owner-resolution server-side + tenant-correct.
|
|
203
261
|
const hostCapability = sessionHostCapability(viewerRecord, tenant.appDomain, nowSeconds);
|
|
204
262
|
locals.viewer = {
|
|
@@ -214,12 +272,26 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
214
272
|
const isPreviewHost = host.toLowerCase().startsWith("preview.");
|
|
215
273
|
const queryToken = url.searchParams.get("__preview");
|
|
216
274
|
const cookieToken = context.cookies.get("tot_preview")?.value ?? null;
|
|
217
|
-
// Per-developer AI-edit draft
|
|
275
|
+
// Per-developer AI-edit draft: the signed
|
|
218
276
|
// tot_dev_draft cookie's mere PRESENCE opts the request out of the shared page
|
|
219
277
|
// cache below (a draft view must never be cached publicly); it's verified —
|
|
220
278
|
// signature, expiry, tenant binding — before the overlay is applied further down.
|
|
221
279
|
const draftCookie = context.cookies.get(DEV_DRAFT_COOKIE)?.value ?? null;
|
|
222
280
|
|
|
281
|
+
// Active managed installs share the same declaration/resolver/CSP/broker/cache
|
|
282
|
+
// plane as tenant-config integrations. Read only canonical active rows and their
|
|
283
|
+
// current immutable snapshot; candidates, suspended rows, and drafts produce no
|
|
284
|
+
// declaration. A missing/unmigrated binding fails closed to no managed effects.
|
|
285
|
+
const appsD1 = await readD1("STOREFRONT_APPS_DB");
|
|
286
|
+
let managedIntegrationDeclarations = [] as Awaited<ReturnType<typeof loadActiveManagedInstallDeclarations>>;
|
|
287
|
+
if (appsD1) {
|
|
288
|
+
try {
|
|
289
|
+
managedIntegrationDeclarations = await loadActiveManagedInstallDeclarations(fromD1(appsD1), tenant.tenant_id);
|
|
290
|
+
} catch {
|
|
291
|
+
managedIntegrationDeclarations = [];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
223
295
|
// ---- Digest-addressed static-bundle serving (ADR 0012) --------------------
|
|
224
296
|
// For a static-publish tenant's PRODUCTION reads only (never preview/test — the
|
|
225
297
|
// "Worker serves interactive preview, R2 serves prod" split), read the live
|
|
@@ -242,6 +314,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
242
314
|
!queryToken &&
|
|
243
315
|
!cookieToken &&
|
|
244
316
|
!draftCookie &&
|
|
317
|
+
managedIntegrationDeclarations.length === 0 &&
|
|
245
318
|
!url.pathname.startsWith("/api/");
|
|
246
319
|
if (staticBundleEligible) {
|
|
247
320
|
const bundleBucket = await readR2("STATIC_BUNDLES");
|
|
@@ -293,7 +366,62 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
293
366
|
// usable without provisioning R2.
|
|
294
367
|
}
|
|
295
368
|
|
|
296
|
-
// ----
|
|
369
|
+
// ---- Tenant-local integration materialization (ext-kernel-09) --------------
|
|
370
|
+
// Resolve a tenant's OWN declared, exact-version integrations into the effective
|
|
371
|
+
// plan the render engine serves, THROUGH the interim marketing bridge (which
|
|
372
|
+
// refuses a regulated tenant and fail-closes an invalid declaration set). Done
|
|
373
|
+
// HERE, before the edge-cache read, so a declared tenant's `planRevision` folds
|
|
374
|
+
// into the page-cache key below (a rollback/uninstall resolves a new revision →
|
|
375
|
+
// the old warm entry is unreachable → a clean re-render without the removed
|
|
376
|
+
// origins, never a TTL wait) AND its reviewed CSP origins reach the strict header.
|
|
377
|
+
//
|
|
378
|
+
// An UNDECLARED tenant does NONE of this: `loadTenantIntegrationDeclarations`
|
|
379
|
+
// returns the empty set, so `integrationCsp` stays undefined (the page-cache key
|
|
380
|
+
// is byte-identical to the non-integration form) and the environment-only plan is
|
|
381
|
+
// resolved below exactly as before — zero cost.
|
|
382
|
+
let resolvedBrowserEnvironment: BrowserEnvironment | null = null;
|
|
383
|
+
const integrationDeclarations = [
|
|
384
|
+
...loadTenantIntegrationDeclarations(tenant),
|
|
385
|
+
...managedIntegrationDeclarations,
|
|
386
|
+
];
|
|
387
|
+
if (integrationDeclarations.length > 0) {
|
|
388
|
+
const declPreviewSecret = (await readEnv("CUSTOMIZATION_PREVIEW_SECRET")) ?? null;
|
|
389
|
+
const declEnv = await resolveCustomizationEnv({
|
|
390
|
+
isPreviewHost,
|
|
391
|
+
tenantId: tenant.tenant_id,
|
|
392
|
+
token: queryToken ?? cookieToken,
|
|
393
|
+
secret: declPreviewSecret,
|
|
394
|
+
nowSeconds: Math.floor(Date.now() / 1000),
|
|
395
|
+
pathname: url.pathname,
|
|
396
|
+
});
|
|
397
|
+
const declFeatures = resolveTenantFeatures(tenant);
|
|
398
|
+
resolvedBrowserEnvironment = requestBrowserEnvironment({
|
|
399
|
+
host,
|
|
400
|
+
isPreviewHost,
|
|
401
|
+
customizationEnv: declEnv.env,
|
|
402
|
+
canonicalHost: tenant.canonicalDomain || host,
|
|
403
|
+
siteSurface: deriveSiteSurface({ catalog: declFeatures.catalog, pathname: renderPath }),
|
|
404
|
+
});
|
|
405
|
+
const bridge = resolveInterimBridgePlan({
|
|
406
|
+
tenantPublicId: tenant.tenant_id,
|
|
407
|
+
tenant,
|
|
408
|
+
environment: resolvedBrowserEnvironment,
|
|
409
|
+
declarations: integrationDeclarations,
|
|
410
|
+
});
|
|
411
|
+
if (bridge.ok) {
|
|
412
|
+
// A resolved plan serves; its compiled CSP additions + `planRevision` reach the
|
|
413
|
+
// cache key and the header. A refusal (regulated) or an invalid set leaves the
|
|
414
|
+
// plan unset here → the environment-only fallback below fills it (nothing loads).
|
|
415
|
+
locals.integrationPlan = bridge.plan;
|
|
416
|
+
const compiled = compileIntegrationCspFromPlan(
|
|
417
|
+
bridge.plan,
|
|
418
|
+
collectDeclaredCspProfiles(integrationDeclarations),
|
|
419
|
+
);
|
|
420
|
+
if (compiled.ok) locals.integrationCsp = compiled.value;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ---- Edge page cache: short-circuit production browse reads on a hit,
|
|
297
425
|
// BEFORE building the client / theme / rendering. Misses store the final
|
|
298
426
|
// response at the end of this middleware.
|
|
299
427
|
//
|
|
@@ -312,12 +440,27 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
312
440
|
let pageKey: Request | null = null;
|
|
313
441
|
if (pageCacheEligible && cache && kv) {
|
|
314
442
|
const version = await readCacheVersion(kv, tenant.tenant_id);
|
|
315
|
-
|
|
443
|
+
// Fold the resolved integration plan revision into the key so a warm entry is
|
|
444
|
+
// version-scoped: a rollback/uninstall resolves a new revision → old entry
|
|
445
|
+
// unreachable → clean re-render without the removed origins (never a TTL wait).
|
|
446
|
+
// Undefined until the integration plane is resolved for this tenant — the key
|
|
447
|
+
// is then byte-identical to the non-integration form. The store below reuses
|
|
448
|
+
// this same `pageKey`, so read and write stay consistent.
|
|
449
|
+
pageKey = pageCacheKey(
|
|
450
|
+
tenant.tenant_id,
|
|
451
|
+
url.pathname,
|
|
452
|
+
url.search,
|
|
453
|
+
version,
|
|
454
|
+
locals.integrationCsp?.planRevision,
|
|
455
|
+
);
|
|
316
456
|
const hit = await getCachedPage(cache, pageKey);
|
|
317
|
-
|
|
457
|
+
// The cached artifact is nonce-free (carries the placeholder). Stamp a fresh
|
|
458
|
+
// per-response nonce into its body + CSP header so a warm hit is served with a
|
|
459
|
+
// brand-new nonce, never the one an earlier response rendered with.
|
|
460
|
+
if (hit) return await stampResponseNonce(hit, freshNonce());
|
|
318
461
|
}
|
|
319
462
|
|
|
320
|
-
// Catalog source selection (
|
|
463
|
+
// Catalog source selection (docs/architecture/product-catalog.md).
|
|
321
464
|
// A store's product source graduates over its life: JSON fixtures (dev default) ->
|
|
322
465
|
// D1 edge replica (staging) -> hosted ToT catalog (go-live). Here we pick the read
|
|
323
466
|
// backend: USE_D1_CATALOG=1 (+ a CATALOG_DB binding) reads the D1 edge replica;
|
|
@@ -337,7 +480,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
337
480
|
? `${proto}://${host}${basePath}`
|
|
338
481
|
: `https://${tenant.canonicalDomain}`;
|
|
339
482
|
|
|
340
|
-
// Live vs test customization
|
|
483
|
+
// Live vs test customization. Production hosts always serve "live" and
|
|
341
484
|
// ignore any token; a "preview." host is auth-gated by a signed, tenant-bound,
|
|
342
485
|
// short-TTL preview token (?__preview= on first hit, then the tot_preview
|
|
343
486
|
// cookie). Test is never indexable (X-Robots-Tag below).
|
|
@@ -459,9 +602,34 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
459
602
|
}
|
|
460
603
|
const capabilities = resolveCapabilities(tenant, publishedCaps);
|
|
461
604
|
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
|
|
605
|
+
// Embed opt-in (self-serve, config-as-code on the same spine as capabilities):
|
|
606
|
+
// the build-time TenantConfig.embeds default is overlaid with the tenant's
|
|
607
|
+
// published embeds.json for THIS env, then filtered to the vetted catalog. A
|
|
608
|
+
// tenant enables a catalogued embed by committing embeds.json — no platform
|
|
609
|
+
// change — but can never introduce a non-catalog origin.
|
|
610
|
+
let publishedEmbeds: string[] | null = null;
|
|
611
|
+
const publishedEmbedsRaw = await readPublishedArtifact(
|
|
612
|
+
kvGet,
|
|
613
|
+
tenant.tenant_id,
|
|
614
|
+
customization.env,
|
|
615
|
+
embedsArtifactPath(tenant.tenant_id),
|
|
616
|
+
);
|
|
617
|
+
if (publishedEmbedsRaw) {
|
|
618
|
+
try {
|
|
619
|
+
const parsed = JSON.parse(publishedEmbedsRaw);
|
|
620
|
+
if (Array.isArray(parsed)) publishedEmbeds = parsed.filter((s) => typeof s === "string");
|
|
621
|
+
} catch {
|
|
622
|
+
/* malformed published artifact — keep the build-time default */
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const effectiveEmbeds = resolveEffectiveEmbeds(tenant.embeds, publishedEmbeds);
|
|
626
|
+
|
|
627
|
+
// Nonce placeholder: the platform's inline tags AND the CSP header are rendered
|
|
628
|
+
// with this sentinel, never a live nonce (ADR 0018). A fresh per-response nonce
|
|
629
|
+
// is stamped in at serve time (stampResponseNonce below), so the cached artifact
|
|
630
|
+
// is nonce-free and every response — cold render or warm cache hit — gets a fresh
|
|
631
|
+
// matching nonce/CSP pair.
|
|
632
|
+
const cspNonce = INTEGRATION_NONCE_PLACEHOLDER;
|
|
465
633
|
|
|
466
634
|
locals.tenant = tenant;
|
|
467
635
|
// Effective capability flags for this tenant (commerce defaults unless the
|
|
@@ -504,6 +672,31 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
504
672
|
// tenant-agnostic; the Layout uses this to keep root-absolute links inside
|
|
505
673
|
// the tenant prefix on the client.
|
|
506
674
|
locals.basePath = basePath;
|
|
675
|
+
// Renderer-owned four-stage browser environment (ADR 0014 E1 / 0017), computed
|
|
676
|
+
// ONCE here from authoritative request state and serialized before app code by
|
|
677
|
+
// both document mounts (Layout.astro + rawHtmlResponse). The candidate render
|
|
678
|
+
// paths build their own `candidate`-stage object instead of this one.
|
|
679
|
+
// Reuse the environment resolved for a declared tenant's integration plane above
|
|
680
|
+
// (identical inputs → identical object) so the plan's environment and this object
|
|
681
|
+
// never diverge; an undeclared tenant computes it here for the first time.
|
|
682
|
+
locals.browserEnvironment =
|
|
683
|
+
resolvedBrowserEnvironment ??
|
|
684
|
+
requestBrowserEnvironment({
|
|
685
|
+
host,
|
|
686
|
+
isPreviewHost,
|
|
687
|
+
customizationEnv: customization.env,
|
|
688
|
+
canonicalHost: tenant.canonicalDomain || host,
|
|
689
|
+
siteSurface: deriveSiteSurface({ catalog: locals.features.catalog, pathname: renderPath }),
|
|
690
|
+
});
|
|
691
|
+
// Resolved effective plan (ext-kernel-03) for this request — the fixed-phase
|
|
692
|
+
// render engine (ext-kernel-04) consumes it in BOTH document mounts (Layout.astro
|
|
693
|
+
// + rawHtmlResponse). A declared tenant's plan is already resolved above (through
|
|
694
|
+
// the interim bridge); an undeclared tenant — or one the bridge refused / failed
|
|
695
|
+
// closed — resolves the environment-only plan here, so a page with no serving
|
|
696
|
+
// integration emits no vendor runtime/markup.
|
|
697
|
+
locals.integrationPlan =
|
|
698
|
+
locals.integrationPlan ??
|
|
699
|
+
resolveEnvironmentOnlyPlan(locals.browserEnvironment, tenant.tenant_id);
|
|
507
700
|
|
|
508
701
|
// /dev was renamed to /cockpit (2026-07-20). 302-redirect any old /dev link —
|
|
509
702
|
// host-routed (/dev) or path-routed (/<vendor>/dev) — to the new path, preserving
|
|
@@ -532,7 +725,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
532
725
|
nonce: cspNonce,
|
|
533
726
|
});
|
|
534
727
|
|
|
535
|
-
// Baseline security headers
|
|
728
|
+
// Baseline security headers on every response — set before the page-cache
|
|
536
729
|
// store below so cached copies carry them too.
|
|
537
730
|
applySecurityHeaders(response.headers);
|
|
538
731
|
|
|
@@ -556,9 +749,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
556
749
|
// site unstyled. The BUILD is CSP-clean (styles are externalized via
|
|
557
750
|
// `inlineStylesheets:"never"`), so we enforce there and only REPORT in dev —
|
|
558
751
|
// violations are still surfaced, but styling/HMR work locally.
|
|
559
|
-
const cspHeader = import.meta.env.DEV
|
|
560
|
-
? "Content-Security-Policy-Report-Only"
|
|
561
|
-
: "Content-Security-Policy";
|
|
752
|
+
const cspHeader = cspHeaderName(import.meta.env.DEV);
|
|
562
753
|
response.headers.set(
|
|
563
754
|
"Reporting-Endpoints",
|
|
564
755
|
`${CSP_REPORT_GROUP}="${CSP_REPORT_PATH}"`,
|
|
@@ -567,40 +758,66 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
567
758
|
// strict policy only when this tenant has a store — so add-to-cart, the
|
|
568
759
|
// sidecart iframe/XHR, and the cross-origin form POST all pass.
|
|
569
760
|
const checkoutCsp = commerce ? checkoutCspAdditions(commerce.origin) : null;
|
|
570
|
-
// Private-app widget origins
|
|
761
|
+
// Private-app widget origins — read back AFTER next() so this reflects
|
|
571
762
|
// exactly the placements that rendered on THIS page, never a wildcard.
|
|
572
763
|
const widgetCsp = appWidgetCspAdditions(locals.activeWidgetPlacements ?? []);
|
|
764
|
+
// Third-party marketing embeds (e.g. a Pipedrive form) the tenant opted into
|
|
765
|
+
// via .tot/config.json `embeds:`. Only scan the HTML body when the tenant
|
|
766
|
+
// declared at least one embed — zero cost for everyone else — and add each
|
|
767
|
+
// provider's origins ONLY if its marker is actually on THIS page.
|
|
768
|
+
// Prefer an opt-in the render handler resolved for a tenant the URL router
|
|
769
|
+
// couldn't (the candidate renderer sets locals.embedOptIn — see candidateRender);
|
|
770
|
+
// otherwise use the router-resolved tenant's effective embeds.
|
|
771
|
+
const embedsForScan = locals.embedOptIn ?? effectiveEmbeds;
|
|
772
|
+
let embedCsp = { scriptSrc: [] as string[], connectSrc: [] as string[], frameSrc: [] as string[] };
|
|
773
|
+
if (
|
|
774
|
+
embedsForScan.length > 0 &&
|
|
775
|
+
(response.headers.get("content-type") ?? "").includes("text/html")
|
|
776
|
+
) {
|
|
777
|
+
embedCsp = embedCspAdditions(embedsForScan, await response.clone().text());
|
|
778
|
+
}
|
|
779
|
+
const cspOptions = {
|
|
780
|
+
nonce: cspNonce,
|
|
781
|
+
...STOREFRONT_CSP_ALLOWLIST,
|
|
782
|
+
// Pin Astro's static inline hydration scripts + island style by hash
|
|
783
|
+
// (they're identical every request); my dynamic inline tags use the nonce.
|
|
784
|
+
scriptSrc: [
|
|
785
|
+
...STOREFRONT_CSP_ALLOWLIST.scriptSrc,
|
|
786
|
+
...STATIC_SCRIPT_HASHES,
|
|
787
|
+
...(checkoutCsp?.scriptSrc ?? []),
|
|
788
|
+
...embedCsp.scriptSrc,
|
|
789
|
+
],
|
|
790
|
+
styleSrc: [
|
|
791
|
+
...STOREFRONT_CSP_ALLOWLIST.styleSrc,
|
|
792
|
+
...STATIC_STYLE_HASHES,
|
|
793
|
+
...(checkoutCsp?.styleSrc ?? []),
|
|
794
|
+
],
|
|
795
|
+
fontSrc: [...STOREFRONT_CSP_ALLOWLIST.fontSrc, ...(checkoutCsp?.fontSrc ?? [])],
|
|
796
|
+
connectSrc: [
|
|
797
|
+
...STOREFRONT_CSP_ALLOWLIST.connectSrc,
|
|
798
|
+
...(checkoutCsp?.connectSrc ?? []),
|
|
799
|
+
...embedCsp.connectSrc,
|
|
800
|
+
],
|
|
801
|
+
imgSrc: [...STOREFRONT_CSP_ALLOWLIST.imgSrc, ...(checkoutCsp?.imgSrc ?? [])],
|
|
802
|
+
frameSrc: [
|
|
803
|
+
...(checkoutCsp?.frameSrc ?? []),
|
|
804
|
+
...widgetCsp.frameSrc,
|
|
805
|
+
...embedCsp.frameSrc,
|
|
806
|
+
],
|
|
807
|
+
formAction: checkoutCsp?.formAction ?? [],
|
|
808
|
+
reportGroup: CSP_REPORT_GROUP,
|
|
809
|
+
reportUri: CSP_REPORT_PATH,
|
|
810
|
+
};
|
|
811
|
+
// Version-scoped integration plane: APPEND the resolved plan's compiled origins
|
|
812
|
+
// (curated adapter profiles + advanced exact origins) to the six contributable
|
|
813
|
+
// directives only — never the base. Absent (no integration) this is a no-op, so
|
|
814
|
+
// the header is byte-identical to a non-integration tenant's.
|
|
573
815
|
response.headers.set(
|
|
574
816
|
cspHeader,
|
|
575
|
-
buildCsp({
|
|
576
|
-
nonce: cspNonce,
|
|
577
|
-
...STOREFRONT_CSP_ALLOWLIST,
|
|
578
|
-
// Pin Astro's static inline hydration scripts + island style by hash
|
|
579
|
-
// (they're identical every request); my dynamic inline tags use the nonce.
|
|
580
|
-
scriptSrc: [
|
|
581
|
-
...STOREFRONT_CSP_ALLOWLIST.scriptSrc,
|
|
582
|
-
...STATIC_SCRIPT_HASHES,
|
|
583
|
-
...(checkoutCsp?.scriptSrc ?? []),
|
|
584
|
-
],
|
|
585
|
-
styleSrc: [
|
|
586
|
-
...STOREFRONT_CSP_ALLOWLIST.styleSrc,
|
|
587
|
-
...STATIC_STYLE_HASHES,
|
|
588
|
-
...(checkoutCsp?.styleSrc ?? []),
|
|
589
|
-
],
|
|
590
|
-
fontSrc: [...STOREFRONT_CSP_ALLOWLIST.fontSrc, ...(checkoutCsp?.fontSrc ?? [])],
|
|
591
|
-
connectSrc: [
|
|
592
|
-
...STOREFRONT_CSP_ALLOWLIST.connectSrc,
|
|
593
|
-
...(checkoutCsp?.connectSrc ?? []),
|
|
594
|
-
],
|
|
595
|
-
imgSrc: [...STOREFRONT_CSP_ALLOWLIST.imgSrc, ...(checkoutCsp?.imgSrc ?? [])],
|
|
596
|
-
frameSrc: [...(checkoutCsp?.frameSrc ?? []), ...widgetCsp.frameSrc],
|
|
597
|
-
formAction: checkoutCsp?.formAction ?? [],
|
|
598
|
-
reportGroup: CSP_REPORT_GROUP,
|
|
599
|
-
reportUri: CSP_REPORT_PATH,
|
|
600
|
-
}),
|
|
817
|
+
buildCsp(foldIntegrationCsp(cspOptions, locals.integrationCsp?.additions ?? {})),
|
|
601
818
|
);
|
|
602
819
|
|
|
603
|
-
//
|
|
820
|
+
// Store the fully-rendered, CSP-stamped response for the next reader. Only
|
|
604
821
|
// production (no preview token reached here) cacheable GETs have a pageKey;
|
|
605
822
|
// putCachedPage additionally skips non-200, non-HTML, and cookie-bearing
|
|
606
823
|
// responses. Also expose Cache-Control to any fronting CDN.
|
|
@@ -609,7 +826,12 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
609
826
|
"Cache-Control",
|
|
610
827
|
"public, s-maxage=300, stale-while-revalidate=86400",
|
|
611
828
|
);
|
|
829
|
+
// Store the nonce-free artifact (the placeholder is still in the body + CSP
|
|
830
|
+
// header) BEFORE stamping — so the cache never holds a live/reusable nonce.
|
|
612
831
|
await putCachedPage(cache, pageKey, response);
|
|
613
832
|
}
|
|
614
|
-
|
|
833
|
+
// Stamp a fresh per-response nonce into this cold response's body + CSP header
|
|
834
|
+
// (the warm-hit path above stamps its own). Both paths therefore serve a fresh,
|
|
835
|
+
// matching nonce/CSP pair, and no nonce is reused across responses (ADR 0018).
|
|
836
|
+
return await stampResponseNonce(response, freshNonce());
|
|
615
837
|
});
|
|
@@ -43,8 +43,13 @@ const rawHtml = await content.getPageHtml(slug);
|
|
|
43
43
|
if (rawHtml && rawHtmlPolicy(tenant).allowed) {
|
|
44
44
|
// Shared chrome: a body-only fragment gets wrapped in the tenant's chrome.html;
|
|
45
45
|
// a full document serves verbatim (backward-compatible). See rawMarketingHtml.ts.
|
|
46
|
-
const chromeWrapper = await content.getChromeHtml();
|
|
47
|
-
return rawHtmlResponse(rawHtml, {
|
|
46
|
+
const chromeWrapper = await content.getChromeHtml(`/${slug}`);
|
|
47
|
+
return rawHtmlResponse(rawHtml, {
|
|
48
|
+
nonce: Astro.locals.cspNonce,
|
|
49
|
+
basePath,
|
|
50
|
+
chromeWrapper,
|
|
51
|
+
plan: Astro.locals.integrationPlan,
|
|
52
|
+
});
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
const page = await content.getPage(slug);
|
|
@@ -59,7 +64,7 @@ const description =
|
|
|
59
64
|
page.seoDescription ||
|
|
60
65
|
(page.intro ? truncate(page.intro, 160) : undefined);
|
|
61
66
|
|
|
62
|
-
// Structured pages
|
|
67
|
+
// Structured pages drive the body from `sections`; legacy pages keep the
|
|
63
68
|
// sanitized `body_html`. Prefer structured whenever a page ships sections.
|
|
64
69
|
const structured = (page.sections?.length ?? 0) > 0;
|
|
65
70
|
|
|
@@ -109,7 +114,7 @@ const updatedLabel = page.updated
|
|
|
109
114
|
{structured && <ProseToc sections={page.sections!} />}
|
|
110
115
|
</header>
|
|
111
116
|
|
|
112
|
-
{/* Body — constrained to a readable measure. Structured pages
|
|
117
|
+
{/* Body — constrained to a readable measure. Structured pages render
|
|
113
118
|
framework widgets from `sections`; legacy pages keep the sanitized HTML. */}
|
|
114
119
|
<div class="container-prose pb-[clamp(3rem,7vw,6rem)] pt-8">
|
|
115
120
|
{structured ? (
|