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.
- package/package.json +76 -10
- package/site/content/docs/ai/mcp.mdx +0 -1
- package/site/content/docs/deployment/docker-swarm.mdx +48 -111
- package/site/content/docs/deployment/docker.mdx +43 -36
- package/site/content/docs/deployment/index.mdx +9 -9
- package/site/content/docs/deployment/kubernetes.mdx +4 -4
- package/site/content/docs/elements/ai.mdx +7 -13
- package/site/content/docs/elements/channel.mdx +9 -13
- package/site/content/docs/elements/clock.mdx +8 -14
- package/site/content/docs/elements/flow.mdx +46 -54
- package/site/content/docs/elements/gate.mdx +6 -11
- package/site/content/docs/elements/signal.mdx +2 -8
- package/site/content/docs/elements/store.mdx +79 -93
- package/site/content/docs/elements/vault.mdx +89 -76
- package/site/content/docs/get-started/basic-usage.mdx +8 -14
- package/site/content/docs/get-started/installation.mdx +36 -36
- package/site/content/docs/get-started/introduction.mdx +11 -0
- package/site/content/docs/get-started/why.mdx +3 -4
- package/site/content/docs/index.mdx +0 -1
- package/site/content/docs/meta.json +0 -1
- package/site/content/docs/plugins/anonymous.mdx +1 -1
- package/site/content/docs/plugins/magic-link.mdx +11 -11
- package/site/content/docs/plugins/otp.mdx +2 -2
- package/site/content/docs/plugins/passkey.mdx +1 -1
- package/site/content/docs/plugins/two-factor.mdx +1 -1
- package/site/content/docs/plugins/username.mdx +1 -1
- package/site/content/docs/recipes/caddy.mdx +1 -1
- package/site/content/docs/recipes/cockroachdb.mdx +2 -2
- package/site/content/docs/recipes/dragonfly.mdx +1 -1
- package/site/content/docs/recipes/index.mdx +1 -2
- package/site/content/docs/recipes/llama-cpp.mdx +21 -23
- package/site/content/docs/recipes/mailpit.mdx +11 -11
- package/site/content/docs/recipes/meilisearch.mdx +10 -10
- package/site/content/docs/recipes/meta.json +0 -1
- package/site/content/docs/recipes/ollama.mdx +1 -1
- package/site/content/docs/recipes/pgdog.mdx +12 -9
- package/site/content/docs/recipes/postgres.mdx +3 -3
- package/site/content/docs/recipes/redis.mdx +2 -2
- package/site/content/docs/recipes/rustfs.mdx +10 -7
- package/site/content/docs/recipes/sglang.mdx +1 -1
- package/site/content/docs/recipes/supabase-docker.mdx +1 -1
- package/site/content/docs/recipes/timescale.mdx +2 -2
- package/site/content/docs/recipes/valkey.mdx +1 -1
- package/site/content/docs/recipes/vllm.mdx +1 -1
- package/site/content/docs/recipes/yugabytedb.mdx +2 -2
- package/site/content/docs/reference/cli.md +61 -38
- package/site/content/docs/reference/configuration.mdx +145 -70
- package/site/content/docs/reference/environment-variables.mdx +36 -28
- package/site/content/docs/reference/errors.mdx +28 -29
- package/site/content/docs/reference/fx.mdx +14 -7
- package/site/content/docs/reference/i18n.mdx +17 -6
- package/site/content/docs/reference/index.mdx +5 -0
- package/site/content/docs/reference/meta.json +1 -0
- package/site/content/docs/reference/migrating-environments.mdx +158 -0
- package/site/content/docs/reference/plugins.mdx +2 -2
- package/src/auth/gate-auth.test.ts +12 -10
- package/src/cli/ai-setup/ai-setup.test.ts +30 -16
- package/src/cli/ai-setup/apply.ts +111 -15
- package/src/cli/ai-setup/catalog.ts +3 -3
- package/src/cli/ai-setup/index.ts +1 -1
- package/src/cli/ai-setup/prompts.ts +1 -1
- package/src/cli/ai-setup/recommend.test.ts +1 -1
- package/src/cli/ask-seed.test.ts +3 -3
- package/src/cli/build.ts +8 -9
- package/src/cli/db-auto-push.test.ts +6 -3
- package/src/cli/db-auto-push.ts +7 -5
- package/src/cli/db-seed.ts +7 -8
- package/src/cli/db.test.ts +70 -71
- package/src/cli/db.ts +86 -12
- package/src/cli/dev-controller.test.ts +35 -0
- package/src/cli/dev-controller.ts +298 -0
- package/src/cli/dev-controls.ts +11 -65
- package/src/cli/dev-db-push.test.ts +47 -15
- package/src/cli/dev-schema-sync.test.ts +11 -11
- package/src/cli/dev-schema-sync.ts +10 -10
- package/src/cli/dev-session-lock.test.ts +106 -0
- package/src/cli/dev-session-lock.ts +209 -0
- package/src/cli/dev.test.ts +246 -114
- package/src/cli/dev.ts +153 -205
- package/src/cli/doc-staleness.test.ts +3 -6
- package/src/cli/docker-clean.test.ts +33 -1
- package/src/cli/docker-clean.ts +28 -33
- package/src/cli/docker-cli.test.ts +12 -11
- package/src/cli/docker.ts +95 -20
- package/src/cli/doctor.test.ts +40 -0
- package/src/cli/doctor.ts +142 -9
- package/src/cli/drizzle-env.test.ts +11 -13
- package/src/cli/drizzle-env.ts +18 -11
- package/src/cli/ensure-drizzle-config.ts +11 -14
- package/src/cli/hero-meta.test.ts +15 -15
- package/src/cli/hero-meta.ts +10 -9
- package/src/cli/index.ts +36 -6
- package/src/cli/load-config.images.test.ts +44 -10
- package/src/cli/load-config.source.test.ts +50 -0
- package/src/cli/load-config.ts +35 -29
- package/src/cli/meilisearch-local.ts +1 -2
- package/src/cli/mode.ts +7 -58
- package/src/cli/registry.ts +175 -19
- package/src/cli/resolve-dev-sql-env.test.ts +8 -16
- package/src/cli/resolve-dev-sql-env.ts +13 -15
- package/src/cli/safe-defaults.test.ts +29 -11
- package/src/cli/schema.ts +65 -16
- package/src/cli/test.ts +58 -0
- package/src/cli/tui/App.tsx +252 -0
- package/src/cli/tui/DevLive.tsx +116 -0
- package/src/cli/tui/components/ConfirmDialog.tsx +48 -0
- package/src/cli/tui/components/HeaderBar.tsx +67 -0
- package/src/cli/tui/components/LogStream.tsx +47 -0
- package/src/cli/tui/components/PanelCard.tsx +39 -0
- package/src/cli/tui/components/SelectRow.tsx +37 -0
- package/src/cli/tui/components/SlashPalette.tsx +172 -0
- package/src/cli/tui/components/Spinner.tsx +32 -0
- package/src/cli/tui/components/StatusBar.tsx +51 -0
- package/src/cli/tui/components/StatusDot.tsx +37 -0
- package/src/cli/tui/components/TabBar.tsx +49 -0
- package/src/cli/tui/components/Wordmark.tsx +43 -0
- package/src/cli/tui/format.ts +38 -0
- package/src/cli/tui/keys.test.ts +18 -0
- package/src/cli/tui/keys.ts +26 -0
- package/src/cli/tui/launch.test.ts +18 -0
- package/src/cli/tui/launch.ts +57 -0
- package/src/cli/tui/panels/Dashboard.tsx +201 -0
- package/src/cli/tui/panels/Database.tsx +198 -0
- package/src/cli/tui/panels/Dev.tsx +257 -0
- package/src/cli/tui/panels/Docker.tsx +357 -0
- package/src/cli/tui/panels/Navigator.test.ts +22 -0
- package/src/cli/tui/panels/Navigator.tsx +213 -0
- package/src/cli/tui/slash-catalog.test.ts +31 -0
- package/src/cli/tui/slash-catalog.ts +184 -0
- package/src/cli/tui/slash-run.ts +54 -0
- package/src/cli/tui/state/ports.ts +95 -0
- package/src/cli/tui/theme.ts +24 -0
- package/src/cli/vault-cmd.test.ts +402 -0
- package/src/cli/vault-cmd.ts +727 -69
- package/src/cli/vault-secure-input.test.ts +56 -0
- package/src/cli/vault-secure-input.ts +124 -0
- package/src/compiler/effects-infer.ts +7 -0
- package/src/compiler/extract.ts +12 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +7 -5
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
- package/src/compiler/fixtures/skyport.expected.json +1 -1
- package/src/compiler/generate-adopt.ts +27 -1
- package/src/compiler-entry.ts +7 -0
- package/src/config/define-config.test.ts +111 -47
- package/src/config/driver-defaults.test.ts +115 -0
- package/src/config/driver-defaults.ts +156 -0
- package/src/config/index.ts +246 -131
- package/src/config/resolve-driver.test.ts +14 -19
- package/src/console/server/app.ts +4 -2
- package/src/console/server/console.test.ts +58 -1
- package/src/console/server/flows.ts +22 -2
- package/src/console/server/index.ts +3 -0
- package/src/console/server/operator-db.test.ts +55 -13
- package/src/console/server/operator-db.ts +305 -263
- package/src/console/server/serve.ts +16 -7
- package/src/console/server/state.ts +31 -9
- package/src/console/server/vault.test.ts +120 -5
- package/src/console/server/vault.ts +208 -12
- package/src/console/ui/dist/assets/{index-D-9KFce5.js → index--Y0Uaz-m.js} +2 -2
- package/src/console/ui/dist/assets/panel-vault-BkZdJPX8.js +1 -0
- package/src/console/ui/dist/assets/style-C3L349iZ.css +3 -0
- package/src/console/ui/dist/index.html +2 -2
- package/src/console/ui/shell/client.ts +16 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +54 -1
- package/src/console/ui/vault/VaultA11yView.tsx +33 -1
- package/src/console/ui/vault/backend.test.ts +75 -0
- package/src/console/ui/vault/backend.ts +117 -0
- package/src/console/ui/vault/fixture.ts +21 -2
- package/src/console/ui/vault/index.ts +11 -1
- package/src/console/ui/vault/types.ts +26 -0
- package/src/docker/compose.ts +164 -61
- package/src/docker/derive.ts +106 -12
- package/src/docker/docker.test.ts +156 -110
- package/src/docker/helpers.ts +98 -0
- package/src/docker/images-config.test.ts +163 -0
- package/src/docker/index.ts +30 -5
- package/src/docker/recipes/index.ts +0 -3
- package/src/docker/recipes/llama-cpp.ts +4 -4
- package/src/docker/recipes/pgdog.ts +9 -2
- package/src/docker/resources.test.ts +71 -0
- package/src/docker/resources.ts +176 -0
- package/src/docker/stack-id.test.ts +11 -4
- package/src/docker/stack-id.ts +42 -32
- package/src/docker/stack.integration.test.ts +4 -15
- package/src/docker/types.ts +35 -3
- package/src/drivers/ai-ollama.integration.test.ts +2 -2
- package/src/drivers/conformance.test.ts +11 -12
- package/src/drivers/drizzle-dialect.test.ts +0 -4
- package/src/drivers/drizzle-dialect.ts +7 -13
- package/src/drivers/index.ts +24 -7
- package/src/drivers/journal-postgres.test.ts +3 -1
- package/src/drivers/journal-postgres.ts +4 -2
- package/src/drivers/meilisearch.ts +1 -1
- package/src/drivers/pglite.ts +28 -5
- package/src/drivers/types.ts +9 -11
- package/src/drivers/vault-aws-secrets-manager.test.ts +159 -0
- package/src/drivers/vault-aws-secrets-manager.ts +333 -0
- package/src/drivers/vault-builtin.test.ts +110 -0
- package/src/drivers/vault-builtin.ts +184 -0
- package/src/drivers/vault-driver-removal.test.ts +13 -0
- package/src/drivers/vault-managed.test.ts +95 -0
- package/src/drivers/vault-managed.ts +101 -25
- package/src/drivers/vault-types.ts +25 -5
- package/src/elements/ai/runtime.ts +1 -1
- package/src/elements/clock/chaos.test.ts +4 -1
- package/src/elements/clock/durable.ts +57 -4
- package/src/elements/clock.test.ts +13 -14
- package/src/elements/gate/constants.ts +18 -0
- package/src/elements/gate.test.ts +1 -1
- package/src/elements/gate.ts +2 -2
- package/src/elements/store/domain-ddl.test.ts +5 -12
- package/src/elements/store/emit-drizzle.ts +44 -19
- package/src/elements/store/index-boot.test.ts +15 -116
- package/src/elements/store/load-plugin-tables.ts +1 -1
- package/src/elements/store/resource-list-docs.fixture.ts +12 -6
- package/src/elements/store/resource-list-docs.test.ts +64 -44
- package/src/elements/store/resource.test.ts +17 -10
- package/src/elements/store/runtime.ts +9 -12
- package/src/elements/store/schema-decl.test.ts +16 -32
- package/src/elements/store/seed.test.ts +1 -2
- package/src/elements/store/seed.ts +7 -8
- package/src/elements/store/sql-condition.test.ts +2 -2
- package/src/elements/store/sql-session.test.ts +8 -10
- package/src/elements/store/sql-session.ts +10 -2
- package/src/elements/store.test.ts +13 -13
- package/src/elements/vault/audit.ts +233 -0
- package/src/elements/vault/boot-chain.test.ts +78 -0
- package/src/elements/vault/boot-chain.ts +43 -39
- package/src/elements/vault/builtin-adapter.test.ts +479 -0
- package/src/elements/vault/builtin-adapter.ts +1184 -0
- package/src/elements/vault/crypto.test.ts +237 -0
- package/src/elements/vault/crypto.ts +431 -0
- package/src/elements/vault/declare.ts +164 -3
- package/src/elements/vault/errors.ts +56 -0
- package/src/elements/vault/kms-unseal.test.ts +76 -0
- package/src/elements/vault/kms-unseal.ts +181 -0
- package/src/elements/vault/path.ts +90 -0
- package/src/elements/vault/resilience.test.ts +102 -0
- package/src/elements/vault/resilience.ts +107 -0
- package/src/elements/vault/runtime.ts +36 -4
- package/src/elements/vault/security-checklist.test.ts +130 -0
- package/src/elements/vault/storage.ts +448 -0
- package/src/elements/vault/types.ts +288 -0
- package/src/elements/vault/unseal.ts +150 -0
- package/src/elements/vault.test.ts +72 -4
- package/src/elements/vault.ts +91 -2
- package/src/full.ts +248 -0
- package/src/http.ts +41 -0
- package/src/i18n/interpolate-simple.ts +37 -0
- package/src/i18n/messages.ts +51 -4
- package/src/i18n-entry.ts +7 -0
- package/src/index.ts +35 -145
- package/src/journal-entry.ts +20 -0
- package/src/kernel/adopt-barrel-fresh.test.ts +25 -10
- package/src/kernel/app.ts +40 -42
- package/src/kernel/auto-registry.test.ts +2 -2
- package/src/kernel/boot-bind/ai.test.ts +5 -5
- package/src/kernel/boot-bind/ai.ts +2 -1
- package/src/kernel/boot-bind/channel.test.ts +6 -6
- package/src/kernel/boot-bind/channel.ts +5 -4
- package/src/kernel/boot-bind/clock.ts +5 -5
- package/src/kernel/boot-bind/gate.ts +6 -7
- package/src/kernel/boot-bind/honor-config.test.ts +42 -42
- package/src/kernel/boot-bind/journal.ts +5 -5
- package/src/kernel/boot-bind/signal.ts +3 -1
- package/src/kernel/boot-bind/store.test.ts +55 -37
- package/src/kernel/boot-bind/store.ts +24 -34
- package/src/kernel/boot-bind/vault.ts +19 -4
- package/src/kernel/boot.test.ts +12 -4
- package/src/kernel/boot.ts +31 -19
- package/src/kernel/budget-entry.ts +1 -1
- package/src/kernel/compensate.test.ts +288 -1
- package/src/kernel/compensate.ts +126 -0
- package/src/kernel/concurrency.test.ts +10 -9
- package/src/kernel/effects-stamping.test.ts +27 -2
- package/src/kernel/element-registries.ts +5 -3
- package/src/kernel/flow.ts +4 -2
- package/src/kernel/fx.test.ts +119 -3
- package/src/kernel/fx.ts +215 -40
- package/src/kernel/index.ts +14 -0
- package/src/kernel/journal.ts +130 -7
- package/src/kernel/pipeline.test.ts +20 -6
- package/src/kernel/redacted.test.ts +35 -0
- package/src/kernel/redacted.ts +24 -2
- package/src/kernel/router/create-default.ts +16 -0
- package/src/kernel/router/create-edge.ts +18 -0
- package/src/kernel/router/create.ts +23 -0
- package/src/kernel/router/index.ts +23 -0
- package/src/kernel/router/linear.ts +88 -0
- package/src/kernel/router/regexp.ts +188 -0
- package/src/kernel/router/smart.ts +120 -0
- package/src/kernel/router/trie.ts +108 -0
- package/src/kernel/router/types.ts +56 -0
- package/src/kernel/router.ts +18 -532
- package/src/kernel-entry.ts +43 -0
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/plugins/config-source.test.ts +7 -7
- package/src/release/build-lib.ts +58 -0
- package/src/release/exports.test.ts +4 -2
- package/src/release/exports.ts +30 -4
- package/src/release/measure.ts +101 -22
- package/src/runs/duckdb.ts +7 -2
- package/src/shared/optional-peer.ts +41 -0
- package/src/term.test.ts +3 -44
- package/src/test/create-test-app.ts +12 -0
- package/src/test/provisions.integration.test.ts +1 -1
- package/src/test/reset-element-registries.ts +2 -0
- package/src/upgrade/codemods.test.ts +7 -20
- package/src/upgrade/codemods.ts +93 -60
- package/site/content/docs/console/access.mdx +0 -29
- package/site/content/docs/console/ai.mdx +0 -35
- package/site/content/docs/console/architecture.mdx +0 -35
- package/site/content/docs/console/channels.mdx +0 -37
- package/site/content/docs/console/clock.mdx +0 -51
- package/site/content/docs/console/flows.mdx +0 -31
- package/site/content/docs/console/gates.mdx +0 -73
- package/site/content/docs/console/index.mdx +0 -54
- package/site/content/docs/console/manifest-diff.mdx +0 -34
- package/site/content/docs/console/meta.json +0 -24
- package/site/content/docs/console/overview.mdx +0 -40
- package/site/content/docs/console/plugins.mdx +0 -41
- package/site/content/docs/console/privacy.mdx +0 -32
- package/site/content/docs/console/runs.mdx +0 -50
- package/site/content/docs/console/signals.mdx +0 -40
- package/site/content/docs/console/store.mdx +0 -32
- package/site/content/docs/console/tenancy.mdx +0 -32
- package/site/content/docs/console/traces.mdx +0 -38
- package/site/content/docs/console/vault.mdx +0 -41
- package/site/content/docs/recipes/openbao.mdx +0 -141
- package/src/cli/ask-dev-mode.ts +0 -40
- package/src/cli/dev-mode.test.ts +0 -45
- package/src/cli/dev-mode.ts +0 -78
- package/src/cli/openbao-bootstrap.test.ts +0 -150
- package/src/cli/openbao-bootstrap.ts +0 -321
- package/src/cli/openbao-restart.integration.test.ts +0 -145
- package/src/console/ui/dist/assets/panel-vault-Za1GyJfM.js +0 -1
- package/src/console/ui/dist/assets/style-C8MxEWPd.css +0 -3
- package/src/docker/recipes/openbao.ts +0 -47
- package/src/drivers/libsql.ts +0 -179
- package/src/drivers/sqlite.ts +0 -44
- package/src/drivers/vault-openbao.test.ts +0 -97
- package/src/drivers/vault-openbao.ts +0 -143
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless `oke dev` controller — UI-agnostic lifecycle + events.
|
|
3
|
+
*
|
|
4
|
+
* Ink TUI and plain CLI both consume this; never kill a foreign pid.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { APP_PORT, CONSOLE_PORT, DOCS_MCP_PORT, MCP_PORT } from "../runtime/types.ts";
|
|
9
|
+
import type { DevStatus } from "../term.ts";
|
|
10
|
+
import { type DevOptions, type DevSession, runDev } from "./dev.ts";
|
|
11
|
+
import {
|
|
12
|
+
clearDevSessionLock,
|
|
13
|
+
type DevOwnership,
|
|
14
|
+
type DevSessionLock,
|
|
15
|
+
type DevSessionPorts,
|
|
16
|
+
probeDevPortsBusy,
|
|
17
|
+
resolveDevOwnership,
|
|
18
|
+
writeDevSessionLock,
|
|
19
|
+
} from "./dev-session-lock.ts";
|
|
20
|
+
|
|
21
|
+
/** Controller event kinds. */
|
|
22
|
+
export type DevControllerEvent =
|
|
23
|
+
| { readonly type: "boot"; readonly phase: string; readonly detail?: string }
|
|
24
|
+
| {
|
|
25
|
+
readonly type: "ready";
|
|
26
|
+
readonly ports: DevSessionPorts;
|
|
27
|
+
readonly ownership: DevOwnership;
|
|
28
|
+
}
|
|
29
|
+
| {
|
|
30
|
+
readonly type: "status";
|
|
31
|
+
readonly service: string;
|
|
32
|
+
readonly status: DevStatus;
|
|
33
|
+
readonly detail?: string;
|
|
34
|
+
}
|
|
35
|
+
| { readonly type: "log"; readonly text: string }
|
|
36
|
+
| { readonly type: "error"; readonly message: string; readonly fatal?: boolean }
|
|
37
|
+
| {
|
|
38
|
+
readonly type: "exit";
|
|
39
|
+
readonly code: number;
|
|
40
|
+
readonly signal?: string;
|
|
41
|
+
}
|
|
42
|
+
| { readonly type: "ownership"; readonly ownership: DevOwnership };
|
|
43
|
+
|
|
44
|
+
/** Listener for {@link DevController} events. */
|
|
45
|
+
export type DevControllerListener = (event: DevControllerEvent) => void;
|
|
46
|
+
|
|
47
|
+
/** Options for {@link DevController.start}. */
|
|
48
|
+
export type DevControllerStartOptions = Omit<DevOptions, "keepAlive" | "write" | "onReady"> & {
|
|
49
|
+
readonly cwd?: string;
|
|
50
|
+
/** Forward formatted chrome to stdout as well as events (default false). */
|
|
51
|
+
readonly echoLogs?: boolean;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Headless orchestration for a project’s dev stack.
|
|
56
|
+
*/
|
|
57
|
+
export class DevController {
|
|
58
|
+
private readonly listeners = new Set<DevControllerListener>();
|
|
59
|
+
private session: DevSession | null = null;
|
|
60
|
+
private ownership: DevOwnership = "stopped";
|
|
61
|
+
private lock: DevSessionLock | null = null;
|
|
62
|
+
private starting = false;
|
|
63
|
+
private readonly preferredPorts: DevSessionPorts;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param preferredPorts - Default ports when probing without a lock
|
|
67
|
+
*/
|
|
68
|
+
constructor(
|
|
69
|
+
preferredPorts: DevSessionPorts = {
|
|
70
|
+
app: APP_PORT,
|
|
71
|
+
console: CONSOLE_PORT,
|
|
72
|
+
mcp: MCP_PORT,
|
|
73
|
+
docsMcp: DOCS_MCP_PORT,
|
|
74
|
+
},
|
|
75
|
+
) {
|
|
76
|
+
this.preferredPorts = preferredPorts;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Current ownership classification. */
|
|
80
|
+
getOwnership(): DevOwnership {
|
|
81
|
+
return this.ownership;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Live session when managed and started. */
|
|
85
|
+
getSession(): DevSession | null {
|
|
86
|
+
return this.session;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Last lock we wrote (managed only). */
|
|
90
|
+
getLock(): DevSessionLock | null {
|
|
91
|
+
return this.lock;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Subscribe to controller events.
|
|
96
|
+
*
|
|
97
|
+
* @param listener - Callback
|
|
98
|
+
*/
|
|
99
|
+
on(listener: DevControllerListener): () => void {
|
|
100
|
+
this.listeners.add(listener);
|
|
101
|
+
return () => {
|
|
102
|
+
this.listeners.delete(listener);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Emit an event to all listeners.
|
|
108
|
+
*
|
|
109
|
+
* @param event - Payload
|
|
110
|
+
*/
|
|
111
|
+
emit(event: DevControllerEvent): void {
|
|
112
|
+
for (const listener of this.listeners) {
|
|
113
|
+
try {
|
|
114
|
+
listener(event);
|
|
115
|
+
} catch {
|
|
116
|
+
// listeners must not break the controller
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Discover an existing stack without starting one.
|
|
123
|
+
*
|
|
124
|
+
* @param cwd - Project root
|
|
125
|
+
*/
|
|
126
|
+
async attach(cwd: string = process.cwd()): Promise<{
|
|
127
|
+
readonly ownership: DevOwnership;
|
|
128
|
+
readonly lock: DevSessionLock | null;
|
|
129
|
+
readonly busy: Awaited<ReturnType<typeof probeDevPortsBusy>>;
|
|
130
|
+
}> {
|
|
131
|
+
const root = resolve(cwd);
|
|
132
|
+
const resolved = await resolveDevOwnership(root, this.preferredPorts);
|
|
133
|
+
this.ownership = resolved.ownership;
|
|
134
|
+
this.lock = resolved.lock;
|
|
135
|
+
this.emit({ type: "ownership", ownership: this.ownership });
|
|
136
|
+
if (this.ownership === "managed" || this.ownership === "external") {
|
|
137
|
+
const ports = resolved.lock?.ports ?? this.preferredPorts;
|
|
138
|
+
this.emit({
|
|
139
|
+
type: "ready",
|
|
140
|
+
ports,
|
|
141
|
+
ownership: this.ownership,
|
|
142
|
+
});
|
|
143
|
+
this.emit({
|
|
144
|
+
type: "log",
|
|
145
|
+
text:
|
|
146
|
+
this.ownership === "managed"
|
|
147
|
+
? "Attached to managed oke dev session\n"
|
|
148
|
+
: "Connected to external oke dev (read-only)\n",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return resolved;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Start a managed session. Refuses if ports are already taken by another stack.
|
|
156
|
+
*
|
|
157
|
+
* @param options - Dev options (keepAlive forced false)
|
|
158
|
+
*/
|
|
159
|
+
async start(options: DevControllerStartOptions = {}): Promise<number> {
|
|
160
|
+
if (this.starting) {
|
|
161
|
+
this.emit({ type: "error", message: "dev start already in progress" });
|
|
162
|
+
return 1;
|
|
163
|
+
}
|
|
164
|
+
if (this.session) {
|
|
165
|
+
this.emit({ type: "error", message: "dev session already running (managed)" });
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
170
|
+
const attached = await this.attach(cwd);
|
|
171
|
+
if (attached.ownership === "external") {
|
|
172
|
+
this.emit({
|
|
173
|
+
type: "error",
|
|
174
|
+
message: "oke dev already running externally — attach only (will not start a second stack)",
|
|
175
|
+
fatal: false,
|
|
176
|
+
});
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
if (attached.ownership === "managed" && attached.lock && attached.lock.pid !== process.pid) {
|
|
180
|
+
// Another managed process still alive — do not steal.
|
|
181
|
+
this.emit({
|
|
182
|
+
type: "error",
|
|
183
|
+
message: `oke dev already managed by pid ${attached.lock.pid}`,
|
|
184
|
+
fatal: false,
|
|
185
|
+
});
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this.starting = true;
|
|
190
|
+
this.emit({ type: "boot", phase: "starting" });
|
|
191
|
+
|
|
192
|
+
const echo = options.echoLogs === true;
|
|
193
|
+
const write = (text: string): void => {
|
|
194
|
+
this.emit({ type: "log", text });
|
|
195
|
+
if (echo) process.stdout.write(text);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const result = await runDev({
|
|
200
|
+
...options,
|
|
201
|
+
cwd,
|
|
202
|
+
keepAlive: false,
|
|
203
|
+
write,
|
|
204
|
+
onReady: async (session) => {
|
|
205
|
+
const ports: DevSessionPorts = {
|
|
206
|
+
app: session.appPort,
|
|
207
|
+
console: session.consolePort,
|
|
208
|
+
mcp: session.mcpPort,
|
|
209
|
+
docsMcp: session.docsMcpPort,
|
|
210
|
+
};
|
|
211
|
+
const lock: DevSessionLock = {
|
|
212
|
+
pid: process.pid,
|
|
213
|
+
ports,
|
|
214
|
+
startedAt: new Date().toISOString(),
|
|
215
|
+
cwd,
|
|
216
|
+
};
|
|
217
|
+
await writeDevSessionLock(cwd, lock);
|
|
218
|
+
this.lock = lock;
|
|
219
|
+
this.session = session;
|
|
220
|
+
this.ownership = "managed";
|
|
221
|
+
this.emit({ type: "ownership", ownership: "managed" });
|
|
222
|
+
this.emit({ type: "ready", ports, ownership: "managed" });
|
|
223
|
+
this.emit({ type: "boot", phase: "ready" });
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (result.code !== 0 || !result.session) {
|
|
228
|
+
this.ownership = "stopped";
|
|
229
|
+
this.session = null;
|
|
230
|
+
this.emit({ type: "error", message: "oke dev failed to start", fatal: true });
|
|
231
|
+
this.emit({ type: "exit", code: result.code });
|
|
232
|
+
return result.code;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// onReady may have already set session; ensure we hold it.
|
|
236
|
+
this.session = result.session;
|
|
237
|
+
return 0;
|
|
238
|
+
} catch (err) {
|
|
239
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
240
|
+
this.emit({ type: "error", message, fatal: true });
|
|
241
|
+
this.emit({ type: "exit", code: 1 });
|
|
242
|
+
return 1;
|
|
243
|
+
} finally {
|
|
244
|
+
this.starting = false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Stop only a session this controller owns. Never kills an external pid.
|
|
250
|
+
*/
|
|
251
|
+
async stop(): Promise<void> {
|
|
252
|
+
if (this.ownership === "external") {
|
|
253
|
+
this.emit({
|
|
254
|
+
type: "error",
|
|
255
|
+
message: "refusing to stop external oke dev session",
|
|
256
|
+
fatal: false,
|
|
257
|
+
});
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (this.ownership !== "managed" || !this.session) {
|
|
261
|
+
this.ownership = "stopped";
|
|
262
|
+
this.emit({ type: "ownership", ownership: "stopped" });
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const cwd = this.lock?.cwd ?? process.cwd();
|
|
267
|
+
try {
|
|
268
|
+
this.session.stop();
|
|
269
|
+
} catch (err) {
|
|
270
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
271
|
+
this.emit({ type: "error", message });
|
|
272
|
+
}
|
|
273
|
+
this.session = null;
|
|
274
|
+
this.lock = null;
|
|
275
|
+
this.ownership = "stopped";
|
|
276
|
+
await clearDevSessionLock(cwd);
|
|
277
|
+
this.emit({ type: "ownership", ownership: "stopped" });
|
|
278
|
+
this.emit({ type: "exit", code: 0 });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Stop then start (managed only).
|
|
283
|
+
*
|
|
284
|
+
* @param options - Start options
|
|
285
|
+
*/
|
|
286
|
+
async restart(options: DevControllerStartOptions = {}): Promise<number> {
|
|
287
|
+
if (this.ownership === "external") {
|
|
288
|
+
this.emit({
|
|
289
|
+
type: "error",
|
|
290
|
+
message: "cannot restart an external oke dev session",
|
|
291
|
+
fatal: false,
|
|
292
|
+
});
|
|
293
|
+
return 1;
|
|
294
|
+
}
|
|
295
|
+
await this.stop();
|
|
296
|
+
return this.start(options);
|
|
297
|
+
}
|
|
298
|
+
}
|
package/src/cli/dev-controls.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Dev control key mapping + pure dispatcher (tests).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Compose health / model phase update the status board above Logs — it
|
|
7
|
-
* already lists every service with a live ● status, so refresh alone is
|
|
8
|
-
* enough to see the stack; there is no separate services panel.
|
|
4
|
+
* Interactive raw-mode stdin was removed — Ink `useInput` owns TTY input
|
|
5
|
+
* via {@link ./tui/keys.ts}.
|
|
9
6
|
*/
|
|
10
7
|
|
|
11
8
|
import { formatStatusLine, termColorEnabled, termStyle } from "../term.ts";
|
|
9
|
+
import { mapDevControlInput } from "./tui/keys.ts";
|
|
12
10
|
|
|
13
11
|
/** Compose action against the whole stack. */
|
|
14
12
|
export type DevComposeControlAction = "up" | "stop";
|
|
15
13
|
|
|
16
|
-
/** Options for {@link
|
|
14
|
+
/** Options for {@link createDevControlDispatcher}. */
|
|
17
15
|
export type StartDevControlsOptions = {
|
|
18
16
|
readonly write: (text: string) => void;
|
|
19
17
|
/** Called on `q` / Ctrl+C. */
|
|
@@ -23,14 +21,13 @@ export type StartDevControlsOptions = {
|
|
|
23
21
|
*/
|
|
24
22
|
readonly onRefresh?: () => void | Promise<void>;
|
|
25
23
|
/**
|
|
26
|
-
* Show help in a clean pane
|
|
27
|
-
* interleaved with request logs).
|
|
24
|
+
* Show help in a clean pane.
|
|
28
25
|
*
|
|
29
26
|
* @param body - Formatted panel text
|
|
30
27
|
*/
|
|
31
28
|
readonly onShowPanel?: (body: string) => void | Promise<void>;
|
|
32
29
|
/**
|
|
33
|
-
* After a compose up/stop — sync health into the board
|
|
30
|
+
* After a compose up/stop — sync health into the board.
|
|
34
31
|
*/
|
|
35
32
|
readonly onComposeSettled?: () => void | Promise<void>;
|
|
36
33
|
/**
|
|
@@ -39,16 +36,9 @@ export type StartDevControlsOptions = {
|
|
|
39
36
|
* @param action - up / stop
|
|
40
37
|
*/
|
|
41
38
|
readonly composeAction: (action: DevComposeControlAction) => Promise<void>;
|
|
42
|
-
readonly stdin?: NodeJS.ReadStream;
|
|
43
|
-
readonly isTTY?: boolean;
|
|
44
39
|
readonly color?: boolean;
|
|
45
40
|
};
|
|
46
41
|
|
|
47
|
-
/** Handle returned by {@link startDevControls}. */
|
|
48
|
-
export type DevControlsHandle = {
|
|
49
|
-
readonly stop: () => void;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
42
|
/**
|
|
53
43
|
* One-line keys hint under Logs.
|
|
54
44
|
*
|
|
@@ -97,24 +87,18 @@ export function formatDevControlsHelp(color: boolean = termColorEnabled()): stri
|
|
|
97
87
|
*/
|
|
98
88
|
export function parseDevControlKey(chunk: string): string | null {
|
|
99
89
|
if (chunk.length === 0) return null;
|
|
100
|
-
if (chunk === "\u0003") return "
|
|
101
|
-
|
|
102
|
-
if (ch === "?" || ch === "h" || ch === "H") return "?";
|
|
103
|
-
if (ch === "q" || ch === "Q") return "q";
|
|
104
|
-
if (ch === "r" || ch === "R") return "r";
|
|
105
|
-
if (ch === "u" || ch === "U") return "u";
|
|
106
|
-
if (ch === "x" || ch === "X") return "x";
|
|
107
|
-
return null;
|
|
90
|
+
if (chunk === "\u0003") return mapDevControlInput("c", { ctrl: true });
|
|
91
|
+
return mapDevControlInput(chunk[0] ?? "", {});
|
|
108
92
|
}
|
|
109
93
|
|
|
110
|
-
/** Key dispatcher for tests
|
|
94
|
+
/** Key dispatcher for tests. */
|
|
111
95
|
export type DevControlDispatcher = {
|
|
112
96
|
readonly handleKey: (key: string) => void;
|
|
113
97
|
readonly stop: () => void;
|
|
114
98
|
};
|
|
115
99
|
|
|
116
100
|
/**
|
|
117
|
-
* Pure key dispatcher (no stdin) — used by tests
|
|
101
|
+
* Pure key dispatcher (no stdin) — used by tests.
|
|
118
102
|
*
|
|
119
103
|
* @param opts - Quit / compose actions
|
|
120
104
|
*/
|
|
@@ -165,41 +149,3 @@ export function createDevControlDispatcher(opts: StartDevControlsOptions): DevCo
|
|
|
165
149
|
},
|
|
166
150
|
};
|
|
167
151
|
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Start raw-mode keyboard controls. No-op when stdin is not a TTY.
|
|
171
|
-
*
|
|
172
|
-
* @param opts - Quit / compose actions
|
|
173
|
-
*/
|
|
174
|
-
export function startDevControls(opts: StartDevControlsOptions): DevControlsHandle {
|
|
175
|
-
const stdin = opts.stdin ?? process.stdin;
|
|
176
|
-
const isTTY = opts.isTTY ?? stdin.isTTY === true;
|
|
177
|
-
const dispatcher = createDevControlDispatcher(opts);
|
|
178
|
-
|
|
179
|
-
if (!isTTY || typeof stdin.setRawMode !== "function") {
|
|
180
|
-
return { stop: () => dispatcher.stop() };
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const onData = (buf: string | Buffer): void => {
|
|
184
|
-
const chunk = typeof buf === "string" ? buf : buf.toString("utf8");
|
|
185
|
-
const key = parseDevControlKey(chunk);
|
|
186
|
-
if (key) dispatcher.handleKey(key);
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
stdin.setRawMode(true);
|
|
190
|
-
stdin.resume();
|
|
191
|
-
stdin.setEncoding("utf8");
|
|
192
|
-
stdin.on("data", onData);
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
stop() {
|
|
196
|
-
dispatcher.stop();
|
|
197
|
-
stdin.off("data", onData);
|
|
198
|
-
try {
|
|
199
|
-
if (typeof stdin.setRawMode === "function") stdin.setRawMode(false);
|
|
200
|
-
} catch {
|
|
201
|
-
// already closed
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
}
|
|
@@ -6,10 +6,39 @@ import { afterEach, describe, expect, test } from "bun:test";
|
|
|
6
6
|
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
-
import { runDev, type DevSession, type DevWatchFn } from "./dev.ts";
|
|
9
|
+
import { runDev, type DevOptions, type DevSession, type DevWatchFn } from "./dev.ts";
|
|
10
10
|
|
|
11
11
|
const sessions: DevSession[] = [];
|
|
12
12
|
|
|
13
|
+
const STUB_IMAGES = {
|
|
14
|
+
"store.sql": "postgres:16-alpine",
|
|
15
|
+
"store.kv": "redis:7-alpine",
|
|
16
|
+
} as const;
|
|
17
|
+
const STUB_CREDENTIALS = {
|
|
18
|
+
"store.sql": {
|
|
19
|
+
user: "oke",
|
|
20
|
+
password: "test-password-not-in-yaml",
|
|
21
|
+
database: "oke",
|
|
22
|
+
},
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
function stubCompose(): Pick<
|
|
26
|
+
DevOptions,
|
|
27
|
+
"images" | "credentials" | "composeUp" | "composeHealth" | "composeStop"
|
|
28
|
+
> {
|
|
29
|
+
return {
|
|
30
|
+
images: STUB_IMAGES,
|
|
31
|
+
credentials: STUB_CREDENTIALS,
|
|
32
|
+
composeUp: async () => {},
|
|
33
|
+
composeHealth: async () =>
|
|
34
|
+
new Map([
|
|
35
|
+
["store-sql", "ready"],
|
|
36
|
+
["store-kv", "ready"],
|
|
37
|
+
]),
|
|
38
|
+
composeStop: () => {},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
13
42
|
afterEach(() => {
|
|
14
43
|
for (const s of sessions.splice(0)) s.stop();
|
|
15
44
|
});
|
|
@@ -46,25 +75,26 @@ async function scaffoldProject(): Promise<string> {
|
|
|
46
75
|
join(dir, "oke.config.ts"),
|
|
47
76
|
`import { defineConfig } from ${JSON.stringify(configMod)};
|
|
48
77
|
export default defineConfig({
|
|
49
|
-
drivers: { store: { sql: {
|
|
78
|
+
drivers: { store: { sql: { dev: "postgres", test: "pglite", prod: "postgres" } } },
|
|
50
79
|
});
|
|
51
80
|
`,
|
|
52
81
|
);
|
|
53
82
|
await writeFile(join(dir, "src", "app.ts"), `export {};\n`);
|
|
54
83
|
await writeFile(
|
|
55
84
|
join(dir, "src", "schema.ts"),
|
|
56
|
-
`import {
|
|
57
|
-
export const entries =
|
|
85
|
+
`import { pgTable, text } from "drizzle-orm/pg-core";
|
|
86
|
+
export const entries = pgTable("entries", { id: text("id").primaryKey() });
|
|
58
87
|
`,
|
|
59
88
|
);
|
|
60
89
|
await writeFile(
|
|
61
90
|
join(dir, "drizzle.config.ts"),
|
|
62
91
|
`import { defineConfig } from "drizzle-kit";
|
|
63
92
|
export default defineConfig({
|
|
64
|
-
dialect: "
|
|
93
|
+
dialect: "postgresql",
|
|
94
|
+
driver: "pglite",
|
|
65
95
|
schema: "./src/schema.ts",
|
|
66
96
|
out: "./drizzle",
|
|
67
|
-
dbCredentials: { url: "
|
|
97
|
+
dbCredentials: { url: "memory://" },
|
|
68
98
|
});
|
|
69
99
|
`,
|
|
70
100
|
);
|
|
@@ -83,7 +113,7 @@ describe("oke dev db auto-push", () => {
|
|
|
83
113
|
|
|
84
114
|
const result = await runDev({
|
|
85
115
|
cwd,
|
|
86
|
-
|
|
116
|
+
...stubCompose(),
|
|
87
117
|
keepAlive: false,
|
|
88
118
|
dryRun: false,
|
|
89
119
|
appPort: 0,
|
|
@@ -127,8 +157,8 @@ describe("oke dev db auto-push", () => {
|
|
|
127
157
|
const before = pushes.length;
|
|
128
158
|
await writeFile(
|
|
129
159
|
join(cwd, "src", "schema.ts"),
|
|
130
|
-
`import {
|
|
131
|
-
export const entries =
|
|
160
|
+
`import { integer, pgTable, text } from "drizzle-orm/pg-core";
|
|
161
|
+
export const entries = pgTable("entries", {
|
|
132
162
|
id: text("id").primaryKey(),
|
|
133
163
|
n: integer("n"),
|
|
134
164
|
});
|
|
@@ -139,7 +169,7 @@ export const entries = sqliteTable("entries", {
|
|
|
139
169
|
expect(pushes.length).toBeGreaterThan(before);
|
|
140
170
|
});
|
|
141
171
|
|
|
142
|
-
test("schema.
|
|
172
|
+
test("schema.drizzle.ts change does not re-trigger dbPush", async () => {
|
|
143
173
|
const cwd = await scaffoldProject();
|
|
144
174
|
const pushes: string[] = [];
|
|
145
175
|
const watcher = controlledWatcher();
|
|
@@ -150,7 +180,7 @@ export const entries = sqliteTable("entries", {
|
|
|
150
180
|
|
|
151
181
|
const result = await runDev({
|
|
152
182
|
cwd,
|
|
153
|
-
|
|
183
|
+
...stubCompose(),
|
|
154
184
|
keepAlive: false,
|
|
155
185
|
dryRun: false,
|
|
156
186
|
appPort: 0,
|
|
@@ -191,7 +221,9 @@ export const entries = sqliteTable("entries", {
|
|
|
191
221
|
const before = pushes.length;
|
|
192
222
|
expect(before).toBeGreaterThanOrEqual(1);
|
|
193
223
|
|
|
194
|
-
// Simulate emit writing schema.
|
|
224
|
+
// Simulate emit writing schema.drizzle.ts after push — must not loop.
|
|
225
|
+
watcher.change("schema.drizzle.ts");
|
|
226
|
+
watcher.change("src/db/schema.drizzle.ts");
|
|
195
227
|
watcher.change("schema.generated.ts");
|
|
196
228
|
watcher.change("src/schema.generated.ts");
|
|
197
229
|
await Bun.sleep(500);
|
|
@@ -205,7 +237,7 @@ export const entries = sqliteTable("entries", {
|
|
|
205
237
|
|
|
206
238
|
const result = await runDev({
|
|
207
239
|
cwd,
|
|
208
|
-
|
|
240
|
+
...stubCompose(),
|
|
209
241
|
noDbPush: true,
|
|
210
242
|
keepAlive: false,
|
|
211
243
|
appPort: 0,
|
|
@@ -241,8 +273,8 @@ export const entries = sqliteTable("entries", {
|
|
|
241
273
|
expect(result.code).toBe(0);
|
|
242
274
|
await writeFile(
|
|
243
275
|
join(cwd, "src", "schema.ts"),
|
|
244
|
-
`import {
|
|
245
|
-
export const entries =
|
|
276
|
+
`import { pgTable, text } from "drizzle-orm/pg-core";
|
|
277
|
+
export const entries = pgTable("entries", { id: text("id").primaryKey(), x: text("x") });
|
|
246
278
|
`,
|
|
247
279
|
);
|
|
248
280
|
watcher.change("schema.ts");
|
|
@@ -22,41 +22,41 @@ async function tempDir(): Promise<string> {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
describe("sqlDialectForEnv", () => {
|
|
25
|
-
test("
|
|
25
|
+
test("test → postgresql (pglite), dev → postgresql", async () => {
|
|
26
26
|
const config = {
|
|
27
27
|
drivers: {
|
|
28
|
-
store: { sql: {
|
|
28
|
+
store: { sql: { dev: "postgres", test: "pglite", prod: "postgres" } },
|
|
29
29
|
},
|
|
30
30
|
} as never;
|
|
31
|
-
expect(sqlDialectForEnv(config, "
|
|
32
|
-
expect(sqlDialectForEnv(config, "
|
|
31
|
+
expect(sqlDialectForEnv(config, "test").dialect).toBe("postgresql");
|
|
32
|
+
expect(sqlDialectForEnv(config, "dev").dialect).toBe("postgresql");
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
test("missing config →
|
|
36
|
-
expect(sqlDialectForEnv(null, "
|
|
35
|
+
test("missing config → postgresql default", () => {
|
|
36
|
+
expect(sqlDialectForEnv(null, "test").dialect).toBe("postgresql");
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
describe("syncDevSchema", () => {
|
|
41
|
-
test("
|
|
41
|
+
test("test: ensures drizzle config, emits, pushes with env", async () => {
|
|
42
42
|
const dir = await tempDir();
|
|
43
43
|
const calls: { cwd: string; env: ConfigEnv }[] = [];
|
|
44
|
-
const result = await syncDevSchema(dir, "
|
|
44
|
+
const result = await syncDevSchema(dir, "test", {
|
|
45
45
|
write: () => {},
|
|
46
46
|
pushFn: async (cwd, env) => {
|
|
47
47
|
calls.push({ cwd, env });
|
|
48
48
|
return 0;
|
|
49
49
|
},
|
|
50
50
|
});
|
|
51
|
-
expect(result.dialect).toBe("
|
|
51
|
+
expect(result.dialect).toBe("postgresql");
|
|
52
52
|
expect(result.pushed).toBe(true);
|
|
53
|
-
expect(calls).toEqual([{ cwd: dir, env: "
|
|
53
|
+
expect(calls).toEqual([{ cwd: dir, env: "test" }]);
|
|
54
54
|
expect(await Bun.file(join(dir, "drizzle.config.ts")).exists()).toBe(true);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
test("push failure surfaces code without throwing", async () => {
|
|
58
58
|
const dir = await tempDir();
|
|
59
|
-
const result = await syncDevSchema(dir, "
|
|
59
|
+
const result = await syncDevSchema(dir, "test", {
|
|
60
60
|
write: () => {},
|
|
61
61
|
pushFn: async () => 1,
|
|
62
62
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared
|
|
2
|
+
* Shared schema sync pipeline for `oke dev` / `oke db`
|
|
3
3
|
* one-shot start. Ensures artefacts, emits for the active dialect, then
|
|
4
4
|
* pushes via drizzle-kit. Data planes stay isolated — no row copying.
|
|
5
5
|
*/
|
|
@@ -24,7 +24,7 @@ import { loadOkeConfig, resolveImages } from "./load-config.ts";
|
|
|
24
24
|
export interface DevSchemaSyncOptions {
|
|
25
25
|
/** Write output (default: stdout). */
|
|
26
26
|
readonly write?: (text: string) => void;
|
|
27
|
-
/** Skip emitting `schema.
|
|
27
|
+
/** Skip emitting `schema.drizzle.ts` (tests). */
|
|
28
28
|
readonly skipEmit?: boolean;
|
|
29
29
|
/** Injectable push (tests). Default: `runDb("push", ...)`. */
|
|
30
30
|
readonly pushFn?: (cwd: string, env: ConfigEnv) => Promise<number>;
|
|
@@ -40,7 +40,7 @@ export interface DevSchemaSyncOptions {
|
|
|
40
40
|
/** Outcome of {@link syncDevSchema}. */
|
|
41
41
|
export interface DevSchemaSyncResult {
|
|
42
42
|
readonly env: ConfigEnv;
|
|
43
|
-
readonly dialect: "
|
|
43
|
+
readonly dialect: "postgresql";
|
|
44
44
|
readonly pushed: boolean;
|
|
45
45
|
readonly code: number;
|
|
46
46
|
}
|
|
@@ -54,11 +54,11 @@ export interface DevSchemaSyncResult {
|
|
|
54
54
|
export function sqlDialectForEnv(
|
|
55
55
|
config: OkeConfig | null | undefined,
|
|
56
56
|
env: ConfigEnv,
|
|
57
|
-
): { readonly driverId: string; readonly dialect: "
|
|
58
|
-
const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "
|
|
59
|
-
if (driverId !== "
|
|
57
|
+
): { readonly driverId: string; readonly dialect: "postgresql" } {
|
|
58
|
+
const driverId = resolveDriverId(config?.drivers?.store?.sql, env) ?? "postgres";
|
|
59
|
+
if (driverId !== "postgres" && driverId !== "pglite") {
|
|
60
60
|
throw new Error(
|
|
61
|
-
`store.sql driver "${driverId}" is not supported by drizzle-kit (
|
|
61
|
+
`store.sql driver "${driverId}" is not supported by drizzle-kit (postgres | pglite)`,
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
return { driverId, dialect: drizzleDialectFromSqlDriver(driverId) };
|
|
@@ -103,7 +103,7 @@ async function ensureDockerStack(
|
|
|
103
103
|
await writeDerivedFiles(derived, resolve(cwd, DEFAULT_DOCKER_DIR), { writeStackEnv: true });
|
|
104
104
|
if (!quietComposeReady) {
|
|
105
105
|
write(
|
|
106
|
-
"oke: docker compose env ready under docker/.env.docker (start containers with `oke dev
|
|
106
|
+
"oke: docker compose env ready under docker/.env.docker (start containers with `oke dev` or docker compose up)\n",
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -114,7 +114,7 @@ async function ensureDockerStack(
|
|
|
114
114
|
* exists (writes `docker/.env.docker`) before pushing to the live DB.
|
|
115
115
|
*
|
|
116
116
|
* @param cwd - Project root
|
|
117
|
-
* @param env - Target
|
|
117
|
+
* @param env - Target {@link ConfigEnv} (`dev` for compose, `test` for laptop)
|
|
118
118
|
* @param options - Injectables
|
|
119
119
|
*/
|
|
120
120
|
export async function syncDevSchema(
|
|
@@ -130,7 +130,7 @@ export async function syncDevSchema(
|
|
|
130
130
|
|
|
131
131
|
const { dialect } = sqlDialectForEnv(config, env);
|
|
132
132
|
|
|
133
|
-
if (env === "
|
|
133
|
+
if (env === "dev") {
|
|
134
134
|
if (!config) {
|
|
135
135
|
throw new Error("docker mode: oke.config.ts not found");
|
|
136
136
|
}
|