@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,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure state-derivation for the publish lifecycle's FAIL-VISIBLE invariant.
|
|
3
|
+
*
|
|
4
|
+
* ── The invariant this module enforces ──────────────────────────────────────
|
|
5
|
+
* "Fail-visible, never fail-silent." Every candidate/PR and every step of its
|
|
6
|
+
* lifecycle (submitted → reconciled → integrated → built → dispatched) stays
|
|
7
|
+
* VISIBLE, with an honest state and a concrete next step, from creation until it
|
|
8
|
+
* is terminally dispatched (shipped) or explicitly discarded by a human. The
|
|
9
|
+
* three rules below are load-bearing and exhaustively unit-tested here, decoupled
|
|
10
|
+
* from any DOM/transport:
|
|
11
|
+
*
|
|
12
|
+
* 1. DERIVE FROM THE ACTUAL RESULT — a step's success/failure is read from the
|
|
13
|
+
* real underlying signal (did the merge land? did the build pass?), never
|
|
14
|
+
* from a separate optimistic flag that can disagree with reality. This is
|
|
15
|
+
* what killed the false "Integration did not land" modal: the merge HAD
|
|
16
|
+
* landed (a real `aggregateSha` was written) while a later build failed, yet
|
|
17
|
+
* the UX asserted the whole thing didn't land.
|
|
18
|
+
*
|
|
19
|
+
* 2. UNKNOWN STATE = ERROR STATE — if the outcome cannot be positively
|
|
20
|
+
* determined (network fault mid-flight, an HTTP error with no honest reason,
|
|
21
|
+
* a build sha whose result we can't confirm), render an explicit
|
|
22
|
+
* "unknown — needs attention" state with a re-check/rebuild next step. Never
|
|
23
|
+
* hide it and never optimistically show success. "Not sure" ⇒ "probably stuck."
|
|
24
|
+
*
|
|
25
|
+
* 3. NEVER A DEAD END — every failure carries (a) WHICH step failed, (b) a
|
|
26
|
+
* concrete NEXT-STEP action (rebuild / update / re-check), and (c) enough
|
|
27
|
+
* identity to act on it. A build that never produced a renderable revision
|
|
28
|
+
* must surface as "build failed — rebuild", never a link that 404s.
|
|
29
|
+
*
|
|
30
|
+
* All functions are total and side-effect-free.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** The next step a human can take on a failed/uncertain step. */
|
|
34
|
+
export type LifecycleActionKind = "rebuild" | "update-change" | "recheck" | "none";
|
|
35
|
+
|
|
36
|
+
export interface LifecycleAction {
|
|
37
|
+
kind: LifecycleActionKind;
|
|
38
|
+
label: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Integrate outcome (the "Integration did not land" fix) ───────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The honest classification of ONE accept/integrate attempt. Distinguishes the
|
|
45
|
+
* cases the UX previously collapsed into a single false "did not land":
|
|
46
|
+
*
|
|
47
|
+
* - `green` — merge landed AND the aggregate built green.
|
|
48
|
+
* - `already-integrated` — the candidate had already merged before this click.
|
|
49
|
+
* - `merge-conflict` — the merge itself was refused (nothing landed);
|
|
50
|
+
* offer conflict recovery.
|
|
51
|
+
* - `not-base` — the candidate does not target `preview` (dev fix).
|
|
52
|
+
* - `integrated-build-failed`— THE INCIDENT: the merge LANDED (a real
|
|
53
|
+
* `aggregateSha` exists) but the preview build/evidence
|
|
54
|
+
* failed. NOT "did not land" — it landed; the build is
|
|
55
|
+
* what's stuck. Next step: rebuild.
|
|
56
|
+
* - `not-landed` — the merge did not land, for another named reason.
|
|
57
|
+
* - `unknown` — indeterminate (rule 2). Treat as stuck; re-check.
|
|
58
|
+
*/
|
|
59
|
+
export type IntegrateOutcomeKind =
|
|
60
|
+
| "green"
|
|
61
|
+
| "already-integrated"
|
|
62
|
+
| "merge-conflict"
|
|
63
|
+
| "not-base"
|
|
64
|
+
| "integrated-build-failed"
|
|
65
|
+
| "not-landed"
|
|
66
|
+
| "unknown";
|
|
67
|
+
|
|
68
|
+
/** The raw facts of an integrate attempt, from the HTTP result + the IntegrateOutcome body. */
|
|
69
|
+
export interface IntegrateOutcomeFacts {
|
|
70
|
+
/** `res.ok` — the transport succeeded (2xx). */
|
|
71
|
+
httpOk: boolean;
|
|
72
|
+
/** The fetch threw (network fault mid-flight) — the outcome is indeterminate. */
|
|
73
|
+
networkError?: boolean;
|
|
74
|
+
/** `data.ok` — the aggregate went green. */
|
|
75
|
+
ok?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* `data.aggregateSha` — THE actual-result signal. The orchestrator writes a
|
|
78
|
+
* non-null sha IFF the forge merge landed (green OR build-failed-red); it is
|
|
79
|
+
* null when the merge itself never happened. This is the discriminator between
|
|
80
|
+
* "did not land" and "landed but build failed".
|
|
81
|
+
*/
|
|
82
|
+
aggregateSha?: string | null;
|
|
83
|
+
/** `data.queueState` — the aggregate's honest state after this cycle. */
|
|
84
|
+
queueState?: string | null;
|
|
85
|
+
/** `data.reason` — the honest refusal category, when not green. */
|
|
86
|
+
reason?: string | null;
|
|
87
|
+
/** `data.statusMessage` — human/audit detail for a non-green outcome. */
|
|
88
|
+
statusMessage?: string | null;
|
|
89
|
+
/** `data.error` — a transport/validation error message (non-2xx). */
|
|
90
|
+
error?: string | null;
|
|
91
|
+
/** Identity for the copy: "PR #16" / a changeId (rule 3c). */
|
|
92
|
+
target: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface IntegrateOutcomeView {
|
|
96
|
+
kind: IntegrateOutcomeKind;
|
|
97
|
+
/** Did the forge merge land into the preview aggregate? (The truth, not a flag.) */
|
|
98
|
+
landed: boolean;
|
|
99
|
+
/** Did the aggregate end green? */
|
|
100
|
+
ok: boolean;
|
|
101
|
+
/** The outcome seal for the ceremony: success vs. attention. */
|
|
102
|
+
seal: "ok" | "err";
|
|
103
|
+
/** Which lifecycle step failed, or null on success. */
|
|
104
|
+
failStage: "merge" | "evidence" | null;
|
|
105
|
+
/** The outcome title — HONEST about what landed. */
|
|
106
|
+
title: string;
|
|
107
|
+
/** The outcome detail copy. */
|
|
108
|
+
detail: string;
|
|
109
|
+
/** Show the merge-conflict recovery panel (merge-conflict only). */
|
|
110
|
+
conflictGuidance: boolean;
|
|
111
|
+
/** The concrete next step (rule 3b), or null when none applies. */
|
|
112
|
+
action: LifecycleAction | null;
|
|
113
|
+
/**
|
|
114
|
+
* The "Review the preview build" deep-link state (rule 3 / no-404): shown ONLY
|
|
115
|
+
* when a real, renderable revision exists — i.e. the aggregate went green. A
|
|
116
|
+
* build-failed sha is NOT renderable, so the link is withheld to avoid a 404.
|
|
117
|
+
*/
|
|
118
|
+
review: { show: boolean; sha: string | null };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Refusal reasons that mean the merge itself never landed (aggregate untouched). */
|
|
122
|
+
const MERGE_NEVER_LANDED_REASONS = new Set(["not_preview_base", "merge_failed", "candidate_not_open"]);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Classify an integrate attempt from its ACTUAL result. Precedence is ordered so a
|
|
126
|
+
* definite, honest signal always wins over an optimistic one.
|
|
127
|
+
*/
|
|
128
|
+
export function classifyIntegrateOutcome(f: IntegrateOutcomeFacts): IntegrateOutcomeView {
|
|
129
|
+
const target = f.target || "This change";
|
|
130
|
+
|
|
131
|
+
// Rule 2: a network fault mid-flight means we DON'T KNOW whether the merge
|
|
132
|
+
// landed. Never guess — render the explicit unknown state with a re-check step.
|
|
133
|
+
if (f.networkError) {
|
|
134
|
+
return {
|
|
135
|
+
kind: "unknown",
|
|
136
|
+
landed: false,
|
|
137
|
+
ok: false,
|
|
138
|
+
seal: "err",
|
|
139
|
+
failStage: null,
|
|
140
|
+
title: "We couldn't confirm the result",
|
|
141
|
+
detail:
|
|
142
|
+
"The connection dropped while adding " + target + " to your next release, so its result " +
|
|
143
|
+
"could not be confirmed. It may have gone through. Re-check before retrying — do not assume it failed.",
|
|
144
|
+
conflictGuidance: false,
|
|
145
|
+
action: { kind: "recheck", label: "Re-check status" },
|
|
146
|
+
review: { show: false, sha: null },
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Rule 1: green is derived from the real ok signal.
|
|
151
|
+
if (f.httpOk && f.ok) {
|
|
152
|
+
const sha = f.aggregateSha ?? null;
|
|
153
|
+
return {
|
|
154
|
+
kind: "green",
|
|
155
|
+
landed: true,
|
|
156
|
+
ok: true,
|
|
157
|
+
seal: "ok",
|
|
158
|
+
failStage: null,
|
|
159
|
+
title: "Added to your next release",
|
|
160
|
+
detail:
|
|
161
|
+
target + " is in. The preview build passed all your store's checks — this is exactly what " +
|
|
162
|
+
"you review, and what Publish takes live. Nothing is live yet.",
|
|
163
|
+
conflictGuidance: false,
|
|
164
|
+
action: null,
|
|
165
|
+
review: { show: Boolean(sha), sha },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// A non-2xx transport with no honest IntegrateOutcome body is indeterminate.
|
|
170
|
+
if (!f.httpOk && !f.reason && f.aggregateSha == null) {
|
|
171
|
+
return {
|
|
172
|
+
kind: "unknown",
|
|
173
|
+
landed: false,
|
|
174
|
+
ok: false,
|
|
175
|
+
seal: "err",
|
|
176
|
+
failStage: null,
|
|
177
|
+
title: "We couldn't confirm the result",
|
|
178
|
+
detail:
|
|
179
|
+
(f.error || f.statusMessage || "The server returned an error with no confirmed state.") +
|
|
180
|
+
" " + target + "'s result could not be confirmed — re-check before retrying.",
|
|
181
|
+
conflictGuidance: false,
|
|
182
|
+
action: { kind: "recheck", label: "Re-check status" },
|
|
183
|
+
review: { show: false, sha: null },
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// The candidate had already merged before this click — a success being re-heard,
|
|
188
|
+
// not a failure (the row went stale). Never conflict guidance.
|
|
189
|
+
if (f.reason === "candidate_not_open") {
|
|
190
|
+
return {
|
|
191
|
+
kind: "already-integrated",
|
|
192
|
+
landed: true,
|
|
193
|
+
ok: false,
|
|
194
|
+
seal: "ok",
|
|
195
|
+
failStage: null,
|
|
196
|
+
title: "Already added",
|
|
197
|
+
detail:
|
|
198
|
+
target + " was added to your next release earlier — this attempt found nothing left to do. " +
|
|
199
|
+
"It will drop off the queue on the next refresh; the change is recorded below.",
|
|
200
|
+
conflictGuidance: false,
|
|
201
|
+
action: null,
|
|
202
|
+
review: { show: false, sha: null },
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// The merge was refused because the candidate branched off the current preview and
|
|
207
|
+
// both edit the same content — a fork, not a failure of the change. Nothing landed.
|
|
208
|
+
if (f.reason === "merge_failed") {
|
|
209
|
+
return {
|
|
210
|
+
kind: "merge-conflict",
|
|
211
|
+
landed: false,
|
|
212
|
+
ok: false,
|
|
213
|
+
seal: "err",
|
|
214
|
+
failStage: "merge",
|
|
215
|
+
title: "Preview changed while you worked",
|
|
216
|
+
detail:
|
|
217
|
+
target + " edits content that also changed on preview after it was created, so it " +
|
|
218
|
+
"couldn't be added as-is. Nothing was added — your next release is exactly as it was. You can " +
|
|
219
|
+
"update this change automatically below.",
|
|
220
|
+
conflictGuidance: true,
|
|
221
|
+
action: { kind: "update-change", label: "Update this change automatically" },
|
|
222
|
+
review: { show: false, sha: null },
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// The candidate does not target the preview branch — a developer fix, not a retry.
|
|
227
|
+
if (f.reason === "not_preview_base") {
|
|
228
|
+
return {
|
|
229
|
+
kind: "not-base",
|
|
230
|
+
landed: false,
|
|
231
|
+
ok: false,
|
|
232
|
+
seal: "err",
|
|
233
|
+
failStage: "merge",
|
|
234
|
+
title: "Couldn't add this change",
|
|
235
|
+
detail:
|
|
236
|
+
target + " couldn't be added — it isn't based on your store's preview. " +
|
|
237
|
+
(f.statusMessage || ""),
|
|
238
|
+
conflictGuidance: false,
|
|
239
|
+
action: null,
|
|
240
|
+
review: { show: false, sha: null },
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// THE INCIDENT (rule 1): a real `aggregateSha` was written ⇒ the forge merge
|
|
245
|
+
// LANDED. If we're here it did not go green, so the preview BUILD/evidence is
|
|
246
|
+
// what failed. This is NOT "did not land" — say exactly what happened and offer
|
|
247
|
+
// the rebuild. The review link is WITHHELD (rule 3): the sha never built, so
|
|
248
|
+
// linking it would 404.
|
|
249
|
+
if (f.aggregateSha) {
|
|
250
|
+
return {
|
|
251
|
+
kind: "integrated-build-failed",
|
|
252
|
+
landed: true,
|
|
253
|
+
ok: false,
|
|
254
|
+
seal: "err",
|
|
255
|
+
failStage: "evidence",
|
|
256
|
+
title: "Added, but the preview build didn't pass",
|
|
257
|
+
detail:
|
|
258
|
+
target + " was added to your next release, but the preview build didn't pass your store's " +
|
|
259
|
+
"checks" +
|
|
260
|
+
(f.statusMessage ? " (" + f.statusMessage + ")" : "") +
|
|
261
|
+
". Nothing is live. Rebuild the preview to finish — the change stays in.",
|
|
262
|
+
conflictGuidance: false,
|
|
263
|
+
action: { kind: "rebuild", label: "Rebuild the preview build" },
|
|
264
|
+
review: { show: false, sha: null },
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// The merge genuinely did not land (no sha), for a named or generic reason.
|
|
269
|
+
const reasonKnownNotLanded = f.reason ? MERGE_NEVER_LANDED_REASONS.has(f.reason) : false;
|
|
270
|
+
return {
|
|
271
|
+
kind: reasonKnownNotLanded || f.reason ? "not-landed" : "unknown",
|
|
272
|
+
landed: false,
|
|
273
|
+
ok: false,
|
|
274
|
+
seal: "err",
|
|
275
|
+
failStage: "merge",
|
|
276
|
+
title: f.reason ? "Couldn't add this change" : "We couldn't confirm the result",
|
|
277
|
+
detail:
|
|
278
|
+
(f.statusMessage || f.error ||
|
|
279
|
+
"The change couldn't be added and your next release is unchanged.") +
|
|
280
|
+
(f.reason ? "" : " Its result could not be confirmed — re-check the queue."),
|
|
281
|
+
conflictGuidance: false,
|
|
282
|
+
action: { kind: f.reason ? "recheck" : "recheck", label: "Re-check status" },
|
|
283
|
+
review: { show: false, sha: null },
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ── Build link (the "Open Build" 404 fix) ────────────────────────────────────
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Whether a `/preview/rev/<sha>/` deep-link is safe to offer, derived from the
|
|
291
|
+
* ACTUAL build result — never from the mere presence of a sha. The caller passes
|
|
292
|
+
* `built`: is THIS sha a genuinely-built, renderable revision? A link is offered
|
|
293
|
+
* only when it is. Every other case surfaces an honest state + next step instead of
|
|
294
|
+
* a dead link (rules 1 & 3). Two call-sites, two `built` rules:
|
|
295
|
+
*
|
|
296
|
+
* - The aggregate CARD's own sha is always the last-GREEN materialized sha (a red
|
|
297
|
+
* cycle leaves it untouched), so `built = sha exists`. Opening it reaches the
|
|
298
|
+
* last-good preview even while the current cycle is red.
|
|
299
|
+
* - A specific integration RUN's sha is renderable IFF that run passed, so
|
|
300
|
+
* `built = run.state === "passed"`. A failed run's sha never materialized —
|
|
301
|
+
* linking it is the exact "Open Build → 404" trap this guards.
|
|
302
|
+
*/
|
|
303
|
+
export type BuildLinkKind = "ready" | "building" | "failed" | "none";
|
|
304
|
+
|
|
305
|
+
export interface BuildLinkFacts {
|
|
306
|
+
/** The sha the link would open, or null. */
|
|
307
|
+
sha?: string | null;
|
|
308
|
+
/** Is that sha a genuinely-built, renderable revision? */
|
|
309
|
+
built: boolean;
|
|
310
|
+
/** A build is in flight right now (so "not ready yet", not "failed"). */
|
|
311
|
+
building?: boolean;
|
|
312
|
+
/** A build was attempted (distinguishes "failed" from "never built"). */
|
|
313
|
+
attempted?: boolean;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export interface BuildLinkView {
|
|
317
|
+
/** Is the rev deep-link safe to render as a link? */
|
|
318
|
+
available: boolean;
|
|
319
|
+
kind: BuildLinkKind;
|
|
320
|
+
/** Link/label text. */
|
|
321
|
+
label: string;
|
|
322
|
+
/** A caption for the non-link states. */
|
|
323
|
+
note: string;
|
|
324
|
+
/** The next step when the build failed (rule 3b), else null. */
|
|
325
|
+
action: LifecycleAction | null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function classifyBuildLink(f: BuildLinkFacts): BuildLinkView {
|
|
329
|
+
const sha = f.sha ?? null;
|
|
330
|
+
|
|
331
|
+
// Genuinely built: the sha names a renderable revision — safe to link (even if a
|
|
332
|
+
// newer cycle is in flight or red, this reaches the last-good preview).
|
|
333
|
+
if (sha && f.built) {
|
|
334
|
+
return { available: true, kind: "ready", label: "Open build", note: "", action: null };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// No renderable sha, but a build is in flight — not openable yet, not failed.
|
|
338
|
+
if (f.building) {
|
|
339
|
+
return {
|
|
340
|
+
available: false,
|
|
341
|
+
kind: "building",
|
|
342
|
+
label: "Build in progress…",
|
|
343
|
+
note: "The preview build is still running. It'll be openable once it finishes.",
|
|
344
|
+
action: null,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// A build was attempted but produced no renderable revision — the exact
|
|
349
|
+
// "Open Build → 404" trap. Surface the failure + the rebuild, never a dead link.
|
|
350
|
+
if (f.attempted) {
|
|
351
|
+
return {
|
|
352
|
+
available: false,
|
|
353
|
+
kind: "failed",
|
|
354
|
+
label: "Preview build failed",
|
|
355
|
+
note:
|
|
356
|
+
"The last preview build didn't finish, so there's nothing to open yet. Rebuild it to try again.",
|
|
357
|
+
action: { kind: "rebuild", label: "Rebuild the preview build" },
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Nothing has been built yet.
|
|
362
|
+
return {
|
|
363
|
+
available: false,
|
|
364
|
+
kind: "none",
|
|
365
|
+
label: "No build yet",
|
|
366
|
+
note: "No preview build has been produced for this store yet.",
|
|
367
|
+
action: null,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type LiveReleaseSummaryView =
|
|
2
|
+
| { kind: "current"; label: string }
|
|
3
|
+
| { kind: "publish"; label: string; disabled: boolean; title: string }
|
|
4
|
+
| { kind: "empty"; label: string };
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Derive the collapsed release panel's single status/action affordance.
|
|
8
|
+
* Public-target freshness is deliberately absent: only staged-vs-live equality
|
|
9
|
+
* is allowed to claim that the next release is current.
|
|
10
|
+
*/
|
|
11
|
+
export function liveReleaseSummaryState(input: {
|
|
12
|
+
matchesLive: boolean;
|
|
13
|
+
hasStagedRelease: boolean;
|
|
14
|
+
publishDisabled: boolean;
|
|
15
|
+
publishTitle: string;
|
|
16
|
+
}): LiveReleaseSummaryView {
|
|
17
|
+
if (input.matchesLive) {
|
|
18
|
+
return { kind: "current", label: "Live is up to date with next release" };
|
|
19
|
+
}
|
|
20
|
+
if (input.hasStagedRelease) {
|
|
21
|
+
return {
|
|
22
|
+
kind: "publish",
|
|
23
|
+
label: "Publish next release",
|
|
24
|
+
disabled: input.publishDisabled,
|
|
25
|
+
title: input.publishTitle,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return { kind: "empty", label: "No release staged" };
|
|
29
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mergeDoctorOnFailure — the admin#publish half of C5 (the merge-doctor PUSH).
|
|
3
|
+
*
|
|
4
|
+
* When an accept/integrate or an aggregate ship ACTION FAILS in the Publish tab, the
|
|
5
|
+
* developer shouldn't have to go read the top-of-tab health banner or a per-row chip
|
|
6
|
+
* to understand WHY — the diagnosis should show up right where the failure surfaced.
|
|
7
|
+
* These pure reducers turn the SAME `/api/changes` payload the tab already fetches
|
|
8
|
+
* (via {@link mergeRowFindings} / {@link mergeHealthBanner}) into a compact one-line
|
|
9
|
+
* hint the client island appends to the failure's own status/outcome copy.
|
|
10
|
+
*
|
|
11
|
+
* No new fetch and no new analyzer: this is the SAME analysis {@link mergeRowFindings}
|
|
12
|
+
* and {@link mergeHealthBanner} already run for the banner + row chips — reduced to
|
|
13
|
+
* "the one thing this failure means". Pure + unit-tested; the DOM wiring in
|
|
14
|
+
* `adminPublishTab.ts` is thin glue over these.
|
|
15
|
+
*/
|
|
16
|
+
import { mergeRowFindings, mergeRowKey, type MergeRowFinding } from "./mergeRowFindings";
|
|
17
|
+
import { mergeHealthBanner } from "./mergeHealthBanner";
|
|
18
|
+
|
|
19
|
+
// The `/api/changes` body shape, derived from the analyzers that consume it — so this
|
|
20
|
+
// module stays inside lib/publish and never imports the control-plane moat
|
|
21
|
+
// (lib/preview) directly (the renderer-no-controlplane depcruise fence).
|
|
22
|
+
type ChangesBody = Parameters<typeof mergeRowFindings>[0];
|
|
23
|
+
|
|
24
|
+
export interface DoctorFailureHint {
|
|
25
|
+
/** Whether there's anything to append (false ⇒ the failure's own copy stands alone). */
|
|
26
|
+
show: boolean;
|
|
27
|
+
/** The compact one-line diagnosis to append at the failure moment ("" when !show). */
|
|
28
|
+
text: string;
|
|
29
|
+
/** The dominant row finding when the failure maps to a specific candidate (else null). */
|
|
30
|
+
finding: MergeRowFinding | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const NONE: DoctorFailureHint = { show: false, text: "", finding: null };
|
|
34
|
+
|
|
35
|
+
/** The compact one-liner for a single candidate row's finding — ownership tag +
|
|
36
|
+
* what's wrong + the remedy, the same three facts the row chip's tooltip carries. */
|
|
37
|
+
export function formatRowHint(f: MergeRowFinding): string {
|
|
38
|
+
return `Merge doctor (${f.ownershipLabel}): ${f.detail} → ${f.remedy}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Diagnosis appended when an accept/integrate FAILS on ONE candidate row. Looks that
|
|
43
|
+
* candidate's dominant finding up in the SAME per-row index the queue renders (built
|
|
44
|
+
* rows keyed by changeId, not-built PRs by number). Returns {@link NONE} when the row
|
|
45
|
+
* carries no finding (e.g. a transient/network failure the doctor can't see) — so a
|
|
46
|
+
* failure the analyzer has no opinion on gets no misleading hint.
|
|
47
|
+
*/
|
|
48
|
+
export function integrateFailureDoctor(
|
|
49
|
+
changesBody: ChangesBody,
|
|
50
|
+
target: { changeId?: string | null; prNumber?: number | null },
|
|
51
|
+
opts: { nowMs?: number } = {},
|
|
52
|
+
): DoctorFailureHint {
|
|
53
|
+
const findings = mergeRowFindings(changesBody, { nowMs: opts.nowMs });
|
|
54
|
+
const changeKey = target.changeId != null ? mergeRowKey(target.changeId, null) : null;
|
|
55
|
+
const prKey = target.prNumber != null ? mergeRowKey(null, target.prNumber) : null;
|
|
56
|
+
const finding =
|
|
57
|
+
(changeKey ? findings.get(changeKey) : undefined) ??
|
|
58
|
+
(prKey ? findings.get(prKey) : undefined) ??
|
|
59
|
+
null;
|
|
60
|
+
if (!finding) return NONE;
|
|
61
|
+
return { show: true, text: formatRowHint(finding), finding };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Diagnosis appended when the aggregate SHIP (publish) is refused/fails. A ship
|
|
66
|
+
* failure is an aggregate-level event (most tellingly "not green"), so it uses the
|
|
67
|
+
* SAME roll-up the top-of-tab health banner shows — "what's keeping the aggregate
|
|
68
|
+
* from going green" — rather than a single row. Returns {@link NONE} when the queue
|
|
69
|
+
* is clean of developer/platform work (nothing the doctor can add).
|
|
70
|
+
*/
|
|
71
|
+
export function shipFailureDoctor(
|
|
72
|
+
changesBody: ChangesBody,
|
|
73
|
+
opts: { nowMs?: number } = {},
|
|
74
|
+
): DoctorFailureHint {
|
|
75
|
+
const banner = mergeHealthBanner(changesBody, { nowMs: opts.nowMs });
|
|
76
|
+
if (!banner.show) return NONE;
|
|
77
|
+
return { show: true, text: `Merge doctor: ${banner.text}`, finding: null };
|
|
78
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mergeHealthBanner — the wiring seam between the mergeDoctor analyzer (the
|
|
3
|
+
* PURE brain, `lib/preview/mergeDoctor.ts`) and the admin Publish tab's
|
|
4
|
+
* top-of-page health banner. Runs the analyzer over the SAME `/api/changes`
|
|
5
|
+
* body the queue already fetched (no extra read) and reduces it to the three
|
|
6
|
+
* things the DOM toggle needs: whether to show at all, the one-line text, and
|
|
7
|
+
* the severity tone.
|
|
8
|
+
*
|
|
9
|
+
* `show` is FALSE whenever nothing is developer-actionable and no platform
|
|
10
|
+
* blocker is outstanding — i.e. exactly when {@link attentionBanner} would
|
|
11
|
+
* return its "Nothing needs your attention." sentinel — so a healthy queue
|
|
12
|
+
* renders no empty banner chrome.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
analyzeMerge,
|
|
16
|
+
attentionBanner,
|
|
17
|
+
OWNERSHIP,
|
|
18
|
+
type ChangesInput,
|
|
19
|
+
type BranchesInput,
|
|
20
|
+
} from "@/lib/preview/mergeDoctor";
|
|
21
|
+
|
|
22
|
+
export interface MergeHealthBanner {
|
|
23
|
+
/** Render the banner? False when the queue is clean of developer/platform work. */
|
|
24
|
+
show: boolean;
|
|
25
|
+
/** The one-line attention summary (empty string when `show` is false). */
|
|
26
|
+
text: string;
|
|
27
|
+
/** Highest outstanding severity — drives the amber (warn) vs red (blocker) look. */
|
|
28
|
+
tone: "blocker" | "warn";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Reduce a `/api/changes` body (the queue payload the tab already holds) to the
|
|
33
|
+
* merge-health banner state. `branchesBody` is optional; the banner only counts
|
|
34
|
+
* developer-actionable + platform-blocker findings, none of which come from the
|
|
35
|
+
* branch plane, so it is safe to omit when the branches panel hasn't loaded.
|
|
36
|
+
*/
|
|
37
|
+
export function mergeHealthBanner(
|
|
38
|
+
changesBody: ChangesInput["body"],
|
|
39
|
+
opts: { branchesBody?: BranchesInput["body"]; nowMs?: number } = {},
|
|
40
|
+
): MergeHealthBanner {
|
|
41
|
+
const result = analyzeMerge({
|
|
42
|
+
changes: { status: 200, body: changesBody ?? {} },
|
|
43
|
+
branches: opts.branchesBody ? { status: 200, body: opts.branchesBody } : {},
|
|
44
|
+
tenant: changesBody?.owner ?? null,
|
|
45
|
+
nowMs: opts.nowMs,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const devActionable = result.findings.filter(
|
|
49
|
+
(f) => f.ownership === OWNERSHIP.developer && f.severity !== "info",
|
|
50
|
+
);
|
|
51
|
+
const platformBlockers = result.findings.filter(
|
|
52
|
+
(f) => f.ownership === OWNERSHIP.platform && f.severity === "blocker",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const show = devActionable.length > 0 || platformBlockers.length > 0;
|
|
56
|
+
const tone: "blocker" | "warn" =
|
|
57
|
+
platformBlockers.length > 0 || devActionable.some((f) => f.severity === "blocker")
|
|
58
|
+
? "blocker"
|
|
59
|
+
: "warn";
|
|
60
|
+
|
|
61
|
+
return { show, text: show ? attentionBanner(result) : "", tone };
|
|
62
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mergeRowFindings — the per-row companion to {@link mergeHealthBanner}. Both
|
|
3
|
+
* reduce the SAME `/api/changes` body (the queue payload the tab already holds)
|
|
4
|
+
* through the SAME pure analyzer (`lib/preview/mergeDoctor.ts`); the banner rolls
|
|
5
|
+
* findings up into one top-of-tab line, this one indexes them BY ROW so each
|
|
6
|
+
* candidate can carry its own ownership-tagged chip + the mapped action.
|
|
7
|
+
*
|
|
8
|
+
* It emits no new fetch and no per-row analysis call: one `analyzeMerge` pass over
|
|
9
|
+
* the already-fetched body, then a `Map` keyed by {@link mergeRowKey} so the queue
|
|
10
|
+
* renderer can look a row's finding up in O(1). A row with no per-row finding
|
|
11
|
+
* (healthy, or a banner/branch-plane finding that names no candidate) is simply
|
|
12
|
+
* absent from the map — the renderer shows no chip.
|
|
13
|
+
*
|
|
14
|
+
* The `cta` this exposes is the finding's OWN mapped action reduced to what the DOM
|
|
15
|
+
* needs to light up an EXISTING queue button (resolve → the conflict-recovery
|
|
16
|
+
* button, build/retry → the build button) — never a re-declared API path.
|
|
17
|
+
*/
|
|
18
|
+
import {
|
|
19
|
+
analyzeMerge,
|
|
20
|
+
type ChangesInput,
|
|
21
|
+
type BranchesInput,
|
|
22
|
+
type Finding,
|
|
23
|
+
type FindingAction,
|
|
24
|
+
type FindingCode,
|
|
25
|
+
type OwnershipKind,
|
|
26
|
+
type Severity,
|
|
27
|
+
} from "@/lib/preview/mergeDoctor";
|
|
28
|
+
|
|
29
|
+
/** A single candidate row's dominant merge-doctor finding, DOM-ready. */
|
|
30
|
+
export interface MergeRowFinding {
|
|
31
|
+
code: FindingCode;
|
|
32
|
+
severity: Severity;
|
|
33
|
+
ownership: OwnershipKind;
|
|
34
|
+
/** The chip's ownership tag — the ticket-deflection axis, in operator words. */
|
|
35
|
+
ownershipLabel: string;
|
|
36
|
+
/** Chip tone in this repo's convention (`""` = neutral); driven by severity. */
|
|
37
|
+
tone: "red" | "amber" | "";
|
|
38
|
+
detail: string;
|
|
39
|
+
remedy: string;
|
|
40
|
+
/**
|
|
41
|
+
* The mapped next step, or null for an informational-only finding (chip, no
|
|
42
|
+
* button). `kind` selects WHICH existing button to light up; `label` is the
|
|
43
|
+
* finding's own action label (used only for the fallback CTA when no inline
|
|
44
|
+
* button exists to prime).
|
|
45
|
+
*/
|
|
46
|
+
cta: { kind: FindingAction["kind"]; label: string } | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Ownership tag copy — developer self-serves; platform is "on us"; operator tidies. */
|
|
50
|
+
const OWNERSHIP_LABEL: Record<OwnershipKind, string> = {
|
|
51
|
+
developer: "You",
|
|
52
|
+
platform: "On us",
|
|
53
|
+
operator: "Housekeeping",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const TONE_BY_SEVERITY: Record<Severity, "red" | "amber" | ""> = {
|
|
57
|
+
blocker: "red",
|
|
58
|
+
warn: "amber",
|
|
59
|
+
info: "",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const SEVERITY_RANK: Record<Severity, number> = { blocker: 0, warn: 1, info: 2 };
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The stable per-row key shared by producer (this module) and consumer (the queue
|
|
66
|
+
* renderer). A built candidate is keyed by its `changeId`; a not-built forge PR has
|
|
67
|
+
* no changeId, so it is keyed by `prNumber`. Returns null when a finding names
|
|
68
|
+
* neither (a banner/branch-plane finding that belongs to no single row).
|
|
69
|
+
*/
|
|
70
|
+
export function mergeRowKey(changeId: string | null, prNumber: number | null): string | null {
|
|
71
|
+
if (changeId != null) return "change:" + changeId;
|
|
72
|
+
if (prNumber != null) return "pr:" + prNumber;
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function toRowFinding(f: Finding): MergeRowFinding {
|
|
77
|
+
return {
|
|
78
|
+
code: f.code,
|
|
79
|
+
severity: f.severity,
|
|
80
|
+
ownership: f.ownership,
|
|
81
|
+
ownershipLabel: OWNERSHIP_LABEL[f.ownership] ?? f.ownership,
|
|
82
|
+
tone: TONE_BY_SEVERITY[f.severity],
|
|
83
|
+
detail: f.detail,
|
|
84
|
+
remedy: f.remedy,
|
|
85
|
+
cta: f.action ? { kind: f.action.kind, label: f.action.label } : null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* When two findings land on the same row, the more severe one wins; on a tie, the
|
|
91
|
+
* one that carries an action (a CTA to light up) beats a purely informational one.
|
|
92
|
+
*/
|
|
93
|
+
function candidateWins(current: MergeRowFinding, candidate: Finding): boolean {
|
|
94
|
+
const rc = SEVERITY_RANK[candidate.severity];
|
|
95
|
+
const rk = SEVERITY_RANK[current.severity];
|
|
96
|
+
if (rc !== rk) return rc < rk;
|
|
97
|
+
return Boolean(candidate.action) && !current.cta;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Index a `/api/changes` body's merge findings by row. `branchesBody` is optional
|
|
102
|
+
* and only feeds branch-plane findings, none of which name a candidate row, so it
|
|
103
|
+
* never changes the per-row map — accepted for parity with {@link mergeHealthBanner}.
|
|
104
|
+
*/
|
|
105
|
+
export function mergeRowFindings(
|
|
106
|
+
changesBody: ChangesInput["body"],
|
|
107
|
+
opts: { branchesBody?: BranchesInput["body"]; nowMs?: number } = {},
|
|
108
|
+
): Map<string, MergeRowFinding> {
|
|
109
|
+
const result = analyzeMerge({
|
|
110
|
+
changes: { status: 200, body: changesBody ?? {} },
|
|
111
|
+
branches: opts.branchesBody ? { status: 200, body: opts.branchesBody } : {},
|
|
112
|
+
tenant: changesBody?.owner ?? null,
|
|
113
|
+
nowMs: opts.nowMs,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const byRow = new Map<string, MergeRowFinding>();
|
|
117
|
+
for (const f of result.findings) {
|
|
118
|
+
const key = mergeRowKey(f.changeId, f.prNumber);
|
|
119
|
+
if (key === null) continue; // banner/branch-plane finding — belongs to no row
|
|
120
|
+
const current = byRow.get(key);
|
|
121
|
+
if (!current || candidateWins(current, f)) byRow.set(key, toRowFinding(f));
|
|
122
|
+
}
|
|
123
|
+
return byRow;
|
|
124
|
+
}
|