@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/astro.config.mjs +3 -3
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +12 -14
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +4 -4
- package/apps/storefront/src/layouts/Layout.astro +100 -20
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
- package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
- package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/apps/adminService.ts +10 -0
- package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +264 -10
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/blog/collection.ts +97 -10
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +57 -3
- package/apps/storefront/src/lib/blog/types.ts +9 -9
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +12 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +18 -6
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
- package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
- package/apps/storefront/src/lib/newsletter/send.ts +3 -3
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/seo/alternates.ts +1 -1
- package/apps/storefront/src/lib/social/notify.ts +4 -4
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +9 -4
- package/apps/storefront/src/middleware/index.ts +273 -51
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +16 -2
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
- package/apps/storefront/src/pages/blog/[slug].astro +5 -5
- package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
- package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
- package/apps/storefront/src/pages/blog/index.astro +2 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
- package/apps/storefront/src/pages/cockpit.astro +61 -1362
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
- package/apps/storefront/src/pages/the-build/index.astro +2 -2
- package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
- package/apps/storefront/src/styles/admin.css +27 -1
- package/apps/storefront/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +41 -37
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-runtime.ts +32 -1
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +16 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +5 -5
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +45 -9
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/dev/file-browser.mjs +2 -2
- package/scripts/dev/file-writer.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/tenant-source-root.mjs +1 -1
- package/scripts/tot-dev.mjs +2 -2
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/apps/admin/gtm-migration-install — the review->install surface of
|
|
3
|
+
* the GTM migration assistant. It takes a reviewed draft's `draftId`, LOADS the
|
|
4
|
+
* stored draft server-side, and converts it into a DISABLED desired
|
|
5
|
+
* managed-analytics install + draft revision, strictly through WS1's pinned
|
|
6
|
+
* PUBLIC contract. It returns the resulting disabled install/revision + a
|
|
7
|
+
* correlation id.
|
|
8
|
+
*
|
|
9
|
+
* Auth mirrors the read-only analyze route (`gtm-migration.ts`): the tenant is
|
|
10
|
+
* `auth.tenantId` from the resolved operator session — NEVER the request body.
|
|
11
|
+
*
|
|
12
|
+
* SECURITY BOUNDARY (a reviewer enforces this):
|
|
13
|
+
* - LOAD-BY-ID ONLY: the body carries a `draftId` (+ optional
|
|
14
|
+
* `targetInstallId` and its original `expectedRevision`), never a draft
|
|
15
|
+
* object. The stable mutation identity comes from the required
|
|
16
|
+
* `Idempotency-Key` header. A body that tries to smuggle draft data
|
|
17
|
+
* (`proposedConfiguration`/`proposedAdapterId`/`findings`/`draft`/inline
|
|
18
|
+
* `configuration`) is rejected — the stored draft is loaded from WS1 and
|
|
19
|
+
* re-validated against the pinned validator; untrusted draft data never gets
|
|
20
|
+
* past review.
|
|
21
|
+
* - DISABLED-ONLY: the produced install is `status:"disabled"` with a
|
|
22
|
+
* `status:"draft"` revision. Any attempt to force enable/publish/activate in
|
|
23
|
+
* the body is refused. This route imports NO registry/D1/R2/materializer/
|
|
24
|
+
* lifecycle dependency and writes NOTHING to storefront's own stores — every
|
|
25
|
+
* effect goes through the pinned WS1 client, which exposes no enable op.
|
|
26
|
+
*/
|
|
27
|
+
import type { APIContext } from "astro";
|
|
28
|
+
import {
|
|
29
|
+
validateGetIntegrationInstallRequest,
|
|
30
|
+
validateGetMigrationDraftRequest,
|
|
31
|
+
validateMutationRequest,
|
|
32
|
+
} from "@tot/public-runtime";
|
|
33
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
34
|
+
import { json } from "@/lib/apps/adminService.js";
|
|
35
|
+
import {
|
|
36
|
+
reviewInstallDraft,
|
|
37
|
+
type ReviewInstallResult,
|
|
38
|
+
type Ws1ManagedAnalyticsClient,
|
|
39
|
+
} from "@/lib/apps/gtmMigrationInstall.js";
|
|
40
|
+
import { resolveWs1ManagedAnalyticsClient } from "@/lib/apps/ws1ManagedAnalyticsClient.js";
|
|
41
|
+
|
|
42
|
+
export const prerender = false;
|
|
43
|
+
|
|
44
|
+
/** Fields that would smuggle inline draft data past review — a body may carry only ids. */
|
|
45
|
+
const DRAFT_DATA_FIELDS = ["draft", "proposedConfiguration", "proposedAdapterId", "findings", "configuration"] as const;
|
|
46
|
+
/** Fields that would try to enable/publish/activate — this path produces a disabled draft only. */
|
|
47
|
+
const ENABLE_FIELDS = ["status", "enabled", "enable", "publish", "publishes", "activate"] as const;
|
|
48
|
+
|
|
49
|
+
interface ParsedRequest {
|
|
50
|
+
readonly draftId: string;
|
|
51
|
+
readonly targetInstallId?: string;
|
|
52
|
+
readonly expectedRevision?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Parse ids plus optional revise CAS state, and NOTHING that could enable or resubmit a draft. */
|
|
56
|
+
function parseRequest(body: unknown): ParsedRequest | { error: string } {
|
|
57
|
+
if (!body || typeof body !== "object") return { error: "A JSON object body with a draftId is required." };
|
|
58
|
+
const o = body as Record<string, unknown>;
|
|
59
|
+
|
|
60
|
+
for (const field of DRAFT_DATA_FIELDS) {
|
|
61
|
+
if (field in o) {
|
|
62
|
+
return { error: "Submit a draftId only; the reviewed draft is loaded server-side and cannot be supplied inline." };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const field of ENABLE_FIELDS) {
|
|
66
|
+
if (field in o) {
|
|
67
|
+
return { error: "This path produces a disabled draft only; it cannot enable, publish, or activate." };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const rawDraftId = o.draftId;
|
|
72
|
+
if (typeof rawDraftId !== "string" || !rawDraftId.trim()) return { error: "A draftId is required." };
|
|
73
|
+
const draftCheck = validateGetMigrationDraftRequest({
|
|
74
|
+
apiVersion: "v1",
|
|
75
|
+
query: { draftId: rawDraftId },
|
|
76
|
+
});
|
|
77
|
+
if (!draftCheck.ok) {
|
|
78
|
+
return { error: "draftId must be a valid opaque identifier." };
|
|
79
|
+
}
|
|
80
|
+
const draftId = draftCheck.value.query.draftId;
|
|
81
|
+
|
|
82
|
+
let targetInstallId: string | undefined;
|
|
83
|
+
if ("targetInstallId" in o) {
|
|
84
|
+
const t = o.targetInstallId;
|
|
85
|
+
if (typeof t !== "string" || !t.trim()) return { error: "targetInstallId must be a non-empty string when present." };
|
|
86
|
+
const installCheck = validateGetIntegrationInstallRequest({
|
|
87
|
+
apiVersion: "v1",
|
|
88
|
+
query: { installId: t },
|
|
89
|
+
});
|
|
90
|
+
if (!installCheck.ok) {
|
|
91
|
+
return { error: "targetInstallId must be a valid opaque identifier." };
|
|
92
|
+
}
|
|
93
|
+
targetInstallId = installCheck.value.query.installId;
|
|
94
|
+
}
|
|
95
|
+
let expectedRevision: number | undefined;
|
|
96
|
+
if ("expectedRevision" in o) {
|
|
97
|
+
if (!Number.isInteger(o.expectedRevision) || (o.expectedRevision as number) < 0) {
|
|
98
|
+
return { error: "expectedRevision must be a non-negative integer when present." };
|
|
99
|
+
}
|
|
100
|
+
expectedRevision = o.expectedRevision as number;
|
|
101
|
+
}
|
|
102
|
+
if (targetInstallId !== undefined && expectedRevision === undefined) {
|
|
103
|
+
return { error: "expectedRevision is required when targetInstallId is present so retries preserve the original CAS request." };
|
|
104
|
+
}
|
|
105
|
+
if (targetInstallId === undefined && expectedRevision !== undefined) {
|
|
106
|
+
return { error: "expectedRevision is only accepted when targetInstallId is present." };
|
|
107
|
+
}
|
|
108
|
+
return { draftId, targetInstallId, expectedRevision };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
112
|
+
const url = new URL(context.request.url);
|
|
113
|
+
const requestedTenant = url.searchParams.get("tenant");
|
|
114
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
115
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
116
|
+
|
|
117
|
+
const correlationId = crypto.randomUUID();
|
|
118
|
+
|
|
119
|
+
let body: unknown;
|
|
120
|
+
try {
|
|
121
|
+
body = await context.request.json();
|
|
122
|
+
} catch {
|
|
123
|
+
return json({ ok: false, correlationId, error: "The request body must be JSON." }, 400);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const parsed = parseRequest(body);
|
|
127
|
+
if ("error" in parsed) return json({ ok: false, correlationId, error: parsed.error }, 400);
|
|
128
|
+
|
|
129
|
+
const idempotencyKey = context.request.headers.get("idempotency-key")?.trim();
|
|
130
|
+
const keyCheck = validateMutationRequest({
|
|
131
|
+
apiVersion: "v1",
|
|
132
|
+
idempotencyKey,
|
|
133
|
+
expectedRevision: 0,
|
|
134
|
+
body: {},
|
|
135
|
+
});
|
|
136
|
+
if (!keyCheck.ok) {
|
|
137
|
+
return json({ ok: false, correlationId, error: "A valid Idempotency-Key header is required." }, 400);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let client: Ws1ManagedAnalyticsClient | null;
|
|
141
|
+
try {
|
|
142
|
+
client = await resolveWs1ManagedAnalyticsClient(context, auth.tenantId);
|
|
143
|
+
} catch {
|
|
144
|
+
return json({ ok: false, correlationId, error: "Managed analytics service resolution failed safely.", code: "internal_error" }, 502);
|
|
145
|
+
}
|
|
146
|
+
if (!client) return json({ ok: false, correlationId, error: "Managed analytics service is not configured." }, 503);
|
|
147
|
+
|
|
148
|
+
let result: ReviewInstallResult;
|
|
149
|
+
try {
|
|
150
|
+
result = await reviewInstallDraft(client, {
|
|
151
|
+
draftId: parsed.draftId,
|
|
152
|
+
targetInstallId: parsed.targetInstallId,
|
|
153
|
+
expectedRevision: parsed.expectedRevision,
|
|
154
|
+
idempotencyKey: keyCheck.value.idempotencyKey,
|
|
155
|
+
correlationId,
|
|
156
|
+
});
|
|
157
|
+
} catch {
|
|
158
|
+
return json({ ok: false, correlationId, error: "Managed analytics review failed safely.", code: "internal_error" }, 502);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!result.ok) {
|
|
162
|
+
return json({ ok: false, correlationId, error: result.error, code: result.code }, result.status);
|
|
163
|
+
}
|
|
164
|
+
return json(
|
|
165
|
+
{
|
|
166
|
+
ok: true,
|
|
167
|
+
correlationId,
|
|
168
|
+
install: result.install,
|
|
169
|
+
revision: result.revision,
|
|
170
|
+
idempotencyReplayed: result.idempotencyReplayed,
|
|
171
|
+
},
|
|
172
|
+
200,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/apps/admin/gtm-migration — the READ-ONLY "Migrate from GTM" analyze
|
|
3
|
+
* surface. Accepts an uploaded GTM container export (a JSON request body, or a
|
|
4
|
+
* `multipart/form-data` file part), runs the SAFE `@tot/public-runtime` pipeline
|
|
5
|
+
* (parse -> graph -> classify) over it, and returns a REDACTED report: object
|
|
6
|
+
* accounting, the disposition summary, per-object dispositions, the dependency
|
|
7
|
+
* graph, unresolved findings, and recommendations. `?format=bundle` returns the
|
|
8
|
+
* same report as a downloadable JSON attachment with a provenance stamp.
|
|
9
|
+
*
|
|
10
|
+
* Auth mirrors the read-only `integrations` route: the tenant is `auth.tenantId`
|
|
11
|
+
* from the resolved operator session — NEVER the request body — and any tenant
|
|
12
|
+
* membership admits (analyze is a diagnostic read, not a manage action).
|
|
13
|
+
*
|
|
14
|
+
* READ-ONLY BOUNDARY (a reviewer enforces this): this route writes NOTHING,
|
|
15
|
+
* builds no integration or draft, publishes nothing, activates nothing, and
|
|
16
|
+
* emits no markup/script/network-to-third-parties/event-listener. It imports
|
|
17
|
+
* only the operator-session gate, the JSON responder, and the pure, inert
|
|
18
|
+
* pipeline + report builder — no registry, D1, R2, or lifecycle dependency is
|
|
19
|
+
* imported or reachable. It parses inert data and returns a report. Ids from a
|
|
20
|
+
* foreign container are reflected back only; they are never used to look
|
|
21
|
+
* anything up about this tenant.
|
|
22
|
+
*/
|
|
23
|
+
import type { APIContext } from "astro";
|
|
24
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
25
|
+
import { json } from "@/lib/apps/adminService.js";
|
|
26
|
+
import { analyzeGtmExport } from "@/lib/apps/gtmMigrationReport.js";
|
|
27
|
+
|
|
28
|
+
export const prerender = false;
|
|
29
|
+
|
|
30
|
+
/** The multipart field name the upload UI posts the export file under. */
|
|
31
|
+
const FILE_FIELD = "file";
|
|
32
|
+
|
|
33
|
+
/** Read the uploaded export text from a JSON body or a multipart file part. */
|
|
34
|
+
async function readUploadText(request: Request): Promise<string | { error: string }> {
|
|
35
|
+
const contentType = (request.headers.get("content-type") ?? "").toLowerCase();
|
|
36
|
+
if (contentType.includes("multipart/form-data")) {
|
|
37
|
+
let form: FormData;
|
|
38
|
+
try {
|
|
39
|
+
form = await request.formData();
|
|
40
|
+
} catch {
|
|
41
|
+
return { error: "could not read the uploaded form" };
|
|
42
|
+
}
|
|
43
|
+
const part = form.get(FILE_FIELD);
|
|
44
|
+
if (part == null) return { error: `no "${FILE_FIELD}" part in the upload` };
|
|
45
|
+
if (typeof part === "string") return part;
|
|
46
|
+
return await part.text();
|
|
47
|
+
}
|
|
48
|
+
// Any other content type: the raw request body IS the export document.
|
|
49
|
+
return await request.text();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
53
|
+
const url = new URL(context.request.url);
|
|
54
|
+
const requestedTenant = url.searchParams.get("tenant");
|
|
55
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
56
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
57
|
+
|
|
58
|
+
const upload = await readUploadText(context.request);
|
|
59
|
+
const correlationId = crypto.randomUUID();
|
|
60
|
+
const generatedAt = new Date().toISOString();
|
|
61
|
+
|
|
62
|
+
if (typeof upload !== "string") {
|
|
63
|
+
return json({ ok: false, correlationId, error: upload.error, violations: [] }, 400);
|
|
64
|
+
}
|
|
65
|
+
if (upload.trim() === "") {
|
|
66
|
+
return json(
|
|
67
|
+
{ ok: false, correlationId, error: "The upload was empty.", violations: [] },
|
|
68
|
+
400,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const result = analyzeGtmExport(upload, { tenantId: auth.tenantId, correlationId, generatedAt });
|
|
73
|
+
|
|
74
|
+
if (!result.ok) {
|
|
75
|
+
// Structured parse violations + a stable, safe error and the correlation id.
|
|
76
|
+
return json(
|
|
77
|
+
{ ok: false, correlationId, error: result.error, violations: result.violations },
|
|
78
|
+
422,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const wantsBundle = url.searchParams.get("format") === "bundle";
|
|
83
|
+
const responseBody = { ok: true, correlationId, report: result.report };
|
|
84
|
+
if (!wantsBundle) return json(responseBody, 200);
|
|
85
|
+
|
|
86
|
+
// Support-bundle download: the same redacted report as a JSON attachment.
|
|
87
|
+
const filename = `gtm-migration-report-${correlationId}.json`;
|
|
88
|
+
return new Response(JSON.stringify(responseBody, null, 2), {
|
|
89
|
+
status: 200,
|
|
90
|
+
headers: {
|
|
91
|
+
"content-type": "application/json",
|
|
92
|
+
"cache-control": "no-store",
|
|
93
|
+
"content-disposition": `attachment; filename="${filename}"`,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/apps/admin/integration-lifecycle — execute an integration-config
|
|
3
|
+
* lifecycle write action (ext-admin-07): `preview` a candidate (its effective
|
|
4
|
+
* code/config/CSP/consent/assurance diff), `publish` it to live THROUGH the
|
|
5
|
+
* policy-06 promotion gate, or `rollback` live to a known-good revision. The
|
|
6
|
+
* install-status actions (`suspend` / `resume` / `uninstall`) keep their own
|
|
7
|
+
* registry-backed routes; this route owns the three that ride the
|
|
8
|
+
* candidate/change-control substrate.
|
|
9
|
+
*
|
|
10
|
+
* Authorization mirrors every mutating admin route: `resolveOperatorSession`
|
|
11
|
+
* resolves the tenant from the host/console session (never the body), and
|
|
12
|
+
* `requireManageRole` denies a read-only console `developer` — so a developer or
|
|
13
|
+
* vendor never holds tenant publish authority. `requireOwnedInstall` rejects a
|
|
14
|
+
* foreign-tenant `installId` with the same 404 a missing one gets.
|
|
15
|
+
*
|
|
16
|
+
* The pure {@link handleLifecycleRequest} decides and persists ONLY on success —
|
|
17
|
+
* a blocked publish leaves the ACTUAL last-good state untouched. The lifecycle
|
|
18
|
+
* state + candidate-evidence + gate-input materialization is the control plane's
|
|
19
|
+
* job (the same materialization ext-admin-02's read model resolves per install);
|
|
20
|
+
* until an install is materialized the controller answers a 409, never a
|
|
21
|
+
* fabricated success.
|
|
22
|
+
*/
|
|
23
|
+
import type { APIContext } from "astro";
|
|
24
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
25
|
+
import {
|
|
26
|
+
getRegistryService,
|
|
27
|
+
requireOwnedInstall,
|
|
28
|
+
requireManageRole,
|
|
29
|
+
parseInstallId,
|
|
30
|
+
json,
|
|
31
|
+
} from "@/lib/apps/adminService.js";
|
|
32
|
+
import {
|
|
33
|
+
handleLifecycleRequest,
|
|
34
|
+
type LifecycleDeps,
|
|
35
|
+
type LifecycleWriteAction,
|
|
36
|
+
} from "@/lib/apps/integrationLifecycleController.js";
|
|
37
|
+
import { parseEnvironment, type IntegrationEnvironment } from "@tot/public-runtime";
|
|
38
|
+
import { loadTenantLocalLifecycleState } from "@/lib/apps/tenantIntegrationMaterialization.js";
|
|
39
|
+
import { resolveAdminTenant } from "@/config/adminTenantLookup";
|
|
40
|
+
|
|
41
|
+
export const prerender = false;
|
|
42
|
+
|
|
43
|
+
const WRITE_ACTIONS: ReadonlySet<string> = new Set(["preview", "publish", "rollback"]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build the control-plane materialization seam for one tenant's installs. A tenant's
|
|
47
|
+
* OWN (tenant-local) declaration is materialized through the interim bridge (ext-kernel-09):
|
|
48
|
+
* `load` returns its REAL live-by-config lifecycle state instead of the former blanket
|
|
49
|
+
* `null`, so a declared install is no longer reported `not_materialized`. It carries no
|
|
50
|
+
* separate candidate/promotion gate — an on-by-default declaration is already effective —
|
|
51
|
+
* so `resolveCandidateEvidence`/`resolveGate` stay null and a `preview`/`publish` refuses
|
|
52
|
+
* fail-closed (no candidate) rather than fabricating one. A managed-app install with a
|
|
53
|
+
* persisted candidate lifecycle remains the control plane's separate concern.
|
|
54
|
+
*/
|
|
55
|
+
function integrationLifecycleDeps(_context: APIContext, tenantId: string, stage: IntegrationEnvironment): LifecycleDeps {
|
|
56
|
+
return {
|
|
57
|
+
load: async (installId) => {
|
|
58
|
+
// Best-effort: a resolver hiccup answers "unmaterialized" (fail-closed 409),
|
|
59
|
+
// never a 500 — an install with no materialized declaration is null either way.
|
|
60
|
+
try {
|
|
61
|
+
const tenant = await resolveAdminTenant(tenantId);
|
|
62
|
+
if (!tenant || tenant.tenant_id !== tenantId) return null;
|
|
63
|
+
return loadTenantLocalLifecycleState(tenant, installId, stage);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`[integration-lifecycle] materialization skipped for ${tenantId}: ${String(err)}`);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
save: async () => {},
|
|
70
|
+
resolveCandidateEvidence: async () => null,
|
|
71
|
+
resolveGate: async () => null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
76
|
+
const requestUrl = new URL(context.request.url);
|
|
77
|
+
const requestedTenant = requestUrl.searchParams.get("tenant");
|
|
78
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
79
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
80
|
+
const manageDenied = requireManageRole(auth);
|
|
81
|
+
if (manageDenied) return manageDenied;
|
|
82
|
+
|
|
83
|
+
let body: unknown;
|
|
84
|
+
try {
|
|
85
|
+
body = await context.request.json();
|
|
86
|
+
} catch {
|
|
87
|
+
return json({ error: "invalid JSON body" }, 400);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const action = (body as { action?: unknown }).action;
|
|
91
|
+
if (typeof action !== "string" || !WRITE_ACTIONS.has(action)) {
|
|
92
|
+
return json({ error: "action must be one of: preview, publish, rollback" }, 400);
|
|
93
|
+
}
|
|
94
|
+
const installId = parseInstallId(body);
|
|
95
|
+
if (!installId) return json({ error: "installId is required" }, 400);
|
|
96
|
+
|
|
97
|
+
const registry = await getRegistryService();
|
|
98
|
+
if (registry instanceof Response) return registry;
|
|
99
|
+
|
|
100
|
+
const owned = await requireOwnedInstall(registry, installId, auth.tenantId);
|
|
101
|
+
if (owned instanceof Response) return owned;
|
|
102
|
+
|
|
103
|
+
const { expectedLiveRevision, targetRevision } = body as {
|
|
104
|
+
expectedLiveRevision?: string | null;
|
|
105
|
+
targetRevision?: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const stage: IntegrationEnvironment = parseEnvironment(requestUrl.searchParams.get("stage")) ?? "live";
|
|
109
|
+
const deps = integrationLifecycleDeps(context, auth.tenantId, stage);
|
|
110
|
+
const result = await handleLifecycleRequest(deps, {
|
|
111
|
+
action: action as LifecycleWriteAction,
|
|
112
|
+
installId,
|
|
113
|
+
actor: auth.actor,
|
|
114
|
+
...(expectedLiveRevision !== undefined ? { expectedLiveRevision } : {}),
|
|
115
|
+
...(typeof targetRevision === "string" ? { targetRevision } : {}),
|
|
116
|
+
});
|
|
117
|
+
return json(result.body, result.status);
|
|
118
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/apps/admin/integrations — the effective integration admin read model
|
|
3
|
+
* (ext-admin-02): the tenant-scoped data behind ext-admin-01's `/admin#integrations`
|
|
4
|
+
* IA. Combines each install's desired config, resolved effective plan, compiled CSP,
|
|
5
|
+
* consent, assurance, candidate, and rollback availability into ONE safe view.
|
|
6
|
+
*
|
|
7
|
+
* Auth mirrors `list.ts` exactly: the `?tenant=` query param is only ever a CANDIDATE
|
|
8
|
+
* into `resolveOperatorSession` — the tenant actually used is `auth.tenantId` from the
|
|
9
|
+
* resolved session, never the raw param — so another tenant, or a vendor with no
|
|
10
|
+
* membership, is denied (401/403) before any read model is assembled. Read-only: any
|
|
11
|
+
* membership role admits. The pure {@link assembleIntegrationReadModel} is the second,
|
|
12
|
+
* defense-in-depth gate: it re-asserts every materialized input belongs to
|
|
13
|
+
* `auth.tenantId` and redacts secrets, so a mis-plumbed control plane cannot leak.
|
|
14
|
+
*
|
|
15
|
+
* The control-plane materialization of effective plans, candidate evidence, and
|
|
16
|
+
* assurance records is resolved per install where present; an install with none yet
|
|
17
|
+
* surfaces honestly as `configuring` (no effective authority, no rollback), never a
|
|
18
|
+
* fabricated live state.
|
|
19
|
+
*/
|
|
20
|
+
import type { APIContext } from "astro";
|
|
21
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
22
|
+
import { getRegistryService, json } from "@/lib/apps/adminService.js";
|
|
23
|
+
import { parseEnvironment, type IntegrationEnvironment } from "@tot/public-runtime";
|
|
24
|
+
import {
|
|
25
|
+
assembleIntegrationReadModel,
|
|
26
|
+
type IntegrationReadModelInputItem,
|
|
27
|
+
} from "@/lib/apps/integrationReadModel.js";
|
|
28
|
+
import { tenantLocalReadModelItems } from "@/lib/apps/tenantIntegrationMaterialization.js";
|
|
29
|
+
import { resolveAdminTenant } from "@/config/adminTenantLookup";
|
|
30
|
+
|
|
31
|
+
export const prerender = false;
|
|
32
|
+
|
|
33
|
+
/** A merchant-legible purpose line for an install, never an internal adapter name. */
|
|
34
|
+
function purposeFor(appId: string): string {
|
|
35
|
+
return `Integration: ${appId}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function GET(context: APIContext): Promise<Response> {
|
|
39
|
+
const url = new URL(context.request.url);
|
|
40
|
+
const requestedTenant = url.searchParams.get("tenant");
|
|
41
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
42
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
43
|
+
|
|
44
|
+
const registry = await getRegistryService();
|
|
45
|
+
if (registry instanceof Response) return registry;
|
|
46
|
+
|
|
47
|
+
const stage: IntegrationEnvironment = parseEnvironment(url.searchParams.get("stage")) ?? "staged";
|
|
48
|
+
|
|
49
|
+
const installs = await registry.listByTenant(auth.tenantId);
|
|
50
|
+
const items: IntegrationReadModelInputItem[] = installs
|
|
51
|
+
.filter((install) => install.status !== "uninstalled")
|
|
52
|
+
.map((install) => ({
|
|
53
|
+
install: {
|
|
54
|
+
installId: install.installId,
|
|
55
|
+
appId: install.appId,
|
|
56
|
+
appVersion: install.appVersion,
|
|
57
|
+
tenantId: install.tenantId,
|
|
58
|
+
status: install.status,
|
|
59
|
+
},
|
|
60
|
+
purpose: purposeFor(install.appId),
|
|
61
|
+
// The desired config is the tenant's own input; when the manifest snapshot is not
|
|
62
|
+
// a structured object it contributes nothing — never a fabricated value.
|
|
63
|
+
desiredConfig: {},
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
// ext-kernel-09: a tenant's OWN (tenant-local) declarations are materialized through
|
|
67
|
+
// the interim bridge and appended with their REAL effective plan + compiled CSP, so a
|
|
68
|
+
// declared integration surfaces effective state — not `configuring`. Resolved off the
|
|
69
|
+
// auth-bound tenant config only (the tenant id is `auth.tenantId`, never the param).
|
|
70
|
+
// Best-effort + additive: a resolver hiccup degrades to the managed-app view rather
|
|
71
|
+
// than failing the whole read model closed.
|
|
72
|
+
try {
|
|
73
|
+
const tenantConfig = await resolveAdminTenant(auth.tenantId);
|
|
74
|
+
if (tenantConfig && tenantConfig.tenant_id === auth.tenantId) {
|
|
75
|
+
items.push(...tenantLocalReadModelItems(tenantConfig, stage));
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error(`[admin-integrations] tenant-local materialization skipped for ${auth.tenantId}: ${String(err)}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = assembleIntegrationReadModel({ tenantPublicId: auth.tenantId, stage, items });
|
|
82
|
+
if (!result.ok) {
|
|
83
|
+
// A cross-tenant input or surviving secret is a server-side integrity failure, not
|
|
84
|
+
// something to echo back field-by-field.
|
|
85
|
+
return json({ error: "integration read model failed integrity checks" }, 500);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return json({ readModel: result.value });
|
|
89
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/apps/admin/platform-adapters — the first-class native adapter
|
|
3
|
+
* surface (ext-admin-04). Two actions over the pure, edge-safe adapter core
|
|
4
|
+
* ({@link platformAdapterCatalog} / {@link buildPlatformAdapterDeclaration}):
|
|
5
|
+
*
|
|
6
|
+
* - `catalog` — the first-class adapter cards (consent purposes + performance
|
|
7
|
+
* budget + CSP origins + per-stage destination behavior), so a merchant sees the
|
|
8
|
+
* material effects before configuring anything.
|
|
9
|
+
* - `build` — turn a merchant's public destination ids (per environment) into a
|
|
10
|
+
* tenant-scoped declaration + resolved effective plan + read-model card, or
|
|
11
|
+
* refuse. Fail-closed: an unknown adapter, a missing/invalid live id, or a
|
|
12
|
+
* non-live stage that reuses the live id blocks with violations.
|
|
13
|
+
*
|
|
14
|
+
* Auth mirrors the other `/api/apps/admin/*` routes: the tenant is `auth.tenantId`
|
|
15
|
+
* from the resolved operator session — NEVER the request body — so a build is only
|
|
16
|
+
* ever scoped to the caller's own tenant. The build environment is derived server-side
|
|
17
|
+
* as a `candidate` stage, so the browser can never forge a live-stage declaration.
|
|
18
|
+
* Configuring is a manage action ({@link requireManageRole}). The route does NOT
|
|
19
|
+
* persist — staging/publishing a produced candidate is ext-admin-07's lifecycle.
|
|
20
|
+
*/
|
|
21
|
+
import type { APIContext } from "astro";
|
|
22
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
23
|
+
import { json, requireManageRole } from "@/lib/apps/adminService.js";
|
|
24
|
+
import {
|
|
25
|
+
platformAdapterCatalog,
|
|
26
|
+
buildPlatformAdapterDeclaration,
|
|
27
|
+
resolveBrowserEnvironment,
|
|
28
|
+
INTEGRATION_SITE_SURFACES,
|
|
29
|
+
type IntegrationSiteSurface,
|
|
30
|
+
} from "@tot/public-runtime";
|
|
31
|
+
|
|
32
|
+
export const prerender = false;
|
|
33
|
+
|
|
34
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
35
|
+
const url = new URL(context.request.url);
|
|
36
|
+
const requestedTenant = url.searchParams.get("tenant");
|
|
37
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
38
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
39
|
+
const roleDenied = requireManageRole(auth);
|
|
40
|
+
if (roleDenied) return roleDenied;
|
|
41
|
+
|
|
42
|
+
let body: unknown;
|
|
43
|
+
try {
|
|
44
|
+
body = await context.request.json();
|
|
45
|
+
} catch {
|
|
46
|
+
return json({ error: "request body must be JSON" }, 400);
|
|
47
|
+
}
|
|
48
|
+
if (typeof body !== "object" || body === null) {
|
|
49
|
+
return json({ error: "request body must be a JSON object" }, 400);
|
|
50
|
+
}
|
|
51
|
+
const action = (body as { action?: unknown }).action;
|
|
52
|
+
|
|
53
|
+
if (action === "catalog") {
|
|
54
|
+
return json({ catalog: platformAdapterCatalog() });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (action === "build") {
|
|
58
|
+
const fields = (body as { fields?: unknown }).fields;
|
|
59
|
+
const base = typeof fields === "object" && fields !== null ? (fields as Record<string, unknown>) : {};
|
|
60
|
+
const surfaceParam = (base["siteSurface"] as IntegrationSiteSurface | undefined) ?? "storefront";
|
|
61
|
+
const siteSurface = INTEGRATION_SITE_SURFACES.includes(surfaceParam) ? surfaceParam : "storefront";
|
|
62
|
+
// A build always targets a candidate; the environment is server-derived, so a
|
|
63
|
+
// browser can never forge a live-stage declaration.
|
|
64
|
+
const environment = resolveBrowserEnvironment({
|
|
65
|
+
siteSurface,
|
|
66
|
+
canonicalHost: url.host,
|
|
67
|
+
revision: typeof base["revision"] === "string" ? (base["revision"] as string) : "adapter-candidate",
|
|
68
|
+
dev: false,
|
|
69
|
+
localHost: false,
|
|
70
|
+
candidate: true,
|
|
71
|
+
previewHost: false,
|
|
72
|
+
customizationEnv: "test",
|
|
73
|
+
});
|
|
74
|
+
const result = buildPlatformAdapterDeclaration({
|
|
75
|
+
adapterId: base["adapterId"],
|
|
76
|
+
ids: base["ids"],
|
|
77
|
+
tenantPublicId: auth.tenantId,
|
|
78
|
+
environment,
|
|
79
|
+
});
|
|
80
|
+
const status = result.ok ? 200 : result.reason === "unknown-adapter" ? 404 : 422;
|
|
81
|
+
return json({ result }, status);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return json({ error: 'action must be "catalog" or "build"' }, 400);
|
|
85
|
+
}
|