turbine-orm 0.81.0 → 0.90.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 (295) hide show
  1. package/README.md +26 -25
  2. package/dist/checkout.d.ts +27 -2
  3. package/dist/checkout.js +75 -4
  4. package/dist/cjs/checkout.d.ts +27 -2
  5. package/dist/cjs/checkout.js +76 -3
  6. package/dist/cjs/cli/code-first-tags.d.ts +62 -0
  7. package/dist/cjs/cli/code-first-tags.js +124 -0
  8. package/dist/cjs/cli/compile-query.js +9 -0
  9. package/dist/cjs/cli/config.d.ts +35 -6
  10. package/dist/cjs/cli/config.js +17 -1
  11. package/dist/cjs/cli/destructive.d.ts +7 -4
  12. package/dist/cjs/cli/destructive.js +224 -20
  13. package/dist/cjs/cli/error-catalog.js +36 -0
  14. package/dist/cjs/cli/error-text.d.ts +15 -0
  15. package/dist/cjs/cli/error-text.js +40 -0
  16. package/dist/cjs/cli/index.d.ts +62 -4
  17. package/dist/cjs/cli/index.js +568 -108
  18. package/dist/cjs/cli/local-server-guard.d.ts +53 -0
  19. package/dist/cjs/cli/local-server-guard.js +119 -0
  20. package/dist/cjs/cli/local-server-output.d.ts +41 -0
  21. package/dist/cjs/cli/local-server-output.js +103 -0
  22. package/dist/cjs/cli/mcp.d.ts +8 -0
  23. package/dist/cjs/cli/mcp.js +121 -32
  24. package/dist/cjs/cli/migrate.d.ts +113 -5
  25. package/dist/cjs/cli/migrate.js +270 -21
  26. package/dist/cjs/cli/observe.d.ts +6 -1
  27. package/dist/cjs/cli/observe.js +85 -55
  28. package/dist/cjs/cli/pii-predicate-guard.d.ts +43 -23
  29. package/dist/cjs/cli/pii-predicate-guard.js +98 -37
  30. package/dist/cjs/cli/prisma-resolve.js +17 -13
  31. package/dist/cjs/cli/prisma-schema.js +12 -4
  32. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  33. package/dist/cjs/cli/studio.d.ts +14 -2
  34. package/dist/cjs/cli/studio.js +244 -69
  35. package/dist/cjs/cli/ui.d.ts +1 -1
  36. package/dist/cjs/client.d.ts +558 -85
  37. package/dist/cjs/client.js +1270 -395
  38. package/dist/cjs/connection-guard.d.ts +13 -0
  39. package/dist/cjs/connection-guard.js +28 -0
  40. package/dist/cjs/connection-url.d.ts +51 -16
  41. package/dist/cjs/connection-url.js +67 -34
  42. package/dist/cjs/diagnostics.d.ts +81 -0
  43. package/dist/cjs/diagnostics.js +147 -0
  44. package/dist/cjs/dialect.d.ts +449 -20
  45. package/dist/cjs/dialect.js +117 -76
  46. package/dist/cjs/engine-config.d.ts +7 -6
  47. package/dist/cjs/errors.d.ts +126 -6
  48. package/dist/cjs/errors.js +256 -13
  49. package/dist/cjs/generate.d.ts +57 -7
  50. package/dist/cjs/generate.js +635 -279
  51. package/dist/cjs/global-filters-config.d.ts +59 -0
  52. package/dist/cjs/global-filters-config.js +367 -0
  53. package/dist/cjs/index.d.ts +9 -4
  54. package/dist/cjs/index.js +10 -2
  55. package/dist/cjs/introspect.d.ts +31 -6
  56. package/dist/cjs/introspect.js +108 -50
  57. package/dist/cjs/log-codes.d.ts +12 -0
  58. package/dist/cjs/log-codes.js +77 -0
  59. package/dist/cjs/logger.d.ts +87 -0
  60. package/dist/cjs/logger.js +98 -0
  61. package/dist/cjs/mssql.d.ts +8 -5
  62. package/dist/cjs/mssql.js +352 -65
  63. package/dist/cjs/mysql.d.ts +3 -5
  64. package/dist/cjs/mysql.js +420 -70
  65. package/dist/cjs/nested-write.d.ts +3 -0
  66. package/dist/cjs/nested-write.js +179 -114
  67. package/dist/cjs/pipeline-submittable.d.ts +12 -0
  68. package/dist/cjs/pipeline-submittable.js +118 -44
  69. package/dist/cjs/pipeline.js +21 -26
  70. package/dist/cjs/powdb-introspect.d.ts +5 -2
  71. package/dist/cjs/powdb-introspect.js +20 -15
  72. package/dist/cjs/powdb-shared.d.ts +36 -5
  73. package/dist/cjs/powdb-shared.js +67 -8
  74. package/dist/cjs/powdb.d.ts +4 -0
  75. package/dist/cjs/powdb.js +183 -51
  76. package/dist/cjs/powql.d.ts +394 -51
  77. package/dist/cjs/powql.js +1829 -284
  78. package/dist/cjs/prisma-compat.d.ts +23 -15
  79. package/dist/cjs/prisma-compat.js +507 -372
  80. package/dist/cjs/query/aggregates.d.ts +17 -1
  81. package/dist/cjs/query/aggregates.js +261 -29
  82. package/dist/cjs/query/batched-loader.d.ts +25 -6
  83. package/dist/cjs/query/batched-loader.js +103 -20
  84. package/dist/cjs/query/builder.d.ts +325 -184
  85. package/dist/cjs/query/builder.js +1452 -490
  86. package/dist/cjs/query/column-ref.d.ts +61 -0
  87. package/dist/cjs/query/column-ref.js +73 -0
  88. package/dist/cjs/query/compound-unique.d.ts +21 -0
  89. package/dist/cjs/query/compound-unique.js +98 -18
  90. package/dist/cjs/query/deferred.d.ts +53 -16
  91. package/dist/cjs/query/expr.d.ts +217 -0
  92. package/dist/cjs/query/expr.js +859 -0
  93. package/dist/cjs/query/filters.d.ts +155 -53
  94. package/dist/cjs/query/filters.js +613 -76
  95. package/dist/cjs/query/index.d.ts +2 -0
  96. package/dist/cjs/query/index.js +7 -1
  97. package/dist/cjs/query/json-value.d.ts +43 -0
  98. package/dist/cjs/query/json-value.js +59 -0
  99. package/dist/cjs/query/normalize-args.d.ts +87 -0
  100. package/dist/cjs/query/normalize-args.js +527 -0
  101. package/dist/cjs/query/option-surface.d.ts +16 -4
  102. package/dist/cjs/query/option-surface.js +57 -14
  103. package/dist/cjs/query/relation-names.d.ts +14 -0
  104. package/dist/cjs/query/relation-names.js +77 -18
  105. package/dist/cjs/query/relations.d.ts +30 -3
  106. package/dist/cjs/query/relations.js +219 -63
  107. package/dist/cjs/query/row-lock.d.ts +49 -0
  108. package/dist/cjs/query/row-lock.js +138 -0
  109. package/dist/cjs/query/sql-comment.d.ts +161 -0
  110. package/dist/cjs/query/sql-comment.js +381 -0
  111. package/dist/cjs/query/stream-idle.d.ts +132 -0
  112. package/dist/cjs/query/stream-idle.js +212 -0
  113. package/dist/cjs/query/table-client.d.ts +101 -0
  114. package/dist/cjs/query/table-client.js +74 -0
  115. package/dist/cjs/query/types.d.ts +677 -298
  116. package/dist/cjs/query/utils.d.ts +105 -6
  117. package/dist/cjs/query/utils.js +426 -19
  118. package/dist/cjs/query/warn-registry.d.ts +18 -0
  119. package/dist/cjs/query/warn-registry.js +18 -0
  120. package/dist/cjs/query/where-compile.d.ts +138 -14
  121. package/dist/cjs/query/where-compile.js +513 -29
  122. package/dist/cjs/query/where.d.ts +159 -59
  123. package/dist/cjs/query/where.js +716 -246
  124. package/dist/cjs/query/writes.d.ts +112 -50
  125. package/dist/cjs/query/writes.js +1498 -200
  126. package/dist/cjs/query-events.d.ts +4 -2
  127. package/dist/cjs/query-events.js +3 -1
  128. package/dist/cjs/realtime.d.ts +7 -3
  129. package/dist/cjs/realtime.js +13 -2
  130. package/dist/cjs/schema-builder.d.ts +55 -4
  131. package/dist/cjs/schema-builder.js +382 -18
  132. package/dist/cjs/schema-metadata.d.ts +21 -1
  133. package/dist/cjs/schema-metadata.js +29 -7
  134. package/dist/cjs/schema-sql.d.ts +75 -5
  135. package/dist/cjs/schema-sql.js +497 -45
  136. package/dist/cjs/schema.d.ts +26 -5
  137. package/dist/cjs/schema.js +110 -13
  138. package/dist/cjs/sql-placeholders.d.ts +56 -0
  139. package/dist/cjs/sql-placeholders.js +204 -0
  140. package/dist/cjs/sqlite.d.ts +75 -20
  141. package/dist/cjs/sqlite.js +593 -95
  142. package/dist/cjs/supplied-pool-types.d.ts +48 -0
  143. package/dist/cjs/supplied-pool-types.js +256 -0
  144. package/dist/cjs/transaction-control.d.ts +51 -0
  145. package/dist/cjs/transaction-control.js +316 -0
  146. package/dist/cjs/transaction-state.d.ts +342 -0
  147. package/dist/cjs/transaction-state.js +707 -0
  148. package/dist/cjs/typed-sql.d.ts +37 -3
  149. package/dist/cjs/typed-sql.js +102 -8
  150. package/dist/cli/code-first-tags.d.ts +62 -0
  151. package/dist/cli/code-first-tags.js +120 -0
  152. package/dist/cli/compile-query.js +9 -0
  153. package/dist/cli/config.d.ts +35 -6
  154. package/dist/cli/config.js +16 -1
  155. package/dist/cli/destructive.d.ts +7 -4
  156. package/dist/cli/destructive.js +224 -20
  157. package/dist/cli/error-catalog.js +36 -0
  158. package/dist/cli/error-text.d.ts +15 -0
  159. package/dist/cli/error-text.js +37 -0
  160. package/dist/cli/index.d.ts +62 -4
  161. package/dist/cli/index.js +569 -112
  162. package/dist/cli/local-server-guard.d.ts +53 -0
  163. package/dist/cli/local-server-guard.js +113 -0
  164. package/dist/cli/local-server-output.d.ts +41 -0
  165. package/dist/cli/local-server-output.js +98 -0
  166. package/dist/cli/mcp.d.ts +8 -0
  167. package/dist/cli/mcp.js +121 -34
  168. package/dist/cli/migrate.d.ts +113 -5
  169. package/dist/cli/migrate.js +261 -21
  170. package/dist/cli/observe.d.ts +6 -1
  171. package/dist/cli/observe.js +84 -54
  172. package/dist/cli/pii-predicate-guard.d.ts +43 -23
  173. package/dist/cli/pii-predicate-guard.js +100 -35
  174. package/dist/cli/prisma-resolve.js +15 -11
  175. package/dist/cli/prisma-schema.js +12 -4
  176. package/dist/cli/studio-ui.generated.js +1 -1
  177. package/dist/cli/studio.d.ts +14 -2
  178. package/dist/cli/studio.js +246 -71
  179. package/dist/cli/ui.d.ts +1 -1
  180. package/dist/client.d.ts +558 -85
  181. package/dist/client.js +1275 -400
  182. package/dist/connection-guard.d.ts +13 -0
  183. package/dist/connection-guard.js +27 -0
  184. package/dist/connection-url.d.ts +51 -16
  185. package/dist/connection-url.js +63 -34
  186. package/dist/diagnostics.d.ts +81 -0
  187. package/dist/diagnostics.js +144 -0
  188. package/dist/dialect.d.ts +449 -20
  189. package/dist/dialect.js +115 -43
  190. package/dist/engine-config.d.ts +7 -6
  191. package/dist/errors.d.ts +126 -6
  192. package/dist/errors.js +252 -12
  193. package/dist/generate.d.ts +57 -7
  194. package/dist/generate.js +635 -281
  195. package/dist/global-filters-config.d.ts +59 -0
  196. package/dist/global-filters-config.js +361 -0
  197. package/dist/index.d.ts +9 -4
  198. package/dist/index.js +4 -2
  199. package/dist/introspect.d.ts +31 -6
  200. package/dist/introspect.js +106 -50
  201. package/dist/log-codes.d.ts +12 -0
  202. package/dist/log-codes.js +74 -0
  203. package/dist/logger.d.ts +87 -0
  204. package/dist/logger.js +93 -0
  205. package/dist/mssql.d.ts +8 -5
  206. package/dist/mssql.js +354 -67
  207. package/dist/mysql.d.ts +3 -5
  208. package/dist/mysql.js +423 -73
  209. package/dist/nested-write.d.ts +3 -0
  210. package/dist/nested-write.js +177 -112
  211. package/dist/pipeline-submittable.d.ts +12 -0
  212. package/dist/pipeline-submittable.js +117 -44
  213. package/dist/pipeline.js +21 -26
  214. package/dist/powdb-introspect.d.ts +5 -2
  215. package/dist/powdb-introspect.js +21 -16
  216. package/dist/powdb-shared.d.ts +36 -5
  217. package/dist/powdb-shared.js +65 -8
  218. package/dist/powdb.d.ts +4 -0
  219. package/dist/powdb.js +183 -51
  220. package/dist/powql.d.ts +394 -51
  221. package/dist/powql.js +1836 -291
  222. package/dist/prisma-compat.d.ts +23 -15
  223. package/dist/prisma-compat.js +508 -373
  224. package/dist/query/aggregates.d.ts +17 -1
  225. package/dist/query/aggregates.js +263 -32
  226. package/dist/query/batched-loader.d.ts +25 -6
  227. package/dist/query/batched-loader.js +101 -21
  228. package/dist/query/builder.d.ts +325 -184
  229. package/dist/query/builder.js +1458 -495
  230. package/dist/query/column-ref.d.ts +61 -0
  231. package/dist/query/column-ref.js +66 -0
  232. package/dist/query/compound-unique.d.ts +21 -0
  233. package/dist/query/compound-unique.js +97 -19
  234. package/dist/query/deferred.d.ts +53 -16
  235. package/dist/query/expr.d.ts +217 -0
  236. package/dist/query/expr.js +848 -0
  237. package/dist/query/filters.d.ts +155 -53
  238. package/dist/query/filters.js +597 -72
  239. package/dist/query/index.d.ts +2 -0
  240. package/dist/query/index.js +4 -0
  241. package/dist/query/json-value.d.ts +43 -0
  242. package/dist/query/json-value.js +54 -0
  243. package/dist/query/normalize-args.d.ts +87 -0
  244. package/dist/query/normalize-args.js +517 -0
  245. package/dist/query/option-surface.d.ts +16 -4
  246. package/dist/query/option-surface.js +56 -13
  247. package/dist/query/relation-names.d.ts +14 -0
  248. package/dist/query/relation-names.js +77 -19
  249. package/dist/query/relations.d.ts +30 -3
  250. package/dist/query/relations.js +220 -67
  251. package/dist/query/row-lock.d.ts +49 -0
  252. package/dist/query/row-lock.js +131 -0
  253. package/dist/query/sql-comment.d.ts +161 -0
  254. package/dist/query/sql-comment.js +374 -0
  255. package/dist/query/stream-idle.d.ts +132 -0
  256. package/dist/query/stream-idle.js +203 -0
  257. package/dist/query/table-client.d.ts +101 -0
  258. package/dist/query/table-client.js +70 -0
  259. package/dist/query/types.d.ts +677 -298
  260. package/dist/query/utils.d.ts +105 -6
  261. package/dist/query/utils.js +414 -18
  262. package/dist/query/warn-registry.d.ts +18 -0
  263. package/dist/query/warn-registry.js +18 -0
  264. package/dist/query/where-compile.d.ts +138 -14
  265. package/dist/query/where-compile.js +508 -31
  266. package/dist/query/where.d.ts +159 -59
  267. package/dist/query/where.js +711 -248
  268. package/dist/query/writes.d.ts +112 -50
  269. package/dist/query/writes.js +1496 -202
  270. package/dist/query-events.d.ts +4 -2
  271. package/dist/query-events.js +3 -1
  272. package/dist/realtime.d.ts +7 -3
  273. package/dist/realtime.js +13 -2
  274. package/dist/schema-builder.d.ts +55 -4
  275. package/dist/schema-builder.js +381 -18
  276. package/dist/schema-metadata.d.ts +21 -1
  277. package/dist/schema-metadata.js +28 -7
  278. package/dist/schema-sql.d.ts +75 -5
  279. package/dist/schema-sql.js +494 -44
  280. package/dist/schema.d.ts +26 -5
  281. package/dist/schema.js +109 -13
  282. package/dist/sql-placeholders.d.ts +56 -0
  283. package/dist/sql-placeholders.js +200 -0
  284. package/dist/sqlite.d.ts +75 -20
  285. package/dist/sqlite.js +597 -99
  286. package/dist/supplied-pool-types.d.ts +48 -0
  287. package/dist/supplied-pool-types.js +247 -0
  288. package/dist/transaction-control.d.ts +51 -0
  289. package/dist/transaction-control.js +311 -0
  290. package/dist/transaction-state.d.ts +342 -0
  291. package/dist/transaction-state.js +696 -0
  292. package/dist/typed-sql.d.ts +37 -3
  293. package/dist/typed-sql.js +100 -8
  294. package/package.json +7 -4
  295. package/skills/turbine-orm/SKILL.md +56 -6
package/dist/client.d.ts CHANGED
@@ -21,14 +21,18 @@
21
21
  * const users = db.table<User>('users');
22
22
  * ```
23
23
  */
24
+ import { type DiagnosticsConfig } from './diagnostics.js';
24
25
  import { type Dialect } from './dialect.js';
25
26
  import { type ErrorMessageMode } from './errors.js';
27
+ import { type TurbineLogger } from './logger.js';
26
28
  import { type ObserveConfig, type ObserveHandle } from './observe.js';
27
29
  import type { PgCompatPool, PgCompatPoolClient } from './pg-types.js';
28
30
  import { type PipelineOptions, type PipelineResults } from './pipeline.js';
29
31
  import { type DeferredQuery, type GlobalFilters, type JsonEncoding, type QueryEventListener, QueryInterface, type QueryInterfaceOptions, type RelationLoadStrategy, type TemporalInfinityReading } from './query/index.js';
32
+ import { type SqlCommentOptions } from './query/sql-comment.js';
30
33
  import { type ListenOptions, type NotificationHandler, type Subscription } from './realtime.js';
31
34
  import type { SchemaMetadata } from './schema.js';
35
+ import { type TransactionScope, TransactionState } from './transaction-state.js';
32
36
  import { TypedSqlQuery } from './typed-sql.js';
33
37
  export interface RetryOptions {
34
38
  maxAttempts?: number;
@@ -74,54 +78,103 @@ export interface TurbineConfig {
74
78
  * like `@neondatabase/serverless`, `@vercel/postgres`, or any other pg-API
75
79
  * compatible pool. When provided, all connection-string fields are ignored
76
80
  * and Turbine will NOT create its own pg.Pool.
81
+ *
82
+ * Turbine's own queries on it read `bigint` as a number and, under
83
+ * {@link utcTimestamps}, zone-less temporals as UTC, by attaching parsers to
84
+ * each query (node-postgres' query-level `types`). Your own queries on the
85
+ * pool, and the pg module's parser table, are left alone. A driver that does
86
+ * not read a query's `types` keeps its own readings, as does every driver on
87
+ * the `edge-light` / `browser` builds, where Turbine has no parsers to attach.
77
88
  */
78
- pool?: PgCompatPool;
89
+ pool?: PgCompatPool | undefined;
79
90
  /** Postgres connection string (e.g. postgres://user:pass@host:5432/db) */
80
- connectionString?: string;
91
+ connectionString?: string | undefined;
81
92
  /** Host (used if connectionString is not set) */
82
- host?: string;
93
+ host?: string | undefined;
83
94
  /** Port (default: 5432) */
84
- port?: number;
95
+ port?: number | undefined;
85
96
  /** Database name */
86
- database?: string;
97
+ database?: string | undefined;
87
98
  /** Username */
88
- user?: string;
99
+ user?: string | undefined;
89
100
  /** Password */
90
- password?: string;
101
+ password?: string | undefined;
91
102
  /** SSL/TLS options for the connection (required for most cloud providers) */
92
103
  ssl?: boolean | {
93
- rejectUnauthorized?: boolean;
94
- ca?: string;
95
- key?: string;
96
- cert?: string;
97
- };
104
+ rejectUnauthorized?: boolean | undefined;
105
+ ca?: string | undefined;
106
+ key?: string | undefined;
107
+ cert?: string | undefined;
108
+ } | undefined;
98
109
  /** Maximum number of connections in the pool (default: 10) */
99
- poolSize?: number;
110
+ poolSize?: number | undefined;
100
111
  /** Idle timeout in ms before a connection is closed (default: 30000) */
101
- idleTimeoutMs?: number;
112
+ idleTimeoutMs?: number | undefined;
102
113
  /** Connection timeout in ms (default: 5000) */
103
- connectionTimeoutMs?: number;
114
+ connectionTimeoutMs?: number | undefined;
104
115
  /** pg-style alias for {@link poolSize}; the explicit field wins when both are set. */
105
- max?: number;
116
+ max?: number | undefined;
106
117
  /** pg-style alias for {@link idleTimeoutMs}; the explicit field wins when both are set. */
107
- idleTimeoutMillis?: number;
118
+ idleTimeoutMillis?: number | undefined;
108
119
  /** pg-style alias for {@link connectionTimeoutMs}; the explicit field wins when both are set. */
109
- connectionTimeoutMillis?: number;
110
- /** Enable query logging to console (default: false) */
111
- logging?: boolean;
120
+ connectionTimeoutMillis?: number | undefined;
121
+ /**
122
+ * Emit Turbine's lifecycle lines (pool created, raw SQL preview, transaction
123
+ * committed, ...) at the logger's `debug` level, plus the errors a throwing
124
+ * `$on` listener raises. Default: false. With the default logger the lines
125
+ * go to `console`. Bound parameter values are never logged.
126
+ */
127
+ logging?: boolean | undefined;
128
+ /**
129
+ * Where Turbine's log lines go. Each line is a message plus a structured
130
+ * entry whose `code` is stable (`'pool-error'`, `'unknown-config-key'`); the
131
+ * message text is not. Every method is optional and a missing one drops that
132
+ * level, so `{}` silences Turbine entirely. Default: `console`, each line
133
+ * prefixed `[turbine]`.
134
+ *
135
+ * The arguments are `(message, entry)`, like `console` and winston. For pino
136
+ * or bunyan, which take `(object, message)`, adapt:
137
+ * `{ warn: (msg, e) => log.warn(e, msg), error: (msg, e) => log.error(e, msg) }`.
138
+ *
139
+ * No line carries a bound parameter value, whatever `logQueryParams` says.
140
+ * Once-per-process warnings are deduplicated process-wide, so a warning one
141
+ * client already emitted does not reach a second client's logger.
142
+ */
143
+ logger?: TurbineLogger | undefined;
144
+ /**
145
+ * What Turbine checks and says about itself: per-query advice (unknown
146
+ * options, unordered pages, missing FK indexes, ...) and the SQL-cache
147
+ * self-check. `'development'` turns both on; `'production'` turns both off.
148
+ * An object overrides either part: `{ mode: 'production', cacheCheck: {
149
+ * sample: 0.001 } }`.
150
+ *
151
+ * Default: `'production'` when `NODE_ENV` is `'production'` when the client is
152
+ * constructed (or when there is no `process`, as on an edge runtime), else
153
+ * `'development'`. Read once, at construction. Set it explicitly on a worker
154
+ * or serverless function that does not set `NODE_ENV`.
155
+ *
156
+ * Construction warnings (a misspelled config key, an option an external pool
157
+ * ignores, prepared statements behind a pooler) do not depend on it: they
158
+ * describe production hazards, fire once per process, and are always on.
159
+ */
160
+ diagnostics?: DiagnosticsConfig | undefined;
112
161
  /** Default LIMIT applied to findMany() when no limit is specified (opt-in, default: undefined) */
113
- defaultLimit?: number;
162
+ defaultLimit?: number | undefined;
114
163
  /**
115
164
  * Log a warning when findMany() is called without a limit (default: false).
116
165
  * Pass a per-table map (`{ users: false }`) to override the default for
117
166
  * specific tables; per-call `warnOnUnlimited` on findMany args wins over both.
118
167
  */
119
- warnOnUnlimited?: boolean | Record<string, boolean>;
168
+ warnOnUnlimited?: boolean | Record<string, boolean> | undefined;
120
169
  /**
121
170
  * Interpret Postgres `timestamp` (without time zone) values as UTC, both
122
171
  * at the driver level (type parsers for OIDs 1114 `timestamp`, 1082 `date`,
123
- * and their array forms 1115 / 1182, registered only when Turbine owns the
124
- * pool) and when coercing nested-relation JSON dates. A `date` column is
172
+ * and their array forms 1115 / 1182) and when coercing nested-relation JSON
173
+ * dates. The parsers are registered on the pg module when Turbine owns the
174
+ * pool; on a pool you supply they are attached to each of Turbine's own
175
+ * queries instead, and your own queries on that pool keep your parsers. A
176
+ * bound `Date` in `raw` / `rawUnsafe` / `sql` is sent as its UTC fields under
177
+ * this setting, so it reads back unchanged from a zone-less column. A `date` column is
125
178
  * zone-less too, so it reads back as UTC midnight rather than the process's
126
179
  * local midnight. This is the
127
180
  * Prisma/Rails/Django convention and makes results independent of the
@@ -146,15 +199,13 @@ export interface TurbineConfig {
146
199
  * `ValidationError` rather than handing back a client whose writes and reads
147
200
  * disagree. Give every
148
201
  * client in the process the same value, or isolate the odd one in its own
149
- * process. A client on an EXTERNAL pool never REGISTERS a parser (it inherits
150
- * whatever configuration the caller's driver has, so a process containing
151
- * only external-pool clients is untouched), but it does take part in the
152
- * agreement check: registration is process-global, so once a Turbine-owned
153
- * client has installed the parsers an external-pool client reads through them
154
- * too, and a disagreeing one would write local `date` literals while reading
155
- * UTC.
202
+ * process. A client on an EXTERNAL pool never REGISTERS a parser (a process
203
+ * containing only external-pool clients keeps the pg module untouched), but
204
+ * it does take part in the agreement check: with `false` it reads through the
205
+ * pg module's table, which a Turbine-owned client may have set to UTC, and
206
+ * would then write local `date` literals while reading UTC.
156
207
  */
157
- utcTimestamps?: boolean;
208
+ utcTimestamps?: boolean | undefined;
158
209
  /**
159
210
  * How a Postgres temporal `infinity` / `-infinity` is handed back:
160
211
  * `'preserve'` (the default, the JS numbers `Infinity` / `-Infinity`) or
@@ -177,11 +228,11 @@ export interface TurbineConfig {
177
228
  * strategy, every write projection, `groupBy` keys and `_min` / `_max`.
178
229
  *
179
230
  * Leaving the option unset selects `'preserve'` AND enables a one-time
180
- * warning (per process, per field, not silenced by `NODE_ENV=production`) the
231
+ * warning (per process, per field, not silenced by `diagnostics: 'production'`) the
181
232
  * first time a stored infinity is actually read, describing that reading and
182
233
  * both escapes. Naming either reading explicitly silences it.
183
234
  */
184
- temporalInfinity?: TemporalInfinityReading;
235
+ temporalInfinity?: TemporalInfinityReading | undefined;
185
236
  /**
186
237
  * Pin `plan_cache_mode` on every connection this client opens, fixing how the
187
238
  * PostgreSQL backend chooses between a custom plan (re-planned per parameter
@@ -279,7 +330,101 @@ export interface TurbineConfig {
279
330
  * `ignore_startup_parameters`). Set it on the role or server there
280
331
  * (`ALTER ROLE ... SET plan_cache_mode = ...`) instead.
281
332
  */
282
- planCacheMode?: PlanCacheMode;
333
+ planCacheMode?: PlanCacheMode | undefined;
334
+ /**
335
+ * A server-side ceiling, in milliseconds, on every statement this client's
336
+ * connections run: PostgreSQL's `statement_timeout`. A statement that runs
337
+ * longer is cancelled by the server, which rolls back its effect and frees the
338
+ * connection, and it rejects with `TimeoutError` (E002, `outcome:
339
+ * 'not-executed'`). It bounds what a per-query `timeout` cannot reach: a
340
+ * forgotten query, a stream's `FETCH`, a statement inside `$transaction`.
341
+ *
342
+ * Applied as the `options=-c statement_timeout=...` connection parameter on
343
+ * pools Turbine opens (the primary and string `replicas`), so it is in force
344
+ * from each connection's first statement and nothing is ever `SET` on a
345
+ * session. `0` sends `statement_timeout=0`, which disables a timeout the
346
+ * server or role would otherwise apply. Unset (the default) sends nothing.
347
+ *
348
+ * Must be a non-negative integer (E003). PostgreSQL only (E017 elsewhere). An
349
+ * external `pool` is not touched: the option is then a no-op with a one-time
350
+ * dev warning, and the driver's own connection setup should set the GUC. A
351
+ * connection pooler may refuse startup parameters (PgBouncer's
352
+ * `ignore_startup_parameters`); set it on the role there instead
353
+ * (`ALTER ROLE app SET statement_timeout = '30s'`).
354
+ */
355
+ statementTimeoutMs?: number | undefined;
356
+ /**
357
+ * Close a pooled connection once it is this many seconds old, when it is
358
+ * next idle, and open a fresh one on demand. Default `1800` (30 minutes) on
359
+ * pools Turbine opens; `0` keeps connections for as long as they are in use.
360
+ *
361
+ * Recycling bounds what a long-lived server session accumulates: the
362
+ * prepared statements and their cached plans, catalog caches, and memory a
363
+ * backend keeps after a large query. It also moves connections onto a new
364
+ * server after a DNS failover or a replica scale-out. A connection that is
365
+ * checked out is never closed under its holder; it is retired when it comes
366
+ * back. Must be a non-negative integer (E003). Ignored, with a one-time dev
367
+ * warning, on an external `pool`.
368
+ */
369
+ maxLifetimeSeconds?: number | undefined;
370
+ /**
371
+ * Close a cursor stream (`findManyStream`, `findManyStreamBatches`) and
372
+ * release its connection once its consumer has asked for nothing for this
373
+ * many milliseconds. Default `300000` (five minutes); `0` disables. A stream
374
+ * call's own `idleTimeoutMs` overrides it.
375
+ *
376
+ * A consumer that stops pulling without calling `return()` (an iterator that
377
+ * is stored and forgotten, a response stream torn down without propagating)
378
+ * would otherwise hold a pooled connection forever and, on PostgreSQL, an
379
+ * open transaction whose snapshot holds back vacuum for the whole database.
380
+ * On expiry Turbine closes the cursor, releases the connection, and logs
381
+ * `stream-idle-timeout`; the next pull, if one comes, throws `TimeoutError`
382
+ * (E002). Only streams that hold a connection between pulls are watched.
383
+ *
384
+ * On PostgreSQL the stream's own transaction also gets a server-side limit,
385
+ * `idle_in_transaction_session_timeout` at this value plus 10 seconds
386
+ * (transaction-local, never a session `SET`; a stricter setting already in
387
+ * force is kept), so a process that stops running timers (frozen between
388
+ * serverless invocations) cannot hold the transaction open either. Must be a
389
+ * whole number of milliseconds (E003).
390
+ */
391
+ streamIdleTimeoutMs?: number | undefined;
392
+ /**
393
+ * Append sqlcommenter-style key/values to every statement this client builds,
394
+ * so `pg_stat_activity`, the server log, `auto_explain` and database APM
395
+ * tools can attribute a statement to a service, model, feature or trace:
396
+ *
397
+ * ```sql
398
+ * SELECT ... /*action='findMany',application='billing',model='users',tag='checkout'*\/
399
+ * ```
400
+ *
401
+ * `static` adds constant pairs, `model: true` the table and operation,
402
+ * `tag: true` the label of the innermost `db.$tag(label, fn)`, and `dynamic`
403
+ * per-statement pairs computed in the caller's async context (a W3C
404
+ * `traceparent`). Keys are lower-case `[a-z][a-z0-9_]*`; values are
405
+ * URL-encoded (so none can end or nest the comment) and at most 256
406
+ * characters. A malformed `static` key or value is E003 here; a bad
407
+ * `dynamic` one is left out with a `sql-comment-dropped` warning, never
408
+ * thrown.
409
+ *
410
+ * Model statements get it: reads, writes, streams, pipelines and
411
+ * `$transaction` batches. Raw SQL, transaction control and `set_config`
412
+ * calls do not. It is added after the SQL-template cache, so caching is
413
+ * unchanged, and `$on('query')` events keep `sql` commentless and carry the
414
+ * comment as `comment`.
415
+ *
416
+ * Prepared statements: a statement whose comment is static, model or tag is
417
+ * still prepared, under a name of its own (counted against the same budget
418
+ * as every other name). One carrying any `dynamic` value is sent unnamed,
419
+ * so it is parsed and planned on every execution (tens of microseconds per
420
+ * statement on a local PostgreSQL, more for nested reads): return dynamic
421
+ * values only when they are worth that, for example only for sampled traces.
422
+ *
423
+ * Refused (E017) on PowDB, which runs no SQL, and `dynamic` on MySQL, whose
424
+ * engine keeps a server-side prepared statement per distinct text and would
425
+ * exhaust the server's `max_prepared_stmt_count`.
426
+ */
427
+ sqlComment?: SqlCommentOptions | undefined;
283
428
  /**
284
429
  * Refuse a nested `connect` / `connectOrCreate` that would re-parent a
285
430
  * to-many child already owned by a different parent. Off by default,
@@ -290,7 +435,7 @@ export interface TurbineConfig {
290
435
  * take another tenant's row. See
291
436
  * {@link import('./nested-write.js').NestedWriteContext.scopedConnect}.
292
437
  */
293
- scopedConnect?: boolean;
438
+ scopedConnect?: boolean | undefined;
294
439
  /**
295
440
  * Default strategy for resolving `with`-clause relations, applied to every
296
441
  * `findMany`/`findUnique`/`findFirst` unless overridden per query.
@@ -313,7 +458,7 @@ export interface TurbineConfig {
313
458
  * batched loaders (an ineligible relation falls back to them silently even
314
459
  * under `'join'`; `'auto'` resolves to PowDB's own default).
315
460
  */
316
- relationLoadStrategy?: RelationLoadStrategy;
461
+ relationLoadStrategy?: RelationLoadStrategy | undefined;
317
462
  /**
318
463
  * When `true`, every to-many `with` relation with no explicit `orderBy` is
319
464
  * loaded ordered by the target table's primary key ascending, so unordered
@@ -323,7 +468,7 @@ export interface TurbineConfig {
323
468
  * `stableRelationOrder` overrides this. Default `false` (SQL is byte-identical
324
469
  * when off). SQL engines only. See {@link RelationLoadStrategy}.
325
470
  */
326
- stableRelationOrder?: boolean;
471
+ stableRelationOrder?: boolean | undefined;
327
472
  /**
328
473
  * When `true`, a `findMany` that paginates (`limit` / `take` / `offset`) but
329
474
  * declares no `orderBy` is ordered by the table's primary key ascending
@@ -341,7 +486,7 @@ export interface TurbineConfig {
341
486
  * emitted SQL is byte-identical to before, and a dev-mode warning points at
342
487
  * the affected queries.
343
488
  */
344
- implicitPkOrdering?: boolean;
489
+ implicitPkOrdering?: boolean | undefined;
345
490
  /**
346
491
  * Parent-row ceiling for the `relationLoadStrategy: 'auto'` to-one rule: a
347
492
  * to-one relation stays in the single-statement join when the query's `limit`
@@ -349,7 +494,7 @@ export interface TurbineConfig {
349
494
  * query is unbounded or bounded above it. Defaults to 1000 (see
350
495
  * `AUTO_TO_ONE_JOIN_MAX_ROWS`). Only consulted under `'auto'`.
351
496
  */
352
- autoToOneJoinMaxRows?: number;
497
+ autoToOneJoinMaxRows?: number | undefined;
353
498
  /**
354
499
  * Round-trip time to the database, in milliseconds, used to DERIVE the
355
500
  * `relationLoadStrategy: 'auto'` to-one threshold instead of guessing a row
@@ -363,7 +508,7 @@ export interface TurbineConfig {
363
508
  * which reproduces the historical 1000-row threshold exactly. Overridden by
364
509
  * an explicit `autoToOneJoinMaxRows`; only consulted under `'auto'`.
365
510
  */
366
- autoRoundTripMs?: number;
511
+ autoRoundTripMs?: number | undefined;
367
512
  /**
368
513
  * How nested-relation subqueries encode each row's JSON.
369
514
  *
@@ -391,7 +536,7 @@ export interface TurbineConfig {
391
536
  * `relationLoadStrategy: 'flatten'`, which is refused while positional is
392
537
  * active because a flattened relation emits no JSON to encode.
393
538
  */
394
- jsonEncoding?: JsonEncoding;
539
+ jsonEncoding?: JsonEncoding | undefined;
395
540
  /**
396
541
  * Controls how `NotFoundError` (and other where-aware errors) format their
397
542
  * messages.
@@ -405,7 +550,7 @@ export interface TurbineConfig {
405
550
  * The full `where` object is always available as `err.where` for
406
551
  * programmatic access regardless of mode.
407
552
  */
408
- errorMessages?: ErrorMessageMode;
553
+ errorMessages?: ErrorMessageMode | undefined;
409
554
  /**
410
555
  * Whether `$on('query')` listeners receive the real bound parameter values.
411
556
  *
@@ -422,13 +567,19 @@ export interface TurbineConfig {
422
567
  * events while keeping error MESSAGES redacted, which is the combination
423
568
  * that was previously unreachable.
424
569
  */
425
- logQueryParams?: boolean;
570
+ logQueryParams?: boolean | undefined;
426
571
  /**
427
572
  * Enable prepared statements. Queries are submitted with `{ name, text, values }`
428
573
  * to the pg driver, which caches the parse+plan on the server per connection.
429
574
  *
430
575
  * Default: `true` for Turbine-owned pools, `false` for external pools (serverless
431
- * drivers may not support named statements).
576
+ * drivers may not support named statements) and for a connection string that
577
+ * names a connection pooler (a `pooler` / `pgbouncer` host, or port 6432 or
578
+ * 6543). A transaction-mode pooler runs each transaction on any server
579
+ * connection, so a statement named on one is missing (or already present) on
580
+ * the next, unless the pooler tracks prepared statements (PgBouncer 1.21+
581
+ * with `max_prepared_statements` above 0). Set `true` on such a pooler only
582
+ * when it does; Turbine warns once in development when you do.
432
583
  *
433
584
  * Override with `TURBINE_DISABLE_PREPARED=1` env var.
434
585
  *
@@ -444,14 +595,14 @@ export interface TurbineConfig {
444
595
  * through a fixed allowlist or set this to `false`. Documented for readers at
445
596
  * https://turbineorm.dev/queries#client-escape-hatches
446
597
  */
447
- preparedStatements?: boolean;
598
+ preparedStatements?: boolean | undefined;
448
599
  /**
449
600
  * Enable the SQL template cache. Repeated queries with the same shape reuse
450
601
  * cached SQL text instead of rebuilding from scratch.
451
602
  *
452
603
  * Default: `true`. Set to `false` as a nuclear kill switch.
453
604
  */
454
- sqlCache?: boolean;
605
+ sqlCache?: boolean | undefined;
455
606
  /**
456
607
  * Maximum number of distinct SQL templates each per-table LRU cache retains.
457
608
  *
@@ -461,9 +612,9 @@ export interface TurbineConfig {
461
612
  * memory; lower it to cap memory. `0` disables caching entirely (identical to
462
613
  * `sqlCache: false`); a negative value is ignored (treated as the default).
463
614
  */
464
- sqlCacheSize?: number;
615
+ sqlCacheSize?: number | undefined;
465
616
  /** SQL dialect implementation. Defaults to PostgreSQL. Internal Phase-1 seam for dialect packages. */
466
- dialect?: Dialect;
617
+ dialect?: Dialect | undefined;
467
618
  /**
468
619
  * Read replicas. When set, read-only operations issued outside a transaction
469
620
  * (`findMany`, `findFirst`, `findUnique`, `*OrThrow`, `count`, `aggregate`,
@@ -485,7 +636,7 @@ export interface TurbineConfig {
485
636
  * without replication lag. Omitting `replicas` (or passing `[]`) leaves the
486
637
  * default single-pool path completely unchanged.
487
638
  */
488
- replicas?: readonly (string | PgCompatPool)[];
639
+ replicas?: readonly (string | PgCompatPool)[] | undefined;
489
640
  /**
490
641
  * Automatic WHERE filters applied to every query, keyed by table accessor
491
642
  * (`db[name]`). Each value is AND-merged into the compiled WHERE of every
@@ -511,7 +662,7 @@ export interface TurbineConfig {
511
662
  * }});
512
663
  * ```
513
664
  */
514
- globalFilters?: GlobalFilters;
665
+ globalFilters?: GlobalFilters | undefined;
515
666
  }
516
667
  /** Parameters passed to middleware functions */
517
668
  export interface MiddlewareParams {
@@ -543,6 +694,13 @@ export interface TransactionOptions {
543
694
  * bound parameters (never interpolated); the GUC name is additionally
544
695
  * validated against a strict identifier regex.
545
696
  *
697
+ * Names must be namespaced custom settings (`app.current_tenant`, with a
698
+ * dot). A name without a dot is one of PostgreSQL's own settings (`role`,
699
+ * `search_path`, `statement_timeout`, `session_authorization`), which can
700
+ * change who the transaction runs as or lift its limits, so it is refused
701
+ * (E003) unless listed in {@link allowBuiltinSettings}. An application that
702
+ * maps request data into the context then cannot be steered into setting one.
703
+ *
546
704
  * @example
547
705
  * ```ts
548
706
  * await db.$transaction(
@@ -555,21 +713,71 @@ export interface TransactionOptions {
555
713
  * ```
556
714
  */
557
715
  sessionContext?: Record<string, string | number | boolean>;
716
+ /**
717
+ * PostgreSQL's own settings that {@link sessionContext} may set, by name
718
+ * (`['statement_timeout']`). Every other name in `sessionContext` must be a
719
+ * namespaced custom setting. List only what the code itself chooses to set,
720
+ * never a name taken from a request.
721
+ */
722
+ allowBuiltinSettings?: readonly string[];
723
+ /**
724
+ * Run the transaction READ ONLY (`BEGIN READ ONLY` on PostgreSQL, `START
725
+ * TRANSACTION READ ONLY` on MySQL): any write inside it is refused by the
726
+ * server. Use it for multi-statement reads that must see one consistent
727
+ * snapshot (with `isolationLevel: 'RepeatableRead'`) and must not write.
728
+ * Other engines have no read-only transaction and throw
729
+ * `UnsupportedFeatureError` (E017).
730
+ */
731
+ readOnly?: boolean;
732
+ }
733
+ /** Which of a client's pools a pool event came from. */
734
+ export interface PoolEventOrigin {
735
+ /** `'primary'`, or `'replica'` for one of the string `replicas`. */
736
+ pool: 'primary' | 'replica';
737
+ /** Index into `replicas` when `pool` is `'replica'`. */
738
+ replica?: number;
739
+ }
740
+ /** A pool lifecycle event: the connection's error, and the pool it belonged to. */
741
+ export interface PoolEvent extends PoolEventOrigin {
742
+ /** The driver's error: a server restart, an administrator's terminate, a network drop. */
743
+ error: Error;
558
744
  }
559
745
  /**
560
- * The read-only `QueryInterface` operations that a read-replica setup may route
561
- * to a replica pool. Every other method (all writes, plus internals) stays on
562
- * the primary. Kept as a Set so the routing proxy's `get` trap is O(1).
746
+ * `$on('poolError')`: an IDLE connection in a pool this client opened failed,
747
+ * and the pool discarded it. No query was affected. Without a listener the
748
+ * error is logged at `error` level (code `'pool-error'`), through `logger`.
563
749
  */
750
+ export type PoolErrorEvent = PoolEvent;
564
751
  /**
565
- * Operations that may be served by a read replica.
752
+ * `$on('connectionLost')`: a connection failed while CHECKED OUT, so the query,
753
+ * stream or transaction using it rejected (with `ConnectionError`, E004).
754
+ * Emitted once per connection, for pools this client opened.
755
+ */
756
+ export type ConnectionLostEvent = PoolEvent;
757
+ export interface DisconnectOptions {
758
+ /**
759
+ * Stop waiting for checked-out connections after this many milliseconds.
760
+ * Statements still running then are cancelled on the server and their
761
+ * connections closed, so a transaction among them rolls back. Without it,
762
+ * `disconnect()` waits for every connection to be returned.
763
+ */
764
+ timeoutMs?: number | undefined;
765
+ }
766
+ export interface DisconnectResult {
767
+ /** True when connections were still checked out at the deadline and were closed. */
768
+ timedOut: boolean;
769
+ /** How many checked-out connections were cancelled and closed at the deadline. */
770
+ forcedConnections: number;
771
+ }
772
+ /**
773
+ * Operations that may be served by a read replica: the `read` entries of
774
+ * {@link TABLE_METHODS}. Every other method (writes, `build*`, internals) stays
775
+ * on the primary. A Set so the routing proxy's `get` trap is O(1).
566
776
  *
567
- * This is a hand-maintained list of names, so it drifts the moment a read
568
- * method is added to `QueryInterface` and not added here. The failure is
569
- * SILENT and directional: the missing operation keeps working, it just runs on
570
- * the PRIMARY, so no test fails and no user sees an error, only a busier
571
- * primary. `findManyStreamBatches` was added and missed exactly this way.
572
- * `src/test/read-operations-drift.test.ts` closes the loop mechanically.
777
+ * Derived, not listed, because a hand-kept list drifts SILENTLY: a read method
778
+ * missing here keeps working on the primary, so no test fails. A new public
779
+ * QueryInterface method cannot compile until it is classified in TABLE_METHODS.
780
+ * `src/test/read-operations-drift.test.ts` is the run-time check.
573
781
  *
574
782
  * Exported for that test only.
575
783
  */
@@ -578,6 +786,10 @@ export declare const READ_OPERATIONS: ReadonlySet<string>;
578
786
  * A transaction-scoped client that provides the same table accessor API as TurbineClient.
579
787
  * All queries run on a dedicated connection within a BEGIN/COMMIT block.
580
788
  * Supports nested transactions via SAVEPOINTs.
789
+ *
790
+ * Closed when its transaction settles (commit, rollback, timeout, error): every
791
+ * later call through it throws `ValidationError` (E003) instead of running on a
792
+ * connection that is back in the pool and may belong to another request.
581
793
  */
582
794
  export declare class TransactionClient {
583
795
  private readonly client;
@@ -595,9 +807,19 @@ export declare class TransactionClient {
595
807
  */
596
808
  private readonly sourcePool?;
597
809
  private readonly tableCache;
598
- private savepointCounter;
599
810
  /** Active SQL dialect, owns savepoint keywords and raw-SQL placeholders. */
600
811
  private readonly dialect;
812
+ /** Open/closed state, first failure and savepoint blocks, shared by everything this client hands out. */
813
+ private readonly state;
814
+ /**
815
+ * Where in the transaction this handle sits: the transaction itself, or the
816
+ * nested block whose callback received it. Statements carry it, so one issued
817
+ * through an outer handle from inside a block can be told from one issued
818
+ * through the block's own.
819
+ */
820
+ private readonly scope;
821
+ /** What {@link $connection} hands out, built on first use. */
822
+ private connectionHandle?;
601
823
  constructor(client: PgCompatPoolClient, schema: SchemaMetadata, middlewares: Middleware[], queryOptions?: QueryInterfaceOptions | undefined,
602
824
  /**
603
825
  * The parent pool this transaction runs on. Only its `readonly` and
@@ -611,21 +833,83 @@ export declare class TransactionClient {
611
833
  sourcePool?: {
612
834
  readonly readonly?: boolean;
613
835
  readonly capabilities?: unknown;
614
- } | undefined);
836
+ } | undefined,
837
+ /**
838
+ * The state of the transaction this client belongs to. `$transaction`
839
+ * passes the one it opened, which also turns on per-write savepoints for
840
+ * multi-statement writes. Omitted for Turbine's own implicit transactions.
841
+ */
842
+ state?: TransactionState,
843
+ /** The nested block this handle belongs to; omitted for the transaction's own handle. */
844
+ scope?: TransactionScope);
615
845
  /**
616
846
  * Get a QueryInterface for a table within this transaction.
617
847
  * Uses the dedicated transaction connection instead of the pool.
618
848
  */
619
849
  table<T extends object = Record<string, unknown>>(name: string): QueryInterface<T>;
850
+ /**
851
+ * The driver connection this transaction runs on, for code that needs one:
852
+ * another library's query function, a COPY stream, a driver feature Turbine
853
+ * does not wrap. Statements sent on it run inside this transaction.
854
+ *
855
+ * - It closes with the transaction: a statement sent after COMMIT or
856
+ * ROLLBACK is refused with `ValidationError` (E003) instead of running on a
857
+ * connection that is back in the pool.
858
+ * - `release()` is refused. The transaction returns the connection.
859
+ * - COMMIT, ROLLBACK, BEGIN, SAVEPOINT, RELEASE and SET TRANSACTION are
860
+ * refused with `ValidationError` (E003): the transaction ends itself, and
861
+ * Turbine would then commit or roll back a transaction that is no longer
862
+ * open. Use `tx.$transaction` for a savepoint.
863
+ * - `end()` and `connect()` are refused like `release()`, and the driver's
864
+ * `connection` object is not reachable through it.
865
+ * - Its queries use the driver's own parsers. On a pool you supplied, that is
866
+ * your parser configuration, not Turbine's int8 and UTC readings.
867
+ *
868
+ * Typed as the driver-neutral client. For the pg-only surface, cast:
869
+ * `tx.$connection() as unknown as PoolClient`.
870
+ *
871
+ * Throws `UnsupportedFeatureError` (E017) where no driver connection backs
872
+ * the transaction: PowDB, and a stateless HTTP driver.
873
+ */
874
+ $connection(): PgCompatPoolClient;
620
875
  /**
621
876
  * Execute a nested transaction via SAVEPOINT.
622
- * If the inner function throws, only the savepoint is rolled back.
877
+ *
878
+ * If the inner function throws, only the savepoint is rolled back. Blocks
879
+ * started side by side (`Promise.all`) run one after another, because
880
+ * savepoints are a stack on the connection and a rollback in one would
881
+ * otherwise undo a sibling that already reported success.
882
+ *
883
+ * The callback receives a `tx` of its own, bound to the block. Use it for the
884
+ * work inside the block: a statement through the OUTER `tx` from inside the
885
+ * block would run in the block's savepoint and be undone with it, so it is
886
+ * refused with `ValidationError` (E003).
623
887
  */
624
888
  $transaction<R>(fn: (tx: TransactionClient) => Promise<R>): Promise<R>;
889
+ /** A handle on the same transaction bound to nested block `scope`. */
890
+ private inScope;
625
891
  /**
626
- * Execute a raw SQL query within this transaction.
892
+ * Execute a raw SQL query within this transaction, as a tagged template:
893
+ * every `${value}` is sent as a bound parameter.
627
894
  */
628
895
  raw<T extends Record<string, unknown> = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): Promise<T[]>;
896
+ /**
897
+ * Execute SQL text built at runtime within this transaction. See
898
+ * {@link TurbineClient.rawUnsafe}: nothing in `text` is escaped, so it must
899
+ * never contain untrusted input. Values go in `values`, referenced as `$1`,
900
+ * `$2`, ... (the engine's own placeholders on other engines).
901
+ */
902
+ rawUnsafe<T extends Record<string, unknown> = Record<string, unknown>>(text: string, ...values: unknown[]): Promise<T[]>;
903
+ /**
904
+ * Typed raw SQL on this transaction's connection, like
905
+ * {@link TurbineClient.sql}: await it for `T[]`, or call `.one()` /
906
+ * `.scalar()`. Refused, like every other `tx` call, once the transaction has
907
+ * settled.
908
+ */
909
+ sql<T extends Record<string, unknown> = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): TypedSqlQuery<T>;
910
+ private runRaw;
911
+ /** Bound Dates as UTC text on a PostgreSQL client reading temporal types as UTC. */
912
+ private rawParams;
629
913
  /**
630
914
  * @internal The `turbine-orm/prisma-compat` adapter's transaction seam. NOT
631
915
  * application API: use {@link raw}, whose tagged template makes concatenating
@@ -663,7 +947,8 @@ export declare class TransactionClient {
663
947
  *
664
948
  * pg driver errors thrown by queries are translated into typed Turbine
665
949
  * errors via wrapPgError so transaction-scoped queries surface the same
666
- * typed errors as pool-scoped queries.
950
+ * typed errors as pool-scoped queries. Every statement passes through the
951
+ * transaction state, which refuses it once the transaction has settled.
667
952
  */
668
953
  private createTxPool;
669
954
  }
@@ -681,6 +966,13 @@ export declare class TurbineClient {
681
966
  * `db.pool as unknown as Pool`.
682
967
  */
683
968
  readonly pool: PgCompatPool;
969
+ /**
970
+ * The pool Turbine's own statements go through: {@link pool}, or for a
971
+ * caller-supplied PostgreSQL pool a view of it whose queries carry Turbine's
972
+ * int8 and temporal parsers (see supplied-pool-types.ts). `pool` stays the
973
+ * caller's object, and queries the caller runs on it keep their parsers.
974
+ */
975
+ private readonly queryPool;
684
976
  /** The schema metadata this client was built from */
685
977
  readonly schema: SchemaMetadata;
686
978
  private static int8ParserRegistered;
@@ -708,6 +1000,10 @@ export declare class TurbineClient {
708
1000
  */
709
1001
  private static utcTimestampParsersRegistered;
710
1002
  private readonly logging;
1003
+ /** Where this client's log lines go (the `logger` option, else console). */
1004
+ private readonly logger;
1005
+ /** The `diagnostics` option, resolved once at construction. */
1006
+ private readonly diagnostics;
711
1007
  /** Active SQL dialect, owns transaction keywords, set_config, raw-SQL placeholders, capability flags. */
712
1008
  private readonly dialect;
713
1009
  /** Validated `plan_cache_mode` to pin on every owned connection, or undefined to issue nothing. */
@@ -716,6 +1012,8 @@ export declare class TurbineClient {
716
1012
  private readonly middlewares;
717
1013
  private readonly queryListeners;
718
1014
  private queryOptions;
1015
+ /** The resolved `sqlComment` every accessor shares, for the batch paths that bypass them. */
1016
+ private get sqlCommenter();
719
1017
  private readonly errorMessagesSafe;
720
1018
  /**
721
1019
  * THIS client's error-message mode. Not the process default: see
@@ -732,6 +1030,10 @@ export declare class TurbineClient {
732
1030
  private readonly ownsPool;
733
1031
  /** Active LISTEN subscriptions, torn down on disconnect() so it never hangs */
734
1032
  private readonly activeSubscriptions;
1033
+ /** `$on('poolError')` / `$on('connectionLost')` listeners. */
1034
+ private readonly poolListeners;
1035
+ /** What each pool Turbine opened has checked out, for `disconnect({ timeoutMs })`. */
1036
+ private readonly checkoutWatches;
735
1037
  /**
736
1038
  * Read-replica pools in round-robin order. Empty when no replicas are
737
1039
  * configured, in which case `table()` takes the original single-pool path.
@@ -778,8 +1080,10 @@ export declare class TurbineClient {
778
1080
  private static resolveTemporalInfinity;
779
1081
  private static resolvePlanCacheMode;
780
1082
  /**
781
- * Pin `plan_cache_mode` on every connection an OWNED pool opens, by putting
782
- * it in the pool's **connection parameters** rather than issuing a `SET`.
1083
+ * The pg pool config for a pool Turbine opens: the `maxLifetimeSeconds`
1084
+ * recycling default, plus `plan_cache_mode` and `statement_timeout` pinned
1085
+ * on every connection through the pool's **connection parameters** rather
1086
+ * than a `SET`.
783
1087
  *
784
1088
  * PostgreSQL's `options` startup parameter (`-c plan_cache_mode=...`) is
785
1089
  * applied by the backend as it starts the session, so the setting is in force
@@ -813,7 +1117,50 @@ export declare class TurbineClient {
813
1117
  * should set the GUC on the server or role instead
814
1118
  * (`ALTER ROLE ... SET plan_cache_mode = ...`).
815
1119
  */
816
- private static withPlanCacheMode;
1120
+ private static ownedPoolConfig;
1121
+ /**
1122
+ * Validate `statementTimeoutMs`: a non-negative integer (E003), on
1123
+ * PostgreSQL only (E017), since it is applied as a PostgreSQL connection
1124
+ * parameter.
1125
+ */
1126
+ private static resolveStatementTimeout;
1127
+ /**
1128
+ * The first connection target this client opens that names a connection
1129
+ * pooler (see `detectPooler`), as `host:port`, or `undefined`. Covers the
1130
+ * connection string, the DATABASE_URL fallback, host/port settings, and
1131
+ * string replicas, each read the way node-postgres reads it: a host or port
1132
+ * the target leaves unset is `PGHOST` / `PGPORT`.
1133
+ */
1134
+ private static poolerEndpoint;
1135
+ /** Always on: named statements behind a transaction pooler fail in production first. */
1136
+ private static warnPreparedOnPooler;
1137
+ /**
1138
+ * `pool` as Turbine's own statements should reach it: with Turbine's int8
1139
+ * and temporal parsers on every query, for a PostgreSQL pool built on
1140
+ * node-postgres. Any other pool is returned unchanged.
1141
+ */
1142
+ private typedPool;
1143
+ /**
1144
+ * Listen to a pool this client opened: its idle-connection errors become
1145
+ * `poolError` events, a checked-out connection's loss a `connectionLost`
1146
+ * event, and its checkouts are tracked for `disconnect({ timeoutMs })`.
1147
+ * Also the connection guards every owned pool gets (connection-guard.ts):
1148
+ * no unheard `'error'`, a settle after a freeze, typed checkout timeouts.
1149
+ */
1150
+ private watchOwnedPool;
1151
+ /**
1152
+ * Hand a pool event to its listeners. A `poolError` with no listener is
1153
+ * logged at `error`, always; a listener that throws is ignored, like a query
1154
+ * listener.
1155
+ */
1156
+ private emitPoolEvent;
1157
+ /**
1158
+ * `statementTimeoutMs` and an explicit `maxLifetimeSeconds` reach only the
1159
+ * pools Turbine opens. On an external primary they do nothing, which a
1160
+ * warning says once, naming where the setting belongs instead. Always on: an
1161
+ * ignored timeout means no timeout in production.
1162
+ */
1163
+ private warnExternalPoolOptions;
817
1164
  /**
818
1165
  * Refuse a `utcTimestamps` value that contradicts the one an earlier client
819
1166
  * in this process settled the zone-less temporal read parsers (OIDs 1114,
@@ -886,8 +1233,22 @@ export declare class TurbineClient {
886
1233
  * ```
887
1234
  */
888
1235
  $use(middleware: Middleware): void;
889
- $on(_event: 'query', listener: QueryEventListener): void;
890
- $off(_event: 'query', listener: QueryEventListener): void;
1236
+ /**
1237
+ * Subscribe to client events:
1238
+ * - `'query'`: one {@link QueryEvent} per statement.
1239
+ * - `'poolError'`: an idle pooled connection failed ({@link PoolErrorEvent}).
1240
+ * - `'connectionLost'`: a checked-out connection failed ({@link ConnectionLostEvent}).
1241
+ *
1242
+ * Pool events come from the pools this client opened; an external `pool`
1243
+ * emits its own. Any other event name throws `ValidationError` (E003).
1244
+ */
1245
+ $on(event: 'query', listener: QueryEventListener): void;
1246
+ $on(event: 'poolError', listener: (event: PoolErrorEvent) => void): void;
1247
+ $on(event: 'connectionLost', listener: (event: ConnectionLostEvent) => void): void;
1248
+ $off(event: 'query', listener: QueryEventListener): void;
1249
+ $off(event: 'poolError', listener: (event: PoolErrorEvent) => void): void;
1250
+ $off(event: 'connectionLost', listener: (event: ConnectionLostEvent) => void): void;
1251
+ private listenersFor;
891
1252
  private observeEngine?;
892
1253
  $observe(config: ObserveConfig): Promise<ObserveHandle>;
893
1254
  /**
@@ -987,7 +1348,13 @@ export declare class TurbineClient {
987
1348
  * extended-query pipeline protocol (one TCP flush, one round-trip).
988
1349
  * On HTTP-based drivers it falls back to sequential execution.
989
1350
  */
990
- pipeline<T extends readonly DeferredQuery<unknown>[]>(...args: T | [T, PipelineOptions?]): Promise<PipelineResults<T>>;
1351
+ pipeline<T extends readonly DeferredQuery<unknown>[]>(...queries: T): Promise<PipelineResults<T>>;
1352
+ pipeline<T extends readonly DeferredQuery<unknown>[]>(queries: readonly [...T], options?: PipelineOptions): Promise<PipelineResults<T>>;
1353
+ /**
1354
+ * The `sqlComment` of each deferred query, named by its own model and action
1355
+ * (a pipeline can mix tables), or `undefined` when this client adds none.
1356
+ */
1357
+ private statementComments;
991
1358
  /**
992
1359
  * {@link pipeline} with one `$on('query')` event per statement. Only taken
993
1360
  * when a listener is registered, so an unobserved pipeline runs exactly as
@@ -1006,6 +1373,10 @@ export declare class TurbineClient {
1006
1373
  pipelineSupported(): Promise<boolean>;
1007
1374
  /**
1008
1375
  * Execute a raw SQL query with parameter interpolation via tagged templates.
1376
+ * Every `${value}` is sent as a bound parameter. Only a real tagged-template
1377
+ * call is accepted: an array passed in its place throws `ValidationError`
1378
+ * (E003), since its text would reach the SQL unescaped. For statement text
1379
+ * built at runtime, see {@link rawUnsafe}.
1009
1380
  *
1010
1381
  * @example
1011
1382
  * ```ts
@@ -1017,6 +1388,26 @@ export declare class TurbineClient {
1017
1388
  * ```
1018
1389
  */
1019
1390
  raw<T extends Record<string, unknown> = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): Promise<T[]>;
1391
+ /**
1392
+ * Execute SQL text built at runtime: DDL read from a file, a statement in
1393
+ * another engine's own language (PowQL), a migration step. Values go in
1394
+ * `values` and are referenced from the text with the engine's placeholders
1395
+ * (`$1`, `$2`, ... on PostgreSQL), so they are still bound.
1396
+ *
1397
+ * NOTHING IN `text` IS ESCAPED. It is the one raw entry point where
1398
+ * concatenating input into the statement is possible, which is why it is
1399
+ * separate from {@link raw} and named for it: never build `text` from
1400
+ * request data. Prefer {@link raw} or {@link sql} wherever the statement is
1401
+ * known when the code is written.
1402
+ *
1403
+ * @example
1404
+ * ```ts
1405
+ * for (const statement of ddlStatements) await db.rawUnsafe(statement);
1406
+ * await db.rawUnsafe('SELECT * FROM users WHERE id = $1', userId);
1407
+ * ```
1408
+ */
1409
+ rawUnsafe<T extends Record<string, unknown> = Record<string, unknown>>(text: string, ...values: unknown[]): Promise<T[]>;
1410
+ private runRaw;
1020
1411
  /**
1021
1412
  * @internal The `turbine-orm/prisma-compat` adapter's pool-level raw seam,
1022
1413
  * the twin of {@link TransactionClient.rawQuery}. NOT application API: use
@@ -1030,6 +1421,12 @@ export declare class TurbineClient {
1030
1421
  rows: T[];
1031
1422
  rowCount: number | null;
1032
1423
  }>;
1424
+ /**
1425
+ * Bound Dates as UTC text, on a PostgreSQL client reading zone-less temporal
1426
+ * types as UTC (see {@link utcDateParams}). Typed builder writes convert per
1427
+ * column instead and never come through here.
1428
+ */
1429
+ private rawParams;
1033
1430
  /**
1034
1431
  * Run `fn` with `tag` attached to every `$on('query')` event it causes, so a
1035
1432
  * listener can attribute queries to the feature or code path that issued
@@ -1086,10 +1483,16 @@ export declare class TurbineClient {
1086
1483
  * connection. For the typed API, use `$transaction()` instead.
1087
1484
  *
1088
1485
  * The client is typed as {@link PgCompatPoolClient}, the driver-neutral
1089
- * contract: `query(text, values)` plus `release()`, which is all any
1090
- * supported driver guarantees. For the pg-only surface (cursors, COPY
1091
- * streams, the `QueryConfig` object form), import `PoolClient` from the pg
1092
- * package and cast: `client as unknown as PoolClient`.
1486
+ * contract: `query(text, values)`, which is all any supported driver
1487
+ * guarantees. For the pg-only surface (cursors, COPY streams, the
1488
+ * `QueryConfig` object form), import `PoolClient` from the pg package and
1489
+ * cast: `client as unknown as PoolClient`.
1490
+ *
1491
+ * Like `$transaction`'s `tx`, the client is closed when the transaction
1492
+ * settles: a query sent through it afterwards throws `ValidationError`
1493
+ * (E003). The callback must not `release()` it, and must await every query
1494
+ * it starts: one still running when the callback returns rolls the
1495
+ * transaction back with E003.
1093
1496
  *
1094
1497
  * @example
1095
1498
  * ```ts
@@ -1142,13 +1545,50 @@ export declare class TurbineClient {
1142
1545
  * ]);
1143
1546
  * ```
1144
1547
  */
1145
- $transaction<T extends readonly DeferredQuery<unknown>[]>(queries: readonly [...T]): Promise<PipelineResults<T>>;
1548
+ $transaction<T extends readonly DeferredQuery<unknown>[]>(queries: readonly [...T], options?: TransactionOptions): Promise<PipelineResults<T>>;
1146
1549
  private runTransaction;
1147
1550
  /**
1148
- * Execute a batch of {@link DeferredQuery} objects atomically inside one
1149
- * transaction. Backs the `$transaction([...])` array overload. Reuses the raw
1150
- * {@link transaction} machinery (BEGIN/COMMIT/ROLLBACK + connection release);
1151
- * each result is passed through its query's `transform`.
1551
+ * Validate every transaction option and build the statements they need,
1552
+ * BEFORE a pool slot is taken: a bad argument is the caller's bug and should
1553
+ * not cost a connection to discover.
1554
+ */
1555
+ private resolveTransactionOptions;
1556
+ /**
1557
+ * The one transaction lifecycle both `$transaction` forms share, so an option
1558
+ * one form honours is honoured by the other: BEGIN (isolation level, read
1559
+ * only) -> `sessionContext` GUCs -> body, raced against `timeout` -> COMMIT,
1560
+ * and ROLLBACK on any failure.
1561
+ *
1562
+ * The transaction state is ended as soon as the body settles, BEFORE the
1563
+ * COMMIT, so a `tx` that leaked out of the callback can never slip a
1564
+ * statement in ahead of the COMMIT or onto the connection after it. Work the
1565
+ * body started and did not await is caught there too: the transaction rolls
1566
+ * back rather than committing whatever part of it had run.
1567
+ *
1568
+ * A transaction that will not commit (timed out, failed, unawaited work) is
1569
+ * stopped on the server before its connection is released: the running
1570
+ * statement is cancelled and ROLLBACK sent, so its locks are freed now (see
1571
+ * {@link rollbackOnServer}). A connection that cannot be confirmed clean is
1572
+ * destroyed instead of reused.
1573
+ */
1574
+ private runInTransaction;
1575
+ /**
1576
+ * Validate `sessionContext` and build its `set_config` statements, before
1577
+ * any connection is taken.
1578
+ *
1579
+ * A name must be a namespaced custom setting (`app.tenant_id`) unless it is
1580
+ * listed in `allowBuiltinSettings`: an un-dotted name is one of PostgreSQL's
1581
+ * own settings (`role`, `search_path`, `statement_timeout`), and a context
1582
+ * built from request data must not be able to reach one. A value that is not
1583
+ * a string, number or boolean is refused, so a missing tenant id cannot reach
1584
+ * a row-level security policy as the text `'undefined'`.
1585
+ */
1586
+ private resolveSessionContext;
1587
+ /**
1588
+ * Run a batch of {@link DeferredQuery} objects on a transaction's connection.
1589
+ * Backs the `$transaction([...])` array overload; the transaction itself
1590
+ * (BEGIN/COMMIT/ROLLBACK, options, release) is {@link runInTransaction}'s.
1591
+ * Each result is passed through its query's `transform`.
1152
1592
  *
1153
1593
  * Execution strategy on the single transaction connection:
1154
1594
  * - **Sequential (default).** Await each statement's reply before sending
@@ -1169,7 +1609,7 @@ export declare class TurbineClient {
1169
1609
  * rethrowing, which keeps the connection's request/reply pairing intact and
1170
1610
  * means ROLLBACK is only issued once no statement is still in flight.
1171
1611
  */
1172
- private transactionBatch;
1612
+ private runBatch;
1173
1613
  /**
1174
1614
  * Convenience wrapper around `$transaction` for the multi-tenant / RLS case:
1175
1615
  * runs `fn` inside a transaction with the given session GUCs applied via
@@ -1212,7 +1652,8 @@ export declare class TurbineClient {
1212
1652
  * ends it instead. Postgres does not hold notifications for a disconnected
1213
1653
  * listener, so anything sent during the gap is lost: use
1214
1654
  * `options.onReconnect` to resynchronise. `options.onError` receives the loss
1215
- * and each failed attempt (default: one `console.error` line each).
1655
+ * and each failed attempt (default: one log line each through `logger`, at
1656
+ * `warn` while it reconnects and `error` once it stops).
1216
1657
  *
1217
1658
  * @example
1218
1659
  * ```ts
@@ -1262,15 +1703,47 @@ export declare class TurbineClient {
1262
1703
  * Throws if the connection fails.
1263
1704
  */
1264
1705
  connect(): Promise<void>;
1706
+ /**
1707
+ * Run one cheap round trip (`SELECT 1`) on the primary and resolve with its
1708
+ * latency in milliseconds, for health checks and for setting
1709
+ * `autoRoundTripMs`. The time measures the statement on a checked-out
1710
+ * connection, not the wait for one: an exhausted pool rejects with
1711
+ * `ConnectionError` (E004) after `connectionTimeoutMs` instead. Rejects with
1712
+ * the typed connection error when the database cannot be reached.
1713
+ */
1714
+ $ping(): Promise<number>;
1715
+ /**
1716
+ * `SELECT 1` on a checked-out primary connection, timed. A connection the
1717
+ * server closed while idle says nothing about whether the database is
1718
+ * reachable, so it runs on a fresh one when the first turns out to be dead
1719
+ * (see openCheckout).
1720
+ */
1721
+ private roundTrip;
1265
1722
  /**
1266
1723
  * Gracefully shut down the connection pool.
1267
1724
  *
1268
- * If Turbine was given an external pool via `TurbineConfig.pool`, this
1269
- * method is a no-op, the caller is responsible for the pool's lifecycle.
1725
+ * Waits for every checked-out connection to be returned, then closes them
1726
+ * all. With `timeoutMs`, it stops waiting at the deadline: each connection
1727
+ * still in use has its running statement cancelled on the server and is
1728
+ * closed (a transaction on it rolls back), and the result says how many
1729
+ * were. Resolves within about `timeoutMs` plus two seconds of grace.
1730
+ *
1731
+ * If Turbine was given an external pool via `TurbineConfig.pool`, the
1732
+ * primary is left open, the caller is responsible for its lifecycle;
1733
+ * Turbine-owned replica pools are still closed.
1270
1734
  */
1271
1735
  disconnect(): Promise<void>;
1736
+ disconnect(options: DisconnectOptions): Promise<DisconnectResult>;
1737
+ /**
1738
+ * Cancel the running statement of every connection still checked out of a
1739
+ * pool this client opened, then close the connection. Cancelling first is
1740
+ * what stops the work on the server: a closed socket alone is noticed only
1741
+ * when the backend next writes to it. Returns how many were closed.
1742
+ */
1743
+ private forceCloseCheckedOut;
1272
1744
  /** Alias for disconnect() */
1273
1745
  end(): Promise<void>;
1746
+ end(options: DisconnectOptions): Promise<DisconnectResult>;
1274
1747
  /**
1275
1748
  * Pool statistics for monitoring. Returns zeros for pools that don't
1276
1749
  * expose connection counts (e.g., stateless HTTP drivers like Neon).