okengine 0.22.0 → 0.23.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/AGENTS.md +7 -0
- package/manifest.v1.schema.json +44 -0
- package/package.json +32 -19
- package/site/content/docs/ai/mcp.mdx +14 -10
- package/site/content/docs/ai/skills.mdx +1 -1
- package/site/content/docs/elements/ai/agents.mdx +61 -13
- package/site/content/docs/elements/ai/decide.mdx +536 -0
- package/site/content/docs/elements/ai/events.mdx +373 -0
- package/site/content/docs/elements/ai/index.mdx +9 -3
- package/site/content/docs/elements/ai/meta.json +1 -1
- package/site/content/docs/elements/ai/prompts.mdx +4 -2
- package/site/content/docs/elements/channel/index.mdx +4 -4
- package/site/content/docs/elements/channel/receipts.mdx +7 -5
- package/site/content/docs/elements/channel/sms.mdx +2 -1
- package/site/content/docs/elements/flow/index.mdx +22 -18
- package/site/content/docs/elements/gate/auth.mdx +4 -1
- package/site/content/docs/elements/signal/index.mdx +15 -14
- package/site/content/docs/elements/store/index.mdx +1 -1
- package/site/content/docs/elements/store/kv.mdx +5 -5
- package/site/content/docs/plugins/otp.mdx +3 -3
- package/site/content/docs/recipes/dragonfly.mdx +5 -5
- package/site/content/docs/recipes/index.mdx +6 -6
- package/site/content/docs/recipes/postgres.mdx +12 -8
- package/site/content/docs/recipes/redis.mdx +4 -4
- package/site/content/docs/reference/cli.mdx +2 -1
- package/site/content/docs/reference/configuration.mdx +9 -7
- package/site/content/docs/reference/fx.mdx +19 -10
- package/src/auth/api-key-sql.test.ts +44 -0
- package/src/auth/api-key-sql.ts +1 -0
- package/src/auth/api-keys.ts +30 -12
- package/src/auth/config.ts +6 -2
- package/src/auth/gate-auth.test.ts +20 -0
- package/src/auth/tenants.ts +17 -0
- package/src/cli/agents-md.test.ts +25 -0
- package/src/cli/ask-vault-gaps.test.ts +38 -1
- package/src/cli/ask-vault-gaps.ts +35 -2
- package/src/cli/decide.ts +174 -0
- package/src/cli/decision-lock-watch.test.ts +45 -0
- package/src/cli/decision-lock-watch.ts +51 -0
- package/src/cli/dev-app-runner.ts +34 -8
- package/src/cli/dev-auth-secret.test.ts +49 -0
- package/src/cli/dev-auth-secret.ts +49 -0
- package/src/cli/dev-hot.test.ts +41 -0
- package/src/cli/dev-hot.ts +48 -0
- package/src/cli/dev.test.ts +1 -1
- package/src/cli/dev.ts +24 -6
- package/src/cli/doctor-diff.test.ts +29 -1
- package/src/cli/doctor-diff.ts +35 -0
- package/src/cli/eval.ts +142 -1
- package/src/cli/index.ts +5 -0
- package/src/cli/load-config.images.test.ts +2 -2
- package/src/cli/load-config.ts +1 -1
- package/src/cli/manifest-pr-diff.ts +16 -0
- package/src/cli/mcp-from-console.ts +48 -0
- package/src/cli/registry.ts +35 -1
- package/src/client/agent.ts +307 -0
- package/src/client/transport.test.ts +20 -0
- package/src/client/transport.ts +2 -1
- package/src/client/types.ts +1 -1
- package/src/client-react/index.ts +7 -1
- package/src/client-react/use-agent-run.test.ts +271 -0
- package/src/client-react/use-agent-run.ts +193 -0
- package/src/compiler/ai-approval.test.ts +36 -0
- package/src/compiler/ai-repair.test.ts +22 -0
- package/src/compiler/decisions.extract.test.ts +164 -0
- package/src/compiler/effects-fetch.test.ts +77 -0
- package/src/compiler/effects-infer.ts +114 -17
- package/src/compiler/effects-join.test.ts +48 -0
- package/src/compiler/extract.test.ts +28 -0
- package/src/compiler/extract.ts +279 -13
- package/src/compiler/fx-index.ts +58 -4
- package/src/compiler/response.ts +35 -2
- package/src/config/index.ts +21 -3
- package/src/console/index.ts +1 -1
- package/src/console/server/ai-runs-flows.ts +269 -0
- package/src/console/server/ai-runs.test.ts +323 -0
- package/src/console/server/ai-runs.ts +562 -0
- package/src/console/server/ai.ts +12 -1
- package/src/console/server/app.ts +10 -1
- package/src/console/server/decisions-flows.ts +157 -0
- package/src/console/server/decisions.test.ts +66 -0
- package/src/console/server/decisions.ts +229 -0
- package/src/console/server/flows.ts +50 -2
- package/src/console/server/invoke-user-flow.ts +9 -0
- package/src/console/server/runs-ingest.ts +16 -0
- package/src/console/server/serve.ts +5 -0
- package/src/console/server/state.ts +25 -1
- package/src/console/server/store-stats.test.ts +47 -0
- package/src/console/server/store-stats.ts +92 -18
- package/src/console/ui-next/dist/assets/access-page-C_N4qvai.js +4 -0
- package/src/console/ui-next/dist/assets/{agent-disclosure-Bkohc_8X.js → agent-disclosure-Cq1TxniW.js} +1 -1
- package/src/console/ui-next/dist/assets/{cache-glyph-rL0lHxat.js → cache-glyph-BaI4uxZz.js} +1 -1
- package/src/console/ui-next/dist/assets/{call-pii-button-Deo4PP18.js → call-pii-button-DNyK7IIY.js} +1 -1
- package/src/console/ui-next/dist/assets/{collapsible-CoJ6amHf.js → collapsible-CRhBJMZR.js} +1 -1
- package/src/console/ui-next/dist/assets/confirm-sheet-DZfuJXP4.js +1 -0
- package/src/console/ui-next/dist/assets/copy-inline-button-BTjPVjRT.js +1 -0
- package/src/console/ui-next/dist/assets/decisions-page-D9twj8Xy.js +1 -0
- package/src/console/ui-next/dist/assets/detail-header-Dk5LipF6.js +1 -0
- package/src/console/ui-next/dist/assets/dropdown-menu-CRGrj5JU.js +1 -0
- package/src/console/ui-next/dist/assets/duration-tone-CsHWlVPU.js +9 -0
- package/src/console/ui-next/dist/assets/element-icons-C6t8Vlml.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-chrome-Dslh2yE8.js +1 -0
- package/src/console/ui-next/dist/assets/explorer-empty-CIVg7Jgb.js +1 -0
- package/src/console/ui-next/dist/assets/flows-page-ZaTJAhR9.js +1 -0
- package/src/console/ui-next/dist/assets/{highlighted-json-CPYBzDh0.js → highlighted-json-DQ-EMAfz.js} +1 -1
- package/src/console/ui-next/dist/assets/{http-method-DTPevKmM.js → http-method-Cn_Fl79s.js} +1 -1
- package/src/console/ui-next/dist/assets/index-BlrN49Hs.css +2 -0
- package/src/console/ui-next/dist/assets/index-CmRFeWdT.js +58 -0
- package/src/console/ui-next/dist/assets/observability-page-Cn6R_dXz.js +8 -0
- package/src/console/ui-next/dist/assets/{replica-lag-BM27lXld.js → replica-lag-Bx6EZhSe.js} +7 -7
- package/src/console/ui-next/dist/assets/request-meta-DmdiUrfh.js +1 -0
- package/src/console/ui-next/dist/assets/shortcut-keys-DTE1sjTC.js +1 -0
- package/src/console/ui-next/dist/assets/store-page-8P6vGXnn.js +41 -0
- package/src/console/ui-next/dist/assets/trace-detail-sheet-BfGvpFSv.js +2 -0
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CWdUHbc0.js +55 -0
- package/src/console/ui-next/dist/assets/units-page-CZOFZzay.js +1 -0
- package/src/console/ui-next/dist/assets/use-vault-list-BAyQG8Re.js +1 -0
- package/src/console/ui-next/dist/assets/vault-page-ki_ishmX.js +2 -0
- package/src/console/ui-next/dist/index.html +4 -3
- package/src/console/ui-next/seed-parked-approval.ts +126 -0
- package/src/console/ui-next/src/client.ts +224 -1
- package/src/console/ui-next/src/components/explorer/detail-header.tsx +9 -0
- package/src/console/ui-next/src/components/explorer/explorer-start-toggle.tsx +5 -1
- package/src/console/ui-next/src/components/ui/sheet-form.tsx +21 -12
- package/src/console/ui-next/src/features/flows/decisions/decisions-page.tsx +216 -0
- package/src/console/ui-next/src/features/flows/decisions/resolve.test.ts +48 -0
- package/src/console/ui-next/src/features/flows/decisions/resolve.ts +111 -0
- package/src/console/ui-next/src/features/flows/flows-page.tsx +10 -0
- package/src/console/ui-next/src/features/flows/graph/element-map.ts +1 -0
- package/src/console/ui-next/src/features/flows/traces/effect-kind.ts +6 -0
- package/src/console/ui-next/src/features/flows/traces/effect-summary.test.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/effect-summary.ts +24 -0
- package/src/console/ui-next/src/features/flows/traces/http-status.ts +58 -0
- package/src/console/ui-next/src/features/flows/traces/request-client-card.tsx +355 -0
- package/src/console/ui-next/src/features/flows/traces/request-client.test.ts +114 -0
- package/src/console/ui-next/src/features/flows/traces/request-client.ts +423 -0
- package/src/console/ui-next/src/features/flows/traces/request-input-view.test.ts +52 -6
- package/src/console/ui-next/src/features/flows/traces/request-input-view.ts +85 -19
- package/src/console/ui-next/src/features/flows/traces/trace-detail-sheet.tsx +505 -80
- package/src/console/ui-next/src/features/flows/traces/trace-lanes.test.ts +61 -0
- package/src/console/ui-next/src/features/flows/traces/trace-lanes.ts +78 -0
- package/src/console/ui-next/src/features/flows/traces/trace-nav.test.ts +48 -0
- package/src/console/ui-next/src/features/flows/traces/trace-nav.ts +57 -0
- package/src/console/ui-next/src/features/flows/traces/trace-request-section.tsx +275 -96
- package/src/console/ui-next/src/features/flows/traces/traces-pane.tsx +16 -2
- package/src/console/ui-next/src/features/observability/detail/ai-runs.tsx +369 -0
- package/src/console/ui-next/src/features/observability/observability-page.tsx +31 -1
- package/src/console/ui-next/src/features/observability/state/observability-selection.ts +18 -3
- package/src/console/ui-next/src/features/store/performance/kv-performance-panel.tsx +12 -1
- package/src/console/ui-next/src/features/store/performance/performance-panel.tsx +14 -1
- package/src/console/ui-next/src/features/units/call/call-api-panel.tsx +1 -1
- package/src/console/ui-next/src/features/units/detail/flow-contract-panel.tsx +6 -2
- package/src/console/ui-next/src/features/units/explorer/units-tree.tsx +73 -18
- package/src/console/ui-next/src/features/units/lib/unit-tree.test.ts +21 -0
- package/src/console/ui-next/src/features/units/lib/unit-tree.ts +82 -0
- package/src/console/ui-next/src/features/units/units-page.tsx +25 -7
- package/src/console/ui-next/src/router.tsx +17 -0
- package/src/console/ui-next/ui-next-seed-runs.ts +23 -0
- package/src/console/ui-next/vite-console-kernel-plugin.ts +43 -3
- package/src/console/ui-next/vite.config.ts +9 -0
- package/src/docker/recipes/dragonfly.ts +3 -2
- package/src/docker/recipes/redis.ts +1 -1
- package/src/drivers/ai-anthropic.ts +212 -21
- package/src/drivers/ai-mock.ts +34 -1
- package/src/drivers/ai-openai-compatible.ts +67 -26
- package/src/drivers/ai-preconnect.ts +18 -0
- package/src/drivers/ai-stream.test.ts +62 -0
- package/src/drivers/ai-types.ts +20 -0
- package/src/drivers/journal-postgres.test.ts +22 -1
- package/src/drivers/journal-postgres.ts +511 -13
- package/src/drivers/memory-ddl.test.ts +18 -0
- package/src/drivers/memory.ts +21 -1
- package/src/drivers/pg-rls.test.ts +31 -0
- package/src/drivers/pg-rls.ts +15 -0
- package/src/drivers/postgres.test.ts +9 -0
- package/src/drivers/postgres.ts +49 -11
- package/src/drivers/signal-compete.test.ts +234 -0
- package/src/drivers/signal-postgres.ts +39 -19
- package/src/drivers/signal-redis.ts +167 -11
- package/src/drivers/signal-types.ts +12 -0
- package/src/elements/ai/agent-event-slot.ts +27 -0
- package/src/elements/ai/approval-http.ts +207 -0
- package/src/elements/ai/approval.test.ts +741 -0
- package/src/elements/ai/approval.ts +228 -0
- package/src/elements/ai/decisions/bind.ts +163 -0
- package/src/elements/ai/decisions/certificate.test.ts +291 -0
- package/src/elements/ai/decisions/certificate.ts +377 -0
- package/src/elements/ai/decisions/certify.ts +314 -0
- package/src/elements/ai/decisions/decide.test.ts +953 -0
- package/src/elements/ai/decisions/e2e.test.ts +399 -0
- package/src/elements/ai/decisions/export.test.ts +120 -0
- package/src/elements/ai/decisions/export.ts +146 -0
- package/src/elements/ai/decisions/fixtures/openrouter-request.json +24 -0
- package/src/elements/ai/decisions/fixtures/openrouter-response.json +22 -0
- package/src/elements/ai/decisions/fixtures/typesafe-request.json +19 -0
- package/src/elements/ai/decisions/fixtures/typesafe-response.json +13 -0
- package/src/elements/ai/decisions/http.test.ts +154 -0
- package/src/elements/ai/decisions/http.ts +240 -0
- package/src/elements/ai/decisions/labels.test.ts +282 -0
- package/src/elements/ai/decisions/labels.ts +257 -0
- package/src/elements/ai/decisions/openrouter.live.test.ts +136 -0
- package/src/elements/ai/decisions/openrouter.ts +36 -0
- package/src/elements/ai/decisions/provider.ts +102 -0
- package/src/elements/ai/decisions/typesafe.ts +36 -0
- package/src/elements/ai/declare.ts +252 -1
- package/src/elements/ai/events.test.ts +379 -0
- package/src/elements/ai/events.ts +138 -0
- package/src/elements/ai/mcp-protocol.ts +5 -6
- package/src/elements/ai/run-events.test.ts +461 -0
- package/src/elements/ai/run-events.ts +522 -0
- package/src/elements/ai/runtime.ts +1187 -105
- package/src/elements/ai/stream-turn.ts +214 -0
- package/src/elements/ai/stream.live.test.ts +76 -0
- package/src/elements/ai/subagent.test.ts +136 -0
- package/src/elements/ai.test.ts +497 -3
- package/src/elements/ai.ts +16 -1
- package/src/elements/channel/runtime.ts +11 -0
- package/src/elements/channel/sql-ledger.ts +251 -0
- package/src/elements/clock/durable.ts +27 -1
- package/src/elements/signal/runtime.ts +9 -0
- package/src/elements/signal.test.ts +1 -0
- package/src/elements/store/cache.test.ts +120 -15
- package/src/elements/store/cache.ts +200 -39
- package/src/elements/store/prepare-row.test.ts +6 -2
- package/src/elements/store/sql-session.test.ts +53 -0
- package/src/elements/store/sql-session.ts +206 -10
- package/src/elements/store.ts +1 -0
- package/src/i18n/catalogs/ar.ts +5 -0
- package/src/i18n/catalogs/en.ts +5 -0
- package/src/index.ts +2 -0
- package/src/kernel/agent-event-store.ts +448 -0
- package/src/kernel/app.ts +135 -17
- package/src/kernel/auto-cache.test.ts +228 -0
- package/src/kernel/boot-bind/ai.ts +3 -0
- package/src/kernel/boot-bind/channel.ts +63 -2
- package/src/kernel/boot-bind/honor-config.test.ts +40 -10
- package/src/kernel/boot-bind/signal.ts +61 -25
- package/src/kernel/boot-bind/store.test.ts +26 -0
- package/src/kernel/boot-bind/store.ts +82 -6
- package/src/kernel/boot.ts +31 -6
- package/src/kernel/builtin-errors.ts +2 -0
- package/src/kernel/capability.ts +3 -0
- package/src/kernel/decision-label-store.ts +441 -0
- package/src/kernel/effects.test.ts +2 -1
- package/src/kernel/effects.ts +13 -1
- package/src/kernel/element-registries.ts +3 -0
- package/src/kernel/errors-compiler.ts +11 -0
- package/src/kernel/errors-text.ts +20 -0
- package/src/kernel/errors.ts +10 -0
- package/src/kernel/flow.test.ts +2 -3
- package/src/kernel/flow.ts +33 -3
- package/src/kernel/fx-call-types.test.ts +32 -0
- package/src/kernel/fx-decide.ts +765 -0
- package/src/kernel/fx-sql-handle.ts +46 -7
- package/src/kernel/fx.test.ts +13 -8
- package/src/kernel/fx.ts +339 -45
- package/src/kernel/http-frame.test.ts +94 -0
- package/src/kernel/http-frame.ts +203 -0
- package/src/kernel/idempotency.test.ts +2 -1
- package/src/kernel/journal.test.ts +114 -0
- package/src/kernel/journal.ts +471 -51
- package/src/kernel/json-result.ts +10 -0
- package/src/kernel/pipeline-tenant.ts +5 -1
- package/src/kernel/pipeline.test.ts +45 -0
- package/src/kernel/pipeline.ts +5 -0
- package/src/kernel/signal-tx.ts +28 -0
- package/src/kernel/sse-id.ts +32 -0
- package/src/kernel/store-transaction.test.ts +95 -0
- package/src/manifest/diff.ts +1 -0
- package/src/manifest/types.ts +29 -2
- package/src/mcp/ai-tools.test.ts +202 -0
- package/src/mcp/authorization.ts +66 -0
- package/src/mcp/docs-server.ts +19 -2
- package/src/mcp/mcp.test.ts +83 -0
- package/src/mcp/protocol.ts +8 -2
- package/src/mcp/server.ts +23 -2
- package/src/mcp/session.ts +3 -0
- package/src/mcp/tools.ts +168 -0
- package/src/mcp/versions.ts +60 -0
- package/src/plugins/index.ts +1 -0
- package/src/plugins/mena.ts +56 -0
- package/src/release/http-graph.test.ts +2 -0
- package/src/release/http-graph.ts +5 -0
- package/src/release/measure.ts +39 -14
- package/src/runs/collect.ts +4 -1
- package/src/runs/parquet.test.ts +27 -1
- package/src/runs/parquet.ts +19 -0
- package/src/runs/types.ts +31 -0
- package/src/runtime/json-code-block.test.ts +106 -47
- package/src/runtime/json-code-block.ts +1209 -46
- package/src/term.test.ts +13 -0
- package/src/term.ts +81 -49
- package/src/console/ui-next/dist/assets/PlusSignIcon-CwG3nxfu.js +0 -1
- package/src/console/ui-next/dist/assets/access-page-Ze6ckjdd.js +0 -4
- package/src/console/ui-next/dist/assets/copy-inline-button-CWG5_ZOh.js +0 -1
- package/src/console/ui-next/dist/assets/detail-header-b_ZzIsmU.js +0 -1
- package/src/console/ui-next/dist/assets/dropdown-menu-nwXEO1Ac.js +0 -1
- package/src/console/ui-next/dist/assets/duration-tone-BbQ_8z50.js +0 -9
- package/src/console/ui-next/dist/assets/element-icons-CwyLpVXz.js +0 -1
- package/src/console/ui-next/dist/assets/explorer-empty-C8sSCqYT.js +0 -1
- package/src/console/ui-next/dist/assets/flows-page-D2E7BtKK.js +0 -1
- package/src/console/ui-next/dist/assets/index-VxoEz295.css +0 -2
- package/src/console/ui-next/dist/assets/index-vTuwmeQz.js +0 -58
- package/src/console/ui-next/dist/assets/observability-page-SJTSzHFJ.js +0 -4
- package/src/console/ui-next/dist/assets/request-meta-BjFT7DNl.js +0 -1
- package/src/console/ui-next/dist/assets/shortcut-keys-CUNegEV4.js +0 -1
- package/src/console/ui-next/dist/assets/store-page-Qj9ThRE-.js +0 -41
- package/src/console/ui-next/dist/assets/trace-detail-sheet-Cmitq3da.js +0 -2
- package/src/console/ui-next/dist/assets/tree-expand-toggle-CbDIB-7x.js +0 -55
- package/src/console/ui-next/dist/assets/units-page-_PuYqFty.js +0 -1
- package/src/console/ui-next/dist/assets/use-vault-list-Cs45Czkt.js +0 -1
- package/src/console/ui-next/dist/assets/vault-page-BOMr0go9.js +0 -2
|
@@ -51,6 +51,24 @@ export type { WhereMap } from "./sql-condition.ts";
|
|
|
51
51
|
*/
|
|
52
52
|
export type InferSelectRow<T> = T extends { readonly $inferSelect: infer R } ? R : SqlRow;
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Insert row from a Drizzle table's `$inferInsert`, or {@link SqlRow}.
|
|
56
|
+
*
|
|
57
|
+
* @typeParam T - Table handle or Drizzle table
|
|
58
|
+
*/
|
|
59
|
+
export type InferInsertRow<T> = T extends { readonly $inferInsert: infer R } ? R : SqlRow;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Equality map or a Drizzle SQL fragment (`queryChunks`).
|
|
63
|
+
* A bare `unknown` would swallow the row type.
|
|
64
|
+
*
|
|
65
|
+
* @typeParam TRow - Selected or inserted row
|
|
66
|
+
*/
|
|
67
|
+
export type SqlPredicate<TRow> =
|
|
68
|
+
| { readonly [K in keyof TRow]?: TRow[K] }
|
|
69
|
+
| { readonly queryChunks: readonly unknown[] }
|
|
70
|
+
| undefined;
|
|
71
|
+
|
|
54
72
|
/** Serialize stamp frames on a shared connection so concurrent identities cannot interleave. */
|
|
55
73
|
const rlsStampTails = new WeakMap<SqlConnection, Promise<unknown>>();
|
|
56
74
|
|
|
@@ -119,6 +137,9 @@ function notifySqlCdc(event: {
|
|
|
119
137
|
*/
|
|
120
138
|
const cdcMutationStorage = new AsyncLocalStorage<{ readonly mutationId: string }>();
|
|
121
139
|
|
|
140
|
+
/** Pinned connection for {@link SqlStoreHandle.transaction}. */
|
|
141
|
+
const sqlTxStorage = new AsyncLocalStorage<SqlConnection>();
|
|
142
|
+
|
|
122
143
|
/**
|
|
123
144
|
* Read the ambient mutation id, or `undefined` outside a stamped request.
|
|
124
145
|
*/
|
|
@@ -274,7 +295,7 @@ export interface SelectFromBuilder<TRow = SqlRow> extends PromiseLike<TRow[]> {
|
|
|
274
295
|
*
|
|
275
296
|
* @param where - Condition
|
|
276
297
|
*/
|
|
277
|
-
where(where:
|
|
298
|
+
where(where: SqlPredicate<TRow>): SelectWhereBuilder<TRow>;
|
|
278
299
|
/**
|
|
279
300
|
* Order rows with Drizzle `asc()` / `desc()` terms.
|
|
280
301
|
*
|
|
@@ -312,14 +333,18 @@ export interface SelectBuilder<TLocked extends SqlRow | undefined = undefined> {
|
|
|
312
333
|
): SelectFromBuilder<TLocked extends undefined ? InferSelectRow<TTable> : TLocked>;
|
|
313
334
|
}
|
|
314
335
|
|
|
315
|
-
/**
|
|
316
|
-
|
|
336
|
+
/**
|
|
337
|
+
* Fluent insert builder.
|
|
338
|
+
*
|
|
339
|
+
* @typeParam TRow - `$inferInsert` when the table is a Drizzle table
|
|
340
|
+
*/
|
|
341
|
+
export interface InsertBuilder<TRow = SqlRow> {
|
|
317
342
|
/**
|
|
318
343
|
* Provide row values.
|
|
319
344
|
*
|
|
320
345
|
* @param row - Row to insert
|
|
321
346
|
*/
|
|
322
|
-
values(row:
|
|
347
|
+
values(row: TRow): InsertValuesBuilder;
|
|
323
348
|
}
|
|
324
349
|
|
|
325
350
|
/**
|
|
@@ -340,7 +365,7 @@ export interface DeleteBuilder {
|
|
|
340
365
|
*
|
|
341
366
|
* @param where - Equality map or Drizzle SQL
|
|
342
367
|
*/
|
|
343
|
-
where(where:
|
|
368
|
+
where(where: SqlPredicate<SqlRow>): Promise<number>;
|
|
344
369
|
}
|
|
345
370
|
|
|
346
371
|
/** Fluent update builder. */
|
|
@@ -360,7 +385,7 @@ export interface UpdateSetBuilder {
|
|
|
360
385
|
*
|
|
361
386
|
* @param where - Equality map or Drizzle SQL
|
|
362
387
|
*/
|
|
363
|
-
where(where:
|
|
388
|
+
where(where: SqlPredicate<SqlRow>): Promise<number>;
|
|
364
389
|
}
|
|
365
390
|
|
|
366
391
|
/**
|
|
@@ -393,7 +418,7 @@ export interface SqlStoreHandle {
|
|
|
393
418
|
*
|
|
394
419
|
* @param table - Target table
|
|
395
420
|
*/
|
|
396
|
-
insert(table:
|
|
421
|
+
insert<TTable>(table: TTable): InsertBuilder<InferInsertRow<TTable>>;
|
|
397
422
|
/**
|
|
398
423
|
* Start an update on `table`.
|
|
399
424
|
*
|
|
@@ -415,6 +440,22 @@ export interface SqlStoreHandle {
|
|
|
415
440
|
*/
|
|
416
441
|
delete(table: TableHandle | unknown): DeleteBuilder;
|
|
417
442
|
delete(table: TableHandle | unknown, id: string): Promise<boolean>;
|
|
443
|
+
/**
|
|
444
|
+
* Run `fn` on one pinned connection. `fx.emit` inside `fn` stages on the
|
|
445
|
+
* signal outbox and publishes only after this SQL transaction commits.
|
|
446
|
+
*
|
|
447
|
+
* @param fn - Transaction body. `tx` is this handle, bound to the pin.
|
|
448
|
+
*/
|
|
449
|
+
transaction<T>(fn: (tx: SqlStoreHandle) => Promise<T>): Promise<T>;
|
|
450
|
+
/**
|
|
451
|
+
* Execute a Drizzle select, insert, update, or delete builder.
|
|
452
|
+
* Calls `toSQL()` so `drizzle-orm` stays off the edge graph.
|
|
453
|
+
*
|
|
454
|
+
* @param query - Builder with `toSQL()`
|
|
455
|
+
*/
|
|
456
|
+
run<T extends SqlRow = SqlRow>(query: {
|
|
457
|
+
toSQL(): { readonly sql: string; readonly params: readonly unknown[] };
|
|
458
|
+
}): Promise<T[]>;
|
|
418
459
|
/**
|
|
419
460
|
* True when at least one row matches.
|
|
420
461
|
*
|
|
@@ -424,7 +465,9 @@ export interface SqlStoreHandle {
|
|
|
424
465
|
exists(table: TableHandle | unknown, idOrWhere: string | WhereMap): Promise<boolean>;
|
|
425
466
|
/**
|
|
426
467
|
* Insert when no row matches `matchOn`; never touches an existing match
|
|
427
|
-
* unless `options.onExisting` is `"update"`.
|
|
468
|
+
* unless `options.onExisting` is `"update"`. A match that is only the
|
|
469
|
+
* primary key is one `INSERT … ON CONFLICT` round trip on postgres and
|
|
470
|
+
* pglite. Memory SQL, and any other predicate, stays a read then a write.
|
|
428
471
|
*
|
|
429
472
|
* @param table - Table
|
|
430
473
|
* @param matchOn - Equality map or Drizzle condition identifying the row
|
|
@@ -562,6 +605,10 @@ export function createSqlStoreHandle(
|
|
|
562
605
|
}
|
|
563
606
|
}
|
|
564
607
|
|
|
608
|
+
function activeConnection(): SqlConnection {
|
|
609
|
+
return sqlTxStorage.getStore() ?? connection;
|
|
610
|
+
}
|
|
611
|
+
|
|
565
612
|
function query(sql: string, params: readonly unknown[] = []): Promise<SqlRow[]> {
|
|
566
613
|
return withSchemaGuard(() => withRlsStamp((conn) => conn.query(sql, params), sql));
|
|
567
614
|
}
|
|
@@ -570,9 +617,29 @@ export function createSqlStoreHandle(
|
|
|
570
617
|
return withSchemaGuard(() => withRlsStamp((conn) => conn.exec(sql, params), sql));
|
|
571
618
|
}
|
|
572
619
|
|
|
620
|
+
/**
|
|
621
|
+
* Rewrite Drizzle `$1` placeholders to the connection's `?` form.
|
|
622
|
+
*
|
|
623
|
+
* @param sql - Dialect SQL
|
|
624
|
+
* @param params - Bindings in `$n` order (1-based)
|
|
625
|
+
*/
|
|
626
|
+
function fromDialect(
|
|
627
|
+
sql: string,
|
|
628
|
+
params: readonly unknown[],
|
|
629
|
+
): { sql: string; params: unknown[] } {
|
|
630
|
+
if (!sql.includes("$")) return { sql, params: [...params] };
|
|
631
|
+
const ordered: unknown[] = [];
|
|
632
|
+
const text = sql.replace(/\$(\d+)/g, (_match, index: string) => {
|
|
633
|
+
ordered.push(params[Number(index) - 1]);
|
|
634
|
+
return "?";
|
|
635
|
+
});
|
|
636
|
+
return { sql: text, params: ordered };
|
|
637
|
+
}
|
|
638
|
+
|
|
573
639
|
async function withRlsStamp<T>(fn: (conn: SqlConnection) => Promise<T>, sql: string): Promise<T> {
|
|
574
|
-
if (!rls || !RLS_CONTEXT_DRIVERS.has(connection.driverId)) return fn(
|
|
575
|
-
if (isRlsStampExemptSql(sql)) return fn(
|
|
640
|
+
if (!rls || !RLS_CONTEXT_DRIVERS.has(connection.driverId)) return fn(activeConnection());
|
|
641
|
+
if (isRlsStampExemptSql(sql)) return fn(activeConnection());
|
|
642
|
+
if (sqlTxStorage.getStore()) return fn(activeConnection());
|
|
576
643
|
const run = (): Promise<T> => applyRlsStamp(fn);
|
|
577
644
|
// PGlite is one backend session — concurrent identities must not interleave.
|
|
578
645
|
// Pooled postgres pins each stamp via `transaction()` instead.
|
|
@@ -685,6 +752,101 @@ export function createSqlStoreHandle(
|
|
|
685
752
|
return coerceCompiledWhere(table, compileWhere(where));
|
|
686
753
|
}
|
|
687
754
|
|
|
755
|
+
/**
|
|
756
|
+
* Primary-key equality only. Any other predicate cannot use `ON CONFLICT`.
|
|
757
|
+
*
|
|
758
|
+
* @param compiled - Match clause
|
|
759
|
+
* @param pkCol - Primary key SQL name
|
|
760
|
+
*/
|
|
761
|
+
function primaryKeyEquality(
|
|
762
|
+
compiled: ReturnType<typeof compileWhere>,
|
|
763
|
+
pkCol: string,
|
|
764
|
+
): { readonly value: unknown } | undefined {
|
|
765
|
+
if (compiled.predicates.length !== 1) return undefined;
|
|
766
|
+
const pred = compiled.predicates[0];
|
|
767
|
+
if (!pred || pred.op !== "=" || pred.column !== pkCol) return undefined;
|
|
768
|
+
return { value: pred.value };
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* One round trip: `INSERT … ON CONFLICT (pk)`.
|
|
773
|
+
*
|
|
774
|
+
* `DO NOTHING` when the existing row must stay. `DO UPDATE` captures the
|
|
775
|
+
* pre-image in a CTE so CDC still sees before and after.
|
|
776
|
+
*
|
|
777
|
+
* @param table - Table
|
|
778
|
+
* @param name - SQL table name
|
|
779
|
+
* @param pkCol - Primary key SQL name
|
|
780
|
+
* @param pkValue - Bound primary key
|
|
781
|
+
* @param values - Insert / update payload
|
|
782
|
+
* @param onExisting - `"update"` rewrites the existing row
|
|
783
|
+
*/
|
|
784
|
+
async function upsertOnPrimaryKey(
|
|
785
|
+
table: TableHandle | unknown,
|
|
786
|
+
name: string,
|
|
787
|
+
pkCol: string,
|
|
788
|
+
pkValue: unknown,
|
|
789
|
+
values: SqlRow,
|
|
790
|
+
onExisting: "update" | undefined,
|
|
791
|
+
): Promise<UpsertResult> {
|
|
792
|
+
const prepared = prepareInsertRow(table, values);
|
|
793
|
+
if (!(pkCol in prepared)) prepared[pkCol] = pkValue;
|
|
794
|
+
const cols = Object.keys(prepared);
|
|
795
|
+
const colList = cols.map(quoteIdent).join(", ");
|
|
796
|
+
const placeholders = cols.map(() => "?").join(", ");
|
|
797
|
+
const params = cols.map((col) => prepared[col]);
|
|
798
|
+
const quotedPk = quoteIdent(pkCol);
|
|
799
|
+
const quotedName = quoteIdent(name);
|
|
800
|
+
const setEntries =
|
|
801
|
+
onExisting === "update"
|
|
802
|
+
? Object.entries(prepareUpdateRow(table, values)).filter(([col]) => col !== pkCol)
|
|
803
|
+
: [];
|
|
804
|
+
if (onExisting !== "update" || setEntries.length === 0) {
|
|
805
|
+
const rows = await query(
|
|
806
|
+
`INSERT INTO ${quotedName} (${colList}) VALUES (${placeholders})` +
|
|
807
|
+
` ON CONFLICT (${quotedPk}) DO NOTHING RETURNING *`,
|
|
808
|
+
params,
|
|
809
|
+
);
|
|
810
|
+
if (rows.length === 0) {
|
|
811
|
+
return { status: onExisting === "update" ? "changed" : "already-existed" };
|
|
812
|
+
}
|
|
813
|
+
notifySqlCdc({
|
|
814
|
+
tableName: name,
|
|
815
|
+
op: "insert",
|
|
816
|
+
before: null,
|
|
817
|
+
after: toJs(table, rows)[0] ?? null,
|
|
818
|
+
});
|
|
819
|
+
return { status: "upserted" };
|
|
820
|
+
}
|
|
821
|
+
const setSql = setEntries
|
|
822
|
+
.map(([col]) => `${quoteIdent(col)} = EXCLUDED.${quoteIdent(col)}`)
|
|
823
|
+
.join(", ");
|
|
824
|
+
// The old row is a CTE, then read from an outer SELECT. INSERT …
|
|
825
|
+
// RETURNING cannot see that CTE on PGlite, so the before image would
|
|
826
|
+
// be null and an update would look like an insert.
|
|
827
|
+
const result = await query(
|
|
828
|
+
`WITH __oke_old AS (` +
|
|
829
|
+
`SELECT * FROM ${quotedName} WHERE ${quotedPk} = ? FOR UPDATE` +
|
|
830
|
+
`), __oke_new AS (` +
|
|
831
|
+
`INSERT INTO ${quotedName} (${colList}) VALUES (${placeholders})` +
|
|
832
|
+
` ON CONFLICT (${quotedPk}) DO UPDATE SET ${setSql}` +
|
|
833
|
+
` RETURNING *` +
|
|
834
|
+
`) SELECT (SELECT row_to_json(__oke_old) FROM __oke_old) AS __oke_before_data,` +
|
|
835
|
+
` (SELECT row_to_json(__oke_new) FROM __oke_new) AS __oke_after_data`,
|
|
836
|
+
[pkValue, ...params],
|
|
837
|
+
);
|
|
838
|
+
const raw = result[0];
|
|
839
|
+
const before = raw ? jsonImageToJs(table, raw.__oke_before_data) : null;
|
|
840
|
+
const after = raw ? jsonImageToJs(table, raw.__oke_after_data) : null;
|
|
841
|
+
notifySqlCdc({
|
|
842
|
+
tableName: name,
|
|
843
|
+
op: before ? "update" : "insert",
|
|
844
|
+
before,
|
|
845
|
+
after,
|
|
846
|
+
});
|
|
847
|
+
return { status: before ? "changed" : "upserted" };
|
|
848
|
+
}
|
|
849
|
+
|
|
688
850
|
/**
|
|
689
851
|
* Postgres `row_to_json` images come back as JSON strings on pglite and
|
|
690
852
|
* objects on Bun.SQL — normalize to the JS row mapping (snake→camel).
|
|
@@ -1026,6 +1188,19 @@ export function createSqlStoreHandle(
|
|
|
1026
1188
|
if (!compiled.clause) {
|
|
1027
1189
|
throw new Error("upsert() requires at least one matchOn predicate");
|
|
1028
1190
|
}
|
|
1191
|
+
const pkCol = resolvePkColumn(table);
|
|
1192
|
+
const pkMatch = primaryKeyEquality(compiled, pkCol);
|
|
1193
|
+
// Memory SQL has no ON CONFLICT. Postgres and PGlite do.
|
|
1194
|
+
if (pkMatch && connection.driverId !== "memory") {
|
|
1195
|
+
return upsertOnPrimaryKey(
|
|
1196
|
+
table,
|
|
1197
|
+
name,
|
|
1198
|
+
pkCol,
|
|
1199
|
+
pkMatch.value,
|
|
1200
|
+
values,
|
|
1201
|
+
upsertOptions?.onExisting,
|
|
1202
|
+
);
|
|
1203
|
+
}
|
|
1029
1204
|
const found = await query(
|
|
1030
1205
|
`SELECT 1 AS "ok" FROM ${quoteIdent(name)} WHERE ${compiled.clause} LIMIT 1`,
|
|
1031
1206
|
compiled.params,
|
|
@@ -1222,6 +1397,27 @@ export function createSqlStoreHandle(
|
|
|
1222
1397
|
});
|
|
1223
1398
|
},
|
|
1224
1399
|
|
|
1400
|
+
async transaction<T>(fn: (tx: SqlStoreHandle) => Promise<T>): Promise<T> {
|
|
1401
|
+
if (!connection.transaction) {
|
|
1402
|
+
throw new Error("fx.store().transaction needs SqlConnection.transaction");
|
|
1403
|
+
}
|
|
1404
|
+
return connection.transaction(async (txConn) =>
|
|
1405
|
+
sqlTxStorage.run(txConn, () => fn(handle as SqlStoreHandle)),
|
|
1406
|
+
);
|
|
1407
|
+
},
|
|
1408
|
+
|
|
1409
|
+
async run<T extends SqlRow = SqlRow>(builder: {
|
|
1410
|
+
toSQL(): { readonly sql: string; readonly params: readonly unknown[] };
|
|
1411
|
+
}): Promise<T[]> {
|
|
1412
|
+
const compiled = builder.toSQL();
|
|
1413
|
+
const dialect = fromDialect(compiled.sql, compiled.params);
|
|
1414
|
+
if (isSqlDml(dialect.sql)) {
|
|
1415
|
+
await exec(dialect.sql, dialect.params);
|
|
1416
|
+
return [];
|
|
1417
|
+
}
|
|
1418
|
+
return query(dialect.sql, dialect.params) as Promise<T[]>;
|
|
1419
|
+
},
|
|
1420
|
+
|
|
1225
1421
|
async ensureTable(table: TableHandle) {
|
|
1226
1422
|
const cols = Object.values(table.columns);
|
|
1227
1423
|
const pk = resolvePkColumn(table);
|
package/src/elements/store.ts
CHANGED
package/src/i18n/catalogs/ar.ts
CHANGED
|
@@ -40,6 +40,10 @@ export const builtinAr = {
|
|
|
40
40
|
cause: 'التدفق "{flow}" يضمّن عبر "{resource}" دون الإعلان عنه.',
|
|
41
41
|
fix: 'أضف "{resource}" إلى effects.embeds لهذا التدفق.',
|
|
42
42
|
},
|
|
43
|
+
"1010": {
|
|
44
|
+
cause: 'التدفق "{flow}" يقرر "{resource}" دون الإعلان عنه.',
|
|
45
|
+
fix: 'أضف "{resource}" إلى effects.decides لهذا التدفق.',
|
|
46
|
+
},
|
|
43
47
|
"1240": {
|
|
44
48
|
cause: 'التدفق "{flow}" يُصدِر الإشارة "{resource}" بلا مشترك.',
|
|
45
49
|
fix: "أضف on({resource}, …) أو عيّن الإشارة '{'optional: true'}'.",
|
|
@@ -90,6 +94,7 @@ export const builtinAr = {
|
|
|
90
94
|
IdempotencyKeyInvalid: "يجب أن يكون Idempotency-Key من 16 إلى 255 حرفًا ASCII قابلًا للطباعة.",
|
|
91
95
|
IdempotencyKeyReused: "استُخدم Idempotency-Key هذا من قبل مع طلب مختلف.",
|
|
92
96
|
IdempotencyInProgress: "ما زال Idempotency-Key هذا قيد التنفيذ. أعد المحاولة بعد المهلة.",
|
|
97
|
+
JournalLeaseBusy: "هذا التشغيل مقفل لدى عامل آخر. أعد المحاولة بعد المهلة.",
|
|
93
98
|
InvalidQuery: "الاستعلام غير صالح.",
|
|
94
99
|
UnsupportedMediaType: "نوع المحتوى هذا غير مدعوم.",
|
|
95
100
|
AuthFailed: "فشلت المصادقة.",
|
package/src/i18n/catalogs/en.ts
CHANGED
|
@@ -40,6 +40,10 @@ export const builtinEn = {
|
|
|
40
40
|
cause: 'Flow "{flow}" embeds with "{resource}" without declaring it.',
|
|
41
41
|
fix: "Add \"{resource}\" to this flow''s effects.embeds.",
|
|
42
42
|
},
|
|
43
|
+
"1010": {
|
|
44
|
+
cause: 'Flow "{flow}" decides "{resource}" without declaring it.',
|
|
45
|
+
fix: "Add \"{resource}\" to this flow''s effects.decides.",
|
|
46
|
+
},
|
|
43
47
|
"1240": {
|
|
44
48
|
cause: 'Flow "{flow}" emits signal "{resource}" with no subscriber.',
|
|
45
49
|
fix: "Add on({resource}, …) or mark the signal '{'optional: true'}'.",
|
|
@@ -90,6 +94,7 @@ export const builtinEn = {
|
|
|
90
94
|
IdempotencyKeyInvalid: "Idempotency-Key must be 16–255 printable ASCII characters.",
|
|
91
95
|
IdempotencyKeyReused: "This Idempotency-Key was already used with a different request.",
|
|
92
96
|
IdempotencyInProgress: "This Idempotency-Key is still running. Retry after the given delay.",
|
|
97
|
+
JournalLeaseBusy: "This run is locked by another worker. Retry after the given delay.",
|
|
93
98
|
InvalidQuery: "The query is not valid.",
|
|
94
99
|
UnsupportedMediaType: "That content type is not supported.",
|
|
95
100
|
AuthFailed: "Authentication failed.",
|