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