@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,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hosted /dev activity log (hosted-dev-activity-bridge). Polls
|
|
3
|
+
* GET /api/dev/activity — a translated, file-level feed of what the local dev
|
|
4
|
+
* loop reported — and renders it newest-first. Also drives the "your edit
|
|
5
|
+
* landed" celebration: the first poll primes a baseline (sessionStorage, same
|
|
6
|
+
* survive-a-reload rationale as the local stepper's editBase), and any LATER
|
|
7
|
+
* event past that baseline triggers the banner + a lightweight confetti
|
|
8
|
+
* burst. POLL (not SSE): Workers are stateless per request, so a redeploy
|
|
9
|
+
* mid-session needs no reconnect logic here. Imported from a processed
|
|
10
|
+
* `<script>` in cockpit.astro (ADR 0013), rendered only when hostedActivity
|
|
11
|
+
* (never alongside the isLocalDevLoop-gated local scripts). basePath is read
|
|
12
|
+
* from `document.body.dataset`.
|
|
13
|
+
*/
|
|
14
|
+
export function initHostedActivity(): void {
|
|
15
|
+
const root = document.getElementById("hostedActivity");
|
|
16
|
+
if (!root) return;
|
|
17
|
+
|
|
18
|
+
// The request's tenant prefix ("" host-routed, "/<tenant>" path-routed). Every
|
|
19
|
+
// /api/dev/* call below carries it so the activity feed + the CLI sign-in-code mint
|
|
20
|
+
// run in THIS store's scope (locals.tenant on the API request) — the same store this
|
|
21
|
+
// /<tenant>/cockpit rendered for, never the session's first membership.
|
|
22
|
+
const basePath = document.body.dataset.cockpitBasePath || "";
|
|
23
|
+
|
|
24
|
+
// ?reset — a TRUE reset for re-testing the onboarding loop: clear THIS browser's
|
|
25
|
+
// stepper progress AND the server-side KV (live-runtime record + activity buffer),
|
|
26
|
+
// then reload clean. Clearing the server too means a stopped local loop stops reading
|
|
27
|
+
// as "up" immediately instead of lingering until the runtime TTL/live-window expires.
|
|
28
|
+
try {
|
|
29
|
+
if (/[?&]reset\b/.test(location.search)) {
|
|
30
|
+
["tot.hosted.steps", "tot.hosted.current", "tot.hosted.editBase"].forEach((k) => {
|
|
31
|
+
try { window.sessionStorage.removeItem(k); } catch (e) {}
|
|
32
|
+
});
|
|
33
|
+
let reloaded = false;
|
|
34
|
+
const doneReset = () => { if (reloaded) return; reloaded = true; location.replace(location.pathname); };
|
|
35
|
+
setTimeout(doneReset, 2000); // fallback so a hung request still lands us fresh
|
|
36
|
+
try { fetch(basePath + "/api/dev/activity", { method: "DELETE", cache: "no-store" }).then(doneReset, doneReset); }
|
|
37
|
+
catch (e) { doneReset(); }
|
|
38
|
+
return; // the post-clear reload re-runs this script against a fresh, empty state
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {}
|
|
41
|
+
|
|
42
|
+
const tableEl: any = document.getElementById("haTable");
|
|
43
|
+
const bodyEl: any = document.getElementById("haBody");
|
|
44
|
+
const emptyEl: any = document.getElementById("haEmpty");
|
|
45
|
+
|
|
46
|
+
// Bridge strip — the rail's power source, fed by the SAME poll's `runtime` object
|
|
47
|
+
// (no second poller). `runtime` is { cliVersion, runnerVersion, url, aliveAt, live }
|
|
48
|
+
// reported by the developer's local `tot` CLI; absent until its data layer lands,
|
|
49
|
+
// so every read here is defensive (missing runtime -> the strip stays hidden).
|
|
50
|
+
const bridge: any = document.getElementById("bridge");
|
|
51
|
+
const bUrl: any = document.getElementById("bUrl");
|
|
52
|
+
const bGo: any = document.getElementById("bGo");
|
|
53
|
+
const bKicker = document.getElementById("bKicker");
|
|
54
|
+
const bHint: any = document.getElementById("bHint");
|
|
55
|
+
const bDiag: any = document.getElementById("bDiag");
|
|
56
|
+
const bTxt: any = document.getElementById("bTxt");
|
|
57
|
+
const bObstacle: any = document.getElementById("bObstacle");
|
|
58
|
+
const bObTitle = document.getElementById("bObTitle");
|
|
59
|
+
const bObMsg = document.getElementById("bObMsg");
|
|
60
|
+
const bObCmd: any = document.getElementById("bObCmd");
|
|
61
|
+
const bObNote = document.getElementById("bObNote");
|
|
62
|
+
const doneRunMeta = document.getElementById("doneRunMeta");
|
|
63
|
+
let sawLive = false;
|
|
64
|
+
function updateBridge(rt: any, obstacle: any) {
|
|
65
|
+
if (!bridge) return;
|
|
66
|
+
const live = !!(rt && rt.live);
|
|
67
|
+
if (live) sawLive = true;
|
|
68
|
+
// Obstacle lane (v2 §6): a failed `tot start` is the ONE reason the strip
|
|
69
|
+
// shows BEFORE the loop was ever live this page-view — the developer needs the
|
|
70
|
+
// fix, not a silent "watching…". The server returns `obstacle` only when there
|
|
71
|
+
// is no live heartbeat, so a live loop supersedes it. Fill from server copy.
|
|
72
|
+
if (obstacle && !live) {
|
|
73
|
+
bridge.hidden = false;
|
|
74
|
+
bridge.setAttribute("data-live", "obstacle");
|
|
75
|
+
if (bTxt) bTxt.hidden = true;
|
|
76
|
+
if (bDiag) bDiag.hidden = true;
|
|
77
|
+
if (bGo) bGo.hidden = true;
|
|
78
|
+
if (bHint) bHint.hidden = true;
|
|
79
|
+
if (bObstacle) bObstacle.hidden = false;
|
|
80
|
+
if (bObTitle) bObTitle.textContent = obstacle.title || "Setup hit a snag";
|
|
81
|
+
if (bObMsg) bObMsg.textContent = obstacle.message || "";
|
|
82
|
+
if (bObCmd) {
|
|
83
|
+
if (obstacle.fixCommand) { bObCmd.textContent = obstacle.fixCommand; bObCmd.hidden = false; }
|
|
84
|
+
else bObCmd.hidden = true;
|
|
85
|
+
}
|
|
86
|
+
if (bObNote) bObNote.textContent = obstacle.fixNote || "";
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Back to the normal live/quiet strip: restore the pieces the obstacle state hid.
|
|
90
|
+
if (bObstacle) bObstacle.hidden = true;
|
|
91
|
+
if (bTxt) bTxt.hidden = false;
|
|
92
|
+
if (!rt || !rt.url) { bridge.hidden = true; return; }
|
|
93
|
+
// On FIRST arrival, a stale runtime record (offline, never seen live this
|
|
94
|
+
// page-view — e.g. left over from a prior run, still within the heartbeat TTL)
|
|
95
|
+
// must NOT show — let Step 1 "Get your store running" do the work. Only surface
|
|
96
|
+
// the strip once it's live, or after it's gone quiet HAVING been live this page-view.
|
|
97
|
+
if (!live && !sawLive) { bridge.hidden = true; return; }
|
|
98
|
+
bridge.hidden = false;
|
|
99
|
+
bridge.setAttribute("data-live", live ? "1" : "0");
|
|
100
|
+
if (bUrl) { bUrl.textContent = rt.url; bUrl.href = rt.url; }
|
|
101
|
+
// No point offering "Open your store" to a server that stopped answering.
|
|
102
|
+
if (bGo) { bGo.href = rt.url; bGo.hidden = !live; }
|
|
103
|
+
if (bKicker) bKicker.textContent = live ? "Live on your machine" : "Went quiet";
|
|
104
|
+
if (bHint) bHint.hidden = live; // recovery hint only while offline
|
|
105
|
+
if (doneRunMeta) doneRunMeta.textContent = rt.url; // the collapsed Step-1 row's meta
|
|
106
|
+
if (bDiag) {
|
|
107
|
+
if (rt.cliVersion) {
|
|
108
|
+
bDiag.textContent = "tot CLI v" + rt.cliVersion + (rt.runnerVersion ? " · runner v" + rt.runnerVersion : "");
|
|
109
|
+
bDiag.hidden = false;
|
|
110
|
+
} else {
|
|
111
|
+
bDiag.hidden = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
updateEditHint(rt); // suggest the exact "open this file" command on Step 2
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Item 6 — once the CLI reports the developer's $EDITOR, show the EXACT command to
|
|
118
|
+
// open the first file to edit (e.g. `code content/home.html`). Without one, guess
|
|
119
|
+
// from the browser's OS (a reasonable proxy for the local dev machine in the common
|
|
120
|
+
// case of viewing the cockpit on the same laptop) and default to VS Code (`code`) —
|
|
121
|
+
// the most common case — with an alternate + AI-agent note underneath.
|
|
122
|
+
const editOpenCmd = document.getElementById("editOpenCmd");
|
|
123
|
+
const editOpenHint = document.getElementById("editOpenHint");
|
|
124
|
+
function platformOpenDirCmd(dirArg: string) {
|
|
125
|
+
const plat = String(((navigator as any).userAgentData && (navigator as any).userAgentData.platform) || navigator.platform || navigator.userAgent || "");
|
|
126
|
+
if (/win/i.test(plat)) return "explorer " + dirArg;
|
|
127
|
+
if (/mac/i.test(plat)) return "open " + dirArg;
|
|
128
|
+
return "xdg-open " + dirArg;
|
|
129
|
+
}
|
|
130
|
+
function updateEditHint(rt: any) {
|
|
131
|
+
if (!editOpenCmd) return;
|
|
132
|
+
const editor = rt && typeof rt.editor === "string" ? rt.editor : "";
|
|
133
|
+
const cwd = rt && typeof rt.cwd === "string" ? rt.cwd : "";
|
|
134
|
+
// Prefer the FULL path (works from any directory) when the CLI reports the
|
|
135
|
+
// project dir; else a project-relative path with a "run it from there" note.
|
|
136
|
+
const file = cwd ? (cwd.replace(/\/+$/, "") + "/content/home.html") : "content/home.html";
|
|
137
|
+
editOpenCmd.textContent = (editor || "code") + " " + file;
|
|
138
|
+
if (editOpenHint) {
|
|
139
|
+
if (editor) {
|
|
140
|
+
editOpenHint.textContent = cwd
|
|
141
|
+
? "Run that anywhere to open the file — then change a line and save."
|
|
142
|
+
: "Run that from your project directory — then change a line and save.";
|
|
143
|
+
} else {
|
|
144
|
+
const altCmd = platformOpenDirCmd(cwd || ".");
|
|
145
|
+
editOpenHint.textContent = "Using VS Code? Run that. Prefer Cursor, another editor, or an AI coding " +
|
|
146
|
+
"agent like Claude? Same idea — open the folder (`" + altCmd + "`) or file above and edit there. " +
|
|
147
|
+
"Set $EDITOR and we'll show your exact command instead.";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const SS = window.sessionStorage;
|
|
153
|
+
function load(k: string, d: any) { try { const v = SS.getItem(k); return v === null ? d : JSON.parse(v); } catch (e) { return d; } }
|
|
154
|
+
function put(k: string, v: any) { try { SS.setItem(k, JSON.stringify(v)); } catch (e) {} }
|
|
155
|
+
|
|
156
|
+
let baseline = load("tot.hosted.editBase", null); // null = not yet primed by a first poll
|
|
157
|
+
let expanded = false; // activity list: last 3 by default, expand to all
|
|
158
|
+
// Have we seen the store go live during THIS PAGE VIEW? In-memory ONLY (not
|
|
159
|
+
// persisted): a fresh land or a reload starts false, so a stale/offline record can
|
|
160
|
+
// never greet the developer with "went quiet" — Step 1 owns that moment. The
|
|
161
|
+
// recovery ("went quiet") state shows only after we actually watched it live → drop.
|
|
162
|
+
|
|
163
|
+
// Guided accordion. Each step opens with its content; when its activity is detected
|
|
164
|
+
// it animates CLOSED to a reopenable done-bar, and a single "Next Up" panel at the
|
|
165
|
+
// bottom expands to tease the following step with ONE CTA that opens it.
|
|
166
|
+
// steps = completion flags {run, edit}
|
|
167
|
+
// current = the OPEN step key, or null between steps (Next Up showing)
|
|
168
|
+
// Both persisted so a reload restores the exact place in the flow.
|
|
169
|
+
const steps = load("tot.hosted.steps", { run: false, edit: false });
|
|
170
|
+
// Live-driven flow. current is the EXPLICITLY-opened step ("run" when Step 1 was
|
|
171
|
+
// reopened, "edit"/"next" for those) or null — the default view, where Step 1's
|
|
172
|
+
// open/minimized state is driven by whether localhost is LIVE. NOT persisted: a
|
|
173
|
+
// refresh lands in this default view (progress in steps still persists).
|
|
174
|
+
let current: string | null = null;
|
|
175
|
+
let liveNow = false; // is localhost live right now? set each poll from runtime.live
|
|
176
|
+
const bodies: any = {
|
|
177
|
+
run: document.getElementById("stage-run"),
|
|
178
|
+
edit: document.getElementById("hosted-stage-edit"),
|
|
179
|
+
next: document.getElementById("hosted-stage-next"),
|
|
180
|
+
};
|
|
181
|
+
// Rail station elements — the reskin's DOM (applyView targets these).
|
|
182
|
+
const stEls: any = {
|
|
183
|
+
run: document.getElementById("st-run"),
|
|
184
|
+
edit: document.getElementById("st-edit"),
|
|
185
|
+
next: document.getElementById("st-next"),
|
|
186
|
+
};
|
|
187
|
+
const segRun: any = document.getElementById("seg-run");
|
|
188
|
+
const segEdit: any = document.getElementById("seg-edit");
|
|
189
|
+
const doneRunEl: any = document.getElementById("done-run");
|
|
190
|
+
const doneEditEl: any = document.getElementById("done-edit");
|
|
191
|
+
const doneEditMeta = document.getElementById("doneEditMeta");
|
|
192
|
+
const lockEditEl: any = document.getElementById("lock-edit");
|
|
193
|
+
const lockNextEl: any = document.getElementById("lock-next");
|
|
194
|
+
const teaseEditEl: any = document.getElementById("tease-edit");
|
|
195
|
+
const teaseNextEl: any = document.getElementById("tease-next");
|
|
196
|
+
const quietNoteEl: any = document.getElementById("quietNote");
|
|
197
|
+
|
|
198
|
+
// Persisted state mutations funnel through here so the sessionStorage write can
|
|
199
|
+
// never be forgotten at a call site (progress in steps + the edit baseline are
|
|
200
|
+
// the only things that survive a reload).
|
|
201
|
+
function completeStep(key: string) {
|
|
202
|
+
if (steps[key]) return false; // already done — no-op
|
|
203
|
+
steps[key] = true; put("tot.hosted.steps", steps);
|
|
204
|
+
return true; // "just completed" — caller may celebrate
|
|
205
|
+
}
|
|
206
|
+
function setBaseline(v: number) { baseline = v; put("tot.hosted.editBase", baseline); }
|
|
207
|
+
|
|
208
|
+
// ============================================================================
|
|
209
|
+
// STATE → VIEW → DOM. The flow's behaviour is split into three layers so the
|
|
210
|
+
// rules live in ONE readable place and every render goes through one path:
|
|
211
|
+
// 1. derive — computeView(): pure, reads state (steps/current/liveNow),
|
|
212
|
+
// returns a view model. EVERY visibility/emphasis rule is here.
|
|
213
|
+
// 2. apply — applyView(): the ONLY code that mutates the rail DOM (station
|
|
214
|
+
// statuses, segment lit/flow, step bodies, done rows, locked
|
|
215
|
+
// labels, teasers, quiet note, activity).
|
|
216
|
+
// 3. renderSteps(): apply(compute()). Call after ANY state change — never poke
|
|
217
|
+
// an element directly from a handler or the poll.
|
|
218
|
+
// The signal-rail reskin kept layer 1's RULES verbatim (only added derived
|
|
219
|
+
// fields) and rewrote applyView() + the markup/CSS it targets.
|
|
220
|
+
// ============================================================================
|
|
221
|
+
|
|
222
|
+
// --- derive ---------------------------------------------------------------
|
|
223
|
+
function unlocked(step: string) {
|
|
224
|
+
return step === "run" || (step === "edit" && steps.run) || (step === "next" && steps.edit);
|
|
225
|
+
}
|
|
226
|
+
// Step 1 is OPEN while you still need to get the store running (localhost not live) or
|
|
227
|
+
// when you explicitly reopened it; it MINIMIZES to a done row once localhost is live —
|
|
228
|
+
// in opposition to revealing the Step-2 teaser.
|
|
229
|
+
function step1Open() {
|
|
230
|
+
return current === "run" || (current === null && !(steps.run && liveNow));
|
|
231
|
+
}
|
|
232
|
+
// The complete view model — no DOM access, so the rules can be read top to bottom:
|
|
233
|
+
// open which step BODY is expanded {run, edit, next}
|
|
234
|
+
// seg which rail segment is LIT {run, edit}
|
|
235
|
+
// flow do lit segments flow downward (live) boolean
|
|
236
|
+
// stRun/Edit/Next each station's status "open" | "done" | "next" | "locked"
|
|
237
|
+
// teaseEdit/Next is that step's forward teaser shown (live-gated)
|
|
238
|
+
// doneRun/Edit is that step's collapsed done row shown
|
|
239
|
+
// lockEdit/Next is that step's locked label shown
|
|
240
|
+
// quietNote the Step-1 "went quiet" note (re-inflated after a drop)
|
|
241
|
+
// showActivity is "Your activity" visible (NEVER while Step 1 is open)
|
|
242
|
+
function computeView() {
|
|
243
|
+
const s1 = step1Open();
|
|
244
|
+
// Forward teasers show ONLY while localhost is live — a forward CTA only makes
|
|
245
|
+
// sense while the loop is alive. When the loop goes quiet Step 1 re-inflates to own
|
|
246
|
+
// that moment, so the teaser retracts. Either is also hidden while the step it
|
|
247
|
+
// teases is already open.
|
|
248
|
+
const teaseEdit = steps.run && !steps.edit && liveNow && current !== "edit";
|
|
249
|
+
const teaseNext = steps.edit && liveNow && current !== "next";
|
|
250
|
+
return {
|
|
251
|
+
open: { run: s1, edit: current === "edit", next: current === "next" },
|
|
252
|
+
seg: { run: steps.run, edit: steps.edit },
|
|
253
|
+
flow: liveNow,
|
|
254
|
+
stRun: s1 ? "open" : "done",
|
|
255
|
+
stEdit: steps.edit ? "done" : (current === "edit") ? "open" : teaseEdit ? "next" : "locked",
|
|
256
|
+
stNext: (current === "next") ? "open" : teaseNext ? "next" : "locked",
|
|
257
|
+
teaseEdit: teaseEdit,
|
|
258
|
+
teaseNext: teaseNext,
|
|
259
|
+
doneRun: !s1 && steps.run,
|
|
260
|
+
doneEdit: steps.edit && current !== "edit",
|
|
261
|
+
lockEdit: !steps.run,
|
|
262
|
+
lockNext: !steps.edit,
|
|
263
|
+
quietNote: s1 && steps.run && !liveNow && sawLive,
|
|
264
|
+
showActivity: !s1 && steps.run,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// --- apply (targets the rail DOM; the rules live in computeView above) -----
|
|
269
|
+
function applyView(v: ReturnType<typeof computeView>) {
|
|
270
|
+
if (stEls.run) stEls.run.setAttribute("data-st", v.stRun);
|
|
271
|
+
if (stEls.edit) stEls.edit.setAttribute("data-st", v.stEdit);
|
|
272
|
+
if (stEls.next) stEls.next.setAttribute("data-st", v.stNext);
|
|
273
|
+
if (segRun) { segRun.setAttribute("data-lit", v.seg.run ? "1" : "0"); segRun.setAttribute("data-flow", v.flow ? "1" : "0"); }
|
|
274
|
+
if (segEdit) { segEdit.setAttribute("data-lit", v.seg.edit ? "1" : "0"); segEdit.setAttribute("data-flow", v.flow ? "1" : "0"); }
|
|
275
|
+
if (bodies.run) bodies.run.hidden = !v.open.run;
|
|
276
|
+
if (bodies.edit) bodies.edit.hidden = !v.open.edit;
|
|
277
|
+
if (bodies.next) bodies.next.hidden = !v.open.next;
|
|
278
|
+
if (doneRunEl) doneRunEl.hidden = !v.doneRun;
|
|
279
|
+
if (doneEditEl) doneEditEl.hidden = !v.doneEdit;
|
|
280
|
+
if (lockEditEl) lockEditEl.hidden = !v.lockEdit;
|
|
281
|
+
if (lockNextEl) lockNextEl.hidden = !v.lockNext;
|
|
282
|
+
if (teaseEditEl) teaseEditEl.hidden = !v.teaseEdit;
|
|
283
|
+
if (teaseNextEl) teaseNextEl.hidden = !v.teaseNext;
|
|
284
|
+
if (quietNoteEl) quietNoteEl.hidden = !v.quietNote;
|
|
285
|
+
if (root) (root as any).hidden = !v.showActivity;
|
|
286
|
+
}
|
|
287
|
+
// Single render entry point — derive, then apply. (Named renderSteps so the
|
|
288
|
+
// existing call sites keep working; distinct from the activity render(events).)
|
|
289
|
+
function renderSteps() { applyView(computeView()); }
|
|
290
|
+
|
|
291
|
+
function expandStage(el: any) {
|
|
292
|
+
if (!el) return;
|
|
293
|
+
el.hidden = false;
|
|
294
|
+
el.classList.add("expanding");
|
|
295
|
+
setTimeout(() => { if (el && el.classList) el.classList.remove("expanding"); }, 420);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Open a step (from a teaser CTA or a collapsed done row) — animates it in.
|
|
299
|
+
function goTo(step: string) {
|
|
300
|
+
if (!unlocked(step)) return;
|
|
301
|
+
current = step;
|
|
302
|
+
renderSteps();
|
|
303
|
+
expandStage(bodies[step]);
|
|
304
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Rail click wiring: done rows reopen their step; teaser CTAs open the teased step.
|
|
308
|
+
if (doneRunEl) doneRunEl.addEventListener("click", () => { goTo("run"); });
|
|
309
|
+
if (doneEditEl) doneEditEl.addEventListener("click", () => { goTo("edit"); });
|
|
310
|
+
const ctaEdit = document.getElementById("cta-edit");
|
|
311
|
+
if (ctaEdit) ctaEdit.addEventListener("click", () => { goTo("edit"); });
|
|
312
|
+
const ctaNext = document.getElementById("cta-next");
|
|
313
|
+
if (ctaNext) ctaNext.addEventListener("click", () => { goTo("next"); });
|
|
314
|
+
|
|
315
|
+
// Absolute clock time with seconds (e.g. "2:57:14 PM") — a fixed point, not a
|
|
316
|
+
// churning duration. Full date+time stays on the hover title (render()).
|
|
317
|
+
function rel(ts: number | string): string {
|
|
318
|
+
try {
|
|
319
|
+
return new Date(Number(ts)).toLocaleTimeString([], { hour: "numeric", minute: "2-digit", second: "2-digit" });
|
|
320
|
+
} catch (x) {
|
|
321
|
+
return "";
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Toast — reuses the shared #toast element (also driven by the copy script).
|
|
326
|
+
const toastEl = document.getElementById("toast");
|
|
327
|
+
const toastMsgEl = document.getElementById("toastMsg");
|
|
328
|
+
let toastT: ReturnType<typeof setTimeout>;
|
|
329
|
+
function toast(msg: string) {
|
|
330
|
+
if (!toastEl || !toastMsgEl) return;
|
|
331
|
+
toastMsgEl.textContent = msg;
|
|
332
|
+
toastEl.classList.add("show");
|
|
333
|
+
clearTimeout(toastT);
|
|
334
|
+
toastT = setTimeout(() => { toastEl.classList.remove("show"); }, 2600);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Celebration — replaces the full-screen confetti. Stamps the Step-2 node (spark
|
|
338
|
+
// burst + stamp, via the .celebrating class) and shows a toast. Reduced-motion
|
|
339
|
+
// disables the burst/stamp in CSS; the toast still shows.
|
|
340
|
+
function celebrate(file?: string) {
|
|
341
|
+
const st = stEls.edit;
|
|
342
|
+
if (st) {
|
|
343
|
+
st.classList.remove("celebrating");
|
|
344
|
+
void st.offsetWidth; // restart the burst animation
|
|
345
|
+
st.classList.add("celebrating");
|
|
346
|
+
setTimeout(() => { if (st && st.classList) st.classList.remove("celebrating"); }, 1100);
|
|
347
|
+
}
|
|
348
|
+
if (doneEditMeta && file) doneEditMeta.textContent = file;
|
|
349
|
+
toast("Saved " + (file || "your file") + " — your store reloaded. That's the loop.");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const toggleEl: any = document.getElementById("haToggle");
|
|
353
|
+
const VISIBLE = 3; // show the latest 3 by default; expand for the rest
|
|
354
|
+
if (toggleEl) {
|
|
355
|
+
toggleEl.addEventListener("click", () => { expanded = !expanded; render(lastEvents); });
|
|
356
|
+
}
|
|
357
|
+
let lastEvents: any[] = [];
|
|
358
|
+
|
|
359
|
+
function render(events: any[]) {
|
|
360
|
+
lastEvents = events;
|
|
361
|
+
// Item 3 — drop the "up"/"running locally" pings; the live widget at the top
|
|
362
|
+
// already conveys that. Only real FILE activity belongs in the table, and each
|
|
363
|
+
// field is its own <td> so time / action / file never run together.
|
|
364
|
+
const real = (events || []).filter((e) => e && e.event !== "up");
|
|
365
|
+
Array.prototype.forEach.call(bodyEl.querySelectorAll(".ha-row"), (n: any) => { n.remove(); });
|
|
366
|
+
if (!real.length) {
|
|
367
|
+
emptyEl.hidden = false;
|
|
368
|
+
if (tableEl) tableEl.hidden = true;
|
|
369
|
+
if (toggleEl) toggleEl.hidden = true;
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
emptyEl.hidden = true;
|
|
373
|
+
if (tableEl) tableEl.hidden = false;
|
|
374
|
+
const newestFirst = real.slice().reverse();
|
|
375
|
+
const shown = expanded ? newestFirst : newestFirst.slice(0, VISIBLE);
|
|
376
|
+
shown.forEach((e, i) => {
|
|
377
|
+
const tr = document.createElement("tr");
|
|
378
|
+
tr.className = i === 0 ? "ha-row is-new" : "ha-row"; // accent the newest row
|
|
379
|
+
const when = document.createElement("td");
|
|
380
|
+
when.className = "ha-when";
|
|
381
|
+
when.textContent = rel(e.at);
|
|
382
|
+
try { when.title = new Date(e.at).toLocaleString(); } catch (x) {} // exact time on hover
|
|
383
|
+
const desc = document.createElement("td");
|
|
384
|
+
desc.className = "ha-desc";
|
|
385
|
+
desc.textContent = e.description;
|
|
386
|
+
const file = document.createElement("td");
|
|
387
|
+
file.className = "ha-file";
|
|
388
|
+
file.textContent = e.file || ""; // the concrete file name, e.g. content/home.html
|
|
389
|
+
tr.appendChild(when); tr.appendChild(desc); tr.appendChild(file);
|
|
390
|
+
bodyEl.appendChild(tr);
|
|
391
|
+
});
|
|
392
|
+
if (toggleEl) {
|
|
393
|
+
if (real.length > VISIBLE) {
|
|
394
|
+
toggleEl.hidden = false;
|
|
395
|
+
toggleEl.textContent = expanded ? "Show fewer" : "Show all " + real.length;
|
|
396
|
+
} else {
|
|
397
|
+
toggleEl.hidden = true;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Adaptive poll cadence + visibility pause (2026-07-15 KV-quota incident: a
|
|
403
|
+
// single backgrounded tab, polling flat-out every 3s with no pause, burned
|
|
404
|
+
// through the ENTIRE account-wide daily KV read quota by itself and took
|
|
405
|
+
// down real customer traffic with 500s). Two rules:
|
|
406
|
+
// 1. HIDDEN tab → don't poll at all. A backgrounded/minimized tab can't be
|
|
407
|
+
// "watching" the loop; resume (with an immediate catch-up poll) the
|
|
408
|
+
// instant it becomes visible again.
|
|
409
|
+
// 2. VISIBLE but the loop is already fully celebrated (steps.run AND
|
|
410
|
+
// steps.edit) → the high-stakes "is it working yet" moment has passed,
|
|
411
|
+
// so back off to a slow cadence. Still visible AND still mid-loop →
|
|
412
|
+
// keep the fast cadence (this is the one case where sub-second-feeling
|
|
413
|
+
// responsiveness genuinely matters — a real developer staring at the
|
|
414
|
+
// screen waiting to see their save land).
|
|
415
|
+
const FAST_MS = 3000; // actively watching for the loop to prove itself
|
|
416
|
+
const SLOW_MS = 20000; // loop already celebrated — just an occasional glance
|
|
417
|
+
let pollTimer: ReturnType<typeof setTimeout> | null = null;
|
|
418
|
+
|
|
419
|
+
function nextDelay() {
|
|
420
|
+
return (steps.run && steps.edit) ? SLOW_MS : FAST_MS;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function scheduleNext() {
|
|
424
|
+
if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
|
|
425
|
+
if (document.hidden) return; // stay fully paused; visibilitychange resumes us
|
|
426
|
+
pollTimer = setTimeout(poll, nextDelay());
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function poll() {
|
|
430
|
+
fetch(basePath + "/api/dev/activity", { cache: "no-store" })
|
|
431
|
+
.then((r) => (r.ok ? r.json() : Promise.reject()))
|
|
432
|
+
.then((d) => {
|
|
433
|
+
const events = (d && d.events) || [];
|
|
434
|
+
const rt = d && d.runtime;
|
|
435
|
+
const isLive = !!(rt && rt.live);
|
|
436
|
+
liveNow = isLive; // drives Step 1 minimize + the Step-2 teaser visibility below
|
|
437
|
+
updateBridge(rt, d && d.obstacle); // same poll drives the bridge strip (+ obstacle lane)
|
|
438
|
+
render(events);
|
|
439
|
+
// b1 fix — Step-2 completion must key off the SAME events the feed shows:
|
|
440
|
+
// REAL file edits (event !== "up"), NOT the single newest buffer entry. The
|
|
441
|
+
// local loop interleaves recurring "up" liveness pings into this one ring
|
|
442
|
+
// buffer, so the newest event is frequently an "up" ping even right after a
|
|
443
|
+
// save. The old gate read events[last]: when an "up" ping landed AFTER the
|
|
444
|
+
// edit it (1) made lastIsEdit false so completeStep("edit") never fired, and
|
|
445
|
+
// (2) still advanced the baseline watermark past the edit's timestamp —
|
|
446
|
+
// permanently stranding Step 2 even though the save rendered in "Your
|
|
447
|
+
// activity". Filter to real edits (mirroring render()'s filter) and advance
|
|
448
|
+
// the watermark only from real edits, so a liveness ping can neither satisfy
|
|
449
|
+
// nor poison the gate.
|
|
450
|
+
const realEdits = events.filter((e: any) => e && e.event !== "up");
|
|
451
|
+
const lastEdit = realEdits.length ? realEdits[realEdits.length - 1] : null;
|
|
452
|
+
const latestEdit = lastEdit ? lastEdit.at : 0;
|
|
453
|
+
// Step 1 "Get running" completes once localhost reports LIVE (or any activity
|
|
454
|
+
// arrives). Once complete, renderSteps() minimizes Step 1 and reveals the Step-2 nudge.
|
|
455
|
+
if (isLive || events.length) completeStep("run");
|
|
456
|
+
if (baseline === null) {
|
|
457
|
+
setBaseline(latestEdit); // first poll primes the watermark from real edits — no edit inferred yet
|
|
458
|
+
} else if (latestEdit > baseline) {
|
|
459
|
+
setBaseline(latestEdit);
|
|
460
|
+
// A real FILE edit landed after the watermark → Step 2 completes (once).
|
|
461
|
+
// completeStep flips + persists once, returning true on that first poll only.
|
|
462
|
+
if (completeStep("edit")) {
|
|
463
|
+
celebrate(lastEdit && lastEdit.file);
|
|
464
|
+
if (current === "edit") current = null; // minimize Step 2 → reveal Step-3 teaser
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// ALWAYS re-render so a localhost drop hides the Step-2 nudge + reopens Step 1.
|
|
468
|
+
renderSteps();
|
|
469
|
+
})
|
|
470
|
+
.catch(() => {})
|
|
471
|
+
.then(scheduleNext); // reschedule (or not, if now hidden) regardless of outcome
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
document.addEventListener("visibilitychange", () => {
|
|
475
|
+
if (document.hidden) {
|
|
476
|
+
if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
|
|
477
|
+
} else {
|
|
478
|
+
poll(); // catch up immediately; poll() reschedules itself at the end
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// Item 7/10 — Step 3 "Submit for preview" opens the "coming soon" dialog (the
|
|
483
|
+
// compliance-reviewed preview pipeline isn't wired into this panel yet).
|
|
484
|
+
const submitBtn = document.getElementById("submitPreview");
|
|
485
|
+
const comingSoon: any = document.getElementById("comingSoon");
|
|
486
|
+
if (submitBtn && comingSoon) {
|
|
487
|
+
submitBtn.addEventListener("click", () => {
|
|
488
|
+
if (typeof comingSoon.showModal === "function") comingSoon.showModal();
|
|
489
|
+
else comingSoon.setAttribute("open", "");
|
|
490
|
+
});
|
|
491
|
+
const csClose = document.getElementById("comingSoonClose");
|
|
492
|
+
if (csClose) csClose.addEventListener("click", () => {
|
|
493
|
+
if (typeof comingSoon.close === "function") comingSoon.close();
|
|
494
|
+
comingSoon.removeAttribute("open");
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
renderSteps();
|
|
499
|
+
if (!document.hidden) poll(); // if the tab starts hidden, visibilitychange kicks it off later
|
|
500
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Info popover: toggle on the ⓘ button, dismiss on outside click. Imported
|
|
3
|
+
* from a processed `<script>` in cockpit.astro.
|
|
4
|
+
*/
|
|
5
|
+
export function initInfoPopover(): void {
|
|
6
|
+
const info1 = document.getElementById("info1");
|
|
7
|
+
if (!info1) return;
|
|
8
|
+
const openBtn = info1.querySelector(".info-btn");
|
|
9
|
+
if (!openBtn) return;
|
|
10
|
+
openBtn.addEventListener("click", (e) => { e.stopPropagation(); info1.classList.toggle("open"); });
|
|
11
|
+
document.addEventListener("click", (e) => { if (!info1.contains(e.target as Node)) info1.classList.remove("open"); });
|
|
12
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy the "connect the MCP" command (d3-unhold-dev-step0-connect-mcp). Static
|
|
3
|
+
* text (no minting), so unlike the setup-command copy button (copyCommand.ts)
|
|
4
|
+
* this button is visible from load. Imported from a processed `<script>` in
|
|
5
|
+
* cockpit.astro.
|
|
6
|
+
*/
|
|
7
|
+
export function initMcpCopy(): void {
|
|
8
|
+
const copyIcon = document.getElementById("mcpCopyIcon");
|
|
9
|
+
const cmdText = document.getElementById("mcpCmdText");
|
|
10
|
+
const toast = document.getElementById("toast");
|
|
11
|
+
const toastMsg = document.getElementById("toastMsg");
|
|
12
|
+
if (!copyIcon || !cmdText) return;
|
|
13
|
+
let tT: ReturnType<typeof setTimeout>;
|
|
14
|
+
function ping(m: string) {
|
|
15
|
+
if (!toast || !toastMsg) return;
|
|
16
|
+
toastMsg.textContent = m; toast.classList.add("show");
|
|
17
|
+
clearTimeout(tT); tT = setTimeout(() => { toast.classList.remove("show"); }, 1900);
|
|
18
|
+
}
|
|
19
|
+
copyIcon.addEventListener("click", () => {
|
|
20
|
+
const text = cmdText.textContent || "";
|
|
21
|
+
const done = navigator.clipboard
|
|
22
|
+
? navigator.clipboard.writeText(text).then(() => true).catch(() => false)
|
|
23
|
+
: Promise.resolve(false);
|
|
24
|
+
done.then((ok) => {
|
|
25
|
+
if (ok) {
|
|
26
|
+
copyIcon.setAttribute("data-copied", "1");
|
|
27
|
+
setTimeout(() => { copyIcon.removeAttribute("data-copied"); }, 1600);
|
|
28
|
+
ping("Copied");
|
|
29
|
+
} else {
|
|
30
|
+
ping("Select the command above to copy");
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live loop Monitor — polls the local dev server's /__tot/dev-loop-status
|
|
3
|
+
* and renders the loopbar: server health, watched file, and the save→reload
|
|
4
|
+
* timeline. Imported from a processed `<script>` in cockpit.astro (ADR 0013),
|
|
5
|
+
* rendered only when isLocalDevLoop (the storefront's own `astro dev`, never
|
|
6
|
+
* the hosted panel). Broadcasts window.__totLoop + the "tot:loop-state"
|
|
7
|
+
* CustomEvent so the stepper and ask widgets share this one poll instead of
|
|
8
|
+
* each opening their own. All dynamic values go through textContent, never
|
|
9
|
+
* innerHTML.
|
|
10
|
+
*/
|
|
11
|
+
declare global {
|
|
12
|
+
interface Window {
|
|
13
|
+
__totLoop?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function initMonitor(): void {
|
|
18
|
+
const bar: any = document.getElementById("loopbar");
|
|
19
|
+
if (!bar) return;
|
|
20
|
+
const elStatus: any = document.getElementById("lbStatus");
|
|
21
|
+
const elWatch: any = document.getElementById("lbWatch");
|
|
22
|
+
const elLast: any = document.getElementById("lbLast");
|
|
23
|
+
const elServer: any = document.getElementById("lbServer");
|
|
24
|
+
const elUrl: any = document.getElementById("lbUrl");
|
|
25
|
+
const elFile: any = document.getElementById("lbFile");
|
|
26
|
+
const elSave: any = document.getElementById("lbSave");
|
|
27
|
+
const elReload: any = document.getElementById("lbReload");
|
|
28
|
+
const head = document.getElementById("lbHead");
|
|
29
|
+
|
|
30
|
+
if (head) head.addEventListener("click", () => { bar.classList.toggle("open"); });
|
|
31
|
+
|
|
32
|
+
function rel(ts: number | string): string {
|
|
33
|
+
const d = Math.max(0, Math.round((Date.now() - Number(ts)) / 1000));
|
|
34
|
+
return d < 2 ? "just now" : d < 60 ? d + "s ago" : Math.floor(d / 60) + "m ago";
|
|
35
|
+
}
|
|
36
|
+
function setTs(el: any, ts: number | undefined, emptyText: string) {
|
|
37
|
+
if (ts) {
|
|
38
|
+
el.setAttribute("data-ts", String(ts));
|
|
39
|
+
el.classList.remove("dim");
|
|
40
|
+
el.textContent = rel(ts);
|
|
41
|
+
} else {
|
|
42
|
+
el.removeAttribute("data-ts");
|
|
43
|
+
el.classList.add("dim");
|
|
44
|
+
el.textContent = emptyText;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
setInterval(() => {
|
|
48
|
+
[elSave, elReload].forEach((el) => {
|
|
49
|
+
const ts = el.getAttribute("data-ts");
|
|
50
|
+
if (ts) el.textContent = rel(ts);
|
|
51
|
+
});
|
|
52
|
+
}, 1000);
|
|
53
|
+
|
|
54
|
+
function render(s: any) {
|
|
55
|
+
bar.classList.add("live");
|
|
56
|
+
const port = s.port || null;
|
|
57
|
+
const url = port ? ("localhost:" + port + (s.tenant ? "/" + s.tenant + "/" : "/")) : "—";
|
|
58
|
+
elServer.textContent = s.server ? ("running" + (port ? " · port " + port : "")) : "not running";
|
|
59
|
+
elServer.classList.toggle("dim", !s.server);
|
|
60
|
+
elUrl.textContent = url;
|
|
61
|
+
elUrl.classList.toggle("dim", !port);
|
|
62
|
+
elFile.textContent = s.watchedFile || "content/*.html";
|
|
63
|
+
elFile.classList.toggle("dim", !s.watchedFile);
|
|
64
|
+
elWatch.textContent = s.watchedFile || "your content";
|
|
65
|
+
setTs(elSave, s.lastSaveAt, "no saves yet");
|
|
66
|
+
setTs(elReload, s.lastReloadAt, "waiting for a save");
|
|
67
|
+
|
|
68
|
+
const saved = s.lastSaveAt || 0;
|
|
69
|
+
const reloaded = s.lastReloadAt || 0;
|
|
70
|
+
let state, status, last;
|
|
71
|
+
if (!saved) {
|
|
72
|
+
state = "running"; status = "Running"; last = "reloads on save";
|
|
73
|
+
} else if (reloaded >= saved) {
|
|
74
|
+
state = "running"; status = "Running"; last = "reloaded";
|
|
75
|
+
} else if (Date.now() - saved < 3000) {
|
|
76
|
+
state = "reloading"; status = "Reloading…"; last = "change detected";
|
|
77
|
+
} else {
|
|
78
|
+
state = "stale"; status = "Stale"; last = "save not reflected — restart?";
|
|
79
|
+
}
|
|
80
|
+
bar.setAttribute("data-state", state);
|
|
81
|
+
elStatus.textContent = status;
|
|
82
|
+
elLast.textContent = last;
|
|
83
|
+
// Broadcast the snapshot so other consumers (stepper, Ask) share this
|
|
84
|
+
// one poll instead of each opening their own. window.__totLoop holds the
|
|
85
|
+
// latest for listeners that attach late.
|
|
86
|
+
window.__totLoop = s;
|
|
87
|
+
window.dispatchEvent(new CustomEvent("tot:loop-state", { detail: s }));
|
|
88
|
+
}
|
|
89
|
+
function offline() {
|
|
90
|
+
bar.classList.add("live");
|
|
91
|
+
bar.setAttribute("data-state", "stale");
|
|
92
|
+
elStatus.textContent = "Offline";
|
|
93
|
+
elLast.textContent = "dev server not reachable";
|
|
94
|
+
elServer.textContent = "not reachable";
|
|
95
|
+
const off = { server: false, offline: true };
|
|
96
|
+
window.__totLoop = off;
|
|
97
|
+
window.dispatchEvent(new CustomEvent("tot:loop-state", { detail: off }));
|
|
98
|
+
}
|
|
99
|
+
function poll() {
|
|
100
|
+
fetch("/__tot/dev-loop-status", { cache: "no-store" })
|
|
101
|
+
.then((r) => (r.ok ? r.json() : Promise.reject()))
|
|
102
|
+
.then(render)
|
|
103
|
+
.catch(offline);
|
|
104
|
+
}
|
|
105
|
+
poll();
|
|
106
|
+
setInterval(poll, 1000);
|
|
107
|
+
}
|