@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/astro.config.mjs +3 -3
- package/apps/storefront/dev-plugins/tenant-hot-reload.mjs +8 -3
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +12 -14
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +18 -10
- package/apps/storefront/src/layouts/Layout.astro +100 -20
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
- package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
- package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/apps/adminService.ts +10 -0
- package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +264 -10
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/blog/collection.ts +114 -17
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +76 -9
- package/apps/storefront/src/lib/blog/types.ts +9 -9
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +12 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +18 -6
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
- package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
- package/apps/storefront/src/lib/newsletter/send.ts +3 -3
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/seo/alternates.ts +1 -1
- package/apps/storefront/src/lib/social/notify.ts +4 -4
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +203 -69
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +9 -4
- package/apps/storefront/src/middleware/index.ts +273 -51
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +16 -2
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
- package/apps/storefront/src/pages/blog/[slug].astro +5 -5
- package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
- package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
- package/apps/storefront/src/pages/blog/index.astro +2 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
- package/apps/storefront/src/pages/cockpit.astro +61 -1362
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +42 -31
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +8 -3
- package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +6 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +6 -2
- package/apps/storefront/src/pages/style-guide/index.astro +19 -5
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
- package/apps/storefront/src/pages/the-build/index.astro +2 -2
- package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
- package/apps/storefront/src/styles/admin.css +27 -1
- package/apps/storefront/src/themes/tenants/index.ts +19 -7
- package/apps/storefront/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +41 -37
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-runtime.ts +32 -1
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +16 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +5 -5
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +136 -9
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/build/copy-tenant-assets.mjs +12 -5
- package/scripts/dev/ai-edit.mjs +2 -1
- package/scripts/dev/file-browser.mjs +2 -2
- package/scripts/dev/file-writer.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/tenant-source-root.mjs +3 -2
- package/scripts/tot-dev.mjs +5 -4
|
@@ -9,18 +9,29 @@
|
|
|
9
9
|
* event, so the two concerns stay separate: analytics stays anonymized, and
|
|
10
10
|
* this stays a narrow, owner-only-readable accountability record.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* email
|
|
14
|
-
*
|
|
15
|
-
* it through is a real follow-up, not done here; email is what every call site
|
|
16
|
-
* already has.
|
|
12
|
+
* MCP-submitted candidates carry the tot20-issued stable subject plus a verified
|
|
13
|
+
* email/display label. Storefront-local UI actions still use their authenticated
|
|
14
|
+
* session email until the same subject is threaded through `SessionRecord`.
|
|
17
15
|
*/
|
|
18
16
|
import { displayNameFromEmail } from "./recordActivity";
|
|
19
17
|
|
|
20
18
|
export type ChangeActorAction = "submit" | "reject" | "resolve" | "accept" | "ship" | "build";
|
|
21
19
|
|
|
20
|
+
export interface ChangeActorIdentity {
|
|
21
|
+
kind: "human" | "service";
|
|
22
|
+
/** Immutable identity-provider subject for a person, or app subject for a service. */
|
|
23
|
+
subject: string;
|
|
24
|
+
/** Present only for a verified human identity. */
|
|
25
|
+
email: string | null;
|
|
26
|
+
/** Owner-facing label. */
|
|
27
|
+
name: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
export interface ChangeActorRecord {
|
|
23
|
-
|
|
31
|
+
/** Older records may predate structured identity and omit these two fields. */
|
|
32
|
+
kind?: ChangeActorIdentity["kind"];
|
|
33
|
+
subject?: string;
|
|
34
|
+
email: string | null;
|
|
24
35
|
/** Best-effort display name — no real name is captured anywhere today, so
|
|
25
36
|
* this is always {@link displayNameFromEmail}'s local-part guess. Swap the
|
|
26
37
|
* source here (not the callers) if a real name ever becomes available. */
|
|
@@ -37,6 +48,14 @@ function attributionKey(tenantId: string, subjectId: string, action: ChangeActor
|
|
|
37
48
|
return `cust:change-actor:${tenantId}:${subjectId}:${action}`;
|
|
38
49
|
}
|
|
39
50
|
|
|
51
|
+
function submitAuthorizationKey(
|
|
52
|
+
tenantId: string,
|
|
53
|
+
changeId: string,
|
|
54
|
+
authorizationId: string,
|
|
55
|
+
): string {
|
|
56
|
+
return `cust:change-actor-authorization:${tenantId}:${changeId}:${authorizationId}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
40
59
|
/**
|
|
41
60
|
* Persist who took `action` on `subjectId` (a `changeId` for reject/accept/build,
|
|
42
61
|
* or the aggregate id for ship). Best-effort — never blocks or fails the
|
|
@@ -51,6 +70,8 @@ export async function recordChangeActor(
|
|
|
51
70
|
): Promise<void> {
|
|
52
71
|
try {
|
|
53
72
|
const record: ChangeActorRecord = {
|
|
73
|
+
kind: "human",
|
|
74
|
+
subject: `email:${email.trim().toLowerCase()}`,
|
|
54
75
|
email,
|
|
55
76
|
name: displayNameFromEmail(email),
|
|
56
77
|
at: new Date().toISOString(),
|
|
@@ -61,6 +82,65 @@ export async function recordChangeActor(
|
|
|
61
82
|
}
|
|
62
83
|
}
|
|
63
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Persist an actor as a required part of a privileged mutation's authorization
|
|
87
|
+
* boundary. Unlike {@link recordChangeActor}, storage failures propagate so the
|
|
88
|
+
* caller can fail closed before performing the mutation.
|
|
89
|
+
*/
|
|
90
|
+
export async function recordRequiredChangeActor(
|
|
91
|
+
kv: ChangeActorKv,
|
|
92
|
+
tenantId: string,
|
|
93
|
+
subjectId: string,
|
|
94
|
+
action: ChangeActorAction,
|
|
95
|
+
actor: ChangeActorIdentity,
|
|
96
|
+
at: string,
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
const record: ChangeActorRecord = { ...actor, at };
|
|
99
|
+
await kv.put(attributionKey(tenantId, subjectId, action), JSON.stringify(record));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Stage an immutable, per-attempt submit authorization before the forge write.
|
|
104
|
+
* This deliberately does not replace the canonical `submit` actor: a failed
|
|
105
|
+
* Gitea update must never make an older PR look like it was submitted by the
|
|
106
|
+
* actor whose later attempt failed.
|
|
107
|
+
*/
|
|
108
|
+
export async function recordCandidateSubmitAuthorization(
|
|
109
|
+
kv: ChangeActorKv,
|
|
110
|
+
tenantId: string,
|
|
111
|
+
changeId: string,
|
|
112
|
+
authorizationId: string,
|
|
113
|
+
actor: ChangeActorIdentity,
|
|
114
|
+
authorizedAt: string,
|
|
115
|
+
): Promise<void> {
|
|
116
|
+
const record: ChangeActorRecord = { ...actor, at: authorizedAt };
|
|
117
|
+
await kv.put(
|
|
118
|
+
submitAuthorizationKey(tenantId, changeId, authorizationId),
|
|
119
|
+
JSON.stringify(record),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Read one exact pre-forge authorization. Missing/corrupt data fails closed. */
|
|
124
|
+
export async function readCandidateSubmitAuthorization(
|
|
125
|
+
kv: ChangeActorKv,
|
|
126
|
+
tenantId: string,
|
|
127
|
+
changeId: string,
|
|
128
|
+
authorizationId: string,
|
|
129
|
+
): Promise<ChangeActorRecord | null> {
|
|
130
|
+
try {
|
|
131
|
+
const raw = await kv.get(submitAuthorizationKey(tenantId, changeId, authorizationId));
|
|
132
|
+
return raw ? JSON.parse(raw) as ChangeActorRecord : null;
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Browser responses never need the immutable identity-provider subject. */
|
|
139
|
+
export function ownerVisibleChangeActor(record: ChangeActorRecord): ChangeActorRecord {
|
|
140
|
+
const { subject: _subject, ...visible } = record;
|
|
141
|
+
return visible;
|
|
142
|
+
}
|
|
143
|
+
|
|
64
144
|
/** Read every recorded action's attribution for one subject. Best-effort per
|
|
65
145
|
* action: a missing/corrupt record is simply absent from the result, never
|
|
66
146
|
* a thrown error. */
|
|
@@ -101,7 +101,7 @@ export function observeDeployVersion(
|
|
|
101
101
|
input: DeployVersionObservedInput = {},
|
|
102
102
|
): ActivityEvent | undefined {
|
|
103
103
|
if (observedThisIsolate) return undefined;
|
|
104
|
-
// Kill switch
|
|
104
|
+
// Kill switch: telemetry off ⇒ skip the observation entirely (fail-open). Do
|
|
105
105
|
// NOT set the per-isolate guard, so re-enabling emits the observation on the next call.
|
|
106
106
|
if (isActivityDisabled()) return undefined;
|
|
107
107
|
observedThisIsolate = true;
|
|
@@ -22,6 +22,7 @@ const TAB_IDS = [
|
|
|
22
22
|
"ai-workflows",
|
|
23
23
|
"blog",
|
|
24
24
|
"publish",
|
|
25
|
+
"integrations",
|
|
25
26
|
"settings",
|
|
26
27
|
] as const;
|
|
27
28
|
|
|
@@ -35,6 +36,7 @@ const ASK_CONTEXTS: Record<string, string> = {
|
|
|
35
36
|
"ai-workflows": "AI Workflows",
|
|
36
37
|
blog: "Blog",
|
|
37
38
|
publish: "Publish",
|
|
39
|
+
integrations: "Integrations",
|
|
38
40
|
settings: "Settings",
|
|
39
41
|
};
|
|
40
42
|
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* staff-override authorization (`resolveAdminTenantMode`) that admin.astro
|
|
5
5
|
* actually runs. This module re-exports that type so every mock service under
|
|
6
6
|
* `lib/admin/**` can keep importing it from a stable relative path
|
|
7
|
-
* (`../../envelope`) without declaring a second, competing interface
|
|
8
|
-
* `envelope-type-duplication-follow-up`).
|
|
7
|
+
* (`../../envelope`) without declaring a second, competing interface.
|
|
9
8
|
*/
|
|
10
9
|
export type { AdminTenantEnvelope } from "@/lib/adminTenantEnvelope";
|
|
@@ -18,6 +18,7 @@ import { DEFAULT_APP_GATEWAY_KID, APP_GATEWAY_KID_ENV } from "./gatewayKeys.js";
|
|
|
18
18
|
import { fromD1 } from "../d1/catalog.js";
|
|
19
19
|
import { readD1, readEnv } from "../env.js";
|
|
20
20
|
import { MANAGE_ROLES, type AdminAuthResult } from "./adminSession.js";
|
|
21
|
+
import { ManagedAnalyticsApiV1Facade } from "./managedAnalyticsService.js";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* No-store JSON response — install/rotate bodies carry a one-time client
|
|
@@ -44,6 +45,13 @@ export async function getRegistryService(): Promise<AppRegistryService | Respons
|
|
|
44
45
|
return new AppRegistryService(db, credentials);
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/** Build the managed-analytics v1 service over the same tenant control-plane D1. */
|
|
49
|
+
export async function getManagedAnalyticsService(): Promise<ManagedAnalyticsApiV1Facade | Response> {
|
|
50
|
+
const d1 = await readD1("STOREFRONT_APPS_DB");
|
|
51
|
+
if (!d1) return json({ error: "app registry not configured" }, 503);
|
|
52
|
+
return new ManagedAnalyticsApiV1Facade(fromD1(d1));
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
/** HTTP status per `RegistryError` code — the one shared fail-closed mapping. */
|
|
48
56
|
const STATUS_BY_REGISTRY_CODE: Record<RegistryErrorCode, number> = {
|
|
49
57
|
scope_denied: 400,
|
|
@@ -52,6 +60,8 @@ const STATUS_BY_REGISTRY_CODE: Record<RegistryErrorCode, number> = {
|
|
|
52
60
|
invalid_state: 409,
|
|
53
61
|
invalid_webhook_endpoint: 400,
|
|
54
62
|
invalid_widget_placement: 400,
|
|
63
|
+
unknown_version: 404,
|
|
64
|
+
invalid_version_facets: 400,
|
|
55
65
|
};
|
|
56
66
|
|
|
57
67
|
/** Map a `RegistryError` to its HTTP status. Never echoes internals — code + message only. */
|
|
@@ -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
|
|
@@ -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
|
+
}
|