@palbase/backend 33.0.2 → 34.1.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.
Files changed (84) hide show
  1. package/dist/bin/palbase-backend.cjs +2273 -358
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +9 -7
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/{chunk-YIQ4RS4F.js → chunk-5UGPXKMJ.js} +331 -5
  6. package/dist/chunk-5UGPXKMJ.js.map +1 -0
  7. package/dist/chunk-GHNC2UHS.js +20 -0
  8. package/dist/chunk-GHNC2UHS.js.map +1 -0
  9. package/dist/{chunk-AAT5G7KY.js → chunk-JJW3L62T.js} +2 -2
  10. package/dist/chunk-JJW3L62T.js.map +1 -0
  11. package/dist/{chunk-SI4KGEM3.js → chunk-P7MGSAFT.js} +102 -96
  12. package/dist/chunk-P7MGSAFT.js.map +1 -0
  13. package/dist/{chunk-XOX6RFPZ.js → chunk-PRH45NZU.js} +46 -3
  14. package/dist/chunk-PRH45NZU.js.map +1 -0
  15. package/dist/{chunk-26GH54D2.js → chunk-RZLY4BOY.js} +5 -3
  16. package/dist/{chunk-26GH54D2.js.map → chunk-RZLY4BOY.js.map} +1 -1
  17. package/dist/{chunk-KATPXCJ5.js → chunk-UMESJJRS.js} +19 -40
  18. package/dist/chunk-UMESJJRS.js.map +1 -0
  19. package/dist/chunk-VXPNPVAG.js +40 -0
  20. package/dist/chunk-VXPNPVAG.js.map +1 -0
  21. package/dist/{chunk-WWUG2QXF.js → chunk-X2UYIXDS.js} +59 -4
  22. package/dist/chunk-X2UYIXDS.js.map +1 -0
  23. package/dist/{chunk-C6COAB3E.js → chunk-XZPDPYVY.js} +1905 -195
  24. package/dist/chunk-XZPDPYVY.js.map +1 -0
  25. package/dist/{chunk-TVCCR6SO.js → chunk-YO5GYM73.js} +58 -9
  26. package/dist/chunk-YO5GYM73.js.map +1 -0
  27. package/dist/db/env.cjs.map +1 -1
  28. package/dist/db/env.d.cts +2 -0
  29. package/dist/db/env.d.ts +2 -0
  30. package/dist/db/index.cjs +285 -2
  31. package/dist/db/index.cjs.map +1 -1
  32. package/dist/db/index.d.cts +46 -3
  33. package/dist/db/index.d.ts +46 -3
  34. package/dist/db/index.js +14 -5
  35. package/dist/{index-DMZG3kpo.d.ts → endpoint-BXPdsTA5.d.ts} +370 -58
  36. package/dist/{index-DB_nW-AV.d.cts → endpoint-DqhUP7RT.d.cts} +370 -58
  37. package/dist/engine/index.cjs +2279 -356
  38. package/dist/engine/index.cjs.map +1 -1
  39. package/dist/engine/index.d.cts +5 -5
  40. package/dist/engine/index.d.ts +5 -5
  41. package/dist/engine/index.js +17 -7
  42. package/dist/{index-DLveQoOf.d.cts → index-Co0Hj_aO.d.cts} +108 -50
  43. package/dist/{index-D-3duy8Y.d.ts → index-MhCL7ll4.d.ts} +108 -50
  44. package/dist/index.cjs +806 -386
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +7 -6
  47. package/dist/index.d.ts +7 -6
  48. package/dist/index.js +27 -15
  49. package/dist/index.js.map +1 -1
  50. package/dist/openapi/index.cjs +7 -0
  51. package/dist/openapi/index.cjs.map +1 -1
  52. package/dist/openapi/index.d.cts +3 -3
  53. package/dist/openapi/index.d.ts +3 -3
  54. package/dist/openapi/index.js +4 -3
  55. package/dist/openapi/index.js.map +1 -1
  56. package/dist/{registry-DSTThhKf.d.cts → registry-BkIVHPzB.d.cts} +3 -1
  57. package/dist/{registry-JjF5lcj4.d.ts → registry-CvGLR5j1.d.ts} +3 -1
  58. package/dist/test/index.cjs +103 -66
  59. package/dist/test/index.cjs.map +1 -1
  60. package/dist/test/index.d.cts +3 -3
  61. package/dist/test/index.d.ts +3 -3
  62. package/dist/test/index.js +33 -7
  63. package/dist/test/index.js.map +1 -1
  64. package/docs/README.md +1 -1
  65. package/docs/auth.md +58 -0
  66. package/docs/database.md +308 -0
  67. package/docs/examples/check-query-contracts.ts +5 -0
  68. package/docs/examples/database-dx.contract.json +65 -0
  69. package/docs/examples/database-dx.ts +63 -0
  70. package/docs/examples/money-transfer.ts +73 -0
  71. package/docs/llms-full.txt +382 -1
  72. package/docs/schema.md +15 -0
  73. package/package.json +1 -2
  74. package/template/db/public.ts +12 -0
  75. package/template/package.json +1 -1
  76. package/dist/chunk-AAT5G7KY.js.map +0 -1
  77. package/dist/chunk-C6COAB3E.js.map +0 -1
  78. package/dist/chunk-KATPXCJ5.js.map +0 -1
  79. package/dist/chunk-SI4KGEM3.js.map +0 -1
  80. package/dist/chunk-TVCCR6SO.js.map +0 -1
  81. package/dist/chunk-WWUG2QXF.js.map +0 -1
  82. package/dist/chunk-XOX6RFPZ.js.map +0 -1
  83. package/dist/chunk-YIQ4RS4F.js.map +0 -1
  84. package/stack-images.json +0 -24
@@ -1,8 +1,8 @@
1
- import '../index-DB_nW-AV.cjs';
2
- export { A as App, g as AuthVerifier, B as BootRefused, h as CreateAppOptions, D as DECLARATION_REFUSAL, a as DeclarationRefused, E as EgressPolicy, j as EngineConfig, M as ModuleClients, R as RateLimiter, k as RequestDatabase, l as RouteEntry, m as RuntimeHooks, S as ScrubResult, n as SqlDriver, o as SqlTx, p as buildRouteTable, q as createApp, r as createLazyTransaction, s as createOps, t as createRequestDatabase, u as effectiveAuth, v as hostAllowed, w as installEgressFence, i as isDeclarationRefused, x as loadConfig, y as makeMemoryCache, z as matchRoute, F as quoteIdent, G as scrubSecrets, H as withTables } from '../index-DLveQoOf.cjs';
3
- import '../db/env.cjs';
4
- import '../stack.cjs';
1
+ import '../endpoint-DqhUP7RT.cjs';
2
+ export { A as App, g as AuthVerifier, B as BootRefused, h as COMMAND_EXECUTOR_DDL, j as CreateAppOptions, D as DECLARATION_REFUSAL, a as DeclarationRefused, E as EgressPolicy, k as EngineConfig, M as ModuleClients, R as RateLimiter, l as RequestDatabase, m as RouteEntry, n as RuntimeHooks, S as ScrubResult, o as SqlDriver, p as SqlTx, q as buildRouteTable, r as createApp, s as createLazyTransaction, t as createOps, u as createRequestDatabase, v as effectiveAuth, w as hostAllowed, x as installCommandExecutor, y as installEgressFence, i as isDeclarationRefused, z as loadConfig, F as makeMemoryCache, G as matchRoute, H as quoteIdent, I as scrubSecrets, J as setCursorKey, K as withQueryCancellation, L as withTables } from '../index-Co0Hj_aO.cjs';
5
3
  import 'zod';
4
+ import '../stack.cjs';
5
+ import '../db/env.cjs';
6
6
  import 'node:async_hooks';
7
7
  import '../module-Dl1KFVtc.cjs';
8
- import '../registry-DSTThhKf.cjs';
8
+ import '../registry-BkIVHPzB.cjs';
@@ -1,8 +1,8 @@
1
- import '../index-DMZG3kpo.js';
2
- export { A as App, g as AuthVerifier, B as BootRefused, h as CreateAppOptions, D as DECLARATION_REFUSAL, a as DeclarationRefused, E as EgressPolicy, j as EngineConfig, M as ModuleClients, R as RateLimiter, k as RequestDatabase, l as RouteEntry, m as RuntimeHooks, S as ScrubResult, n as SqlDriver, o as SqlTx, p as buildRouteTable, q as createApp, r as createLazyTransaction, s as createOps, t as createRequestDatabase, u as effectiveAuth, v as hostAllowed, w as installEgressFence, i as isDeclarationRefused, x as loadConfig, y as makeMemoryCache, z as matchRoute, F as quoteIdent, G as scrubSecrets, H as withTables } from '../index-D-3duy8Y.js';
3
- import '../db/env.js';
4
- import '../stack.js';
1
+ import '../endpoint-BXPdsTA5.js';
2
+ export { A as App, g as AuthVerifier, B as BootRefused, h as COMMAND_EXECUTOR_DDL, j as CreateAppOptions, D as DECLARATION_REFUSAL, a as DeclarationRefused, E as EgressPolicy, k as EngineConfig, M as ModuleClients, R as RateLimiter, l as RequestDatabase, m as RouteEntry, n as RuntimeHooks, S as ScrubResult, o as SqlDriver, p as SqlTx, q as buildRouteTable, r as createApp, s as createLazyTransaction, t as createOps, u as createRequestDatabase, v as effectiveAuth, w as hostAllowed, x as installCommandExecutor, y as installEgressFence, i as isDeclarationRefused, z as loadConfig, F as makeMemoryCache, G as matchRoute, H as quoteIdent, I as scrubSecrets, J as setCursorKey, K as withQueryCancellation, L as withTables } from '../index-MhCL7ll4.js';
5
3
  import 'zod';
4
+ import '../stack.js';
5
+ import '../db/env.js';
6
6
  import 'node:async_hooks';
7
7
  import '../module-Dl1KFVtc.js';
8
- import '../registry-JjF5lcj4.js';
8
+ import '../registry-CvGLR5j1.js';
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AuthVerifier,
3
3
  BootRefused,
4
+ COMMAND_EXECUTOR_DDL,
4
5
  RateLimiter,
5
6
  buildRouteTable,
6
7
  createApp,
@@ -9,27 +10,33 @@ import {
9
10
  createRequestDatabase,
10
11
  effectiveAuth,
11
12
  hostAllowed,
13
+ installCommandExecutor,
12
14
  installEgressFence,
13
15
  loadConfig,
14
16
  makeMemoryCache,
15
17
  matchRoute,
16
18
  quoteIdent,
17
19
  scrubSecrets,
20
+ setCursorKey,
21
+ withQueryCancellation,
18
22
  withTables
19
- } from "../chunk-C6COAB3E.js";
23
+ } from "../chunk-XZPDPYVY.js";
20
24
  import {
21
25
  DECLARATION_REFUSAL,
22
26
  DeclarationRefused,
23
27
  isDeclarationRefused
24
- } from "../chunk-WWUG2QXF.js";
25
- import "../chunk-TVCCR6SO.js";
26
- import "../chunk-XOX6RFPZ.js";
27
- import "../chunk-SI4KGEM3.js";
28
- import "../chunk-AAT5G7KY.js";
29
- import "../chunk-KATPXCJ5.js";
28
+ } from "../chunk-X2UYIXDS.js";
29
+ import "../chunk-GHNC2UHS.js";
30
+ import "../chunk-YO5GYM73.js";
31
+ import "../chunk-PRH45NZU.js";
32
+ import "../chunk-P7MGSAFT.js";
33
+ import "../chunk-JJW3L62T.js";
34
+ import "../chunk-UMESJJRS.js";
35
+ import "../chunk-VXPNPVAG.js";
30
36
  export {
31
37
  AuthVerifier,
32
38
  BootRefused,
39
+ COMMAND_EXECUTOR_DDL,
33
40
  DECLARATION_REFUSAL,
34
41
  DeclarationRefused,
35
42
  RateLimiter,
@@ -40,6 +47,7 @@ export {
40
47
  createRequestDatabase,
41
48
  effectiveAuth,
42
49
  hostAllowed,
50
+ installCommandExecutor,
43
51
  installEgressFence,
44
52
  isDeclarationRefused,
45
53
  loadConfig,
@@ -47,6 +55,8 @@ export {
47
55
  matchRoute,
48
56
  quoteIdent,
49
57
  scrubSecrets,
58
+ setCursorKey,
59
+ withQueryCancellation,
50
60
  withTables
51
61
  };
52
62
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
- import { D as DBClient, b as DBOps, T as TxPlanBody, c as TxPlanResponse, A as AuthSpec, C as CacheClient, a as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-DB_nW-AV.cjs';
1
+ import { c as DatabaseDiagnosticsOptions, d as DatabaseDiagnostics, a as DBClient, T as TransactionIsolation, e as DBOps, P as PreparedRead, C as CompiledRead, f as RawPageOptions, g as Page, h as TxPlanBody, i as CommandOptions, j as TxPlanResponse, A as AuthSpec, k as CacheClient, b as RuntimeServices, _ as __runWithRuntime, l as __requestALS } from './endpoint-DqhUP7RT.cjs';
2
2
  import { T as Token } from './module-Dl1KFVtc.cjs';
3
- import { R as RouteMeta } from './registry-DSTThhKf.cjs';
3
+ import { R as RouteMeta } from './registry-BkIVHPzB.cjs';
4
4
 
5
5
  /**
6
6
  * engine/config.ts — settings from the environment, and the gate that refuses
@@ -47,6 +47,11 @@ interface EngineConfig {
47
47
  anonKey: string;
48
48
  /** Secret key. Used for privileged module calls. */
49
49
  serviceRoleKey: string;
50
+ /** Optional dedicated cursor encryption secret, shared by all replicas. */
51
+ cursorSecret?: string;
52
+ /** Install the invoker-only PostgreSQL command helper at boot. Requires DDL
53
+ * rights on the connection role; execution retains each request's RLS role. */
54
+ enableCommands?: boolean;
50
55
  /** HMAC the realtime broadcast token is signed with. Empty ⇒ broadcast
51
56
  * returns a clear `realtime_unconfigured` error instead of failing silently. */
52
57
  realtimeSecret: string;
@@ -65,6 +70,7 @@ interface EngineConfig {
65
70
  */
66
71
  dbServiceRole: string;
67
72
  poolMax: number;
73
+ databaseDiagnostics?: DatabaseDiagnosticsOptions;
68
74
  }
69
75
  /**
70
76
  * THE ENVIRONMENT THIS STACK WAS HANDED IS UNUSABLE. Carries the missing names.
@@ -87,42 +93,62 @@ declare class BootRefused extends Error {
87
93
  */
88
94
  declare function loadConfig(env: Record<string, string | undefined>): EngineConfig;
89
95
 
90
- /**
91
- * engine/db.ts — a real pooled connection, and the identity every request is
92
- * bound to inside it.
93
- *
94
- * # Why one transaction per request
95
- *
96
- * In the isolate architecture every `Database.*` call was its own HTTP hop to a
97
- * capability surface, so two writes in one handler could not be atomic — a
98
- * handler that wrote and then threw left the first write behind. Here the whole
99
- * request runs inside one transaction: it commits when the handler returns and
100
- * rolls back when it throws. Atomicity stops being something the author has to
101
- * ask for.
102
- *
103
- * # Why it opens lazily
104
- *
105
- * A handler that touches no table must cost no round trip. Opening eagerly cost
106
- * four (BEGIN + bind + … + COMMIT) on endpoints that never query — measured at
107
- * 1,243 rps against 31,579 for the same endpoint once the open became lazy.
108
- *
109
- * # How the caller's identity reaches RLS
110
- *
111
- * One statement, not three:
112
- *
113
- * select set_config('role',$1,true),
114
- * set_config('search_path','public',true),
115
- * set_config('request.jwt.claims',$2,true)
116
- *
117
- * `set_config(..., is_local => true)` is transaction-scoped exactly like
118
- * `SET LOCAL`, but takes BOUND PARAMETERS, which `SET LOCAL` cannot. So the
119
- * role and the caller's claims travel as parameters user identity is never
120
- * spliced into SQL text and `auth.uid()` resolves inside RLS policies, which
121
- * means the row filter is enforced by Postgres rather than by our code.
122
- */
96
+ type CancellableQuery = Promise<unknown> & {
97
+ cancel?: () => unknown;
98
+ };
99
+ /** Abort callback work and active statements, then let the transaction owner
100
+ * await rollback. COMMIT is never raced with a timer: its acknowledged result
101
+ * or connection failure remains authoritative. */
102
+ declare class TransactionControl {
103
+ private controller;
104
+ private active;
105
+ private cancelBackend?;
106
+ private cancellation;
107
+ private timer?;
108
+ private unlink;
109
+ private rejection;
110
+ readonly deadline: number | undefined;
111
+ get signal(): AbortSignal;
112
+ constructor(timeoutMs?: number, external?: AbortSignal);
113
+ check(): void;
114
+ remainingMs(): number | undefined;
115
+ race<T>(work: Promise<T>): Promise<T>;
116
+ bindBackend(cancel: () => Promise<void>): void;
117
+ /** Keep the application's connection parked until the cancellation request
118
+ * and all statements settle; a late cancel must not hit its next borrower. */
119
+ drainCancellation(): Promise<void>;
120
+ watch(query: CancellableQuery): Promise<unknown>;
121
+ dispose(): void;
122
+ }
123
+
124
+ type Timing = "queueMs" | "poolMs" | "setupMs" | "sqlMs" | "commitMs" | "rollbackMs";
125
+ /** Allocated only when diagnostics or a budget is configured. One collector
126
+ * follows the request's user/service handles and independent atomic attempts. */
127
+ declare class DatabaseTrace {
128
+ private readonly options;
129
+ private readonly start;
130
+ private ended?;
131
+ private failure?;
132
+ private readonly state;
133
+ constructor(options: DatabaseDiagnosticsOptions);
134
+ check(): void;
135
+ time(key: Timing, started: number): void;
136
+ retry(): void;
137
+ committed(): void;
138
+ commitFailed(): void;
139
+ failed(): void;
140
+ rolledBack(): void;
141
+ private refuse;
142
+ private emit;
143
+ query(sql: string, name: string | undefined, run: () => Promise<unknown>): Promise<unknown>;
144
+ get snapshot(): DatabaseDiagnostics;
145
+ complete(outcome: "committed" | "failed"): void;
146
+ }
123
147
 
124
148
  /** The slice of a SQL driver the engine uses. `Bun.sql` satisfies it. */
125
149
  interface SqlDriver {
150
+ /** Optional reliable cancellation through a separate control connection. */
151
+ cancelBackend?(pid: number, startedAt: string): Promise<void>;
126
152
  /** Run a parameterised statement. */
127
153
  unsafe(sql: string, params?: unknown[]): Promise<unknown>;
128
154
  /** Open a transaction; the driver commits when `cb` resolves and rolls back
@@ -130,7 +156,9 @@ interface SqlDriver {
130
156
  begin<T>(cb: (tx: SqlTx) => Promise<T>): Promise<T>;
131
157
  }
132
158
  interface SqlTx {
133
- unsafe(sql: string, params?: unknown[]): Promise<unknown>;
159
+ unsafe(sql: string, params?: unknown[], metadata?: {
160
+ name?: string;
161
+ }): Promise<unknown>;
134
162
  savepoint<T>(cb: (sp: SqlTx) => Promise<T>): Promise<T>;
135
163
  }
136
164
  type Row = Record<string, unknown>;
@@ -147,6 +175,12 @@ declare function quoteIdent(name: string): string;
147
175
  */
148
176
  declare function createLazyTransaction(sql: SqlDriver, role: string, claimsJson: string, options?: {
149
177
  lockTimeout?: string;
178
+ statementTimeout?: string;
179
+ isolation?: TransactionIsolation;
180
+ readOnly?: boolean;
181
+ scope?: symbol;
182
+ control?: TransactionControl;
183
+ trace?: DatabaseTrace;
150
184
  }): {
151
185
  ensure: () => Promise<SqlTx>;
152
186
  readonly opened: boolean;
@@ -186,7 +220,13 @@ interface FindManyOptions {
186
220
  * refused without it — see `offsetClause`. */
187
221
  offset?: number;
188
222
  }
189
- declare function createOps(tx: TxLike): {
223
+ interface OpsContext {
224
+ readonly cursorScope?: string;
225
+ readonly commands?: boolean;
226
+ readonly trace?: DatabaseTrace;
227
+ }
228
+ declare function createOps(tx: TxLike, context?: OpsContext): {
229
+ diagnostics(): DatabaseDiagnostics | null;
190
230
  query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
191
231
  insert(table: string, data: Row): Promise<Row>;
192
232
  /**
@@ -203,10 +243,7 @@ declare function createOps(tx: TxLike): {
203
243
  * bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
204
244
  * yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
205
245
  */
206
- insertMany(table: string, rows: readonly Row[], opts?: {
207
- onConflict: readonly string[];
208
- action?: "ignore" | "update";
209
- }): Promise<Row[]>;
246
+ insertMany: DBOps["insertMany"];
210
247
  /**
211
248
  * Bir ADI kilitle — satırı değil (FR-054).
212
249
  *
@@ -338,8 +375,16 @@ declare function createOps(tx: TxLike): {
338
375
  groupBy?: readonly string[];
339
376
  }): Promise<Record<string, unknown> | Record<string, unknown>[]>;
340
377
  count(table: string, where?: Row): Promise<number>;
378
+ findUnique(table: string, where: Row, opts?: {
379
+ select?: readonly string[];
380
+ with?: Record<string, unknown>;
381
+ }): Promise<Row | null>;
341
382
  findById(table: string, id: string): Promise<Row | null>;
383
+ compileQuery(query: PreparedRead): CompiledRead;
384
+ readQuery(query: PreparedRead): Promise<Row[]>;
385
+ explainQuery(query: PreparedRead): Promise<unknown[]>;
342
386
  findMany(table: string, query?: Row, opts?: FindManyOptions): Promise<Row[]>;
387
+ page(table: string, query?: Row, opts?: RawPageOptions): Promise<Page<Row>>;
343
388
  /**
344
389
  * Tek-SQL hibrit arama (FR-014): iki kol CTE + FULL OUTER JOIN + RRF
345
390
  * (1/(50+rank), CLAIM-N4). Operatör şema-nitelikli (C-10, M-1); GUC
@@ -495,15 +540,9 @@ declare function createOps(tx: TxLike): {
495
540
  * "transport.txPlan is not a function" while every test that covered
496
541
  * transactions passed against a mock that did implement it.
497
542
  */
543
+ command(plan: TxPlanBody, options?: CommandOptions): Promise<TxPlanResponse>;
498
544
  txPlan(plan: TxPlanBody): Promise<TxPlanResponse>;
499
545
  };
500
- /**
501
- * Merge the typed `.tables` accessor onto a raw op surface.
502
- *
503
- * Mirrors what the pod runtime does, including the recursive application to the
504
- * transaction callback: without it `tx.tables.rooms.insert(...)` throws
505
- * "Cannot read properties of undefined".
506
- */
507
546
  declare function withTables<T extends ReturnType<typeof createOps>>(ops: T, schema?: {
508
547
  tables?: Record<string, {
509
548
  name?: string;
@@ -580,7 +619,7 @@ declare function createRequestDatabase(sql: SqlDriver, identity: {
580
619
  role: string;
581
620
  serviceRole: string;
582
621
  claimsJson: string;
583
- }): RequestDatabase;
622
+ }, diagnostics?: DatabaseDiagnosticsOptions): RequestDatabase;
584
623
 
585
624
  /**
586
625
  * The two ways a stack refuses to boot — and they are two because their CURES
@@ -926,6 +965,25 @@ interface MemoryCacheOptions {
926
965
  */
927
966
  declare function makeMemoryCache(opts?: MemoryCacheOptions): CacheClient;
928
967
 
968
+ /** One application secret shared by replicas. Rotation invalidates old cursors.
969
+ * Encryption also hides sort values omitted by the public projection. */
970
+ declare function setCursorKey(secret: string | null): void;
971
+
972
+ /** Statements remain sequential inside PostgreSQL. Each sees preceding writes,
973
+ * including triggers, unlike sibling data-modifying CTEs in one SQL statement.
974
+ * The exception block is a subtransaction, matching a savepoint plan. */
975
+ declare const COMMAND_EXECUTOR_DDL = "\nCREATE SCHEMA IF NOT EXISTS palbase_runtime;\nCREATE OR REPLACE FUNCTION palbase_runtime.command_v1(steps jsonb) RETURNS jsonb\nLANGUAGE plpgsql VOLATILE SECURITY INVOKER SET search_path = pg_catalog, public AS $pb_command$\nDECLARE\n results jsonb := '[]'::jsonb;\n step jsonb; chunk jsonb; binding jsonb; source jsonb; rows jsonb;\n statement text; rendered text; detail text; guard jsonb;\n index integer := 0; affected integer; passed boolean;\nBEGIN\n FOR step IN SELECT value FROM jsonb_array_elements(steps) LOOP\n IF step->>'empty' = 'true' THEN rows := '[]'::jsonb;\n ELSE\n statement := '';\n FOR chunk IN SELECT value FROM jsonb_array_elements(step->'chunks') LOOP\n IF jsonb_typeof(chunk) = 'string' THEN statement := statement || (chunk #>> '{}');\n ELSE\n binding := step->'bindings'->((chunk->>'bind')::integer);\n IF binding ? 'ref' THEN\n source := results->((binding->'ref'->>'op')::integer)->'rows'->0;\n IF source IS NULL OR NOT source ? (binding->'ref'->>'field') THEN\n RAISE EXCEPTION 'Command reference has no source row/column; use expectOne before reading it'\n USING ERRCODE = 'PBC02', DETAIL = jsonb_build_object('error_code','tx_ref_unresolved','op',index)::text;\n END IF;\n source := source->(binding->'ref'->>'field');\n ELSE source := binding->'value'; END IF;\n rendered := CASE WHEN source IS NULL OR source = 'null'::jsonb THEN NULL\n WHEN binding->>'json' = 'true' THEN source::text\n WHEN jsonb_typeof(source) = 'string' THEN source #>> '{}' ELSE source::text END;\n -- PostgreSQL quotes the value itself. Values reach this function as\n -- one bound JSON parameter, never as application-generated SQL text.\n statement := statement || quote_nullable(rendered);\n END IF;\n END LOOP;\n EXECUTE statement INTO rows;\n END IF;\n affected := jsonb_array_length(rows);\n guard := step->'guard';\n IF guard IS NOT NULL AND guard <> 'null'::jsonb THEN\n passed := CASE guard->>'kind'\n WHEN 'one' THEN affected = 1 WHEN 'none' THEN affected = 0\n WHEN 'atLeast' THEN affected >= (guard->>'n')::integer\n WHEN 'atMost' THEN affected <= (guard->>'n')::integer ELSE false END;\n IF NOT passed THEN\n RAISE EXCEPTION 'Command expectation failed'\n USING ERRCODE = 'PBC01', DETAIL = jsonb_build_object('error_code','tx_guard_failed','slot',guard->'slot','op',index)::text;\n END IF;\n END IF;\n results := results || jsonb_build_array(jsonb_build_object('rows',rows,'rows_affected',affected));\n index := index + 1;\n END LOOP;\n RETURN jsonb_build_object('results',results);\nEXCEPTION WHEN SQLSTATE 'PBC01' OR SQLSTATE 'PBC02' THEN\n GET STACKED DIAGNOSTICS detail = PG_EXCEPTION_DETAIL;\n RETURN jsonb_build_object('error',detail::jsonb);\nEND\n$pb_command$;\nREVOKE ALL ON FUNCTION palbase_runtime.command_v1(jsonb) FROM PUBLIC;\nCREATE OR REPLACE FUNCTION palbase_runtime.command_rows_v1(rows jsonb, guard jsonb) RETURNS jsonb\nLANGUAGE plpgsql VOLATILE SECURITY INVOKER SET search_path = pg_catalog, public AS $pb_rows$\nDECLARE affected integer := jsonb_array_length(rows); passed boolean;\nBEGIN\n IF guard IS NOT NULL AND guard <> 'null'::jsonb THEN\n passed := CASE guard->>'kind'\n WHEN 'one' THEN affected = 1 WHEN 'none' THEN affected = 0\n WHEN 'atLeast' THEN affected >= (guard->>'n')::integer\n WHEN 'atMost' THEN affected <= (guard->>'n')::integer ELSE false END;\n IF NOT passed THEN\n RAISE EXCEPTION 'Command expectation failed'\n USING ERRCODE = 'PBC01', DETAIL = jsonb_build_object('error_code','tx_guard_failed','slot',guard->'slot')::text;\n END IF;\n END IF;\n RETURN rows;\nEND\n$pb_rows$;\nREVOKE ALL ON FUNCTION palbase_runtime.command_rows_v1(jsonb,jsonb) FROM PUBLIC;\n";
976
+ /** Run once with a migration/owner connection. Runtime execution retains each
977
+ * caller's role and RLS; there is no SECURITY DEFINER capability. */
978
+ declare function installCommandExecutor(sql: SqlDriver, roles: readonly string[]): Promise<void>;
979
+
980
+ /** The control pool must be separate from the application pool, connect as the
981
+ * same session user and to the same PostgreSQL server. Cancellation must still
982
+ * have a connection when every application connection is occupied. */
983
+ declare function withQueryCancellation(application: SqlDriver, control: SqlDriver): SqlDriver & {
984
+ close(): Promise<void>;
985
+ };
986
+
929
987
  /**
930
988
  * engine/fence.ts — what the tenant's own code may reach.
931
989
  *
@@ -1063,4 +1121,4 @@ interface App {
1063
1121
  }
1064
1122
  declare function createApp(opts: CreateAppOptions): Promise<App>;
1065
1123
 
1066
- export { type App as A, BootRefused as B, type Container as C, DECLARATION_REFUSAL as D, type EgressPolicy as E, quoteIdent as F, scrubSecrets as G, withTables as H, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, DeclarationRefused as a, DiError as b, type DiKind as c, type ModulePressure as d, assertNoOrphanEntryPoints as e, buildContainer as f, AuthVerifier as g, type CreateAppOptions as h, isDeclarationRefused as i, type EngineConfig as j, type RequestDatabase as k, type RouteEntry as l, type RuntimeHooks as m, type SqlDriver as n, type SqlTx as o, buildRouteTable as p, createApp as q, createLazyTransaction as r, createOps as s, createRequestDatabase as t, effectiveAuth as u, hostAllowed as v, installEgressFence as w, loadConfig as x, makeMemoryCache as y, matchRoute as z };
1124
+ export { type App as A, BootRefused as B, type Container as C, DECLARATION_REFUSAL as D, type EgressPolicy as E, makeMemoryCache as F, matchRoute as G, quoteIdent as H, scrubSecrets as I, setCursorKey as J, withQueryCancellation as K, withTables as L, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, DeclarationRefused as a, DiError as b, type DiKind as c, type ModulePressure as d, assertNoOrphanEntryPoints as e, buildContainer as f, AuthVerifier as g, COMMAND_EXECUTOR_DDL as h, isDeclarationRefused as i, type CreateAppOptions as j, type EngineConfig as k, type RequestDatabase as l, type RouteEntry as m, type RuntimeHooks as n, type SqlDriver as o, type SqlTx as p, buildRouteTable as q, createApp as r, createLazyTransaction as s, createOps as t, createRequestDatabase as u, effectiveAuth as v, hostAllowed as w, installCommandExecutor as x, installEgressFence as y, loadConfig as z };
@@ -1,6 +1,6 @@
1
- import { D as DBClient, b as DBOps, T as TxPlanBody, c as TxPlanResponse, A as AuthSpec, C as CacheClient, a as RuntimeServices, _ as __runWithRuntime, d as __requestALS } from './index-DMZG3kpo.js';
1
+ import { c as DatabaseDiagnosticsOptions, d as DatabaseDiagnostics, a as DBClient, T as TransactionIsolation, e as DBOps, P as PreparedRead, C as CompiledRead, f as RawPageOptions, g as Page, h as TxPlanBody, i as CommandOptions, j as TxPlanResponse, A as AuthSpec, k as CacheClient, b as RuntimeServices, _ as __runWithRuntime, l as __requestALS } from './endpoint-BXPdsTA5.js';
2
2
  import { T as Token } from './module-Dl1KFVtc.js';
3
- import { R as RouteMeta } from './registry-JjF5lcj4.js';
3
+ import { R as RouteMeta } from './registry-CvGLR5j1.js';
4
4
 
5
5
  /**
6
6
  * engine/config.ts — settings from the environment, and the gate that refuses
@@ -47,6 +47,11 @@ interface EngineConfig {
47
47
  anonKey: string;
48
48
  /** Secret key. Used for privileged module calls. */
49
49
  serviceRoleKey: string;
50
+ /** Optional dedicated cursor encryption secret, shared by all replicas. */
51
+ cursorSecret?: string;
52
+ /** Install the invoker-only PostgreSQL command helper at boot. Requires DDL
53
+ * rights on the connection role; execution retains each request's RLS role. */
54
+ enableCommands?: boolean;
50
55
  /** HMAC the realtime broadcast token is signed with. Empty ⇒ broadcast
51
56
  * returns a clear `realtime_unconfigured` error instead of failing silently. */
52
57
  realtimeSecret: string;
@@ -65,6 +70,7 @@ interface EngineConfig {
65
70
  */
66
71
  dbServiceRole: string;
67
72
  poolMax: number;
73
+ databaseDiagnostics?: DatabaseDiagnosticsOptions;
68
74
  }
69
75
  /**
70
76
  * THE ENVIRONMENT THIS STACK WAS HANDED IS UNUSABLE. Carries the missing names.
@@ -87,42 +93,62 @@ declare class BootRefused extends Error {
87
93
  */
88
94
  declare function loadConfig(env: Record<string, string | undefined>): EngineConfig;
89
95
 
90
- /**
91
- * engine/db.ts — a real pooled connection, and the identity every request is
92
- * bound to inside it.
93
- *
94
- * # Why one transaction per request
95
- *
96
- * In the isolate architecture every `Database.*` call was its own HTTP hop to a
97
- * capability surface, so two writes in one handler could not be atomic — a
98
- * handler that wrote and then threw left the first write behind. Here the whole
99
- * request runs inside one transaction: it commits when the handler returns and
100
- * rolls back when it throws. Atomicity stops being something the author has to
101
- * ask for.
102
- *
103
- * # Why it opens lazily
104
- *
105
- * A handler that touches no table must cost no round trip. Opening eagerly cost
106
- * four (BEGIN + bind + … + COMMIT) on endpoints that never query — measured at
107
- * 1,243 rps against 31,579 for the same endpoint once the open became lazy.
108
- *
109
- * # How the caller's identity reaches RLS
110
- *
111
- * One statement, not three:
112
- *
113
- * select set_config('role',$1,true),
114
- * set_config('search_path','public',true),
115
- * set_config('request.jwt.claims',$2,true)
116
- *
117
- * `set_config(..., is_local => true)` is transaction-scoped exactly like
118
- * `SET LOCAL`, but takes BOUND PARAMETERS, which `SET LOCAL` cannot. So the
119
- * role and the caller's claims travel as parameters user identity is never
120
- * spliced into SQL text and `auth.uid()` resolves inside RLS policies, which
121
- * means the row filter is enforced by Postgres rather than by our code.
122
- */
96
+ type CancellableQuery = Promise<unknown> & {
97
+ cancel?: () => unknown;
98
+ };
99
+ /** Abort callback work and active statements, then let the transaction owner
100
+ * await rollback. COMMIT is never raced with a timer: its acknowledged result
101
+ * or connection failure remains authoritative. */
102
+ declare class TransactionControl {
103
+ private controller;
104
+ private active;
105
+ private cancelBackend?;
106
+ private cancellation;
107
+ private timer?;
108
+ private unlink;
109
+ private rejection;
110
+ readonly deadline: number | undefined;
111
+ get signal(): AbortSignal;
112
+ constructor(timeoutMs?: number, external?: AbortSignal);
113
+ check(): void;
114
+ remainingMs(): number | undefined;
115
+ race<T>(work: Promise<T>): Promise<T>;
116
+ bindBackend(cancel: () => Promise<void>): void;
117
+ /** Keep the application's connection parked until the cancellation request
118
+ * and all statements settle; a late cancel must not hit its next borrower. */
119
+ drainCancellation(): Promise<void>;
120
+ watch(query: CancellableQuery): Promise<unknown>;
121
+ dispose(): void;
122
+ }
123
+
124
+ type Timing = "queueMs" | "poolMs" | "setupMs" | "sqlMs" | "commitMs" | "rollbackMs";
125
+ /** Allocated only when diagnostics or a budget is configured. One collector
126
+ * follows the request's user/service handles and independent atomic attempts. */
127
+ declare class DatabaseTrace {
128
+ private readonly options;
129
+ private readonly start;
130
+ private ended?;
131
+ private failure?;
132
+ private readonly state;
133
+ constructor(options: DatabaseDiagnosticsOptions);
134
+ check(): void;
135
+ time(key: Timing, started: number): void;
136
+ retry(): void;
137
+ committed(): void;
138
+ commitFailed(): void;
139
+ failed(): void;
140
+ rolledBack(): void;
141
+ private refuse;
142
+ private emit;
143
+ query(sql: string, name: string | undefined, run: () => Promise<unknown>): Promise<unknown>;
144
+ get snapshot(): DatabaseDiagnostics;
145
+ complete(outcome: "committed" | "failed"): void;
146
+ }
123
147
 
124
148
  /** The slice of a SQL driver the engine uses. `Bun.sql` satisfies it. */
125
149
  interface SqlDriver {
150
+ /** Optional reliable cancellation through a separate control connection. */
151
+ cancelBackend?(pid: number, startedAt: string): Promise<void>;
126
152
  /** Run a parameterised statement. */
127
153
  unsafe(sql: string, params?: unknown[]): Promise<unknown>;
128
154
  /** Open a transaction; the driver commits when `cb` resolves and rolls back
@@ -130,7 +156,9 @@ interface SqlDriver {
130
156
  begin<T>(cb: (tx: SqlTx) => Promise<T>): Promise<T>;
131
157
  }
132
158
  interface SqlTx {
133
- unsafe(sql: string, params?: unknown[]): Promise<unknown>;
159
+ unsafe(sql: string, params?: unknown[], metadata?: {
160
+ name?: string;
161
+ }): Promise<unknown>;
134
162
  savepoint<T>(cb: (sp: SqlTx) => Promise<T>): Promise<T>;
135
163
  }
136
164
  type Row = Record<string, unknown>;
@@ -147,6 +175,12 @@ declare function quoteIdent(name: string): string;
147
175
  */
148
176
  declare function createLazyTransaction(sql: SqlDriver, role: string, claimsJson: string, options?: {
149
177
  lockTimeout?: string;
178
+ statementTimeout?: string;
179
+ isolation?: TransactionIsolation;
180
+ readOnly?: boolean;
181
+ scope?: symbol;
182
+ control?: TransactionControl;
183
+ trace?: DatabaseTrace;
150
184
  }): {
151
185
  ensure: () => Promise<SqlTx>;
152
186
  readonly opened: boolean;
@@ -186,7 +220,13 @@ interface FindManyOptions {
186
220
  * refused without it — see `offsetClause`. */
187
221
  offset?: number;
188
222
  }
189
- declare function createOps(tx: TxLike): {
223
+ interface OpsContext {
224
+ readonly cursorScope?: string;
225
+ readonly commands?: boolean;
226
+ readonly trace?: DatabaseTrace;
227
+ }
228
+ declare function createOps(tx: TxLike, context?: OpsContext): {
229
+ diagnostics(): DatabaseDiagnostics | null;
190
230
  query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
191
231
  insert(table: string, data: Row): Promise<Row>;
192
232
  /**
@@ -203,10 +243,7 @@ declare function createOps(tx: TxLike): {
203
243
  * bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
204
244
  * yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
205
245
  */
206
- insertMany(table: string, rows: readonly Row[], opts?: {
207
- onConflict: readonly string[];
208
- action?: "ignore" | "update";
209
- }): Promise<Row[]>;
246
+ insertMany: DBOps["insertMany"];
210
247
  /**
211
248
  * Bir ADI kilitle — satırı değil (FR-054).
212
249
  *
@@ -338,8 +375,16 @@ declare function createOps(tx: TxLike): {
338
375
  groupBy?: readonly string[];
339
376
  }): Promise<Record<string, unknown> | Record<string, unknown>[]>;
340
377
  count(table: string, where?: Row): Promise<number>;
378
+ findUnique(table: string, where: Row, opts?: {
379
+ select?: readonly string[];
380
+ with?: Record<string, unknown>;
381
+ }): Promise<Row | null>;
341
382
  findById(table: string, id: string): Promise<Row | null>;
383
+ compileQuery(query: PreparedRead): CompiledRead;
384
+ readQuery(query: PreparedRead): Promise<Row[]>;
385
+ explainQuery(query: PreparedRead): Promise<unknown[]>;
342
386
  findMany(table: string, query?: Row, opts?: FindManyOptions): Promise<Row[]>;
387
+ page(table: string, query?: Row, opts?: RawPageOptions): Promise<Page<Row>>;
343
388
  /**
344
389
  * Tek-SQL hibrit arama (FR-014): iki kol CTE + FULL OUTER JOIN + RRF
345
390
  * (1/(50+rank), CLAIM-N4). Operatör şema-nitelikli (C-10, M-1); GUC
@@ -495,15 +540,9 @@ declare function createOps(tx: TxLike): {
495
540
  * "transport.txPlan is not a function" while every test that covered
496
541
  * transactions passed against a mock that did implement it.
497
542
  */
543
+ command(plan: TxPlanBody, options?: CommandOptions): Promise<TxPlanResponse>;
498
544
  txPlan(plan: TxPlanBody): Promise<TxPlanResponse>;
499
545
  };
500
- /**
501
- * Merge the typed `.tables` accessor onto a raw op surface.
502
- *
503
- * Mirrors what the pod runtime does, including the recursive application to the
504
- * transaction callback: without it `tx.tables.rooms.insert(...)` throws
505
- * "Cannot read properties of undefined".
506
- */
507
546
  declare function withTables<T extends ReturnType<typeof createOps>>(ops: T, schema?: {
508
547
  tables?: Record<string, {
509
548
  name?: string;
@@ -580,7 +619,7 @@ declare function createRequestDatabase(sql: SqlDriver, identity: {
580
619
  role: string;
581
620
  serviceRole: string;
582
621
  claimsJson: string;
583
- }): RequestDatabase;
622
+ }, diagnostics?: DatabaseDiagnosticsOptions): RequestDatabase;
584
623
 
585
624
  /**
586
625
  * The two ways a stack refuses to boot — and they are two because their CURES
@@ -926,6 +965,25 @@ interface MemoryCacheOptions {
926
965
  */
927
966
  declare function makeMemoryCache(opts?: MemoryCacheOptions): CacheClient;
928
967
 
968
+ /** One application secret shared by replicas. Rotation invalidates old cursors.
969
+ * Encryption also hides sort values omitted by the public projection. */
970
+ declare function setCursorKey(secret: string | null): void;
971
+
972
+ /** Statements remain sequential inside PostgreSQL. Each sees preceding writes,
973
+ * including triggers, unlike sibling data-modifying CTEs in one SQL statement.
974
+ * The exception block is a subtransaction, matching a savepoint plan. */
975
+ declare const COMMAND_EXECUTOR_DDL = "\nCREATE SCHEMA IF NOT EXISTS palbase_runtime;\nCREATE OR REPLACE FUNCTION palbase_runtime.command_v1(steps jsonb) RETURNS jsonb\nLANGUAGE plpgsql VOLATILE SECURITY INVOKER SET search_path = pg_catalog, public AS $pb_command$\nDECLARE\n results jsonb := '[]'::jsonb;\n step jsonb; chunk jsonb; binding jsonb; source jsonb; rows jsonb;\n statement text; rendered text; detail text; guard jsonb;\n index integer := 0; affected integer; passed boolean;\nBEGIN\n FOR step IN SELECT value FROM jsonb_array_elements(steps) LOOP\n IF step->>'empty' = 'true' THEN rows := '[]'::jsonb;\n ELSE\n statement := '';\n FOR chunk IN SELECT value FROM jsonb_array_elements(step->'chunks') LOOP\n IF jsonb_typeof(chunk) = 'string' THEN statement := statement || (chunk #>> '{}');\n ELSE\n binding := step->'bindings'->((chunk->>'bind')::integer);\n IF binding ? 'ref' THEN\n source := results->((binding->'ref'->>'op')::integer)->'rows'->0;\n IF source IS NULL OR NOT source ? (binding->'ref'->>'field') THEN\n RAISE EXCEPTION 'Command reference has no source row/column; use expectOne before reading it'\n USING ERRCODE = 'PBC02', DETAIL = jsonb_build_object('error_code','tx_ref_unresolved','op',index)::text;\n END IF;\n source := source->(binding->'ref'->>'field');\n ELSE source := binding->'value'; END IF;\n rendered := CASE WHEN source IS NULL OR source = 'null'::jsonb THEN NULL\n WHEN binding->>'json' = 'true' THEN source::text\n WHEN jsonb_typeof(source) = 'string' THEN source #>> '{}' ELSE source::text END;\n -- PostgreSQL quotes the value itself. Values reach this function as\n -- one bound JSON parameter, never as application-generated SQL text.\n statement := statement || quote_nullable(rendered);\n END IF;\n END LOOP;\n EXECUTE statement INTO rows;\n END IF;\n affected := jsonb_array_length(rows);\n guard := step->'guard';\n IF guard IS NOT NULL AND guard <> 'null'::jsonb THEN\n passed := CASE guard->>'kind'\n WHEN 'one' THEN affected = 1 WHEN 'none' THEN affected = 0\n WHEN 'atLeast' THEN affected >= (guard->>'n')::integer\n WHEN 'atMost' THEN affected <= (guard->>'n')::integer ELSE false END;\n IF NOT passed THEN\n RAISE EXCEPTION 'Command expectation failed'\n USING ERRCODE = 'PBC01', DETAIL = jsonb_build_object('error_code','tx_guard_failed','slot',guard->'slot','op',index)::text;\n END IF;\n END IF;\n results := results || jsonb_build_array(jsonb_build_object('rows',rows,'rows_affected',affected));\n index := index + 1;\n END LOOP;\n RETURN jsonb_build_object('results',results);\nEXCEPTION WHEN SQLSTATE 'PBC01' OR SQLSTATE 'PBC02' THEN\n GET STACKED DIAGNOSTICS detail = PG_EXCEPTION_DETAIL;\n RETURN jsonb_build_object('error',detail::jsonb);\nEND\n$pb_command$;\nREVOKE ALL ON FUNCTION palbase_runtime.command_v1(jsonb) FROM PUBLIC;\nCREATE OR REPLACE FUNCTION palbase_runtime.command_rows_v1(rows jsonb, guard jsonb) RETURNS jsonb\nLANGUAGE plpgsql VOLATILE SECURITY INVOKER SET search_path = pg_catalog, public AS $pb_rows$\nDECLARE affected integer := jsonb_array_length(rows); passed boolean;\nBEGIN\n IF guard IS NOT NULL AND guard <> 'null'::jsonb THEN\n passed := CASE guard->>'kind'\n WHEN 'one' THEN affected = 1 WHEN 'none' THEN affected = 0\n WHEN 'atLeast' THEN affected >= (guard->>'n')::integer\n WHEN 'atMost' THEN affected <= (guard->>'n')::integer ELSE false END;\n IF NOT passed THEN\n RAISE EXCEPTION 'Command expectation failed'\n USING ERRCODE = 'PBC01', DETAIL = jsonb_build_object('error_code','tx_guard_failed','slot',guard->'slot')::text;\n END IF;\n END IF;\n RETURN rows;\nEND\n$pb_rows$;\nREVOKE ALL ON FUNCTION palbase_runtime.command_rows_v1(jsonb,jsonb) FROM PUBLIC;\n";
976
+ /** Run once with a migration/owner connection. Runtime execution retains each
977
+ * caller's role and RLS; there is no SECURITY DEFINER capability. */
978
+ declare function installCommandExecutor(sql: SqlDriver, roles: readonly string[]): Promise<void>;
979
+
980
+ /** The control pool must be separate from the application pool, connect as the
981
+ * same session user and to the same PostgreSQL server. Cancellation must still
982
+ * have a connection when every application connection is occupied. */
983
+ declare function withQueryCancellation(application: SqlDriver, control: SqlDriver): SqlDriver & {
984
+ close(): Promise<void>;
985
+ };
986
+
929
987
  /**
930
988
  * engine/fence.ts — what the tenant's own code may reach.
931
989
  *
@@ -1063,4 +1121,4 @@ interface App {
1063
1121
  }
1064
1122
  declare function createApp(opts: CreateAppOptions): Promise<App>;
1065
1123
 
1066
- export { type App as A, BootRefused as B, type Container as C, DECLARATION_REFUSAL as D, type EgressPolicy as E, quoteIdent as F, scrubSecrets as G, withTables as H, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, DeclarationRefused as a, DiError as b, type DiKind as c, type ModulePressure as d, assertNoOrphanEntryPoints as e, buildContainer as f, AuthVerifier as g, type CreateAppOptions as h, isDeclarationRefused as i, type EngineConfig as j, type RequestDatabase as k, type RouteEntry as l, type RuntimeHooks as m, type SqlDriver as n, type SqlTx as o, buildRouteTable as p, createApp as q, createLazyTransaction as r, createOps as s, createRequestDatabase as t, effectiveAuth as u, hostAllowed as v, installEgressFence as w, loadConfig as x, makeMemoryCache as y, matchRoute as z };
1124
+ export { type App as A, BootRefused as B, type Container as C, DECLARATION_REFUSAL as D, type EgressPolicy as E, makeMemoryCache as F, matchRoute as G, quoteIdent as H, scrubSecrets as I, setCursorKey as J, withQueryCancellation as K, withTables as L, type ModuleClients as M, RateLimiter as R, type ScrubResult as S, DeclarationRefused as a, DiError as b, type DiKind as c, type ModulePressure as d, assertNoOrphanEntryPoints as e, buildContainer as f, AuthVerifier as g, COMMAND_EXECUTOR_DDL as h, isDeclarationRefused as i, type CreateAppOptions as j, type EngineConfig as k, type RequestDatabase as l, type RouteEntry as m, type RuntimeHooks as n, type SqlDriver as o, type SqlTx as p, buildRouteTable as q, createApp as r, createLazyTransaction as s, createOps as t, createRequestDatabase as u, effectiveAuth as v, hostAllowed as w, installCommandExecutor as x, installEgressFence as y, loadConfig as z };