tina4-nodejs 3.13.85 → 3.13.86
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 +37 -31
- package/package.json +22 -5
- package/packages/cli/dist/bin.js +97 -91
- package/packages/core/dist/index.js +97 -91
- package/packages/core/public/js/tina4-dev-admin.min.js +90 -87
- package/packages/core/src/session.ts +2 -2
- package/packages/core/src/sessionHandlers/databaseHandler.ts +5 -6
- package/packages/frond/dist/index.js +11 -5
- package/packages/frond/src/engine.ts +27 -5
- package/packages/orm/dist/index.js +97 -91
- package/packages/orm/src/adapters/firebird.ts +11 -11
- 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 +17 -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 +2 -2
- package/packages/orm/src/types.ts +4 -4
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Tina4 Session — Pluggable session backends, zero core dependencies.
|
|
3
3
|
*
|
|
4
4
|
* File-based sessions by default. Redis backend available via raw TCP (no ioredis needed).
|
|
5
|
-
* Database (SQLite) backend available via
|
|
5
|
+
* Database (SQLite) backend available via Node's built-in node:sqlite.
|
|
6
6
|
*
|
|
7
7
|
* import { Session, RedisSessionHandler } from "@tina4/core";
|
|
8
8
|
*
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* redisPort: 6379,
|
|
16
16
|
* });
|
|
17
17
|
*
|
|
18
|
-
* // Database backend (SQLite via
|
|
18
|
+
* // Database backend (SQLite via node:sqlite)
|
|
19
19
|
* const session = new Session("database");
|
|
20
20
|
* // or: new Session("db");
|
|
21
21
|
*
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tina4 Database Session Handler — SQLite via
|
|
2
|
+
* Tina4 Database Session Handler — SQLite via Node's built-in node:sqlite,
|
|
3
|
+
* zero extra dependencies.
|
|
3
4
|
*
|
|
4
|
-
* Uses the same `
|
|
5
|
+
* Uses the same `node:sqlite` (DatabaseSync) the ORM's SQLite adapter uses —
|
|
6
|
+
* no third-party driver, nothing to install.
|
|
5
7
|
* Stores sessions in a `tina4_session` table with JSON data and expiry.
|
|
6
8
|
*
|
|
7
9
|
* Configure via environment variables:
|
|
8
10
|
* TINA4_DATABASE_URL (default: "sqlite:///data/tina4_sessions.db")
|
|
9
|
-
*
|
|
10
|
-
* The handler dynamically imports `better-sqlite3` and throws a clear
|
|
11
|
-
* error if the package is not installed.
|
|
12
11
|
*/
|
|
13
12
|
import { DatabaseSync } from "node:sqlite";
|
|
14
13
|
import type { SessionHandler } from "../session.js";
|
|
@@ -35,7 +34,7 @@ export interface DatabaseSessionConfig {
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
|
-
* Database session handler using
|
|
37
|
+
* Database session handler using node:sqlite (synchronous SQLite).
|
|
39
38
|
*
|
|
40
39
|
* Stores session data as JSON in a `tina4_session` table.
|
|
41
40
|
* Expiry is checked on read; expired rows are cleaned up lazily.
|
|
@@ -316,11 +316,14 @@ function resolveVar(expr, context) {
|
|
|
316
316
|
return value;
|
|
317
317
|
}
|
|
318
318
|
function findOutsideQuotes(expr, needle) {
|
|
319
|
+
if (!expr.includes(needle)) return -1;
|
|
319
320
|
let inQuote = null;
|
|
320
321
|
let depth = 0;
|
|
321
322
|
let bracketDepth = 0;
|
|
322
323
|
let i = 0;
|
|
323
|
-
|
|
324
|
+
const needleLen = needle.length;
|
|
325
|
+
const lastStart = expr.length - needleLen;
|
|
326
|
+
while (i <= lastStart) {
|
|
324
327
|
const ch = expr[i];
|
|
325
328
|
if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
|
|
326
329
|
if (inQuote === null) {
|
|
@@ -339,7 +342,7 @@ function findOutsideQuotes(expr, needle) {
|
|
|
339
342
|
else if (ch === ")") depth--;
|
|
340
343
|
else if (ch === "[") bracketDepth++;
|
|
341
344
|
else if (ch === "]") bracketDepth--;
|
|
342
|
-
if (depth === 0 && bracketDepth === 0 && expr.
|
|
345
|
+
if (depth === 0 && bracketDepth === 0 && expr.startsWith(needle, i)) {
|
|
343
346
|
return i;
|
|
344
347
|
}
|
|
345
348
|
i++;
|
|
@@ -347,13 +350,16 @@ function findOutsideQuotes(expr, needle) {
|
|
|
347
350
|
return -1;
|
|
348
351
|
}
|
|
349
352
|
function splitOutsideQuotes(expr, sep) {
|
|
353
|
+
if (!expr.includes(sep)) return [expr];
|
|
350
354
|
const parts = [];
|
|
351
355
|
let currentStart = 0;
|
|
352
356
|
let inQuote = null;
|
|
353
357
|
let depth = 0;
|
|
354
358
|
let bracketDepth = 0;
|
|
355
359
|
let i = 0;
|
|
356
|
-
|
|
360
|
+
const sepLen = sep.length;
|
|
361
|
+
const lastStart = expr.length - sepLen;
|
|
362
|
+
while (i <= lastStart) {
|
|
357
363
|
const ch = expr[i];
|
|
358
364
|
if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
|
|
359
365
|
if (inQuote === null) {
|
|
@@ -372,9 +378,9 @@ function splitOutsideQuotes(expr, sep) {
|
|
|
372
378
|
else if (ch === ")") depth--;
|
|
373
379
|
else if (ch === "[") bracketDepth++;
|
|
374
380
|
else if (ch === "]") bracketDepth--;
|
|
375
|
-
if (depth === 0 && bracketDepth === 0 && expr.
|
|
381
|
+
if (depth === 0 && bracketDepth === 0 && expr.startsWith(sep, i)) {
|
|
376
382
|
parts.push(expr.slice(currentStart, i));
|
|
377
|
-
i +=
|
|
383
|
+
i += sepLen;
|
|
378
384
|
currentStart = i;
|
|
379
385
|
continue;
|
|
380
386
|
}
|
|
@@ -444,11 +444,23 @@ function resolveVar(expr: string, context: Record<string, unknown>): unknown {
|
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
function findOutsideQuotes(expr: string, needle: string): number {
|
|
447
|
+
// Fast path. This is the hottest function in a render: profiling the Python
|
|
448
|
+
// twin showed 415,200 calls and 53% of render time for one 20-row loop
|
|
449
|
+
// template, and the overwhelming majority return -1 because the needle simply
|
|
450
|
+
// is not in the expression. includes() is a native scan, so bailing here skips
|
|
451
|
+
// the whole JS character loop. Exact, not a heuristic: a needle absent from
|
|
452
|
+
// the string cannot be present outside quotes either.
|
|
453
|
+
if (!expr.includes(needle)) return -1;
|
|
454
|
+
|
|
447
455
|
let inQuote: string | null = null;
|
|
448
456
|
let depth = 0;
|
|
449
457
|
let bracketDepth = 0;
|
|
450
458
|
let i = 0;
|
|
451
|
-
|
|
459
|
+
// Hoisted out of the loop condition -- both lengths were recomputed on every
|
|
460
|
+
// single iteration.
|
|
461
|
+
const needleLen = needle.length;
|
|
462
|
+
const lastStart = expr.length - needleLen;
|
|
463
|
+
while (i <= lastStart) {
|
|
452
464
|
const ch = expr[i];
|
|
453
465
|
if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
|
|
454
466
|
if (inQuote === null) {
|
|
@@ -464,7 +476,9 @@ function findOutsideQuotes(expr: string, needle: string): number {
|
|
|
464
476
|
else if (ch === ")") depth--;
|
|
465
477
|
else if (ch === "[") bracketDepth++;
|
|
466
478
|
else if (ch === "]") bracketDepth--;
|
|
467
|
-
|
|
479
|
+
// startsWith(needle, i) rather than slice(i, i + needleLen) === needle: the
|
|
480
|
+
// slice allocated a throwaway string at every character position.
|
|
481
|
+
if (depth === 0 && bracketDepth === 0 && expr.startsWith(needle, i)) {
|
|
468
482
|
return i;
|
|
469
483
|
}
|
|
470
484
|
i++;
|
|
@@ -473,13 +487,20 @@ function findOutsideQuotes(expr: string, needle: string): number {
|
|
|
473
487
|
}
|
|
474
488
|
|
|
475
489
|
function splitOutsideQuotes(expr: string, sep: string): string[] {
|
|
490
|
+
// Fast path, same reasoning as findOutsideQuotes: no separator anywhere means
|
|
491
|
+
// no split, and includes() is a native scan versus a JS character loop.
|
|
492
|
+
if (!expr.includes(sep)) return [expr];
|
|
493
|
+
|
|
476
494
|
const parts: string[] = [];
|
|
477
495
|
let currentStart = 0;
|
|
478
496
|
let inQuote: string | null = null;
|
|
479
497
|
let depth = 0;
|
|
480
498
|
let bracketDepth = 0;
|
|
481
499
|
let i = 0;
|
|
482
|
-
|
|
500
|
+
// Hoisted out of the loop condition -- recomputed every iteration before.
|
|
501
|
+
const sepLen = sep.length;
|
|
502
|
+
const lastStart = expr.length - sepLen;
|
|
503
|
+
while (i <= lastStart) {
|
|
483
504
|
const ch = expr[i];
|
|
484
505
|
if ((ch === '"' || ch === "'") && depth === 0 && bracketDepth === 0) {
|
|
485
506
|
if (inQuote === null) {
|
|
@@ -495,9 +516,10 @@ function splitOutsideQuotes(expr: string, sep: string): string[] {
|
|
|
495
516
|
else if (ch === ")") depth--;
|
|
496
517
|
else if (ch === "[") bracketDepth++;
|
|
497
518
|
else if (ch === "]") bracketDepth--;
|
|
498
|
-
|
|
519
|
+
// startsWith avoids allocating a throwaway slice at every position.
|
|
520
|
+
if (depth === 0 && bracketDepth === 0 && expr.startsWith(sep, i)) {
|
|
499
521
|
parts.push(expr.slice(currentStart, i));
|
|
500
|
-
i +=
|
|
522
|
+
i += sepLen;
|
|
501
523
|
currentStart = i;
|
|
502
524
|
continue;
|
|
503
525
|
}
|