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.
@@ -3415,6 +3415,7 @@ var init_request = __esm({
3415
3415
  var engine_exports = {};
3416
3416
  __export(engine_exports, {
3417
3417
  Frond: () => Frond,
3418
+ TEMPLATE_CACHE_MAX: () => TEMPLATE_CACHE_MAX,
3418
3419
  setFormTokenSessionId: () => setFormTokenSessionId
3419
3420
  });
3420
3421
  import { createHash as createHash2, createHmac as createHmac2, randomBytes as randomBytes2 } from "node:crypto";
@@ -3490,6 +3491,14 @@ function renderDump(value) {
3490
3491
  function liveAttr(value) {
3491
3492
  return String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3492
3493
  }
3494
+ function capCache(cache, maxEntries) {
3495
+ if (cache.size < maxEntries) return;
3496
+ let drop = Math.floor(maxEntries / 2);
3497
+ for (const key of cache.keys()) {
3498
+ cache.delete(key);
3499
+ if (--drop <= 0) break;
3500
+ }
3501
+ }
3493
3502
  function tokenize(source) {
3494
3503
  const rawBlocks = [];
3495
3504
  source = source.replace(RAW_BLOCK_RE, (_match, content) => {
@@ -3703,11 +3712,14 @@ function resolveVar(expr, context) {
3703
3712
  return value;
3704
3713
  }
3705
3714
  function findOutsideQuotes(expr, needle) {
3715
+ if (!expr.includes(needle)) return -1;
3706
3716
  let inQuote = null;
3707
3717
  let depth = 0;
3708
3718
  let bracketDepth = 0;
3709
3719
  let i = 0;
3710
- while (i <= expr.length - needle.length) {
3720
+ const needleLen = needle.length;
3721
+ const lastStart = expr.length - needleLen;
3722
+ while (i <= lastStart) {
3711
3723
  const ch = expr[i];
3712
3724
  if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
3713
3725
  if (inQuote === null) {
@@ -3726,7 +3738,7 @@ function findOutsideQuotes(expr, needle) {
3726
3738
  else if (ch === ")") depth--;
3727
3739
  else if (ch === "[") bracketDepth++;
3728
3740
  else if (ch === "]") bracketDepth--;
3729
- if (depth === 0 && bracketDepth === 0 && expr.slice(i, i + needle.length) === needle) {
3741
+ if (depth === 0 && bracketDepth === 0 && expr.startsWith(needle, i)) {
3730
3742
  return i;
3731
3743
  }
3732
3744
  i++;
@@ -3734,13 +3746,16 @@ function findOutsideQuotes(expr, needle) {
3734
3746
  return -1;
3735
3747
  }
3736
3748
  function splitOutsideQuotes(expr, sep5) {
3749
+ if (!expr.includes(sep5)) return [expr];
3737
3750
  const parts = [];
3738
3751
  let currentStart = 0;
3739
3752
  let inQuote = null;
3740
3753
  let depth = 0;
3741
3754
  let bracketDepth = 0;
3742
3755
  let i = 0;
3743
- while (i <= expr.length - sep5.length) {
3756
+ const sepLen = sep5.length;
3757
+ const lastStart = expr.length - sepLen;
3758
+ while (i <= lastStart) {
3744
3759
  const ch = expr[i];
3745
3760
  if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
3746
3761
  if (inQuote === null) {
@@ -3759,9 +3774,9 @@ function splitOutsideQuotes(expr, sep5) {
3759
3774
  else if (ch === ")") depth--;
3760
3775
  else if (ch === "[") bracketDepth++;
3761
3776
  else if (ch === "]") bracketDepth--;
3762
- if (depth === 0 && bracketDepth === 0 && expr.slice(i, i + sep5.length) === sep5) {
3777
+ if (depth === 0 && bracketDepth === 0 && expr.startsWith(sep5, i)) {
3763
3778
  parts.push(expr.slice(currentStart, i));
3764
- i += sep5.length;
3779
+ i += sepLen;
3765
3780
  currentStart = i;
3766
3781
  continue;
3767
3782
  }
@@ -3835,6 +3850,9 @@ function evalExpr(expr, context) {
3835
3850
  }).join("");
3836
3851
  }
3837
3852
  }
3853
+ if (expr.startsWith("not ")) {
3854
+ return evalComparison(expr, context);
3855
+ }
3838
3856
  for (const op of [" not in ", " in ", " is not ", " is ", "!=", "==", ">=", "<=", ">", "<", " and ", " or ", " not "]) {
3839
3857
  if (findOutsideQuotes(expr, op) >= 0) {
3840
3858
  return evalComparison(expr, context);
@@ -4390,7 +4408,7 @@ function _generateFormToken(descriptor = "") {
4390
4408
  function _generateFormTokenValue(descriptor = "") {
4391
4409
  return new SafeString(_buildFormTokenJwt(descriptor));
4392
4410
  }
4393
- 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;
4411
+ 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;
4394
4412
  var init_engine = __esm({
4395
4413
  "../frond/src/engine.ts"() {
4396
4414
  "use strict";
@@ -4423,6 +4441,7 @@ var init_engine = __esm({
4423
4441
  LIVE_SRC_RE = /src\s+["']([^"']+)["']/;
4424
4442
  filterChainCache = /* @__PURE__ */ new Map();
4425
4443
  pathParseCache = /* @__PURE__ */ new Map();
4444
+ TEMPLATE_CACHE_MAX = 256;
4426
4445
  TOKEN_RE = /(\{%-?\s*[\s\S]*?\s*-?%\})|(\{\{-?\s*[\s\S]*?\s*-?\}\})|(\{#[\s\S]*?#\})/g;
4427
4446
  RAW_BLOCK_RE = /\{%-?\s*raw\s*-?%\}([\s\S]*?)\{%-?\s*endraw\s*-?%\}/g;
4428
4447
  VarRef = class {
@@ -4828,6 +4847,7 @@ var init_engine = __esm({
4828
4847
  const source = readFileSync4(filePath, "utf-8");
4829
4848
  const mtime = statSync4(filePath).mtimeMs;
4830
4849
  const tokens = tokenize(source);
4850
+ capCache(this.compiled, TEMPLATE_CACHE_MAX);
4831
4851
  this.compiled.set(template, { tokens, mtime, cachedAt: Date.now() });
4832
4852
  return this.executeWithSource(source, tokens, context);
4833
4853
  }
@@ -4842,6 +4862,7 @@ var init_engine = __esm({
4842
4862
  }
4843
4863
  }
4844
4864
  const tokens = tokenize(source);
4865
+ capCache(this.compiledStrings, TEMPLATE_CACHE_MAX);
4845
4866
  this.compiledStrings.set(key, { tokens, cachedAt: Date.now() });
4846
4867
  return this.executeCached(tokens, context);
4847
4868
  }
@@ -5057,6 +5078,9 @@ var init_engine = __esm({
5057
5078
  } else if (tag === "macro") {
5058
5079
  const skip = this.handleMacro(tokens, i, context);
5059
5080
  i = skip;
5081
+ } else if (tag === "import") {
5082
+ this.handleImportAs(content, context);
5083
+ i++;
5060
5084
  } else if (tag === "from") {
5061
5085
  this.handleFromImport(content, context);
5062
5086
  i++;
@@ -5623,7 +5647,7 @@ var init_engine = __esm({
5623
5647
  return i2;
5624
5648
  }
5625
5649
  const macroName = m[1];
5626
- const paramNames = m[2].split(",").map((p) => p.trim()).filter(Boolean);
5650
+ const params = _Frond.parseMacroParams(m[2]);
5627
5651
  const bodyTokens = [];
5628
5652
  let i = start2 + 1;
5629
5653
  while (i < tokens.length) {
@@ -5638,13 +5662,94 @@ var init_engine = __esm({
5638
5662
  const capturedContext = { ...context };
5639
5663
  context[macroName] = (...args) => {
5640
5664
  const macroCtx = { ...capturedContext };
5641
- for (let pi = 0; pi < paramNames.length; pi++) {
5642
- macroCtx[paramNames[pi]] = pi < args.length ? args[pi] : null;
5665
+ for (let pi = 0; pi < params.length; pi++) {
5666
+ const [pname, pdefault] = params[pi];
5667
+ macroCtx[pname] = pi < args.length ? args[pi] : pdefault;
5643
5668
  }
5644
5669
  return new SafeString(engine.renderTokens([...bodyTokens], macroCtx));
5645
5670
  };
5646
5671
  return i;
5647
5672
  }
5673
+ /**
5674
+ * Parse a macro parameter list into [name, default] pairs.
5675
+ *
5676
+ * Handles: name, name="default", name='default'. Splitting on "," alone left a
5677
+ * defaulted parameter literally NAMED `greeting='Hello'`, so the body's
5678
+ * {{ greeting }} matched nothing (rendered empty) AND the caller's positional
5679
+ * argument was stored under that junk key and lost. Mirrors the Python master's
5680
+ * _parse_macro_params. The default is null when none is declared.
5681
+ */
5682
+ static parseMacroParams(rawParams) {
5683
+ return rawParams.split(",").map((p) => p.trim()).filter(Boolean).map((p) => {
5684
+ const eq = p.indexOf("=");
5685
+ if (eq === -1) return [p, null];
5686
+ const name = p.slice(0, eq).trim();
5687
+ let dflt = p.slice(eq + 1).trim();
5688
+ if (dflt.length >= 2 && (dflt.startsWith('"') && dflt.endsWith('"') || dflt.startsWith("'") && dflt.endsWith("'"))) {
5689
+ dflt = dflt.slice(1, -1);
5690
+ }
5691
+ return [name, dflt];
5692
+ });
5693
+ }
5694
+ /**
5695
+ * {% import "file" as alias %} -- load EVERY macro in a file under one namespace.
5696
+ *
5697
+ * The alias is bound as a plain object of macro functions, so {{ alias.greet(x) }}
5698
+ * resolves through the engine's existing dotted-call path and each macro keeps the
5699
+ * same argument binding, default handling and SafeString output as any other macro.
5700
+ * A namespace object (not a class) is deliberate: a function stored as a class
5701
+ * attribute binds as a method and would inject the namespace as the first argument,
5702
+ * which is exactly the argument-shift bug the Python master carried (fixed there
5703
+ * with types.SimpleNamespace). Both import forms must render identically.
5704
+ */
5705
+ handleImportAs(content, context) {
5706
+ const m = content.match(/^import\s+["'](.+?)["']\s+as\s+(\w+)/);
5707
+ if (!m) return;
5708
+ const filename = m[1];
5709
+ const alias = m[2];
5710
+ const namespace = {};
5711
+ const source = this.load(filename);
5712
+ const tokens = tokenize(source);
5713
+ let i = 0;
5714
+ while (i < tokens.length) {
5715
+ const [ttype, raw] = tokens[i];
5716
+ if (ttype === "BLOCK") {
5717
+ const [tagContent] = stripTag(raw);
5718
+ if ((tagContent.split(/\s+/)[0] || "") === "macro") {
5719
+ const macroM = tagContent.match(/^macro\s+(\w+)\s*\(([^)]*)\)/);
5720
+ if (macroM) {
5721
+ const macroName = macroM[1];
5722
+ const params = _Frond.parseMacroParams(macroM[2]);
5723
+ const bodyTokens = [];
5724
+ i++;
5725
+ while (i < tokens.length) {
5726
+ if (tokens[i][0] === "BLOCK" && tokens[i][1].includes("endmacro")) {
5727
+ i++;
5728
+ break;
5729
+ }
5730
+ bodyTokens.push(tokens[i]);
5731
+ i++;
5732
+ }
5733
+ const capturedBody = [...bodyTokens];
5734
+ const capturedParams = [...params];
5735
+ const capturedCtx = { ...context };
5736
+ const engine = this;
5737
+ namespace[macroName] = (...args) => {
5738
+ const macroCtx = { ...capturedCtx };
5739
+ for (let pi = 0; pi < capturedParams.length; pi++) {
5740
+ const [pname, pdefault] = capturedParams[pi];
5741
+ macroCtx[pname] = pi < args.length ? args[pi] : pdefault;
5742
+ }
5743
+ return new SafeString(engine.renderTokens([...capturedBody], macroCtx));
5744
+ };
5745
+ continue;
5746
+ }
5747
+ }
5748
+ }
5749
+ i++;
5750
+ }
5751
+ context[alias] = namespace;
5752
+ }
5648
5753
  handleFromImport(content, context) {
5649
5754
  const m = content.match(/^from\s+["'](.+?)["']\s+import\s+(.+)/);
5650
5755
  if (!m) return;
@@ -5662,7 +5767,7 @@ var init_engine = __esm({
5662
5767
  const macroM = tagContent.match(/^macro\s+(\w+)\s*\(([^)]*)\)/);
5663
5768
  if (macroM && names.includes(macroM[1])) {
5664
5769
  const macroName = macroM[1];
5665
- const paramNames = macroM[2].split(",").map((p) => p.trim()).filter(Boolean);
5770
+ const paramNames = _Frond.parseMacroParams(macroM[2]);
5666
5771
  const bodyTokens = [];
5667
5772
  i++;
5668
5773
  while (i < tokens.length) {
@@ -5680,7 +5785,8 @@ var init_engine = __esm({
5680
5785
  context[macroName] = (...args) => {
5681
5786
  const macroCtx = { ...capturedCtx };
5682
5787
  for (let pi = 0; pi < capturedParams.length; pi++) {
5683
- macroCtx[capturedParams[pi]] = pi < args.length ? args[pi] : null;
5788
+ const [pname, pdefault] = capturedParams[pi];
5789
+ macroCtx[pname] = pi < args.length ? args[pi] : pdefault;
5684
5790
  }
5685
5791
  return new SafeString(engine.renderTokens([...capturedBody], macroCtx));
5686
5792
  };
@@ -8787,7 +8893,14 @@ function fullAnalysis(root = "src") {
8787
8893
  total_functions: allFunctions.length,
8788
8894
  avg_complexity: Math.round(avgCC * 100) / 100,
8789
8895
  avg_maintainability: Math.round(avgMI * 10) / 10,
8896
+ // Display-only: the top-15 for the "most complex functions" report.
8897
+ // Do NOT source offenders / --fail-on from this — capping here silently
8898
+ // hides the 16th+ over-threshold function from the gate. offenders()
8899
+ // reads "all_functions" (below) instead.
8790
8900
  most_complex_functions: allFunctions.slice(0, 15),
8901
+ // Full, uncapped, complexity-sorted list — offenders()/--fail-on use this
8902
+ // so no function over the complexity threshold ever escapes the gate.
8903
+ all_functions: allFunctions,
8791
8904
  file_metrics: fileMetrics,
8792
8905
  violations,
8793
8906
  dependency_graph: importGraph,
@@ -29454,7 +29567,7 @@ var init_sqlite = __esm({
29454
29567
  this.db.exec("ROLLBACK");
29455
29568
  throw e;
29456
29569
  }
29457
- return { totalAffected, lastInsertId: lastId };
29570
+ return { totalAffected, lastId };
29458
29571
  }
29459
29572
  query(sql, params) {
29460
29573
  const stmt = this.db.prepare(sql);
@@ -29480,13 +29593,13 @@ var init_sqlite = __esm({
29480
29593
  }
29481
29594
  insert(table2, data) {
29482
29595
  if (Array.isArray(data)) {
29483
- if (data.length === 0) return { success: true, rowsAffected: 0 };
29596
+ if (data.length === 0) return { success: true, affectedRows: 0 };
29484
29597
  const keys2 = Object.keys(data[0]);
29485
29598
  const placeholders2 = keys2.map(() => "?").join(", ");
29486
29599
  const sql2 = `INSERT INTO "${table2}" ("${keys2.join('", "')}") VALUES (${placeholders2})`;
29487
29600
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
29488
29601
  const result = this.executeMany(sql2, paramsList);
29489
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
29602
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
29490
29603
  }
29491
29604
  const keys = Object.keys(data);
29492
29605
  const placeholders = keys.map(() => "?").join(", ");
@@ -29495,9 +29608,9 @@ var init_sqlite = __esm({
29495
29608
  try {
29496
29609
  const result = this.db.prepare(sql).run(...toSqlParams(values));
29497
29610
  this._lastInsertId = result.lastInsertRowid;
29498
- return { success: true, rowsAffected: Number(result.changes), lastInsertId: result.lastInsertRowid };
29611
+ return { success: true, affectedRows: Number(result.changes), lastId: result.lastInsertRowid };
29499
29612
  } catch (e) {
29500
- return { success: false, rowsAffected: 0, error: e.message };
29613
+ return { success: false, affectedRows: 0, error: e.message };
29501
29614
  }
29502
29615
  }
29503
29616
  update(table2, data, filter, params) {
@@ -29507,9 +29620,9 @@ var init_sqlite = __esm({
29507
29620
  const values = [...Object.values(data), ...Object.values(filter)];
29508
29621
  try {
29509
29622
  const result = this.db.prepare(sql).run(...toSqlParams(values));
29510
- return { success: true, rowsAffected: Number(result.changes) };
29623
+ return { success: true, affectedRows: Number(result.changes) };
29511
29624
  } catch (e) {
29512
- return { success: false, rowsAffected: 0, error: e.message };
29625
+ return { success: false, affectedRows: 0, error: e.message };
29513
29626
  }
29514
29627
  }
29515
29628
  delete(table2, filter, params) {
@@ -29517,17 +29630,17 @@ var init_sqlite = __esm({
29517
29630
  let totalAffected = 0;
29518
29631
  for (const row of filter) {
29519
29632
  const result = this.delete(table2, row);
29520
- totalAffected += result.rowsAffected;
29633
+ totalAffected += result.affectedRows;
29521
29634
  }
29522
- return { success: true, rowsAffected: totalAffected };
29635
+ return { success: true, affectedRows: totalAffected };
29523
29636
  }
29524
29637
  if (typeof filter === "string") {
29525
29638
  const sql2 = filter ? `DELETE FROM "${table2}" WHERE ${filter}` : `DELETE FROM "${table2}"`;
29526
29639
  try {
29527
29640
  const result = this.db.prepare(sql2).run(...toSqlParams(params ?? []));
29528
- return { success: true, rowsAffected: Number(result.changes) };
29641
+ return { success: true, affectedRows: Number(result.changes) };
29529
29642
  } catch (e) {
29530
- return { success: false, rowsAffected: 0, error: e.message };
29643
+ return { success: false, affectedRows: 0, error: e.message };
29531
29644
  }
29532
29645
  }
29533
29646
  const whereClauses = Object.keys(filter).map((k) => `"${k}" = ?`).join(" AND ");
@@ -29535,9 +29648,9 @@ var init_sqlite = __esm({
29535
29648
  const values = Object.values(filter);
29536
29649
  try {
29537
29650
  const result = this.db.prepare(sql).run(...toSqlParams(values));
29538
- return { success: true, rowsAffected: Number(result.changes) };
29651
+ return { success: true, affectedRows: Number(result.changes) };
29539
29652
  } catch (e) {
29540
- return { success: false, rowsAffected: 0, error: e.message };
29653
+ return { success: false, affectedRows: 0, error: e.message };
29541
29654
  }
29542
29655
  }
29543
29656
  _inTransaction = false;
@@ -29778,7 +29891,7 @@ var init_postgres = __esm({
29778
29891
  }
29779
29892
  /**
29780
29893
  * Normalise an `id` column value (typed `unknown` because pg row values are
29781
- * `unknown`) into the shape `_lastInsertId` / `DatabaseResult.lastInsertId`
29894
+ * `unknown`) into the shape `_lastInsertId` / `DatabaseResult.lastId`
29782
29895
  * expect. At runtime PG returns numeric PKs as number/bigint (the int8/numeric
29783
29896
  * type parsers above coerce them to Number); a numeric string is coerced to a
29784
29897
  * number so the SERIAL path always returns the integer id.
@@ -29813,8 +29926,8 @@ var init_postgres = __esm({
29813
29926
  for (const params of paramsList) {
29814
29927
  const result = await this.executeAsync(sql, params);
29815
29928
  totalAffected++;
29816
- if (result && typeof result === "object" && "lastInsertId" in result) {
29817
- lastId = result.lastInsertId;
29929
+ if (result && typeof result === "object" && "lastId" in result) {
29930
+ lastId = result.lastId;
29818
29931
  }
29819
29932
  }
29820
29933
  if (owns) await this.commitAsync();
@@ -29827,7 +29940,7 @@ var init_postgres = __esm({
29827
29940
  }
29828
29941
  throw e;
29829
29942
  }
29830
- return { totalAffected, lastInsertId: lastId };
29943
+ return { totalAffected, lastId };
29831
29944
  }
29832
29945
  /** Async execute for real usage. */
29833
29946
  async executeAsync(sql, params) {
@@ -29875,17 +29988,17 @@ var init_postgres = __esm({
29875
29988
  async insertAsync(table2, data) {
29876
29989
  this.ensureConnected();
29877
29990
  if (Array.isArray(data)) {
29878
- if (data.length === 0) return { success: true, rowsAffected: 0 };
29991
+ if (data.length === 0) return { success: true, affectedRows: 0 };
29879
29992
  const keys2 = Object.keys(data[0]);
29880
29993
  const placeholders2 = keys2.map(() => "?").join(", ");
29881
29994
  const sql2 = `INSERT INTO "${table2}" ("${keys2.join('", "')}") VALUES (${placeholders2})`;
29882
29995
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
29883
29996
  try {
29884
29997
  const result = await this.executeManyAsync(sql2, paramsList);
29885
- if (result.lastInsertId !== void 0) this._lastInsertId = result.lastInsertId;
29886
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
29998
+ if (result.lastId !== void 0) this._lastInsertId = result.lastId;
29999
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
29887
30000
  } catch (e) {
29888
- return { success: false, rowsAffected: 0, error: e.message };
30001
+ return { success: false, affectedRows: 0, error: e.message };
29889
30002
  }
29890
30003
  }
29891
30004
  const keys = Object.keys(data);
@@ -29899,11 +30012,11 @@ var init_postgres = __esm({
29899
30012
  if (id !== null) this._lastInsertId = id;
29900
30013
  return {
29901
30014
  success: true,
29902
- rowsAffected: result.rowCount ?? 1,
29903
- lastInsertId: id ?? void 0
30015
+ affectedRows: result.rowCount ?? 1,
30016
+ lastId: id ?? void 0
29904
30017
  };
29905
30018
  } catch (e) {
29906
- return { success: false, rowsAffected: 0, error: e.message };
30019
+ return { success: false, affectedRows: 0, error: e.message };
29907
30020
  }
29908
30021
  }
29909
30022
  update(table2, data, filter, params) {
@@ -29920,9 +30033,9 @@ var init_postgres = __esm({
29920
30033
  const values = [...Object.values(data), ...Object.values(filter)];
29921
30034
  try {
29922
30035
  const result = await this.client.query(sql, values);
29923
- return { success: true, rowsAffected: result.rowCount ?? 0 };
30036
+ return { success: true, affectedRows: result.rowCount ?? 0 };
29924
30037
  } catch (e) {
29925
- return { success: false, rowsAffected: 0, error: e.message };
30038
+ return { success: false, affectedRows: 0, error: e.message };
29926
30039
  }
29927
30040
  }
29928
30041
  delete(table2, filter, params) {
@@ -29937,9 +30050,9 @@ var init_postgres = __esm({
29937
30050
  const values = Object.values(filter);
29938
30051
  try {
29939
30052
  const result = await this.client.query(sql, values);
29940
- return { success: true, rowsAffected: result.rowCount ?? 0 };
30053
+ return { success: true, affectedRows: result.rowCount ?? 0 };
29941
30054
  } catch (e) {
29942
- return { success: false, rowsAffected: 0, error: e.message };
30055
+ return { success: false, affectedRows: 0, error: e.message };
29943
30056
  }
29944
30057
  }
29945
30058
  startTransaction() {
@@ -29947,18 +30060,21 @@ var init_postgres = __esm({
29947
30060
  }
29948
30061
  async startTransactionAsync() {
29949
30062
  await this.executeAsync("BEGIN");
30063
+ this._inTransaction = true;
29950
30064
  }
29951
30065
  commit() {
29952
30066
  throw new Error("Use commitAsync() for PostgreSQL.");
29953
30067
  }
29954
30068
  async commitAsync() {
29955
30069
  await this.executeAsync("COMMIT");
30070
+ this._inTransaction = false;
29956
30071
  }
29957
30072
  rollback() {
29958
30073
  throw new Error("Use rollbackAsync() for PostgreSQL.");
29959
30074
  }
29960
30075
  async rollbackAsync() {
29961
30076
  await this.executeAsync("ROLLBACK");
30077
+ this._inTransaction = false;
29962
30078
  }
29963
30079
  tables() {
29964
30080
  throw new Error("Use tablesAsync() for PostgreSQL.");
@@ -30173,7 +30289,7 @@ var init_mysql = __esm({
30173
30289
  }
30174
30290
  throw e;
30175
30291
  }
30176
- return { totalAffected, lastInsertId: lastId };
30292
+ return { totalAffected, lastId };
30177
30293
  }
30178
30294
  async executeAsync(sql, params) {
30179
30295
  this.ensureConnected();
@@ -30219,17 +30335,17 @@ var init_mysql = __esm({
30219
30335
  async insertAsync(table2, data) {
30220
30336
  this.ensureConnected();
30221
30337
  if (Array.isArray(data)) {
30222
- if (data.length === 0) return { success: true, rowsAffected: 0 };
30338
+ if (data.length === 0) return { success: true, affectedRows: 0 };
30223
30339
  const keys2 = Object.keys(data[0]);
30224
30340
  const placeholders2 = keys2.map(() => "?").join(", ");
30225
30341
  const sql2 = `INSERT INTO \`${table2}\` (\`${keys2.join("`, `")}\`) VALUES (${placeholders2})`;
30226
30342
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
30227
30343
  try {
30228
30344
  const result = await this.executeManyAsync(sql2, paramsList);
30229
- if (result.lastInsertId !== void 0) this._lastInsertId = result.lastInsertId;
30230
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
30345
+ if (result.lastId !== void 0) this._lastInsertId = result.lastId;
30346
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
30231
30347
  } catch (e) {
30232
- return { success: false, rowsAffected: 0, error: e.message };
30348
+ return { success: false, affectedRows: 0, error: e.message };
30233
30349
  }
30234
30350
  }
30235
30351
  const keys = Object.keys(data);
@@ -30241,11 +30357,11 @@ var init_mysql = __esm({
30241
30357
  this._lastInsertId = result.insertId ?? null;
30242
30358
  return {
30243
30359
  success: true,
30244
- rowsAffected: result.affectedRows ?? 1,
30245
- lastInsertId: result.insertId
30360
+ affectedRows: result.affectedRows ?? 1,
30361
+ lastId: result.insertId
30246
30362
  };
30247
30363
  } catch (e) {
30248
- return { success: false, rowsAffected: 0, error: e.message };
30364
+ return { success: false, affectedRows: 0, error: e.message };
30249
30365
  }
30250
30366
  }
30251
30367
  update(table2, data, filter, params) {
@@ -30259,9 +30375,9 @@ var init_mysql = __esm({
30259
30375
  const values = [...Object.values(data), ...Object.values(filter)];
30260
30376
  try {
30261
30377
  const result = await this.queryPromise(sql, values);
30262
- return { success: true, rowsAffected: result.affectedRows ?? 0 };
30378
+ return { success: true, affectedRows: result.affectedRows ?? 0 };
30263
30379
  } catch (e) {
30264
- return { success: false, rowsAffected: 0, error: e.message };
30380
+ return { success: false, affectedRows: 0, error: e.message };
30265
30381
  }
30266
30382
  }
30267
30383
  delete(table2, filter, params) {
@@ -30274,9 +30390,9 @@ var init_mysql = __esm({
30274
30390
  const values = Object.values(filter);
30275
30391
  try {
30276
30392
  const result = await this.queryPromise(sql, values);
30277
- return { success: true, rowsAffected: result.affectedRows ?? 0 };
30393
+ return { success: true, affectedRows: result.affectedRows ?? 0 };
30278
30394
  } catch (e) {
30279
- return { success: false, rowsAffected: 0, error: e.message };
30395
+ return { success: false, affectedRows: 0, error: e.message };
30280
30396
  }
30281
30397
  }
30282
30398
  startTransaction() {
@@ -30617,17 +30733,17 @@ var init_mssql = __esm({
30617
30733
  async insertAsync(table2, data) {
30618
30734
  this.ensureConnected();
30619
30735
  if (Array.isArray(data)) {
30620
- if (data.length === 0) return { success: true, rowsAffected: 0 };
30736
+ if (data.length === 0) return { success: true, affectedRows: 0 };
30621
30737
  const keys2 = Object.keys(data[0]);
30622
30738
  const placeholders2 = keys2.map(() => "?").join(", ");
30623
30739
  const sql2 = `INSERT INTO [${table2}] ([${keys2.join("], [")}]) VALUES (${placeholders2})`;
30624
30740
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
30625
30741
  try {
30626
30742
  const result = await this.executeManyAsync(sql2, paramsList);
30627
- if (result.lastInsertId !== void 0) this._lastInsertId = result.lastInsertId;
30628
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
30743
+ if (result.lastId !== void 0) this._lastInsertId = result.lastId;
30744
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
30629
30745
  } catch (e) {
30630
- return { success: false, rowsAffected: 0, error: e.message };
30746
+ return { success: false, affectedRows: 0, error: e.message };
30631
30747
  }
30632
30748
  }
30633
30749
  const keys = Object.keys(data);
@@ -30643,12 +30759,12 @@ var init_mssql = __esm({
30643
30759
  // A single-object insert affects exactly one row. Do NOT use
30644
30760
  // result.rowCount here: the statement is "INSERT ...; SELECT
30645
30761
  // SCOPE_IDENTITY()", and tedious sums the row counts of BOTH statements
30646
- // (1 for the INSERT + 1 for the SELECT), which reported rowsAffected=2.
30647
- rowsAffected: 1,
30648
- lastInsertId: id ?? void 0
30762
+ // (1 for the INSERT + 1 for the SELECT), which reported affectedRows=2.
30763
+ affectedRows: 1,
30764
+ lastId: id ?? void 0
30649
30765
  };
30650
30766
  } catch (e) {
30651
- return { success: false, rowsAffected: 0, error: e.message };
30767
+ return { success: false, affectedRows: 0, error: e.message };
30652
30768
  }
30653
30769
  }
30654
30770
  update(table2, data, filter, params) {
@@ -30665,9 +30781,9 @@ var init_mssql = __esm({
30665
30781
  const values = [...Object.values(data), ...Object.values(filter)];
30666
30782
  try {
30667
30783
  const result = await this.execSqlPromise(sql, values);
30668
- return { success: true, rowsAffected: result.rowCount };
30784
+ return { success: true, affectedRows: result.rowCount };
30669
30785
  } catch (e) {
30670
- return { success: false, rowsAffected: 0, error: e.message };
30786
+ return { success: false, affectedRows: 0, error: e.message };
30671
30787
  }
30672
30788
  }
30673
30789
  delete(table2, filter, params) {
@@ -30682,9 +30798,9 @@ var init_mssql = __esm({
30682
30798
  const values = Object.values(filter);
30683
30799
  try {
30684
30800
  const result = await this.execSqlPromise(sql, values);
30685
- return { success: true, rowsAffected: result.rowCount };
30801
+ return { success: true, affectedRows: result.rowCount };
30686
30802
  } catch (e) {
30687
- return { success: false, rowsAffected: 0, error: e.message };
30803
+ return { success: false, affectedRows: 0, error: e.message };
30688
30804
  }
30689
30805
  }
30690
30806
  startTransaction() {
@@ -30977,10 +31093,35 @@ var init_firebird = __esm({
30977
31093
  translated = SQLTranslator.ilikeToLike(translated);
30978
31094
  return translated;
30979
31095
  }
31096
+ /**
31097
+ * The handle every statement runs on. While an explicit transaction is open
31098
+ * (startTransactionAsync set `this.transaction`), statements MUST run on that
31099
+ * transaction object so they are undone by rollbackAsync() / persisted by
31100
+ * commitAsync() — node-firebird's transaction exposes the same
31101
+ * query()/execute() as the connection. With no transaction open we run on
31102
+ * `this.db`, whose per-statement work auto-commits on the connection.
31103
+ *
31104
+ * This matches the Python master's contract (tina4_python/database/firebird.py):
31105
+ * there, ALL statements run on the single connection and start_transaction()
31106
+ * merely suppresses the per-statement autocommit in execute() so the batch
31107
+ * stays open until commit()/rollback(). node-firebird has no such suppression
31108
+ * hook — its `db.query/execute` always auto-commit — so the equivalent is to
31109
+ * route statements through the transaction object instead. Same observable
31110
+ * behaviour: an open transaction is atomic and rolls back cleanly.
31111
+ *
31112
+ * Previously every statement ran on `this.db` unconditionally, so the
31113
+ * transaction created by startTransactionAsync() never saw a single statement
31114
+ * — rollbackAsync() rolled back an EMPTY transaction and the already
31115
+ * auto-committed write survived (silent no-op). Twin of the PHP pdo_firebird
31116
+ * bug fixed in 3.13.86.
31117
+ */
31118
+ statementHandle() {
31119
+ return this.transaction ?? this.db;
31120
+ }
30980
31121
  queryPromise(sql, params) {
30981
31122
  return new Promise((resolve21, reject) => {
30982
31123
  const translated = this.translateSql(sql);
30983
- this.db.query(translated, params ?? [], (err, result) => {
31124
+ this.statementHandle().query(translated, params ?? [], (err, result) => {
30984
31125
  if (err) reject(err);
30985
31126
  else resolve21(result ?? []);
30986
31127
  });
@@ -30989,7 +31130,7 @@ var init_firebird = __esm({
30989
31130
  executePromise(sql, params) {
30990
31131
  return new Promise((resolve21, reject) => {
30991
31132
  const translated = this.translateSql(sql);
30992
- this.db.execute(translated, params ?? [], (err) => {
31133
+ this.statementHandle().execute(translated, params ?? [], (err) => {
30993
31134
  if (err) reject(err);
30994
31135
  else resolve21();
30995
31136
  });
@@ -31053,16 +31194,16 @@ var init_firebird = __esm({
31053
31194
  async insertAsync(table2, data) {
31054
31195
  this.ensureConnected();
31055
31196
  if (Array.isArray(data)) {
31056
- if (data.length === 0) return { success: true, rowsAffected: 0 };
31197
+ if (data.length === 0) return { success: true, affectedRows: 0 };
31057
31198
  const keys2 = Object.keys(data[0]);
31058
31199
  const placeholders2 = keys2.map(() => "?").join(", ");
31059
31200
  const sql2 = `INSERT INTO "${table2}" ("${keys2.join('", "')}") VALUES (${placeholders2})`;
31060
31201
  const paramsList = data.map((row) => keys2.map((k) => row[k]));
31061
31202
  try {
31062
31203
  const result = await this.executeManyAsync(sql2, paramsList);
31063
- return { success: true, rowsAffected: result.totalAffected, lastInsertId: result.lastInsertId };
31204
+ return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
31064
31205
  } catch (e) {
31065
- return { success: false, rowsAffected: 0, error: e.message };
31206
+ return { success: false, affectedRows: 0, error: e.message };
31066
31207
  }
31067
31208
  }
31068
31209
  const keys = Object.keys(data);
@@ -31073,10 +31214,10 @@ var init_firebird = __esm({
31073
31214
  await this.executePromise(sql, values);
31074
31215
  return {
31075
31216
  success: true,
31076
- rowsAffected: 1
31217
+ affectedRows: 1
31077
31218
  };
31078
31219
  } catch (e) {
31079
- return { success: false, rowsAffected: 0, error: e.message };
31220
+ return { success: false, affectedRows: 0, error: e.message };
31080
31221
  }
31081
31222
  }
31082
31223
  update(table2, data, filter, params) {
@@ -31090,9 +31231,9 @@ var init_firebird = __esm({
31090
31231
  const values = [...Object.values(data), ...Object.values(filter)];
31091
31232
  try {
31092
31233
  await this.executePromise(sql, values);
31093
- return { success: true, rowsAffected: 1 };
31234
+ return { success: true, affectedRows: 1 };
31094
31235
  } catch (e) {
31095
- return { success: false, rowsAffected: 0, error: e.message };
31236
+ return { success: false, affectedRows: 0, error: e.message };
31096
31237
  }
31097
31238
  }
31098
31239
  delete(table2, filter, params) {
@@ -31105,9 +31246,9 @@ var init_firebird = __esm({
31105
31246
  const values = Object.values(filter);
31106
31247
  try {
31107
31248
  await this.executePromise(sql, values);
31108
- return { success: true, rowsAffected: 1 };
31249
+ return { success: true, affectedRows: 1 };
31109
31250
  } catch (e) {
31110
- return { success: false, rowsAffected: 0, error: e.message };
31251
+ return { success: false, affectedRows: 0, error: e.message };
31111
31252
  }
31112
31253
  }
31113
31254
  startTransaction() {
@@ -31603,14 +31744,14 @@ var init_mongodb = __esm({
31603
31744
  const col = this.db.collection(table2);
31604
31745
  try {
31605
31746
  if (Array.isArray(data)) {
31606
- if (data.length === 0) return { success: true, rowsAffected: 0 };
31747
+ if (data.length === 0) return { success: true, affectedRows: 0 };
31607
31748
  const result2 = await col.insertMany(data, { session: this.session });
31608
- return { success: true, rowsAffected: result2.insertedCount };
31749
+ return { success: true, affectedRows: result2.insertedCount };
31609
31750
  }
31610
31751
  const result = await col.insertOne(data, { session: this.session });
31611
- return { success: true, rowsAffected: 1, lastInsertId: void 0 };
31752
+ return { success: true, affectedRows: 1, lastId: void 0 };
31612
31753
  } catch (e) {
31613
- return { success: false, rowsAffected: 0, error: e.message };
31754
+ return { success: false, affectedRows: 0, error: e.message };
31614
31755
  }
31615
31756
  }
31616
31757
  update(table2, data, filter) {
@@ -31621,9 +31762,9 @@ var init_mongodb = __esm({
31621
31762
  const col = this.db.collection(table2);
31622
31763
  try {
31623
31764
  const result = await col.updateMany(filter, { $set: data }, { session: this.session });
31624
- return { success: true, rowsAffected: result.modifiedCount };
31765
+ return { success: true, affectedRows: result.modifiedCount };
31625
31766
  } catch (e) {
31626
- return { success: false, rowsAffected: 0, error: e.message };
31767
+ return { success: false, affectedRows: 0, error: e.message };
31627
31768
  }
31628
31769
  }
31629
31770
  delete(table2, filter) {
@@ -31639,21 +31780,21 @@ var init_mongodb = __esm({
31639
31780
  const r = await col.deleteMany(f, { session: this.session });
31640
31781
  total += r.deletedCount;
31641
31782
  }
31642
- return { success: true, rowsAffected: total };
31783
+ return { success: true, affectedRows: total };
31643
31784
  }
31644
31785
  if (typeof filter === "string") {
31645
31786
  if (!filter.trim()) {
31646
31787
  const r2 = await col.deleteMany({}, { session: this.session });
31647
- return { success: true, rowsAffected: r2.deletedCount };
31788
+ return { success: true, affectedRows: r2.deletedCount };
31648
31789
  }
31649
31790
  const { filter: parsedFilter } = parseWhereClause(filter, []);
31650
31791
  const r = await col.deleteMany(parsedFilter, { session: this.session });
31651
- return { success: true, rowsAffected: r.deletedCount };
31792
+ return { success: true, affectedRows: r.deletedCount };
31652
31793
  }
31653
31794
  const result = await col.deleteMany(filter, { session: this.session });
31654
- return { success: true, rowsAffected: result.deletedCount };
31795
+ return { success: true, affectedRows: result.deletedCount };
31655
31796
  } catch (e) {
31656
- return { success: false, rowsAffected: 0, error: e.message };
31797
+ return { success: false, affectedRows: 0, error: e.message };
31657
31798
  }
31658
31799
  }
31659
31800
  startTransaction() {
@@ -31913,8 +32054,8 @@ var init_odbc = __esm({
31913
32054
  async executeAsync(sql, params) {
31914
32055
  this.ensureConnected();
31915
32056
  const result = await this.connection.query(sql, params ?? []);
31916
- if (result && typeof result === "object" && "lastInsertId" in result) {
31917
- this._lastInsertId = result.lastInsertId;
32057
+ if (result && typeof result === "object" && "lastId" in result) {
32058
+ this._lastInsertId = result.lastId;
31918
32059
  }
31919
32060
  return result;
31920
32061
  }
@@ -31935,7 +32076,7 @@ var init_odbc = __esm({
31935
32076
  throw e;
31936
32077
  }
31937
32078
  if (lastId !== void 0) this._lastInsertId = lastId;
31938
- return { totalAffected, lastInsertId: lastId };
32079
+ return { totalAffected, lastId };
31939
32080
  }
31940
32081
  /** Run a SELECT and return all matching rows. */
31941
32082
  async queryAsync(sql, params) {
@@ -31971,9 +32112,9 @@ var init_odbc = __esm({
31971
32112
  const values = Object.values(data);
31972
32113
  try {
31973
32114
  await this.connection.query(sql, values);
31974
- return { success: true, rowsAffected: 1, lastInsertId: this._lastInsertId ?? void 0 };
32115
+ return { success: true, affectedRows: 1, lastId: this._lastInsertId ?? void 0 };
31975
32116
  } catch (e) {
31976
- return { success: false, rowsAffected: 0, error: e.message };
32117
+ return { success: false, affectedRows: 0, error: e.message };
31977
32118
  }
31978
32119
  }
31979
32120
  /** Update rows in a table matching filter. */
@@ -31985,9 +32126,9 @@ var init_odbc = __esm({
31985
32126
  const values = [...Object.values(data), ...Object.values(filter)];
31986
32127
  try {
31987
32128
  await this.connection.query(sql, values);
31988
- return { success: true, rowsAffected: 1 };
32129
+ return { success: true, affectedRows: 1 };
31989
32130
  } catch (e) {
31990
- return { success: false, rowsAffected: 0, error: e.message };
32131
+ return { success: false, affectedRows: 0, error: e.message };
31991
32132
  }
31992
32133
  }
31993
32134
  /** Delete rows from a table. */
@@ -31997,17 +32138,17 @@ var init_odbc = __esm({
31997
32138
  let totalAffected = 0;
31998
32139
  for (const row of filter) {
31999
32140
  const result = await this.deleteAsync(table2, row);
32000
- totalAffected += result.rowsAffected;
32141
+ totalAffected += result.affectedRows;
32001
32142
  }
32002
- return { success: true, rowsAffected: totalAffected };
32143
+ return { success: true, affectedRows: totalAffected };
32003
32144
  }
32004
32145
  if (typeof filter === "string") {
32005
32146
  const sql2 = filter ? `DELETE FROM "${table2}" WHERE ${filter}` : `DELETE FROM "${table2}"`;
32006
32147
  try {
32007
32148
  await this.connection.query(sql2, []);
32008
- return { success: true, rowsAffected: 1 };
32149
+ return { success: true, affectedRows: 1 };
32009
32150
  } catch (e) {
32010
- return { success: false, rowsAffected: 0, error: e.message };
32151
+ return { success: false, affectedRows: 0, error: e.message };
32011
32152
  }
32012
32153
  }
32013
32154
  const whereClauses = Object.keys(filter).map((k) => `"${k}" = ?`).join(" AND ");
@@ -32015,9 +32156,9 @@ var init_odbc = __esm({
32015
32156
  const values = Object.values(filter);
32016
32157
  try {
32017
32158
  await this.connection.query(sql, values);
32018
- return { success: true, rowsAffected: 1 };
32159
+ return { success: true, affectedRows: 1 };
32019
32160
  } catch (e) {
32020
- return { success: false, rowsAffected: 0, error: e.message };
32161
+ return { success: false, affectedRows: 0, error: e.message };
32021
32162
  }
32022
32163
  }
32023
32164
  /** Begin a transaction. */
@@ -32197,7 +32338,7 @@ function extractLastInsertId(result) {
32197
32338
  const r = result;
32198
32339
  if (r.lastInsertRowid !== void 0 && r.lastInsertRowid !== null) return r.lastInsertRowid;
32199
32340
  if (r.rows?.[0]?.id !== void 0 && r.rows[0].id !== null) return r.rows[0].id;
32200
- if (r.lastInsertId !== void 0 && r.lastInsertId !== null) return r.lastInsertId;
32341
+ if (r.lastId !== void 0 && r.lastId !== null) return r.lastId;
32201
32342
  }
32202
32343
  return null;
32203
32344
  }
@@ -32963,14 +33104,15 @@ var init_database = __esm({
32963
33104
  async executeMany(sql, paramSets = []) {
32964
33105
  const adapter = this.getNextAdapter();
32965
33106
  const results = [];
32966
- await adapterStartTransaction(adapter);
33107
+ const owns = !this.inExplicitTransaction();
33108
+ if (owns) await adapterStartTransaction(adapter);
32967
33109
  try {
32968
33110
  for (const params of paramSets) {
32969
33111
  results.push(await adapterExecute(adapter, sql, params));
32970
33112
  }
32971
- await adapterCommit(adapter);
33113
+ if (owns) await adapterCommit(adapter);
32972
33114
  } catch (e) {
32973
- await adapterRollback(adapter);
33115
+ if (owns) await adapterRollback(adapter);
32974
33116
  throw e;
32975
33117
  }
32976
33118
  return results;