@tokenoftrust/storefront-runner 1.4.1 → 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 +17 -1
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +54 -0
- package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
- package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
- 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/0002_snapshot.json +1160 -0
- package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -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 +56 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/js/dashboard-team.js +38 -2
- 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/Seo.astro +65 -1
- package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +1435 -0
- package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1980 -2564
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
- package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
- package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
- package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
- 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 +146 -21
- package/apps/storefront/src/lib/activity/alerts.ts +17 -19
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +89 -9
- package/apps/storefront/src/lib/activity/deployVersion.ts +20 -27
- package/apps/storefront/src/lib/activity/ingest.ts +8 -8
- package/apps/storefront/src/lib/activity/ingestAuth.ts +1 -1
- package/apps/storefront/src/lib/activity/killSwitch.ts +6 -6
- package/apps/storefront/src/lib/activity/query.ts +9 -9
- package/apps/storefront/src/lib/activity/recordActivity.ts +11 -15
- package/apps/storefront/src/lib/activity/store.ts +4 -6
- package/apps/storefront/src/lib/activity/uiActor.ts +12 -12
- package/apps/storefront/src/lib/activity/workerCommit.ts +6 -6
- package/apps/storefront/src/lib/admin/adminShell.ts +292 -0
- package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
- package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
- package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
- package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
- package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
- package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
- package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
- package/apps/storefront/src/lib/apps/adminService.ts +14 -4
- package/apps/storefront/src/lib/apps/adminSession.ts +54 -45
- package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
- package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/credentials.ts +2 -2
- package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
- 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/healthAggregate.ts +5 -5
- 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/attributionService.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
- package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +9 -1
- package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +7 -7
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +270 -16
- package/apps/storefront/src/lib/apps/scopes.ts +2 -2
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
- package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
- 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/widgets/eligibility.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
- package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
- package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
- 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 +11 -11
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
- package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
- package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +2 -4
- package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
- package/apps/storefront/src/lib/auth/session.ts +7 -8
- package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
- package/apps/storefront/src/lib/basePath.ts +9 -2
- package/apps/storefront/src/lib/blog/access.ts +29 -0
- package/apps/storefront/src/lib/blog/collection.ts +400 -0
- package/apps/storefront/src/lib/blog/markdown.ts +174 -0
- package/apps/storefront/src/lib/blog/provider.ts +62 -3
- package/apps/storefront/src/lib/blog/reactions.ts +87 -0
- package/apps/storefront/src/lib/blog/rss.ts +92 -0
- package/apps/storefront/src/lib/blog/teaser.ts +83 -0
- package/apps/storefront/src/lib/blog/types.ts +34 -0
- package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
- 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 +14 -2
- package/apps/storefront/src/lib/d1/schema-apps.ts +372 -17
- package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
- package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
- package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
- 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/cockpitStore.ts +1 -9
- package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
- package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
- package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +34 -5
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
- package/apps/storefront/src/lib/i18n.ts +25 -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/jsonld.ts +118 -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/monitoring/manifest.ts +11 -11
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
- package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
- package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
- package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
- package/apps/storefront/src/lib/newsletter/send.ts +265 -0
- package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +4625 -0
- package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
- 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/dispatchHealth.ts +5 -7
- package/apps/storefront/src/lib/publish/domainState.ts +22 -13
- 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/panelPolling.ts +78 -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 +121 -51
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -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/rate/kvWindow.ts +53 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/search/index.ts +2 -2
- package/apps/storefront/src/lib/seo/alternates.ts +42 -0
- package/apps/storefront/src/lib/seo/meta.ts +65 -0
- package/apps/storefront/src/lib/social/golive.ts +212 -0
- package/apps/storefront/src/lib/social/notify.ts +32 -0
- package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
- 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 +32 -0
- package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
- package/apps/storefront/src/lib/the-build/toc.ts +95 -0
- package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
- package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
- package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
- package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
- package/apps/storefront/src/middleware/index.ts +385 -52
- 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 +25 -1313
- 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/blog/[collection]/[slug]/react.ts +104 -0
- 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/newsletter/[collection]/subscribe.ts +146 -0
- package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
- package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
- package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +20 -2
- package/apps/storefront/src/pages/blog/[slug].astro +99 -19
- package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
- package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
- package/apps/storefront/src/pages/blog/index.astro +4 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
- package/apps/storefront/src/pages/cockpit.astro +77 -1353
- package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
- 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/search.astro +1 -1
- package/apps/storefront/src/pages/sitemap.xml.ts +21 -1
- 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 +129 -0
- package/apps/storefront/src/pages/the-build/index.astro +146 -0
- package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
- package/apps/storefront/src/styles/admin.css +1106 -0
- package/apps/storefront/tsconfig.json +6 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +3 -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 +89 -33
- 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/catalog-d1.ts +1 -1
- 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-preview.ts +25 -2
- package/packages/public-runtime/src/customization-runtime.ts +156 -3
- package/packages/public-runtime/src/customization-versioning.ts +10 -0
- 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 +17 -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 +357 -0
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +81 -17
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/build/copy-tenant-assets.mjs +14 -4
- package/scripts/dev/ai-edit.mjs +1 -1
- package/scripts/dev/checkout-watch.mjs +2 -2
- package/scripts/dev/file-browser.mjs +4 -6
- package/scripts/dev/file-writer.mjs +3 -3
- package/scripts/dev/git-status.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/locate-handler.mjs +3 -3
- package/scripts/dev/port-check.mjs +3 -3
- package/scripts/dev/publish.mjs +5 -9
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/shot.mjs +1 -1
- package/scripts/dev/tenant-source-root.mjs +1 -1
- package/scripts/dev/unified-diff.mjs +8 -3
- package/scripts/tot-dev.mjs +8 -11
- package/apps/storefront/src/lib/dev/vcBinding.ts +0 -80
|
@@ -16,12 +16,20 @@
|
|
|
16
16
|
* Idempotent: it wipes+rewrites the destination on every run. `TENANT_ASSETS_DEST`
|
|
17
17
|
* overrides the destination (used by tests / spikes).
|
|
18
18
|
*
|
|
19
|
-
* VERSIONED TENANTS
|
|
19
|
+
* VERSIONED TENANTS: a tenant whose assets are
|
|
20
20
|
* served version-scoped from R2 (src/pages/tenants/[id]/[...path].ts) must NOT be
|
|
21
21
|
* baked here — otherwise the static file would shadow the R2 route. Opt a tenant
|
|
22
22
|
* out by adding a `tenants/<id>/.tot/assets-versioned` marker file, or by listing
|
|
23
23
|
* its id in the `TENANT_ASSETS_VERSIONED` env (comma-separated). Non-versioned
|
|
24
|
-
* tenants
|
|
24
|
+
* tenants are baked here.
|
|
25
|
+
*
|
|
26
|
+
* TENANT_ASSETS_BAKE_VERSIONED=1 overrides ALL of the above and bakes every
|
|
27
|
+
* tenant's public/ regardless of versioned-ness. Set by the STATIC-plane bundle
|
|
28
|
+
* build (scripts/publish/materialize-tenant.mjs): the S3/CloudFront static site
|
|
29
|
+
* has no R2 asset route — a bundle is self-contained by definition — so
|
|
30
|
+
* skipping a versioned tenant's assets there just produces a bundle whose pages
|
|
31
|
+
* reference files that don't exist. The Worker-plane build never sets this, so
|
|
32
|
+
* R2 shadowing semantics are unchanged there.
|
|
25
33
|
*/
|
|
26
34
|
import { cp, mkdir, readdir, rm } from "node:fs/promises";
|
|
27
35
|
import { existsSync } from "node:fs";
|
|
@@ -44,9 +52,11 @@ const versionedEnv = new Set(
|
|
|
44
52
|
.map((s) => s.trim())
|
|
45
53
|
.filter(Boolean),
|
|
46
54
|
);
|
|
55
|
+
const bakeVersioned = process.env.TENANT_ASSETS_BAKE_VERSIONED === "1";
|
|
47
56
|
const isVersioned = (id) =>
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
!bakeVersioned &&
|
|
58
|
+
(versionedEnv.has(id) ||
|
|
59
|
+
existsSync(join(src, id, ".tot", "assets-versioned")));
|
|
50
60
|
|
|
51
61
|
let copied = 0;
|
|
52
62
|
let skipped = 0;
|
package/scripts/dev/ai-edit.mjs
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* `tot` CLI, so `~/.tot/credentials.json` is directly readable. No token
|
|
16
16
|
* plumbing needed between the CLI and the dev server.
|
|
17
17
|
*
|
|
18
|
-
* Scope
|
|
18
|
+
* Scope: content + theme.json ONLY.
|
|
19
19
|
* The MCP tool enforces this server-side; nothing here widens it.
|
|
20
20
|
*/
|
|
21
21
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from "node:fs";
|
|
@@ -35,8 +35,8 @@ export function resync(sourceRoot, targetRoot, rel) {
|
|
|
35
35
|
* RELIABILITY-FIRST: defaults to an mtime POLL, not native `fs.watch`. Native watch
|
|
36
36
|
* is event-driven but LOSSY on macOS — editors that save via write-temp-then-rename
|
|
37
37
|
* (VS Code, vim, JetBrains, …) routinely deliver NO change event on the original
|
|
38
|
-
* path, so the save silently fails to hot-reload (the
|
|
39
|
-
*
|
|
38
|
+
* path, so the save silently fails to hot-reload. The poll stat()s the tree on a
|
|
39
|
+
* short interval and never misses an edit.
|
|
40
40
|
*
|
|
41
41
|
* Opt into event-driven native watch with TOT_DEV_NATIVE_WATCH=1 (it still falls
|
|
42
42
|
* back to polling where recursive watch is unavailable, e.g. Linux). Dependency-free
|
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Follows the scripts/dev delegation pattern (see ai-edit.mjs): all logic lives
|
|
7
7
|
* here, dependency-free and testable under `node --test`; a thin dev-only Vite
|
|
8
|
-
* plugin (dev-plugins/dev-file-browser.mjs
|
|
8
|
+
* plugin (dev-plugins/dev-file-browser.mjs) maps
|
|
9
9
|
* req/res onto `handleTree` / `handleFile`. Every path is routed through
|
|
10
10
|
* safe-path.mjs; the source root comes from tenant-source-root.mjs (never SSR).
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* the tree here carries structure only, so this module owns no git surface and
|
|
14
|
-
* doesn't collide with C4.
|
|
12
|
+
* The tree here carries structure only, so this module owns no git surface.
|
|
15
13
|
*/
|
|
16
14
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
17
15
|
import { join, extname, sep } from "node:path";
|
|
@@ -111,8 +109,8 @@ export function readFile(sourceRoot, relPath) {
|
|
|
111
109
|
}
|
|
112
110
|
|
|
113
111
|
const norm = relPath.split(sep).join("/");
|
|
114
|
-
// The viewer needs the on-disk version (
|
|
115
|
-
// the write allowlist (
|
|
112
|
+
// The viewer needs the on-disk version (baseVersion) + whether the file is in
|
|
113
|
+
// the write allowlist (editable vs read-only) — surfaced on every ok read.
|
|
116
114
|
const mtimeMs = st.mtimeMs;
|
|
117
115
|
const writable = matchesWriteAllowlist(norm);
|
|
118
116
|
if (BINARY_EXT.has(extname(norm).toLowerCase())) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* lands on disk (and C1's Monitor timestamps the save→reload) — this module never
|
|
16
16
|
* sends its own reload.
|
|
17
17
|
*
|
|
18
|
-
* Stale-write guard
|
|
18
|
+
* Stale-write guard: writeSourceFile/handleWrite accept an optional
|
|
19
19
|
* baseVersion — the mtimeMs the client captured when it opened or last saved the
|
|
20
20
|
* file. If the file on disk has since changed (someone edited it via $EDITOR, git,
|
|
21
21
|
* or the AI ask box while autosave was armed), the write is refused with
|
|
@@ -74,7 +74,7 @@ export function writeSourceFile(sourceRoot, relPath, content, opts = {}) {
|
|
|
74
74
|
} catch {
|
|
75
75
|
/* leaf doesn't exist yet — a new file, which is fine */
|
|
76
76
|
}
|
|
77
|
-
// Stale-write guard
|
|
77
|
+
// Stale-write guard: if the client supplied the version it last saw and the
|
|
78
78
|
// file has since changed on disk (or now exists when it expected none), refuse
|
|
79
79
|
// rather than clobber. Omit baseVersion to force-save.
|
|
80
80
|
const baseVersion = opts && opts.baseVersion;
|
|
@@ -123,7 +123,7 @@ export function handleWrite(env = process.env, body = {}) {
|
|
|
123
123
|
return { status: 400, json: { ok: false, status: "no_path", message: "path is required" } };
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
const result = writeSourceFile(root.sourceRoot, relPath, body.content, {
|
|
126
|
+
const result = writeSourceFile(root.sourceRoot, relPath, /** @type {string} */ (body.content), {
|
|
127
127
|
baseVersion: body ? body.baseVersion : undefined,
|
|
128
128
|
});
|
|
129
129
|
const status = result.ok
|
|
@@ -118,10 +118,10 @@ export function computeDiff(gitCwd, sourceRoot, relPath) {
|
|
|
118
118
|
} catch {
|
|
119
119
|
workingExists = false;
|
|
120
120
|
}
|
|
121
|
-
if (
|
|
121
|
+
if (st && st.isDirectory()) {
|
|
122
122
|
return { ok: false, status: "is_dir", message: `${relPath} is a directory` };
|
|
123
123
|
}
|
|
124
|
-
if (workingExists) {
|
|
124
|
+
if (workingExists && st) {
|
|
125
125
|
if (BINARY_EXT.has(extname(posix).toLowerCase())) {
|
|
126
126
|
return { ok: true, path: posix, status: fileStatus(gitCwd, relPath), shown: false, reason: "binary" };
|
|
127
127
|
}
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* integration-guardrails.mjs — make the epic's integration discipline routine and
|
|
3
|
+
* VISIBLE before a unit branch is folded into the umbrella. One command answers
|
|
4
|
+
* the four questions the lead integrator otherwise asks (and hand-writes a note
|
|
5
|
+
* about) for every unit:
|
|
6
|
+
*
|
|
7
|
+
* 1. Ownership — does this branch only touch files inside its declared area,
|
|
8
|
+
* or did it wander into files another in-flight unit owns?
|
|
9
|
+
* 2. Base drift — has the umbrella moved far enough ahead of this branch's
|
|
10
|
+
* merge-base that a refresh/rebase is due before folding?
|
|
11
|
+
* 3. Sibling — do any files this branch touches ALSO appear on a concurrent
|
|
12
|
+
* overlap issue/ fix/ wip/ impl/ branch (a hot-file collision, e.g.
|
|
13
|
+
* the admin Publish module the whole epic keeps colliding on)?
|
|
14
|
+
* 4. Focused — do the scoped lint/typecheck/test commands for the touched
|
|
15
|
+
* checks files actually PASS (not just "did you run something")?
|
|
16
|
+
*
|
|
17
|
+
* It then prints a structured **integrator handoff note** (files touched grouped
|
|
18
|
+
* by area, drift status, overlaps, check results) that the lead can paste straight
|
|
19
|
+
* into an integration commit/PR instead of composing by hand.
|
|
20
|
+
*
|
|
21
|
+
* Design mirrors scripts/preview/*-doctor.sh: findings are ordered so the FIRST
|
|
22
|
+
* ✗ (blocker) or ⚠ (warning) is the thing to deal with, each carries its remedy,
|
|
23
|
+
* and the exit code is non-zero when a blocker (a failed focused check) is present
|
|
24
|
+
* so it is safe to drop into a pre-fold gate.
|
|
25
|
+
*
|
|
26
|
+
* The git/exec-driven parts live in `main()`; everything checkable — glob→area
|
|
27
|
+
* matching, drift classification, sibling-overlap join, check derivation, note
|
|
28
|
+
* formatting — is a pure exported function with no I/O so it unit-tests under
|
|
29
|
+
* `node --test` (the scripts/dev convention), with no build step and no deps
|
|
30
|
+
* beyond node: builtins + the `git` CLI.
|
|
31
|
+
*
|
|
32
|
+
* Ownership convention: a branch's area is a list of path globs, declared either
|
|
33
|
+
* • inline: --area 'scripts/dev/**' --area 'docs/dev/**' (repeatable), or
|
|
34
|
+
* • in a manifest `.tot-ownership.json` at the repo root, keyed by branch name:
|
|
35
|
+
* { "areas": { "issue/integration-conflict-guardrails": ["scripts/dev/**"] } }
|
|
36
|
+
* With no area declared the ownership check is SKIPPED (reported as such) — it
|
|
37
|
+
* never fabricates a verdict it can't back.
|
|
38
|
+
*
|
|
39
|
+
* Usage:
|
|
40
|
+
* node scripts/dev/integration-guardrails.mjs [--umbrella <ref>] [--area <glob>]...
|
|
41
|
+
* [--manifest <path>] [--no-siblings] [--no-checks] [--json]
|
|
42
|
+
* # defaults: --umbrella feature/8500-storefront-integration, manifest .tot-ownership.json
|
|
43
|
+
*/
|
|
44
|
+
import { execFileSync } from "node:child_process";
|
|
45
|
+
|
|
46
|
+
/** Umbrella branch this epic folds into. */
|
|
47
|
+
export const DEFAULT_UMBRELLA = "feature/8500-storefront-integration";
|
|
48
|
+
|
|
49
|
+
/** Branch-name prefixes that denote a concurrent, in-flight unit. */
|
|
50
|
+
export const UNIT_BRANCH_PREFIXES = ["issue/", "fix/", "wip/", "impl/"];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Compile a path glob to a RegExp. Supports `**` (any chars incl. `/`), `*`
|
|
54
|
+
* (any chars except `/`), and a trailing `/` as shorthand for a directory prefix
|
|
55
|
+
* (`scripts/dev/` ≡ `scripts/dev/**`). All other regex metacharacters are escaped.
|
|
56
|
+
* @param {string} glob
|
|
57
|
+
* @returns {RegExp}
|
|
58
|
+
*/
|
|
59
|
+
export function globToRegExp(glob) {
|
|
60
|
+
let g = String(glob).replace(/^\.\//, "");
|
|
61
|
+
if (g.endsWith("/")) g += "**";
|
|
62
|
+
let re = "";
|
|
63
|
+
for (let i = 0; i < g.length; i++) {
|
|
64
|
+
const c = g[i];
|
|
65
|
+
if (c === "*") {
|
|
66
|
+
if (g[i + 1] === "*") {
|
|
67
|
+
re += ".*";
|
|
68
|
+
i++;
|
|
69
|
+
} else {
|
|
70
|
+
re += "[^/]*";
|
|
71
|
+
}
|
|
72
|
+
} else if ("\\^$.|?+()[]{}".includes(c)) {
|
|
73
|
+
re += "\\" + c;
|
|
74
|
+
} else {
|
|
75
|
+
re += c;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return new RegExp("^" + re + "$");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* True iff `file` (a posix repo-relative path) matches any of `globs`.
|
|
83
|
+
* @param {string} file
|
|
84
|
+
* @param {string[]} globs
|
|
85
|
+
* @returns {boolean}
|
|
86
|
+
*/
|
|
87
|
+
export function matchesAny(file, globs) {
|
|
88
|
+
const norm = String(file).replace(/^\.\//, "");
|
|
89
|
+
return (globs || []).some((g) => globToRegExp(g).test(norm));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Partition touched files into those inside the declared ownership area and those
|
|
94
|
+
* outside it (cross-area edits that warrant integrator review).
|
|
95
|
+
* @param {string[]} files
|
|
96
|
+
* @param {string[]} areaGlobs
|
|
97
|
+
* @returns {{ declared: boolean, inArea: string[], crossArea: string[] }}
|
|
98
|
+
*/
|
|
99
|
+
export function classifyOwnership(files, areaGlobs) {
|
|
100
|
+
const declared = Array.isArray(areaGlobs) && areaGlobs.length > 0;
|
|
101
|
+
if (!declared) return { declared: false, inArea: [...(files || [])], crossArea: [] };
|
|
102
|
+
const inArea = [];
|
|
103
|
+
const crossArea = [];
|
|
104
|
+
for (const f of files || []) {
|
|
105
|
+
if (matchesAny(f, areaGlobs)) inArea.push(f);
|
|
106
|
+
else crossArea.push(f);
|
|
107
|
+
}
|
|
108
|
+
return { declared: true, inArea, crossArea };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Classify how stale a branch's base is from the count of umbrella commits landed
|
|
113
|
+
* since the branch's merge-base. `warn` recommends a refresh; `stale` strongly
|
|
114
|
+
* recommends one before folding.
|
|
115
|
+
* @param {number} behind umbrella commits ahead of the merge-base
|
|
116
|
+
* @param {{ warnAt?: number, staleAt?: number }} [opts]
|
|
117
|
+
* @returns {{ level: "fresh"|"warn"|"stale", behind: number, recommend: string }}
|
|
118
|
+
*/
|
|
119
|
+
export function classifyBaseDrift(behind, opts = {}) {
|
|
120
|
+
const warnAt = opts.warnAt ?? 10;
|
|
121
|
+
const staleAt = opts.staleAt ?? 30;
|
|
122
|
+
const n = Number.isFinite(behind) ? behind : 0;
|
|
123
|
+
if (n >= staleAt) {
|
|
124
|
+
return {
|
|
125
|
+
level: "stale",
|
|
126
|
+
behind: n,
|
|
127
|
+
recommend: `umbrella is ${n} commits ahead of this branch's base — refresh before folding (git merge ${DEFAULT_UMBRELLA}, or rebase) so conflicts surface here, not in the integrator's fold`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (n >= warnAt) {
|
|
131
|
+
return {
|
|
132
|
+
level: "warn",
|
|
133
|
+
behind: n,
|
|
134
|
+
recommend: `umbrella is ${n} commits ahead — a refresh (git merge ${DEFAULT_UMBRELLA}) is recommended before folding`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return { level: "fresh", behind: n, recommend: "base is fresh — no refresh needed" };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Join this branch's touched files against each sibling branch's touched files,
|
|
142
|
+
* returning the files that appear on at least one sibling (hot-file collisions).
|
|
143
|
+
* @param {string[]} ourFiles
|
|
144
|
+
* @param {Record<string, string[]>} siblingFilesByBranch
|
|
145
|
+
* @returns {Array<{ file: string, branches: string[] }>}
|
|
146
|
+
*/
|
|
147
|
+
export function computeSiblingOverlaps(ourFiles, siblingFilesByBranch) {
|
|
148
|
+
const ours = new Set(ourFiles || []);
|
|
149
|
+
/** @type {Map<string, string[]>} */
|
|
150
|
+
const hits = new Map();
|
|
151
|
+
for (const [branch, files] of Object.entries(siblingFilesByBranch || {})) {
|
|
152
|
+
for (const f of files || []) {
|
|
153
|
+
if (!ours.has(f)) continue;
|
|
154
|
+
const list = hits.get(f) || [];
|
|
155
|
+
if (!list.includes(branch)) list.push(branch);
|
|
156
|
+
hits.set(f, list);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return [...hits.entries()]
|
|
160
|
+
.map(([file, branches]) => ({ file, branches: branches.sort() }))
|
|
161
|
+
.sort((a, b) => a.file.localeCompare(b.file));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Derive the focused check commands implied by a set of touched files — scoped to
|
|
166
|
+
* what changed rather than the whole `pnpm run check`. Pure/deterministic so the
|
|
167
|
+
* mapping is unit-tested; the runner in `main()` executes each returned command.
|
|
168
|
+
*
|
|
169
|
+
* • touched `scripts/**` (or storefront dev-plugins/integrations) `*.test.mjs`,
|
|
170
|
+
* plus the `.test.mjs` sibling of any touched non-test `.mjs`, → one `node --test`
|
|
171
|
+
* over exactly those files.
|
|
172
|
+
* • touched `scripts/**` `.mjs`/`.js` → `typecheck:scripts` (checkJs over scripts/).
|
|
173
|
+
* • touched `apps/storefront/src/**` `.ts`/`.astro`/`.tsx` → the storefront package typecheck.
|
|
174
|
+
*
|
|
175
|
+
* @param {string[]} files
|
|
176
|
+
* @param {(p: string) => boolean} [existsFn] whether a repo-relative path exists (for the .test.mjs sibling probe); defaults to always-true
|
|
177
|
+
* @returns {Array<{ name: string, command: string, args: string[] }>}
|
|
178
|
+
*/
|
|
179
|
+
export function deriveChecks(files, existsFn) {
|
|
180
|
+
const has = typeof existsFn === "function" ? existsFn : () => true;
|
|
181
|
+
const list = files || [];
|
|
182
|
+
const isScript = (f) => /^scripts\//.test(f) || /^apps\/storefront\/(dev-plugins|integrations)\//.test(f);
|
|
183
|
+
|
|
184
|
+
/** @type {Set<string>} */
|
|
185
|
+
const testFiles = new Set();
|
|
186
|
+
for (const f of list) {
|
|
187
|
+
if (!isScript(f)) continue;
|
|
188
|
+
if (f.endsWith(".test.mjs")) {
|
|
189
|
+
testFiles.add(f);
|
|
190
|
+
} else if (f.endsWith(".mjs") || f.endsWith(".js")) {
|
|
191
|
+
const sibling = f.replace(/\.(mjs|js)$/, ".test.mjs");
|
|
192
|
+
if (has(sibling)) testFiles.add(sibling);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** @type {Array<{ name: string, command: string, args: string[] }>} */
|
|
197
|
+
const checks = [];
|
|
198
|
+
if (testFiles.size > 0) {
|
|
199
|
+
checks.push({ name: "dev-script tests", command: "node", args: ["--test", ...[...testFiles].sort()] });
|
|
200
|
+
}
|
|
201
|
+
if (list.some((f) => /^scripts\/.*\.(mjs|js)$/.test(f))) {
|
|
202
|
+
checks.push({ name: "scripts typecheck", command: "pnpm", args: ["run", "typecheck:scripts"] });
|
|
203
|
+
}
|
|
204
|
+
if (list.some((f) => /^apps\/storefront\/src\/.*\.(ts|tsx|astro)$/.test(f))) {
|
|
205
|
+
checks.push({ name: "storefront typecheck", command: "pnpm", args: ["--filter", "@tot/storefront", "typecheck"] });
|
|
206
|
+
}
|
|
207
|
+
return checks;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @typedef {Object} GuardrailResult
|
|
212
|
+
* @property {string} branch
|
|
213
|
+
* @property {string} umbrella
|
|
214
|
+
* @property {string} mergeBase
|
|
215
|
+
* @property {string[]} files
|
|
216
|
+
* @property {{ level: "fresh"|"warn"|"stale", behind: number, recommend: string }} drift
|
|
217
|
+
* @property {{ declared: boolean, inArea: string[], crossArea: string[] }} ownership
|
|
218
|
+
* @property {Array<{ file: string, branches: string[] }>} overlaps
|
|
219
|
+
* @property {Array<{ name: string, ok: boolean, skipped?: boolean, detail?: string }>} checks
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Render the integrator handoff note (markdown) from a result. This is the note
|
|
224
|
+
* the lead otherwise writes by hand when folding a unit.
|
|
225
|
+
* @param {GuardrailResult} r
|
|
226
|
+
* @returns {string}
|
|
227
|
+
*/
|
|
228
|
+
export function formatHandoffNote(r) {
|
|
229
|
+
const lines = [];
|
|
230
|
+
lines.push(`### Integration handoff — ${r.branch}`);
|
|
231
|
+
lines.push("");
|
|
232
|
+
lines.push(`- **Umbrella:** ${r.umbrella} (merge-base ${short(r.mergeBase)})`);
|
|
233
|
+
lines.push(`- **Base drift:** ${r.drift.level} (${r.drift.behind} commit(s) behind umbrella)`);
|
|
234
|
+
|
|
235
|
+
const own = r.ownership;
|
|
236
|
+
if (!own.declared) {
|
|
237
|
+
lines.push(`- **Ownership:** not declared — ${r.files.length} file(s) touched, area unverified`);
|
|
238
|
+
} else {
|
|
239
|
+
lines.push(
|
|
240
|
+
`- **Ownership:** ${own.inArea.length} in-area, ${own.crossArea.length} cross-area` +
|
|
241
|
+
(own.crossArea.length ? ` ⚠ (${own.crossArea.join(", ")})` : ""),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (r.overlaps.length) {
|
|
246
|
+
lines.push(`- **Hot-file overlap:** ${r.overlaps.length} file(s) also on sibling branches ⚠`);
|
|
247
|
+
for (const o of r.overlaps) lines.push(` - \`${o.file}\` ↔ ${o.branches.join(", ")}`);
|
|
248
|
+
} else {
|
|
249
|
+
lines.push(`- **Hot-file overlap:** none`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (r.checks.length) {
|
|
253
|
+
const parts = r.checks.map((c) => `${c.skipped ? "skipped" : c.ok ? "✓" : "✗"} ${c.name}`);
|
|
254
|
+
lines.push(`- **Focused checks:** ${parts.join(", ")}`);
|
|
255
|
+
} else {
|
|
256
|
+
lines.push(`- **Focused checks:** none derived (no checkable files touched)`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
lines.push("");
|
|
260
|
+
lines.push(`**Files touched (${r.files.length}):**`);
|
|
261
|
+
for (const f of r.files) {
|
|
262
|
+
const tag = own.declared && own.crossArea.includes(f) ? " ⚠ cross-area" : "";
|
|
263
|
+
lines.push(`- \`${f}\`${tag}`);
|
|
264
|
+
}
|
|
265
|
+
return lines.join("\n");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** @param {string} sha */
|
|
269
|
+
function short(sha) {
|
|
270
|
+
return String(sha || "").slice(0, 9);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ─── git-driven CLI (not covered by unit tests — the pure logic above is) ─────
|
|
274
|
+
|
|
275
|
+
/** Run git in the repo; returns trimmed stdout, or "" on failure. @param {string[]} args */
|
|
276
|
+
function git(args) {
|
|
277
|
+
try {
|
|
278
|
+
return execFileSync("git", args, { encoding: "utf8", maxBuffer: 16 * 1024 * 1024 }).trim();
|
|
279
|
+
} catch {
|
|
280
|
+
return "";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Resolve the umbrella ref to fold against. A worktree's LOCAL umbrella branch is
|
|
286
|
+
* routinely behind what was last fetched, so prefer `origin/<ref>` when it exists
|
|
287
|
+
* and is not behind the local ref. If `ref` already names a remote/explicit rev,
|
|
288
|
+
* it is used as-is.
|
|
289
|
+
* @param {string} ref
|
|
290
|
+
* @returns {string}
|
|
291
|
+
*/
|
|
292
|
+
function resolveUmbrella(ref) {
|
|
293
|
+
if (ref.includes("/") && git(["rev-parse", "--verify", "--quiet", ref])) {
|
|
294
|
+
// already a remote-tracking or fully-qualified ref the caller chose
|
|
295
|
+
if (ref.startsWith("origin/") || ref.startsWith("refs/")) return ref;
|
|
296
|
+
}
|
|
297
|
+
const remote = `origin/${ref}`;
|
|
298
|
+
const remoteSha = git(["rev-parse", "--verify", "--quiet", remote]);
|
|
299
|
+
const localSha = git(["rev-parse", "--verify", "--quiet", ref]);
|
|
300
|
+
if (!remoteSha) return ref; // no remote-tracking ref — use whatever we were given
|
|
301
|
+
if (!localSha) return remote;
|
|
302
|
+
// Use the remote unless the local ref is strictly ahead of it (local has commits
|
|
303
|
+
// origin lacks — e.g. an integrator working locally ahead of a push).
|
|
304
|
+
const localAhead = Number(git(["rev-list", "--count", `${remote}..${localSha}`]) || "0");
|
|
305
|
+
return localAhead > 0 ? ref : remote;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Union of the files this unit changes: commits since the umbrella merge-base,
|
|
310
|
+
* plus working-tree changes vs HEAD (staged + unstaged), plus untracked files.
|
|
311
|
+
* Uses prefix-free plumbing (`diff --name-only`, `ls-files --others`) rather than
|
|
312
|
+
* `status --porcelain` so there is no status-column slicing to get wrong.
|
|
313
|
+
* @param {string} base
|
|
314
|
+
* @returns {string[]}
|
|
315
|
+
*/
|
|
316
|
+
function touchedFiles(base) {
|
|
317
|
+
const set = new Set();
|
|
318
|
+
const add = (out) => {
|
|
319
|
+
for (const l of out.split("\n")) if (l.trim()) set.add(l.trim());
|
|
320
|
+
};
|
|
321
|
+
if (base) add(git(["diff", "--name-only", `${base}..HEAD`]));
|
|
322
|
+
add(git(["diff", "--name-only", "HEAD"])); // staged + unstaged vs HEAD
|
|
323
|
+
add(git(["ls-files", "--others", "--exclude-standard"])); // untracked
|
|
324
|
+
return [...set].sort();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** @param {string} branch @param {string} umbrella */
|
|
328
|
+
function branchTouchedFiles(branch, umbrella) {
|
|
329
|
+
const base = git(["merge-base", branch, umbrella]);
|
|
330
|
+
if (!base) return [];
|
|
331
|
+
const out = git(["diff", "--name-only", `${base}..${branch}`]);
|
|
332
|
+
return out ? out.split("\n").filter(Boolean) : [];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** @param {string[]} argv */
|
|
336
|
+
function parseArgs(argv) {
|
|
337
|
+
const opts = {
|
|
338
|
+
umbrella: DEFAULT_UMBRELLA,
|
|
339
|
+
areas: /** @type {string[]} */ ([]),
|
|
340
|
+
manifest: ".tot-ownership.json",
|
|
341
|
+
siblings: true,
|
|
342
|
+
checks: true,
|
|
343
|
+
json: false,
|
|
344
|
+
};
|
|
345
|
+
for (let i = 0; i < argv.length; i++) {
|
|
346
|
+
const a = argv[i];
|
|
347
|
+
if (a === "--umbrella") opts.umbrella = argv[++i];
|
|
348
|
+
else if (a === "--area") opts.areas.push(argv[++i]);
|
|
349
|
+
else if (a === "--manifest") opts.manifest = argv[++i];
|
|
350
|
+
else if (a === "--no-siblings") opts.siblings = false;
|
|
351
|
+
else if (a === "--no-checks") opts.checks = false;
|
|
352
|
+
else if (a === "--json") opts.json = true;
|
|
353
|
+
else if (a === "-h" || a === "--help") opts.help = true;
|
|
354
|
+
else throw new Error(`unknown argument: ${a}`);
|
|
355
|
+
}
|
|
356
|
+
return opts;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function main() {
|
|
360
|
+
const { readFileSync, existsSync } = await import("node:fs");
|
|
361
|
+
let opts;
|
|
362
|
+
try {
|
|
363
|
+
opts = parseArgs(process.argv.slice(2));
|
|
364
|
+
} catch (e) {
|
|
365
|
+
process.stderr.write(`${e.message}\n`);
|
|
366
|
+
process.exit(2);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (opts.help) {
|
|
370
|
+
process.stdout.write(
|
|
371
|
+
"usage: node scripts/dev/integration-guardrails.mjs [--umbrella <ref>] [--area <glob>]... " +
|
|
372
|
+
"[--manifest <path>] [--no-siblings] [--no-checks] [--json]\n",
|
|
373
|
+
);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const branch = git(["rev-parse", "--abbrev-ref", "HEAD"]) || "HEAD";
|
|
378
|
+
// Fold against the SHARED umbrella tip, not a possibly-stale local ref: prefer
|
|
379
|
+
// origin/<umbrella> when it exists AND is at least as advanced as the local ref
|
|
380
|
+
// (a local umbrella branch in a worktree is routinely behind what was fetched).
|
|
381
|
+
const umbrella = resolveUmbrella(opts.umbrella);
|
|
382
|
+
const mergeBase = git(["merge-base", "HEAD", umbrella]);
|
|
383
|
+
if (!mergeBase) {
|
|
384
|
+
process.stderr.write(`✗ no merge-base between HEAD and ${umbrella} — is the umbrella fetched?\n`);
|
|
385
|
+
process.exit(1);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const behind = Number(git(["rev-list", "--count", `${mergeBase}..${umbrella}`]) || "0");
|
|
389
|
+
const files = touchedFiles(mergeBase);
|
|
390
|
+
|
|
391
|
+
// Ownership area: explicit --area wins; else manifest keyed by branch name.
|
|
392
|
+
let areas = opts.areas;
|
|
393
|
+
if (areas.length === 0 && existsSync(opts.manifest)) {
|
|
394
|
+
try {
|
|
395
|
+
const manifest = JSON.parse(readFileSync(opts.manifest, "utf8"));
|
|
396
|
+
const declared = manifest && manifest.areas && manifest.areas[branch];
|
|
397
|
+
if (Array.isArray(declared)) areas = declared;
|
|
398
|
+
} catch {
|
|
399
|
+
/* a malformed manifest just means "no declared area" */
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const drift = classifyBaseDrift(behind);
|
|
404
|
+
const ownership = classifyOwnership(files, areas);
|
|
405
|
+
|
|
406
|
+
/** @type {Record<string, string[]>} */
|
|
407
|
+
const siblingFiles = {};
|
|
408
|
+
if (opts.siblings) {
|
|
409
|
+
const raw = git(["for-each-ref", "--format=%(refname:short)", "refs/heads"]);
|
|
410
|
+
for (const b of raw.split("\n").filter(Boolean)) {
|
|
411
|
+
if (b === branch) continue;
|
|
412
|
+
if (!UNIT_BRANCH_PREFIXES.some((p) => b.startsWith(p))) continue;
|
|
413
|
+
const bf = branchTouchedFiles(b, umbrella);
|
|
414
|
+
if (bf.length) siblingFiles[b] = bf;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const overlaps = computeSiblingOverlaps(files, siblingFiles);
|
|
418
|
+
|
|
419
|
+
/** @type {Array<{ name: string, ok: boolean, skipped?: boolean, detail?: string }>} */
|
|
420
|
+
const checkResults = [];
|
|
421
|
+
const derived = deriveChecks(files, (p) => existsSync(p));
|
|
422
|
+
if (!opts.checks) {
|
|
423
|
+
for (const c of derived) checkResults.push({ name: c.name, ok: true, skipped: true });
|
|
424
|
+
} else {
|
|
425
|
+
for (const c of derived) {
|
|
426
|
+
try {
|
|
427
|
+
execFileSync(c.command, c.args, { stdio: "pipe", encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
428
|
+
checkResults.push({ name: c.name, ok: true });
|
|
429
|
+
} catch (e) {
|
|
430
|
+
const tail = String((e && (e.stdout || "")) + (e && (e.stderr || ""))).trim().split("\n").slice(-6).join("\n");
|
|
431
|
+
checkResults.push({ name: c.name, ok: false, detail: tail });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** @type {GuardrailResult} */
|
|
437
|
+
const result = { branch, umbrella, mergeBase, files, drift, ownership, overlaps, checks: checkResults };
|
|
438
|
+
|
|
439
|
+
if (opts.json) {
|
|
440
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
441
|
+
} else {
|
|
442
|
+
renderReport(result);
|
|
443
|
+
process.stdout.write("\n" + formatHandoffNote(result) + "\n");
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const blocker = checkResults.some((c) => !c.ok && !c.skipped);
|
|
447
|
+
process.exit(blocker ? 1 : 0);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** @param {GuardrailResult} r */
|
|
451
|
+
function renderReport(r) {
|
|
452
|
+
const ok = (s) => process.stdout.write(` ✓ ${s}\n`);
|
|
453
|
+
const bad = (s) => process.stdout.write(` ✗ ${s}\n`);
|
|
454
|
+
const warn = (s) => process.stdout.write(` ⚠ ${s}\n`);
|
|
455
|
+
const note = (s) => process.stdout.write(` → ${s}\n`);
|
|
456
|
+
|
|
457
|
+
process.stdout.write(`\n== integration guardrails: ${r.branch} → ${r.umbrella} ==\n`);
|
|
458
|
+
|
|
459
|
+
// 1. base drift
|
|
460
|
+
if (r.drift.level === "fresh") ok(`base fresh (${r.drift.behind} behind umbrella)`);
|
|
461
|
+
else warn(`base drift: ${r.drift.behind} behind umbrella`), note(r.drift.recommend);
|
|
462
|
+
|
|
463
|
+
// 2. ownership
|
|
464
|
+
if (!r.ownership.declared) {
|
|
465
|
+
warn(`ownership area not declared for ${r.branch}`);
|
|
466
|
+
note("declare it in .tot-ownership.json (areas[<branch>]) or pass --area <glob> to enable the check");
|
|
467
|
+
} else if (r.ownership.crossArea.length === 0) {
|
|
468
|
+
ok(`ownership clean — all ${r.ownership.inArea.length} touched file(s) inside declared area`);
|
|
469
|
+
} else {
|
|
470
|
+
warn(`${r.ownership.crossArea.length} cross-area edit(s) outside declared area`);
|
|
471
|
+
for (const f of r.ownership.crossArea) note(f);
|
|
472
|
+
note("confirm these are intended, or move them to their owner's branch before folding");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// 3. sibling overlap
|
|
476
|
+
if (r.overlaps.length === 0) ok("no hot-file overlap with sibling unit branches");
|
|
477
|
+
else {
|
|
478
|
+
warn(`${r.overlaps.length} file(s) also touched by concurrent unit branch(es)`);
|
|
479
|
+
for (const o of r.overlaps) note(`${o.file} ↔ ${o.branches.join(", ")}`);
|
|
480
|
+
note("coordinate fold order with the integrator; expect a conflict on these — refresh base first");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// 4. focused checks
|
|
484
|
+
if (r.checks.length === 0) ok("no focused checks derived (no checkable files touched)");
|
|
485
|
+
for (const c of r.checks) {
|
|
486
|
+
if (c.skipped) note(`check skipped (--no-checks): ${c.name}`);
|
|
487
|
+
else if (c.ok) ok(`focused check passed: ${c.name}`);
|
|
488
|
+
else {
|
|
489
|
+
bad(`focused check FAILED: ${c.name}`);
|
|
490
|
+
if (c.detail) for (const l of c.detail.split("\n")) note(l);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Run only when invoked directly (not when imported by the test).
|
|
496
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
497
|
+
main();
|
|
498
|
+
}
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* POST /__tot/locate {domPath, path} → {ok, path, line, col} | {status}
|
|
4
4
|
* POST /__tot/open-in-editor {path, line, col} → {ok} | {status}
|
|
5
5
|
*
|
|
6
|
-
* Keeps locate.mjs a PURE parse5 walk
|
|
6
|
+
* Keeps locate.mjs a PURE parse5 walk; this module
|
|
7
7
|
* adds the env/path-resolution/refusal concerns and is itself unit-testable under
|
|
8
8
|
* `node --test` with no running dev server (the scripts/dev delegation pattern — see
|
|
9
9
|
* file-browser.mjs). The thin dev-plugins/dev-locate.mjs wrapper maps req/res onto it.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* The served page wraps
|
|
12
12
|
* the raw content region (<main>, the whole of content/home.html) in the storefront's
|
|
13
13
|
* own chrome (skip-link, header, nav, footer) that is NOT in the tenant source. So we
|
|
14
14
|
* resolve the clicked URL to its raw content source and walk THAT from <body>:
|
|
@@ -90,7 +90,7 @@ export function handleLocate(env = process.env, body = {}) {
|
|
|
90
90
|
|
|
91
91
|
const html = readFileSync(abs, "utf8");
|
|
92
92
|
const found = locate(html, body.domPath);
|
|
93
|
-
if (typeof found.line === "number") {
|
|
93
|
+
if ("line" in found && typeof found.line === "number") {
|
|
94
94
|
return { status: 200, json: { ok: true, path: rel, line: found.line, col: found.col } };
|
|
95
95
|
}
|
|
96
96
|
// The walk missed: the element is outside the raw content region — i.e. the storefront
|
|
@@ -8,9 +8,9 @@ import net from "node:net";
|
|
|
8
8
|
* predicts astro's own bind on either IP family.
|
|
9
9
|
*
|
|
10
10
|
* WHY THIS EXISTS: Vite's `strictPort` defaults OFF, so when the requested port is
|
|
11
|
-
* taken astro SILENTLY moves to the next free port
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* taken astro SILENTLY moves to the next free port — stranding the CLI's liveness
|
|
12
|
+
* poll on the ORIGINAL port (answered by a stale foreign server) → a false
|
|
13
|
+
* "✨ You're live" pointing at the wrong server. Pre-flighting the port and
|
|
14
14
|
* failing clearly removes that whole class of silent failure.
|
|
15
15
|
*
|
|
16
16
|
* Dependency-free (node:net only) so it copies into the standalone runner and is
|