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,132 @@
1
+ /**
2
+ * turbine-orm: the abandoned-stream idle guard.
3
+ *
4
+ * A cursor stream holds a checked-out connection between pulls (and, on
5
+ * PostgreSQL, an open transaction whose snapshot pins the xmin horizon for the
6
+ * whole database). A consumer that stops pulling without calling `return()`
7
+ * leaves the stream suspended at a `yield` forever, and a suspended generator
8
+ * runs no code, so nothing would ever release either. This guard is the code
9
+ * that runs instead: a timer that notices the consumer has asked for nothing for
10
+ * the configured time and closes the stream from outside.
11
+ *
12
+ * The consuming generator owns the {@link StreamIdleState}: it sets `suspended`
13
+ * around each yield and bumps `pulls` when the consumer resumes it. That is two
14
+ * writes per yield and no timer work, which is what keeps a per-row stream as
15
+ * cheap as before (a timer refreshed per row costs about 15% per row; a
16
+ * `setTimeout` per row more than doubles it).
17
+ *
18
+ * Leaf module: imports errors.ts only, so it stays in the serverless graph
19
+ * without pulling anything node-only.
20
+ */
21
+ import { TimeoutError } from '../errors.js';
22
+ /** The default for `streamIdleTimeoutMs` (client) and `idleTimeoutMs` (per call). */
23
+ export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
24
+ /**
25
+ * How much later than the idle timeout the PostgreSQL server-side limit fires.
26
+ * The client timer always expires a stream first (it ticks at most every
27
+ * {@link MAX_TICK_MS}, half this grace), so the server limit only acts when the
28
+ * process has stopped running timers: frozen between serverless invocations,
29
+ * or an event loop blocked for longer than the grace.
30
+ */
31
+ export declare const STREAM_IDLE_BACKSTOP_GRACE_MS = 10000;
32
+ /**
33
+ * The largest idle timeout accepted. PostgreSQL's
34
+ * `idle_in_transaction_session_timeout` is a 32-bit count of milliseconds, and
35
+ * the backstop adds the grace to it.
36
+ */
37
+ export declare const MAX_STREAM_IDLE_TIMEOUT_MS: number;
38
+ /**
39
+ * Validate an idle timeout: a whole number of milliseconds from 0 (disabled)
40
+ * to {@link MAX_STREAM_IDLE_TIMEOUT_MS}, else E003. `undefined` means "not
41
+ * set here", so the caller falls back to the next level.
42
+ */
43
+ export declare function resolveStreamIdleTimeout(value: unknown, name: string): number | undefined;
44
+ /**
45
+ * What the generator that yields to the consumer tells the guard. Written
46
+ * around every yield; read by the guard's timer.
47
+ */
48
+ export interface StreamIdleState {
49
+ /** How many times the consumer has resumed the stream. */
50
+ pulls: number;
51
+ /** The stream is suspended at a yield, waiting for the consumer to ask for more. */
52
+ suspended: boolean;
53
+ /** Rows handed to the consumer so far, for the log line. */
54
+ rows: number;
55
+ /** Set when the guard closed the stream; the next pull throws it. */
56
+ expired: TimeoutError | undefined;
57
+ }
58
+ export declare function newStreamIdleState(): StreamIdleState;
59
+ /** The E002 the next pull on an expired stream throws. */
60
+ export declare function streamIdleExpiredError(action: string, table: string, timeoutMs: number, unit: string): TimeoutError;
61
+ /**
62
+ * The PostgreSQL server-side limit for a stream's own transaction:
63
+ * `idle_in_transaction_session_timeout` set to the idle timeout plus the grace,
64
+ * TRANSACTION-LOCAL (`set_config(..., true)`), so it ends with the stream's
65
+ * COMMIT and cannot reach another client through a transaction pooler.
66
+ *
67
+ * A stricter limit already in force (a role or server setting below ours) is
68
+ * kept, so a stream never loosens what the operator configured. The value is a
69
+ * validated integer, never caller text: a GUC value cannot be a bind parameter.
70
+ * Reading `pg_settings` rather than calling `set_config` directly also means an
71
+ * engine without the setting gets no row and no error.
72
+ */
73
+ export declare function streamBackstopStatement(timeoutMs: number): string;
74
+ /**
75
+ * The timer half of the guard. One per open cursor stream, `unref()`ed so it
76
+ * never keeps a process alive.
77
+ *
78
+ * It ticks every `min(timeout / 4, 5 s)`. A tick that finds the stream
79
+ * suspended with no pull since the previous tick counts as idle; enough idle
80
+ * ticks in a row to cover the timeout expire the stream, so expiry lands
81
+ * between 1x the timeout and the timeout plus one tick. Time Turbine spends
82
+ * fetching never counts, because the stream is not suspended then.
83
+ *
84
+ * With `keepalive` set (PostgreSQL, where {@link streamBackstopStatement} is
85
+ * in force), a tick also sends a no-op statement when the connection has been
86
+ * quiet for half the timeout. A consumer that pulls rows of an already-fetched
87
+ * batch slowly is alive while the server sees an idle transaction; without the
88
+ * keepalive the server limit would end a stream the consumer is still reading.
89
+ * The server limit therefore only fires when this timer cannot run.
90
+ */
91
+ export declare class StreamIdleGuard {
92
+ private readonly state;
93
+ private readonly timeoutMs;
94
+ private readonly expiredError;
95
+ private readonly onExpire;
96
+ private readonly keepalive?;
97
+ private readonly timer;
98
+ private readonly idleTicksToExpire;
99
+ private lastPulls;
100
+ private idleTicks;
101
+ private lastStatementAt;
102
+ private statementsInFlight;
103
+ private keepaliveInFlight;
104
+ /** Settles once an expired stream's cleanup has run (connection released). */
105
+ expiry: Promise<void> | undefined;
106
+ constructor(state: StreamIdleState, timeoutMs: number, expiredError: () => TimeoutError, onExpire: () => Promise<void>, keepalive?: (() => Promise<unknown>) | undefined);
107
+ /** Bracket a statement on the stream's connection, so the keepalive knows it is busy. */
108
+ statementStarted(): void;
109
+ statementSettled(): void;
110
+ stop(): void;
111
+ private tick;
112
+ }
113
+ /** The connection surface a stream drives; structurally `StreamableConnection` in dialect.ts. */
114
+ interface StreamConnection {
115
+ query(text: string, values?: unknown[]): Promise<{
116
+ rows: Record<string, unknown>[];
117
+ }>;
118
+ }
119
+ /**
120
+ * The stream's connection, as the dialect sees it, with two additions: every
121
+ * statement is reported to the guard (for the keepalive), and, when `backstop`
122
+ * is given, the dialect's own BEGIN carries {@link streamBackstopStatement} in
123
+ * the same simple-protocol round trip, so the limit is in force from the
124
+ * transaction's first statement at no extra cost. Matching the exact BEGIN text
125
+ * the dialect emits keeps the dialect itself unchanged; the live backstop test
126
+ * fails if that text ever stops matching.
127
+ */
128
+ export declare function watchStreamConnection(base: StreamConnection, guard: () => StreamIdleGuard | undefined, backstop?: {
129
+ begin: string;
130
+ statement: string;
131
+ }): StreamConnection;
132
+ export {};
@@ -0,0 +1,203 @@
1
+ /**
2
+ * turbine-orm: the abandoned-stream idle guard.
3
+ *
4
+ * A cursor stream holds a checked-out connection between pulls (and, on
5
+ * PostgreSQL, an open transaction whose snapshot pins the xmin horizon for the
6
+ * whole database). A consumer that stops pulling without calling `return()`
7
+ * leaves the stream suspended at a `yield` forever, and a suspended generator
8
+ * runs no code, so nothing would ever release either. This guard is the code
9
+ * that runs instead: a timer that notices the consumer has asked for nothing for
10
+ * the configured time and closes the stream from outside.
11
+ *
12
+ * The consuming generator owns the {@link StreamIdleState}: it sets `suspended`
13
+ * around each yield and bumps `pulls` when the consumer resumes it. That is two
14
+ * writes per yield and no timer work, which is what keeps a per-row stream as
15
+ * cheap as before (a timer refreshed per row costs about 15% per row; a
16
+ * `setTimeout` per row more than doubles it).
17
+ *
18
+ * Leaf module: imports errors.ts only, so it stays in the serverless graph
19
+ * without pulling anything node-only.
20
+ */
21
+ import { TimeoutError, ValidationError } from '../errors.js';
22
+ /** The default for `streamIdleTimeoutMs` (client) and `idleTimeoutMs` (per call). */
23
+ export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300_000;
24
+ /**
25
+ * How much later than the idle timeout the PostgreSQL server-side limit fires.
26
+ * The client timer always expires a stream first (it ticks at most every
27
+ * {@link MAX_TICK_MS}, half this grace), so the server limit only acts when the
28
+ * process has stopped running timers: frozen between serverless invocations,
29
+ * or an event loop blocked for longer than the grace.
30
+ */
31
+ export const STREAM_IDLE_BACKSTOP_GRACE_MS = 10_000;
32
+ /**
33
+ * The largest idle timeout accepted. PostgreSQL's
34
+ * `idle_in_transaction_session_timeout` is a 32-bit count of milliseconds, and
35
+ * the backstop adds the grace to it.
36
+ */
37
+ export const MAX_STREAM_IDLE_TIMEOUT_MS = 2_147_483_647 - STREAM_IDLE_BACKSTOP_GRACE_MS;
38
+ /** The longest gap between two idle checks, so expiry lands within 5 s of the timeout. */
39
+ const MAX_TICK_MS = 5_000;
40
+ /**
41
+ * Validate an idle timeout: a whole number of milliseconds from 0 (disabled)
42
+ * to {@link MAX_STREAM_IDLE_TIMEOUT_MS}, else E003. `undefined` means "not
43
+ * set here", so the caller falls back to the next level.
44
+ */
45
+ export function resolveStreamIdleTimeout(value, name) {
46
+ if (value === undefined)
47
+ return undefined;
48
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0 || value > MAX_STREAM_IDLE_TIMEOUT_MS) {
49
+ throw new ValidationError(`Invalid ${name}: ${typeof value === 'string' ? JSON.stringify(value) : String(value)}. Expected a whole ` +
50
+ `number of milliseconds from 0 (no idle timeout) to ${MAX_STREAM_IDLE_TIMEOUT_MS}.`);
51
+ }
52
+ return value;
53
+ }
54
+ export function newStreamIdleState() {
55
+ return { pulls: 0, suspended: false, rows: 0, expired: undefined };
56
+ }
57
+ /** The E002 the next pull on an expired stream throws. */
58
+ export function streamIdleExpiredError(action, table, timeoutMs, unit) {
59
+ return new TimeoutError(timeoutMs, 'Stream', {
60
+ message: `${action} on "${table}" was closed after ${timeoutMs} ms with no ${unit} requested: its cursor and ` +
61
+ `connection were released so an abandoned stream cannot hold a pooled connection and the database's ` +
62
+ `snapshot. Consume faster, raise streamIdleTimeoutMs (client) or idleTimeoutMs (this call), or pass 0 ` +
63
+ 'to disable.',
64
+ outcome: 'not-executed',
65
+ });
66
+ }
67
+ /**
68
+ * The PostgreSQL server-side limit for a stream's own transaction:
69
+ * `idle_in_transaction_session_timeout` set to the idle timeout plus the grace,
70
+ * TRANSACTION-LOCAL (`set_config(..., true)`), so it ends with the stream's
71
+ * COMMIT and cannot reach another client through a transaction pooler.
72
+ *
73
+ * A stricter limit already in force (a role or server setting below ours) is
74
+ * kept, so a stream never loosens what the operator configured. The value is a
75
+ * validated integer, never caller text: a GUC value cannot be a bind parameter.
76
+ * Reading `pg_settings` rather than calling `set_config` directly also means an
77
+ * engine without the setting gets no row and no error.
78
+ */
79
+ export function streamBackstopStatement(timeoutMs) {
80
+ const limit = timeoutMs + STREAM_IDLE_BACKSTOP_GRACE_MS;
81
+ if (!Number.isSafeInteger(limit) || limit <= STREAM_IDLE_BACKSTOP_GRACE_MS || limit > 2_147_483_647) {
82
+ throw new ValidationError(`Invalid stream idle timeout ${String(timeoutMs)} for the server-side limit.`);
83
+ }
84
+ return ("SELECT set_config('idle_in_transaction_session_timeout', CASE WHEN s.setting::bigint > 0 AND " +
85
+ `s.setting::bigint < ${limit} THEN s.setting ELSE '${limit}' END, true) FROM pg_settings s ` +
86
+ "WHERE s.name = 'idle_in_transaction_session_timeout'");
87
+ }
88
+ /**
89
+ * The timer half of the guard. One per open cursor stream, `unref()`ed so it
90
+ * never keeps a process alive.
91
+ *
92
+ * It ticks every `min(timeout / 4, 5 s)`. A tick that finds the stream
93
+ * suspended with no pull since the previous tick counts as idle; enough idle
94
+ * ticks in a row to cover the timeout expire the stream, so expiry lands
95
+ * between 1x the timeout and the timeout plus one tick. Time Turbine spends
96
+ * fetching never counts, because the stream is not suspended then.
97
+ *
98
+ * With `keepalive` set (PostgreSQL, where {@link streamBackstopStatement} is
99
+ * in force), a tick also sends a no-op statement when the connection has been
100
+ * quiet for half the timeout. A consumer that pulls rows of an already-fetched
101
+ * batch slowly is alive while the server sees an idle transaction; without the
102
+ * keepalive the server limit would end a stream the consumer is still reading.
103
+ * The server limit therefore only fires when this timer cannot run.
104
+ */
105
+ export class StreamIdleGuard {
106
+ state;
107
+ timeoutMs;
108
+ expiredError;
109
+ onExpire;
110
+ keepalive;
111
+ timer;
112
+ idleTicksToExpire;
113
+ lastPulls;
114
+ idleTicks = 0;
115
+ lastStatementAt = Date.now();
116
+ statementsInFlight = 0;
117
+ keepaliveInFlight = false;
118
+ /** Settles once an expired stream's cleanup has run (connection released). */
119
+ expiry;
120
+ constructor(state, timeoutMs, expiredError, onExpire, keepalive) {
121
+ this.state = state;
122
+ this.timeoutMs = timeoutMs;
123
+ this.expiredError = expiredError;
124
+ this.onExpire = onExpire;
125
+ this.keepalive = keepalive;
126
+ const tickMs = Math.max(1, Math.min(Math.floor(timeoutMs / 4), MAX_TICK_MS));
127
+ this.idleTicksToExpire = Math.ceil(timeoutMs / tickMs);
128
+ this.lastPulls = state.pulls;
129
+ this.timer = setInterval(() => this.tick(), tickMs);
130
+ this.timer.unref?.();
131
+ }
132
+ /** Bracket a statement on the stream's connection, so the keepalive knows it is busy. */
133
+ statementStarted() {
134
+ this.statementsInFlight++;
135
+ this.lastStatementAt = Date.now();
136
+ }
137
+ statementSettled() {
138
+ this.statementsInFlight--;
139
+ this.lastStatementAt = Date.now();
140
+ }
141
+ stop() {
142
+ clearInterval(this.timer);
143
+ }
144
+ tick() {
145
+ if (this.expiry)
146
+ return;
147
+ if (!this.state.suspended || this.state.pulls !== this.lastPulls) {
148
+ this.lastPulls = this.state.pulls;
149
+ this.idleTicks = 0;
150
+ }
151
+ else if (++this.idleTicks >= this.idleTicksToExpire) {
152
+ this.stop();
153
+ // Set before any await, so a pull that races the cleanup already sees it.
154
+ this.state.expired = this.expiredError();
155
+ this.expiry = this.onExpire();
156
+ return;
157
+ }
158
+ if (this.keepalive &&
159
+ !this.keepaliveInFlight &&
160
+ this.statementsInFlight === 0 &&
161
+ Date.now() - this.lastStatementAt >= this.timeoutMs / 2) {
162
+ this.keepaliveInFlight = true;
163
+ this.statementStarted();
164
+ // A failed keepalive is not reported here: the stream's next statement
165
+ // meets the same broken connection and reports it with context.
166
+ this.keepalive()
167
+ .catch(() => undefined)
168
+ .finally(() => {
169
+ this.keepaliveInFlight = false;
170
+ this.statementSettled();
171
+ });
172
+ }
173
+ }
174
+ }
175
+ /**
176
+ * The stream's connection, as the dialect sees it, with two additions: every
177
+ * statement is reported to the guard (for the keepalive), and, when `backstop`
178
+ * is given, the dialect's own BEGIN carries {@link streamBackstopStatement} in
179
+ * the same simple-protocol round trip, so the limit is in force from the
180
+ * transaction's first statement at no extra cost. Matching the exact BEGIN text
181
+ * the dialect emits keeps the dialect itself unchanged; the live backstop test
182
+ * fails if that text ever stops matching.
183
+ */
184
+ export function watchStreamConnection(base, guard, backstop) {
185
+ let backstopSent = false;
186
+ return {
187
+ query: async (text, values) => {
188
+ let sql = text;
189
+ if (backstop && !backstopSent && values === undefined && text === backstop.begin) {
190
+ backstopSent = true;
191
+ sql = `${text}; ${backstop.statement}`;
192
+ }
193
+ const g = guard();
194
+ g?.statementStarted();
195
+ try {
196
+ return await base.query(sql, values);
197
+ }
198
+ finally {
199
+ g?.statementSettled();
200
+ }
201
+ },
202
+ };
203
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * turbine-orm, the table accessor contract.
3
+ *
4
+ * `db.users` is a `QueryInterface` on the SQL engines and a `PowqlInterface` on
5
+ * PowDB. The second is a parallel class, not a subclass, so without a shared
6
+ * type the compiler cannot see a method one engine has and the other lacks, and
7
+ * the user meets it as `TypeError: ... is not a function` at run time.
8
+ *
9
+ * {@link TableClient} is that shared type. It is DERIVED from QueryInterface
10
+ * (a `Pick` of the classified methods), so the SQL builder stays the one source
11
+ * of truth for every signature, and `PowqlInterface implements TableClient`
12
+ * makes a missing or mismatched method a build error.
13
+ *
14
+ * Type-only imports: this module emits {@link TABLE_METHODS} and nothing else,
15
+ * so it adds no edge to the import graph.
16
+ */
17
+ import type { QueryInterface } from './builder.js';
18
+ import type { DefaultInputTypes, TableInputTypes } from './types.js';
19
+ /**
20
+ * How a table method is routed.
21
+ *
22
+ * - `read`: may be served by a read replica (`READ_OPERATIONS` in client.ts is
23
+ * derived from these).
24
+ * - `write`: always runs on the primary.
25
+ * - `deferred`: returns a `DeferredQuery` for `db.pipeline()` /
26
+ * `$transaction([...])` without running anything.
27
+ * - `primary`: anything else on the accessor; runs on the primary.
28
+ */
29
+ export type TableMethodKind = 'read' | 'write' | 'deferred' | 'primary';
30
+ /**
31
+ * Every public method of a table accessor, with its routing class.
32
+ *
33
+ * `satisfies` refuses a key QueryInterface does not have; the exhaustiveness
34
+ * check below refuses a QueryInterface member missing from here.
35
+ */
36
+ export declare const TABLE_METHODS: {
37
+ readonly findMany: "read";
38
+ readonly findFirst: "read";
39
+ readonly findFirstOrThrow: "read";
40
+ readonly findUnique: "read";
41
+ readonly findUniqueOrThrow: "read";
42
+ readonly count: "read";
43
+ readonly aggregate: "read";
44
+ readonly groupBy: "read";
45
+ readonly findManyStream: "read";
46
+ readonly findManyStreamBatches: "read";
47
+ readonly explain: "primary";
48
+ readonly cacheStats: "primary";
49
+ readonly create: "write";
50
+ readonly createMany: "write";
51
+ readonly update: "write";
52
+ readonly updateMany: "write";
53
+ readonly delete: "write";
54
+ readonly deleteMany: "write";
55
+ readonly upsert: "write";
56
+ readonly buildFindMany: "deferred";
57
+ readonly buildFindFirst: "deferred";
58
+ readonly buildFindFirstOrThrow: "deferred";
59
+ readonly buildFindUnique: "deferred";
60
+ readonly buildFindUniqueOrThrow: "deferred";
61
+ readonly buildCount: "deferred";
62
+ readonly buildAggregate: "deferred";
63
+ readonly buildGroupBy: "deferred";
64
+ readonly buildCreate: "deferred";
65
+ readonly buildCreateMany: "deferred";
66
+ readonly buildUpdate: "deferred";
67
+ readonly buildUpdateMany: "deferred";
68
+ readonly buildDelete: "deferred";
69
+ readonly buildDeleteMany: "deferred";
70
+ readonly buildUpsert: "deferred";
71
+ };
72
+ /** A method name in the table accessor contract. */
73
+ export type TableMethod = keyof typeof TABLE_METHODS;
74
+ /**
75
+ * Public QueryInterface members that are SQL-engine internals (cache
76
+ * fingerprinting, the upsert lookup decision, a test hook), deliberately
77
+ * outside the accessor contract, so PowDB need not implement them.
78
+ */
79
+ export type SqlInternalMember = 'collectWhereParams' | 'fingerprintWhere' | 'withFingerprint' | 'upsertNeedsLookup' | 'resetUnlimitedWarnings';
80
+ /** Public QueryInterface members classified neither as a table method nor as internal. */
81
+ type Unclassified = Exclude<keyof QueryInterface<object>, TableMethod | SqlInternalMember>;
82
+ /**
83
+ * Fails the build, naming the member, when QueryInterface gains a public
84
+ * member nobody classified: add it to TABLE_METHODS (and to PowqlInterface) or
85
+ * to SqlInternalMember.
86
+ */
87
+ type AssertEveryMemberClassified<U extends never> = U;
88
+ export type EveryQueryInterfaceMemberClassified = AssertEveryMemberClassified<Unclassified>;
89
+ /** The table accessor contract every engine's accessor class implements. */
90
+ export type TableClient<T extends object, R extends object = {}, M extends TableInputTypes = DefaultInputTypes<T, R>> = Pick<QueryInterface<T, R, M>, TableMethod>;
91
+ /**
92
+ * View a factory-built accessor as the `QueryInterface` the client's public
93
+ * `table()` type names.
94
+ *
95
+ * Sound because the exhaustiveness check above guarantees `TableClient` holds
96
+ * every public QueryInterface member except the SQL internals, which nothing
97
+ * outside the SQL builder reaches through an accessor without first checking
98
+ * the member exists. The cast hides only private members.
99
+ */
100
+ export declare function asQueryInterface<T extends object>(client: TableClient<T>): QueryInterface<T>;
101
+ export {};
@@ -0,0 +1,70 @@
1
+ /**
2
+ * turbine-orm, the table accessor contract.
3
+ *
4
+ * `db.users` is a `QueryInterface` on the SQL engines and a `PowqlInterface` on
5
+ * PowDB. The second is a parallel class, not a subclass, so without a shared
6
+ * type the compiler cannot see a method one engine has and the other lacks, and
7
+ * the user meets it as `TypeError: ... is not a function` at run time.
8
+ *
9
+ * {@link TableClient} is that shared type. It is DERIVED from QueryInterface
10
+ * (a `Pick` of the classified methods), so the SQL builder stays the one source
11
+ * of truth for every signature, and `PowqlInterface implements TableClient`
12
+ * makes a missing or mismatched method a build error.
13
+ *
14
+ * Type-only imports: this module emits {@link TABLE_METHODS} and nothing else,
15
+ * so it adds no edge to the import graph.
16
+ */
17
+ /**
18
+ * Every public method of a table accessor, with its routing class.
19
+ *
20
+ * `satisfies` refuses a key QueryInterface does not have; the exhaustiveness
21
+ * check below refuses a QueryInterface member missing from here.
22
+ */
23
+ export const TABLE_METHODS = {
24
+ findMany: 'read',
25
+ findFirst: 'read',
26
+ findFirstOrThrow: 'read',
27
+ findUnique: 'read',
28
+ findUniqueOrThrow: 'read',
29
+ count: 'read',
30
+ aggregate: 'read',
31
+ groupBy: 'read',
32
+ findManyStream: 'read',
33
+ findManyStreamBatches: 'read',
34
+ explain: 'primary',
35
+ cacheStats: 'primary',
36
+ create: 'write',
37
+ createMany: 'write',
38
+ update: 'write',
39
+ updateMany: 'write',
40
+ delete: 'write',
41
+ deleteMany: 'write',
42
+ upsert: 'write',
43
+ buildFindMany: 'deferred',
44
+ buildFindFirst: 'deferred',
45
+ buildFindFirstOrThrow: 'deferred',
46
+ buildFindUnique: 'deferred',
47
+ buildFindUniqueOrThrow: 'deferred',
48
+ buildCount: 'deferred',
49
+ buildAggregate: 'deferred',
50
+ buildGroupBy: 'deferred',
51
+ buildCreate: 'deferred',
52
+ buildCreateMany: 'deferred',
53
+ buildUpdate: 'deferred',
54
+ buildUpdateMany: 'deferred',
55
+ buildDelete: 'deferred',
56
+ buildDeleteMany: 'deferred',
57
+ buildUpsert: 'deferred',
58
+ };
59
+ /**
60
+ * View a factory-built accessor as the `QueryInterface` the client's public
61
+ * `table()` type names.
62
+ *
63
+ * Sound because the exhaustiveness check above guarantees `TableClient` holds
64
+ * every public QueryInterface member except the SQL internals, which nothing
65
+ * outside the SQL builder reaches through an accessor without first checking
66
+ * the member exists. The cast hides only private members.
67
+ */
68
+ export function asQueryInterface(client) {
69
+ return client;
70
+ }