okengine 0.10.3 → 0.11.1
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/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 -7
- package/site/content/docs/elements/channel.mdx +9 -7
- package/site/content/docs/elements/clock.mdx +8 -8
- package/site/content/docs/elements/flow.mdx +46 -47
- package/site/content/docs/elements/gate.mdx +6 -5
- package/site/content/docs/elements/signal.mdx +2 -2
- package/site/content/docs/elements/store.mdx +71 -78
- package/site/content/docs/elements/vault.mdx +89 -70
- package/site/content/docs/get-started/basic-usage.mdx +8 -8
- 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 +2 -2
- 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 +11 -4
- 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 +27 -15
- 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 +1 -1
- 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 +166 -121
- package/src/cli/dev.ts +138 -192
- 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 +55 -15
- 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 +16 -12
- package/src/cli/load-config.source.test.ts +50 -0
- package/src/cli/load-config.ts +34 -28
- 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 +28 -10
- 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/fixtures/skyport/oke.config.ts +3 -3
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -1
- package/src/compiler/fixtures/skyport.expected.json +1 -1
- package/src/compiler-entry.ts +7 -0
- package/src/config/define-config.test.ts +111 -47
- package/src/config/driver-defaults.test.ts +16 -23
- package/src/config/driver-defaults.ts +33 -63
- package/src/config/index.ts +169 -144
- 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 +3 -8
- 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/config.ts +13 -3
- package/src/elements/gate/constants.ts +18 -0
- package/src/elements/gate/declare.ts +1 -1
- package/src/elements/gate/strategies.ts +2 -12
- 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-auth.ts +98 -0
- package/src/kernel/app.ts +77 -110
- 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 +3 -10
- package/src/kernel/boot-bind/clock.ts +2 -2
- package/src/kernel/boot-bind/gate.ts +5 -9
- package/src/kernel/boot-bind/honor-config.test.ts +42 -42
- package/src/kernel/boot-bind/journal.ts +2 -2
- package/src/kernel/boot-bind/store.test.ts +43 -44
- package/src/kernel/boot-bind/store.ts +16 -41
- package/src/kernel/boot-bind/vault.ts +16 -1
- package/src/kernel/boot.test.ts +13 -19
- 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 +61 -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/lazy-src.ts +79 -0
- 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/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
package/src/cli/db.test.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
|
-
import { Database } from "bun:sqlite";
|
|
7
6
|
import { mkdir, mkdtemp, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
8
7
|
import { tmpdir } from "node:os";
|
|
9
8
|
import { join, resolve } from "node:path";
|
|
9
|
+
import { connectPglite } from "../drivers/pglite.ts";
|
|
10
10
|
import { createFx } from "../kernel/fx.ts";
|
|
11
11
|
import { defineSeed, type SeedFn } from "../elements/store/seed.ts";
|
|
12
12
|
import { EXIT_OK, EXIT_RUNTIME, EXIT_USAGE } from "./exit.ts";
|
|
@@ -151,7 +151,7 @@ describe("oke db emit — live plugged plugin tables", () => {
|
|
|
151
151
|
join(dir, "oke.config.ts"),
|
|
152
152
|
`import { defineConfig } from ${JSON.stringify(CONFIG_MOD)};
|
|
153
153
|
export default defineConfig({
|
|
154
|
-
drivers: { store: { sql: {
|
|
154
|
+
drivers: { store: { sql: { dev: "postgres", test: "pglite", prod: "postgres" } } },
|
|
155
155
|
db: { declare: "src/schema.decl.ts", generated: "src/schema.generated.ts" },
|
|
156
156
|
});
|
|
157
157
|
`,
|
|
@@ -185,10 +185,10 @@ export const app = oke({ name: "plugin-emit" }).plug(audit);
|
|
|
185
185
|
join(dir, "drizzle.config.ts"),
|
|
186
186
|
`import { defineConfig } from "drizzle-kit";
|
|
187
187
|
export default defineConfig({
|
|
188
|
-
dialect: "
|
|
188
|
+
dialect: "postgresql",
|
|
189
189
|
schema: "./src/schema.generated.ts",
|
|
190
190
|
out: "./drizzle",
|
|
191
|
-
dbCredentials: { url: "
|
|
191
|
+
dbCredentials: { url: "postgres://127.0.0.1:5432/oke" },
|
|
192
192
|
});
|
|
193
193
|
`,
|
|
194
194
|
);
|
|
@@ -205,8 +205,8 @@ export default defineConfig({
|
|
|
205
205
|
expect(out.join("")).toMatch(/emitted.*schema\.generated\.ts/);
|
|
206
206
|
|
|
207
207
|
const generated = await readFile(join(dir, "src", "schema.generated.ts"), "utf8");
|
|
208
|
-
expect(generated).toContain('
|
|
209
|
-
expect(generated).toContain('
|
|
208
|
+
expect(generated).toContain('pgTable("notes"');
|
|
209
|
+
expect(generated).toContain('pgTable("audit_events"');
|
|
210
210
|
expect(generated).toContain('text("actor_id")');
|
|
211
211
|
expect(generated).toContain('text("action")');
|
|
212
212
|
expect(generated).toContain(".notNull()");
|
|
@@ -220,7 +220,7 @@ export default defineConfig({
|
|
|
220
220
|
join(dir, "oke.config.ts"),
|
|
221
221
|
`import { defineConfig } from ${JSON.stringify(CONFIG_MOD)};
|
|
222
222
|
export default defineConfig({
|
|
223
|
-
drivers: { store: { sql: {
|
|
223
|
+
drivers: { store: { sql: { test: "pglite" } } },
|
|
224
224
|
});
|
|
225
225
|
`,
|
|
226
226
|
);
|
|
@@ -239,12 +239,13 @@ export const app = oke({ name: "plugin-only" }).plug(
|
|
|
239
239
|
|
|
240
240
|
const { loadOkeConfig } = await import("./load-config.ts");
|
|
241
241
|
const loaded = await loadOkeConfig(dir);
|
|
242
|
-
const emitted = await emitAbstractSchemaPrestep(dir, loaded.config, () => {}, "
|
|
242
|
+
const emitted = await emitAbstractSchemaPrestep(dir, loaded.config, () => {}, "test");
|
|
243
243
|
expect(emitted).toBe(true);
|
|
244
244
|
|
|
245
|
-
const generated = await readFile(join(dir, "src", "db", "schema.
|
|
246
|
-
expect(generated).toContain('
|
|
247
|
-
|
|
245
|
+
const generated = await readFile(join(dir, "src", "db", "schema.drizzle.ts"), "utf8");
|
|
246
|
+
expect(generated).toContain('pgTable("metrics_daily"');
|
|
247
|
+
// Postgres dialect maps abstract integer → bigint (32-bit INTEGER is too narrow).
|
|
248
|
+
expect(generated).toContain('bigint("hits", { mode: "number" })');
|
|
248
249
|
});
|
|
249
250
|
});
|
|
250
251
|
|
|
@@ -252,17 +253,49 @@ describe("oke db multi-migration catch-up", () => {
|
|
|
252
253
|
/**
|
|
253
254
|
* Real drizzle-kit generate/migrate through `oke db` — proves multi-file
|
|
254
255
|
* versioned migrations and that re-migrate / environment lag only apply
|
|
255
|
-
* previously-unapplied files (drizzle-kit `__drizzle_migrations`
|
|
256
|
-
* The CLI wrapper must not interfere with that bookkeeping.
|
|
256
|
+
* previously-unapplied files (drizzle-kit `drizzle.__drizzle_migrations`
|
|
257
|
+
* history). The CLI wrapper must not interfere with that bookkeeping.
|
|
257
258
|
*/
|
|
258
259
|
test("second migrate skips applied files; lag DB catches up with only the new one", async () => {
|
|
259
260
|
const dir = await mkdtemp(join(import.meta.dir, ".tmp-db-mig-"));
|
|
261
|
+
const pgdata = join(dir, "app.pglite");
|
|
262
|
+
async function migrationNames(): Promise<string[]> {
|
|
263
|
+
const conn = await connectPglite({ url: pgdata });
|
|
264
|
+
try {
|
|
265
|
+
const rows = await conn.query(`SELECT name FROM drizzle.__drizzle_migrations ORDER BY id`);
|
|
266
|
+
return rows.map((r) => String(r.name));
|
|
267
|
+
} finally {
|
|
268
|
+
await conn.close();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async function noteColumns(): Promise<string[]> {
|
|
272
|
+
const conn = await connectPglite({ url: pgdata });
|
|
273
|
+
try {
|
|
274
|
+
const rows = await conn.query(
|
|
275
|
+
`SELECT column_name AS name FROM information_schema.columns
|
|
276
|
+
WHERE table_schema = 'public' AND table_name = 'notes'`,
|
|
277
|
+
);
|
|
278
|
+
return rows.map((r) => String(r.name));
|
|
279
|
+
} finally {
|
|
280
|
+
await conn.close();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async function notesTableExists(): Promise<boolean> {
|
|
284
|
+
const conn = await connectPglite({ url: pgdata });
|
|
285
|
+
try {
|
|
286
|
+
const rows = await conn.query(`SELECT to_regclass('public.notes') AS reg`);
|
|
287
|
+
return rows[0]?.reg != null;
|
|
288
|
+
} finally {
|
|
289
|
+
await conn.close();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
260
293
|
try {
|
|
261
294
|
await mkdir(join(dir, "src"), { recursive: true });
|
|
262
295
|
await writeFile(
|
|
263
296
|
join(dir, "src", "schema.ts"),
|
|
264
|
-
`import {
|
|
265
|
-
export const notes =
|
|
297
|
+
`import { pgTable, text } from "drizzle-orm/pg-core";
|
|
298
|
+
export const notes = pgTable("notes", {
|
|
266
299
|
id: text("id").primaryKey(),
|
|
267
300
|
title: text("title").notNull(),
|
|
268
301
|
});
|
|
@@ -272,10 +305,11 @@ export const notes = sqliteTable("notes", {
|
|
|
272
305
|
join(dir, "drizzle.config.ts"),
|
|
273
306
|
`import { defineConfig } from "drizzle-kit";
|
|
274
307
|
export default defineConfig({
|
|
275
|
-
dialect: "
|
|
308
|
+
dialect: "postgresql",
|
|
309
|
+
driver: "pglite",
|
|
276
310
|
schema: "./src/schema.ts",
|
|
277
311
|
out: "./drizzle",
|
|
278
|
-
dbCredentials: { url:
|
|
312
|
+
dbCredentials: { url: ${JSON.stringify(pgdata)} },
|
|
279
313
|
});
|
|
280
314
|
`,
|
|
281
315
|
);
|
|
@@ -293,20 +327,13 @@ export default defineConfig({
|
|
|
293
327
|
expect(mig1.code).toBe(EXIT_OK);
|
|
294
328
|
expect(mig1.out).toContain("oke db migrate: applied");
|
|
295
329
|
|
|
296
|
-
|
|
297
|
-
expect(
|
|
298
|
-
db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get(),
|
|
299
|
-
).toBeTruthy();
|
|
300
|
-
let history = db.query("SELECT name FROM __drizzle_migrations ORDER BY id").all() as Array<{
|
|
301
|
-
name: string;
|
|
302
|
-
}>;
|
|
303
|
-
expect(history.map((r) => r.name)).toEqual([initialMig]);
|
|
304
|
-
db.close();
|
|
330
|
+
expect(await notesTableExists()).toBe(true);
|
|
331
|
+
expect(await migrationNames()).toEqual([initialMig]);
|
|
305
332
|
|
|
306
333
|
await writeFile(
|
|
307
334
|
join(dir, "src", "schema.ts"),
|
|
308
|
-
`import {
|
|
309
|
-
export const notes =
|
|
335
|
+
`import { integer, pgTable, text } from "drizzle-orm/pg-core";
|
|
336
|
+
export const notes = pgTable("notes", {
|
|
310
337
|
id: text("id").primaryKey(),
|
|
311
338
|
title: text("title").notNull(),
|
|
312
339
|
pinned: integer("pinned").notNull().default(0),
|
|
@@ -333,54 +360,31 @@ export const notes = sqliteTable("notes", {
|
|
|
333
360
|
const mig2 = await runDbFresh("migrate", dir);
|
|
334
361
|
expect(mig2.code).toBe(EXIT_OK);
|
|
335
362
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
name: string;
|
|
339
|
-
}>;
|
|
340
|
-
expect(history.map((r) => r.name)).toEqual([firstMig, secondMig]);
|
|
341
|
-
const cols = db.query("PRAGMA table_info(notes)").all() as Array<{ name: string }>;
|
|
342
|
-
expect(cols.map((c) => c.name)).toContain("pinned");
|
|
343
|
-
db.close();
|
|
363
|
+
expect(await migrationNames()).toEqual([firstMig, secondMig]);
|
|
364
|
+
expect(await noteColumns()).toContain("pinned");
|
|
344
365
|
|
|
345
366
|
// Idempotent re-migrate: history unchanged, no error.
|
|
346
367
|
const migNoop = await runDbFresh("migrate", dir);
|
|
347
368
|
expect(migNoop.code).toBe(EXIT_OK);
|
|
348
|
-
|
|
349
|
-
const historyNoop = db
|
|
350
|
-
.query("SELECT name FROM __drizzle_migrations ORDER BY id")
|
|
351
|
-
.all() as Array<{ name: string }>;
|
|
352
|
-
expect(historyNoop.map((r) => r.name)).toEqual([firstMig, secondMig]);
|
|
353
|
-
db.close();
|
|
369
|
+
expect(await migrationNames()).toEqual([firstMig, secondMig]);
|
|
354
370
|
|
|
355
371
|
// Staging/prod lag: fresh DB with only the first migration applied,
|
|
356
372
|
// then both files present — catch up applies only the second.
|
|
357
|
-
await rm(
|
|
373
|
+
await rm(pgdata, { recursive: true, force: true });
|
|
358
374
|
const stash = join(dir, "_stash_second");
|
|
359
375
|
await rm(stash, { recursive: true, force: true });
|
|
360
376
|
await rename(join(dir, "drizzle", secondMig), stash);
|
|
361
377
|
|
|
362
378
|
const lagFirst = await runDbFresh("migrate", dir);
|
|
363
379
|
expect(lagFirst.code).toBe(EXIT_OK);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
.query("SELECT name FROM __drizzle_migrations ORDER BY id")
|
|
367
|
-
.all() as Array<{ name: string }>;
|
|
368
|
-
expect(lagHist1.map((r) => r.name)).toEqual([firstMig]);
|
|
369
|
-
const lagCols1 = db.query("PRAGMA table_info(notes)").all() as Array<{ name: string }>;
|
|
370
|
-
expect(lagCols1.map((c) => c.name)).not.toContain("pinned");
|
|
371
|
-
db.close();
|
|
380
|
+
expect(await migrationNames()).toEqual([firstMig]);
|
|
381
|
+
expect(await noteColumns()).not.toContain("pinned");
|
|
372
382
|
|
|
373
383
|
await rename(stash, join(dir, "drizzle", secondMig));
|
|
374
384
|
const lagCatchUp = await runDbFresh("migrate", dir);
|
|
375
385
|
expect(lagCatchUp.code).toBe(EXIT_OK);
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
.query("SELECT name FROM __drizzle_migrations ORDER BY id")
|
|
379
|
-
.all() as Array<{ name: string }>;
|
|
380
|
-
expect(lagHist2.map((r) => r.name)).toEqual([firstMig, secondMig]);
|
|
381
|
-
const lagCols2 = db.query("PRAGMA table_info(notes)").all() as Array<{ name: string }>;
|
|
382
|
-
expect(lagCols2.map((c) => c.name)).toContain("pinned");
|
|
383
|
-
db.close();
|
|
386
|
+
expect(await migrationNames()).toEqual([firstMig, secondMig]);
|
|
387
|
+
expect(await noteColumns()).toContain("pinned");
|
|
384
388
|
} finally {
|
|
385
389
|
await rm(dir, { recursive: true, force: true });
|
|
386
390
|
}
|
|
@@ -409,7 +413,7 @@ describe("oke db seed", () => {
|
|
|
409
413
|
}
|
|
410
414
|
|
|
411
415
|
async function seedWith(
|
|
412
|
-
env: "
|
|
416
|
+
env: "dev" | "test" | "prod",
|
|
413
417
|
extras: Parameters<typeof runSeed>[0] = {},
|
|
414
418
|
) {
|
|
415
419
|
const order: string[] = [];
|
|
@@ -429,20 +433,15 @@ describe("oke db seed", () => {
|
|
|
429
433
|
}
|
|
430
434
|
|
|
431
435
|
test("essential array order is execution order", async () => {
|
|
432
|
-
const { code, order, out } = await seedWith("
|
|
436
|
+
const { code, order, out } = await seedWith("dev");
|
|
433
437
|
expect(code).toBe(EXIT_OK);
|
|
434
438
|
expect(order.slice(0, 2)).toEqual(["a", "b"]);
|
|
435
439
|
expect(out).toContain("oke db seed: essential a");
|
|
436
440
|
expect(out).toContain("oke db seed: ok");
|
|
437
441
|
});
|
|
438
442
|
|
|
439
|
-
test("
|
|
440
|
-
const { order } = await seedWith("
|
|
441
|
-
expect(order).toEqual(["a", "b", "dev"]);
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
test("docker runs essential + dev, never prod", async () => {
|
|
445
|
-
const { order } = await seedWith("docker");
|
|
443
|
+
test("dev runs essential + dev, never prod", async () => {
|
|
444
|
+
const { order } = await seedWith("dev");
|
|
446
445
|
expect(order).toEqual(["a", "b", "dev"]);
|
|
447
446
|
});
|
|
448
447
|
|
|
@@ -493,11 +492,11 @@ describe("oke db seed", () => {
|
|
|
493
492
|
expect(code).toBe(EXIT_OK);
|
|
494
493
|
});
|
|
495
494
|
|
|
496
|
-
test("
|
|
495
|
+
test("dev confirm proceeds when confirmEnv returns true", async () => {
|
|
497
496
|
const out: string[] = [];
|
|
498
497
|
const order: string[] = [];
|
|
499
498
|
const code = await runSeed({
|
|
500
|
-
env: "
|
|
499
|
+
env: "dev",
|
|
501
500
|
force: false,
|
|
502
501
|
stdinIsTTY: true,
|
|
503
502
|
seedDef: trackingDef(order),
|
|
@@ -506,7 +505,7 @@ describe("oke db seed", () => {
|
|
|
506
505
|
stop: async () => {},
|
|
507
506
|
}),
|
|
508
507
|
confirmEnv: async (env, target) => {
|
|
509
|
-
expect(env).toBe("
|
|
508
|
+
expect(env).toBe("dev");
|
|
510
509
|
expect(target.length).toBeGreaterThan(0);
|
|
511
510
|
return true;
|
|
512
511
|
},
|
package/src/cli/db.ts
CHANGED
|
@@ -81,7 +81,7 @@ export async function withDrizzleKitEnv<T>(
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/** Subcommands under `oke db`. */
|
|
84
|
-
export type DbSubcommand = "push" | "generate" | "migrate" | "seed";
|
|
84
|
+
export type DbSubcommand = "push" | "generate" | "migrate" | "seed" | "studio";
|
|
85
85
|
|
|
86
86
|
/** Options for {@link runDb}. */
|
|
87
87
|
export interface DbOptions {
|
|
@@ -167,6 +167,10 @@ export async function runDb(sub: DbSubcommand, options: DbOptions = {}): Promise
|
|
|
167
167
|
const loaded = await loadOkeConfig(cwd).catch(() => null);
|
|
168
168
|
const env = options.env ?? (await resolveDevSqlEnv(cwd));
|
|
169
169
|
|
|
170
|
+
if (sub === "studio") {
|
|
171
|
+
return runStudio(cwd, options, write, env, loaded?.config ?? null);
|
|
172
|
+
}
|
|
173
|
+
|
|
170
174
|
if (sub === "seed") {
|
|
171
175
|
return runSeed({
|
|
172
176
|
cwd,
|
|
@@ -208,7 +212,7 @@ export async function runDb(sub: DbSubcommand, options: DbOptions = {}): Promise
|
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
/**
|
|
211
|
-
* Emit `schema.
|
|
215
|
+
* Emit `schema.drizzle.ts` from abstract decls + live plugged plugin tables.
|
|
212
216
|
*
|
|
213
217
|
* @param cwd - Project root
|
|
214
218
|
* @param config - Loaded oke config
|
|
@@ -220,12 +224,16 @@ export async function emitAbstractSchemaPrestep(
|
|
|
220
224
|
cwd: string,
|
|
221
225
|
config: OkeConfig | null | undefined,
|
|
222
226
|
write: (text: string) => void = () => {},
|
|
223
|
-
env: ConfigEnv = "
|
|
227
|
+
env: ConfigEnv = "test",
|
|
224
228
|
options: {
|
|
225
229
|
readonly pluginTables?: readonly TableContribution[];
|
|
226
230
|
readonly entry?: string;
|
|
227
231
|
} = {},
|
|
228
232
|
): Promise<boolean> {
|
|
233
|
+
// System stubs (core / auth / Manifest stores) — separate from domain Drizzle.
|
|
234
|
+
// Re-run here so plugins added later refresh `.oke/schema/oke.ts` on db push.
|
|
235
|
+
await refreshSystemSchema(cwd, write);
|
|
236
|
+
|
|
229
237
|
const driverId = resolveDriverId(config?.drivers?.store?.sql, env);
|
|
230
238
|
const dialect: SqlDialect = dialectFromDriverId(driverId);
|
|
231
239
|
const pluginTables =
|
|
@@ -245,6 +253,24 @@ export async function emitAbstractSchemaPrestep(
|
|
|
245
253
|
return result.emitted;
|
|
246
254
|
}
|
|
247
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Quietly refresh `.oke/schema/oke.ts` (never fails the domain db path).
|
|
258
|
+
*
|
|
259
|
+
* @param cwd - Project root
|
|
260
|
+
* @param write - Optional log sink
|
|
261
|
+
*/
|
|
262
|
+
async function refreshSystemSchema(
|
|
263
|
+
cwd: string,
|
|
264
|
+
write: (text: string) => void = () => {},
|
|
265
|
+
): Promise<void> {
|
|
266
|
+
try {
|
|
267
|
+
const { runSchemaGenerate } = await import("./schema.ts");
|
|
268
|
+
await runSchemaGenerate({ cwd, write: () => {} });
|
|
269
|
+
} catch (err) {
|
|
270
|
+
write(`oke schema generate: skipped — ${err instanceof Error ? err.message : String(err)}\n`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
248
274
|
/**
|
|
249
275
|
* Push domain schema to the live database (dev).
|
|
250
276
|
*
|
|
@@ -438,18 +464,58 @@ export async function detectDbDrift(options: DbOptions = {}): Promise<{
|
|
|
438
464
|
}
|
|
439
465
|
|
|
440
466
|
/**
|
|
441
|
-
*
|
|
467
|
+
* Open drizzle-kit Studio against the project config / env overlay.
|
|
468
|
+
*
|
|
469
|
+
* @param cwd - Project root
|
|
470
|
+
* @param options - Paths / injectables
|
|
471
|
+
* @param write - stdout writer
|
|
472
|
+
* @param env - Config env
|
|
473
|
+
* @param config - Loaded oke config
|
|
474
|
+
*/
|
|
475
|
+
export async function runStudio(
|
|
476
|
+
cwd: string,
|
|
477
|
+
options: DbOptions,
|
|
478
|
+
write: (text: string) => void = (t) => process.stdout.write(t),
|
|
479
|
+
env?: ConfigEnv,
|
|
480
|
+
config?: OkeConfig | null,
|
|
481
|
+
): Promise<number> {
|
|
482
|
+
const activeEnv = env ?? (await resolveDevSqlEnv(cwd));
|
|
483
|
+
const loaded = config ?? (await loadOkeConfig(cwd).catch(() => null))?.config ?? null;
|
|
484
|
+
const configPath = await resolveDrizzleConfigPath(cwd, {
|
|
485
|
+
config: options.config,
|
|
486
|
+
loadedConfig: loaded,
|
|
487
|
+
});
|
|
488
|
+
if (!(await Bun.file(configPath).exists())) {
|
|
489
|
+
write(`oke db studio: missing ${configPath}\n`);
|
|
490
|
+
return EXIT_RUNTIME;
|
|
491
|
+
}
|
|
492
|
+
write(`oke db studio: opening drizzle-kit studio (${configPath})\n`);
|
|
493
|
+
return withDrizzleKitEnv(cwd, loaded, activeEnv, async () => {
|
|
494
|
+
const proc = Bun.spawn(["bunx", "drizzle-kit", "studio", "--config", configPath], {
|
|
495
|
+
cwd,
|
|
496
|
+
stdout: "inherit",
|
|
497
|
+
stderr: "inherit",
|
|
498
|
+
stdin: "inherit",
|
|
499
|
+
env: process.env,
|
|
500
|
+
});
|
|
501
|
+
const code = await proc.exited;
|
|
502
|
+
return code === 0 ? EXIT_OK : EXIT_RUNTIME;
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* CLI entry: parse `oke db <push|generate|migrate|seed|studio> […]`.
|
|
442
508
|
*
|
|
443
509
|
* @param args - Args after `db`
|
|
444
510
|
*/
|
|
445
511
|
export async function dbCli(args: readonly string[]): Promise<number> {
|
|
446
512
|
const sub = args[0];
|
|
447
513
|
if (!sub || sub === "--help" || sub === "-h") {
|
|
448
|
-
console.log(`oke db push|generate|migrate|seed [--config|-c] [--env name] [--force]
|
|
514
|
+
console.log(`oke db push|generate|migrate|seed|studio [--config|-c] [--env name] [--force]
|
|
449
515
|
|
|
450
516
|
Domain schema sync via drizzle-kit, plus explicit seed.
|
|
451
517
|
When src/db/schema.decl.ts and/or a plugged app entry exists, emits
|
|
452
|
-
schema.
|
|
518
|
+
schema.drizzle.ts from store.schema.table + live plugin .table()
|
|
453
519
|
contributions — then runs drizzle-kit.
|
|
454
520
|
Hand-written src/schema.ts remains supported (emit skipped if nothing to emit).
|
|
455
521
|
Not the same as \`oke schema generate\` (core/plugin stub tables).
|
|
@@ -458,14 +524,21 @@ Not the same as \`oke schema generate\` (core/plugin stub tables).
|
|
|
458
524
|
generate Write versioned SQL under drizzle/ for review
|
|
459
525
|
migrate Apply generated migrations (explicit; never automatic in prod)
|
|
460
526
|
seed Run defineSeed (essential + env category); never at boot
|
|
527
|
+
studio Open drizzle-kit Studio (long-running)
|
|
461
528
|
|
|
462
|
-
--env Override config env (
|
|
529
|
+
--env Override config env (dev|test|prod)
|
|
463
530
|
--force Skip docker/prod confirmation prompt (CI)
|
|
464
531
|
--entry App entry for seed boot / plugin table discovery
|
|
465
532
|
`);
|
|
466
533
|
return sub ? EXIT_OK : EXIT_USAGE;
|
|
467
534
|
}
|
|
468
|
-
if (
|
|
535
|
+
if (
|
|
536
|
+
sub !== "push" &&
|
|
537
|
+
sub !== "generate" &&
|
|
538
|
+
sub !== "migrate" &&
|
|
539
|
+
sub !== "seed" &&
|
|
540
|
+
sub !== "studio"
|
|
541
|
+
) {
|
|
469
542
|
console.error(`oke db: unknown subcommand "${sub}"`);
|
|
470
543
|
return EXIT_USAGE;
|
|
471
544
|
}
|
|
@@ -481,7 +554,7 @@ Not the same as \`oke schema generate\` (core/plugin stub tables).
|
|
|
481
554
|
const value = args[++i];
|
|
482
555
|
const parsed = parseConfigEnv(value);
|
|
483
556
|
if (!parsed) {
|
|
484
|
-
console.error(`oke db: invalid --env ${JSON.stringify(value)} (
|
|
557
|
+
console.error(`oke db: invalid --env ${JSON.stringify(value)} (dev|test|prod)`);
|
|
485
558
|
return EXIT_USAGE;
|
|
486
559
|
}
|
|
487
560
|
env = parsed;
|
|
@@ -499,9 +572,10 @@ Not the same as \`oke schema generate\` (core/plugin stub tables).
|
|
|
499
572
|
}
|
|
500
573
|
|
|
501
574
|
function parseConfigEnv(value: string | undefined): ConfigEnv | undefined {
|
|
502
|
-
if (value === "
|
|
503
|
-
|
|
504
|
-
|
|
575
|
+
if (value === "dev" || value === "test" || value === "prod") return value;
|
|
576
|
+
// Soft-compat for older CLI flags / docs.
|
|
577
|
+
if (value === "local") return "test";
|
|
578
|
+
if (value === "docker") return "dev";
|
|
505
579
|
return undefined;
|
|
506
580
|
}
|
|
507
581
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { DevController } from "./dev-controller.ts";
|
|
3
|
+
|
|
4
|
+
describe("DevController", () => {
|
|
5
|
+
test("refuses to stop an external session", async () => {
|
|
6
|
+
const ctrl = new DevController();
|
|
7
|
+
const errors: string[] = [];
|
|
8
|
+
ctrl.on((e) => {
|
|
9
|
+
if (e.type === "error") errors.push(e.message);
|
|
10
|
+
});
|
|
11
|
+
// Force external via attach with busy ports inject — attach uses real probe.
|
|
12
|
+
// Simulate by calling stop after manually setting ownership through attach
|
|
13
|
+
// against a free project with no lock: stop should be a no-op (stopped).
|
|
14
|
+
await ctrl.attach(process.cwd());
|
|
15
|
+
if (ctrl.getOwnership() === "stopped") {
|
|
16
|
+
await ctrl.stop();
|
|
17
|
+
expect(ctrl.getOwnership()).toBe("stopped");
|
|
18
|
+
}
|
|
19
|
+
// Direct external refusal path:
|
|
20
|
+
(ctrl as unknown as { ownership: string }).ownership = "external";
|
|
21
|
+
await ctrl.stop();
|
|
22
|
+
expect(errors.some((m) => m.includes("refusing to stop external"))).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("emit delivers events to listeners", () => {
|
|
26
|
+
const ctrl = new DevController();
|
|
27
|
+
const types: string[] = [];
|
|
28
|
+
const off = ctrl.on((e) => types.push(e.type));
|
|
29
|
+
ctrl.emit({ type: "boot", phase: "x" });
|
|
30
|
+
ctrl.emit({ type: "log", text: "hi" });
|
|
31
|
+
off();
|
|
32
|
+
ctrl.emit({ type: "exit", code: 0 });
|
|
33
|
+
expect(types).toEqual(["boot", "log"]);
|
|
34
|
+
});
|
|
35
|
+
});
|