@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,7 +6,7 @@ import { withBase } from "@/lib/basePath";
|
|
|
6
6
|
const { envelope } = Astro.props;
|
|
7
7
|
const settings = getSettingsViewModel(envelope);
|
|
8
8
|
|
|
9
|
-
// Checkout styling editor
|
|
9
|
+
// Checkout styling editor. Renders the current CheckoutStyle and saves it
|
|
10
10
|
// through the versioned publish spine (POST /api/preview/checkout-style —
|
|
11
11
|
// colocated with reconcile/promote in the control-plane publish cluster). The
|
|
12
12
|
// endpoint is base-path-prefixed so it stays inside a path-routed tenant, and
|
|
@@ -607,7 +607,7 @@ function formatChangeSummary(change: string): string {
|
|
|
607
607
|
}
|
|
608
608
|
}
|
|
609
609
|
|
|
610
|
-
/* Checkout styling editor
|
|
610
|
+
/* Checkout styling editor */
|
|
611
611
|
.checkout-style-editor {
|
|
612
612
|
display: grid;
|
|
613
613
|
gap: 14px;
|
|
@@ -38,13 +38,13 @@ interface Props {
|
|
|
38
38
|
const { envelope } = Astro.props;
|
|
39
39
|
const cspNonce = Astro.locals.cspNonce;
|
|
40
40
|
|
|
41
|
-
// Real orders
|
|
41
|
+
// Real orders: fetch this tenant's checkout orders via the checkout order client
|
|
42
42
|
// and map to the view model. The per-tenant bearer is a server-side Worker secret;
|
|
43
43
|
// absent (or any upstream error) → an empty-but-valid model so the tab still
|
|
44
|
-
// renders
|
|
45
|
-
// endpoint
|
|
44
|
+
// renders. Live data is gated on the ToT-core orders
|
|
45
|
+
// endpoint — until then this resolves empty on qa.
|
|
46
46
|
const orderBearer = await readEnv("FOXY_ORDER_API_KEY");
|
|
47
|
-
// Server-backed filters + pagination
|
|
47
|
+
// Server-backed filters + pagination: the queue filter + cursor come from
|
|
48
48
|
// the URL and are applied by the order API server-side; payment/search remain
|
|
49
49
|
// client-side refinements on the returned page.
|
|
50
50
|
const orderFilters = parseOrderFilters(Astro.url.searchParams);
|
|
@@ -444,7 +444,7 @@ const holdCount = vm.rows.filter((r) => r.shipState === "hold_review_required").
|
|
|
444
444
|
const setLoading = () => root.setAttribute("data-orders-loading", "");
|
|
445
445
|
|
|
446
446
|
// Queue filter + Refresh + pagination are SERVER-backed: they navigate so the
|
|
447
|
-
// order API re-applies the filter/cursor and returns a fresh page
|
|
447
|
+
// order API re-applies the filter/cursor and returns a fresh page. Any
|
|
448
448
|
// filter change resets pagination to the first page (drops the cursor).
|
|
449
449
|
const navTo = (mutate) => {
|
|
450
450
|
const url = new URL(window.location.href);
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { ChromeNavItem, ChromeLink } from "@/lib/chrome/model";
|
|
19
19
|
import { withBase } from "@/lib/basePath";
|
|
20
|
+
import { isCurrentNavHref } from "@/lib/chrome/navCurrent";
|
|
20
21
|
|
|
21
22
|
interface Props {
|
|
22
23
|
item: ChromeNavItem;
|
|
@@ -32,6 +33,11 @@ const { basePath } = Astro.locals;
|
|
|
32
33
|
|
|
33
34
|
const itemHref = withBase(basePath, item.href);
|
|
34
35
|
const hrefOf = (l: ChromeLink) => (l.external ? l.href : withBase(basePath, l.href));
|
|
36
|
+
// Exact-path match only — a parent item's own href never gets aria-current
|
|
37
|
+
// just because the visitor is on a page nested under it. See navCurrent.ts.
|
|
38
|
+
const currentPath = Astro.url.pathname;
|
|
39
|
+
const itemCurrent = isCurrentNavHref(currentPath, itemHref);
|
|
40
|
+
const isChildCurrent = (l: ChromeLink) => isCurrentNavHref(currentPath, hrefOf(l), l.external);
|
|
35
41
|
const hasMenu =
|
|
36
42
|
(item.columns && item.columns.length > 0) ||
|
|
37
43
|
(item.children && item.children.length > 0);
|
|
@@ -77,6 +83,7 @@ const groups = isMega
|
|
|
77
83
|
<li>
|
|
78
84
|
<a
|
|
79
85
|
href={hrefOf(child)}
|
|
86
|
+
aria-current={isChildCurrent(child) ? "page" : undefined}
|
|
80
87
|
class="flex items-center gap-1.5 rounded-sm px-2 py-1.5 text-sm text-muted transition-colors hover:bg-bg hover:text-primary"
|
|
81
88
|
{...(child.external ? { rel: "noopener noreferrer" } : { "data-astro-prefetch": true })}
|
|
82
89
|
>
|
|
@@ -100,6 +107,7 @@ const groups = isMega
|
|
|
100
107
|
<a
|
|
101
108
|
href={itemHref}
|
|
102
109
|
data-astro-prefetch
|
|
110
|
+
aria-current={itemCurrent ? "page" : undefined}
|
|
103
111
|
class:list={[
|
|
104
112
|
"flex items-center gap-1.5 border-b border-border py-2.5 text-sm font-medium text-text transition-colors last:border-b-0 hover:text-accent",
|
|
105
113
|
item.flag && "text-accent",
|
|
@@ -119,6 +127,7 @@ const groups = isMega
|
|
|
119
127
|
<a
|
|
120
128
|
href={itemHref}
|
|
121
129
|
data-astro-prefetch
|
|
130
|
+
aria-current={itemCurrent ? "page" : undefined}
|
|
122
131
|
class:list={[
|
|
123
132
|
"inline-flex items-center gap-1 py-2 text-sm transition-colors hover:text-accent group-focus-within:text-accent",
|
|
124
133
|
item.flag && "font-semibold text-accent",
|
|
@@ -149,6 +158,7 @@ const groups = isMega
|
|
|
149
158
|
<li>
|
|
150
159
|
<a
|
|
151
160
|
href={hrefOf(child)}
|
|
161
|
+
aria-current={isChildCurrent(child) ? "page" : undefined}
|
|
152
162
|
class="flex items-center gap-1.5 rounded-sm px-2 py-1 text-sm text-muted transition-colors hover:bg-bg hover:text-primary"
|
|
153
163
|
{...(child.external ? { rel: "noopener noreferrer" } : { "data-astro-prefetch": true })}
|
|
154
164
|
>
|
|
@@ -171,6 +181,7 @@ const groups = isMega
|
|
|
171
181
|
<li>
|
|
172
182
|
<a
|
|
173
183
|
href={hrefOf(child)}
|
|
184
|
+
aria-current={isChildCurrent(child) ? "page" : undefined}
|
|
174
185
|
class="block rounded-sm px-3 py-1.5 text-sm text-muted transition-colors hover:bg-bg hover:text-primary"
|
|
175
186
|
{...(child.external ? { rel: "noopener noreferrer" } : { "data-astro-prefetch": true })}
|
|
176
187
|
>
|
|
@@ -187,6 +198,7 @@ const groups = isMega
|
|
|
187
198
|
<a
|
|
188
199
|
href={itemHref}
|
|
189
200
|
data-astro-prefetch
|
|
201
|
+
aria-current={itemCurrent ? "page" : undefined}
|
|
190
202
|
class:list={[
|
|
191
203
|
"inline-flex items-center gap-1 py-2 text-sm transition-colors hover:text-accent",
|
|
192
204
|
item.flag && "font-semibold text-accent",
|
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
* The newsletter is a plain POST form (progressive); it does not embed any cart
|
|
8
8
|
* or checkout logic (commerce boundary).
|
|
9
9
|
*/
|
|
10
|
-
import type { ChromeLink, FooterColumn, FooterNewsletter, SocialLink, ChromeLogo } from "@/lib/chrome/model";
|
|
10
|
+
import type { ChromeLink, FooterColumn, FooterNewsletter, SocialLink, ChromeLogo, FooterVariant } from "@/lib/chrome/model";
|
|
11
11
|
import { withBase } from "@/lib/basePath";
|
|
12
12
|
|
|
13
13
|
interface Props {
|
|
14
14
|
columns: FooterColumn[];
|
|
15
15
|
brand?: { logo?: ChromeLogo; wordmark?: string; contact?: string[] };
|
|
16
|
+
/** Content-shape variant (lib/chrome/model.ts FooterVariant). Defaults to
|
|
17
|
+
* "default" — the only member today; threaded through so a future variant
|
|
18
|
+
* is a drop-in without touching every caller. */
|
|
19
|
+
variant?: FooterVariant;
|
|
16
20
|
newsletter?: FooterNewsletter;
|
|
17
21
|
/** Payment method labels rendered as chips (e.g. "Visa", "PayPal"). */
|
|
18
22
|
payments?: string[];
|
|
@@ -29,6 +33,7 @@ interface Props {
|
|
|
29
33
|
const {
|
|
30
34
|
columns,
|
|
31
35
|
brand,
|
|
36
|
+
variant = "default",
|
|
32
37
|
newsletter,
|
|
33
38
|
payments = [],
|
|
34
39
|
social = [],
|
|
@@ -43,7 +48,7 @@ const hrefOf = (l: ChromeLink) => (l.external ? l.href : withBase(basePath, l.hr
|
|
|
43
48
|
const showLogo = !!brand?.logo && /^(https?:|\/)/.test(brand.logo.src);
|
|
44
49
|
---
|
|
45
50
|
|
|
46
|
-
<footer class:list={["border-t border-border bg-surface text-text", className]}>
|
|
51
|
+
<footer class:list={["border-t border-border bg-surface text-text", className]} data-variant={variant}>
|
|
47
52
|
<div class="container-prose py-12">
|
|
48
53
|
<div class="grid gap-10 md:grid-cols-[1.4fr_repeat(3,1fr)]">
|
|
49
54
|
<!-- Brand -->
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* <SiteHeader> — logo/wordmark + search island + account/auth + cart-count
|
|
4
4
|
* affordance, with the primary <MegaMenu> nav below and a mobile drawer (the
|
|
5
5
|
* <MobileNav> island). Tenant-agnostic; theme-token skinned. This is the
|
|
6
|
-
* CANONICAL site header
|
|
6
|
+
* CANONICAL site header — the former `components/nav/Header.astro`
|
|
7
7
|
* app-Layout implementation is retired; every feature it had (search typeahead,
|
|
8
8
|
* mobile drawer with focus-trap/scroll-lock, the session-aware auth entry point,
|
|
9
9
|
* the saved-items/wishlist cue, marketing header CTAs, a dark header variant)
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* COMMERCE BOUNDARY: the cart is a LINK only — no cart state, no add-to-cart.
|
|
14
14
|
* The count is a static affordance the managed cart can hydrate later.
|
|
15
15
|
*/
|
|
16
|
-
import type { ChromeNavItem, ChromeLogo, ChromeCta } from "@/lib/chrome/model";
|
|
16
|
+
import type { ChromeNavItem, ChromeLogo, ChromeCta, HeaderVariant } from "@/lib/chrome/model";
|
|
17
17
|
import MegaMenu from "@/components/chrome/MegaMenu.astro";
|
|
18
|
+
import NavDropdown from "@/components/chrome/NavDropdown.astro";
|
|
18
19
|
import SearchTypeahead from "@/components/islands/SearchTypeahead.tsx";
|
|
19
20
|
import MobileNav from "@/components/islands/MobileNav.tsx";
|
|
20
21
|
import { withBase } from "@/lib/basePath";
|
|
@@ -23,6 +24,11 @@ interface Props {
|
|
|
23
24
|
logo?: ChromeLogo;
|
|
24
25
|
/** Fallback wordmark text when no logo image. */
|
|
25
26
|
wordmark?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Home/brand link target (lib/chrome/model.ts ChromeBrand.href). Run through
|
|
29
|
+
* `withBase` same as every other same-origin href. Omit to default to "/".
|
|
30
|
+
*/
|
|
31
|
+
brandHref?: string;
|
|
26
32
|
nav?: ChromeNavItem[];
|
|
27
33
|
/** Search form action (GET). Omit to hide search. Mounts the live SearchTypeahead island. */
|
|
28
34
|
searchAction?: string;
|
|
@@ -41,6 +47,15 @@ interface Props {
|
|
|
41
47
|
cartCount?: number;
|
|
42
48
|
/** A brand-fixed "dark" header bar (e.g. a navy bar with a light logo) vs. the default light bar. */
|
|
43
49
|
variant?: "light" | "dark";
|
|
50
|
+
/**
|
|
51
|
+
* Content-shape variant (lib/chrome/model.ts HeaderVariant) — independent of
|
|
52
|
+
* `variant`'s light/dark TONE above. "minimal" (e.g. checkout/policy pages)
|
|
53
|
+
* renders brand + a reduced action set only: no search, no marketing CTAs,
|
|
54
|
+
* no saved-items affordance, no primary-nav bar, no mobile-menu toggle.
|
|
55
|
+
* Defaults to "primary" (the full header) so every existing caller is
|
|
56
|
+
* unaffected.
|
|
57
|
+
*/
|
|
58
|
+
layout?: HeaderVariant;
|
|
44
59
|
sticky?: boolean;
|
|
45
60
|
class?: string;
|
|
46
61
|
}
|
|
@@ -48,6 +63,7 @@ interface Props {
|
|
|
48
63
|
const {
|
|
49
64
|
logo,
|
|
50
65
|
wordmark,
|
|
66
|
+
brandHref,
|
|
51
67
|
nav = [],
|
|
52
68
|
searchAction,
|
|
53
69
|
announcement,
|
|
@@ -58,14 +74,16 @@ const {
|
|
|
58
74
|
cartHref,
|
|
59
75
|
cartCount = 0,
|
|
60
76
|
variant = "light",
|
|
77
|
+
layout = "primary",
|
|
61
78
|
sticky = true,
|
|
62
79
|
class: className,
|
|
63
80
|
} = Astro.props;
|
|
64
81
|
|
|
65
82
|
const { basePath } = Astro.locals;
|
|
66
|
-
const homeHref = withBase(basePath, "/");
|
|
83
|
+
const homeHref = withBase(basePath, brandHref ?? "/");
|
|
67
84
|
const showLogo = !!logo && /^(https?:|\/)/.test(logo.src);
|
|
68
85
|
const dark = variant === "dark";
|
|
86
|
+
const minimal = layout === "minimal";
|
|
69
87
|
|
|
70
88
|
const ctaClass = (v?: string) =>
|
|
71
89
|
v === "secondary" || v === "ghost"
|
|
@@ -86,6 +104,11 @@ const mobileNav = nav.map((item) => ({
|
|
|
86
104
|
href: withBase(basePath, child.href),
|
|
87
105
|
})),
|
|
88
106
|
}));
|
|
107
|
+
|
|
108
|
+
// Current-page path, for aria-current="page" — threaded to the MobileNav
|
|
109
|
+
// island (which can't read Astro.url itself) so it marks the same link the
|
|
110
|
+
// server-rendered nav (NavDropdown, via Astro.url internally) marks.
|
|
111
|
+
const currentPath = Astro.url.pathname;
|
|
89
112
|
---
|
|
90
113
|
|
|
91
114
|
<header class:list={[sticky && "sticky top-0 z-40", className]}>
|
|
@@ -133,7 +156,7 @@ const mobileNav = nav.map((item) => ({
|
|
|
133
156
|
|
|
134
157
|
<!-- Search (live typeahead island) -->
|
|
135
158
|
{
|
|
136
|
-
searchAction && (
|
|
159
|
+
!minimal && searchAction && (
|
|
137
160
|
<div class="hidden flex-1 md:flex">
|
|
138
161
|
<SearchTypeahead client:idle action={searchAction} />
|
|
139
162
|
</div>
|
|
@@ -143,7 +166,7 @@ const mobileNav = nav.map((item) => ({
|
|
|
143
166
|
<!-- Actions: saved items + marketing CTAs + auth + account + cart (link only) + mobile menu -->
|
|
144
167
|
<div class="ml-auto flex items-center gap-1">
|
|
145
168
|
{
|
|
146
|
-
savedItemsHref && (
|
|
169
|
+
!minimal && savedItemsHref && (
|
|
147
170
|
<a
|
|
148
171
|
href={withBase(basePath, savedItemsHref)}
|
|
149
172
|
aria-label="Saved items"
|
|
@@ -161,7 +184,7 @@ const mobileNav = nav.map((item) => ({
|
|
|
161
184
|
)
|
|
162
185
|
}
|
|
163
186
|
{
|
|
164
|
-
headerCtas.length > 0 && (
|
|
187
|
+
!minimal && headerCtas.length > 0 && (
|
|
165
188
|
<div class="hidden items-center gap-2 sm:flex">
|
|
166
189
|
{headerCtas.map((cta) => (
|
|
167
190
|
<a href={withBase(basePath, cta.href)} class={ctaClass(cta.variant)}>
|
|
@@ -220,9 +243,9 @@ const mobileNav = nav.map((item) => ({
|
|
|
220
243
|
|
|
221
244
|
<!-- Mobile menu: the MobileNav island (focus-trap, scroll-lock, Escape-to-close) -->
|
|
222
245
|
{
|
|
223
|
-
nav.length > 0 && (
|
|
246
|
+
!minimal && nav.length > 0 && (
|
|
224
247
|
<div class="md:hidden">
|
|
225
|
-
<MobileNav client:idle nav={mobileNav} announcement={announcement} />
|
|
248
|
+
<MobileNav client:idle nav={mobileNav} announcement={announcement} currentPath={currentPath} />
|
|
226
249
|
</div>
|
|
227
250
|
)
|
|
228
251
|
}
|
|
@@ -230,12 +253,25 @@ const mobileNav = nav.map((item) => ({
|
|
|
230
253
|
</div>
|
|
231
254
|
</div>
|
|
232
255
|
|
|
233
|
-
<!-- Primary nav bar
|
|
256
|
+
<!-- Primary nav bar: desktop MegaMenu (hidden md:block) + a no-JS-safe mobile
|
|
257
|
+
fallback (md:hidden) below it. The fallback renders the SAME nav data as
|
|
258
|
+
real, crawlable <a href> links via a native <details>/<summary> accordion
|
|
259
|
+
(NavDropdown mode="accordion") — it works with zero JavaScript, since it
|
|
260
|
+
is below the `md` breakpoint the ONLY nav affordance a no-JS visitor has
|
|
261
|
+
(the MobileNav drawer above is a client:idle island and renders nothing
|
|
262
|
+
without JS). Once MobileNav hydrates it hides this fallback (see its
|
|
263
|
+
[data-mobile-nav-fallback] effect) so JS-enabled visitors get the drawer
|
|
264
|
+
as the primary/enhanced experience; this is additive, not a replacement. -->
|
|
234
265
|
{
|
|
235
|
-
nav.length > 0 && (
|
|
266
|
+
!minimal && nav.length > 0 && (
|
|
236
267
|
<div class="border-b border-border bg-surface">
|
|
237
268
|
<div class="container-prose">
|
|
238
269
|
<MegaMenu items={nav} />
|
|
270
|
+
<nav aria-label="Mobile" data-mobile-nav-fallback class="md:hidden">
|
|
271
|
+
<ul class="grid">
|
|
272
|
+
{nav.map((item) => <NavDropdown item={item} mode="accordion" />)}
|
|
273
|
+
</ul>
|
|
274
|
+
</nav>
|
|
239
275
|
</div>
|
|
240
276
|
</div>
|
|
241
277
|
)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* require an of-age adult to sign; the wording is fixed here and the age
|
|
5
5
|
* reflects tenant.compliance.minAge (defaults to 21). Renders NOTHING when
|
|
6
6
|
* `enabled` is false. Driven by tenant.compliance.adultSignature — never hand-
|
|
7
|
-
* authored on a marketing page
|
|
7
|
+
* authored on a marketing page.
|
|
8
8
|
*/
|
|
9
9
|
import ComplianceNotice from "./ComplianceNotice.astro";
|
|
10
10
|
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* FDA NicotineWarning follows). It owns ONLY the chrome: the fixed legal text
|
|
6
6
|
* is supplied by each sibling widget via <slot />, and every sibling renders it
|
|
7
7
|
* ONLY when its tenant.compliance data is present. This component adds no copy
|
|
8
|
-
* of its own — it never fabricates legal wording
|
|
9
|
-
* compliance-widgets-platform-owned).
|
|
8
|
+
* of its own — it never fabricates legal wording.
|
|
10
9
|
*
|
|
11
10
|
* Variants — one visual treatment per placement, token-styled so they re-skin
|
|
12
11
|
* with the theme and never carry a hardcoded brand color:
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Excise-tax DISPLAY cue. Presentational only — it surfaces that applicable
|
|
4
4
|
* excise taxes apply (and, optionally, a per-jurisdiction breakdown for shopper
|
|
5
5
|
* transparency); it NEVER computes tax. The actual excise-tax CALCULATION is
|
|
6
|
-
* product/commerce-owned
|
|
7
|
-
* NOTHING when no note is supplied.
|
|
6
|
+
* product/commerce-owned. Renders NOTHING when no note is supplied.
|
|
8
7
|
*
|
|
9
8
|
* The optional jurisdiction breakdown is rendered as inline "label detail"
|
|
10
9
|
* spans (valid inside the primitive's paragraph) so it re-skins with the theme
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Postal Service carriage; only whether it renders (and the age) comes from
|
|
7
7
|
* tenant.compliance.pactAct / .minAge. Renders NOTHING when `enabled` is false,
|
|
8
8
|
* so absent data never shows placeholder legal text. Never hand-author this on a
|
|
9
|
-
* marketing page
|
|
9
|
+
* marketing page.
|
|
10
10
|
*/
|
|
11
11
|
import ComplianceNotice from "./ComplianceNotice.astro";
|
|
12
12
|
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* ("WARNING: … known to the State of California to cause …"), the ⚠ symbol, and
|
|
6
6
|
* the official warnings URL. Only the variable slots — harm endpoint(s) and the
|
|
7
7
|
* chemical name(s) named in the long form — come from tenant.compliance.prop65.
|
|
8
|
-
* Renders NOTHING when the config is absent, so it never fabricates a warning
|
|
9
|
-
* (decision compliance-widgets-platform-owned).
|
|
8
|
+
* Renders NOTHING when the config is absent, so it never fabricates a warning.
|
|
10
9
|
*
|
|
11
10
|
* `true` → short cancer + reproductive-harm warning. A Prop65Config selects the
|
|
12
11
|
* harm endpoint(s); providing `chemicals` switches to the long form.
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Purchase-quantity-limit DISPLAY. Surfaces a shopper-facing limit (e.g. state
|
|
4
4
|
* or PACT-Act order caps). Display surface ONLY — the limit text comes from
|
|
5
5
|
* tenant.compliance.purchaseLimit and the actual enforcement stays in the
|
|
6
|
-
* product/commerce layer, never hand-built here
|
|
7
|
-
*
|
|
8
|
-
* text) is absent.
|
|
6
|
+
* product/commerce layer, never hand-built here. Renders NOTHING when the
|
|
7
|
+
* config (or its text) is absent.
|
|
9
8
|
*/
|
|
10
9
|
import type { PurchaseLimitConfig } from "@tot/public-runtime";
|
|
11
10
|
import ComplianceNotice from "./ComplianceNotice.astro";
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Shipping-restriction DISPLAY notice (e.g. PACT Act no-ship states, adult
|
|
4
4
|
* signature, carrier limits). Presentational only — it renders the tenant's
|
|
5
5
|
* configured `compliance.shippingRestriction` text; the actual ship/no-ship
|
|
6
|
-
* DECISION for an address stays in the product/commerce layer
|
|
7
|
-
*
|
|
6
|
+
* DECISION for an address stays in the product/commerce layer. Renders
|
|
7
|
+
* NOTHING when `text` is absent.
|
|
8
8
|
*
|
|
9
9
|
* This is the platform component behind the footer shipping band the app Layout
|
|
10
10
|
* previously inlined — same data, same footer placement, now token-styled via
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* State/jurisdiction sale-eligibility DISPLAY notice. Presentational only — it
|
|
4
4
|
* SHOWS the tenant-configured eligibility messaging (which states a product is /
|
|
5
5
|
* isn't available in); it does NOT decide whether a given cart/address is
|
|
6
|
-
* eligible — that computation stays product-owned
|
|
7
|
-
*
|
|
8
|
-
* is supplied, so absent config never shows a placeholder.
|
|
6
|
+
* eligible — that computation stays product-owned. Renders NOTHING when no
|
|
7
|
+
* eligibility data is supplied, so absent config never shows a placeholder.
|
|
9
8
|
*
|
|
10
9
|
* Message precedence: an explicit `note` wins; otherwise a sentence is composed
|
|
11
10
|
* from `restrictedStates` ("Not available for sale in: …") or, failing that,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
3
|
* <TrustStrip> — the quiet row of proof tokens a store repeats across pages
|
|
4
|
-
* ("Ships from USA", "Lab-tested", a rating). REAL-OR-ABSENT by construction
|
|
5
|
-
*
|
|
6
|
-
* markup and any rating that isn't a real positive aggregate, reusing
|
|
4
|
+
* ("Ships from USA", "Lab-tested", a rating). REAL-OR-ABSENT by construction:
|
|
5
|
+
* sanitizeTrustStrip() drops any claim that is scraped review-widget
|
|
6
|
+
* markup and any rating that isn't a real positive aggregate, reusing the same
|
|
7
7
|
* detector. If nothing real is left the strip renders NOTHING — never a
|
|
8
|
-
* fabricated badge. Plain text + one rating glyph; styled from
|
|
8
|
+
* fabricated badge. Plain text + one rating glyph; styled from design tokens.
|
|
9
9
|
* data-widget="trust-strip".
|
|
10
10
|
*/
|
|
11
11
|
import type { TrustStripItem } from "@/lib/content/trustStrip";
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* prefers-reduced-motion.
|
|
12
12
|
*/
|
|
13
13
|
import { useCallback, useEffect, useRef, useState } from "preact/hooks";
|
|
14
|
+
import { isCurrentNavHref } from "@/lib/chrome/navCurrent";
|
|
14
15
|
|
|
15
16
|
interface NavChild {
|
|
16
17
|
label: string;
|
|
@@ -25,9 +26,13 @@ interface NavItem {
|
|
|
25
26
|
interface Props {
|
|
26
27
|
nav: NavItem[];
|
|
27
28
|
announcement?: string;
|
|
29
|
+
/** Current request path (Astro.url.pathname), for aria-current="page" — see navCurrent.ts. */
|
|
30
|
+
currentPath?: string;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
const DRAWER_ID = "mobile-drawer";
|
|
34
|
+
/** Matches SiteHeader.astro's no-JS fallback <nav data-mobile-nav-fallback>. */
|
|
35
|
+
const FALLBACK_NAV_SELECTOR = "[data-mobile-nav-fallback]";
|
|
31
36
|
|
|
32
37
|
function prefersReducedMotion(): boolean {
|
|
33
38
|
return (
|
|
@@ -37,7 +42,7 @@ function prefersReducedMotion(): boolean {
|
|
|
37
42
|
);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
export default function MobileNav({ nav, announcement }: Props) {
|
|
45
|
+
export default function MobileNav({ nav, announcement, currentPath }: Props) {
|
|
41
46
|
const [open, setOpen] = useState(false);
|
|
42
47
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
43
48
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
@@ -45,6 +50,17 @@ export default function MobileNav({ nav, announcement }: Props) {
|
|
|
45
50
|
|
|
46
51
|
const close = useCallback(() => setOpen(false), []);
|
|
47
52
|
|
|
53
|
+
// The no-JS fallback nav is only needed when JS never runs. Since this
|
|
54
|
+
// component mounting IS JS running, hide it in favor of the drawer above —
|
|
55
|
+
// additive progressive enhancement, not a replacement of either layer.
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
document
|
|
58
|
+
.querySelectorAll<HTMLElement>(FALLBACK_NAV_SELECTOR)
|
|
59
|
+
.forEach((el) => {
|
|
60
|
+
el.hidden = true;
|
|
61
|
+
});
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
48
64
|
// Body scroll lock while open.
|
|
49
65
|
useEffect(() => {
|
|
50
66
|
if (!open) return;
|
|
@@ -202,6 +218,9 @@ export default function MobileNav({ nav, announcement }: Props) {
|
|
|
202
218
|
<div class="px-3 py-2">
|
|
203
219
|
<a
|
|
204
220
|
href={item.href}
|
|
221
|
+
aria-current={
|
|
222
|
+
currentPath && isCurrentNavHref(currentPath, item.href) ? "page" : undefined
|
|
223
|
+
}
|
|
205
224
|
class="block font-display text-base font-medium text-text hover:text-primary"
|
|
206
225
|
>
|
|
207
226
|
{item.label}
|
|
@@ -211,6 +230,9 @@ export default function MobileNav({ nav, announcement }: Props) {
|
|
|
211
230
|
<li key={child.href}>
|
|
212
231
|
<a
|
|
213
232
|
href={child.href}
|
|
233
|
+
aria-current={
|
|
234
|
+
currentPath && isCurrentNavHref(currentPath, child.href) ? "page" : undefined
|
|
235
|
+
}
|
|
214
236
|
class="block rounded px-2 py-1.5 font-body text-sm text-muted hover:bg-border/40 hover:text-text"
|
|
215
237
|
>
|
|
216
238
|
{child.label}
|
|
@@ -222,6 +244,9 @@ export default function MobileNav({ nav, announcement }: Props) {
|
|
|
222
244
|
) : (
|
|
223
245
|
<a
|
|
224
246
|
href={item.href}
|
|
247
|
+
aria-current={
|
|
248
|
+
currentPath && isCurrentNavHref(currentPath, item.href) ? "page" : undefined
|
|
249
|
+
}
|
|
225
250
|
class="block rounded px-3 py-2.5 font-display text-base font-medium text-text hover:bg-border/40 hover:text-primary"
|
|
226
251
|
>
|
|
227
252
|
{item.label}
|
|
@@ -27,6 +27,7 @@ import type {
|
|
|
27
27
|
} from "@tot/public-runtime";
|
|
28
28
|
import { priceForPurchaseOption } from "@tot/public-runtime";
|
|
29
29
|
import { formatPrice } from "@/lib/format";
|
|
30
|
+
import { useCheckoutHandoffCorrelation } from "@/components/islands/useCheckoutHandoffCorrelation";
|
|
30
31
|
|
|
31
32
|
interface OptionDef {
|
|
32
33
|
name: string;
|
|
@@ -55,7 +56,7 @@ interface Props {
|
|
|
55
56
|
byVariantId: Record<string, Record<string, VariantCartPayload>>;
|
|
56
57
|
};
|
|
57
58
|
/**
|
|
58
|
-
* Membership pricing
|
|
59
|
+
* Membership pricing. Present only when an eligible member is resolved
|
|
59
60
|
* for this request (Phase 2 shopper login). Undefined → list pricing only,
|
|
60
61
|
* which is the Phase-1 anonymous behavior.
|
|
61
62
|
*/
|
|
@@ -63,6 +64,13 @@ interface Props {
|
|
|
63
64
|
membershipRule: MembershipTierRule;
|
|
64
65
|
stacking: MembershipStackingPolicy;
|
|
65
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* Base-path-aware URL of the checkout-correlation mint endpoint
|
|
69
|
+
* (ext-commerce-10). When a store is wired the island posts the shopper's live
|
|
70
|
+
* consent snapshot here and, if the server attributes, carries the returned
|
|
71
|
+
* passthrough field on the managed-cart form. Undefined → no correlation.
|
|
72
|
+
*/
|
|
73
|
+
handoffUrl?: string;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
const LOW_STOCK_THRESHOLD = 5;
|
|
@@ -89,7 +97,14 @@ export default function VariantSelector({
|
|
|
89
97
|
productTitle,
|
|
90
98
|
checkoutCart,
|
|
91
99
|
pricing,
|
|
100
|
+
handoffUrl,
|
|
92
101
|
}: Props) {
|
|
102
|
+
// Checkout-correlation handoff mint (ext-commerce-10): resolved once the server
|
|
103
|
+
// attributes this shopper (analytics consent granted + signing secret present),
|
|
104
|
+
// else null → the buy form carries no correlation and the order is unattributed.
|
|
105
|
+
const correlationField = useCheckoutHandoffCorrelation(
|
|
106
|
+
checkoutCart ? handoffUrl : undefined,
|
|
107
|
+
);
|
|
93
108
|
const sortedOptions = useMemo(
|
|
94
109
|
() => [...options].sort((a, b) => a.position - b.position),
|
|
95
110
|
[options],
|
|
@@ -219,7 +234,7 @@ export default function VariantSelector({
|
|
|
219
234
|
}
|
|
220
235
|
}
|
|
221
236
|
|
|
222
|
-
// Compose member + subscription pricing
|
|
237
|
+
// Compose member + subscription pricing. The pricing context carries the
|
|
223
238
|
// resolved member tier only when an eligible member was resolved server-side;
|
|
224
239
|
// otherwise it is a non-member and list pricing stands.
|
|
225
240
|
const pricingCtx = pricing
|
|
@@ -474,6 +489,16 @@ export default function VariantSelector({
|
|
|
474
489
|
value={String(quantity)}
|
|
475
490
|
/>
|
|
476
491
|
)}
|
|
492
|
+
{/* Checkout-correlation passthrough field (ext-commerce-10). Rides the
|
|
493
|
+
handoff as a never-signed hidden field the provider echoes back on
|
|
494
|
+
the order webhook; absent when unattributed. */}
|
|
495
|
+
{canBuy && correlationField && (
|
|
496
|
+
<input
|
|
497
|
+
type="hidden"
|
|
498
|
+
name={correlationField.name}
|
|
499
|
+
value={correlationField.value}
|
|
500
|
+
/>
|
|
501
|
+
)}
|
|
477
502
|
<button
|
|
478
503
|
type="submit"
|
|
479
504
|
disabled={!canBuy}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client hook for the checkout-correlation handoff mint (ext-commerce-10).
|
|
3
|
+
*
|
|
4
|
+
* The shopper's analytics consent is a live CLIENT-side value, but the correlation
|
|
5
|
+
* token is minted under the SERVER-only signing secret. So at the handoff this hook
|
|
6
|
+
* posts the current consent snapshot (the renderer-owned `__TOT_INTEGRATION_CONSENT__`
|
|
7
|
+
* global) to `POST /api/commerce/checkout-handoff` and, when the server attributes,
|
|
8
|
+
* returns the opaque passthrough cart field for the managed-cart form to carry.
|
|
9
|
+
*
|
|
10
|
+
* Fail-safe by construction: no handoff URL, a network error, or an unattributed
|
|
11
|
+
* response yields `null` — the buy form renders and submits with NO correlation field,
|
|
12
|
+
* so the order is simply unattributed and checkout is never blocked.
|
|
13
|
+
*/
|
|
14
|
+
import { useEffect, useState } from "preact/hooks";
|
|
15
|
+
|
|
16
|
+
/** The renderer-owned frozen consent-snapshot global (`render-phases.ts`). */
|
|
17
|
+
const INTEGRATION_CONSENT_GLOBAL = "__TOT_INTEGRATION_CONSENT__";
|
|
18
|
+
|
|
19
|
+
/** The opaque passthrough cart field to attach to the managed-cart form. */
|
|
20
|
+
export interface HandoffCartField {
|
|
21
|
+
name: string;
|
|
22
|
+
value: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Read the `{ attributed, cartField }` handoff response fail-closed into a cart field,
|
|
27
|
+
* or `null` for any non-attributed / malformed shape. Pure — unit-tested directly.
|
|
28
|
+
*/
|
|
29
|
+
export function parseHandoffCartField(data: unknown): HandoffCartField | null {
|
|
30
|
+
if (!data || typeof data !== "object") return null;
|
|
31
|
+
const d = data as Record<string, unknown>;
|
|
32
|
+
if (d.attributed !== true) return null;
|
|
33
|
+
const field = d.cartField;
|
|
34
|
+
if (!field || typeof field !== "object") return null;
|
|
35
|
+
const { name, value } = field as Record<string, unknown>;
|
|
36
|
+
return typeof name === "string" && name.length > 0 && typeof value === "string" && value.length > 0
|
|
37
|
+
? { name, value }
|
|
38
|
+
: null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The live consent snapshot the renderer inlined + the CMP/native banner updates. */
|
|
42
|
+
function readClientConsent(): unknown {
|
|
43
|
+
return (globalThis as Record<string, unknown>)[INTEGRATION_CONSENT_GLOBAL] ?? null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fetch the handoff correlation for this page. Returns the cart field to attach once
|
|
48
|
+
* the server attributes, else `null`. `handoffUrl` absent (no store / marketing
|
|
49
|
+
* tenant) is a no-op.
|
|
50
|
+
*/
|
|
51
|
+
export function useCheckoutHandoffCorrelation(handoffUrl?: string): HandoffCartField | null {
|
|
52
|
+
const [field, setField] = useState<HandoffCartField | null>(null);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!handoffUrl) return;
|
|
56
|
+
let cancelled = false;
|
|
57
|
+
fetch(handoffUrl, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "content-type": "application/json" },
|
|
60
|
+
body: JSON.stringify({ consent: readClientConsent() }),
|
|
61
|
+
})
|
|
62
|
+
.then((res) => res.json())
|
|
63
|
+
.then((data) => {
|
|
64
|
+
if (!cancelled) setField(parseHandoffCartField(data));
|
|
65
|
+
})
|
|
66
|
+
.catch(() => {
|
|
67
|
+
// Fail-safe: leave the order unattributed; never disturb the buy form.
|
|
68
|
+
});
|
|
69
|
+
return () => {
|
|
70
|
+
cancelled = true;
|
|
71
|
+
};
|
|
72
|
+
}, [handoffUrl]);
|
|
73
|
+
|
|
74
|
+
return field;
|
|
75
|
+
}
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* offer" affordance shared by <TierCards>, <TierComparisonTable>, and the
|
|
5
5
|
* subscription <SavingsExplainer>. When a group shows illustrative figures,
|
|
6
6
|
* the widgets render this so an example price / savings number is never
|
|
7
|
-
* mistaken for a live offer
|
|
8
|
-
* education only, no pricing mechanics).
|
|
7
|
+
* mistaken for a live offer — education only, no pricing mechanics.
|
|
9
8
|
*
|
|
10
9
|
* Two forms:
|
|
11
10
|
* - "badge" a compact pill placed at the group header (the visible marker)
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* checkout, or subscription mechanics — each tier's CTA is a plain handoff link
|
|
7
7
|
* into the tenant's own (product-owned) subscribe/learn flow.
|
|
8
8
|
*
|
|
9
|
-
* Illustrative-figures affordance
|
|
10
|
-
*
|
|
9
|
+
* Illustrative-figures affordance: a tier's figure is treated as an EXAMPLE
|
|
10
|
+
* by default (isIllustrative). When any
|
|
11
11
|
* shown tier is illustrative the group header carries a first-class
|
|
12
12
|
* <IllustrativeLabel> badge, each such figure is prefixed with an "e.g."
|
|
13
13
|
* affordance + data-illustrative hook, and a footnote spells it out — so an
|