okengine 0.14.1 → 0.16.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 +3 -4
- package/site/content/docs/elements/ai.mdx +2 -1
- package/site/content/docs/elements/channel.mdx +1 -0
- package/site/content/docs/elements/clock.mdx +3 -0
- package/site/content/docs/elements/flow.mdx +9 -0
- package/site/content/docs/elements/gate.mdx +80 -22
- package/site/content/docs/elements/signal.mdx +4 -4
- package/site/content/docs/elements/store.mdx +84 -27
- package/site/content/docs/elements/vault.mdx +4 -0
- package/site/content/docs/get-started/basic-usage.mdx +22 -13
- package/site/content/docs/get-started/index.mdx +5 -0
- package/site/content/docs/get-started/introduction.mdx +11 -3
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +932 -0
- package/site/content/docs/reference/cli.md +13 -10
- package/site/content/docs/reference/client.mdx +28 -21
- package/site/content/docs/reference/fx.mdx +29 -18
- package/site/content/docs/reference/security.md +2 -2
- package/src/auth/api-key-sql.test.ts +47 -0
- package/src/auth/api-key-sql.ts +254 -0
- package/src/auth/api-keys.ts +273 -14
- package/src/auth/auth.test.ts +56 -0
- package/src/auth/bindings.ts +14 -5
- package/src/auth/config.ts +5 -5
- package/src/auth/cross-plane.ts +10 -3
- package/src/auth/index.ts +25 -1
- package/src/auth/schema.ts +8 -0
- package/src/cli/ai-setup/ai-setup.test.ts +62 -0
- package/src/cli/ai-setup/apply.ts +32 -1
- package/src/cli/attach-host-console.test.ts +40 -2
- package/src/cli/attach-host-console.ts +9 -0
- package/src/cli/build.test.ts +4 -2
- package/src/cli/db-seed.ts +48 -15
- package/src/cli/db.test.ts +104 -0
- package/src/cli/db.ts +122 -10
- package/src/cli/dev-controls.test.ts +20 -0
- package/src/cli/dev-controls.ts +13 -0
- package/src/cli/dev.test.ts +15 -4
- package/src/cli/dev.ts +102 -29
- package/src/cli/ensure-drizzle-config.ts +2 -1
- package/src/cli/tui/DevLive.tsx +12 -2
- package/src/cli/tui/keys.test.ts +2 -1
- package/src/cli/tui/keys.ts +3 -2
- package/src/compiler/aot.test.ts +1 -1
- package/src/compiler/effects-infer.ts +17 -2
- package/src/compiler/extract.test.ts +409 -33
- package/src/compiler/extract.ts +318 -67
- package/src/compiler/flow-path.test.ts +96 -0
- package/src/compiler/flow-path.ts +173 -0
- package/src/compiler/generate-adopt.test.ts +112 -3
- package/src/compiler/generate-adopt.ts +280 -26
- package/src/compiler/schema-from-ast.ts +609 -0
- package/src/console/server/access.test.ts +116 -8
- package/src/console/server/access.ts +70 -23
- package/src/console/server/app.ts +1 -0
- package/src/console/server/bind.ts +2 -2
- package/src/console/server/console.test.ts +3 -0
- package/src/console/server/dev-identities.test.ts +127 -0
- package/src/console/server/dev-identities.ts +229 -0
- package/src/console/server/flows-invoke.test.ts +21 -4
- package/src/console/server/flows.ts +233 -98
- package/src/console/server/gates.test.ts +24 -0
- package/src/console/server/gates.ts +8 -21
- package/src/console/server/index.ts +1 -0
- package/src/console/server/invoke-user-flow.test.ts +3 -1
- package/src/console/server/invoke-user-flow.ts +17 -2
- package/src/console/server/iso-at.test.ts +9 -0
- package/src/console/server/iso-at.ts +11 -0
- package/src/console/server/live.test.ts +26 -1
- package/src/console/server/live.ts +4 -2
- package/src/console/server/operator-db.ts +2 -0
- package/src/console/server/security-headers.ts +6 -3
- package/src/console/server/spa-proxy.test.ts +1 -0
- package/src/console/server/sql-catalog.ts +147 -7
- package/src/console/server/sql-rls.test.ts +16 -0
- package/src/console/server/state.ts +37 -55
- package/src/console/server/store.test.ts +7 -4
- package/src/console/server/store.ts +142 -58
- package/src/console/ui-next/dist/assets/access-page-CAGHrA9H.js +4 -0
- package/src/console/ui-next/dist/assets/agent-disclosure-BP0Y0Sux.js +1 -0
- package/src/console/ui-next/dist/assets/cache-glyph-B5X-NM0-.js +1 -0
- package/src/console/ui-next/dist/assets/call-pii-button-ChAUiLo9.js +1 -0
- package/src/console/ui-next/dist/assets/collapsible-DGnOM2ph.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-DBHhgHKP.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-DhHM1iaZ.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-_NjTEo5_.js +1 -0
- package/src/console/ui-next/dist/assets/{duration-tone-oiRxPVsZ.js → duration-tone-sC3lGABz.js} +1 -1
- package/src/console/ui-next/dist/assets/element-icons-BVXtRyd3.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-2uIhBu0_.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-B-OUtiAu.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CvDPvveV.js → highlighted-json-Awq7gYdu.js} +1 -1
- package/src/console/ui-next/dist/assets/http-method-_UHM2ODJ.js +1 -0
- package/src/console/ui-next/dist/assets/index-CGoZkILK.js +66 -0
- package/src/console/ui-next/dist/assets/index-Ck88Jmv8.css +2 -0
- package/src/console/ui-next/dist/assets/link-BX6Vqztd.js +1 -0
- package/src/console/ui-next/dist/assets/observability-page-BDyXalNR.js +4 -0
- package/src/console/ui-next/dist/assets/react-D8E3mtu1.js +1 -0
- package/src/console/ui-next/dist/assets/replica-lag-DKRrbvdo.js +18 -0
- package/src/console/ui-next/dist/assets/request-meta-DV0ywz7t.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-JQIZlWfm.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DKxNTe_m.js +1 -0
- package/src/console/ui-next/dist/assets/{skeleton-CL_X0GCj.js → skeleton-D-czQJT6.js} +1 -1
- package/src/console/ui-next/dist/assets/store-page-Xh8Kn3rx.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-B09O8rA6.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-DkOXA12R.js +54 -0
- package/src/console/ui-next/dist/assets/units-page-Dpk40kOQ.js +1 -0
- package/src/console/ui-next/dist/assets/{use-vault-list-Cl79j_ku.js → use-vault-list-uk4WVboC.js} +1 -1
- package/src/console/ui-next/dist/assets/useMutation-B8EO02Ej.js +1 -0
- package/src/console/ui-next/dist/assets/useRender-BE2A9BWC.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-B1dB9Ft0.js +2 -0
- package/src/console/ui-next/dist/index.html +12 -10
- package/src/console/ui-next/index.html +3 -3
- package/src/console/ui-next/src/client.ts +202 -7
- package/src/console/ui-next/src/components/motion/table/index.tsx +16 -0
- package/src/console/ui-next/src/components/motion/table/types.ts +4 -0
- package/src/console/ui-next/src/components/shell/app-sidebar.tsx +2 -0
- package/src/console/ui-next/src/components/shell/command-items.test.ts +1 -0
- package/src/console/ui-next/src/components/shell/command-items.ts +8 -0
- package/src/console/ui-next/src/components/shell/shell-layout.tsx +2 -1
- package/src/console/ui-next/src/components/ui/kbd.tsx +2 -2
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +8 -3
- package/src/console/ui-next/src/features/access/access-page.tsx +347 -0
- package/src/console/ui-next/src/features/access/data/use-access.ts +23 -0
- package/src/console/ui-next/src/features/access/detail/access-detail.tsx +552 -0
- package/src/console/ui-next/src/features/access/explorer/access-list.tsx +125 -0
- package/src/console/ui-next/src/features/access/lib/format-when.test.ts +139 -0
- package/src/console/ui-next/src/features/access/lib/format-when.ts +296 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.test.ts +34 -0
- package/src/console/ui-next/src/features/access/lib/scope-groups.ts +40 -0
- package/src/console/ui-next/src/features/access/sheets/access-allow-fields.tsx +236 -0
- package/src/console/ui-next/src/features/access/sheets/access-create-sheet.tsx +316 -0
- package/src/console/ui-next/src/features/access/sheets/access-edit-sheet.tsx +240 -0
- package/src/console/ui-next/src/features/access/sheets/access-expiry-fields.tsx +66 -0
- package/src/console/ui-next/src/features/access/sheets/access-refresh-sheet.tsx +104 -0
- package/src/console/ui-next/src/features/access/sheets/access-scope-field.tsx +115 -0
- package/src/console/ui-next/src/features/access/sheets/access-secret-sheet.tsx +89 -0
- package/src/console/ui-next/src/features/access/sheets/access-sheet-search.tsx +49 -0
- package/src/console/ui-next/src/features/access/state/access-selection.ts +84 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.test.ts +5 -0
- package/src/console/ui-next/src/features/auth/auth-redirect.ts +18 -2
- package/src/console/ui-next/src/features/flows/traces/http-method.ts +19 -0
- package/src/console/ui-next/src/features/flows/traces/trace-detail.test.ts +19 -1
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +10 -3
- package/src/console/ui-next/src/features/not-found/not-found-page.tsx +1 -1
- package/src/console/ui-next/src/features/store/detail/browse-section.tsx +45 -1
- package/src/console/ui-next/src/features/store/detail/resource-panel.tsx +78 -16
- package/src/console/ui-next/src/features/store/detail/store-row-detail-sheet.tsx +155 -14
- package/src/console/ui-next/src/features/store/explorer/store-tree.tsx +33 -133
- package/src/console/ui-next/src/features/store/files/file-preview.tsx +4 -1
- package/src/console/ui-next/src/features/store/grid/rls-policy-sheet.tsx +326 -165
- package/src/console/ui-next/src/features/store/grid/sql-insert-sheet.tsx +6 -0
- package/src/console/ui-next/src/features/store/grid/store-data-grid.tsx +50 -3
- package/src/console/ui-next/src/features/store/lib/files-meta.test.ts +3 -0
- package/src/console/ui-next/src/features/store/lib/files-meta.ts +12 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.test.ts +25 -0
- package/src/console/ui-next/src/features/store/lib/query-defaults.ts +85 -3
- package/src/console/ui-next/src/features/store/lib/query-gate.ts +30 -4
- package/src/console/ui-next/src/features/store/lib/rls-policy.test.ts +139 -0
- package/src/console/ui-next/src/features/store/lib/rls-policy.ts +223 -21
- package/src/console/ui-next/src/features/store/lib/schema-graph.ts +4 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.test.ts +24 -0
- package/src/console/ui-next/src/features/store/lib/sql-catalog.ts +19 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.test.ts +6 -0
- package/src/console/ui-next/src/features/store/lib/store-tree.ts +13 -0
- package/src/console/ui-next/src/features/store/query/query-console.tsx +48 -14
- package/src/console/ui-next/src/features/store/query/query-gate-menu.tsx +19 -20
- package/src/console/ui-next/src/features/store/query/query-gate-parts.tsx +2 -1
- package/src/console/ui-next/src/features/store/query/query-results.tsx +110 -83
- package/src/console/ui-next/src/features/store/schema/schema-table-node.tsx +17 -0
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +112 -72
- package/src/console/ui-next/src/features/units/lib/contract-input.test.ts +206 -1
- package/src/console/ui-next/src/features/units/lib/contract-input.ts +55 -0
- package/src/console/ui-next/src/features/vault/lib/types.ts +1 -1
- package/src/console/ui-next/src/lib/document-meta.test.ts +1 -0
- package/src/console/ui-next/src/lib/document-meta.ts +2 -1
- package/src/console/ui-next/src/lib/last-module-search.test.ts +1 -0
- package/src/console/ui-next/src/lib/last-module-search.ts +2 -1
- package/src/console/ui-next/src/lib/shortcut.test.ts +2 -0
- package/src/console/ui-next/src/lib/shortcut.ts +3 -0
- package/src/console/ui-next/src/router.tsx +10 -1
- package/src/console/ui-next/ui-next-seed-app-schema.ts +88 -37
- package/src/console/ui-next/ui-next-seed-store.ts +43 -15
- package/src/console/xss.gate.test.ts +1 -0
- package/src/docker/compose-health.test.ts +27 -0
- package/src/docker/compose-health.ts +19 -3
- package/src/docker/docker.test.ts +3 -0
- package/src/docker/recipes/pgdog.ts +2 -0
- package/src/drivers/clock-postgres.ts +13 -9
- package/src/drivers/instances-postgres.ts +4 -6
- package/src/drivers/journal-postgres.ts +13 -9
- package/src/drivers/pg-rls.test.ts +87 -0
- package/src/drivers/pg-rls.ts +233 -7
- package/src/drivers/pglite.ts +26 -3
- package/src/drivers/postgres.test.ts +23 -0
- package/src/drivers/postgres.ts +133 -9
- package/src/drivers/types.ts +10 -0
- package/src/elements/clock/declare.ts +26 -1
- package/src/elements/gate/boot.ts +5 -5
- package/src/elements/gate/declare.ts +38 -4
- package/src/elements/gate/flatten.ts +1 -1
- package/src/elements/gate/runtime.ts +1 -1
- package/src/elements/store/cache.ts +1 -1
- package/src/elements/store/declare.ts +9 -6
- package/src/elements/store/emit-drizzle.ts +44 -8
- package/src/elements/store/resource.test.ts +1 -1
- package/src/elements/store/rls-identity.test.ts +95 -0
- package/src/elements/store/rls-identity.ts +116 -0
- package/src/elements/store/runtime.ts +18 -0
- package/src/elements/store/schema-decl.test.ts +37 -0
- package/src/elements/store/schema-decl.ts +260 -41
- package/src/elements/store/seed.ts +1 -1
- package/src/elements/store/sql-condition.test.ts +12 -0
- package/src/elements/store/sql-condition.ts +26 -2
- package/src/elements/store/sql-rls-isolation.test.ts +165 -0
- package/src/elements/store/sql-rls-stamp.test.ts +117 -0
- package/src/elements/store/sql-select.types.test.ts +71 -0
- package/src/elements/store/sql-session.ts +135 -35
- package/src/elements/store/upsert-app.test.ts +1 -1
- package/src/elements/store.ts +17 -0
- package/src/http.ts +20 -3
- package/src/index.ts +11 -1
- package/src/kernel/adopt-barrel-fresh.test.ts +1 -1
- package/src/kernel/adopt-routes.ts +30 -2
- package/src/kernel/api-key-host-persist.test.ts +143 -0
- package/src/kernel/app.ts +145 -16
- package/src/kernel/auth-resolve.ts +10 -5
- package/src/kernel/auto-cache.test.ts +8 -8
- package/src/kernel/auto-registry.test.ts +49 -9
- package/src/kernel/boot.test.ts +5 -5
- package/src/kernel/boot.ts +2 -2
- package/src/kernel/effects-stamping.test.ts +3 -3
- package/src/kernel/element-registries.ts +12 -5
- package/src/kernel/errors.ts +24 -0
- package/src/kernel/flow-units.ts +64 -0
- package/src/kernel/flow.ts +29 -17
- package/src/kernel/fx-auth-keys.test.ts +88 -0
- package/src/kernel/fx-auth-keys.ts +198 -0
- package/src/kernel/fx.test.ts +17 -1
- package/src/kernel/fx.ts +59 -18
- package/src/kernel/horizontal-child.ts +4 -4
- package/src/kernel/http-path-pending.ts +20 -0
- package/src/kernel/http-resource.ts +4 -0
- package/src/kernel/http-stream.test.ts +4 -4
- package/src/kernel/pipeline-api-key.test.ts +192 -7
- package/src/kernel/pipeline.test.ts +2 -2
- package/src/kernel/pipeline.ts +4 -2
- package/src/kernel/plugin-elements.test.ts +1 -1
- package/src/kernel/plugin-needs.test.ts +1 -1
- package/src/kernel/ready.test.ts +2 -2
- package/src/kernel/resource-mount.test.ts +14 -0
- package/src/kernel/run-duration.test.ts +2 -2
- package/src/kernel/stamp-http.test.ts +79 -0
- package/src/kernel/stamp-http.ts +75 -0
- package/src/kernel/triggers.ts +52 -27
- package/src/manifest/types.ts +22 -3
- package/src/release/build-lib.ts +1 -0
- package/src/release/measure.ts +2 -2
- package/src/runtime/json-code-block.test.ts +2 -2
- package/src/runtime/serve.test.ts +4 -4
- package/src/term.test.ts +52 -4
- package/src/term.ts +125 -17
- package/src/test/reset-element-registries.ts +8 -3
- package/src/cli/ask-seed.test.ts +0 -96
- package/src/cli/ask-seed.ts +0 -82
- package/src/console/ui-next/dist/assets/cache-glyph-BanhLsEY.js +0 -1
- package/src/console/ui-next/dist/assets/call-pii-button-bqkxMrJH.js +0 -1
- package/src/console/ui-next/dist/assets/collapsible-DYb0xU8C.js +0 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-DDCRmG62.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-HjTnVQoR.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-DxDsOd4f.js +0 -1
- package/src/console/ui-next/dist/assets/http-method-CJCBYL2j.js +0 -1
- package/src/console/ui-next/dist/assets/index-Ce6WKWKM.js +0 -66
- package/src/console/ui-next/dist/assets/index-D4Ldtj79.css +0 -2
- package/src/console/ui-next/dist/assets/link-DF7SZ9Ek.js +0 -1
- package/src/console/ui-next/dist/assets/observability-page-BEZDzyYh.js +0 -4
- package/src/console/ui-next/dist/assets/query-gate-parts-1m8m1iNp.js +0 -2
- package/src/console/ui-next/dist/assets/react-B1ML8gxg.js +0 -1
- package/src/console/ui-next/dist/assets/replica-lag-C8_BRt2x.js +0 -18
- package/src/console/ui-next/dist/assets/sheet-form-D-ata7jy.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-Cgzuv4k1.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-DO4IsVqv.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-v3LXdYpr.js +0 -45
- package/src/console/ui-next/dist/assets/trace-detail-sheet-DFLFfUUX.js +0 -2
- package/src/console/ui-next/dist/assets/units-page-C0gW6Kdo.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-DuKzqwzW.js +0 -2
|
@@ -41,6 +41,20 @@ const SEED_SPACES = [
|
|
|
41
41
|
] as const;
|
|
42
42
|
|
|
43
43
|
const SEED_MEMBERS = [
|
|
44
|
+
{
|
|
45
|
+
id: "mem_demo",
|
|
46
|
+
space_id: "space_eng",
|
|
47
|
+
name: "Demo User",
|
|
48
|
+
email: "demo@example.com",
|
|
49
|
+
role: "owner",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "mem_admin",
|
|
53
|
+
space_id: "space_eng",
|
|
54
|
+
name: "Avery Cole",
|
|
55
|
+
email: "admin@keel.dev",
|
|
56
|
+
role: "admin",
|
|
57
|
+
},
|
|
44
58
|
{
|
|
45
59
|
id: "mem_aria",
|
|
46
60
|
space_id: "space_eng",
|
|
@@ -48,6 +62,13 @@ const SEED_MEMBERS = [
|
|
|
48
62
|
email: "aria@keel.dev",
|
|
49
63
|
role: "project_manager",
|
|
50
64
|
},
|
|
65
|
+
{
|
|
66
|
+
id: "mem_lead",
|
|
67
|
+
space_id: "space_eng",
|
|
68
|
+
name: "Jules Hart",
|
|
69
|
+
email: "jules@keel.dev",
|
|
70
|
+
role: "lead",
|
|
71
|
+
},
|
|
51
72
|
{
|
|
52
73
|
id: "mem_ben",
|
|
53
74
|
space_id: "space_eng",
|
|
@@ -55,6 +76,13 @@ const SEED_MEMBERS = [
|
|
|
55
76
|
email: "ben@keel.dev",
|
|
56
77
|
role: "developer",
|
|
57
78
|
},
|
|
79
|
+
{
|
|
80
|
+
id: "mem_eli",
|
|
81
|
+
space_id: "space_eng",
|
|
82
|
+
name: "Eli Park",
|
|
83
|
+
email: "eli@keel.dev",
|
|
84
|
+
role: "contributor",
|
|
85
|
+
},
|
|
58
86
|
{
|
|
59
87
|
id: "mem_cai",
|
|
60
88
|
space_id: "space_des",
|
|
@@ -62,6 +90,20 @@ const SEED_MEMBERS = [
|
|
|
62
90
|
email: "cai@keel.dev",
|
|
63
91
|
role: "member",
|
|
64
92
|
},
|
|
93
|
+
{
|
|
94
|
+
id: "mem_member",
|
|
95
|
+
space_id: "space_des",
|
|
96
|
+
name: "Member",
|
|
97
|
+
email: "member@example.com",
|
|
98
|
+
role: "member",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "mem_commenter",
|
|
102
|
+
space_id: "space_gtm",
|
|
103
|
+
name: "Kit Rowe",
|
|
104
|
+
email: "kit@keel.dev",
|
|
105
|
+
role: "commenter",
|
|
106
|
+
},
|
|
65
107
|
{
|
|
66
108
|
id: "mem_dia",
|
|
67
109
|
space_id: "space_gtm",
|
|
@@ -69,13 +111,6 @@ const SEED_MEMBERS = [
|
|
|
69
111
|
email: "dia@keel.dev",
|
|
70
112
|
role: "guest",
|
|
71
113
|
},
|
|
72
|
-
{
|
|
73
|
-
id: "mem_eli",
|
|
74
|
-
space_id: "space_eng",
|
|
75
|
-
name: "Eli Park",
|
|
76
|
-
email: "eli@keel.dev",
|
|
77
|
-
role: "developer",
|
|
78
|
-
},
|
|
79
114
|
] as const;
|
|
80
115
|
|
|
81
116
|
const SEED_GOALS = [
|
|
@@ -640,14 +675,7 @@ function generateKeelVolume(): {
|
|
|
640
675
|
email: `${p.id}@keel.dev`,
|
|
641
676
|
role: "member",
|
|
642
677
|
}));
|
|
643
|
-
const emails = [
|
|
644
|
-
"aria@keel.dev",
|
|
645
|
-
"ben@keel.dev",
|
|
646
|
-
"cai@keel.dev",
|
|
647
|
-
"dia@keel.dev",
|
|
648
|
-
"eli@keel.dev",
|
|
649
|
-
...members.map((m) => m.email),
|
|
650
|
-
];
|
|
678
|
+
const emails = [...SEED_MEMBERS.map((m) => m.email), ...members.map((m) => m.email)];
|
|
651
679
|
|
|
652
680
|
const projects = Array.from({ length: GENERATED.projects }, (_, i) => ({
|
|
653
681
|
id: `proj_g_${String(i + 1).padStart(2, "0")}`,
|
|
@@ -16,6 +16,12 @@ describe("compose-health", () => {
|
|
|
16
16
|
expect(composeRowToStatus({ State: "stopped" })).toBe("error");
|
|
17
17
|
expect(composeRowToStatus({ Status: "Exited (0) 3 seconds ago" })).toBe("error");
|
|
18
18
|
expect(composeRowToStatus({ State: "running" })).toBe("ready");
|
|
19
|
+
expect(composeRowToStatus({ State: "restarting" })).toBe("pending");
|
|
20
|
+
expect(composeRowToStatus({ State: "running", Status: "Up Less than a second" })).toBe(
|
|
21
|
+
"pending",
|
|
22
|
+
);
|
|
23
|
+
expect(composeRowToStatus({ State: "running", Status: "Up 2 seconds" })).toBe("pending");
|
|
24
|
+
expect(composeRowToStatus({ State: "running", Status: "Up 2 minutes" })).toBe("ready");
|
|
19
25
|
expect(composeRowToStatus({ Status: "Up 2 seconds (health: starting)" })).toBe("pending");
|
|
20
26
|
});
|
|
21
27
|
|
|
@@ -60,6 +66,27 @@ describe("compose-health", () => {
|
|
|
60
66
|
expect(updates).toEqual(["store-sql:pending", "store-sql:ready"]);
|
|
61
67
|
});
|
|
62
68
|
|
|
69
|
+
test("watchComposeHealth waits through compose restarting", async () => {
|
|
70
|
+
let calls = 0;
|
|
71
|
+
const map = await watchComposeHealth({
|
|
72
|
+
files: ["compose.yml"],
|
|
73
|
+
cwd: "/tmp",
|
|
74
|
+
env: {},
|
|
75
|
+
intervalMs: 1,
|
|
76
|
+
timeoutMs: 5_000,
|
|
77
|
+
sleep: async () => {},
|
|
78
|
+
run: async () => {
|
|
79
|
+
calls += 1;
|
|
80
|
+
if (calls === 1) {
|
|
81
|
+
return JSON.stringify([{ Service: "store-sql", State: "restarting" }]);
|
|
82
|
+
}
|
|
83
|
+
return JSON.stringify([{ Service: "store-sql", Health: "healthy", State: "running" }]);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
expect(calls).toBe(2);
|
|
87
|
+
expect(map.get("store-sql")).toBe("ready");
|
|
88
|
+
});
|
|
89
|
+
|
|
63
90
|
test("readComposeHealth / watch use ps -a", async () => {
|
|
64
91
|
let seen: readonly string[] = [];
|
|
65
92
|
await watchComposeHealth({
|
|
@@ -20,22 +20,27 @@ export type ComposePsRow = {
|
|
|
20
20
|
export function composeRowToStatus(row: ComposePsRow): DevStatus {
|
|
21
21
|
const health = (row.Health ?? "").trim().toLowerCase();
|
|
22
22
|
const state = (row.State ?? "").trim().toLowerCase();
|
|
23
|
+
const statusText = (row.Status ?? "").toLowerCase();
|
|
23
24
|
if (health === "healthy") return "ready";
|
|
24
25
|
if (health === "unhealthy") return "error";
|
|
25
26
|
if (health === "starting") return "pending";
|
|
26
|
-
if (state === "
|
|
27
|
+
if (state === "restarting") return "pending";
|
|
28
|
+
if (state === "running" || state === "up") {
|
|
29
|
+
// Right after `compose restart`, State is "running" before Health is
|
|
30
|
+
// "starting". Treat a fresh Up without (healthy) as still coming up.
|
|
31
|
+
if (isFreshComposeUp(statusText) && !statusText.includes("(healthy)")) return "pending";
|
|
32
|
+
return "ready";
|
|
33
|
+
}
|
|
27
34
|
if (
|
|
28
35
|
state === "exited" ||
|
|
29
36
|
state === "dead" ||
|
|
30
37
|
state === "stopped" ||
|
|
31
|
-
state === "restarting" ||
|
|
32
38
|
state === "removing" ||
|
|
33
39
|
state === "oomkilled"
|
|
34
40
|
) {
|
|
35
41
|
return "error";
|
|
36
42
|
}
|
|
37
43
|
// Status text for stopped containers (`Exited (0) 2 seconds ago`).
|
|
38
|
-
const statusText = (row.Status ?? "").toLowerCase();
|
|
39
44
|
if (statusText.startsWith("exited") || statusText.startsWith("dead")) return "error";
|
|
40
45
|
if (state === "created" || state === "paused" || state.length === 0) return "pending";
|
|
41
46
|
// Status text like "Up 3 seconds (healthy)"
|
|
@@ -48,6 +53,17 @@ export function composeRowToStatus(row: ComposePsRow): DevStatus {
|
|
|
48
53
|
return "pending";
|
|
49
54
|
}
|
|
50
55
|
|
|
56
|
+
/**
|
|
57
|
+
* True when compose `Status` is a just-started container (health not reported yet).
|
|
58
|
+
*
|
|
59
|
+
* @param statusText - Lowercased `Status` from `docker compose ps`
|
|
60
|
+
*/
|
|
61
|
+
function isFreshComposeUp(statusText: string): boolean {
|
|
62
|
+
if (statusText.includes("less than a second")) return true;
|
|
63
|
+
const seconds = /up\s+(\d+)\s+seconds?/i.exec(statusText);
|
|
64
|
+
return seconds !== null && Number(seconds[1]) < 8;
|
|
65
|
+
}
|
|
66
|
+
|
|
51
67
|
/**
|
|
52
68
|
* Parse NDJSON or a JSON array from `docker compose ps --format json`.
|
|
53
69
|
*
|
|
@@ -392,11 +392,13 @@ describe("image recipes", () => {
|
|
|
392
392
|
expect(pgdogToml).toContain('host = "0.0.0.0"');
|
|
393
393
|
expect(pgdogToml).toContain("port = 6432");
|
|
394
394
|
expect(pgdogToml).toContain('pooler_mode = "transaction"');
|
|
395
|
+
expect(pgdogToml).toContain("default_pool_size = 20");
|
|
395
396
|
expect(pgdogToml).toContain("[[databases]]");
|
|
396
397
|
expect(pgdogToml).toContain('name = "oke"');
|
|
397
398
|
expect(pgdogToml).toContain('host = "store-sql"');
|
|
398
399
|
expect(pgdogToml).toContain("port = 5432");
|
|
399
400
|
expect(pgdogToml).toContain('database_name = "oke"');
|
|
401
|
+
expect(pgdogToml).toContain("pool_size = 20");
|
|
400
402
|
|
|
401
403
|
const usersToml = buildPgDogUsersToml({
|
|
402
404
|
user: "oke",
|
|
@@ -936,6 +938,7 @@ describe("deriveInfrastructure", () => {
|
|
|
936
938
|
|
|
937
939
|
const pgdogToml = result.files.find((f) => f.path === "pgdog/pgdog.toml")!.content;
|
|
938
940
|
expect(pgdogToml).toContain('pooler_mode = "transaction"');
|
|
941
|
+
expect(pgdogToml).toContain("pool_size = 20");
|
|
939
942
|
expect(pgdogToml).toContain('host = "store-sql"');
|
|
940
943
|
expect(pgdogToml).not.toContain(fixedCreds["store.sql"].password);
|
|
941
944
|
|
|
@@ -35,12 +35,14 @@ export function buildPgDogToml(opts: {
|
|
|
35
35
|
'host = "0.0.0.0"',
|
|
36
36
|
"port = 6432",
|
|
37
37
|
'pooler_mode = "transaction"',
|
|
38
|
+
"default_pool_size = 20",
|
|
38
39
|
"",
|
|
39
40
|
"[[databases]]",
|
|
40
41
|
`name = ${tomlString(db)}`,
|
|
41
42
|
`host = ${tomlString(host)}`,
|
|
42
43
|
`port = ${port}`,
|
|
43
44
|
`database_name = ${tomlString(db)}`,
|
|
45
|
+
"pool_size = 20",
|
|
44
46
|
"",
|
|
45
47
|
].join("\n");
|
|
46
48
|
}
|
|
@@ -7,7 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { CronRow, CronStatus, CronStore } from "../elements/clock/reconcile.ts";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
resolvePostgresUrl,
|
|
12
|
+
sharedPostgresClient,
|
|
13
|
+
toPostgresParams,
|
|
14
|
+
withPinnedPostgres,
|
|
15
|
+
type PostgresClientLike,
|
|
16
|
+
} from "./postgres.ts";
|
|
11
17
|
|
|
12
18
|
/** Row shape in `oke_crons` (lease columns mirror `oke_signal_messages`). */
|
|
13
19
|
interface CronDbRow {
|
|
@@ -69,16 +75,16 @@ export interface BunCronClient {
|
|
|
69
75
|
close?(options?: { timeout?: number }): Promise<void>;
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
function wrapBunClient(client:
|
|
78
|
+
function wrapBunClient(client: PostgresClientLike): PostgresCronSql {
|
|
73
79
|
const api: PostgresCronSql = {
|
|
74
80
|
async query(sql, params = []) {
|
|
75
|
-
const pg = toPostgresParams(sql);
|
|
81
|
+
const pg = toPostgresParams(sql, params);
|
|
76
82
|
const result = await client.unsafe(pg, [...params]);
|
|
77
83
|
if (Array.isArray(result)) return result as Record<string, unknown>[];
|
|
78
84
|
return Array.from(result as ArrayLike<Record<string, unknown>>);
|
|
79
85
|
},
|
|
80
86
|
async exec(sql, params = []) {
|
|
81
|
-
const pg = toPostgresParams(sql);
|
|
87
|
+
const pg = toPostgresParams(sql, params);
|
|
82
88
|
const result = await client.unsafe(pg, [...params]);
|
|
83
89
|
if (
|
|
84
90
|
result &&
|
|
@@ -92,7 +98,7 @@ function wrapBunClient(client: BunCronClient): PostgresCronSql {
|
|
|
92
98
|
return { changes: 0 };
|
|
93
99
|
},
|
|
94
100
|
async begin(fn) {
|
|
95
|
-
return client
|
|
101
|
+
return withPinnedPostgres(client, (tx) => fn(wrapBunClient(tx)));
|
|
96
102
|
},
|
|
97
103
|
async close() {
|
|
98
104
|
await client.close?.();
|
|
@@ -365,10 +371,8 @@ export async function createPostgresCronStore(
|
|
|
365
371
|
const sql =
|
|
366
372
|
options.sql ??
|
|
367
373
|
wrapBunClient(
|
|
368
|
-
options.client ??
|
|
369
|
-
(
|
|
370
|
-
options.url ?? process.env.DATABASE_URL ?? "postgres://localhost:5432/oke",
|
|
371
|
-
) as unknown as BunCronClient),
|
|
374
|
+
(options.client ??
|
|
375
|
+
sharedPostgresClient(resolvePostgresUrl(options.url))) as PostgresClientLike,
|
|
372
376
|
);
|
|
373
377
|
|
|
374
378
|
await ensureSchema(sql);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ConfigEnv } from "../config/index.ts";
|
|
9
9
|
import type { InstanceRow, InstanceStore } from "../kernel/instances.ts";
|
|
10
|
-
import { toPostgresParams } from "./postgres.ts";
|
|
10
|
+
import { resolvePostgresUrl, sharedPostgresClient, toPostgresParams } from "./postgres.ts";
|
|
11
11
|
|
|
12
12
|
/** Minimal SQL surface for the postgres instance store. */
|
|
13
13
|
export interface PostgresInstanceSql {
|
|
@@ -40,13 +40,13 @@ const UPSERT_SQL = `INSERT INTO oke_instances (id, started_at, heartbeat_at, lea
|
|
|
40
40
|
function wrapBunClient(client: BunInstanceClient): PostgresInstanceSql {
|
|
41
41
|
return {
|
|
42
42
|
async query(sql, params = []) {
|
|
43
|
-
const pg = toPostgresParams(sql);
|
|
43
|
+
const pg = toPostgresParams(sql, params);
|
|
44
44
|
const result = await client.unsafe(pg, [...params]);
|
|
45
45
|
if (Array.isArray(result)) return result as Record<string, unknown>[];
|
|
46
46
|
return Array.from(result as ArrayLike<Record<string, unknown>>);
|
|
47
47
|
},
|
|
48
48
|
async exec(sql, params = []) {
|
|
49
|
-
const pg = toPostgresParams(sql);
|
|
49
|
+
const pg = toPostgresParams(sql, params);
|
|
50
50
|
const result = await client.unsafe(pg, [...params]);
|
|
51
51
|
if (
|
|
52
52
|
result &&
|
|
@@ -109,9 +109,7 @@ export async function createPostgresInstanceStore(
|
|
|
109
109
|
options.sql ??
|
|
110
110
|
wrapBunClient(
|
|
111
111
|
options.client ??
|
|
112
|
-
(
|
|
113
|
-
options.url ?? process.env.DATABASE_URL ?? "postgres://localhost:5432/oke",
|
|
114
|
-
) as unknown as BunInstanceClient),
|
|
112
|
+
(sharedPostgresClient(resolvePostgresUrl(options.url)) as unknown as BunInstanceClient),
|
|
115
113
|
);
|
|
116
114
|
|
|
117
115
|
await ensureSchema(sql);
|
|
@@ -14,7 +14,13 @@ import {
|
|
|
14
14
|
type JournalRun,
|
|
15
15
|
type JournalStore,
|
|
16
16
|
} from "../kernel/journal.ts";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
resolvePostgresUrl,
|
|
19
|
+
sharedPostgresClient,
|
|
20
|
+
toPostgresParams,
|
|
21
|
+
withPinnedPostgres,
|
|
22
|
+
type PostgresClientLike,
|
|
23
|
+
} from "./postgres.ts";
|
|
18
24
|
|
|
19
25
|
/** Row shape in `oke_journal_runs` (lease columns mirror `oke_crons`). */
|
|
20
26
|
interface JournalDbRow {
|
|
@@ -87,16 +93,16 @@ export interface BunJournalClient {
|
|
|
87
93
|
close?(options?: { timeout?: number }): Promise<void>;
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
function wrapBunClient(client:
|
|
96
|
+
function wrapBunClient(client: PostgresClientLike): PostgresJournalSql {
|
|
91
97
|
const api: PostgresJournalSql = {
|
|
92
98
|
async query(sql, params = []) {
|
|
93
|
-
const pg = toPostgresParams(sql);
|
|
99
|
+
const pg = toPostgresParams(sql, params);
|
|
94
100
|
const result = await client.unsafe(pg, [...params]);
|
|
95
101
|
if (Array.isArray(result)) return result as Record<string, unknown>[];
|
|
96
102
|
return Array.from(result as ArrayLike<Record<string, unknown>>);
|
|
97
103
|
},
|
|
98
104
|
async exec(sql, params = []) {
|
|
99
|
-
const pg = toPostgresParams(sql);
|
|
105
|
+
const pg = toPostgresParams(sql, params);
|
|
100
106
|
const result = await client.unsafe(pg, [...params]);
|
|
101
107
|
if (
|
|
102
108
|
result &&
|
|
@@ -110,7 +116,7 @@ function wrapBunClient(client: BunJournalClient): PostgresJournalSql {
|
|
|
110
116
|
return { changes: 0 };
|
|
111
117
|
},
|
|
112
118
|
async begin(fn) {
|
|
113
|
-
return client
|
|
119
|
+
return withPinnedPostgres(client, (tx) => fn(wrapBunClient(tx)));
|
|
114
120
|
},
|
|
115
121
|
async close() {
|
|
116
122
|
await client.close?.();
|
|
@@ -437,10 +443,8 @@ export async function createPostgresJournalStore(
|
|
|
437
443
|
const sql =
|
|
438
444
|
options.sql ??
|
|
439
445
|
wrapBunClient(
|
|
440
|
-
options.client ??
|
|
441
|
-
(
|
|
442
|
-
options.url ?? process.env.DATABASE_URL ?? "postgres://localhost:5432/oke",
|
|
443
|
-
) as unknown as BunJournalClient),
|
|
446
|
+
(options.client ??
|
|
447
|
+
sharedPostgresClient(resolvePostgresUrl(options.url))) as PostgresClientLike,
|
|
444
448
|
);
|
|
445
449
|
|
|
446
450
|
await ensureSchema(sql);
|
|
@@ -2,7 +2,13 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
import {
|
|
3
3
|
buildAlterPolicySql,
|
|
4
4
|
buildCreatePolicySql,
|
|
5
|
+
buildRlsIdentityPreludeSql,
|
|
6
|
+
emitPgPolicySource,
|
|
7
|
+
formatPolicyRole,
|
|
8
|
+
OKE_RLS_HELPER_STATEMENTS,
|
|
9
|
+
OKE_RLS_ROLE,
|
|
5
10
|
parseSqlPolicySpec,
|
|
11
|
+
rlsScopesJson,
|
|
6
12
|
sqlPolicyRowId,
|
|
7
13
|
} from "./pg-rls.ts";
|
|
8
14
|
|
|
@@ -36,6 +42,22 @@ describe("parseSqlPolicySpec", () => {
|
|
|
36
42
|
expect(spec.withCheck).toBe("true");
|
|
37
43
|
});
|
|
38
44
|
|
|
45
|
+
test("accepts Drizzle as/to/for aliases", () => {
|
|
46
|
+
const spec = parseSqlPolicySpec({
|
|
47
|
+
name: "gate_read",
|
|
48
|
+
table: "bookings",
|
|
49
|
+
for: "select",
|
|
50
|
+
as: "permissive",
|
|
51
|
+
to: "current_user",
|
|
52
|
+
using: "oke.gate() = 'member'",
|
|
53
|
+
});
|
|
54
|
+
expect(spec.command).toBe("SELECT");
|
|
55
|
+
expect(spec.behavior).toBe("PERMISSIVE");
|
|
56
|
+
expect(spec.roles).toEqual(["current_user"]);
|
|
57
|
+
expect(buildCreatePolicySql(spec)).toContain("TO current_user");
|
|
58
|
+
expect(buildCreatePolicySql(spec)).not.toContain('"current_user"');
|
|
59
|
+
});
|
|
60
|
+
|
|
39
61
|
test("rejects stacked statements in USING", () => {
|
|
40
62
|
expect(() =>
|
|
41
63
|
parseSqlPolicySpec({
|
|
@@ -69,3 +91,68 @@ describe("buildAlterPolicySql", () => {
|
|
|
69
91
|
).toThrow("invalid USING");
|
|
70
92
|
});
|
|
71
93
|
});
|
|
94
|
+
|
|
95
|
+
describe("formatPolicyRole", () => {
|
|
96
|
+
test("leaves Drizzle special roles unquoted", () => {
|
|
97
|
+
expect(formatPolicyRole("public")).toBe("public");
|
|
98
|
+
expect(formatPolicyRole("current_user")).toBe("current_user");
|
|
99
|
+
expect(formatPolicyRole("member")).toBe('"member"');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("emitPgPolicySource", () => {
|
|
104
|
+
test("emits drizzle pgPolicy with oke helpers", () => {
|
|
105
|
+
const spec = parseSqlPolicySpec({
|
|
106
|
+
name: "gate_member_select",
|
|
107
|
+
table: "bookings",
|
|
108
|
+
command: "SELECT",
|
|
109
|
+
using: "oke.gate() = 'member'",
|
|
110
|
+
});
|
|
111
|
+
expect(emitPgPolicySource(spec)).toContain('pgPolicy("gate_member_select"');
|
|
112
|
+
expect(emitPgPolicySource(spec)).toContain('for: "select"');
|
|
113
|
+
expect(emitPgPolicySource(spec)).toContain("oke.gate() = 'member'");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("rlsScopesJson", () => {
|
|
118
|
+
test("sorts unique scopes", () => {
|
|
119
|
+
expect(rlsScopesJson(["booking:create", "member", "member"])).toBe(
|
|
120
|
+
'["booking:create","member"]',
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("buildRlsIdentityPreludeSql", () => {
|
|
126
|
+
test("emits one statement per exec — never a multi-command batch", () => {
|
|
127
|
+
const stmts = buildRlsIdentityPreludeSql({
|
|
128
|
+
gate: "member",
|
|
129
|
+
userId: "alice",
|
|
130
|
+
scopes: ["member"],
|
|
131
|
+
});
|
|
132
|
+
expect(stmts.map((s) => s.sql)).toEqual([
|
|
133
|
+
`SET LOCAL ROLE ${OKE_RLS_ROLE}`,
|
|
134
|
+
"SET LOCAL row_security = on",
|
|
135
|
+
"SELECT set_config('oke.gate', ?, true), set_config('oke.user', ?, true), set_config('oke.scopes', ?, true)",
|
|
136
|
+
]);
|
|
137
|
+
for (const stmt of stmts) {
|
|
138
|
+
expect(stmt.sql.includes(";")).toBe(false);
|
|
139
|
+
}
|
|
140
|
+
expect(stmts[2]?.params).toEqual(["member", "alice", '["member"]']);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("OKE_RLS_HELPER_STATEMENTS", () => {
|
|
145
|
+
test("has_scope uses jsonb_exists so ? is not a placeholder", () => {
|
|
146
|
+
const hasScope = OKE_RLS_HELPER_STATEMENTS.find((s) => s.includes("oke.has_scope"));
|
|
147
|
+
expect(hasScope).toBeDefined();
|
|
148
|
+
expect(hasScope).toContain("jsonb_exists(");
|
|
149
|
+
expect(hasScope).not.toMatch(/jsonb\s+\?/);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("pins search_path to public so schema oke is not the table home", () => {
|
|
153
|
+
expect(OKE_RLS_HELPER_STATEMENTS).toContain("SET search_path TO public, oke");
|
|
154
|
+
expect(
|
|
155
|
+
OKE_RLS_HELPER_STATEMENTS.some((s) => s.includes("ALTER TABLE oke.%I SET SCHEMA public")),
|
|
156
|
+
).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
});
|