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/schema.d.ts CHANGED
@@ -79,10 +79,24 @@ export interface ColumnMetadata {
79
79
  pgTypeSchema?: string;
80
80
  /** TypeScript type string (e.g. 'number', 'string', 'Date') */
81
81
  tsType: string;
82
+ /**
83
+ * The TypeScript type a `defineSchema()` file declared for this column, WITHOUT
84
+ * `| null`. Only `turbine generate` reads it. Every engine branches on
85
+ * {@link tsType}, which stays the type mapped from the database type (MySQL
86
+ * boolean normalization and PowDB column types depend on that). Absent for
87
+ * introspected metadata and for columns that declare no type.
88
+ */
89
+ declaredTsType?: string;
82
90
  /** Whether the column allows NULL */
83
91
  nullable: boolean;
84
92
  /** Whether the column has a DEFAULT, is serial, or is generated */
85
93
  hasDefault: boolean;
94
+ /**
95
+ * The column's declared DEFAULT expression as SQL text (for example `now()`, `'active'`, `0`), when the
96
+ * metadata source knows it. defineSchema records it; engines with no server-side defaults (PowDB) apply it
97
+ * client-side. Absent for introspected metadata.
98
+ */
99
+ defaultValue?: string | null;
86
100
  /**
87
101
  * Whether the **database server** generates this column's value on insert -
88
102
  * a `serial`/`BIGSERIAL` sequence, an `IDENTITY` column, or PowDB's `auto`
@@ -114,10 +128,10 @@ export interface ColumnMetadata {
114
128
  * True when this column holds personally identifiable information (PII).
115
129
  * Tagged in `defineSchema` (`pii: true`) and carried through generated
116
130
  * metadata. A PII column is EXCLUDED from default projections: it comes back
117
- * only when explicitly named in `select` or when the query passes
118
- * `includePii: UNSAFE` (full opt-in, the imported sentinel symbol, since a
119
- * literal `true` is what a stray JSON body deserializes to). Studio redacts
120
- * PII cells by default.
131
+ * only from a read that passes `includePii: UNSAFE` (the imported sentinel
132
+ * symbol, since a literal `true` is what a stray JSON body deserializes to).
133
+ * Naming it in `select` without that flag throws (E003), and a write never
134
+ * returns it. Studio redacts PII cells by default.
121
135
  * Optional / defaults to `false`; untagged schemas behave exactly as before.
122
136
  * Introspection never auto-tags PII (it is a code-first declaration).
123
137
  */
@@ -228,8 +242,15 @@ export interface IndexMetadata {
228
242
  */
229
243
  declared?: boolean;
230
244
  }
231
- /** Map a Postgres type to its TypeScript equivalent */
245
+ /** Map a Postgres type to the TypeScript type a read returns for it. */
232
246
  export declare function pgTypeToTs(pgType: string, nullable: boolean): string;
247
+ /**
248
+ * Map a Postgres type to the TypeScript type the write path accepts for it
249
+ * (the generated `*Create` / `*Update` field types). Wider than
250
+ * {@link pgTypeToTs} where the runtime takes more than one spelling, narrower
251
+ * where the parsed read value cannot be written back (see `PG_TO_TS_INPUT`).
252
+ */
253
+ export declare function pgTypeToTsInput(pgType: string, nullable: boolean): string;
233
254
  /** Check if a Postgres type is a date/timestamp that needs Date parsing */
234
255
  export declare function isDateType(pgType: string): boolean;
235
256
  /**
package/dist/schema.js CHANGED
@@ -14,6 +14,17 @@ export function normalizeKeyColumns(key) {
14
14
  // ---------------------------------------------------------------------------
15
15
  // Type mapping: Postgres → TypeScript
16
16
  // ---------------------------------------------------------------------------
17
+ /**
18
+ * The TypeScript row types below describe what a READ returns, which is what
19
+ * the pg driver's text parsers produce (plus the parsers TurbineClient
20
+ * registers), not what the type is called. A nested relation read reproduces
21
+ * the same value through `JSON_WIRE_COERCION_OIDS` in query/utils.ts.
22
+ * `src/test/types-runtime-parity.integration.test.ts` reads every type live and
23
+ * compares it with this table.
24
+ */
25
+ const PG_INTERVAL_TS = '{ years?: number; months?: number; days?: number; hours?: number; minutes?: number; seconds?: number; milliseconds?: number }';
26
+ const PG_POINT_TS = '{ x: number; y: number }';
27
+ const PG_CIRCLE_TS = '{ x: number; y: number; radius: number }';
17
28
  const PG_TO_TS = {
18
29
  // Integers
19
30
  int2: 'number',
@@ -45,7 +56,9 @@ const PG_TO_TS = {
45
56
  date: 'Date',
46
57
  time: 'string',
47
58
  timetz: 'string',
48
- interval: 'string',
59
+ // The driver parses an interval into an object (postgres-interval). Zero
60
+ // fields are absent, so every field is optional.
61
+ interval: PG_INTERVAL_TS,
49
62
  // JSON
50
63
  json: 'unknown',
51
64
  jsonb: 'unknown',
@@ -55,17 +68,34 @@ const PG_TO_TS = {
55
68
  inet: 'string',
56
69
  cidr: 'string',
57
70
  macaddr: 'string',
58
- // Geometric
59
- point: 'string',
71
+ macaddr8: 'string',
72
+ // Geometric: the driver parses point and circle; the others stay text.
73
+ point: PG_POINT_TS,
60
74
  line: 'string',
61
75
  lseg: 'string',
62
76
  box: 'string',
63
77
  path: 'string',
64
78
  polygon: 'string',
65
- circle: 'string',
79
+ circle: PG_CIRCLE_TS,
80
+ // Bit strings come back as their text form ('1010').
81
+ bit: 'string',
82
+ varbit: 'string',
66
83
  // TSVector
67
84
  tsvector: 'string',
68
85
  tsquery: 'string',
86
+ // Ranges and multiranges come back as their text form ('[1,5)').
87
+ int4range: 'string',
88
+ int8range: 'string',
89
+ numrange: 'string',
90
+ tsrange: 'string',
91
+ tstzrange: 'string',
92
+ daterange: 'string',
93
+ int4multirange: 'string',
94
+ int8multirange: 'string',
95
+ nummultirange: 'string',
96
+ tsmultirange: 'string',
97
+ tstzmultirange: 'string',
98
+ datemultirange: 'string',
69
99
  // pgvector, embeddings. Mapped to `number[]` for DX (the natural shape an app
70
100
  // passes when inserting / comparing embeddings). NOTE: like `numeric` above,
71
101
  // there is a runtime caveat, pg has no built-in parser for the `vector` type,
@@ -77,6 +107,35 @@ const PG_TO_TS = {
77
107
  // param, so writing/comparing is unaffected by the read-side caveat.
78
108
  vector: 'number[]',
79
109
  };
110
+ /**
111
+ * Array types whose element type is NOT the scalar's row type. The driver's
112
+ * `bigint[]` parser returns every element as a string (the scalar int8 parser
113
+ * TurbineClient registers does not apply to arrays), so `bigint[]` reads as
114
+ * `string[]` while `bigint` reads as `number`. The driver has no `circle[]`
115
+ * parser, so its elements stay text (`'<(1,2),3>'`) where a scalar circle is an
116
+ * object.
117
+ */
118
+ const PG_ARRAY_TO_TS = {
119
+ _int8: 'string[]',
120
+ _circle: 'string[]',
121
+ };
122
+ /**
123
+ * Types whose WRITE input differs from the row type. Each entry is what the
124
+ * write path accepts, checked live:
125
+ * - int8: a `number`, a decimal string (what a large value reads back as) or
126
+ * a `bigint`.
127
+ * - numeric: the exact string, or a `number`.
128
+ * - interval / point / circle: the text form. The parsed object a read
129
+ * returns is not accepted for point and circle, and a plain object is read
130
+ * as an update operator, so the object form is not part of the input type.
131
+ */
132
+ const PG_TO_TS_INPUT = {
133
+ int8: 'number | string | bigint',
134
+ numeric: 'string | number',
135
+ interval: 'string',
136
+ point: 'string',
137
+ circle: 'string',
138
+ };
80
139
  const DATE_TYPES = new Set(['timestamptz', 'timestamp', 'date']);
81
140
  /**
82
141
  * Postgres type → the array cast `createMany`'s `UNNEST(ARRAY[...]::<type>)`
@@ -154,21 +213,53 @@ const PG_TO_ARRAY = {
154
213
  // pgvector
155
214
  vector: 'vector[]',
156
215
  };
157
- /** Map a Postgres type to its TypeScript equivalent */
216
+ /** Map a Postgres type to the TypeScript type a read returns for it. */
158
217
  export function pgTypeToTs(pgType, nullable) {
159
- // Array types: udt_name starts with '_'
218
+ let tsType;
219
+ if (Object.hasOwn(PG_ARRAY_TO_TS, pgType)) {
220
+ tsType = PG_ARRAY_TO_TS[pgType];
221
+ }
222
+ else if (pgType.startsWith('_')) {
223
+ // Array types: udt_name starts with '_'
224
+ tsType = arrayOf(pgTypeToTs(pgType.slice(1), false));
225
+ }
226
+ else {
227
+ tsType = Object.hasOwn(PG_TO_TS, pgType) ? PG_TO_TS[pgType] : 'unknown';
228
+ }
229
+ return nullable ? `${tsType} | null` : tsType;
230
+ }
231
+ /**
232
+ * Map a Postgres type to the TypeScript type the write path accepts for it
233
+ * (the generated `*Create` / `*Update` field types). Wider than
234
+ * {@link pgTypeToTs} where the runtime takes more than one spelling, narrower
235
+ * where the parsed read value cannot be written back (see `PG_TO_TS_INPUT`).
236
+ */
237
+ export function pgTypeToTsInput(pgType, nullable) {
238
+ let tsType;
160
239
  if (pgType.startsWith('_')) {
161
- const elementType = pgTypeToTs(pgType.slice(1), false);
162
- const tsType = `${elementType}[]`;
163
- return nullable ? `${tsType} | null` : tsType;
240
+ const element = pgType.slice(1);
241
+ tsType = Object.hasOwn(PG_TO_TS_INPUT, element) ? arrayOf(PG_TO_TS_INPUT[element]) : pgTypeToTs(pgType, false);
242
+ }
243
+ else {
244
+ tsType = Object.hasOwn(PG_TO_TS_INPUT, pgType) ? PG_TO_TS_INPUT[pgType] : pgTypeToTs(pgType, false);
164
245
  }
165
- const tsType = PG_TO_TS[pgType] ?? 'unknown';
166
246
  return nullable ? `${tsType} | null` : tsType;
167
247
  }
248
+ /** `T[]`, parenthesized when `T` is a union. */
249
+ function arrayOf(element) {
250
+ return element.includes('|') ? `(${element})[]` : `${element}[]`;
251
+ }
168
252
  /** Check if a Postgres type is a date/timestamp that needs Date parsing */
169
253
  export function isDateType(pgType) {
170
254
  return DATE_TYPES.has(pgType);
171
255
  }
256
+ /**
257
+ * A precision suffix with the whitespace around it: `(6)` in `time(6)` or
258
+ * `time (6) with time zone`. Every quantifier is bounded, because a leading
259
+ * `\s*` retried from each position of a long whitespace run is quadratic in
260
+ * the type name's length; no real type name comes near the bounds.
261
+ */
262
+ const TYPE_PRECISION = /\s{0,32}\(\s{0,32}\d{1,9}\s{0,32}\)\s{0,32}/;
172
263
  /**
173
264
  * Classify a column type as a TIME-OF-DAY type (`time` / `timetz`), or `null`
174
265
  * for anything else.
@@ -194,7 +285,7 @@ export function timeOfDayKind(dbType) {
194
285
  .toLowerCase()
195
286
  // Precision can sit at the end (`time(6)`) or mid-spelling
196
287
  // (`time(6) with time zone`), so strip it wherever it appears.
197
- .replace(/\s*\(\s*\d+\s*\)\s*/, ' ')
288
+ .replace(TYPE_PRECISION, ' ')
198
289
  .trim();
199
290
  if (t === 'time' || t === 'time without time zone')
200
291
  return 'time';
@@ -229,7 +320,7 @@ export function localDateTimeKind(dbType) {
229
320
  .toLowerCase()
230
321
  // Precision can sit at the end (`timestamp(3)`) or mid-spelling
231
322
  // (`timestamp(3) without time zone`), so strip it wherever it appears.
232
- .replace(/\s*\(\s*\d+\s*\)\s*/, ' ')
323
+ .replace(TYPE_PRECISION, ' ')
233
324
  .trim();
234
325
  if (t === 'date')
235
326
  return 'date';
@@ -239,13 +330,18 @@ export function localDateTimeKind(dbType) {
239
330
  }
240
331
  /** Get the Postgres array cast type for UNNEST batch inserts */
241
332
  export function pgArrayType(pgType) {
242
- return PG_TO_ARRAY[pgType] ?? 'text[]';
333
+ return Object.hasOwn(PG_TO_ARRAY, pgType) ? PG_TO_ARRAY[pgType] : 'text[]';
243
334
  }
244
335
  // ---------------------------------------------------------------------------
245
336
  // Name conversion utilities
246
337
  // ---------------------------------------------------------------------------
247
338
  /** snake_case → camelCase */
248
339
  export function snakeToCamel(s) {
340
+ // The pattern needs an underscore, so a name without one is returned as-is
341
+ // without running the regex. Hot: every where/orderBy key goes through
342
+ // resolveRelation, which calls this for each non-relation key.
343
+ if (s.indexOf('_') === -1)
344
+ return s;
249
345
  return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
250
346
  }
251
347
  /** camelCase → snake_case */
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Where the bind placeholders are in a statement, read with the lexical rules
3
+ * of the engine that will run it.
4
+ *
5
+ * The SQLite, MySQL and SQL Server shims rewrite placeholders before handing a
6
+ * statement to their driver: a `$n` written in raw SQL becomes the engine's own
7
+ * spelling, and MySQL expands an `in` list into one placeholder per element. A
8
+ * rewrite that lands inside a string literal, a quoted identifier or a comment
9
+ * changes what the statement means, so the scan has to agree with the ENGINE
10
+ * about which characters are code. That is why this is not the migration
11
+ * tokenizer in `cli/sql-statements.ts`: that one copies PostgreSQL's lexer
12
+ * (dollar quoting, `E''` strings, nested comments) and knows nothing of
13
+ * backtick or bracket identifiers, `#` comments or MySQL's backslash escapes,
14
+ * each of which would put a rewrite inside quoted text here.
15
+ *
16
+ * Where a reading is ambiguous the scan treats more text as quoted, never less:
17
+ * a placeholder it fails to see reaches the driver unrewritten and fails
18
+ * loudly, while one it invents inside a literal silently changes stored data.
19
+ * MySQL's backslash escapes are the case in point. A pool Turbine builds turns
20
+ * them off (`NO_BACKSLASH_ESCAPES`), an injected pool may not, and treating
21
+ * `\'` as an escape is the reading that can only hide a placeholder.
22
+ *
23
+ * Zero imports: every engine entry shares it.
24
+ */
25
+ /** Which engine's lexical rules to apply. */
26
+ export type PlaceholderSyntax = 'mysql' | 'sqlite' | 'mssql';
27
+ /** One placeholder found in code (never inside a literal, a quoted identifier or a comment). */
28
+ export interface PlaceholderToken {
29
+ /** `$n` (the portable positional spelling), a named `:name` (MySQL only), or a bare `?`. */
30
+ readonly kind: 'positional' | 'named' | 'question';
31
+ /** Offset of the first character (`$` or `:`). */
32
+ readonly start: number;
33
+ /** Offset just past the last character. */
34
+ readonly end: number;
35
+ /** The digits of a `$n`, the name of a `:name`, or `?`. */
36
+ readonly name: string;
37
+ }
38
+ /**
39
+ * Every placeholder in `sql` that the engine would read as code, in order.
40
+ *
41
+ * `$n` is reported only where the engine would see a token of its own: not
42
+ * when the `$` continues a word (`price$1`), and not when the digits run on
43
+ * into one (`$1abc`, which MySQL reads as an identifier).
44
+ */
45
+ export declare function scanPlaceholders(sql: string, syntax: PlaceholderSyntax): PlaceholderToken[];
46
+ /**
47
+ * Rewrite every `$n` in `sql` to the engine's spelling of value `n`, checked
48
+ * against the number of values the statement was given. Returns `sql` itself
49
+ * when it holds no `$n`. A statement given no values is never rewritten: on
50
+ * MySQL `$1` is a legal identifier, and with nothing to bind there is nothing
51
+ * a placeholder could mean.
52
+ *
53
+ * `refuse` throws the caller's error for a `$n` that names no value, and for a
54
+ * bare `?`: these shims bind values by name, so a `?` could never receive one.
55
+ */
56
+ export declare function rewritePositionalPlaceholders(sql: string, syntax: PlaceholderSyntax, valueCount: number, spell: (index: number) => string, refuse: (message: string) => never): string;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Where the bind placeholders are in a statement, read with the lexical rules
3
+ * of the engine that will run it.
4
+ *
5
+ * The SQLite, MySQL and SQL Server shims rewrite placeholders before handing a
6
+ * statement to their driver: a `$n` written in raw SQL becomes the engine's own
7
+ * spelling, and MySQL expands an `in` list into one placeholder per element. A
8
+ * rewrite that lands inside a string literal, a quoted identifier or a comment
9
+ * changes what the statement means, so the scan has to agree with the ENGINE
10
+ * about which characters are code. That is why this is not the migration
11
+ * tokenizer in `cli/sql-statements.ts`: that one copies PostgreSQL's lexer
12
+ * (dollar quoting, `E''` strings, nested comments) and knows nothing of
13
+ * backtick or bracket identifiers, `#` comments or MySQL's backslash escapes,
14
+ * each of which would put a rewrite inside quoted text here.
15
+ *
16
+ * Where a reading is ambiguous the scan treats more text as quoted, never less:
17
+ * a placeholder it fails to see reaches the driver unrewritten and fails
18
+ * loudly, while one it invents inside a literal silently changes stored data.
19
+ * MySQL's backslash escapes are the case in point. A pool Turbine builds turns
20
+ * them off (`NO_BACKSLASH_ESCAPES`), an injected pool may not, and treating
21
+ * `\'` as an escape is the reading that can only hide a placeholder.
22
+ *
23
+ * Zero imports: every engine entry shares it.
24
+ */
25
+ const DIGIT = /[0-9]/;
26
+ /** Characters that continue an unquoted identifier or number on every engine here. */
27
+ const WORD = /[A-Za-z0-9_$\u0080-￿]/;
28
+ /** SQL Server also continues an identifier with `@` and `#` (`@p1`, `#temp`). */
29
+ const MSSQL_WORD = /[A-Za-z0-9_$@#\u0080-￿]/;
30
+ /** mysql2's named placeholder: `:` then digits, or a letter and word characters. */
31
+ const MYSQL_NAMED = /^:([0-9]+|[A-Za-z][A-Za-z0-9_]*)/;
32
+ /**
33
+ * Offset just past a quoted run that opened at `i`. `close` doubled (`''`,
34
+ * `""`, `` ` ` ``, and `]]` on SQL Server) is an escaped quote, not the end;
35
+ * with `backslash`, a backslash escapes the next character too.
36
+ */
37
+ function skipQuoted(sql, i, close, doubled, backslash) {
38
+ const n = sql.length;
39
+ let j = i + 1;
40
+ while (j < n) {
41
+ const ch = sql[j];
42
+ if (backslash && ch === '\\') {
43
+ j += 2;
44
+ continue;
45
+ }
46
+ if (ch === close) {
47
+ if (doubled && sql[j + 1] === close) {
48
+ j += 2;
49
+ continue;
50
+ }
51
+ return j + 1;
52
+ }
53
+ j++;
54
+ }
55
+ return n;
56
+ }
57
+ /** Offset just past a block comment opening at `i`. SQL Server nests them; MySQL and SQLite do not. */
58
+ function skipBlockComment(sql, i, nests) {
59
+ const n = sql.length;
60
+ let depth = 1;
61
+ let j = i + 2;
62
+ while (j < n) {
63
+ if (nests && sql[j] === '/' && sql[j + 1] === '*') {
64
+ depth++;
65
+ j += 2;
66
+ }
67
+ else if (sql[j] === '*' && sql[j + 1] === '/') {
68
+ j += 2;
69
+ if (--depth === 0)
70
+ return j;
71
+ }
72
+ else {
73
+ j++;
74
+ }
75
+ }
76
+ return n;
77
+ }
78
+ /** True when `--` at `i` opens a comment. MySQL needs whitespace (or the end) after the dashes. */
79
+ function opensLineComment(sql, i, syntax) {
80
+ if (sql[i] !== '-' || sql[i + 1] !== '-')
81
+ return false;
82
+ if (syntax !== 'mysql')
83
+ return true;
84
+ const after = sql[i + 2];
85
+ return after === undefined || /\s/.test(after) || after.charCodeAt(0) < 32;
86
+ }
87
+ /**
88
+ * Every placeholder in `sql` that the engine would read as code, in order.
89
+ *
90
+ * `$n` is reported only where the engine would see a token of its own: not
91
+ * when the `$` continues a word (`price$1`), and not when the digits run on
92
+ * into one (`$1abc`, which MySQL reads as an identifier).
93
+ */
94
+ export function scanPlaceholders(sql, syntax) {
95
+ const out = [];
96
+ const n = sql.length;
97
+ const word = syntax === 'mssql' ? MSSQL_WORD : WORD;
98
+ const backslash = syntax === 'mysql';
99
+ let i = 0;
100
+ while (i < n) {
101
+ const ch = sql[i];
102
+ if (opensLineComment(sql, i, syntax) || (syntax === 'mysql' && ch === '#')) {
103
+ while (i < n && sql[i] !== '\n')
104
+ i++;
105
+ continue;
106
+ }
107
+ if (ch === '/' && sql[i + 1] === '*') {
108
+ i = skipBlockComment(sql, i, syntax === 'mssql');
109
+ continue;
110
+ }
111
+ if (ch === "'") {
112
+ i = skipQuoted(sql, i, "'", true, backslash);
113
+ continue;
114
+ }
115
+ if (ch === '"') {
116
+ // A string on MySQL, an identifier on SQLite and SQL Server: skipped either way.
117
+ i = skipQuoted(sql, i, '"', true, backslash);
118
+ continue;
119
+ }
120
+ if (ch === '`' && syntax !== 'mssql') {
121
+ i = skipQuoted(sql, i, '`', true, false);
122
+ continue;
123
+ }
124
+ if (ch === '[' && syntax !== 'mysql') {
125
+ // SQLite ends a bracket identifier at the first `]`; SQL Server escapes it as `]]`.
126
+ i = skipQuoted(sql, i, ']', syntax === 'mssql', false);
127
+ continue;
128
+ }
129
+ if (ch === '$' && DIGIT.test(sql[i + 1] ?? '')) {
130
+ let j = i + 1;
131
+ while (j < n && DIGIT.test(sql[j]))
132
+ j++;
133
+ if (j < n && word.test(sql[j])) {
134
+ // `$1abc`: one word to the engine, not a placeholder.
135
+ while (j < n && word.test(sql[j]))
136
+ j++;
137
+ i = j;
138
+ continue;
139
+ }
140
+ out.push({ kind: 'positional', start: i, end: j, name: sql.slice(i + 1, j) });
141
+ i = j;
142
+ continue;
143
+ }
144
+ if (ch === '?') {
145
+ out.push({ kind: 'question', start: i, end: i + 1, name: '?' });
146
+ i++;
147
+ continue;
148
+ }
149
+ if (ch === ':' && syntax === 'mysql') {
150
+ const m = MYSQL_NAMED.exec(sql.slice(i, i + 64));
151
+ if (m) {
152
+ out.push({ kind: 'named', start: i, end: i + m[0].length, name: m[1] });
153
+ i += m[0].length;
154
+ continue;
155
+ }
156
+ }
157
+ if (word.test(ch)) {
158
+ // A whole identifier, keyword or number, so a `$` or `:` inside it is never a placeholder.
159
+ let j = i + 1;
160
+ while (j < n && word.test(sql[j]))
161
+ j++;
162
+ i = j;
163
+ continue;
164
+ }
165
+ i++;
166
+ }
167
+ return out;
168
+ }
169
+ /**
170
+ * Rewrite every `$n` in `sql` to the engine's spelling of value `n`, checked
171
+ * against the number of values the statement was given. Returns `sql` itself
172
+ * when it holds no `$n`. A statement given no values is never rewritten: on
173
+ * MySQL `$1` is a legal identifier, and with nothing to bind there is nothing
174
+ * a placeholder could mean.
175
+ *
176
+ * `refuse` throws the caller's error for a `$n` that names no value, and for a
177
+ * bare `?`: these shims bind values by name, so a `?` could never receive one.
178
+ */
179
+ export function rewritePositionalPlaceholders(sql, syntax, valueCount, spell, refuse) {
180
+ if (valueCount === 0 || !/\$[0-9]|\?/.test(sql))
181
+ return sql;
182
+ const tokens = scanPlaceholders(sql, syntax).filter((t) => t.kind !== 'named');
183
+ if (tokens.length === 0)
184
+ return sql;
185
+ let out = '';
186
+ let last = 0;
187
+ for (const t of tokens) {
188
+ if (t.kind === 'question') {
189
+ refuse('A `?` placeholder cannot be bound here: write the values as $1, $2, and so on.');
190
+ }
191
+ const index = Number(t.name);
192
+ if (!Number.isSafeInteger(index) || index < 1 || index > valueCount) {
193
+ refuse(`$${t.name} names no value: the statement was given ${valueCount} ` +
194
+ `value${valueCount === 1 ? '' : 's'}, numbered $1 to $${valueCount}. Pass one value per placeholder.`);
195
+ }
196
+ out += sql.slice(last, t.start) + spell(index);
197
+ last = t.end;
198
+ }
199
+ return out + sql.slice(last);
200
+ }
package/dist/sqlite.d.ts CHANGED
@@ -19,18 +19,24 @@
19
19
  * - `RETURNING` + `ON CONFLICT … DO UPDATE` (SQLite ≥ 3.35) → create / upsert /
20
20
  * update / delete return real rows in a single statement (`resultStrategy =
21
21
  * 'returning'`, same as Postgres).
22
- * - **Single-writer:** one connection / one write transaction at a time;
23
- * concurrent writers get `SQLITE_BUSY` (treated as retryable). `journal_mode =
24
- * WAL` is enabled for file databases to allow concurrent readers.
22
+ * - **Single-writer:** one connection / one transaction at a time. On one
23
+ * client, statements from other callers wait (FIFO, up to `busyTimeoutMs`)
24
+ * while a transaction is open instead of running inside it; another
25
+ * process's writer gets `SQLITE_BUSY`. Both surface as the retryable
26
+ * `SerializationFailureError`. `journal_mode = WAL` is enabled for file
27
+ * databases to allow concurrent readers across connections.
25
28
  * - **Unsupported (throw `UnsupportedFeatureError`):** pgvector distance ops,
26
29
  * LISTEN/NOTIFY (`$listen` / `$notify`), RLS `sessionContext`. Advisory-lock
27
30
  * migration locking is unavailable, SQLite is single-writer, so migrations
28
31
  * serialize naturally.
29
- * - **Type affinity caveats:** SQLite has no native `BOOLEAN` (0/1 integers) or
30
- * `DATE` (TEXT/INTEGER). Booleans bind as 1/0; `Date` values bind as ISO-8601
31
- * text; columns declared `TIMESTAMP`/`DATETIME`/`DATE` are coerced back to
32
- * `Date`. Integers wider than `Number.MAX_SAFE_INTEGER` come back as strings
33
- * (the same safe-int policy Turbine uses for Postgres `int8`).
32
+ * - **Type affinity caveats:** SQLite has no native `BOOLEAN` (0/1 integers),
33
+ * `JSON` (text) or `DATE` (TEXT/INTEGER). Booleans bind as 1/0; `Date` values
34
+ * bind as ISO-8601 text; an integral JS number binds as INTEGER. A column the
35
+ * schema declares boolean or json/jsonb reads back as a boolean or a parsed
36
+ * value on every path; date/time columns are coerced back to `Date`.
37
+ * Integers wider than `Number.MAX_SAFE_INTEGER` come back as strings (the
38
+ * same safe-int policy Turbine uses for Postgres `int8`). `numeric` has no
39
+ * exact storage: it reads back as a JS number.
34
40
  * - **Case-insensitive matching** uses `COLLATE NOCASE`, which is **ASCII-only**
35
41
  * (no Unicode case folding).
36
42
  *
@@ -68,7 +74,7 @@
68
74
  *
69
75
  * const db = turbineSqlite(':memory:', schemaDefToMetadata(schema));
70
76
  * for (const stmt of schemaToSQL(schema, { dialect: sqliteDialect })) {
71
- * await db.raw([stmt] as never);
77
+ * await db.rawUnsafe(stmt);
72
78
  * }
73
79
  *
74
80
  * // `db.table(...)`, not `db.users`: the typed property accessors are emitted
@@ -88,27 +94,75 @@ type QueryArg = string | {
88
94
  text: string;
89
95
  values?: unknown[];
90
96
  };
97
+ /** Options for {@link SqlitePool}. */
98
+ export interface SqlitePoolOptions {
99
+ /**
100
+ * How long, in ms, a statement waits for a transaction another caller has
101
+ * open on this handle before failing with a retryable
102
+ * `SerializationFailureError`. Default: 5000, the same bound as
103
+ * `PRAGMA busy_timeout`.
104
+ */
105
+ busyTimeoutMs?: number;
106
+ /**
107
+ * The client's schema. A column it declares boolean or json/jsonb is read
108
+ * back as a boolean or a parsed value, whatever its SQLite declared type.
109
+ * `turbineSqlite` passes the schema it was given.
110
+ */
111
+ schema?: SchemaMetadata;
112
+ }
91
113
  /**
92
114
  * A `PgCompatPool` backed by a single `node:sqlite` `DatabaseSync` connection.
93
115
  * SQLite is single-connection by nature (a `:memory:` database is per-handle),
94
- * so `connect()` hands back a client over the **same** handle, transactions
95
- * (`BEGIN`/`COMMIT`/`ROLLBACK`, `SAVEPOINT` nesting) just run on it. Queries are
96
- * serialized; this is the documented single-writer model.
116
+ * so `connect()` hands back a client over the **same** handle.
117
+ *
118
+ * One handle carries one transaction, so the pool decides who may use it. A
119
+ * caller whose statement opens a transaction OWNS the handle until that
120
+ * transaction ends; every other caller's statements, plain queries included,
121
+ * wait their turn in FIFO order (bounded by `busyTimeoutMs`) instead of running
122
+ * inside it. Without that, a write issued outside an open transaction joined
123
+ * it, reported success, and was rolled back with it, and reads saw its
124
+ * uncommitted rows. A checkout that does not open a transaction (a stream, a
125
+ * connectivity check) holds nothing, so other callers keep running beside it.
97
126
  */
98
127
  export declare class SqlitePool implements PgCompatPool {
99
128
  /** The underlying `node:sqlite` handle, exposed as an escape hatch (seed/DDL). */
100
129
  readonly db: DatabaseSync;
101
130
  private closed;
102
131
  /**
103
- * `sql -> declared-boolean result columns` (or `null` for none). Bounded like
132
+ * `sql -> result columns to decode` (or `null` for none). Bounded like
104
133
  * the query builder's SQL-template cache, since a caller can generate
105
134
  * unbounded distinct statements (raw SQL, an IN-list that inlines).
106
135
  */
107
- private readonly booleanCache;
108
- constructor(db: DatabaseSync);
136
+ private readonly decoderCache;
137
+ /** Schema columns whose stored form is not their declared JS type (see {@link declaredDecoders}). */
138
+ private readonly declared;
139
+ private readonly busyTimeoutMs;
140
+ /** Who may run statements on the handle right now; `null` when nobody holds it. */
141
+ private owner;
142
+ private readonly waiters;
143
+ /** The owner for statements sent through `pool.query()` rather than a checkout. */
144
+ private readonly poolOwner;
145
+ /** Transaction state when the driver cannot report it (`isTransaction` arrived in Node 22.16). */
146
+ private trackedTransaction;
147
+ constructor(db: DatabaseSync, options?: SqlitePoolOptions);
109
148
  query(text: QueryArg, values?: unknown[]): Promise<any>;
110
149
  connect(): Promise<PgCompatPoolClient>;
111
150
  end(): Promise<void>;
151
+ /** Is the handle inside a transaction right now? */
152
+ private inTransaction;
153
+ /** Run one statement for `owner`, waiting first if another caller's transaction holds the handle. */
154
+ private runAs;
155
+ /** True when the current async context is the callback of the transaction that owns the handle. */
156
+ private calledFromOwnersCallback;
157
+ private waitForHandle;
158
+ /** Give the handle to the next waiter, which runs its statement as soon as it resumes. */
159
+ private handOff;
160
+ /**
161
+ * A checkout ended. If it still holds an open transaction (a timed-out or
162
+ * abandoned callback), roll it back first, so the next caller does not
163
+ * start inside it.
164
+ */
165
+ private releaseOwner;
112
166
  }
113
167
  /**
114
168
  * Map a SQLite declared column type (type affinity) to a TypeScript type.
@@ -131,10 +185,7 @@ export declare const sqliteDialect: Dialect;
131
185
  * Exposed directly so callers (and tests) can introspect an in-process
132
186
  * `:memory:` database without round-tripping through a file path.
133
187
  */
134
- export declare function introspectSqliteDatabase(db: DatabaseSync, options?: {
135
- include?: string[];
136
- exclude?: string[];
137
- }): {
188
+ export declare function introspectSqliteDatabase(db: DatabaseSync, options?: Pick<IntrospectOptions, 'include' | 'exclude' | 'logger'>): {
138
189
  tables: Record<string, TableMetadata>;
139
190
  enums: {};
140
191
  };
@@ -159,7 +210,11 @@ export interface TurbineSqliteOptions extends EngineClientConfig {
159
210
  * Ignored for `':memory:'`. Default: `true`.
160
211
  */
161
212
  wal?: boolean;
162
- /** `PRAGMA busy_timeout` in ms, how long a writer waits on `SQLITE_BUSY`. Default: 5000. */
213
+ /**
214
+ * How long, in ms, a statement waits for the database: `PRAGMA busy_timeout`
215
+ * for another process's lock, and the wait behind a transaction another
216
+ * caller has open on this client. Default: 5000.
217
+ */
163
218
  busyTimeoutMs?: number;
164
219
  /** Enable `PRAGMA foreign_keys` enforcement. Default: `true`. */
165
220
  foreignKeys?: boolean;