@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
|
@@ -27,6 +27,14 @@ export const appInstalls = sqliteTable(
|
|
|
27
27
|
webhookEndpoint: text("webhook_endpoint"),
|
|
28
28
|
widgetPlacements: text("widget_placements").notNull().default("[]"),
|
|
29
29
|
installActor: text("install_actor").notNull(),
|
|
30
|
+
// Nullable discriminator keeps the managed-analytics v1 lifecycle on the
|
|
31
|
+
// canonical install row instead of creating a second install authority.
|
|
32
|
+
managedKind: text("managed_kind"),
|
|
33
|
+
managedRevision: integer("managed_revision").notNull().default(0),
|
|
34
|
+
managedActiveRevisionId: text("managed_active_revision_id"),
|
|
35
|
+
// Validated non-live candidate only. Live effects remain in manifest and
|
|
36
|
+
// app_version_snapshots, the registry's established serving authorities.
|
|
37
|
+
managedCandidate: text("managed_candidate"),
|
|
30
38
|
createdAt: text("created_at").notNull(),
|
|
31
39
|
updatedAt: text("updated_at").notNull(),
|
|
32
40
|
},
|
|
@@ -70,6 +78,23 @@ export const appAuditLog = sqliteTable(
|
|
|
70
78
|
}),
|
|
71
79
|
);
|
|
72
80
|
|
|
81
|
+
// Inert reviewed migration suggestions. This is deliberately NOT an install,
|
|
82
|
+
// live effect, revision, or publish authority; conversion requires a separate
|
|
83
|
+
// canonical `app_installs` mutation and drafts can never be enabled.
|
|
84
|
+
export const integrationMigrationDrafts = sqliteTable(
|
|
85
|
+
"integration_migration_drafts",
|
|
86
|
+
{
|
|
87
|
+
draftId: text("draft_id").primaryKey(),
|
|
88
|
+
tenantId: text("tenant_id").notNull(),
|
|
89
|
+
revision: integer("revision").notNull(),
|
|
90
|
+
state: text("state").notNull().default("open"),
|
|
91
|
+
record: text("record").notNull(),
|
|
92
|
+
createdAt: text("created_at").notNull(),
|
|
93
|
+
updatedAt: text("updated_at").notNull(),
|
|
94
|
+
},
|
|
95
|
+
(t) => ({ idxIntegrationMigrationDraftsTenant: index("idx_integration_migration_drafts_tenant").on(t.tenantId, t.createdAt) }),
|
|
96
|
+
);
|
|
97
|
+
|
|
73
98
|
// --- Operational activity timeline ------------------------------------------
|
|
74
99
|
// The low-volume / high-value LIFECYCLE cohort of the actor×action telemetry
|
|
75
100
|
// spine (DECISIONS.md §8). Only actions whose catalog `spec.sinks` include
|
|
@@ -344,3 +369,254 @@ export const newsletterSends = sqliteTable(
|
|
|
344
369
|
},
|
|
345
370
|
(t) => ({ pk: primaryKey({ columns: [t.tenantId, t.collection, t.digestId] }) }),
|
|
346
371
|
);
|
|
372
|
+
|
|
373
|
+
// --- Canonical publish-candidate identity -----------------------------------
|
|
374
|
+
// ONE canonical candidate per `(tenant_id, repository, pr_number)` — the real
|
|
375
|
+
// forge PR. Retries/rebuilds are NOT new candidates; they are child `attempts`
|
|
376
|
+
// (below). This is the durable identity spine other units resolve against; the
|
|
377
|
+
// KV `ReviewEnvironment` (`cust:*`) and aggregate/release models (`rel:*`) are
|
|
378
|
+
// read projections that reference this row's `change_id`/`pr_number`.
|
|
379
|
+
//
|
|
380
|
+
// `id` is a SYNTHETIC surrogate (`cand_<uuid>`), deliberately DISTINCT from the
|
|
381
|
+
// canonical key so foreign keys (attempts) never depend on the mutable natural
|
|
382
|
+
// key. `change_id` is the REAL forge change id — the stable branch/build
|
|
383
|
+
// identity (`candidate/<change_id>`) — preserved, never derived from `id`.
|
|
384
|
+
// `tenant_id` is the isolation boundary, written from the resolved session,
|
|
385
|
+
// NEVER a client body. The UNIQUE `(tenant_id, repository, pr_number)` is what
|
|
386
|
+
// makes "one candidate per PR" a schema invariant (upsert-by-key, not
|
|
387
|
+
// insert-then-dedupe); the UNIQUE `(tenant_id, change_id)` keeps the branch/
|
|
388
|
+
// build identity unambiguous for resolve-by-changeId callers.
|
|
389
|
+
export const candidates = sqliteTable(
|
|
390
|
+
"candidates",
|
|
391
|
+
{
|
|
392
|
+
id: text("id").primaryKey(),
|
|
393
|
+
tenantId: text("tenant_id").notNull(),
|
|
394
|
+
// The `<tenant>-<tag>` forge repository the PR lives in.
|
|
395
|
+
repository: text("repository").notNull(),
|
|
396
|
+
// The forge PR number — the third leg of the canonical key.
|
|
397
|
+
prNumber: integer("pr_number").notNull(),
|
|
398
|
+
// The real forge change id — stable branch/build identity (`candidate/<change_id>`).
|
|
399
|
+
changeId: text("change_id").notNull(),
|
|
400
|
+
// Source branch, conventionally `candidate/<change_id>` (nullable convenience mirror).
|
|
401
|
+
branch: text("branch"),
|
|
402
|
+
title: text("title"),
|
|
403
|
+
author: text("author"),
|
|
404
|
+
// Canonical candidate lifecycle: open | preparing | ready | needs-attention | accepted | rejected | closed.
|
|
405
|
+
status: text("status").notNull().default("open"),
|
|
406
|
+
// The PR head/base shas as last reconciled (nullable until first read).
|
|
407
|
+
headSha: text("head_sha"),
|
|
408
|
+
baseSha: text("base_sha"),
|
|
409
|
+
createdAt: text("created_at").notNull(),
|
|
410
|
+
updatedAt: text("updated_at").notNull(),
|
|
411
|
+
},
|
|
412
|
+
(t) => ({
|
|
413
|
+
uqCandidatesCanonical: uniqueIndex("uq_candidates_tenant_repo_pr").on(
|
|
414
|
+
t.tenantId,
|
|
415
|
+
t.repository,
|
|
416
|
+
t.prNumber,
|
|
417
|
+
),
|
|
418
|
+
uqCandidatesChange: uniqueIndex("uq_candidates_tenant_change").on(t.tenantId, t.changeId),
|
|
419
|
+
idxCandidatesTenantStatus: index("idx_candidates_tenant_status").on(t.tenantId, t.status),
|
|
420
|
+
}),
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
// One `attempt` = one build/check/rebase EXECUTION of a candidate. Attempts are
|
|
424
|
+
// the child rows retries produce — they must NEVER queue as separate top-level
|
|
425
|
+
// candidate rows. `candidate_id` references `candidates.id` in code only (no SQL
|
|
426
|
+
// FK — repo convention, matches `webhook_deliveries.event_id` / `orders`).
|
|
427
|
+
// `tenant_id` is denormalized for tenant-scoped reads/isolation without a join.
|
|
428
|
+
// `attempt_number` is monotonic per candidate (1,2,3…) giving stable ordering;
|
|
429
|
+
// UNIQUE `(candidate_id, attempt_number)` makes a double-assigned number a loud
|
|
430
|
+
// failure rather than a silent duplicate. `kind`: build | check | rebase;
|
|
431
|
+
// `status`: pending | running | passed | failed | cancelled (mirrors
|
|
432
|
+
// `IntegrationRun`'s state vocabulary). `evidence` is a JSON EvidenceReport
|
|
433
|
+
// snapshot; `detail` a human-legible terminal-state message.
|
|
434
|
+
export const candidateAttempts = sqliteTable(
|
|
435
|
+
"candidate_attempts",
|
|
436
|
+
{
|
|
437
|
+
id: text("id").primaryKey(),
|
|
438
|
+
candidateId: text("candidate_id").notNull(),
|
|
439
|
+
tenantId: text("tenant_id").notNull(),
|
|
440
|
+
attemptNumber: integer("attempt_number").notNull(),
|
|
441
|
+
kind: text("kind").notNull(),
|
|
442
|
+
status: text("status").notNull().default("pending"),
|
|
443
|
+
// The candidate head sha this attempt executed against, or null if unknown.
|
|
444
|
+
headSha: text("head_sha"),
|
|
445
|
+
detail: text("detail"),
|
|
446
|
+
evidence: text("evidence"),
|
|
447
|
+
startedAt: text("started_at").notNull(),
|
|
448
|
+
// Null while pending/running; set on a terminal transition.
|
|
449
|
+
finishedAt: text("finished_at"),
|
|
450
|
+
},
|
|
451
|
+
(t) => ({
|
|
452
|
+
uqAttemptNumber: uniqueIndex("uq_candidate_attempts_candidate_number").on(
|
|
453
|
+
t.candidateId,
|
|
454
|
+
t.attemptNumber,
|
|
455
|
+
),
|
|
456
|
+
idxAttemptsCandidate: index("idx_candidate_attempts_candidate").on(t.candidateId),
|
|
457
|
+
idxAttemptsTenant: index("idx_candidate_attempts_tenant").on(t.tenantId),
|
|
458
|
+
}),
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
// One active submit per tenant/change. This serializes identity authorization,
|
|
462
|
+
// the Gitea mutation, and audit finalization so a verified callback cannot
|
|
463
|
+
// become stale between its forge read and its KV writes. `expires_at` is
|
|
464
|
+
// diagnostic only; recovery is explicit so a slow live submit cannot be
|
|
465
|
+
// time-fenced incorrectly.
|
|
466
|
+
export const candidateSubmitLeases = sqliteTable(
|
|
467
|
+
"candidate_submit_leases",
|
|
468
|
+
{
|
|
469
|
+
tenantId: text("tenant_id").notNull(),
|
|
470
|
+
changeId: text("change_id").notNull(),
|
|
471
|
+
authorizationId: text("authorization_id").notNull(),
|
|
472
|
+
status: text("status").notNull().default("active"),
|
|
473
|
+
acquiredAt: text("acquired_at").notNull(),
|
|
474
|
+
expiresAt: text("expires_at").notNull(),
|
|
475
|
+
},
|
|
476
|
+
(t) => ({
|
|
477
|
+
pk: primaryKey({ columns: [t.tenantId, t.changeId] }),
|
|
478
|
+
idxExpires: index("idx_candidate_submit_leases_expires").on(t.expiresAt),
|
|
479
|
+
}),
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// --- Merchant consent configuration -----------------------------------------
|
|
483
|
+
// APPEND-ONLY revision history of a tenant's consent configuration (the
|
|
484
|
+
// ext-admin-03 surface that feeds the ext-policy-03 broker). Each save is a NEW
|
|
485
|
+
// immutable row — the current config is `MAX(revision)` per tenant — so the table
|
|
486
|
+
// IS the audit trail (actor + timestamp per revision, never mutated in place) and
|
|
487
|
+
// a revert is a forward-only new revision that copies a prior `config` and records
|
|
488
|
+
// `reverted_from`. `config` is the JSON `TenantConsentConfig` (never secret-bearing
|
|
489
|
+
// — consent config carries no secrets). `tenant_id` is the isolation boundary,
|
|
490
|
+
// written from the resolved session, never a client body. UNIQUE
|
|
491
|
+
// `(tenant_id, revision)` makes the monotonic revision a schema invariant.
|
|
492
|
+
export const consentConfigRevisions = sqliteTable(
|
|
493
|
+
"consent_config_revisions",
|
|
494
|
+
{
|
|
495
|
+
id: text("id").primaryKey(),
|
|
496
|
+
tenantId: text("tenant_id").notNull(),
|
|
497
|
+
// Monotonic per tenant (1, 2, 3…) — the latest revision is the current config.
|
|
498
|
+
revision: integer("revision").notNull(),
|
|
499
|
+
// The JSON-serialized validated `TenantConsentConfig`.
|
|
500
|
+
config: text("config").notNull(),
|
|
501
|
+
// Opaque actor id — never email/PII (matches `app_audit_log`).
|
|
502
|
+
actor: text("actor").notNull(),
|
|
503
|
+
// Human-legible note (e.g. the reason, or "revert to revision N").
|
|
504
|
+
note: text("note"),
|
|
505
|
+
// When this revision copies an earlier one, the source revision — else null.
|
|
506
|
+
revertedFrom: integer("reverted_from"),
|
|
507
|
+
createdAt: text("created_at").notNull(),
|
|
508
|
+
},
|
|
509
|
+
(t) => ({
|
|
510
|
+
uqConsentConfigTenantRevision: uniqueIndex("uq_consent_config_tenant_revision").on(
|
|
511
|
+
t.tenantId,
|
|
512
|
+
t.revision,
|
|
513
|
+
),
|
|
514
|
+
idxConsentConfigTenant: index("idx_consent_config_tenant").on(t.tenantId),
|
|
515
|
+
}),
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
// Demonstrated CMP demand — one row per `(tenant, adapter)` demand signal. The set
|
|
519
|
+
// of SELECTABLE CMP adapters is DERIVED from these rows (distinct-tenant count over
|
|
520
|
+
// a threshold), never a hardcoded catalog: the first CMP Storefront adapts is
|
|
521
|
+
// whichever real demand first proves (ADR 0014 P2 / 0025). UNIQUE
|
|
522
|
+
// `(tenant_id, adapter_id)` makes demand a per-tenant fact (a tenant re-asking does
|
|
523
|
+
// not inflate the count); the `adapter_id` index serves the per-adapter rollup.
|
|
524
|
+
export const consentCmpDemand = sqliteTable(
|
|
525
|
+
"consent_cmp_demand",
|
|
526
|
+
{
|
|
527
|
+
id: text("id").primaryKey(),
|
|
528
|
+
tenantId: text("tenant_id").notNull(),
|
|
529
|
+
adapterId: text("adapter_id").notNull(),
|
|
530
|
+
// Where the demand came from (a merchant request, a support ticket), for the record.
|
|
531
|
+
source: text("source"),
|
|
532
|
+
at: text("at").notNull(),
|
|
533
|
+
},
|
|
534
|
+
(t) => ({
|
|
535
|
+
uqConsentCmpDemandTenantAdapter: uniqueIndex("uq_consent_cmp_demand_tenant_adapter").on(
|
|
536
|
+
t.tenantId,
|
|
537
|
+
t.adapterId,
|
|
538
|
+
),
|
|
539
|
+
idxConsentCmpDemandAdapter: index("idx_consent_cmp_demand_adapter").on(t.adapterId),
|
|
540
|
+
}),
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
// --- Commerce conversion attribution (ext-commerce-04) -----------------------
|
|
544
|
+
// The single-use ledger for the signed checkout-correlation token's `jti`
|
|
545
|
+
// (`packages/public-runtime/src/integration/correlation-token.ts`). The order join
|
|
546
|
+
// consumes a token EXACTLY once: a replay of a consumed `jti` is refused. `jti` is
|
|
547
|
+
// the natural key (the token's own 96-bit nonce); `expires_at` is the token's Unix-
|
|
548
|
+
// seconds `exp` so a consumed row can be pruned once the token is already `expired`
|
|
549
|
+
// (a past-`exp` token is rejected before the guard is even reached). Carries no PII
|
|
550
|
+
// and no secret — the `jti` is an opaque nonce, never the token or the signing key.
|
|
551
|
+
export const correlationReplay = sqliteTable("correlation_replay", {
|
|
552
|
+
jti: text("jti").primaryKey(),
|
|
553
|
+
expiresAt: integer("expires_at").notNull(),
|
|
554
|
+
consumedAt: text("consumed_at").notNull(),
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// --- Retained managed-app version facets (ext-apps-02) -----------------------
|
|
558
|
+
// APPEND-ONLY retention of every activated managed-app VERSION's COMPLETE
|
|
559
|
+
// restorable facet set, and the atomic-rollback spine over it. One row per
|
|
560
|
+
// activation of an install; the CURRENT active version is `MAX(seq)` per install
|
|
561
|
+
// (same "the latest row is current" model as `consent_config_revisions`). The
|
|
562
|
+
// whole restorable facet set (code ref/digest, entry module, public config,
|
|
563
|
+
// contributions, granted scopes, consent declaration, CSP origins) is one JSON
|
|
564
|
+
// blob in `facets` — so retaining a version and rolling one back are each a
|
|
565
|
+
// SINGLE-ROW insert: the restore is atomic by construction (you get the whole
|
|
566
|
+
// prior facet set or nothing — never a partial/mixed activation). A rollback is
|
|
567
|
+
// forward-only: it appends a NEW activation copying a retained version's facets
|
|
568
|
+
// and records `restored_from_seq`, so history is never rewritten and the rollback
|
|
569
|
+
// is itself an audited row. `tenant_id` is the isolation boundary, denormalized
|
|
570
|
+
// for tenant-scoped reads (no SQL FK — repo convention). `facets` carries no
|
|
571
|
+
// secret material (proved by the ext-kernel-01 secret scan before insert). UNIQUE
|
|
572
|
+
// `(install_id, seq)` makes the monotonic sequence a schema invariant.
|
|
573
|
+
export const appVersionSnapshots = sqliteTable(
|
|
574
|
+
"app_version_snapshots",
|
|
575
|
+
{
|
|
576
|
+
id: text("id").primaryKey(),
|
|
577
|
+
installId: text("install_id").notNull(),
|
|
578
|
+
tenantId: text("tenant_id").notNull(),
|
|
579
|
+
// Monotonic per install (1, 2, 3…); the largest is the active version.
|
|
580
|
+
seq: integer("seq").notNull(),
|
|
581
|
+
// The app package version this activation serves (mirror of facets.appVersion).
|
|
582
|
+
appVersion: text("app_version").notNull(),
|
|
583
|
+
// The complete restorable facet set as one JSON blob (AppVersionFacets).
|
|
584
|
+
facets: text("facets").notNull(),
|
|
585
|
+
// Opaque actor id — never email/PII (matches `app_audit_log`).
|
|
586
|
+
actor: text("actor").notNull(),
|
|
587
|
+
// Human-legible note (e.g. the reason, or "rollback to version N").
|
|
588
|
+
note: text("note"),
|
|
589
|
+
// When this activation restores an earlier one, its source `seq` — else null.
|
|
590
|
+
restoredFromSeq: integer("restored_from_seq"),
|
|
591
|
+
createdAt: text("created_at").notNull(),
|
|
592
|
+
},
|
|
593
|
+
(t) => ({
|
|
594
|
+
uqAppVersionSnapshotsInstallSeq: uniqueIndex("uq_app_version_snapshots_install_seq").on(
|
|
595
|
+
t.installId,
|
|
596
|
+
t.seq,
|
|
597
|
+
),
|
|
598
|
+
idxAppVersionSnapshotsInstall: index("idx_app_version_snapshots_install").on(t.installId),
|
|
599
|
+
}),
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
// The per-`(tenant, external_order_id)` attribution ledger. An order's attribution is
|
|
603
|
+
// decided ONCE (on its first `order.created`/`order.fulfilled` conversion) and reused
|
|
604
|
+
// for every later lifecycle conversion, so the correlation `jti` is consumed once per
|
|
605
|
+
// order — not once per lifecycle event — and a duplicate/replayed/out-of-order webhook
|
|
606
|
+
// reproduces the same outcome. `attributed` is 0/1; when attributed, `correlation_id`
|
|
607
|
+
// + `correlation_expires_at` hold the shared thread; when not, `reason` records why
|
|
608
|
+
// (missing/expired/context_mismatch/replay/…). Never customer identity — only the
|
|
609
|
+
// opaque correlation handle. `(tenant_id, external_order_id)` is the natural key.
|
|
610
|
+
export const orderAttribution = sqliteTable(
|
|
611
|
+
"order_attribution",
|
|
612
|
+
{
|
|
613
|
+
tenantId: text("tenant_id").notNull(),
|
|
614
|
+
externalOrderId: text("external_order_id").notNull(),
|
|
615
|
+
attributed: integer("attributed").notNull().default(0),
|
|
616
|
+
correlationId: text("correlation_id"),
|
|
617
|
+
correlationExpiresAt: text("correlation_expires_at"),
|
|
618
|
+
reason: text("reason"),
|
|
619
|
+
recordedAt: text("recorded_at").notNull(),
|
|
620
|
+
},
|
|
621
|
+
(t) => ({ pk: primaryKey({ columns: [t.tenantId, t.externalOrderId] }) }),
|
|
622
|
+
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Stuck? Ask" recovery affordance. A quiet FAB that opens a scoped Q&A
|
|
3
|
+
* for "why isn't the loop working" — NOT general AI editing. Static answers
|
|
4
|
+
* ship as markup; the two stateful answers (is it running? / save-vs-reload)
|
|
5
|
+
* update from the shared tot:loop-state signal + window.__totLoop (monitor.ts
|
|
6
|
+
* — no second poll). "Reconnect preview" reloads the page, re-establishing
|
|
7
|
+
* the HMR connection and re-reading loop state (recovers a stale client); a
|
|
8
|
+
* fully-down server is handled by the answer text (run tot start). Imported
|
|
9
|
+
* from a processed `<script>` in cockpit.astro (ADR 0013), rendered only when
|
|
10
|
+
* isLocalDevLoop.
|
|
11
|
+
*/
|
|
12
|
+
export function initAsk(): void {
|
|
13
|
+
const fab = document.getElementById("askFab");
|
|
14
|
+
const panel = document.getElementById("askPanel");
|
|
15
|
+
if (!fab || !panel) return;
|
|
16
|
+
const ansRunning = document.getElementById("ans-running");
|
|
17
|
+
const ansChange = document.getElementById("ans-change");
|
|
18
|
+
const restart = document.getElementById("restartBtn");
|
|
19
|
+
|
|
20
|
+
fab.addEventListener("click", (e) => { e.stopPropagation(); panel.classList.toggle("open"); });
|
|
21
|
+
document.addEventListener("click", (e) => {
|
|
22
|
+
if (!panel.contains(e.target as Node) && e.target !== fab && !fab.contains(e.target as Node)) panel.classList.remove("open");
|
|
23
|
+
});
|
|
24
|
+
if (restart) restart.addEventListener("click", () => { location.reload(); });
|
|
25
|
+
|
|
26
|
+
function rel(ts: number | string): string {
|
|
27
|
+
const d = Math.max(0, Math.round((Date.now() - Number(ts)) / 1000));
|
|
28
|
+
return d < 2 ? "just now" : d < 60 ? d + "s ago" : Math.floor(d / 60) + "m ago";
|
|
29
|
+
}
|
|
30
|
+
function update(s: any) {
|
|
31
|
+
if (!s) return;
|
|
32
|
+
if (ansRunning) {
|
|
33
|
+
ansRunning.textContent = (s.offline || !s.server)
|
|
34
|
+
? "Your dev server isn't reachable right now — run tot start in your terminal, then reload this page."
|
|
35
|
+
: "Yes — your server is running" + (s.port ? " on port " + s.port : "") + ". This page is connected to it.";
|
|
36
|
+
}
|
|
37
|
+
if (ansChange) {
|
|
38
|
+
if (!s.lastSaveAt) {
|
|
39
|
+
ansChange.textContent = "No saves seen yet. Edit a watched file (like content/home.html) and save — it'll show up here.";
|
|
40
|
+
} else if ((s.lastReloadAt || 0) >= s.lastSaveAt) {
|
|
41
|
+
ansChange.textContent = "Working: your last save (" + rel(s.lastSaveAt) + ") triggered a reload (" + rel(s.lastReloadAt) + "). If the page didn't visibly change, make sure you edited the file the page renders from.";
|
|
42
|
+
} else {
|
|
43
|
+
ansChange.textContent = "Your save (" + rel(s.lastSaveAt) + ") hasn't been reflected by a reload yet — the preview may be stale. Try Reconnect preview below.";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
window.addEventListener("tot:loop-state", (e: any) => { update(e.detail); });
|
|
48
|
+
if (window.__totLoop) update(window.__totLoop);
|
|
49
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C8 · Click-to-source capstone — the cockpit side. Runs the live-preview
|
|
3
|
+
* iframe (mini + full-screen dialog) and LISTENS for the framed picker's
|
|
4
|
+
* tot:locate postMessage. The picker (ai-widget-client.js, auto-injected into
|
|
5
|
+
* the same-origin preview) resolves a click to {path,line,col} via
|
|
6
|
+
* /__tot/locate, then — because it is framed — postMessages the result here
|
|
7
|
+
* instead of showing its own toast. We verify event.origin === our origin,
|
|
8
|
+
* then hand off to files.ts's window.__totOpenFileAt to open the source +
|
|
9
|
+
* drop the caret. Only content-region hits ever arrive (chrome/refused
|
|
10
|
+
* clicks stay silent in the frame), per decision
|
|
11
|
+
* click-to-source-anchors-at-content-region. Imported from a processed
|
|
12
|
+
* `<script>` in cockpit.astro (ADR 0013), rendered only when isLocalDevLoop.
|
|
13
|
+
* isDev and the hosted app domain are read from `document.body.dataset`.
|
|
14
|
+
*/
|
|
15
|
+
export function initClickToSource(): void {
|
|
16
|
+
const isDev = document.body.dataset.cockpitIsDev === "true";
|
|
17
|
+
const hostedAppDomain = document.body.dataset.cockpitAppDomain || "";
|
|
18
|
+
const dlg: any = document.getElementById("previewDialog");
|
|
19
|
+
const expandBtn = document.getElementById("previewExpand");
|
|
20
|
+
const closeBtn = document.getElementById("previewClose");
|
|
21
|
+
const dlgFrame: any = document.getElementById("previewDialogFrame");
|
|
22
|
+
const miniFrame: any = document.getElementById("previewFrame");
|
|
23
|
+
const urlEl = document.getElementById("previewUrl");
|
|
24
|
+
|
|
25
|
+
if (miniFrame) {
|
|
26
|
+
if (isDev) {
|
|
27
|
+
// Point the preview at the DEV tenant (TOT_DEV_TENANT), read from the server
|
|
28
|
+
// via /__tot/dev-loop-status — NOT the SSR appDomain (workerd can't see
|
|
29
|
+
// TOT_DEV_*). Same-origin, so the auto-injected picker can postMessage back.
|
|
30
|
+
fetch("/__tot/dev-loop-status")
|
|
31
|
+
.then((r) => r.json())
|
|
32
|
+
.then((s) => {
|
|
33
|
+
const tenant = s && s.tenant;
|
|
34
|
+
if (!tenant) return;
|
|
35
|
+
const src = "/" + tenant + "/";
|
|
36
|
+
miniFrame.src = src;
|
|
37
|
+
if (urlEl) urlEl.textContent = "localhost preview · " + tenant;
|
|
38
|
+
})
|
|
39
|
+
.catch(() => { /* dev server unreachable — preview stays blank */ });
|
|
40
|
+
} else if (hostedAppDomain) {
|
|
41
|
+
// Hosted: the SSR-resolved tenant IS the signed-in developer's own store
|
|
42
|
+
// (cp-env's capability gate already scoped it) — same-origin, so a cp-write
|
|
43
|
+
// save + this reload shows the developer's own draft overlay via the
|
|
44
|
+
// middleware's tot_dev_draft cookie check.
|
|
45
|
+
miniFrame.src = "/" + hostedAppDomain + "/";
|
|
46
|
+
if (urlEl) urlEl.textContent = "your store · " + hostedAppDomain;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (expandBtn && dlg) {
|
|
51
|
+
expandBtn.addEventListener("click", () => {
|
|
52
|
+
if (dlgFrame && !dlgFrame.getAttribute("src") && miniFrame) dlgFrame.src = miniFrame.getAttribute("src") || miniFrame.src;
|
|
53
|
+
if (typeof dlg.showModal === "function") dlg.showModal(); else dlg.setAttribute("open", "");
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (closeBtn && dlg) closeBtn.addEventListener("click", () => { dlg.close ? dlg.close() : dlg.removeAttribute("open"); });
|
|
57
|
+
if (dlg) dlg.addEventListener("click", (e: MouseEvent) => { if (e.target === dlg) { dlg.close ? dlg.close() : dlg.removeAttribute("open"); } });
|
|
58
|
+
|
|
59
|
+
// The one privileged inbound message: a source location from OUR OWN origin's
|
|
60
|
+
// framed preview. Reject any other origin, shape, or type outright.
|
|
61
|
+
window.addEventListener("message", (e) => {
|
|
62
|
+
if (e.origin !== window.location.origin) return;
|
|
63
|
+
const d: any = e.data;
|
|
64
|
+
if (!d || d.type !== "tot:locate" || typeof d.path !== "string") return;
|
|
65
|
+
if (typeof (window as any).__totOpenFileAt === "function") {
|
|
66
|
+
const opened = (window as any).__totOpenFileAt(d.path, d.line, d.col);
|
|
67
|
+
// If the preview was expanded full-screen, close it so the editor + caret are visible.
|
|
68
|
+
if (opened && dlg && dlg.open) { dlg.close ? dlg.close() : dlg.removeAttribute("open"); }
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy the one command. Two behaviours:
|
|
3
|
+
* · fastpath (signed-in dev): mint a single-use `tot login --code … && tot start` from
|
|
4
|
+
* /api/dev/cli-signin-code, reveal it (textContent, never innerHTML — it carries a
|
|
5
|
+
* live code), and copy the whole thing.
|
|
6
|
+
* · otherwise: copy the generic install-and-run command directly.
|
|
7
|
+
*
|
|
8
|
+
* Also drives rendezvous approval: after a command is generated, the
|
|
9
|
+
* terminal ATTACHES its own PKCE key and prints a fingerprint; the developer
|
|
10
|
+
* confirms it here — the load-bearing anti-interception step, since
|
|
11
|
+
* approving the wrong fingerprint is how a hijack is caught.
|
|
12
|
+
*
|
|
13
|
+
* Imported from a processed `<script>` in cockpit.astro. The values below are
|
|
14
|
+
* carried as data-attrs on #copyCmd because this module runs standalone (not
|
|
15
|
+
* inside the same closure as hostedActivity.ts's basePath).
|
|
16
|
+
*/
|
|
17
|
+
export function initCopyCommand(): void {
|
|
18
|
+
const btn: any = document.getElementById("copyCmd");
|
|
19
|
+
const textEl = document.getElementById("cli-cmd-text");
|
|
20
|
+
const note = document.getElementById("cli-cmd-note");
|
|
21
|
+
const toast = document.getElementById("toast");
|
|
22
|
+
const toastMsg = document.getElementById("toastMsg");
|
|
23
|
+
const copyIcon: any = document.getElementById("copyIcon");
|
|
24
|
+
if (!btn || !textEl) return;
|
|
25
|
+
const generic = btn.getAttribute("data-generic") || "";
|
|
26
|
+
const fastpath = btn.getAttribute("data-fastpath") === "1";
|
|
27
|
+
// The request's tenant prefix ("" host-routed, "/<tenant>" path-routed). Carried
|
|
28
|
+
// as a data-attr because `basePath` is otherwise defined only inside the SEPARATE
|
|
29
|
+
// hostedActivity module (hostedActivity.ts) — referencing it here (the
|
|
30
|
+
// cli-signin-code / -status / -approve fetches below) was a ReferenceError that
|
|
31
|
+
// the mint's catch surfaced as the opaque "Couldn't reach the sign-in service"
|
|
32
|
+
// with no request ever sent.
|
|
33
|
+
const basePath = btn.getAttribute("data-base-path") || "";
|
|
34
|
+
let tT: ReturnType<typeof setTimeout>;
|
|
35
|
+
function ping(m: string) {
|
|
36
|
+
if (!toast) return;
|
|
37
|
+
if (toastMsg) toastMsg.textContent = m;
|
|
38
|
+
toast.classList.add("show");
|
|
39
|
+
clearTimeout(tT); tT = setTimeout(() => { toast.classList.remove("show"); }, 1900);
|
|
40
|
+
}
|
|
41
|
+
function copy(text: string): Promise<boolean> {
|
|
42
|
+
if (navigator.clipboard) return navigator.clipboard.writeText(text).then(() => true).catch(() => false);
|
|
43
|
+
return Promise.resolve(false);
|
|
44
|
+
}
|
|
45
|
+
function label(s: string) { btn.childNodes[btn.childNodes.length - 1].textContent = s; }
|
|
46
|
+
|
|
47
|
+
// Standard copy-icon affordance on the terminal box. Once the command is
|
|
48
|
+
// revealed, this is the obvious, always-there way to (re)copy it — the
|
|
49
|
+
// icon→check swap is its own inline confirmation, so copying no longer
|
|
50
|
+
// depends on the transient toast being noticed.
|
|
51
|
+
let iconT: ReturnType<typeof setTimeout>;
|
|
52
|
+
function flashCopied() {
|
|
53
|
+
if (!copyIcon) return;
|
|
54
|
+
copyIcon.setAttribute("data-copied", "1");
|
|
55
|
+
copyIcon.setAttribute("aria-label", "Copied");
|
|
56
|
+
copyIcon.setAttribute("title", "Copied");
|
|
57
|
+
clearTimeout(iconT);
|
|
58
|
+
iconT = setTimeout(() => {
|
|
59
|
+
copyIcon.removeAttribute("data-copied");
|
|
60
|
+
copyIcon.setAttribute("aria-label", "Copy the command");
|
|
61
|
+
copyIcon.setAttribute("title", "Copy the command");
|
|
62
|
+
}, 1600);
|
|
63
|
+
}
|
|
64
|
+
function revealIcon() {
|
|
65
|
+
if (copyIcon) copyIcon.hidden = false;
|
|
66
|
+
}
|
|
67
|
+
if (copyIcon) {
|
|
68
|
+
copyIcon.addEventListener("click", () => {
|
|
69
|
+
copy(textEl.textContent || "").then((ok) => {
|
|
70
|
+
if (ok) { flashCopied(); ping("Copied — paste & press Enter"); }
|
|
71
|
+
else ping("Select the command above to copy");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── Rendezvous approval: after a command is generated, the terminal ATTACHES
|
|
77
|
+
// its own PKCE key and prints a fingerprint; the developer confirms it HERE. This
|
|
78
|
+
// is the load-bearing anti-interception step — approving the wrong fingerprint is
|
|
79
|
+
// how a hijack is caught, so the confirm is an explicit, human decision.
|
|
80
|
+
const rzvRoot: any = document.getElementById("rzvApprove");
|
|
81
|
+
const rzvWait: any = document.getElementById("rzvWait");
|
|
82
|
+
const rzvConfirm: any = document.getElementById("rzvConfirm");
|
|
83
|
+
const rzvFp: any = document.getElementById("rzvFp");
|
|
84
|
+
const rzvApproveBtn: any = document.getElementById("rzvApproveBtn");
|
|
85
|
+
const rzvDenyBtn: any = document.getElementById("rzvDenyBtn");
|
|
86
|
+
const rzvDone: any = document.getElementById("rzvDone");
|
|
87
|
+
let rzvTimer: ReturnType<typeof setTimeout> | null = null;
|
|
88
|
+
let rzvCode: string | null = null;
|
|
89
|
+
let rzvDecided = false;
|
|
90
|
+
function rzvShow(el: any) { if (el) el.hidden = false; }
|
|
91
|
+
function rzvHide(el: any) { if (el) el.hidden = true; }
|
|
92
|
+
// Once the terminal has ATTACHED, the command has plainly been pasted and
|
|
93
|
+
// run — hide the copy affordances so the approve prompt is the page's
|
|
94
|
+
// single focus (DZ 2026-08-18). An EXPIRED sign-in restores the generate
|
|
95
|
+
// button (a fresh mint is needed); the stale command's copy icon stays gone.
|
|
96
|
+
function rzvHideCopyCtas() {
|
|
97
|
+
if (btn) btn.hidden = true;
|
|
98
|
+
if (copyIcon) copyIcon.hidden = true;
|
|
99
|
+
}
|
|
100
|
+
function rzvRestoreGenerate() {
|
|
101
|
+
if (btn) btn.hidden = false;
|
|
102
|
+
}
|
|
103
|
+
function rzvStopPoll() { if (rzvTimer) { clearTimeout(rzvTimer); rzvTimer = null; } }
|
|
104
|
+
function rzvFinish(msg: string) {
|
|
105
|
+
rzvStopPoll(); rzvDecided = true;
|
|
106
|
+
rzvHide(rzvWait); rzvHide(rzvConfirm);
|
|
107
|
+
if (rzvDone) { rzvDone.textContent = msg; rzvDone.hidden = false; }
|
|
108
|
+
}
|
|
109
|
+
async function rzvPoll(): Promise<void> {
|
|
110
|
+
if (rzvDecided) return;
|
|
111
|
+
let r: Response, b: any;
|
|
112
|
+
try {
|
|
113
|
+
r = await fetch(basePath + "/api/dev/cli-signin-status", {
|
|
114
|
+
method: "POST", headers: { "content-type": "application/json" },
|
|
115
|
+
body: JSON.stringify({ rendezvousCode: rzvCode }),
|
|
116
|
+
});
|
|
117
|
+
b = await r.json().catch(() => ({}));
|
|
118
|
+
} catch (e) { rzvTimer = setTimeout(rzvPoll, 2800); return; }
|
|
119
|
+
if (!r || !r.ok) { rzvTimer = setTimeout(rzvPoll, 3200); return; }
|
|
120
|
+
if (b.state === "attached" && b.fingerprint) {
|
|
121
|
+
rzvHideCopyCtas();
|
|
122
|
+
rzvHide(rzvWait); rzvShow(rzvConfirm);
|
|
123
|
+
if (rzvFp) { rzvFp.textContent = b.fingerprint; rzvFp.setAttribute("data-fp", b.fingerprint); }
|
|
124
|
+
return; // stop polling — wait for the human decision
|
|
125
|
+
}
|
|
126
|
+
if (b.state === "approved") { rzvHideCopyCtas(); rzvFinish("Signed in ✓ — your terminal is good to go."); return; }
|
|
127
|
+
if (b.state === "denied") { rzvHideCopyCtas(); rzvFinish("Rejected — that terminal wasn't approved. You're safe; generate a fresh command to try again."); return; }
|
|
128
|
+
if (b.state === "expired") { rzvRestoreGenerate(); rzvFinish("This sign-in expired. Generate a fresh setup command above."); return; }
|
|
129
|
+
rzvTimer = setTimeout(rzvPoll, 2500); // pending → keep watching
|
|
130
|
+
}
|
|
131
|
+
function rzvStart(code: string) {
|
|
132
|
+
rzvCode = code; rzvDecided = false;
|
|
133
|
+
if (rzvApproveBtn) rzvApproveBtn.disabled = false;
|
|
134
|
+
if (rzvDenyBtn) rzvDenyBtn.disabled = false;
|
|
135
|
+
rzvShow(rzvRoot); rzvShow(rzvWait); rzvHide(rzvConfirm); rzvHide(rzvDone);
|
|
136
|
+
rzvStopPoll(); rzvPoll();
|
|
137
|
+
}
|
|
138
|
+
async function rzvDecide(decision: string): Promise<void> {
|
|
139
|
+
if (rzvDecided) return;
|
|
140
|
+
const fp = rzvFp ? (rzvFp.getAttribute("data-fp") || "") : "";
|
|
141
|
+
if (rzvApproveBtn) rzvApproveBtn.disabled = true;
|
|
142
|
+
if (rzvDenyBtn) rzvDenyBtn.disabled = true;
|
|
143
|
+
let r: Response;
|
|
144
|
+
try {
|
|
145
|
+
r = await fetch(basePath + "/api/dev/cli-signin-approve", {
|
|
146
|
+
method: "POST", headers: { "content-type": "application/json" },
|
|
147
|
+
body: JSON.stringify({ rendezvousCode: rzvCode, fingerprint: fp, decision: decision }),
|
|
148
|
+
});
|
|
149
|
+
} catch (e) {
|
|
150
|
+
if (rzvApproveBtn) rzvApproveBtn.disabled = false;
|
|
151
|
+
if (rzvDenyBtn) rzvDenyBtn.disabled = false;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (decision === "deny") { rzvFinish("Rejected — that terminal wasn't approved."); return; }
|
|
155
|
+
if (r.ok) { rzvFinish("Signed in ✓ — your terminal is good to go."); return; }
|
|
156
|
+
if (rzvApproveBtn) rzvApproveBtn.disabled = false;
|
|
157
|
+
if (rzvDenyBtn) rzvDenyBtn.disabled = false;
|
|
158
|
+
const m = r.status === 403 ? "That didn't match your identity — generate a fresh command and try again."
|
|
159
|
+
: r.status === 409 ? "That code didn't match a waiting terminal — re-check it, or generate a fresh command."
|
|
160
|
+
: "Couldn't complete approval — try again.";
|
|
161
|
+
if (rzvDone) { rzvDone.textContent = m; rzvDone.hidden = false; }
|
|
162
|
+
}
|
|
163
|
+
if (rzvApproveBtn) rzvApproveBtn.addEventListener("click", () => { rzvDecide("approve"); });
|
|
164
|
+
if (rzvDenyBtn) rzvDenyBtn.addEventListener("click", () => { rzvDecide("deny"); });
|
|
165
|
+
|
|
166
|
+
if (!fastpath) {
|
|
167
|
+
btn.addEventListener("click", () => {
|
|
168
|
+
copy(generic).then((ok) => {
|
|
169
|
+
label(ok ? " Copied — paste & press Enter" : " Copy the command");
|
|
170
|
+
ping(ok ? "Copied the command" : "Select the command to copy");
|
|
171
|
+
setTimeout(() => { label(" Copy the command"); }, 2600);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let busy = false;
|
|
178
|
+
btn.addEventListener("click", async () => {
|
|
179
|
+
if (busy) return;
|
|
180
|
+
busy = true;
|
|
181
|
+
label(" …"); btn.disabled = true;
|
|
182
|
+
let res: Response, body: any;
|
|
183
|
+
try {
|
|
184
|
+
res = await fetch(basePath + "/api/dev/cli-signin-code", {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: { "content-type": "application/json" },
|
|
187
|
+
body: "{}",
|
|
188
|
+
});
|
|
189
|
+
body = await res.json().catch(() => ({}));
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (note) note.textContent = "Couldn't reach the sign-in service — try again in a moment.";
|
|
192
|
+
label(" Copy the command"); btn.disabled = false; busy = false;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!res.ok || !body.command) {
|
|
196
|
+
const byStatus: Record<number, string> = {
|
|
197
|
+
401: "Your session expired — reload the page and sign in again.",
|
|
198
|
+
403: "This account doesn't have developer access to a store yet.",
|
|
199
|
+
409: "Your store isn't set up yet — reload this page for its status.",
|
|
200
|
+
502: "The sign-in service is having a moment — try again.",
|
|
201
|
+
503: "Terminal sign-in isn't configured on this environment.",
|
|
202
|
+
};
|
|
203
|
+
if (note) note.textContent = byStatus[res.status] || "Couldn't mint a sign-in command — try again.";
|
|
204
|
+
label(" Copy the command"); btn.disabled = false; busy = false;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// Reveal the real command (carries the non-secret rendezvous handle) as a pure
|
|
208
|
+
// text node, then copy it.
|
|
209
|
+
textEl.textContent = body.command;
|
|
210
|
+
textEl.classList.remove("is-placeholder");
|
|
211
|
+
revealIcon();
|
|
212
|
+
// Start watching for the terminal to attach so we can prompt for approval.
|
|
213
|
+
if (body.rendezvousCode) rzvStart(body.rendezvousCode);
|
|
214
|
+
const ok = await copy(body.command);
|
|
215
|
+
if (ok) flashCopied();
|
|
216
|
+
label(ok ? " Copied ✓" : " Select & copy");
|
|
217
|
+
ping(ok ? "Copied — paste & press Enter" : "Select the command above to copy");
|
|
218
|
+
if (note) {
|
|
219
|
+
note.textContent = ok
|
|
220
|
+
? "Copied. Paste into your terminal and press Enter — it signs you in, runs your store, and opens your browser. Single-use, short-lived code."
|
|
221
|
+
: "Select the command above and copy it, then paste into your terminal and press Enter. Single-use, short-lived code.";
|
|
222
|
+
}
|
|
223
|
+
btn.disabled = false;
|
|
224
|
+
setTimeout(() => { label(" Copy the command"); }, 4000);
|
|
225
|
+
busy = false;
|
|
226
|
+
});
|
|
227
|
+
}
|