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