okengine 0.10.2 → 0.11.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.
Files changed (343) hide show
  1. package/package.json +76 -10
  2. package/site/content/docs/ai/mcp.mdx +0 -1
  3. package/site/content/docs/deployment/docker-swarm.mdx +48 -111
  4. package/site/content/docs/deployment/docker.mdx +43 -36
  5. package/site/content/docs/deployment/index.mdx +9 -9
  6. package/site/content/docs/deployment/kubernetes.mdx +4 -4
  7. package/site/content/docs/elements/ai.mdx +7 -13
  8. package/site/content/docs/elements/channel.mdx +9 -13
  9. package/site/content/docs/elements/clock.mdx +8 -14
  10. package/site/content/docs/elements/flow.mdx +46 -54
  11. package/site/content/docs/elements/gate.mdx +6 -11
  12. package/site/content/docs/elements/signal.mdx +2 -8
  13. package/site/content/docs/elements/store.mdx +79 -93
  14. package/site/content/docs/elements/vault.mdx +89 -76
  15. package/site/content/docs/get-started/basic-usage.mdx +8 -14
  16. package/site/content/docs/get-started/installation.mdx +36 -36
  17. package/site/content/docs/get-started/introduction.mdx +11 -0
  18. package/site/content/docs/get-started/why.mdx +3 -4
  19. package/site/content/docs/index.mdx +0 -1
  20. package/site/content/docs/meta.json +0 -1
  21. package/site/content/docs/plugins/anonymous.mdx +1 -1
  22. package/site/content/docs/plugins/magic-link.mdx +11 -11
  23. package/site/content/docs/plugins/otp.mdx +2 -2
  24. package/site/content/docs/plugins/passkey.mdx +1 -1
  25. package/site/content/docs/plugins/two-factor.mdx +1 -1
  26. package/site/content/docs/plugins/username.mdx +1 -1
  27. package/site/content/docs/recipes/caddy.mdx +1 -1
  28. package/site/content/docs/recipes/cockroachdb.mdx +2 -2
  29. package/site/content/docs/recipes/dragonfly.mdx +1 -1
  30. package/site/content/docs/recipes/index.mdx +1 -2
  31. package/site/content/docs/recipes/llama-cpp.mdx +21 -23
  32. package/site/content/docs/recipes/mailpit.mdx +11 -11
  33. package/site/content/docs/recipes/meilisearch.mdx +10 -10
  34. package/site/content/docs/recipes/meta.json +0 -1
  35. package/site/content/docs/recipes/ollama.mdx +1 -1
  36. package/site/content/docs/recipes/pgdog.mdx +12 -9
  37. package/site/content/docs/recipes/postgres.mdx +3 -3
  38. package/site/content/docs/recipes/redis.mdx +2 -2
  39. package/site/content/docs/recipes/rustfs.mdx +10 -7
  40. package/site/content/docs/recipes/sglang.mdx +1 -1
  41. package/site/content/docs/recipes/supabase-docker.mdx +1 -1
  42. package/site/content/docs/recipes/timescale.mdx +2 -2
  43. package/site/content/docs/recipes/valkey.mdx +1 -1
  44. package/site/content/docs/recipes/vllm.mdx +1 -1
  45. package/site/content/docs/recipes/yugabytedb.mdx +2 -2
  46. package/site/content/docs/reference/cli.md +61 -38
  47. package/site/content/docs/reference/configuration.mdx +145 -70
  48. package/site/content/docs/reference/environment-variables.mdx +36 -28
  49. package/site/content/docs/reference/errors.mdx +28 -29
  50. package/site/content/docs/reference/fx.mdx +14 -7
  51. package/site/content/docs/reference/i18n.mdx +17 -6
  52. package/site/content/docs/reference/index.mdx +5 -0
  53. package/site/content/docs/reference/meta.json +1 -0
  54. package/site/content/docs/reference/migrating-environments.mdx +158 -0
  55. package/site/content/docs/reference/plugins.mdx +2 -2
  56. package/src/auth/gate-auth.test.ts +12 -10
  57. package/src/cli/ai-setup/ai-setup.test.ts +30 -16
  58. package/src/cli/ai-setup/apply.ts +111 -15
  59. package/src/cli/ai-setup/catalog.ts +3 -3
  60. package/src/cli/ai-setup/index.ts +1 -1
  61. package/src/cli/ai-setup/prompts.ts +1 -1
  62. package/src/cli/ai-setup/recommend.test.ts +1 -1
  63. package/src/cli/ask-seed.test.ts +3 -3
  64. package/src/cli/build.ts +8 -9
  65. package/src/cli/db-auto-push.test.ts +6 -3
  66. package/src/cli/db-auto-push.ts +7 -5
  67. package/src/cli/db-seed.ts +7 -8
  68. package/src/cli/db.test.ts +70 -71
  69. package/src/cli/db.ts +86 -12
  70. package/src/cli/dev-controller.test.ts +35 -0
  71. package/src/cli/dev-controller.ts +298 -0
  72. package/src/cli/dev-controls.ts +11 -65
  73. package/src/cli/dev-db-push.test.ts +47 -15
  74. package/src/cli/dev-schema-sync.test.ts +11 -11
  75. package/src/cli/dev-schema-sync.ts +10 -10
  76. package/src/cli/dev-session-lock.test.ts +106 -0
  77. package/src/cli/dev-session-lock.ts +209 -0
  78. package/src/cli/dev.test.ts +246 -114
  79. package/src/cli/dev.ts +153 -205
  80. package/src/cli/doc-staleness.test.ts +3 -6
  81. package/src/cli/docker-clean.test.ts +33 -1
  82. package/src/cli/docker-clean.ts +28 -33
  83. package/src/cli/docker-cli.test.ts +12 -11
  84. package/src/cli/docker.ts +95 -20
  85. package/src/cli/doctor.test.ts +40 -0
  86. package/src/cli/doctor.ts +142 -9
  87. package/src/cli/drizzle-env.test.ts +11 -13
  88. package/src/cli/drizzle-env.ts +18 -11
  89. package/src/cli/ensure-drizzle-config.ts +11 -14
  90. package/src/cli/hero-meta.test.ts +15 -15
  91. package/src/cli/hero-meta.ts +10 -9
  92. package/src/cli/index.ts +36 -6
  93. package/src/cli/load-config.images.test.ts +44 -10
  94. package/src/cli/load-config.source.test.ts +50 -0
  95. package/src/cli/load-config.ts +35 -29
  96. package/src/cli/meilisearch-local.ts +1 -2
  97. package/src/cli/mode.ts +7 -58
  98. package/src/cli/registry.ts +175 -19
  99. package/src/cli/resolve-dev-sql-env.test.ts +8 -16
  100. package/src/cli/resolve-dev-sql-env.ts +13 -15
  101. package/src/cli/safe-defaults.test.ts +29 -11
  102. package/src/cli/schema.ts +65 -16
  103. package/src/cli/test.ts +58 -0
  104. package/src/cli/tui/App.tsx +252 -0
  105. package/src/cli/tui/DevLive.tsx +116 -0
  106. package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
  107. package/src/cli/tui/components/HeaderBar.tsx +67 -0
  108. package/src/cli/tui/components/LogStream.tsx +47 -0
  109. package/src/cli/tui/components/PanelCard.tsx +39 -0
  110. package/src/cli/tui/components/SelectRow.tsx +37 -0
  111. package/src/cli/tui/components/SlashPalette.tsx +172 -0
  112. package/src/cli/tui/components/Spinner.tsx +32 -0
  113. package/src/cli/tui/components/StatusBar.tsx +51 -0
  114. package/src/cli/tui/components/StatusDot.tsx +37 -0
  115. package/src/cli/tui/components/TabBar.tsx +49 -0
  116. package/src/cli/tui/components/Wordmark.tsx +43 -0
  117. package/src/cli/tui/format.ts +38 -0
  118. package/src/cli/tui/keys.test.ts +18 -0
  119. package/src/cli/tui/keys.ts +26 -0
  120. package/src/cli/tui/launch.test.ts +18 -0
  121. package/src/cli/tui/launch.ts +57 -0
  122. package/src/cli/tui/panels/Dashboard.tsx +201 -0
  123. package/src/cli/tui/panels/Database.tsx +198 -0
  124. package/src/cli/tui/panels/Dev.tsx +257 -0
  125. package/src/cli/tui/panels/Docker.tsx +357 -0
  126. package/src/cli/tui/panels/Navigator.test.ts +22 -0
  127. package/src/cli/tui/panels/Navigator.tsx +213 -0
  128. package/src/cli/tui/slash-catalog.test.ts +31 -0
  129. package/src/cli/tui/slash-catalog.ts +184 -0
  130. package/src/cli/tui/slash-run.ts +54 -0
  131. package/src/cli/tui/state/ports.ts +95 -0
  132. package/src/cli/tui/theme.ts +24 -0
  133. package/src/cli/vault-cmd.test.ts +402 -0
  134. package/src/cli/vault-cmd.ts +727 -69
  135. package/src/cli/vault-secure-input.test.ts +56 -0
  136. package/src/cli/vault-secure-input.ts +124 -0
  137. package/src/compiler/effects-infer.ts +7 -0
  138. package/src/compiler/extract.ts +12 -0
  139. package/src/compiler/fixtures/skyport/oke.config.ts +7 -5
  140. package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
  141. package/src/compiler/fixtures/skyport.expected.json +1 -1
  142. package/src/compiler/generate-adopt.ts +27 -1
  143. package/src/compiler-entry.ts +7 -0
  144. package/src/config/define-config.test.ts +111 -47
  145. package/src/config/driver-defaults.test.ts +115 -0
  146. package/src/config/driver-defaults.ts +156 -0
  147. package/src/config/index.ts +246 -131
  148. package/src/config/resolve-driver.test.ts +14 -19
  149. package/src/console/server/app.ts +4 -2
  150. package/src/console/server/console.test.ts +58 -1
  151. package/src/console/server/flows.ts +22 -2
  152. package/src/console/server/index.ts +3 -0
  153. package/src/console/server/operator-db.test.ts +55 -13
  154. package/src/console/server/operator-db.ts +305 -263
  155. package/src/console/server/serve.ts +16 -7
  156. package/src/console/server/state.ts +31 -9
  157. package/src/console/server/vault.test.ts +120 -5
  158. package/src/console/server/vault.ts +208 -12
  159. package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
  160. package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
  161. package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
  162. package/src/console/ui/dist/index.html +2 -2
  163. package/src/console/ui/shell/client.ts +16 -0
  164. package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
  165. package/src/console/ui/vault/VaultA11yView.tsx +33 -1
  166. package/src/console/ui/vault/backend.test.ts +75 -0
  167. package/src/console/ui/vault/backend.ts +117 -0
  168. package/src/console/ui/vault/fixture.ts +21 -2
  169. package/src/console/ui/vault/index.ts +11 -1
  170. package/src/console/ui/vault/types.ts +26 -0
  171. package/src/docker/compose.ts +164 -61
  172. package/src/docker/derive.ts +106 -12
  173. package/src/docker/docker.test.ts +156 -110
  174. package/src/docker/helpers.ts +98 -0
  175. package/src/docker/images-config.test.ts +163 -0
  176. package/src/docker/index.ts +30 -5
  177. package/src/docker/recipes/index.ts +0 -3
  178. package/src/docker/recipes/llama-cpp.ts +4 -4
  179. package/src/docker/recipes/pgdog.ts +9 -2
  180. package/src/docker/resources.test.ts +71 -0
  181. package/src/docker/resources.ts +176 -0
  182. package/src/docker/stack-id.test.ts +11 -4
  183. package/src/docker/stack-id.ts +42 -32
  184. package/src/docker/stack.integration.test.ts +4 -15
  185. package/src/docker/types.ts +35 -3
  186. package/src/drivers/ai-ollama.integration.test.ts +2 -2
  187. package/src/drivers/conformance.test.ts +11 -12
  188. package/src/drivers/drizzle-dialect.test.ts +0 -4
  189. package/src/drivers/drizzle-dialect.ts +7 -13
  190. package/src/drivers/index.ts +24 -7
  191. package/src/drivers/journal-postgres.test.ts +3 -1
  192. package/src/drivers/journal-postgres.ts +4 -2
  193. package/src/drivers/meilisearch.ts +1 -1
  194. package/src/drivers/pglite.ts +28 -5
  195. package/src/drivers/types.ts +9 -11
  196. package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
  197. package/src/drivers/vault-aws-secrets-manager.ts +333 -0
  198. package/src/drivers/vault-builtin.test.ts +110 -0
  199. package/src/drivers/vault-builtin.ts +184 -0
  200. package/src/drivers/vault-driver-removal.test.ts +13 -0
  201. package/src/drivers/vault-managed.test.ts +95 -0
  202. package/src/drivers/vault-managed.ts +101 -25
  203. package/src/drivers/vault-types.ts +25 -5
  204. package/src/elements/ai/runtime.ts +1 -1
  205. package/src/elements/clock/chaos.test.ts +4 -1
  206. package/src/elements/clock/durable.ts +57 -4
  207. package/src/elements/clock.test.ts +13 -14
  208. package/src/elements/gate/constants.ts +18 -0
  209. package/src/elements/gate.test.ts +1 -1
  210. package/src/elements/gate.ts +2 -2
  211. package/src/elements/store/domain-ddl.test.ts +5 -12
  212. package/src/elements/store/emit-drizzle.ts +44 -19
  213. package/src/elements/store/index-boot.test.ts +15 -116
  214. package/src/elements/store/load-plugin-tables.ts +1 -1
  215. package/src/elements/store/resource-list-docs.fixture.ts +12 -6
  216. package/src/elements/store/resource-list-docs.test.ts +64 -44
  217. package/src/elements/store/resource.test.ts +17 -10
  218. package/src/elements/store/runtime.ts +9 -12
  219. package/src/elements/store/schema-decl.test.ts +16 -32
  220. package/src/elements/store/seed.test.ts +1 -2
  221. package/src/elements/store/seed.ts +7 -8
  222. package/src/elements/store/sql-condition.test.ts +2 -2
  223. package/src/elements/store/sql-session.test.ts +8 -10
  224. package/src/elements/store/sql-session.ts +10 -2
  225. package/src/elements/store.test.ts +13 -13
  226. package/src/elements/vault/audit.ts +233 -0
  227. package/src/elements/vault/boot-chain.test.ts +78 -0
  228. package/src/elements/vault/boot-chain.ts +43 -39
  229. package/src/elements/vault/builtin-adapter.test.ts +479 -0
  230. package/src/elements/vault/builtin-adapter.ts +1184 -0
  231. package/src/elements/vault/crypto.test.ts +237 -0
  232. package/src/elements/vault/crypto.ts +431 -0
  233. package/src/elements/vault/declare.ts +164 -3
  234. package/src/elements/vault/errors.ts +56 -0
  235. package/src/elements/vault/kms-unseal.test.ts +76 -0
  236. package/src/elements/vault/kms-unseal.ts +181 -0
  237. package/src/elements/vault/path.ts +90 -0
  238. package/src/elements/vault/resilience.test.ts +102 -0
  239. package/src/elements/vault/resilience.ts +107 -0
  240. package/src/elements/vault/runtime.ts +36 -4
  241. package/src/elements/vault/security-checklist.test.ts +130 -0
  242. package/src/elements/vault/storage.ts +448 -0
  243. package/src/elements/vault/types.ts +288 -0
  244. package/src/elements/vault/unseal.ts +150 -0
  245. package/src/elements/vault.test.ts +72 -4
  246. package/src/elements/vault.ts +91 -2
  247. package/src/full.ts +248 -0
  248. package/src/http.ts +41 -0
  249. package/src/i18n/interpolate-simple.ts +37 -0
  250. package/src/i18n/messages.ts +51 -4
  251. package/src/i18n-entry.ts +7 -0
  252. package/src/index.ts +35 -145
  253. package/src/journal-entry.ts +20 -0
  254. package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
  255. package/src/kernel/app.ts +40 -42
  256. package/src/kernel/auto-registry.test.ts +2 -2
  257. package/src/kernel/boot-bind/ai.test.ts +5 -5
  258. package/src/kernel/boot-bind/ai.ts +2 -1
  259. package/src/kernel/boot-bind/channel.test.ts +6 -6
  260. package/src/kernel/boot-bind/channel.ts +5 -4
  261. package/src/kernel/boot-bind/clock.ts +5 -5
  262. package/src/kernel/boot-bind/gate.ts +6 -7
  263. package/src/kernel/boot-bind/honor-config.test.ts +42 -42
  264. package/src/kernel/boot-bind/journal.ts +5 -5
  265. package/src/kernel/boot-bind/signal.ts +3 -1
  266. package/src/kernel/boot-bind/store.test.ts +55 -37
  267. package/src/kernel/boot-bind/store.ts +24 -34
  268. package/src/kernel/boot-bind/vault.ts +19 -4
  269. package/src/kernel/boot.test.ts +12 -4
  270. package/src/kernel/boot.ts +31 -19
  271. package/src/kernel/budget-entry.ts +1 -1
  272. package/src/kernel/compensate.test.ts +288 -1
  273. package/src/kernel/compensate.ts +126 -0
  274. package/src/kernel/concurrency.test.ts +10 -9
  275. package/src/kernel/effects-stamping.test.ts +27 -2
  276. package/src/kernel/element-registries.ts +5 -3
  277. package/src/kernel/flow.ts +4 -2
  278. package/src/kernel/fx.test.ts +119 -3
  279. package/src/kernel/fx.ts +215 -40
  280. package/src/kernel/index.ts +14 -0
  281. package/src/kernel/journal.ts +130 -7
  282. package/src/kernel/pipeline.test.ts +20 -6
  283. package/src/kernel/redacted.test.ts +35 -0
  284. package/src/kernel/redacted.ts +24 -2
  285. package/src/kernel/router/create-default.ts +16 -0
  286. package/src/kernel/router/create-edge.ts +18 -0
  287. package/src/kernel/router/create.ts +23 -0
  288. package/src/kernel/router/index.ts +23 -0
  289. package/src/kernel/router/linear.ts +88 -0
  290. package/src/kernel/router/regexp.ts +188 -0
  291. package/src/kernel/router/smart.ts +120 -0
  292. package/src/kernel/router/trie.ts +108 -0
  293. package/src/kernel/router/types.ts +56 -0
  294. package/src/kernel/router.ts +18 -532
  295. package/src/kernel-entry.ts +43 -0
  296. package/src/manifest/fixtures/skyport.excerpt.json +1 -1
  297. package/src/manifest/fixtures/skyport.manifest.json +1 -1
  298. package/src/plugins/config-source.test.ts +7 -7
  299. package/src/release/build-lib.ts +58 -0
  300. package/src/release/exports.test.ts +4 -2
  301. package/src/release/exports.ts +30 -4
  302. package/src/release/measure.ts +101 -22
  303. package/src/runs/duckdb.ts +7 -2
  304. package/src/shared/optional-peer.ts +41 -0
  305. package/src/term.test.ts +3 -44
  306. package/src/test/create-test-app.ts +12 -0
  307. package/src/test/provisions.integration.test.ts +1 -1
  308. package/src/test/reset-element-registries.ts +2 -0
  309. package/src/upgrade/codemods.test.ts +7 -20
  310. package/src/upgrade/codemods.ts +93 -60
  311. package/site/content/docs/console/access.mdx +0 -29
  312. package/site/content/docs/console/ai.mdx +0 -35
  313. package/site/content/docs/console/architecture.mdx +0 -35
  314. package/site/content/docs/console/channels.mdx +0 -37
  315. package/site/content/docs/console/clock.mdx +0 -51
  316. package/site/content/docs/console/flows.mdx +0 -31
  317. package/site/content/docs/console/gates.mdx +0 -73
  318. package/site/content/docs/console/index.mdx +0 -54
  319. package/site/content/docs/console/manifest-diff.mdx +0 -34
  320. package/site/content/docs/console/meta.json +0 -24
  321. package/site/content/docs/console/overview.mdx +0 -40
  322. package/site/content/docs/console/plugins.mdx +0 -41
  323. package/site/content/docs/console/privacy.mdx +0 -32
  324. package/site/content/docs/console/runs.mdx +0 -50
  325. package/site/content/docs/console/signals.mdx +0 -40
  326. package/site/content/docs/console/store.mdx +0 -32
  327. package/site/content/docs/console/tenancy.mdx +0 -32
  328. package/site/content/docs/console/traces.mdx +0 -38
  329. package/site/content/docs/console/vault.mdx +0 -41
  330. package/site/content/docs/recipes/openbao.mdx +0 -141
  331. package/src/cli/ask-dev-mode.ts +0 -40
  332. package/src/cli/dev-mode.test.ts +0 -45
  333. package/src/cli/dev-mode.ts +0 -78
  334. package/src/cli/openbao-bootstrap.test.ts +0 -150
  335. package/src/cli/openbao-bootstrap.ts +0 -321
  336. package/src/cli/openbao-restart.integration.test.ts +0 -145
  337. package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
  338. package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
  339. package/src/docker/recipes/openbao.ts +0 -47
  340. package/src/drivers/libsql.ts +0 -179
  341. package/src/drivers/sqlite.ts +0 -44
  342. package/src/drivers/vault-openbao.test.ts +0 -97
  343. package/src/drivers/vault-openbao.ts +0 -143
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Real, already-established default driver ids per {@link ConfigEnv} —
3
+ * one source of truth for every driver map that has a single default table.
4
+ *
5
+ * Philosophy: we test on what we deploy. `dev`/`prod` share production
6
+ * protocols (Postgres, Redis, …); `test` uses PGLite for SQL and memory
7
+ * drivers where the protocol is simple.
8
+ *
9
+ * `store.index`, `channel.whatsapp` / `push`, and `ai` are intentionally
10
+ * absent — they don't have one established default across all three envs
11
+ * and keep using {@link resolveDriverId}'s legacy cascade.
12
+ * @module
13
+ */
14
+
15
+ import {
16
+ mergeEnvDriverMap,
17
+ normalizeEnvDriverMap,
18
+ type DriversConfig,
19
+ type EnvDriverMap,
20
+ type EnvDriverInput,
21
+ } from "./index.ts";
22
+
23
+ /** `drivers.store.sql` — Postgres for dev/prod, PGLite for test. */
24
+ export const STORE_SQL_DEFAULTS: EnvDriverMap = {
25
+ dev: "postgres",
26
+ test: "pglite",
27
+ prod: "postgres",
28
+ };
29
+
30
+ /** `drivers.store.kv` — Redis for dev/prod, in-memory for test. */
31
+ export const STORE_KV_DEFAULTS: EnvDriverMap = {
32
+ dev: "redis",
33
+ test: "memory",
34
+ prod: "redis",
35
+ };
36
+
37
+ /** `drivers.store.files` — S3 for dev/prod, in-memory for test. */
38
+ export const STORE_FILES_DEFAULTS: EnvDriverMap = {
39
+ dev: "s3",
40
+ test: "memory",
41
+ prod: "s3",
42
+ };
43
+
44
+ /** `drivers.signal` — Redis for dev/prod, in-memory for test. */
45
+ export const SIGNAL_DEFAULTS: EnvDriverMap = {
46
+ dev: "redis",
47
+ test: "memory",
48
+ prod: "redis",
49
+ };
50
+
51
+ /** `drivers.clock` — Postgres cron for dev/prod, frozen clock in test. */
52
+ export const CLOCK_DEFAULTS: EnvDriverMap = {
53
+ dev: "postgres",
54
+ test: "frozen",
55
+ prod: "postgres",
56
+ };
57
+
58
+ /** `drivers.journal` — Postgres for dev/prod, in-memory for test. */
59
+ export const JOURNAL_DEFAULTS: EnvDriverMap = {
60
+ dev: "postgres",
61
+ test: "memory",
62
+ prod: "postgres",
63
+ };
64
+
65
+ /**
66
+ * `drivers.vault` — OS env vars in dev, in-memory for test, and the built-in
67
+ * encrypted-at-rest store in prod (env layers still resolve first).
68
+ */
69
+ export const VAULT_DEFAULTS: EnvDriverMap = {
70
+ dev: "env",
71
+ test: "memory",
72
+ prod: "vault",
73
+ };
74
+
75
+ /** `drivers.channel.email` — SMTP for dev/prod, console inbox for test. */
76
+ export const CHANNEL_EMAIL_DEFAULTS: EnvDriverMap = {
77
+ dev: "smtp",
78
+ test: "console",
79
+ prod: "smtp",
80
+ };
81
+
82
+ /**
83
+ * `drivers.channel.sms` — no default provider in any env; SMS is opt-in
84
+ * only (an unconfigured env simply gets no SMS channel).
85
+ */
86
+ export const CHANNEL_SMS_DEFAULTS: EnvDriverMap = {};
87
+
88
+ /** Every driver map with one established default table, grouped by element. */
89
+ export const DRIVER_DEFAULTS = {
90
+ store: {
91
+ sql: STORE_SQL_DEFAULTS,
92
+ kv: STORE_KV_DEFAULTS,
93
+ files: STORE_FILES_DEFAULTS,
94
+ },
95
+ signal: SIGNAL_DEFAULTS,
96
+ clock: CLOCK_DEFAULTS,
97
+ journal: JOURNAL_DEFAULTS,
98
+ vault: VAULT_DEFAULTS,
99
+ channel: {
100
+ email: CHANNEL_EMAIL_DEFAULTS,
101
+ sms: CHANNEL_SMS_DEFAULTS,
102
+ },
103
+ } as const;
104
+
105
+ /**
106
+ * Every driver map from {@link DRIVER_DEFAULTS}, fully resolved — the
107
+ * developer's `oke.config.ts` override merged onto the real default,
108
+ * per-key, for all three {@link ConfigEnv} slots at once. This is the "full
109
+ * picture" `oke doctor` prints: what boot actually uses for every env, even
110
+ * when the config on disk only pins one key.
111
+ */
112
+ export interface EffectiveDriversConfig {
113
+ readonly store: {
114
+ readonly sql: EnvDriverMap;
115
+ readonly kv: EnvDriverMap;
116
+ readonly files: EnvDriverMap;
117
+ };
118
+ readonly signal: EnvDriverMap;
119
+ readonly clock: EnvDriverMap;
120
+ readonly journal: EnvDriverMap;
121
+ readonly vault: EnvDriverMap;
122
+ readonly channel: {
123
+ readonly email: EnvDriverMap;
124
+ readonly sms: EnvDriverMap;
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Merge every driver map in {@link DRIVER_DEFAULTS} against the matching
130
+ * slot of a loaded `oke.config.ts` `drivers` block.
131
+ *
132
+ * @param drivers - `drivers` block from a loaded `oke.config.ts` (already
133
+ * normalized if it came through `defineConfig`; normalized again here so
134
+ * an un-normalized/raw block is still handled)
135
+ */
136
+ export function resolveEffectiveDrivers(
137
+ drivers: DriversConfig | undefined,
138
+ ): EffectiveDriversConfig {
139
+ const merge = (map: EnvDriverInput | undefined, defaults: EnvDriverMap) =>
140
+ mergeEnvDriverMap(normalizeEnvDriverMap(map), defaults);
141
+ return {
142
+ store: {
143
+ sql: merge(drivers?.store?.sql, STORE_SQL_DEFAULTS),
144
+ kv: merge(drivers?.store?.kv, STORE_KV_DEFAULTS),
145
+ files: merge(drivers?.store?.files, STORE_FILES_DEFAULTS),
146
+ },
147
+ signal: merge(drivers?.signal, SIGNAL_DEFAULTS),
148
+ clock: merge(drivers?.clock, CLOCK_DEFAULTS),
149
+ journal: merge(drivers?.journal, JOURNAL_DEFAULTS),
150
+ vault: merge(drivers?.vault, VAULT_DEFAULTS),
151
+ channel: {
152
+ email: merge(drivers?.channel?.email, CHANNEL_EMAIL_DEFAULTS),
153
+ sms: merge(drivers?.channel?.sms, CHANNEL_SMS_DEFAULTS),
154
+ },
155
+ };
156
+ }
@@ -2,21 +2,22 @@
2
2
  * `oke.config.ts` surface. Subpath: `okengine/config`.
3
3
  *
4
4
  * Driver maps are protocol-named; vendor/image choice lives in `images`.
5
+ * Three environment targets: `dev` (Docker Compose), `test` (PGLite), `prod`.
5
6
  * @module
6
7
  */
7
8
 
8
9
  /**
9
10
  * Environment role keys used in driver maps.
10
11
  *
11
- * - `local` — laptop defaults (`oke dev` / `oke dev -l`)
12
- * - `docker` — compose infra + host Bun (`oke dev -d`)
13
- * - `test` — automated tests
12
+ * - `dev` — local development via Docker Compose (`oke dev`)
13
+ * - `test` — automated tests (`oke test` / PGLite)
14
14
  * - `prod` — production deploy
15
15
  */
16
- export type ConfigEnv = "local" | "docker" | "test" | "prod";
16
+ import type { VaultElementConfig } from "../elements/vault/types.ts";
17
17
 
18
- /** Legacy driver-map keys accepted during soft-compat migration. */
19
- type LegacyConfigEnv = "dev" | "stack";
18
+ export type { VaultElementConfig };
19
+
20
+ export type ConfigEnv = "dev" | "test" | "prod";
20
21
 
21
22
  /** Pool options for SQL drivers. */
22
23
  export interface DriverPoolOptions {
@@ -38,29 +39,26 @@ export type DriverRef =
38
39
  readonly key?: unknown;
39
40
  };
40
41
 
41
- /** Map of env → driver ref. */
42
+ /** Map of env → driver ref (canonical three-key shape). */
42
43
  export type EnvDriverMap = Partial<Record<ConfigEnv, DriverRef>>;
43
44
 
44
- /**
45
- * Raw map that may still use deprecated `dev` / `stack` keys.
46
- * Normalized by {@link normalizeEnvDriverMap}.
47
- */
48
- export type RawEnvDriverMap = Partial<Record<ConfigEnv | LegacyConfigEnv, DriverRef>>;
45
+ /** Driver pin: bare ref (all envs) or per-env map (`dev` / `test` / `prod`). */
46
+ export type EnvDriverInput = DriverRef | EnvDriverMap;
49
47
 
50
48
  /** Store facet driver maps. */
51
49
  export interface StoreDriversConfig {
52
- readonly sql?: EnvDriverMap;
53
- readonly kv?: EnvDriverMap;
54
- readonly files?: EnvDriverMap;
55
- readonly index?: EnvDriverMap;
50
+ readonly sql?: EnvDriverInput;
51
+ readonly kv?: EnvDriverInput;
52
+ readonly files?: EnvDriverInput;
53
+ readonly index?: EnvDriverInput;
56
54
  }
57
55
 
58
56
  /** Channel medium → env driver map. */
59
57
  export interface ChannelDriversConfig {
60
- readonly email?: EnvDriverMap;
61
- readonly sms?: EnvDriverMap;
62
- readonly whatsapp?: EnvDriverMap;
63
- readonly push?: EnvDriverMap;
58
+ readonly email?: EnvDriverInput;
59
+ readonly sms?: EnvDriverInput;
60
+ readonly whatsapp?: EnvDriverInput;
61
+ readonly push?: EnvDriverInput;
64
62
  }
65
63
 
66
64
  /** Top-level drivers block in {@link OkeConfig}. */
@@ -71,18 +69,65 @@ export interface DriversConfig {
71
69
  */
72
70
  readonly prod?: readonly string[];
73
71
  readonly store?: StoreDriversConfig;
74
- readonly signal?: EnvDriverMap;
75
- readonly clock?: EnvDriverMap;
72
+ readonly signal?: EnvDriverInput;
73
+ readonly clock?: EnvDriverInput;
76
74
  /** Durable-run journal: `memory` · `file` · `postgres`. */
77
- readonly journal?: EnvDriverMap;
78
- readonly vault?: EnvDriverMap;
75
+ readonly journal?: EnvDriverInput;
76
+ readonly vault?: EnvDriverInput;
79
77
  readonly channel?: ChannelDriversConfig;
80
- readonly ai?: EnvDriverMap;
81
- readonly runs?: EnvDriverMap;
78
+ readonly ai?: EnvDriverInput;
79
+ readonly runs?: EnvDriverInput;
82
80
  }
83
81
 
84
- /** Image lock entries keyed by element role (`store.sql`, …). */
85
- export type ImagesConfig = Readonly<Record<string, string>>;
82
+ /**
83
+ * Container image pins, nested the same way as {@link DriversConfig}:
84
+ * `store.*` / `channel.*` facets nest under their owning element; roles with
85
+ * no driver counterpart (`vault` picks a driver but pins its own image;
86
+ * `ai`, `pgdog`, `proxy` have no config-level element at all) stay flat.
87
+ *
88
+ * Always fully explicit — `images` has no hidden default merge. Omitted keys
89
+ * mean "no container for that role," never "inherit a default image."
90
+ */
91
+ export interface ImagesConfig {
92
+ readonly store?: {
93
+ readonly sql?: string;
94
+ readonly kv?: string;
95
+ readonly files?: string;
96
+ readonly index?: string;
97
+ };
98
+ readonly channel?: {
99
+ readonly email?: string;
100
+ };
101
+ readonly vault?: string;
102
+ readonly ai?: string;
103
+ readonly pgdog?: string;
104
+ readonly proxy?: string;
105
+ }
106
+
107
+ /**
108
+ * Flatten {@link ImagesConfig} to the dotted-role `Record<string, string>`
109
+ * every internal consumer (compose role matching, credentials, env prefixes)
110
+ * has always kept working with — the nesting is a config-surface / type-safety
111
+ * concern only, not an internal representation change.
112
+ *
113
+ * @param images - Nested config-surface images (or `undefined`)
114
+ */
115
+ export function flattenImagesConfig(
116
+ images: ImagesConfig | undefined,
117
+ ): Readonly<Record<string, string>> {
118
+ if (!images) return {};
119
+ const out: Record<string, string> = {};
120
+ if (images.store?.sql) out["store.sql"] = images.store.sql;
121
+ if (images.store?.kv) out["store.kv"] = images.store.kv;
122
+ if (images.store?.files) out["store.files"] = images.store.files;
123
+ if (images.store?.index) out["store.index"] = images.store.index;
124
+ if (images.channel?.email) out["channel.email"] = images.channel.email;
125
+ if (images.vault) out.vault = images.vault;
126
+ if (images.ai) out.ai = images.ai;
127
+ if (images.pgdog) out.pgdog = images.pgdog;
128
+ if (images.proxy) out.proxy = images.proxy;
129
+ return out;
130
+ }
86
131
 
87
132
  /** i18n config. */
88
133
  export interface I18nConfig {
@@ -142,9 +187,9 @@ export type PrivacyConfig = Readonly<Record<string, unknown>>;
142
187
  */
143
188
  export interface DbConfig {
144
189
  /**
145
- * Auto-run `oke db push` when domain schema files change under `oke dev`
146
- * (local mode only). Default `true`. Set `false` to opt out explicitly.
147
- * Forced off for `docker` / `prod`.
190
+ * Auto-run `oke db push` when domain schema files change under `oke dev`.
191
+ * Default `true`. Set `false` to opt out explicitly.
192
+ * Forced off for `prod`.
148
193
  */
149
194
  readonly autoPush?: boolean;
150
195
  /**
@@ -160,7 +205,8 @@ export interface DbConfig {
160
205
  readonly declare?: string;
161
206
  /**
162
207
  * Generated Drizzle schema path written from abstract decls.
163
- * Default `"src/db/schema.generated.ts"` (legacy `"src/schema.generated.ts"`).
208
+ * Default `"src/db/schema.drizzle.ts"` (prior `"src/db/schema.generated.ts"`;
209
+ * legacy `"src/schema.generated.ts"`).
164
210
  * Point `drizzle.config.ts` `schema` at this file when using the abstract path.
165
211
  */
166
212
  readonly generated?: string;
@@ -193,6 +239,11 @@ export interface OkeConfig {
193
239
  * Domain Drizzle schema sync (`oke db …`). Not related to `oke schema`.
194
240
  */
195
241
  readonly db?: DbConfig;
242
+ /**
243
+ * Encrypted-at-rest Vault settings (algorithm, master key, audit, seal).
244
+ * Contracts stay in code — this block only configures the backend.
245
+ */
246
+ readonly vault?: VaultElementConfig;
196
247
  readonly topology?: "monolith" | "services";
197
248
  readonly ports?: PortsConfig;
198
249
  readonly console?: ConsoleConfig;
@@ -201,8 +252,8 @@ export interface OkeConfig {
201
252
  /**
202
253
  * Whether domain auto-DDL (`ensureFromMeta`) should run for this env.
203
254
  *
204
- * - `ensure` — `CREATE TABLE IF NOT EXISTS` on first touch (test / local opt-out)
205
- * - `off` — migrations / `oke db push` own DDL (docker, prod, local+autoPush)
255
+ * - `ensure` — `CREATE TABLE IF NOT EXISTS` on first touch (test / dev opt-out)
256
+ * - `off` — migrations / `oke db push` own DDL (dev+autoPush, prod)
206
257
  */
207
258
  export type DomainDdlMode = "ensure" | "off";
208
259
 
@@ -214,173 +265,237 @@ export type DomainDdlMode = "ensure" | "off";
214
265
  */
215
266
  export function resolveDomainDdlMode(env: ConfigEnv, autoPush = true): DomainDdlMode {
216
267
  if (env === "test") return "ensure";
217
- if (env === "docker" || env === "prod") return "off";
268
+ if (env === "prod") return "off";
218
269
  return autoPush ? "off" : "ensure";
219
270
  }
220
271
 
221
- /** Whether a legacy-key deprecation warning was already emitted this process. */
222
- let legacyKeyWarned = false;
272
+ /**
273
+ * Extract the protocol id from a {@link DriverRef}.
274
+ *
275
+ * @param ref - Driver ref
276
+ */
277
+ export function driverRefId(ref: DriverRef | undefined): string | undefined {
278
+ if (ref === undefined) return undefined;
279
+ return typeof ref === "string" ? ref : ref.driver;
280
+ }
281
+
282
+ /**
283
+ * True when `value` is a bare driver pin (string or `{ driver, … }`), not an env map.
284
+ *
285
+ * @param value - Unknown driver input
286
+ */
287
+ function isBareDriverRef(value: unknown): value is DriverRef {
288
+ if (typeof value === "string") return true;
289
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
290
+ const obj = value as Record<string, unknown>;
291
+ return typeof obj.driver === "string";
292
+ }
223
293
 
224
294
  /**
225
- * Normalize deprecated `dev`/`stack` driver-map keys to `local`/`docker`.
295
+ * Expand a bare {@link DriverRef} to all three env keys.
226
296
  *
227
- * Emits a one-time stderr warning when legacy keys are present.
297
+ * @param ref - Driver ref applied to every env
298
+ */
299
+ export function expandDriverRefToMap(ref: DriverRef): EnvDriverMap {
300
+ return { dev: ref, test: ref, prod: ref };
301
+ }
302
+
303
+ /**
304
+ * Expand a bare driver pin to `{ dev, test, prod }`, or pass through a map.
228
305
  *
229
- * @param raw - Env → driver map (possibly legacy keys)
306
+ * @param raw - Env → driver map, bare ref, or undefined
230
307
  */
231
- export function normalizeEnvDriverMap(
232
- raw: RawEnvDriverMap | EnvDriverMap | undefined,
233
- ): EnvDriverMap | undefined {
234
- if (!raw) return undefined;
235
- const hasLegacy =
236
- (raw as RawEnvDriverMap).dev !== undefined || (raw as RawEnvDriverMap).stack !== undefined;
237
- if (hasLegacy && !legacyKeyWarned) {
238
- legacyKeyWarned = true;
239
- console.warn(
240
- "oke: driver map keys `dev`/`stack` are deprecated — use `local`/`docker` (run `oke upgrade`)",
241
- );
242
- }
243
- const out: EnvDriverMap = { ...(raw as EnvDriverMap) };
244
- const legacy = raw as RawEnvDriverMap;
245
- if (legacy.local === undefined && legacy.dev !== undefined) {
246
- out.local = legacy.dev;
247
- }
248
- if (legacy.docker === undefined && legacy.stack !== undefined) {
249
- out.docker = legacy.stack;
250
- }
251
- delete (out as RawEnvDriverMap).dev;
252
- delete (out as RawEnvDriverMap).stack;
308
+ export function normalizeEnvDriverMap(raw: EnvDriverInput | undefined): EnvDriverMap | undefined {
309
+ if (raw === undefined) return undefined;
310
+ if (isBareDriverRef(raw)) return expandDriverRefToMap(raw);
311
+ const record = raw as EnvDriverMap;
312
+ const out: EnvDriverMap = {};
313
+ if (record.dev !== undefined) out.dev = record.dev;
314
+ if (record.test !== undefined) out.test = record.test;
315
+ if (record.prod !== undefined) out.prod = record.prod;
253
316
  return out;
254
317
  }
255
318
 
256
319
  /**
257
- * Normalize every env map under a drivers block.
320
+ * Apply `fn` to every env-driver slot under a drivers block.
258
321
  *
259
322
  * @param drivers - Drivers block
323
+ * @param fn - Per-slot transform
260
324
  */
261
- export function normalizeDriversConfig(
262
- drivers: DriversConfig | undefined,
263
- ): DriversConfig | undefined {
264
- if (!drivers) return undefined;
325
+ function mapDriversSlots(
326
+ drivers: DriversConfig,
327
+ fn: (input: EnvDriverInput | undefined) => EnvDriverInput | undefined,
328
+ ): DriversConfig {
265
329
  const store = drivers.store
266
330
  ? {
267
331
  ...drivers.store,
268
- sql: normalizeEnvDriverMap(drivers.store.sql as RawEnvDriverMap),
269
- kv: normalizeEnvDriverMap(drivers.store.kv as RawEnvDriverMap),
270
- files: normalizeEnvDriverMap(drivers.store.files as RawEnvDriverMap),
271
- index: normalizeEnvDriverMap(drivers.store.index as RawEnvDriverMap),
332
+ sql: fn(drivers.store.sql),
333
+ kv: fn(drivers.store.kv),
334
+ files: fn(drivers.store.files),
335
+ index: fn(drivers.store.index),
272
336
  }
273
337
  : undefined;
274
338
  const channel = drivers.channel
275
339
  ? {
276
340
  ...drivers.channel,
277
- email: normalizeEnvDriverMap(drivers.channel.email as RawEnvDriverMap),
278
- sms: normalizeEnvDriverMap(drivers.channel.sms as RawEnvDriverMap),
279
- whatsapp: normalizeEnvDriverMap(drivers.channel.whatsapp as RawEnvDriverMap),
280
- push: normalizeEnvDriverMap(drivers.channel.push as RawEnvDriverMap),
341
+ email: fn(drivers.channel.email),
342
+ sms: fn(drivers.channel.sms),
343
+ whatsapp: fn(drivers.channel.whatsapp),
344
+ push: fn(drivers.channel.push),
281
345
  }
282
346
  : undefined;
283
347
  return {
284
348
  ...drivers,
285
349
  ...(store !== undefined ? { store } : {}),
286
- signal: normalizeEnvDriverMap(drivers.signal as RawEnvDriverMap),
287
- clock: normalizeEnvDriverMap(drivers.clock as RawEnvDriverMap),
288
- journal: normalizeEnvDriverMap(drivers.journal as RawEnvDriverMap),
289
- vault: normalizeEnvDriverMap(drivers.vault as RawEnvDriverMap),
350
+ signal: fn(drivers.signal),
351
+ clock: fn(drivers.clock),
352
+ journal: fn(drivers.journal),
353
+ vault: fn(drivers.vault),
290
354
  ...(channel !== undefined ? { channel } : {}),
291
- ai: normalizeEnvDriverMap(drivers.ai as RawEnvDriverMap),
292
- runs: normalizeEnvDriverMap(drivers.runs as RawEnvDriverMap),
355
+ ai: fn(drivers.ai),
356
+ runs: fn(drivers.runs),
293
357
  };
294
358
  }
295
359
 
296
360
  /**
297
- * Fill missing `docker` pins from `prod` (compose infra production protocols).
361
+ * Normalize every env map under a drivers block (expand shorthand + rewrite keys).
362
+ *
363
+ * @param drivers - Drivers block
364
+ */
365
+ export function normalizeDriversConfig(
366
+ drivers: DriversConfig | undefined,
367
+ ): DriversConfig | undefined {
368
+ if (!drivers) return undefined;
369
+ return mapDriversSlots(drivers, normalizeEnvDriverMap);
370
+ }
371
+
372
+ /**
373
+ * Merge a developer's partial {@link EnvDriverMap} onto the real default map
374
+ * for one specific driver — per environment key, never a whole-object
375
+ * replace. An unset key keeps that driver's own real default; it never
376
+ * inherits a sibling key's value.
377
+ *
378
+ * @param override - Developer-supplied partial map (may be `undefined`)
379
+ * @param defaults - Real, already-established default map for this driver
380
+ */
381
+ export function mergeEnvDriverMap(
382
+ override: EnvDriverMap | undefined,
383
+ defaults: EnvDriverMap,
384
+ ): EnvDriverMap {
385
+ return {
386
+ dev: override?.dev ?? defaults.dev,
387
+ test: override?.test ?? defaults.test,
388
+ prod: override?.prod ?? defaults.prod,
389
+ };
390
+ }
391
+
392
+ /**
393
+ * Fill missing `dev` pins from `prod` (compose laptop ≈ production protocols).
298
394
  *
299
395
  * @param map - Env → driver map
300
396
  */
301
- export function fillDockerFromProd(map: EnvDriverMap | undefined): EnvDriverMap | undefined {
397
+ export function fillDevFromProd(map: EnvDriverMap | undefined): EnvDriverMap | undefined {
302
398
  if (!map) return undefined;
303
- if (map.docker !== undefined || map.prod === undefined) return map;
304
- return { ...map, docker: map.prod };
399
+ if (map.dev !== undefined || map.prod === undefined) return map;
400
+ return { ...map, dev: map.prod };
305
401
  }
306
402
 
307
403
  /**
308
- * Copy production driver pins onto `docker` wherever `docker` is omitted.
404
+ * Copy production driver pins onto `dev` wherever `dev` is omitted.
309
405
  *
310
406
  * @param drivers - Drivers block
311
407
  */
312
- export function fillDriversDockerFromProd(
408
+ export function fillDriversDevFromProd(
313
409
  drivers: DriversConfig | undefined,
314
410
  ): DriversConfig | undefined {
315
411
  if (!drivers) return undefined;
316
- const store = drivers.store
317
- ? {
318
- ...drivers.store,
319
- sql: fillDockerFromProd(drivers.store.sql),
320
- kv: fillDockerFromProd(drivers.store.kv),
321
- files: fillDockerFromProd(drivers.store.files),
322
- index: fillDockerFromProd(drivers.store.index),
323
- }
324
- : undefined;
325
- const channel = drivers.channel
326
- ? {
327
- ...drivers.channel,
328
- email: fillDockerFromProd(drivers.channel.email),
329
- sms: fillDockerFromProd(drivers.channel.sms),
330
- whatsapp: fillDockerFromProd(drivers.channel.whatsapp),
331
- push: fillDockerFromProd(drivers.channel.push),
412
+ return mapDriversSlots(drivers, (input) => fillDevFromProd(input as EnvDriverMap | undefined));
413
+ }
414
+
415
+ /**
416
+ * Walk every driver pin and throw if `sqlite` appears or test SQL is not `pglite`.
417
+ *
418
+ * @param drivers - Normalized drivers block
419
+ */
420
+ export function assertDriverSafety(drivers: DriversConfig | undefined): void {
421
+ if (!drivers) return;
422
+ const maps: readonly (EnvDriverMap | undefined)[] = [
423
+ drivers.store?.sql as EnvDriverMap | undefined,
424
+ drivers.store?.kv as EnvDriverMap | undefined,
425
+ drivers.store?.files as EnvDriverMap | undefined,
426
+ drivers.store?.index as EnvDriverMap | undefined,
427
+ drivers.signal as EnvDriverMap | undefined,
428
+ drivers.clock as EnvDriverMap | undefined,
429
+ drivers.journal as EnvDriverMap | undefined,
430
+ drivers.vault as EnvDriverMap | undefined,
431
+ drivers.channel?.email as EnvDriverMap | undefined,
432
+ drivers.channel?.sms as EnvDriverMap | undefined,
433
+ drivers.channel?.whatsapp as EnvDriverMap | undefined,
434
+ drivers.channel?.push as EnvDriverMap | undefined,
435
+ drivers.ai as EnvDriverMap | undefined,
436
+ drivers.runs as EnvDriverMap | undefined,
437
+ ];
438
+ for (const map of maps) {
439
+ if (!map) continue;
440
+ for (const env of ["dev", "test", "prod"] as const) {
441
+ const id = driverRefId(map[env]);
442
+ if (id === "sqlite" || id === "libsql") {
443
+ throw new Error(
444
+ `oke.config: "${id}" was removed — use postgres (dev/prod) or pglite (test); index → pgvector/memory`,
445
+ );
332
446
  }
333
- : undefined;
334
- return {
335
- ...drivers,
336
- ...(store !== undefined ? { store } : {}),
337
- signal: fillDockerFromProd(drivers.signal),
338
- clock: fillDockerFromProd(drivers.clock),
339
- journal: fillDockerFromProd(drivers.journal),
340
- vault: fillDockerFromProd(drivers.vault),
341
- ...(channel !== undefined ? { channel } : {}),
342
- ai: fillDockerFromProd(drivers.ai),
343
- runs: fillDockerFromProd(drivers.runs),
344
- };
447
+ }
448
+ }
449
+ const sqlTest = driverRefId((drivers.store?.sql as EnvDriverMap | undefined)?.test);
450
+ if (sqlTest !== undefined && sqlTest !== "pglite") {
451
+ throw new Error(`oke.config: drivers.store.sql.test must be "pglite" (got "${sqlTest}")`);
452
+ }
345
453
  }
346
454
 
347
455
  /**
348
456
  * Define an `oke.config.ts` document.
349
457
  *
350
- * Missing `docker` driver pins are filled from `prod` so `oke dev -d` uses the
351
- * same server protocols as production (vault `env` for `docker/.env.docker`).
352
- * Legacy `dev`/`stack` keys are normalized to `local`/`docker`.
458
+ * Bare string pins expand to all three envs. Missing `dev` pins fill from
459
+ * `prod`. Rejects `sqlite` / `libsql` and non-`pglite` test SQL.
353
460
  *
354
461
  * @param config - Driver / tenancy / i18n / topology
355
462
  */
356
463
  export function defineConfig(config: OkeConfig): OkeConfig {
357
464
  if (!config.drivers) return config;
358
465
  const normalized = normalizeDriversConfig(config.drivers);
466
+ const filled = fillDriversDevFromProd(normalized);
467
+ assertDriverSafety(filled);
359
468
  return {
360
469
  ...config,
361
- drivers: fillDriversDockerFromProd(normalized),
470
+ drivers: filled,
362
471
  };
363
472
  }
364
473
 
365
474
  /**
366
475
  * Resolve a driver id for an env from an {@link EnvDriverMap}.
367
476
  *
368
- * `docker` falls back to `prod` then `local` so existing configs keep working
369
- * until an explicit `docker:` pin is added. Legacy keys are normalized first.
477
+ * With `defaults` given, the developer's map is merged onto it per-key via
478
+ * {@link mergeEnvDriverMap} and the active env's slot is read directly a
479
+ * key the developer never set keeps that driver's real default, full stop.
370
480
  *
371
- * @param map - Env driver map
481
+ * Without `defaults` (legacy path `store.index`, `channel.whatsapp` /
482
+ * `push`, `ai`): cascade is `env → dev → prod → test`.
483
+ *
484
+ * @param map - Env → driver map (or bare ref)
372
485
  * @param env - Active environment
486
+ * @param defaults - Real default map for this specific driver (per-key merge)
373
487
  */
374
488
  export function resolveDriverId(
375
- map: EnvDriverMap | RawEnvDriverMap | undefined,
489
+ map: EnvDriverInput | undefined,
376
490
  env: ConfigEnv,
491
+ defaults?: EnvDriverMap,
377
492
  ): string | undefined {
378
- if (!map) return undefined;
379
- const normalized = normalizeEnvDriverMap(map as RawEnvDriverMap) ?? map;
380
- const ref =
381
- env === "docker"
382
- ? (normalized.docker ?? normalized.prod ?? normalized.local ?? normalized.test)
383
- : (normalized[env] ?? normalized.local ?? normalized.test);
384
- if (ref === undefined) return undefined;
385
- return typeof ref === "string" ? ref : ref.driver;
493
+ const normalized = normalizeEnvDriverMap(map);
494
+ if (defaults) {
495
+ const merged = mergeEnvDriverMap(normalized, defaults);
496
+ return driverRefId(merged[env]);
497
+ }
498
+ if (!normalized) return undefined;
499
+ const ref = normalized[env] ?? normalized.dev ?? normalized.prod ?? normalized.test;
500
+ return driverRefId(ref);
386
501
  }