turbine-orm 0.29.0 → 0.31.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 (48) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/cli/index.js +5 -0
  3. package/dist/cjs/cli/mcp.js +22 -92
  4. package/dist/cjs/client.js +47 -6
  5. package/dist/cjs/generate.js +71 -25
  6. package/dist/cjs/index.js +4 -1
  7. package/dist/cjs/introspect.js +350 -120
  8. package/dist/cjs/mssql.js +42 -136
  9. package/dist/cjs/mysql.js +16 -129
  10. package/dist/cjs/optional-peer-import.cjs +122 -0
  11. package/dist/cjs/powdb.js +579 -89
  12. package/dist/cjs/powql.js +56 -26
  13. package/dist/cjs/query/builder.js +601 -86
  14. package/dist/cjs/query/filters.js +80 -2
  15. package/dist/cjs/schema-metadata.js +316 -0
  16. package/dist/cjs/sqlite.js +8 -89
  17. package/dist/cli/index.d.ts +2 -0
  18. package/dist/cli/index.js +5 -0
  19. package/dist/cli/mcp.d.ts +18 -0
  20. package/dist/cli/mcp.js +22 -93
  21. package/dist/client.d.ts +19 -2
  22. package/dist/client.js +47 -6
  23. package/dist/generate.d.ts +16 -4
  24. package/dist/generate.js +71 -25
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.js +2 -0
  27. package/dist/introspect.d.ts +94 -1
  28. package/dist/introspect.js +345 -120
  29. package/dist/mssql.js +40 -104
  30. package/dist/mysql.js +14 -97
  31. package/dist/optional-peer-import.cjs +89 -0
  32. package/dist/optional-peer-import.d.cts +53 -0
  33. package/dist/powdb.d.ts +118 -23
  34. package/dist/powdb.js +574 -88
  35. package/dist/powql.d.ts +6 -0
  36. package/dist/powql.js +58 -28
  37. package/dist/query/builder.d.ts +145 -8
  38. package/dist/query/builder.js +602 -87
  39. package/dist/query/deferred.d.ts +7 -2
  40. package/dist/query/filters.d.ts +46 -1
  41. package/dist/query/filters.js +76 -1
  42. package/dist/query/index.d.ts +1 -1
  43. package/dist/query/types.d.ts +85 -11
  44. package/dist/schema-metadata.d.ts +77 -0
  45. package/dist/schema-metadata.js +313 -0
  46. package/dist/schema.d.ts +10 -0
  47. package/dist/sqlite.js +9 -90
  48. package/package.json +3 -3
package/dist/cjs/powql.js CHANGED
@@ -142,7 +142,14 @@ class PowqlInterface {
142
142
  }
143
143
  this.meta = meta;
144
144
  this.defaultLimit = options.defaultLimit;
145
- this.warnOnUnlimited = options.warnOnUnlimited !== false;
145
+ // Same per-table resolution as QueryInterface: an object map accepts BOTH
146
+ // the snake_case table name and the camelCase accessor as keys (snake_case
147
+ // wins on conflict); unlisted tables keep the default (warn on).
148
+ const warnOpt = options.warnOnUnlimited;
149
+ this.warnOnUnlimited =
150
+ typeof warnOpt === 'object' && warnOpt !== null
151
+ ? (warnOpt[table] ?? warnOpt[(0, schema_js_1.snakeToCamel)(table)]) !== false
152
+ : warnOpt !== false;
146
153
  this.onQuery = options._onQuery;
147
154
  }
148
155
  // -------------------------------------------------------------------------
@@ -447,7 +454,7 @@ class PowqlInterface {
447
454
  const chunk = targetPks.slice(i, i + MAX_RELATION_KEYS);
448
455
  const params = [];
449
456
  const ph = chunk.map((v) => this.param(v, params)).join(', ');
450
- const { rows } = await this.exec(`${through.table} filter .${targetJCol} in (${ph}) { .${sourceJCol} }`, params, timeout);
457
+ const { rows } = await this.exec(`${(0, powdb_js_1.quotePowqlIdent)(through.table)} filter .${targetJCol} in (${ph}) { .${sourceJCol} }`, params, timeout);
451
458
  for (const r of rows) {
452
459
  const v = r[sourceJCol];
453
460
  if (v != null)
@@ -599,7 +606,7 @@ class PowqlInterface {
599
606
  }
600
607
  const limitClause = limit !== undefined ? ` limit ${this.param(limit, params)}` : '';
601
608
  const offsetClause = args.offset ? ` offset ${this.param(args.offset, params)}` : '';
602
- const powql = `${this.table}${distinct}${filter}${order}${limitClause}${offsetClause} ${this.projection(cols)}`;
609
+ const powql = `${this.qt}${distinct}${filter}${order}${limitClause}${offsetClause} ${this.projection(cols)}`;
603
610
  const { rows } = await this.exec(powql, params, args.timeout);
604
611
  return rows;
605
612
  }
@@ -748,7 +755,7 @@ class PowqlInterface {
748
755
  const chunk = parentKeys.slice(i, i + MAX_RELATION_KEYS);
749
756
  const params = [];
750
757
  const placeholders = chunk.map((v) => this.param(v, params)).join(', ');
751
- const powql = `${through.table} filter .${sourceJCol} in (${placeholders}) { .${sourceJCol}, .${targetJCol} }`;
758
+ const powql = `${(0, powdb_js_1.quotePowqlIdent)(through.table)} filter .${sourceJCol} in (${placeholders}) { .${sourceJCol}, .${targetJCol} }`;
752
759
  const { rows } = await this.exec(powql, params, timeout);
753
760
  for (const row of rows) {
754
761
  const sv = String(row[sourceJCol]);
@@ -830,6 +837,14 @@ class PowqlInterface {
830
837
  }
831
838
  return out;
832
839
  }
840
+ /**
841
+ * The table name as a PowQL type reference — backtick-quoted when it is a
842
+ * reserved word (e.g. a table named `order`). Used in every emitted
843
+ * statement; plain `this.table` stays in error messages.
844
+ */
845
+ get qt() {
846
+ return (0, powdb_js_1.quotePowqlIdent)(this.table);
847
+ }
833
848
  async create(args) {
834
849
  return this.withMiddleware('create', args, async () => {
835
850
  if ((0, nested_write_js_1.hasRelationFields)(args.data, this.meta)) {
@@ -838,9 +853,11 @@ class PowqlInterface {
838
853
  const data = this.applyPkDefault(args.data);
839
854
  const assigns = this.scalarData(data);
840
855
  const params = [];
841
- const body = assigns.map((a) => `${a.col.name} := ${this.writeRef(a.value, a.col, params)}`).join(', ');
856
+ const body = assigns
857
+ .map((a) => `${(0, powdb_js_1.quotePowqlIdent)(a.col.name)} := ${this.writeRef(a.value, a.col, params)}`)
858
+ .join(', ');
842
859
  // `returning` surfaces the inserted row (all columns, schema order) in one round-trip.
843
- const { rows } = await this.exec(`insert ${this.table} { ${body} } returning`, params, args.timeout);
860
+ const { rows } = await this.exec(`insert ${this.qt} { ${body} } returning`, params, args.timeout);
844
861
  const row = rows.length ? this.shape(rows)[0] : null;
845
862
  if (!row)
846
863
  throw new errors_js_1.NotFoundError({ table: this.table, where: data });
@@ -855,10 +872,10 @@ class PowqlInterface {
855
872
  const params = [];
856
873
  const tuples = inputs.map((d) => {
857
874
  const assigns = this.scalarData(d);
858
- return `{ ${assigns.map((a) => `${a.col.name} := ${this.writeRef(a.value, a.col, params)}`).join(', ')} }`;
875
+ return `{ ${assigns.map((a) => `${(0, powdb_js_1.quotePowqlIdent)(a.col.name)} := ${this.writeRef(a.value, a.col, params)}`).join(', ')} }`;
859
876
  });
860
877
  // Multi-row insert with `returning` hands back every inserted row in one round-trip.
861
- const { rows } = await this.exec(`insert ${this.table} ${tuples.join(', ')} returning`, params, args.timeout);
878
+ const { rows } = await this.exec(`insert ${this.qt} ${tuples.join(', ')} returning`, params, args.timeout);
862
879
  return this.shape(rows);
863
880
  });
864
881
  }
@@ -873,7 +890,7 @@ class PowqlInterface {
873
890
  this.assertCompiledWhere(where, false, 'update');
874
891
  const setClause = this.buildUpdateAssignments(args.data, params);
875
892
  // `returning` hands back the post-update row(s); take the first (single-row contract).
876
- const { rows } = await this.exec(`${this.table} filter ${where} update { ${setClause} } returning`, params, args.timeout);
893
+ const { rows } = await this.exec(`${this.qt} filter ${where} update { ${setClause} } returning`, params, args.timeout);
877
894
  const row = rows.length ? this.shape(rows)[0] : null;
878
895
  if (!row)
879
896
  throw new errors_js_1.NotFoundError({ table: this.table, where: args.where });
@@ -888,7 +905,7 @@ class PowqlInterface {
888
905
  this.assertCompiledWhere(where, args.allowFullTableScan, 'updateMany');
889
906
  const setClause = this.buildUpdateAssignments(args.data, params);
890
907
  const filter = where ? ` filter ${where}` : '';
891
- const { rowCount } = await this.exec(`${this.table}${filter} update { ${setClause} }`, params, args.timeout);
908
+ const { rowCount } = await this.exec(`${this.qt}${filter} update { ${setClause} }`, params, args.timeout);
892
909
  return { count: rowCount };
893
910
  });
894
911
  }
@@ -903,7 +920,7 @@ class PowqlInterface {
903
920
  }
904
921
  const colMeta = this.column(field);
905
922
  const ref = this.ref(field);
906
- const col = colMeta.name;
923
+ const col = (0, powdb_js_1.quotePowqlIdent)(colMeta.name);
907
924
  if (value !== null && typeof value === 'object' && !(value instanceof Date)) {
908
925
  const opObj = value;
909
926
  if ('set' in opObj)
@@ -961,21 +978,34 @@ class PowqlInterface {
961
978
  // drifts from `powdbDialect`; falls back to the literal lowercase keywords.
962
979
  const d = this.options.dialect;
963
980
  const client = await this.pool.connect();
981
+ let began = false;
964
982
  try {
965
983
  await client.query(d?.beginStatement?.() ?? 'begin');
984
+ began = true;
966
985
  const { TransactionClient } = await Promise.resolve().then(() => __importStar(require('./client.js')));
967
986
  const tx = new TransactionClient(client, this.schema, this.middlewares, this.options);
968
987
  const ctx = { schema: this.schema, tx: tx };
969
- const result = await fn(ctx);
988
+ // Plant the single-writer re-entrancy marker for the implicit tx's
989
+ // subtree (same seam TurbineClient.$transaction uses) — user code that
990
+ // fires db.$transaction from inside (e.g. $use middleware around a
991
+ // nested-write child op) must fast-fail E017, not queue into deadlock.
992
+ const wrap = client
993
+ .wrapTransactionCallback;
994
+ const result = await (wrap ? wrap(() => fn(ctx)) : fn(ctx));
970
995
  await client.query(d?.commitStatement?.() ?? 'commit');
971
996
  return result;
972
997
  }
973
998
  catch (err) {
974
- try {
975
- await client.query(d?.rollbackStatement?.() ?? 'rollback');
976
- }
977
- catch {
978
- /* best-effort — the connection may be gone */
999
+ // Only roll back a transaction we actually opened — a failed BEGIN
1000
+ // (queue timeout, re-entrancy E017) must not emit a stray ROLLBACK that
1001
+ // could land inside another transaction on a shared engine handle.
1002
+ if (began) {
1003
+ try {
1004
+ await client.query(d?.rollbackStatement?.() ?? 'rollback');
1005
+ }
1006
+ catch {
1007
+ /* best-effort — the connection may be gone */
1008
+ }
979
1009
  }
980
1010
  throw err;
981
1011
  }
@@ -998,7 +1028,7 @@ class PowqlInterface {
998
1028
  const where = this.buildWhere(resolvedWhere, params);
999
1029
  this.assertCompiledWhere(where, false, 'delete');
1000
1030
  // `returning` hands back the deleted row(s) — no separate pre-image reselect needed.
1001
- const { rows } = await this.exec(`${this.table} filter ${where} delete returning`, params, args.timeout);
1031
+ const { rows } = await this.exec(`${this.qt} filter ${where} delete returning`, params, args.timeout);
1002
1032
  const row = rows.length ? this.shape(rows)[0] : null;
1003
1033
  if (!row)
1004
1034
  throw new errors_js_1.NotFoundError({ table: this.table, where: args.where });
@@ -1012,7 +1042,7 @@ class PowqlInterface {
1012
1042
  const where = this.buildWhere(resolvedWhere, params);
1013
1043
  this.assertCompiledWhere(where, args.allowFullTableScan, 'deleteMany');
1014
1044
  const filter = where ? ` filter ${where}` : '';
1015
- const { rowCount } = await this.exec(`${this.table}${filter} delete`, params, args.timeout);
1045
+ const { rowCount } = await this.exec(`${this.qt}${filter} delete`, params, args.timeout);
1016
1046
  return { count: rowCount };
1017
1047
  });
1018
1048
  }
@@ -1027,14 +1057,14 @@ class PowqlInterface {
1027
1057
  }
1028
1058
  const params = [];
1029
1059
  const createBody = this.scalarData(createData)
1030
- .map((a) => `${a.col.name} := ${this.writeRef(a.value, a.col, params)}`)
1060
+ .map((a) => `${(0, powdb_js_1.quotePowqlIdent)(a.col.name)} := ${this.writeRef(a.value, a.col, params)}`)
1031
1061
  .join(', ');
1032
1062
  const updateBody = this.buildUpdateAssignments(args.update, params);
1033
1063
  // PowDB 0.7.0's `upsert` statement does NOT accept a trailing `returning`
1034
1064
  // (verified: "unexpected trailing token … 'returning'"), because it is one
1035
1065
  // atomic insert-or-update, not two branches. So upsert alone keeps the
1036
1066
  // reselect-by-PK fetch; create/update/delete all use `returning`.
1037
- await this.exec(`upsert ${this.table} on .${pkCol} { ${createBody} } on conflict { ${updateBody} }`, params, args.timeout);
1067
+ await this.exec(`upsert ${this.qt} on .${pkCol} { ${createBody} } on conflict { ${updateBody} }`, params, args.timeout);
1038
1068
  const pkField = this.meta.reverseColumnMap[pkCol] ?? pkCol;
1039
1069
  const row = await this.reselectByPk(createData[pkField], args.timeout);
1040
1070
  if (!row)
@@ -1079,7 +1109,7 @@ class PowqlInterface {
1079
1109
  const resolvedWhere = await this.resolveRelationFilters(args.where, args.timeout);
1080
1110
  const where = this.buildWhere(resolvedWhere, params);
1081
1111
  const filter = where ? ` filter ${where}` : '';
1082
- const { rows } = await this.exec(`count(${this.table}${filter})`, params, args.timeout);
1112
+ const { rows } = await this.exec(`count(${this.qt}${filter})`, params, args.timeout);
1083
1113
  return Number((rows[0]?.value ?? rows[0]?.count ?? 0));
1084
1114
  });
1085
1115
  }
@@ -1099,12 +1129,12 @@ class PowqlInterface {
1099
1129
  };
1100
1130
  if (args._count) {
1101
1131
  if (args._count === true) {
1102
- result._count = (await scalar(`count(${this.table}${filter})`)) ?? 0;
1132
+ result._count = (await scalar(`count(${this.qt}${filter})`)) ?? 0;
1103
1133
  }
1104
1134
  else {
1105
1135
  const counts = {};
1106
1136
  for (const field of Object.keys(args._count).filter((f) => args._count[f])) {
1107
- counts[field] = (await scalar(`count(${this.table}${filter} { ${this.ref(field)} })`)) ?? 0;
1137
+ counts[field] = (await scalar(`count(${this.qt}${filter} { ${this.ref(field)} })`)) ?? 0;
1108
1138
  }
1109
1139
  result._count = counts;
1110
1140
  }
@@ -1116,7 +1146,7 @@ class PowqlInterface {
1116
1146
  const acc = {};
1117
1147
  for (const field of Object.keys(spec).filter((f) => spec[f])) {
1118
1148
  const powfn = fn.slice(1); // sum/avg/min/max
1119
- acc[field] = await scalar(`${powfn}(${this.table}${filter} { ${this.ref(field)} })`);
1149
+ acc[field] = await scalar(`${powfn}(${this.qt}${filter} { ${this.ref(field)} })`);
1120
1150
  }
1121
1151
  result[fn] = acc;
1122
1152
  }
@@ -1150,7 +1180,7 @@ class PowqlInterface {
1150
1180
  }
1151
1181
  const having = this.buildHaving(args.having, params);
1152
1182
  const order = this.buildOrder(args.orderBy);
1153
- const powql = `${this.table}${filter} group ${groupKeys.join(', ')}${having}${order} { ${proj.join(', ')} }`;
1183
+ const powql = `${this.qt}${filter} group ${groupKeys.join(', ')}${having}${order} { ${proj.join(', ')} }`;
1154
1184
  const { rows } = await this.exec(powql, params, args.timeout);
1155
1185
  // Reshape: group keys → camel fields + coerced; aggregates → nested {_sum:{field}}.
1156
1186
  return rows.map((raw) => {