@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/astro.config.mjs +3 -3
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +12 -14
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +4 -4
- package/apps/storefront/src/layouts/Layout.astro +100 -20
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
- package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
- package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/apps/adminService.ts +10 -0
- package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +264 -10
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/blog/collection.ts +97 -10
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +57 -3
- package/apps/storefront/src/lib/blog/types.ts +9 -9
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +12 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +18 -6
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
- package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
- package/apps/storefront/src/lib/newsletter/send.ts +3 -3
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/seo/alternates.ts +1 -1
- package/apps/storefront/src/lib/social/notify.ts +4 -4
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +9 -4
- package/apps/storefront/src/middleware/index.ts +273 -51
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +16 -2
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
- package/apps/storefront/src/pages/blog/[slug].astro +5 -5
- package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
- package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
- package/apps/storefront/src/pages/blog/index.astro +2 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
- package/apps/storefront/src/pages/cockpit.astro +61 -1362
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
- package/apps/storefront/src/pages/the-build/index.astro +2 -2
- package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
- package/apps/storefront/src/styles/admin.css +27 -1
- package/apps/storefront/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +41 -37
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-runtime.ts +32 -1
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +16 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +5 -5
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +45 -9
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/dev/file-browser.mjs +2 -2
- package/scripts/dev/file-writer.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/tenant-source-root.mjs +1 -1
- package/scripts/tot-dev.mjs +2 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared "buffer-or-stream on `Accept`" plumbing for a long-running admin
|
|
3
|
+
* route. ONE emit-driven work function serves BOTH transports:
|
|
4
|
+
*
|
|
5
|
+
* - a non-streaming caller (the CLI, any `Accept: application/json` client) gets
|
|
6
|
+
* the buffered terminal JSON body + status the work resolved — `emit` is a no-op;
|
|
7
|
+
* - a streaming caller (`Accept: text/event-stream`, the admin Publish tab) gets
|
|
8
|
+
* each `emit(event, data)` as an SSE frame AS IT HAPPENS, then a terminal
|
|
9
|
+
* `result` event carrying the SAME payload the JSON body would have, then close.
|
|
10
|
+
*
|
|
11
|
+
* The work runs ONCE either way; the terminal payload + status are IDENTICAL. Only
|
|
12
|
+
* the delivery differs. This is the generalized form of the pattern
|
|
13
|
+
* `POST /api/preview/build` first shipped inline — extracted here so every
|
|
14
|
+
* long-running route (build / integrate / rebuild / ship) streams the same way
|
|
15
|
+
* without re-implementing the ReadableStream/encoder/close dance.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A buffered JSON Response — the non-streaming terminal body. Inlined (not imported
|
|
20
|
+
* from the change/grants controlplane) so this generic transport helper stays in the
|
|
21
|
+
* `lib/publish` render layer, respecting the renderer-no-controlplane boundary.
|
|
22
|
+
*/
|
|
23
|
+
function jsonResponse(body: unknown, status = 200): Response {
|
|
24
|
+
return new Response(JSON.stringify(body), {
|
|
25
|
+
status,
|
|
26
|
+
headers: { "content-type": "application/json", "cache-control": "no-store" },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Emit one SSE event. A no-op in buffered (JSON) mode. */
|
|
31
|
+
export type SseEmit = (event: string, data: Record<string, unknown>) => void;
|
|
32
|
+
|
|
33
|
+
/** The terminal outcome of the work: the HTTP status + the JSON/`result` payload. */
|
|
34
|
+
export interface StreamWorkResult {
|
|
35
|
+
status: number;
|
|
36
|
+
payload: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** True when the caller asked for an SSE stream (`Accept: text/event-stream`). */
|
|
40
|
+
export function wantsEventStream(request: Request): boolean {
|
|
41
|
+
return (request.headers.get("accept") || "").includes("text/event-stream");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Run `work` and deliver its terminal payload as either a buffered JSON Response
|
|
46
|
+
* (default) or a streamed SSE Response (when the caller sent
|
|
47
|
+
* `Accept: text/event-stream`). In stream mode each `emit` becomes an
|
|
48
|
+
* `event: <name>\ndata: <json>\n\n` frame; the terminal payload is sent as a
|
|
49
|
+
* `result` event and the stream closes. `onError` maps an UNEXPECTED throw (the
|
|
50
|
+
* work is expected to be fail-safe and return its own error payload) to a terminal
|
|
51
|
+
* result — in JSON mode it becomes the response body+status, in SSE mode the
|
|
52
|
+
* `result` event.
|
|
53
|
+
*/
|
|
54
|
+
export async function respondBufferedOrStreamed(
|
|
55
|
+
request: Request,
|
|
56
|
+
work: (emit: SseEmit) => Promise<StreamWorkResult>,
|
|
57
|
+
opts?: { onError?: (error: unknown) => StreamWorkResult },
|
|
58
|
+
): Promise<Response> {
|
|
59
|
+
const onError =
|
|
60
|
+
opts?.onError ??
|
|
61
|
+
((error: unknown): StreamWorkResult => ({
|
|
62
|
+
status: 500,
|
|
63
|
+
payload: {
|
|
64
|
+
ok: false,
|
|
65
|
+
outcome: "failed",
|
|
66
|
+
errors: [`operation failed: ${error instanceof Error ? error.message : String(error)}`],
|
|
67
|
+
},
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
// Buffered JSON mode — the CLI / any non-streaming caller. `emit` is a no-op; the
|
|
71
|
+
// work's own status + payload are returned verbatim (same contract as before).
|
|
72
|
+
if (!wantsEventStream(request)) {
|
|
73
|
+
try {
|
|
74
|
+
const { status, payload } = await work(() => {});
|
|
75
|
+
return jsonResponse(payload, status);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
const { status, payload } = onError(error);
|
|
78
|
+
return jsonResponse(payload, status);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// SSE mode — stream each phase as it happens, then a terminal `result` event
|
|
83
|
+
// carrying the SAME payload the JSON body would have, then close. Workers stream a
|
|
84
|
+
// ReadableStream body natively — no infra.
|
|
85
|
+
const encoder = new TextEncoder();
|
|
86
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
87
|
+
start(controller) {
|
|
88
|
+
const send: SseEmit = (event, data) => {
|
|
89
|
+
try {
|
|
90
|
+
controller.enqueue(encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`));
|
|
91
|
+
} catch {
|
|
92
|
+
/* controller already closed (client disconnected) — nothing to do */
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
void (async () => {
|
|
96
|
+
try {
|
|
97
|
+
const { payload } = await work(send);
|
|
98
|
+
send("result", payload);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
send("result", onError(error).payload);
|
|
101
|
+
} finally {
|
|
102
|
+
try {
|
|
103
|
+
controller.close();
|
|
104
|
+
} catch {
|
|
105
|
+
/* already closed */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
return new Response(stream, {
|
|
112
|
+
status: 200,
|
|
113
|
+
headers: {
|
|
114
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
115
|
+
"cache-control": "no-cache, no-transform",
|
|
116
|
+
"x-accel-buffering": "no",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure formatting for the admin Publish tab's shared SCROLLING LIVE-LOG console —
|
|
3
|
+
* the "CI build log" panel reused across ALL FOUR long-running flows (build /
|
|
4
|
+
* integrate / rebuild / ship). Where `buildStream.ts` owns the build-flight
|
|
5
|
+
* stage-RAIL (checkmarks), this owns the LOG: one human-readable, timestamped line
|
|
6
|
+
* appended per server-pushed `phase` event, then a terminal ✓/✗ line.
|
|
7
|
+
*
|
|
8
|
+
* It is transport-agnostic (it consumes already-parsed {@link SseEvent}s from
|
|
9
|
+
* `parseSseChunk`) and flow-agnostic: ONE phase→label map covers every phase any of
|
|
10
|
+
* the four routes emits, so the SAME console renders an integrate merge, a ship
|
|
11
|
+
* promote, or a build read with no per-flow branching. The terminal-line extractor
|
|
12
|
+
* reads the union of the four routes' honest result shapes (build's
|
|
13
|
+
* `ok`/`outcome`/`errors`, the queue's `ok`/`queueState`/`statusMessage`/`reason`,
|
|
14
|
+
* ship's `ok`/`state`/`message`) without any one flow's assumptions leaking in.
|
|
15
|
+
*
|
|
16
|
+
* The DOM wiring that appends these to a scrollable `<div>` and auto-scrolls is
|
|
17
|
+
* verified on a live preview, per this codebase's client-island convention (see
|
|
18
|
+
* `reviewReleaseWorkspace/controller.ts`). Only this pure formatting is unit-tested.
|
|
19
|
+
*/
|
|
20
|
+
import type { SseEvent } from "./buildStream.js";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The human label for every `phase` a streamed route emits, across all four flows.
|
|
24
|
+
* Deliberately honest-but-neutral wording so the SAME line reads true whether the
|
|
25
|
+
* phase came from a candidate build or an aggregate rebuild.
|
|
26
|
+
*/
|
|
27
|
+
export const STREAM_PHASE_LABEL: Record<string, string> = {
|
|
28
|
+
// Accept-to-preview (integrate) — the queue's merge boundary.
|
|
29
|
+
merging: "Merging your change into the shared preview…",
|
|
30
|
+
// Build (candidate) — head resolution.
|
|
31
|
+
resolving: "Locating the pull request's latest commit…",
|
|
32
|
+
// Shared reconcile boundaries (build candidate + aggregate integrate/rebuild).
|
|
33
|
+
reading: "Reading and validating your store's files…",
|
|
34
|
+
publishing: "Building and publishing this version…",
|
|
35
|
+
checking: "Re-running the combined evidence checks…",
|
|
36
|
+
// Publish-live (ship) — the orchestrator's post-gate boundaries.
|
|
37
|
+
pinning: "Pinning the exact reviewed build…",
|
|
38
|
+
promoting: "Promoting the reviewed build to your live channel…",
|
|
39
|
+
verifying: "Verifying your live store serves the new build…",
|
|
40
|
+
recording: "Recording the release in your ledger…",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The label for one phase: a server-supplied `label` wins (a route may send a more
|
|
45
|
+
* specific line, e.g. build's "Locating the latest commit for PR #12…"), else the
|
|
46
|
+
* shared map, else `null` for an unknown phase (the caller ignores it — never
|
|
47
|
+
* fabricates a line for a phase it doesn't understand).
|
|
48
|
+
*/
|
|
49
|
+
export function phaseLogLabel(phase: string, serverLabel?: string | null): string | null {
|
|
50
|
+
if (serverLabel && serverLabel.trim()) return serverLabel.trim();
|
|
51
|
+
return STREAM_PHASE_LABEL[phase] ?? null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** `m:ss` elapsed, matching the dialog's own elapsed-clock format. Clamps negatives to 0. */
|
|
55
|
+
export function formatElapsed(ms: number): string {
|
|
56
|
+
const s = Math.max(0, Math.floor(ms / 1000));
|
|
57
|
+
return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The log line for a `phase` event, or `null` to skip (unknown phase / not a phase
|
|
62
|
+
* event). Shape: `[m:ss] <label>`. Pure — the console appends the returned string.
|
|
63
|
+
*/
|
|
64
|
+
export function logLineForPhase(
|
|
65
|
+
ev: SseEvent,
|
|
66
|
+
elapsedMs: number,
|
|
67
|
+
): string | null {
|
|
68
|
+
if (ev.event !== "phase") return null;
|
|
69
|
+
let parsed: unknown = null;
|
|
70
|
+
try {
|
|
71
|
+
parsed = JSON.parse(ev.data);
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const phase = parsed && typeof (parsed as { phase?: unknown }).phase === "string"
|
|
76
|
+
? (parsed as { phase: string }).phase
|
|
77
|
+
: null;
|
|
78
|
+
if (!phase) return null;
|
|
79
|
+
const serverLabel =
|
|
80
|
+
parsed && typeof (parsed as { label?: unknown }).label === "string"
|
|
81
|
+
? (parsed as { label: string }).label
|
|
82
|
+
: null;
|
|
83
|
+
const label = phaseLogLabel(phase, serverLabel);
|
|
84
|
+
if (!label) return null;
|
|
85
|
+
return `[${formatElapsed(elapsedMs)}] ${label}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** A terminal result across the four flows — the union of their honest shapes. */
|
|
89
|
+
export interface StreamTerminalResult {
|
|
90
|
+
ok?: boolean;
|
|
91
|
+
/** build */
|
|
92
|
+
outcome?: string | null;
|
|
93
|
+
errors?: string[] | null;
|
|
94
|
+
error?: string | null;
|
|
95
|
+
/** integrate/rebuild */
|
|
96
|
+
queueState?: string | null;
|
|
97
|
+
statusMessage?: string | null;
|
|
98
|
+
reason?: string | null;
|
|
99
|
+
/** ship */
|
|
100
|
+
state?: string | null;
|
|
101
|
+
message?: string | null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** True iff the terminal result is a clean success, reading each flow's own success signal. */
|
|
105
|
+
export function isStreamResultOk(result: StreamTerminalResult): boolean {
|
|
106
|
+
if (result.state !== undefined && result.state !== null) return result.state === "shipped";
|
|
107
|
+
if (result.ok === true) return true;
|
|
108
|
+
return result.outcome === "ok";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** A SHORT one-line reason for a non-ok terminal (the full detail lives in the outcome phase). */
|
|
112
|
+
function streamResultReason(result: StreamTerminalResult): string {
|
|
113
|
+
const first = (v: string[] | null | undefined) => (v && v.length ? v[0] : "");
|
|
114
|
+
const candidate =
|
|
115
|
+
result.statusMessage ||
|
|
116
|
+
result.message ||
|
|
117
|
+
first(result.errors) ||
|
|
118
|
+
result.error ||
|
|
119
|
+
(result.outcome === "blocked" ? "a check blocked this build" : "") ||
|
|
120
|
+
result.reason ||
|
|
121
|
+
result.queueState ||
|
|
122
|
+
result.state ||
|
|
123
|
+
"it did not complete";
|
|
124
|
+
const line = String(candidate).replace(/\s+/g, " ").trim();
|
|
125
|
+
return line.length > 160 ? line.slice(0, 157) + "…" : line;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The terminal log line for a `result` event: `{ ok, line }`. `line` is
|
|
130
|
+
* `[m:ss] ✓ Done.` on success, `[m:ss] ✗ Stopped — <short reason>` otherwise. Pure.
|
|
131
|
+
*/
|
|
132
|
+
export function terminalLogLine(
|
|
133
|
+
result: StreamTerminalResult,
|
|
134
|
+
elapsedMs: number,
|
|
135
|
+
): { ok: boolean; line: string } {
|
|
136
|
+
const ok = isStreamResultOk(result);
|
|
137
|
+
const stamp = `[${formatElapsed(elapsedMs)}]`;
|
|
138
|
+
return {
|
|
139
|
+
ok,
|
|
140
|
+
line: ok ? `${stamp} ✓ Done.` : `${stamp} ✗ Stopped — ${streamResultReason(result)}`,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The merchant-facing version counter — a monotonic, human "v1, v2, …" label for
|
|
3
|
+
* the tenant's published versions, DERIVED from the live-promotion history so it is
|
|
4
|
+
* never fabricated and never the raw SHA (which is meaningless to a merchant).
|
|
5
|
+
*
|
|
6
|
+
* A version's number is its rank by FIRST promotion to `live`: the earliest-ever
|
|
7
|
+
* promoted sha is v1, the next distinct sha is v2, and so on. This has the two
|
|
8
|
+
* properties merchants expect:
|
|
9
|
+
* - monotonic increasing — a newer publish always gets a higher number; and
|
|
10
|
+
* - stable for a given sha — a rollback re-points `live` at a prior version (per
|
|
11
|
+
* `PromotionEvent`'s contract a plain rollback is not even recorded, and a
|
|
12
|
+
* re-promote keeps the sha's ORIGINAL first-promotion rank), so "rolled back to
|
|
13
|
+
* v3" reads honestly and a subsequent fresh publish becomes v(N+1), never
|
|
14
|
+
* reusing a retired number.
|
|
15
|
+
*
|
|
16
|
+
* Pure (no I/O) so it unit-tests without a Worker/KV, and shared by both the
|
|
17
|
+
* live-version resolver (the "Live version" metric) and the versions-panel view so
|
|
18
|
+
* the two surfaces always agree on a version's number.
|
|
19
|
+
*/
|
|
20
|
+
import type { PromotionEvent } from "@tot/public-runtime";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Map each promoted versionId → its 1-based version number, ranked by earliest
|
|
24
|
+
* promotion time. Input order is irrelevant (the rank is computed from the `at`
|
|
25
|
+
* timestamps), so it accepts the newest-first list `listPromotions` returns as-is.
|
|
26
|
+
* A tie on `at` between two distinct shas is broken by versionId for determinism.
|
|
27
|
+
*/
|
|
28
|
+
export function assignVersionNumbers(promotions: PromotionEvent[]): Map<string, number> {
|
|
29
|
+
const earliest = new Map<string, string>();
|
|
30
|
+
for (const p of promotions) {
|
|
31
|
+
const seen = earliest.get(p.versionId);
|
|
32
|
+
if (seen == null || p.at < seen) earliest.set(p.versionId, p.at);
|
|
33
|
+
}
|
|
34
|
+
const ordered = [...earliest.entries()].sort((a, b) =>
|
|
35
|
+
a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0,
|
|
36
|
+
);
|
|
37
|
+
const numbers = new Map<string, number>();
|
|
38
|
+
ordered.forEach(([versionId], i) => numbers.set(versionId, i + 1));
|
|
39
|
+
return numbers;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The version number for one versionId, or `null` when nothing in the promotion
|
|
44
|
+
* history matches (an unpromoted/unknown sha — the label falls back to the raw SHA).
|
|
45
|
+
*/
|
|
46
|
+
export function versionNumberFor(
|
|
47
|
+
promotions: PromotionEvent[],
|
|
48
|
+
versionId: string | null | undefined,
|
|
49
|
+
): number | null {
|
|
50
|
+
if (!versionId) return null;
|
|
51
|
+
return assignVersionNumbers(promotions).get(versionId) ?? null;
|
|
52
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Canonical RAW chrome renderer
|
|
2
|
+
* Canonical RAW chrome renderer.
|
|
3
3
|
*
|
|
4
4
|
* ONE platform-shipped chrome wrapper, driven entirely by a tenant's `chrome.json`
|
|
5
|
-
* (typed `
|
|
5
|
+
* (typed `ChromeConfig`, lib/chrome/model.ts) — CONTENT ONLY. It replaces the three
|
|
6
6
|
* divergent, hand-authored `content/<tenant>/chrome.html` documents with a single
|
|
7
7
|
* generator, so chrome lives in ONE place and a tenant supplies only data. The
|
|
8
8
|
* output is a full wrapper document carrying the splice markers the rawMarketingHtml
|
|
9
9
|
* pipeline understands (`<!--PAGE_HEAD-->` / `<!--PAGE_BODY-->`), so body-only page
|
|
10
10
|
* fragments get wrapped exactly as before (see lib/rawMarketingHtml.ts,
|
|
11
|
-
* wrapFragmentInChrome).
|
|
12
|
-
* -
|
|
13
|
-
* (/shared/commerce-chrome.css
|
|
11
|
+
* wrapFragmentInChrome). Two governing constraints:
|
|
12
|
+
* - Single stylesheet: the wrapper LINKS the platform stylesheet
|
|
13
|
+
* (/shared/commerce-chrome.css) and emits NO inline chrome CSS — every
|
|
14
14
|
* color/space/type comes from theme tokens the tenant's linked *-theme.css sets.
|
|
15
|
-
* -
|
|
15
|
+
* - Materialization-compatible: this is a deterministic server-side string; the spliced
|
|
16
16
|
* document is a stable, self-contained artifact SEOMaterialize can capture (the
|
|
17
17
|
* behavior JS is progressive enhancement over already-rendered markup).
|
|
18
18
|
*
|
|
19
|
-
* The markup mirrors, class-for-class, the semantic markup the
|
|
19
|
+
* The markup mirrors, class-for-class, the semantic markup the shared chrome stylesheet styles
|
|
20
20
|
* (`.site-header`, `.header-row`, `.primary-nav`, `.mega`, `.site-footer`, …) — the
|
|
21
21
|
* SAME DOM the retired tenant chrome.html files carried, now generated from config.
|
|
22
22
|
*
|
|
@@ -25,17 +25,23 @@
|
|
|
25
25
|
* defense-in-depth, not a sanitizer for untrusted input.
|
|
26
26
|
*/
|
|
27
27
|
import { CHROME_BODY_MARKER, CHROME_HEAD_MARKER } from "./rawMarketingHtml.js";
|
|
28
|
+
import { isCurrentNavHref } from "./chrome/navCurrent.js";
|
|
28
29
|
import type {
|
|
29
30
|
ChromeAgeGate,
|
|
30
31
|
ChromeAnnouncement,
|
|
32
|
+
ChromeAssignments,
|
|
31
33
|
ChromeBrand,
|
|
34
|
+
ChromeConfig,
|
|
32
35
|
ChromeFooter,
|
|
36
|
+
ChromeHeader,
|
|
33
37
|
ChromeLink,
|
|
34
38
|
ChromeNavItem,
|
|
35
39
|
ChromeUtilityNav,
|
|
40
|
+
HeaderVariant,
|
|
36
41
|
SocialLink,
|
|
37
|
-
TenantChrome,
|
|
38
42
|
} from "./chrome/model.js";
|
|
43
|
+
import { resolveChromeAssignment } from "./chrome/model.js";
|
|
44
|
+
import { resolveHeaderVariant } from "./chrome/variant.js";
|
|
39
45
|
|
|
40
46
|
/** HTML-escape text/attribute content. Covers the five significant characters. */
|
|
41
47
|
function esc(value: string): string {
|
|
@@ -177,8 +183,18 @@ function renderUtilityNav(u: ChromeUtilityNav): string {
|
|
|
177
183
|
);
|
|
178
184
|
}
|
|
179
185
|
|
|
180
|
-
/**
|
|
181
|
-
|
|
186
|
+
/**
|
|
187
|
+
* A primary-nav item — flat link, or a mega panel when it has columns/children.
|
|
188
|
+
*
|
|
189
|
+
* `currentPath`, when given, is the tenant-relative request path (e.g. "/" or
|
|
190
|
+
* "/collections/all") in the SAME unprefixed form the config's own `href`
|
|
191
|
+
* values are authored in — basePath prefixing happens later, as a single
|
|
192
|
+
* pass over the whole composed document (rawMarketingHtml.ts,
|
|
193
|
+
* prefixTenantLinks), so comparing here before that pass keeps both sides
|
|
194
|
+
* unprefixed and exact-match-only (see navCurrent.ts). Omitted ⇒ no item is
|
|
195
|
+
* ever marked current (callers that don't know the request path, e.g. tests).
|
|
196
|
+
*/
|
|
197
|
+
function renderNavItem(item: ChromeNavItem, currentPath?: string): string {
|
|
182
198
|
const columns = item.columns?.length
|
|
183
199
|
? item.columns
|
|
184
200
|
: item.children?.length
|
|
@@ -189,14 +205,23 @@ function renderNavItem(item: ChromeNavItem): string {
|
|
|
189
205
|
const badge = item.badge ? `<span class="new-badge">${esc(item.badge)}</span>` : "";
|
|
190
206
|
const caret = hasPanel ? `<span class="caret" aria-hidden="true">▾</span>` : "";
|
|
191
207
|
const haspopup = hasPanel ? ' aria-haspopup="true"' : "";
|
|
192
|
-
const
|
|
208
|
+
const itemCurrent = currentPath !== undefined && isCurrentNavHref(currentPath, item.href);
|
|
209
|
+
const ariaCurrent = itemCurrent ? ' aria-current="page"' : "";
|
|
210
|
+
const link = `<a${linkClass} href="${esc(item.href)}"${haspopup}${ariaCurrent}>${esc(item.label)}${badge}${caret}</a>`;
|
|
193
211
|
if (!hasPanel) return `<li class="nav-item">${link}</li>`;
|
|
194
212
|
|
|
195
213
|
const wide = columns.length > 1 ? " mega--wide" : "";
|
|
196
214
|
const cols = columns
|
|
197
215
|
.map((c) => {
|
|
198
216
|
const heading = c.title ? `<h4>${esc(c.title)}</h4>` : "";
|
|
199
|
-
const links = c.links
|
|
217
|
+
const links = c.links
|
|
218
|
+
.map((l) => {
|
|
219
|
+
const childCurrent =
|
|
220
|
+
currentPath !== undefined && isCurrentNavHref(currentPath, l.href, l.external);
|
|
221
|
+
const childAriaCurrent = childCurrent ? ' aria-current="page"' : "";
|
|
222
|
+
return `<a href="${esc(l.href)}"${relFor(l)}${childAriaCurrent}>${esc(l.label)}</a>`;
|
|
223
|
+
})
|
|
224
|
+
.join("");
|
|
200
225
|
return `<div class="mega__col">${heading}${links}</div>`;
|
|
201
226
|
})
|
|
202
227
|
.join("");
|
|
@@ -204,46 +229,73 @@ function renderNavItem(item: ChromeNavItem): string {
|
|
|
204
229
|
return `<li class="nav-item">${link}${mega}</li>`;
|
|
205
230
|
}
|
|
206
231
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
232
|
+
/**
|
|
233
|
+
* `variant` is the RESOLVED header variant for this page — the page-assignment
|
|
234
|
+
* override applied over `header.variant` by the caller. It is passed in rather
|
|
235
|
+
* than read off `header` so the rendered markup and its `data-variant` can
|
|
236
|
+
* never disagree with the variant the assignment actually decided.
|
|
237
|
+
*/
|
|
238
|
+
function renderHeader(header: ChromeHeader, variant: HeaderVariant, currentPath?: string): string {
|
|
239
|
+
const search = header.search
|
|
240
|
+
? `<form class="header-search" action="${esc(header.search.action ?? "/search")}" method="get" role="search">\n` +
|
|
241
|
+
` <label class="sr-only" for="hdr-q">${esc(header.search.label ?? "Search")}</label>\n` +
|
|
242
|
+
` <input id="hdr-q" type="search" name="q" placeholder="${esc(header.search.placeholder ?? "Search")}" autocomplete="off">\n` +
|
|
212
243
|
` <button type="submit" aria-label="Search">${ICON_SEARCH}</button>\n` +
|
|
213
244
|
` </form>`
|
|
214
245
|
: "";
|
|
215
|
-
const memberCue =
|
|
216
|
-
? `<a class="member-cue" href="${esc(
|
|
246
|
+
const memberCue = header.memberCue
|
|
247
|
+
? `<a class="member-cue" href="${esc(header.memberCue.href)}" aria-label="${esc(header.memberCue.label)}">${esc(header.memberCue.label)}</a>`
|
|
217
248
|
: "";
|
|
218
|
-
const account =
|
|
219
|
-
? `<a href="${esc(
|
|
249
|
+
const account = header.accountHref
|
|
250
|
+
? `<a href="${esc(header.accountHref)}" aria-label="Account">${ICON_ACCOUNT}</a>`
|
|
220
251
|
: "";
|
|
221
|
-
const cart =
|
|
222
|
-
? `<a class="cart" href="${esc(
|
|
252
|
+
const cart = header.cartHref
|
|
253
|
+
? `<a class="cart" href="${esc(header.cartHref)}" aria-label="Cart">${ICON_CART}<span class="count">0</span></a>`
|
|
223
254
|
: "";
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
255
|
+
// "minimal" (e.g. checkout/policy pages): brand + a reduced action set only —
|
|
256
|
+
// no primary nav bar, no search, no mobile-menu toggle. "primary": the full header.
|
|
257
|
+
const minimal = variant === "minimal";
|
|
258
|
+
// Header CTAs, on the same `!minimal` rule the Astro SiteHeader applies, so the
|
|
259
|
+
// two surfaces render the same governed config identically. Each carries its
|
|
260
|
+
// stable `id` as `data-cta-id`, so the governed identity is present in the
|
|
261
|
+
// served DOM rather than implied by label or href.
|
|
262
|
+
const ctas =
|
|
263
|
+
minimal || !header.ctas?.length
|
|
264
|
+
? ""
|
|
265
|
+
: header.ctas
|
|
266
|
+
.map(
|
|
267
|
+
(c) =>
|
|
268
|
+
`<a class="header-cta header-cta--${esc(c.variant ?? "primary")}" ` +
|
|
269
|
+
`data-cta-id="${esc(c.id)}" href="${esc(c.href)}">${esc(c.label)}</a>`,
|
|
270
|
+
)
|
|
271
|
+
.join("");
|
|
272
|
+
const hamburger = minimal
|
|
273
|
+
? ""
|
|
274
|
+
: `<button class="hamburger" id="hamburger" aria-label="Toggle navigation menu" aria-expanded="false" aria-controls="primaryNav"><span></span><span></span><span></span></button>`;
|
|
275
|
+
const navItems = header.nav.map((item) => renderNavItem(item, currentPath)).join("\n ");
|
|
276
|
+
const navBar = minimal
|
|
277
|
+
? ""
|
|
278
|
+
: ` <nav class="primary-nav-bar" aria-label="Primary">\n` +
|
|
279
|
+
` <div class="container">\n` +
|
|
280
|
+
` <nav class="primary-nav" id="primaryNav">\n` +
|
|
281
|
+
` <ul>\n ${navItems}\n </ul>\n` +
|
|
282
|
+
` </nav>\n` +
|
|
283
|
+
` </div>\n` +
|
|
284
|
+
` </nav>\n`;
|
|
227
285
|
return (
|
|
228
|
-
`<header class="site-header">\n` +
|
|
286
|
+
`<header class="site-header" data-variant="${esc(variant)}">\n` +
|
|
229
287
|
` <div class="container header-row">\n` +
|
|
230
|
-
` ${renderBrand(
|
|
231
|
-
(search ? ` ${search}\n` : "") +
|
|
232
|
-
` <div class="header-actions">${memberCue}${account}${cart}${hamburger}</div>\n` +
|
|
288
|
+
` ${renderBrand(header.brand)}\n` +
|
|
289
|
+
(!minimal && search ? ` ${search}\n` : "") +
|
|
290
|
+
` <div class="header-actions">${memberCue}${ctas}${account}${cart}${hamburger}</div>\n` +
|
|
233
291
|
` </div>\n` +
|
|
234
|
-
|
|
235
|
-
` <div class="container">\n` +
|
|
236
|
-
` <nav class="primary-nav" id="primaryNav">\n` +
|
|
237
|
-
` <ul>\n ${navItems}\n </ul>\n` +
|
|
238
|
-
` </nav>\n` +
|
|
239
|
-
` </div>\n` +
|
|
240
|
-
` </nav>\n` +
|
|
292
|
+
navBar +
|
|
241
293
|
`</header>`
|
|
242
294
|
);
|
|
243
295
|
}
|
|
244
296
|
|
|
245
297
|
/**
|
|
246
|
-
* Age-gate config, embedded as JSON
|
|
298
|
+
* Age-gate config, embedded as JSON. The CHROME_BEHAVIOR_JS mechanism
|
|
247
299
|
* reads this and builds the dialog itself inside a closed shadow root — the same
|
|
248
300
|
* tamper-resistance pattern components/islands/IsolatedAgeGate.tsx uses for
|
|
249
301
|
* component pages (see its header comment). `<` is escaped so an authored value
|
|
@@ -335,9 +387,16 @@ function renderFooter(footer: ChromeFooter): string {
|
|
|
335
387
|
* — a single canonical source, never copy-pasted per tenant. Each block is guarded on
|
|
336
388
|
* the presence of its elements, so a wrapper that omits the announce bar / age gate
|
|
337
389
|
* runs the remaining behavior harmlessly. Progressive enhancement: the markup already
|
|
338
|
-
* works without it (nav is
|
|
390
|
+
* works without it (nav is a normal in-flow stacked list at mobile widths — see the
|
|
391
|
+
* `.js-nav` gate in commerce-chrome.css's max-width:860px block — and the age gate
|
|
392
|
+
* defaults open only when JS enables it).
|
|
339
393
|
*/
|
|
340
394
|
export const CHROME_BEHAVIOR_JS = `(function () {
|
|
395
|
+
// Marks the document as JS-enabled so commerce-chrome.css can turn the
|
|
396
|
+
// always-reachable stacked mobile nav into an off-canvas drawer (progressive
|
|
397
|
+
// enhancement, not a requirement — see the .js-nav rules there).
|
|
398
|
+
document.documentElement.classList.add('js-nav');
|
|
399
|
+
|
|
341
400
|
// Mobile nav drawer
|
|
342
401
|
var burger = document.getElementById('hamburger');
|
|
343
402
|
var nav = document.getElementById('primaryNav');
|
|
@@ -367,7 +426,7 @@ export const CHROME_BEHAVIOR_JS = `(function () {
|
|
|
367
426
|
}
|
|
368
427
|
}
|
|
369
428
|
|
|
370
|
-
// 21+ age gate — shadow-isolated
|
|
429
|
+
// 21+ age gate — shadow-isolated. Only the host <div> lives in light
|
|
371
430
|
// DOM (data-tot-compliance, the same marker components/islands/IsolatedAgeGate.tsx
|
|
372
431
|
// uses); its contents sit behind a closed shadow root, so tenant CSS/JS can't
|
|
373
432
|
// select, restyle, or hide the dialog. A MutationObserver re-asserts the host the
|
|
@@ -444,11 +503,11 @@ export const CHROME_BEHAVIOR_JS = `(function () {
|
|
|
444
503
|
}
|
|
445
504
|
})();`;
|
|
446
505
|
|
|
447
|
-
/** The platform-shipped chrome stylesheet every rendered wrapper links
|
|
506
|
+
/** The platform-shipped chrome stylesheet every rendered wrapper links. */
|
|
448
507
|
export const SHARED_CHROME_STYLESHEET = "/shared/commerce-chrome.css";
|
|
449
508
|
|
|
450
509
|
/**
|
|
451
|
-
* The platform-shipped RAW-PAGE-BODY marketing stylesheet
|
|
510
|
+
* The platform-shipped RAW-PAGE-BODY marketing stylesheet. Linked only
|
|
452
511
|
* when a tenant opts in (chrome.sharedMarketingCss) and AFTER the chrome sheet, so
|
|
453
512
|
* its page-level variants (e.g. .sec-head) win over chrome's home-section ones.
|
|
454
513
|
*/
|
|
@@ -480,8 +539,22 @@ function faviconMime(href: string): string {
|
|
|
480
539
|
* `chromeWrapper` so a body-only fragment is spliced in and the whole thing is
|
|
481
540
|
* nonce-stamped + base-path-prefixed. Optional sections (age gate, announcement,
|
|
482
541
|
* utility nav, member cue, footer extras) render only when their config is present.
|
|
542
|
+
*
|
|
543
|
+
* `currentPath`, when given, is the tenant-relative request path in the same
|
|
544
|
+
* unprefixed form nav `href`s are authored in (see renderNavItem) — drives
|
|
545
|
+
* `aria-current="page"` on the primary-nav link that exactly matches it.
|
|
546
|
+
*
|
|
547
|
+
* `assignments`, when given, is the tenant's page-assignment manifest: the
|
|
548
|
+
* entry matching `currentPath` overrides `chrome.header.variant` for THIS page
|
|
549
|
+
* only. Resolution happens once, here, through the same fail-closed
|
|
550
|
+
* resolveHeaderVariant the Layout.astro prop path uses, and the resolved value
|
|
551
|
+
* is what reaches the markup.
|
|
483
552
|
*/
|
|
484
|
-
export function renderRawChrome(
|
|
553
|
+
export function renderRawChrome(
|
|
554
|
+
chrome: ChromeConfig,
|
|
555
|
+
currentPath?: string,
|
|
556
|
+
assignments?: ChromeAssignments | null,
|
|
557
|
+
): string {
|
|
485
558
|
const lang = esc(chrome.lang ?? "en");
|
|
486
559
|
const head = chrome.head ?? {};
|
|
487
560
|
const headLinks = [
|
|
@@ -491,20 +564,25 @@ export function renderRawChrome(chrome: TenantChrome): string {
|
|
|
491
564
|
: "",
|
|
492
565
|
...(head.themeStylesheets ?? []).map((h) => `<link rel="stylesheet" href="${esc(h)}">`),
|
|
493
566
|
`<link rel="stylesheet" href="${SHARED_CHROME_STYLESHEET}">`,
|
|
494
|
-
// Opt-in raw-page-body layer, after the chrome sheet so its variants win
|
|
567
|
+
// Opt-in raw-page-body layer, after the chrome sheet so its variants win.
|
|
495
568
|
chrome.sharedMarketingCss ? `<link rel="stylesheet" href="${SHARED_MARKETING_STYLESHEET}">` : "",
|
|
496
569
|
]
|
|
497
570
|
.filter(Boolean)
|
|
498
571
|
.join("\n");
|
|
499
572
|
|
|
573
|
+
const headerVariant = resolveHeaderVariant(
|
|
574
|
+
resolveChromeAssignment(assignments, currentPath)?.headerVariant,
|
|
575
|
+
chrome.header.variant,
|
|
576
|
+
);
|
|
577
|
+
|
|
500
578
|
const skipHref = esc(chrome.skipLink?.href ?? "#main");
|
|
501
579
|
const skipLabel = esc(chrome.skipLink?.label ?? "Skip to main content");
|
|
502
580
|
|
|
503
581
|
const sections = [
|
|
504
582
|
chrome.warningBar ? `<div class="topbar-warning"><p>${esc(chrome.warningBar)}</p></div>` : "",
|
|
505
|
-
chrome.announcement ? renderAnnouncement(chrome.announcement) : "",
|
|
506
|
-
chrome.utilityNav ? renderUtilityNav(chrome.utilityNav) : "",
|
|
507
|
-
renderHeader(chrome),
|
|
583
|
+
chrome.header.announcement ? renderAnnouncement(chrome.header.announcement) : "",
|
|
584
|
+
chrome.header.utilityNav ? renderUtilityNav(chrome.header.utilityNav) : "",
|
|
585
|
+
renderHeader(chrome.header, headerVariant, currentPath),
|
|
508
586
|
].filter(Boolean);
|
|
509
587
|
|
|
510
588
|
const trailing = [
|