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.
- package/CLAUDE.md +38 -32
- package/package.json +22 -5
- package/packages/cli/dist/bin.js +245 -103
- package/packages/core/dist/index.js +244 -102
- package/packages/core/public/js/tina4-dev-admin.min.js +90 -87
- package/packages/core/src/metrics.ts +11 -2
- package/packages/core/src/session.ts +2 -2
- package/packages/core/src/sessionHandlers/databaseHandler.ts +5 -6
- package/packages/frond/dist/index.js +115 -10
- package/packages/frond/src/engine.ts +186 -11
- package/packages/orm/dist/index.js +244 -102
- package/packages/orm/src/adapters/firebird.ts +39 -13
- package/packages/orm/src/adapters/mongodb.ts +13 -13
- package/packages/orm/src/adapters/mssql.ts +14 -14
- package/packages/orm/src/adapters/mysql.ts +14 -14
- package/packages/orm/src/adapters/odbc.ts +15 -15
- package/packages/orm/src/adapters/postgres.ts +28 -17
- package/packages/orm/src/adapters/sqlite.ts +14 -14
- package/packages/orm/src/autoCrud.ts +1 -1
- package/packages/orm/src/cachedDatabase.ts +1 -1
- package/packages/orm/src/database.ts +17 -5
- package/packages/orm/src/types.ts +4 -4
package/packages/cli/dist/bin.js
CHANGED
|
@@ -1182,7 +1182,7 @@ var init_sqlite = __esm({
|
|
|
1182
1182
|
this.db.exec("ROLLBACK");
|
|
1183
1183
|
throw e;
|
|
1184
1184
|
}
|
|
1185
|
-
return { totalAffected,
|
|
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,
|
|
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,
|
|
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,
|
|
1226
|
+
return { success: true, affectedRows: Number(result.changes), lastId: result.lastInsertRowid };
|
|
1227
1227
|
} catch (e) {
|
|
1228
|
-
return { success: false,
|
|
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,
|
|
1238
|
+
return { success: true, affectedRows: Number(result.changes) };
|
|
1239
1239
|
} catch (e) {
|
|
1240
|
-
return { success: false,
|
|
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.
|
|
1248
|
+
totalAffected += result.affectedRows;
|
|
1249
1249
|
}
|
|
1250
|
-
return { success: true,
|
|
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,
|
|
1256
|
+
return { success: true, affectedRows: Number(result.changes) };
|
|
1257
1257
|
} catch (e) {
|
|
1258
|
-
return { success: false,
|
|
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,
|
|
1266
|
+
return { success: true, affectedRows: Number(result.changes) };
|
|
1267
1267
|
} catch (e) {
|
|
1268
|
-
return { success: false,
|
|
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.
|
|
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" && "
|
|
1545
|
-
lastId = result.
|
|
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,
|
|
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,
|
|
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.
|
|
1614
|
-
return { success: true,
|
|
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,
|
|
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
|
-
|
|
1631
|
-
|
|
1630
|
+
affectedRows: result.rowCount ?? 1,
|
|
1631
|
+
lastId: id ?? void 0
|
|
1632
1632
|
};
|
|
1633
1633
|
} catch (e) {
|
|
1634
|
-
return { success: false,
|
|
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,
|
|
1651
|
+
return { success: true, affectedRows: result.rowCount ?? 0 };
|
|
1652
1652
|
} catch (e) {
|
|
1653
|
-
return { success: false,
|
|
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,
|
|
1668
|
+
return { success: true, affectedRows: result.rowCount ?? 0 };
|
|
1669
1669
|
} catch (e) {
|
|
1670
|
-
return { success: false,
|
|
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,
|
|
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,
|
|
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.
|
|
1958
|
-
return { success: true,
|
|
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,
|
|
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
|
-
|
|
1973
|
-
|
|
1975
|
+
affectedRows: result.affectedRows ?? 1,
|
|
1976
|
+
lastId: result.insertId
|
|
1974
1977
|
};
|
|
1975
1978
|
} catch (e) {
|
|
1976
|
-
return { success: false,
|
|
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,
|
|
1993
|
+
return { success: true, affectedRows: result.affectedRows ?? 0 };
|
|
1991
1994
|
} catch (e) {
|
|
1992
|
-
return { success: false,
|
|
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,
|
|
2008
|
+
return { success: true, affectedRows: result.affectedRows ?? 0 };
|
|
2006
2009
|
} catch (e) {
|
|
2007
|
-
return { success: false,
|
|
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,
|
|
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.
|
|
2356
|
-
return { success: true,
|
|
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,
|
|
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
|
|
2375
|
-
|
|
2376
|
-
|
|
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,
|
|
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,
|
|
2399
|
+
return { success: true, affectedRows: result.rowCount };
|
|
2397
2400
|
} catch (e) {
|
|
2398
|
-
return { success: false,
|
|
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,
|
|
2416
|
+
return { success: true, affectedRows: result.rowCount };
|
|
2414
2417
|
} catch (e) {
|
|
2415
|
-
return { success: false,
|
|
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.
|
|
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.
|
|
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,
|
|
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,
|
|
2819
|
+
return { success: true, affectedRows: result.totalAffected, lastId: result.lastId };
|
|
2792
2820
|
} catch (e) {
|
|
2793
|
-
return { success: false,
|
|
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
|
-
|
|
2832
|
+
affectedRows: 1
|
|
2805
2833
|
};
|
|
2806
2834
|
} catch (e) {
|
|
2807
|
-
return { success: false,
|
|
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,
|
|
2849
|
+
return { success: true, affectedRows: 1 };
|
|
2822
2850
|
} catch (e) {
|
|
2823
|
-
return { success: false,
|
|
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,
|
|
2864
|
+
return { success: true, affectedRows: 1 };
|
|
2837
2865
|
} catch (e) {
|
|
2838
|
-
return { success: false,
|
|
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,
|
|
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,
|
|
3364
|
+
return { success: true, affectedRows: result2.insertedCount };
|
|
3337
3365
|
}
|
|
3338
3366
|
const result = await col.insertOne(data, { session: this.session });
|
|
3339
|
-
return { success: true,
|
|
3367
|
+
return { success: true, affectedRows: 1, lastId: void 0 };
|
|
3340
3368
|
} catch (e) {
|
|
3341
|
-
return { success: false,
|
|
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,
|
|
3380
|
+
return { success: true, affectedRows: result.modifiedCount };
|
|
3353
3381
|
} catch (e) {
|
|
3354
|
-
return { success: false,
|
|
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,
|
|
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,
|
|
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,
|
|
3407
|
+
return { success: true, affectedRows: r.deletedCount };
|
|
3380
3408
|
}
|
|
3381
3409
|
const result = await col.deleteMany(filter, { session: this.session });
|
|
3382
|
-
return { success: true,
|
|
3410
|
+
return { success: true, affectedRows: result.deletedCount };
|
|
3383
3411
|
} catch (e) {
|
|
3384
|
-
return { success: false,
|
|
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" && "
|
|
3645
|
-
this._lastInsertId = result.
|
|
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,
|
|
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,
|
|
3730
|
+
return { success: true, affectedRows: 1, lastId: this._lastInsertId ?? void 0 };
|
|
3703
3731
|
} catch (e) {
|
|
3704
|
-
return { success: false,
|
|
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,
|
|
3744
|
+
return { success: true, affectedRows: 1 };
|
|
3717
3745
|
} catch (e) {
|
|
3718
|
-
return { success: false,
|
|
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.
|
|
3756
|
+
totalAffected += result.affectedRows;
|
|
3729
3757
|
}
|
|
3730
|
-
return { success: true,
|
|
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,
|
|
3764
|
+
return { success: true, affectedRows: 1 };
|
|
3737
3765
|
} catch (e) {
|
|
3738
|
-
return { success: false,
|
|
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,
|
|
3774
|
+
return { success: true, affectedRows: 1 };
|
|
3747
3775
|
} catch (e) {
|
|
3748
|
-
return { success: false,
|
|
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.
|
|
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
|
-
|
|
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, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
13142
|
+
if (depth === 0 && bracketDepth === 0 && expr.startsWith(sep5, i)) {
|
|
13099
13143
|
parts.push(expr.slice(currentStart, i));
|
|
13100
|
-
i +=
|
|
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
|
|
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 <
|
|
14978
|
-
|
|
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]
|
|
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
|
-
|
|
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({
|