@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
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* subscription mechanics; a tier's optional figure and CTA are display/handoff
|
|
7
7
|
* only.
|
|
8
8
|
*
|
|
9
|
-
* Illustrative-figures affordance
|
|
10
|
-
*
|
|
9
|
+
* Illustrative-figures affordance: when any tier shows an illustrative
|
|
10
|
+
* figure the header carries a first-class
|
|
11
11
|
* <IllustrativeLabel> badge, each such figure gets an "e.g." affordance +
|
|
12
12
|
* data-illustrative hook, and a footnote spells it out — an example number is
|
|
13
13
|
* never presented as a live offer.
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* subscription mechanics — the CTA is a link into the tenant's own
|
|
7
7
|
* (product-owned) subscribe/learn flow.
|
|
8
8
|
*
|
|
9
|
-
* Illustrative-figure affordance
|
|
10
|
-
*
|
|
9
|
+
* Illustrative-figure affordance: the figure is treated as an EXAMPLE by
|
|
10
|
+
* default (isIllustrative). When
|
|
11
11
|
* illustrative, the figure carries an "e.g." affordance + a first-class
|
|
12
12
|
* <IllustrativeLabel> badge/footnote so an example number is never presented
|
|
13
13
|
* as a live offer. A tenant opts out only by asserting `real: true` on a
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin integration plane — arbitrary-tenant lookup provider (ext-kernel-09).
|
|
3
|
+
*
|
|
4
|
+
* The `/api/apps/admin/*` integration routes resolve a tenant named in the operator
|
|
5
|
+
* session (`auth.tenantId` — a vendor may administer a tenant that is NOT the
|
|
6
|
+
* request's own host scope, so `Astro.locals.tenant` does not apply). Materializing
|
|
7
|
+
* a tenant's OWN (tenant-local) declarations needs its full config (`compliance`,
|
|
8
|
+
* `siteType`, `integrations`), which requires the concrete resolver selection — and
|
|
9
|
+
* the `tenancy-resolver-fence` extractability invariant forbids pages/lib from
|
|
10
|
+
* importing that directly.
|
|
11
|
+
*
|
|
12
|
+
* This module lives in `src/config` (an approved location) and encapsulates
|
|
13
|
+
* `getTenantResolver()` + `staticTenants`, exposing exactly the by-id lookup the
|
|
14
|
+
* admin routes need, typed against the public `TenantConfig`. Mirrors
|
|
15
|
+
* `previewTenantLookup.ts`; the concrete resolver selection stays in `config`, so a
|
|
16
|
+
* constant single-tenant resolver remains a drop-in and the codebase stays
|
|
17
|
+
* always-extractable.
|
|
18
|
+
*/
|
|
19
|
+
import type { TenantConfig } from "@tot/public-runtime";
|
|
20
|
+
import { getTenantResolver } from "@/config/resolver";
|
|
21
|
+
import { staticTenants } from "@/config/tenants";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the admin operator's tenant id (== appDomain for the built-in tenants) to
|
|
25
|
+
* its config. Strict domain match first (KV-backed in prod, static in dev), then the
|
|
26
|
+
* static registry by appDomain or tenant_id. Null for an unknown tenant.
|
|
27
|
+
*/
|
|
28
|
+
export async function resolveAdminTenant(tenantId: string): Promise<TenantConfig | null> {
|
|
29
|
+
const resolver = await getTenantResolver();
|
|
30
|
+
return (
|
|
31
|
+
(await resolver.resolveStrict(tenantId)) ??
|
|
32
|
+
staticTenants.find((t) => t.appDomain === tenantId || t.tenant_id === tenantId) ??
|
|
33
|
+
null
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -27,14 +27,14 @@ import type { DeepPartial, TenantConfig, ThemeTokens } from "@tot/public-runtime
|
|
|
27
27
|
/** Per-dir overrides the resolver derives from disk before synthesis. */
|
|
28
28
|
export interface DevTenantHints {
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* The resolvable appDomain for a SLUG tenant whose dir name isn't its
|
|
31
31
|
* domain. Read from the checkout's `.tot/config.json` `scope` when that scope
|
|
32
32
|
* looks like a hostname (has a dot) — mirrors scripts/tot-dev.mjs resolveDomain.
|
|
33
33
|
* Undefined ⇒ the colocation convention (dir == appDomain == scope).
|
|
34
34
|
*/
|
|
35
35
|
appDomain?: string;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* True when catalog fixtures for this tenant's data scope are committed on
|
|
38
38
|
* disk (the same `packages/migration/out/catalog/<scope>/` the FixtureToTClient
|
|
39
39
|
* reads). When true we serve a real COMMERCE tenant so /collections + /products
|
|
40
40
|
* render from them; when false we degrade to a marketing home (no catalog).
|
|
@@ -45,9 +45,9 @@ export interface DevTenantHints {
|
|
|
45
45
|
/**
|
|
46
46
|
* A servable TenantConfig for a local dev-loop checkout. Identity defaults to the
|
|
47
47
|
* colocated dir name (dir == id == appDomain == tot_data_scope); a slug tenant can
|
|
48
|
-
* override the resolvable appDomain via `hints.appDomain
|
|
48
|
+
* override the resolvable appDomain via `hints.appDomain`. No Storyblok space
|
|
49
49
|
* (a "PLACEHOLDER" token → LocalContentProvider serves the checkout's content/).
|
|
50
|
-
* Commerce is data-driven
|
|
50
|
+
* Commerce is data-driven: with catalog fixtures on disk the tenant is
|
|
51
51
|
* COMMERCE (catalog on); without them it degrades to a marketing home (siteType
|
|
52
52
|
* "marketing" → no catalog required, so the root renders instead of 404ing).
|
|
53
53
|
* Theme tokens come from the checkout's theme.json.
|
|
@@ -57,7 +57,7 @@ export function synthesizeDevTenant(
|
|
|
57
57
|
themeTokens: DeepPartial<ThemeTokens>,
|
|
58
58
|
hints: DevTenantHints = {},
|
|
59
59
|
): TenantConfig {
|
|
60
|
-
//
|
|
60
|
+
// Prefer the .tot/config.json scope for a slug tenant; else colocation.
|
|
61
61
|
const appDomain = hints.appDomain ?? id;
|
|
62
62
|
const base: TenantConfig = {
|
|
63
63
|
tenant_id: id,
|
|
@@ -74,7 +74,7 @@ export function synthesizeDevTenant(
|
|
|
74
74
|
theme_tokens: themeTokens,
|
|
75
75
|
};
|
|
76
76
|
if (hints.hasCatalog) {
|
|
77
|
-
//
|
|
77
|
+
// Catalog fixtures on disk → a real commerce tenant (siteType omitted ==
|
|
78
78
|
// commerce → catalog on). Cart wires to the shared ToT demo checkout store
|
|
79
79
|
// (test mode, no secret), the same as the bundled demo tenants — so
|
|
80
80
|
// /collections + /products resolve and add-to-cart works on localhost.
|
|
@@ -90,9 +90,9 @@ export function synthesizeDevTenant(
|
|
|
90
90
|
|
|
91
91
|
/** Per-dir disk facts the resolver supplies so synthesis can go beyond colocation. */
|
|
92
92
|
export interface DevTenantSeedOptions {
|
|
93
|
-
/**
|
|
93
|
+
/** Resolved appDomain for a slug tenant (from `.tot/config.json` scope). */
|
|
94
94
|
scopeFor?: (id: string) => string | undefined;
|
|
95
|
-
/**
|
|
95
|
+
/** Whether catalog fixtures are committed on disk for a given data scope. */
|
|
96
96
|
hasCatalog?: (scope: string) => boolean;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -100,7 +100,7 @@ export interface DevTenantSeedOptions {
|
|
|
100
100
|
* The dev tenants to seed ALONGSIDE staticTenants: one synthesized config per
|
|
101
101
|
* local tenant dir that isn't already a static tenant (those keep their richer
|
|
102
102
|
* hardcoded config). `themeFor` supplies each dir's theme tokens; `opts` carries
|
|
103
|
-
* the per-dir disk facts (
|
|
103
|
+
* the per-dir disk facts (catalog presence, slug scope) the resolver reads.
|
|
104
104
|
*
|
|
105
105
|
* @param localDirIds dir names discovered under `tenants/*` (dir == tenant id)
|
|
106
106
|
* @param staticIds tenant_ids already in the static registry (skipped)
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Changing that value flips which tenant a bare/unknown host serves — proof the
|
|
21
21
|
* default is configurable, not hardcoded.
|
|
22
22
|
*/
|
|
23
|
-
import type
|
|
23
|
+
import { tenantIdFromPath, type TenantResolver } from "@tot/public-runtime";
|
|
24
24
|
import { readEnv, readKv } from "@/lib/env";
|
|
25
25
|
import {
|
|
26
26
|
tenantResolver as staticResolver,
|
|
@@ -32,30 +32,38 @@ import { getTenantTheme } from "@/themes/tenants";
|
|
|
32
32
|
import { devTenantsToSeed } from "@/config/devTenantSeed";
|
|
33
33
|
import { MemoryKv } from "@/lib/memoryKv";
|
|
34
34
|
|
|
35
|
-
// LOCAL dev loop discovery: enumerate the colocated
|
|
35
|
+
// LOCAL dev loop discovery: enumerate the colocated tenant dirs on disk,
|
|
36
36
|
// INCLUDING a developer's checkout grafted in by `tot dev` (scripts/tot-dev.mjs).
|
|
37
37
|
// Keys only — the loaders are never invoked. Vite resolves this glob at
|
|
38
38
|
// dev-server boot (after the graft) and re-resolves on HMR, so a newly grafted
|
|
39
39
|
// tenant dir is discovered. Mirrors the content/theme dir globs the app already
|
|
40
40
|
// uses (provider.ts, themes/tenants/index.ts). Depth: from src/config/ the repo
|
|
41
41
|
// root is four levels up (config→src→storefront→apps→root).
|
|
42
|
+
//
|
|
43
|
+
// Both tiers are globbed: real tenants at `tenants/<id>/`, platform test tenants
|
|
44
|
+
// one level deeper at `tenants/e2e/<id>/` (tenantDirRelative). Vite needs literal
|
|
45
|
+
// patterns, so the nesting is spelled out here and `tenantIdFromPath` — the one
|
|
46
|
+
// inverse of that mapping — recovers the id from either shape.
|
|
42
47
|
const localTenantFiles = import.meta.glob([
|
|
43
48
|
"../../../../tenants/*/theme.json",
|
|
44
49
|
"../../../../tenants/*/content/home.html",
|
|
45
50
|
"../../../../tenants/*/content/home.json",
|
|
51
|
+
"../../../../tenants/e2e/*/theme.json",
|
|
52
|
+
"../../../../tenants/e2e/*/content/home.html",
|
|
53
|
+
"../../../../tenants/e2e/*/content/home.json",
|
|
46
54
|
]);
|
|
47
55
|
|
|
48
|
-
/**
|
|
56
|
+
/** Tenant ids with a local dir (dir == tenant id), from the discovery glob keys. */
|
|
49
57
|
function localTenantDirIds(): string[] {
|
|
50
58
|
const ids = new Set<string>();
|
|
51
59
|
for (const key of Object.keys(localTenantFiles)) {
|
|
52
|
-
const id =
|
|
60
|
+
const id = tenantIdFromPath(key);
|
|
53
61
|
if (id) ids.add(id);
|
|
54
62
|
}
|
|
55
63
|
return [...ids];
|
|
56
64
|
}
|
|
57
65
|
|
|
58
|
-
//
|
|
66
|
+
// Catalog fixtures committed on disk, keyed by data scope (== out/catalog dir
|
|
59
67
|
// name). A dev tenant whose scope has fixtures is served as a real COMMERCE tenant
|
|
60
68
|
// (see devTenantSeed); one without them degrades to a marketing home. Keys only —
|
|
61
69
|
// the same fixtures the FixtureToTClient reads. Repo root is four up from config/.
|
|
@@ -73,14 +81,14 @@ function catalogScopes(): Set<string> {
|
|
|
73
81
|
return scopes;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
//
|
|
84
|
+
// A slug tenant (dir name != its domain) declares its resolvable appDomain in
|
|
77
85
|
// the checkout's `.tot/config.json` `scope`. Read eagerly (tiny JSON) so synthesis
|
|
78
86
|
// can resolve it at `/<domain>/`; trusted only when it looks like a hostname (has a
|
|
79
87
|
// dot), mirroring scripts/tot-dev.mjs resolveDomain. Only the DEV fallback consults
|
|
80
88
|
// it. The explicit `.tot` literal in the pattern matches the dotdir; if the glob
|
|
81
89
|
// sees nothing, synthesis simply falls back to the colocation convention.
|
|
82
90
|
const tenantMarkerFiles = import.meta.glob<{ scope?: string }>(
|
|
83
|
-
"../../../../tenants/*/.tot/config.json",
|
|
91
|
+
["../../../../tenants/*/.tot/config.json", "../../../../tenants/e2e/*/.tot/config.json"],
|
|
84
92
|
{ eager: true, import: "default" },
|
|
85
93
|
);
|
|
86
94
|
|
|
@@ -88,7 +96,7 @@ const tenantMarkerFiles = import.meta.glob<{ scope?: string }>(
|
|
|
88
96
|
function slugScopes(): Map<string, string> {
|
|
89
97
|
const map = new Map<string, string>();
|
|
90
98
|
for (const [key, cfg] of Object.entries(tenantMarkerFiles)) {
|
|
91
|
-
const id =
|
|
99
|
+
const id = tenantIdFromPath(key);
|
|
92
100
|
const scope = cfg?.scope;
|
|
93
101
|
// A hostname scope that DIFFERS from the dir is the slug override; a scope
|
|
94
102
|
// equal to the dir (the colocation norm) needs no override.
|
|
@@ -110,8 +118,8 @@ function slugScopes(): Map<string, string> {
|
|
|
110
118
|
function devFallbackResolver(): TenantResolver {
|
|
111
119
|
if (!import.meta.env.DEV) return staticResolver;
|
|
112
120
|
const staticIds = new Set(staticTenants.map((t) => t.tenant_id));
|
|
113
|
-
const withCatalog = catalogScopes();
|
|
114
|
-
const scopes = slugScopes();
|
|
121
|
+
const withCatalog = catalogScopes();
|
|
122
|
+
const scopes = slugScopes();
|
|
115
123
|
const dev = devTenantsToSeed(localTenantDirIds(), staticIds, getTenantTheme, {
|
|
116
124
|
scopeFor: (id) => scopes.get(id),
|
|
117
125
|
hasCatalog: (scope) => withCatalog.has(scope),
|
|
@@ -23,6 +23,9 @@ import { referenceTheme } from "@/themes/reference";
|
|
|
23
23
|
import { mergeTheme } from "@/themes/schema";
|
|
24
24
|
import { organizationLd, websiteLd } from "@/lib/jsonld";
|
|
25
25
|
import { nonceInlineTags, CHROME_BODY_MARKER } from "@/lib/rawMarketingHtml";
|
|
26
|
+
import { resolveHeaderVariant, resolveFooterVariant } from "@/lib/chrome/variant";
|
|
27
|
+
import type { HeaderVariant, FooterVariant } from "@/lib/chrome/model";
|
|
28
|
+
import { renderIntegrationPhases } from "@tot/public-runtime";
|
|
26
29
|
import { readViewerSession } from "@/lib/auth/route";
|
|
27
30
|
import AppWidgetFrame from "@/components/apps/AppWidgetFrame.astro";
|
|
28
31
|
import { getRegistryService } from "@/lib/apps/adminService.js";
|
|
@@ -40,7 +43,18 @@ interface Props {
|
|
|
40
43
|
feeds?: { title: string; href: string }[];
|
|
41
44
|
/** Wider pages (PLP/home) drop the prose max-width. */
|
|
42
45
|
fullBleed?: boolean;
|
|
43
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Explicit per-page header/footer content-variant override (see
|
|
48
|
+
* lib/chrome/model.ts HeaderVariant/FooterVariant). NEVER inferred from the
|
|
49
|
+
* route/CSS/title — a page that wants e.g. a minimal checkout header sets
|
|
50
|
+
* this prop. Absent ⇒ the tenant's chrome-config default
|
|
51
|
+
* (chrome.header.variant / chrome.footer.variant). An unrecognized value
|
|
52
|
+
* (page-supplied or the config's own default) fails closed with an
|
|
53
|
+
* actionable error rather than silently falling back.
|
|
54
|
+
*/
|
|
55
|
+
headerVariant?: HeaderVariant;
|
|
56
|
+
footerVariant?: FooterVariant;
|
|
57
|
+
// --- Article SEO hardening: pass-through to <Seo> (all additive) ---
|
|
44
58
|
article?: import("@/lib/seo/meta.js").ArticleMeta;
|
|
45
59
|
imageAlt?: string;
|
|
46
60
|
imageWidth?: number;
|
|
@@ -48,7 +62,7 @@ interface Props {
|
|
|
48
62
|
twitterSite?: string;
|
|
49
63
|
twitterCreator?: string;
|
|
50
64
|
canonicalOverride?: string;
|
|
51
|
-
// ---
|
|
65
|
+
// --- hreflang / <html lang> (additive) ---
|
|
52
66
|
/** Per-variant `hreflang` alternates for a translated article; threaded to
|
|
53
67
|
* <Seo>. Absent/empty ⇒ no alternate links (singleton unchanged). */
|
|
54
68
|
alternates?: import("@/lib/seo/alternates.js").Alternate[];
|
|
@@ -75,10 +89,20 @@ const {
|
|
|
75
89
|
canonicalOverride,
|
|
76
90
|
alternates,
|
|
77
91
|
lang,
|
|
92
|
+
headerVariant,
|
|
93
|
+
footerVariant,
|
|
78
94
|
} = Astro.props;
|
|
79
95
|
const { tenant, themeStyle, canonicalBase, basePath, cspNonce, features, capabilities } =
|
|
80
96
|
Astro.locals;
|
|
81
97
|
|
|
98
|
+
// Fixed-phase integration engine (ext-kernel-04): ONE engine emits the
|
|
99
|
+
// renderer-owned phases (platform.environment, consent.defaults, head.early,
|
|
100
|
+
// body.start, document.deferred) into the head + body-start + body-deferred
|
|
101
|
+
// boundaries. `rawHtmlResponse` is the other mount of the SAME engine, so both
|
|
102
|
+
// documents carry byte-identical phase markup. The nonce is stamped per response
|
|
103
|
+
// from a placeholder by the middleware, never serialized (ADR 0018).
|
|
104
|
+
const integrationPhases = renderIntegrationPhases(Astro.locals.integrationPlan, cspNonce);
|
|
105
|
+
|
|
82
106
|
// Organization JSON-LD is emitted site-wide (brief §8); pages add their own
|
|
83
107
|
// graphs (Product, BreadcrumbList…) via the jsonLd prop.
|
|
84
108
|
const pageGraphs = jsonLd ? (Array.isArray(jsonLd) ? jsonLd : [jsonLd]) : [];
|
|
@@ -92,6 +116,12 @@ const theme = mergeTheme(referenceTheme, tenant.theme_tokens);
|
|
|
92
116
|
|
|
93
117
|
const chrome = await Astro.locals.content.getChrome();
|
|
94
118
|
|
|
119
|
+
// Explicit variant resolution (fail-closed): the page's override wins over
|
|
120
|
+
// the tenant's chrome-config default; an unrecognized value throws with an
|
|
121
|
+
// actionable message rather than silently falling back (lib/chrome/variant.ts).
|
|
122
|
+
const resolvedHeaderVariant = resolveHeaderVariant(headerVariant, chrome.header.variant);
|
|
123
|
+
const resolvedFooterVariant = resolveFooterVariant(footerVariant, chrome.footer.variant);
|
|
124
|
+
|
|
95
125
|
// SHARED CHROME: if the tenant ships content/<id>/chrome.html (the pixel-perfect
|
|
96
126
|
// header/footer the raw marketing pages use), render THAT chrome around the
|
|
97
127
|
// Layout content too — so commerce pages (collections/products/search/…) share
|
|
@@ -99,7 +129,7 @@ const chrome = await Astro.locals.content.getChrome();
|
|
|
99
129
|
// <!--PAGE_BODY-->: everything before = header, after = footer; pull its tenant
|
|
100
130
|
// stylesheet links into <head>; nonce its inline tags for CSP. Tenants without a
|
|
101
131
|
// chrome.html keep the token-driven Header/Footer (below).
|
|
102
|
-
const chromeHtml = await Astro.locals.content.getChromeHtml();
|
|
132
|
+
const chromeHtml = await Astro.locals.content.getChromeHtml(path);
|
|
103
133
|
let sharedChrome: { header: string; footer: string; css: string[] } | null = null;
|
|
104
134
|
if (chromeHtml && chromeHtml.includes(CHROME_BODY_MARKER)) {
|
|
105
135
|
const headEnd = chromeHtml.indexOf("</head>");
|
|
@@ -119,7 +149,7 @@ if (chromeHtml && chromeHtml.includes(CHROME_BODY_MARKER)) {
|
|
|
119
149
|
|
|
120
150
|
const compliance = tenant.compliance;
|
|
121
151
|
|
|
122
|
-
// Site-chrome data resolution (
|
|
152
|
+
// Site-chrome data resolution (chrome/* is the canonical, prop-driven
|
|
123
153
|
// component set; this Layout is the ONE place that resolves session/feature/
|
|
124
154
|
// theme state into their props. Skipped entirely when sharedChrome (a raw
|
|
125
155
|
// content/<id>/chrome.html override) is present — that path wins outright.
|
|
@@ -153,25 +183,34 @@ if (features.catalog) {
|
|
|
153
183
|
// white logo); a light header always shows the text wordmark (no logo image).
|
|
154
184
|
const headerDark = theme.brand.headerVariant === "dark";
|
|
155
185
|
const headerLogoSrc = headerDark ? theme.brand.logoLight : theme.brand.logoDark;
|
|
156
|
-
const
|
|
186
|
+
const themeHeaderLogo =
|
|
157
187
|
headerDark && headerLogoSrc && /^(https?:|\/)/.test(headerLogoSrc)
|
|
158
188
|
? { src: headerLogoSrc, alt: tenant.displayName }
|
|
159
189
|
: undefined;
|
|
160
190
|
|
|
191
|
+
// chrome.header.brand (lib/chrome/model.ts ChromeBrand) is the governed,
|
|
192
|
+
// schema-validated brand identity — SOURCE OF TRUTH per field when set,
|
|
193
|
+
// falling back to the theme-derived values above only where a field is
|
|
194
|
+
// absent, so a tenant that hasn't authored a brand config is unaffected.
|
|
195
|
+
const brand = chrome.header.brand;
|
|
196
|
+
const brandLogoValid = brand?.logo && /^(https?:|\/)/.test(brand.logo.src) ? brand.logo : undefined;
|
|
197
|
+
const headerLogo = brandLogoValid ?? themeHeaderLogo;
|
|
198
|
+
const headerWordmark = brand?.label ?? tenant.displayName;
|
|
199
|
+
const headerBrandHref = brand?.href;
|
|
200
|
+
|
|
161
201
|
// Footer bottom bar: a "Legal"-titled column's links win; otherwise every
|
|
162
202
|
// column's links flatten into the policy-link row (mirrors the retired
|
|
163
203
|
// nav/Footer.astro exactly).
|
|
164
204
|
const legalCol = chrome.footer.columns.find((c) => /legal/i.test(c.title));
|
|
165
205
|
const bottomLinks = legalCol ? legalCol.links : chrome.footer.columns.flatMap((c) => c.links);
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
}`;
|
|
206
|
+
const legalBase = chrome.footer.bottom?.copyright ?? `© ${new Date().getFullYear()} ${tenant.displayName}`;
|
|
207
|
+
const legalLine = `${legalBase}${chrome.footer.bottom?.note ? ` · ${chrome.footer.bottom.note}` : ""}`;
|
|
169
208
|
|
|
170
209
|
const analyticsToken = import.meta.env.PUBLIC_CF_ANALYTICS_TOKEN;
|
|
171
210
|
const analyticsEnabled =
|
|
172
211
|
analyticsToken && !/REPLACE|PLACEHOLDER/i.test(analyticsToken);
|
|
173
212
|
|
|
174
|
-
// Private-app widgets
|
|
213
|
+
// Private-app widgets — global.footer slot. Resolved once here so it
|
|
175
214
|
// renders for BOTH footer paths below (the tenant-owned chrome.html splice
|
|
176
215
|
// and the generic <Footer>) instead of being owned by either. No dark-mode
|
|
177
216
|
// preference infra exists yet — "light" is the only theme hint until it does.
|
|
@@ -194,6 +233,14 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
194
233
|
<head>
|
|
195
234
|
<meta charset="utf-8" />
|
|
196
235
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
236
|
+
|
|
237
|
+
{/* Renderer-owned head phases (platform.environment → consent.defaults →
|
|
238
|
+
head.early), emitted before any tenant/app code — the Layout half of "one
|
|
239
|
+
engine, two mounts" (rawHtmlResponse is the other). Same engine output, so
|
|
240
|
+
both mounts are byte-identical. Carries only public values; the nonce is a
|
|
241
|
+
script attribute stamped per response, never serialized (ADR 0018). */}
|
|
242
|
+
<Fragment set:html={integrationPhases.head} />
|
|
243
|
+
|
|
197
244
|
<link rel="icon" type="image/svg+xml" href={theme.brand.favicon} />
|
|
198
245
|
|
|
199
246
|
{/* Per-tenant theme tokens injected at the edge (brief §7). The id is the
|
|
@@ -241,6 +288,10 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
241
288
|
}
|
|
242
289
|
</head>
|
|
243
290
|
<body>
|
|
291
|
+
{/* body.start fallback boundary (ext-kernel-04) — the renderer-owned mount
|
|
292
|
+
point for body-start contributions, before any page content. */}
|
|
293
|
+
<Fragment set:html={integrationPhases.bodyStart} />
|
|
294
|
+
|
|
244
295
|
<a
|
|
245
296
|
href="#main"
|
|
246
297
|
class="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:m-3 focus:rounded-md focus:bg-[var(--color-primary)] focus:px-4 focus:py-2 focus:text-[var(--color-primary-contrast)]"
|
|
@@ -257,16 +308,20 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
257
308
|
) : (
|
|
258
309
|
<>
|
|
259
310
|
<div class="sticky top-0 z-40">
|
|
260
|
-
{chrome.announcement &&
|
|
311
|
+
{chrome.header.announcement?.messages[0] && (
|
|
312
|
+
<AnnouncementBar text={chrome.header.announcement.messages[0]} variant="static" />
|
|
313
|
+
)}
|
|
261
314
|
<SiteHeader
|
|
262
315
|
logo={headerLogo}
|
|
263
|
-
wordmark={
|
|
264
|
-
|
|
316
|
+
wordmark={headerWordmark}
|
|
317
|
+
brandHref={headerBrandHref}
|
|
318
|
+
nav={chrome.header.nav}
|
|
265
319
|
variant={headerDark ? "dark" : "light"}
|
|
266
|
-
|
|
320
|
+
layout={resolvedHeaderVariant}
|
|
321
|
+
announcement={chrome.header.announcement?.messages[0]}
|
|
267
322
|
searchAction={features.productSearch ? "/search" : undefined}
|
|
268
323
|
savedItemsHref={features.savedItems ? "/saved" : undefined}
|
|
269
|
-
headerCtas={chrome.
|
|
324
|
+
headerCtas={chrome.header.ctas}
|
|
270
325
|
authLink={authLink}
|
|
271
326
|
sticky={false}
|
|
272
327
|
/>
|
|
@@ -287,8 +342,9 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
287
342
|
<SiteFooter
|
|
288
343
|
brand={{
|
|
289
344
|
wordmark: tenant.displayName,
|
|
290
|
-
contact: chrome.footer.
|
|
345
|
+
contact: chrome.footer.brand?.text ? [chrome.footer.brand.text] : undefined,
|
|
291
346
|
}}
|
|
347
|
+
variant={resolvedFooterVariant}
|
|
292
348
|
columns={chrome.footer.columns}
|
|
293
349
|
newsletter={features.newsletter ? { title: "Newsletter", placeholder: "you@example.com" } : undefined}
|
|
294
350
|
social={chrome.footer.social}
|
|
@@ -298,7 +354,7 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
298
354
|
</>
|
|
299
355
|
)}
|
|
300
356
|
|
|
301
|
-
{/* Private-app widgets (
|
|
357
|
+
{/* Private-app widgets (global.footer) — sandboxed per install; one
|
|
302
358
|
resolution above covers both footer paths. */}
|
|
303
359
|
{footerWidgets.map((w) => (
|
|
304
360
|
<div class="container-prose py-6">
|
|
@@ -426,7 +482,7 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
426
482
|
</script>
|
|
427
483
|
)}
|
|
428
484
|
|
|
429
|
-
{/* Dev-loop theme hot-patch
|
|
485
|
+
{/* Dev-loop theme hot-patch: tenant-hot-reload.mjs broadcasts a
|
|
430
486
|
`tot:theme-vars` HMR custom event on every theme.json save instead of
|
|
431
487
|
forcing a full reload; swap the CSS var block in place so edits repaint
|
|
432
488
|
instantly with no lost scroll position. import.meta.hot is undefined in
|
|
@@ -442,7 +498,7 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
442
498
|
</script>
|
|
443
499
|
)}
|
|
444
500
|
|
|
445
|
-
{/* Private-app widget postMessage bridge
|
|
501
|
+
{/* Private-app widget postMessage bridge — mounted unconditionally
|
|
446
502
|
(cheap no-op with zero tracked iframes); scans for
|
|
447
503
|
`[data-tot-widget-origin]` frames itself, so it doesn't need to know
|
|
448
504
|
which slots rendered above. */}
|
|
@@ -476,14 +532,38 @@ const footerWidgets = await renderWidgetSlot(
|
|
|
476
532
|
}
|
|
477
533
|
</script>
|
|
478
534
|
|
|
479
|
-
{/* Field Web Vitals (
|
|
535
|
+
{/* Field Web Vitals (RUM) — measures LCP/INP/CLS for real visitors and
|
|
480
536
|
beacons per-route samples to the RUM sink. ~1KB, no framework; no-ops
|
|
481
537
|
where PerformanceObserver is absent. This is the per-route-template
|
|
482
|
-
signal
|
|
538
|
+
signal the `core-web-vitals` monitor consumes (the CF Web Analytics
|
|
483
539
|
beacon above only gives aggregate field CWV). */}
|
|
484
540
|
<script>
|
|
485
541
|
import { startWebVitals } from "@/lib/analytics/webVitals";
|
|
486
542
|
startWebVitals();
|
|
487
543
|
</script>
|
|
544
|
+
|
|
545
|
+
{/* Protected-surface tamper sentinel (ext-policy-09, D-R7) — a DETECTIVE
|
|
546
|
+
runtime control. Full-parent third-party code shares this document and its
|
|
547
|
+
DOM reach can't be enforceably narrowed, so this never prevents mutation: it
|
|
548
|
+
detects tampering of the platform-owned compliance/commerce surfaces
|
|
549
|
+
(policy-04 PROTECTED_SURFACES), restores safe state, evidences it, and
|
|
550
|
+
suspends the attributable integration. No-ops when no integration plan is
|
|
551
|
+
present. */}
|
|
552
|
+
<script>
|
|
553
|
+
import { bootTamperSentinel } from "@/lib/compliance/tamper-sentinel-boot";
|
|
554
|
+
bootTamperSentinel();
|
|
555
|
+
</script>
|
|
556
|
+
|
|
557
|
+
{/* document.deferred global-entry boundary (ext-kernel-04) — the last phase,
|
|
558
|
+
the renderer-owned mount point for the deferred integration entry. */}
|
|
559
|
+
<Fragment set:html={integrationPhases.bodyDeferred} />
|
|
560
|
+
|
|
561
|
+
{/* document.deferred lifecycle boot (ext-kernel-05) — initializes the plan's
|
|
562
|
+
integrations exactly once per document, wires the reserved completed-
|
|
563
|
+
navigation event, and disposes on teardown; a no-op for an empty plan. */}
|
|
564
|
+
<script>
|
|
565
|
+
import { bootIntegrationLifecycleFromDocument } from "@tot/public-runtime";
|
|
566
|
+
bootIntegrationLifecycleFromDocument();
|
|
567
|
+
</script>
|
|
488
568
|
</body>
|
|
489
569
|
</html>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { D1Like } from "@/lib/d1/catalog";
|
|
2
|
+
|
|
3
|
+
const DIAGNOSTIC_STUCK_AFTER_MS = 5 * 60 * 1000;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Atomically reserve one tenant/change across authorization, Gitea, and finalization.
|
|
7
|
+
* `expires_at` is an operator-visible stuck-work deadline, never an automatic takeover:
|
|
8
|
+
* a live but slow submit cannot lose ownership underneath its forge mutation.
|
|
9
|
+
*/
|
|
10
|
+
export async function acquireCandidateSubmitLease(
|
|
11
|
+
db: D1Like,
|
|
12
|
+
tenantId: string,
|
|
13
|
+
changeId: string,
|
|
14
|
+
authorizationId: string,
|
|
15
|
+
now = new Date(),
|
|
16
|
+
): Promise<boolean> {
|
|
17
|
+
const acquiredAt = now.toISOString();
|
|
18
|
+
const expiresAt = new Date(now.getTime() + DIAGNOSTIC_STUCK_AFTER_MS).toISOString();
|
|
19
|
+
const row = await db.prepare(`
|
|
20
|
+
INSERT INTO candidate_submit_leases
|
|
21
|
+
(tenant_id, change_id, authorization_id, status, acquired_at, expires_at)
|
|
22
|
+
VALUES (?, ?, ?, 'active', ?, ?)
|
|
23
|
+
ON CONFLICT(tenant_id, change_id) DO UPDATE SET
|
|
24
|
+
authorization_id = excluded.authorization_id,
|
|
25
|
+
status = 'active',
|
|
26
|
+
acquired_at = excluded.acquired_at,
|
|
27
|
+
expires_at = excluded.expires_at
|
|
28
|
+
WHERE candidate_submit_leases.status <> 'active'
|
|
29
|
+
OR candidate_submit_leases.authorization_id = excluded.authorization_id
|
|
30
|
+
RETURNING authorization_id
|
|
31
|
+
`).bind(tenantId, changeId, authorizationId, acquiredAt, expiresAt).first() as
|
|
32
|
+
{ authorization_id?: string } | null;
|
|
33
|
+
return row?.authorization_id === authorizationId;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type CandidateSubmitLeaseState = "active" | "completed" | "missing";
|
|
37
|
+
|
|
38
|
+
/** Atomically prove ownership for the callback work. Active leases are never stolen. */
|
|
39
|
+
export async function renewCandidateSubmitLease(
|
|
40
|
+
db: D1Like,
|
|
41
|
+
tenantId: string,
|
|
42
|
+
changeId: string,
|
|
43
|
+
authorizationId: string,
|
|
44
|
+
now = new Date(),
|
|
45
|
+
): Promise<CandidateSubmitLeaseState> {
|
|
46
|
+
const current = await db.prepare(`
|
|
47
|
+
SELECT authorization_id, status FROM candidate_submit_leases
|
|
48
|
+
WHERE tenant_id = ? AND change_id = ?
|
|
49
|
+
`).bind(tenantId, changeId).first() as
|
|
50
|
+
{ authorization_id?: string; status?: string } | null;
|
|
51
|
+
if (current?.authorization_id !== authorizationId) return "missing";
|
|
52
|
+
if (current.status === "completed") return "completed";
|
|
53
|
+
const expiresAt = new Date(now.getTime() + DIAGNOSTIC_STUCK_AFTER_MS).toISOString();
|
|
54
|
+
const row = await db.prepare(`
|
|
55
|
+
UPDATE candidate_submit_leases SET expires_at = ?
|
|
56
|
+
WHERE tenant_id = ? AND change_id = ? AND authorization_id = ? AND status = 'active'
|
|
57
|
+
RETURNING authorization_id
|
|
58
|
+
`).bind(expiresAt, tenantId, changeId, authorizationId).first() as
|
|
59
|
+
{ authorization_id?: string } | null;
|
|
60
|
+
return row?.authorization_id === authorizationId ? "active" : "missing";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Mark the attempt immutable/idempotent while allowing a later submit to acquire. */
|
|
64
|
+
export async function completeCandidateSubmitLease(
|
|
65
|
+
db: D1Like,
|
|
66
|
+
tenantId: string,
|
|
67
|
+
changeId: string,
|
|
68
|
+
authorizationId: string,
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
await db.prepare(`
|
|
71
|
+
UPDATE candidate_submit_leases SET status = 'completed'
|
|
72
|
+
WHERE tenant_id = ? AND change_id = ? AND authorization_id = ? AND status = 'active'
|
|
73
|
+
`).bind(tenantId, changeId, authorizationId).run();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Release only this attempt; it can never unlock a newer submit. */
|
|
77
|
+
export async function releaseCandidateSubmitLease(
|
|
78
|
+
db: D1Like,
|
|
79
|
+
tenantId: string,
|
|
80
|
+
changeId: string,
|
|
81
|
+
authorizationId: string,
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
await db.prepare(`
|
|
84
|
+
DELETE FROM candidate_submit_leases
|
|
85
|
+
WHERE tenant_id = ? AND change_id = ? AND authorization_id = ? AND status = 'active'
|
|
86
|
+
`).bind(tenantId, changeId, authorizationId).run();
|
|
87
|
+
}
|