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
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import pg from '#pg';
7
7
  import { ValidationError } from '../errors.js';
8
+ import { consoleLogger, logTo } from '../logger.js';
8
9
  import { camelToSnake, localDateTimeKind, snakeToCamel, timeOfDayKind } from '../schema.js';
9
10
  import { shouldWarnOnce, WARN_NS } from './warn-registry.js';
10
11
  // ---------------------------------------------------------------------------
@@ -259,9 +260,15 @@ export function canonicalWriteEntries(meta, entries) {
259
260
  * where walker enumerates) never sees it and no emitted SQL can change.
260
261
  */
261
262
  export const INTERNAL_COMBINATOR = Symbol.for('turbine.internalCombinator');
263
+ /** Shared by every brand: a frozen descriptor saves an allocation per branded where. */
264
+ const INTERNAL_COMBINATOR_DESCRIPTOR = Object.freeze({
265
+ value: true,
266
+ enumerable: false,
267
+ configurable: true,
268
+ });
262
269
  /** Tag `where` as carrying a Turbine-synthesized combinator, and return it. */
263
270
  export function markInternalCombinator(where) {
264
- Object.defineProperty(where, INTERNAL_COMBINATOR, { value: true, enumerable: false, configurable: true });
271
+ Object.defineProperty(where, INTERNAL_COMBINATOR, INTERNAL_COMBINATOR_DESCRIPTOR);
265
272
  return where;
266
273
  }
267
274
  /** Was this WHERE object's combinator synthesized by Turbine? */
@@ -275,12 +282,17 @@ export function isInternalCombinator(where) {
275
282
  export function escSingleQuote(s) {
276
283
  return s.replace(/'/g, "''");
277
284
  }
285
+ const LIKE_META = /[\\%_]/;
286
+ const LIKE_META_G = /[\\%_]/g;
278
287
  /**
279
288
  * Escape LIKE pattern metacharacters: %, _, and \.
280
289
  * Must be used with `ESCAPE '\'` in the LIKE clause.
281
290
  */
282
291
  export function escapeLike(value) {
283
- return value.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_');
292
+ // One pass, and none at all for the common operand with no metacharacter.
293
+ // Identical output to escaping `\` then `%` then `_` in turn: the backslashes
294
+ // the first step adds are never `%` or `_`, so the steps never interact.
295
+ return LIKE_META.test(value) ? value.replace(LIKE_META_G, '\\$&') : value;
284
296
  }
285
297
  // ---------------------------------------------------------------------------
286
298
  // LRU cache, bounded SQL template cache to prevent memory leaks
@@ -366,6 +378,307 @@ export function fnv1a64Hex(s) {
366
378
  export function sqlToPreparedName(sql) {
367
379
  return `t_${fnv1a64Hex(sql)}`;
368
380
  }
381
+ /**
382
+ * How many distinct statement names one process hands out. Every connection
383
+ * keeps each named statement it has prepared, at 9 to 45 KB of server memory
384
+ * apiece, until the connection closes, so the number of names is what bounds
385
+ * that memory. A statement past the budget is sent unnamed: parsed on every
386
+ * execution and never kept, which costs a few microseconds on a cheap query.
387
+ */
388
+ export const PREPARED_STATEMENT_BUDGET = 200;
389
+ /** `Symbol.for` so the ESM and CJS copies of this module share one budget. */
390
+ const PREPARED_NAMES = Symbol.for('turbine.preparedStatementNames');
391
+ function preparedNames() {
392
+ const holder = globalThis;
393
+ let names = holder[PREPARED_NAMES];
394
+ if (!names) {
395
+ names = new Set();
396
+ holder[PREPARED_NAMES] = names;
397
+ }
398
+ return names;
399
+ }
400
+ /**
401
+ * The prepared-statement name for `sql`, or `''` (send it unnamed) once the
402
+ * process has named {@link PREPARED_STATEMENT_BUDGET} other statements.
403
+ *
404
+ * The first shapes a process runs keep their names for its lifetime, which in
405
+ * practice are its hot shapes. A server-side `DEALLOCATE` cannot replace this:
406
+ * a pooled query does not know which connection it will run on, and a
407
+ * connection keeps every name it ever prepared.
408
+ */
409
+ export function boundedPreparedName(sql) {
410
+ return claimPreparedName(sqlToPreparedName(sql));
411
+ }
412
+ /**
413
+ * Claim an already derived statement name from the same budget as
414
+ * {@link boundedPreparedName}: `name` itself, or `''` (send it unnamed) once
415
+ * the budget is spent. `sqlComment`'s `c_` names (query/sql-comment.ts) are
416
+ * claimed here, so a commented statement costs one slot like any other.
417
+ */
418
+ export function claimPreparedName(name) {
419
+ const names = preparedNames();
420
+ if (names.has(name))
421
+ return name;
422
+ if (names.size >= PREPARED_STATEMENT_BUDGET)
423
+ return '';
424
+ names.add(name);
425
+ return name;
426
+ }
427
+ /** @internal Tests only: forget every name handed out. */
428
+ export function resetPreparedNameBudget() {
429
+ preparedNames().clear();
430
+ }
431
+ function argsRewriteError(model, action, path) {
432
+ return new TypeError(`[turbine] middleware cannot change \`params.${path}\` on ${model}.${action}: the query is compiled from the ` +
433
+ 'arguments the caller passed, so a change made in middleware would not reliably reach it. Put the ' +
434
+ 'condition in the query itself, or use `globalFilters` for a filter that applies to every query.');
435
+ }
436
+ /** Plain objects and arrays are copied; a Date, Buffer, class instance or other value is shared as it is. */
437
+ function isPlainContainer(value) {
438
+ if (value === null || typeof value !== 'object')
439
+ return false;
440
+ if (Array.isArray(value))
441
+ return true;
442
+ const proto = Object.getPrototypeOf(value);
443
+ return proto === Object.prototype || proto === null;
444
+ }
445
+ /** Nesting past this is shared, not copied, so a cyclic object cannot recurse without end. */
446
+ const MIDDLEWARE_ARGS_MAX_DEPTH = 100;
447
+ /**
448
+ * A copy of `value` that middleware may read, spread, serialize or
449
+ * `structuredClone`: plain objects and arrays are copied at every depth, and
450
+ * everything else (Date, Buffer, bigint, symbol, class instance) is shared.
451
+ * Symbol-keyed properties are copied by descriptor, so the markers the
452
+ * pipeline reads (the internal-combinator brand, the inclusive-cursor flag,
453
+ * column and expression brands) are on the copy exactly as on the original,
454
+ * non-enumerable ones included.
455
+ */
456
+ function copyArgs(value, depth) {
457
+ if (depth > MIDDLEWARE_ARGS_MAX_DEPTH || !isPlainContainer(value))
458
+ return value;
459
+ let out;
460
+ if (Array.isArray(value)) {
461
+ const items = new Array(value.length);
462
+ // A long list of ids is the common big argument; keep its scalars off the call path.
463
+ for (let i = 0; i < value.length; i++) {
464
+ const item = value[i];
465
+ items[i] = item !== null && typeof item === 'object' ? copyArgs(item, depth + 1) : item;
466
+ }
467
+ out = items;
468
+ }
469
+ else {
470
+ out = Object.getPrototypeOf(value) === null ? Object.create(null) : {};
471
+ for (const key of Object.keys(value)) {
472
+ const item = value[key];
473
+ const copied = item !== null && typeof item === 'object' ? copyArgs(item, depth + 1) : item;
474
+ // `__proto__` from JSON.parse is an own key; assigning it would set the prototype.
475
+ if (key === '__proto__')
476
+ Object.defineProperty(out, key, { value: copied, enumerable: true, writable: true, configurable: true });
477
+ else
478
+ out[key] = copied;
479
+ }
480
+ }
481
+ for (const sym of Object.getOwnPropertySymbols(value)) {
482
+ Object.defineProperty(out, sym, Object.getOwnPropertyDescriptor(value, sym));
483
+ }
484
+ return out;
485
+ }
486
+ /**
487
+ * Where `b` first differs from `a`, as a path suffix (`.where.id`, or `''` for
488
+ * the values themselves), or undefined when they are structurally equal. The
489
+ * suffix is built on the way back out, so an equal pair costs no string work.
490
+ */
491
+ function argsDifference(a, b, depth) {
492
+ if (Object.is(a, b))
493
+ return undefined;
494
+ if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object')
495
+ return '';
496
+ if (a instanceof Date || b instanceof Date) {
497
+ return a instanceof Date && b instanceof Date && Object.is(a.getTime(), b.getTime()) ? undefined : '';
498
+ }
499
+ if (ArrayBuffer.isView(a) || ArrayBuffer.isView(b)) {
500
+ if (!(ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) || a.byteLength !== b.byteLength)
501
+ return '';
502
+ const x = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
503
+ const y = new Uint8Array(b.buffer, b.byteOffset, b.byteLength);
504
+ for (let i = 0; i < x.length; i++)
505
+ if (x[i] !== y[i])
506
+ return '';
507
+ return undefined;
508
+ }
509
+ if (a instanceof RegExp || b instanceof RegExp)
510
+ return String(a) === String(b) ? undefined : '';
511
+ if (a instanceof Map || a instanceof Set || b instanceof Map || b instanceof Set)
512
+ return '';
513
+ if (Array.isArray(a) !== Array.isArray(b) || depth > MIDDLEWARE_ARGS_MAX_DEPTH)
514
+ return '';
515
+ if (Array.isArray(a)) {
516
+ const other = b;
517
+ const shared = Math.min(a.length, other.length);
518
+ for (let i = 0; i < shared; i++) {
519
+ if (Object.is(a[i], other[i]))
520
+ continue;
521
+ const inner = argsDifference(a[i], other[i], depth + 1);
522
+ if (inner !== undefined)
523
+ return `.${i}${inner}`;
524
+ }
525
+ return a.length === other.length ? undefined : `.${shared}`;
526
+ }
527
+ const left = a;
528
+ const right = b;
529
+ for (const key of Object.keys(left)) {
530
+ if (!Object.hasOwn(right, key))
531
+ return `.${key}`;
532
+ const inner = argsDifference(left[key], right[key], depth + 1);
533
+ if (inner !== undefined)
534
+ return `.${key}${inner}`;
535
+ }
536
+ for (const key of Object.keys(right))
537
+ if (!Object.hasOwn(left, key))
538
+ return `.${key}`;
539
+ return undefined;
540
+ }
541
+ const MIDDLEWARE_ARGS = new WeakMap();
542
+ /** The key Node's `util.inspect` looks for, from the global registry so no `node:` import is needed. */
543
+ const NODE_INSPECT = Symbol.for('nodejs.util.inspect.custom');
544
+ /** Makes `console.log(params)` show the values rather than an accessor. Not own, so a spread of `params` never carries it. */
545
+ const MIDDLEWARE_PARAMS_PROTOTYPE = Object.defineProperty({}, NODE_INSPECT, {
546
+ value(_depth, options, inspect) {
547
+ return inspect({ model: this.model, action: this.action, args: this.args }, options);
548
+ },
549
+ });
550
+ /**
551
+ * The `params` a middleware receives. `params.args` is a plain copy of the
552
+ * caller's args, made when it is first read, so middleware can read it, spread
553
+ * it, serialize it and `structuredClone` it, and one that never reads it pays
554
+ * nothing.
555
+ *
556
+ * It is a copy because the query is compiled from the caller's args, not from
557
+ * what middleware sees. A change to the copy would never reach the query, so
558
+ * {@link assertMiddlewareArgsKept} refuses it, naming the path, when the
559
+ * middleware calls `next`. That check compares contents, which is why the copy
560
+ * is not frozen: a frozen object ignores a write silently in a script that is
561
+ * not in strict mode, and a filter added there would be dropped without a word.
562
+ * Assigning to `params.args` itself throws at once.
563
+ */
564
+ export function readOnlyMiddlewareParams(model, action, args) {
565
+ const state = { model, action, original: args, copy: undefined };
566
+ const params = {
567
+ __proto__: MIDDLEWARE_PARAMS_PROTOTYPE,
568
+ model,
569
+ action,
570
+ get args() {
571
+ if (state.copy === undefined)
572
+ state.copy = copyArgs(args, 0);
573
+ return state.copy;
574
+ },
575
+ set args(_replacement) {
576
+ throw argsRewriteError(model, action, 'args');
577
+ },
578
+ };
579
+ MIDDLEWARE_ARGS.set(params, state);
580
+ return params;
581
+ }
582
+ /**
583
+ * Refuse a `next(p)` whose `p.args` is not the caller's args: one that
584
+ * middleware changed in place, or replaced with an object that differs from
585
+ * them. A copy that is structurally equal passes (`structuredClone(params.args)`,
586
+ * `{ ...params.args }`), because nothing it would change is lost. Throws a
587
+ * TypeError naming the first path that differs.
588
+ */
589
+ export function assertMiddlewareArgsKept(handedOut, passed) {
590
+ const state = MIDDLEWARE_ARGS.get(handedOut);
591
+ if (passed === handedOut) {
592
+ if (handedOut.model !== state.model)
593
+ throw argsRewriteError(state.model, state.action, 'model');
594
+ if (handedOut.action !== state.action)
595
+ throw argsRewriteError(state.model, state.action, 'action');
596
+ // Nothing has read the args, so nothing could have changed them.
597
+ if (state.copy === undefined)
598
+ return;
599
+ }
600
+ const given = passed?.args;
601
+ if (given === state.original)
602
+ return;
603
+ const changed = argsDifference(state.original, given, 0);
604
+ if (changed !== undefined)
605
+ throw argsRewriteError(state.model, state.action, `args${changed}`);
606
+ }
607
+ /** Whether `pool` is a node-postgres `Pool`, whose checkouts are real backend connections. */
608
+ export function isNodePostgresPool(pool) {
609
+ const Pool = pg.Pool;
610
+ return typeof Pool === 'function' && pool instanceof Pool;
611
+ }
612
+ /** Whether `client` is a node-postgres connection a cancel request can reach. */
613
+ export function canCancelBackend(client) {
614
+ const c = client;
615
+ return (typeof pg.Connection === 'function' &&
616
+ !!c &&
617
+ typeof c.processID === 'number' &&
618
+ typeof c.secretKey === 'number' &&
619
+ (typeof c.port === 'number' || typeof c.port === 'string'));
620
+ }
621
+ /**
622
+ * Ask the server to cancel whatever `client`'s backend is running, with the
623
+ * protocol's CancelRequest: a separate short connection carrying the backend's
624
+ * process id and secret key. No login, no session state, and a pooler forwards
625
+ * it to the right server backend, which `pg_cancel_backend(pid)` through a
626
+ * pooler would not.
627
+ *
628
+ * Resolves `true` once the server has closed the cancel connection, i.e. has
629
+ * signalled the backend; `false` when the request could not be delivered
630
+ * within `waitMs`. A cancel that arrives after the statement finished is
631
+ * ignored by the server.
632
+ */
633
+ export function sendCancelRequest(client, waitMs) {
634
+ if (!canCancelBackend(client))
635
+ return Promise.resolve(false);
636
+ const target = client;
637
+ const Connection = pg.Connection;
638
+ return new Promise((resolve) => {
639
+ let con;
640
+ let sent = false;
641
+ let done = false;
642
+ const finish = (delivered) => {
643
+ if (done)
644
+ return;
645
+ done = true;
646
+ clearTimeout(timer);
647
+ try {
648
+ con?.stream?.destroy?.();
649
+ }
650
+ catch {
651
+ // already closed
652
+ }
653
+ resolve(delivered);
654
+ };
655
+ const timer = setTimeout(() => finish(false), waitMs);
656
+ try {
657
+ con = new Connection({ ssl: target.ssl, sslNegotiation: target.sslNegotiation });
658
+ const send = () => {
659
+ sent = true;
660
+ con?.cancel(target.processID, target.secretKey);
661
+ };
662
+ con.on('error', () => finish(false));
663
+ con.on('end', () => finish(sent));
664
+ con.on('connect', () => {
665
+ if (!target.ssl)
666
+ send();
667
+ else if (target.sslNegotiation !== 'direct')
668
+ con?.requestSsl();
669
+ });
670
+ con.on('sslconnect', send);
671
+ const host = typeof target.host === 'string' ? target.host : undefined;
672
+ if (host?.startsWith('/'))
673
+ con.connect(`${host}/.s.PGSQL.${String(target.port)}`);
674
+ else
675
+ con.connect(target.port, host);
676
+ }
677
+ catch {
678
+ finish(false);
679
+ }
680
+ });
681
+ }
369
682
  /** Known operator keys, used to detect operator objects vs plain values */
370
683
  export const OPERATOR_KEYS = new Set([
371
684
  'equals',
@@ -527,6 +840,12 @@ const TZ_SUFFIX_RE = /(?:Z|[+-]\d{2}(?::?\d{2})?)$/;
527
840
  * as-is.
528
841
  */
529
842
  export function parseDbDate(value) {
843
+ const date = parseDbDateCommon(value);
844
+ // The shapes JS cannot parse (a BC year, an offset with seconds, a year past
845
+ // 9999) come out invalid; only those pay for the general parse.
846
+ return Number.isNaN(date.getTime()) ? (parseDbDateGeneral(value) ?? date) : date;
847
+ }
848
+ function parseDbDateCommon(value) {
530
849
  // Date-only values (`2026-07-07`, from `date` columns in json_agg output)
531
850
  // have no time to zone-pin, and their `-07` tail must not be read as an
532
851
  // offset. JS parses bare ISO dates as UTC midnight already.
@@ -541,6 +860,79 @@ export function parseDbDate(value) {
541
860
  // normalize `YYYY-MM-DD HH:MM:SS` (driver form) to ISO before pinning UTC
542
861
  return new Date(`${value.replace(' ', 'T')}Z`);
543
862
  }
863
+ const DB_DATE_GENERAL_RE = /^(\d{4,})-(\d\d)-(\d\d)(?:[T ](\d\d):(\d\d):(\d\d)(\.\d+)?)?(?:(Z)|([+-])(\d\d)(?::?(\d\d))?(?::?(\d\d))?)?( BC)?$/;
864
+ /**
865
+ * The Postgres renderings JS `Date` cannot parse: a ` BC` suffix, a
866
+ * local-mean-time offset with seconds (`-04:56:02`, any zone before about
867
+ * 1900), and years of five or more digits. Offset-less text is read as UTC,
868
+ * as in {@link parseDbDate}. `undefined` when the text is none of these.
869
+ */
870
+ function parseDbDateGeneral(value) {
871
+ const m = DB_DATE_GENERAL_RE.exec(value);
872
+ if (!m)
873
+ return undefined;
874
+ let year = Number(m[1]);
875
+ if (m[13])
876
+ year = 1 - year;
877
+ const month = Number(m[2]) - 1;
878
+ const ms = m[7] ? Math.trunc(1000 * Number(`0${m[7]}`)) : 0;
879
+ const date = new Date(Date.UTC(2000, month, Number(m[3]), Number(m[4] ?? 0), Number(m[5] ?? 0), Number(m[6] ?? 0), ms));
880
+ // setUTCFullYear, not Date.UTC: Date.UTC reads years 0-99 as 1900-1999.
881
+ date.setUTCFullYear(year, month, Number(m[3]));
882
+ if (m[9]) {
883
+ const offset = Number(m[10]) * 3600 + Number(m[11] ?? 0) * 60 + Number(m[12] ?? 0);
884
+ return new Date(date.getTime() - (m[9] === '-' ? -offset : offset) * 1000);
885
+ }
886
+ return date;
887
+ }
888
+ /**
889
+ * A float column's value as the driver reads it. JSON has no NaN or infinity,
890
+ * so `json_build_object` renders them as the strings "NaN", "Infinity" and
891
+ * "-Infinity"; every other value passes through.
892
+ */
893
+ export function readJsonFloat(value) {
894
+ if (typeof value !== 'string')
895
+ return value;
896
+ if (value === 'NaN')
897
+ return Number.NaN;
898
+ if (value === 'Infinity')
899
+ return Number.POSITIVE_INFINITY;
900
+ if (value === '-Infinity')
901
+ return Number.NEGATIVE_INFINITY;
902
+ return value;
903
+ }
904
+ /** {@link readJsonFloat} over a float array (at any depth); the input when nothing changes. */
905
+ export function readJsonFloatArray(value) {
906
+ if (!Array.isArray(value))
907
+ return value;
908
+ let out;
909
+ for (let i = 0; i < value.length; i++) {
910
+ const el = value[i];
911
+ const read = Array.isArray(el) ? readJsonFloatArray(el) : readJsonFloat(el);
912
+ if (read !== el) {
913
+ out ??= value.slice();
914
+ out[i] = read;
915
+ }
916
+ }
917
+ return out ?? value;
918
+ }
919
+ let textArrayParser;
920
+ /**
921
+ * An array column the driver has no parser for (an enum array) arrives as
922
+ * Postgres array text, `{ok,sad}`; read it into an array of strings, as the
923
+ * same column reads through a relation. The input when it cannot be parsed.
924
+ */
925
+ export function readTextArray(value) {
926
+ if (typeof value !== 'string' || value.charCodeAt(0) !== 123)
927
+ return value;
928
+ try {
929
+ textArrayParser ??= createPgArrayParser((text) => text);
930
+ return textArrayParser(value) ?? value;
931
+ }
932
+ catch {
933
+ return value;
934
+ }
935
+ }
544
936
  /**
545
937
  * Is `value` one of the two representations a Postgres temporal `infinity` /
546
938
  * `-infinity` reaches the ORM row parser in?
@@ -1079,7 +1471,7 @@ export function isDefaultTextParser(oid, parser) {
1079
1471
  * construction, and only when somebody else's non-default parser is actually
1080
1472
  * being replaced.
1081
1473
  */
1082
- export function warnParserOverwrite(oid, typeName) {
1474
+ export function warnParserOverwrite(oid, typeName, logger = consoleLogger) {
1083
1475
  const getParser = pg.types.getTypeParser;
1084
1476
  const current = getParser(oid, 'text');
1085
1477
  // Turbine's own earlier registration is not a third party's expectation.
@@ -1098,13 +1490,13 @@ export function warnParserOverwrite(oid, typeName) {
1098
1490
  const remedy = TEMPORAL_PARSER_OIDS.has(oid)
1099
1491
  ? ' `utcTimestamps: false` leaves the six temporal OIDs (1114, 1082, 1115, 1182, 1184, 1185) alone entirely.'
1100
1492
  : ' There is no opt-out for this OID: Turbine registers it so bigint values come back as numbers.';
1101
- console.warn(`[turbine] pg type parser for OID ${oid} (${typeName}) was already customized by something else in this ` +
1493
+ logTo(logger, 'warn', `pg type parser for OID ${oid} (${typeName}) was already customized by something else in this ` +
1102
1494
  'process, and Turbine is replacing it. `pg.types.setTypeParser` is process-global and takes effect ' +
1103
1495
  'immediately for EVERY pg.Pool in the process, including pools that already exist and are already ' +
1104
1496
  'querying, so whatever set that parser will now read this column differently. If yours should win, ' +
1105
1497
  `register it AFTER constructing the client.${remedy} This warning fires under \`NODE_ENV=production\` ` +
1106
1498
  'too: which parser wins depends on module evaluation order, so a process can be clean in dev and wrong ' +
1107
- 'in production from import order alone.');
1499
+ 'in production from import order alone.', { code: 'parser-overwrite', oid, type: typeName });
1108
1500
  }
1109
1501
  /**
1110
1502
  * Register Turbine's temporal text parsers on the pg module. SIX OIDs, doing
@@ -1140,16 +1532,16 @@ export function warnParserOverwrite(oid, typeName) {
1140
1532
  * per row at decode time, not captured per pool). If any of the four OIDs is
1141
1533
  * already on a NON-default parser, {@link warnParserOverwrite} says so once.
1142
1534
  */
1143
- export function registerUtcTemporalParsers() {
1535
+ export function registerUtcTemporalParsers(logger) {
1144
1536
  // pg-types declares get/setTypeParser over its own OID enum, which lists the
1145
1537
  // scalar types only. The array OIDs are just as real, so both calls are
1146
1538
  // retyped over a plain number rather than the incomplete enum.
1147
1539
  const getParser = pg.types.getTypeParser;
1148
1540
  const setParser = pg.types.setTypeParser;
1149
- warnParserOverwrite(1114, 'timestamp');
1150
- warnParserOverwrite(1082, 'date');
1151
- warnParserOverwrite(1115, 'timestamp[]');
1152
- warnParserOverwrite(1182, 'date[]');
1541
+ warnParserOverwrite(1114, 'timestamp', logger);
1542
+ warnParserOverwrite(1082, 'date', logger);
1543
+ warnParserOverwrite(1115, 'timestamp[]', logger);
1544
+ warnParserOverwrite(1182, 'date[]', logger);
1153
1545
  const parseDate = createUtcDateParser(getParser(1082, 'text'));
1154
1546
  const parseTimestamp = createUtcTimestampParser(getParser(1114, 'text'));
1155
1547
  setParser(1114, markTurbineParser(parseTimestamp));
@@ -1318,8 +1710,11 @@ function nameCloseness(input, candidate) {
1318
1710
  const b = candidate.toLowerCase();
1319
1711
  if (a === b)
1320
1712
  return 1000;
1321
- if (a.includes(b) || b.includes(a))
1713
+ // Containment only between names of 3+ characters: `id` sits inside almost
1714
+ // any name, so `tenentId` must reach `tenantId` by edit distance instead.
1715
+ if (Math.min(a.length, b.length) >= 3 && (a.includes(b) || b.includes(a))) {
1322
1716
  return 500 + Math.min(a.length, b.length);
1717
+ }
1323
1718
  // Levenshtein, bounded: only near-misses are worth suggesting.
1324
1719
  const rows = a.length + 1;
1325
1720
  const cols = b.length + 1;
@@ -1459,7 +1854,7 @@ export function relationInProjectionMessage(table, field, clause) {
1459
1854
  ' out of the result.';
1460
1855
  }
1461
1856
  /**
1462
- * Dev-only advisory for a sort/grouping term Turbine dropped as redundant
1857
+ * Advice (`diagnostics.advice`) for a sort/grouping term Turbine dropped as redundant
1463
1858
  * (`filters.ts` `dedupeOrderEntries` / `dedupeColumnList`).
1464
1859
  *
1465
1860
  * Deliberately a WARNING and not an error, and deliberately not silent either.
@@ -1468,15 +1863,16 @@ export function relationInProjectionMessage(table, field, clause) {
1468
1863
  * plus an unconditional primary-key tiebreak, which collide exactly when the
1469
1864
  * caller sorts by the primary key). But a caller who wrote the duplicate by
1470
1865
  * hand, or who believes a second key is doing something, should hear about it
1471
- * once. Dev-only for the usual reason: it describes the QUERY the application
1866
+ * once. Advice for the usual reason: it describes the QUERY the application
1472
1867
  * sends, which does not vary with the environment, so a production process
1473
- * learns nothing from repeating it.
1868
+ * learns nothing from repeating it. The caller passes its advice logger, or
1869
+ * `undefined` when advice is off.
1474
1870
  *
1475
1871
  * Keyed per dropped key, so a sort assembled per request from the same UI says
1476
1872
  * it once rather than once per request.
1477
1873
  */
1478
- export function warnRedundantSortTerm(table, clause, dropped) {
1479
- if (process.env.NODE_ENV === 'production')
1874
+ export function warnRedundantSortTerm(advice, table, clause, dropped) {
1875
+ if (!advice)
1480
1876
  return;
1481
1877
  for (const d of dropped) {
1482
1878
  if (!shouldWarnOnce(WARN_NS.redundantSortTerm, `${table}|${clause}|${d.key}`))
@@ -1484,9 +1880,9 @@ export function warnRedundantSortTerm(table, clause, dropped) {
1484
1880
  const how = d.first === d.key
1485
1881
  ? `"${d.key}" is named twice`
1486
1882
  : `"${d.key}" and the earlier "${d.first}" both target ${d.resolved}`;
1487
- console.warn(`[turbine] ${clause} on table "${table}": ${how}, so the later one was dropped. It could not have ` +
1883
+ logTo(advice, 'warn', `${clause} on table "${table}": ${how}, so the later one was dropped. It could not have ` +
1488
1884
  'changed the result (the earlier term already orders those rows), and dropping it keeps the query on ' +
1489
- 'a shared prepared statement. Remove it to silence this.');
1885
+ 'a shared prepared statement. Remove it to silence this.', { code: 'redundant-sort-term', table, clause, key: d.key });
1490
1886
  }
1491
1887
  }
1492
1888
  /**
@@ -127,6 +127,17 @@ export declare const WARN_NS: {
127
127
  * Keyed on the requested mode.
128
128
  */
129
129
  readonly planCacheModeIgnored: "planCacheModeIgnored";
130
+ /**
131
+ * `statementTimeoutMs` or `maxLifetimeSeconds` was set on a client given an
132
+ * EXTERNAL pool, whose connections Turbine never opens (client.ts
133
+ * `warnExternalPoolOptions`). Keyed on the option name.
134
+ */
135
+ readonly connectionOptionIgnored: "connectionOptionIgnored";
136
+ /**
137
+ * `preparedStatements: true` on a connection string that names a connection
138
+ * pooler (client.ts `warnPreparedOnPooler`). Keyed on `host:port`.
139
+ */
140
+ readonly preparedStatementsOnPooler: "preparedStatementsOnPooler";
130
141
  /**
131
142
  * A relation's JSON payload could not be parsed in `parseNestedRow`
132
143
  * (relations.ts). Keyed on `table.relation`, so a page of 10,000 rows whose
@@ -134,4 +145,11 @@ export declare const WARN_NS: {
134
145
  * warn sits in the per-relation loop, which runs once per row per relation.
135
146
  */
136
147
  readonly relationParseFailure: "relationParseFailure";
148
+ /**
149
+ * `findMany` with no row limit (builder.ts `maybeWarnUnlimited`, powql.ts).
150
+ * Keyed on the table and process-wide: every `$transaction` builds its own
151
+ * query interfaces, so a dedupe held on the interface said it again in
152
+ * every transaction.
153
+ */
154
+ readonly unlimitedFindMany: "unlimitedFindMany";
137
155
  };
@@ -162,6 +162,17 @@ export const WARN_NS = {
162
162
  * Keyed on the requested mode.
163
163
  */
164
164
  planCacheModeIgnored: 'planCacheModeIgnored',
165
+ /**
166
+ * `statementTimeoutMs` or `maxLifetimeSeconds` was set on a client given an
167
+ * EXTERNAL pool, whose connections Turbine never opens (client.ts
168
+ * `warnExternalPoolOptions`). Keyed on the option name.
169
+ */
170
+ connectionOptionIgnored: 'connectionOptionIgnored',
171
+ /**
172
+ * `preparedStatements: true` on a connection string that names a connection
173
+ * pooler (client.ts `warnPreparedOnPooler`). Keyed on `host:port`.
174
+ */
175
+ preparedStatementsOnPooler: 'preparedStatementsOnPooler',
165
176
  /**
166
177
  * A relation's JSON payload could not be parsed in `parseNestedRow`
167
178
  * (relations.ts). Keyed on `table.relation`, so a page of 10,000 rows whose
@@ -169,4 +180,11 @@ export const WARN_NS = {
169
180
  * warn sits in the per-relation loop, which runs once per row per relation.
170
181
  */
171
182
  relationParseFailure: 'relationParseFailure',
183
+ /**
184
+ * `findMany` with no row limit (builder.ts `maybeWarnUnlimited`, powql.ts).
185
+ * Keyed on the table and process-wide: every `$transaction` builds its own
186
+ * query interfaces, so a dedupe held on the interface said it again in
187
+ * every transaction.
188
+ */
189
+ unlimitedFindMany: 'unlimitedFindMany',
172
190
  };