turbine-orm 0.77.1 → 0.78.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 (93) hide show
  1. package/README.md +13 -9
  2. package/dist/cjs/cli/config.d.ts +7 -1
  3. package/dist/cjs/cli/config.js +11 -2
  4. package/dist/cjs/cli/destructive.d.ts +1 -1
  5. package/dist/cjs/cli/destructive.js +307 -9
  6. package/dist/cjs/cli/index.js +252 -42
  7. package/dist/cjs/cli/mcp.d.ts +23 -0
  8. package/dist/cjs/cli/mcp.js +190 -152
  9. package/dist/cjs/cli/migrate.d.ts +243 -3
  10. package/dist/cjs/cli/migrate.js +432 -43
  11. package/dist/cjs/cli/sql-statements.js +27 -0
  12. package/dist/cjs/cli/studio.d.ts +0 -1
  13. package/dist/cjs/cli/studio.js +9 -7
  14. package/dist/cjs/client.d.ts +8 -1
  15. package/dist/cjs/client.js +7 -0
  16. package/dist/cjs/connection-url.d.ts +82 -0
  17. package/dist/cjs/connection-url.js +187 -1
  18. package/dist/cjs/errors.d.ts +112 -12
  19. package/dist/cjs/errors.js +558 -114
  20. package/dist/cjs/generate.js +47 -15
  21. package/dist/cjs/index.d.ts +1 -1
  22. package/dist/cjs/introspect.d.ts +33 -0
  23. package/dist/cjs/introspect.js +54 -1
  24. package/dist/cjs/mssql.js +21 -1
  25. package/dist/cjs/nested-write.js +85 -14
  26. package/dist/cjs/pipeline-submittable.d.ts +2 -0
  27. package/dist/cjs/pipeline-submittable.js +88 -3
  28. package/dist/cjs/pipeline.js +13 -1
  29. package/dist/cjs/powdb-introspect.d.ts +5 -1
  30. package/dist/cjs/powdb-introspect.js +5 -1
  31. package/dist/cjs/powql.d.ts +14 -0
  32. package/dist/cjs/powql.js +44 -4
  33. package/dist/cjs/prisma-compat.js +95 -8
  34. package/dist/cjs/query/aggregates.js +56 -6
  35. package/dist/cjs/query/builder.d.ts +76 -13
  36. package/dist/cjs/query/builder.js +188 -58
  37. package/dist/cjs/query/compound-unique.d.ts +76 -0
  38. package/dist/cjs/query/compound-unique.js +129 -0
  39. package/dist/cjs/query/index.d.ts +1 -1
  40. package/dist/cjs/query/types.d.ts +65 -11
  41. package/dist/cjs/query/where.d.ts +85 -19
  42. package/dist/cjs/query/where.js +262 -47
  43. package/dist/cjs/query/writes.d.ts +11 -2
  44. package/dist/cjs/query/writes.js +116 -21
  45. package/dist/cjs/seed.d.ts +16 -0
  46. package/dist/cjs/seed.js +16 -0
  47. package/dist/cli/config.d.ts +7 -1
  48. package/dist/cli/config.js +11 -2
  49. package/dist/cli/destructive.d.ts +1 -1
  50. package/dist/cli/destructive.js +307 -9
  51. package/dist/cli/index.js +254 -44
  52. package/dist/cli/mcp.d.ts +23 -0
  53. package/dist/cli/mcp.js +187 -150
  54. package/dist/cli/migrate.d.ts +243 -3
  55. package/dist/cli/migrate.js +423 -45
  56. package/dist/cli/sql-statements.js +27 -0
  57. package/dist/cli/studio.d.ts +0 -1
  58. package/dist/cli/studio.js +10 -7
  59. package/dist/client.d.ts +8 -1
  60. package/dist/client.js +7 -0
  61. package/dist/connection-url.d.ts +82 -0
  62. package/dist/connection-url.js +183 -0
  63. package/dist/errors.d.ts +112 -12
  64. package/dist/errors.js +558 -114
  65. package/dist/generate.js +47 -15
  66. package/dist/index.d.ts +1 -1
  67. package/dist/introspect.d.ts +33 -0
  68. package/dist/introspect.js +53 -1
  69. package/dist/mssql.js +21 -1
  70. package/dist/nested-write.js +85 -14
  71. package/dist/pipeline-submittable.d.ts +2 -0
  72. package/dist/pipeline-submittable.js +87 -3
  73. package/dist/pipeline.js +14 -2
  74. package/dist/powdb-introspect.d.ts +5 -1
  75. package/dist/powdb-introspect.js +5 -1
  76. package/dist/powql.d.ts +14 -0
  77. package/dist/powql.js +45 -5
  78. package/dist/prisma-compat.js +96 -9
  79. package/dist/query/aggregates.js +56 -6
  80. package/dist/query/builder.d.ts +76 -13
  81. package/dist/query/builder.js +188 -58
  82. package/dist/query/compound-unique.d.ts +76 -0
  83. package/dist/query/compound-unique.js +126 -1
  84. package/dist/query/index.d.ts +1 -1
  85. package/dist/query/types.d.ts +65 -11
  86. package/dist/query/where.d.ts +85 -19
  87. package/dist/query/where.js +260 -47
  88. package/dist/query/writes.d.ts +11 -2
  89. package/dist/query/writes.js +117 -22
  90. package/dist/seed.d.ts +16 -0
  91. package/dist/seed.js +16 -0
  92. package/package.json +3 -3
  93. package/skills/turbine-orm/SKILL.md +37 -10
@@ -403,7 +403,15 @@ function buildUpdate(qi, args) {
403
403
  // The empty-`where` guard checks the USER predicate only, a global filter
404
404
  // must never turn an unguarded mass update into an allowed one.
405
405
  const userHasPredicate = !whereMod.userPredicateIsEmpty(qi, userWhere) || !!lock;
406
- whereMod.assertMutationHasPredicate(qi, 'update', userHasPredicate ? ' WHERE x' : '', (0, types_js_1.resolveUnsafeFlag)(args.allowFullTableScan, 'allowFullTableScan'));
406
+ const allowFullTableScan = (0, types_js_1.resolveUnsafeFlag)(args.allowFullTableScan, 'allowFullTableScan');
407
+ whereMod.assertMutationHasPredicate(qi, 'update', userHasPredicate ? ' WHERE x' : '', allowFullTableScan);
408
+ // `update` returns ONE row, so its where must identify one: the 0.73
409
+ // `findUnique` rule (query/compound-unique.ts) applied to the write that has
410
+ // the same shape. On the USER's where, before the global filter is merged
411
+ // (a filter narrows, it never identifies), and skipped only under the
412
+ // explicit full-table opt-in, which already means "every row".
413
+ if (!allowFullTableScan)
414
+ (0, compound_unique_js_1.assertMutationWhereIdentifiesOneRow)(qi.tableMeta, qi.table, userWhere, 'update');
407
415
  // The SQL is built from the global-filter-merged where (soft-delete keeps an
408
416
  // update from touching already-deleted rows).
409
417
  const whereObj = (whereMod.mergeGlobalFilter(qi, userWhere) ?? {});
@@ -536,7 +544,12 @@ function buildDelete(qi, args) {
536
544
  // Prisma compound-unique selector → the column conjunction (before the guard).
537
545
  const userWhere = (0, compound_unique_js_1.expandCompoundUniqueWhere)(qi.tableMeta, args.where);
538
546
  // Guard the USER predicate (a global filter must not satisfy the guard).
539
- whereMod.assertMutationHasPredicate(qi, 'delete', whereMod.userPredicateIsEmpty(qi, userWhere) ? '' : ' WHERE x', (0, types_js_1.resolveUnsafeFlag)(args.allowFullTableScan, 'allowFullTableScan'));
547
+ const allowFullTableScan = (0, types_js_1.resolveUnsafeFlag)(args.allowFullTableScan, 'allowFullTableScan');
548
+ whereMod.assertMutationHasPredicate(qi, 'delete', whereMod.userPredicateIsEmpty(qi, userWhere) ? '' : ' WHERE x', allowFullTableScan);
549
+ // Same identity rule as `update` (see buildUpdate): one returned row means
550
+ // one addressed row. `deleteMany` is the many-row path.
551
+ if (!allowFullTableScan)
552
+ (0, compound_unique_js_1.assertMutationWhereIdentifiesOneRow)(qi.tableMeta, qi.table, userWhere, 'delete');
540
553
  const whereObj = (whereMod.mergeGlobalFilter(qi, userWhere) ?? {});
541
554
  const whereFp = whereMod.fingerprintWhere(qi, whereObj);
542
555
  const ck = `d:${whereFp}${whereMod.globalFilterCacheSegment(qi)}`;
@@ -590,6 +603,23 @@ function buildUpsert(qi, args) {
590
603
  qi.currentSkip = (0, types_js_1.resolveSkipGlobalFilters)(args.skipGlobalFilters);
591
604
  // Prisma compound-unique selector on the conflict target → its member columns.
592
605
  const upsertWhere = (0, compound_unique_js_1.expandCompoundUniqueWhere)(qi.tableMeta, args.where);
606
+ // An upsert's `where` becomes the CONFLICT TARGET, so it carries the same
607
+ // one-row contract `update` / `delete` do and is refused by the same rule
608
+ // (query/compound-unique.ts), with its own sentence because the consequence
609
+ // differs. Without it a non-unique `where` reached the server as
610
+ // `ON CONFLICT ("role")` and came back as a bare SQLSTATE 42P10 on
611
+ // PostgreSQL, and WORSE than an error on the engines that do not read the
612
+ // conflict target back: MySQL's `ON DUPLICATE KEY UPDATE` ignores it and
613
+ // keys off whichever unique index the row happens to violate, and SQL
614
+ // Server's `MERGE ... ON` matches every row the predicate matches and
615
+ // updates all of them.
616
+ //
617
+ // Before any SQL is assembled, and this is the only where-guard `upsert`
618
+ // runs: `{}` and `{ email: undefined }` are refused here too, and by the
619
+ // right sentence. The empty-`where` guard's own message points at
620
+ // `allowFullTableScan: UNSAFE`, which is not an option on `UpsertArgs` at
621
+ // all, so borrowing it here would name a way out that does not exist.
622
+ (0, compound_unique_js_1.assertMutationWhereIdentifiesOneRow)(qi.tableMeta, qi.table, upsertWhere, 'upsert');
593
623
  // Build the INSERT part from create data
594
624
  const createEntries = writeEntries(qi, args.create);
595
625
  const columns = createEntries.map(([k]) => qi.toSqlColumn(k));
@@ -599,25 +629,47 @@ function buildUpsert(qi, args) {
599
629
  // The conflict target comes from `where` keys, must be unique/PK columns
600
630
  const conflictKeys = Object.keys(upsertWhere).filter((k) => upsertWhere[k] !== undefined);
601
631
  const conflictColumns = conflictKeys.map((k) => qi.toSqlColumn(k));
602
- // Build the UPDATE SET part
632
+ // The conflict-UPDATE SET goes through the SAME operator-aware compiler as
633
+ // `update()` (buildSetClause), so `set` / `increment` / `decrement` /
634
+ // `multiply` / `divide` mean here what they mean there and a misspelled
635
+ // operator gets the same E003. This branch used to bind each value directly,
636
+ // which stored an operator OBJECT as the JSON text `{"set":"x"}` in a text
637
+ // column with no error at all. buildSetClause pushes onto `params` as it
638
+ // goes, so the numbering continues after the create params exactly as the
639
+ // hand-rolled `paramIdx` did. No SQL cache here (the statement is rebuilt per
640
+ // call), so the fingerprint / collect mirrors update() keeps are not needed.
603
641
  const updateEntries = writeEntries(qi, args.update);
604
- let paramIdx = createParams.length + 1;
605
- const setClauses = updateEntries.map(([k]) => {
606
- const clause = `${qi.toSqlColumn(k)} = ${qi.p(paramIdx)}${whereMod.enumCastSuffix(qi, qi.toColumn(k))}`;
607
- paramIdx++;
608
- return clause;
609
- });
610
- const updateParams = updateEntries.map(([k, v]) => coerceWriteValue(qi, k, v));
611
- const params = [...createParams, ...updateParams];
642
+ const params = [...createParams];
643
+ const refQualifier = upsertReferenceQualifier(qi);
644
+ const setClauses = updateEntries.map(([k, v]) => buildSetClause(qi, k, v, params, refQualifier));
612
645
  // Global filter → restrict the conflict-UPDATE (soft-delete / tenancy) so an
613
646
  // upsert never resurrects a soft-deleted row or writes across tenants. Only
614
- // on engines whose upsert can carry a predicate (Postgres); the gf params
615
- // continue the placeholder numbering after create+update params.
647
+ // on engines whose upsert can carry a predicate (Postgres, SQLite); the gf
648
+ // params continue the placeholder numbering after create+update params.
649
+ //
650
+ // Compiled against the TABLE QUALIFIER, not bare: in `ON CONFLICT ... DO
651
+ // UPDATE ... WHERE` both the target table and `excluded` are in scope, so an
652
+ // unqualified column is ambiguous and PostgreSQL rejected EVERY upsert on a
653
+ // globally filtered table at parse time (42702), insert path included.
616
654
  let updateWhere;
617
655
  if (qi.dialect.supportsUpsertUpdateWhere) {
618
656
  const gf = whereMod.resolveGlobalFilter(qi, qi.table);
619
- if (gf)
620
- updateWhere = whereMod.buildWhereClause(qi, gf, params) ?? undefined;
657
+ if (gf) {
658
+ // Compiled through a scope whose FROM-item reference is ALREADY RENDERED
659
+ // (`"users"`), which is what the target table is inside `ON CONFLICT ...
660
+ // DO UPDATE`. `aliasWhereScope` is the wrong seam for that: it takes a
661
+ // BARE alias and quotes it itself for the nested-relation correlation
662
+ // parent, so handing it `q(table)` produced a triple-quoted reference
663
+ // inside an EXISTS body and the whole statement failed with 42P01
664
+ // whenever the configured global filter was a relation filter rather
665
+ // than a plain column. A bare table name is not the answer either: it
666
+ // would leave the qualifier unquoted and break every table whose name
667
+ // needs quoting. `buildRenderedRefWhere` is the rendered-reference seam,
668
+ // one parameter with one meaning, used for the qualifier and the
669
+ // correlation parent alike, and shared with the batched `_count`
670
+ // follow-up, which had the identical bug.
671
+ updateWhere = whereMod.buildRenderedRefWhere(qi, qi.table, qi.tableMeta, qi.q(qi.table), gf, params) ?? undefined;
672
+ }
621
673
  }
622
674
  const sql = qi.dialect.buildUpsertStatement({
623
675
  table: qi.q(qi.table),
@@ -634,11 +686,21 @@ function buildUpsert(qi, args) {
634
686
  transform: (result) => {
635
687
  const row = result.rows[0];
636
688
  if (!row) {
689
+ // With a conflict-UPDATE predicate in play there IS a way to get no
690
+ // row: the key conflicts with a row the global filter hides, the
691
+ // predicate is false for it, the engine skips the update, and the
692
+ // insert cannot happen either because the key is taken. Nothing was
693
+ // written and the hidden row is untouched; say so, rather than "this
694
+ // should never happen".
637
695
  throw new errors_js_1.NotFoundError({
638
696
  table: qi.table,
639
697
  where: args.where,
640
698
  operation: 'upsert',
641
- message: `upsert on "${qi.table}" returned no row from RETURNING *; this should never happen.`,
699
+ message: updateWhere
700
+ ? `upsert on "${qi.table}" wrote nothing: the row that conflicts on the given key is excluded by the ` +
701
+ `configured global filter for "${qi.table}", so it was neither updated (the filter hides it) nor ` +
702
+ 'inserted (the key is taken). Pass `skipGlobalFilters: UNSAFE` to address it anyway.'
703
+ : `upsert on "${qi.table}" returned no row from RETURNING *; this should never happen.`,
642
704
  });
643
705
  }
644
706
  return parseWriteRow(qi, row);
@@ -654,6 +716,29 @@ function buildUpsert(qi, args) {
654
716
  : undefined,
655
717
  };
656
718
  }
719
+ /**
720
+ * The qualifier an upsert's arithmetic SET operator reads its column through.
721
+ *
722
+ * `INSERT ... ON CONFLICT DO UPDATE SET n = n + 1` is 42702 on PostgreSQL
723
+ * because `n` could be the target row's or `excluded`'s, so the reference is
724
+ * qualified with the table name on every engine whose upsert statement names
725
+ * the table directly (PostgreSQL, SQLite, MySQL all accept `"t"."n"` there).
726
+ * SQL Server is the exception and keeps the bare reference: its `MERGE`
727
+ * aliases the target as `T`, so the TABLE NAME does not resolve there at all,
728
+ * and the alias belongs to that dialect's own `buildUpsertStatement` rather
729
+ * than to this module. An arithmetic operator in an mssql upsert may therefore
730
+ * still be refused by the server as an ambiguous column; that shape has never
731
+ * worked (it previously bound the operator OBJECT as the value), and the sound
732
+ * fix is for the dialect to publish its target alias, which is a change to
733
+ * `dialect.ts`. It fails loudly either way, so no wrong answer is possible.
734
+ * `set` and literal values reference no column and are unaffected everywhere.
735
+ *
736
+ * Gated on the dialect NAME, never on hook presence: every engine dialect
737
+ * spreads `postgresDialect`, so a presence test would say yes for all of them.
738
+ */
739
+ function upsertReferenceQualifier(qi) {
740
+ return qi.dialect.name === 'mssql' ? '' : `${qi.q(qi.table)}.`;
741
+ }
657
742
  function buildUpdateMany(qi, args) {
658
743
  assertWritable(qi, 'updateMany');
659
744
  qi.currentSkip = (0, types_js_1.resolveSkipGlobalFilters)(args.skipGlobalFilters);
@@ -916,9 +1001,19 @@ function assertNoGeneratedColumns(qi, data, operation) {
916
1001
  * Returns the SQL fragment (e.g., `"view_count" = "view_count" + $3`) and
917
1002
  * pushes any required params onto the shared params array so that WHERE
918
1003
  * clause numbering continues correctly afterward.
1004
+ *
1005
+ * `refQualifier` prefixes the column REFERENCE an arithmetic operator reads
1006
+ * from (`"view_count" = <qualifier>"view_count" + $n`). A plain `UPDATE` has
1007
+ * one table in scope and passes nothing; `upsert` passes the table name,
1008
+ * because inside `ON CONFLICT ... DO UPDATE SET` the target table and
1009
+ * `excluded` are BOTH in scope and a bare reference is 42702 on PostgreSQL.
1010
+ * The assignment target on the left stays bare on every engine (it can only
1011
+ * ever be the target table's column), and `set` / literal values reference no
1012
+ * column at all, so their SQL is byte-identical with or without a qualifier.
919
1013
  */
920
- function buildSetClause(qi, key, value, params) {
1014
+ function buildSetClause(qi, key, value, params, refQualifier = '') {
921
1015
  const col = qi.toSqlColumn(key);
1016
+ const ref = `${refQualifier}${col}`;
922
1017
  // Enum columns get an explicit `::"EnumName"` cast on their value bind
923
1018
  // (see enumTypeForColumn); `''` everywhere else. Value-invariant, so the
924
1019
  // SQL cache and collectSetParams are unaffected.
@@ -945,19 +1040,19 @@ function buildSetClause(qi, key, value, params) {
945
1040
  }
946
1041
  if (op === 'increment') {
947
1042
  params.push(opValue);
948
- return `${col} = ${col} + ${qi.p(params.length)}`;
1043
+ return `${col} = ${ref} + ${qi.p(params.length)}`;
949
1044
  }
950
1045
  if (op === 'decrement') {
951
1046
  params.push(opValue);
952
- return `${col} = ${col} - ${qi.p(params.length)}`;
1047
+ return `${col} = ${ref} - ${qi.p(params.length)}`;
953
1048
  }
954
1049
  if (op === 'multiply') {
955
1050
  params.push(opValue);
956
- return `${col} = ${col} * ${qi.p(params.length)}`;
1051
+ return `${col} = ${ref} * ${qi.p(params.length)}`;
957
1052
  }
958
1053
  if (op === 'divide') {
959
1054
  params.push(opValue);
960
- return `${col} = ${col} / ${qi.p(params.length)}`;
1055
+ return `${col} = ${ref} / ${qi.p(params.length)}`;
961
1056
  }
962
1057
  }
963
1058
  // Fall through: multi-key objects or non-operator single-key objects
@@ -12,4 +12,20 @@ export type DefinedSeed = () => Promise<void>;
12
12
  * Exported for unit testing the frame parser in isolation.
13
13
  */
14
14
  export declare function parseStackFramePath(line: string): string | null;
15
+ /**
16
+ * Declare a seed: `fn` receives a connected `TurbineClient` and is awaited.
17
+ *
18
+ * The returned function runs the seed on demand. When the module that calls
19
+ * `defineSeed` is itself the process entry point (`npx tsx seed.ts`, or the
20
+ * child `turbine seed` spawns), the seed also runs automatically.
21
+ *
22
+ * The client connects through `DATABASE_URL`. Under `turbine seed` that value
23
+ * is the configured connection string with `search_path` PINNED to the
24
+ * configured `schema` (as the `options=-c search_path` connection parameter,
25
+ * so it can never leak onto a pooled backend the way a `SET` would). Every
26
+ * unqualified table name the seed writes therefore lands in the same schema
27
+ * `push`, `generate` and the generated client use; a seed that must reach
28
+ * another schema qualifies the name. The default `public` adds nothing, so a
29
+ * project that never set `schema` connects exactly as before.
30
+ */
15
31
  export declare function defineSeed(fn: SeedFunction): DefinedSeed;
package/dist/cjs/seed.js CHANGED
@@ -143,6 +143,22 @@ async function runSeed(fn) {
143
143
  await db.disconnect();
144
144
  }
145
145
  }
146
+ /**
147
+ * Declare a seed: `fn` receives a connected `TurbineClient` and is awaited.
148
+ *
149
+ * The returned function runs the seed on demand. When the module that calls
150
+ * `defineSeed` is itself the process entry point (`npx tsx seed.ts`, or the
151
+ * child `turbine seed` spawns), the seed also runs automatically.
152
+ *
153
+ * The client connects through `DATABASE_URL`. Under `turbine seed` that value
154
+ * is the configured connection string with `search_path` PINNED to the
155
+ * configured `schema` (as the `options=-c search_path` connection parameter,
156
+ * so it can never leak onto a pooled backend the way a `SET` would). Every
157
+ * unqualified table name the seed writes therefore lands in the same schema
158
+ * `push`, `generate` and the generated client use; a seed that must reach
159
+ * another schema qualifies the name. The default `public` adds nothing, so a
160
+ * project that never set `schema` connects exactly as before.
161
+ */
146
162
  function defineSeed(fn) {
147
163
  const run = () => runSeed(fn);
148
164
  if (isDirectSeedModule()) {
@@ -230,5 +230,11 @@ export declare function connectionStringHasPassword(connectionString: string): b
230
230
  * `.env` that holds the real value. The refusal lives here rather than at the
231
231
  * call site so no future caller can reintroduce the leak by passing the raw
232
232
  * `--url` through.
233
+ *
234
+ * `schema` is the RESOLVED Postgres schema (`--schema` or the default), written
235
+ * as the config's `schema` value. `init --schema app` probes `app` and then
236
+ * used to write `schema: 'public'` regardless, so the next documented step,
237
+ * `push`, diffed the starter schema against the wrong namespace and proposed
238
+ * dropping every column it found there.
233
239
  */
234
- export declare function configTemplate(connectionString?: string): string;
240
+ export declare function configTemplate(connectionString?: string, schema?: string): string;
@@ -255,9 +255,18 @@ export function connectionStringHasPassword(connectionString) {
255
255
  * `.env` that holds the real value. The refusal lives here rather than at the
256
256
  * call site so no future caller can reintroduce the leak by passing the raw
257
257
  * `--url` through.
258
+ *
259
+ * `schema` is the RESOLVED Postgres schema (`--schema` or the default), written
260
+ * as the config's `schema` value. `init --schema app` probes `app` and then
261
+ * used to write `schema: 'public'` regardless, so the next documented step,
262
+ * `push`, diffed the starter schema against the wrong namespace and proposed
263
+ * dropping every column it found there.
258
264
  */
259
- export function configTemplate(connectionString) {
265
+ export function configTemplate(connectionString, schema = 'public') {
260
266
  const inlineUrl = connectionString && !connectionStringHasPassword(connectionString) ? connectionString : undefined;
267
+ // Same delimiter rule as the URL below: a schema name is an arbitrary
268
+ // identifier, and a quote in it must not end the TS string early.
269
+ const schemaLiteral = schema.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
261
270
  // Single quotes are the string delimiter in the emitted TS, so a connection
262
271
  // string containing one would otherwise produce a config file that does not
263
272
  // parse (a password-free URL can still carry a quote in a query parameter).
@@ -278,7 +287,7 @@ ${urlLine}
278
287
  out: './generated/turbine',
279
288
 
280
289
  /** Postgres schema to introspect (default: public) */
281
- schema: 'public',
290
+ schema: '${schemaLiteral}',
282
291
 
283
292
  /** Tables to exclude from generation */
284
293
  // exclude: ['_migrations', '_sessions'],
@@ -28,7 +28,7 @@
28
28
  * migration runner. Two lexers is how this guard came to disagree with the
29
29
  * executor about what a file contained (see that module's header).
30
30
  */
31
- export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'alter-column-type' | 'merge-delete' | 'rename' | 'dynamic-destructive';
31
+ export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'alter-column-type' | 'merge-delete' | 'rename' | 'rewrite-rule' | 'dynamic-destructive' | 'dynamic-unclassified';
32
32
  export interface DestructiveStatement {
33
33
  /** The offending SQL statement (trimmed, possibly long, display truncated) */
34
34
  statement: string;