tina4-nodejs 3.13.136 → 3.13.138
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 +33 -3
- package/COMMERCIAL-LICENSE.md +8 -0
- package/LICENSE +373 -0
- package/NOTICE +11 -0
- package/README.md +24 -2
- package/package.json +44 -6
- package/packages/cli/dist/bin.js +19440 -18017
- package/packages/cli/src/bin.ts +8 -0
- package/packages/cli/src/commands/build.ts +8 -0
- package/packages/cli/src/commands/generate.ts +8 -0
- package/packages/cli/src/commands/init.ts +8 -0
- package/packages/cli/src/commands/lint.ts +8 -0
- package/packages/cli/src/commands/migrate.ts +8 -0
- package/packages/cli/src/commands/migrateCreate.ts +8 -0
- package/packages/cli/src/commands/migrateRollback.ts +8 -0
- package/packages/cli/src/commands/migrateStatus.ts +8 -0
- package/packages/cli/src/commands/queue.ts +8 -0
- package/packages/cli/src/commands/routes.ts +8 -0
- package/packages/cli/src/commands/seed.ts +8 -0
- package/packages/cli/src/commands/serve.ts +11 -0
- package/packages/cli/src/commands/test.ts +8 -0
- package/packages/core/dist/index.js +19432 -18012
- package/packages/core/gallery/auth/src/routes/api/gallery/auth/login/post.ts +8 -0
- package/packages/core/gallery/auth/src/routes/api/gallery/auth/verify/get.ts +8 -0
- package/packages/core/gallery/auth/src/routes/gallery/auth/get.ts +8 -0
- package/packages/core/gallery/database/src/routes/api/gallery/db/notes/get.ts +8 -0
- package/packages/core/gallery/database/src/routes/api/gallery/db/notes/post.ts +8 -0
- package/packages/core/gallery/database/src/routes/api/gallery/db/tables/get.ts +8 -0
- package/packages/core/gallery/error-overlay/src/routes/api/gallery/crash/get.ts +8 -0
- package/packages/core/gallery/orm/src/routes/api/gallery/products/get.ts +8 -0
- package/packages/core/gallery/orm/src/routes/api/gallery/products/post.ts +8 -0
- package/packages/core/gallery/queue/src/lib/queueDb.ts +8 -0
- package/packages/core/gallery/queue/src/routes/api/gallery/queue/consume/post.ts +8 -0
- package/packages/core/gallery/queue/src/routes/api/gallery/queue/fail/post.ts +8 -0
- package/packages/core/gallery/queue/src/routes/api/gallery/queue/produce/post.ts +8 -0
- package/packages/core/gallery/queue/src/routes/api/gallery/queue/retry/post.ts +8 -0
- package/packages/core/gallery/queue/src/routes/api/gallery/queue/status/get.ts +8 -0
- package/packages/core/gallery/queue/src/routes/gallery/queue/get.ts +8 -0
- package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/get.ts +8 -0
- package/packages/core/gallery/rest-api/src/routes/api/gallery/hello/post.ts +8 -0
- package/packages/core/gallery/templates/src/routes/gallery/page/get.ts +8 -0
- package/packages/core/gallery/templates/src/templates/gallery_page.twig +8 -0
- package/packages/core/public/THIRD-PARTY-NOTICES.md +55 -0
- package/packages/core/public/bundled-component-provenance.json +48 -0
- package/packages/core/public/js/tina4js.min.js +13 -3
- package/packages/core/public/licenses/swagger-ui-3.10.0-original-notice.txt +11 -0
- package/packages/core/public/licenses/swagger-ui-5.17.14-NOTICE.txt +2 -0
- package/packages/core/public/licenses/swagger-ui-Apache-2.0.txt +202 -0
- package/packages/core/public/licenses/tina4-css-MIT.txt +21 -0
- package/packages/core/public/licenses/tina4-dev-admin-components.json +240 -0
- package/packages/core/public/licenses/tina4-dev-admin-third-party.txt +919 -0
- package/packages/core/public/licenses/tina4-js-MIT.txt +20 -0
- package/packages/core/src/__feedback/widget.js +8 -0
- package/packages/core/src/_missing.ts +8 -0
- package/packages/core/src/ai.ts +8 -0
- package/packages/core/src/aiClient.ts +22 -7
- package/packages/core/src/api.ts +15 -3
- package/packages/core/src/auth.ts +21 -8
- package/packages/core/src/authGate.ts +8 -0
- package/packages/core/src/background.ts +8 -0
- package/packages/core/src/cache.ts +43 -4
- package/packages/core/src/constants.ts +8 -0
- package/packages/core/src/container.ts +8 -0
- package/packages/core/src/context/chunker.ts +8 -0
- package/packages/core/src/context/index.ts +8 -0
- package/packages/core/src/devAdmin.ts +185 -59
- package/packages/core/src/devMailbox.ts +8 -0
- package/packages/core/src/dispatchPipeline.ts +8 -0
- package/packages/core/src/docs.ts +8 -0
- package/packages/core/src/docsAutoDiscovery.ts +8 -0
- package/packages/core/src/docsParser.ts +8 -0
- package/packages/core/src/docsScanner.ts +44 -0
- package/packages/core/src/docsSignatures.ts +8 -0
- package/packages/core/src/dotenv.ts +8 -0
- package/packages/core/src/env.ts +8 -0
- package/packages/core/src/errorOverlay.ts +8 -0
- package/packages/core/src/events.ts +8 -0
- package/packages/core/src/fakeData.ts +8 -0
- package/packages/core/src/feedback.ts +8 -0
- package/packages/core/src/graphql.ts +16 -5
- package/packages/core/src/health.ts +8 -0
- package/packages/core/src/htmlElement.ts +8 -0
- package/packages/core/src/i18n.ts +8 -0
- package/packages/core/src/index.ts +8 -0
- package/packages/core/src/job.ts +8 -0
- package/packages/core/src/logger.ts +8 -0
- package/packages/core/src/mcp.test.ts +8 -0
- package/packages/core/src/mcp.ts +8 -0
- package/packages/core/src/messenger.ts +143 -86
- package/packages/core/src/metrics.ts +13 -0
- package/packages/core/src/middleware.ts +1178 -1096
- package/packages/core/src/mqtt.ts +8 -0
- package/packages/core/src/mqttMessage.ts +8 -0
- package/packages/core/src/optionalPackage.ts +48 -0
- package/packages/core/src/plan.ts +8 -0
- package/packages/core/src/portTakeover.ts +8 -0
- package/packages/core/src/projectIndex.ts +8 -0
- package/packages/core/src/projectIndexExtractors.ts +8 -0
- package/packages/core/src/projectIndexStorage.ts +8 -0
- package/packages/core/src/push.ts +8 -0
- package/packages/core/src/queue.ts +8 -0
- package/packages/core/src/queueBackends/kafkaBackend.ts +8 -0
- package/packages/core/src/queueBackends/liteBackend.ts +8 -0
- package/packages/core/src/queueBackends/mongoBackend.ts +21 -8
- package/packages/core/src/queueBackends/rabbitmqBackend.ts +8 -0
- package/packages/core/src/rateLimiter.ts +8 -0
- package/packages/core/src/request.ts +59 -10
- package/packages/core/src/response.ts +79 -5
- package/packages/core/src/routeDiscovery.ts +8 -0
- package/packages/core/src/router.ts +8 -0
- package/packages/core/src/server.ts +73 -24
- package/packages/core/src/service.ts +8 -0
- package/packages/core/src/session.ts +8 -0
- package/packages/core/src/sessionHandlers/childError.ts +8 -0
- package/packages/core/src/sessionHandlers/databaseHandler.ts +8 -0
- package/packages/core/src/sessionHandlers/memcachedHandler.ts +8 -0
- package/packages/core/src/sessionHandlers/mongoClient.ts +8 -0
- package/packages/core/src/sessionHandlers/mongoHandler.ts +8 -0
- package/packages/core/src/sessionHandlers/respClient.ts +8 -0
- package/packages/core/src/sessionHandlers/sqlClient.ts +8 -0
- package/packages/core/src/sessionHandlers/syncBridge.ts +8 -0
- package/packages/core/src/sessionHandlers/syncSocket.ts +8 -0
- package/packages/core/src/sessionHandlers/valkeyHandler.ts +8 -0
- package/packages/core/src/sso.ts +8 -0
- package/packages/core/src/static.ts +8 -0
- package/packages/core/src/test.ts +8 -0
- package/packages/core/src/testClient.ts +8 -0
- package/packages/core/src/testing.ts +8 -0
- package/packages/core/src/transport.ts +369 -0
- package/packages/core/src/trustedProxy.ts +8 -0
- package/packages/core/src/types.ts +13 -0
- package/packages/core/src/validator.ts +8 -0
- package/packages/core/src/version.ts +8 -0
- package/packages/core/src/websocket.ts +8 -0
- package/packages/core/src/websocketBackplane.ts +49 -32
- package/packages/core/src/websocketConnection.ts +8 -0
- package/packages/core/src/wsdl.ts +197 -115
- package/packages/core/templates/errors/302.twig +8 -0
- package/packages/core/templates/errors/401.twig +8 -0
- package/packages/core/templates/errors/403.twig +8 -0
- package/packages/core/templates/errors/404.twig +8 -0
- package/packages/core/templates/errors/500.twig +8 -0
- package/packages/core/templates/errors/502.twig +8 -0
- package/packages/core/templates/errors/503.twig +8 -0
- package/packages/core/templates/errors/base.twig +8 -0
- package/packages/frond/dist/index.js +151 -17
- package/packages/frond/src/engine.ts +177 -17
- package/packages/frond/src/index.ts +8 -0
- package/packages/orm/dist/index.js +8135 -6713
- package/packages/orm/src/adapters/firebird.ts +50 -3
- package/packages/orm/src/adapters/mongodb.ts +8 -0
- package/packages/orm/src/adapters/mssql.ts +29 -7
- package/packages/orm/src/adapters/mysql.ts +17 -1
- package/packages/orm/src/adapters/odbc.ts +11 -0
- package/packages/orm/src/adapters/pg-types.d.ts +8 -0
- package/packages/orm/src/adapters/postgres.ts +34 -8
- package/packages/orm/src/adapters/sqlDialect.ts +57 -0
- package/packages/orm/src/adapters/sqlite.ts +26 -0
- package/packages/orm/src/autoCrud.ts +78 -36
- package/packages/orm/src/baseModel.ts +61 -29
- package/packages/orm/src/cachedDatabase.ts +67 -1
- package/packages/orm/src/connectTimeout.ts +8 -0
- package/packages/orm/src/database.ts +649 -401
- package/packages/orm/src/databaseResult.ts +8 -0
- package/packages/orm/src/databaseUrl.ts +8 -0
- package/packages/orm/src/docstore.ts +23 -1
- package/packages/orm/src/fakeData.ts +8 -0
- package/packages/orm/src/graph/adapters/arango.ts +8 -0
- package/packages/orm/src/graph/adapters/bolt.ts +8 -0
- package/packages/orm/src/graph/adapters/ultipa.ts +8 -0
- package/packages/orm/src/graph/connectTimeout.ts +8 -0
- package/packages/orm/src/graph/errors.ts +8 -0
- package/packages/orm/src/graph/graphAdapter.ts +8 -0
- package/packages/orm/src/graph/graphDatabase.ts +8 -0
- package/packages/orm/src/graph/graphUrl.ts +10 -1
- package/packages/orm/src/graph/shapes.ts +8 -0
- package/packages/orm/src/index.ts +147 -139
- package/packages/orm/src/migration.ts +21 -0
- package/packages/orm/src/model.ts +8 -0
- package/packages/orm/src/modelCollection.ts +8 -0
- package/packages/orm/src/point.ts +8 -0
- package/packages/orm/src/query.ts +149 -33
- package/packages/orm/src/queryBuilder.ts +8 -0
- package/packages/orm/src/realtime/index.ts +8 -0
- package/packages/orm/src/realtime/models/attachment.ts +8 -0
- package/packages/orm/src/realtime/models/channel.ts +8 -0
- package/packages/orm/src/realtime/models/channelMember.ts +8 -0
- package/packages/orm/src/realtime/models/message.ts +8 -0
- package/packages/orm/src/realtime/models/workspace.ts +8 -0
- package/packages/orm/src/realtime/realtime.ts +8 -0
- package/packages/orm/src/realtime/storage.ts +43 -12
- package/packages/orm/src/seeder.ts +8 -0
- package/packages/orm/src/seederTable.ts +8 -0
- package/packages/orm/src/seederTypes.ts +8 -0
- package/packages/orm/src/sqlTranslator.ts +185 -8
- package/packages/orm/src/types.ts +15 -0
- package/packages/orm/src/validation.ts +8 -0
- package/packages/swagger/src/generator.ts +8 -0
- package/packages/swagger/src/index.ts +8 -0
- package/packages/swagger/src/ui.ts +8 -0
- package/types/core/src/aiClient.d.ts +2 -2
- package/types/core/src/background.d.ts +0 -16
- package/types/core/src/cache.d.ts +6 -0
- package/types/core/src/devAdmin.d.ts +18 -12
- package/types/core/src/docs.d.ts +0 -17
- package/types/core/src/errorOverlay.d.ts +0 -24
- package/types/core/src/events.d.ts +0 -12
- package/types/core/src/feedback.d.ts +0 -23
- package/types/core/src/graphql.d.ts +0 -21
- package/types/core/src/htmlElement.d.ts +0 -15
- package/types/core/src/messenger.d.ts +12 -1
- package/types/core/src/metrics.d.ts +2 -0
- package/types/core/src/middleware.d.ts +45 -1
- package/types/core/src/mqtt.d.ts +0 -33
- package/types/core/src/optionalPackage.d.ts +25 -0
- package/types/core/src/plan.d.ts +0 -10
- package/types/core/src/projectIndex.d.ts +0 -10
- package/types/core/src/queueBackends/mongoBackend.d.ts +2 -0
- package/types/core/src/request.d.ts +12 -0
- package/types/core/src/response.d.ts +13 -0
- package/types/core/src/server.d.ts +19 -0
- package/types/core/src/sessionHandlers/childError.d.ts +0 -15
- package/types/core/src/transport.d.ts +28 -0
- package/types/core/src/trustedProxy.d.ts +0 -16
- package/types/core/src/types.d.ts +5 -0
- package/types/core/src/websocketBackplane.d.ts +1 -0
- package/types/core/src/wsdl.d.ts +8 -22
- package/types/frond/src/engine.d.ts +7 -1
- package/types/orm/src/adapters/firebird.d.ts +12 -0
- package/types/orm/src/adapters/mssql.d.ts +11 -0
- package/types/orm/src/adapters/mysql.d.ts +2 -0
- package/types/orm/src/adapters/postgres.d.ts +11 -0
- package/types/orm/src/adapters/sqlDialect.d.ts +25 -0
- package/types/orm/src/adapters/sqlite.d.ts +7 -0
- package/types/orm/src/baseModel.d.ts +5 -0
- package/types/orm/src/cachedDatabase.d.ts +27 -0
- package/types/orm/src/database.d.ts +46 -7
- package/types/orm/src/graph/graphUrl.d.ts +0 -8
- package/types/orm/src/index.d.ts +1 -1
- package/types/orm/src/query.d.ts +48 -2
- package/types/orm/src/realtime/storage.d.ts +2 -1
- package/types/orm/src/sqlTranslator.d.ts +70 -18
- package/types/orm/src/types.d.ts +6 -0
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Tina4 Firebird Adapter — uses the `node-firebird` package (optional peer dependency).
|
|
3
11
|
*
|
|
4
12
|
* Install: npm install node-firebird
|
|
5
13
|
* URL format: firebird://user:pass@host:port/path/to/database.fdb
|
|
6
14
|
*/
|
|
7
|
-
import { firebirdDialect, buildInsert, buildSetClause, buildWhereClause } from "./sqlDialect.js";
|
|
15
|
+
import { firebirdDialect, buildInsert, buildSetClause, buildWhereClause, quoteIdentifierWith } from "./sqlDialect.js";
|
|
8
16
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
9
17
|
import { SQLTranslator } from "../sqlTranslator.js";
|
|
10
18
|
import { connectTimeoutMillis, withConnectTimeout } from "../connectTimeout.js";
|
|
@@ -235,6 +243,15 @@ export class FirebirdAdapter implements DatabaseAdapter {
|
|
|
235
243
|
return 'firebird';
|
|
236
244
|
}
|
|
237
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Quote a name the ORM emits the way Firebird stores it: UPPER CASE (see
|
|
248
|
+
* fbQuote). An already-quoted name passes through - the escape hatch for a
|
|
249
|
+
* genuinely case-sensitive table. Python master: FirebirdAdapter.quote_identifier.
|
|
250
|
+
*/
|
|
251
|
+
quoteIdentifier(name: string): string {
|
|
252
|
+
return quoteIdentifierWith(name, '"', '"', true);
|
|
253
|
+
}
|
|
254
|
+
|
|
238
255
|
/** ADR-0044: readable/writable native boolean. */
|
|
239
256
|
autocommit = true;
|
|
240
257
|
|
|
@@ -523,17 +540,39 @@ export class FirebirdAdapter implements DatabaseAdapter {
|
|
|
523
540
|
|
|
524
541
|
async executeAsync(sql: string, params?: unknown[]): Promise<unknown> {
|
|
525
542
|
this.ensureConnected();
|
|
543
|
+
if (/\bRETURNING\b/i.test(sql)) {
|
|
544
|
+
// A RETURNING statement hands back rows (the ORM reads the generated key
|
|
545
|
+
// from them, as it does on PostgreSQL). node-firebird answers a single
|
|
546
|
+
// returned row as a bare object, so normalise it to a row list.
|
|
547
|
+
const returned = await this.queryPromise(sql, params);
|
|
548
|
+
const rows = Array.isArray(returned) ? returned : returned ? [returned] : [];
|
|
549
|
+
return { rows: rows.map((row) => foldColumnNames(row)) };
|
|
550
|
+
}
|
|
526
551
|
await this.executePromise(sql, params);
|
|
527
552
|
return undefined;
|
|
528
553
|
}
|
|
529
554
|
|
|
555
|
+
/**
|
|
556
|
+
* Run a row-producing statement ONCE and return its rows, for
|
|
557
|
+
* Database.execute(). node-firebird's execute() returns no rows, so a SELECT
|
|
558
|
+
* or an `INSERT ... RETURNING` through execute() used to lose them.
|
|
559
|
+
*/
|
|
560
|
+
async executeRowsAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> {
|
|
561
|
+
this.ensureConnected();
|
|
562
|
+
return this.queryAsync<T>(sql, params);
|
|
563
|
+
}
|
|
564
|
+
|
|
530
565
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[] {
|
|
531
566
|
throw new Error("Use queryAsync() for Firebird.");
|
|
532
567
|
}
|
|
533
568
|
|
|
534
569
|
async queryAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> {
|
|
535
570
|
this.ensureConnected();
|
|
536
|
-
const
|
|
571
|
+
const result = await this.queryPromise(sql, params);
|
|
572
|
+
// A singleton statement (`INSERT ... VALUES ... RETURNING id`) comes back from
|
|
573
|
+
// node-firebird as ONE row object, not an array of rows (MEASURED), so it is
|
|
574
|
+
// wrapped here rather than iterated as if it were a list.
|
|
575
|
+
const rows: unknown[] = Array.isArray(result) ? result : (result && typeof result === "object" ? [result] : []);
|
|
537
576
|
const decoded: T[] = [];
|
|
538
577
|
for (const row of rows as T[]) {
|
|
539
578
|
decoded.push(await this.decodeBlobs(foldColumnNames(row)));
|
|
@@ -565,6 +604,9 @@ export class FirebirdAdapter implements DatabaseAdapter {
|
|
|
565
604
|
}
|
|
566
605
|
|
|
567
606
|
async fetchAsync<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number): Promise<T[]> {
|
|
607
|
+
// A write that returns rows (INSERT/UPDATE/DELETE ... RETURNING) runs exactly
|
|
608
|
+
// as written: a LIMIT/OFFSET appended to DML is a syntax error (#133 contract).
|
|
609
|
+
if (SQLTranslator.isWriteStatement(sql)) return this.queryAsync<T>(sql, params);
|
|
568
610
|
let effectiveSql = sql;
|
|
569
611
|
if (limit !== undefined) {
|
|
570
612
|
const offset = skip ?? 0;
|
|
@@ -581,7 +623,12 @@ export class FirebirdAdapter implements DatabaseAdapter {
|
|
|
581
623
|
}
|
|
582
624
|
|
|
583
625
|
async fetchOneAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T | null> {
|
|
584
|
-
|
|
626
|
+
// A write runs unpaginated: Firebird rejects `INSERT ... RETURNING id ROWS 1
|
|
627
|
+
// TO 1` (ROWS is not valid on INSERT, and on UPDATE/DELETE it must precede
|
|
628
|
+
// RETURNING), so fetchOne() of a RETURNING write failed (#133 contract).
|
|
629
|
+
const rows = SQLTranslator.isWriteStatement(sql)
|
|
630
|
+
? await this.queryAsync<T>(sql, params)
|
|
631
|
+
: await this.fetchAsync<T>(sql, params, 1, 0);
|
|
585
632
|
return rows[0] ?? null;
|
|
586
633
|
}
|
|
587
634
|
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Tina4 MongoDB Adapter — uses the `mongodb` package (optional peer dependency).
|
|
3
11
|
*
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Tina4 MSSQL Adapter — uses the `tedious` package (optional peer dependency).
|
|
3
11
|
*
|
|
@@ -231,12 +239,23 @@ export class MssqlAdapter implements DatabaseAdapter {
|
|
|
231
239
|
* `startAt` lets a caller that has already consumed N placeholders (an UPDATE
|
|
232
240
|
* whose SET values are @p0..@p{N-1}) continue the numbering into a raw WHERE
|
|
233
241
|
* fragment instead of restarting at @p0.
|
|
242
|
+
*
|
|
243
|
+
* Only REAL placeholders are rewritten: a `?` inside a string literal, a
|
|
244
|
+
* quoted identifier or a comment is left alone (tina4-python#138).
|
|
234
245
|
*/
|
|
235
246
|
private convertPlaceholders(sql: string, startAt = 0): string {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
247
|
+
return SQLTranslator.replacePlaceholders(sql, (index) => `@p${startAt + index}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* The SQL the driver receives. With NO parameters it is sent EXACTLY as
|
|
252
|
+
* written (the cross-framework contract): there is nothing to bind.
|
|
253
|
+
*
|
|
254
|
+
* tina4: with parameters every `?` outside a literal, quoted identifier or
|
|
255
|
+
* comment is a placeholder.
|
|
256
|
+
*/
|
|
257
|
+
private bindable(sql: string, params?: unknown[]): string {
|
|
258
|
+
return params && params.length > 0 ? this.convertPlaceholders(sql) : sql;
|
|
240
259
|
}
|
|
241
260
|
|
|
242
261
|
execute(sql: string, params?: unknown[]): unknown {
|
|
@@ -275,7 +294,7 @@ export class MssqlAdapter implements DatabaseAdapter {
|
|
|
275
294
|
async executeAsync(sql: string, params?: unknown[]): Promise<unknown> {
|
|
276
295
|
this.ensureConnected();
|
|
277
296
|
const translated = this.translateSql(sql);
|
|
278
|
-
const converted = this.
|
|
297
|
+
const converted = this.bindable(translated, params);
|
|
279
298
|
return this.execSqlPromise(converted, params);
|
|
280
299
|
}
|
|
281
300
|
|
|
@@ -286,7 +305,7 @@ export class MssqlAdapter implements DatabaseAdapter {
|
|
|
286
305
|
async queryAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> {
|
|
287
306
|
this.ensureConnected();
|
|
288
307
|
const translated = this.translateSql(sql);
|
|
289
|
-
const converted = this.
|
|
308
|
+
const converted = this.bindable(translated, params);
|
|
290
309
|
const result = await this.execSqlPromise(converted, params);
|
|
291
310
|
return result.rows as T[];
|
|
292
311
|
}
|
|
@@ -296,6 +315,9 @@ export class MssqlAdapter implements DatabaseAdapter {
|
|
|
296
315
|
}
|
|
297
316
|
|
|
298
317
|
async fetchAsync<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number): Promise<T[]> {
|
|
318
|
+
// A write that returns rows (INSERT/UPDATE/DELETE ... RETURNING) runs exactly
|
|
319
|
+
// as written: a LIMIT/OFFSET appended to DML is a syntax error (#133 contract).
|
|
320
|
+
if (SQLTranslator.isWriteStatement(sql)) return this.queryAsync<T>(sql, params);
|
|
299
321
|
let effectiveSql = sql;
|
|
300
322
|
if (limit !== undefined && limit > 0) {
|
|
301
323
|
// MSSQL-PAGINATION-DIVERGE: ONE pagination strategy across all four
|
|
@@ -430,7 +452,7 @@ export class MssqlAdapter implements DatabaseAdapter {
|
|
|
430
452
|
// `WHERE [0] = @p0 AND [1] = @p1 ...` — db.truncate() was broken outright.
|
|
431
453
|
if (typeof filter === "string") {
|
|
432
454
|
const sql = filter
|
|
433
|
-
? `DELETE FROM [${table}] WHERE ${this.
|
|
455
|
+
? `DELETE FROM [${table}] WHERE ${this.bindable(filter, params)}`
|
|
434
456
|
: `DELETE FROM [${table}]`;
|
|
435
457
|
try {
|
|
436
458
|
const result = await this.execSqlPromise(sql, params ?? []);
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Tina4 MySQL Adapter — uses the `mysql2` package (optional peer dependency).
|
|
3
11
|
*
|
|
4
12
|
* Install: npm install mysql2
|
|
5
13
|
* URL format: mysql://user:pass@host:port/database
|
|
6
14
|
*/
|
|
7
|
-
import { MYSQL_DIALECT, buildInsert, buildSetClause, buildWhereClause } from "./sqlDialect.js";
|
|
15
|
+
import { MYSQL_DIALECT, buildInsert, buildSetClause, buildWhereClause, quoteIdentifierWith } from "./sqlDialect.js";
|
|
8
16
|
import type { DatabaseAdapter, DatabaseResult, ColumnInfo, FieldDefinition } from "../types.js";
|
|
9
17
|
import { SQLTranslator } from "../sqlTranslator.js";
|
|
10
18
|
import { connectTarget, connectTimeoutMillis, driverConnectTimeoutMillis, withConnectTimeout } from "../connectTimeout.js";
|
|
@@ -62,6 +70,11 @@ export class MysqlAdapter implements DatabaseAdapter {
|
|
|
62
70
|
return 'mysql';
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
/** MySQL identifiers are backtick-quoted; a double-quoted token is a string. */
|
|
74
|
+
quoteIdentifier(name: string): string {
|
|
75
|
+
return quoteIdentifierWith(name, "`", "`");
|
|
76
|
+
}
|
|
77
|
+
|
|
65
78
|
/** ADR-0044: readable/writable native boolean. */
|
|
66
79
|
autocommit = true;
|
|
67
80
|
|
|
@@ -218,6 +231,9 @@ export class MysqlAdapter implements DatabaseAdapter {
|
|
|
218
231
|
}
|
|
219
232
|
|
|
220
233
|
async fetchAsync<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number): Promise<T[]> {
|
|
234
|
+
// A write that returns rows (INSERT/UPDATE/DELETE ... RETURNING) runs exactly
|
|
235
|
+
// as written: a LIMIT/OFFSET appended to DML is a syntax error (#133 contract).
|
|
236
|
+
if (SQLTranslator.isWriteStatement(sql)) return this.queryAsync<T>(sql, params);
|
|
221
237
|
let effectiveSql = sql;
|
|
222
238
|
if (limit !== undefined) {
|
|
223
239
|
effectiveSql += ` LIMIT ${limit}`;
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Tina4 ODBC Adapter — uses the `odbc` package (optional peer dependency).
|
|
3
11
|
*
|
|
@@ -270,6 +278,9 @@ export class OdbcAdapter implements DatabaseAdapter {
|
|
|
270
278
|
limit?: number,
|
|
271
279
|
skip?: number,
|
|
272
280
|
): Promise<T[]> {
|
|
281
|
+
// A write that returns rows (INSERT/UPDATE/DELETE ... RETURNING) runs exactly
|
|
282
|
+
// as written: a LIMIT/OFFSET appended to DML is a syntax error (#133 contract).
|
|
283
|
+
if (SQLTranslator.isWriteStatement(sql)) return this.queryAsync<T>(sql, params);
|
|
273
284
|
// See SQLTranslator.appendLimit: the old inline check was a substring search,
|
|
274
285
|
// so a LIMIT in a string literal or a trailing comment silently dropped the
|
|
275
286
|
// row cap and returned every row.
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Minimal ambient type surface for the optional `pg` (node-postgres) peer
|
|
3
11
|
* dependency. `@types/pg` is intentionally NOT a dependency (pg is an optional
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* Tina4 PostgreSQL Adapter — uses the `pg` package (optional peer dependency).
|
|
3
11
|
*
|
|
@@ -159,12 +167,27 @@ export class PostgresAdapter implements DatabaseAdapter {
|
|
|
159
167
|
// `startAt` lets a caller that has already consumed N placeholders (an UPDATE
|
|
160
168
|
// whose SET values are $1..$N) continue the numbering into a raw WHERE
|
|
161
169
|
// fragment instead of restarting at $1.
|
|
170
|
+
//
|
|
171
|
+
// Only REAL placeholders are rewritten: a `?` inside a string literal (incl.
|
|
172
|
+
// E'...' and $$...$$), a quoted identifier or a comment is left alone. A plain
|
|
173
|
+
// text replace bound `SELECT 'why?' AS v, ? AS n`'s parameter INTO the
|
|
174
|
+
// literal and left the real marker unbound (tina4-python#138).
|
|
162
175
|
private convertPlaceholders(sql: string, startAt = 1): string {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
176
|
+
return SQLTranslator.replacePlaceholders(sql, (index) => `$${startAt + index}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The SQL the driver receives. With NO parameters it is sent EXACTLY as
|
|
181
|
+
* written (the cross-framework contract): there is nothing to bind, and the
|
|
182
|
+
* rewrite would turn PostgreSQL's jsonb operators `?`, `?|` and `?&` into
|
|
183
|
+
* unbound markers.
|
|
184
|
+
*
|
|
185
|
+
* tina4: with parameters every `?` outside a literal, quoted identifier or
|
|
186
|
+
* comment is a placeholder - use jsonb_exists(), jsonb_exists_any() or
|
|
187
|
+
* jsonb_exists_all() instead of the `?`, `?|` or `?&` operators.
|
|
188
|
+
*/
|
|
189
|
+
private bindable(sql: string, params?: unknown[]): string {
|
|
190
|
+
return params && params.length > 0 ? this.convertPlaceholders(sql) : sql;
|
|
168
191
|
}
|
|
169
192
|
|
|
170
193
|
/**
|
|
@@ -232,7 +255,7 @@ export class PostgresAdapter implements DatabaseAdapter {
|
|
|
232
255
|
/** Async execute for real usage. */
|
|
233
256
|
async executeAsync(sql: string, params?: unknown[]): Promise<unknown> {
|
|
234
257
|
this.ensureConnected();
|
|
235
|
-
const convertedSql = this.
|
|
258
|
+
const convertedSql = this.bindable(sql, params);
|
|
236
259
|
const result = await this.client!.query(convertedSql, params);
|
|
237
260
|
if (result.rows?.[0]?.id !== undefined) {
|
|
238
261
|
this._lastInsertId = this.normalizeId(result.rows[0].id);
|
|
@@ -247,7 +270,7 @@ export class PostgresAdapter implements DatabaseAdapter {
|
|
|
247
270
|
/** Async query for real usage. */
|
|
248
271
|
async queryAsync<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]> {
|
|
249
272
|
this.ensureConnected();
|
|
250
|
-
const convertedSql = this.
|
|
273
|
+
const convertedSql = this.bindable(sql, params);
|
|
251
274
|
const result = await this.client!.query(convertedSql, params);
|
|
252
275
|
return (result.rows as T[]).map(row => this.decodeBlobs(row));
|
|
253
276
|
}
|
|
@@ -257,6 +280,9 @@ export class PostgresAdapter implements DatabaseAdapter {
|
|
|
257
280
|
}
|
|
258
281
|
|
|
259
282
|
async fetchAsync<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number): Promise<T[]> {
|
|
283
|
+
// A write that returns rows (INSERT/UPDATE/DELETE ... RETURNING) runs exactly
|
|
284
|
+
// as written: a LIMIT/OFFSET appended to DML is a syntax error (#133 contract).
|
|
285
|
+
if (SQLTranslator.isWriteStatement(sql)) return this.queryAsync<T>(sql, params);
|
|
260
286
|
let effectiveSql = sql;
|
|
261
287
|
if (limit !== undefined) {
|
|
262
288
|
effectiveSql += ` LIMIT ${limit}`;
|
|
@@ -378,7 +404,7 @@ export class PostgresAdapter implements DatabaseAdapter {
|
|
|
378
404
|
// was broken outright on PostgreSQL.
|
|
379
405
|
if (typeof filter === "string") {
|
|
380
406
|
const sql = filter
|
|
381
|
-
? `DELETE FROM "${table}" WHERE ${this.
|
|
407
|
+
? `DELETE FROM "${table}" WHERE ${this.bindable(filter, params)}`
|
|
382
408
|
: `DELETE FROM "${table}"`;
|
|
383
409
|
try {
|
|
384
410
|
const result = await this.client!.query(sql, params ?? []);
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* One CRUD SQL builder for every engine, instead of one per adapter.
|
|
3
11
|
*
|
|
@@ -32,6 +40,35 @@ export interface Dialect {
|
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
const doubleQuote = (name: string): string => `"${name}"`;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Quote a table/column name the ORM emits, in one engine's identifier quotes.
|
|
46
|
+
*
|
|
47
|
+
* Port of the Python master's `quote_identifier` (adapter.py; Firebird's
|
|
48
|
+
* override upper-cases): idempotent (an already-quoted name is returned
|
|
49
|
+
* unchanged), dot-aware (`schema.table` quotes each part), and a non-identifier
|
|
50
|
+
* (`*`, `COUNT(*)`, an expression) is passed through untouched. An embedded
|
|
51
|
+
* closing quote is doubled.
|
|
52
|
+
*
|
|
53
|
+
* `upperCase` is Firebird's rule: an unquoted identifier is stored UPPER CASE,
|
|
54
|
+
* so a lower-case name must be quoted upper-case to match it.
|
|
55
|
+
*/
|
|
56
|
+
export function quoteIdentifierWith(name: string, open: string, close: string, upperCase = false): string {
|
|
57
|
+
if (!name) return name;
|
|
58
|
+
const trimmed = name.trim();
|
|
59
|
+
if (trimmed.length >= 2 && trimmed.startsWith(open) && trimmed.endsWith(close)) return trimmed;
|
|
60
|
+
if (trimmed.includes(".")) {
|
|
61
|
+
return trimmed.split(".").map((part) => quoteIdentifierWith(part, open, close, upperCase)).join(".");
|
|
62
|
+
}
|
|
63
|
+
if (!/^[\p{L}\p{N}_$]+$/u.test(trimmed)) return trimmed;
|
|
64
|
+
const body = upperCase ? trimmed.toUpperCase() : trimmed;
|
|
65
|
+
return `${open}${body.split(close).join(close + close)}${close}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The ANSI default: `"name"` (SQLite, PostgreSQL, MSSQL, ODBC). */
|
|
69
|
+
export function quoteIdentifierAnsi(name: string): string {
|
|
70
|
+
return quoteIdentifierWith(name, '"', '"');
|
|
71
|
+
}
|
|
35
72
|
const questionMark = (): string => "?";
|
|
36
73
|
|
|
37
74
|
/** SQLite, and ODBC which follows the SQL standard spelling. */
|
|
@@ -64,6 +101,23 @@ export function firebirdDialect(fbQuote: (name: string) => string): Dialect {
|
|
|
64
101
|
return { quote: fbQuote, marker: questionMark };
|
|
65
102
|
}
|
|
66
103
|
|
|
104
|
+
const PLAIN_COLUMN_NAME = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Refuse a data or filter-map key that is not a plain identifier.
|
|
108
|
+
*
|
|
109
|
+
* tina4: ADR-0069 (G3) - the write helpers emit these keys as column names, and
|
|
110
|
+
* the dialect quoters do not escape, so a key must be a plain identifier
|
|
111
|
+
* (letters, digits, underscore, dollar; not starting with a digit) before any
|
|
112
|
+
* SQL is built. Valid names are emitted exactly as before. Table names are
|
|
113
|
+
* developer code and are not checked here.
|
|
114
|
+
*/
|
|
115
|
+
export function assertColumnNames(keys: Iterable<string>): void {
|
|
116
|
+
for (const key of keys) {
|
|
117
|
+
if (!PLAIN_COLUMN_NAME.test(key)) throw new Error(`Invalid column name '${key}'`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
67
121
|
/**
|
|
68
122
|
* `INSERT INTO <table> (<cols>) VALUES (<markers>)`.
|
|
69
123
|
*
|
|
@@ -81,6 +135,7 @@ export function buildInsert(
|
|
|
81
135
|
suffix = "",
|
|
82
136
|
startAt = 1,
|
|
83
137
|
): string {
|
|
138
|
+
assertColumnNames(keys);
|
|
84
139
|
const columns = keys.map((k) => dialect.quote(k)).join(", ");
|
|
85
140
|
const placeholders = keys.map((_, i) => dialect.marker(startAt + i)).join(", ");
|
|
86
141
|
return `INSERT INTO ${dialect.quote(table)} (${columns}) VALUES (${placeholders})${suffix}`;
|
|
@@ -98,6 +153,7 @@ export function buildSetClause(
|
|
|
98
153
|
keys: string[],
|
|
99
154
|
startAt = 1,
|
|
100
155
|
): string {
|
|
156
|
+
assertColumnNames(keys);
|
|
101
157
|
return keys
|
|
102
158
|
.map((k, i) => `${dialect.quote(k)} = ${dialect.marker(startAt + i)}`)
|
|
103
159
|
.join(", ");
|
|
@@ -114,6 +170,7 @@ export function buildWhereClause(
|
|
|
114
170
|
keys: string[],
|
|
115
171
|
startAt = 1,
|
|
116
172
|
): string {
|
|
173
|
+
assertColumnNames(keys);
|
|
117
174
|
return keys
|
|
118
175
|
.map((k, i) => `${dialect.quote(k)} = ${dialect.marker(startAt + i)}`)
|
|
119
176
|
.join(" AND ");
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2026 Code Infinity
|
|
3
|
+
SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import { DatabaseSync } from "node:sqlite";
|
|
2
10
|
import { ANSI_DIALECT, buildInsert, buildSetClause, buildWhereClause } from "./sqlDialect.js";
|
|
3
11
|
import { mkdirSync } from "node:fs";
|
|
@@ -219,7 +227,25 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
219
227
|
return (params ? stmt.all(...toSqlParams(params)) : stmt.all()) as T[];
|
|
220
228
|
}
|
|
221
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Run a row-producing statement ONCE and return its rows, for
|
|
232
|
+
* Database.execute(). execute()'s run() discards rows, so a SELECT or an
|
|
233
|
+
* `INSERT ... RETURNING` through execute() used to lose them. A write still
|
|
234
|
+
* records its last insert id, exactly as execute() does.
|
|
235
|
+
*/
|
|
236
|
+
executeRows<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[] {
|
|
237
|
+
const rows = this.query<T>(sql, params);
|
|
238
|
+
if (SQLTranslator.isWriteStatement(sql)) {
|
|
239
|
+
const last = this.db.prepare("SELECT last_insert_rowid() AS id").get() as { id?: number | bigint } | undefined;
|
|
240
|
+
if (last?.id !== undefined) this._lastInsertId = last.id;
|
|
241
|
+
}
|
|
242
|
+
return rows;
|
|
243
|
+
}
|
|
244
|
+
|
|
222
245
|
fetch<T = Record<string, unknown>>(sql: string, params?: unknown[], limit?: number, skip?: number): T[] {
|
|
246
|
+
// A write that returns rows (INSERT/UPDATE/DELETE ... RETURNING) runs exactly
|
|
247
|
+
// as written: a LIMIT/OFFSET appended to DML is a syntax error (#133 contract).
|
|
248
|
+
if (SQLTranslator.isWriteStatement(sql)) return this.query<T>(sql, params);
|
|
223
249
|
// SQLTranslator.appendLimit owns BOTH halves of this decision: it scrubs
|
|
224
250
|
// literals and comments before asking "does the caller already have a
|
|
225
251
|
// LIMIT?", and it appends on a new line so the clause can never land inside
|