@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,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, transport-free builder for the admin "Migrate from GTM" report.
|
|
3
|
+
*
|
|
4
|
+
* Runs the SAFE `@tot/public-runtime` pipeline (`parseGtmExport` →
|
|
5
|
+
* `buildMigrationGraph` → `classifyMigration`) over an uploaded, untrusted GTM
|
|
6
|
+
* container export and turns the result into a REDACTED, inert report object.
|
|
7
|
+
*
|
|
8
|
+
* READ-ONLY / NON-EXECUTING CONTRACT (a reviewer WILL reject violations):
|
|
9
|
+
* - Nothing here executes, evaluates, interprets, or renders any part of the
|
|
10
|
+
* export — it only reads the inert typed model the parser produced.
|
|
11
|
+
* - It emits NO markup, NO script, NO network effect, NO event listener, and
|
|
12
|
+
* has NO live authority. It builds a plain DATA object and returns it.
|
|
13
|
+
* - The full inert `raw` field bag of every record is DROPPED — the report
|
|
14
|
+
* carries only accounting, dispositions, a redacted node/edge graph,
|
|
15
|
+
* findings, and recommendations. Code-bearing GTM fields never leave here.
|
|
16
|
+
* - Every emitted string field is run through {@link redactText}, so a
|
|
17
|
+
* secret-shaped value that rode in on a name/id/reference is masked.
|
|
18
|
+
* - Foreign / other-tenant ids in the upload are reflected back verbatim (or
|
|
19
|
+
* redacted if secret-shaped) and are never cross-referenced against this
|
|
20
|
+
* tenant — the report discloses nothing about the requesting tenant.
|
|
21
|
+
*/
|
|
22
|
+
import {
|
|
23
|
+
parseGtmExport,
|
|
24
|
+
buildMigrationGraph,
|
|
25
|
+
classifyMigration,
|
|
26
|
+
REASON_CATALOG,
|
|
27
|
+
type GtmContainer,
|
|
28
|
+
type GtmParseAccounting,
|
|
29
|
+
type GtmViolation,
|
|
30
|
+
type MigrationGraph,
|
|
31
|
+
type GtmObjectKind,
|
|
32
|
+
type GtmEdgeKind,
|
|
33
|
+
type DispositionReport,
|
|
34
|
+
type DispositionClass,
|
|
35
|
+
type ObjectDisposition,
|
|
36
|
+
type DanglingDisposition,
|
|
37
|
+
type ReasonCode,
|
|
38
|
+
} from "@tot/public-runtime";
|
|
39
|
+
|
|
40
|
+
/** Report schema version — bumped when the emitted shape changes. */
|
|
41
|
+
export const GTM_MIGRATION_REPORT_VERSION = "1";
|
|
42
|
+
|
|
43
|
+
// ── Redaction ────────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
/** Recognized secret prefixes (Google API key, Stripe, GitHub PAT, Slack, JWT). */
|
|
46
|
+
const SECRET_PREFIX_RE =
|
|
47
|
+
/^(AIza[0-9A-Za-z_-]{10,}|sk_[A-Za-z0-9]{16,}|pk_[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|eyJ[A-Za-z0-9_-]{10,})$/;
|
|
48
|
+
|
|
49
|
+
/** A single token looks secret-shaped: a known prefix, or long + high-entropy. */
|
|
50
|
+
function looksSecret(token: string): boolean {
|
|
51
|
+
if (SECRET_PREFIX_RE.test(token)) return true;
|
|
52
|
+
// Long opaque token with both letters and digits and only token-safe chars —
|
|
53
|
+
// a measurement id like `G-ABC123` (short) never trips this; an API key does.
|
|
54
|
+
return (
|
|
55
|
+
token.length >= 24 &&
|
|
56
|
+
/[0-9]/.test(token) &&
|
|
57
|
+
/[A-Za-z]/.test(token) &&
|
|
58
|
+
/^[A-Za-z0-9_+/=.-]+$/.test(token)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Candidate opaque tokens inside free text (min length keeps human words out). */
|
|
63
|
+
const TOKEN_RE = /[A-Za-z0-9_+/=.-]{12,}/g;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Mask every secret-shaped token inside a string, leaving ordinary human text
|
|
67
|
+
* (names, labels, short ids, sentences) untouched. `null` passes through.
|
|
68
|
+
*/
|
|
69
|
+
export function redactText(value: string | null): string | null {
|
|
70
|
+
if (value == null) return value;
|
|
71
|
+
return value.replace(TOKEN_RE, (m) => (looksSecret(m) ? "[redacted]" : m));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Emitted shape ──────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
export interface GtmReportNode {
|
|
77
|
+
readonly key: string;
|
|
78
|
+
readonly kind: GtmObjectKind;
|
|
79
|
+
readonly id: string | null;
|
|
80
|
+
readonly name: string | null;
|
|
81
|
+
readonly type: string | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface GtmReportEdge {
|
|
85
|
+
readonly from: string;
|
|
86
|
+
readonly to: string;
|
|
87
|
+
readonly kind: GtmEdgeKind;
|
|
88
|
+
readonly via: string | null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface GtmReportDangling {
|
|
92
|
+
readonly from: string;
|
|
93
|
+
readonly kind: string;
|
|
94
|
+
readonly ref: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** One classified object, redacted (no `raw` bag). */
|
|
98
|
+
export interface GtmReportObject {
|
|
99
|
+
readonly nodeKey: string;
|
|
100
|
+
readonly kind: GtmObjectKind;
|
|
101
|
+
readonly id: string | null;
|
|
102
|
+
readonly name: string | null;
|
|
103
|
+
readonly type: string | null;
|
|
104
|
+
readonly disposition: DispositionClass;
|
|
105
|
+
readonly reasonCode: string;
|
|
106
|
+
readonly explanation: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** One classified dangling reference, redacted. */
|
|
110
|
+
export interface GtmReportDanglingDisposition {
|
|
111
|
+
readonly from: string;
|
|
112
|
+
readonly refKind: string;
|
|
113
|
+
readonly ref: string;
|
|
114
|
+
readonly disposition: DispositionClass;
|
|
115
|
+
readonly reasonCode: string;
|
|
116
|
+
readonly explanation: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface GtmRecommendation {
|
|
120
|
+
readonly reasonCode: string;
|
|
121
|
+
readonly disposition: DispositionClass;
|
|
122
|
+
readonly count: number;
|
|
123
|
+
readonly explanation: string;
|
|
124
|
+
readonly nextStep: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface GtmMigrationReport {
|
|
128
|
+
readonly provenance: {
|
|
129
|
+
readonly correlationId: string;
|
|
130
|
+
readonly generatedAt: string;
|
|
131
|
+
readonly reportVersion: string;
|
|
132
|
+
/** The REQUESTING tenant (this tenant's own id) — never a lookup key. */
|
|
133
|
+
readonly tenantId: string;
|
|
134
|
+
readonly exportFormatVersion: number;
|
|
135
|
+
readonly container: {
|
|
136
|
+
readonly containerId: string | null;
|
|
137
|
+
readonly publicId: string | null;
|
|
138
|
+
readonly path: string | null;
|
|
139
|
+
readonly name: string | null;
|
|
140
|
+
};
|
|
141
|
+
readonly redacted: true;
|
|
142
|
+
};
|
|
143
|
+
readonly counts: {
|
|
144
|
+
readonly parse: {
|
|
145
|
+
readonly counts: Record<string, number>;
|
|
146
|
+
readonly unknownTopLevelKeys: string[];
|
|
147
|
+
readonly unknownContainerKeys: string[];
|
|
148
|
+
readonly truncatedStrings: number;
|
|
149
|
+
readonly quarantinedKeys: number;
|
|
150
|
+
readonly totalNodes: number;
|
|
151
|
+
};
|
|
152
|
+
readonly graph: {
|
|
153
|
+
readonly nodeCount: number;
|
|
154
|
+
readonly perKind: Record<GtmObjectKind, number>;
|
|
155
|
+
readonly edgeCount: number;
|
|
156
|
+
readonly perEdgeKind: Record<GtmEdgeKind, number>;
|
|
157
|
+
readonly danglingCount: number;
|
|
158
|
+
readonly builtInVariablesReferenced: string[];
|
|
159
|
+
readonly tokensCapped: boolean;
|
|
160
|
+
};
|
|
161
|
+
readonly accounting: DispositionReport["accounting"];
|
|
162
|
+
};
|
|
163
|
+
readonly dispositionSummary: DispositionReport["summary"];
|
|
164
|
+
readonly dispositions: GtmReportObject[];
|
|
165
|
+
readonly dangling: GtmReportDanglingDisposition[];
|
|
166
|
+
readonly graph: {
|
|
167
|
+
readonly nodes: GtmReportNode[];
|
|
168
|
+
readonly edges: GtmReportEdge[];
|
|
169
|
+
readonly danglingReferences: GtmReportDangling[];
|
|
170
|
+
};
|
|
171
|
+
readonly findings: {
|
|
172
|
+
readonly dangling: GtmReportDanglingDisposition[];
|
|
173
|
+
readonly unsupported: GtmReportObject[];
|
|
174
|
+
};
|
|
175
|
+
readonly recommendations: GtmRecommendation[];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ── Builder ──────────────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
/** Human next-step per disposition class — the actionable half of a recommendation. */
|
|
181
|
+
const NEXT_STEP_BY_CLASS: Record<DispositionClass, string> = {
|
|
182
|
+
"automatically-portable": "Moves automatically during migration; no action needed.",
|
|
183
|
+
"portable-with-review": "Review and confirm the native mapping before migrating.",
|
|
184
|
+
"requires-redesign": "Rebuild this capability with a native design; there is no direct mapping.",
|
|
185
|
+
"unsupported-by-design": "Not supported; rebuild the intent as a native integration, or drop it.",
|
|
186
|
+
retire: "Retire it; there is no migration target.",
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
function redactObject(o: ObjectDisposition): GtmReportObject {
|
|
190
|
+
return {
|
|
191
|
+
nodeKey: o.nodeKey,
|
|
192
|
+
kind: o.kind,
|
|
193
|
+
id: redactText(o.id),
|
|
194
|
+
name: redactText(o.name),
|
|
195
|
+
type: o.type,
|
|
196
|
+
disposition: o.disposition,
|
|
197
|
+
reasonCode: o.reasonCode,
|
|
198
|
+
explanation: redactText(o.explanation) ?? "",
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function redactDangling(d: DanglingDisposition): GtmReportDanglingDisposition {
|
|
203
|
+
return {
|
|
204
|
+
from: d.from,
|
|
205
|
+
refKind: d.refKind,
|
|
206
|
+
ref: redactText(d.ref) ?? "",
|
|
207
|
+
disposition: d.disposition,
|
|
208
|
+
reasonCode: d.reasonCode,
|
|
209
|
+
explanation: redactText(d.explanation) ?? "",
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Aggregate recommendations from the catalog for every reason code that appears. */
|
|
214
|
+
function buildRecommendations(disposition: DispositionReport): GtmRecommendation[] {
|
|
215
|
+
const counts = new Map<string, number>();
|
|
216
|
+
for (const o of disposition.objects) counts.set(o.reasonCode, (counts.get(o.reasonCode) ?? 0) + 1);
|
|
217
|
+
for (const d of disposition.dangling) counts.set(d.reasonCode, (counts.get(d.reasonCode) ?? 0) + 1);
|
|
218
|
+
|
|
219
|
+
const recs: GtmRecommendation[] = [];
|
|
220
|
+
// Stable order: the catalog's own key order.
|
|
221
|
+
for (const reasonCode of Object.keys(REASON_CATALOG) as ReasonCode[]) {
|
|
222
|
+
const count = counts.get(reasonCode);
|
|
223
|
+
if (!count) continue;
|
|
224
|
+
const entry = REASON_CATALOG[reasonCode];
|
|
225
|
+
recs.push({
|
|
226
|
+
reasonCode,
|
|
227
|
+
disposition: entry.disposition,
|
|
228
|
+
count,
|
|
229
|
+
explanation: entry.explanation,
|
|
230
|
+
nextStep: NEXT_STEP_BY_CLASS[entry.disposition],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return recs;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Build the redacted report from an already-parsed container + its downstream
|
|
238
|
+
* graph and disposition report. Pure and deterministic.
|
|
239
|
+
*/
|
|
240
|
+
export function buildGtmMigrationReport(args: {
|
|
241
|
+
container: GtmContainer;
|
|
242
|
+
accounting: GtmParseAccounting;
|
|
243
|
+
graph: MigrationGraph;
|
|
244
|
+
disposition: DispositionReport;
|
|
245
|
+
correlationId: string;
|
|
246
|
+
generatedAt: string;
|
|
247
|
+
tenantId: string;
|
|
248
|
+
}): GtmMigrationReport {
|
|
249
|
+
const { container, accounting, graph, disposition } = args;
|
|
250
|
+
const cv = container.containerVersion;
|
|
251
|
+
|
|
252
|
+
const dispositions = disposition.objects.map(redactObject);
|
|
253
|
+
const dangling = disposition.dangling.map(redactDangling);
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
provenance: {
|
|
257
|
+
correlationId: args.correlationId,
|
|
258
|
+
generatedAt: args.generatedAt,
|
|
259
|
+
reportVersion: GTM_MIGRATION_REPORT_VERSION,
|
|
260
|
+
tenantId: args.tenantId,
|
|
261
|
+
exportFormatVersion: container.exportFormatVersion,
|
|
262
|
+
container: {
|
|
263
|
+
containerId: redactText(cv.containerId),
|
|
264
|
+
publicId: redactText(cv.publicId),
|
|
265
|
+
path: redactText(cv.path),
|
|
266
|
+
name: redactText(cv.name),
|
|
267
|
+
},
|
|
268
|
+
redacted: true,
|
|
269
|
+
},
|
|
270
|
+
counts: {
|
|
271
|
+
parse: {
|
|
272
|
+
counts: accounting.counts,
|
|
273
|
+
unknownTopLevelKeys: accounting.unknownTopLevelKeys,
|
|
274
|
+
unknownContainerKeys: accounting.unknownContainerKeys,
|
|
275
|
+
truncatedStrings: accounting.truncatedStrings,
|
|
276
|
+
quarantinedKeys: accounting.quarantinedKeys,
|
|
277
|
+
totalNodes: accounting.totalNodes,
|
|
278
|
+
},
|
|
279
|
+
graph: {
|
|
280
|
+
nodeCount: graph.accounting.nodeCount,
|
|
281
|
+
perKind: graph.accounting.perKind,
|
|
282
|
+
edgeCount: graph.accounting.edgeCount,
|
|
283
|
+
perEdgeKind: graph.accounting.perEdgeKind,
|
|
284
|
+
danglingCount: graph.accounting.danglingCount,
|
|
285
|
+
builtInVariablesReferenced: graph.accounting.builtInVariablesReferenced,
|
|
286
|
+
tokensCapped: graph.accounting.tokensCapped,
|
|
287
|
+
},
|
|
288
|
+
accounting: disposition.accounting,
|
|
289
|
+
},
|
|
290
|
+
dispositionSummary: disposition.summary,
|
|
291
|
+
dispositions,
|
|
292
|
+
dangling,
|
|
293
|
+
graph: {
|
|
294
|
+
// Node `record` (the full inert bag) is deliberately DROPPED here.
|
|
295
|
+
nodes: graph.nodes.map((n) => ({
|
|
296
|
+
key: n.key,
|
|
297
|
+
kind: n.kind,
|
|
298
|
+
id: redactText(n.id),
|
|
299
|
+
name: redactText(n.name),
|
|
300
|
+
type: n.type,
|
|
301
|
+
})),
|
|
302
|
+
edges: graph.edges.map((e) => ({
|
|
303
|
+
from: e.from,
|
|
304
|
+
to: e.to,
|
|
305
|
+
kind: e.kind,
|
|
306
|
+
via: redactText(e.via ?? null),
|
|
307
|
+
})),
|
|
308
|
+
danglingReferences: graph.danglingReferences.map((d) => ({
|
|
309
|
+
from: d.from,
|
|
310
|
+
kind: d.kind,
|
|
311
|
+
ref: redactText(d.ref) ?? "",
|
|
312
|
+
})),
|
|
313
|
+
},
|
|
314
|
+
findings: {
|
|
315
|
+
dangling,
|
|
316
|
+
unsupported: dispositions.filter((o) => o.disposition === "unsupported-by-design"),
|
|
317
|
+
},
|
|
318
|
+
recommendations: buildRecommendations(disposition),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ── Analyze entry point ────────────────────────────────────────────────────────
|
|
323
|
+
|
|
324
|
+
export interface GtmAnalyzeOk {
|
|
325
|
+
readonly ok: true;
|
|
326
|
+
readonly correlationId: string;
|
|
327
|
+
readonly report: GtmMigrationReport;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export interface GtmAnalyzeErr {
|
|
331
|
+
readonly ok: false;
|
|
332
|
+
readonly correlationId: string;
|
|
333
|
+
readonly error: string;
|
|
334
|
+
readonly violations: GtmViolation[];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Parse + graph + classify an uploaded export into a redacted report, or return
|
|
339
|
+
* the structured parse violations with a stable, safe error and the correlation
|
|
340
|
+
* id. NEVER throws on bad input, NEVER executes any part of the export.
|
|
341
|
+
*/
|
|
342
|
+
export function analyzeGtmExport(
|
|
343
|
+
input: string | Uint8Array,
|
|
344
|
+
opts: { tenantId: string; correlationId: string; generatedAt: string },
|
|
345
|
+
): GtmAnalyzeOk | GtmAnalyzeErr {
|
|
346
|
+
const parsed = parseGtmExport(input);
|
|
347
|
+
if (!parsed.ok) {
|
|
348
|
+
return {
|
|
349
|
+
ok: false,
|
|
350
|
+
correlationId: opts.correlationId,
|
|
351
|
+
error: "The uploaded file is not a valid GTM container export.",
|
|
352
|
+
violations: parsed.violations,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
const graph = buildMigrationGraph(parsed.container);
|
|
356
|
+
const disposition = classifyMigration(graph);
|
|
357
|
+
const report = buildGtmMigrationReport({
|
|
358
|
+
container: parsed.container,
|
|
359
|
+
accounting: parsed.accounting,
|
|
360
|
+
graph,
|
|
361
|
+
disposition,
|
|
362
|
+
correlationId: opts.correlationId,
|
|
363
|
+
generatedAt: opts.generatedAt,
|
|
364
|
+
tenantId: opts.tenantId,
|
|
365
|
+
});
|
|
366
|
+
return { ok: true, correlationId: opts.correlationId, report };
|
|
367
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Migrate from GTM" client controller — the hydration behind the analyze panel
|
|
3
|
+
* inside the `/admin#integrations` surface. It binds a file-input upload to the
|
|
4
|
+
* READ-ONLY analyze route and renders the redacted report, with no mock data:
|
|
5
|
+
*
|
|
6
|
+
* - POST /api/apps/admin/gtm-migration → the redacted report JSON
|
|
7
|
+
* (inventory counts, dependency graph, dispositions by class, unresolved
|
|
8
|
+
* findings, recommendations).
|
|
9
|
+
* - POST /api/apps/admin/gtm-migration?format=bundle → the same report as a
|
|
10
|
+
* downloadable JSON support bundle (Content-Disposition attachment).
|
|
11
|
+
*
|
|
12
|
+
* Analyzing is a pure diagnostic: uploading and rendering the report adds NO
|
|
13
|
+
* markup, CSP origin, network-to-third-parties, or event listener to the
|
|
14
|
+
* storefront runtime, and can never publish or activate anything. Every render
|
|
15
|
+
* helper below is a pure DOM builder, exported so the panel is unit-testable in
|
|
16
|
+
* a DOM environment with no network.
|
|
17
|
+
*/
|
|
18
|
+
import type {
|
|
19
|
+
GtmMigrationReport,
|
|
20
|
+
GtmReportObject,
|
|
21
|
+
GtmRecommendation,
|
|
22
|
+
} from "./gtmMigrationReport";
|
|
23
|
+
|
|
24
|
+
interface AnalyzeOkBody {
|
|
25
|
+
ok: true;
|
|
26
|
+
correlationId: string;
|
|
27
|
+
report: GtmMigrationReport;
|
|
28
|
+
}
|
|
29
|
+
interface AnalyzeErrBody {
|
|
30
|
+
ok?: false;
|
|
31
|
+
correlationId?: string;
|
|
32
|
+
error?: string;
|
|
33
|
+
violations?: Array<{ path?: string; code?: string; message?: string }>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ── Pure DOM helpers ───────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
function el(tag: string, className?: string, text?: string): HTMLElement {
|
|
39
|
+
const node = document.createElement(tag);
|
|
40
|
+
if (className) node.className = className;
|
|
41
|
+
if (text != null) node.textContent = text;
|
|
42
|
+
return node;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const CLASS_LABEL: Record<string, string> = {
|
|
46
|
+
"automatically-portable": "Automatically portable",
|
|
47
|
+
"portable-with-review": "Portable with review",
|
|
48
|
+
"requires-redesign": "Requires redesign",
|
|
49
|
+
"unsupported-by-design": "Unsupported by design",
|
|
50
|
+
retire: "Retire",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Inventory counts: one small stat card per object kind that has any objects. */
|
|
54
|
+
export function renderInventory(container: HTMLElement, report: GtmMigrationReport): void {
|
|
55
|
+
container.textContent = "";
|
|
56
|
+
const perKind = report.counts.graph.perKind;
|
|
57
|
+
const grid = el("div", "gtm-stat-grid");
|
|
58
|
+
grid.appendChild(statCard("Objects", String(report.counts.graph.nodeCount)));
|
|
59
|
+
grid.appendChild(statCard("Dependencies", String(report.counts.graph.edgeCount)));
|
|
60
|
+
grid.appendChild(statCard("Broken references", String(report.counts.graph.danglingCount)));
|
|
61
|
+
for (const [kind, count] of Object.entries(perKind)) {
|
|
62
|
+
if (!count) continue;
|
|
63
|
+
grid.appendChild(statCard(kind, String(count)));
|
|
64
|
+
}
|
|
65
|
+
container.appendChild(grid);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function statCard(label: string, value: string): HTMLElement {
|
|
69
|
+
const card = el("div", "gtm-stat");
|
|
70
|
+
card.appendChild(el("strong", undefined, value));
|
|
71
|
+
card.appendChild(el("span", undefined, label));
|
|
72
|
+
return card;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Dispositions grouped by class, each object with its explanation. */
|
|
76
|
+
export function renderDispositions(container: HTMLElement, report: GtmMigrationReport): void {
|
|
77
|
+
container.textContent = "";
|
|
78
|
+
const byClass = new Map<string, GtmReportObject[]>();
|
|
79
|
+
for (const o of report.dispositions) {
|
|
80
|
+
const list = byClass.get(o.disposition) ?? [];
|
|
81
|
+
list.push(o);
|
|
82
|
+
byClass.set(o.disposition, list);
|
|
83
|
+
}
|
|
84
|
+
for (const [cls, count] of Object.entries(report.dispositionSummary.byClass)) {
|
|
85
|
+
if (!count) continue;
|
|
86
|
+
const group = el("div", "gtm-disposition-group");
|
|
87
|
+
const head = el("h4");
|
|
88
|
+
head.appendChild(el("span", undefined, CLASS_LABEL[cls] ?? cls));
|
|
89
|
+
head.appendChild(el("span", "chip", String(count)));
|
|
90
|
+
group.appendChild(head);
|
|
91
|
+
const items = byClass.get(cls) ?? [];
|
|
92
|
+
const ul = el("ul", "gtm-list");
|
|
93
|
+
for (const o of items) {
|
|
94
|
+
const li = el("li");
|
|
95
|
+
li.appendChild(el("strong", undefined, o.name ?? o.id ?? o.nodeKey));
|
|
96
|
+
li.appendChild(el("span", "subtle", ` ${o.kind}${o.type ? ` · ${o.type}` : ""}`));
|
|
97
|
+
li.appendChild(el("p", "note", o.explanation));
|
|
98
|
+
ul.appendChild(li);
|
|
99
|
+
}
|
|
100
|
+
group.appendChild(ul);
|
|
101
|
+
container.appendChild(group);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Dependency graph as a readable edge list (no heavy viz). */
|
|
106
|
+
export function renderGraph(container: HTMLElement, report: GtmMigrationReport): void {
|
|
107
|
+
container.textContent = "";
|
|
108
|
+
const nameByKey = new Map<string, string>();
|
|
109
|
+
for (const n of report.graph.nodes) nameByKey.set(n.key, n.name ?? n.id ?? n.key);
|
|
110
|
+
if (report.graph.edges.length === 0) {
|
|
111
|
+
container.appendChild(el("p", "subtle", "No dependencies resolved between objects."));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const ul = el("ul", "gtm-list");
|
|
115
|
+
for (const e of report.graph.edges) {
|
|
116
|
+
const li = el("li");
|
|
117
|
+
li.appendChild(
|
|
118
|
+
el(
|
|
119
|
+
"span",
|
|
120
|
+
undefined,
|
|
121
|
+
`${nameByKey.get(e.from) ?? e.from} → ${nameByKey.get(e.to) ?? e.to}`,
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
li.appendChild(el("span", "chip", e.kind));
|
|
125
|
+
ul.appendChild(li);
|
|
126
|
+
}
|
|
127
|
+
container.appendChild(ul);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Unresolved findings: broken references + unsupported objects. */
|
|
131
|
+
export function renderFindings(container: HTMLElement, report: GtmMigrationReport): void {
|
|
132
|
+
container.textContent = "";
|
|
133
|
+
const { dangling, unsupported } = report.findings;
|
|
134
|
+
if (dangling.length === 0 && unsupported.length === 0) {
|
|
135
|
+
container.appendChild(el("p", "subtle", "No unresolved findings."));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (unsupported.length > 0) {
|
|
139
|
+
container.appendChild(el("h4", undefined, `Unsupported by design (${unsupported.length})`));
|
|
140
|
+
const ul = el("ul", "gtm-list");
|
|
141
|
+
for (const o of unsupported) {
|
|
142
|
+
const li = el("li");
|
|
143
|
+
li.appendChild(el("strong", undefined, o.name ?? o.id ?? o.nodeKey));
|
|
144
|
+
li.appendChild(el("p", "note", o.explanation));
|
|
145
|
+
ul.appendChild(li);
|
|
146
|
+
}
|
|
147
|
+
container.appendChild(ul);
|
|
148
|
+
}
|
|
149
|
+
if (dangling.length > 0) {
|
|
150
|
+
container.appendChild(el("h4", undefined, `Broken references (${dangling.length})`));
|
|
151
|
+
const ul = el("ul", "gtm-list");
|
|
152
|
+
for (const d of dangling) {
|
|
153
|
+
const li = el("li");
|
|
154
|
+
li.appendChild(el("span", undefined, `${d.from} → ${d.refKind} "${d.ref}"`));
|
|
155
|
+
li.appendChild(el("p", "note", d.explanation));
|
|
156
|
+
ul.appendChild(li);
|
|
157
|
+
}
|
|
158
|
+
container.appendChild(ul);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Recommendations derived from the reason catalog. */
|
|
163
|
+
export function renderRecommendations(
|
|
164
|
+
container: HTMLElement,
|
|
165
|
+
recommendations: GtmRecommendation[],
|
|
166
|
+
): void {
|
|
167
|
+
container.textContent = "";
|
|
168
|
+
if (recommendations.length === 0) {
|
|
169
|
+
container.appendChild(el("p", "subtle", "No recommendations."));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const ul = el("ul", "gtm-list");
|
|
173
|
+
for (const r of recommendations) {
|
|
174
|
+
const li = el("li");
|
|
175
|
+
const head = el("div");
|
|
176
|
+
head.appendChild(el("strong", undefined, CLASS_LABEL[r.disposition] ?? r.disposition));
|
|
177
|
+
head.appendChild(el("span", "chip", `${r.count}`));
|
|
178
|
+
head.appendChild(el("span", "subtle", ` ${r.reasonCode}`));
|
|
179
|
+
li.appendChild(head);
|
|
180
|
+
li.appendChild(el("p", "note", r.nextStep));
|
|
181
|
+
ul.appendChild(li);
|
|
182
|
+
}
|
|
183
|
+
container.appendChild(ul);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Render the whole report into the results region. Pure over the DOM. */
|
|
187
|
+
export function renderReport(root: HTMLElement, report: GtmMigrationReport): void {
|
|
188
|
+
root.hidden = false;
|
|
189
|
+
const inventory = root.querySelector<HTMLElement>("[data-gtm-inventory]");
|
|
190
|
+
const graph = root.querySelector<HTMLElement>("[data-gtm-graph]");
|
|
191
|
+
const dispositions = root.querySelector<HTMLElement>("[data-gtm-dispositions]");
|
|
192
|
+
const findings = root.querySelector<HTMLElement>("[data-gtm-findings]");
|
|
193
|
+
const recommendations = root.querySelector<HTMLElement>("[data-gtm-recommendations]");
|
|
194
|
+
if (inventory) renderInventory(inventory, report);
|
|
195
|
+
if (graph) renderGraph(graph, report);
|
|
196
|
+
if (dispositions) renderDispositions(dispositions, report);
|
|
197
|
+
if (findings) renderFindings(findings, report);
|
|
198
|
+
if (recommendations) renderRecommendations(recommendations, report.recommendations);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ── Controller ───────────────────────────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
function withTenant(base: string, tenant: string | undefined, extra?: Record<string, string>): string {
|
|
204
|
+
const qs = new URLSearchParams();
|
|
205
|
+
if (tenant) qs.set("tenant", tenant);
|
|
206
|
+
for (const [k, v] of Object.entries(extra ?? {})) qs.set(k, v);
|
|
207
|
+
const suffix = qs.toString();
|
|
208
|
+
return suffix ? `${base}?${suffix}` : base;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function readJson<T>(res: Response): Promise<T> {
|
|
212
|
+
try {
|
|
213
|
+
return (await res.json()) as T;
|
|
214
|
+
} catch {
|
|
215
|
+
return {} as T;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function initGtmMigrationTab(): void {
|
|
220
|
+
const panel = document.querySelector<HTMLElement>("[data-gtm-migration-panel]");
|
|
221
|
+
if (!panel) return;
|
|
222
|
+
|
|
223
|
+
const endpoint = panel.dataset.gtmEndpoint;
|
|
224
|
+
if (!endpoint) return;
|
|
225
|
+
const tenant = panel.dataset.targetTenant || undefined;
|
|
226
|
+
|
|
227
|
+
const fileInput = panel.querySelector<HTMLInputElement>("[data-gtm-file]");
|
|
228
|
+
const analyzeBtn = panel.querySelector<HTMLButtonElement>("[data-gtm-analyze]");
|
|
229
|
+
const downloadBtn = panel.querySelector<HTMLButtonElement>("[data-gtm-download]");
|
|
230
|
+
const statusLine = panel.querySelector<HTMLElement>("[data-gtm-status]");
|
|
231
|
+
const results = panel.querySelector<HTMLElement>("[data-gtm-results]");
|
|
232
|
+
|
|
233
|
+
const setStatus = (message: string, tone: "" | "error" = ""): void => {
|
|
234
|
+
if (!statusLine) return;
|
|
235
|
+
statusLine.textContent = message;
|
|
236
|
+
statusLine.dataset.tone = tone;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const selectedFile = (): File | null => {
|
|
240
|
+
const f = fileInput?.files?.[0];
|
|
241
|
+
return f ?? null;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
async function analyze(): Promise<void> {
|
|
245
|
+
const file = selectedFile();
|
|
246
|
+
if (!file) {
|
|
247
|
+
setStatus("Choose a GTM export file to analyze.", "error");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
setStatus(`Analyzing ${file.name}…`);
|
|
251
|
+
if (downloadBtn) downloadBtn.disabled = true;
|
|
252
|
+
try {
|
|
253
|
+
const form = new FormData();
|
|
254
|
+
form.append("file", file);
|
|
255
|
+
const res = await fetch(withTenant(endpoint!, tenant), { method: "POST", body: form });
|
|
256
|
+
if (!res.ok) {
|
|
257
|
+
const err = await readJson<AnalyzeErrBody>(res);
|
|
258
|
+
const detail =
|
|
259
|
+
err.violations && err.violations.length > 0
|
|
260
|
+
? `: ${err.violations.map((v) => `${v.path ?? "(input)"} ${v.message ?? v.code ?? ""}`.trim()).join("; ")}`
|
|
261
|
+
: "";
|
|
262
|
+
setStatus(`${err.error ?? `Analyze failed (${res.status}).`}${detail}`, "error");
|
|
263
|
+
if (results) results.hidden = true;
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const body = await readJson<AnalyzeOkBody>(res);
|
|
267
|
+
if (!body.report) {
|
|
268
|
+
setStatus("The analyze response was empty.", "error");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (results) renderReport(results, body.report);
|
|
272
|
+
if (downloadBtn) downloadBtn.disabled = false;
|
|
273
|
+
const total = body.report.dispositionSummary.total;
|
|
274
|
+
setStatus(`Analyzed ${total} object(s). Correlation ${body.correlationId}.`);
|
|
275
|
+
} catch {
|
|
276
|
+
setStatus("Could not reach the analyze service.", "error");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function download(): Promise<void> {
|
|
281
|
+
const file = selectedFile();
|
|
282
|
+
if (!file) {
|
|
283
|
+
setStatus("Choose and analyze a GTM export before downloading the bundle.", "error");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
setStatus("Preparing support bundle…");
|
|
287
|
+
try {
|
|
288
|
+
const form = new FormData();
|
|
289
|
+
form.append("file", file);
|
|
290
|
+
const res = await fetch(withTenant(endpoint!, tenant, { format: "bundle" }), {
|
|
291
|
+
method: "POST",
|
|
292
|
+
body: form,
|
|
293
|
+
});
|
|
294
|
+
if (!res.ok) {
|
|
295
|
+
setStatus(`Could not build the support bundle (${res.status}).`, "error");
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const blob = await res.blob();
|
|
299
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
300
|
+
const a = document.createElement("a");
|
|
301
|
+
a.href = objectUrl;
|
|
302
|
+
a.download = `gtm-migration-report.json`;
|
|
303
|
+
document.body.appendChild(a);
|
|
304
|
+
a.click();
|
|
305
|
+
a.remove();
|
|
306
|
+
URL.revokeObjectURL(objectUrl);
|
|
307
|
+
setStatus("Support bundle downloaded.");
|
|
308
|
+
} catch {
|
|
309
|
+
setStatus("Could not reach the analyze service to build the bundle.", "error");
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
analyzeBtn?.addEventListener("click", () => void analyze());
|
|
314
|
+
downloadBtn?.addEventListener("click", () => void download());
|
|
315
|
+
}
|