tina4-nodejs 3.13.85 → 3.13.87

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.
@@ -1181,7 +1181,7 @@ var init_sqlite = __esm({
1181
1181
  this.db.exec("ROLLBACK");
1182
1182
  throw e;
1183
1183
  }
1184
- return { totalAffected, lastInsertId: lastId };
1184
+ return { totalAffected, lastId };
1185
1185
  }
1186
1186
  query(sql, params) {
1187
1187
  const stmt = this.db.prepare(sql);
@@ -1207,13 +1207,13 @@ var init_sqlite = __esm({
1207
1207
  }
1208
1208
  insert(table2, data) {
1209
1209
  if (Array.isArray(data)) {
1210
- if (data.length === 0) return { success: true, rowsAffected: 0 };
1210
+ if (data.length === 0) return { success: true, affectedRows: 0 };
1211
1211
  const keys2 = Object.keys(data[0]);
1212
1212
  const placeholders2 = keys2.map(() => "?").join(", ");
1213
1213
  const sql2 = `INSERT INTO "${table2}" ("${keys2.join('", "')}") VALUES (${placeholders2})`;
1214
1214
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
1215
1215
  const result = this.executeMany(sql2, paramsList);
1216
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
1216
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
1217
1217
  }
1218
1218
  const keys = Object.keys(data);
1219
1219
  const placeholders = keys.map(() => "?").join(", ");
@@ -1222,9 +1222,9 @@ var init_sqlite = __esm({
1222
1222
  try {
1223
1223
  const result = this.db.prepare(sql).run(...toSqlParams(values));
1224
1224
  this._lastInsertId = result.lastInsertRowid;
1225
- return { success: true, rowsAffected: Number(result.changes), lastInsertId: result.lastInsertRowid };
1225
+ return { success: true, affectedRows: Number(result.changes), lastId: result.lastInsertRowid };
1226
1226
  } catch (e) {
1227
- return { success: false, rowsAffected: 0, error: e.message };
1227
+ return { success: false, affectedRows: 0, error: e.message };
1228
1228
  }
1229
1229
  }
1230
1230
  update(table2, data, filter, params) {
@@ -1234,9 +1234,9 @@ var init_sqlite = __esm({
1234
1234
  const values = [...Object.values(data), ...Object.values(filter)];
1235
1235
  try {
1236
1236
  const result = this.db.prepare(sql).run(...toSqlParams(values));
1237
- return { success: true, rowsAffected: Number(result.changes) };
1237
+ return { success: true, affectedRows: Number(result.changes) };
1238
1238
  } catch (e) {
1239
- return { success: false, rowsAffected: 0, error: e.message };
1239
+ return { success: false, affectedRows: 0, error: e.message };
1240
1240
  }
1241
1241
  }
1242
1242
  delete(table2, filter, params) {
@@ -1244,17 +1244,17 @@ var init_sqlite = __esm({
1244
1244
  let totalAffected = 0;
1245
1245
  for (const row of filter) {
1246
1246
  const result = this.delete(table2, row);
1247
- totalAffected += result.rowsAffected;
1247
+ totalAffected += result.affectedRows;
1248
1248
  }
1249
- return { success: true, rowsAffected: totalAffected };
1249
+ return { success: true, affectedRows: totalAffected };
1250
1250
  }
1251
1251
  if (typeof filter === "string") {
1252
1252
  const sql2 = filter ? `DELETE FROM "${table2}" WHERE ${filter}` : `DELETE FROM "${table2}"`;
1253
1253
  try {
1254
1254
  const result = this.db.prepare(sql2).run(...toSqlParams(params ?? []));
1255
- return { success: true, rowsAffected: Number(result.changes) };
1255
+ return { success: true, affectedRows: Number(result.changes) };
1256
1256
  } catch (e) {
1257
- return { success: false, rowsAffected: 0, error: e.message };
1257
+ return { success: false, affectedRows: 0, error: e.message };
1258
1258
  }
1259
1259
  }
1260
1260
  const whereClauses = Object.keys(filter).map((k) => `"${k}" = ?`).join(" AND ");
@@ -1262,9 +1262,9 @@ var init_sqlite = __esm({
1262
1262
  const values = Object.values(filter);
1263
1263
  try {
1264
1264
  const result = this.db.prepare(sql).run(...toSqlParams(values));
1265
- return { success: true, rowsAffected: Number(result.changes) };
1265
+ return { success: true, affectedRows: Number(result.changes) };
1266
1266
  } catch (e) {
1267
- return { success: false, rowsAffected: 0, error: e.message };
1267
+ return { success: false, affectedRows: 0, error: e.message };
1268
1268
  }
1269
1269
  }
1270
1270
  _inTransaction = false;
@@ -1505,7 +1505,7 @@ var init_postgres = __esm({
1505
1505
  }
1506
1506
  /**
1507
1507
  * Normalise an `id` column value (typed `unknown` because pg row values are
1508
- * `unknown`) into the shape `_lastInsertId` / `DatabaseResult.lastInsertId`
1508
+ * `unknown`) into the shape `_lastInsertId` / `DatabaseResult.lastId`
1509
1509
  * expect. At runtime PG returns numeric PKs as number/bigint (the int8/numeric
1510
1510
  * type parsers above coerce them to Number); a numeric string is coerced to a
1511
1511
  * number so the SERIAL path always returns the integer id.
@@ -1540,8 +1540,8 @@ var init_postgres = __esm({
1540
1540
  for (const params of paramsList) {
1541
1541
  const result = await this.executeAsync(sql, params);
1542
1542
  totalAffected++;
1543
- if (result && typeof result === "object" && "lastInsertId" in result) {
1544
- lastId = result.lastInsertId;
1543
+ if (result && typeof result === "object" && "lastId" in result) {
1544
+ lastId = result.lastId;
1545
1545
  }
1546
1546
  }
1547
1547
  if (owns) await this.commitAsync();
@@ -1554,7 +1554,7 @@ var init_postgres = __esm({
1554
1554
  }
1555
1555
  throw e;
1556
1556
  }
1557
- return { totalAffected, lastInsertId: lastId };
1557
+ return { totalAffected, lastId };
1558
1558
  }
1559
1559
  /** Async execute for real usage. */
1560
1560
  async executeAsync(sql, params) {
@@ -1602,17 +1602,17 @@ var init_postgres = __esm({
1602
1602
  async insertAsync(table2, data) {
1603
1603
  this.ensureConnected();
1604
1604
  if (Array.isArray(data)) {
1605
- if (data.length === 0) return { success: true, rowsAffected: 0 };
1605
+ if (data.length === 0) return { success: true, affectedRows: 0 };
1606
1606
  const keys2 = Object.keys(data[0]);
1607
1607
  const placeholders2 = keys2.map(() => "?").join(", ");
1608
1608
  const sql2 = `INSERT INTO "${table2}" ("${keys2.join('", "')}") VALUES (${placeholders2})`;
1609
1609
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
1610
1610
  try {
1611
1611
  const result = await this.executeManyAsync(sql2, paramsList);
1612
- if (result.lastInsertId !== void 0) this._lastInsertId = result.lastInsertId;
1613
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
1612
+ if (result.lastId !== void 0) this._lastInsertId = result.lastId;
1613
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
1614
1614
  } catch (e) {
1615
- return { success: false, rowsAffected: 0, error: e.message };
1615
+ return { success: false, affectedRows: 0, error: e.message };
1616
1616
  }
1617
1617
  }
1618
1618
  const keys = Object.keys(data);
@@ -1626,11 +1626,11 @@ var init_postgres = __esm({
1626
1626
  if (id !== null) this._lastInsertId = id;
1627
1627
  return {
1628
1628
  success: true,
1629
- rowsAffected: result.rowCount ?? 1,
1630
- lastInsertId: id ?? void 0
1629
+ affectedRows: result.rowCount ?? 1,
1630
+ lastId: id ?? void 0
1631
1631
  };
1632
1632
  } catch (e) {
1633
- return { success: false, rowsAffected: 0, error: e.message };
1633
+ return { success: false, affectedRows: 0, error: e.message };
1634
1634
  }
1635
1635
  }
1636
1636
  update(table2, data, filter, params) {
@@ -1647,9 +1647,9 @@ var init_postgres = __esm({
1647
1647
  const values = [...Object.values(data), ...Object.values(filter)];
1648
1648
  try {
1649
1649
  const result = await this.client.query(sql, values);
1650
- return { success: true, rowsAffected: result.rowCount ?? 0 };
1650
+ return { success: true, affectedRows: result.rowCount ?? 0 };
1651
1651
  } catch (e) {
1652
- return { success: false, rowsAffected: 0, error: e.message };
1652
+ return { success: false, affectedRows: 0, error: e.message };
1653
1653
  }
1654
1654
  }
1655
1655
  delete(table2, filter, params) {
@@ -1664,9 +1664,9 @@ var init_postgres = __esm({
1664
1664
  const values = Object.values(filter);
1665
1665
  try {
1666
1666
  const result = await this.client.query(sql, values);
1667
- return { success: true, rowsAffected: result.rowCount ?? 0 };
1667
+ return { success: true, affectedRows: result.rowCount ?? 0 };
1668
1668
  } catch (e) {
1669
- return { success: false, rowsAffected: 0, error: e.message };
1669
+ return { success: false, affectedRows: 0, error: e.message };
1670
1670
  }
1671
1671
  }
1672
1672
  startTransaction() {
@@ -1674,18 +1674,21 @@ var init_postgres = __esm({
1674
1674
  }
1675
1675
  async startTransactionAsync() {
1676
1676
  await this.executeAsync("BEGIN");
1677
+ this._inTransaction = true;
1677
1678
  }
1678
1679
  commit() {
1679
1680
  throw new Error("Use commitAsync() for PostgreSQL.");
1680
1681
  }
1681
1682
  async commitAsync() {
1682
1683
  await this.executeAsync("COMMIT");
1684
+ this._inTransaction = false;
1683
1685
  }
1684
1686
  rollback() {
1685
1687
  throw new Error("Use rollbackAsync() for PostgreSQL.");
1686
1688
  }
1687
1689
  async rollbackAsync() {
1688
1690
  await this.executeAsync("ROLLBACK");
1691
+ this._inTransaction = false;
1689
1692
  }
1690
1693
  tables() {
1691
1694
  throw new Error("Use tablesAsync() for PostgreSQL.");
@@ -1900,7 +1903,7 @@ var init_mysql = __esm({
1900
1903
  }
1901
1904
  throw e;
1902
1905
  }
1903
- return { totalAffected, lastInsertId: lastId };
1906
+ return { totalAffected, lastId };
1904
1907
  }
1905
1908
  async executeAsync(sql, params) {
1906
1909
  this.ensureConnected();
@@ -1946,17 +1949,17 @@ var init_mysql = __esm({
1946
1949
  async insertAsync(table2, data) {
1947
1950
  this.ensureConnected();
1948
1951
  if (Array.isArray(data)) {
1949
- if (data.length === 0) return { success: true, rowsAffected: 0 };
1952
+ if (data.length === 0) return { success: true, affectedRows: 0 };
1950
1953
  const keys2 = Object.keys(data[0]);
1951
1954
  const placeholders2 = keys2.map(() => "?").join(", ");
1952
1955
  const sql2 = `INSERT INTO \`${table2}\` (\`${keys2.join("`, `")}\`) VALUES (${placeholders2})`;
1953
1956
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
1954
1957
  try {
1955
1958
  const result = await this.executeManyAsync(sql2, paramsList);
1956
- if (result.lastInsertId !== void 0) this._lastInsertId = result.lastInsertId;
1957
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
1959
+ if (result.lastId !== void 0) this._lastInsertId = result.lastId;
1960
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
1958
1961
  } catch (e) {
1959
- return { success: false, rowsAffected: 0, error: e.message };
1962
+ return { success: false, affectedRows: 0, error: e.message };
1960
1963
  }
1961
1964
  }
1962
1965
  const keys = Object.keys(data);
@@ -1968,11 +1971,11 @@ var init_mysql = __esm({
1968
1971
  this._lastInsertId = result.insertId ?? null;
1969
1972
  return {
1970
1973
  success: true,
1971
- rowsAffected: result.affectedRows ?? 1,
1972
- lastInsertId: result.insertId
1974
+ affectedRows: result.affectedRows ?? 1,
1975
+ lastId: result.insertId
1973
1976
  };
1974
1977
  } catch (e) {
1975
- return { success: false, rowsAffected: 0, error: e.message };
1978
+ return { success: false, affectedRows: 0, error: e.message };
1976
1979
  }
1977
1980
  }
1978
1981
  update(table2, data, filter, params) {
@@ -1986,9 +1989,9 @@ var init_mysql = __esm({
1986
1989
  const values = [...Object.values(data), ...Object.values(filter)];
1987
1990
  try {
1988
1991
  const result = await this.queryPromise(sql, values);
1989
- return { success: true, rowsAffected: result.affectedRows ?? 0 };
1992
+ return { success: true, affectedRows: result.affectedRows ?? 0 };
1990
1993
  } catch (e) {
1991
- return { success: false, rowsAffected: 0, error: e.message };
1994
+ return { success: false, affectedRows: 0, error: e.message };
1992
1995
  }
1993
1996
  }
1994
1997
  delete(table2, filter, params) {
@@ -2001,9 +2004,9 @@ var init_mysql = __esm({
2001
2004
  const values = Object.values(filter);
2002
2005
  try {
2003
2006
  const result = await this.queryPromise(sql, values);
2004
- return { success: true, rowsAffected: result.affectedRows ?? 0 };
2007
+ return { success: true, affectedRows: result.affectedRows ?? 0 };
2005
2008
  } catch (e) {
2006
- return { success: false, rowsAffected: 0, error: e.message };
2009
+ return { success: false, affectedRows: 0, error: e.message };
2007
2010
  }
2008
2011
  }
2009
2012
  startTransaction() {
@@ -2344,17 +2347,17 @@ var init_mssql = __esm({
2344
2347
  async insertAsync(table2, data) {
2345
2348
  this.ensureConnected();
2346
2349
  if (Array.isArray(data)) {
2347
- if (data.length === 0) return { success: true, rowsAffected: 0 };
2350
+ if (data.length === 0) return { success: true, affectedRows: 0 };
2348
2351
  const keys2 = Object.keys(data[0]);
2349
2352
  const placeholders2 = keys2.map(() => "?").join(", ");
2350
2353
  const sql2 = `INSERT INTO [${table2}] ([${keys2.join("], [")}]) VALUES (${placeholders2})`;
2351
2354
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
2352
2355
  try {
2353
2356
  const result = await this.executeManyAsync(sql2, paramsList);
2354
- if (result.lastInsertId !== void 0) this._lastInsertId = result.lastInsertId;
2355
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
2357
+ if (result.lastId !== void 0) this._lastInsertId = result.lastId;
2358
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
2356
2359
  } catch (e) {
2357
- return { success: false, rowsAffected: 0, error: e.message };
2360
+ return { success: false, affectedRows: 0, error: e.message };
2358
2361
  }
2359
2362
  }
2360
2363
  const keys = Object.keys(data);
@@ -2370,12 +2373,12 @@ var init_mssql = __esm({
2370
2373
  // A single-object insert affects exactly one row. Do NOT use
2371
2374
  // result.rowCount here: the statement is "INSERT ...; SELECT
2372
2375
  // SCOPE_IDENTITY()", and tedious sums the row counts of BOTH statements
2373
- // (1 for the INSERT + 1 for the SELECT), which reported rowsAffected=2.
2374
- rowsAffected: 1,
2375
- lastInsertId: id ?? void 0
2376
+ // (1 for the INSERT + 1 for the SELECT), which reported affectedRows=2.
2377
+ affectedRows: 1,
2378
+ lastId: id ?? void 0
2376
2379
  };
2377
2380
  } catch (e) {
2378
- return { success: false, rowsAffected: 0, error: e.message };
2381
+ return { success: false, affectedRows: 0, error: e.message };
2379
2382
  }
2380
2383
  }
2381
2384
  update(table2, data, filter, params) {
@@ -2392,9 +2395,9 @@ var init_mssql = __esm({
2392
2395
  const values = [...Object.values(data), ...Object.values(filter)];
2393
2396
  try {
2394
2397
  const result = await this.execSqlPromise(sql, values);
2395
- return { success: true, rowsAffected: result.rowCount };
2398
+ return { success: true, affectedRows: result.rowCount };
2396
2399
  } catch (e) {
2397
- return { success: false, rowsAffected: 0, error: e.message };
2400
+ return { success: false, affectedRows: 0, error: e.message };
2398
2401
  }
2399
2402
  }
2400
2403
  delete(table2, filter, params) {
@@ -2409,9 +2412,9 @@ var init_mssql = __esm({
2409
2412
  const values = Object.values(filter);
2410
2413
  try {
2411
2414
  const result = await this.execSqlPromise(sql, values);
2412
- return { success: true, rowsAffected: result.rowCount };
2415
+ return { success: true, affectedRows: result.rowCount };
2413
2416
  } catch (e) {
2414
- return { success: false, rowsAffected: 0, error: e.message };
2417
+ return { success: false, affectedRows: 0, error: e.message };
2415
2418
  }
2416
2419
  }
2417
2420
  startTransaction() {
@@ -2704,10 +2707,35 @@ var init_firebird = __esm({
2704
2707
  translated = SQLTranslator.ilikeToLike(translated);
2705
2708
  return translated;
2706
2709
  }
2710
+ /**
2711
+ * The handle every statement runs on. While an explicit transaction is open
2712
+ * (startTransactionAsync set `this.transaction`), statements MUST run on that
2713
+ * transaction object so they are undone by rollbackAsync() / persisted by
2714
+ * commitAsync() — node-firebird's transaction exposes the same
2715
+ * query()/execute() as the connection. With no transaction open we run on
2716
+ * `this.db`, whose per-statement work auto-commits on the connection.
2717
+ *
2718
+ * This matches the Python master's contract (tina4_python/database/firebird.py):
2719
+ * there, ALL statements run on the single connection and start_transaction()
2720
+ * merely suppresses the per-statement autocommit in execute() so the batch
2721
+ * stays open until commit()/rollback(). node-firebird has no such suppression
2722
+ * hook — its `db.query/execute` always auto-commit — so the equivalent is to
2723
+ * route statements through the transaction object instead. Same observable
2724
+ * behaviour: an open transaction is atomic and rolls back cleanly.
2725
+ *
2726
+ * Previously every statement ran on `this.db` unconditionally, so the
2727
+ * transaction created by startTransactionAsync() never saw a single statement
2728
+ * — rollbackAsync() rolled back an EMPTY transaction and the already
2729
+ * auto-committed write survived (silent no-op). Twin of the PHP pdo_firebird
2730
+ * bug fixed in 3.13.86.
2731
+ */
2732
+ statementHandle() {
2733
+ return this.transaction ?? this.db;
2734
+ }
2707
2735
  queryPromise(sql, params) {
2708
2736
  return new Promise((resolve21, reject) => {
2709
2737
  const translated = this.translateSql(sql);
2710
- this.db.query(translated, params ?? [], (err, result) => {
2738
+ this.statementHandle().query(translated, params ?? [], (err, result) => {
2711
2739
  if (err) reject(err);
2712
2740
  else resolve21(result ?? []);
2713
2741
  });
@@ -2716,7 +2744,7 @@ var init_firebird = __esm({
2716
2744
  executePromise(sql, params) {
2717
2745
  return new Promise((resolve21, reject) => {
2718
2746
  const translated = this.translateSql(sql);
2719
- this.db.execute(translated, params ?? [], (err) => {
2747
+ this.statementHandle().execute(translated, params ?? [], (err) => {
2720
2748
  if (err) reject(err);
2721
2749
  else resolve21();
2722
2750
  });
@@ -2780,16 +2808,16 @@ var init_firebird = __esm({
2780
2808
  async insertAsync(table2, data) {
2781
2809
  this.ensureConnected();
2782
2810
  if (Array.isArray(data)) {
2783
- if (data.length === 0) return { success: true, rowsAffected: 0 };
2811
+ if (data.length === 0) return { success: true, affectedRows: 0 };
2784
2812
  const keys2 = Object.keys(data[0]);
2785
2813
  const placeholders2 = keys2.map(() => "?").join(", ");
2786
2814
  const sql2 = `INSERT INTO "${table2}" ("${keys2.join('", "')}") VALUES (${placeholders2})`;
2787
2815
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
2788
2816
  try {
2789
2817
  const result = await this.executeManyAsync(sql2, paramsList);
2790
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
2818
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
2791
2819
  } catch (e) {
2792
- return { success: false, rowsAffected: 0, error: e.message };
2820
+ return { success: false, affectedRows: 0, error: e.message };
2793
2821
  }
2794
2822
  }
2795
2823
  const keys = Object.keys(data);
@@ -2800,10 +2828,10 @@ var init_firebird = __esm({
2800
2828
  await this.executePromise(sql, values);
2801
2829
  return {
2802
2830
  success: true,
2803
- rowsAffected: 1
2831
+ affectedRows: 1
2804
2832
  };
2805
2833
  } catch (e) {
2806
- return { success: false, rowsAffected: 0, error: e.message };
2834
+ return { success: false, affectedRows: 0, error: e.message };
2807
2835
  }
2808
2836
  }
2809
2837
  update(table2, data, filter, params) {
@@ -2817,9 +2845,9 @@ var init_firebird = __esm({
2817
2845
  const values = [...Object.values(data), ...Object.values(filter)];
2818
2846
  try {
2819
2847
  await this.executePromise(sql, values);
2820
- return { success: true, rowsAffected: 1 };
2848
+ return { success: true, affectedRows: 1 };
2821
2849
  } catch (e) {
2822
- return { success: false, rowsAffected: 0, error: e.message };
2850
+ return { success: false, affectedRows: 0, error: e.message };
2823
2851
  }
2824
2852
  }
2825
2853
  delete(table2, filter, params) {
@@ -2832,9 +2860,9 @@ var init_firebird = __esm({
2832
2860
  const values = Object.values(filter);
2833
2861
  try {
2834
2862
  await this.executePromise(sql, values);
2835
- return { success: true, rowsAffected: 1 };
2863
+ return { success: true, affectedRows: 1 };
2836
2864
  } catch (e) {
2837
- return { success: false, rowsAffected: 0, error: e.message };
2865
+ return { success: false, affectedRows: 0, error: e.message };
2838
2866
  }
2839
2867
  }
2840
2868
  startTransaction() {
@@ -3330,14 +3358,14 @@ var init_mongodb = __esm({
3330
3358
  const col = this.db.collection(table2);
3331
3359
  try {
3332
3360
  if (Array.isArray(data)) {
3333
- if (data.length === 0) return { success: true, rowsAffected: 0 };
3361
+ if (data.length === 0) return { success: true, affectedRows: 0 };
3334
3362
  const result2 = await col.insertMany(data, { session: this.session });
3335
- return { success: true, rowsAffected: result2.insertedCount };
3363
+ return { success: true, affectedRows: result2.insertedCount };
3336
3364
  }
3337
3365
  const result = await col.insertOne(data, { session: this.session });
3338
- return { success: true, rowsAffected: 1, lastInsertId: void 0 };
3366
+ return { success: true, affectedRows: 1, lastId: void 0 };
3339
3367
  } catch (e) {
3340
- return { success: false, rowsAffected: 0, error: e.message };
3368
+ return { success: false, affectedRows: 0, error: e.message };
3341
3369
  }
3342
3370
  }
3343
3371
  update(table2, data, filter) {
@@ -3348,9 +3376,9 @@ var init_mongodb = __esm({
3348
3376
  const col = this.db.collection(table2);
3349
3377
  try {
3350
3378
  const result = await col.updateMany(filter, { $set: data }, { session: this.session });
3351
- return { success: true, rowsAffected: result.modifiedCount };
3379
+ return { success: true, affectedRows: result.modifiedCount };
3352
3380
  } catch (e) {
3353
- return { success: false, rowsAffected: 0, error: e.message };
3381
+ return { success: false, affectedRows: 0, error: e.message };
3354
3382
  }
3355
3383
  }
3356
3384
  delete(table2, filter) {
@@ -3366,21 +3394,21 @@ var init_mongodb = __esm({
3366
3394
  const r = await col.deleteMany(f, { session: this.session });
3367
3395
  total += r.deletedCount;
3368
3396
  }
3369
- return { success: true, rowsAffected: total };
3397
+ return { success: true, affectedRows: total };
3370
3398
  }
3371
3399
  if (typeof filter === "string") {
3372
3400
  if (!filter.trim()) {
3373
3401
  const r2 = await col.deleteMany({}, { session: this.session });
3374
- return { success: true, rowsAffected: r2.deletedCount };
3402
+ return { success: true, affectedRows: r2.deletedCount };
3375
3403
  }
3376
3404
  const { filter: parsedFilter } = parseWhereClause(filter, []);
3377
3405
  const r = await col.deleteMany(parsedFilter, { session: this.session });
3378
- return { success: true, rowsAffected: r.deletedCount };
3406
+ return { success: true, affectedRows: r.deletedCount };
3379
3407
  }
3380
3408
  const result = await col.deleteMany(filter, { session: this.session });
3381
- return { success: true, rowsAffected: result.deletedCount };
3409
+ return { success: true, affectedRows: result.deletedCount };
3382
3410
  } catch (e) {
3383
- return { success: false, rowsAffected: 0, error: e.message };
3411
+ return { success: false, affectedRows: 0, error: e.message };
3384
3412
  }
3385
3413
  }
3386
3414
  startTransaction() {
@@ -3640,8 +3668,8 @@ var init_odbc = __esm({
3640
3668
  async executeAsync(sql, params) {
3641
3669
  this.ensureConnected();
3642
3670
  const result = await this.connection.query(sql, params ?? []);
3643
- if (result && typeof result === "object" && "lastInsertId" in result) {
3644
- this._lastInsertId = result.lastInsertId;
3671
+ if (result && typeof result === "object" && "lastId" in result) {
3672
+ this._lastInsertId = result.lastId;
3645
3673
  }
3646
3674
  return result;
3647
3675
  }
@@ -3662,7 +3690,7 @@ var init_odbc = __esm({
3662
3690
  throw e;
3663
3691
  }
3664
3692
  if (lastId !== void 0) this._lastInsertId = lastId;
3665
- return { totalAffected, lastInsertId: lastId };
3693
+ return { totalAffected, lastId };
3666
3694
  }
3667
3695
  /** Run a SELECT and return all matching rows. */
3668
3696
  async queryAsync(sql, params) {
@@ -3698,9 +3726,9 @@ var init_odbc = __esm({
3698
3726
  const values = Object.values(data);
3699
3727
  try {
3700
3728
  await this.connection.query(sql, values);
3701
- return { success: true, rowsAffected: 1, lastInsertId: this._lastInsertId ?? void 0 };
3729
+ return { success: true, affectedRows: 1, lastId: this._lastInsertId ?? void 0 };
3702
3730
  } catch (e) {
3703
- return { success: false, rowsAffected: 0, error: e.message };
3731
+ return { success: false, affectedRows: 0, error: e.message };
3704
3732
  }
3705
3733
  }
3706
3734
  /** Update rows in a table matching filter. */
@@ -3712,9 +3740,9 @@ var init_odbc = __esm({
3712
3740
  const values = [...Object.values(data), ...Object.values(filter)];
3713
3741
  try {
3714
3742
  await this.connection.query(sql, values);
3715
- return { success: true, rowsAffected: 1 };
3743
+ return { success: true, affectedRows: 1 };
3716
3744
  } catch (e) {
3717
- return { success: false, rowsAffected: 0, error: e.message };
3745
+ return { success: false, affectedRows: 0, error: e.message };
3718
3746
  }
3719
3747
  }
3720
3748
  /** Delete rows from a table. */
@@ -3724,17 +3752,17 @@ var init_odbc = __esm({
3724
3752
  let totalAffected = 0;
3725
3753
  for (const row of filter) {
3726
3754
  const result = await this.deleteAsync(table2, row);
3727
- totalAffected += result.rowsAffected;
3755
+ totalAffected += result.affectedRows;
3728
3756
  }
3729
- return { success: true, rowsAffected: totalAffected };
3757
+ return { success: true, affectedRows: totalAffected };
3730
3758
  }
3731
3759
  if (typeof filter === "string") {
3732
3760
  const sql2 = filter ? `DELETE FROM "${table2}" WHERE ${filter}` : `DELETE FROM "${table2}"`;
3733
3761
  try {
3734
3762
  await this.connection.query(sql2, []);
3735
- return { success: true, rowsAffected: 1 };
3763
+ return { success: true, affectedRows: 1 };
3736
3764
  } catch (e) {
3737
- return { success: false, rowsAffected: 0, error: e.message };
3765
+ return { success: false, affectedRows: 0, error: e.message };
3738
3766
  }
3739
3767
  }
3740
3768
  const whereClauses = Object.keys(filter).map((k) => `"${k}" = ?`).join(" AND ");
@@ -3742,9 +3770,9 @@ var init_odbc = __esm({
3742
3770
  const values = Object.values(filter);
3743
3771
  try {
3744
3772
  await this.connection.query(sql, values);
3745
- return { success: true, rowsAffected: 1 };
3773
+ return { success: true, affectedRows: 1 };
3746
3774
  } catch (e) {
3747
- return { success: false, rowsAffected: 0, error: e.message };
3775
+ return { success: false, affectedRows: 0, error: e.message };
3748
3776
  }
3749
3777
  }
3750
3778
  /** Begin a transaction. */
@@ -3924,7 +3952,7 @@ function extractLastInsertId(result) {
3924
3952
  const r = result;
3925
3953
  if (r.lastInsertRowid !== void 0 && r.lastInsertRowid !== null) return r.lastInsertRowid;
3926
3954
  if (r.rows?.[0]?.id !== void 0 && r.rows[0].id !== null) return r.rows[0].id;
3927
- if (r.lastInsertId !== void 0 && r.lastInsertId !== null) return r.lastInsertId;
3955
+ if (r.lastId !== void 0 && r.lastId !== null) return r.lastId;
3928
3956
  }
3929
3957
  return null;
3930
3958
  }
@@ -4690,14 +4718,15 @@ var init_database = __esm({
4690
4718
  async executeMany(sql, paramSets = []) {
4691
4719
  const adapter = this.getNextAdapter();
4692
4720
  const results = [];
4693
- await adapterStartTransaction(adapter);
4721
+ const owns = !this.inExplicitTransaction();
4722
+ if (owns) await adapterStartTransaction(adapter);
4694
4723
  try {
4695
4724
  for (const params of paramSets) {
4696
4725
  results.push(await adapterExecute(adapter, sql, params));
4697
4726
  }
4698
- await adapterCommit(adapter);
4727
+ if (owns) await adapterCommit(adapter);
4699
4728
  } catch (e) {
4700
- await adapterRollback(adapter);
4729
+ if (owns) await adapterRollback(adapter);
4701
4730
  throw e;
4702
4731
  }
4703
4732
  return results;
@@ -12750,6 +12779,7 @@ var init_request = __esm({
12750
12779
  var engine_exports = {};
12751
12780
  __export(engine_exports, {
12752
12781
  Frond: () => Frond,
12782
+ TEMPLATE_CACHE_MAX: () => TEMPLATE_CACHE_MAX,
12753
12783
  setFormTokenSessionId: () => setFormTokenSessionId
12754
12784
  });
12755
12785
  import { createHash as createHash2, createHmac as createHmac3, randomBytes as randomBytes4 } from "node:crypto";
@@ -12825,6 +12855,14 @@ function renderDump(value) {
12825
12855
  function liveAttr(value) {
12826
12856
  return String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
12827
12857
  }
12858
+ function capCache(cache, maxEntries) {
12859
+ if (cache.size < maxEntries) return;
12860
+ let drop = Math.floor(maxEntries / 2);
12861
+ for (const key of cache.keys()) {
12862
+ cache.delete(key);
12863
+ if (--drop <= 0) break;
12864
+ }
12865
+ }
12828
12866
  function tokenize(source) {
12829
12867
  const rawBlocks = [];
12830
12868
  source = source.replace(RAW_BLOCK_RE, (_match, content) => {
@@ -13038,11 +13076,14 @@ function resolveVar(expr, context) {
13038
13076
  return value;
13039
13077
  }
13040
13078
  function findOutsideQuotes(expr, needle) {
13079
+ if (!expr.includes(needle)) return -1;
13041
13080
  let inQuote = null;
13042
13081
  let depth = 0;
13043
13082
  let bracketDepth = 0;
13044
13083
  let i = 0;
13045
- while (i <= expr.length - needle.length) {
13084
+ const needleLen = needle.length;
13085
+ const lastStart = expr.length - needleLen;
13086
+ while (i <= lastStart) {
13046
13087
  const ch = expr[i];
13047
13088
  if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
13048
13089
  if (inQuote === null) {
@@ -13061,7 +13102,7 @@ function findOutsideQuotes(expr, needle) {
13061
13102
  else if (ch === ")") depth--;
13062
13103
  else if (ch === "[") bracketDepth++;
13063
13104
  else if (ch === "]") bracketDepth--;
13064
- if (depth === 0 && bracketDepth === 0 && expr.slice(i, i + needle.length) === needle) {
13105
+ if (depth === 0 && bracketDepth === 0 && expr.startsWith(needle, i)) {
13065
13106
  return i;
13066
13107
  }
13067
13108
  i++;
@@ -13069,13 +13110,16 @@ function findOutsideQuotes(expr, needle) {
13069
13110
  return -1;
13070
13111
  }
13071
13112
  function splitOutsideQuotes(expr, sep5) {
13113
+ if (!expr.includes(sep5)) return [expr];
13072
13114
  const parts = [];
13073
13115
  let currentStart = 0;
13074
13116
  let inQuote = null;
13075
13117
  let depth = 0;
13076
13118
  let bracketDepth = 0;
13077
13119
  let i = 0;
13078
- while (i <= expr.length - sep5.length) {
13120
+ const sepLen = sep5.length;
13121
+ const lastStart = expr.length - sepLen;
13122
+ while (i <= lastStart) {
13079
13123
  const ch = expr[i];
13080
13124
  if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
13081
13125
  if (inQuote === null) {
@@ -13094,9 +13138,9 @@ function splitOutsideQuotes(expr, sep5) {
13094
13138
  else if (ch === ")") depth--;
13095
13139
  else if (ch === "[") bracketDepth++;
13096
13140
  else if (ch === "]") bracketDepth--;
13097
- if (depth === 0 && bracketDepth === 0 && expr.slice(i, i + sep5.length) === sep5) {
13141
+ if (depth === 0 && bracketDepth === 0 && expr.startsWith(sep5, i)) {
13098
13142
  parts.push(expr.slice(currentStart, i));
13099
- i += sep5.length;
13143
+ i += sepLen;
13100
13144
  currentStart = i;
13101
13145
  continue;
13102
13146
  }
@@ -13170,6 +13214,9 @@ function evalExpr(expr, context) {
13170
13214
  }).join("");
13171
13215
  }
13172
13216
  }
13217
+ if (expr.startsWith("not ")) {
13218
+ return evalComparison(expr, context);
13219
+ }
13173
13220
  for (const op of [" not in ", " in ", " is not ", " is ", "!=", "==", ">=", "<=", ">", "<", " and ", " or ", " not "]) {
13174
13221
  if (findOutsideQuotes(expr, op) >= 0) {
13175
13222
  return evalComparison(expr, context);
@@ -13725,7 +13772,7 @@ function _generateFormToken(descriptor = "") {
13725
13772
  function _generateFormTokenValue(descriptor = "") {
13726
13773
  return new SafeString(_buildFormTokenJwt(descriptor));
13727
13774
  }
13728
- var SafeString, NUMERIC_RE, METHOD_CALL_RE, FN_CALL_RE, IS_NOT_RE, IS_RE, NOT_IN_RE, IN_RE, DIVISIBLE_BY_RE, FILTER_WITH_ARGS_RE, FILTER_COMPARISON_RE, TITLE_WORD_RE, STRIP_TAGS_RE, FORMAT_RE, LEADING_WS_RE, TRAILING_WS_RE, THOUSANDS_RE, LIVE_RE, LIVE_WS_RE, LIVE_SRC_RE, filterChainCache, pathParseCache, TOKEN_RE, RAW_BLOCK_RE, VarRef, BUILTIN_FILTERS, _formTokenSessionId, Frond;
13775
+ var SafeString, NUMERIC_RE, METHOD_CALL_RE, FN_CALL_RE, IS_NOT_RE, IS_RE, NOT_IN_RE, IN_RE, DIVISIBLE_BY_RE, FILTER_WITH_ARGS_RE, FILTER_COMPARISON_RE, TITLE_WORD_RE, STRIP_TAGS_RE, FORMAT_RE, LEADING_WS_RE, TRAILING_WS_RE, THOUSANDS_RE, LIVE_RE, LIVE_WS_RE, LIVE_SRC_RE, filterChainCache, pathParseCache, TEMPLATE_CACHE_MAX, TOKEN_RE, RAW_BLOCK_RE, VarRef, BUILTIN_FILTERS, _formTokenSessionId, Frond;
13729
13776
  var init_engine = __esm({
13730
13777
  "../frond/src/engine.ts"() {
13731
13778
  "use strict";
@@ -13758,6 +13805,7 @@ var init_engine = __esm({
13758
13805
  LIVE_SRC_RE = /src\s+["']([^"']+)["']/;
13759
13806
  filterChainCache = /* @__PURE__ */ new Map();
13760
13807
  pathParseCache = /* @__PURE__ */ new Map();
13808
+ TEMPLATE_CACHE_MAX = 256;
13761
13809
  TOKEN_RE = /(\{%-?\s*[\s\S]*?\s*-?%\})|(\{\{-?\s*[\s\S]*?\s*-?\}\})|(\{#[\s\S]*?#\})/g;
13762
13810
  RAW_BLOCK_RE = /\{%-?\s*raw\s*-?%\}([\s\S]*?)\{%-?\s*endraw\s*-?%\}/g;
13763
13811
  VarRef = class {
@@ -14163,6 +14211,7 @@ var init_engine = __esm({
14163
14211
  const source = readFileSync6(filePath, "utf-8");
14164
14212
  const mtime = statSync6(filePath).mtimeMs;
14165
14213
  const tokens = tokenize(source);
14214
+ capCache(this.compiled, TEMPLATE_CACHE_MAX);
14166
14215
  this.compiled.set(template, { tokens, mtime, cachedAt: Date.now() });
14167
14216
  return this.executeWithSource(source, tokens, context);
14168
14217
  }
@@ -14177,6 +14226,7 @@ var init_engine = __esm({
14177
14226
  }
14178
14227
  }
14179
14228
  const tokens = tokenize(source);
14229
+ capCache(this.compiledStrings, TEMPLATE_CACHE_MAX);
14180
14230
  this.compiledStrings.set(key, { tokens, cachedAt: Date.now() });
14181
14231
  return this.executeCached(tokens, context);
14182
14232
  }
@@ -14392,6 +14442,9 @@ var init_engine = __esm({
14392
14442
  } else if (tag === "macro") {
14393
14443
  const skip = this.handleMacro(tokens, i, context);
14394
14444
  i = skip;
14445
+ } else if (tag === "import") {
14446
+ this.handleImportAs(content, context);
14447
+ i++;
14395
14448
  } else if (tag === "from") {
14396
14449
  this.handleFromImport(content, context);
14397
14450
  i++;
@@ -14958,7 +15011,7 @@ var init_engine = __esm({
14958
15011
  return i2;
14959
15012
  }
14960
15013
  const macroName = m[1];
14961
- const paramNames = m[2].split(",").map((p) => p.trim()).filter(Boolean);
15014
+ const params = _Frond.parseMacroParams(m[2]);
14962
15015
  const bodyTokens = [];
14963
15016
  let i = start2 + 1;
14964
15017
  while (i < tokens.length) {
@@ -14973,13 +15026,94 @@ var init_engine = __esm({
14973
15026
  const capturedContext = { ...context };
14974
15027
  context[macroName] = (...args) => {
14975
15028
  const macroCtx = { ...capturedContext };
14976
- for (let pi = 0; pi < paramNames.length; pi++) {
14977
- macroCtx[paramNames[pi]] = pi < args.length ? args[pi] : null;
15029
+ for (let pi = 0; pi < params.length; pi++) {
15030
+ const [pname, pdefault] = params[pi];
15031
+ macroCtx[pname] = pi < args.length ? args[pi] : pdefault;
14978
15032
  }
14979
15033
  return new SafeString(engine.renderTokens([...bodyTokens], macroCtx));
14980
15034
  };
14981
15035
  return i;
14982
15036
  }
15037
+ /**
15038
+ * Parse a macro parameter list into [name, default] pairs.
15039
+ *
15040
+ * Handles: name, name="default", name='default'. Splitting on "," alone left a
15041
+ * defaulted parameter literally NAMED `greeting='Hello'`, so the body's
15042
+ * {{ greeting }} matched nothing (rendered empty) AND the caller's positional
15043
+ * argument was stored under that junk key and lost. Mirrors the Python master's
15044
+ * _parse_macro_params. The default is null when none is declared.
15045
+ */
15046
+ static parseMacroParams(rawParams) {
15047
+ return rawParams.split(",").map((p) => p.trim()).filter(Boolean).map((p) => {
15048
+ const eq = p.indexOf("=");
15049
+ if (eq === -1) return [p, null];
15050
+ const name = p.slice(0, eq).trim();
15051
+ let dflt = p.slice(eq + 1).trim();
15052
+ if (dflt.length >= 2 && (dflt.startsWith('"') && dflt.endsWith('"') || dflt.startsWith("'") && dflt.endsWith("'"))) {
15053
+ dflt = dflt.slice(1, -1);
15054
+ }
15055
+ return [name, dflt];
15056
+ });
15057
+ }
15058
+ /**
15059
+ * {% import "file" as alias %} -- load EVERY macro in a file under one namespace.
15060
+ *
15061
+ * The alias is bound as a plain object of macro functions, so {{ alias.greet(x) }}
15062
+ * resolves through the engine's existing dotted-call path and each macro keeps the
15063
+ * same argument binding, default handling and SafeString output as any other macro.
15064
+ * A namespace object (not a class) is deliberate: a function stored as a class
15065
+ * attribute binds as a method and would inject the namespace as the first argument,
15066
+ * which is exactly the argument-shift bug the Python master carried (fixed there
15067
+ * with types.SimpleNamespace). Both import forms must render identically.
15068
+ */
15069
+ handleImportAs(content, context) {
15070
+ const m = content.match(/^import\s+["'](.+?)["']\s+as\s+(\w+)/);
15071
+ if (!m) return;
15072
+ const filename = m[1];
15073
+ const alias = m[2];
15074
+ const namespace = {};
15075
+ const source = this.load(filename);
15076
+ const tokens = tokenize(source);
15077
+ let i = 0;
15078
+ while (i < tokens.length) {
15079
+ const [ttype, raw] = tokens[i];
15080
+ if (ttype === "BLOCK") {
15081
+ const [tagContent] = stripTag(raw);
15082
+ if ((tagContent.split(/\s+/)[0] || "") === "macro") {
15083
+ const macroM = tagContent.match(/^macro\s+(\w+)\s*\(([^)]*)\)/);
15084
+ if (macroM) {
15085
+ const macroName = macroM[1];
15086
+ const params = _Frond.parseMacroParams(macroM[2]);
15087
+ const bodyTokens = [];
15088
+ i++;
15089
+ while (i < tokens.length) {
15090
+ if (tokens[i][0] === "BLOCK" && tokens[i][1].includes("endmacro")) {
15091
+ i++;
15092
+ break;
15093
+ }
15094
+ bodyTokens.push(tokens[i]);
15095
+ i++;
15096
+ }
15097
+ const capturedBody = [...bodyTokens];
15098
+ const capturedParams = [...params];
15099
+ const capturedCtx = { ...context };
15100
+ const engine = this;
15101
+ namespace[macroName] = (...args) => {
15102
+ const macroCtx = { ...capturedCtx };
15103
+ for (let pi = 0; pi < capturedParams.length; pi++) {
15104
+ const [pname, pdefault] = capturedParams[pi];
15105
+ macroCtx[pname] = pi < args.length ? args[pi] : pdefault;
15106
+ }
15107
+ return new SafeString(engine.renderTokens([...capturedBody], macroCtx));
15108
+ };
15109
+ continue;
15110
+ }
15111
+ }
15112
+ }
15113
+ i++;
15114
+ }
15115
+ context[alias] = namespace;
15116
+ }
14983
15117
  handleFromImport(content, context) {
14984
15118
  const m = content.match(/^from\s+["'](.+?)["']\s+import\s+(.+)/);
14985
15119
  if (!m) return;
@@ -14997,7 +15131,7 @@ var init_engine = __esm({
14997
15131
  const macroM = tagContent.match(/^macro\s+(\w+)\s*\(([^)]*)\)/);
14998
15132
  if (macroM && names.includes(macroM[1])) {
14999
15133
  const macroName = macroM[1];
15000
- const paramNames = macroM[2].split(",").map((p) => p.trim()).filter(Boolean);
15134
+ const paramNames = _Frond.parseMacroParams(macroM[2]);
15001
15135
  const bodyTokens = [];
15002
15136
  i++;
15003
15137
  while (i < tokens.length) {
@@ -15015,7 +15149,8 @@ var init_engine = __esm({
15015
15149
  context[macroName] = (...args) => {
15016
15150
  const macroCtx = { ...capturedCtx };
15017
15151
  for (let pi = 0; pi < capturedParams.length; pi++) {
15018
- macroCtx[capturedParams[pi]] = pi < args.length ? args[pi] : null;
15152
+ const [pname, pdefault] = capturedParams[pi];
15153
+ macroCtx[pname] = pi < args.length ? args[pi] : pdefault;
15019
15154
  }
15020
15155
  return new SafeString(engine.renderTokens([...capturedBody], macroCtx));
15021
15156
  };
@@ -18122,7 +18257,14 @@ function fullAnalysis(root = "src") {
18122
18257
  total_functions: allFunctions.length,
18123
18258
  avg_complexity: Math.round(avgCC * 100) / 100,
18124
18259
  avg_maintainability: Math.round(avgMI * 10) / 10,
18260
+ // Display-only: the top-15 for the "most complex functions" report.
18261
+ // Do NOT source offenders / --fail-on from this — capping here silently
18262
+ // hides the 16th+ over-threshold function from the gate. offenders()
18263
+ // reads "all_functions" (below) instead.
18125
18264
  most_complex_functions: allFunctions.slice(0, 15),
18265
+ // Full, uncapped, complexity-sorted list — offenders()/--fail-on use this
18266
+ // so no function over the complexity threshold ever escapes the gate.
18267
+ all_functions: allFunctions,
18126
18268
  file_metrics: fileMetrics,
18127
18269
  violations,
18128
18270
  dependency_graph: importGraph,