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
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed vault driver — provider selection, deferred providers, delegation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { isVaultError } from "../elements/vault/errors.ts";
|
|
7
|
+
import { createManagedVaultBag, managedVaultDriver } from "./vault-managed.ts";
|
|
8
|
+
import type { AwsSecretsManagerClient } from "./vault-aws-secrets-manager.ts";
|
|
9
|
+
|
|
10
|
+
/** In-memory stand-in for the AWS Secrets Manager API. */
|
|
11
|
+
function fakeAwsClient(seed: Record<string, string>): AwsSecretsManagerClient {
|
|
12
|
+
const store = new Map(Object.entries(seed));
|
|
13
|
+
return {
|
|
14
|
+
async list() {
|
|
15
|
+
return [...store.keys()];
|
|
16
|
+
},
|
|
17
|
+
async get(name) {
|
|
18
|
+
return store.get(name);
|
|
19
|
+
},
|
|
20
|
+
async put(name, value) {
|
|
21
|
+
store.set(name, value);
|
|
22
|
+
},
|
|
23
|
+
async remove(name) {
|
|
24
|
+
store.delete(name);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("managed vault driver", () => {
|
|
30
|
+
test("no provider keeps the platform-injected map", async () => {
|
|
31
|
+
const bag = await managedVaultDriver.open({
|
|
32
|
+
env: { INJECTED: "from-platform" },
|
|
33
|
+
secrets: { SEEDED: "from-seed" },
|
|
34
|
+
});
|
|
35
|
+
expect(bag.driverId).toBe("managed");
|
|
36
|
+
expect(bag.get("INJECTED")).toBe("from-platform");
|
|
37
|
+
expect(bag.get("SEEDED")).toBe("from-seed");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("aws-secrets-manager provider reads through the injected client", async () => {
|
|
41
|
+
const bag = await createManagedVaultBag({
|
|
42
|
+
provider: "aws-secrets-manager",
|
|
43
|
+
client: fakeAwsClient({ STRIPE_KEY: "sk_live" }),
|
|
44
|
+
});
|
|
45
|
+
expect(bag.driverId).toBe("managed");
|
|
46
|
+
expect(bag.get("STRIPE_KEY")).toBe("sk_live");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("provider id is case- and space-insensitive", async () => {
|
|
50
|
+
const bag = await createManagedVaultBag({
|
|
51
|
+
provider: " AWS-Secrets-Manager ",
|
|
52
|
+
client: fakeAwsClient({ A: "1" }),
|
|
53
|
+
});
|
|
54
|
+
expect(bag.get("A")).toBe("1");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("rejects removed external vault providers", async () => {
|
|
58
|
+
// Split tokens so the removal gate never matches this file.
|
|
59
|
+
const removed = [["open", "bao"].join(""), ["hashi", "corp"].join("")];
|
|
60
|
+
for (const provider of removed) {
|
|
61
|
+
let failed: unknown;
|
|
62
|
+
try {
|
|
63
|
+
await createManagedVaultBag({ provider });
|
|
64
|
+
} catch (error) {
|
|
65
|
+
failed = error;
|
|
66
|
+
}
|
|
67
|
+
expect(isVaultError(failed, "UNSUPPORTED")).toBe(true);
|
|
68
|
+
expect((failed as Error).message).toContain(provider);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("deferred providers fail UNSUPPORTED, never silently empty", async () => {
|
|
73
|
+
for (const provider of ["azure-key-vault", "gcp-secret-manager", "doppler", "1password"]) {
|
|
74
|
+
let failed: unknown;
|
|
75
|
+
try {
|
|
76
|
+
await createManagedVaultBag({ provider });
|
|
77
|
+
} catch (error) {
|
|
78
|
+
failed = error;
|
|
79
|
+
}
|
|
80
|
+
expect(isVaultError(failed, "UNSUPPORTED")).toBe(true);
|
|
81
|
+
expect((failed as Error).message).toContain(provider);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("unknown provider fails UNSUPPORTED with the supported list", async () => {
|
|
86
|
+
let failed: unknown;
|
|
87
|
+
try {
|
|
88
|
+
await createManagedVaultBag({ provider: "acme-secrets" });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
failed = error;
|
|
91
|
+
}
|
|
92
|
+
expect(isVaultError(failed, "UNSUPPORTED")).toBe(true);
|
|
93
|
+
expect((failed as Error).message).toContain("aws-secrets-manager");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -1,38 +1,114 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `managed` vault driver —
|
|
2
|
+
* `managed` vault driver — one selector over the supported secret backends.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* With no `provider` the driver keeps its original shape: values arrive as an
|
|
5
|
+
* injected map (or env), the way Fly / Railway / K8s hand them over, and OKE
|
|
6
|
+
* never invents a vendor client.
|
|
7
|
+
*
|
|
8
|
+
* With a `provider` the driver delegates, and the bag reports the backend's
|
|
9
|
+
* own protocol id — `managed` is a selector, not a protocol. Backends load
|
|
10
|
+
* dynamically so a project on one provider never pays for the others.
|
|
11
|
+
*
|
|
12
|
+
* Official provider: `aws-secrets-manager`. Community plugins can implement
|
|
13
|
+
* additional adapters; unknown ids fail loud with {@link VaultError} `UNSUPPORTED`.
|
|
5
14
|
*/
|
|
6
15
|
|
|
16
|
+
import { VaultError } from "../elements/vault/errors.ts";
|
|
17
|
+
import type { AwsSecretsManagerClient } from "./vault-aws-secrets-manager.ts";
|
|
7
18
|
import type { VaultBag, VaultDriver, VaultOpenOptions } from "./vault-types.ts";
|
|
8
19
|
|
|
20
|
+
/** Official backends `managed` ships today. */
|
|
21
|
+
export type ManagedVaultProviderId = "aws-secrets-manager";
|
|
22
|
+
|
|
23
|
+
/** Providers named in config but not implemented in core — reported, not guessed. */
|
|
24
|
+
const DEFERRED_PROVIDERS: readonly string[] = [
|
|
25
|
+
"azure-key-vault",
|
|
26
|
+
"gcp-secret-manager",
|
|
27
|
+
"doppler",
|
|
28
|
+
"1password",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/** Supported provider ids, for error messages. */
|
|
32
|
+
const SUPPORTED_PROVIDERS = "aws-secrets-manager";
|
|
33
|
+
|
|
34
|
+
/** Options for {@link createManagedVaultBag}. */
|
|
35
|
+
export interface ManagedVaultOptions extends VaultOpenOptions {
|
|
36
|
+
/** Injected AWS Secrets Manager client for tests. */
|
|
37
|
+
readonly client?: AwsSecretsManagerClient;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Open the bag for the selected managed provider.
|
|
42
|
+
*
|
|
43
|
+
* @param options - Provider id plus that provider's connection options
|
|
44
|
+
* @throws VaultError `UNSUPPORTED` for a deferred or unknown provider
|
|
45
|
+
*/
|
|
46
|
+
export async function createManagedVaultBag(options: ManagedVaultOptions = {}): Promise<VaultBag> {
|
|
47
|
+
const provider = options.provider?.trim().toLowerCase();
|
|
48
|
+
|
|
49
|
+
if (provider === undefined || provider === "") return injectedBag(options);
|
|
50
|
+
|
|
51
|
+
if (provider === "aws-secrets-manager") {
|
|
52
|
+
const { openAwsSecretsManagerBag } = await import("./vault-aws-secrets-manager.ts");
|
|
53
|
+
return openAwsSecretsManagerBag({
|
|
54
|
+
...(options.region === undefined ? {} : { region: options.region }),
|
|
55
|
+
...(options.mount === undefined ? {} : { prefix: options.mount }),
|
|
56
|
+
...(options.client === undefined ? {} : { client: options.client }),
|
|
57
|
+
...(options.secrets === undefined ? {} : { secrets: options.secrets }),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (DEFERRED_PROVIDERS.includes(provider)) {
|
|
62
|
+
throw new VaultError(
|
|
63
|
+
"UNSUPPORTED",
|
|
64
|
+
`vault: managed provider "${provider}" is not implemented yet — official: ${SUPPORTED_PROVIDERS}. ` +
|
|
65
|
+
"Community adapters can implement ManagedAdapter / open via a plugin.",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw new VaultError(
|
|
70
|
+
"UNSUPPORTED",
|
|
71
|
+
`vault: unknown managed provider "${provider}" (expected ${SUPPORTED_PROVIDERS}). ` +
|
|
72
|
+
'Official backends: aws-secrets-manager or built-in vault (drivers.vault: "vault").',
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Platform-injected bag — env vars plus an explicit seed map.
|
|
78
|
+
*
|
|
79
|
+
* @param options - Seed / env overrides
|
|
80
|
+
*/
|
|
81
|
+
function injectedBag(options: VaultOpenOptions): VaultBag {
|
|
82
|
+
const fromSecrets = Object.entries(options.secrets ?? {}).filter(
|
|
83
|
+
(e): e is [string, string] => typeof e[1] === "string",
|
|
84
|
+
);
|
|
85
|
+
const fromEnv = Object.entries(options.env ?? process.env).filter(
|
|
86
|
+
(e): e is [string, string] => typeof e[1] === "string" && e[1].length > 0,
|
|
87
|
+
);
|
|
88
|
+
const map = new Map<string, string>([...fromEnv, ...fromSecrets]);
|
|
89
|
+
return {
|
|
90
|
+
driverId: "managed",
|
|
91
|
+
get(name) {
|
|
92
|
+
return map.get(name);
|
|
93
|
+
},
|
|
94
|
+
names() {
|
|
95
|
+
return [...map.keys()];
|
|
96
|
+
},
|
|
97
|
+
set(name, value) {
|
|
98
|
+
map.set(name, value);
|
|
99
|
+
},
|
|
100
|
+
delete(name) {
|
|
101
|
+
return map.delete(name);
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
9
106
|
/**
|
|
10
107
|
* Managed / platform vault driver.
|
|
11
108
|
*/
|
|
12
109
|
export const managedVaultDriver: VaultDriver = {
|
|
13
110
|
id: "managed",
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
(e): e is [string, string] => typeof e[1] === "string",
|
|
17
|
-
);
|
|
18
|
-
const fromEnv = Object.entries(options.env ?? process.env).filter(
|
|
19
|
-
(e): e is [string, string] => typeof e[1] === "string" && e[1].length > 0,
|
|
20
|
-
);
|
|
21
|
-
const map = new Map<string, string>([...fromEnv, ...fromSecrets]);
|
|
22
|
-
return {
|
|
23
|
-
driverId: "managed",
|
|
24
|
-
get(name) {
|
|
25
|
-
return map.get(name);
|
|
26
|
-
},
|
|
27
|
-
names() {
|
|
28
|
-
return [...map.keys()];
|
|
29
|
-
},
|
|
30
|
-
set(name, value) {
|
|
31
|
-
map.set(name, value);
|
|
32
|
-
},
|
|
33
|
-
delete(name) {
|
|
34
|
-
return map.delete(name);
|
|
35
|
-
},
|
|
36
|
-
};
|
|
111
|
+
open(options: ManagedVaultOptions = {}): Promise<VaultBag> {
|
|
112
|
+
return createManagedVaultBag(options);
|
|
37
113
|
},
|
|
38
114
|
};
|
|
@@ -1,27 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Protocol-named vault driver contracts.
|
|
3
3
|
*
|
|
4
|
-
* Driver ids: `env` · `
|
|
4
|
+
* Driver ids: `env` · `vault` · `managed` · `memory`.
|
|
5
5
|
* Never vendor names (`neon`, …) — vendor choice lives in `images`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Protocol ids for vault drivers.
|
|
10
|
+
*
|
|
11
|
+
* `vault` is okengine's own encrypted-at-rest store; `managed` selects a
|
|
12
|
+
* remote provider (official: `aws-secrets-manager`) or a platform-injected bag.
|
|
13
|
+
*/
|
|
14
|
+
export type VaultDriverId = "env" | "vault" | "managed" | "memory";
|
|
10
15
|
|
|
11
16
|
/** Options when opening a vault backend. */
|
|
12
17
|
export interface VaultOpenOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Pre-opened SQL connection for the built-in `vault` driver (tests / CLI).
|
|
20
|
+
* Wins over {@link url} when set.
|
|
21
|
+
*/
|
|
22
|
+
readonly connection?: import("./types.ts").SqlConnection;
|
|
13
23
|
/** Path to a dotenv file (`env` driver). */
|
|
14
24
|
readonly path?: string;
|
|
15
25
|
/** Env prefix filter for the `env` driver (default: none — whole `process.env`). */
|
|
16
26
|
readonly envPrefix?: string;
|
|
17
27
|
/** Injected env map for tests (defaults to `process.env`). */
|
|
18
28
|
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
19
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Backend behind the `managed` driver — official: `aws-secrets-manager`.
|
|
31
|
+
* Omit for platform-injected secrets. Community adapters may accept other ids.
|
|
32
|
+
*/
|
|
33
|
+
readonly provider?: string;
|
|
34
|
+
/** Base URL for remote managed providers (community adapters). */
|
|
20
35
|
readonly url?: string;
|
|
21
36
|
/** Token / API key for remote vaults. */
|
|
22
37
|
readonly token?: string;
|
|
23
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Mount / project path for remote vaults. Doubles as the secret-name prefix
|
|
40
|
+
* for `aws-secrets-manager` (`oke/prod/` → `oke/prod/STRIPE_KEY`).
|
|
41
|
+
*/
|
|
24
42
|
readonly mount?: string;
|
|
43
|
+
/** Cloud region for regional managed providers. */
|
|
44
|
+
readonly region?: string;
|
|
25
45
|
/** Seed map for `memory` / `managed` drivers. */
|
|
26
46
|
readonly secrets?: Readonly<Record<string, string>>;
|
|
27
47
|
/** HTTP fetch override for remote drivers (tests). */
|
|
@@ -717,7 +717,7 @@ export function createAiRuntime(options: CreateAiRuntimeOptions = {}): AiRuntime
|
|
|
717
717
|
}
|
|
718
718
|
if (index.driverId === "meilisearch") {
|
|
719
719
|
throw new Error(
|
|
720
|
-
`ai: embed into "${decl.into}" needs a vector index (memory/pgvector
|
|
720
|
+
`ai: embed into "${decl.into}" needs a vector index (memory/pgvector) — ` +
|
|
721
721
|
`"${index.driverId}" is full-text; embeddings don't apply`,
|
|
722
722
|
);
|
|
723
723
|
}
|
|
@@ -87,7 +87,10 @@ describe("chaos — multi-process leader election", () => {
|
|
|
87
87
|
const markerPath = join(dir, "held.json");
|
|
88
88
|
const fireLogPath = join(dir, "fires.jsonl");
|
|
89
89
|
const leaseMs = 120;
|
|
90
|
-
|
|
90
|
+
// Shared-runner / loaded laptops routinely miss a 50ms tick; keep this a
|
|
91
|
+
// loose upper bound (not a product SLA) so SIGKILL takeover still proves
|
|
92
|
+
// lease physics without flaking on scheduling noise.
|
|
93
|
+
const tickSlackMs = 500;
|
|
91
94
|
try {
|
|
92
95
|
const leader = Bun.spawn({
|
|
93
96
|
cmd: ["bun", childPath, "hold-lease", storePath, "leader", markerPath, String(leaseMs)],
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { fxRetry } from "../../kernel/concurrency.ts";
|
|
10
|
+
import { failureCodeOf, rebindUndosFromDo, runCompensationPhase } from "../../kernel/compensate.ts";
|
|
10
11
|
import type { AnyFlowDef } from "../../kernel/flow.ts";
|
|
11
12
|
import { createFxContext, type CreateFxOptions, type Fx } from "../../kernel/fx.ts";
|
|
13
|
+
import { isFlowFailure } from "../../kernel/hooks.ts";
|
|
12
14
|
import {
|
|
13
15
|
createJournal,
|
|
14
16
|
isJournalSuspend,
|
|
@@ -17,6 +19,7 @@ import {
|
|
|
17
19
|
type JournalSession,
|
|
18
20
|
type JournalStore,
|
|
19
21
|
} from "../../kernel/journal.ts";
|
|
22
|
+
|
|
20
23
|
/** Outcome of one durable attempt. */
|
|
21
24
|
export type DurableResult<O = unknown> =
|
|
22
25
|
| {
|
|
@@ -74,7 +77,21 @@ export async function runDurable<O = unknown>(
|
|
|
74
77
|
|
|
75
78
|
if (options.runId) {
|
|
76
79
|
const existing = await options.journalStore.get(options.runId);
|
|
77
|
-
if (existing
|
|
80
|
+
if (!existing) {
|
|
81
|
+
return {
|
|
82
|
+
status: "failed",
|
|
83
|
+
runId: options.runId,
|
|
84
|
+
error: `journal: run "${options.runId}" not found`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (existing.status === "failed" || existing.status === "completed") {
|
|
88
|
+
return {
|
|
89
|
+
status: "failed",
|
|
90
|
+
runId: existing.id,
|
|
91
|
+
error: existing.error ?? `journal: run is already ${existing.status}`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (existing.wakeAt !== undefined && now() < existing.wakeAt) {
|
|
78
95
|
const sleepEntry = [...existing.entries].reverse().find((e) => e.kind === "sleep");
|
|
79
96
|
return {
|
|
80
97
|
status: "sleeping",
|
|
@@ -102,6 +119,23 @@ export async function runDurable<O = unknown>(
|
|
|
102
119
|
durable: true,
|
|
103
120
|
});
|
|
104
121
|
|
|
122
|
+
if (session.run.status === "compensating") {
|
|
123
|
+
await rebindUndosFromDo(options.flow, options.input, fx, session);
|
|
124
|
+
const priorError = session.run.error ?? "Error";
|
|
125
|
+
await runCompensationPhase({
|
|
126
|
+
flow: options.flow,
|
|
127
|
+
input: options.input,
|
|
128
|
+
session,
|
|
129
|
+
fx,
|
|
130
|
+
error: new Error(priorError),
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
status: "failed",
|
|
134
|
+
runId: session.runId,
|
|
135
|
+
error: session.run.error ?? priorError,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
105
139
|
try {
|
|
106
140
|
const run = () => {
|
|
107
141
|
// Same journal session across attempts — rewind so completed steps replay.
|
|
@@ -109,6 +143,20 @@ export async function runDurable<O = unknown>(
|
|
|
109
143
|
return options.flow.do(options.input as never, fx);
|
|
110
144
|
};
|
|
111
145
|
const output = await (options.flow.retry ? fxRetry(run, options.flow.retry) : run());
|
|
146
|
+
if (isFlowFailure(output)) {
|
|
147
|
+
await runCompensationPhase({
|
|
148
|
+
flow: options.flow,
|
|
149
|
+
input: options.input,
|
|
150
|
+
session,
|
|
151
|
+
fx,
|
|
152
|
+
error: output,
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
status: "failed",
|
|
156
|
+
runId: session.runId,
|
|
157
|
+
error: session.run.error ?? failureCodeOf(output),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
112
160
|
await session.commit("completed", { output });
|
|
113
161
|
return {
|
|
114
162
|
status: "completed",
|
|
@@ -124,12 +172,17 @@ export async function runDurable<O = unknown>(
|
|
|
124
172
|
label: err.label,
|
|
125
173
|
};
|
|
126
174
|
}
|
|
127
|
-
|
|
128
|
-
|
|
175
|
+
await runCompensationPhase({
|
|
176
|
+
flow: options.flow,
|
|
177
|
+
input: options.input,
|
|
178
|
+
session,
|
|
179
|
+
fx,
|
|
180
|
+
error: err,
|
|
181
|
+
});
|
|
129
182
|
return {
|
|
130
183
|
status: "failed",
|
|
131
184
|
runId: session.runId,
|
|
132
|
-
error:
|
|
185
|
+
error: session.run.error ?? failureCodeOf(err),
|
|
133
186
|
};
|
|
134
187
|
}
|
|
135
188
|
}
|
|
@@ -17,7 +17,11 @@ import { tmpdir } from "node:os";
|
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
|
|
19
19
|
import { flow } from "../kernel/flow.ts";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
createFileJournalStore,
|
|
22
|
+
createJournal,
|
|
23
|
+
createMemoryJournalStore,
|
|
24
|
+
} from "../kernel/journal.ts";
|
|
21
25
|
import {
|
|
22
26
|
clock,
|
|
23
27
|
createClockRuntime,
|
|
@@ -65,7 +69,6 @@ describe("durable journal", () => {
|
|
|
65
69
|
const calls: string[] = [];
|
|
66
70
|
const journalStore = createMemoryJournalStore();
|
|
67
71
|
const tt = createTimeTravel(0);
|
|
68
|
-
let crashAfterFirstStep = true;
|
|
69
72
|
|
|
70
73
|
const charge = flow("payments.charge", {
|
|
71
74
|
durable: true,
|
|
@@ -74,9 +77,6 @@ describe("durable journal", () => {
|
|
|
74
77
|
calls.push("create-intent");
|
|
75
78
|
return { id: "pi_1" };
|
|
76
79
|
});
|
|
77
|
-
if (crashAfterFirstStep) {
|
|
78
|
-
throw new Error("KILLED");
|
|
79
|
-
}
|
|
80
80
|
return fx.step("confirm", () => {
|
|
81
81
|
calls.push("confirm");
|
|
82
82
|
return intent.id === "pi_1";
|
|
@@ -84,17 +84,16 @@ describe("durable journal", () => {
|
|
|
84
84
|
},
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
// Simulate crash after the first step: leave status `running` (orphans,
|
|
88
|
+
// not terminal `failed` — failed runs refuse resume).
|
|
89
|
+
const journal = createJournal({ store: journalStore, now: tt.now });
|
|
90
|
+
const crashed = await journal.start("payments.charge", { orderId: "o1" });
|
|
91
|
+
await crashed.step("create-intent", () => {
|
|
92
|
+
calls.push("create-intent");
|
|
93
|
+
return { id: "pi_1" };
|
|
92
94
|
});
|
|
93
|
-
|
|
94
|
-
expect(calls).toEqual(["create-intent"]);
|
|
95
|
+
const runId = crashed.runId;
|
|
95
96
|
|
|
96
|
-
crashAfterFirstStep = false;
|
|
97
|
-
const runId = first.status === "failed" ? first.runId : "";
|
|
98
97
|
const second = await runDurable({
|
|
99
98
|
flow: charge,
|
|
100
99
|
input: { orderId: "o1" },
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `oke({ gate })` — nested Gate bag (auth · policies · rate · posture).
|
|
3
|
+
*
|
|
4
|
+
* Auth resolution is sync-lazy via {@link requirePackageModule} so HTTP-only
|
|
5
|
+
* apps never evaluate `auth/config` (and so `dist/` can ship a separate chunk).
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
|
-
import {
|
|
8
|
+
import type { GateAuthOptions, ResolvedGateAuth } from "../../auth/config.ts";
|
|
9
|
+
import { requirePackageModule } from "../../shared/lazy-src.ts";
|
|
6
10
|
import type { GateDecl } from "./declare.ts";
|
|
7
11
|
|
|
8
12
|
/** Rate-limit defaults under Gate. */
|
|
@@ -51,8 +55,14 @@ export interface ResolveGateConfigOptions {
|
|
|
51
55
|
*/
|
|
52
56
|
export function resolveGateConfig(options: ResolveGateConfigOptions = {}): ResolvedGateConfig {
|
|
53
57
|
const bag = options.gate ?? {};
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
let auth: ResolvedGateAuth | undefined;
|
|
59
|
+
if (bag.auth !== undefined) {
|
|
60
|
+
const { resolveGateAuth } = requirePackageModule<typeof import("../../auth/config.ts")>(
|
|
61
|
+
"auth/config",
|
|
62
|
+
"auth-config",
|
|
63
|
+
);
|
|
64
|
+
auth = resolveGateAuth({ auth: bag.auth, env: options.env });
|
|
65
|
+
}
|
|
56
66
|
const rateLimitEnabled =
|
|
57
67
|
bag.rateLimit?.enabled !== undefined
|
|
58
68
|
? bag.rateLimit.enabled
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate rate-limit constants — kept free of strategy / kv-lua imports so
|
|
3
|
+
* `gate.public` declaration does not pull Lua registration into HTTP-only apps.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RateStrategy } from "../../manifest/types.ts";
|
|
7
|
+
|
|
8
|
+
/** Default rate strategy (unified-theory §16). */
|
|
9
|
+
export const DEFAULT_RATE_STRATEGY: RateStrategy = "sliding-window-counter";
|
|
10
|
+
|
|
11
|
+
/** All five strategy ids. */
|
|
12
|
+
export const ALL_RATE_STRATEGIES: readonly RateStrategy[] = [
|
|
13
|
+
"fixed-window",
|
|
14
|
+
"sliding-window-counter",
|
|
15
|
+
"sliding-log",
|
|
16
|
+
"token-bucket",
|
|
17
|
+
"leaky-bucket",
|
|
18
|
+
];
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { RateStrategy } from "../../manifest/types.ts";
|
|
8
|
-
import { DEFAULT_RATE_STRATEGY } from "./
|
|
8
|
+
import { DEFAULT_RATE_STRATEGY } from "./constants.ts";
|
|
9
9
|
|
|
10
10
|
/** Context passed to policy predicates at evaluation time. */
|
|
11
11
|
export interface GatePolicyContext {
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
import { registerLuaScript, type LuaKvStore } from "../../drivers/kv-lua.ts";
|
|
12
12
|
import type { RateStrategy } from "../../manifest/types.ts";
|
|
13
13
|
|
|
14
|
+
export { ALL_RATE_STRATEGIES, DEFAULT_RATE_STRATEGY } from "./constants.ts";
|
|
15
|
+
|
|
14
16
|
/** Result of a rate-limit take attempt. */
|
|
15
17
|
export interface RateTakeResult {
|
|
16
18
|
/** Whether the request is allowed. */
|
|
@@ -338,18 +340,6 @@ export const RATE_STRATEGIES: Record<RateStrategy, StrategyDef> = {
|
|
|
338
340
|
"leaky-bucket": leakyBucket,
|
|
339
341
|
};
|
|
340
342
|
|
|
341
|
-
/** Default rate strategy (unified-theory §16). */
|
|
342
|
-
export const DEFAULT_RATE_STRATEGY: RateStrategy = "sliding-window-counter";
|
|
343
|
-
|
|
344
|
-
/** All five strategy ids. */
|
|
345
|
-
export const ALL_RATE_STRATEGIES: readonly RateStrategy[] = [
|
|
346
|
-
"fixed-window",
|
|
347
|
-
"sliding-window-counter",
|
|
348
|
-
"sliding-log",
|
|
349
|
-
"token-bucket",
|
|
350
|
-
"leaky-bucket",
|
|
351
|
-
];
|
|
352
|
-
|
|
353
343
|
for (const def of Object.values(RATE_STRATEGIES)) {
|
|
354
344
|
registerLuaScript(def.lua, (store, keys, args) => def.run(store, keys, args));
|
|
355
345
|
}
|
|
@@ -177,7 +177,7 @@ describe("gate boot posture", () => {
|
|
|
177
177
|
});
|
|
178
178
|
|
|
179
179
|
test("unguardedHttp allow has no effect outside env === test", () => {
|
|
180
|
-
for (const env of ["
|
|
180
|
+
for (const env of ["dev", "prod"] as const) {
|
|
181
181
|
expect(() => assertHttpGatePosture(unguarded, { unguardedHttp: "allow", env })).toThrow(
|
|
182
182
|
GateBootError,
|
|
183
183
|
);
|
package/src/elements/gate.ts
CHANGED
|
@@ -45,11 +45,11 @@ export {
|
|
|
45
45
|
type GateRuntime,
|
|
46
46
|
} from "./gate/runtime.ts";
|
|
47
47
|
|
|
48
|
+
export { ALL_RATE_STRATEGIES, DEFAULT_RATE_STRATEGY } from "./gate/constants.ts";
|
|
49
|
+
|
|
48
50
|
export {
|
|
49
51
|
takeRate,
|
|
50
52
|
luaForStrategy,
|
|
51
|
-
ALL_RATE_STRATEGIES,
|
|
52
|
-
DEFAULT_RATE_STRATEGY,
|
|
53
53
|
RATE_STRATEGIES,
|
|
54
54
|
type RateTakeResult,
|
|
55
55
|
type TakeRateOptions,
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { describe, expect, test } from "bun:test";
|
|
6
|
-
import { Database } from "bun:sqlite";
|
|
7
6
|
import { OkeError, OKE_ERRORS } from "../../kernel/errors.ts";
|
|
8
|
-
import {
|
|
7
|
+
import { memorySqlDriver } from "../../drivers/memory.ts";
|
|
9
8
|
import { defineTable } from "./table.ts";
|
|
10
9
|
import { createSqlStoreHandle } from "./sql-session.ts";
|
|
11
10
|
|
|
@@ -13,8 +12,7 @@ const notes = defineTable("notes", { id: true, title: true });
|
|
|
13
12
|
|
|
14
13
|
describe("domain DDL policy", () => {
|
|
15
14
|
test("ensure mode creates tables on first touch", async () => {
|
|
16
|
-
const
|
|
17
|
-
const conn = await sqliteDriver.connect({ client: db, role: "primary" });
|
|
15
|
+
const conn = await memorySqlDriver.connect({ role: "primary" });
|
|
18
16
|
const handle = createSqlStoreHandle("sql:app", {
|
|
19
17
|
connection: conn,
|
|
20
18
|
classifications: new Map(),
|
|
@@ -28,12 +26,11 @@ describe("domain DDL policy", () => {
|
|
|
28
26
|
});
|
|
29
27
|
|
|
30
28
|
test("prod/off mode never CREATE TABLE IF NOT EXISTS", async () => {
|
|
31
|
-
const db = new Database(":memory:");
|
|
32
29
|
const execSql: string[] = [];
|
|
33
|
-
const base = await
|
|
30
|
+
const base = await memorySqlDriver.connect({ role: "primary" });
|
|
34
31
|
const conn = {
|
|
35
32
|
...base,
|
|
36
|
-
driverId: "
|
|
33
|
+
driverId: "memory" as const,
|
|
37
34
|
role: "primary" as const,
|
|
38
35
|
async query(sql: string, params?: readonly unknown[]) {
|
|
39
36
|
execSql.push(sql);
|
|
@@ -61,8 +58,7 @@ describe("domain DDL policy", () => {
|
|
|
61
58
|
});
|
|
62
59
|
|
|
63
60
|
test("off mode remaps missing table to DOMAIN_SCHEMA_MISSING", async () => {
|
|
64
|
-
const
|
|
65
|
-
const conn = await sqliteDriver.connect({ client: db, role: "primary" });
|
|
61
|
+
const conn = await memorySqlDriver.connect({ role: "primary" });
|
|
66
62
|
const handle = createSqlStoreHandle("sql:app", {
|
|
67
63
|
connection: conn,
|
|
68
64
|
classifications: new Map(),
|
|
@@ -78,9 +74,6 @@ describe("domain DDL policy", () => {
|
|
|
78
74
|
expect(err).toBeInstanceOf(OkeError);
|
|
79
75
|
const oke = err as OkeError;
|
|
80
76
|
expect(oke.code).toBe(OKE_ERRORS.DOMAIN_SCHEMA_MISSING.code);
|
|
81
|
-
expect(oke.causeText).toContain("migrations have not been applied");
|
|
82
|
-
expect(oke.fix).toContain("oke db migrate");
|
|
83
|
-
expect(oke.docsUrl).toContain(`/e/${OKE_ERRORS.DOMAIN_SCHEMA_MISSING.code}`);
|
|
84
77
|
await conn.close();
|
|
85
78
|
});
|
|
86
79
|
});
|