@tokenoftrust/storefront-runner 1.4.1 → 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 +17 -1
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +54 -0
- package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
- package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
- 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/0002_snapshot.json +1160 -0
- package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -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 +56 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/js/dashboard-team.js +38 -2
- 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/Seo.astro +65 -1
- package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +1435 -0
- package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1980 -2564
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
- package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
- package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
- package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
- 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 +146 -21
- package/apps/storefront/src/lib/activity/alerts.ts +17 -19
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +89 -9
- package/apps/storefront/src/lib/activity/deployVersion.ts +20 -27
- package/apps/storefront/src/lib/activity/ingest.ts +8 -8
- package/apps/storefront/src/lib/activity/ingestAuth.ts +1 -1
- package/apps/storefront/src/lib/activity/killSwitch.ts +6 -6
- package/apps/storefront/src/lib/activity/query.ts +9 -9
- package/apps/storefront/src/lib/activity/recordActivity.ts +11 -15
- package/apps/storefront/src/lib/activity/store.ts +4 -6
- package/apps/storefront/src/lib/activity/uiActor.ts +12 -12
- package/apps/storefront/src/lib/activity/workerCommit.ts +6 -6
- package/apps/storefront/src/lib/admin/adminShell.ts +292 -0
- package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
- package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
- package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
- package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
- package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
- package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
- package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
- package/apps/storefront/src/lib/apps/adminService.ts +14 -4
- package/apps/storefront/src/lib/apps/adminSession.ts +54 -45
- package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
- package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/credentials.ts +2 -2
- package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
- 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/healthAggregate.ts +5 -5
- 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/attributionService.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
- package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +9 -1
- package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +7 -7
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +270 -16
- package/apps/storefront/src/lib/apps/scopes.ts +2 -2
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
- package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
- 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/widgets/eligibility.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
- package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
- package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
- 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 +11 -11
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
- package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
- package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +2 -4
- package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
- package/apps/storefront/src/lib/auth/session.ts +7 -8
- package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
- package/apps/storefront/src/lib/basePath.ts +9 -2
- package/apps/storefront/src/lib/blog/access.ts +29 -0
- package/apps/storefront/src/lib/blog/collection.ts +400 -0
- package/apps/storefront/src/lib/blog/markdown.ts +174 -0
- package/apps/storefront/src/lib/blog/provider.ts +62 -3
- package/apps/storefront/src/lib/blog/reactions.ts +87 -0
- package/apps/storefront/src/lib/blog/rss.ts +92 -0
- package/apps/storefront/src/lib/blog/teaser.ts +83 -0
- package/apps/storefront/src/lib/blog/types.ts +34 -0
- package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
- 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 +14 -2
- package/apps/storefront/src/lib/d1/schema-apps.ts +372 -17
- package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
- package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
- package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
- 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/cockpitStore.ts +1 -9
- package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
- package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
- package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +34 -5
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
- package/apps/storefront/src/lib/i18n.ts +25 -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/jsonld.ts +118 -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/monitoring/manifest.ts +11 -11
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
- package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
- package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
- package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
- package/apps/storefront/src/lib/newsletter/send.ts +265 -0
- package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +4625 -0
- package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
- 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/dispatchHealth.ts +5 -7
- package/apps/storefront/src/lib/publish/domainState.ts +22 -13
- 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/panelPolling.ts +78 -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 +121 -51
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -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/rate/kvWindow.ts +53 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/search/index.ts +2 -2
- package/apps/storefront/src/lib/seo/alternates.ts +42 -0
- package/apps/storefront/src/lib/seo/meta.ts +65 -0
- package/apps/storefront/src/lib/social/golive.ts +212 -0
- package/apps/storefront/src/lib/social/notify.ts +32 -0
- package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
- 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 +32 -0
- package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
- package/apps/storefront/src/lib/the-build/toc.ts +95 -0
- package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
- package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
- package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
- package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
- package/apps/storefront/src/middleware/index.ts +385 -52
- 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 +25 -1313
- 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/blog/[collection]/[slug]/react.ts +104 -0
- 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/newsletter/[collection]/subscribe.ts +146 -0
- package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
- package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
- package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +20 -2
- package/apps/storefront/src/pages/blog/[slug].astro +99 -19
- package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
- package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
- package/apps/storefront/src/pages/blog/index.astro +4 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
- package/apps/storefront/src/pages/cockpit.astro +77 -1353
- package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
- 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/search.astro +1 -1
- package/apps/storefront/src/pages/sitemap.xml.ts +21 -1
- 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 +129 -0
- package/apps/storefront/src/pages/the-build/index.astro +146 -0
- package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
- package/apps/storefront/src/styles/admin.css +1106 -0
- package/apps/storefront/tsconfig.json +6 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +3 -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 +89 -33
- 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/catalog-d1.ts +1 -1
- 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-preview.ts +25 -2
- package/packages/public-runtime/src/customization-runtime.ts +156 -3
- package/packages/public-runtime/src/customization-versioning.ts +10 -0
- 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 +17 -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 +357 -0
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +81 -17
- 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/build/copy-tenant-assets.mjs +14 -4
- package/scripts/dev/ai-edit.mjs +1 -1
- package/scripts/dev/checkout-watch.mjs +2 -2
- package/scripts/dev/file-browser.mjs +4 -6
- package/scripts/dev/file-writer.mjs +3 -3
- package/scripts/dev/git-status.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/locate-handler.mjs +3 -3
- package/scripts/dev/port-check.mjs +3 -3
- package/scripts/dev/publish.mjs +5 -9
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/shot.mjs +1 -1
- package/scripts/dev/tenant-source-root.mjs +1 -1
- package/scripts/dev/unified-diff.mjs +8 -3
- package/scripts/tot-dev.mjs +8 -11
- package/apps/storefront/src/lib/dev/vcBinding.ts +0 -80
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progressive stepper + staged flow. Drives Run → Edit → What's next with
|
|
3
|
+
* progressive disclosure: only the current stage shows, completed stages
|
|
4
|
+
* collapse to a done-bar, and the flow AUTO-ADVANCES on monitor.ts's REAL loop
|
|
5
|
+
* signal (never a timer) — Step 1 completes when the local server is live,
|
|
6
|
+
* Step 2 when a real save→reload is observed since the edit step began.
|
|
7
|
+
* Consumes the shared tot:loop-state event from monitor.ts (one poll for the
|
|
8
|
+
* whole page). Imported from a processed `<script>` in cockpit.astro (ADR
|
|
9
|
+
* 0013), rendered only when isLocalDevLoop.
|
|
10
|
+
*
|
|
11
|
+
* Progress is persisted in sessionStorage. This is REQUIRED, not a nicety:
|
|
12
|
+
* saving a watched file triggers Vite's global full-reload, which reloads
|
|
13
|
+
* THIS /dev tab too — resetting in-memory state and re-baselining the very
|
|
14
|
+
* save that should advance Edit→Next. Persisting done-flags + current stage +
|
|
15
|
+
* the edit baseline (the lastSaveAt watermark captured when the edit step
|
|
16
|
+
* began, BEFORE the qualifying save) lets the flow survive that reload and
|
|
17
|
+
* credit the save.
|
|
18
|
+
*/
|
|
19
|
+
export function initStepper(): void {
|
|
20
|
+
const stepper: any = document.getElementById("stepper");
|
|
21
|
+
if (!stepper) return;
|
|
22
|
+
const stages: any = { run: document.getElementById("stage-run"), edit: document.getElementById("stage-edit"), next: document.getElementById("stage-next") };
|
|
23
|
+
const bars: any = { run: document.getElementById("bar-run"), edit: document.getElementById("bar-edit") };
|
|
24
|
+
const celRun = document.getElementById("cel-run");
|
|
25
|
+
const celEdit = document.getElementById("cel-edit");
|
|
26
|
+
const toNext: any = document.getElementById("toNext");
|
|
27
|
+
const detect = document.getElementById("detect");
|
|
28
|
+
const detectTxt = document.getElementById("detectTxt");
|
|
29
|
+
const detectEdit = document.getElementById("detect-edit");
|
|
30
|
+
const detectEditTxt = document.getElementById("detectEditTxt");
|
|
31
|
+
const barRunMeta = document.getElementById("bar-run-meta");
|
|
32
|
+
const barEditMeta = document.getElementById("bar-edit-meta");
|
|
33
|
+
const toast = document.getElementById("toast");
|
|
34
|
+
const toastMsg = document.getElementById("toastMsg");
|
|
35
|
+
let tT: ReturnType<typeof setTimeout>;
|
|
36
|
+
const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
37
|
+
|
|
38
|
+
const SS = window.sessionStorage;
|
|
39
|
+
function load(k: string, d: any) { try { const v = SS.getItem(k); return v === null ? d : JSON.parse(v); } catch (e) { return d; } }
|
|
40
|
+
function put(k: string, v: any) { try { SS.setItem(k, JSON.stringify(v)); } catch (e) {} }
|
|
41
|
+
|
|
42
|
+
function ping(m: string, once?: string) {
|
|
43
|
+
if (once && load(once, false)) return;
|
|
44
|
+
if (once) put(once, true);
|
|
45
|
+
if (!toast || !toastMsg) return;
|
|
46
|
+
toastMsg.textContent = m; toast.classList.add("show");
|
|
47
|
+
clearTimeout(tT); tT = setTimeout(() => { toast.classList.remove("show"); }, 1900);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const state = {
|
|
51
|
+
unlocked: load("tot.unlocked", { run: true, edit: false, next: false }),
|
|
52
|
+
done: load("tot.done", { run: false, edit: false }),
|
|
53
|
+
current: load("tot.current", "run"),
|
|
54
|
+
};
|
|
55
|
+
let editBase = load("tot.editBase", null);
|
|
56
|
+
function persist() { put("tot.unlocked", state.unlocked); put("tot.done", state.done); put("tot.current", state.current); }
|
|
57
|
+
|
|
58
|
+
function render() {
|
|
59
|
+
Array.prototype.forEach.call(stepper.querySelectorAll(".snode"), (n: any) => {
|
|
60
|
+
const s = n.getAttribute("data-go");
|
|
61
|
+
n.setAttribute("data-st", state.current === s ? "active" : state.done[s] ? "done" : state.unlocked[s] ? "done" : "locked");
|
|
62
|
+
});
|
|
63
|
+
Object.keys(stages).forEach((s) => { if (stages[s]) stages[s].hidden = state.current !== s; });
|
|
64
|
+
if (bars.run) bars.run.hidden = !(state.done.run && state.current !== "run");
|
|
65
|
+
if (bars.edit) bars.edit.hidden = !(state.done.edit && state.current !== "edit");
|
|
66
|
+
// Contextual celebrate banners reflect achieved state (survive reloads).
|
|
67
|
+
if (celRun && state.done.run) celRun.hidden = false;
|
|
68
|
+
if (celEdit && state.done.edit) celEdit.hidden = false;
|
|
69
|
+
if (toNext) toNext.disabled = !state.unlocked.next;
|
|
70
|
+
}
|
|
71
|
+
function goTo(s: string) {
|
|
72
|
+
if (!state.unlocked[s]) return;
|
|
73
|
+
state.current = s; persist(); render();
|
|
74
|
+
window.scrollTo({ top: 0, behavior: reduce ? "auto" : "smooth" });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function onSignal(s: any) {
|
|
78
|
+
if (!s || s.offline) return;
|
|
79
|
+
|
|
80
|
+
// Step 1 — local server is live.
|
|
81
|
+
if (s.server && !state.done.run) {
|
|
82
|
+
state.done.run = true; state.unlocked.edit = true;
|
|
83
|
+
if (editBase === null) { editBase = s.lastSaveAt || 0; put("tot.editBase", editBase); }
|
|
84
|
+
persist();
|
|
85
|
+
if (barRunMeta && s.port) barRunMeta.textContent = "localhost:" + s.port + (s.tenant ? "/" + s.tenant + "/" : "/");
|
|
86
|
+
if (detect) detect.setAttribute("data-listening", "false");
|
|
87
|
+
if (detectTxt) detectTxt.textContent = "Your store is live on this machine.";
|
|
88
|
+
if (state.current === "run") { goTo("edit"); } else { render(); }
|
|
89
|
+
ping("Your store is live", "tot.pinged.run");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Step 2 — a NEW save (since the edit step began) has been reflected by a reload.
|
|
93
|
+
if (state.done.run && !state.done.edit && editBase !== null) {
|
|
94
|
+
const newSave = (s.lastSaveAt || 0) > editBase;
|
|
95
|
+
const reflected = (s.lastSaveAt || 0) > 0 && (s.lastReloadAt || 0) >= (s.lastSaveAt || 0);
|
|
96
|
+
if (newSave && !reflected && detectEditTxt) {
|
|
97
|
+
detectEditTxt.textContent = "Change detected — reloading your store…";
|
|
98
|
+
}
|
|
99
|
+
if (newSave && reflected) {
|
|
100
|
+
state.done.edit = true; state.unlocked.next = true; persist();
|
|
101
|
+
if (barEditMeta && s.watchedFile) barEditMeta.textContent = s.watchedFile;
|
|
102
|
+
if (detectEdit) detectEdit.setAttribute("data-listening", "false");
|
|
103
|
+
if (detectEditTxt) detectEditTxt.textContent = "Saved — your store reloaded. That's the loop.";
|
|
104
|
+
if (state.current === "edit") { goTo("next"); } else { render(); }
|
|
105
|
+
ping("Saved — your page reloaded", "tot.pinged.edit");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
render();
|
|
111
|
+
Array.prototype.forEach.call(document.querySelectorAll("[data-go]"), (el: any) => {
|
|
112
|
+
el.addEventListener("click", () => { goTo(el.getAttribute("data-go")); });
|
|
113
|
+
});
|
|
114
|
+
window.addEventListener("tot:loop-state", (e: any) => { onSignal(e.detail); });
|
|
115
|
+
if (window.__totLoop) onSignal(window.__totLoop);
|
|
116
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme toggle: flip data-theme on <html>, seeded from the OS preference on
|
|
3
|
+
* first click, and PERSISTED to localStorage so a reload stays on the manual
|
|
4
|
+
* choice (the is:inline head seed script reads this same key before first
|
|
5
|
+
* paint — that one stays is:inline because it must run synchronously before
|
|
6
|
+
* CSS paints, the one legitimate is:inline case per ADR 0013). Clearing
|
|
7
|
+
* storage falls back to prefers-color-scheme again, same as a first-ever
|
|
8
|
+
* visit. Imported from a processed `<script>` in cockpit.astro.
|
|
9
|
+
*/
|
|
10
|
+
export function initThemeToggle(): void {
|
|
11
|
+
const root = document.documentElement;
|
|
12
|
+
const btn = document.getElementById("themeBtn");
|
|
13
|
+
if (!btn) return;
|
|
14
|
+
const KEY = "tot.theme";
|
|
15
|
+
function store(v: string) { try { localStorage.setItem(KEY, v); } catch (e) {} }
|
|
16
|
+
btn.addEventListener("click", () => {
|
|
17
|
+
const cur = root.getAttribute("data-theme");
|
|
18
|
+
const next = cur === "dark" ? "light" : cur === "light" ? "dark"
|
|
19
|
+
: (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "dark");
|
|
20
|
+
root.setAttribute("data-theme", next);
|
|
21
|
+
store(next);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -3,20 +3,12 @@
|
|
|
3
3
|
* cockpit page, its /api/dev/* routes, and the CLI sign-in-code mint all share, so
|
|
4
4
|
* they never disagree about the developer's store.
|
|
5
5
|
*
|
|
6
|
-
* REGRESSION FIXED (8500 <tenant>/cockpit): the page and the mint used to derive
|
|
7
|
-
* the store purely from the session's FIRST membership
|
|
8
|
-
* (`session.tenants?.[0]?.resource ?? session.resource`), ignoring the store the
|
|
9
|
-
* request URL actually named. An invite to `/<tenantB>/cockpit` therefore rendered —
|
|
10
|
-
* and minted a `tot login --code … && tot start` for — whatever store happened to be
|
|
11
|
-
* listed first in the session, "losing track of the store" for any developer who
|
|
12
|
-
* belongs to more than one.
|
|
13
|
-
*
|
|
14
6
|
* Rule: the URL wins WHEN THE VIEWER CAN ACCESS IT. The middleware has already
|
|
15
7
|
* resolved the request's host/path to `locals.tenant` (the trusted, authenticated
|
|
16
8
|
* store seam — never client body input), so when that is a SPECIFIC store (not the
|
|
17
9
|
* public marketing home) and the signed-in developer holds a membership for it (or a
|
|
18
10
|
* live staff admission), that store is authoritative. Otherwise we fall back to the
|
|
19
|
-
* session's own first store (the generic-host `/cockpit` case
|
|
11
|
+
* session's own first store (the generic-host `/cockpit` case), and for an
|
|
20
12
|
* anonymous viewer to the path store's branding, else the caller-supplied `?tenant=`.
|
|
21
13
|
*
|
|
22
14
|
* Access is gated with the SAME primitive (`viewerCanAccess`) the vendor dashboard
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-developer DRAFT overlay for the /dev live preview — the signed token + KV key
|
|
3
|
-
* contract
|
|
3
|
+
* contract for the same-origin draft overlay.
|
|
4
4
|
*
|
|
5
5
|
* WHAT A DRAFT IS: when the /dev page's onscreen AI loop edits the developer's site
|
|
6
6
|
* (`/api/content-edit/ai-edit`), the edited artifacts land in TENANT_CACHE KV under
|
|
@@ -1,41 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Read whether a tenant appDomain's store is SET UP — its forge repo is provisioned —
|
|
3
3
|
* from the credential-free MCP preview-status seam (tot-mcp
|
|
4
|
-
* GET /internal/preview-status;
|
|
4
|
+
* GET /internal/preview-status; alias /internal/dev-access/preview-status). The
|
|
5
5
|
* seam answers `{ tenant, app, repo, provisioned, previewReleased }`.
|
|
6
6
|
*
|
|
7
|
-
* This is the cockpit's own small reader
|
|
8
|
-
*
|
|
9
|
-
* so
|
|
10
|
-
* It gates the CLI sign-in-code mint + the /cockpit setup fastpath so a developer whose
|
|
7
|
+
* This is the cockpit's own small reader over the same MCP-origin + service-secret seam
|
|
8
|
+
* `cliSignInCode.ts` uses. It gates the CLI sign-in-code mint + the /cockpit setup
|
|
9
|
+
* fastpath so a developer whose
|
|
11
10
|
* store isn't provisioned yet gets an honest "not set up" state instead of a
|
|
12
11
|
* `tot login --code … && tot start` command that would die at checkout on
|
|
13
|
-
* "Repo <owner>-main is not provisioned"
|
|
12
|
+
* "Repo <owner>-main is not provisioned".
|
|
14
13
|
*
|
|
15
14
|
* - MCP host ← the ORIGIN of MCP_APP_BINDING_URL (one source, so this read hits the
|
|
16
15
|
* SAME MCP tier the sign-in-code mint does).
|
|
17
16
|
* - auth ← header x-mcp-service-secret: MCP_SERVICE_SHARED_SECRET.
|
|
18
17
|
*
|
|
19
18
|
* Fail-closed but non-blocking by design: an UNCONFIGURED seam (no URL/secret) →
|
|
20
|
-
* { configured:false } so callers keep
|
|
19
|
+
* { configured:false } so callers keep the default behavior; any non-2xx /
|
|
21
20
|
* transport / parse fault → { configured:true, ok:false } so a caller can distinguish
|
|
22
21
|
* "definitely not provisioned" (ok + provisioned:false) from "couldn't tell". Never throws.
|
|
23
22
|
*/
|
|
24
23
|
import { readEnv } from "@/lib/env";
|
|
24
|
+
import { FetchHttpTransport, type HttpGetTransport } from "@/lib/http/fetchTransport";
|
|
25
25
|
|
|
26
|
-
export
|
|
27
|
-
get(
|
|
28
|
-
url: string,
|
|
29
|
-
headers: Record<string, string>,
|
|
30
|
-
): Promise<{ status: number; json(): Promise<unknown> }>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class FetchGetTransport implements HttpGetTransport {
|
|
34
|
-
async get(url: string, headers: Record<string, string>) {
|
|
35
|
-
const res = await fetch(url, { method: "GET", headers });
|
|
36
|
-
return { status: res.status, json: () => res.json() };
|
|
37
|
-
}
|
|
38
|
-
}
|
|
26
|
+
export type { HttpGetTransport };
|
|
39
27
|
|
|
40
28
|
export interface TenantPreviewStatus {
|
|
41
29
|
/** False when the seam isn't wired on this deploy (no URL/secret) — gate inactive. */
|
|
@@ -68,7 +56,7 @@ export async function readTenantPreviewStatus(
|
|
|
68
56
|
return { configured: false, ok: false, reason: "bad-mcp-url" };
|
|
69
57
|
}
|
|
70
58
|
|
|
71
|
-
const transport = deps.transport ?? new
|
|
59
|
+
const transport = deps.transport ?? new FetchHttpTransport();
|
|
72
60
|
const full = `${endpoint}?tenant=${encodeURIComponent(tenant.toLowerCase())}`;
|
|
73
61
|
let res: { status: number; json(): Promise<unknown> };
|
|
74
62
|
try {
|
|
@@ -21,20 +21,14 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { readEnv } from "@/lib/env";
|
|
23
23
|
import { mintMcpClientAssertion, McpClientAssertionError } from "@/lib/auth/mcpClientAssertion";
|
|
24
|
+
import { FetchHttpTransport, type HttpTransport } from "@/lib/http/fetchTransport";
|
|
25
|
+
|
|
26
|
+
export type { HttpTransport };
|
|
24
27
|
|
|
25
28
|
const JWT_BEARER_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
26
29
|
/** The path whose audience the MCP binds EVERY rendezvous assertion to. */
|
|
27
30
|
const RENDEZVOUS_AUDIENCE_PATH = "/oauth/device/rendezvous";
|
|
28
31
|
|
|
29
|
-
/** Injectable transport so the broker calls unit-test with no network. */
|
|
30
|
-
export interface HttpTransport {
|
|
31
|
-
post(
|
|
32
|
-
url: string,
|
|
33
|
-
headers: Record<string, string>,
|
|
34
|
-
body: unknown,
|
|
35
|
-
): Promise<{ status: number; json(): Promise<unknown> }>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
32
|
// Bound every rendezvous call to the MCP. The MCP runs on AppRunner (scale-to-zero),
|
|
39
33
|
// so the FIRST call after idle hits a cold instance that can take many seconds to
|
|
40
34
|
// answer. Without a bound the storefront Worker's subrequest hangs and the browser's
|
|
@@ -44,20 +38,6 @@ export interface HttpTransport {
|
|
|
44
38
|
// 502 ("having a moment — try again"); the retry lands on a now-warm MCP and succeeds.
|
|
45
39
|
const RENDEZVOUS_FETCH_TIMEOUT_MS = 12_000;
|
|
46
40
|
|
|
47
|
-
export class FetchHttpTransport implements HttpTransport {
|
|
48
|
-
async post(url: string, headers: Record<string, string>, body: unknown) {
|
|
49
|
-
const res = await fetch(url, {
|
|
50
|
-
method: "POST",
|
|
51
|
-
headers: { "Content-Type": "application/json", ...headers },
|
|
52
|
-
body: JSON.stringify(body),
|
|
53
|
-
// A timeout/abort rejects the fetch → callRendezvous's catch → a 502 the cockpit
|
|
54
|
-
// renders as a retryable error, instead of a hung request the browser gives up on.
|
|
55
|
-
signal: AbortSignal.timeout(RENDEZVOUS_FETCH_TIMEOUT_MS),
|
|
56
|
-
});
|
|
57
|
-
return { status: res.status, json: () => res.json() };
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
41
|
export interface RendezvousDeps {
|
|
62
42
|
transport?: HttpTransport;
|
|
63
43
|
/** Override the assertion minter in tests (defaults to the real private-key signer). */
|
|
@@ -97,7 +77,12 @@ async function callRendezvous(
|
|
|
97
77
|
): Promise<{ status: number; body: Record<string, unknown> }> {
|
|
98
78
|
const mint = deps.mintAssertion ?? ((aud: string) => mintMcpClientAssertion(aud));
|
|
99
79
|
const clientAssertion = await mint(target.audience);
|
|
100
|
-
const transport =
|
|
80
|
+
const transport =
|
|
81
|
+
deps.transport ??
|
|
82
|
+
new FetchHttpTransport({
|
|
83
|
+
defaultHeaders: { "Content-Type": "application/json" },
|
|
84
|
+
timeoutMs: RENDEZVOUS_FETCH_TIMEOUT_MS,
|
|
85
|
+
});
|
|
101
86
|
const res = await transport.post(
|
|
102
87
|
`${target.origin}${path}`,
|
|
103
88
|
{},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-DEVELOPER "local dev runtime" latest-state — the LIVE half of the
|
|
3
|
-
* local→hosted activity bridge
|
|
3
|
+
* local→hosted activity bridge. Where activityStore.ts holds a ring buffer
|
|
4
4
|
* of file-SAVE events, this holds the most recent heartbeat a developer's LOCAL
|
|
5
5
|
* `tot dev`/`tot start` sent (see the CLI's dev-heartbeat.mjs). The hosted cockpit
|
|
6
6
|
* reads it to show a LIVE, clickable local-dev link + the running CLI version.
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
* response with Set-Cookie, and anything non-200/non-HTML. In `astro dev` there
|
|
16
16
|
* is no `caches` global, so every helper is a clean no-op.
|
|
17
17
|
*
|
|
18
|
-
* Note on the CSP nonce: a stored page
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* Note on the CSP nonce (ADR 0018): a stored page is NONCE-FREE — its inline tags
|
|
19
|
+
* and CSP header carry the nonce placeholder, not a live nonce. A fresh per-response
|
|
20
|
+
* nonce is stamped in at serve time (stampResponseNonce), on both the cold render and
|
|
21
|
+
* the warm cache hit, so no cached artifact ever holds a reusable nonce and every
|
|
22
|
+
* response gets its own matching nonce/CSP pair.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
/** Synthetic internal origin for cache keys — never hits the network. */
|
|
@@ -62,8 +63,36 @@ export function pageCacheKey(
|
|
|
62
63
|
pathname: string,
|
|
63
64
|
search: string,
|
|
64
65
|
version: number,
|
|
66
|
+
planRevision?: string,
|
|
65
67
|
): Request {
|
|
66
|
-
|
|
68
|
+
// A stored page carries the CSP header it was rendered with, INCLUDING any
|
|
69
|
+
// version-scoped integration origins. Folding the resolved `planRevision` into
|
|
70
|
+
// the key makes a warm entry plan-scoped: a rollback/uninstall resolves a new
|
|
71
|
+
// revision, so the old entry is unreachable (a clean miss that re-renders
|
|
72
|
+
// without the removed origins) instead of surviving to TTL. Absent (no
|
|
73
|
+
// integration plane resolved) the key is byte-identical to the non-integration
|
|
74
|
+
// form, so a plain tenant pays nothing and its cache is never busted.
|
|
75
|
+
const planSegment = planRevision ? `/pr:${planRevision}` : "";
|
|
76
|
+
return new Request(
|
|
77
|
+
`${CACHE_ORIGIN}/${tenantId}/v${version}${planSegment}${pathname}${search}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Cache key for a digest-addressed static-bundle response (ADR 0012).
|
|
83
|
+
* Keyed by `treeDigest` instead of the integer `cachever` `pageCacheKey` uses —
|
|
84
|
+
* a pointer flip to a NEW digest is a different key, so it is a clean cache
|
|
85
|
+
* miss (never a stale serve of the old digest); an unchanged digest keeps
|
|
86
|
+
* serving its warm entry across requests. Shares `getCachedPage`/
|
|
87
|
+
* `putCachedPage` with the page cache (both just take a Request key + Cache).
|
|
88
|
+
*/
|
|
89
|
+
export function staticBundleCacheKey(
|
|
90
|
+
tenantId: string,
|
|
91
|
+
digest: string,
|
|
92
|
+
pathname: string,
|
|
93
|
+
search: string,
|
|
94
|
+
): Request {
|
|
95
|
+
return new Request(`${CACHE_ORIGIN}/${tenantId}/digest-${digest}${pathname}${search}`);
|
|
67
96
|
}
|
|
68
97
|
|
|
69
98
|
/** Current cache version for a tenant (defaults to 1). KV read is edge-cached. */
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The storefront's OWN branded "your open change now conflicts" email. When the shared
|
|
3
|
+
* preview base advances (another change was accepted/integrated), every OTHER open
|
|
4
|
+
* candidate is auto-rebased onto the new base; a clean rebuild is silent, but a genuine
|
|
5
|
+
* same-line overlap can't be auto-combined and needs the author's call. This is the push
|
|
6
|
+
* notification for that case — the author is pinged the MOMENT their change actually
|
|
7
|
+
* conflicts with someone else's merge, not at their own accept attempt.
|
|
8
|
+
*
|
|
9
|
+
* Rendered here (our brand), transmitted by tot20 (see lib/messaging/messagesClient.ts).
|
|
10
|
+
* Email HTML rules: inline styles only, table-free simple layout, a visible fallback URL
|
|
11
|
+
* under the button.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Minimal HTML escape for untrusted interpolation (file paths, store name). */
|
|
15
|
+
function esc(s: string): string {
|
|
16
|
+
return s
|
|
17
|
+
.replace(/&/g, "&")
|
|
18
|
+
.replace(/</g, "<")
|
|
19
|
+
.replace(/>/g, ">")
|
|
20
|
+
.replace(/"/g, """);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CandidateConflictEmailInput {
|
|
24
|
+
/** Display name of the store/experience. Default "Regulated Storefront". */
|
|
25
|
+
storeName?: string;
|
|
26
|
+
/** The author's open change PR number, when known. */
|
|
27
|
+
prNumber?: number | null;
|
|
28
|
+
/** Repo-relative paths that changed on both sides and need the author's pick. */
|
|
29
|
+
unresolved: string[];
|
|
30
|
+
/** Absolute deep-link to the resolve UI (admin#publish), or null when no origin is known. */
|
|
31
|
+
resolveUrl?: string | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const FILE_LIMIT = 12;
|
|
35
|
+
|
|
36
|
+
export function renderCandidateConflictEmail(input: CandidateConflictEmailInput): {
|
|
37
|
+
subject: string;
|
|
38
|
+
html: string;
|
|
39
|
+
} {
|
|
40
|
+
const store = input.storeName?.trim() || "Regulated Storefront";
|
|
41
|
+
const label = input.prNumber != null ? `PR #${input.prNumber}` : "your change";
|
|
42
|
+
const subject = `Action needed: ${label} now conflicts with ${store}`;
|
|
43
|
+
const url = input.resolveUrl || null;
|
|
44
|
+
|
|
45
|
+
const files = input.unresolved.slice(0, FILE_LIMIT);
|
|
46
|
+
const overflow = input.unresolved.length - files.length;
|
|
47
|
+
const fileList = files.length
|
|
48
|
+
? `<ul style="margin:8px 0 0;padding-left:20px;color:#33415b;font-size:14px;">${files
|
|
49
|
+
.map((f) => `<li style="margin:2px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">${esc(f)}</li>`)
|
|
50
|
+
.join("")}${overflow > 0 ? `<li style="margin:2px 0;color:#8593a8;">…and ${overflow} more file(s)</li>` : ""}</ul>`
|
|
51
|
+
: "";
|
|
52
|
+
|
|
53
|
+
const cta = url
|
|
54
|
+
? `<div style="margin:28px 0;">
|
|
55
|
+
<a href="${esc(url)}" style="display:inline-block;background:#0b7a6d;color:#ffffff;text-decoration:none;font-weight:600;font-size:16px;padding:14px 28px;border-radius:999px;">Resolve it now</a>
|
|
56
|
+
</div>
|
|
57
|
+
<p style="margin:0 0 4px;font-size:13px;color:#55637a;">Or paste this link into your browser:</p>
|
|
58
|
+
<p style="margin:0 0 24px;font-size:13px;word-break:break-all;"><a href="${esc(url)}" style="color:#0b7a6d;">${esc(url)}</a></p>`
|
|
59
|
+
: "";
|
|
60
|
+
|
|
61
|
+
const html = `<!doctype html>
|
|
62
|
+
<html lang="en">
|
|
63
|
+
<head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /></head>
|
|
64
|
+
<body style="margin:0;padding:0;background:#f7f9fc;">
|
|
65
|
+
<div style="max-width:520px;margin:0 auto;padding:32px 20px;font:16px/1.6 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;color:#33415b;">
|
|
66
|
+
<div style="font:600 12px/1 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.12em;text-transform:uppercase;color:#0b7a6d;margin:0 0 24px;">
|
|
67
|
+
Token of Trust© · ${esc(store)}
|
|
68
|
+
</div>
|
|
69
|
+
<h1 style="font-size:22px;line-height:1.3;letter-spacing:-.02em;margin:0 0 12px;color:#0a2540;">${esc(label)} needs your input</h1>
|
|
70
|
+
<p style="margin:0 0 8px;color:#33415b;">The store moved on — another change was just accepted into <strong>${esc(store)}</strong>, and we tried to rebuild ${esc(label)} on top of it automatically. Most of it merged cleanly, but these files were edited on both sides and need you to pick which version wins:</p>
|
|
71
|
+
${fileList}
|
|
72
|
+
${cta}
|
|
73
|
+
<p style="margin:16px 0 0;font-size:14px;color:#33415b;">You can also resolve it from the command line — <code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">tot preview --strategy=ours</code> keeps your version, <code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">tot preview --strategy=theirs</code> takes the store's.</p>
|
|
74
|
+
<p style="margin:24px 0 0;font-size:12px;color:#8593a8;border-top:1px solid #e3e8ef;padding-top:16px;">Your change is safe and still open — it just stays not-mergeable until you resolve these files. Nothing was overwritten.</p>
|
|
75
|
+
</div>
|
|
76
|
+
</body>
|
|
77
|
+
</html>`;
|
|
78
|
+
|
|
79
|
+
return { subject, html };
|
|
80
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one shared `fetch`-backed HTTP transport seam used by the storefront's
|
|
3
|
+
* server-to-server clients. A transport is injectable so those clients unit-test
|
|
4
|
+
* with no network; this is the production implementation they default to.
|
|
5
|
+
*
|
|
6
|
+
* Both `post` and `get` return the minimal `{ status, json }` shape the callers
|
|
7
|
+
* read — never the raw `Response` — so a test stub and the real transport are
|
|
8
|
+
* interchangeable. `defaultHeaders` are merged under each call's own headers
|
|
9
|
+
* (per-call wins); `timeoutMs`, when set, aborts each request via
|
|
10
|
+
* `AbortSignal.timeout` so a hung subrequest becomes a fast, retryable failure.
|
|
11
|
+
*/
|
|
12
|
+
export interface HttpResponseLike {
|
|
13
|
+
status: number;
|
|
14
|
+
json(): Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface HttpTransport {
|
|
18
|
+
post(url: string, headers: Record<string, string>, body: unknown): Promise<HttpResponseLike>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface HttpGetTransport {
|
|
22
|
+
get(url: string, headers: Record<string, string>): Promise<HttpResponseLike>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface FetchHttpTransportOptions {
|
|
26
|
+
/** Merged UNDER each call's headers (a per-call header of the same name wins). */
|
|
27
|
+
defaultHeaders?: Record<string, string>;
|
|
28
|
+
/** When set, every request aborts after this many ms via `AbortSignal.timeout`. */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class FetchHttpTransport implements HttpTransport, HttpGetTransport {
|
|
33
|
+
private readonly defaultHeaders: Record<string, string>;
|
|
34
|
+
private readonly timeoutMs?: number;
|
|
35
|
+
|
|
36
|
+
constructor(options: FetchHttpTransportOptions = {}) {
|
|
37
|
+
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
38
|
+
this.timeoutMs = options.timeoutMs;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private signal(): AbortSignal | undefined {
|
|
42
|
+
return this.timeoutMs != null ? AbortSignal.timeout(this.timeoutMs) : undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async post(url: string, headers: Record<string, string>, body: unknown): Promise<HttpResponseLike> {
|
|
46
|
+
const res = await fetch(url, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { ...this.defaultHeaders, ...headers },
|
|
49
|
+
body: JSON.stringify(body),
|
|
50
|
+
signal: this.signal(),
|
|
51
|
+
});
|
|
52
|
+
return { status: res.status, json: () => res.json() };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async get(url: string, headers: Record<string, string>): Promise<HttpResponseLike> {
|
|
56
|
+
const res = await fetch(url, {
|
|
57
|
+
method: "GET",
|
|
58
|
+
headers: { ...this.defaultHeaders, ...headers },
|
|
59
|
+
signal: this.signal(),
|
|
60
|
+
});
|
|
61
|
+
return { status: res.status, json: () => res.json() };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Human-readable, in-language display names for the reader language switcher,
|
|
2
|
+
// keyed by BCP-47 primary subtag. Unmapped codes fall back to the uppercased code.
|
|
3
|
+
const LANGUAGE_LABELS: Record<string, string> = {
|
|
4
|
+
en: "English",
|
|
5
|
+
es: "Español",
|
|
6
|
+
fr: "Français",
|
|
7
|
+
de: "Deutsch",
|
|
8
|
+
it: "Italiano",
|
|
9
|
+
pt: "Português",
|
|
10
|
+
nl: "Nederlands",
|
|
11
|
+
sv: "Svenska",
|
|
12
|
+
pl: "Polski",
|
|
13
|
+
ru: "Русский",
|
|
14
|
+
ja: "日本語",
|
|
15
|
+
ko: "한국어",
|
|
16
|
+
zh: "中文",
|
|
17
|
+
ar: "العربية",
|
|
18
|
+
hi: "हिन्दी",
|
|
19
|
+
tr: "Türkçe",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function languageLabel(code: string): string {
|
|
23
|
+
const primary = code.toLowerCase().split(/[-_]/)[0] ?? code;
|
|
24
|
+
return LANGUAGE_LABELS[primary] ?? code.toUpperCase();
|
|
25
|
+
}
|