@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Operator-session resolution for the Private Apps admin API
|
|
3
|
-
* (`/api/apps/admin
|
|
3
|
+
* (`/api/apps/admin/*`). Mirrors
|
|
4
4
|
* `../grants/session.ts`'s owner-session resolution — same two paths, same
|
|
5
5
|
* fail-closed shape — but resolves `{ tenantId, actor }` instead of `{ owner }`:
|
|
6
6
|
*
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* DEDICATED `APPS_ADMIN_SECRET` only — unlike grants' session, this does
|
|
28
28
|
* NOT fall back to the shared `PREVIEW_RECONCILE_SECRET`, since that
|
|
29
29
|
* secret must not double as a cross-tenant app-registry master key
|
|
30
|
-
*
|
|
30
|
+
* Unconfigured `APPS_ADMIN_SECRET`
|
|
31
31
|
* means this path is unavailable — fails closed rather than trusting
|
|
32
32
|
* another deployment's secret. The tenant is resolved server-side from
|
|
33
33
|
* `APPS_ADMIN_TENANT` env or the `X-Tot-Tenant` header (honoured only
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* Routes depend only on the `{ ok, tenantId, actor }` result. `authorizeAdmin`
|
|
39
39
|
* is the pure core of path 2 (unit-testable, no I/O).
|
|
40
40
|
*
|
|
41
|
-
*
|
|
41
|
+
* Console admin session. The two paths above are host-bound:
|
|
42
42
|
* they resolve exactly one tenant (`context.locals.tenant`). The
|
|
43
43
|
* `/api/apps/admin/*` surface is also reachable from the multi-tenant vendor
|
|
44
44
|
* console, where a signed-in viewer may hold memberships on several vendors —
|
|
@@ -61,6 +61,11 @@ import { sha256Hex } from "@tot/public-runtime";
|
|
|
61
61
|
import { readEnv } from "@/lib/env";
|
|
62
62
|
import { readViewerSession } from "@/lib/auth/route.js";
|
|
63
63
|
import { viewerCanAccess, membershipFor } from "@/lib/dashboard/tenantSelection.js";
|
|
64
|
+
import {
|
|
65
|
+
authorizeBearerOperator,
|
|
66
|
+
resolveHostBoundSession,
|
|
67
|
+
type SessionFailure,
|
|
68
|
+
} from "@/lib/auth/operatorSession.js";
|
|
64
69
|
|
|
65
70
|
/** Capabilities that make a signed-in viewer an app-registry operator for their tenant. */
|
|
66
71
|
const ADMIN_CAPABILITIES: ReadonlySet<string> = new Set(["owner", "admin"]);
|
|
@@ -88,33 +93,30 @@ export interface AdminAuthInput {
|
|
|
88
93
|
|
|
89
94
|
export type AdminAuthResult =
|
|
90
95
|
| { ok: true; tenantId: string; actor: string; role?: string }
|
|
91
|
-
|
|
|
96
|
+
| SessionFailure;
|
|
97
|
+
|
|
98
|
+
/** The one authorized-success shape both host-bound and Bearer admin paths return. */
|
|
99
|
+
type AdminAuthOk = Extract<AdminAuthResult, { ok: true }>;
|
|
92
100
|
|
|
93
101
|
/**
|
|
94
102
|
* Decide the authenticated tenant from env + request, with NO I/O —
|
|
95
103
|
* unit-testable. Fail-closed: unconfigured gate → 503, bad/absent Bearer →
|
|
96
|
-
* 401, no resolvable tenant → 400.
|
|
104
|
+
* 401, no resolvable tenant → 400. A thin adapter over the shared
|
|
105
|
+
* Bearer-operator core that maps its resolved `principal` to this surface's
|
|
106
|
+
* `tenantId` and stamps the fixed `BEARER_ACTOR` (no per-caller identity here).
|
|
97
107
|
*/
|
|
98
108
|
export function authorizeAdmin(input: AdminAuthInput): AdminAuthResult {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (!tenantId) {
|
|
111
|
-
return {
|
|
112
|
-
ok: false,
|
|
113
|
-
status: 400,
|
|
114
|
-
message: "tenant unresolved (set APPS_ADMIN_TENANT or X-Tot-Tenant)",
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
return { ok: true, tenantId, actor: BEARER_ACTOR };
|
|
109
|
+
const res = authorizeBearerOperator({
|
|
110
|
+
authHeader: input.authHeader,
|
|
111
|
+
gateSecret: input.gateSecret,
|
|
112
|
+
configuredPrincipal: input.configuredTenant,
|
|
113
|
+
requestedPrincipal: input.requestedTenant,
|
|
114
|
+
unconfiguredMessage: "apps admin operator secret not configured",
|
|
115
|
+
unresolvedMessage: "tenant unresolved (set APPS_ADMIN_TENANT or X-Tot-Tenant)",
|
|
116
|
+
});
|
|
117
|
+
return res.ok
|
|
118
|
+
? { ok: true, tenantId: res.principal, actor: BEARER_ACTOR }
|
|
119
|
+
: res;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
/** One-way, stable pseudonym for an audit `actor` — never the email itself. */
|
|
@@ -128,30 +130,37 @@ async function actorFromEmail(email: string): Promise<string> {
|
|
|
128
130
|
* from the body.
|
|
129
131
|
*/
|
|
130
132
|
export async function resolveAdminSession(context: APIContext): Promise<AdminAuthResult> {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
return resolveHostBoundSession<AdminAuthOk>(context, {
|
|
134
|
+
capabilities: ADMIN_CAPABILITIES,
|
|
135
|
+
// A resolved-but-insufficient viewer fails closed with 403 and does NOT
|
|
136
|
+
// fall through to the Bearer gate — an authenticated-but-under-privileged
|
|
137
|
+
// caller must not be silently admitted by a secret it also presents.
|
|
138
|
+
onInsufficientCapability: {
|
|
139
|
+
mode: "failClosed",
|
|
140
|
+
failure: {
|
|
138
141
|
ok: false,
|
|
139
142
|
status: 403,
|
|
140
143
|
message: "not authorized to manage apps for this tenant",
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
// Path 1: the tenant IS the resolved host's appDomain — never the body.
|
|
147
|
+
resolveViewer: async (viewer, tenantId) => ({
|
|
148
|
+
ok: true,
|
|
149
|
+
tenantId,
|
|
150
|
+
actor: await actorFromEmail(viewer.email),
|
|
151
|
+
}),
|
|
152
|
+
// Path 2 (fallback): the headless-console Bearer operator secret. Dedicated
|
|
153
|
+
// secret only — no fallback to another deployment's secret.
|
|
154
|
+
resolveBearer: async (ctx) => {
|
|
155
|
+
const gateSecret = await readEnv("APPS_ADMIN_SECRET");
|
|
156
|
+
const configuredTenant = await readEnv("APPS_ADMIN_TENANT");
|
|
157
|
+
return authorizeAdmin({
|
|
158
|
+
authHeader: ctx.request.headers.get("authorization"),
|
|
159
|
+
gateSecret,
|
|
160
|
+
configuredTenant,
|
|
161
|
+
requestedTenant: ctx.request.headers.get("x-tot-tenant"),
|
|
162
|
+
});
|
|
163
|
+
},
|
|
155
164
|
});
|
|
156
165
|
}
|
|
157
166
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The scoped-API authorization boundary — every
|
|
3
3
|
* `/api/apps/v1/*` route calls `authorizeAppRequest` FIRST. Verifies the
|
|
4
|
-
* app-gateway bearer JWT (
|
|
4
|
+
* app-gateway bearer JWT (`AppTokenVerifier`), resolves the calling
|
|
5
5
|
* install from the registry, and enforces the single scope the route
|
|
6
6
|
* requires per `docs/private-apps/contract/openapi.yaml`'s `x-requiredScope`.
|
|
7
7
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Response helpers shared by every `/api/apps/v1/*` route
|
|
3
|
-
*
|
|
2
|
+
* Response helpers shared by every `/api/apps/v1/*` route — a no-store JSON
|
|
3
|
+
* responder (mirrors `../grants/route.ts` /
|
|
4
4
|
* `./adminService.ts`'s pattern) plus the `tot-request-id` / `traceparent`
|
|
5
5
|
* response headers `docs/private-apps/contract/openapi.yaml` documents on
|
|
6
6
|
* every operation.
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The managed-app version-retention + atomic-rollback spine (ext-apps-02) — the
|
|
3
|
+
* MANAGED-APP analog of the tenant integration lifecycle's known-good/rollback
|
|
4
|
+
* (`integrationLifecycle.ts`). Where that module keeps a tenant's own resolved
|
|
5
|
+
* integration revisions and repoints `live` to a known-good one, this persists
|
|
6
|
+
* every activated managed-app VERSION's COMPLETE restorable facet set and rolls an
|
|
7
|
+
* installed app back to a prior version's whole facet set in one atomic step.
|
|
8
|
+
*
|
|
9
|
+
* Two acceptance properties are schema-level, not conventions:
|
|
10
|
+
* - RETAINED — every activation is a new immutable `app_version_snapshots` row
|
|
11
|
+
* carrying the app's FULL restorable facet set (code ref/digest + entry module,
|
|
12
|
+
* public config, contributions, granted scopes, consent declaration, CSP
|
|
13
|
+
* origins) as one JSON blob. Nothing is mutated in place; the row list IS the
|
|
14
|
+
* retention history and audit trail (actor + time per activation).
|
|
15
|
+
* - ATOMIC ROLLBACK — a rollback restores a retained version's COMPLETE facet set
|
|
16
|
+
* in a SINGLE-ROW insert that copies that version's `facets` blob verbatim. You
|
|
17
|
+
* get the whole prior facet set or nothing — never a partial/mixed activation,
|
|
18
|
+
* never a silently-defaulted facet. It is forward-only (records `restoredFromSeq`
|
|
19
|
+
* and appends a new activation), so history is never rewritten and the rollback
|
|
20
|
+
* is itself an audited row.
|
|
21
|
+
*
|
|
22
|
+
* Fail-closed, mirroring the tenant lifecycle's discipline: a retained/restored
|
|
23
|
+
* facet set is scanned for secret material ({@link findForbiddenFields}) BEFORE any
|
|
24
|
+
* write, and a rollback to a version that was never retained is REFUSED with the
|
|
25
|
+
* store left completely untouched (no new row, current version unchanged).
|
|
26
|
+
*
|
|
27
|
+
* Pure over the `Queryable` seam (same shape `ConsentConfigStore`/`AppRegistryService`
|
|
28
|
+
* use), so it runs unchanged against real D1 in the Worker
|
|
29
|
+
* (`fromD1(env.STOREFRONT_APPS_DB)`) and an in-memory `node:sqlite` in tests. The
|
|
30
|
+
* current active version is `MAX(seq)` per install — the same "the latest row is
|
|
31
|
+
* current" model as `consent_config_revisions`.
|
|
32
|
+
*/
|
|
33
|
+
import type {
|
|
34
|
+
ExecutionTier,
|
|
35
|
+
PlanIntegrationEntry,
|
|
36
|
+
PlanIntegrationConsent,
|
|
37
|
+
NormalizedContribution,
|
|
38
|
+
} from "@tot/public-runtime";
|
|
39
|
+
import { findForbiddenFields } from "@tot/public-runtime";
|
|
40
|
+
import type { Queryable, QueryStatement } from "../d1/catalog.js";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* One managed-app version's COMPLETE restorable facet set — everything a rollback
|
|
44
|
+
* must put back to reconstitute the prior version with no missing or defaulted
|
|
45
|
+
* facet. All values are public (no secret material); the store proves it with a
|
|
46
|
+
* secret scan before persisting.
|
|
47
|
+
*/
|
|
48
|
+
export interface AppVersionFacets {
|
|
49
|
+
/** The app package version this activation serves. */
|
|
50
|
+
appVersion: string;
|
|
51
|
+
/** The code identity — content digest of the pinned package. */
|
|
52
|
+
digest: string;
|
|
53
|
+
/** Where this version's code runs (elevated vs boxed). */
|
|
54
|
+
executionTier: ExecutionTier;
|
|
55
|
+
/** The exact-version entry module the lifecycle runtime imports. */
|
|
56
|
+
entry: PlanIntegrationEntry;
|
|
57
|
+
/** Resolved per-version public config; carries no secret material. */
|
|
58
|
+
publicConfig: Record<string, unknown>;
|
|
59
|
+
/** The normalized contributions this version registers. */
|
|
60
|
+
contributions: NormalizedContribution[];
|
|
61
|
+
/** The scopes granted to this version. */
|
|
62
|
+
scopes: string[];
|
|
63
|
+
/** The consent declaration the entry loader gates on. */
|
|
64
|
+
consent: PlanIntegrationConsent;
|
|
65
|
+
/** Sorted, deduped exact CSP origins this version needs. */
|
|
66
|
+
cspOrigins: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** One retained activation — an immutable version-retention/audit-trail entry. */
|
|
70
|
+
export interface AppVersionSnapshot {
|
|
71
|
+
id: string;
|
|
72
|
+
installId: string;
|
|
73
|
+
tenantId: string;
|
|
74
|
+
/** Monotonic per install; the largest is the currently active version. */
|
|
75
|
+
seq: number;
|
|
76
|
+
/** Mirror of `facets.appVersion` for cheap listing/filtering. */
|
|
77
|
+
appVersion: string;
|
|
78
|
+
/** The complete restorable facet set. */
|
|
79
|
+
facets: AppVersionFacets;
|
|
80
|
+
/** Opaque actor id — never PII. */
|
|
81
|
+
actor: string;
|
|
82
|
+
note: string | null;
|
|
83
|
+
/** The source `seq` when this activation is a rollback, else null. */
|
|
84
|
+
restoredFromSeq: number | null;
|
|
85
|
+
createdAt: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Retain (activate) a new managed-app version, persisting its full facet set. */
|
|
89
|
+
export interface RetainVersionInput {
|
|
90
|
+
installId: string;
|
|
91
|
+
tenantId: string;
|
|
92
|
+
facets: AppVersionFacets;
|
|
93
|
+
actor: string;
|
|
94
|
+
note?: string | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Roll an install back to a retained version, restoring its complete facet set. */
|
|
98
|
+
export interface RollbackVersionInput {
|
|
99
|
+
installId: string;
|
|
100
|
+
/** The retained activation's `seq` — its stable version handle — to restore. */
|
|
101
|
+
toSeq: number;
|
|
102
|
+
actor: string;
|
|
103
|
+
note?: string | null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface PlannedVersionWrite {
|
|
107
|
+
snapshot: AppVersionSnapshot;
|
|
108
|
+
statement: QueryStatement;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type AppVersionErrorCode = "unknown_target" | "integrity";
|
|
112
|
+
|
|
113
|
+
/** Thrown by every fail-closed refusal here — callers branch on `.code`. */
|
|
114
|
+
export class AppVersionError extends Error {
|
|
115
|
+
constructor(
|
|
116
|
+
message: string,
|
|
117
|
+
readonly code: AppVersionErrorCode,
|
|
118
|
+
readonly detail?: string[],
|
|
119
|
+
) {
|
|
120
|
+
super(message);
|
|
121
|
+
this.name = "AppVersionError";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface SnapshotRow {
|
|
126
|
+
id: string;
|
|
127
|
+
install_id: string;
|
|
128
|
+
tenant_id: string;
|
|
129
|
+
seq: number;
|
|
130
|
+
app_version: string;
|
|
131
|
+
facets: string;
|
|
132
|
+
actor: string;
|
|
133
|
+
note: string | null;
|
|
134
|
+
restored_from_seq: number | null;
|
|
135
|
+
created_at: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function toSnapshot(row: SnapshotRow): AppVersionSnapshot {
|
|
139
|
+
return {
|
|
140
|
+
id: row.id,
|
|
141
|
+
installId: row.install_id,
|
|
142
|
+
tenantId: row.tenant_id,
|
|
143
|
+
seq: row.seq,
|
|
144
|
+
appVersion: row.app_version,
|
|
145
|
+
facets: JSON.parse(row.facets) as AppVersionFacets,
|
|
146
|
+
actor: row.actor,
|
|
147
|
+
note: row.note,
|
|
148
|
+
restoredFromSeq: row.restored_from_seq,
|
|
149
|
+
createdAt: row.created_at,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export class AppVersionStore {
|
|
154
|
+
constructor(
|
|
155
|
+
private readonly db: Queryable,
|
|
156
|
+
private readonly now: () => string = () => new Date().toISOString(),
|
|
157
|
+
private readonly newId: () => string = () => crypto.randomUUID(),
|
|
158
|
+
) {}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Retain (activate) a managed-app version by appending a new immutable snapshot
|
|
162
|
+
* carrying its COMPLETE restorable facet set. The `seq` is assigned as
|
|
163
|
+
* `MAX(seq)+1` for that install; the UNIQUE `(install_id, seq)` index makes a
|
|
164
|
+
* concurrent double-assignment fail loudly rather than overwrite an earlier
|
|
165
|
+
* activation. Fail-closed: a facet set naming secret material is REFUSED before
|
|
166
|
+
* any write (defense in depth over the plan/evidence contracts that produced it).
|
|
167
|
+
*/
|
|
168
|
+
async retainVersion(input: RetainVersionInput): Promise<AppVersionSnapshot> {
|
|
169
|
+
const planned = await this.planRetainVersion(input);
|
|
170
|
+
await this.db.run(planned.statement.sql, ...(planned.statement.params ?? []));
|
|
171
|
+
return planned.snapshot;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Validate and plan retention without writing, for a caller-owned D1 batch. */
|
|
175
|
+
async planRetainVersion(input: RetainVersionInput): Promise<PlannedVersionWrite> {
|
|
176
|
+
return this.planAppend(input.installId, input.tenantId, input.facets, input.actor, input.note ?? null, null);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Roll an install back to a retained version, restoring its COMPLETE facet set in
|
|
181
|
+
* one atomic single-row insert that copies the target activation's `facets` blob
|
|
182
|
+
* verbatim (forward-only: records `restoredFromSeq`). No partial/mixed state is
|
|
183
|
+
* reachable — the whole prior facet set is restored or nothing is. Fail-closed: a
|
|
184
|
+
* `toSeq` that was never retained is REFUSED (`unknown_target`) and the store is
|
|
185
|
+
* left completely untouched — no new row, the current version unchanged.
|
|
186
|
+
*/
|
|
187
|
+
async rollbackTo(input: RollbackVersionInput): Promise<AppVersionSnapshot> {
|
|
188
|
+
const planned = await this.planRollbackTo(input);
|
|
189
|
+
await this.db.run(planned.statement.sql, ...(planned.statement.params ?? []));
|
|
190
|
+
return planned.snapshot;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Plan a forward rollback without advancing MAX(seq) until the batch commits. */
|
|
194
|
+
async planRollbackTo(input: RollbackVersionInput): Promise<PlannedVersionWrite> {
|
|
195
|
+
const target = await this.getSnapshot(input.installId, input.toSeq);
|
|
196
|
+
if (!target) {
|
|
197
|
+
throw new AppVersionError(
|
|
198
|
+
`Cannot roll back install "${input.installId}" to version seq ${input.toSeq}: no such retained version.`,
|
|
199
|
+
"unknown_target",
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const note = input.note ?? `Rollback to version ${target.appVersion} (seq ${input.toSeq}).`;
|
|
203
|
+
return this.planAppend(
|
|
204
|
+
input.installId,
|
|
205
|
+
target.tenantId,
|
|
206
|
+
target.facets,
|
|
207
|
+
input.actor,
|
|
208
|
+
note,
|
|
209
|
+
input.toSeq,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** The current (active) version of an install — `MAX(seq)` — or null if none retained. */
|
|
214
|
+
async currentVersion(installId: string): Promise<AppVersionSnapshot | null> {
|
|
215
|
+
const row = await this.db.first<SnapshotRow>(
|
|
216
|
+
`SELECT * FROM app_version_snapshots WHERE install_id = ? ORDER BY seq DESC LIMIT 1`,
|
|
217
|
+
installId,
|
|
218
|
+
);
|
|
219
|
+
return row ? toSnapshot(row) : null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** A specific retained activation by its `seq`, or null if absent. */
|
|
223
|
+
async getSnapshot(installId: string, seq: number): Promise<AppVersionSnapshot | null> {
|
|
224
|
+
const row = await this.db.first<SnapshotRow>(
|
|
225
|
+
`SELECT * FROM app_version_snapshots WHERE install_id = ? AND seq = ?`,
|
|
226
|
+
installId,
|
|
227
|
+
seq,
|
|
228
|
+
);
|
|
229
|
+
return row ? toSnapshot(row) : null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** An install's full retention history, newest first — the version audit trail. */
|
|
233
|
+
async listVersions(installId: string): Promise<AppVersionSnapshot[]> {
|
|
234
|
+
const rows = await this.db.all<SnapshotRow>(
|
|
235
|
+
`SELECT * FROM app_version_snapshots WHERE install_id = ? ORDER BY seq DESC`,
|
|
236
|
+
installId,
|
|
237
|
+
);
|
|
238
|
+
return rows.map(toSnapshot);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Append a new activation. Shared by retain (source `null`) and rollback (source
|
|
243
|
+
* `restoredFromSeq`). Scans the facet set for secret material BEFORE the insert,
|
|
244
|
+
* then writes exactly one row — the atomicity boundary: the whole facet set lands
|
|
245
|
+
* or nothing does.
|
|
246
|
+
*/
|
|
247
|
+
private async planAppend(
|
|
248
|
+
installId: string,
|
|
249
|
+
tenantId: string,
|
|
250
|
+
facets: AppVersionFacets,
|
|
251
|
+
actor: string,
|
|
252
|
+
note: string | null,
|
|
253
|
+
restoredFromSeq: number | null,
|
|
254
|
+
): Promise<PlannedVersionWrite> {
|
|
255
|
+
const forbidden = findForbiddenFields(facets, "facets");
|
|
256
|
+
if (forbidden.length > 0) {
|
|
257
|
+
throw new AppVersionError(
|
|
258
|
+
`Refusing to persist version facets naming secret material: ${forbidden.map((v) => v.path).join(", ")}.`,
|
|
259
|
+
"integrity",
|
|
260
|
+
forbidden.map((v) => v.path),
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const maxRow = await this.db.first<{ max_seq: number | null }>(
|
|
264
|
+
`SELECT MAX(seq) AS max_seq FROM app_version_snapshots WHERE install_id = ?`,
|
|
265
|
+
installId,
|
|
266
|
+
);
|
|
267
|
+
const seq = (maxRow?.max_seq ?? 0) + 1;
|
|
268
|
+
const createdAt = this.now();
|
|
269
|
+
const id = `avs_${this.newId()}`;
|
|
270
|
+
const statement: QueryStatement = {
|
|
271
|
+
sql: `INSERT INTO app_version_snapshots
|
|
272
|
+
(id, install_id, tenant_id, seq, app_version, facets, actor, note, restored_from_seq, created_at)
|
|
273
|
+
VALUES (?,?,?,?,?,?,?,?,?,?)`,
|
|
274
|
+
params: [id, installId, tenantId, seq, facets.appVersion, JSON.stringify(facets), actor, note, restoredFromSeq, createdAt],
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
statement,
|
|
278
|
+
snapshot: { id, installId, tenantId, seq, appVersion: facets.appVersion, facets, actor, note, restoredFromSeq, createdAt },
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Maps the internal `CatalogProduct` read model to the
|
|
2
|
+
* Maps the internal `CatalogProduct` read model to the scoped-API contract's
|
|
3
3
|
* `Product` shape (`docs/private-apps/contract/openapi.yaml`, schema
|
|
4
4
|
* `Product` — `additionalProperties: false`). Drops every internal field
|
|
5
5
|
* the contract doesn't declare (variants, images, metafields, source, …) so
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The merchant consent configuration store — the durable spine behind ext-admin-03.
|
|
3
|
+
* It persists a tenant's consent configuration as an APPEND-ONLY revision history and
|
|
4
|
+
* the demonstrated-CMP-demand ledger, and reads back exactly the shapes the pure
|
|
5
|
+
* ext-policy-03 layer consumes.
|
|
6
|
+
*
|
|
7
|
+
* Two acceptance properties are schema-level, not conventions:
|
|
8
|
+
* - AUDITED — every save is a new immutable revision that names its actor and time;
|
|
9
|
+
* nothing is mutated in place, so the revision list IS the audit trail.
|
|
10
|
+
* - REVERSIBLE — a revert is a forward-only new revision that copies a prior config
|
|
11
|
+
* and records `revertedFrom`; history is never rewritten, and rolling back leaves
|
|
12
|
+
* its own audited entry.
|
|
13
|
+
*
|
|
14
|
+
* Pure over the `Queryable` seam (same shape `CandidateStore`/`OrdersStore` use), so it
|
|
15
|
+
* runs unchanged against real D1 in the Worker (`fromD1(env.STOREFRONT_APPS_DB)`) and an
|
|
16
|
+
* in-memory `node:sqlite` in tests. This is the DATA MODEL + repository API only — the
|
|
17
|
+
* validation (`validateConsentConfig`) and the broker materialization
|
|
18
|
+
* (`toTenantConsentPolicy`/`buildTenantConsentBroker`) live in `@tot/public-runtime`;
|
|
19
|
+
* this store persists already-validated JSON and never re-derives policy.
|
|
20
|
+
*/
|
|
21
|
+
import type { Queryable } from "../d1/catalog.js";
|
|
22
|
+
import type { CmpDemandLedger, TenantConsentConfig } from "@tot/public-runtime";
|
|
23
|
+
|
|
24
|
+
/** One persisted consent-config revision — an immutable audit-trail entry. */
|
|
25
|
+
export interface ConsentConfigRevision {
|
|
26
|
+
id: string;
|
|
27
|
+
tenantId: string;
|
|
28
|
+
/** Monotonic per tenant; the largest is the current config. */
|
|
29
|
+
revision: number;
|
|
30
|
+
config: TenantConsentConfig;
|
|
31
|
+
/** Opaque actor id — never PII. */
|
|
32
|
+
actor: string;
|
|
33
|
+
note: string | null;
|
|
34
|
+
/** The source revision when this one is a revert, else null. */
|
|
35
|
+
revertedFrom: number | null;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Save a new revision of a tenant's (already-validated) consent configuration. */
|
|
40
|
+
export interface SaveConsentConfigInput {
|
|
41
|
+
tenantId: string;
|
|
42
|
+
config: TenantConsentConfig;
|
|
43
|
+
actor: string;
|
|
44
|
+
note?: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Revert a tenant's consent configuration to a prior revision (forward-only). */
|
|
48
|
+
export interface RevertConsentConfigInput {
|
|
49
|
+
tenantId: string;
|
|
50
|
+
/** The revision whose config to restore as a NEW revision. */
|
|
51
|
+
toRevision: number;
|
|
52
|
+
actor: string;
|
|
53
|
+
note?: string | null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Record demonstrated demand for a CMP adapter from one tenant. */
|
|
57
|
+
export interface RecordCmpDemandInput {
|
|
58
|
+
tenantId: string;
|
|
59
|
+
adapterId: string;
|
|
60
|
+
source?: string | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface RevisionRow {
|
|
64
|
+
id: string;
|
|
65
|
+
tenant_id: string;
|
|
66
|
+
revision: number;
|
|
67
|
+
config: string;
|
|
68
|
+
actor: string;
|
|
69
|
+
note: string | null;
|
|
70
|
+
reverted_from: number | null;
|
|
71
|
+
created_at: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface DemandRow {
|
|
75
|
+
id: string;
|
|
76
|
+
tenant_id: string;
|
|
77
|
+
adapter_id: string;
|
|
78
|
+
source: string | null;
|
|
79
|
+
at: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function toRevision(row: RevisionRow): ConsentConfigRevision {
|
|
83
|
+
return {
|
|
84
|
+
id: row.id,
|
|
85
|
+
tenantId: row.tenant_id,
|
|
86
|
+
revision: row.revision,
|
|
87
|
+
config: JSON.parse(row.config) as TenantConsentConfig,
|
|
88
|
+
actor: row.actor,
|
|
89
|
+
note: row.note,
|
|
90
|
+
revertedFrom: row.reverted_from,
|
|
91
|
+
createdAt: row.created_at,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class ConsentConfigStore {
|
|
96
|
+
constructor(
|
|
97
|
+
private readonly db: Queryable,
|
|
98
|
+
private readonly now: () => string = () => new Date().toISOString(),
|
|
99
|
+
private readonly newId: () => string = () => crypto.randomUUID(),
|
|
100
|
+
) {}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Append a new revision of a tenant's consent config. The revision number is
|
|
104
|
+
* assigned as `MAX(revision)+1` for that tenant; the UNIQUE `(tenant_id, revision)`
|
|
105
|
+
* index makes a concurrent double-assignment fail loudly rather than overwrite an
|
|
106
|
+
* earlier entry. The prior revisions are never touched — the history is the audit.
|
|
107
|
+
*/
|
|
108
|
+
async saveRevision(input: SaveConsentConfigInput): Promise<ConsentConfigRevision> {
|
|
109
|
+
return this.appendRevision(input.tenantId, input.config, input.actor, input.note ?? null, null);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Restore a prior revision as a NEW revision (forward-only revert). Reads the target
|
|
114
|
+
* revision's config and appends it again with `revertedFrom` set — so a rollback is
|
|
115
|
+
* itself an audited entry and no history is rewritten. Throws if the target is absent.
|
|
116
|
+
*/
|
|
117
|
+
async revertTo(input: RevertConsentConfigInput): Promise<ConsentConfigRevision> {
|
|
118
|
+
const target = await this.getRevision(input.tenantId, input.toRevision);
|
|
119
|
+
if (!target) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Cannot revert tenant "${input.tenantId}" to revision ${input.toRevision}: no such revision.`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const note = input.note ?? `Revert to revision ${input.toRevision}.`;
|
|
125
|
+
return this.appendRevision(input.tenantId, target.config, input.actor, note, input.toRevision);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private async appendRevision(
|
|
129
|
+
tenantId: string,
|
|
130
|
+
config: TenantConsentConfig,
|
|
131
|
+
actor: string,
|
|
132
|
+
note: string | null,
|
|
133
|
+
revertedFrom: number | null,
|
|
134
|
+
): Promise<ConsentConfigRevision> {
|
|
135
|
+
const maxRow = await this.db.first<{ max_revision: number | null }>(
|
|
136
|
+
`SELECT MAX(revision) AS max_revision FROM consent_config_revisions WHERE tenant_id = ?`,
|
|
137
|
+
tenantId,
|
|
138
|
+
);
|
|
139
|
+
const revision = (maxRow?.max_revision ?? 0) + 1;
|
|
140
|
+
const createdAt = this.now();
|
|
141
|
+
await this.db.run(
|
|
142
|
+
`INSERT INTO consent_config_revisions
|
|
143
|
+
(id, tenant_id, revision, config, actor, note, reverted_from, created_at)
|
|
144
|
+
VALUES (?,?,?,?,?,?,?,?)`,
|
|
145
|
+
`ccr_${this.newId()}`,
|
|
146
|
+
tenantId,
|
|
147
|
+
revision,
|
|
148
|
+
JSON.stringify(config),
|
|
149
|
+
actor,
|
|
150
|
+
note,
|
|
151
|
+
revertedFrom,
|
|
152
|
+
createdAt,
|
|
153
|
+
);
|
|
154
|
+
const row = await this.db.first<RevisionRow>(
|
|
155
|
+
`SELECT * FROM consent_config_revisions WHERE tenant_id = ? AND revision = ?`,
|
|
156
|
+
tenantId,
|
|
157
|
+
revision,
|
|
158
|
+
);
|
|
159
|
+
return toRevision(row!);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** The current (latest) revision of a tenant's consent config, or null if none saved. */
|
|
163
|
+
async currentRevision(tenantId: string): Promise<ConsentConfigRevision | null> {
|
|
164
|
+
const row = await this.db.first<RevisionRow>(
|
|
165
|
+
`SELECT * FROM consent_config_revisions WHERE tenant_id = ?
|
|
166
|
+
ORDER BY revision DESC LIMIT 1`,
|
|
167
|
+
tenantId,
|
|
168
|
+
);
|
|
169
|
+
return row ? toRevision(row) : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** A specific revision, or null if absent. */
|
|
173
|
+
async getRevision(tenantId: string, revision: number): Promise<ConsentConfigRevision | null> {
|
|
174
|
+
const row = await this.db.first<RevisionRow>(
|
|
175
|
+
`SELECT * FROM consent_config_revisions WHERE tenant_id = ? AND revision = ?`,
|
|
176
|
+
tenantId,
|
|
177
|
+
revision,
|
|
178
|
+
);
|
|
179
|
+
return row ? toRevision(row) : null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** A tenant's full revision history, newest first — the audit trail. */
|
|
183
|
+
async listRevisions(tenantId: string): Promise<ConsentConfigRevision[]> {
|
|
184
|
+
const rows = await this.db.all<RevisionRow>(
|
|
185
|
+
`SELECT * FROM consent_config_revisions WHERE tenant_id = ? ORDER BY revision DESC`,
|
|
186
|
+
tenantId,
|
|
187
|
+
);
|
|
188
|
+
return rows.map(toRevision);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Record demonstrated demand for a CMP adapter from one tenant. Idempotent per
|
|
193
|
+
* `(tenant, adapter)` — a tenant re-asking updates the recorded time/source on the
|
|
194
|
+
* SAME row and never inflates the distinct-tenant demand count.
|
|
195
|
+
*/
|
|
196
|
+
async recordCmpDemand(input: RecordCmpDemandInput): Promise<void> {
|
|
197
|
+
await this.db.run(
|
|
198
|
+
`INSERT INTO consent_cmp_demand (id, tenant_id, adapter_id, source, at)
|
|
199
|
+
VALUES (?,?,?,?,?)
|
|
200
|
+
ON CONFLICT(tenant_id, adapter_id) DO UPDATE SET
|
|
201
|
+
source = excluded.source,
|
|
202
|
+
at = excluded.at`,
|
|
203
|
+
`cmd_${this.newId()}`,
|
|
204
|
+
input.tenantId,
|
|
205
|
+
input.adapterId,
|
|
206
|
+
input.source ?? null,
|
|
207
|
+
this.now(),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* The whole demand ledger, in the `@tot/public-runtime` shape — passed straight into
|
|
213
|
+
* `validateConsentConfig` so a CMP choice is admitted only from demonstrated demand.
|
|
214
|
+
*/
|
|
215
|
+
async demandLedger(): Promise<CmpDemandLedger> {
|
|
216
|
+
const rows = await this.db.all<DemandRow>(
|
|
217
|
+
`SELECT * FROM consent_cmp_demand ORDER BY adapter_id ASC, tenant_id ASC`,
|
|
218
|
+
);
|
|
219
|
+
return {
|
|
220
|
+
signals: rows.map((r) => ({
|
|
221
|
+
adapterId: r.adapter_id,
|
|
222
|
+
tenantPublicId: r.tenant_id,
|
|
223
|
+
at: r.at,
|
|
224
|
+
source: r.source ?? undefined,
|
|
225
|
+
})),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|