cartethyia 2.5.9 → 2.6.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/.env.example +15 -42
- package/README.md +11 -10
- package/dist/dashboard/assets/{BannedPage-Brvuj_Eb.js → BannedPage-l6XMnfr9.js} +1 -1
- package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-CqfXtI3y.js → CliToolDetailPage-DwOzAW45.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-GQeS91bg.js → CliToolsPage-BT3Up3QA.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BDRsTXBB.js → CombosPage-6kYIUpzR.js} +1 -1
- package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-zP8AFHX0.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-BmfrNZ14.js → CustomizationPage-C7m-H1Rz.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-DoreIgm8.js → LoginPage-CbphLvjg.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C7pDb41R.js → ModelPicker-DVny4S5_.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-BnoPgA7w.js → ProvidersPage-Dfb2896L.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-Bq_TGgdb.js → ProxyPage-BaCIdAUt.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-Day3mCOn.js → SetupPage-D4g6vOGc.js} +1 -1
- package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-DQxllnB5.js → SortableList-C93i14BD.js} +1 -1
- package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-uMExoWu1.js → StudioPage-SgQpymjm.js} +1 -1
- package/dist/dashboard/assets/{StudioPage-uMExoWu1.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
- package/dist/dashboard/assets/{UsagePage-DXa1oR-4.js → UsagePage-BFME-eba.js} +2 -2
- package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
- package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
- package/dist/dashboard/assets/{app-BIt446yE.js → app-2KhtHCv9.js} +1 -1
- package/dist/dashboard/assets/{app-BIt446yE.js.gz → app-2KhtHCv9.js.gz} +0 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
- package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
- package/dist/dashboard/assets/{badge-CR72dasT.js → badge-CKsF123d.js} +1 -1
- package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
- package/dist/dashboard/assets/{banner-CI9_eSfO.js → banner-8j7uChA9.js} +1 -1
- package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
- package/dist/dashboard/assets/{card-DS7CQEsO.js → card-CdsSlWA3.js} +1 -1
- package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
- package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-611pSsaX.js → clipboard-button-uL5aYLfW.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-k37exjNt.js → dialog-C07hr5_G.js} +1 -1
- package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CX7N8ucR.js → drawer-BPO1TcKH.js} +1 -1
- package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CTP8AyiS.js → entry-Bz_CtDfC.js} +1 -1
- package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-sltk1qUi.js → github-badge-DmQreSDd.js} +1 -1
- package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
- package/dist/dashboard/assets/{index-z3zDOSMM.js → index-Bblu6erR.js} +2 -2
- package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BhY2sSnO.js → inline-C9dCYaBZ.js} +1 -1
- package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
- package/dist/dashboard/assets/{input-V1umFiBk.js → input-CjCnXweJ.js} +1 -1
- package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BBOYycyS.js → layout-V6y8DAkl.js} +1 -1
- package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-1zzZYM0B.js → list-row-CM1AaG_y.js} +1 -1
- package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
- package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
- package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
- package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
- package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-DkfMR5Qs.js → numbers-BOevJzdz.js} +1 -1
- package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
- package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
- package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
- package/dist/dashboard/assets/{select-C6MMiaio.js → select-C980c4cz.js} +1 -1
- package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
- package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
- package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
- package/dist/dashboard/assets/{stack-BHTfXdz_.js → stack-_gfTdLVB.js} +1 -1
- package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
- package/dist/dashboard/assets/{state-mPDRlQkl.js → state-2c5V4zo7.js} +1 -1
- package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DOmJxg7H.js → switch-Dkke7Naf.js} +1 -1
- package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
- package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -51
- package/migrations/README.md +52 -52
- package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
- package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
- package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/package.json +96 -97
- package/scripts/cli/admin.ts +126 -125
- package/scripts/cli/autostart.ts +237 -237
- package/scripts/cli/background.ts +120 -110
- package/scripts/cli/bin/carte.cjs +8 -8
- package/scripts/cli/bin/cartethyia.cjs +167 -167
- package/scripts/cli/console-menu.ts +308 -275
- package/scripts/cli/controller.ts +145 -0
- package/scripts/cli/doctor.ts +1 -1
- package/scripts/cli/env.ts +99 -99
- package/scripts/cli/gateway.ts +264 -261
- package/scripts/{health.ts → cli/health.ts} +4 -4
- package/scripts/cli/instance.ts +234 -224
- package/scripts/cli/main.ts +188 -164
- package/scripts/cli/platform.ts +64 -64
- package/scripts/cli/prompt.ts +275 -107
- package/scripts/cli/services.ts +88 -88
- package/scripts/cli/setup.ts +445 -445
- package/scripts/cli/start.ts +85 -82
- package/scripts/cli/startup-signal.ts +23 -23
- package/scripts/cli/telemetry.ts +70 -0
- package/scripts/cli/tray/index.ts +263 -263
- package/scripts/cli/tray/windows.ps1 +107 -107
- package/scripts/cli/tray.ts +192 -208
- package/scripts/cli/update.ts +39 -39
- package/scripts/cli/version.ts +67 -67
- package/scripts/cli/wordmark.ts +58 -58
- package/src/boot/app.ts +16 -1
- package/src/boot/dependencies.ts +128 -25
- package/src/config/package-paths.ts +24 -0
- package/src/config/resources.ts +137 -0
- package/src/config/runtime.ts +133 -76
- package/src/config/spec.ts +30 -29
- package/src/config/telemetry.ts +0 -25
- package/src/console/api-keys/bucket-spend.ts +14 -17
- package/src/console/api-keys/contracts.ts +410 -410
- package/src/console/api-keys/routes.ts +12 -0
- package/src/console/audit/store.ts +2 -3
- package/src/console/auth/setup-service.ts +8 -1
- package/src/console/backup/routes.ts +7 -0
- package/src/console/backup/service.ts +463 -463
- package/src/console/backup/store.ts +16 -11
- package/src/console/backup/validate.ts +1 -4
- package/src/console/cli-tools/fs-ops.ts +18 -2
- package/src/console/data-management/contracts.ts +1 -8
- package/src/console/data-management/operations.ts +61 -48
- package/src/console/data-management/routes.ts +4 -2
- package/src/console/domain-registration.ts +25 -3
- package/src/console/inferhub/client.ts +139 -0
- package/src/console/inferhub/key-store.ts +129 -0
- package/src/console/inferhub/normalize.ts +347 -0
- package/src/console/inferhub/routes.ts +411 -0
- package/src/console/inferhub/store.ts +109 -0
- package/src/console/inferhub/types.ts +183 -0
- package/src/console/observability/contracts.ts +2 -41
- package/src/console/observability/store.ts +176 -371
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/store.ts +23 -36
- package/src/console/quota/account-quota-shared.ts +52 -52
- package/src/console/quota/account-quota-tenant.ts +16 -24
- package/src/console/quota/refresh.ts +441 -441
- package/src/console/remote-support/service.ts +270 -114
- package/src/console/routing/pools/import-parse.ts +206 -206
- package/src/console/routing/pools/operations.ts +543 -543
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/routes.ts +181 -181
- package/src/console/routing/pools/store.ts +1 -1
- package/src/console/settings/contracts.ts +42 -40
- package/src/console/settings/store.ts +25 -25
- package/src/console/share/share-stats.ts +22 -4
- package/src/console/share/share-usage.ts +30 -4
- package/src/console/shared/cursor.ts +15 -0
- package/src/console/updates/repository-updates.ts +40 -39
- package/src/console/updates/routes.ts +12 -0
- package/src/main.ts +13 -5
- package/src/network/pool/resolver.ts +1104 -1104
- package/src/observability/client-name.ts +22 -0
- package/src/observability/event-log.ts +5 -29
- package/src/observability/event-query.ts +690 -0
- package/src/observability/log-ring.ts +26 -2
- package/src/observability/log-sanitize.ts +118 -0
- package/src/observability/logger.ts +17 -13
- package/src/observability/metrics.ts +35 -0
- package/src/observability/payload-capture.ts +114 -41
- package/src/observability/payload-store.ts +249 -24
- package/src/observability/raw-event-writer.ts +118 -0
- package/src/observability/runtime-metrics.ts +71 -1
- package/src/observability/telemetry-buffer.ts +100 -17
- package/src/observability/telemetry-reconciliation.ts +417 -0
- package/src/persistence/boot.ts +20 -9
- package/src/persistence/db-pg.ts +13 -0
- package/src/persistence/db-sqlite.ts +569 -497
- package/src/persistence/dialect.ts +3 -2
- package/src/persistence/migrate.ts +181 -181
- package/src/persistence/schema.ts +1496 -1406
- package/src/persistence/shared-cache.ts +5 -1
- package/src/persistence/stores/api-key-store.ts +581 -581
- package/src/persistence/stores/telemetry-store.ts +124 -63
- package/src/protocol/reasoning-text.ts +30 -0
- package/src/protocol/registry.ts +2 -0
- package/src/protocol/response/chat.ts +33 -7
- package/src/protocol/response/codex.ts +14 -2
- package/src/protocol/response/messages.ts +5 -2
- package/src/providers/authentication/oauth-flow-store.ts +3 -10
- package/src/providers/bundled-discovery-providers.ts +122 -122
- package/src/providers/default-registry.ts +14 -0
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +43 -7
- package/src/providers/discovery/probing-service.ts +70 -32
- package/src/providers/integrations/antigravity/antigravity.ts +9 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
- package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
- package/src/providers/integrations/buddy/capabilities.ts +4 -0
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
- package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
- package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
- package/src/providers/integrations/claude/claude-quota.ts +2 -2
- package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
- package/src/providers/integrations/claude/claude.ts +3 -4
- package/src/providers/integrations/cline/cline.ts +94 -8
- package/src/providers/integrations/codex/codex.ts +61 -37
- package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
- package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
- package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
- package/src/providers/integrations/cursor/cursor-request.ts +11 -0
- package/src/providers/integrations/cursor/cursor.ts +29 -17
- package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
- package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
- package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
- package/src/providers/integrations/github/github.ts +3 -1
- package/src/providers/integrations/grok/grok-quota.ts +279 -279
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
- package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
- package/src/providers/integrations/hermes.ts +7 -1
- package/src/providers/integrations/inferhub.ts +201 -73
- package/src/providers/integrations/kilo/kilo.ts +152 -152
- package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
- package/src/providers/integrations/opencode/provider.ts +2 -0
- package/src/providers/integrations/openrouter/provider.ts +129 -129
- package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
- package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
- package/src/providers/integrations/trae/trae-headers.ts +17 -48
- package/src/providers/integrations/trae/trae-oauth.ts +4 -26
- package/src/providers/integrations/trae/trae-quota.ts +389 -384
- package/src/providers/integrations/trae/trae-realm.ts +15 -2
- package/src/providers/integrations/trae/trae.ts +22 -24
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/account-health-service.ts +605 -279
- package/src/providers/provider-metadata.ts +12 -1
- package/src/providers/provider-model-definition.ts +373 -373
- package/src/providers/provider-probe.ts +3 -0
- package/src/providers/quota/quota-contracts.ts +3 -7
- package/src/providers/search/search-provider.ts +2 -6
- package/src/security/admission/contracts.ts +10 -0
- package/src/security/admission/in-memory-store.ts +89 -14
- package/src/security/admission/lua.ts +191 -182
- package/src/security/admission/redis-store.ts +30 -0
- package/src/security/admission/service.ts +18 -0
- package/src/security/api-key-auth-cache.ts +96 -0
- package/src/security/api-key-auth.ts +11 -100
- package/src/security/client-router-fingerprint.ts +3 -7
- package/src/security/ip-boundary.ts +7 -3
- package/src/shared/coerce.ts +67 -0
- package/src/shared/hash.ts +6 -0
- package/src/shared/time.ts +7 -0
- package/src/transport/dispatch/attempt-finalize.ts +26 -138
- package/src/transport/dispatch/attempt-loop.ts +103 -81
- package/src/transport/dispatch/capture-policy.ts +78 -0
- package/src/transport/dispatch/proxy-request.ts +4 -2
- package/src/transport/dispatch/streaming/encoder.ts +111 -5
- package/src/transport/dispatch/streaming/stream.ts +19 -2
- package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
- package/src/transport/failure-policy.ts +27 -0
- package/src/transport/guards/abuse-guard.ts +2 -2
- package/src/transport/guards/auth-guard.ts +17 -0
- package/src/transport/guards/body-policy.ts +3 -2
- package/src/transport/guards/concurrency-guard.ts +89 -13
- package/src/transport/guards/early-rejection-capture.ts +43 -3
- package/src/transport/guards/error-lifecycle.ts +10 -2
- package/src/transport/guards/guard-errors.ts +23 -0
- package/src/transport/guards/headroom-guard.ts +41 -7
- package/src/transport/guards/pipeline.ts +24 -0
- package/src/transport/guards/request-context.ts +2 -1
- package/src/transport/routing/admission.ts +51 -9
- package/src/transport/routing/bounded-lru.ts +49 -0
- package/src/transport/routing/candidate-index.ts +88 -0
- package/src/transport/routing/reservations.ts +121 -7
- package/src/transport/routing/round-robin.ts +10 -22
- package/src/transport/routing/route-catalog.ts +26 -13
- package/src/transport/routing/route-model.ts +22 -2
- package/src/transport/routing/router.ts +53 -31
- package/src/transport/surface/chat/encode.ts +2 -3
- package/src/transport/surface/chat/parse.ts +2 -1
- package/src/transport/surface/dialects.ts +11 -3
- package/src/transport/surface/messages/encode.ts +2 -1
- package/src/transport/surface/messages/parse.ts +3 -1
- package/src/transport/surface/messages/stream.ts +2 -6
- package/src/workers/maintenance-lease.ts +200 -0
- package/src/workers/scheduler.ts +101 -1
- package/dist/dashboard/assets/BannedPage-Brvuj_Eb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CqfXtI3y.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-GQeS91bg.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BDRsTXBB.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-zP8AFHX0.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BmfrNZ14.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DoreIgm8.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BnoPgA7w.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-Bq_TGgdb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-D9DHXvom.js +0 -1
- package/dist/dashboard/assets/QuotaPage-D9DHXvom.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js +0 -1
- package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-Day3mCOn.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DQxllnB5.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DXa1oR-4.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/entry-B9Xvtnfj.js +0 -1
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-ByMGkR3C.js +0 -4
- package/dist/dashboard/assets/entry-ByMGkR3C.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/index-z3zDOSMM.js.gz +0 -0
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/select-C6MMiaio.js.gz +0 -0
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/scripts/build/aot.ts +0 -143
- package/scripts/build/build.ts +0 -24
- package/scripts/build/compile.ts +0 -88
- package/scripts/build/restart.ts +0 -120
- package/scripts/build/start.ts +0 -25
- package/src/observability/event-compaction.ts +0 -317
- package/src/observability/event-day-index.ts +0 -440
|
@@ -87,8 +87,9 @@ export function caseInsensitiveLike(column: SQL, pattern: SQL): SQL {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/** The later of two timestamps. */
|
|
90
|
-
export function laterOf(left: SQL, right: SQL): SQL {
|
|
91
|
-
|
|
90
|
+
export function laterOf(left: SQL, right: SQL, database?: unknown): SQL {
|
|
91
|
+
const isSqlite = database === undefined ? sqliteDialect() : hasSqliteDialect(database);
|
|
92
|
+
return isSqlite ? sql`max(${left}, ${right})` : sql`greatest(${left}, ${right})`;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
/** The current timestamp, in the spelling the backend's default uses. */
|
|
@@ -1,181 +1,181 @@
|
|
|
1
|
-
/** Migration discovery + ledger helpers shared by every database backend. */
|
|
2
|
-
import { sql } from "drizzle-orm";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
5
|
-
import { basename, resolve } from "node:path";
|
|
6
|
-
import { resolvePackagePath } from "../config/package-paths";
|
|
7
|
-
import type { CartethyiaDatabase } from "./db-handle";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* SQL-only migration ledger. Migration files are the source of truth; this
|
|
11
|
-
* table stores only which numbered files have already run.
|
|
12
|
-
*/
|
|
13
|
-
export const MIGRATION_LEDGER_TABLE = "cartethyia_schema_migrations";
|
|
14
|
-
|
|
15
|
-
export const LEDGER_DDL = `CREATE TABLE IF NOT EXISTS ${MIGRATION_LEDGER_TABLE} (
|
|
16
|
-
migration_id text PRIMARY KEY,
|
|
17
|
-
applied_at timestamptz NOT NULL DEFAULT now()
|
|
18
|
-
)`;
|
|
19
|
-
|
|
20
|
-
export function resolveMigrationsFolder(): string {
|
|
21
|
-
const folder = resolvePackagePath("migrations", "full-postgres");
|
|
22
|
-
if (!existsSync(folder)) {
|
|
23
|
-
throw new Error(`Migrations folder not found: ${folder}`);
|
|
24
|
-
}
|
|
25
|
-
return folder;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function migrationFiles(folder: string): readonly string[] {
|
|
29
|
-
return readdirSync(folder)
|
|
30
|
-
.filter((file) => /^\d{4}_.+\.sql$/.test(file))
|
|
31
|
-
.sort()
|
|
32
|
-
.map((file) => resolve(folder, file));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function migrationIdFor(file: string): string {
|
|
36
|
-
return basename(file);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** The only migration file a fresh database builds from. */
|
|
40
|
-
export const BASELINE_MIGRATION_ID = "0000_baseline.sql";
|
|
41
|
-
const BASELINE_SCHEMA_MARKER_PREFIX = "baseline-schema:";
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Identifies the exact baseline schema recorded by a fresh database.
|
|
45
|
-
*
|
|
46
|
-
* The hash is taken over the *statements*, with comments stripped, because the
|
|
47
|
-
* raw file text is not the thing that matters: a comment edit changes the hash
|
|
48
|
-
* without changing the schema, and a changed hash makes every existing database
|
|
49
|
-
* refuse to boot ("different baseline schema"). That is not hypothetical — a
|
|
50
|
-
* documentation-only edit to the SQLite baseline header shipped in 2.4.6 and
|
|
51
|
-
* locked out every database created by 2.4.5.
|
|
52
|
-
*
|
|
53
|
-
* Stripping comments makes the marker track the schema a database was built
|
|
54
|
-
* from, which is what the check is actually asserting.
|
|
55
|
-
*/
|
|
56
|
-
export function baselineSchemaMarker(source: string): string {
|
|
57
|
-
return `${BASELINE_SCHEMA_MARKER_PREFIX}${hashStatements(source)}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Markers recorded by releases that hashed the raw baseline file text.
|
|
62
|
-
*
|
|
63
|
-
* Every 2.4.x release shipped the same SQLite baseline, so a database created by
|
|
64
|
-
* any of them carries this one marker. The *schema* is identical to the current
|
|
65
|
-
* baseline (verified: stripping comments from both yields the same hash), so
|
|
66
|
-
* these are accepted as proof of the same schema instead of telling every
|
|
67
|
-
* existing user to recreate their database.
|
|
68
|
-
*
|
|
69
|
-
* These are frozen history. Never add a marker for a schema that actually
|
|
70
|
-
* differs — that would let an incompatible database boot against a newer
|
|
71
|
-
* runtime, which is the failure this check exists to prevent.
|
|
72
|
-
*/
|
|
73
|
-
const LEGACY_BASELINE_SCHEMA_MARKERS: readonly string[] = [
|
|
74
|
-
// full-postgres/0000_baseline.sql — unchanged across v2.4.0-v2.4.6, so this is
|
|
75
|
-
// the raw-text hash every existing Full-mode database carries.
|
|
76
|
-
"baseline-schema:bf7d2b4f1f99b4470c81850a77cc4ebc989997fbe8c8b68ec62833164c73a6ad",
|
|
77
|
-
// lite-sqlite3/0000_baseline.sql, v2.4.0 – v2.4.5 (raw-text sha256).
|
|
78
|
-
"baseline-schema:438c5e014c5f86f483a84e8a106b0e62143d4eb0b0daad728a300bcc683d8c02",
|
|
79
|
-
// lite-sqlite3/0000_baseline.sql, v2.4.6 (same schema, different header).
|
|
80
|
-
"baseline-schema:405083572bb108bfe42e726b6f3d0b47c8d76c8e1de1a3a5d5fbbbe88a7ac4dd",
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
/** Accepted markers for the current baseline schema, current form first. */
|
|
84
|
-
export function acceptedBaselineSchemaMarkers(source: string): readonly string[] {
|
|
85
|
-
return [baselineSchemaMarker(source), ...LEGACY_BASELINE_SCHEMA_MARKERS];
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Hash of the baseline's statements, with comments and formatting removed. */
|
|
89
|
-
function hashStatements(source: string): string {
|
|
90
|
-
const statements = source
|
|
91
|
-
// `--` to end of line, which is the only comment form these baselines use.
|
|
92
|
-
.replace(/--[^\n]*/g, "")
|
|
93
|
-
.split(/--> statement-breakpoint/)
|
|
94
|
-
.map((part) => part.replace(/\s+/g, " ").trim())
|
|
95
|
-
.filter((part) => part.length > 0)
|
|
96
|
-
.join("\n");
|
|
97
|
-
return createHash("sha256").update(statements).digest("hex");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Rejects databases whose schema was created by retired migration files or
|
|
102
|
-
* which have tables but no record of the current baseline.
|
|
103
|
-
*/
|
|
104
|
-
export function assertMigrationHistorySupported(
|
|
105
|
-
applied: ReadonlySet<string>,
|
|
106
|
-
expected: readonly string[],
|
|
107
|
-
hasTenants: boolean,
|
|
108
|
-
currentBaselineMarker?: string,
|
|
109
|
-
): void {
|
|
110
|
-
const supported = new Set(expected);
|
|
111
|
-
const retired = [...applied].filter((migrationId) => !supported.has(migrationId));
|
|
112
|
-
if (retired.length > 0) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`Database contains retired migration history (${retired.join(", ")}). ` +
|
|
115
|
-
"Create a fresh database and restore from a backup; in-place upgrades are no longer supported.",
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
if (hasTenants && !applied.has(BASELINE_MIGRATION_ID)) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
"Database contains an unversioned schema. Create a fresh database and restore from a backup; " +
|
|
121
|
-
"in-place upgrades are no longer supported.",
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
// Any marker in `expected` counts, not just the current one: a database built
|
|
125
|
-
// by an older release carries a frozen historical marker for the same schema.
|
|
126
|
-
const acceptedMarker =
|
|
127
|
-
currentBaselineMarker !== undefined &&
|
|
128
|
-
expected.some((id) => id.startsWith(BASELINE_SCHEMA_MARKER_PREFIX) && applied.has(id));
|
|
129
|
-
if (hasTenants && currentBaselineMarker !== undefined && !acceptedMarker) {
|
|
130
|
-
throw new Error(
|
|
131
|
-
"Database was created from a different baseline schema. Create a fresh database and restore from a backup; " +
|
|
132
|
-
"in-place upgrades are no longer supported.",
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Splits a migration file on the drizzle `statement-breakpoint` markers the
|
|
139
|
-
* files already carry. Only the embedded backend needs per-statement
|
|
140
|
-
* application (single-statement failure tolerance); the pg path sends the
|
|
141
|
-
* whole file at once exactly as before.
|
|
142
|
-
*/
|
|
143
|
-
export function splitStatements(source: string): string[] {
|
|
144
|
-
return source
|
|
145
|
-
.split(/--> statement-breakpoint/g)
|
|
146
|
-
.map((part) => part.trim())
|
|
147
|
-
.filter((part) => part.length > 0);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Readiness view of the migration ledger: the numbered SQL files discovered on
|
|
152
|
-
* disk compared against the ids recorded in {@link MIGRATION_LEDGER_TABLE}.
|
|
153
|
-
*/
|
|
154
|
-
export interface MigrationLedgerStatus {
|
|
155
|
-
/** Migration file names discovered on disk, in apply order. */
|
|
156
|
-
readonly expected: readonly string[];
|
|
157
|
-
/** Expected migrations that have no ledger row. */
|
|
158
|
-
readonly pending: readonly string[];
|
|
159
|
-
/** True only when every expected migration is recorded in the ledger. */
|
|
160
|
-
readonly applied: boolean;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Compares the migration files discovered on disk against the applied-migration
|
|
165
|
-
* ledger. A reachable ledger alone is not readiness: an interrupted upgrade
|
|
166
|
-
* leaves the table present while later migrations are still unapplied.
|
|
167
|
-
*
|
|
168
|
-
* Runs on the drizzle handle, so it is identical on every backend.
|
|
169
|
-
*/
|
|
170
|
-
export async function readMigrationLedgerStatus(
|
|
171
|
-
db: CartethyiaDatabase,
|
|
172
|
-
folder: string = resolveMigrationsFolder(),
|
|
173
|
-
): Promise<MigrationLedgerStatus> {
|
|
174
|
-
const expected = migrationFiles(folder).map((file) => migrationIdFor(file));
|
|
175
|
-
const result = await db.execute<{ migration_id: string }>(
|
|
176
|
-
sql.raw(`SELECT migration_id FROM ${MIGRATION_LEDGER_TABLE}`),
|
|
177
|
-
);
|
|
178
|
-
const applied = new Set(result.rows.map((row) => row.migration_id));
|
|
179
|
-
const pending = expected.filter((migrationId) => !applied.has(migrationId));
|
|
180
|
-
return { expected, pending, applied: pending.length === 0 };
|
|
181
|
-
}
|
|
1
|
+
/** Migration discovery + ledger helpers shared by every database backend. */
|
|
2
|
+
import { sql } from "drizzle-orm";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
5
|
+
import { basename, resolve } from "node:path";
|
|
6
|
+
import { resolvePackagePath } from "../config/package-paths";
|
|
7
|
+
import type { CartethyiaDatabase } from "./db-handle";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* SQL-only migration ledger. Migration files are the source of truth; this
|
|
11
|
+
* table stores only which numbered files have already run.
|
|
12
|
+
*/
|
|
13
|
+
export const MIGRATION_LEDGER_TABLE = "cartethyia_schema_migrations";
|
|
14
|
+
|
|
15
|
+
export const LEDGER_DDL = `CREATE TABLE IF NOT EXISTS ${MIGRATION_LEDGER_TABLE} (
|
|
16
|
+
migration_id text PRIMARY KEY,
|
|
17
|
+
applied_at timestamptz NOT NULL DEFAULT now()
|
|
18
|
+
)`;
|
|
19
|
+
|
|
20
|
+
export function resolveMigrationsFolder(): string {
|
|
21
|
+
const folder = resolvePackagePath("migrations", "full-postgres");
|
|
22
|
+
if (!existsSync(folder)) {
|
|
23
|
+
throw new Error(`Migrations folder not found: ${folder}`);
|
|
24
|
+
}
|
|
25
|
+
return folder;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function migrationFiles(folder: string): readonly string[] {
|
|
29
|
+
return readdirSync(folder)
|
|
30
|
+
.filter((file) => /^\d{4}_.+\.sql$/.test(file))
|
|
31
|
+
.sort()
|
|
32
|
+
.map((file) => resolve(folder, file));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function migrationIdFor(file: string): string {
|
|
36
|
+
return basename(file);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The only migration file a fresh database builds from. */
|
|
40
|
+
export const BASELINE_MIGRATION_ID = "0000_baseline.sql";
|
|
41
|
+
const BASELINE_SCHEMA_MARKER_PREFIX = "baseline-schema:";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Identifies the exact baseline schema recorded by a fresh database.
|
|
45
|
+
*
|
|
46
|
+
* The hash is taken over the *statements*, with comments stripped, because the
|
|
47
|
+
* raw file text is not the thing that matters: a comment edit changes the hash
|
|
48
|
+
* without changing the schema, and a changed hash makes every existing database
|
|
49
|
+
* refuse to boot ("different baseline schema"). That is not hypothetical — a
|
|
50
|
+
* documentation-only edit to the SQLite baseline header shipped in 2.4.6 and
|
|
51
|
+
* locked out every database created by 2.4.5.
|
|
52
|
+
*
|
|
53
|
+
* Stripping comments makes the marker track the schema a database was built
|
|
54
|
+
* from, which is what the check is actually asserting.
|
|
55
|
+
*/
|
|
56
|
+
export function baselineSchemaMarker(source: string): string {
|
|
57
|
+
return `${BASELINE_SCHEMA_MARKER_PREFIX}${hashStatements(source)}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Markers recorded by releases that hashed the raw baseline file text.
|
|
62
|
+
*
|
|
63
|
+
* Every 2.4.x release shipped the same SQLite baseline, so a database created by
|
|
64
|
+
* any of them carries this one marker. The *schema* is identical to the current
|
|
65
|
+
* baseline (verified: stripping comments from both yields the same hash), so
|
|
66
|
+
* these are accepted as proof of the same schema instead of telling every
|
|
67
|
+
* existing user to recreate their database.
|
|
68
|
+
*
|
|
69
|
+
* These are frozen history. Never add a marker for a schema that actually
|
|
70
|
+
* differs — that would let an incompatible database boot against a newer
|
|
71
|
+
* runtime, which is the failure this check exists to prevent.
|
|
72
|
+
*/
|
|
73
|
+
const LEGACY_BASELINE_SCHEMA_MARKERS: readonly string[] = [
|
|
74
|
+
// full-postgres/0000_baseline.sql — unchanged across v2.4.0-v2.4.6, so this is
|
|
75
|
+
// the raw-text hash every existing Full-mode database carries.
|
|
76
|
+
"baseline-schema:bf7d2b4f1f99b4470c81850a77cc4ebc989997fbe8c8b68ec62833164c73a6ad",
|
|
77
|
+
// lite-sqlite3/0000_baseline.sql, v2.4.0 – v2.4.5 (raw-text sha256).
|
|
78
|
+
"baseline-schema:438c5e014c5f86f483a84e8a106b0e62143d4eb0b0daad728a300bcc683d8c02",
|
|
79
|
+
// lite-sqlite3/0000_baseline.sql, v2.4.6 (same schema, different header).
|
|
80
|
+
"baseline-schema:405083572bb108bfe42e726b6f3d0b47c8d76c8e1de1a3a5d5fbbbe88a7ac4dd",
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
/** Accepted markers for the current baseline schema, current form first. */
|
|
84
|
+
export function acceptedBaselineSchemaMarkers(source: string): readonly string[] {
|
|
85
|
+
return [baselineSchemaMarker(source), ...LEGACY_BASELINE_SCHEMA_MARKERS];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Hash of the baseline's statements, with comments and formatting removed. */
|
|
89
|
+
function hashStatements(source: string): string {
|
|
90
|
+
const statements = source
|
|
91
|
+
// `--` to end of line, which is the only comment form these baselines use.
|
|
92
|
+
.replace(/--[^\n]*/g, "")
|
|
93
|
+
.split(/--> statement-breakpoint/)
|
|
94
|
+
.map((part) => part.replace(/\s+/g, " ").trim())
|
|
95
|
+
.filter((part) => part.length > 0)
|
|
96
|
+
.join("\n");
|
|
97
|
+
return createHash("sha256").update(statements).digest("hex");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Rejects databases whose schema was created by retired migration files or
|
|
102
|
+
* which have tables but no record of the current baseline.
|
|
103
|
+
*/
|
|
104
|
+
export function assertMigrationHistorySupported(
|
|
105
|
+
applied: ReadonlySet<string>,
|
|
106
|
+
expected: readonly string[],
|
|
107
|
+
hasTenants: boolean,
|
|
108
|
+
currentBaselineMarker?: string,
|
|
109
|
+
): void {
|
|
110
|
+
const supported = new Set(expected);
|
|
111
|
+
const retired = [...applied].filter((migrationId) => !supported.has(migrationId));
|
|
112
|
+
if (retired.length > 0) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Database contains retired migration history (${retired.join(", ")}). ` +
|
|
115
|
+
"Create a fresh database and restore from a backup; in-place upgrades are no longer supported.",
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (hasTenants && !applied.has(BASELINE_MIGRATION_ID)) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
"Database contains an unversioned schema. Create a fresh database and restore from a backup; " +
|
|
121
|
+
"in-place upgrades are no longer supported.",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
// Any marker in `expected` counts, not just the current one: a database built
|
|
125
|
+
// by an older release carries a frozen historical marker for the same schema.
|
|
126
|
+
const acceptedMarker =
|
|
127
|
+
currentBaselineMarker !== undefined &&
|
|
128
|
+
expected.some((id) => id.startsWith(BASELINE_SCHEMA_MARKER_PREFIX) && applied.has(id));
|
|
129
|
+
if (hasTenants && currentBaselineMarker !== undefined && !acceptedMarker) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"Database was created from a different baseline schema. Create a fresh database and restore from a backup; " +
|
|
132
|
+
"in-place upgrades are no longer supported.",
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Splits a migration file on the drizzle `statement-breakpoint` markers the
|
|
139
|
+
* files already carry. Only the embedded backend needs per-statement
|
|
140
|
+
* application (single-statement failure tolerance); the pg path sends the
|
|
141
|
+
* whole file at once exactly as before.
|
|
142
|
+
*/
|
|
143
|
+
export function splitStatements(source: string): string[] {
|
|
144
|
+
return source
|
|
145
|
+
.split(/--> statement-breakpoint/g)
|
|
146
|
+
.map((part) => part.trim())
|
|
147
|
+
.filter((part) => part.length > 0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Readiness view of the migration ledger: the numbered SQL files discovered on
|
|
152
|
+
* disk compared against the ids recorded in {@link MIGRATION_LEDGER_TABLE}.
|
|
153
|
+
*/
|
|
154
|
+
export interface MigrationLedgerStatus {
|
|
155
|
+
/** Migration file names discovered on disk, in apply order. */
|
|
156
|
+
readonly expected: readonly string[];
|
|
157
|
+
/** Expected migrations that have no ledger row. */
|
|
158
|
+
readonly pending: readonly string[];
|
|
159
|
+
/** True only when every expected migration is recorded in the ledger. */
|
|
160
|
+
readonly applied: boolean;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Compares the migration files discovered on disk against the applied-migration
|
|
165
|
+
* ledger. A reachable ledger alone is not readiness: an interrupted upgrade
|
|
166
|
+
* leaves the table present while later migrations are still unapplied.
|
|
167
|
+
*
|
|
168
|
+
* Runs on the drizzle handle, so it is identical on every backend.
|
|
169
|
+
*/
|
|
170
|
+
export async function readMigrationLedgerStatus(
|
|
171
|
+
db: CartethyiaDatabase,
|
|
172
|
+
folder: string = resolveMigrationsFolder(),
|
|
173
|
+
): Promise<MigrationLedgerStatus> {
|
|
174
|
+
const expected = migrationFiles(folder).map((file) => migrationIdFor(file));
|
|
175
|
+
const result = await db.execute<{ migration_id: string }>(
|
|
176
|
+
sql.raw(`SELECT migration_id FROM ${MIGRATION_LEDGER_TABLE}`),
|
|
177
|
+
);
|
|
178
|
+
const applied = new Set(result.rows.map((row) => row.migration_id));
|
|
179
|
+
const pending = expected.filter((migrationId) => !applied.has(migrationId));
|
|
180
|
+
return { expected, pending, applied: pending.length === 0 };
|
|
181
|
+
}
|