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
@@ -0,0 +1,48 @@
1
+ /**
2
+ * turbine-orm, Turbine's column readings on a pool the caller supplied.
3
+ *
4
+ * Turbine reads `bigint` (int8) as a number and, under `utcTimestamps`, the
5
+ * zone-less `timestamp` and `date` types (and their arrays) as UTC. On a pool
6
+ * Turbine opens, those parsers are registered on the pg module. A pool the
7
+ * caller supplies keeps the caller's parser table, so Turbine attaches its
8
+ * parsers to each of its OWN queries instead: node-postgres reads a
9
+ * query-level `types` in place of the connection's. The caller's own queries
10
+ * on the same pool keep the caller's parsers.
11
+ *
12
+ * Everything Turbine does not govern is read from the table the query would
13
+ * otherwise have used (the connection's, else the pool's `types` option, else
14
+ * the driver's), so a parser the caller installed for another type still
15
+ * applies to Turbine's reads.
16
+ */
17
+ import type { PgCompatPool, PgCompatPoolClient } from './pg-types.js';
18
+ /** Where node-postgres reads a parser from: `pg.types`, a pool's `types` option, a client's table. */
19
+ export interface TypeParserTable {
20
+ getTypeParser(oid: number, format?: string): unknown;
21
+ }
22
+ /** int8 as a number while the number is exact, else the driver's string. */
23
+ export declare const parseInt8: (text: string) => number | string;
24
+ /**
25
+ * A parser table with Turbine's readings over `base`. `base` is read at lookup
26
+ * time, so a parser the caller registers later is still the fallback. Built
27
+ * parsers are cached per OID and rebuilt when what they were built over
28
+ * changes.
29
+ */
30
+ export declare function turbineTypeTable(base: () => TypeParserTable, utcTimestamps: boolean): TypeParserTable;
31
+ /**
32
+ * The pool Turbine sends its own queries through, for a caller-supplied
33
+ * PostgreSQL pool: `query` and every client from `connect` carry Turbine's
34
+ * parsers. Everything else (`end`, counts, events, `instanceof`) is the pool's
35
+ * own.
36
+ *
37
+ * Returns `undefined`, meaning "use the pool as it is", for a pool that is not
38
+ * built on node-postgres (it may not read a query config at all) and for a
39
+ * build without the driver's parsers. A driver that accepts a config but
40
+ * ignores its `types` keeps its own readings.
41
+ */
42
+ export declare function typedQueryPool(pool: PgCompatPool, utcTimestamps: boolean): PgCompatPool | undefined;
43
+ /**
44
+ * The driver connection behind a client {@link typedQueryPool} handed out, or
45
+ * `client` itself. Used where the caller is given the connection to run their
46
+ * own statements on, which keep the driver's readings.
47
+ */
48
+ export declare function rawClientOf(client: PgCompatPoolClient): PgCompatPoolClient;
@@ -0,0 +1,247 @@
1
+ /**
2
+ * turbine-orm, Turbine's column readings on a pool the caller supplied.
3
+ *
4
+ * Turbine reads `bigint` (int8) as a number and, under `utcTimestamps`, the
5
+ * zone-less `timestamp` and `date` types (and their arrays) as UTC. On a pool
6
+ * Turbine opens, those parsers are registered on the pg module. A pool the
7
+ * caller supplies keeps the caller's parser table, so Turbine attaches its
8
+ * parsers to each of its OWN queries instead: node-postgres reads a
9
+ * query-level `types` in place of the connection's. The caller's own queries
10
+ * on the same pool keep the caller's parsers.
11
+ *
12
+ * Everything Turbine does not govern is read from the table the query would
13
+ * otherwise have used (the connection's, else the pool's `types` option, else
14
+ * the driver's), so a parser the caller installed for another type still
15
+ * applies to Turbine's reads.
16
+ */
17
+ import pg from '#pg';
18
+ import { createFastTimestamptzParser, createPgArrayParser, createUtcDateParser, createUtcTimestampParser, isDefaultTextParser, markTurbineParser, } from './query/utils.js';
19
+ /** Same key `markTurbineParser` sets (`Symbol.for`, so ESM and CJS copies agree). */
20
+ const TURBINE_PARSER = Symbol.for('turbine.typeParser');
21
+ /** Reads the driver connection back out of a typed client (see {@link rawClientOf}). */
22
+ const RAW_CLIENT = Symbol.for('turbine.rawClient');
23
+ function isTurbineParser(parser) {
24
+ return typeof parser === 'function' && parser[TURBINE_PARSER] === true;
25
+ }
26
+ /** int8 as a number while the number is exact, else the driver's string. */
27
+ export const parseInt8 = markTurbineParser((text) => {
28
+ const n = Number(text);
29
+ return Number.isSafeInteger(n) ? n : text;
30
+ });
31
+ /** The OIDs `utcTimestamps` governs (1184/1185 only for decode speed, as in `registerUtcTemporalParsers`). */
32
+ const UTC_OIDS = new Set([1082, 1114, 1115, 1182, 1184, 1185]);
33
+ /** Array OID to element OID, for the forms built over a scalar parser. */
34
+ const ARRAY_ELEMENT = { 1115: 1114, 1182: 1082, 1185: 1184 };
35
+ /**
36
+ * A parser table with Turbine's readings over `base`. `base` is read at lookup
37
+ * time, so a parser the caller registers later is still the fallback. Built
38
+ * parsers are cached per OID and rebuilt when what they were built over
39
+ * changes.
40
+ */
41
+ export function turbineTypeTable(base, utcTimestamps) {
42
+ const built = new Map();
43
+ const parserFor = (oid) => {
44
+ const fallback = base().getTypeParser(oid, 'text');
45
+ if (oid !== 20 && !(utcTimestamps && UTC_OIDS.has(oid)))
46
+ return fallback;
47
+ const elementOid = ARRAY_ELEMENT[oid];
48
+ const element = elementOid === undefined ? undefined : parserFor(elementOid);
49
+ const cached = built.get(oid);
50
+ if (cached && cached.from === fallback && cached.element === element)
51
+ return cached.parser;
52
+ const parser = isTurbineParser(fallback) ? fallback : build(oid, fallback, element);
53
+ built.set(oid, { from: fallback, element, parser });
54
+ return parser;
55
+ };
56
+ const build = (oid, fallback, element) => {
57
+ switch (oid) {
58
+ case 20:
59
+ return parseInt8;
60
+ case 1082:
61
+ return markTurbineParser(createUtcDateParser(fallback));
62
+ case 1114:
63
+ return markTurbineParser(createUtcTimestampParser(fallback));
64
+ case 1115:
65
+ case 1182:
66
+ return markTurbineParser(createPgArrayParser(element));
67
+ case 1184:
68
+ // Speed only: a caller's own timestamptz parser is kept, as on owned pools.
69
+ return isDefaultTextParser(1184, fallback)
70
+ ? markTurbineParser(createFastTimestamptzParser(fallback))
71
+ : fallback;
72
+ case 1185:
73
+ return isTurbineParser(element) && isDefaultTextParser(1185, fallback)
74
+ ? markTurbineParser(createPgArrayParser(element))
75
+ : fallback;
76
+ default:
77
+ return fallback;
78
+ }
79
+ };
80
+ return {
81
+ getTypeParser(oid, format = 'text') {
82
+ return format === 'text' ? parserFor(oid) : base().getTypeParser(oid, format);
83
+ },
84
+ };
85
+ }
86
+ let driverTypes;
87
+ /**
88
+ * Whether this build has the real driver's parsers to build from. Under the
89
+ * `edge-light` and `browser` conditions `#pg` is a stand-in with none, so
90
+ * Turbine leaves a supplied pool's parsing to that pool's driver there.
91
+ */
92
+ function driverTypesAvailable() {
93
+ if (driverTypes === undefined) {
94
+ try {
95
+ const arrays = pg.types
96
+ .arrayParser;
97
+ driverTypes = Array.isArray(arrays.create('{}', (v) => v).parse());
98
+ }
99
+ catch {
100
+ driverTypes = false;
101
+ }
102
+ }
103
+ return driverTypes;
104
+ }
105
+ function isTable(value) {
106
+ return (typeof value === 'object' &&
107
+ value !== null &&
108
+ typeof value.getTypeParser === 'function');
109
+ }
110
+ /**
111
+ * The table a query on `pool` would use without Turbine: a connected client's
112
+ * (which already folds in the pool's `types` option and the driver copy the
113
+ * pool was built from), else the pool's `types` option, else this module's
114
+ * driver. pg-pool keeps every client it opened in `_clients`; a pool without
115
+ * that field falls through to the option and the driver.
116
+ */
117
+ function poolTypeBase(pool) {
118
+ let fromClient;
119
+ return () => {
120
+ if (fromClient)
121
+ return fromClient;
122
+ const clients = pool._clients;
123
+ if (Array.isArray(clients)) {
124
+ for (const client of clients) {
125
+ const types = client?._types;
126
+ if (isTable(types)) {
127
+ fromClient = types;
128
+ return types;
129
+ }
130
+ }
131
+ }
132
+ const configured = pool.options?.types;
133
+ return isTable(configured) ? configured : pg.types;
134
+ };
135
+ }
136
+ /**
137
+ * `(text, values)` or a query config, with `types` attached. A submittable
138
+ * (a cursor) carries its own result: it gets the table the same way
139
+ * node-postgres gives it the connection's, only when it has none.
140
+ */
141
+ function withTypes(textOrConfig, values, types) {
142
+ if (typeof textOrConfig === 'string') {
143
+ return values === undefined ? { text: textOrConfig, types } : { text: textOrConfig, values, types };
144
+ }
145
+ if (typeof textOrConfig !== 'object' || textOrConfig === null)
146
+ return textOrConfig;
147
+ const config = textOrConfig;
148
+ if (typeof config.submit === 'function') {
149
+ if (config._result && !config._result._types)
150
+ config._result._types = types;
151
+ return config;
152
+ }
153
+ if (config.types !== undefined)
154
+ return config;
155
+ return values === undefined ? { ...config, types } : { ...config, values, types };
156
+ }
157
+ /** Forward every other member to the target, bound to it, so the driver's internals see the real object. */
158
+ function forwardingProxy(target, own) {
159
+ const bound = new Map();
160
+ return new Proxy(target, {
161
+ get(t, prop) {
162
+ const mine = own(prop);
163
+ if (mine !== undefined)
164
+ return mine;
165
+ const value = Reflect.get(t, prop, t);
166
+ if (typeof value !== 'function')
167
+ return value;
168
+ const hit = bound.get(prop);
169
+ if (hit && hit.fn === value)
170
+ return hit.bound;
171
+ const fn = value.bind(t);
172
+ bound.set(prop, { fn: value, bound: fn });
173
+ return fn;
174
+ },
175
+ set(t, prop, value) {
176
+ return Reflect.set(t, prop, value, t);
177
+ },
178
+ });
179
+ }
180
+ /**
181
+ * The pool Turbine sends its own queries through, for a caller-supplied
182
+ * PostgreSQL pool: `query` and every client from `connect` carry Turbine's
183
+ * parsers. Everything else (`end`, counts, events, `instanceof`) is the pool's
184
+ * own.
185
+ *
186
+ * Returns `undefined`, meaning "use the pool as it is", for a pool that is not
187
+ * built on node-postgres (it may not read a query config at all) and for a
188
+ * build without the driver's parsers. A driver that accepts a config but
189
+ * ignores its `types` keeps its own readings.
190
+ */
191
+ export function typedQueryPool(pool, utcTimestamps) {
192
+ if (typeof pool.Client !== 'function' || !driverTypesAvailable()) {
193
+ return undefined;
194
+ }
195
+ const target = pool;
196
+ const table = turbineTypeTable(poolTypeBase(pool), utcTimestamps);
197
+ const clients = new WeakMap();
198
+ const typedClient = (client) => {
199
+ const known = clients.get(client);
200
+ if (known)
201
+ return known;
202
+ const own = client._types;
203
+ const clientTable = isTable(own) ? turbineTypeTable(() => own, utcTimestamps) : table;
204
+ const send = client.query;
205
+ const query = (textOrConfig, values, ...more) => more.length > 0 || typeof values === 'function'
206
+ ? send.call(client, textOrConfig, values, ...more)
207
+ : send.call(client, withTypes(textOrConfig, values, clientTable));
208
+ const typed = forwardingProxy(client, (prop) => {
209
+ if (prop === 'query')
210
+ return query;
211
+ if (prop === '_types')
212
+ return clientTable;
213
+ if (prop === RAW_CLIENT)
214
+ return client;
215
+ return undefined;
216
+ });
217
+ clients.set(client, typed);
218
+ return typed;
219
+ };
220
+ const query = (textOrConfig, values, ...more) => {
221
+ const send = target.query;
222
+ return more.length > 0 || typeof values === 'function'
223
+ ? send.call(pool, textOrConfig, values, ...more)
224
+ : send.call(pool, withTypes(textOrConfig, values, table));
225
+ };
226
+ const connect = (...args) => {
227
+ const open = target.connect;
228
+ if (args.length > 0)
229
+ return open.apply(pool, args);
230
+ return open.call(pool).then(typedClient);
231
+ };
232
+ return forwardingProxy(target, (prop) => {
233
+ if (prop === 'query')
234
+ return query;
235
+ if (prop === 'connect')
236
+ return connect;
237
+ return undefined;
238
+ });
239
+ }
240
+ /**
241
+ * The driver connection behind a client {@link typedQueryPool} handed out, or
242
+ * `client` itself. Used where the caller is given the connection to run their
243
+ * own statements on, which keep the driver's readings.
244
+ */
245
+ export function rawClientOf(client) {
246
+ return client[RAW_CLIENT] ?? client;
247
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * turbine-orm, finding transaction-control statements in SQL the caller wrote
3
+ *
4
+ * A `COMMIT`, `ROLLBACK`, `BEGIN`, `SAVEPOINT` (and the rest of the family) sent
5
+ * through a transaction's raw paths ends or reshapes the transaction behind the
6
+ * state that tracks it: later statements autocommit, a savepoint sits on a stack
7
+ * Turbine did not push, and the final COMMIT or ROLLBACK reports on a transaction
8
+ * that is no longer there. {@link TransactionState.runRaw} refuses them, and this
9
+ * leaf is how it recognises one.
10
+ *
11
+ * It reads statement STARTS only: the leading words of every statement in the
12
+ * text, found with the lexical rules of the engine that will run it, so a
13
+ * `COMMIT` inside a string literal, a quoted identifier, a dollar-quoted body or a
14
+ * comment is not mistaken for a statement. It is NOT a second statement
15
+ * splitter: `cli/sql-statements.ts` owns that job for migrations, and
16
+ * `src/test/fx-tx-control.test.ts` holds this file to it (same statement
17
+ * boundaries, same first word, over an adversarial corpus). It lives apart from
18
+ * that file because the CLI tokenizer is a CLI module (the library imports one
19
+ * file from `cli/`, `destructive.ts`, and nothing else), and importing it here
20
+ * would put it in every engine entry and the edge entry, whose size is a
21
+ * published claim.
22
+ *
23
+ * Where a reading is ambiguous the scan treats more text as quoted, never less:
24
+ * a statement it fails to see goes through exactly as it did before this guard
25
+ * existed, while one it invents inside a literal would refuse a valid query.
26
+ *
27
+ * Zero imports.
28
+ */
29
+ /** Which engine's lexical rules to read the text with. */
30
+ export type ControlSyntax = 'postgresql' | 'mysql' | 'sqlite' | 'mssql';
31
+ export type ControlKind = 'begin' | 'commit' | 'rollback' | 'rollback-to' | 'savepoint' | 'release' | 'set-transaction' | 'prepare';
32
+ /** One transaction-control statement found in a piece of SQL. */
33
+ export interface ControlStatement {
34
+ readonly kind: ControlKind;
35
+ /** The statement's leading keyword(s) in upper case, for a message (`COMMIT`, `START TRANSACTION`). */
36
+ readonly label: string;
37
+ }
38
+ /**
39
+ * Every transaction-control statement in `sql`, in order. Empty when there is
40
+ * none, which is the answer for almost every statement and costs one pass.
41
+ */
42
+ export declare function findTransactionControl(sql: string, syntax: ControlSyntax): ControlStatement[];
43
+ /**
44
+ * The leading words of every statement in `sql`, lower-cased (`?` stands for a
45
+ * token that is not a word: a literal, a quoted identifier, a symbol). This is
46
+ * the whole of what the scan decides, exported so the test that holds it to
47
+ * `cli/sql-statements.ts` can compare statement boundaries directly.
48
+ */
49
+ export declare function leadingWords(sql: string, syntax: ControlSyntax): string[][];
50
+ /** Whether `text` could hold a transaction-control statement at all (a cheap test before the scan). */
51
+ export declare function mayHoldTransactionControl(text: string): boolean;
@@ -0,0 +1,311 @@
1
+ /**
2
+ * turbine-orm, finding transaction-control statements in SQL the caller wrote
3
+ *
4
+ * A `COMMIT`, `ROLLBACK`, `BEGIN`, `SAVEPOINT` (and the rest of the family) sent
5
+ * through a transaction's raw paths ends or reshapes the transaction behind the
6
+ * state that tracks it: later statements autocommit, a savepoint sits on a stack
7
+ * Turbine did not push, and the final COMMIT or ROLLBACK reports on a transaction
8
+ * that is no longer there. {@link TransactionState.runRaw} refuses them, and this
9
+ * leaf is how it recognises one.
10
+ *
11
+ * It reads statement STARTS only: the leading words of every statement in the
12
+ * text, found with the lexical rules of the engine that will run it, so a
13
+ * `COMMIT` inside a string literal, a quoted identifier, a dollar-quoted body or a
14
+ * comment is not mistaken for a statement. It is NOT a second statement
15
+ * splitter: `cli/sql-statements.ts` owns that job for migrations, and
16
+ * `src/test/fx-tx-control.test.ts` holds this file to it (same statement
17
+ * boundaries, same first word, over an adversarial corpus). It lives apart from
18
+ * that file because the CLI tokenizer is a CLI module (the library imports one
19
+ * file from `cli/`, `destructive.ts`, and nothing else), and importing it here
20
+ * would put it in every engine entry and the edge entry, whose size is a
21
+ * published claim.
22
+ *
23
+ * Where a reading is ambiguous the scan treats more text as quoted, never less:
24
+ * a statement it fails to see goes through exactly as it did before this guard
25
+ * existed, while one it invents inside a literal would refuse a valid query.
26
+ *
27
+ * Zero imports.
28
+ */
29
+ /** Words that may follow `BEGIN` when it opens a transaction (and not a block, `BEGIN TRY`, `BEGIN ATOMIC`). */
30
+ const BEGIN_NEXT = new Set([
31
+ 'work',
32
+ 'transaction',
33
+ 'tran',
34
+ 'isolation',
35
+ 'read',
36
+ 'not',
37
+ 'deferrable',
38
+ 'deferred',
39
+ 'immediate',
40
+ 'exclusive',
41
+ 'distributed',
42
+ ]);
43
+ /** Leading words kept per statement: enough for `ROLLBACK WORK TO SAVEPOINT name`. */
44
+ const LEADING_WORDS = 4;
45
+ const WORD_START = /[A-Za-z_\u0080-￿]/;
46
+ const WORD_PART = /[A-Za-z0-9_$\u0080-￿]/;
47
+ const RULES = {
48
+ postgresql: {
49
+ nestedBlockComments: true,
50
+ hashComment: false,
51
+ dashNeedsSpace: false,
52
+ backslashAlways: false,
53
+ dollarQuotes: true,
54
+ backtick: false,
55
+ bracket: false,
56
+ },
57
+ mysql: {
58
+ nestedBlockComments: false,
59
+ hashComment: true,
60
+ dashNeedsSpace: true,
61
+ backslashAlways: true,
62
+ dollarQuotes: false,
63
+ backtick: true,
64
+ bracket: false,
65
+ },
66
+ sqlite: {
67
+ nestedBlockComments: false,
68
+ hashComment: false,
69
+ dashNeedsSpace: false,
70
+ backslashAlways: false,
71
+ dollarQuotes: false,
72
+ backtick: true,
73
+ bracket: true,
74
+ },
75
+ mssql: {
76
+ nestedBlockComments: true,
77
+ hashComment: false,
78
+ dashNeedsSpace: false,
79
+ backslashAlways: false,
80
+ dollarQuotes: false,
81
+ backtick: false,
82
+ bracket: true,
83
+ },
84
+ };
85
+ /** Offset just past the quoted run that opens at `i`, doubled closing quotes honoured. */
86
+ function skipQuoted(sql, i, close, backslash) {
87
+ const n = sql.length;
88
+ let j = i + 1;
89
+ while (j < n) {
90
+ const ch = sql[j];
91
+ if (backslash && ch === '\\') {
92
+ j += 2;
93
+ continue;
94
+ }
95
+ if (ch === close) {
96
+ if (sql[j + 1] === close) {
97
+ j += 2;
98
+ continue;
99
+ }
100
+ return j + 1;
101
+ }
102
+ j++;
103
+ }
104
+ return n;
105
+ }
106
+ /** Offset just past the block comment that opens at `i`. */
107
+ function skipBlockComment(sql, i, nests) {
108
+ const n = sql.length;
109
+ let depth = 1;
110
+ let j = i + 2;
111
+ while (j < n) {
112
+ if (nests && sql[j] === '/' && sql[j + 1] === '*') {
113
+ depth++;
114
+ j += 2;
115
+ }
116
+ else if (sql[j] === '*' && sql[j + 1] === '/') {
117
+ j += 2;
118
+ if (--depth === 0)
119
+ return j;
120
+ }
121
+ else {
122
+ j++;
123
+ }
124
+ }
125
+ return n;
126
+ }
127
+ /** Offset just past the end of the line that `i` is on. */
128
+ function skipLine(sql, i) {
129
+ const end = sql.indexOf('\n', i);
130
+ return end === -1 ? sql.length : end + 1;
131
+ }
132
+ /**
133
+ * Every transaction-control statement in `sql`, in order. Empty when there is
134
+ * none, which is the answer for almost every statement and costs one pass.
135
+ */
136
+ export function findTransactionControl(sql, syntax) {
137
+ const found = [];
138
+ for (const words of leadingWords(sql, syntax)) {
139
+ const statement = classify(words, syntax);
140
+ if (statement !== undefined)
141
+ found.push(statement);
142
+ }
143
+ return found;
144
+ }
145
+ /**
146
+ * The leading words of every statement in `sql`, lower-cased (`?` stands for a
147
+ * token that is not a word: a literal, a quoted identifier, a symbol). This is
148
+ * the whole of what the scan decides, exported so the test that holds it to
149
+ * `cli/sql-statements.ts` can compare statement boundaries directly.
150
+ */
151
+ export function leadingWords(sql, syntax) {
152
+ const rules = RULES[syntax];
153
+ const statements = [];
154
+ const n = sql.length;
155
+ let words = [];
156
+ /** Where the previous word ended, and the word, so `E'...'` is read as one token. */
157
+ let lastWordEnd = -1;
158
+ let lastWord = '';
159
+ /** Inside a `BEGIN ATOMIC ... END` routine body, whose semicolons do not end the statement. */
160
+ let atomic = false;
161
+ let caseDepth = 0;
162
+ const finish = () => {
163
+ if (words.length > 0)
164
+ statements.push(words);
165
+ words = [];
166
+ };
167
+ const note = (word) => {
168
+ if (words.length < LEADING_WORDS)
169
+ words.push(word);
170
+ };
171
+ let i = 0;
172
+ while (i < n) {
173
+ const ch = sql[i];
174
+ if (ch === ';') {
175
+ if (!atomic)
176
+ finish();
177
+ i++;
178
+ continue;
179
+ }
180
+ if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r' || ch === '\f') {
181
+ i++;
182
+ continue;
183
+ }
184
+ if (ch === '-' && sql[i + 1] === '-') {
185
+ const after = sql[i + 2];
186
+ if (!rules.dashNeedsSpace || after === undefined || /\s/.test(after) || after.charCodeAt(0) < 32) {
187
+ i = skipLine(sql, i);
188
+ continue;
189
+ }
190
+ }
191
+ if (ch === '#' && rules.hashComment) {
192
+ i = skipLine(sql, i);
193
+ continue;
194
+ }
195
+ if (ch === '/' && sql[i + 1] === '*') {
196
+ i = skipBlockComment(sql, i, rules.nestedBlockComments);
197
+ continue;
198
+ }
199
+ if (ch === "'") {
200
+ // PostgreSQL's `E'...'` honours backslash escapes; every other `'...'` there does not.
201
+ const escaped = rules.backslashAlways || (syntax === 'postgresql' && lastWordEnd === i && /^[eE]$/.test(lastWord));
202
+ i = skipQuoted(sql, i, "'", escaped);
203
+ note('?');
204
+ continue;
205
+ }
206
+ if (ch === '"') {
207
+ i = skipQuoted(sql, i, '"', rules.backslashAlways);
208
+ note('?');
209
+ continue;
210
+ }
211
+ if (ch === '`' && rules.backtick) {
212
+ i = skipQuoted(sql, i, '`', false);
213
+ note('?');
214
+ continue;
215
+ }
216
+ if (ch === '[' && rules.bracket) {
217
+ i = skipQuoted(sql, i, ']', false);
218
+ note('?');
219
+ continue;
220
+ }
221
+ if (ch === '$' && rules.dollarQuotes) {
222
+ const tag = /^\$([A-Za-z_\u0080-￿][A-Za-z0-9_\u0080-￿]*)?\$/.exec(sql.slice(i, i + 130));
223
+ if (tag) {
224
+ const close = sql.indexOf(tag[0], i + tag[0].length);
225
+ i = close === -1 ? n : close + tag[0].length;
226
+ note('?');
227
+ continue;
228
+ }
229
+ }
230
+ if (WORD_START.test(ch)) {
231
+ // A whole identifier, so a `$` inside it never opens a dollar quote.
232
+ let j = i + 1;
233
+ while (j < n && WORD_PART.test(sql[j]))
234
+ j++;
235
+ const word = sql.slice(i, j).toLowerCase();
236
+ if (syntax === 'postgresql') {
237
+ // PostgreSQL 14's SQL-standard function body: its inner semicolons are not statement ends.
238
+ // `CASE ... END` inside it is the only other END a body can hold.
239
+ if (!atomic && word === 'atomic' && lastWord === 'begin' && lastWordEnd <= i && sqlGap(sql, lastWordEnd, i)) {
240
+ atomic = true;
241
+ caseDepth = 0;
242
+ }
243
+ else if (atomic) {
244
+ if (word === 'case')
245
+ caseDepth++;
246
+ else if (word === 'end') {
247
+ if (caseDepth > 0)
248
+ caseDepth--;
249
+ else
250
+ atomic = false;
251
+ }
252
+ }
253
+ }
254
+ note(word);
255
+ lastWordEnd = j;
256
+ lastWord = word;
257
+ i = j;
258
+ continue;
259
+ }
260
+ note('?');
261
+ lastWord = '';
262
+ i++;
263
+ }
264
+ finish();
265
+ return statements;
266
+ }
267
+ /** Whether only whitespace lies between `from` and `to` (comments count as something). */
268
+ function sqlGap(sql, from, to) {
269
+ return /^\s*$/.test(sql.slice(from, to));
270
+ }
271
+ /** The control statement whose leading `words` these are, if they are one. */
272
+ function classify(words, syntax) {
273
+ const [w0, w1, w2] = words;
274
+ switch (w0) {
275
+ case 'begin':
276
+ return w1 === undefined || BEGIN_NEXT.has(w1) ? { kind: 'begin', label: 'BEGIN' } : undefined;
277
+ case 'start':
278
+ return w1 === 'transaction' ? { kind: 'begin', label: 'START TRANSACTION' } : undefined;
279
+ case 'commit':
280
+ return { kind: 'commit', label: 'COMMIT' };
281
+ case 'end':
282
+ return (syntax === 'postgresql' || syntax === 'sqlite') &&
283
+ (w1 === undefined || w1 === 'work' || w1 === 'transaction' || w1 === 'and')
284
+ ? { kind: 'commit', label: 'END' }
285
+ : undefined;
286
+ case 'rollback':
287
+ return w1 === 'to' || ((w1 === 'work' || w1 === 'transaction' || w1 === 'tran') && w2 === 'to')
288
+ ? { kind: 'rollback-to', label: 'ROLLBACK TO SAVEPOINT' }
289
+ : { kind: 'rollback', label: 'ROLLBACK' };
290
+ case 'abort':
291
+ return syntax === 'postgresql' ? { kind: 'rollback', label: 'ABORT' } : undefined;
292
+ case 'savepoint':
293
+ return { kind: 'savepoint', label: 'SAVEPOINT' };
294
+ case 'save':
295
+ return syntax === 'mssql' && (w1 === 'tran' || w1 === 'transaction')
296
+ ? { kind: 'savepoint', label: 'SAVE TRANSACTION' }
297
+ : undefined;
298
+ case 'release':
299
+ return { kind: 'release', label: 'RELEASE SAVEPOINT' };
300
+ case 'set':
301
+ return w1 === 'transaction' ? { kind: 'set-transaction', label: 'SET TRANSACTION' } : undefined;
302
+ case 'prepare':
303
+ return w1 === 'transaction' ? { kind: 'prepare', label: 'PREPARE TRANSACTION' } : undefined;
304
+ default:
305
+ return undefined;
306
+ }
307
+ }
308
+ /** Whether `text` could hold a transaction-control statement at all (a cheap test before the scan). */
309
+ export function mayHoldTransactionControl(text) {
310
+ return /\b(?:begin|start|commit|end|rollback|abort|savepoint|save|release|set|prepare)\b/i.test(text);
311
+ }