tina4-nodejs 3.13.92 → 3.13.94

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.
Files changed (134) hide show
  1. package/CLAUDE.md +16 -3
  2. package/README.md +1 -1
  3. package/package.json +12 -9
  4. package/packages/cli/dist/bin.js +1260 -969
  5. package/packages/core/dist/index.js +1260 -969
  6. package/packages/core/src/devMailbox.ts +20 -44
  7. package/packages/core/src/index.ts +2 -2
  8. package/packages/core/src/messenger.ts +72 -0
  9. package/packages/core/src/queueBackends/kafkaBackend.ts +108 -12
  10. package/packages/core/src/sessionHandlers/childError.ts +72 -0
  11. package/packages/core/src/sessionHandlers/mongoClient.ts +9 -3
  12. package/packages/core/src/sessionHandlers/redisHandler.ts +18 -5
  13. package/packages/core/src/sessionHandlers/respClient.ts +5 -1
  14. package/packages/frond/dist/index.js +74 -31
  15. package/packages/frond/src/engine.ts +99 -33
  16. package/packages/orm/dist/index.js +3055 -2764
  17. package/packages/orm/src/adapters/sqlite.ts +4 -1
  18. package/packages/orm/src/database.ts +108 -8
  19. package/types/cli/src/bin.d.ts +92 -0
  20. package/types/cli/src/commands/build.d.ts +2 -0
  21. package/types/cli/src/commands/generate.d.ts +47 -0
  22. package/types/cli/src/commands/init.d.ts +1 -0
  23. package/types/cli/src/commands/metrics.d.ts +6 -0
  24. package/types/cli/src/commands/migrate.d.ts +1 -0
  25. package/types/cli/src/commands/migrateCreate.d.ts +1 -0
  26. package/types/cli/src/commands/migrateRollback.d.ts +1 -0
  27. package/types/cli/src/commands/migrateStatus.d.ts +1 -0
  28. package/types/cli/src/commands/queue.d.ts +20 -0
  29. package/types/cli/src/commands/routes.d.ts +1 -0
  30. package/types/cli/src/commands/seed.d.ts +1 -0
  31. package/types/cli/src/commands/serve.d.ts +6 -0
  32. package/types/cli/src/commands/test.d.ts +1 -0
  33. package/types/core/src/ai.d.ts +64 -0
  34. package/types/core/src/api.d.ts +262 -0
  35. package/types/core/src/auth.d.ts +154 -0
  36. package/types/core/src/authGate.d.ts +20 -0
  37. package/types/core/src/background.d.ts +34 -0
  38. package/types/core/src/cache.d.ts +160 -0
  39. package/types/core/src/constants.d.ts +38 -0
  40. package/types/core/src/container.d.ts +44 -0
  41. package/types/core/src/context/chunker.d.ts +31 -0
  42. package/types/core/src/context/index.d.ts +93 -0
  43. package/types/core/src/devAdmin.d.ts +179 -0
  44. package/types/core/src/devMailbox.d.ts +54 -0
  45. package/types/core/src/docs.d.ts +141 -0
  46. package/types/core/src/docsAutoDiscovery.d.ts +6 -0
  47. package/types/core/src/dotenv.d.ts +65 -0
  48. package/types/core/src/env.d.ts +28 -0
  49. package/types/core/src/errorOverlay.d.ts +36 -0
  50. package/types/core/src/events.d.ts +75 -0
  51. package/types/core/src/fakeData.d.ts +55 -0
  52. package/types/core/src/feedback.d.ts +90 -0
  53. package/types/core/src/graphql.d.ts +207 -0
  54. package/types/core/src/health.d.ts +22 -0
  55. package/types/core/src/htmlElement.d.ts +75 -0
  56. package/types/core/src/i18n.d.ts +37 -0
  57. package/types/core/src/index.d.ts +93 -0
  58. package/types/core/src/job.d.ts +39 -0
  59. package/types/core/src/logger.d.ts +123 -0
  60. package/types/core/src/mcp.d.ts +248 -0
  61. package/types/core/src/messenger.d.ts +191 -0
  62. package/types/core/src/metrics.d.ts +77 -0
  63. package/types/core/src/middleware.d.ts +207 -0
  64. package/types/core/src/mqtt.d.ts +257 -0
  65. package/types/core/src/mqttMessage.d.ts +67 -0
  66. package/types/core/src/plan.d.ts +96 -0
  67. package/types/core/src/projectIndex.d.ts +56 -0
  68. package/types/core/src/queue.d.ts +219 -0
  69. package/types/core/src/queueBackends/kafkaBackend.d.ts +117 -0
  70. package/types/core/src/queueBackends/liteBackend.d.ts +119 -0
  71. package/types/core/src/queueBackends/mongoBackend.d.ts +97 -0
  72. package/types/core/src/queueBackends/rabbitmqBackend.d.ts +55 -0
  73. package/types/core/src/rateLimiter.d.ts +49 -0
  74. package/types/core/src/request.d.ts +25 -0
  75. package/types/core/src/response.d.ts +28 -0
  76. package/types/core/src/routeDiscovery.d.ts +12 -0
  77. package/types/core/src/router.d.ts +355 -0
  78. package/types/core/src/scss.d.ts +19 -0
  79. package/types/core/src/server.d.ts +131 -0
  80. package/types/core/src/service.d.ts +115 -0
  81. package/types/core/src/session.d.ts +256 -0
  82. package/types/core/src/sessionHandlers/childError.d.ts +34 -0
  83. package/types/core/src/sessionHandlers/databaseHandler.d.ts +42 -0
  84. package/types/core/src/sessionHandlers/mongoClient.d.ts +24 -0
  85. package/types/core/src/sessionHandlers/mongoHandler.d.ts +61 -0
  86. package/types/core/src/sessionHandlers/redisHandler.d.ts +60 -0
  87. package/types/core/src/sessionHandlers/respClient.d.ts +22 -0
  88. package/types/core/src/sessionHandlers/valkeyHandler.d.ts +65 -0
  89. package/types/core/src/static.d.ts +2 -0
  90. package/types/core/src/test.d.ts +94 -0
  91. package/types/core/src/testClient.d.ts +36 -0
  92. package/types/core/src/testing.d.ts +58 -0
  93. package/types/core/src/types.d.ts +219 -0
  94. package/types/core/src/validator.d.ts +52 -0
  95. package/types/core/src/websocket.d.ts +376 -0
  96. package/types/core/src/websocketBackplane.d.ts +166 -0
  97. package/types/core/src/websocketConnection.d.ts +54 -0
  98. package/types/core/src/wsdl.d.ts +101 -0
  99. package/types/frond/src/engine.d.ts +263 -0
  100. package/types/frond/src/index.d.ts +2 -0
  101. package/types/orm/src/adapters/firebird.d.ts +138 -0
  102. package/types/orm/src/adapters/mongodb.d.ts +81 -0
  103. package/types/orm/src/adapters/mssql.d.ts +70 -0
  104. package/types/orm/src/adapters/mysql.d.ts +66 -0
  105. package/types/orm/src/adapters/odbc.d.ts +97 -0
  106. package/types/orm/src/adapters/postgres.d.ts +85 -0
  107. package/types/orm/src/adapters/sqlite.d.ts +56 -0
  108. package/types/orm/src/autoCrud.d.ts +73 -0
  109. package/types/orm/src/baseModel.d.ts +391 -0
  110. package/types/orm/src/cachedDatabase.d.ts +177 -0
  111. package/types/orm/src/database.d.ts +609 -0
  112. package/types/orm/src/databaseResult.d.ts +85 -0
  113. package/types/orm/src/docstore.d.ts +182 -0
  114. package/types/orm/src/fakeData.d.ts +22 -0
  115. package/types/orm/src/index.d.ts +40 -0
  116. package/types/orm/src/migration.d.ts +275 -0
  117. package/types/orm/src/model.d.ts +7 -0
  118. package/types/orm/src/query.d.ts +14 -0
  119. package/types/orm/src/queryBuilder.d.ts +173 -0
  120. package/types/orm/src/realtime/index.d.ts +7 -0
  121. package/types/orm/src/realtime/models/attachment.d.ts +43 -0
  122. package/types/orm/src/realtime/models/channel.d.ts +32 -0
  123. package/types/orm/src/realtime/models/channelMember.d.ts +32 -0
  124. package/types/orm/src/realtime/models/message.d.ts +36 -0
  125. package/types/orm/src/realtime/models/workspace.d.ts +26 -0
  126. package/types/orm/src/realtime/realtime.d.ts +24 -0
  127. package/types/orm/src/realtime/storage.d.ts +61 -0
  128. package/types/orm/src/seeder.d.ts +118 -0
  129. package/types/orm/src/sqlTranslator.d.ts +134 -0
  130. package/types/orm/src/types.d.ts +138 -0
  131. package/types/orm/src/validation.d.ts +6 -0
  132. package/types/swagger/src/generator.d.ts +46 -0
  133. package/types/swagger/src/index.d.ts +2 -0
  134. package/types/swagger/src/ui.d.ts +11 -0
@@ -0,0 +1,609 @@
1
+ import type { DatabaseAdapter, DatabaseResult as DatabaseWriteResult, ColumnInfo, FieldDefinition } from "./types.js";
2
+ import { DatabaseResult } from "./databaseResult.js";
3
+ import { type CachedAdapterOptions } from "./cachedDatabase.js";
4
+ /**
5
+ * v3.13.12 — strip trailing `;` and whitespace from user-supplied SQL
6
+ * before the framework wraps it with COUNT(*) subqueries or appends
7
+ * LIMIT/OFFSET clauses. Without this, `"SELECT * FROM t;"` becomes
8
+ * `"SELECT * FROM t; LIMIT 100 OFFSET 0"` — a syntax error on every
9
+ * engine. Internal semicolons (in string literals, between meaningful
10
+ * statements) are left alone; drivers reject those if the engine
11
+ * doesn't support multi-statement.
12
+ *
13
+ * Exported so adapters and external tooling can compose it.
14
+ */
15
+ export declare function stripTrailingSemicolons(sql: string): string;
16
+ /**
17
+ * Adapter bridge helpers (v3.14.0, Option A).
18
+ *
19
+ * The public Database/BaseModel/QueryBuilder API is async so it works on the
20
+ * async adapters (PostgreSQL/MySQL/MSSQL/Firebird/Mongo). SQLite implements
21
+ * only the synchronous methods (`node:sqlite` is sync); the async adapters
22
+ * implement only the `*Async` variants and make the sync methods throw.
23
+ *
24
+ * Each helper prefers the adapter's `*Async` method when present and awaits it,
25
+ * otherwise falls back to the sync method. For SQLite the fallback resolves
26
+ * instantly; for async adapters the awaited promise does the real work. This is
27
+ * the single chokepoint every public read/write flows through.
28
+ */
29
+ export declare function adapterFetch<T = Record<string, unknown>>(adapter: DatabaseAdapter, sql: string, params?: unknown[], limit?: number, skip?: number, noCache?: boolean): Promise<T[]>;
30
+ export declare function adapterQuery<T = Record<string, unknown>>(adapter: DatabaseAdapter, sql: string, params?: unknown[]): Promise<T[]>;
31
+ export declare function adapterFetchOne<T = Record<string, unknown>>(adapter: DatabaseAdapter, sql: string, params?: unknown[]): Promise<T | null>;
32
+ export declare function adapterExecute(adapter: DatabaseAdapter, sql: string, params?: unknown[]): Promise<unknown>;
33
+ export declare function adapterStartTransaction(adapter: DatabaseAdapter): Promise<void>;
34
+ export declare function adapterCommit(adapter: DatabaseAdapter): Promise<void>;
35
+ export declare function adapterRollback(adapter: DatabaseAdapter): Promise<void>;
36
+ export declare function adapterTableExists(adapter: DatabaseAdapter, name: string): Promise<boolean>;
37
+ export declare function adapterTables(adapter: DatabaseAdapter): Promise<string[]>;
38
+ export declare function adapterColumns(adapter: DatabaseAdapter, table: string): Promise<ColumnInfo[]>;
39
+ export declare function adapterCreateTable(adapter: DatabaseAdapter, name: string, columns: Record<string, FieldDefinition>): Promise<void>;
40
+ /**
41
+ * Extract the engine-assigned auto-increment id from an `execute()` result.
42
+ *
43
+ * SQLite returns `{ lastInsertRowid }`. PostgreSQL (pg) returns a result whose
44
+ * `rows[0].id` holds the value when the statement had a `RETURNING` clause
45
+ * (insertAsync adds one). MySQL/MSSQL adapters set the adapter's lastId,
46
+ * so callers fall back to `adapter.lastInsertId()` when the result has neither.
47
+ */
48
+ export declare function extractLastInsertId(result: unknown): number | bigint | null;
49
+ /**
50
+ * Wrap a raw adapter with the query cache so BOTH `db.fetch()` (via the
51
+ * Database wrapper) AND ORM reads (via `getAdapter()` / `getNamedAdapter()`)
52
+ * are cached through the same store and counters.
53
+ *
54
+ * Idempotent: an already-wrapped adapter is returned as-is, so re-binding the
55
+ * same adapter (or binding the adapter a Database wrapper already holds) never
56
+ * double-wraps. `options.sharedCache` backs all pooled connections with one
57
+ * store so a write on any connection invalidates reads cached by all of them.
58
+ *
59
+ * Caching is OFF by default — both layers are opt-in. Turn the request-scoped
60
+ * layer on with TINA4_AUTO_CACHING=true (for read-heavy endpoints) and/or the
61
+ * persistent cross-request layer with TINA4_DB_CACHE=true. With both unset the
62
+ * wrapper passes everything straight through (no cached read-after-write footgun).
63
+ */
64
+ export declare function wrapWithCache(adapter: DatabaseAdapter, options?: CachedAdapterOptions): DatabaseAdapter;
65
+ /**
66
+ * Resolve the underlying wrapped adapter for a given raw adapter — used so the
67
+ * Database wrapper and `getAdapter()` end up holding the SAME
68
+ * CachedDatabaseAdapter instance (one cache, one set of counters).
69
+ */
70
+ export declare function setAdapter(adapter: DatabaseAdapter): DatabaseAdapter;
71
+ /**
72
+ * Clear the request-scoped query cache on every live connection at the start of
73
+ * each HTTP request, so request-scoped caching never serves rows across
74
+ * requests. Persistent-mode connections (TINA4_DB_CACHE=true) are untouched.
75
+ *
76
+ * The request dispatcher calls this. Mirrors Python's
77
+ * `Database.reset_request_caches()`.
78
+ */
79
+ export declare function resetRequestCaches(): void;
80
+ /**
81
+ * Public, user-facing API to bind a database connection.
82
+ *
83
+ * - No `name` → registers `adapter` as the global default connection
84
+ * (what `getAdapter()` returns and what every model resolves to unless it
85
+ * declares `static _db`). This is the manual equivalent of the auto-binding
86
+ * that `initDatabase()` performs from `.env`/`TINA4_DATABASE_URL`.
87
+ * - With `name` → registers `adapter` in the named registry. A model with
88
+ * `static _db = name` resolves to it via `getNamedAdapter(name)`.
89
+ *
90
+ * Mirrors the Python master `bind_database(db, name=None)`.
91
+ *
92
+ * import { bindDatabase, createAdapterFromUrl } from "@tina4/orm";
93
+ *
94
+ * // Default connection
95
+ * bindDatabase(adapter);
96
+ *
97
+ * // Named secondary connection built from a URL (kept synchronous —
98
+ * // build the adapter first, then bind it)
99
+ * bindDatabase(await createAdapterFromUrl(url, user, pass), "analytics");
100
+ *
101
+ * `bindDatabase` itself is synchronous: it takes an already-constructed
102
+ * adapter. Use `createAdapterFromUrl()` to build a named secondary adapter
103
+ * from a URL without making it the default.
104
+ */
105
+ export declare function bindDatabase(adapter: DatabaseAdapter, name?: string): void;
106
+ export declare function getAdapter(): DatabaseAdapter;
107
+ /**
108
+ * Register a named adapter for multi-database support.
109
+ * Models reference it via `static _db = 'name'`.
110
+ */
111
+ export declare function setNamedAdapter(name: string, adapter: DatabaseAdapter): void;
112
+ /**
113
+ * Get a named adapter previously registered via `bindDatabase(adapter, name)`
114
+ * (or the lower-level `setNamedAdapter(name, adapter)`).
115
+ *
116
+ * Throws a clear error if the name isn't registered — a model that declares
117
+ * `static _db = "name"` resolves through here, so a missing name means the
118
+ * connection was never bound. The message tells the developer exactly how to
119
+ * fix it rather than silently falling back to the default connection (which
120
+ * would hide the mistake and write to the wrong database).
121
+ */
122
+ export declare function getNamedAdapter(name: string): DatabaseAdapter;
123
+ export declare function closeDatabase(): void;
124
+ export interface DatabaseConfig {
125
+ type?: "sqlite" | "postgres" | "mysql" | "mssql" | "sqlserver" | "firebird" | "mongodb" | "odbc";
126
+ path?: string;
127
+ url?: string;
128
+ host?: string;
129
+ port?: number;
130
+ user?: string;
131
+ username?: string;
132
+ password?: string;
133
+ database?: string;
134
+ /** ODBC-specific: full connection string, e.g. "DSN=MyDSN" or "DRIVER={SQL Server};SERVER=host;DATABASE=db" */
135
+ connectionString?: string;
136
+ }
137
+ /**
138
+ * Parsed result from a TINA4_DATABASE_URL connection string.
139
+ */
140
+ export interface ParsedDatabaseUrl {
141
+ type: "sqlite" | "postgres" | "mysql" | "mssql" | "firebird" | "mongodb" | "odbc";
142
+ path?: string;
143
+ host?: string;
144
+ port?: number;
145
+ user?: string;
146
+ password?: string;
147
+ database?: string;
148
+ /** ODBC-specific: raw connection string passed to odbc.connect() */
149
+ connectionString?: string;
150
+ }
151
+ /**
152
+ * Parse a TINA4_DATABASE_URL connection string into its components.
153
+ *
154
+ * Supported formats:
155
+ * sqlite:///path/to/db.sqlite
156
+ * sqlite://./relative/path.db
157
+ * postgresql://user:pass@host:port/dbname
158
+ * postgres://user:pass@host:port/dbname
159
+ * mysql://user:pass@host:port/dbname
160
+ *
161
+ * @param url - The connection URL string.
162
+ * @param username - Optional username to merge when the URL has no credentials.
163
+ * @param password - Optional password to merge when the URL has no credentials.
164
+ * @returns Parsed database configuration.
165
+ * @throws Error if the URL scheme is not supported.
166
+ */
167
+ export declare function parseDatabaseUrl(url: string, username?: string, password?: string): ParsedDatabaseUrl;
168
+ /**
169
+ * A wrapper class around a DatabaseAdapter that provides a clean, high-level API.
170
+ *
171
+ * Mirrors the Database class in Python/Ruby Tina4 implementations.
172
+ *
173
+ * Usage:
174
+ * const db = await Database.create("sqlite:///path/to/db.sqlite");
175
+ * const rows = db.fetch("SELECT * FROM users WHERE active = ?", [true], 10, 0);
176
+ * const user = db.fetchOne("SELECT * FROM users WHERE id = ?", [1]);
177
+ * db.insert("users", { name: "Alice", email: "alice@example.com" });
178
+ * db.update("users", { name: "Bob" }, { id: 1 });
179
+ * db.delete("users", { id: 1 });
180
+ * db.close();
181
+ *
182
+ * Connection pooling:
183
+ * const db = await Database.create("sqlite:///data/app.db", undefined, undefined, 4);
184
+ * // 4 connections, round-robin rotation
185
+ */
186
+ export declare class Database {
187
+ private adapter;
188
+ /** Connection pool — array of adapters with lazy creation */
189
+ private pool;
190
+ /** Pool size (0 = single connection) */
191
+ private _poolSize;
192
+ /** Round-robin index */
193
+ private poolIndex;
194
+ /** Factory for creating new adapters (used by pool) */
195
+ private adapterFactory;
196
+ /** table -> primary-key column name (or null), introspected once */
197
+ private _pkCache;
198
+ /**
199
+ * Whether a standalone write auto-commits. ON by default — a write made
200
+ * outside an explicit transaction commits on its own connection before
201
+ * returning (so it's durable and visible across pooled connections). Inside
202
+ * startTransaction()/commit()/rollback() the per-statement commit is
203
+ * suppressed, so explicit transactions stay atomic. Set TINA4_AUTOCOMMIT=false
204
+ * for strict manual-commit mode.
205
+ */
206
+ private autoCommit;
207
+ private lastError;
208
+ /** Database engine type (sqlite, postgres, mysql, mssql, firebird) */
209
+ private dbType;
210
+ /**
211
+ * Async-local storage for the adapter pinned to the current transaction.
212
+ *
213
+ * With pooling enabled, ordinary calls round-robin through the pool. Inside
214
+ * a transaction, however, all calls must land on the SAME adapter — otherwise
215
+ * startTransaction(), execute() and commit() each rotate to a different
216
+ * connection and the transaction is meaningless (executes autocommit on
217
+ * whatever adapter they hit; the final commit lands on yet another adapter
218
+ * that has nothing to commit; rollback() is silently no-op'd).
219
+ *
220
+ * AsyncLocalStorage is the Node analog of Python's threading.local. It pins
221
+ * the adapter to the current async task tree so concurrent transactions on
222
+ * the same Database don't clobber each other. startTransaction() sets the
223
+ * pin via .enterWith(); commit()/rollback() clear it.
224
+ */
225
+ private txStore;
226
+ /**
227
+ * Create a Database wrapping an existing adapter.
228
+ * For creating a Database from a URL, use the async static factories:
229
+ * Database.create(url) or Database.fromEnv()
230
+ */
231
+ constructor(adapter: DatabaseAdapter);
232
+ /**
233
+ * Set the engine type ("sqlite" | "postgres" | "mysql" | "mssql" |
234
+ * "firebird" | "mongodb"). The static `Database.create` factory assigns the
235
+ * private `dbType` directly; `initDatabase()` (a free function, no private
236
+ * access) routes through this setter so a URL connection is correctly typed.
237
+ * Without it a `postgres://` connection kept the `"sqlite"` default and
238
+ * `getNextId()` took the SQLite branch — hitting the non-existent
239
+ * `tina4_sequences` table on PostgreSQL instead of native sequences (#255).
240
+ */
241
+ setDbType(type: string): void;
242
+ /**
243
+ * Async factory: creates a Database from a connection URL.
244
+ * Works with all adapter types (sqlite, postgres, mysql, mssql, firebird).
245
+ *
246
+ * @param url - Connection URL
247
+ * @param username - Optional username
248
+ * @param password - Optional password
249
+ * @param pool - Number of pooled connections (0 = single, N>0 = round-robin)
250
+ */
251
+ static create(url: string, username?: string, password?: string, pool?: number): Promise<Database>;
252
+ /**
253
+ * Create a Database from an environment variable.
254
+ * @param envKey - Name of the env var holding the connection URL. Defaults to "TINA4_DATABASE_URL".
255
+ * @param pool - Number of pooled connections (0 = single, N>0 = round-robin)
256
+ */
257
+ static fromEnv(envKey?: string, pool?: number): Promise<Database>;
258
+ /**
259
+ * Get the next adapter — from pool (round-robin) or single connection.
260
+ *
261
+ * If a transaction is active (an adapter is pinned in async-local storage),
262
+ * that adapter is returned for every call so the whole transaction is
263
+ * atomic on one connection. Otherwise pooled mode round-robins.
264
+ */
265
+ private getNextAdapter;
266
+ /** Get the underlying adapter (for advanced / escape-hatch usage). */
267
+ getAdapter(): DatabaseAdapter;
268
+ /** Get the pool size (0 = single connection mode). */
269
+ poolSize(): number;
270
+ /** Alias for poolSize() — returns total pool size (0 = single connection mode). */
271
+ size(): number;
272
+ /** Get the number of active (created) connections in the pool. */
273
+ activeCount(): number;
274
+ /**
275
+ * Borrow a connection from the pool (or the single adapter).
276
+ * The caller is responsible for returning it via checkin().
277
+ */
278
+ checkout(): DatabaseAdapter;
279
+ /**
280
+ * Return a borrowed connection to the pool.
281
+ * For round-robin pools this is a no-op (connections stay in the pool array),
282
+ * but the method exists for API parity and future pooling strategies.
283
+ */
284
+ checkin(_adapter: DatabaseAdapter): void;
285
+ /**
286
+ * Close all pooled connections and clear the pool.
287
+ * Equivalent to close() but named for explicit pool teardown.
288
+ */
289
+ closeAll(): void;
290
+ /** Query rows with optional pagination. Returns a DatabaseResult wrapper.
291
+ *
292
+ * Async since v3.14.0 (Option A): the public API awaits the adapter's
293
+ * `*Async` method when present (PostgreSQL/MySQL/MSSQL/Firebird/Mongo) and
294
+ * falls back to the synchronous method for SQLite (`node:sqlite` is sync, so
295
+ * the fallback resolves instantly). This is the breaking change that makes
296
+ * the wrapper work uniformly across every engine.
297
+ */
298
+ fetch(sql: string, params?: unknown[], limit?: number, offset?: number, opts?: {
299
+ noCache?: boolean;
300
+ }): Promise<DatabaseResult>;
301
+ /**
302
+ * Fetch a single row or null.
303
+ *
304
+ * Pass `{ noCache: true }` as the trailing options object to bypass the
305
+ * query cache for this one call — no lookup, no store, run directly
306
+ * (mirrors the Python master's `no_cache`). Default preserves caching.
307
+ */
308
+ fetchOne<T = Record<string, unknown>>(sql: string, params?: unknown[], opts?: {
309
+ noCache?: boolean;
310
+ }): Promise<T | null>;
311
+ /**
312
+ * Fetch rows and return the records array directly.
313
+ *
314
+ * Symmetric with `fetchOne`. For the common case where you just want
315
+ * the rows and don't need the `DatabaseResult` metadata, this is one
316
+ * less attribute access than `fetch(...).records`.
317
+ *
318
+ * const rows = db.fetchAll("SELECT * FROM users WHERE active = ?", [true]);
319
+ * for (const row of rows) console.log(row.name);
320
+ *
321
+ * Returns `[]` (not `null`) when no rows match. Cross-framework parity
322
+ * with Python `db.fetch_all()`, PHP `$db->fetchAll()`, and Ruby `db.fetch_all`.
323
+ *
324
+ * Pass `{ noCache: true }` as the trailing options object to bypass the
325
+ * query cache for this one call — no lookup, no store, run directly
326
+ * (mirrors the Python master's `no_cache`). The options object is a
327
+ * SEPARATE trailing argument, never the params array.
328
+ */
329
+ fetchAll<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, offset?: number, opts?: {
330
+ noCache?: boolean;
331
+ }): Promise<T[]>;
332
+ /**
333
+ * Execute a write statement.
334
+ *
335
+ * On SUCCESS returns `true` for simple writes, or the result set when the
336
+ * SQL contains RETURNING / CALL / EXEC / SELECT.
337
+ *
338
+ * On a SQL error (bad SQL, constraint violation, dead/aborted connection,
339
+ * missing driver) it FAILS LOUD: it records the cause on `lastError`
340
+ * (readable via `getError()`) and then RE-THROWS — it never swallows the
341
+ * error and returns `false`. This mirrors `fetch()`/`fetchOne()`, which
342
+ * already raise. Callers that need a boolean (e.g. ORM `save()`,
343
+ * `createTable()`, the migration runner, dev-admin/MCP DB tools) must
344
+ * `try/catch` and convert, rather than testing the return value.
345
+ */
346
+ execute(sql: string, params?: unknown[]): Promise<boolean | unknown>;
347
+ /** Insert one row (object) or a batch of rows (array of objects) into a table. */
348
+ insert(table: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<DatabaseWriteResult>;
349
+ /**
350
+ * The table's primary-key column, introspected once and cached.
351
+ *
352
+ * Uses the cross-engine getColumns() contract (v3.13.14, #48), which reports
353
+ * primaryKey per column on every adapter. Resolves to null when the table has
354
+ * no primary key or cannot be introspected.
355
+ */
356
+ primaryKey(table: string): Promise<string[]>;
357
+ /**
358
+ * A failed write must be loud.
359
+ *
360
+ * The adapters catch a SQL error and return { success: false, affectedRows: 0 },
361
+ * so a filterless update produced invalid SQL ("... WHERE ") and reported
362
+ * nothing rather than raising. A caller who does not inspect the result
363
+ * believes the write landed (audit feature 4, P1).
364
+ */
365
+ private static assertWrote;
366
+ /**
367
+ * Update rows. A write with no filter is an error, not a full-table write.
368
+ *
369
+ * With no explicit filter the primary key is taken out of `data` and used as
370
+ * the WHERE clause. With neither a filter nor a primary key in `data` this
371
+ * throws rather than silently changing nothing (audit feature 4, P1).
372
+ */
373
+ update(table: string, data: Record<string, unknown>, filter?: Record<string, unknown>, params?: unknown[]): Promise<DatabaseWriteResult>;
374
+ /** Delete rows. A filterless delete throws; use truncate() to empty a table. */
375
+ delete(table: string, filter?: Record<string, unknown>, params?: unknown[]): Promise<DatabaseWriteResult>;
376
+ /** Remove every row. The explicit spelling of a whole-table delete. */
377
+ truncate(table: string): Promise<DatabaseWriteResult>;
378
+ /** Close all database connections (pool or single). */
379
+ close(): void;
380
+ /**
381
+ * True while an explicit transaction is active on the current async context.
382
+ * startTransaction() pins an adapter into txStore; commit()/rollback() clear
383
+ * it. Standalone writes only auto-commit when this is false, so per-statement
384
+ * commits never break the atomicity of an explicit transaction.
385
+ */
386
+ private inExplicitTransaction;
387
+ /**
388
+ * Start a transaction. Pins the adapter to the current async context for
389
+ * the whole transaction so executes and the final commit/rollback all run
390
+ * on the same connection (critical when pool > 0).
391
+ *
392
+ * Nested-begin guard (DB-contract C): a second startTransaction() on a
393
+ * context that already has a pinned adapter is a double-begin — the inner
394
+ * BEGIN silently commits or no-ops on most engines, leaving the connection
395
+ * mid-transaction with the caller none the wiser. We keep a depth counter and
396
+ * log a clear warning instead of silently re-beginning; the pin stays on the
397
+ * original adapter so the eventual commit/rollback still land on the right
398
+ * connection, and the matching inner commit just unwinds the depth.
399
+ */
400
+ startTransaction(): Promise<void>;
401
+ /**
402
+ * Commit the current transaction.
403
+ *
404
+ * FAIL LOUD (DB-contract C): if the underlying commit raises, capture
405
+ * lastError and RE-THROW — never swallow. On failure the transaction pin is
406
+ * RETAINED so the caller's follow-up rollback() lands on the SAME connection
407
+ * (clearing it would leak a dirty connection back into the pool and route the
408
+ * rollback to a different one). The pin is cleared ONLY on a successful
409
+ * commit. An inner commit of an ignored nested begin (depth > 1) just unwinds
410
+ * the depth — the outer commit is the real one.
411
+ */
412
+ commit(): Promise<void>;
413
+ /**
414
+ * Rollback the current transaction — the terminal cleanup of a transaction,
415
+ * so it ALWAYS clears the pin (and the depth counter), even after a failed
416
+ * commit (it routes to the retained pinned connection and cleans it up). If
417
+ * the underlying rollback itself raises, lastError is captured and the error
418
+ * re-thrown, but the pin is still released so a poisoned connection doesn't
419
+ * stay pinned to this context forever.
420
+ */
421
+ rollback(): Promise<void>;
422
+ /** Check if a table exists. */
423
+ tableExists(name: string): Promise<boolean>;
424
+ /** List all tables in the database. */
425
+ getTables(): Promise<string[]>;
426
+ /**
427
+ * Get column metadata for a table.
428
+ * Uses the adapter's columns() method which handles engine-specific introspection
429
+ * (PRAGMA table_info for SQLite, information_schema.columns for others).
430
+ *
431
+ * @param tableName - Name of the table to inspect.
432
+ * @returns Array of column info objects: { name, type, nullable, default, primaryKey }.
433
+ */
434
+ getColumns(tableName: string): Promise<{
435
+ name: string;
436
+ type: string;
437
+ nullable?: boolean;
438
+ default?: unknown;
439
+ primaryKey?: boolean;
440
+ }[]>;
441
+ /**
442
+ * Execute a SQL statement with multiple parameter sets (batch insert/update).
443
+ * Wraps all executions in a single transaction for atomicity and performance.
444
+ *
445
+ * @param sql - The SQL statement with parameter placeholders.
446
+ * @param paramSets - Array of parameter arrays, one per execution.
447
+ * @returns Array of results from each execution.
448
+ */
449
+ executeMany(sql: string, paramSets?: unknown[][]): Promise<unknown[]>;
450
+ /** Return the last execute() error message, or null. */
451
+ getError(): string | null;
452
+ /**
453
+ * Return query cache statistics from the REAL cache backing this connection.
454
+ *
455
+ * The bound adapter is a CachedDatabaseAdapter (caching is OFF by default —
456
+ * both layers opt-in: request-scoped via TINA4_AUTO_CACHING=true, persistent
457
+ * via TINA4_DB_CACHE=true), so we read the live counters + size + mode from it.
458
+ * Mirrors Python's `Database.cache_stats()`: `{ enabled, mode, hits, misses, size, ttl }`.
459
+ */
460
+ cacheStats(): {
461
+ enabled: boolean;
462
+ mode: "persistent" | "request" | "off";
463
+ hits: number;
464
+ misses: number;
465
+ size: number;
466
+ ttl: number;
467
+ backend?: string;
468
+ };
469
+ /** Flush the query cache and reset counters (mirrors Python `cache_clear()`). */
470
+ cacheClear(): void;
471
+ /**
472
+ * Clear the request-scoped cache at the START of an HTTP request on this
473
+ * connection (no-op in persistent mode). Mirrors Python's
474
+ * `Database.cache_new_request()`.
475
+ */
476
+ cacheNewRequest(): void;
477
+ /** Get the last auto-increment id. */
478
+ getLastId(): string | number;
479
+ /**
480
+ * Create the tina4_sequences table if it doesn't exist.
481
+ * Used by sequenceNext() for race-safe ID generation on
482
+ * SQLite, MySQL, MSSQL, and as a PostgreSQL fallback.
483
+ */
484
+ private ensureSequenceTable;
485
+ /**
486
+ * Best-effort MAX(pk) seed for a new sequence row. 0 if the table is
487
+ * missing/empty. Mirrors Python's `_sequence_seed_value`.
488
+ */
489
+ private sequenceSeedValue;
490
+ /**
491
+ * Atomically increment and return the next value from the sequence table.
492
+ *
493
+ * DB-contract B (no duplicate primary keys under concurrency): the old path
494
+ * was read-increment-read across several `await` points, so two concurrent
495
+ * async callers could read the same `current_value` and return the same id.
496
+ * This now uses a single atomic increment-and-return per engine, pinned to
497
+ * ONE adapter so the two statements (where two are needed) land on the same
498
+ * connection:
499
+ *
500
+ * * SQLite: the SQLiteAdapter does ensure-table + seed + the atomic
501
+ * `UPDATE ... RETURNING current_value` (>= 3.35; else `+1` then `SELECT`)
502
+ * as ONE synchronous burst — no `await` between read and write, so no
503
+ * other async task can interleave (Node analog of Python's _write_lock).
504
+ * * MySQL: `UPDATE ... SET current_value = LAST_INSERT_ID(current_value + 1)`
505
+ * then `SELECT LAST_INSERT_ID()` on the SAME pinned connection
506
+ * (LAST_INSERT_ID is per-connection → race-safe).
507
+ * * MSSQL: `UPDATE ... SET current_value = current_value + 1 OUTPUT
508
+ * inserted.current_value ...` — one atomic statement.
509
+ *
510
+ * Seeding is always a race-safe insert-if-absent (INSERT OR IGNORE /
511
+ * INSERT IGNORE / INSERT ... WHERE NOT EXISTS) seeded from MAX(pk), run
512
+ * BEFORE the increment — never a read-then-insert gap. On error we RAISE
513
+ * (never silently fall back to 1).
514
+ */
515
+ private sequenceNext;
516
+ /**
517
+ * MySQL atomic sequence step. LAST_INSERT_ID(expr) stashes `expr` in this
518
+ * CONNECTION's session var and returns it, so the read-back is per-connection
519
+ * and race-safe. Runs on the pinned adapter.
520
+ */
521
+ private sequenceNextMysql;
522
+ /**
523
+ * MSSQL atomic sequence step. A single `UPDATE ... OUTPUT
524
+ * inserted.current_value` increments and returns the new value in one
525
+ * statement. Runs on the pinned adapter.
526
+ */
527
+ private sequenceNextMssql;
528
+ /**
529
+ * Defensive generic atomic-ish path for any engine not otherwise special-cased
530
+ * (and the SQLite fallback if the adapter lacks the synchronous helper). Seeds
531
+ * if absent, then increments and reads on the pinned connection.
532
+ */
533
+ private sequenceNextGeneric;
534
+ /**
535
+ * Pre-generate the next available primary key ID using engine-aware strategies.
536
+ *
537
+ * - Firebird: auto-creates a generator if missing, then increments via GEN_ID (atomic).
538
+ * - PostgreSQL: tries nextval() first; if sequence missing, auto-creates it
539
+ * seeded from MAX; falls through to sequence table on failure.
540
+ * - SQLite/MySQL/MSSQL: uses tina4_sequences table with atomic UPDATE + SELECT
541
+ * (race-safe, replaces old MAX+1).
542
+ * - Returns 1 if the table is empty or does not exist.
543
+ */
544
+ getNextId(table: string, pkColumn?: string, generatorName?: string): Promise<number>;
545
+ }
546
+ /**
547
+ * Build a connected `DatabaseAdapter` from a connection URL.
548
+ *
549
+ * Used internally by `initDatabase()` and `Database.create()`, and exported so
550
+ * users can construct a NAMED secondary adapter without making it the default:
551
+ *
552
+ * bindDatabase(await createAdapterFromUrl(url, user, pass), "analytics");
553
+ *
554
+ * Unlike `initDatabase()`, this does NOT call `setAdapter()` — it returns a
555
+ * standalone adapter that the caller decides what to do with. For async engines
556
+ * (Postgres/MySQL/MSSQL/Firebird/Mongo) the returned adapter is already
557
+ * connected; SQLite connects lazily.
558
+ */
559
+ export declare function createAdapterFromUrl(url: string, username?: string, password?: string): Promise<DatabaseAdapter>;
560
+ /**
561
+ * Initialize the database from a config object or TINA4_DATABASE_URL env var.
562
+ * Now returns a Database wrapper instance.
563
+ *
564
+ * Priority:
565
+ * 1. config.url (explicit URL)
566
+ * 2. process.env.TINA4_DATABASE_URL
567
+ * 3. config.type + config.path (legacy)
568
+ */
569
+ /**
570
+ * Resolve the connection-pool size from `TINA4_DB_POOL`.
571
+ *
572
+ * Default: 0 (single-connection mode). Any positive integer enables
573
+ * round-robin pooling with that many connections — Database.create() honours
574
+ * this transparently. The env var is the simple deploy-time override; tests
575
+ * and library users can still pass `pool` directly to Database.create().
576
+ */
577
+ export declare function resolveDbPool(): number;
578
+ /**
579
+ * Open a database connection — convention name matching SQLAlchemy
580
+ * `engine.connect()` and the cross-framework Database.get_connection()
581
+ * surface shipped in 3.13.x.
582
+ *
583
+ * Equivalent to `initDatabase({ url })` but with an opinionated, simpler
584
+ * signature: pass a URL string directly, or omit for env-based defaults
585
+ * (falls back to in-memory SQLite when nothing resolves).
586
+ *
587
+ * const db = await Database.getConnection(); // from env
588
+ * const db = await Database.getConnection("sqlite://./app.db"); // explicit URL
589
+ * const db = await Database.getConnection("postgres://localhost/x", { username: "u", password: "p" });
590
+ *
591
+ * Cross-framework parity with Python `Database.get_connection()`, PHP
592
+ * `\Tina4\Database::getConnection()`, and Ruby `Tina4::Database.get_connection`.
593
+ */
594
+ export declare namespace Database {
595
+ function getConnection(url?: string, opts?: {
596
+ username?: string;
597
+ password?: string;
598
+ }): Promise<Database>;
599
+ /**
600
+ * Clear the request-scoped query cache on every live connection.
601
+ *
602
+ * Static convenience mirroring Python's `Database.reset_request_caches()`
603
+ * classmethod. The request dispatcher calls this at the start of each HTTP
604
+ * request so request-scoped caching never serves rows across requests.
605
+ * Persistent-mode connections (TINA4_DB_CACHE=true) are left alone.
606
+ */
607
+ function resetRequestCaches(): void;
608
+ }
609
+ export declare function initDatabase(config?: DatabaseConfig): Promise<Database>;