tina4-nodejs 3.13.94 → 3.13.95
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 +157 -28
- package/README.md +1 -1
- package/package.json +2 -1
- package/packages/cli/dist/bin.js +32418 -29638
- package/packages/cli/src/commands/metrics.ts +17 -11
- package/packages/cli/src/commands/serve.ts +10 -9
- package/packages/core/dist/index.js +32364 -29501
- package/packages/core/src/ai.ts +7 -1
- package/packages/core/src/auth.ts +191 -39
- package/packages/core/src/background.ts +19 -19
- package/packages/core/src/cache.ts +492 -49
- package/packages/core/src/devAdmin.ts +79 -32
- package/packages/core/src/dispatchPipeline.ts +285 -0
- package/packages/core/src/dotenv.ts +185 -40
- package/packages/core/src/index.ts +5 -4
- package/packages/core/src/logger.ts +257 -36
- package/packages/core/src/mcp.ts +1 -1
- package/packages/core/src/messenger.ts +9 -13
- package/packages/core/src/metrics.ts +199 -961
- package/packages/core/src/middleware.ts +390 -123
- package/packages/core/src/queue.ts +188 -32
- package/packages/core/src/queueBackends/kafkaBackend.ts +1 -1
- package/packages/core/src/queueBackends/liteBackend.ts +13 -0
- package/packages/core/src/queueBackends/mongoBackend.ts +101 -9
- package/packages/core/src/queueBackends/rabbitmqBackend.ts +22 -4
- package/packages/core/src/rateLimiter.ts +10 -5
- package/packages/core/src/request.ts +6 -9
- package/packages/core/src/response.ts +46 -1
- package/packages/core/src/router.ts +29 -4
- package/packages/core/src/server.ts +751 -414
- package/packages/core/src/session.ts +244 -27
- package/packages/core/src/sessionHandlers/databaseHandler.ts +338 -48
- package/packages/core/src/sessionHandlers/memcachedHandler.ts +181 -0
- package/packages/core/src/sessionHandlers/mongoClient.ts +293 -208
- package/packages/core/src/sessionHandlers/mongoHandler.ts +88 -8
- package/packages/core/src/sessionHandlers/respClient.ts +16 -147
- package/packages/core/src/sessionHandlers/sqlClient.ts +290 -0
- package/packages/core/src/sessionHandlers/syncBridge.ts +190 -0
- package/packages/core/src/sessionHandlers/syncSocket.ts +236 -0
- package/packages/core/src/testClient.ts +18 -5
- package/packages/core/src/trustedProxy.ts +249 -0
- package/packages/core/src/types.ts +29 -5
- package/packages/core/src/websocket.ts +66 -0
- package/packages/orm/dist/index.js +22367 -19504
- package/packages/orm/src/adapters/firebird.ts +183 -56
- package/packages/orm/src/adapters/mongodb.ts +25 -4
- package/packages/orm/src/adapters/mssql.ts +114 -29
- package/packages/orm/src/adapters/mysql.ts +103 -40
- package/packages/orm/src/adapters/odbc.ts +44 -21
- package/packages/orm/src/adapters/postgres.ts +118 -26
- package/packages/orm/src/adapters/sqlDialect.ts +120 -0
- package/packages/orm/src/adapters/sqlite.ts +60 -24
- package/packages/orm/src/baseModel.ts +135 -40
- package/packages/orm/src/cachedDatabase.ts +43 -19
- package/packages/orm/src/connectTimeout.ts +265 -0
- package/packages/orm/src/database.ts +237 -197
- package/packages/orm/src/databaseResult.ts +65 -13
- package/packages/orm/src/databaseUrl.ts +484 -0
- package/packages/orm/src/docstore.ts +386 -145
- package/packages/orm/src/index.ts +13 -3
- package/packages/orm/src/migration.ts +18 -3
- package/packages/orm/src/queryBuilder.ts +38 -4
- package/packages/orm/src/sqlTranslator.ts +310 -4
- package/packages/orm/src/types.ts +15 -4
- package/types/core/src/ai.d.ts +1 -1
- package/types/core/src/auth.d.ts +28 -5
- package/types/core/src/background.d.ts +3 -3
- package/types/core/src/cache.d.ts +15 -12
- package/types/core/src/dispatchPipeline.d.ts +117 -0
- package/types/core/src/dotenv.d.ts +38 -16
- package/types/core/src/index.d.ts +5 -6
- package/types/core/src/logger.d.ts +93 -16
- package/types/core/src/messenger.d.ts +2 -2
- package/types/core/src/metrics.d.ts +25 -61
- package/types/core/src/middleware.d.ts +134 -11
- package/types/core/src/queue.d.ts +54 -5
- package/types/core/src/queueBackends/kafkaBackend.d.ts +1 -1
- package/types/core/src/queueBackends/liteBackend.d.ts +9 -0
- package/types/core/src/queueBackends/mongoBackend.d.ts +24 -2
- package/types/core/src/queueBackends/rabbitmqBackend.d.ts +3 -3
- package/types/core/src/router.d.ts +14 -3
- package/types/core/src/server.d.ts +15 -0
- package/types/core/src/session.d.ts +87 -2
- package/types/core/src/sessionHandlers/databaseHandler.d.ts +60 -5
- package/types/core/src/sessionHandlers/memcachedHandler.d.ts +60 -0
- package/types/core/src/sessionHandlers/mongoClient.d.ts +16 -5
- package/types/core/src/sessionHandlers/mongoHandler.d.ts +51 -3
- package/types/core/src/sessionHandlers/respClient.d.ts +2 -2
- package/types/core/src/sessionHandlers/sqlClient.d.ts +39 -0
- package/types/core/src/sessionHandlers/syncBridge.d.ts +91 -0
- package/types/core/src/sessionHandlers/syncSocket.d.ts +49 -0
- package/types/core/src/trustedProxy.d.ts +44 -0
- package/types/core/src/types.d.ts +28 -5
- package/types/core/src/websocket.d.ts +26 -0
- package/types/orm/src/adapters/firebird.d.ts +55 -10
- package/types/orm/src/adapters/mongodb.d.ts +2 -2
- package/types/orm/src/adapters/mssql.d.ts +18 -11
- package/types/orm/src/adapters/mysql.d.ts +11 -10
- package/types/orm/src/adapters/odbc.d.ts +9 -12
- package/types/orm/src/adapters/postgres.d.ts +11 -10
- package/types/orm/src/adapters/sqlDialect.d.ts +71 -0
- package/types/orm/src/adapters/sqlite.d.ts +15 -3
- package/types/orm/src/baseModel.d.ts +45 -9
- package/types/orm/src/cachedDatabase.d.ts +18 -5
- package/types/orm/src/connectTimeout.d.ts +100 -0
- package/types/orm/src/database.d.ts +72 -26
- package/types/orm/src/databaseResult.d.ts +24 -0
- package/types/orm/src/databaseUrl.d.ts +125 -0
- package/types/orm/src/docstore.d.ts +102 -43
- package/types/orm/src/index.d.ts +5 -2
- package/types/orm/src/queryBuilder.d.ts +23 -3
- package/types/orm/src/sqlTranslator.d.ts +126 -2
- package/types/orm/src/types.d.ts +14 -4
- package/packages/core/src/sessionHandlers/redisHandler.ts +0 -219
- package/types/core/src/sessionHandlers/redisHandler.d.ts +0 -60
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tina4 Firebird Adapter — uses the `node-firebird` package (optional peer dependency).
|
|
3
|
-
*
|
|
4
|
-
* Install: npm install node-firebird
|
|
5
|
-
* URL format: firebird://user:pass@host:port/path/to/database.fdb
|
|
6
|
-
*/
|
|
7
1
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
8
2
|
/**
|
|
9
3
|
* Turn a URL path component into a Firebird database identifier.
|
|
@@ -57,6 +51,39 @@ export interface FirebirdConfig {
|
|
|
57
51
|
/** Connection charset. Overridden by a `?charset=` URL query; see resolveFirebirdCharset. */
|
|
58
52
|
charset?: string;
|
|
59
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Quote an identifier the way Firebird actually stores it: UPPERCASE.
|
|
56
|
+
*
|
|
57
|
+
* Firebird folds an UNQUOTED identifier to upper case and treats a QUOTED one as
|
|
58
|
+
* case-sensitive. So after the ordinary `CREATE TABLE probe_t (...)` the table is
|
|
59
|
+
* PROBE_T, and `INSERT INTO "probe_t"` matches nothing:
|
|
60
|
+
*
|
|
61
|
+
* Dynamic SQL Error / Table unknown / probe_t
|
|
62
|
+
*
|
|
63
|
+
* That broke the insert path against every conventionally-created table, columns
|
|
64
|
+
* included. A name the caller has ALREADY quoted is passed through untouched,
|
|
65
|
+
* which is the escape hatch for a genuinely case-sensitive `CREATE TABLE "orders"`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function fbQuote(name: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* Firebird's stored column name, folded back only when it was folded.
|
|
70
|
+
*
|
|
71
|
+
* Firebird's identifier folding is ASYMMETRIC. An unquoted `AS x` is stored
|
|
72
|
+
* UPPERCASE, so the driver hands back "X" where every other engine Tina4
|
|
73
|
+
* supports gives "x" — PostgreSQL folds to lower, and MySQL, SQLite and MSSQL
|
|
74
|
+
* preserve what you wrote. Portable code reading row.x broke on Firebird alone.
|
|
75
|
+
*
|
|
76
|
+
* A QUOTED `AS "MyCol"` is stored exactly as written, and that case is
|
|
77
|
+
* deliberate — the caller asked for it — so it is left alone. Folding
|
|
78
|
+
* unconditionally makes a mixed-case key unreachable, the same asymmetric trap
|
|
79
|
+
* that made tableExists miss quoted tables.
|
|
80
|
+
*
|
|
81
|
+
* So: fold back only a name carrying no lowercase letter, the only thing
|
|
82
|
+
* unquoted folding can produce. A quoted ALL-CAPS name is genuinely
|
|
83
|
+
* indistinguishable from a folded one and is lowercased too; that ambiguity is
|
|
84
|
+
* Firebird's, and it is the one spelling this cannot round-trip.
|
|
85
|
+
*/
|
|
86
|
+
export declare function firebirdColumnName(raw: string): string;
|
|
60
87
|
export declare class FirebirdAdapter implements DatabaseAdapter {
|
|
61
88
|
private config;
|
|
62
89
|
private db;
|
|
@@ -116,22 +143,40 @@ export declare class FirebirdAdapter implements DatabaseAdapter {
|
|
|
116
143
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
117
144
|
insertAsync(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseResult>;
|
|
118
145
|
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
119
|
-
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
146
|
+
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
120
147
|
delete(table: string, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
121
|
-
deleteAsync(table: string, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
148
|
+
deleteAsync(table: string, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
122
149
|
startTransaction(): void;
|
|
123
150
|
startTransactionAsync(): Promise<void>;
|
|
124
151
|
commit(): void;
|
|
125
152
|
commitAsync(): Promise<void>;
|
|
126
153
|
rollback(): void;
|
|
127
154
|
rollbackAsync(): Promise<void>;
|
|
128
|
-
|
|
155
|
+
getTables(): string[];
|
|
129
156
|
tablesAsync(): Promise<string[]>;
|
|
130
|
-
|
|
157
|
+
getColumns(table: string): ColumnInfo[];
|
|
131
158
|
columnsAsync(table: string): Promise<ColumnInfo[]>;
|
|
132
159
|
lastInsertId(): number | bigint | null;
|
|
133
160
|
close(): void;
|
|
134
161
|
tableExists(name: string): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Is this table present, under either spelling Firebird could have stored?
|
|
164
|
+
*
|
|
165
|
+
* Firebird's folding rule is ASYMMETRIC:
|
|
166
|
+
* CREATE TABLE foo -> stored as FOO (unquoted folds to UPPER)
|
|
167
|
+
* CREATE TABLE "Foo" -> stored as Foo (quoted keeps its case)
|
|
168
|
+
*
|
|
169
|
+
* So upper-casing is CORRECT for the unquoted case - the common one - and
|
|
170
|
+
* WRONG for a quoted mixed-case table, which is a real thing on Firebird.
|
|
171
|
+
* Dropping the upper-case would not fix that, it would invert which half is
|
|
172
|
+
* broken.
|
|
173
|
+
*
|
|
174
|
+
* tableExistsAsync("Foo") is genuinely AMBIGUOUS: the caller could mean the
|
|
175
|
+
* quoted `Foo` or the unquoted `FOO`. Match EITHER. Do not "simplify" this
|
|
176
|
+
* back to one comparison - that is the bug it replaces, where a quoted
|
|
177
|
+
* mixed-case table read as absent and createTableAsync's idempotency guard
|
|
178
|
+
* (below) never fired.
|
|
179
|
+
*/
|
|
135
180
|
tableExistsAsync(name: string): Promise<boolean>;
|
|
136
181
|
createTable(name: string, columns: Record<string, FieldDefinition>): void;
|
|
137
182
|
createTableAsync(name: string, columns: Record<string, FieldDefinition>): Promise<void>;
|
|
@@ -55,9 +55,9 @@ export declare class MongodbAdapter implements DatabaseAdapter {
|
|
|
55
55
|
commitAsync(): Promise<void>;
|
|
56
56
|
rollback(): void;
|
|
57
57
|
rollbackAsync(): Promise<void>;
|
|
58
|
-
|
|
58
|
+
getTables(): string[];
|
|
59
59
|
tablesAsync(): Promise<string[]>;
|
|
60
|
-
|
|
60
|
+
getColumns(table: string): ColumnInfo[];
|
|
61
61
|
/**
|
|
62
62
|
* Infer column schema by sampling a document from the collection.
|
|
63
63
|
* MongoDB is schema-less; this returns field names and inferred JS types.
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tina4 MSSQL Adapter — uses the `tedious` package (optional peer dependency).
|
|
3
|
-
*
|
|
4
|
-
* Install: npm install tedious
|
|
5
|
-
* URL format: mssql://user:pass@host:port/database
|
|
6
|
-
*/
|
|
7
1
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
8
2
|
export interface MssqlConfig {
|
|
9
3
|
host?: string;
|
|
@@ -16,6 +10,13 @@ export interface MssqlConfig {
|
|
|
16
10
|
}
|
|
17
11
|
export declare class MssqlAdapter implements DatabaseAdapter {
|
|
18
12
|
private config;
|
|
13
|
+
/**
|
|
14
|
+
* Postgres, MySQL and MSSQL all REQUIRE a name for a derived table, so
|
|
15
|
+
* the COUNT probe in Database.countProbe wraps as
|
|
16
|
+
* `FROM (sql) AS _count_query`. SQLite and Firebird leave this unset and
|
|
17
|
+
* get no alias - Firebird rejects `AS` in that position.
|
|
18
|
+
*/
|
|
19
|
+
readonly countSubqueryAlias = "_count_query";
|
|
19
20
|
private connection;
|
|
20
21
|
private _lastInsertId;
|
|
21
22
|
private _inTransaction;
|
|
@@ -27,7 +28,13 @@ export declare class MssqlAdapter implements DatabaseAdapter {
|
|
|
27
28
|
/** Translate SQL for MSSQL dialect. */
|
|
28
29
|
translateSql(sql: string): string;
|
|
29
30
|
private execSqlPromise;
|
|
30
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Convert ? placeholders to @p0, @p1, ... for tedious.
|
|
33
|
+
*
|
|
34
|
+
* `startAt` lets a caller that has already consumed N placeholders (an UPDATE
|
|
35
|
+
* whose SET values are @p0..@p{N-1}) continue the numbering into a raw WHERE
|
|
36
|
+
* fragment instead of restarting at @p0.
|
|
37
|
+
*/
|
|
31
38
|
private convertPlaceholders;
|
|
32
39
|
execute(sql: string, params?: unknown[]): unknown;
|
|
33
40
|
executeMany(sql: string, paramsList: unknown[][]): {
|
|
@@ -48,18 +55,18 @@ export declare class MssqlAdapter implements DatabaseAdapter {
|
|
|
48
55
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
49
56
|
insertAsync(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseResult>;
|
|
50
57
|
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
51
|
-
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
58
|
+
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
52
59
|
delete(table: string, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
53
|
-
deleteAsync(table: string, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
60
|
+
deleteAsync(table: string, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
54
61
|
startTransaction(): void;
|
|
55
62
|
startTransactionAsync(): Promise<void>;
|
|
56
63
|
commit(): void;
|
|
57
64
|
commitAsync(): Promise<void>;
|
|
58
65
|
rollback(): void;
|
|
59
66
|
rollbackAsync(): Promise<void>;
|
|
60
|
-
|
|
67
|
+
getTables(): string[];
|
|
61
68
|
tablesAsync(): Promise<string[]>;
|
|
62
|
-
|
|
69
|
+
getColumns(table: string): ColumnInfo[];
|
|
63
70
|
columnsAsync(table: string): Promise<ColumnInfo[]>;
|
|
64
71
|
lastInsertId(): number | bigint | null;
|
|
65
72
|
close(): void;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tina4 MySQL Adapter — uses the `mysql2` package (optional peer dependency).
|
|
3
|
-
*
|
|
4
|
-
* Install: npm install mysql2
|
|
5
|
-
* URL format: mysql://user:pass@host:port/database
|
|
6
|
-
*/
|
|
7
1
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
8
2
|
export interface MysqlConfig {
|
|
9
3
|
host?: string;
|
|
@@ -15,6 +9,13 @@ export interface MysqlConfig {
|
|
|
15
9
|
}
|
|
16
10
|
export declare class MysqlAdapter implements DatabaseAdapter {
|
|
17
11
|
private config;
|
|
12
|
+
/**
|
|
13
|
+
* Postgres, MySQL and MSSQL all REQUIRE a name for a derived table, so
|
|
14
|
+
* the COUNT probe in Database.countProbe wraps as
|
|
15
|
+
* `FROM (sql) AS _count_query`. SQLite and Firebird leave this unset and
|
|
16
|
+
* get no alias - Firebird rejects `AS` in that position.
|
|
17
|
+
*/
|
|
18
|
+
readonly countSubqueryAlias = "_count_query";
|
|
18
19
|
private connection;
|
|
19
20
|
private _lastInsertId;
|
|
20
21
|
private _inTransaction;
|
|
@@ -44,18 +45,18 @@ export declare class MysqlAdapter implements DatabaseAdapter {
|
|
|
44
45
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
45
46
|
insertAsync(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseResult>;
|
|
46
47
|
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
47
|
-
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
48
|
+
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
48
49
|
delete(table: string, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
49
|
-
deleteAsync(table: string, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
50
|
+
deleteAsync(table: string, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
50
51
|
startTransaction(): void;
|
|
51
52
|
startTransactionAsync(): Promise<void>;
|
|
52
53
|
commit(): void;
|
|
53
54
|
commitAsync(): Promise<void>;
|
|
54
55
|
rollback(): void;
|
|
55
56
|
rollbackAsync(): Promise<void>;
|
|
56
|
-
|
|
57
|
+
getTables(): string[];
|
|
57
58
|
tablesAsync(): Promise<string[]>;
|
|
58
|
-
|
|
59
|
+
getColumns(table: string): ColumnInfo[];
|
|
59
60
|
columnsAsync(table: string): Promise<ColumnInfo[]>;
|
|
60
61
|
lastInsertId(): number | bigint | null;
|
|
61
62
|
close(): void;
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tina4 ODBC Adapter — uses the `odbc` package (optional peer dependency).
|
|
3
|
-
*
|
|
4
|
-
* Install: npm install odbc
|
|
5
|
-
* URL format: odbc:///DSN=MyDSN
|
|
6
|
-
* odbc:///DRIVER={driver};SERVER=host;DATABASE=db
|
|
7
|
-
*
|
|
8
|
-
* The connection string after stripping the "odbc:///" prefix is passed
|
|
9
|
-
* directly to odbc.connect(), so any valid ODBC connection string works.
|
|
10
|
-
*/
|
|
11
1
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
12
2
|
export interface OdbcConfig {
|
|
13
3
|
/** Full ODBC connection string, e.g. "DSN=MyDSN" or "DRIVER={SQL Server};SERVER=host;DATABASE=db" */
|
|
@@ -26,6 +16,13 @@ export declare class OdbcAdapter implements DatabaseAdapter {
|
|
|
26
16
|
constructor(config: OdbcConfig | string);
|
|
27
17
|
/** Extract the raw ODBC connection string from config. */
|
|
28
18
|
private getConnectionString;
|
|
19
|
+
/**
|
|
20
|
+
* The address for a diagnostic message. ODBC hides it inside an opaque
|
|
21
|
+
* driver keyword string, so this reads the standard keywords and falls back to
|
|
22
|
+
* the data-source name - it is never used to connect, only to say which target
|
|
23
|
+
* hung.
|
|
24
|
+
*/
|
|
25
|
+
private describeTarget;
|
|
29
26
|
/** Connect to the ODBC data source. Must be called before using the adapter. */
|
|
30
27
|
connect(): Promise<void>;
|
|
31
28
|
private ensureConnected;
|
|
@@ -43,8 +40,8 @@ export declare class OdbcAdapter implements DatabaseAdapter {
|
|
|
43
40
|
startTransaction(): void;
|
|
44
41
|
commit(): void;
|
|
45
42
|
rollback(): void;
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
getTables(): string[];
|
|
44
|
+
getColumns(table: string): ColumnInfo[];
|
|
48
45
|
tableExists(name: string): boolean;
|
|
49
46
|
createTable(name: string, columns: Record<string, FieldDefinition>): void;
|
|
50
47
|
getTableColumns(name: string): Array<{
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tina4 PostgreSQL Adapter — uses the `pg` package (optional peer dependency).
|
|
3
|
-
*
|
|
4
|
-
* Install: npm install pg @types/pg
|
|
5
|
-
* URL format: postgresql://user:pass@host:port/database
|
|
6
|
-
*/
|
|
7
1
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
8
2
|
export interface PostgresConfig {
|
|
9
3
|
host?: string;
|
|
@@ -15,6 +9,13 @@ export interface PostgresConfig {
|
|
|
15
9
|
}
|
|
16
10
|
export declare class PostgresAdapter implements DatabaseAdapter {
|
|
17
11
|
private config;
|
|
12
|
+
/**
|
|
13
|
+
* Postgres, MySQL and MSSQL all REQUIRE a name for a derived table, so
|
|
14
|
+
* the COUNT probe in Database.countProbe wraps as
|
|
15
|
+
* `FROM (sql) AS _count_query`. SQLite and Firebird leave this unset and
|
|
16
|
+
* get no alias - Firebird rejects `AS` in that position.
|
|
17
|
+
*/
|
|
18
|
+
readonly countSubqueryAlias = "_count_query";
|
|
18
19
|
private client;
|
|
19
20
|
private _lastInsertId;
|
|
20
21
|
private _inTransaction;
|
|
@@ -61,18 +62,18 @@ export declare class PostgresAdapter implements DatabaseAdapter {
|
|
|
61
62
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
62
63
|
insertAsync(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseResult>;
|
|
63
64
|
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
64
|
-
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
65
|
+
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
65
66
|
delete(table: string, filter: Record<string, unknown>, params?: unknown[]): DatabaseResult;
|
|
66
|
-
deleteAsync(table: string, filter: Record<string, unknown>): Promise<DatabaseResult>;
|
|
67
|
+
deleteAsync(table: string, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
67
68
|
startTransaction(): void;
|
|
68
69
|
startTransactionAsync(): Promise<void>;
|
|
69
70
|
commit(): void;
|
|
70
71
|
commitAsync(): Promise<void>;
|
|
71
72
|
rollback(): void;
|
|
72
73
|
rollbackAsync(): Promise<void>;
|
|
73
|
-
|
|
74
|
+
getTables(): string[];
|
|
74
75
|
tablesAsync(): Promise<string[]>;
|
|
75
|
-
|
|
76
|
+
getColumns(table: string): ColumnInfo[];
|
|
76
77
|
columnsAsync(table: string): Promise<ColumnInfo[]>;
|
|
77
78
|
lastInsertId(): number | bigint | string | null;
|
|
78
79
|
close(): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One CRUD SQL builder for every engine, instead of one per adapter.
|
|
3
|
+
*
|
|
4
|
+
* Feature 3's last open item, the 4.3x LOC finding: `insert`/`update`/`delete`
|
|
5
|
+
* built their SQL independently in all seven adapters. Building
|
|
6
|
+
* `INSERT INTO x (a, b) VALUES (?, ?)` is not engine-specific work - Ruby has
|
|
7
|
+
* always done it once - and the seven copies differed in exactly two ways:
|
|
8
|
+
*
|
|
9
|
+
* IDENTIFIER QUOTING "col" | `col` | [col] | Firebird's fbQuote
|
|
10
|
+
* PARAMETER MARKER ? | $1 | @p1
|
|
11
|
+
*
|
|
12
|
+
* Both are captured in a `Dialect` below, so the builders are shared and each
|
|
13
|
+
* adapter declares only what genuinely differs about its engine.
|
|
14
|
+
*
|
|
15
|
+
* These functions build STRINGS and nothing else. Execution and result
|
|
16
|
+
* extraction stay in the adapters on purpose: those really are per-driver
|
|
17
|
+
* (`client.query` vs `lastInsertRowid` vs a Firebird transaction handle), and
|
|
18
|
+
* folding them in here would trade a real duplication for a fake abstraction.
|
|
19
|
+
*
|
|
20
|
+
* MongoDB has no entry: it does not build SQL at all.
|
|
21
|
+
*/
|
|
22
|
+
/** How one engine spells identifiers and parameter markers. */
|
|
23
|
+
export interface Dialect {
|
|
24
|
+
/** Quote a table or column name for this engine. */
|
|
25
|
+
quote(name: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* The parameter marker for the 1-based position `index`. Engines with
|
|
28
|
+
* positional markers ($1, @p1) use the index; the rest ignore it.
|
|
29
|
+
*/
|
|
30
|
+
marker(index: number): string;
|
|
31
|
+
}
|
|
32
|
+
/** SQLite, and ODBC which follows the SQL standard spelling. */
|
|
33
|
+
export declare const ANSI_DIALECT: Dialect;
|
|
34
|
+
/** PostgreSQL: standard quoting, positional $N markers. */
|
|
35
|
+
export declare const POSTGRES_DIALECT: Dialect;
|
|
36
|
+
/** MySQL: backtick quoting. */
|
|
37
|
+
export declare const MYSQL_DIALECT: Dialect;
|
|
38
|
+
/** MSSQL: bracket quoting, named @pN markers. */
|
|
39
|
+
export declare const MSSQL_DIALECT: Dialect;
|
|
40
|
+
/**
|
|
41
|
+
* Firebird quotes only when it has to: an unquoted identifier is folded to
|
|
42
|
+
* UPPER CASE, so quoting a lower-case name would make it unfindable. The
|
|
43
|
+
* adapter owns that rule and passes its own quoter in.
|
|
44
|
+
*/
|
|
45
|
+
export declare function firebirdDialect(fbQuote: (name: string) => string): Dialect;
|
|
46
|
+
/**
|
|
47
|
+
* `INSERT INTO <table> (<cols>) VALUES (<markers>)`.
|
|
48
|
+
*
|
|
49
|
+
* @param suffix Appended verbatim - PostgreSQL passes " RETURNING *" and MSSQL
|
|
50
|
+
* its SCOPE_IDENTITY() probe, the genuinely engine-specific parts.
|
|
51
|
+
* @param startAt Position of the FIRST marker. PostgreSQL numbers its `$N` from
|
|
52
|
+
* 1; MSSQL names its `@pN` from 0 and BINDS by that same name, so
|
|
53
|
+
* shifting it would produce SQL whose parameters do not exist.
|
|
54
|
+
* Engines using `?` ignore this.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildInsert(dialect: Dialect, table: string, keys: string[], suffix?: string, startAt?: number): string;
|
|
57
|
+
/**
|
|
58
|
+
* The `SET a = ?, b = ?` fragment of an UPDATE.
|
|
59
|
+
*
|
|
60
|
+
* @param startAt 1-based position of the FIRST marker. An UPDATE's WHERE
|
|
61
|
+
* clause continues the numbering after the SET values, so a
|
|
62
|
+
* positional engine ($N, @pN) must not restart at 1.
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildSetClause(dialect: Dialect, keys: string[], startAt?: number): string;
|
|
65
|
+
/**
|
|
66
|
+
* The `a = ? AND b = ?` fragment for an object filter.
|
|
67
|
+
*
|
|
68
|
+
* @param startAt 1-based position of the first marker, for the same reason as
|
|
69
|
+
* buildSetClause.
|
|
70
|
+
*/
|
|
71
|
+
export declare function buildWhereClause(dialect: Dialect, keys: string[], startAt?: number): string;
|
|
@@ -2,6 +2,18 @@ import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } fro
|
|
|
2
2
|
export declare class SQLiteAdapter implements DatabaseAdapter {
|
|
3
3
|
private db;
|
|
4
4
|
private _lastInsertId;
|
|
5
|
+
/**
|
|
6
|
+
* TINA4_DATABASE_CONNECT_TIMEOUT DOES NOT APPLY HERE, deliberately.
|
|
7
|
+
*
|
|
8
|
+
* There is no connect() to bound: `node:sqlite` opens the file in this
|
|
9
|
+
* SYNCHRONOUS constructor, and a synchronous call cannot be interrupted by a
|
|
10
|
+
* timer on the same thread - the event loop only gets to run the timer after
|
|
11
|
+
* `new DatabaseSync()` has already returned. There is also no host and no port
|
|
12
|
+
* to name in a timeout error. The one case that could still block is a local
|
|
13
|
+
* file on a wedged network mount, which is a kernel-level stall no JS bound
|
|
14
|
+
* can reach. Stated here so the exclusion reads as a decision rather than an
|
|
15
|
+
* adapter somebody forgot.
|
|
16
|
+
*/
|
|
5
17
|
constructor(dbPath: string);
|
|
6
18
|
execute(sql: string, params?: unknown[]): unknown;
|
|
7
19
|
executeMany(sql: string, paramsList: unknown[][]): {
|
|
@@ -12,14 +24,14 @@ export declare class SQLiteAdapter implements DatabaseAdapter {
|
|
|
12
24
|
fetch<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number): T[];
|
|
13
25
|
fetchOne<T = Record<string, unknown>>(sql: string, params?: unknown[]): T | null;
|
|
14
26
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
15
|
-
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown
|
|
27
|
+
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): DatabaseResult;
|
|
16
28
|
delete(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[], params?: unknown[]): DatabaseResult;
|
|
17
29
|
private _inTransaction;
|
|
18
30
|
startTransaction(): void;
|
|
19
31
|
commit(): void;
|
|
20
32
|
rollback(): void;
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
getTables(): string[];
|
|
34
|
+
getColumns(table: string): ColumnInfo[];
|
|
23
35
|
lastInsertId(): number | bigint | null;
|
|
24
36
|
close(): void;
|
|
25
37
|
/**
|
|
@@ -135,6 +135,21 @@ export declare class BaseModel {
|
|
|
135
135
|
* Get the primary key field name (JS property name).
|
|
136
136
|
*/
|
|
137
137
|
protected static getPkField(): string;
|
|
138
|
+
/**
|
|
139
|
+
* EVERY primary-key field name, in declaration order.
|
|
140
|
+
*
|
|
141
|
+
* A key may span several columns. `getPkField()` returns only the FIRST and
|
|
142
|
+
* is kept for the auto-increment paths, which are single-column by
|
|
143
|
+
* definition. Anything that ADDRESSES a row must use this: keying on one
|
|
144
|
+
* column of a composite key matches every row sharing that value, which is
|
|
145
|
+
* the data-loss shape feature 4 removed from the raw write path below.
|
|
146
|
+
*/
|
|
147
|
+
protected static getPkFields(): string[];
|
|
148
|
+
/** A WHERE naming EVERY primary-key column, and its bound params. */
|
|
149
|
+
protected pkWhere(): {
|
|
150
|
+
sql: string;
|
|
151
|
+
params: unknown[];
|
|
152
|
+
};
|
|
138
153
|
/**
|
|
139
154
|
* Get the primary key database column name (applies fieldMapping).
|
|
140
155
|
*/
|
|
@@ -195,20 +210,41 @@ export declare class BaseModel {
|
|
|
195
210
|
*/
|
|
196
211
|
load(filter?: string, params?: unknown[], include?: string[]): Promise<boolean>;
|
|
197
212
|
/**
|
|
198
|
-
* Find all records
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
213
|
+
* Find all records.
|
|
214
|
+
*
|
|
215
|
+
* BREAKING (3.13.95, parity): the signature is now
|
|
216
|
+
* `all(limit?, offset?, include?, orderBy?)`. It NO LONGER accepts leading
|
|
217
|
+
* `where`/`params`.
|
|
218
|
+
*
|
|
219
|
+
* Node was the sole outlier of the four. The master and the other two never
|
|
220
|
+
* had a filter on `all()`:
|
|
221
|
+
* Python all(limit=100, offset=0, include=None, order_by=None)
|
|
222
|
+
* PHP all(int $limit = 100, int $offset = 0, ?array $include, ?string $orderBy)
|
|
223
|
+
* Ruby all(limit: 100, offset: nil, order_by: nil, include: nil)
|
|
224
|
+
* Node's extra leading parameters shifted every argument, so the same
|
|
225
|
+
* positional call meant different things in different languages -- which is
|
|
226
|
+
* precisely what the parity mandate exists to prevent.
|
|
227
|
+
*
|
|
228
|
+
* MIGRATION: a filtered read moves to `where()`, which already exists and
|
|
229
|
+
* takes the conditions first:
|
|
230
|
+
* before: User.all("age > ?", [28])
|
|
231
|
+
* after: User.where("age > ?", [28])
|
|
232
|
+
* TypeScript callers get a compile error (string is not assignable to number),
|
|
233
|
+
* so the break is loud rather than silent.
|
|
234
|
+
*
|
|
235
|
+
* @param limit Max records (default 100, the shared cross-framework cap).
|
|
236
|
+
* @param offset Records to skip (default 0).
|
|
237
|
+
* @param include Relationship names to eager-load.
|
|
238
|
+
* @param orderBy ORDER BY clause (e.g. "name ASC").
|
|
203
239
|
*/
|
|
204
|
-
static all<T extends BaseModel>(this: new (data?: Record<string, unknown>) => T,
|
|
240
|
+
static all<T extends BaseModel>(this: new (data?: Record<string, unknown>) => T, limit?: number, offset?: number, include?: string[], orderBy?: string): Promise<T[]>;
|
|
205
241
|
/**
|
|
206
242
|
* Query records with a WHERE clause.
|
|
207
243
|
* Matches Python/PHP/Ruby where() API.
|
|
208
244
|
*
|
|
209
245
|
* @param conditions WHERE clause (e.g. "age > ? AND active = ?")
|
|
210
246
|
* @param params Bind parameters
|
|
211
|
-
* @param limit Max records (default
|
|
247
|
+
* @param limit Max records (default 100)
|
|
212
248
|
* @param offset Skip records (default 0)
|
|
213
249
|
* @param include Relationship names to eager-load
|
|
214
250
|
* @param orderBy ORDER BY clause (e.g. "name ASC")
|
|
@@ -299,7 +335,7 @@ export declare class BaseModel {
|
|
|
299
335
|
* @param sql SQL query string.
|
|
300
336
|
* @param params Bind parameters.
|
|
301
337
|
* @param ttl Cache TTL in seconds (default 60).
|
|
302
|
-
* @param limit Max records to return (default
|
|
338
|
+
* @param limit Max records to return (default 100).
|
|
303
339
|
* @param offset Records to skip (default 0).
|
|
304
340
|
* @param include Relationship names to eager-load on cache miss.
|
|
305
341
|
*/
|
|
@@ -311,7 +347,7 @@ export declare class BaseModel {
|
|
|
311
347
|
/**
|
|
312
348
|
* Execute a raw SQL SELECT and return results as model instances.
|
|
313
349
|
*/
|
|
314
|
-
static select<T extends BaseModel>(this: new (data?: Record<string, unknown>) => T, sql: string, params?: unknown[]): Promise<T[]>;
|
|
350
|
+
static select<T extends BaseModel>(this: new (data?: Record<string, unknown>) => T, sql: string, params?: unknown[], limit?: number, offset?: number): Promise<T[]>;
|
|
315
351
|
static selectOne<T extends BaseModel>(this: new (data?: Record<string, unknown>) => T, sql: string, params?: unknown[], include?: string[]): Promise<T | null>;
|
|
316
352
|
/**
|
|
317
353
|
* Permanently delete this instance, bypassing soft delete.
|
|
@@ -83,6 +83,19 @@ export declare class CachedDatabaseAdapter implements DatabaseAdapter {
|
|
|
83
83
|
private backend;
|
|
84
84
|
private backendPromise;
|
|
85
85
|
private backendName;
|
|
86
|
+
/**
|
|
87
|
+
* WHICH DATABASE this wrapper caches for, folded into every cache key.
|
|
88
|
+
* Empty only for an adapter built outside the URL/config funnels, which then
|
|
89
|
+
* behaves exactly as before rather than colliding with a tagged one.
|
|
90
|
+
*
|
|
91
|
+
* Optional-chained on the ADAPTER, not just the property. `setAdapter(null)`
|
|
92
|
+
* is the documented reset idiom (migrateCli.test.ts uses it to clear ORM
|
|
93
|
+
* state between cases) and it reaches here through wrapWithCache. Before the
|
|
94
|
+
* identity field existed the constructor only STORED the adapter, so a null
|
|
95
|
+
* passed through harmlessly; reading `adapter.cacheIdentity` turned that
|
|
96
|
+
* reset into "Cannot read properties of null".
|
|
97
|
+
*/
|
|
98
|
+
private readonly identity;
|
|
86
99
|
constructor(adapter: DatabaseAdapter, options?: CachedAdapterOptions);
|
|
87
100
|
/**
|
|
88
101
|
* Whether the persistent layer should use a distributed/serialised backend.
|
|
@@ -140,13 +153,13 @@ export declare class CachedDatabaseAdapter implements DatabaseAdapter {
|
|
|
140
153
|
fetch<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number, noCache?: boolean): T[];
|
|
141
154
|
fetchOne<T = Record<string, unknown>>(sql: string, params?: unknown[], noCache?: boolean): T | null;
|
|
142
155
|
insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): DatabaseResult;
|
|
143
|
-
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown>): DatabaseResult;
|
|
144
|
-
delete(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[]): DatabaseResult;
|
|
156
|
+
update(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): DatabaseResult;
|
|
157
|
+
delete(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[], params?: unknown[]): DatabaseResult;
|
|
145
158
|
startTransaction(): void;
|
|
146
159
|
commit(): void;
|
|
147
160
|
rollback(): void;
|
|
148
|
-
|
|
149
|
-
|
|
161
|
+
getTables(): string[];
|
|
162
|
+
getColumns(table: string): ColumnInfo[];
|
|
150
163
|
lastInsertId(): number | bigint | string | null;
|
|
151
164
|
close(): void;
|
|
152
165
|
tableExists(name: string): boolean;
|
|
@@ -161,7 +174,7 @@ export declare class CachedDatabaseAdapter implements DatabaseAdapter {
|
|
|
161
174
|
queryAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
162
175
|
executeAsync(sql: string, params?: unknown[]): Promise<unknown>;
|
|
163
176
|
insertAsync(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseResult>;
|
|
164
|
-
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown
|
|
177
|
+
updateAsync(table: string, data: Record<string, unknown>, filter: Record<string, unknown> | string, params?: unknown[]): Promise<DatabaseResult>;
|
|
165
178
|
deleteAsync(table: string, filter: Record<string, unknown> | string | Record<string, unknown>[], params?: unknown[]): Promise<DatabaseResult>;
|
|
166
179
|
startTransactionAsync(): Promise<void>;
|
|
167
180
|
commitAsync(): Promise<void>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** Seconds. Long enough for a cold cross-region connect, short enough to page. */
|
|
2
|
+
export declare const DEFAULT_DATABASE_CONNECT_TIMEOUT_SECONDS = 10;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the connect budget in MILLISECONDS, or `null` when the bound is
|
|
5
|
+
* disabled.
|
|
6
|
+
*
|
|
7
|
+
* Milliseconds because every consumer needs them: `setTimeout` and all four
|
|
8
|
+
* driver knobs are in ms. Seconds are the operator-facing unit, so the variable
|
|
9
|
+
* is read as seconds and converted once, here.
|
|
10
|
+
*
|
|
11
|
+
* Call this ONCE per connect and pass the result down - it is the only resolver,
|
|
12
|
+
* so calling it twice would warn twice about one typo.
|
|
13
|
+
*/
|
|
14
|
+
export declare function connectTimeoutMillis(): number | null;
|
|
15
|
+
/**
|
|
16
|
+
* Clock slack when deciding whether a failed connect was OUR bound expiring.
|
|
17
|
+
*
|
|
18
|
+
* THE DECISION IS MADE BY ELAPSED TIME, NEVER BY MATCHING THE DRIVER'S TEXT.
|
|
19
|
+
* The four clients word an expiry four different ways - pg `timeout expired`,
|
|
20
|
+
* mysql2 `connect ETIMEDOUT`, tedious `Failed to connect to ... in 2000ms`,
|
|
21
|
+
* Mongo `Server selection timed out after 2000 ms` - and a marker table would
|
|
22
|
+
* drift the moment any of them reworded, then MISS. A missed timeout is the
|
|
23
|
+
* whole defect this file exists to prevent, so nothing here reads the message.
|
|
24
|
+
*
|
|
25
|
+
* WHY 50ms, AND WHY NOT ZERO. Python and PHP use no tolerance at all, PHP having
|
|
26
|
+
* measured its four C clients OVERSHOOTING their deadline by ~3ms at a 3s bound -
|
|
27
|
+
* a client that measures its own elapsed time can never report early. Node's
|
|
28
|
+
* knobs are not those: all four are plain JS `setTimeout` calls (pg `client.js`,
|
|
29
|
+
* mysql2 `base/connection.js`, tedious `connection.js`, and the Mongo driver's
|
|
30
|
+
* selection loop), and libuv's loop time is coarse, so a Node timer CAN fire
|
|
31
|
+
* before `performance.now()` agrees the budget has passed. MEASURED, 60 rounds
|
|
32
|
+
* at a 200ms budget:
|
|
33
|
+
*
|
|
34
|
+
* Linux x64, Node v24.18.0 earliest -0.7125ms (fires EARLY)
|
|
35
|
+
* darwin arm64, Node v24.9.0 earliest +0.0872ms (never early)
|
|
36
|
+
*
|
|
37
|
+
* Zero would therefore be a real miss on Linux. 50ms is a ~70x margin on the
|
|
38
|
+
* measured worst case, and still 0.5% of the 10s default. Ruby's 250ms is for a
|
|
39
|
+
* different problem - libpq's `connect_timeout` is INTEGER SECONDS and reads a
|
|
40
|
+
* 10s bound back as 9.998s - which no Node client has.
|
|
41
|
+
*
|
|
42
|
+
* It does NOT inflate the operator's N: it only widens what COUNTS as the bound
|
|
43
|
+
* expiring, never how long anything waits. It errs deliberately: over-translating
|
|
44
|
+
* a genuine fast failure that lands within 50ms of the bound still shows the
|
|
45
|
+
* operator the driver's real error (`Driver reported:`, plus `cause`), whereas
|
|
46
|
+
* under-translating hands them a bare driver message naming no variable.
|
|
47
|
+
*/
|
|
48
|
+
export declare const CONNECT_TIMEOUT_TOLERANCE_MS = 50;
|
|
49
|
+
/**
|
|
50
|
+
* The value for a driver's own connect-timeout option, from the Tina4 budget.
|
|
51
|
+
* `null` in, `null` out - a disabled bound sets no driver option at all.
|
|
52
|
+
*
|
|
53
|
+
* ROUNDED UP, AND NEVER TO 0. Up, so the driver's timer can never expire before
|
|
54
|
+
* our clock has reached the bound - that ordering is the whole basis for the
|
|
55
|
+
* elapsed-time test in `withConnectTimeout`, and rounding down would break it.
|
|
56
|
+
* Never 0, because three of the four knobs read 0 as WAIT FOREVER: pg does
|
|
57
|
+
* `connectionTimeoutMillis || 0` then `if (> 0)`, mysql2 does
|
|
58
|
+
* `if (this.config.connectTimeout)`, and libpq (the same trap Python and Ruby
|
|
59
|
+
* name) treats `connect_timeout=0` as no limit. A sub-millisecond bound must
|
|
60
|
+
* therefore floor at 1ms rather than silently disabling the bound being set.
|
|
61
|
+
*/
|
|
62
|
+
export declare function driverConnectTimeoutMillis(budgetMs: number | null): number | null;
|
|
63
|
+
/**
|
|
64
|
+
* Best-effort host/port for the DIAGNOSTIC, from either a config object or a
|
|
65
|
+
* connection URL. Never used to connect - the adapter has already done that with
|
|
66
|
+
* its own parsing, and this must not become a second, divergent parser that
|
|
67
|
+
* decides where anything dials.
|
|
68
|
+
*/
|
|
69
|
+
export declare function connectTarget(config: {
|
|
70
|
+
host?: string;
|
|
71
|
+
port?: number;
|
|
72
|
+
} | string, defaultPort: number): {
|
|
73
|
+
host: string;
|
|
74
|
+
port: number | string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Bound a driver connect, and name the bound when it expires.
|
|
78
|
+
*
|
|
79
|
+
* @param attempt a THUNK that starts the driver's connect. It is a thunk, not a
|
|
80
|
+
* promise, so OUR CLOCK STARTS FIRST - everything that touches
|
|
81
|
+
* the driver must run inside it. That ordering is load-bearing:
|
|
82
|
+
* the driver arms its own timer somewhere in here (mysql2 arms
|
|
83
|
+
* its at the END OF ITS CONSTRUCTOR, not in `connect()`), and
|
|
84
|
+
* only by starting first can we know that the driver's timer
|
|
85
|
+
* cannot expire before `elapsed` has reached the bound.
|
|
86
|
+
* @param budgetMs from `connectTimeoutMillis()`; `null` runs `attempt` untouched
|
|
87
|
+
* @param host named in the error - an operator needs to know WHICH server hung
|
|
88
|
+
* @param port named in the error alongside the host
|
|
89
|
+
* @param abandon called if the driver answers AFTER we gave up, with whatever it
|
|
90
|
+
* produced. Nobody will ever use that connection, so the adapter
|
|
91
|
+
* closes it here rather than leaking a socket for the life of the
|
|
92
|
+
* process - a connect that is retried every 10s would otherwise
|
|
93
|
+
* accumulate one abandoned connection per attempt, forever.
|
|
94
|
+
*
|
|
95
|
+
* TWO WAYS OUT, both wearing the same message. Normally the DRIVER's timer fires
|
|
96
|
+
* first (it was armed first) and we TRANSLATE its failure; where the driver has
|
|
97
|
+
* no knob, or its knob did not cover the phase that hung, our own timer fires as
|
|
98
|
+
* the backstop and there is no driver diagnosis to report.
|
|
99
|
+
*/
|
|
100
|
+
export declare function withConnectTimeout<T>(attempt: () => Promise<T>, budgetMs: number | null, host: string, port: number | string, abandon?: (arrived: T) => void): Promise<T>;
|