turbine-orm 0.49.0 → 0.50.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 (157) hide show
  1. package/README.md +122 -39
  2. package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
  3. package/dist/cjs/adapters/index.d.ts +110 -0
  4. package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
  5. package/dist/cjs/cli/config.d.ts +181 -0
  6. package/dist/cjs/cli/config.js +32 -6
  7. package/dist/cjs/cli/destructive.d.ts +38 -0
  8. package/dist/cjs/cli/index.d.ts +359 -0
  9. package/dist/cjs/cli/index.js +228 -56
  10. package/dist/cjs/cli/loader.d.ts +61 -0
  11. package/dist/cjs/cli/mcp.d.ts +42 -0
  12. package/dist/cjs/cli/migrate.d.ts +356 -0
  13. package/dist/cjs/cli/migrate.js +131 -40
  14. package/dist/cjs/cli/observe-ui.d.ts +1 -0
  15. package/dist/cjs/cli/observe-ui.js +14 -5
  16. package/dist/cjs/cli/observe.d.ts +25 -0
  17. package/dist/cjs/cli/observe.js +49 -12
  18. package/dist/cjs/cli/pii-tags.d.ts +53 -0
  19. package/dist/cjs/cli/prisma-report.d.ts +33 -0
  20. package/dist/cjs/cli/prisma-report.js +73 -0
  21. package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
  22. package/dist/cjs/cli/prisma-resolve.js +1 -0
  23. package/dist/cjs/cli/prisma-schema.d.ts +176 -0
  24. package/dist/cjs/cli/prisma-schema.js +82 -4
  25. package/dist/cjs/cli/rate-limit.d.ts +32 -0
  26. package/dist/cjs/cli/rate-limit.js +45 -0
  27. package/dist/cjs/cli/studio-demo.d.ts +43 -0
  28. package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
  29. package/dist/cjs/cli/studio.d.ts +207 -0
  30. package/dist/cjs/cli/studio.js +136 -71
  31. package/dist/cjs/cli/ui.d.ts +73 -0
  32. package/dist/cjs/cli/ui.js +51 -9
  33. package/dist/cjs/client.d.ts +837 -0
  34. package/dist/cjs/client.js +3 -0
  35. package/dist/cjs/dialect.d.ts +516 -0
  36. package/dist/cjs/dialect.js +37 -12
  37. package/dist/cjs/errors.d.ts +370 -0
  38. package/dist/cjs/generate.d.ts +137 -0
  39. package/dist/cjs/generate.js +39 -6
  40. package/dist/cjs/index-advisor.d.ts +153 -0
  41. package/dist/cjs/index-stats.d.ts +384 -0
  42. package/dist/cjs/index.d.ts +55 -0
  43. package/dist/cjs/index.js +7 -2
  44. package/dist/cjs/introspect.d.ts +269 -0
  45. package/dist/cjs/mssql.d.ts +232 -0
  46. package/dist/cjs/mssql.js +6 -0
  47. package/dist/cjs/mysql.d.ts +173 -0
  48. package/dist/cjs/mysql.js +16 -0
  49. package/dist/cjs/nested-write.d.ts +96 -0
  50. package/dist/cjs/nested-write.js +414 -24
  51. package/dist/cjs/observe.d.ts +115 -0
  52. package/dist/cjs/optional-peer-import.d.cts +72 -0
  53. package/dist/cjs/pipeline-submittable.d.ts +93 -0
  54. package/dist/cjs/pipeline.d.ts +71 -0
  55. package/dist/cjs/powdb-introspect.d.ts +84 -0
  56. package/dist/cjs/powdb.d.ts +931 -0
  57. package/dist/cjs/powdb.js +106 -21
  58. package/dist/cjs/powql.d.ts +592 -0
  59. package/dist/cjs/powql.js +42 -6
  60. package/dist/cjs/prisma-compat.d.ts +283 -0
  61. package/dist/cjs/prisma-compat.js +167 -9
  62. package/dist/cjs/query/aggregates.d.ts +92 -0
  63. package/dist/cjs/query/aggregates.js +7 -3
  64. package/dist/cjs/query/batched-loader.d.ts +193 -0
  65. package/dist/cjs/query/builder.d.ts +849 -0
  66. package/dist/cjs/query/builder.js +571 -65
  67. package/dist/cjs/query/compound-unique.d.ts +51 -0
  68. package/dist/cjs/query/deferred.d.ts +223 -0
  69. package/dist/cjs/query/filters.d.ts +201 -0
  70. package/dist/cjs/query/index.d.ts +14 -0
  71. package/dist/cjs/query/index.js +6 -1
  72. package/dist/cjs/query/relations.d.ts +609 -0
  73. package/dist/cjs/query/relations.js +693 -46
  74. package/dist/cjs/query/types.d.ts +1300 -0
  75. package/dist/cjs/query/utils.d.ts +209 -0
  76. package/dist/cjs/query/utils.js +208 -1
  77. package/dist/cjs/query/warn-registry.d.ts +68 -0
  78. package/dist/cjs/query/warn-registry.js +9 -0
  79. package/dist/cjs/query/where-compile.d.ts +139 -0
  80. package/dist/cjs/query/where.d.ts +548 -0
  81. package/dist/cjs/query/where.js +58 -22
  82. package/dist/cjs/query/writes.d.ts +172 -0
  83. package/dist/cjs/query/writes.js +105 -12
  84. package/dist/cjs/realtime.d.ts +70 -0
  85. package/dist/cjs/schema-builder.d.ts +354 -0
  86. package/dist/cjs/schema-metadata.d.ts +83 -0
  87. package/dist/cjs/schema-sql.d.ts +217 -0
  88. package/dist/cjs/schema-sql.js +23 -5
  89. package/dist/cjs/schema.d.ts +356 -0
  90. package/dist/cjs/schema.js +125 -0
  91. package/dist/cjs/seed.d.ts +15 -0
  92. package/dist/cjs/serverless.d.ts +142 -0
  93. package/dist/cjs/sqlite.d.ts +143 -0
  94. package/dist/cjs/sqlite.js +4 -0
  95. package/dist/cjs/typed-sql.d.ts +102 -0
  96. package/dist/cli/config.d.ts +18 -4
  97. package/dist/cli/config.js +31 -6
  98. package/dist/cli/index.d.ts +123 -0
  99. package/dist/cli/index.js +223 -58
  100. package/dist/cli/migrate.d.ts +59 -10
  101. package/dist/cli/migrate.js +128 -41
  102. package/dist/cli/observe-ui.d.ts +1 -1
  103. package/dist/cli/observe-ui.js +14 -5
  104. package/dist/cli/observe.d.ts +7 -1
  105. package/dist/cli/observe.js +48 -12
  106. package/dist/cli/prisma-report.d.ts +14 -0
  107. package/dist/cli/prisma-report.js +72 -0
  108. package/dist/cli/prisma-resolve.d.ts +6 -0
  109. package/dist/cli/prisma-resolve.js +1 -0
  110. package/dist/cli/prisma-schema.d.ts +62 -2
  111. package/dist/cli/prisma-schema.js +81 -4
  112. package/dist/cli/rate-limit.d.ts +32 -0
  113. package/dist/cli/rate-limit.js +40 -0
  114. package/dist/cli/studio.d.ts +5 -5
  115. package/dist/cli/studio.js +135 -70
  116. package/dist/cli/ui.d.ts +1 -1
  117. package/dist/cli/ui.js +51 -9
  118. package/dist/client.d.ts +40 -0
  119. package/dist/client.js +3 -0
  120. package/dist/dialect.d.ts +17 -1
  121. package/dist/dialect.js +37 -12
  122. package/dist/generate.js +40 -7
  123. package/dist/index.d.ts +1 -1
  124. package/dist/index.js +1 -1
  125. package/dist/mssql.js +6 -0
  126. package/dist/mysql.js +16 -0
  127. package/dist/nested-write.d.ts +2 -0
  128. package/dist/nested-write.js +415 -25
  129. package/dist/powdb.d.ts +4 -2
  130. package/dist/powdb.js +106 -21
  131. package/dist/powql.d.ts +5 -0
  132. package/dist/powql.js +42 -6
  133. package/dist/prisma-compat.d.ts +2 -0
  134. package/dist/prisma-compat.js +166 -8
  135. package/dist/query/aggregates.js +7 -3
  136. package/dist/query/builder.d.ts +292 -21
  137. package/dist/query/builder.js +570 -64
  138. package/dist/query/deferred.d.ts +39 -0
  139. package/dist/query/index.d.ts +1 -1
  140. package/dist/query/index.js +1 -1
  141. package/dist/query/relations.d.ts +173 -5
  142. package/dist/query/relations.js +688 -47
  143. package/dist/query/types.d.ts +123 -39
  144. package/dist/query/utils.d.ts +116 -0
  145. package/dist/query/utils.js +198 -0
  146. package/dist/query/warn-registry.d.ts +9 -0
  147. package/dist/query/warn-registry.js +9 -0
  148. package/dist/query/where.d.ts +38 -1
  149. package/dist/query/where.js +58 -23
  150. package/dist/query/writes.d.ts +42 -1
  151. package/dist/query/writes.js +104 -13
  152. package/dist/schema-sql.d.ts +14 -0
  153. package/dist/schema-sql.js +23 -5
  154. package/dist/schema.d.ts +38 -0
  155. package/dist/schema.js +123 -0
  156. package/dist/sqlite.js +4 -0
  157. package/package.json +77 -28
@@ -47,6 +47,7 @@ exports.fingerprintScopedWhere = fingerprintScopedWhere;
47
47
  exports.buildRelationFilter = buildRelationFilter;
48
48
  exports.buildSubWhereForRelation = buildSubWhereForRelation;
49
49
  exports.pgTypeForColumn = pgTypeForColumn;
50
+ exports.coerceWhereOperand = coerceWhereOperand;
50
51
  exports.enumTypeForColumn = enumTypeForColumn;
51
52
  exports.enumCastSuffix = enumCastSuffix;
52
53
  exports.assertBindableEqualityValue = assertBindableEqualityValue;
@@ -220,6 +221,7 @@ function collectScalarParams(qi, key, value, params) {
220
221
  meta: qi.tableMeta,
221
222
  table: qi.table,
222
223
  prefix: '',
224
+ rawColumn,
223
225
  });
224
226
  return;
225
227
  default:
@@ -227,7 +229,7 @@ function collectScalarParams(qi, key, value, params) {
227
229
  // the build path, so a cache hit can never silently bind a
228
230
  // misspelled-operator object.
229
231
  assertBindableEqualityValue(qi, rawColumn, value, getColumnPgType(qi, rawColumn), qi.table);
230
- params.push(value);
232
+ params.push(coerceWhereOperand(qi, qi.tableMeta, rawColumn, value));
231
233
  return;
232
234
  }
233
235
  }
@@ -290,24 +292,26 @@ function collectOperatorParams(qi, column, op, params, refCtx) {
290
292
  resolveColumnRef(qi, v, refCtx, op.mode);
291
293
  return true;
292
294
  };
295
+ // Mirrors buildOperatorClauses' temporal bind rewrite exactly.
296
+ const cv = (v) => (refCtx ? coerceWhereOperand(qi, refCtx.meta, refCtx.rawColumn, v) : v);
293
297
  if (op.equals !== undefined && op.equals !== null && !skipRef(op.equals)) {
294
298
  (0, filters_js_1.assertBindableEqualsOperand)(op.equals, `"${column}"`);
295
- params.push(op.equals);
299
+ params.push(cv(op.equals));
296
300
  }
297
301
  if (op.gt !== undefined && !skipRef(op.gt))
298
- params.push(op.gt);
302
+ params.push(cv(op.gt));
299
303
  if (op.gte !== undefined && !skipRef(op.gte))
300
- params.push(op.gte);
304
+ params.push(cv(op.gte));
301
305
  if (op.lt !== undefined && !skipRef(op.lt))
302
- params.push(op.lt);
306
+ params.push(cv(op.lt));
303
307
  if (op.lte !== undefined && !skipRef(op.lte))
304
- params.push(op.lte);
308
+ params.push(cv(op.lte));
305
309
  if (op.not !== undefined && op.not !== null && !skipRef(op.not))
306
- params.push(op.not);
310
+ params.push(cv(op.not));
307
311
  if (op.in !== undefined)
308
- params.push(qi.inParam(op.in));
312
+ params.push(qi.inParam(cv(op.in)));
309
313
  if (op.notIn !== undefined)
310
- params.push(qi.inParam(op.notIn));
314
+ params.push(qi.inParam(cv(op.notIn)));
311
315
  if (op.contains !== undefined)
312
316
  params.push(`%${(0, utils_js_1.escapeLike)(op.contains)}%`);
313
317
  if (op.startsWith !== undefined)
@@ -617,6 +621,7 @@ function buildScalarClause(qi, key, value, params, andClauses) {
617
621
  meta: qi.tableMeta,
618
622
  table: qi.table,
619
623
  prefix: '',
624
+ rawColumn,
620
625
  }));
621
626
  return;
622
627
  default:
@@ -624,7 +629,7 @@ function buildScalarClause(qi, key, value, params, andClauses) {
624
629
  // is almost always a misspelled operator (`startWith` for `startsWith`);
625
630
  // the guard also runs on the cache-hit param-collect path.
626
631
  assertBindableEqualityValue(qi, rawColumn, value, getColumnPgType(qi, rawColumn), qi.table);
627
- params.push(value);
632
+ params.push(coerceWhereOperand(qi, qi.tableMeta, rawColumn, value));
628
633
  andClauses.push(`${column} = ${qi.p(params.length)}`);
629
634
  return;
630
635
  }
@@ -766,11 +771,12 @@ function buildScopedScalarClause(qi, scope, field, value, params, clauses) {
766
771
  meta,
767
772
  table: scope.table,
768
773
  prefix: scope.qualifier,
774
+ rawColumn: col,
769
775
  }));
770
776
  return;
771
777
  }
772
778
  assertBindableEqualityValue(qi, col, value, pgTypeForColumn(qi, meta, col), scope.table);
773
- params.push(value);
779
+ params.push(coerceWhereOperand(qi, meta, col, value));
774
780
  clauses.push(`${qCol} = ${qi.p(params.length)}`);
775
781
  }
776
782
  /**
@@ -822,11 +828,11 @@ function collectScopedScalarParams(qi, scope, field, value, params) {
822
828
  }
823
829
  }
824
830
  if ((0, filters_js_1.isWhereOperator)(value)) {
825
- collectOperatorParams(qi, col, value, params, { meta, table: scope.table, prefix: '' });
831
+ collectOperatorParams(qi, col, value, params, { meta, table: scope.table, prefix: '', rawColumn: col });
826
832
  return;
827
833
  }
828
834
  assertBindableEqualityValue(qi, col, value, pgTypeForColumn(qi, meta, col), scope.table);
829
- params.push(value);
835
+ params.push(coerceWhereOperand(qi, meta, col, value));
830
836
  }
831
837
  /**
832
838
  * Value-invariant fingerprint of a scoped sub-where. Same canonical
@@ -1006,6 +1012,30 @@ function buildSubWhereForRelation(qi, targetTable, subWhere, params) {
1006
1012
  function pgTypeForColumn(_qi, meta, column) {
1007
1013
  return meta.dialectTypes?.[column] ?? meta.pgTypes?.[column] ?? 'text';
1008
1014
  }
1015
+ /**
1016
+ * Rewrite a WHERE operand bound against `column` the same way the write path
1017
+ * rewrites a `data` value ({@link coerceTemporalValue}): a JS `Date` on a
1018
+ * `time` / `timetz` column becomes a time-of-day literal (Postgres otherwise
1019
+ * answers `22007 invalid input syntax for type time` for the ISO timestamp the
1020
+ * driver would send), and on a zone-less `date` / `timestamp` column it becomes
1021
+ * the UTC-component literal, so a predicate matches the value a write of the
1022
+ * same `Date` stored.
1023
+ *
1024
+ * This is a VALUE transform only — it never changes the emitted SQL — so the
1025
+ * SQL-template cache is unaffected, and it is applied on the cache-hit
1026
+ * param-collect path as well as the build path.
1027
+ *
1028
+ * `timestamptz` and every non-temporal column are returned by identity.
1029
+ */
1030
+ function coerceWhereOperand(qi, meta, column, value) {
1031
+ if (!(value instanceof Date) && !Array.isArray(value))
1032
+ return value;
1033
+ return (0, utils_js_1.coerceTemporalValue)(pgTypeForColumn(qi, meta, column), value,
1034
+ // Same PostgreSQL + `utcTimestamps` gate as the write path (see
1035
+ // `utcDateTimeWrites` in writes.ts): only the read/write-symmetric engine
1036
+ // gets the zone-less rewrite. Time-of-day always rewrites.
1037
+ qi.dialect.name === 'postgresql' && qi.utcTimestamps !== false);
1038
+ }
1009
1039
  /**
1010
1040
  * The Postgres enum type name for a column, when the schema knows one.
1011
1041
  *
@@ -1143,6 +1173,9 @@ function columnRefSql(qi, ref, ctx, mode) {
1143
1173
  */
1144
1174
  function buildOperatorClauses(qi, column, op, params, refCtx) {
1145
1175
  const clauses = [];
1176
+ // Temporal bind rewrite, identical to `collectOperatorParams`. Value-only, so
1177
+ // the emitted SQL (and therefore the template cache) is untouched.
1178
+ const cv = (v) => (refCtx ? coerceWhereOperand(qi, refCtx.meta, refCtx.rawColumn, v) : v);
1146
1179
  if (op.equals !== undefined) {
1147
1180
  if (op.equals === null) {
1148
1181
  clauses.push(`${column} IS NULL`);
@@ -1152,7 +1185,7 @@ function buildOperatorClauses(qi, column, op, params, refCtx) {
1152
1185
  }
1153
1186
  else {
1154
1187
  (0, filters_js_1.assertBindableEqualsOperand)(op.equals, column);
1155
- params.push(op.equals);
1188
+ params.push(cv(op.equals));
1156
1189
  clauses.push(`${column} = ${qi.p(params.length)}`);
1157
1190
  }
1158
1191
  }
@@ -1161,7 +1194,7 @@ function buildOperatorClauses(qi, column, op, params, refCtx) {
1161
1194
  clauses.push(`${column} > ${columnRefSql(qi, op.gt, refCtx, op.mode)}`);
1162
1195
  }
1163
1196
  else {
1164
- params.push(op.gt);
1197
+ params.push(cv(op.gt));
1165
1198
  clauses.push(`${column} > ${qi.p(params.length)}`);
1166
1199
  }
1167
1200
  }
@@ -1170,7 +1203,7 @@ function buildOperatorClauses(qi, column, op, params, refCtx) {
1170
1203
  clauses.push(`${column} >= ${columnRefSql(qi, op.gte, refCtx, op.mode)}`);
1171
1204
  }
1172
1205
  else {
1173
- params.push(op.gte);
1206
+ params.push(cv(op.gte));
1174
1207
  clauses.push(`${column} >= ${qi.p(params.length)}`);
1175
1208
  }
1176
1209
  }
@@ -1179,7 +1212,7 @@ function buildOperatorClauses(qi, column, op, params, refCtx) {
1179
1212
  clauses.push(`${column} < ${columnRefSql(qi, op.lt, refCtx, op.mode)}`);
1180
1213
  }
1181
1214
  else {
1182
- params.push(op.lt);
1215
+ params.push(cv(op.lt));
1183
1216
  clauses.push(`${column} < ${qi.p(params.length)}`);
1184
1217
  }
1185
1218
  }
@@ -1188,7 +1221,7 @@ function buildOperatorClauses(qi, column, op, params, refCtx) {
1188
1221
  clauses.push(`${column} <= ${columnRefSql(qi, op.lte, refCtx, op.mode)}`);
1189
1222
  }
1190
1223
  else {
1191
- params.push(op.lte);
1224
+ params.push(cv(op.lte));
1192
1225
  clauses.push(`${column} <= ${qi.p(params.length)}`);
1193
1226
  }
1194
1227
  }
@@ -1200,16 +1233,16 @@ function buildOperatorClauses(qi, column, op, params, refCtx) {
1200
1233
  clauses.push(`${column} != ${columnRefSql(qi, op.not, refCtx, op.mode)}`);
1201
1234
  }
1202
1235
  else {
1203
- params.push(op.not);
1236
+ params.push(cv(op.not));
1204
1237
  clauses.push(`${column} != ${qi.p(params.length)}`);
1205
1238
  }
1206
1239
  }
1207
1240
  if (op.in !== undefined) {
1208
- params.push(qi.inParam(op.in));
1241
+ params.push(qi.inParam(cv(op.in)));
1209
1242
  clauses.push(qi.inClause(column, qi.p(params.length), false));
1210
1243
  }
1211
1244
  if (op.notIn !== undefined) {
1212
- params.push(qi.inParam(op.notIn));
1245
+ params.push(qi.inParam(cv(op.notIn)));
1213
1246
  clauses.push(qi.inClause(column, qi.p(params.length), true));
1214
1247
  }
1215
1248
  const buildLikeClause = (paramRef) => op.mode === 'insensitive' ? qi.dialect.buildInsensitiveLike(column, paramRef) : `${column} LIKE ${paramRef}`;
@@ -1269,7 +1302,10 @@ function vectorOperator(qi, field, rawColumn, metric) {
1269
1302
  throw new errors_js_1.ValidationError(`[turbine] Column "${field}" on table "${qi.table}" is not a vector column ` +
1270
1303
  `(actual type: ${colType}); cannot apply a vector distance operation.`);
1271
1304
  }
1272
- const op = filters_js_1.VECTOR_METRIC_OPERATORS[metric];
1305
+ // ownLookup, not a bare index: an inherited Object.prototype member
1306
+ // ("constructor", "toString", …) would otherwise resolve to a truthy builtin
1307
+ // and be spliced into the ORDER BY / WHERE clause as its source text.
1308
+ const op = (0, utils_js_1.ownLookup)(filters_js_1.VECTOR_METRIC_OPERATORS, metric);
1273
1309
  if (!op) {
1274
1310
  throw new errors_js_1.ValidationError(`[turbine] Unknown vector metric "${metric}" for column "${field}". ` +
1275
1311
  `Valid metrics: ${Object.keys(filters_js_1.VECTOR_METRIC_OPERATORS).join(', ')}.`);
@@ -0,0 +1,172 @@
1
+ /**
2
+ * turbine-orm: write compilation (extracted from builder.ts)
3
+ *
4
+ * SQL builders for the mutating operations (create / createMany / update /
5
+ * delete / upsert / updateMany / deleteMany) plus the write-projection helpers
6
+ * (writeReturningColumns / writeReselectSelection / parseWriteRow, the PII
7
+ * column set, optimistic-lock and atomic-operator SET clauses). All functions
8
+ * take a {@link BuilderCtx} first argument; WHERE compilation is reused from
9
+ * where.ts (via `whereMod`), and the cache / dialect / row-parse primitives
10
+ * stay class-resident, reached through the ctx. See builder.ts for the thin
11
+ * delegating methods and the async execute wrappers.
12
+ */
13
+ import type { ReturningSelection } from '../dialect.js';
14
+ import type { TableMetadata } from '../schema.js';
15
+ import type { DeferredQuery } from './deferred.js';
16
+ import type { CreateArgs, CreateManyArgs, DeleteArgs, DeleteManyArgs, UpdateArgs, UpdateManyArgs, UpsertArgs } from './types.js';
17
+ import type { BuilderCtx } from './where.js';
18
+ /**
19
+ * Normalize one `data` value before it is bound as a write param.
20
+ *
21
+ * Two value shapes need rewriting, both a JS `Date` on a temporal column:
22
+ *
23
+ * 1. A time-of-day column (`time` / `timetz`) — the driver serializes a `Date`
24
+ * as a full ISO timestamp with the process offset and Postgres answers
25
+ * `22007 invalid input syntax for type time`. Rewritten on every engine.
26
+ * 2. A zone-less `date` / `timestamp` column on PostgreSQL — the driver's
27
+ * local-offset serialization stores the PROCESS's calendar fields, so in a
28
+ * non-UTC process the stored value is wrong and (because the read path
29
+ * interprets an offset-less value as UTC) does not round-trip. Rewritten to
30
+ * the UTC components, mirroring `parseDbDate`. See `toLocalDateTimeLiteral`
31
+ * in query/utils.ts.
32
+ *
33
+ * `timestamptz` is deliberately untouched: it stores a real instant and the
34
+ * driver's local-offset string is already correct for it.
35
+ *
36
+ * Every other value, including a string that is already a valid literal, is
37
+ * returned by identity, so this is a byte-for-byte no-op for every non-temporal
38
+ * column.
39
+ */
40
+ export declare function coerceWriteValue(qi: BuilderCtx, key: string, value: unknown): unknown;
41
+ /**
42
+ * Build a `SELECT * ... WHERE <predicate>` that re-fetches the row(s) matched
43
+ * by a write's `where` clause. Used by the `'reselect'` result strategy to
44
+ * return rows from non-RETURNING engines. Reuses the same parameterized WHERE
45
+ * builder as reads, so no user value is interpolated.
46
+ */
47
+ export declare function buildReselectByWhere(qi: BuilderCtx, whereObj: Record<string, unknown>): {
48
+ sql: string;
49
+ params: unknown[];
50
+ };
51
+ export declare function buildCreate<T extends object>(qi: BuilderCtx, args: CreateArgs<T>): DeferredQuery<T>;
52
+ /**
53
+ * Build the `'reselect'` plan for {@link buildCreate}: run the INSERT, then
54
+ * `SELECT * WHERE pk = ?`. Returns `undefined` (skipped) unless the active
55
+ * dialect's result strategy is `'reselect'`, so the PostgreSQL/RETURNING path
56
+ * pays nothing. Not yet wired to a real non-RETURNING engine.
57
+ */
58
+ export declare function makeCreateReselect<T extends object>(qi: BuilderCtx, insertSql: string, insertParams: unknown[], data: Record<string, unknown>): DeferredQuery<T>['reselect'];
59
+ export declare function buildCreateMany<T extends object>(qi: BuilderCtx, args: CreateManyArgs<T>): DeferredQuery<T[]>;
60
+ export declare function buildUpdate<T extends object>(qi: BuilderCtx, args: UpdateArgs<T>): DeferredQuery<T>;
61
+ export declare function buildDelete<T extends object>(qi: BuilderCtx, args: DeleteArgs<T>): DeferredQuery<T>;
62
+ export declare function buildUpsert<T extends object>(qi: BuilderCtx, args: UpsertArgs<T>): DeferredQuery<T>;
63
+ export declare function buildUpdateMany<T extends object>(qi: BuilderCtx, args: UpdateManyArgs<T>): DeferredQuery<{
64
+ count: number;
65
+ }>;
66
+ export declare function buildDeleteMany<T extends object>(qi: BuilderCtx, args: DeleteManyArgs<T>): DeferredQuery<{
67
+ count: number;
68
+ }>;
69
+ /**
70
+ * The snake_case names of a table's PII-tagged (`defineSchema` `pii: true`)
71
+ * columns. PII columns are excluded from default projections (findMany /
72
+ * findUnique / relation subqueries / batched loads) unless the query opts in
73
+ * via `includePii` or names the column explicitly in `select`. Returns an
74
+ * empty set for any table with no PII column, so untagged schemas keep their
75
+ * byte-identical SQL.
76
+ */
77
+ export declare function piiColumns(_qi: BuilderCtx, meta: TableMetadata): Set<string>;
78
+ /**
79
+ * The camelCase field names of a table's PII-tagged columns: the read-side
80
+ * counterpart of {@link piiColumns} applied to already-parsed entities.
81
+ * Used to strip PII from a write's RETURNING/reselect row (writes accept no
82
+ * `includePii`/`select`, so their returned row always applies the default
83
+ * exclusion; you may still write PII fields freely).
84
+ */
85
+ export declare function piiFields(_qi: BuilderCtx, meta: TableMetadata): string[];
86
+ /**
87
+ * The `RETURNING` / `OUTPUT` selection for a write on this table. A table with
88
+ * no PII column returns `'*'` (every column — byte-identical SQL to before);
89
+ * a table WITH PII columns returns an explicit quoted list of every non-PII
90
+ * column so the PII values never leave the database on a write. A PII-tagged
91
+ * PRIMARY KEY column is kept in the projection regardless (the returned row
92
+ * must stay addressable): tag sensitive data, not keys — a PII PK is
93
+ * documented out of scope for stripping. Writes accept no `select`/`includePii`
94
+ * (unlike reads), so this is the whole write-return policy at the SQL level;
95
+ * {@link parseWriteRow} remains as a defense-in-depth strip (a no-op once the
96
+ * SQL already excludes the columns). Derived purely from static per-table
97
+ * schema metadata, so the write SQL cache needs no extra key segment.
98
+ */
99
+ export declare function writeReturningColumns(qi: BuilderCtx): ReturningSelection;
100
+ /**
101
+ * String form of {@link writeReturningColumns} for a `SELECT` list (the
102
+ * `'reselect'` result strategy re-fetches via a SELECT, not RETURNING).
103
+ * `'*'` when there is no PII column; otherwise the comma-joined quoted list.
104
+ */
105
+ export declare function writeReselectSelection(qi: BuilderCtx): string;
106
+ /**
107
+ * Parse a write's returned row (create/update/upsert/delete), then strip the
108
+ * table's PII fields: the write-side read policy. On PII-tagged tables the
109
+ * statement's RETURNING/OUTPUT already omits these columns (see
110
+ * {@link writeReturningColumns}), so this strip is defense-in-depth and a
111
+ * no-op. Untagged tables incur only one `for` over a zero-length field list,
112
+ * so behavior is unchanged.
113
+ */
114
+ export declare function parseWriteRow(qi: BuilderCtx, row: Record<string, unknown>): Record<string, unknown>;
115
+ /**
116
+ * Reject any write against a view (H4). Views are introspected with
117
+ * `isView: true` and are read-only in every engine; a write raises a
118
+ * {@link ValidationError} (E003) rather than emitting SQL Postgres would
119
+ * reject (or, worse, silently applying to an updatable view).
120
+ */
121
+ export declare function assertWritable(qi: BuilderCtx, operation: string): void;
122
+ /**
123
+ * Reject a write whose `data` names a `GENERATED ALWAYS AS (...) STORED`
124
+ * column (H3). Postgres computes these from other columns and errors if you
125
+ * try to write them; we fail early with a clear {@link ValidationError} (E003)
126
+ * instead of surfacing a cryptic driver error. Undefined values are ignored
127
+ * (they're stripped from the statement anyway).
128
+ */
129
+ export declare function assertNoGeneratedColumns(qi: BuilderCtx, data: Record<string, unknown>, operation: string): void;
130
+ /**
131
+ * Build a single SET clause entry for update/updateMany.
132
+ *
133
+ * Supports plain values and atomic operator objects ({ set, increment,
134
+ * decrement, multiply, divide }). An operator object is detected ONLY when
135
+ * it has EXACTLY one key that is one of the 5 operator keys — this avoids
136
+ * misinterpreting JSON column values like `{ set: 'x' }` as operators
137
+ * (real operator objects always have exactly one key, and a plain JSON
138
+ * payload that happens to have a single `set` key is extremely unusual).
139
+ * Multi-key objects are always treated as plain (JSON) values.
140
+ *
141
+ * Returns the SQL fragment (e.g., `"view_count" = "view_count" + $3`) and
142
+ * pushes any required params onto the shared params array so that WHERE
143
+ * clause numbering continues correctly afterward.
144
+ */
145
+ export declare function buildSetClause(qi: BuilderCtx, key: string, value: unknown, params: unknown[]): string;
146
+ /**
147
+ * Reject a `data` value that reached {@link buildSetClause}'s plain-value
148
+ * fallthrough as a plain object with exactly ONE key that is not a known
149
+ * atomic operator. On a scalar column that shape can only be a misspelled
150
+ * operator (`{ incremnt: 1 }`, `{ bogus: 'x' }`); binding it plainly writes the
151
+ * JSON text `{"bogus":"x"}` into the column and silently corrupts it.
152
+ *
153
+ * Deliberately narrow. Skipped for:
154
+ * - json / jsonb columns, which legitimately take arbitrary objects;
155
+ * - class instances (Buffer for bytea, Decimal wrappers, …), arrays and Dates,
156
+ * which {@link isUnmatchedPlainObject} already excludes;
157
+ * - multi-key objects, which are never operator-shaped (a real operator object
158
+ * has exactly one key) and are far more likely a deliberate payload.
159
+ *
160
+ * Shared by the SQL-build path and the cache-hit param-collect path, so a
161
+ * warmed template can never skip the check.
162
+ */
163
+ export declare function assertBindableSetValue(qi: BuilderCtx, key: string, value: unknown): void;
164
+ /**
165
+ * Fingerprint SET clauses for update/updateMany.
166
+ * Captures key names + operator types (set/increment/etc) but not values.
167
+ */
168
+ export declare function fingerprintSet(_qi: BuilderCtx, data: Record<string, unknown>): string;
169
+ /**
170
+ * Collect SET params for update/updateMany. Mirrors buildSetClause param order.
171
+ */
172
+ export declare function collectSetParams(qi: BuilderCtx, data: Record<string, unknown>, params: unknown[]): void;
@@ -45,6 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
45
45
  };
46
46
  })();
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.coerceWriteValue = coerceWriteValue;
48
49
  exports.buildReselectByWhere = buildReselectByWhere;
49
50
  exports.buildCreate = buildCreate;
50
51
  exports.makeCreateReselect = makeCreateReselect;
@@ -62,13 +63,64 @@ exports.parseWriteRow = parseWriteRow;
62
63
  exports.assertWritable = assertWritable;
63
64
  exports.assertNoGeneratedColumns = assertNoGeneratedColumns;
64
65
  exports.buildSetClause = buildSetClause;
66
+ exports.assertBindableSetValue = assertBindableSetValue;
65
67
  exports.fingerprintSet = fingerprintSet;
66
68
  exports.collectSetParams = collectSetParams;
67
69
  const errors_js_1 = require("../errors.js");
68
70
  const schema_js_1 = require("../schema.js");
69
71
  const compound_unique_js_1 = require("./compound-unique.js");
70
72
  const filters_js_1 = require("./filters.js");
73
+ const utils_js_1 = require("./utils.js");
71
74
  const whereMod = __importStar(require("./where.js"));
75
+ /**
76
+ * Normalize one `data` value before it is bound as a write param.
77
+ *
78
+ * Two value shapes need rewriting, both a JS `Date` on a temporal column:
79
+ *
80
+ * 1. A time-of-day column (`time` / `timetz`) — the driver serializes a `Date`
81
+ * as a full ISO timestamp with the process offset and Postgres answers
82
+ * `22007 invalid input syntax for type time`. Rewritten on every engine.
83
+ * 2. A zone-less `date` / `timestamp` column on PostgreSQL — the driver's
84
+ * local-offset serialization stores the PROCESS's calendar fields, so in a
85
+ * non-UTC process the stored value is wrong and (because the read path
86
+ * interprets an offset-less value as UTC) does not round-trip. Rewritten to
87
+ * the UTC components, mirroring `parseDbDate`. See `toLocalDateTimeLiteral`
88
+ * in query/utils.ts.
89
+ *
90
+ * `timestamptz` is deliberately untouched: it stores a real instant and the
91
+ * driver's local-offset string is already correct for it.
92
+ *
93
+ * Every other value, including a string that is already a valid literal, is
94
+ * returned by identity, so this is a byte-for-byte no-op for every non-temporal
95
+ * column.
96
+ */
97
+ function coerceWriteValue(qi, key, value) {
98
+ // Cheap shape check first: the common path costs one check and no lookup.
99
+ if (!(value instanceof Date) && !Array.isArray(value))
100
+ return value;
101
+ // Non-throwing column resolution: this runs on the cache-HIT param-collect
102
+ // path too, where an unknown key must not turn into a different error than
103
+ // the build path already raises.
104
+ const column = (0, utils_js_1.ownLookup)(qi.tableMeta.columnMap, key);
105
+ if (!column)
106
+ return value;
107
+ // Metadata generated by an older Turbine still carries per-column types
108
+ // (`pgTypes` has always been part of TableMetadata), and a column missing
109
+ // from the map resolves to undefined → no coercion, today's behavior.
110
+ return (0, utils_js_1.coerceTemporalValue)(qi.columnPgTypeMap.get(column), value, utcDateTimeWrites(qi));
111
+ }
112
+ /**
113
+ * Whether the zone-less `date` / `timestamp` UTC rewrite applies.
114
+ *
115
+ * PostgreSQL only: MySQL's `TIMESTAMP`/`DATETIME` and SQL Server's
116
+ * `datetime2` are converted or bound by their own drivers, and MySQL in
117
+ * particular reads a zone-less literal in the SESSION time zone, so a UTC
118
+ * literal would be misread there. `utcTimestamps: false` opts out — it is the
119
+ * same switch that turns off the UTC READ parsing, so the two stay symmetric.
120
+ */
121
+ function utcDateTimeWrites(qi) {
122
+ return qi.dialect.name === 'postgresql' && qi.utcTimestamps !== false;
123
+ }
72
124
  /**
73
125
  * Build a `SELECT * ... WHERE <predicate>` that re-fetches the row(s) matched
74
126
  * by a write's `where` clause. Used by the `'reselect'` result strategy to
@@ -86,7 +138,7 @@ function buildCreate(qi, args) {
86
138
  assertNoGeneratedColumns(qi, args.data, 'create');
87
139
  const entries = Object.entries(args.data).filter(([, v]) => v !== undefined);
88
140
  const columns = entries.map(([k]) => qi.toSqlColumn(k));
89
- const params = entries.map(([, v]) => v);
141
+ const params = entries.map(([k, v]) => coerceWriteValue(qi, k, v));
90
142
  // Enum columns get an explicit `::"EnumName"` cast (see enumTypeForColumn).
91
143
  const placeholders = entries.map(([k], i) => `${qi.p(i + 1)}${whereMod.enumCastSuffix(qi, qi.toColumn(k))}`);
92
144
  const sql = qi.dialect.buildInsertStatement({
@@ -132,7 +184,9 @@ function makeCreateReselect(qi, insertSql, insertParams, data) {
132
184
  let idx = 1;
133
185
  for (const pk of qi.tableMeta.primaryKey) {
134
186
  const field = qi.tableMeta.reverseColumnMap[pk] ?? (0, schema_js_1.snakeToCamel)(pk);
135
- selParams.push(data[field] ?? data[pk] ?? insertId);
187
+ // Same rewrite the INSERT's own params got: a temporal PK re-fetched with
188
+ // a raw Date would not match the literal that was just written.
189
+ selParams.push(coerceWriteValue(qi, field, data[field] ?? data[pk] ?? insertId));
136
190
  conds.push(`${qi.q(pk)} = ${qi.p(idx++)}`);
137
191
  }
138
192
  const where = conds.length > 0 ? ` WHERE ${conds.join(' AND ')}` : '';
@@ -157,7 +211,7 @@ function buildCreateMany(qi, args) {
157
211
  const columns = keys.map((k) => qi.toColumn(k));
158
212
  const rowValues = args.data.map((row) => {
159
213
  const record = row;
160
- return keys.map((key) => record[key]);
214
+ return keys.map((key) => coerceWriteValue(qi, key, record[key]));
161
215
  });
162
216
  // Use actual Postgres types for array casts in the default PostgreSQL dialect.
163
217
  // Enum columns cast to `"EnumName"[]` — the generic text[] fallback would
@@ -357,7 +411,7 @@ function buildUpsert(qi, args) {
357
411
  // Build the INSERT part from create data
358
412
  const createEntries = Object.entries(args.create).filter(([, v]) => v !== undefined);
359
413
  const columns = createEntries.map(([k]) => qi.toSqlColumn(k));
360
- const createParams = createEntries.map(([, v]) => v);
414
+ const createParams = createEntries.map(([k, v]) => coerceWriteValue(qi, k, v));
361
415
  // Enum columns get an explicit `::"EnumName"` cast (see enumTypeForColumn).
362
416
  const placeholders = createEntries.map(([k], i) => `${qi.p(i + 1)}${whereMod.enumCastSuffix(qi, qi.toColumn(k))}`);
363
417
  // The conflict target comes from `where` keys — must be unique/PK columns
@@ -371,7 +425,7 @@ function buildUpsert(qi, args) {
371
425
  paramIdx++;
372
426
  return clause;
373
427
  });
374
- const updateParams = updateEntries.map(([, v]) => v);
428
+ const updateParams = updateEntries.map(([k, v]) => coerceWriteValue(qi, k, v));
375
429
  const params = [...createParams, ...updateParams];
376
430
  // Global filter → restrict the conflict-UPDATE (soft-delete / tenancy) so an
377
431
  // upsert never resurrects a soft-deleted row or writes across tenants. Only
@@ -611,7 +665,7 @@ function buildSetClause(qi, key, value, params) {
611
665
  const op = keys[0];
612
666
  const opValue = v[op];
613
667
  if (op === 'set') {
614
- params.push(opValue);
668
+ params.push(coerceWriteValue(qi, key, opValue));
615
669
  return `${col} = ${qi.p(params.length)}${cast}`;
616
670
  }
617
671
  // Arithmetic operators: must be finite numbers
@@ -636,12 +690,44 @@ function buildSetClause(qi, key, value, params) {
636
690
  }
637
691
  }
638
692
  // Fall through: multi-key objects or non-operator single-key objects
639
- // are treated as plain values (e.g., JSONB column payloads).
693
+ // are treated as plain values (e.g., JSONB column payloads), unless the
694
+ // shape can only be a misspelled atomic operator (see below).
695
+ assertBindableSetValue(qi, key, value);
640
696
  }
641
697
  // Plain value (including null, Date, Buffer, arrays, JSON objects)
642
- params.push(value);
698
+ params.push(coerceWriteValue(qi, key, value));
643
699
  return `${col} = ${qi.p(params.length)}${cast}`;
644
700
  }
701
+ /**
702
+ * Reject a `data` value that reached {@link buildSetClause}'s plain-value
703
+ * fallthrough as a plain object with exactly ONE key that is not a known
704
+ * atomic operator. On a scalar column that shape can only be a misspelled
705
+ * operator (`{ incremnt: 1 }`, `{ bogus: 'x' }`); binding it plainly writes the
706
+ * JSON text `{"bogus":"x"}` into the column and silently corrupts it.
707
+ *
708
+ * Deliberately narrow. Skipped for:
709
+ * - json / jsonb columns, which legitimately take arbitrary objects;
710
+ * - class instances (Buffer for bytea, Decimal wrappers, …), arrays and Dates,
711
+ * which {@link isUnmatchedPlainObject} already excludes;
712
+ * - multi-key objects, which are never operator-shaped (a real operator object
713
+ * has exactly one key) and are far more likely a deliberate payload.
714
+ *
715
+ * Shared by the SQL-build path and the cache-hit param-collect path, so a
716
+ * warmed template can never skip the check.
717
+ */
718
+ function assertBindableSetValue(qi, key, value) {
719
+ if (!(0, filters_js_1.isUnmatchedPlainObject)(value))
720
+ return;
721
+ const keys = Object.keys(value);
722
+ if (keys.length !== 1 || filters_js_1.UPDATE_OPERATOR_KEYS.has(keys[0]))
723
+ return;
724
+ const column = qi.toColumn(key);
725
+ if (whereMod.isJsonColumnType(qi, whereMod.getColumnPgType(qi, column)))
726
+ return;
727
+ throw new errors_js_1.ValidationError(`[turbine] Unknown update operator "${keys[0]}" on "${qi.table}.${key}". ` +
728
+ `Supported: ${[...filters_js_1.UPDATE_OPERATOR_KEYS].join(', ')}. ` +
729
+ 'A plain object is only written as a value on a json/jsonb column.');
730
+ }
645
731
  /**
646
732
  * Fingerprint SET clauses for update/updateMany.
647
733
  * Captures key names + operator types (set/increment/etc) but not values.
@@ -668,9 +754,9 @@ function fingerprintSet(_qi, data) {
668
754
  /**
669
755
  * Collect SET params for update/updateMany. Mirrors buildSetClause param order.
670
756
  */
671
- function collectSetParams(_qi, data, params) {
757
+ function collectSetParams(qi, data, params) {
672
758
  const entries = Object.entries(data).filter(([, v]) => v !== undefined);
673
- for (const [, v] of entries) {
759
+ for (const [k, v] of entries) {
674
760
  if (v !== null &&
675
761
  typeof v === 'object' &&
676
762
  !Array.isArray(v) &&
@@ -679,10 +765,17 @@ function collectSetParams(_qi, data, params) {
679
765
  const obj = v;
680
766
  const keys = Object.keys(obj);
681
767
  if (keys.length === 1 && filters_js_1.UPDATE_OPERATOR_KEYS.has(keys[0])) {
682
- params.push(obj[keys[0]]);
768
+ // `set` is the only operator that binds a user value of arbitrary type;
769
+ // the arithmetic operators are finite numbers, which coerceWriteValue
770
+ // returns untouched. Mirrors buildSetClause.
771
+ params.push(coerceWriteValue(qi, k, obj[keys[0]]));
683
772
  continue;
684
773
  }
774
+ // Mirror the build path's misspelled-operator guard: the SET fingerprint
775
+ // is `key:eq` for both a plain value and a bogus operator object, so a
776
+ // cache HIT would otherwise bind the object and skip the check.
777
+ assertBindableSetValue(qi, k, v);
685
778
  }
686
- params.push(v);
779
+ params.push(coerceWriteValue(qi, k, v));
687
780
  }
688
781
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * turbine-orm — LISTEN/NOTIFY realtime pub/sub
3
+ *
4
+ * Postgres LISTEN/NOTIFY is a first-class realtime primitive that neither
5
+ * Prisma nor Drizzle expose ergonomically. This module backs the thin
6
+ * `$listen` / `$notify` methods on TurbineClient.
7
+ *
8
+ * Design — **one dedicated connection per subscription**:
9
+ *
10
+ * Each `$listen(channel, handler)` acquires its OWN long-lived client from
11
+ * the pool, runs `LISTEN "chan"`, and keeps that connection checked out for
12
+ * the life of the subscription. This is the simplest correct model: each
13
+ * subscription owns its lifecycle, `unsubscribe()` cleanly UNLISTENs and
14
+ * releases exactly one connection, and there is no shared multiplexing
15
+ * state to reason about. The trade-off is one pool slot per active channel
16
+ * — for the handful of channels a typical app listens on, that's a fine
17
+ * price for clarity. (A future optimization could multiplex many channels
18
+ * over a single shared notification connection.)
19
+ *
20
+ * Serverless / HTTP-pool caveat:
21
+ *
22
+ * LISTEN requires a *persistent* TCP connection that can push asynchronous
23
+ * notification messages back to the client. Stateless HTTP drivers
24
+ * (Neon HTTP, Vercel Postgres over fetch) cannot hold such a connection, so
25
+ * `$listen` will surface a clear error rather than hang. `$notify` works
26
+ * everywhere — it's a single round-trip `SELECT pg_notify(...)`.
27
+ */
28
+ import type { PgCompatPool } from './client.js';
29
+ /**
30
+ * Validate a LISTEN/NOTIFY channel name. Throws ValidationError on anything
31
+ * that isn't a plain, reasonable-length SQL identifier. This is enforced for
32
+ * BOTH `$listen` (where the channel is interpolated) and `$notify` (where the
33
+ * channel is a bound param) — defensive parity, and it catches user typos
34
+ * loudly.
35
+ */
36
+ export declare function validateChannel(channel: string): void;
37
+ /** Handler invoked with the raw NOTIFY payload string (empty string if none). */
38
+ export type NotificationHandler = (payload: string) => void;
39
+ /**
40
+ * A live LISTEN subscription. Call `unsubscribe()` to UNLISTEN, detach the
41
+ * handler, and release the dedicated connection back to the pool.
42
+ */
43
+ export interface Subscription {
44
+ /** The channel this subscription is listening on. */
45
+ readonly channel: string;
46
+ /**
47
+ * Stop listening: runs `UNLISTEN "chan"`, removes the notification listener,
48
+ * and releases the dedicated connection. Idempotent — safe to call twice.
49
+ */
50
+ unsubscribe(): Promise<void>;
51
+ }
52
+ /**
53
+ * Internal registry handle so TurbineClient can track and tear down active
54
+ * subscriptions on `disconnect()`.
55
+ */
56
+ export interface ActiveSubscription extends Subscription {
57
+ /** Tear down WITHOUT issuing UNLISTEN (used when the pool is being ended). */
58
+ _forceRelease(): void;
59
+ }
60
+ /**
61
+ * Acquire a dedicated connection, run `LISTEN "channel"`, and wire the handler.
62
+ *
63
+ * @param pool the pg-compatible pool to check a long-lived client out of
64
+ * @param channel channel name — MUST already be validated by the caller
65
+ * @param quotedChannel the channel run through quoteIdent (interpolated into SQL)
66
+ * @param handler called with each notification's payload
67
+ * @param onClosed invoked when the subscription releases, so the client can
68
+ * drop it from its active-subscription registry
69
+ */
70
+ export declare function createSubscription(pool: PgCompatPool, channel: string, quotedChannel: string, handler: NotificationHandler, onClosed: (sub: ActiveSubscription) => void): Promise<ActiveSubscription>;