@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
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline dogfood of the eight v84 nonportable-code dispositions.
|
|
3
|
+
*
|
|
4
|
+
* This module sees only reviewed, redacted dossier metadata. It can emit typed
|
|
5
|
+
* semantic events into an in-memory callback, evaluate a pure path mapping, or
|
|
6
|
+
* record an inert retirement. It has no destination, browser, renderer,
|
|
7
|
+
* persistence, network, install, publish, or source-tenant capability.
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
CONSENT_CONFIG_SCHEMA_VERSION,
|
|
11
|
+
CommerceEventBroker,
|
|
12
|
+
ManagedAnalyticsEventBrokerBridge,
|
|
13
|
+
buildTenantConsentBroker,
|
|
14
|
+
validateConsentConfig,
|
|
15
|
+
validateTenantActionRegistry,
|
|
16
|
+
type CommerceEvent,
|
|
17
|
+
} from "@tot/public-runtime";
|
|
18
|
+
import type {
|
|
19
|
+
V84NonportableCodeIntentDossier,
|
|
20
|
+
} from "./v84NonportableCodeCoaching.js";
|
|
21
|
+
import type { V84CloneMigrationEvidence } from "./v84CloneMigrationEvidence.js";
|
|
22
|
+
import { assertExactV84DogfoodContract } from "./v84NonportableRouteContract.js";
|
|
23
|
+
import {
|
|
24
|
+
sameV84ReplayValue,
|
|
25
|
+
type V84CleanCloneReplayContext,
|
|
26
|
+
} from "./v84CleanCloneReplayContext.js";
|
|
27
|
+
|
|
28
|
+
const SOURCE = "tokenoftrust.com" as const;
|
|
29
|
+
const CLONE = "tokenoftrust.com~clone~gtm-migration-v84" as const;
|
|
30
|
+
const AT = "2026-08-30T00:00:00.000Z";
|
|
31
|
+
const CORRELATION = { id: "corr-v84-nonportable-dogfood", expiresAt: "2026-08-30T02:00:00.000Z" };
|
|
32
|
+
const PROTECTED = ["admin", "account", "identity", "age-assurance", "checkout", "payment"] as const;
|
|
33
|
+
|
|
34
|
+
export type V84DogfoodOutcome = "converted" | "retired" | "evidence-rejected";
|
|
35
|
+
|
|
36
|
+
export interface V84DogfoodLedgerRow {
|
|
37
|
+
readonly identity: string;
|
|
38
|
+
readonly intent: string;
|
|
39
|
+
readonly route: string;
|
|
40
|
+
readonly attemptedSteps: readonly string[];
|
|
41
|
+
readonly artifact: Readonly<Record<string, unknown>>;
|
|
42
|
+
readonly expectedVersusActual: { readonly expected: string; readonly actual: string };
|
|
43
|
+
readonly testsAndCloneQa: readonly string[];
|
|
44
|
+
readonly rollbackProof: string;
|
|
45
|
+
readonly finalOutcome: V84DogfoodOutcome;
|
|
46
|
+
readonly productRunbookGap: string | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function consentBroker() {
|
|
50
|
+
const checked = validateConsentConfig({
|
|
51
|
+
schemaVersion: CONSENT_CONFIG_SCHEMA_VERSION,
|
|
52
|
+
purposes: [{ purpose: "analytics", legalBasis: "consent", defaultState: "denied" }],
|
|
53
|
+
cmp: { mode: "none" },
|
|
54
|
+
});
|
|
55
|
+
if (!checked.ok) throw new Error("The fixed local consent policy is invalid.");
|
|
56
|
+
const broker = buildTenantConsentBroker(checked.value);
|
|
57
|
+
broker.setDecisions({ analytics: "granted" });
|
|
58
|
+
return broker;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function vendorAdapterId(dossier: V84NonportableCodeIntentDossier): string {
|
|
62
|
+
const ids: Readonly<Record<string, string>> = {
|
|
63
|
+
"tag:4": "platform.hubspot",
|
|
64
|
+
"tag:18": "platform.capterra",
|
|
65
|
+
"tag:29": "platform.linkedin",
|
|
66
|
+
"tag:31": "platform.g2",
|
|
67
|
+
};
|
|
68
|
+
const adapterId = ids[dossier.sourceObject.nodeKey];
|
|
69
|
+
if (!adapterId) throw new Error("The vendor destination route is not reviewed.");
|
|
70
|
+
return adapterId;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function exerciseNativeHook(dossier: V84NonportableCodeIntentDossier) {
|
|
74
|
+
const catalogLookup = { adapterId: vendorAdapterId(dossier), found: false } as const;
|
|
75
|
+
if (dossier.requiredContract.actionSelection === "owner-selection-required") {
|
|
76
|
+
return {
|
|
77
|
+
kind: "native-contract-refusal",
|
|
78
|
+
hook: dossier.recommendedTotPort.nativeHook,
|
|
79
|
+
approvedChoices: [...dossier.requiredContract.approvedActionNames],
|
|
80
|
+
deliveryCount: 0,
|
|
81
|
+
reason: "owner-action-selection-required",
|
|
82
|
+
catalogLookup,
|
|
83
|
+
} as const;
|
|
84
|
+
}
|
|
85
|
+
const registry = validateTenantActionRegistry({
|
|
86
|
+
schemaVersion: 1,
|
|
87
|
+
actions: dossier.requiredContract.approvedActionNames.map((name) => ({ name })),
|
|
88
|
+
});
|
|
89
|
+
if (!registry.ok) throw new Error("The dossier action contract is invalid.");
|
|
90
|
+
const consent = consentBroker();
|
|
91
|
+
const delivered: CommerceEvent[] = [];
|
|
92
|
+
const broker = new CommerceEventBroker({
|
|
93
|
+
trusted: { tenant: CLONE, environment: "candidate", siteSurface: "marketing" },
|
|
94
|
+
source: `urn:tot:storefront:v84-dogfood:${dossier.dossierId}`,
|
|
95
|
+
consentSnapshot: () => consent.getSnapshot(),
|
|
96
|
+
tenantActionRegistry: registry.value,
|
|
97
|
+
now: () => AT,
|
|
98
|
+
});
|
|
99
|
+
broker.subscribe({
|
|
100
|
+
id: `fake-local-${dossier.dossierId}`,
|
|
101
|
+
consentPurpose: "analytics",
|
|
102
|
+
types: dossier.recommendedTotPort.nativeHook ? [dossier.recommendedTotPort.nativeHook] : [],
|
|
103
|
+
sink: { kind: "callback", handler: (event) => delivered.push(event) },
|
|
104
|
+
});
|
|
105
|
+
const bridge = new ManagedAnalyticsEventBrokerBridge({ broker });
|
|
106
|
+
let results;
|
|
107
|
+
if (dossier.recommendedTotPort.nativeHook === "storefront.action.activated.v1") {
|
|
108
|
+
const initialized = bridge.initialize({ logicalId: `${dossier.dossierId}-document`, path: "/v84-dogfood", correlation: CORRELATION });
|
|
109
|
+
if (!initialized.ok || !initialized.emitted) throw new Error("The local action bridge did not initialize safely.");
|
|
110
|
+
results = dossier.requiredContract.approvedActionNames.map((name, index) =>
|
|
111
|
+
bridge.activate({ logicalId: `${dossier.dossierId}-action-${index + 1}`, name, correlation: CORRELATION }));
|
|
112
|
+
} else {
|
|
113
|
+
results = [bridge.initialize({ logicalId: dossier.dossierId, path: "/v84-dogfood", correlation: CORRELATION })];
|
|
114
|
+
}
|
|
115
|
+
bridge.dispose();
|
|
116
|
+
if (results.some((result) => !result.ok || !result.emitted) || delivered.length !== results.length) {
|
|
117
|
+
throw new Error("The local native-hook exercise did not deliver the exact typed event set.");
|
|
118
|
+
}
|
|
119
|
+
if (delivered.some((event) => event.storefront.tenant !== CLONE || event.storefront.environment === "live")) {
|
|
120
|
+
throw new Error("The local native-hook exercise escaped the diagnostic clone.");
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
kind: "in-memory-native-event-proof",
|
|
124
|
+
hook: delivered[0]?.type,
|
|
125
|
+
payloads: delivered.map((event) => event.data),
|
|
126
|
+
payloadKeys: [...new Set(delivered.flatMap((event) => Object.keys(event.data)))].sort(),
|
|
127
|
+
deliveryCount: delivered.length,
|
|
128
|
+
destination: "fake-local-callback",
|
|
129
|
+
disposed: true,
|
|
130
|
+
catalogLookup,
|
|
131
|
+
} as const;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function retire(dossier: V84NonportableCodeIntentDossier, replayContext?: V84CleanCloneReplayContext) {
|
|
135
|
+
if (dossier.recommendedTotPort.nativeHook !== null) {
|
|
136
|
+
throw new Error("A retirement dossier unexpectedly requested runtime authority.");
|
|
137
|
+
}
|
|
138
|
+
const cloneLocalMarkers = new Set<string>();
|
|
139
|
+
const marker = `${CLONE}:retired:${dossier.sourceObject.nodeKey}`;
|
|
140
|
+
const markerKey = `stage/dogfood/retirement/${dossier.sourceObject.nodeKey}`;
|
|
141
|
+
if (replayContext) replayContext.write(markerKey, marker);
|
|
142
|
+
else cloneLocalMarkers.add(marker);
|
|
143
|
+
const markerObserved = replayContext
|
|
144
|
+
? replayContext.read<string>(markerKey) === marker
|
|
145
|
+
: cloneLocalMarkers.has(marker);
|
|
146
|
+
if (replayContext) replayContext.delete(markerKey);
|
|
147
|
+
else cloneLocalMarkers.delete(marker);
|
|
148
|
+
const markerRemoved = replayContext
|
|
149
|
+
? replayContext.read<string>(markerKey) === undefined
|
|
150
|
+
: !cloneLocalMarkers.has(marker);
|
|
151
|
+
if (!markerObserved || !markerRemoved) throw new Error("The inert retirement marker was not reversible.");
|
|
152
|
+
return {
|
|
153
|
+
kind: "inert-retirement-record",
|
|
154
|
+
retained: { nodeKey: dossier.sourceObject.nodeKey, dossierId: dossier.dossierId },
|
|
155
|
+
runtimeAuthority: "none",
|
|
156
|
+
installCreated: false,
|
|
157
|
+
markerObserved,
|
|
158
|
+
markerRemoved,
|
|
159
|
+
} as const;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function rejectedGap(dossier: V84NonportableCodeIntentDossier): string {
|
|
163
|
+
if (dossier.sourceObject.nodeKey === "variable:38") {
|
|
164
|
+
return "consumer decision required: name the consumer and approve a closed output enum before conversion";
|
|
165
|
+
}
|
|
166
|
+
const gap = dossier.recommendedTotPort.owningProductGap;
|
|
167
|
+
if (!gap) throw new Error("A non-retirement route lacks an exact owning-product gap.");
|
|
168
|
+
const ownerGap = dossier.requiredContract.actionSelection === "owner-selection-required"
|
|
169
|
+
? "; consumer owner must select one approved action name"
|
|
170
|
+
: "";
|
|
171
|
+
return `owning product capability unavailable: ${gap}${ownerGap}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function exercise(
|
|
175
|
+
dossier: V84NonportableCodeIntentDossier,
|
|
176
|
+
replayContext?: V84CleanCloneReplayContext,
|
|
177
|
+
): V84DogfoodLedgerRow {
|
|
178
|
+
if (dossier.recommendedTotPort.route === "retire") {
|
|
179
|
+
return {
|
|
180
|
+
identity: dossier.sourceObject.nodeKey,
|
|
181
|
+
intent: dossier.recoveredBusinessIntent,
|
|
182
|
+
route: "retire",
|
|
183
|
+
attemptedSteps: ["matched the exact redacted identity and graph role", "asserted no native hook", "created and observed a clone-local inert marker", "removed the marker"],
|
|
184
|
+
artifact: retire(dossier, replayContext),
|
|
185
|
+
expectedVersusActual: {
|
|
186
|
+
expected: "retain audit identity with zero runtime behavior",
|
|
187
|
+
actual: "inert retirement recorded; no install, hook, destination, or runtime authority created",
|
|
188
|
+
},
|
|
189
|
+
testsAndCloneQa: ["identity accounted once", "dependency graph checked", "marker observed then removed", "no hook requested", "clone effect counters remain zero"],
|
|
190
|
+
rollbackProof: "No runtime state exists to restore; removing the inert record leaves behavior unchanged.",
|
|
191
|
+
finalOutcome: "retired",
|
|
192
|
+
productRunbookGap: null,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const isMapping = dossier.recommendedTotPort.route === "declarative-typed-mapping";
|
|
196
|
+
const artifact = isMapping ? {
|
|
197
|
+
kind: "declarative-conversion-refusal",
|
|
198
|
+
observedGraph: { consumer: "trigger:39", variable: "variable:38", consumingTagCount: 0 },
|
|
199
|
+
mappingCreated: false,
|
|
200
|
+
reason: "named-consumer-and-output-enum-unresolved",
|
|
201
|
+
} as const : exerciseNativeHook(dossier);
|
|
202
|
+
const gap = rejectedGap(dossier);
|
|
203
|
+
return {
|
|
204
|
+
identity: dossier.sourceObject.nodeKey,
|
|
205
|
+
intent: dossier.recoveredBusinessIntent,
|
|
206
|
+
route: dossier.recommendedTotPort.route,
|
|
207
|
+
attemptedSteps: isMapping
|
|
208
|
+
? ["verified trigger:39 references variable:38", "verified no tag consumes trigger:39", "looked for a named consumer and approved output enum", "refused to invent a mapping"]
|
|
209
|
+
: dossier.requiredContract.actionSelection === "owner-selection-required"
|
|
210
|
+
? ["validated the closed action choice set", "refused to choose for the owner", "confirmed zero delivery", "looked up the owned destination"]
|
|
211
|
+
: ["validated the closed native event contract", "granted local analytics consent", "delivered the exact typed event set to an in-memory callback", "disposed the bridge", "looked up the owned destination"],
|
|
212
|
+
artifact,
|
|
213
|
+
expectedVersusActual: {
|
|
214
|
+
expected: dossier.recommendedTotPort.saferAlternative,
|
|
215
|
+
actual: dossier.requiredContract.actionSelection === "owner-selection-required"
|
|
216
|
+
? `closed choice set validated with zero emission; exercise stopped: ${gap}`
|
|
217
|
+
: isMapping
|
|
218
|
+
? `no mapping was created; exercise stopped: ${gap}`
|
|
219
|
+
: `safe local native contract exercise passed, but conversion stopped: ${gap}`,
|
|
220
|
+
},
|
|
221
|
+
testsAndCloneQa: isMapping
|
|
222
|
+
? ["exact graph edge is present", "consumer tag count is zero", "no mapping or destination effect exists"]
|
|
223
|
+
: dossier.requiredContract.actionSelection === "owner-selection-required"
|
|
224
|
+
? ["all approved choices retained", "zero events emitted before owner selection", "managed vendor adapter absent"]
|
|
225
|
+
: ["typed payload set delivered exactly once each", "clone tenant and candidate environment pinned", "fake callback disposed", "protected surfaces excluded"],
|
|
226
|
+
rollbackProof: isMapping
|
|
227
|
+
? "Discarding the refusal artifact leaves the prior no-mapping state unchanged; no consumer was connected."
|
|
228
|
+
: dossier.requiredContract.actionSelection === "owner-selection-required"
|
|
229
|
+
? "No subscriber, event, install, or destination state was created before the required owner decision."
|
|
230
|
+
: "Disposal removes the only in-memory subscriber; no install or destination state exists.",
|
|
231
|
+
finalOutcome: "evidence-rejected",
|
|
232
|
+
productRunbookGap: gap,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Exercise every reviewed route and return a terminal eight-row ledger. */
|
|
237
|
+
export function buildV84NonportableRouteDogfood(
|
|
238
|
+
evidence: V84CloneMigrationEvidence,
|
|
239
|
+
catalogAdapterIds: readonly string[],
|
|
240
|
+
replayContext?: V84CleanCloneReplayContext,
|
|
241
|
+
) {
|
|
242
|
+
if (replayContext && !sameV84ReplayValue(
|
|
243
|
+
replayContext.read<V84CloneMigrationEvidence>("stage/import/handoff"),
|
|
244
|
+
evidence,
|
|
245
|
+
)) {
|
|
246
|
+
throw new Error("The dogfood proof did not receive the shared import handoff.");
|
|
247
|
+
}
|
|
248
|
+
const safe = assertExactV84DogfoodContract(evidence, catalogAdapterIds);
|
|
249
|
+
const coaching = safe.evidence.nonportableCodeCoaching;
|
|
250
|
+
if (!coaching.policy.sourceReadOnly || !coaching.policy.importedArtifactsInert || coaching.policy.installsOrPublishes) {
|
|
251
|
+
throw new Error("The coaching policy is outside the inert diagnostic scope.");
|
|
252
|
+
}
|
|
253
|
+
if (coaching.dossiers.length !== 8 || new Set(coaching.dossiers.map((item) => item.sourceObject.nodeKey)).size !== 8) {
|
|
254
|
+
throw new Error("The dogfood gate requires exactly eight unique dossiers.");
|
|
255
|
+
}
|
|
256
|
+
if (coaching.dossiers.some((item) => item.recommendedTotPort.status !== "not-installed")) {
|
|
257
|
+
throw new Error("The dogfood input must be the reviewed pre-install coaching state.");
|
|
258
|
+
}
|
|
259
|
+
const ledger = coaching.dossiers.map((dossier) => exercise(dossier, replayContext));
|
|
260
|
+
if (ledger.some((row) => !["converted", "retired", "evidence-rejected"].includes(row.finalOutcome))) {
|
|
261
|
+
throw new Error("Every dogfood route must end in an explicit terminal outcome.");
|
|
262
|
+
}
|
|
263
|
+
const result = {
|
|
264
|
+
schemaVersion: 1 as const,
|
|
265
|
+
scope: { sourceTenant: SOURCE, sourceMode: "read-only-fixture" as const, cloneTenant: CLONE, destinationMode: "fake-local-only" as const },
|
|
266
|
+
ledger,
|
|
267
|
+
summary: {
|
|
268
|
+
total: ledger.length,
|
|
269
|
+
converted: ledger.filter((row) => row.finalOutcome === "converted").length,
|
|
270
|
+
retired: ledger.filter((row) => row.finalOutcome === "retired").length,
|
|
271
|
+
evidenceRejected: ledger.filter((row) => row.finalOutcome === "evidence-rejected").length,
|
|
272
|
+
unexplainedNotInstalled: 0,
|
|
273
|
+
},
|
|
274
|
+
catalogLookup: {
|
|
275
|
+
presentAdapterIds: [...safe.catalogAdapterIds].sort(),
|
|
276
|
+
requestedVendorAdapterIds: ["platform.hubspot", "platform.capterra", "platform.linkedin", "platform.g2"],
|
|
277
|
+
missingVendorAdapterIds: ["platform.hubspot", "platform.capterra", "platform.linkedin", "platform.g2"],
|
|
278
|
+
},
|
|
279
|
+
safety: {
|
|
280
|
+
sourceReads: 0, sourceWrites: 0, clonePersistentWrites: 0, externalNetworkRequests: 0,
|
|
281
|
+
liveSecretsUsed: 0, productionActions: 0, importedCodeExecuted: false,
|
|
282
|
+
originalMarkupRendered: false, runtimeLoaderInitialized: false, dataLayerTouched: false,
|
|
283
|
+
},
|
|
284
|
+
protectedSurfaces: [...PROTECTED],
|
|
285
|
+
};
|
|
286
|
+
if (replayContext) {
|
|
287
|
+
for (const row of ledger) replayContext.write(`stage/dogfood/artifacts/${row.identity}`, row.artifact);
|
|
288
|
+
replayContext.write("stage/dogfood/handoff", result);
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Widget eligibility
|
|
2
|
+
* Widget eligibility. Mirrors
|
|
3
3
|
* `webhooks/emit.ts`'s `isEligibleTarget` discipline: an install is eligible
|
|
4
4
|
* to render its widget at `slot` only while it is LIVE — `active`, scoped for
|
|
5
5
|
* `widgets:launch`, and carrying a validated placement for that slot. This
|
|
6
6
|
* live check is what makes suspending an install actually stop it rendering
|
|
7
|
-
* on the very next page load — the
|
|
7
|
+
* on the very next page load — the "suspension kills widgets"
|
|
8
8
|
* enforcement — with no separate revoke step; it's just this gate re-run on
|
|
9
9
|
* every request.
|
|
10
10
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure prop-building for `<AppWidgetFrame
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Pure prop-building for `<AppWidgetFrame>`. The query-param handoff and the
|
|
3
|
+
* sandbox attrs live here (not in the `.astro` component) so they are
|
|
4
|
+
* unit-testable: `vitest.config.ts` deliberately does NOT
|
|
5
5
|
* render `.astro` components (Astro Container API breaks under vitest@2 +
|
|
6
6
|
* astro@7 — see that file's docstring), so render *logic* lives here and
|
|
7
7
|
* rendered output is verified on a live preview instead.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Widget launch-token minting
|
|
2
|
+
* Widget launch-token minting. Mints the
|
|
3
3
|
* short-lived RS256 JWT a platform-managed sandboxed widget iframe carries at
|
|
4
|
-
* SSR render time:
|
|
4
|
+
* SSR render time: the page-render path calls `mintWidgetLaunchToken` while rendering the
|
|
5
5
|
* page for each declared placement, and the resulting token is what the
|
|
6
6
|
* widget's own endpoint receives as proof it was launched by THIS platform,
|
|
7
7
|
* for THIS install, at THIS placement — never a merchant-forged or replayed
|
|
@@ -31,10 +31,10 @@ import {
|
|
|
31
31
|
export interface MintWidgetLaunchTokenInput {
|
|
32
32
|
/**
|
|
33
33
|
* Only `installId`/`tenantId`/`env` are read. Narrowed from `AppInstall` to
|
|
34
|
-
* this `Pick` so
|
|
34
|
+
* this `Pick` so the render path — which has `placements.ts`'s
|
|
35
35
|
* `ResolvedPlacement` (installId/appOrigin/endpoint), not the full
|
|
36
36
|
* `AppInstall` — can build the minimal object itself instead of re-fetching
|
|
37
|
-
* the install; a full `AppInstall` still satisfies this shape
|
|
37
|
+
* the install; a full `AppInstall` still satisfies this shape.
|
|
38
38
|
*/
|
|
39
39
|
install: Pick<AppInstall, "installId" | "tenantId" | "env">;
|
|
40
40
|
slot: WidgetPlacement;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Widget placement resolution
|
|
3
|
-
* apps have a live widget at this slot" into the concrete list
|
|
2
|
+
* Widget placement resolution. Turns "which
|
|
3
|
+
* apps have a live widget at this slot" into the concrete list the render path needs
|
|
4
4
|
* to render sandboxed iframes and mint launch tokens: reads
|
|
5
5
|
* `registry.listByTenant`, filters to the requested `env` and
|
|
6
6
|
* {@link isWidgetEligible}, then reads each eligible install's `endpoint`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Widget slot render composition
|
|
2
|
+
* Widget slot render composition. The one
|
|
3
3
|
* call a page/layout makes to turn "render slot X for this request" into the
|
|
4
4
|
* concrete, launch-token-bearing list `AppWidgetFrame` needs: resolves live
|
|
5
5
|
* placements (`placements.ts`), mints each one a launch token
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The EDGE transport for the pinned managed-analytics v1 contract: a thin HTTP
|
|
3
|
+
* adapter that satisfies {@link Ws1ManagedAnalyticsClient} by POSTing each
|
|
4
|
+
* pinned request to WS1's lifecycle service and returning the pinned response
|
|
5
|
+
* shape. WS1 owns the actual service; this file only carries the request over
|
|
6
|
+
* the wire.
|
|
7
|
+
*
|
|
8
|
+
* Tenant authority is server-derived, exactly as the pinned contract demands
|
|
9
|
+
* ("no request in this module accepts a tenant id"): the tenant is sent as a
|
|
10
|
+
* server-to-server header alongside the service credential — NEVER in a request
|
|
11
|
+
* body, and NEVER read from the caller's request body. Unconfigured service URL
|
|
12
|
+
* -> `null` (the route answers 503), never a guess.
|
|
13
|
+
*/
|
|
14
|
+
import type { APIContext } from "astro";
|
|
15
|
+
import {
|
|
16
|
+
MANAGED_ANALYTICS_API_V1_VERSION_POLICY,
|
|
17
|
+
type CreateMigrationDraftRequest,
|
|
18
|
+
type CreateMigrationDraftResponse,
|
|
19
|
+
} from "@tot/public-runtime";
|
|
20
|
+
import { readEnv } from "../env.js";
|
|
21
|
+
import type { Ws1ManagedAnalyticsClient } from "./gtmMigrationInstall.js";
|
|
22
|
+
|
|
23
|
+
export interface Ws1ManagedAnalyticsMigrationClient extends Ws1ManagedAnalyticsClient {
|
|
24
|
+
createMigrationDraft(request: CreateMigrationDraftRequest): Promise<CreateMigrationDraftResponse>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type Ws1Fetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
28
|
+
|
|
29
|
+
/** Base URL of WS1's managed-analytics lifecycle service (scheme + host). */
|
|
30
|
+
const SERVICE_URL_ENV = "MANAGED_ANALYTICS_API_URL";
|
|
31
|
+
/** Server-to-server bearer credential for WS1 (scopes the tenant server-side). */
|
|
32
|
+
const SERVICE_SECRET_ENV = "MANAGED_ANALYTICS_API_SECRET";
|
|
33
|
+
|
|
34
|
+
/** The versioned base path, taken from the pinned version policy — never hard-coded twice. */
|
|
35
|
+
const V1_PATH = MANAGED_ANALYTICS_API_V1_VERSION_POLICY.path;
|
|
36
|
+
|
|
37
|
+
/** POST one pinned request to `<base><v1>/<operation>` and return the parsed pinned response. */
|
|
38
|
+
function post<TReq, TRes>(
|
|
39
|
+
fetchImpl: Ws1Fetch,
|
|
40
|
+
baseUrl: string,
|
|
41
|
+
operation: string,
|
|
42
|
+
headers: Record<string, string>,
|
|
43
|
+
): (request: TReq) => Promise<TRes> {
|
|
44
|
+
return async (request: TReq): Promise<TRes> => {
|
|
45
|
+
const res = await fetchImpl(`${baseUrl}${V1_PATH}/${operation}`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: { "content-type": "application/json", ...headers },
|
|
48
|
+
body: JSON.stringify(request),
|
|
49
|
+
});
|
|
50
|
+
// WS1 returns the pinned success OR error envelope as JSON for every status.
|
|
51
|
+
return (await res.json()) as TRes;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Build the tenant-bound public WS1 HTTP client; tests may inject a no-network fetch. */
|
|
56
|
+
export function createWs1ManagedAnalyticsClient(input: {
|
|
57
|
+
readonly baseUrl: string;
|
|
58
|
+
readonly tenantId: string;
|
|
59
|
+
readonly authorization?: string;
|
|
60
|
+
readonly fetchImpl?: Ws1Fetch;
|
|
61
|
+
}): Ws1ManagedAnalyticsMigrationClient {
|
|
62
|
+
const baseUrl = input.baseUrl.replace(/\/$/, "");
|
|
63
|
+
const headers: Record<string, string> = { "x-tot-tenant": input.tenantId };
|
|
64
|
+
if (input.authorization) headers.authorization = input.authorization;
|
|
65
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
66
|
+
return {
|
|
67
|
+
getMigrationDraft: post(fetchImpl, baseUrl, "getMigrationDraft", headers),
|
|
68
|
+
createMigrationDraft: post(fetchImpl, baseUrl, "createMigrationDraft", headers),
|
|
69
|
+
getAdapterDefinition: post(fetchImpl, baseUrl, "getAdapterDefinition", headers),
|
|
70
|
+
getIntegrationInstall: post(fetchImpl, baseUrl, "getIntegrationInstall", headers),
|
|
71
|
+
createIntegrationInstall: post(fetchImpl, baseUrl, "createIntegrationInstall", headers),
|
|
72
|
+
reviseIntegrationInstall: post(fetchImpl, baseUrl, "reviseIntegrationInstall", headers),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Build an HTTP-backed {@link Ws1ManagedAnalyticsClient} for `tenantId`, or
|
|
78
|
+
* `null` when WS1's service URL isn't configured in this runtime. `tenantId`
|
|
79
|
+
* comes from the resolved operator session — never the request body — and is
|
|
80
|
+
* sent as a server-to-server header, keeping every request body tenant-free per
|
|
81
|
+
* the pinned contract.
|
|
82
|
+
*/
|
|
83
|
+
export async function resolveWs1ManagedAnalyticsClient(
|
|
84
|
+
_context: APIContext,
|
|
85
|
+
tenantId: string,
|
|
86
|
+
): Promise<Ws1ManagedAnalyticsClient | null> {
|
|
87
|
+
const baseUrl = (await readEnv(SERVICE_URL_ENV))?.replace(/\/$/, "");
|
|
88
|
+
if (!baseUrl) return null;
|
|
89
|
+
const secret = await readEnv(SERVICE_SECRET_ENV);
|
|
90
|
+
return createWs1ManagedAnalyticsClient({
|
|
91
|
+
baseUrl,
|
|
92
|
+
tenantId,
|
|
93
|
+
...(secret ? { authorization: `Bearer ${secret}` } : {}),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed access guard for tenant-scoped admin APIs (`/api/admin/**`).
|
|
3
|
+
*
|
|
4
|
+
* The admin JSON routes must NOT trust the ambient login gate for their auth:
|
|
5
|
+
* that gate is conditional (`DEMO_GATE_MODE="login"` on a gated host), so a route
|
|
6
|
+
* that leans on it is wide open on any non-gated host. This guard makes each
|
|
7
|
+
* admin API validate the caller EXPLICITLY, bound to the Host-resolved tenant
|
|
8
|
+
* (`locals.tenant.appDomain` — never a client value, never a default), and
|
|
9
|
+
* deny-closed on absence:
|
|
10
|
+
*
|
|
11
|
+
* no session → 401 sign_in_required
|
|
12
|
+
* session, but no standing for THIS tenant → 403 not_authorized
|
|
13
|
+
* member/owner (or staff) of THIS tenant → allowed, entry returned
|
|
14
|
+
*
|
|
15
|
+
* The decision core is PURE (unit-testable); `requireAdminApiEntry` is the thin
|
|
16
|
+
* Astro/I-O wrapper the routes call.
|
|
17
|
+
*/
|
|
18
|
+
import type { APIContext } from "astro";
|
|
19
|
+
import { readViewerSession } from "@/lib/auth/route";
|
|
20
|
+
import { resolveAdminEntry, type AdminEntryDecision } from "@/lib/auth/adminEntry";
|
|
21
|
+
import type { SessionRecord } from "./session.js";
|
|
22
|
+
|
|
23
|
+
export type AdminApiAccess =
|
|
24
|
+
| { ok: true; entry: AdminEntryDecision }
|
|
25
|
+
| { ok: false; status: 401 | 403; reason: "sign_in_required" | "not_authorized" };
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* PURE fail-closed decision for a tenant-scoped admin API caller.
|
|
29
|
+
*
|
|
30
|
+
* @param requireOwner when true, only an owner/admin viewer OR a ToT-staff viewer
|
|
31
|
+
* passes (a plain team member is refused) — for routes that mutate/expose
|
|
32
|
+
* owner-level operational surfaces. Default (false) admits any member/owner/staff
|
|
33
|
+
* of the tenant, matching who reaches the admin shell.
|
|
34
|
+
*/
|
|
35
|
+
export function decideAdminApiAccess(input: {
|
|
36
|
+
record: SessionRecord | null | undefined;
|
|
37
|
+
hostResource: string;
|
|
38
|
+
nowSeconds: number;
|
|
39
|
+
requireOwner?: boolean;
|
|
40
|
+
}): AdminApiAccess {
|
|
41
|
+
const { record, hostResource, nowSeconds, requireOwner } = input;
|
|
42
|
+
if (!record) return { ok: false, status: 401, reason: "sign_in_required" };
|
|
43
|
+
|
|
44
|
+
const entry = resolveAdminEntry({ record, hostResource, nowSeconds });
|
|
45
|
+
const staffScope = Array.isArray(record.staff) && record.staff.length > 0;
|
|
46
|
+
const authorized = requireOwner
|
|
47
|
+
? entry.isOwnerViewer || staffScope
|
|
48
|
+
: entry.admitted;
|
|
49
|
+
if (!authorized) return { ok: false, status: 403, reason: "not_authorized" };
|
|
50
|
+
|
|
51
|
+
return { ok: true, entry };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Astro wrapper: read the viewer session and decide, bound to the routed tenant. */
|
|
55
|
+
export async function requireAdminApiEntry(
|
|
56
|
+
context: APIContext,
|
|
57
|
+
opts?: { requireOwner?: boolean },
|
|
58
|
+
): Promise<AdminApiAccess> {
|
|
59
|
+
const record = await readViewerSession(context);
|
|
60
|
+
return decideAdminApiAccess({
|
|
61
|
+
record,
|
|
62
|
+
hostResource: context.locals.tenant?.appDomain ?? "",
|
|
63
|
+
nowSeconds: Math.floor(Date.now() / 1000),
|
|
64
|
+
requireOwner: opts?.requireOwner,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Admin session-entry resolution
|
|
2
|
+
* Admin session-entry resolution — the SERVER-SIDE decision that puts
|
|
3
3
|
* an authenticated owner/team member into the CORRECT tenant admin context, and
|
|
4
4
|
* keeps a non-member / unauthenticated visitor on the legitimate sign-in gate.
|
|
5
5
|
*
|
|
6
|
-
* NARROW to auth/session ENTRY: it CONSUMES the
|
|
7
|
-
* (`pages/admin.astro` + `components/admin/AdminPublishTab.astro`) and
|
|
6
|
+
* NARROW to auth/session ENTRY: it CONSUMES the admin tab UX
|
|
7
|
+
* (`pages/admin.astro` + `components/admin/AdminPublishTab.astro`) and the
|
|
8
8
|
* ship-capability model (`change/shipCapabilities` + `change/route` decideIsOwner)
|
|
9
9
|
* rather than reimplementing either. Everything it resolves is bound to the
|
|
10
10
|
* RESOLVED tenant, never taken from client input, and never requires a
|
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
* - `hostCapability` — the viewer's owner/admin/member standing FOR THIS host,
|
|
15
15
|
* from {@link sessionHostCapability} (tenants[] membership → resource-scoped
|
|
16
16
|
* session → live staff selection). null ⇒ not a member of this tenant.
|
|
17
|
-
* - `isOwnerViewer` / `shipCapabilities` —
|
|
17
|
+
* - `isOwnerViewer` / `shipCapabilities` — {@link decideIsOwner} +
|
|
18
18
|
* {@link resolveShipCapabilities} over that host-bound capability. This is the
|
|
19
|
-
* OWNER VIEWER identity
|
|
19
|
+
* OWNER VIEWER identity:
|
|
20
20
|
* a signed-in owner/admin viewer over the resolved tenant, NOT the bearer
|
|
21
21
|
* operator console. The ship-axis `capability` is the fail-closed
|
|
22
22
|
* `preview-access` — a viewer session carries no operator ship grant, so the
|
|
23
23
|
* owner is the ONLY source of `shipOnBehalf`/`apexCutover` here.
|
|
24
24
|
*
|
|
25
|
-
* The distinguishing logic
|
|
25
|
+
* The distinguishing logic: `admitted`
|
|
26
26
|
* separates an authenticated OWNER/member (a host-bound capability, or ToT-staff
|
|
27
27
|
* scope) — who must NEVER see the "Coming soon" holding page at their own admin —
|
|
28
28
|
* from an authenticated NON-member and an anonymous visitor, who legitimately do
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
*/
|
|
31
31
|
import type { SessionRecord } from "./session.js";
|
|
32
32
|
import { sessionHostCapability } from "./loginGate.js";
|
|
33
|
-
import { decideIsOwner } from "@/lib/change/route";
|
|
33
|
+
import { decideIsOwner } from "@/lib/change/route/route";
|
|
34
34
|
import {
|
|
35
35
|
resolveShipCapabilities,
|
|
36
36
|
type ShipCapabilities,
|
|
37
|
-
} from "@/lib/change/
|
|
37
|
+
} from "@/lib/change/route/ship-capabilities";
|
|
38
38
|
import type { Capability } from "@/lib/change/types";
|
|
39
39
|
import { OWNER_CAPABILITIES } from "@/lib/grants/session";
|
|
40
40
|
|
|
@@ -56,9 +56,9 @@ export interface AdminEntryDecision {
|
|
|
56
56
|
admitted: boolean;
|
|
57
57
|
/** The owner/admin/member standing bound to this host, or null. */
|
|
58
58
|
hostCapability: string | null;
|
|
59
|
-
/**
|
|
59
|
+
/** The acting viewer IS the tenant owner/admin (superset of ship-on-behalf). */
|
|
60
60
|
isOwnerViewer: boolean;
|
|
61
|
-
/**
|
|
61
|
+
/** The two derived ship-gate capabilities for this viewer entry. */
|
|
62
62
|
shipCapabilities: ShipCapabilities;
|
|
63
63
|
/** Which of the four entry cases this is. */
|
|
64
64
|
principal: AdminEntryPrincipal;
|
|
@@ -90,7 +90,7 @@ export function resolveAdminEntry(input: {
|
|
|
90
90
|
const staffScope = Array.isArray(record?.staff) && record!.staff.length > 0;
|
|
91
91
|
const admitted = hostCapability !== null || staffScope;
|
|
92
92
|
|
|
93
|
-
//
|
|
93
|
+
// Owner resolution — the OWNER VIEWER, not the bearer console: authorized
|
|
94
94
|
// (a member/staff viewer of THIS tenant) AND holding an owner/admin capability.
|
|
95
95
|
const isOwnerViewer = decideIsOwner({
|
|
96
96
|
authorized: admitted,
|