@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
|
@@ -23,18 +23,29 @@ import {
|
|
|
23
23
|
receiveForwardedOrder,
|
|
24
24
|
type OrderForwardInput,
|
|
25
25
|
} from "@/lib/apps/orders/orderForwardReceiver.js";
|
|
26
|
+
import { joinForwardedOrderConversion } from "@/lib/apps/orders/orderConversionD1.js";
|
|
27
|
+
import { D1VerifiedProviderMeasurementStore } from "@/lib/apps/orders/verifiedProviderMeasurement.js";
|
|
26
28
|
import type { OrderLineItem, OrderStatus } from "@/lib/apps/orders/ordersStore.js";
|
|
27
29
|
import { recordActivity } from "@/lib/activity/recordActivity";
|
|
28
30
|
|
|
29
31
|
export const prerender = false;
|
|
30
32
|
|
|
31
33
|
const SIGNATURE_HEADER = "x-order-forward-signature";
|
|
32
|
-
const ORDER_STATUSES: readonly OrderStatus[] = ["created", "fulfilled", "cancelled"];
|
|
34
|
+
const ORDER_STATUSES: readonly OrderStatus[] = ["created", "fulfilled", "refunded", "cancelled"];
|
|
33
35
|
|
|
34
36
|
function json(body: unknown, status: number): Response {
|
|
35
37
|
return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
/** Coarse, non-PII conversion-join diagnostic (never the order id or correlation token). */
|
|
41
|
+
function logConversionDiag(fields: Record<string, unknown>): void {
|
|
42
|
+
try {
|
|
43
|
+
console.warn("[diag:order-conversion]", JSON.stringify(fields));
|
|
44
|
+
} catch {
|
|
45
|
+
/* diagnostics must never break the caller */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
// --- HMAC verification -----------------------------------------------------
|
|
39
50
|
// Same shape as `@tot/private-controlplane`'s `verifyGiteaSignature` (hex
|
|
40
51
|
// HMAC-SHA256 over the raw body, verified via `crypto.subtle.verify` so a
|
|
@@ -85,6 +96,10 @@ interface OrderForwardBody {
|
|
|
85
96
|
lineItems?: unknown;
|
|
86
97
|
rawCustomerId?: unknown;
|
|
87
98
|
createdAt?: unknown;
|
|
99
|
+
providerFields?: unknown;
|
|
100
|
+
providerEventId?: unknown;
|
|
101
|
+
occurredAt?: unknown;
|
|
102
|
+
amountMinor?: unknown;
|
|
88
103
|
}
|
|
89
104
|
|
|
90
105
|
function parseLineItems(value: unknown): OrderLineItem[] | string {
|
|
@@ -128,7 +143,16 @@ function parseOrderForwardInput(body: OrderForwardBody): OrderForwardInput | str
|
|
|
128
143
|
return "rawCustomerId must be a string";
|
|
129
144
|
}
|
|
130
145
|
if (body.createdAt !== undefined && typeof body.createdAt !== "string") return "createdAt must be a string";
|
|
146
|
+
if (
|
|
147
|
+
body.providerFields !== undefined &&
|
|
148
|
+
(typeof body.providerFields !== "object" || body.providerFields === null || Array.isArray(body.providerFields))
|
|
149
|
+
) {
|
|
150
|
+
return "providerFields must be an object";
|
|
151
|
+
}
|
|
131
152
|
|
|
153
|
+
if (body.providerEventId !== undefined && (typeof body.providerEventId !== "string" || !body.providerEventId.trim())) return "providerEventId must be a non-empty string";
|
|
154
|
+
if (body.occurredAt !== undefined && typeof body.occurredAt !== "string") return "occurredAt must be a string";
|
|
155
|
+
if (body.amountMinor !== undefined && (typeof body.amountMinor !== "number" || !Number.isSafeInteger(body.amountMinor) || body.amountMinor < 0)) return "amountMinor must be a non-negative safe integer";
|
|
132
156
|
return {
|
|
133
157
|
tenantId: body.tenantId,
|
|
134
158
|
externalOrderId: body.externalOrderId,
|
|
@@ -140,6 +164,10 @@ function parseOrderForwardInput(body: OrderForwardBody): OrderForwardInput | str
|
|
|
140
164
|
lineItems,
|
|
141
165
|
rawCustomerId: body.rawCustomerId as string | undefined,
|
|
142
166
|
createdAt: body.createdAt as string | undefined,
|
|
167
|
+
providerEventId: body.providerEventId as string | undefined,
|
|
168
|
+
occurredAt: body.occurredAt as string | undefined,
|
|
169
|
+
amountMinor: body.amountMinor as number | undefined,
|
|
170
|
+
providerFields: body.providerFields as Record<string, unknown> | undefined,
|
|
143
171
|
};
|
|
144
172
|
}
|
|
145
173
|
|
|
@@ -168,7 +196,51 @@ export async function POST(context: APIContext): Promise<Response> {
|
|
|
168
196
|
const d1 = await readD1("STOREFRONT_APPS_DB");
|
|
169
197
|
if (!d1) return json({ error: "orders store not configured" }, 503);
|
|
170
198
|
|
|
171
|
-
const
|
|
199
|
+
const db = fromD1(d1);
|
|
200
|
+
const result = await receiveForwardedOrder(db, input);
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
// This local-only outbox runs only after raw-body HMAC verification and the
|
|
204
|
+
// trusted order receiver. Its failure is fail-soft: commerce still ACKs.
|
|
205
|
+
try {
|
|
206
|
+
const measurement = await new D1VerifiedProviderMeasurementStore(db).record(input);
|
|
207
|
+
if (measurement.outcome === "recorded") {
|
|
208
|
+
logConversionDiag({ tenantId: input.tenantId, env: input.env, regulatedMeasurement: "recorded" });
|
|
209
|
+
}
|
|
210
|
+
} catch (cause) {
|
|
211
|
+
logConversionDiag({
|
|
212
|
+
tenantId: input.tenantId,
|
|
213
|
+
env: input.env,
|
|
214
|
+
regulatedMeasurement: "outbox-unavailable",
|
|
215
|
+
error: cause instanceof Error ? cause.name : "unknown",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Join the verified order to its browser handoff correlation and produce the
|
|
220
|
+
// authoritative server conversion event(s) — ext-commerce-05 destinations fan
|
|
221
|
+
// these out. The join owns idempotency + single-use replay defense; a failure
|
|
222
|
+
// here must never fail the order ACK, so it is fail-soft (logged, swallowed).
|
|
223
|
+
try {
|
|
224
|
+
const conversion = await joinForwardedOrderConversion(db, input, result);
|
|
225
|
+
if (conversion.events.length > 0) {
|
|
226
|
+
// Coarse, non-PII diagnostic: attribution outcome + conversion count only —
|
|
227
|
+
// never the order id, correlation token, or any customer field.
|
|
228
|
+
logConversionDiag({
|
|
229
|
+
tenantId: input.tenantId,
|
|
230
|
+
env: input.env,
|
|
231
|
+
conversions: conversion.events.length,
|
|
232
|
+
attributed: conversion.attribution.attributed,
|
|
233
|
+
reason: conversion.attribution.attributed ? undefined : conversion.attribution.reason,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
} catch (cause) {
|
|
237
|
+
logConversionDiag({
|
|
238
|
+
tenantId: input.tenantId,
|
|
239
|
+
env: input.env,
|
|
240
|
+
error: cause instanceof Error ? cause.name : "unknown",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
172
244
|
// D11: HIGH-VOLUME machine action (the tot-foxycart forwarder, HMAC-authed) → analytics
|
|
173
245
|
// only, never timelined. Coarse dimensions only; customer/order ids never stored.
|
|
174
246
|
recordActivity({
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/commerce/checkout-handoff — the checkout-correlation MINT seam
|
|
3
|
+
* (ext-commerce-10).
|
|
4
|
+
*
|
|
5
|
+
* The shopper's analytics consent is a CLIENT-side decision (CMP / native banner),
|
|
6
|
+
* but the correlation token is minted under the SERVER-only signing secret. So at the
|
|
7
|
+
* handoff the client posts its LIVE consent snapshot and the server mints the token,
|
|
8
|
+
* returning ONLY the opaque passthrough cart field to attach to the managed-cart form.
|
|
9
|
+
* The signing secret never leaves the server; the token carries no PII and no secret.
|
|
10
|
+
*
|
|
11
|
+
* Server-owned by construction:
|
|
12
|
+
* - The tenant and the {@link IntegrationEnvironment} stage come from `context.locals`
|
|
13
|
+
* (the renderer-owned `BrowserEnvironment`), NEVER from the request body — a client
|
|
14
|
+
* cannot mint a cross-tenant or cross-stage token.
|
|
15
|
+
* - The consent snapshot is parsed FAIL-CLOSED (`parseConsentSnapshot`): an absent,
|
|
16
|
+
* forged, or tampered snapshot collapses to default-deny → unattributed.
|
|
17
|
+
* - No store on this tenant, consent withheld, or no signing key → an explicit
|
|
18
|
+
* unattributed response; the handoff proceeds, checkout is never blocked.
|
|
19
|
+
*/
|
|
20
|
+
import type { APIContext } from "astro";
|
|
21
|
+
import { parseConsentSnapshot, defaultConsentSnapshot } from "@tot/public-runtime";
|
|
22
|
+
import { resolveCheckoutHandoffCorrelation } from "@/lib/commerce/checkoutHandoffCorrelation";
|
|
23
|
+
|
|
24
|
+
export const prerender = false;
|
|
25
|
+
|
|
26
|
+
function json(body: unknown, status = 200): Response {
|
|
27
|
+
return new Response(JSON.stringify(body), {
|
|
28
|
+
status,
|
|
29
|
+
headers: {
|
|
30
|
+
"content-type": "application/json; charset=utf-8",
|
|
31
|
+
"cache-control": "no-store",
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface HandoffBody {
|
|
37
|
+
/** The shopper's serialized consent snapshot (the `__TOT_INTEGRATION_CONSENT__` global). */
|
|
38
|
+
consent?: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
42
|
+
const { tenant, browserEnvironment, commerce } = context.locals;
|
|
43
|
+
|
|
44
|
+
// No managed checkout on this tenant → nothing to attribute.
|
|
45
|
+
if (!commerce) {
|
|
46
|
+
return json({ attributed: false, reason: "no-store" });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let body: HandoffBody;
|
|
50
|
+
try {
|
|
51
|
+
body = (await context.request.json()) as HandoffBody;
|
|
52
|
+
} catch {
|
|
53
|
+
body = {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Fail-closed: an absent/forged/tampered snapshot is default-deny (essential only).
|
|
57
|
+
const consent = parseConsentSnapshot(body.consent) ?? defaultConsentSnapshot();
|
|
58
|
+
|
|
59
|
+
const result = await resolveCheckoutHandoffCorrelation({
|
|
60
|
+
tenant: tenant.tenant_id,
|
|
61
|
+
// Env-consistency: the request's own resolved stage, never a client value.
|
|
62
|
+
environment: browserEnvironment.environment,
|
|
63
|
+
consent,
|
|
64
|
+
// Opaque per-handoff logical id the order join threads back to.
|
|
65
|
+
eventId: crypto.randomUUID(),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (result.attributed) {
|
|
69
|
+
// Return ONLY the opaque passthrough field; the signing secret never leaves here.
|
|
70
|
+
return json({ attributed: true, cartField: result.cartField });
|
|
71
|
+
}
|
|
72
|
+
return json({ attributed: false, reason: result.reason });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function GET(): Promise<Response> {
|
|
76
|
+
return json({ error: "method_not_allowed" }, 405);
|
|
77
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/compliance/tamper — the server receiver for protected-surface tamper
|
|
3
|
+
* beacons (D-R7, `integration-protected-tamper-recovery`).
|
|
4
|
+
*
|
|
5
|
+
* The browser sentinel restores safe state IN the document and posts a
|
|
6
|
+
* `TamperEvidenceBeacon` here. This route validates it fail-closed, records the
|
|
7
|
+
* durable evidence + operator alert, and — when the beacon is attributable to a
|
|
8
|
+
* single active install — SUSPENDS that install. It is a DETECTIVE control: the
|
|
9
|
+
* suspension is after-the-fact recovery, never prevention.
|
|
10
|
+
*
|
|
11
|
+
* TENANT SCOPE is server-owned: the install lookup and suspend are ALWAYS keyed on
|
|
12
|
+
* `locals.tenant` (middleware-resolved from the request host), never on the
|
|
13
|
+
* body-supplied `tenant` — a page cannot ask to suspend another tenant's app. The
|
|
14
|
+
* beacon body carries no secret (the contract rejects one), and the response is a
|
|
15
|
+
* write-path ack the emitter ignores, so a failed post never breaks the page.
|
|
16
|
+
*/
|
|
17
|
+
import type { APIContext } from "astro";
|
|
18
|
+
import { getRegistryService, json } from "@/lib/apps/adminService.js";
|
|
19
|
+
import type { AppInstall } from "@/lib/apps/registryService.js";
|
|
20
|
+
import { RegistryError } from "@/lib/apps/registryService.js";
|
|
21
|
+
import { planTamperRecovery, type AttributableInstall } from "@tot/public-runtime";
|
|
22
|
+
|
|
23
|
+
export const prerender = false;
|
|
24
|
+
|
|
25
|
+
const SYSTEM_ACTOR = "system:tamper-sentinel";
|
|
26
|
+
|
|
27
|
+
function toAttributable(install: AppInstall): AttributableInstall {
|
|
28
|
+
return {
|
|
29
|
+
installId: install.installId,
|
|
30
|
+
appId: install.appId,
|
|
31
|
+
appVersion: install.appVersion,
|
|
32
|
+
status: install.status,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
37
|
+
const tenantId = context.locals.tenant?.tenant_id;
|
|
38
|
+
// No resolved tenant ⇒ nothing third-party could be scoped to; accept-and-drop so
|
|
39
|
+
// the emitter never errors or retries.
|
|
40
|
+
if (!tenantId) return json({ ok: true, accepted: false }, 202);
|
|
41
|
+
|
|
42
|
+
let body: unknown;
|
|
43
|
+
try {
|
|
44
|
+
body = await context.request.json();
|
|
45
|
+
} catch {
|
|
46
|
+
return json({ ok: false, error: "invalid JSON body" }, 400);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Resolve the tenant's installs (empty when the registry binding isn't configured
|
|
50
|
+
// in this runtime — the beacon is still validated + recorded, just not actioned).
|
|
51
|
+
const registry = await getRegistryService();
|
|
52
|
+
const installs: AttributableInstall[] =
|
|
53
|
+
registry instanceof Response
|
|
54
|
+
? []
|
|
55
|
+
: (await registry.listByTenant(tenantId)).map(toAttributable);
|
|
56
|
+
|
|
57
|
+
const plan = planTamperRecovery(body, installs, {
|
|
58
|
+
recordId: crypto.randomUUID(),
|
|
59
|
+
receivedAt: new Date().toISOString(),
|
|
60
|
+
});
|
|
61
|
+
if (!plan.ok) return json({ ok: false, error: "invalid tamper beacon" }, 400);
|
|
62
|
+
|
|
63
|
+
const { record, suspend } = plan.plan;
|
|
64
|
+
// Durable, secret-free evidence line for operations. The beacon itself is the record.
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.warn(
|
|
67
|
+
`[tamper] tenant=${tenantId} record=${record.recordId} surface=${record.beacon.surface} kind=${record.beacon.kind} recovered=${record.beacon.recovered} app=${record.beacon.attribution?.appId ?? "unattributable"}`,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
let suspended = false;
|
|
71
|
+
if (suspend && !(registry instanceof Response)) {
|
|
72
|
+
try {
|
|
73
|
+
await registry.suspend(suspend.installId, SYSTEM_ACTOR);
|
|
74
|
+
suspended = true;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
// An already-suspended/uninstalled install (invalid_state) is a benign
|
|
77
|
+
// no-op under rapid re-mutation; anything else is swallowed so the write
|
|
78
|
+
// path never errors the emitter. The audit row from a successful suspend is
|
|
79
|
+
// the durable trail.
|
|
80
|
+
if (!(err instanceof RegistryError)) {
|
|
81
|
+
// eslint-disable-next-line no-console
|
|
82
|
+
console.error(`[tamper] suspend failed for ${suspend.installId}`, err);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return json({ ok: true, accepted: true, recovered: record.beacon.recovered, suspended }, 202);
|
|
88
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/** Published managed analytics v1 RPC transport: basePath + client method name. */
|
|
2
|
+
import type { APIContext } from "astro";
|
|
3
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
4
|
+
import { getManagedAnalyticsService, json, requireManageRole } from "@/lib/apps/adminService.js";
|
|
5
|
+
import {
|
|
6
|
+
validateCreateIntegrationInstallRequest, validateCreateMigrationDraftRequest,
|
|
7
|
+
validateDeleteIntegrationInstallRequest, validateDeleteMigrationDraftRequest,
|
|
8
|
+
validateGetAdapterDefinitionRequest, validateGetIntegrationInstallRequest,
|
|
9
|
+
validateGetIntegrationRevisionRequest, validateGetMigrationDraftRequest,
|
|
10
|
+
validateListAdapterDefinitionsRequest, validateListIntegrationInstallsRequest,
|
|
11
|
+
validateListIntegrationRevisionsRequest, validateReviseIntegrationInstallRequest,
|
|
12
|
+
validateSetIntegrationInstallStatusRequest, validateUpdateMigrationDraftRequest,
|
|
13
|
+
type CreateIntegrationInstallRequest, type CreateMigrationDraftRequest,
|
|
14
|
+
type DeleteIntegrationInstallRequest, type DeleteMigrationDraftRequest,
|
|
15
|
+
type GetAdapterDefinitionRequest, type GetIntegrationInstallRequest,
|
|
16
|
+
type GetIntegrationRevisionRequest, type GetMigrationDraftRequest,
|
|
17
|
+
type ListAdapterDefinitionsRequest, type ListIntegrationInstallsRequest,
|
|
18
|
+
type ListIntegrationRevisionsRequest, type ReviseIntegrationInstallRequest,
|
|
19
|
+
type SetIntegrationInstallStatusRequest, type UpdateMigrationDraftRequest,
|
|
20
|
+
} from "@tot/public-runtime";
|
|
21
|
+
import { MANAGED_ANALYTICS_READ_OPERATIONS, managedAnalyticsOpaqueId, sanitizeManagedAnalyticsOutbound, type ManagedAnalyticsOperation } from "@/lib/apps/managedAnalyticsService.js";
|
|
22
|
+
|
|
23
|
+
export const prerender = false;
|
|
24
|
+
type Validator = (input: unknown) => { ok: true; value: unknown } | { ok: false; violations: Array<{ path: string; code: string; message: string }> };
|
|
25
|
+
|
|
26
|
+
// Null-prototype + own-property lookup: `constructor`/prototype names can never
|
|
27
|
+
// enter dispatch, even if a runtime supplies them as a dynamic path segment.
|
|
28
|
+
const VALIDATORS: Readonly<Record<string, Validator>> = Object.assign(Object.create(null) as Record<string, Validator>, {
|
|
29
|
+
listAdapterDefinitions: validateListAdapterDefinitionsRequest,
|
|
30
|
+
getAdapterDefinition: validateGetAdapterDefinitionRequest,
|
|
31
|
+
listIntegrationInstalls: validateListIntegrationInstallsRequest,
|
|
32
|
+
getIntegrationInstall: validateGetIntegrationInstallRequest,
|
|
33
|
+
listIntegrationRevisions: validateListIntegrationRevisionsRequest,
|
|
34
|
+
getIntegrationRevision: validateGetIntegrationRevisionRequest,
|
|
35
|
+
getMigrationDraft: validateGetMigrationDraftRequest,
|
|
36
|
+
createMigrationDraft: validateCreateMigrationDraftRequest,
|
|
37
|
+
updateMigrationDraft: validateUpdateMigrationDraftRequest,
|
|
38
|
+
deleteMigrationDraft: validateDeleteMigrationDraftRequest,
|
|
39
|
+
createIntegrationInstall: validateCreateIntegrationInstallRequest,
|
|
40
|
+
reviseIntegrationInstall: validateReviseIntegrationInstallRequest,
|
|
41
|
+
setIntegrationInstallStatus: validateSetIntegrationInstallStatusRequest,
|
|
42
|
+
deleteIntegrationInstall: validateDeleteIntegrationInstallRequest,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const STATUS: Record<string, number> = {
|
|
46
|
+
invalid_request: 400, unauthorized: 401, forbidden: 403, not_found: 404,
|
|
47
|
+
validation_failed: 422, unsupported_version: 400, stale_revision: 409,
|
|
48
|
+
idempotency_conflict: 409, adapter_deprecated: 409, conflict: 409,
|
|
49
|
+
internal_error: 500,
|
|
50
|
+
};
|
|
51
|
+
function response(body: unknown): Response {
|
|
52
|
+
const code = body && typeof body === "object" && "error" in body
|
|
53
|
+
? (body as { error?: { code?: string } }).error?.code : undefined;
|
|
54
|
+
return json(body, code ? STATUS[code] ?? 500 : 200);
|
|
55
|
+
}
|
|
56
|
+
function routeError(code: string, message: string, details?: Array<{ path: string; code: string; message: string }>): Response {
|
|
57
|
+
return response({ apiVersion: "v1", requestId: managedAnalyticsOpaqueId("req"), error: { code, message, ...(details?.length ? { details } : {}) } });
|
|
58
|
+
}
|
|
59
|
+
function operationResponse(operation: ManagedAnalyticsOperation, body: unknown): Response {
|
|
60
|
+
const safe = sanitizeManagedAnalyticsOutbound(operation, body);
|
|
61
|
+
return safe === null ? routeError("internal_error", "The integration service could not complete the request.") : response(safe);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
65
|
+
try {
|
|
66
|
+
const operation = context.params.operation;
|
|
67
|
+
if (!operation || !Object.prototype.hasOwnProperty.call(VALIDATORS, operation)) {
|
|
68
|
+
return routeError("invalid_request", "Unknown managed analytics v1 operation.");
|
|
69
|
+
}
|
|
70
|
+
const op = operation as ManagedAnalyticsOperation;
|
|
71
|
+
const url = new URL(context.request.url);
|
|
72
|
+
const auth = await resolveOperatorSession(context, url.searchParams.get("tenant"));
|
|
73
|
+
if (!auth.ok) return routeError(auth.status === 401 ? "unauthorized" : "forbidden", "The request is not authorized.");
|
|
74
|
+
if (!MANAGED_ANALYTICS_READ_OPERATIONS.has(op) && requireManageRole(auth)) {
|
|
75
|
+
return routeError("forbidden", "The request is not authorized.");
|
|
76
|
+
}
|
|
77
|
+
let raw: unknown;
|
|
78
|
+
try { raw = await context.request.json(); }
|
|
79
|
+
catch { return routeError("invalid_request", "The request body must be valid JSON."); }
|
|
80
|
+
const parsed = VALIDATORS[operation]!(raw);
|
|
81
|
+
if (!parsed.ok) {
|
|
82
|
+
const unsupportedVersion = parsed.violations.some(({ path, code }) => path === "apiVersion" && code === "unsupported_value");
|
|
83
|
+
return routeError(unsupportedVersion ? "unsupported_version" : "invalid_request", unsupportedVersion ? "The API version is not supported." : "The request failed validation.", parsed.violations);
|
|
84
|
+
}
|
|
85
|
+
const service = await getManagedAnalyticsService();
|
|
86
|
+
if (service instanceof Response) return routeError("internal_error", "The integration service is unavailable.");
|
|
87
|
+
const ctx = { tenantId: auth.tenantId, actor: auth.actor };
|
|
88
|
+
const request = parsed.value;
|
|
89
|
+
switch (op) {
|
|
90
|
+
case "listAdapterDefinitions": return operationResponse(op, service.listAdapterDefinitions(request as ListAdapterDefinitionsRequest));
|
|
91
|
+
case "getAdapterDefinition": return operationResponse(op, service.getAdapterDefinition((request as GetAdapterDefinitionRequest).query.adapterId));
|
|
92
|
+
case "listIntegrationInstalls": return operationResponse(op, await service.listIntegrationInstalls(ctx, request as ListIntegrationInstallsRequest));
|
|
93
|
+
case "getIntegrationInstall": return operationResponse(op, await service.getIntegrationInstall(ctx, (request as GetIntegrationInstallRequest).query.installId));
|
|
94
|
+
case "listIntegrationRevisions": { const value = request as ListIntegrationRevisionsRequest; return operationResponse(op, await service.listIntegrationRevisions(ctx, value.query.installId, value.query)); }
|
|
95
|
+
case "getIntegrationRevision": { const value = request as GetIntegrationRevisionRequest; return operationResponse(op, await service.getIntegrationRevision(ctx, value.query.installId, value.query.revisionId)); }
|
|
96
|
+
case "getMigrationDraft": return operationResponse(op, await service.getMigrationDraft(ctx, (request as GetMigrationDraftRequest).query.draftId));
|
|
97
|
+
case "createMigrationDraft": return operationResponse(op, await service.createMigrationDraft(ctx, request as CreateMigrationDraftRequest));
|
|
98
|
+
case "updateMigrationDraft": return operationResponse(op, await service.updateMigrationDraft(ctx, request as UpdateMigrationDraftRequest));
|
|
99
|
+
case "deleteMigrationDraft": return operationResponse(op, await service.deleteMigrationDraft(ctx, request as DeleteMigrationDraftRequest));
|
|
100
|
+
case "createIntegrationInstall": return operationResponse(op, await service.createIntegrationInstall(ctx, request as CreateIntegrationInstallRequest));
|
|
101
|
+
case "reviseIntegrationInstall": return operationResponse(op, await service.reviseIntegrationInstall(ctx, request as ReviseIntegrationInstallRequest));
|
|
102
|
+
case "setIntegrationInstallStatus": return operationResponse(op, await service.setIntegrationInstallStatus(ctx, request as SetIntegrationInstallStatusRequest));
|
|
103
|
+
case "deleteIntegrationInstall": return operationResponse(op, await service.deleteIntegrationInstall(ctx, request as DeleteIntegrationInstallRequest));
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
return routeError("internal_error", "The integration service could not complete the request.");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -60,8 +60,7 @@ export const POST: APIRoute = async (context) => {
|
|
|
60
60
|
const kv = tenantId ? await readKv("TENANT_CACHE") : undefined;
|
|
61
61
|
if (tenantId && kv) {
|
|
62
62
|
const cfContext =
|
|
63
|
-
(context.locals as {
|
|
64
|
-
null;
|
|
63
|
+
(context.locals as { cfContext?: CfExecutionContextLike })?.cfContext ?? null;
|
|
65
64
|
runAfterResponse(recordBlogView(kv, tenantId, sample.path), cfContext);
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -11,11 +11,11 @@ import { buildAlternates } from "@/lib/seo/alternates.js";
|
|
|
11
11
|
import { languageLabel } from "@/lib/i18n.js";
|
|
12
12
|
import { breadcrumbLd, blogPostingLd } from "@/lib/jsonld.js";
|
|
13
13
|
|
|
14
|
-
const { tenant, canonicalBase, basePath } = Astro.locals;
|
|
14
|
+
const { tenant, canonicalBase, basePath, content } = Astro.locals;
|
|
15
15
|
const slug = Astro.params.slug ?? "";
|
|
16
|
-
const post = await getBlogPost(tenant.tenant_id, slug);
|
|
16
|
+
const post = await getBlogPost(tenant.tenant_id, slug, content);
|
|
17
17
|
if (!post) {
|
|
18
|
-
const redirect = await resolveBlogRedirect(tenant.tenant_id, slug);
|
|
18
|
+
const redirect = await resolveBlogRedirect(tenant.tenant_id, slug, content);
|
|
19
19
|
if (redirect) return Astro.redirect(`${basePath}/blog/${redirect}/`, 301);
|
|
20
20
|
return new Response(null, { status: 404 });
|
|
21
21
|
}
|
|
@@ -38,7 +38,7 @@ const canonical = new URL(`/blog/${post.slug}/`, canonicalBase + "/").toString()
|
|
|
38
38
|
// (the invariant `blog/author/[author].astro` documents). Same slug + same source
|
|
39
39
|
// the archive itself filters on, so the two can't disagree.
|
|
40
40
|
const authorArchiveSlug = authorSlug(post.author);
|
|
41
|
-
const authorHasArchive = (await listBlogPosts(tenant.tenant_id)).some(
|
|
41
|
+
const authorHasArchive = (await listBlogPosts(tenant.tenant_id, content)).some(
|
|
42
42
|
(p) => authorSlug(p.author) === authorArchiveSlug,
|
|
43
43
|
);
|
|
44
44
|
const authorUrl = authorHasArchive
|
|
@@ -63,7 +63,7 @@ const authorHref = authorHasArchive
|
|
|
63
63
|
// I2 — hreflang alternates + per-variant <html lang>. Singleton ⇒ [] alternates
|
|
64
64
|
// and no `lang` override, so a non-translated post is byte-identical to today.
|
|
65
65
|
const translations = post.translationGroupId
|
|
66
|
-
? await getTranslations(tenant.tenant_id, "blog", post.slug)
|
|
66
|
+
? await getTranslations(tenant.tenant_id, "blog", post.slug, { content })
|
|
67
67
|
: [];
|
|
68
68
|
const alternates = buildAlternates(translations, (s) =>
|
|
69
69
|
new URL(`/blog/${s}/`, canonicalBase + "/").toString(),
|
|
@@ -8,15 +8,15 @@ import { breadcrumbLd } from "@/lib/jsonld.js";
|
|
|
8
8
|
// Author archive for the `/blog` collection. Lists exactly the posts whose
|
|
9
9
|
// bylines link here (same source as /blog/index.astro + /blog/[slug].astro),
|
|
10
10
|
// so a byline never points at a page missing its post.
|
|
11
|
-
const { tenant, basePath, canonicalBase } = Astro.locals;
|
|
11
|
+
const { tenant, basePath, canonicalBase, content } = Astro.locals;
|
|
12
12
|
const slug = Astro.params.author ?? "";
|
|
13
13
|
|
|
14
|
-
const allPosts = await listBlogPosts(tenant.tenant_id);
|
|
14
|
+
const allPosts = await listBlogPosts(tenant.tenant_id, content);
|
|
15
15
|
const posts = allPosts.filter((post) => authorSlug(post.author) === slug);
|
|
16
16
|
if (posts.length === 0) return new Response(null, { status: 404 });
|
|
17
17
|
|
|
18
18
|
const authorName = posts[0]!.author;
|
|
19
|
-
const bio = (await getAuthors(tenant.tenant_id, "blog"))[slug];
|
|
19
|
+
const bio = (await getAuthors(tenant.tenant_id, "blog", content))[slug];
|
|
20
20
|
|
|
21
21
|
const href = (path: string) => `${basePath}${path}` || "/";
|
|
22
22
|
const canonical = new URL(`/blog/author/${slug}/`, canonicalBase + "/").toString();
|
|
@@ -5,12 +5,12 @@ import { listBlogPosts } from "@/lib/blog/provider.js";
|
|
|
5
5
|
import { collectPostsByTerm } from "@/lib/blog/collection.js";
|
|
6
6
|
import { breadcrumbLd } from "@/lib/jsonld.js";
|
|
7
7
|
|
|
8
|
-
const { tenant, basePath, canonicalBase } = Astro.locals;
|
|
8
|
+
const { tenant, basePath, canonicalBase, content } = Astro.locals;
|
|
9
9
|
const param = Astro.params.category ?? "";
|
|
10
10
|
|
|
11
11
|
// Same source as the /blog index (the collection's curated list), filtered to
|
|
12
12
|
// one category — so a post can never appear here that isn't already live on /blog.
|
|
13
|
-
const all = await listBlogPosts(tenant.tenant_id);
|
|
13
|
+
const all = await listBlogPosts(tenant.tenant_id, content);
|
|
14
14
|
const match = collectPostsByTerm(all, "categories", param);
|
|
15
15
|
if (!match) return new Response(null, { status: 404 });
|
|
16
16
|
const { label, posts } = match;
|
|
@@ -3,8 +3,8 @@ import Layout from "@/layouts/Layout.astro";
|
|
|
3
3
|
import PostAccessBadge from "@/components/blog/PostAccessBadge.astro";
|
|
4
4
|
import { listBlogPosts } from "@/lib/blog/provider.js";
|
|
5
5
|
|
|
6
|
-
const { tenant, basePath, canonicalBase } = Astro.locals;
|
|
7
|
-
const posts = await listBlogPosts(tenant.tenant_id);
|
|
6
|
+
const { tenant, basePath, canonicalBase, content } = Astro.locals;
|
|
7
|
+
const posts = await listBlogPosts(tenant.tenant_id, content);
|
|
8
8
|
if (posts.length === 0) return new Response(null, { status: 404 });
|
|
9
9
|
|
|
10
10
|
const href = (path: string) => `${basePath}${path}` || "/";
|
|
@@ -5,10 +5,10 @@ import { listBlogPosts } from "@/lib/blog/provider.js";
|
|
|
5
5
|
import { collectPostsByTerm } from "@/lib/blog/collection.js";
|
|
6
6
|
import { breadcrumbLd } from "@/lib/jsonld.js";
|
|
7
7
|
|
|
8
|
-
const { tenant, basePath, canonicalBase } = Astro.locals;
|
|
8
|
+
const { tenant, basePath, canonicalBase, content } = Astro.locals;
|
|
9
9
|
const param = Astro.params.tag ?? "";
|
|
10
10
|
|
|
11
|
-
const all = await listBlogPosts(tenant.tenant_id);
|
|
11
|
+
const all = await listBlogPosts(tenant.tenant_id, content);
|
|
12
12
|
const match = collectPostsByTerm(all, "tags", param);
|
|
13
13
|
if (!match) return new Response(null, { status: 404 });
|
|
14
14
|
const { label, posts } = match;
|