turbine-orm 0.33.0 → 0.35.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 (47) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/client.js +26 -4
  3. package/dist/cjs/dialect.js +1 -0
  4. package/dist/cjs/errors.js +41 -1
  5. package/dist/cjs/index-advisor.js +0 -0
  6. package/dist/cjs/index.js +4 -2
  7. package/dist/cjs/mssql.js +5 -0
  8. package/dist/cjs/mysql.js +4 -0
  9. package/dist/cjs/optional-peer-import.cjs +28 -0
  10. package/dist/cjs/powdb-introspect.js +222 -0
  11. package/dist/cjs/powdb.js +592 -72
  12. package/dist/cjs/powql.js +998 -134
  13. package/dist/cjs/query/builder.js +72 -1
  14. package/dist/cjs/schema-builder.js +16 -0
  15. package/dist/cjs/schema-metadata.js +81 -10
  16. package/dist/cjs/sqlite.js +3 -0
  17. package/dist/client.d.ts +32 -5
  18. package/dist/client.js +26 -4
  19. package/dist/dialect.d.ts +13 -0
  20. package/dist/dialect.js +1 -0
  21. package/dist/errors.d.ts +36 -0
  22. package/dist/errors.js +39 -0
  23. package/dist/index-advisor.d.ts +15 -1
  24. package/dist/index-advisor.js +0 -0
  25. package/dist/index.d.ts +3 -3
  26. package/dist/index.js +2 -2
  27. package/dist/mssql.js +5 -0
  28. package/dist/mysql.js +4 -0
  29. package/dist/optional-peer-import.cjs +28 -0
  30. package/dist/optional-peer-import.d.cts +19 -0
  31. package/dist/powdb-introspect.d.ts +84 -0
  32. package/dist/powdb-introspect.js +219 -0
  33. package/dist/powdb.d.ts +361 -19
  34. package/dist/powdb.js +585 -72
  35. package/dist/powql.d.ts +245 -8
  36. package/dist/powql.js +1001 -137
  37. package/dist/query/builder.d.ts +36 -1
  38. package/dist/query/builder.js +72 -1
  39. package/dist/query/deferred.d.ts +6 -2
  40. package/dist/query/types.d.ts +49 -12
  41. package/dist/schema-builder.d.ts +46 -1
  42. package/dist/schema-builder.js +15 -0
  43. package/dist/schema-metadata.d.ts +13 -7
  44. package/dist/schema-metadata.js +82 -11
  45. package/dist/schema.d.ts +25 -0
  46. package/dist/sqlite.js +3 -0
  47. package/package.json +3 -3
package/dist/powdb.d.ts CHANGED
@@ -91,7 +91,21 @@ export declare class PowdbFloatParam {
91
91
  readonly value: number;
92
92
  constructor(value: number);
93
93
  }
94
- /** The four shapes a PowQL result takes over the wire. */
94
+ /**
95
+ * Marker wrapper for a JS object/array bound to a `json` document column. Both
96
+ * transports serialize `value` with `JSON.stringify` and send the text as a
97
+ * `str` param / string literal, exactly how the PowDB docs insert a json
98
+ * document (the engine validates it as JSON text and stores the canonical
99
+ * binary form). Constructed in {@link PowqlInterface.param} when the target
100
+ * column is `json` and the value is a non-null object/array; a JS string
101
+ * written to a json column passes through RAW (same contract as pg jsonb,
102
+ * pass `'"x"'` to store the JSON string `"x"`), and `null` stays `null`.
103
+ */
104
+ export declare class PowdbJsonParam {
105
+ readonly value: unknown;
106
+ constructor(value: unknown);
107
+ }
108
+ /** The four shapes a PowQL result takes over the legacy string wire. */
95
109
  type PowdbResult = {
96
110
  kind: 'rows';
97
111
  columns: string[];
@@ -106,11 +120,73 @@ type PowdbResult = {
106
120
  kind: 'message';
107
121
  message: string;
108
122
  };
123
+ /**
124
+ * Local structural mirror of `@zvndev/powdb-client`'s `WireValue` (the lossless
125
+ * typed cell of the native wire surface). Defined here rather than imported so
126
+ * the optional peer's types never leak into Turbine's published `.d.ts` (a
127
+ * consumer without the peer installed must still `tsc` cleanly, same rule as
128
+ * every other optional-peer type in this module). `empty` = an unset value
129
+ * (distinct, for a json column, from a JSON-null document, which is
130
+ * `{ type: 'json', value: null }`).
131
+ */
132
+ type PowdbWireValue = {
133
+ type: 'empty';
134
+ } | {
135
+ type: 'int';
136
+ value: bigint;
137
+ } | {
138
+ type: 'float';
139
+ value: number;
140
+ } | {
141
+ type: 'bool';
142
+ value: boolean;
143
+ } | {
144
+ type: 'str';
145
+ value: string;
146
+ } | {
147
+ type: 'datetime';
148
+ value: bigint;
149
+ } | {
150
+ type: 'uuid';
151
+ value: Uint8Array;
152
+ } | {
153
+ type: 'bytes';
154
+ value: Uint8Array;
155
+ } | {
156
+ type: 'json';
157
+ value: unknown;
158
+ pj1?: Uint8Array;
159
+ };
160
+ /** The native (lossless typed) result shape, mirroring `RawNativeQueryResult`. */
161
+ type PowdbRawNativeResult = {
162
+ kind: 'rows';
163
+ columns: string[];
164
+ rows: PowdbWireValue[][];
165
+ } | {
166
+ kind: 'scalar';
167
+ value: PowdbWireValue;
168
+ } | {
169
+ kind: 'ok';
170
+ affected: bigint;
171
+ } | {
172
+ kind: 'message';
173
+ message: string;
174
+ };
109
175
  interface PowdbClient {
110
176
  readonly serverVersion: string;
111
177
  query(query: string, params?: PowdbParam[], opts?: {
112
178
  signal?: AbortSignal;
113
179
  }): Promise<PowdbResult>;
180
+ /**
181
+ * Lossless typed wire surface (client ≥ 0.13, server ≥ 0.13). Optional: an
182
+ * older client omits it, so every call site feature-detects
183
+ * `typeof c.queryNativeRaw === 'function'` before using it and falls back to
184
+ * {@link query}. It NEVER retries as a legacy query, replaying an ambiguous
185
+ * mutation is unsafe, so its use is additionally version-gated server-side.
186
+ */
187
+ queryNativeRaw?(query: string, params?: PowdbParam[], opts?: {
188
+ signal?: AbortSignal;
189
+ }): Promise<PowdbRawNativeResult>;
114
190
  close(): Promise<void>;
115
191
  }
116
192
  interface PowdbClientPool {
@@ -159,16 +235,80 @@ export declare function parsePowdbUrl(connectionString: string): PowdbConnOption
159
235
  * prove it is too old).
160
236
  */
161
237
  export declare function assertSupportedPowdbVersion(version: string | undefined): void;
162
- /** PowQL column types Turbine is willing to emit (the four writable scalars). */
163
- export type PowqlType = 'str' | 'int' | 'float' | 'bool';
238
+ /**
239
+ * Feature capabilities of a bound PowDB connection. Resolved once (from the
240
+ * probed server version on the networked transport, or the addon package
241
+ * version on embedded) and carried on the pool so {@link PowqlInterface} can
242
+ * gate PowQL features that only exist on newer engines, an old engine gets a
243
+ * typed {@link UnsupportedFeatureError} (E017) with a version hint instead of a
244
+ * raw PowQL parse error.
245
+ */
246
+ export interface PowdbCapabilities {
247
+ /** Best-known engine version (e.g. `'0.13.0'`), or `null` when unknowable. */
248
+ engineVersion: string | null;
249
+ /** ≥ 0.12: `json` column type, `->` path filters / ordering / grouping. */
250
+ jsonDocs: boolean;
251
+ /** ≥ 0.13: `alter T add index (.col->seg)` expression indexes. */
252
+ docFieldIndexes: boolean;
253
+ /** ≥ 0.10: `schema` / `describe` introspection statements. */
254
+ introspection: boolean;
255
+ /** ≥ 0.13: server-side joins, hash-accelerated and bounded. */
256
+ serverJoins: boolean;
257
+ /** Networked only: server ≥ 0.13 AND the client exposes `queryNativeRaw`. */
258
+ nativeRaw: boolean;
259
+ }
260
+ /** The feature-gate capability keys (everything except the version/nativeRaw metadata). */
261
+ type PowdbFeatureKey = 'jsonDocs' | 'docFieldIndexes' | 'introspection' | 'serverJoins';
262
+ /**
263
+ * Trusted-caller default: every FEATURE gate on, engine version unknown. Used
264
+ * for a directly-constructed {@link PowdbPool} / {@link PowdbEmbeddedPool} that
265
+ * did not go through {@link turbinePowDB}'s version probe (e.g. an injected
266
+ * pool, or a unit-test pool). `nativeRaw` stays OFF here because it flips the
267
+ * actual wire path and must only be enabled after a real server-version probe,
268
+ * never inferred from a bare construction.
269
+ */
270
+ export declare const ALL_POWDB_CAPABILITIES: PowdbCapabilities;
271
+ /**
272
+ * Derive {@link PowdbCapabilities} from an engine version string. A non-semver /
273
+ * unknown version turns every gate OFF (the E017 hint then tells the caller to
274
+ * upgrade or pass `assumeEngineVersion`). `nativeRaw` requires BOTH the client
275
+ * to expose `queryNativeRaw` (passed in) AND server ≥ 0.13.
276
+ */
277
+ export declare function capabilitiesFromVersion(version: string | undefined | null, opts?: {
278
+ hasNativeRaw?: boolean;
279
+ }): PowdbCapabilities;
280
+ /**
281
+ * Throw a version-hinting {@link UnsupportedFeatureError} (E017) when a gated
282
+ * PowQL feature is used on an engine that does not support it. Keeps old engines
283
+ * getting clean typed errors instead of raw PowQL parse failures.
284
+ */
285
+ export declare function requireCapability(caps: PowdbCapabilities, key: PowdbFeatureKey, feature: string): void;
286
+ /**
287
+ * PowQL column types Turbine emits: the four writable scalars plus PowDB's
288
+ * native `json` document type (added to the map in the 0.12/0.13 parity round,
289
+ * see {@link isJsonColumn}). A `json` column stores a canonical binary document
290
+ * (sorted keys, int/float distinction preserved) that Turbine writes as a JSON
291
+ * string literal and reads back by parsing the canonical JSON text.
292
+ */
293
+ export type PowqlType = 'str' | 'int' | 'float' | 'bool' | 'json';
294
+ /**
295
+ * Does this column map to PowDB's native `json` document type? A Postgres
296
+ * `json`/`jsonb` type (via `dialectType`/`pgType`) is authoritative; otherwise
297
+ * the tsType heuristic (`Record<…>`, `object`, `unknown`, an object/array
298
+ * literal) that the four scalar branches do not claim. Array columns never map
299
+ * to json, a PowDB array only exists INSIDE a json document, so a Postgres
300
+ * array column has no PowDB shape and still throws in {@link powqlColumnType}.
301
+ */
302
+ export declare function isJsonColumn(col: ColumnMetadata): boolean;
164
303
  /**
165
304
  * Map a Turbine column to the PowQL DDL type used in `defineSchema` →
166
305
  * `type T { … }`. Turbine never emits PowDB's `uuid`/`datetime`/`bytes` types,
167
306
  * which cannot hold client-supplied values on the wire (no literal, no cast):
168
307
  * - `Date` → `int` (epoch micros) - `boolean` → `bool`
169
308
  * - integral `number`/`bigint` → `int` - fractional `number` → `float`
309
+ * - JSON / object columns → `json` (native PowDB document type, ≥ 0.12)
170
310
  * - everything else (incl. UUID/PK strings) → `str`
171
- * Array / JSON / bytes columns throw they have no PowDB equivalent.
311
+ * Array (non-json) and bytes columns throw, they have no PowDB equivalent.
172
312
  */
173
313
  export declare function powqlColumnType(col: ColumnMetadata): PowqlType;
174
314
  /**
@@ -198,7 +338,18 @@ export declare const POWQL_KEYWORDS: ReadonlySet<string>;
198
338
  * errors when emitted bare, so quoting is strictly an improvement.
199
339
  */
200
340
  export declare function quotePowqlIdent(name: string): string;
201
- export declare function powqlSchemaDDL(schema: SchemaMetadata): string[];
341
+ /**
342
+ * Options for {@link powqlSchemaDDL}. Additive: with no options the DDL is
343
+ * emitted unconditionally (pure-function callers / tests); pass `capabilities`
344
+ * to gate engine-version-specific features (json columns, and, since the
345
+ * 0.13 parity round, doc-field expression indexes) behind the connection's
346
+ * real capabilities. Doc-field expression index declarations plug into the
347
+ * per-table `indexes` surface consumed here without further signature churn.
348
+ */
349
+ export interface PowqlSchemaDDLOptions {
350
+ capabilities?: PowdbCapabilities;
351
+ }
352
+ export declare function powqlSchemaDDL(schema: SchemaMetadata, opts?: PowqlSchemaDDLOptions): string[];
202
353
  /**
203
354
  * Coerce a single PowDB wire string into the JS value its column type implies.
204
355
  * Every PowDB value arrives as a string; NULL arrives as the bareword `"null"`.
@@ -206,12 +357,26 @@ export declare function powqlSchemaDDL(schema: SchemaMetadata): string[];
206
357
  */
207
358
  export declare function coerceValue(raw: string, col: ColumnMetadata): unknown;
208
359
  /**
209
- * Map one raw PowDB row (snake-cased columns raw wire strings, as produced by
210
- * {@link PowdbPool}) into a typed entity (camelCase fields, coerced values).
211
- * Only the columns present in `raw` are emitted, so partial `select` projections
212
- * round-trip unchanged.
360
+ * Coerce a single cell that arrived over the NATIVE typed wire (decoded from a
361
+ * {@link PowdbWireValue}, so already a JS `bigint`/`number`/`boolean`/`string`/
362
+ * `NativeJson`/`Uint8Array`/`null`, never a bare `"null"` string). Unlike
363
+ * {@link coerceValue} this NEVER collapses the string `"null"` to `null`: an
364
+ * absent value already decoded to `null` (from the `empty` cell), so a genuine
365
+ * str `"null"` stays the string `"null"` (fixes the legacy-wire wart on the
366
+ * native transport). `datetime`-shaped cells (int micros) become `Date`; a
367
+ * bigint on a `number` column follows the int8 safe-integer policy.
368
+ */
369
+ export declare function coerceNativeValue(value: unknown, col: ColumnMetadata): unknown;
370
+ /**
371
+ * Map one raw PowDB row into a typed entity (camelCase fields, coerced values).
372
+ * Only the columns present in `raw` are emitted, so partial `select`
373
+ * projections round-trip unchanged. `native` selects the coercion policy: the
374
+ * default `false` handles the legacy string wire (every cell is a string, via
375
+ * {@link coerceValue}); `true` handles the native typed wire, where non-string
376
+ * cells arrive pre-typed and go through {@link coerceNativeValue} (see F3).
377
+ * Callers on the native transport pass `this.pool.capabilities.nativeRaw`.
213
378
  */
214
- export declare function rowToEntity(raw: Record<string, unknown>, meta: TableMetadata): Record<string, unknown>;
379
+ export declare function rowToEntity(raw: Record<string, unknown>, meta: TableMetadata, native?: boolean): Record<string, unknown>;
215
380
  /**
216
381
  * Translate a PowDB error into a typed Turbine error. Handles BOTH transports,
217
382
  * whose error shapes differ:
@@ -226,6 +391,16 @@ export declare function rowToEntity(raw: Record<string, unknown>, meta: TableMet
226
391
  * fire for both transports), then fall through to the networked `.code` switch.
227
392
  */
228
393
  export declare function wrapPowdbError(err: unknown): Error;
394
+ /**
395
+ * True when `err` is the stale-wire-frame {@link ConnectionError} produced by
396
+ * {@link wrapPowdbError} (its `.cause` is a `protocol_error` PowDBError, or the
397
+ * message carries the invalid-state signature). The opt-in read retry
398
+ * (`retryStaleReads`, evaluated in {@link PowqlInterface}'s exec seam) uses this
399
+ * to decide whether a first-statement READ may be replayed once on a fresh
400
+ * connection; writes are NEVER retried (an ambiguous mutation reply is unsafe
401
+ * to replay, matching the client's own native-path policy).
402
+ */
403
+ export declare function isStaleFramePowdbError(err: unknown): boolean;
229
404
  type QueryArg = string | {
230
405
  name?: string;
231
406
  text: string;
@@ -251,12 +426,40 @@ export interface PowdbPoolOptions {
251
426
  * *after* the transaction has begun.
252
427
  */
253
428
  transactionQueueTimeoutMs?: number;
429
+ /**
430
+ * Feature capabilities of the bound connection. Set by {@link turbinePowDB}
431
+ * from the probed engine version; defaults to {@link ALL_POWDB_CAPABILITIES}
432
+ * (feature gates on, `nativeRaw` off, engine version unknown) for a
433
+ * directly-constructed pool: a "trusted caller".
434
+ */
435
+ capabilities?: PowdbCapabilities;
436
+ /**
437
+ * Opt in to replaying a first-statement READ once, on a fresh connection,
438
+ * when it fails with the stale-wire-frame {@link ConnectionError} (see
439
+ * {@link isStaleFramePowdbError}). Networked only; writes and mid-transaction
440
+ * statements are NEVER retried. Read by {@link PowqlInterface}'s exec seam.
441
+ * Default `false` (typed-error-only).
442
+ */
443
+ retryStaleReads?: boolean;
444
+ /**
445
+ * Mark this pool read-only: {@link PowqlInterface}'s exec seam then fails a
446
+ * write (or a tx-control `begin`) fast with a {@link ReadOnlyError} (E018)
447
+ * before it reaches the wire. An `{ embedded, readonly: true }` target forces
448
+ * this true; a networked pool bound to a read-only role can also set it so
449
+ * writes are rejected locally instead of round-tripping to the engine's
450
+ * refusal. Default `false`.
451
+ */
452
+ readonly?: boolean;
254
453
  }
255
454
  /**
256
455
  * A {@link PgCompatPool} backed by a `@zvndev/powdb-client` `Pool`. The query
257
- * `text` is **PowQL**, not SQL {@link PowqlInterface} generates it. Rows come
258
- * back as raw strings here; per-column JS coercion happens in `PowqlInterface`
259
- * (it owns the schema metadata).
456
+ * `text` is **PowQL**, not SQL ({@link PowqlInterface} generates it). On the
457
+ * legacy string wire cells come back as strings; when `capabilities.nativeRaw`
458
+ * is set (server 0.13 + a client exposing `queryNativeRaw`) this pool routes
459
+ * through the typed native wire instead, so cells arrive pre-typed (a json int
460
+ * as `bigint`, etc.) and each result is tagged with the wire that served it
461
+ * ({@link PowdbTaggedResult}). Per-column JS coercion still happens in
462
+ * `PowqlInterface` (it owns the schema metadata), keyed on that per-result tag.
260
463
  */
261
464
  export declare class PowdbPool implements PgCompatPool {
262
465
  readonly pool: PowdbClientPool;
@@ -280,7 +483,25 @@ export declare class PowdbPool implements PgCompatPool {
280
483
  * live socket holding the process open until the server's idle timeout.
281
484
  */
282
485
  private readonly checkedOut;
486
+ /** Feature capabilities of the bound server (probed version + native-wire feature-detect). */
487
+ readonly capabilities: PowdbCapabilities;
488
+ /** Opt-in first-statement-read replay on a stale wire frame (read by {@link PowqlInterface}). */
489
+ readonly retryStaleReads: boolean;
490
+ /**
491
+ * True when the caller marked this pool read-only (`readonly: true`). Read by
492
+ * {@link PowqlInterface}'s exec seam to fail writes fast with E018 before the
493
+ * wire; the engine's own read-only-role refusal (mapped by
494
+ * {@link wrapPowdbError}) is the backstop for raw / injected paths.
495
+ */
496
+ readonly readonly: boolean;
283
497
  constructor(pool: PowdbClientPool, toParam?: (v: unknown, i: number) => PowdbParam, options?: PowdbPoolOptions);
498
+ /**
499
+ * Run one statement on `c`, choosing the lossless native typed wire when the
500
+ * server supports it (`capabilities.nativeRaw`) AND this client exposes
501
+ * `queryNativeRaw` (a defensive per-call feature-detect, so a heterogeneous
502
+ * injected pool cannot crash). Otherwise the legacy string wire, unchanged.
503
+ */
504
+ private runOnClient;
284
505
  query(text: QueryArg, values?: unknown[]): Promise<any>;
285
506
  /**
286
507
  * Typed guard mirroring {@link PowdbEmbeddedPool}: after `end()` the driver
@@ -300,6 +521,14 @@ interface EmbeddedQueryResult {
300
521
  affected?: bigint;
301
522
  message?: string;
302
523
  }
524
+ /**
525
+ * The embedded addon's native typed result (`@zvndev/powdb-embedded` ≥ 0.14).
526
+ * Mirrors {@link PowdbRawNativeResult}, the same tagged {@link PowdbWireValue}
527
+ * cells (embedded `bytes` arrive as a `Buffer`, which IS a `Uint8Array`, so the
528
+ * decode path is unchanged), including the `message` kind for DDL / status
529
+ * replies, which {@link adaptNativeResult}'s default branch handles at runtime.
530
+ */
531
+ type EmbeddedNativeResult = PowdbRawNativeResult;
303
532
  /** A single in-process embedded database handle (`@zvndev/powdb-embedded`). */
304
533
  interface EmbeddedDatabase {
305
534
  query(powql: string): EmbeddedQueryResult;
@@ -308,6 +537,32 @@ interface EmbeddedDatabase {
308
537
  isPoisoned(): boolean;
309
538
  /** WAL durability selector — `@zvndev/powdb-embedded` ≥ 0.7.1. */
310
539
  setSyncMode?(mode: string): void;
540
+ /**
541
+ * Lossless typed native wire (`@zvndev/powdb-embedded` ≥ 0.14). All optional
542
+ * and feature-detected: an older addon omits them, so {@link PowdbEmbeddedPool}
543
+ * falls back to {@link materializePowql} + {@link query}. `queryWithParams`
544
+ * binds positional `$N` params as {@link PowdbParam} values (the NativeParam
545
+ * union) instead of materializing literals.
546
+ */
547
+ queryNative?(powql: string): EmbeddedNativeResult;
548
+ queryReadonlyNative?(powql: string): EmbeddedNativeResult;
549
+ queryWithParams?(powql: string, params: PowdbParam[]): EmbeddedNativeResult;
550
+ /** Checkpoint-flushing close (`@zvndev/powdb-embedded` ≥ 0.14). Optional (feature-detected). */
551
+ close?(): void;
552
+ }
553
+ interface EmbeddedModule {
554
+ Database: {
555
+ open(dir: string): EmbeddedDatabase;
556
+ /** Open with a per-query memory budget — `@zvndev/powdb-embedded` ≥ 0.7.1. */
557
+ openWithMemoryLimit?(dir: string, limitBytes: number): EmbeddedDatabase;
558
+ /**
559
+ * Open a read-only handle for snapshot serving (`@zvndev/powdb-embedded` ≥
560
+ * 0.14). Optional (feature-detected); a write through such a handle is
561
+ * refused with `readonly mode: statement requires a writer …` (→ E018).
562
+ */
563
+ openReadOnly?(dir: string): EmbeddedDatabase;
564
+ openReadOnlyWithMemoryLimit?(dir: string, limitBytes: number): EmbeddedDatabase;
565
+ };
311
566
  }
312
567
  /**
313
568
  * Encode a JS value as a **PowQL literal** for the embedded driver, which takes
@@ -336,10 +591,14 @@ export declare function encodePowqlLiteral(value: unknown): string;
336
591
  export declare function materializePowql(powql: string, params: unknown[]): string;
337
592
  /**
338
593
  * A {@link PgCompatPool} backed by an in-process `@zvndev/powdb-embedded`
339
- * `Database`. The embedded addon takes **no params array** its `query(powql)`
340
- * accepts only a string — so this pool materializes each positional `$N` into a
341
- * PowQL literal via {@link materializePowql} before handing the text to the
342
- * engine. One handle, single connection: transaction keywords (`begin`/`commit`/
594
+ * `Database`. On the addon's typed native wire (≥ 0.14, when
595
+ * `capabilities.nativeRaw` is set) this pool binds positional `$N` params via
596
+ * `queryWithParams` and decodes the typed cells, exactly like the networked
597
+ * transport. On an older addon (no `queryWithParams`) it falls back to the
598
+ * legacy string wire, which takes **no params array** (its `query(powql)`
599
+ * accepts only a string), so each positional `$N` is materialized into a PowQL
600
+ * literal via {@link materializePowql} before the text is handed to the engine.
601
+ * One handle, single connection: transaction keywords (`begin`/`commit`/
343
602
  * `rollback`) are issued serially as ordinary queries.
344
603
  */
345
604
  export declare class PowdbEmbeddedPool implements PgCompatPool {
@@ -358,8 +617,26 @@ export declare class PowdbEmbeddedPool implements PgCompatPool {
358
617
  private readonly txGate;
359
618
  /** Hold taken by a `begin` issued via `query()` directly (no checked-out client). */
360
619
  private readonly poolHoldRef;
620
+ /**
621
+ * Feature capabilities of the embedded engine (resolved from the addon
622
+ * package version). `nativeRaw` is true when the addon is ≥ 0.14 and the
623
+ * opened handle exposes `queryWithParams` (the typed native wire); an older
624
+ * addon has no such method, so it stays false and the legacy string wire is
625
+ * used.
626
+ */
627
+ readonly capabilities: PowdbCapabilities;
628
+ /** Carried for surface uniformity with {@link PowdbPool}; inert on embedded (no protocol_error frames). */
629
+ readonly retryStaleReads: boolean;
630
+ /**
631
+ * True when this pool was opened read-only (an `{ embedded, readonly: true }`
632
+ * target, or a directly-constructed pool passed `readonly: true`). Read by
633
+ * {@link PowqlInterface}'s exec seam to fail writes fast with E018 before the
634
+ * wire; the engine's own refusal (mapped by {@link wrapPowdbError}) is the
635
+ * backstop for raw / injected paths.
636
+ */
637
+ readonly readonly: boolean;
361
638
  constructor(db: EmbeddedDatabase, options?: PowdbPoolOptions);
362
- /** Materialize `$N` params and hand the PowQL to the in-process engine. */
639
+ /** Run the PowQL on the in-process engine, choosing the native or legacy wire. */
363
640
  private exec;
364
641
  /**
365
642
  * Run one statement, gating transaction control. `holdRef` scopes the gate
@@ -372,9 +649,20 @@ export declare class PowdbEmbeddedPool implements PgCompatPool {
372
649
  connect(): Promise<PgCompatPoolClient>;
373
650
  end(): Promise<void>;
374
651
  }
652
+ export { introspectPowdbDatabase, type PowdbExec, type PowdbIntrospectOptions, } from './powdb-introspect.js';
375
653
  export { PowqlInterface } from './powql.js';
376
654
  /** Options for {@link turbinePowDB}. */
377
- export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited'> {
655
+ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited' | 'relationLoadStrategy'> {
656
+ /**
657
+ * Client-level default `with`-relation load strategy. On PowDB the default is
658
+ * the batched N+1 loaders; setting `'join'` opts INTO native PowQL server-side
659
+ * joins for eligible top-level relations (ineligible ones, e.g. a paged parent
660
+ * or a nested `with`, fall back to the loaders per-relation and silently). A
661
+ * per-query `relationLoadStrategy` arg still overrides this. Requires an engine
662
+ * that advertises `serverJoins` (PowDB ≥ 0.13); a per-query `'join'` on an
663
+ * older engine throws E017, a client-level default silently falls back.
664
+ */
665
+ relationLoadStrategy?: TurbineConfig['relationLoadStrategy'];
378
666
  /** Max pooled connections (default 10). Networked transport only. */
379
667
  connectionLimit?: number;
380
668
  /**
@@ -385,8 +673,44 @@ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'de
385
673
  * transactions queue and run one at a time; only a re-entrant
386
674
  * `db.$transaction` (opened inside an active transaction callback) throws
387
675
  * E017 — queueing that shape would deadlock.
676
+ *
677
+ * Ignored when you inject an already-constructed {@link PowdbPool} (it carries
678
+ * its own {@link PowdbPoolOptions}); set it on that pool's constructor instead.
388
679
  */
389
680
  transactionQueueTimeoutMs?: number;
681
+ /**
682
+ * Opt in to replaying a first-statement READ once (on a fresh connection)
683
+ * when it fails with the stale-wire-frame {@link ConnectionError} that a
684
+ * request can hit after a long idle gap. Networked only; WRITES and any
685
+ * statement inside a transaction are NEVER retried (an ambiguous mutation
686
+ * reply is unsafe to replay). Default `false`: the error is surfaced typed
687
+ * so the caller can decide. See the retry recipe in the docs.
688
+ *
689
+ * Ignored when you inject an already-constructed {@link PowdbPool} (it carries
690
+ * its own {@link PowdbPoolOptions}); set it on that pool's constructor instead.
691
+ */
692
+ retryStaleReads?: boolean;
693
+ /**
694
+ * Override the detected engine version used for capability gating. For exotic
695
+ * deployments and injected pools whose version cannot be probed (a non-semver
696
+ * server string, or an addon whose package.json cannot be resolved): pass
697
+ * e.g. `'0.13.0'` to unlock the features that version supports. Without it, an
698
+ * undetectable version turns every version-gated feature OFF (with a hinting
699
+ * E017).
700
+ */
701
+ assumeEngineVersion?: string;
702
+ /**
703
+ * Mark the client read-only: a write (or a transaction `begin`) fails fast
704
+ * locally with a {@link ReadOnlyError} (E018) before it reaches the wire,
705
+ * rather than round-tripping to the engine's refusal. Works on both
706
+ * transports (a networked pool bound to a read-only role, or an embedded
707
+ * handle). An `{ embedded, readonly: true }` target implies this. Default
708
+ * `false`.
709
+ *
710
+ * Ignored when you inject an already-constructed {@link PowdbPool} (it carries
711
+ * its own {@link PowdbPoolOptions}); set it on that pool's constructor instead.
712
+ */
713
+ readonly?: boolean;
390
714
  /**
391
715
  * Driver-module injection for the networked target forms (URL / host+port):
392
716
  * bypasses the dynamic `import('@zvndev/powdb-client')` and uses this object
@@ -394,6 +718,14 @@ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'de
394
718
  * connections) and advanced embedding; everyday callers never set it.
395
719
  */
396
720
  powdbClientModule?: PowdbModule;
721
+ /**
722
+ * Driver-module injection for the **embedded** target form (`{ embedded }`):
723
+ * bypasses the dynamic `import('@zvndev/powdb-embedded')` and uses this object
724
+ * as the addon module instead. Intended for tests (a fake `Database` factory
725
+ * that records how the handle was opened) and advanced embedding; everyday
726
+ * callers never set it. The symmetric counterpart to {@link powdbClientModule}.
727
+ */
728
+ powdbEmbeddedModule?: EmbeddedModule;
397
729
  }
398
730
  /**
399
731
  * Selects the **embedded** transport — an in-process `@zvndev/powdb-embedded`
@@ -419,6 +751,16 @@ export interface TurbinePowdbEmbeddedTarget {
419
751
  syncMode?: 'full' | 'normal' | 'off';
420
752
  /** Per-query memory budget in bytes (requires `@zvndev/powdb-embedded` ≥ 0.7.1). */
421
753
  memoryLimit?: number;
754
+ /**
755
+ * Open the data directory read-only for snapshot serving (requires
756
+ * `@zvndev/powdb-embedded` ≥ 0.14: `openReadOnly` / `openReadOnlyWithMemoryLimit`).
757
+ * A write through a read-only handle is refused by the engine with
758
+ * `readonly mode: statement requires a writer …` (→ {@link ReadOnlyError}, E018),
759
+ * and Turbine additionally fails writes fast locally (this implies the pool's
760
+ * `readonly` flag). Meaningless together with `syncMode` (a read-only engine
761
+ * never writes), setting both throws a {@link ValidationError}.
762
+ */
763
+ readonly?: boolean;
422
764
  }
423
765
  /**
424
766
  * Bind Turbine to PowDB. `target` is one of: