tina4-nodejs 3.13.97 → 3.13.99
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 +60 -25
- package/package.json +1 -2
- package/packages/cli/dist/bin.js +20620 -18995
- package/packages/cli/src/bin.ts +28 -71
- package/packages/cli/src/commands/migrate.ts +36 -75
- package/packages/cli/src/commands/migrateRollback.ts +10 -1
- package/packages/cli/src/commands/test.ts +92 -21
- package/packages/core/dist/index.js +20459 -18815
- package/packages/core/public/js/tina4-dev-admin.min.js +23 -19
- package/packages/core/src/ai.ts +28 -12
- package/packages/core/src/api.ts +13 -5
- package/packages/core/src/background.ts +9 -3
- package/packages/core/src/devAdmin.ts +135 -20
- package/packages/core/src/dispatchPipeline.ts +185 -1
- package/packages/core/src/docs.ts +33 -5
- package/packages/core/src/env.ts +1 -1
- package/packages/core/src/errorOverlay.ts +39 -48
- package/packages/core/src/fakeData.ts +15 -0
- package/packages/core/src/index.ts +17 -6
- package/packages/core/src/logger.ts +892 -572
- package/packages/core/src/mcp.ts +9 -1
- package/packages/core/src/messenger.ts +31 -4
- package/packages/core/src/middleware.ts +169 -43
- package/packages/core/src/portTakeover.ts +232 -0
- package/packages/core/src/request.ts +57 -8
- package/packages/core/src/response.ts +67 -0
- package/packages/core/src/router.ts +35 -7
- package/packages/core/src/server.ts +450 -190
- package/packages/core/src/static.ts +81 -12
- package/packages/core/src/testClient.ts +126 -137
- package/packages/core/src/testing.ts +16 -12
- package/packages/core/src/types.ts +21 -9
- package/packages/core/src/version.ts +66 -0
- package/packages/core/src/websocket.ts +2 -2
- package/packages/core/src/websocketBackplane.ts +2 -2
- package/packages/frond/dist/index.js +31 -13
- package/packages/frond/src/engine.ts +39 -7
- package/packages/orm/dist/index.js +10879 -9258
- package/packages/orm/src/adapters/firebird.ts +200 -27
- package/packages/orm/src/adapters/mongodb.ts +160 -10
- package/packages/orm/src/adapters/mssql.ts +38 -11
- package/packages/orm/src/adapters/mysql.ts +24 -1
- package/packages/orm/src/adapters/odbc.ts +127 -29
- package/packages/orm/src/adapters/postgres.ts +18 -0
- package/packages/orm/src/adapters/sqlite.ts +93 -14
- package/packages/orm/src/autoCrud.ts +72 -8
- package/packages/orm/src/baseModel.ts +323 -71
- package/packages/orm/src/cachedDatabase.ts +48 -1
- package/packages/orm/src/database.ts +162 -59
- package/packages/orm/src/fakeData.ts +6 -2
- package/packages/orm/src/index.ts +4 -1
- package/packages/orm/src/migration.ts +95 -52
- package/packages/orm/src/query.ts +16 -4
- package/packages/orm/src/seeder.ts +43 -25
- package/packages/orm/src/sqlTranslator.ts +104 -19
- package/packages/orm/src/types.ts +97 -21
- package/packages/orm/src/validation.ts +5 -1
- package/packages/swagger/dist/index.js +3 -2
- package/packages/swagger/src/generator.ts +19 -4
- package/packages/swagger/src/ui.ts +6 -4
- package/types/cli/src/bin.d.ts +0 -22
- package/types/core/src/api.d.ts +11 -4
- package/types/core/src/background.d.ts +5 -2
- package/types/core/src/devAdmin.d.ts +35 -0
- package/types/core/src/dispatchPipeline.d.ts +41 -1
- package/types/core/src/errorOverlay.d.ts +13 -13
- package/types/core/src/index.d.ts +9 -6
- package/types/core/src/logger.d.ts +111 -185
- package/types/core/src/middleware.d.ts +40 -5
- package/types/core/src/portTakeover.d.ts +50 -0
- package/types/core/src/request.d.ts +15 -0
- package/types/core/src/response.d.ts +29 -0
- package/types/core/src/server.d.ts +92 -0
- package/types/core/src/testClient.d.ts +29 -3
- package/types/core/src/testing.d.ts +16 -12
- package/types/core/src/types.d.ts +21 -9
- package/types/core/src/version.d.ts +11 -0
- package/types/core/src/websocketBackplane.d.ts +1 -1
- package/types/frond/src/engine.d.ts +10 -0
- package/types/orm/src/adapters/firebird.d.ts +61 -2
- package/types/orm/src/adapters/mongodb.d.ts +20 -0
- package/types/orm/src/adapters/mssql.d.ts +11 -0
- package/types/orm/src/adapters/mysql.d.ts +11 -0
- package/types/orm/src/adapters/odbc.d.ts +35 -4
- package/types/orm/src/adapters/postgres.d.ts +11 -0
- package/types/orm/src/adapters/sqlite.d.ts +23 -4
- package/types/orm/src/baseModel.d.ts +45 -25
- package/types/orm/src/cachedDatabase.d.ts +27 -1
- package/types/orm/src/database.d.ts +56 -6
- package/types/orm/src/index.d.ts +3 -2
- package/types/orm/src/migration.d.ts +23 -5
- package/types/orm/src/query.d.ts +3 -0
- package/types/orm/src/seeder.d.ts +15 -2
- package/types/orm/src/sqlTranslator.d.ts +17 -4
- package/types/orm/src/types.d.ts +75 -16
- package/packages/core/src/errorOverlay.test.ts +0 -122
|
@@ -144,8 +144,21 @@ export declare class CachedDatabaseAdapter implements DatabaseAdapter {
|
|
|
144
144
|
private backendSetRows;
|
|
145
145
|
private backendGetOne;
|
|
146
146
|
private backendSetOne;
|
|
147
|
+
/** ADR-0044 required capability — delegates to the wrapped adapter. */
|
|
148
|
+
connect(): void | Promise<void>;
|
|
149
|
+
/** ADR-0044 required capability — delegates to the wrapped adapter. */
|
|
150
|
+
getDatabaseType(): string;
|
|
151
|
+
/**
|
|
152
|
+
* ADR-0044 required capability — a native boolean, readable and writable.
|
|
153
|
+
* A getter/setter pair (not a plain field) so it genuinely delegates to the
|
|
154
|
+
* wrapped adapter rather than drifting out of sync with its real setting.
|
|
155
|
+
*/
|
|
156
|
+
get autocommit(): boolean;
|
|
157
|
+
set autocommit(value: boolean);
|
|
158
|
+
get supportsAtomicBatch(): boolean;
|
|
159
|
+
set supportsAtomicBatch(value: boolean);
|
|
147
160
|
execute(sql: string, params?: unknown[]): unknown;
|
|
148
|
-
executeMany(sql: string, paramsList: unknown[][]): {
|
|
161
|
+
executeMany(sql: string, paramsList: unknown[][]): import("./types.js").DatabaseResult | {
|
|
149
162
|
totalAffected: number;
|
|
150
163
|
lastId?: number | bigint;
|
|
151
164
|
};
|
|
@@ -173,6 +186,19 @@ export declare class CachedDatabaseAdapter implements DatabaseAdapter {
|
|
|
173
186
|
fetchOneAsync<T = Record<string, unknown>>(sql: string, params?: unknown[], noCache?: boolean): Promise<T | null>;
|
|
174
187
|
queryAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
175
188
|
executeAsync(sql: string, params?: unknown[]): Promise<unknown>;
|
|
189
|
+
/**
|
|
190
|
+
* ADR-0044: the async passthrough executeMany() itself was missing (unlike
|
|
191
|
+
* its executeAsync/insertAsync siblings above), so adapterExecuteMany()'s
|
|
192
|
+
* `(adapter as any).executeManyAsync` check found nothing on THIS wrapper
|
|
193
|
+
* and fell through to the synchronous executeMany() below — which forwards
|
|
194
|
+
* to the wrapped adapter's OWN sync executeMany(), the throwing "Use
|
|
195
|
+
* executeManyAsync()" stub on every async-native adapter (Postgres/MySQL/
|
|
196
|
+
* MSSQL/Firebird/Mongo). Real bug, caught by executeManyFacadeTxn.test.ts.
|
|
197
|
+
*/
|
|
198
|
+
executeManyAsync(sql: string, paramsList: unknown[][]): Promise<import("./types.js").DatabaseResult | {
|
|
199
|
+
totalAffected: number;
|
|
200
|
+
lastId?: number | bigint;
|
|
201
|
+
}>;
|
|
176
202
|
insertAsync(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseResult>;
|
|
177
203
|
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
178
204
|
deleteAsync(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[], params?: unknown[]): Promise<DatabaseResult>;
|
|
@@ -31,6 +31,32 @@ export declare function adapterFetch<T = Record<string, unknown>>(adapter: Datab
|
|
|
31
31
|
export declare function adapterQuery<T = Record<string, unknown>>(adapter: DatabaseAdapter, sql: string, params?: unknown[]): Promise<T[]>;
|
|
32
32
|
export declare function adapterFetchOne<T = Record<string, unknown>>(adapter: DatabaseAdapter, sql: string, params?: unknown[]): Promise<T | null>;
|
|
33
33
|
export declare function adapterExecute(adapter: DatabaseAdapter, sql: string, params?: unknown[]): Promise<unknown>;
|
|
34
|
+
/**
|
|
35
|
+
* ADR-0044: the adapter-level batch primitive, called exactly once by
|
|
36
|
+
* Database#executeMany (never looped) — one aggregate DatabaseResult for the
|
|
37
|
+
* whole batch. Normalises whichever native shape an adapter returns: SQLite's
|
|
38
|
+
* `{success, affectedRows, lastId}` (the shared write shape already used by
|
|
39
|
+
* insert/update/delete) or an async-native adapter's `{totalAffected, lastId}`
|
|
40
|
+
* (pre-ADR-0044 shape, not yet unified per-adapter — normalised HERE at the
|
|
41
|
+
* one chokepoint every public write flows through, so the facade's contract
|
|
42
|
+
* is uniform without touching each of the five adapter files' internals).
|
|
43
|
+
*/
|
|
44
|
+
export declare function adapterExecuteMany(adapter: DatabaseAdapter, sql: string, paramsList: unknown[][]): Promise<import("./types.js").DatabaseResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Insert one row (or a batch) through the adapter's OWN native insert path
|
|
47
|
+
* (each adapter's `buildInsert()`/`Dialect`, feature 3's SQL builder
|
|
48
|
+
* consolidation) instead of hand-built SQL. This is the ONLY correct way to
|
|
49
|
+
* insert into a caller-named table/columns: Firebird's Dialect quotes only
|
|
50
|
+
* when it has to (an unquoted identifier folds to UPPERCASE, so quoting a
|
|
51
|
+
* lower-case name makes it unfindable — SQL error -204 "Table unknown"),
|
|
52
|
+
* while PostgreSQL/MSSQL/SQLite quote unconditionally. A caller that hand-
|
|
53
|
+
* quotes with one fixed style (e.g. always `"col"`) works on three engines
|
|
54
|
+
* and silently breaks on the fourth. seedTable()/seedOrm() route through
|
|
55
|
+
* this so their engine portability matches insert()/insertAsync()'s, which
|
|
56
|
+
* the write-path + provider contract suites already prove on all four real
|
|
57
|
+
* engines (features 9/10/11/12).
|
|
58
|
+
*/
|
|
59
|
+
export declare function adapterInsert(adapter: DatabaseAdapter, table: string, data: Record<string, unknown>): Promise<unknown>;
|
|
34
60
|
export declare function adapterStartTransaction(adapter: DatabaseAdapter): Promise<void>;
|
|
35
61
|
export declare function adapterCommit(adapter: DatabaseAdapter): Promise<void>;
|
|
36
62
|
export declare function adapterRollback(adapter: DatabaseAdapter): Promise<void>;
|
|
@@ -394,7 +420,19 @@ export declare class Database {
|
|
|
394
420
|
* `try/catch` and convert, rather than testing the return value.
|
|
395
421
|
*/
|
|
396
422
|
execute(sql: string, params?: unknown[]): Promise<boolean | unknown>;
|
|
397
|
-
/**
|
|
423
|
+
/**
|
|
424
|
+
* Insert one row (object) or a batch of rows (array of objects) into a table.
|
|
425
|
+
*
|
|
426
|
+
* FAIL LOUD, matching update()/delete()/truncate(): a real driver failure
|
|
427
|
+
* (e.g. a NOT NULL / UNIQUE constraint violation) throws rather than
|
|
428
|
+
* resolving to `{ success: false, affectedRows: 0 }`. The async adapters
|
|
429
|
+
* (Postgres/MySQL/MSSQL/Firebird) already throw directly from insertAsync();
|
|
430
|
+
* SQLiteAdapter.insert() is the one adapter that CATCHES the driver error
|
|
431
|
+
* and returns a `{ success: false, error }` result instead (its own
|
|
432
|
+
* documented contract for the synchronous path) — assertWrote is what
|
|
433
|
+
* converts that into the same thrown DatabaseException every other engine
|
|
434
|
+
* already produces, exactly as it already does for update/delete/truncate.
|
|
435
|
+
*/
|
|
398
436
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseWriteResult>;
|
|
399
437
|
/**
|
|
400
438
|
* The table's primary-key column, introspected once and cached.
|
|
@@ -487,16 +525,28 @@ export declare class Database {
|
|
|
487
525
|
nullable?: boolean;
|
|
488
526
|
default?: unknown;
|
|
489
527
|
primaryKey?: boolean;
|
|
528
|
+
primaryKeyPosition?: number | null;
|
|
490
529
|
}[]>;
|
|
491
530
|
/**
|
|
492
|
-
* Execute a SQL statement with multiple parameter sets
|
|
493
|
-
* Wraps
|
|
531
|
+
* Execute a SQL statement with multiple parameter sets as ONE aggregate
|
|
532
|
+
* batch (ADR-0044). Wraps the single delegated call in a transaction for
|
|
533
|
+
* atomicity — never loops #execute itself.
|
|
534
|
+
*
|
|
535
|
+
* BREAKING (ADR-0044, pre-3.14.0): used to return one result PER ROW
|
|
536
|
+
* (`unknown[]`, callers indexed into it) built by the FACADE looping
|
|
537
|
+
* execute()/adapterExecute() per chunk or per row. It now delegates to the
|
|
538
|
+
* adapter's OWN executeMany/executeManyAsync exactly once (DBA-D02: facade
|
|
539
|
+
* delegates once, never a facade row loop) and returns the SAME shared
|
|
540
|
+
* DatabaseResult shape insert()/update()/delete() already return
|
|
541
|
+
* ({success, affectedRows, lastId}) — affectedRows is the total ROW count,
|
|
542
|
+
* never the number of chunks/statements. A caller that indexed into the old
|
|
543
|
+
* per-row array must switch to inspecting the aggregate result.
|
|
494
544
|
*
|
|
495
545
|
* @param sql - The SQL statement with parameter placeholders.
|
|
496
|
-
* @param paramSets - Array of parameter arrays, one per
|
|
497
|
-
* @returns
|
|
546
|
+
* @param paramSets - Array of parameter arrays, one per row.
|
|
547
|
+
* @returns The aggregate DatabaseResult for the whole batch.
|
|
498
548
|
*/
|
|
499
|
-
executeMany(sql: string, paramSets?: unknown[][]): Promise<
|
|
549
|
+
executeMany(sql: string, paramSets?: unknown[][]): Promise<DatabaseWriteResult>;
|
|
500
550
|
/** Return the last execute() error message, or null. */
|
|
501
551
|
getError(): string | null;
|
|
502
552
|
/**
|
package/types/orm/src/index.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export type { FieldType, FieldDefinition, ModelDefinition, DatabaseAdapter, DatabaseResult as DatabaseWriteResult, ColumnInfo, QueryOptions, RelationshipDefinition, } from "./types.js";
|
|
2
|
+
export { REQUIRED_ADAPTER_CAPABILITIES, NOT_REQUIRED_ON_ADAPTER } from "./types.js";
|
|
2
3
|
export { DatabaseResult } from "./databaseResult.js";
|
|
3
4
|
export type { ColumnInfoResult } from "./databaseResult.js";
|
|
4
5
|
export { Database, initDatabase, getAdapter, setAdapter, bindDatabase, createAdapterFromUrl, closeDatabase, parseDatabaseUrl, setNamedAdapter, getNamedAdapter, resolveDbPool, stripTrailingSemicolons, wrapWithCache, resetRequestCaches } from "./database.js";
|
|
5
|
-
export { adapterFetch, adapterQuery, adapterFetchOne, adapterExecute, adapterStartTransaction, adapterCommit, adapterRollback, adapterTableExists, adapterTables, adapterColumns, adapterCreateTable, extractLastInsertId, } from "./database.js";
|
|
6
|
+
export { adapterFetch, adapterQuery, adapterFetchOne, adapterExecute, adapterInsert, adapterStartTransaction, adapterCommit, adapterRollback, adapterTableExists, adapterTables, adapterColumns, adapterCreateTable, extractLastInsertId, } from "./database.js";
|
|
6
7
|
export type { DatabaseConfig } from "./database.js";
|
|
7
8
|
export { DatabaseUrl, redactCredentials } from "./databaseUrl.js";
|
|
8
9
|
export type { DatabaseEngine } from "./databaseUrl.js";
|
|
9
10
|
export { discoverModels } from "./model.js";
|
|
10
11
|
export type { DiscoveredModel } from "./model.js";
|
|
11
|
-
export { syncModels, ensureMigrationTable, getNextBatch, isMigrationApplied, recordMigration, applyMigration, rollback, getAppliedMigrations, getLastBatchMigrations, removeMigrationRecord, migrate, createMigration, status, Migration, splitStatements, parseSetTerm, normalizeQuotes, sortMigrationFiles, shouldSkipCreateTable, } from "./migration.js";
|
|
12
|
+
export { syncModels, ensureMigrationTable, getNextBatch, isMigrationApplied, recordMigration, applyMigration, rollback, getAppliedMigrations, getLastBatchMigrations, removeMigrationRecord, migrate, createMigration, status, Migration, splitStatements, parseSetTerm, normalizeQuotes, sortMigrationFiles, shouldSkipCreateTable, shouldSkipForFirebird, } from "./migration.js";
|
|
12
13
|
export type { MigrationResult, MigrationStatus } from "./migration.js";
|
|
13
14
|
export { AutoCrud, generateCrudRoutes, crudEligibleModels } from "./autoCrud.js";
|
|
14
15
|
export type { AutoCrudOptions } from "./autoCrud.js";
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from "./types.js";
|
|
2
2
|
import type { DiscoveredModel } from "./model.js";
|
|
3
|
+
/**
|
|
4
|
+
* If stmt is an ALTER TABLE ... ADD on Firebird and the column already exists,
|
|
5
|
+
* returns a skip reason string. Returns null if the statement should execute normally.
|
|
6
|
+
*
|
|
7
|
+
* Exported (like its shouldSkipCreateTable sibling) so it can be driven
|
|
8
|
+
* directly against a REAL Firebird connection in
|
|
9
|
+
* test/migrationContract.test.ts -- no fake adapter needed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function shouldSkipForFirebird(db: DatabaseAdapter, stmt: string): Promise<string | null>;
|
|
3
12
|
/**
|
|
4
13
|
* Make CREATE TABLE idempotent on engines lacking IF NOT EXISTS.
|
|
5
14
|
*
|
|
@@ -53,17 +62,26 @@ export declare function removeMigrationRecord(name: string): Promise<void>;
|
|
|
53
62
|
/**
|
|
54
63
|
* Rollback the last batch of migrations using .down.sql files.
|
|
55
64
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
65
|
+
* FAIL-SAFE (MIG-DEC-02, reuses the Python reference model): for each
|
|
66
|
+
* migration in the last batch (in reverse order), the down artifact must
|
|
67
|
+
* actually run before the tracking record is removed. A MISSING .down.sql
|
|
68
|
+
* (or, on the legacy Map API, no registered down function) or a FAILING down
|
|
69
|
+
* statement now THROWS instead of logging a warning/error and still deleting
|
|
70
|
+
* the record — the old behaviour was the exact MIG-ROLLBACK-DROPS-LEDGER bug:
|
|
71
|
+
* the schema stayed applied but the ledger row vanished, silently untracked.
|
|
72
|
+
* The DELETE runs inside the SAME transaction as the down statements, so a
|
|
73
|
+
* partially-executed down (some statements ran, a later one failed) rolls
|
|
74
|
+
* back too — no half-reversed schema left behind either.
|
|
61
75
|
*
|
|
62
76
|
* @param migrationsDir - Directory containing migration files (default: "migrations")
|
|
63
77
|
* @param delimiter - SQL statement delimiter (default: ";")
|
|
64
78
|
* @returns Array of the down-migration files that were run, e.g.
|
|
65
79
|
* "000001_create_users.down.sql". (The legacy down-FUNCTION Map API returns the
|
|
66
80
|
* bare migration name instead, since no .down.sql file is involved there.)
|
|
81
|
+
* @throws When a migration in the batch has no down artifact, or its down
|
|
82
|
+
* statement(s) fail — the batch stops at that migration; earlier
|
|
83
|
+
* migrations in the SAME call that already rolled back stay rolled back
|
|
84
|
+
* (each is its own transaction).
|
|
67
85
|
*
|
|
68
86
|
* NOTE on return form (intentional, cross-framework): migration return values reflect
|
|
69
87
|
* WHAT each method acted on, so the forms differ by method and that is by design (not
|
package/types/orm/src/query.d.ts
CHANGED
|
@@ -10,5 +10,8 @@ export declare function buildQuery(tableName: string, options: QueryOptions, ext
|
|
|
10
10
|
sql: string;
|
|
11
11
|
countSql: string;
|
|
12
12
|
params: unknown[];
|
|
13
|
+
limit: number;
|
|
14
|
+
offset: number;
|
|
15
|
+
page: number;
|
|
13
16
|
};
|
|
14
17
|
export declare function parseQueryString(query: Record<string, string>): QueryOptions;
|
|
@@ -21,7 +21,18 @@ export interface SeedOptions {
|
|
|
21
21
|
overrides?: Record<string, unknown>;
|
|
22
22
|
/** Delete every existing row in the target before seeding (P2). */
|
|
23
23
|
clear?: boolean;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* PRNG seed for reproducible FakeData output (P3). Honoured by seedOrm and
|
|
26
|
+
* seedModels, which build and seed their own FakeData internally.
|
|
27
|
+
*
|
|
28
|
+
* NOT honoured by seedTable (SEED-TABLE-SEED-INERT, SEED-DEC-01, ratified
|
|
29
|
+
* 2026-08-11 — same principle as the no-op ForeignKeyField on_delete):
|
|
30
|
+
* seedTable has no generators of its own to seed — fieldMap callables are
|
|
31
|
+
* opaque — so this used to be a silent no-op there. Passing it to seedTable
|
|
32
|
+
* now THROWS instead. Build your own `new FakeData(seed)` and close over it
|
|
33
|
+
* in fieldMap: `const fake = new FakeData(42); seedTable(db, table, count,
|
|
34
|
+
* { name: () => fake.name() })`.
|
|
35
|
+
*/
|
|
25
36
|
seed?: number;
|
|
26
37
|
/** Re-raise on the first failed row instead of skipping it (P1). */
|
|
27
38
|
strict?: boolean;
|
|
@@ -63,8 +74,10 @@ export declare function autoFieldMap(db: DatabaseAdapter, table: string, fake?:
|
|
|
63
74
|
* (or a static value). If not provided, no rows are inserted.
|
|
64
75
|
* @param overrides - (legacy positional) Static values applied to every row.
|
|
65
76
|
* Prefer `opts.overrides`.
|
|
66
|
-
* @param opts - Seed options: `{ overrides, clear,
|
|
77
|
+
* @param opts - Seed options: `{ overrides, clear, strict }`. `opts.seed` is
|
|
78
|
+
* NOT honoured here (see {@link SeedOptions.seed}) and throws if supplied.
|
|
67
79
|
* @returns A SeedSummary `{ seeded, failed, errors }`.
|
|
80
|
+
* @throws {Error} If `opts.seed` is defined (SEED-TABLE-SEED-INERT removal).
|
|
68
81
|
*
|
|
69
82
|
* @example
|
|
70
83
|
* const fake = new FakeData();
|
|
@@ -31,18 +31,31 @@ export declare class SQLTranslator {
|
|
|
31
31
|
* Does NOT convert if OFFSET is present (TOP doesn't support it).
|
|
32
32
|
*/
|
|
33
33
|
static limitToTop(sql: string): string;
|
|
34
|
+
/** Replace string literals, quoted identifiers and comments with opaque
|
|
35
|
+
* `\x00N\x00` tokens (doubled-quote escapes handled). */
|
|
36
|
+
private static maskLiterals;
|
|
37
|
+
/** Inverse of maskLiterals. */
|
|
38
|
+
private static restoreLiterals;
|
|
39
|
+
private static readonly PRIMARY;
|
|
34
40
|
/**
|
|
35
|
-
* Convert
|
|
41
|
+
* Convert `||` string concatenation to `CONCAT(...)` for MySQL/MSSQL.
|
|
36
42
|
*
|
|
37
|
-
*
|
|
43
|
+
* Rewrites ONLY `||` operators joining expression operands OUTSIDE any string
|
|
44
|
+
* literal or comment, and only the operand chain — never the whole statement:
|
|
45
|
+
* SELECT a || b FROM t -> SELECT CONCAT(a, b) FROM t
|
|
46
|
+
* WHERE data = 'a||b' -> WHERE data = 'a||b' (literal untouched)
|
|
38
47
|
*/
|
|
39
48
|
static concatPipesToFunc(sql: string): string;
|
|
40
49
|
/**
|
|
41
|
-
* Convert TRUE/FALSE to 1/0 for engines without boolean type
|
|
50
|
+
* Convert bare TRUE/FALSE to 1/0 for engines without a boolean type. A
|
|
51
|
+
* TRUE/FALSE INSIDE a string literal is data and is left untouched
|
|
52
|
+
* (`WHERE label = 'TRUE'` is preserved).
|
|
42
53
|
*/
|
|
43
54
|
static booleanToInt(sql: string): string;
|
|
44
55
|
/**
|
|
45
|
-
* Convert ILIKE to LOWER() LIKE LOWER() for engines
|
|
56
|
+
* Convert `col ILIKE pattern` to `LOWER(col) LIKE LOWER(pattern)` for engines
|
|
57
|
+
* without ILIKE. The pattern operand is captured whole (a multi-word
|
|
58
|
+
* `'%two words%'` survives) and an ILIKE INSIDE a string literal is untouched.
|
|
46
59
|
*/
|
|
47
60
|
static ilikeToLike(sql: string): string;
|
|
48
61
|
/**
|
package/types/orm/src/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type FieldType = "string" | "integer" | "number" | "numeric" | "boolean" | "datetime" | "text" | "json" | "foreignKey";
|
|
1
|
+
export type FieldType = "string" | "integer" | "number" | "numeric" | "decimal" | "boolean" | "datetime" | "text" | "json" | "foreignKey";
|
|
2
2
|
export interface FieldDefinition {
|
|
3
3
|
type: FieldType;
|
|
4
4
|
primaryKey?: boolean;
|
|
@@ -10,6 +10,15 @@ export interface FieldDefinition {
|
|
|
10
10
|
min?: number;
|
|
11
11
|
max?: number;
|
|
12
12
|
pattern?: string;
|
|
13
|
+
/**
|
|
14
|
+
* For type "decimal": the fixed precision/scale of a real DECIMAL(p, s)
|
|
15
|
+
* column. `number`/`numeric` stay a floating type (the documented money
|
|
16
|
+
* guidance); a `decimal` field keeps the declared scale in the COLUMN, so
|
|
17
|
+
* createTable emits DECIMAL(precision, scale) — identical on
|
|
18
|
+
* PostgreSQL/MySQL/MSSQL/Firebird/SQLite. Default 10 / 2 when omitted.
|
|
19
|
+
*/
|
|
20
|
+
precision?: number;
|
|
21
|
+
scale?: number;
|
|
13
22
|
/** For type "foreignKey": the referenced model name (string) */
|
|
14
23
|
references?: string;
|
|
15
24
|
/** For type "foreignKey": override the has-many property name on the referenced model */
|
|
@@ -50,6 +59,12 @@ export interface ColumnInfo {
|
|
|
50
59
|
nullable?: boolean;
|
|
51
60
|
default?: unknown;
|
|
52
61
|
primaryKey?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* ADR-0044 amendment (Feature 5 Decision 7, 2026-08-10): null for a
|
|
64
|
+
* non-key column; for a composite key this is the 1-based DECLARED
|
|
65
|
+
* PRIMARY KEY (...) order, not table-column order.
|
|
66
|
+
*/
|
|
67
|
+
primaryKeyPosition?: number | null;
|
|
53
68
|
}
|
|
54
69
|
export interface DatabaseResult {
|
|
55
70
|
success: boolean;
|
|
@@ -57,44 +72,88 @@ export interface DatabaseResult {
|
|
|
57
72
|
lastId?: number | bigint | string;
|
|
58
73
|
error?: string;
|
|
59
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* ADR-0044 (feature 3, plan/v3/fixtures/adapter_contract.json): the exact
|
|
77
|
+
* fourteen adapter capabilities every DatabaseAdapter implementation must
|
|
78
|
+
* provide (DBA-S01). Kept as data so the conformance suite can check it
|
|
79
|
+
* without a second hand-maintained copy of the list.
|
|
80
|
+
*/
|
|
81
|
+
export declare const REQUIRED_ADAPTER_CAPABILITIES: readonly ["connect", "close", "getDatabaseType", "execute", "executeMany", "fetch", "fetchOne", "startTransaction", "commit", "rollback", "autocommit", "getTables", "getColumns", "tableExists"];
|
|
82
|
+
/**
|
|
83
|
+
* ADR-0044 NOT_REQUIRED_ON_ADAPTER (DBA-S03): engine-neutral composition that
|
|
84
|
+
* the adapter CONTRACT does not require. Node keeps these as REQUIRED
|
|
85
|
+
* TypeScript interface members anyway (unlike Python/PHP/Ruby's stricter
|
|
86
|
+
* runtime reflection) because `database.ts`/`cachedDatabase.ts` call them
|
|
87
|
+
* directly at dozens of sites with no optional-chaining guard, so making them
|
|
88
|
+
* TS-optional ripples into a much larger refactor than this pass covers;
|
|
89
|
+
* this constant records the ADR's INTENT for the conformance suite to check
|
|
90
|
+
* even though the compiler will not enforce their absence.
|
|
91
|
+
*/
|
|
92
|
+
export declare const NOT_REQUIRED_ON_ADAPTER: readonly ["query", "insert", "update", "delete", "truncate", "fetchAll", "createTable", "addColumn", "lastInsertId", "error", "sqlTranslation"];
|
|
60
93
|
export interface DatabaseAdapter {
|
|
94
|
+
/** Connect (ADR-0044 canonical lifecycle name). May be sync or async. */
|
|
95
|
+
connect(): void | Promise<void>;
|
|
96
|
+
/** Return the canonical, credential-free engine name ("sqlite", "postgres", ...). */
|
|
97
|
+
getDatabaseType(): string;
|
|
61
98
|
/** Execute a statement (INSERT, UPDATE, DELETE, DDL). */
|
|
62
99
|
execute(sql: string, params?: unknown[]): unknown;
|
|
63
|
-
/**
|
|
64
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Execute a single SQL statement with multiple parameter sets as ONE
|
|
102
|
+
* aggregate result (ADR-0044). The shared write shape `DatabaseResult`
|
|
103
|
+
* ({success, affectedRows, lastId?}) is the target; the pre-ADR-0044 async
|
|
104
|
+
* adapters (Postgres/MySQL/MSSQL/Firebird/Mongo) still return their
|
|
105
|
+
* original `{totalAffected, lastId?}` shape internally today — the union
|
|
106
|
+
* covers both while `adapterExecuteMany()` in database.ts normalises
|
|
107
|
+
* whichever shape it receives at the one chokepoint every public batch
|
|
108
|
+
* write flows through.
|
|
109
|
+
*/
|
|
110
|
+
executeMany(sql: string, paramsList: unknown[][]): DatabaseResult | {
|
|
65
111
|
totalAffected: number;
|
|
66
112
|
lastId?: number | bigint;
|
|
67
113
|
};
|
|
68
|
-
/**
|
|
69
|
-
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
|
|
70
|
-
/** Fetch rows with optional pagination (limit/skip). */
|
|
114
|
+
/** Fetch rows with optional pagination (limit/skip). Native list, no envelope. */
|
|
71
115
|
fetch<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number): T[];
|
|
72
|
-
/** Fetch a single row or null. */
|
|
116
|
+
/** Fetch a single row or null. No pagination count probe. */
|
|
73
117
|
fetchOne<T = Record<string, unknown>>(sql: string, params?: unknown[]): T | null;
|
|
74
|
-
/** Insert one or more rows into a table, returns result with lastId. */
|
|
75
|
-
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
76
|
-
/** Update rows in a table matching filter (object or string WHERE), returns affected row count. */
|
|
77
|
-
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): DatabaseResult;
|
|
78
|
-
/** Delete rows from a table matching filter (object, string WHERE, or array of objects). */
|
|
79
|
-
delete(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[], params?: unknown[]): DatabaseResult;
|
|
80
118
|
/** Start a transaction. */
|
|
81
119
|
startTransaction(): void;
|
|
82
120
|
/** Commit the current transaction. */
|
|
83
121
|
commit(): void;
|
|
84
122
|
/** Rollback the current transaction. */
|
|
85
123
|
rollback(): void;
|
|
124
|
+
/**
|
|
125
|
+
* Native boolean, readable and writable. A plain field is the idiomatic JS
|
|
126
|
+
* shape for "readable and writable" — no getter/setter ceremony needed.
|
|
127
|
+
*/
|
|
128
|
+
autocommit: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* ADR-0044 / DBA-P02: whether this adapter's deployment can guarantee an
|
|
131
|
+
* atomic multi-row batch write. Every built-in adapter defaults true (see
|
|
132
|
+
* each adapter's field initializer); a deployment that genuinely cannot (a
|
|
133
|
+
* standalone MongoDB without a replica set is the motivating real case)
|
|
134
|
+
* sets this false so executeMany rejects BEFORE the first write.
|
|
135
|
+
*/
|
|
136
|
+
supportsAtomicBatch?: boolean;
|
|
86
137
|
/** List all tables in the database. */
|
|
87
138
|
getTables(): string[];
|
|
88
139
|
/** List columns with types for a table. */
|
|
89
140
|
getColumns(table: string): ColumnInfo[];
|
|
90
|
-
/** Get the last inserted id (auto-increment integer, or a UUID/string PK). */
|
|
91
|
-
lastInsertId(): number | bigint | string | null;
|
|
92
141
|
/** Close the connection. */
|
|
93
142
|
close(): void;
|
|
94
143
|
/** Check if a table exists. */
|
|
95
144
|
tableExists(name: string): boolean;
|
|
96
|
-
/**
|
|
145
|
+
/** Insert one or more rows into a table, returns result with lastId. */
|
|
146
|
+
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
147
|
+
/** Update rows in a table matching filter (object or string WHERE), returns affected row count. */
|
|
148
|
+
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): DatabaseResult;
|
|
149
|
+
/** Delete rows from a table matching filter (object, string WHERE, or array of objects). */
|
|
150
|
+
delete(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[], params?: unknown[]): DatabaseResult;
|
|
151
|
+
/** Query rows (legacy convenience, superseded by fetch). */
|
|
152
|
+
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
|
|
153
|
+
/** Create a table from field definitions (legacy, DDL composition lives above the adapter). */
|
|
97
154
|
createTable(name: string, columns: Record<string, FieldDefinition>): void;
|
|
155
|
+
/** Get the last inserted id (legacy — prefer DatabaseResult.lastId from execute/executeMany). */
|
|
156
|
+
lastInsertId(): number | bigint | string | null;
|
|
98
157
|
/** Get raw column info (legacy, used by migration). */
|
|
99
158
|
getTableColumns?(name: string): Array<{
|
|
100
159
|
name: string;
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for errorOverlay module.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { renderErrorOverlay, renderProductionError, isDebugMode } from "./errorOverlay.js";
|
|
6
|
-
|
|
7
|
-
let passed = 0;
|
|
8
|
-
let failed = 0;
|
|
9
|
-
|
|
10
|
-
function assert(condition: boolean, message: string): void {
|
|
11
|
-
if (condition) {
|
|
12
|
-
passed++;
|
|
13
|
-
} else {
|
|
14
|
-
failed++;
|
|
15
|
-
console.error(` FAIL: ${message}`);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function assertIncludes(html: string, needle: string, label: string): void {
|
|
20
|
-
assert(html.includes(needle), `${label}: expected HTML to include "${needle}"`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function assertNotIncludes(html: string, needle: string, label: string): void {
|
|
24
|
-
assert(!html.includes(needle), `${label}: expected HTML NOT to include "${needle}"`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function makeError(): Error {
|
|
28
|
-
try {
|
|
29
|
-
throw new TypeError("something broke");
|
|
30
|
-
} catch (e) {
|
|
31
|
-
return e as Error;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ── renderErrorOverlay ──
|
|
36
|
-
|
|
37
|
-
const err = makeError();
|
|
38
|
-
const html = renderErrorOverlay(err);
|
|
39
|
-
|
|
40
|
-
assert(typeof html === "string", "returns a string");
|
|
41
|
-
assert(html.startsWith("<!DOCTYPE html>"), "starts with DOCTYPE");
|
|
42
|
-
assertIncludes(html, "TypeError", "contains exception type");
|
|
43
|
-
assertIncludes(html, "something broke", "contains exception message");
|
|
44
|
-
assertIncludes(html, "errorOverlay.test", "contains file path");
|
|
45
|
-
assertIncludes(html, "▶", "contains error line marker");
|
|
46
|
-
assertIncludes(html, "Stack Trace", "contains stack trace section");
|
|
47
|
-
assertIncludes(html, "<details", "uses details element");
|
|
48
|
-
assertIncludes(html, "open", "stack trace open by default");
|
|
49
|
-
assertIncludes(html, "Environment", "contains environment section");
|
|
50
|
-
assertIncludes(html, "Tina4 Node.js", "contains framework name");
|
|
51
|
-
assertIncludes(html, "TINA4_DEBUG", "contains debug reference");
|
|
52
|
-
|
|
53
|
-
// With request
|
|
54
|
-
const htmlWithReq = renderErrorOverlay(err, {
|
|
55
|
-
method: "GET",
|
|
56
|
-
url: "/api/users",
|
|
57
|
-
headers: { host: "localhost" },
|
|
58
|
-
});
|
|
59
|
-
assertIncludes(htmlWithReq, "GET", "request method shown");
|
|
60
|
-
assertIncludes(htmlWithReq, "/api/users", "request URL shown");
|
|
61
|
-
assertIncludes(htmlWithReq, "localhost", "request header shown");
|
|
62
|
-
assertIncludes(htmlWithReq, "Request Details", "request section present");
|
|
63
|
-
|
|
64
|
-
// Without request
|
|
65
|
-
assertNotIncludes(html, "Request Details", "no request section when no request");
|
|
66
|
-
|
|
67
|
-
// XSS escaping
|
|
68
|
-
const xssErr = new Error('<script>alert("xss")</script>');
|
|
69
|
-
const xssHtml = renderErrorOverlay(xssErr);
|
|
70
|
-
assertNotIncludes(xssHtml, "<script>", "escapes script tags");
|
|
71
|
-
assertIncludes(xssHtml, "<script>", "HTML-encodes script tags");
|
|
72
|
-
|
|
73
|
-
// ── renderProductionError ──
|
|
74
|
-
|
|
75
|
-
const prodHtml = renderProductionError();
|
|
76
|
-
assert(prodHtml.startsWith("<!DOCTYPE html>"), "production: starts with DOCTYPE");
|
|
77
|
-
assertIncludes(prodHtml, "500", "production: contains 500");
|
|
78
|
-
assertIncludes(prodHtml, "Internal Server Error", "production: default message");
|
|
79
|
-
assertNotIncludes(prodHtml, "Stack Trace", "production: no stack trace");
|
|
80
|
-
|
|
81
|
-
const prod404 = renderProductionError(404, "Not Found");
|
|
82
|
-
assertIncludes(prod404, "404", "production 404: contains code");
|
|
83
|
-
assertIncludes(prod404, "Not Found", "production 404: contains message");
|
|
84
|
-
|
|
85
|
-
// ── isDebugMode ──
|
|
86
|
-
|
|
87
|
-
const origDebug = process.env.TINA4_DEBUG;
|
|
88
|
-
|
|
89
|
-
process.env.TINA4_DEBUG = "true";
|
|
90
|
-
assert(isDebugMode() === true, "isDebugMode: true => true");
|
|
91
|
-
|
|
92
|
-
process.env.TINA4_DEBUG = "false";
|
|
93
|
-
assert(isDebugMode() === false, "isDebugMode: false => false");
|
|
94
|
-
|
|
95
|
-
process.env.TINA4_DEBUG = "TRUE";
|
|
96
|
-
assert(isDebugMode() === true, "isDebugMode: TRUE (uppercase) => true (case insensitive)");
|
|
97
|
-
|
|
98
|
-
process.env.TINA4_DEBUG = "1";
|
|
99
|
-
assert(isDebugMode() === true, "isDebugMode: 1 => true");
|
|
100
|
-
|
|
101
|
-
process.env.TINA4_DEBUG = "yes";
|
|
102
|
-
assert(isDebugMode() === true, "isDebugMode: yes => true");
|
|
103
|
-
|
|
104
|
-
process.env.TINA4_DEBUG = "on";
|
|
105
|
-
assert(isDebugMode() === true, "isDebugMode: on => true");
|
|
106
|
-
|
|
107
|
-
process.env.TINA4_DEBUG = "0";
|
|
108
|
-
assert(isDebugMode() === false, "isDebugMode: 0 => false");
|
|
109
|
-
|
|
110
|
-
delete process.env.TINA4_DEBUG;
|
|
111
|
-
assert(isDebugMode() === false, "isDebugMode: unset => false");
|
|
112
|
-
|
|
113
|
-
// Restore
|
|
114
|
-
if (origDebug !== undefined) {
|
|
115
|
-
process.env.TINA4_DEBUG = origDebug;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ── Summary ──
|
|
119
|
-
console.log(`\nerrorOverlay tests: ${passed} passed, ${failed} failed`);
|
|
120
|
-
if (failed > 0) {
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|