tina4-nodejs 3.13.98 → 3.13.100
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 +24 -25
- package/package.json +1 -2
- package/packages/cli/dist/bin.js +20698 -18983
- 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 +20561 -18828
- package/packages/core/public/js/tina4-dev-admin.min.js +23 -19
- package/packages/core/src/ai.ts +38 -13
- 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 +149 -49
- package/packages/frond/src/engine.ts +234 -52
- package/packages/orm/dist/index.js +10941 -9231
- 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/ai.d.ts +29 -0
- 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 +60 -8
- 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
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { QueryBuilder } from "./queryBuilder.js";
|
|
2
|
-
import { QueryCache } from "./sqlTranslator.js";
|
|
3
2
|
import type { DatabaseAdapter, FieldDefinition, RelationshipDefinition } from "./types.js";
|
|
4
3
|
/**
|
|
5
4
|
* Convert a snake_case name to camelCase.
|
|
@@ -29,23 +28,6 @@ export declare function toDbFieldValue(def: FieldDefinition | undefined, value:
|
|
|
29
28
|
* null stays null; a non-decodable string keeps its raw form.
|
|
30
29
|
*/
|
|
31
30
|
export declare function fromDbFieldValue(def: FieldDefinition | undefined, value: unknown): unknown;
|
|
32
|
-
/**
|
|
33
|
-
* BaseModel provides instance methods for ORM models.
|
|
34
|
-
* Models extend this class and define static properties.
|
|
35
|
-
*
|
|
36
|
-
* Usage:
|
|
37
|
-
* class User extends BaseModel {
|
|
38
|
-
* static tableName = "users";
|
|
39
|
-
* static fields = { id: { type: "integer", primaryKey: true, autoIncrement: true }, ... };
|
|
40
|
-
* static softDelete = true;
|
|
41
|
-
* static tableFilter = "active = 1";
|
|
42
|
-
* static hasOne = [{ model: "Profile", foreignKey: "user_id" }];
|
|
43
|
-
* static hasMany = [{ model: "Post", foreignKey: "author_id" }];
|
|
44
|
-
* static _db = "secondary";
|
|
45
|
-
* static fieldMapping = { firstName: "first_name", lastName: "last_name" };
|
|
46
|
-
* static autoMap = true; // auto-generate fieldMapping from camelCase → snake_case
|
|
47
|
-
* }
|
|
48
|
-
*/
|
|
49
31
|
export declare class BaseModel {
|
|
50
32
|
static tableName: string;
|
|
51
33
|
static fields: Record<string, FieldDefinition>;
|
|
@@ -55,7 +37,6 @@ export declare class BaseModel {
|
|
|
55
37
|
static hasMany?: RelationshipDefinition[];
|
|
56
38
|
static belongsTo?: RelationshipDefinition[];
|
|
57
39
|
static _db?: string;
|
|
58
|
-
static _queryCache?: QueryCache;
|
|
59
40
|
/**
|
|
60
41
|
* When true, auto-generates fieldMapping entries from camelCase field names
|
|
61
42
|
* to snake_case DB column names. Explicit fieldMapping entries always win.
|
|
@@ -315,7 +296,7 @@ export declare class BaseModel {
|
|
|
315
296
|
* Validate this instance's values against the model's field definitions.
|
|
316
297
|
* Returns an array of error strings (empty array means valid).
|
|
317
298
|
*/
|
|
318
|
-
validate(): string[];
|
|
299
|
+
validate(isUpdate?: boolean): string[];
|
|
319
300
|
/**
|
|
320
301
|
* Generate and execute CREATE TABLE DDL from the model's field definitions.
|
|
321
302
|
* Uses the adapter's createTable method if available, otherwise builds SQL directly.
|
|
@@ -330,18 +311,36 @@ export declare class BaseModel {
|
|
|
330
311
|
*/
|
|
331
312
|
static exists(pkValue: unknown): Promise<boolean>;
|
|
332
313
|
/**
|
|
333
|
-
*
|
|
314
|
+
* Every table a cached query touches: this model's table plus every FROM/JOIN
|
|
315
|
+
* table in `sql`. A write to any of these busts the entry (CACHE-DEC-01).
|
|
316
|
+
*/
|
|
317
|
+
static _cacheTags(sql: string): string[];
|
|
318
|
+
/**
|
|
319
|
+
* Run a raw SQL query with results cached by TTL.
|
|
320
|
+
*
|
|
321
|
+
* Invalidation (CACHE-DEC-01): the entry is tagged by every table the query
|
|
322
|
+
* touches (this model's table plus any FROM/JOIN tables) in ONE process-wide
|
|
323
|
+
* shared cache, so a write through the ORM (save/delete/forceDelete/restore)
|
|
324
|
+
* to ANY of those tables busts it -- including a cross-table JOIN cached on a
|
|
325
|
+
* different model. `ttl <= 0` means NO-CACHE: the query runs and the rows are
|
|
326
|
+
* returned but nothing is stored, so every read hits the database.
|
|
334
327
|
*
|
|
335
328
|
* @param sql SQL query string.
|
|
336
329
|
* @param params Bind parameters.
|
|
337
|
-
* @param ttl Cache TTL in seconds (default 60).
|
|
330
|
+
* @param ttl Cache TTL in seconds (default 60; <= 0 = no-cache).
|
|
338
331
|
* @param limit Max records to return (default 100).
|
|
339
332
|
* @param offset Records to skip (default 0).
|
|
340
333
|
* @param include Relationship names to eager-load on cache miss.
|
|
341
334
|
*/
|
|
342
335
|
static cached<T extends BaseModel>(this: new (data?: Record<string, unknown>) => T, sql: string, params?: unknown[], ttl?: number, limit?: number, offset?: number, include?: string[]): Promise<T[]>;
|
|
343
336
|
/**
|
|
344
|
-
*
|
|
337
|
+
* Invalidate every cached query that touches this model's table.
|
|
338
|
+
*
|
|
339
|
+
* Tag-scoped, NOT a wholesale flush: a cached JOIN on another model that reads
|
|
340
|
+
* this table is busted too (it carries this table's tag), while a query that
|
|
341
|
+
* never touches this table is left intact. Called after every ORM write
|
|
342
|
+
* (save/delete/forceDelete/restore) so a read-after-write never serves a
|
|
343
|
+
* stale/deleted row (CACHE-DEC-01).
|
|
345
344
|
*/
|
|
346
345
|
static clearCache(): void;
|
|
347
346
|
/**
|
|
@@ -380,6 +379,11 @@ export declare class BaseModel {
|
|
|
380
379
|
hasOne<T extends BaseModel, R extends BaseModel>(this: T, relatedClass: typeof BaseModel & (new (data?: Record<string, unknown>) => R), foreignKey: string): Promise<R | null>;
|
|
381
380
|
/**
|
|
382
381
|
* Load has-many related model instances.
|
|
382
|
+
*
|
|
383
|
+
* With no explicit `limit` this returns the WHOLE set (paged internally, like
|
|
384
|
+
* the lazy accessor), never a silent row cap -- so an imperatively-loaded
|
|
385
|
+
* has_many yields the SAME row count as the lazy path. An explicit `limit`
|
|
386
|
+
* still pages.
|
|
383
387
|
*/
|
|
384
388
|
hasMany<T extends BaseModel, R extends BaseModel>(this: T, relatedClass: typeof BaseModel & (new (data?: Record<string, unknown>) => R), foreignKey: string, limit?: number, offset?: number): Promise<R[]>;
|
|
385
389
|
/**
|
|
@@ -394,10 +398,26 @@ export declare class BaseModel {
|
|
|
394
398
|
/**
|
|
395
399
|
* Process foreignKey fields on every registered model so the cross-model
|
|
396
400
|
* _fkRegistry (and each model's belongsTo/hasMany) is fully wired regardless
|
|
397
|
-
* of which model was used first
|
|
398
|
-
*
|
|
401
|
+
* of which model was used first, then attach the lazy relationship accessors.
|
|
402
|
+
* Idempotent — every step guards against duplicates.
|
|
399
403
|
*/
|
|
400
404
|
private static _processAllForeignKeys;
|
|
405
|
+
/**
|
|
406
|
+
* REL-NODE-AUTOWIRE-DEAD: attach a lazy-loading accessor for each declared
|
|
407
|
+
* relationship (belongsTo/hasOne/hasMany) on this model's prototype, so
|
|
408
|
+
* `post.author` / `author.posts` resolve on attribute access. The accessor is
|
|
409
|
+
* async (Node lazy load) and caches into `_relCache` — the SAME cache eager
|
|
410
|
+
* loading fills, so `toDict` stays consistent once a relation has been loaded.
|
|
411
|
+
* Reuses the imperative belongsTo()/hasOne() path and the cross-model registry;
|
|
412
|
+
* a soft-deleted child is excluded and the has-many read is uncapped.
|
|
413
|
+
*/
|
|
414
|
+
static _wireRelationshipAccessors(): void;
|
|
415
|
+
/**
|
|
416
|
+
* Lazy has-many read for a relationship accessor: excludes soft-deleted
|
|
417
|
+
* children and returns the WHOLE set (adapterQuery is uncapped, so the tail is
|
|
418
|
+
* never lost). Ordered by the child PK for a stable read.
|
|
419
|
+
*/
|
|
420
|
+
private static _loadHasManyLazy;
|
|
401
421
|
/**
|
|
402
422
|
* Resolve a model class by name from the registry.
|
|
403
423
|
*/
|
|
@@ -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;
|