turbine-orm 0.21.0 → 0.23.0
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/README.md +19 -2
- package/dist/cjs/client.js +11 -2
- package/dist/cjs/generate.js +4 -0
- package/dist/cjs/introspect.js +6 -0
- package/dist/cjs/powdb.js +865 -0
- package/dist/cjs/powql.js +1262 -0
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.js +11 -2
- package/dist/generate.js +4 -0
- package/dist/introspect.js +6 -0
- package/dist/powdb.d.ts +341 -0
- package/dist/powdb.js +816 -0
- package/dist/powql.d.ts +205 -0
- package/dist/powql.js +1226 -0
- package/dist/query/builder.d.ts +8 -0
- package/dist/schema.d.ts +10 -0
- package/package.json +16 -1
package/README.md
CHANGED
|
@@ -771,9 +771,9 @@ When Turbine receives an external pool, `db.disconnect()` is a no-op: the caller
|
|
|
771
771
|
|
|
772
772
|
## Database engines
|
|
773
773
|
|
|
774
|
-
Turbine is **Postgres-first** — `import { TurbineClient } from 'turbine-orm'` targets PostgreSQL, and the safety bundle above is built around it. When you need another database, the same typed API runs on **SQLite**, **MySQL 8**, and **SQL Server** through subpath exports. Multi-engine is *additive*, not a pivot: pick the engine that fits, keep the same `findMany` / `with` / `where` API.
|
|
774
|
+
Turbine is **Postgres-first** — `import { TurbineClient } from 'turbine-orm'` targets PostgreSQL, and the safety bundle above is built around it. When you need another database, the same typed API runs on **SQLite**, **MySQL 8**, and **SQL Server** through subpath exports — plus **PowDB**, a single-node embedded database with its own query language (PowQL). Multi-engine is *additive*, not a pivot: pick the engine that fits, keep the same `findMany` / `with` / `where` API.
|
|
775
775
|
|
|
776
|
-
The root install stays one dependency (`pg`). Each engine's driver is its own concern: SQLite needs nothing (Node's built-in `node:sqlite`), MySQL
|
|
776
|
+
Two engines run **in-process** (no server): **SQLite** (always — there is no SQLite wire protocol) and **PowDB**, which uniquely runs *both* in-process (embedded) *and* over a network client against the same data. The root install stays one dependency (`pg`). Each engine's driver is its own concern: SQLite needs nothing (Node's built-in `node:sqlite`), while MySQL, SQL Server, and PowDB use **optional peer dependencies** you install only if you use them.
|
|
777
777
|
|
|
778
778
|
```bash
|
|
779
779
|
# SQLite — zero extra deps (Node >= 22.5, built-in node:sqlite)
|
|
@@ -784,6 +784,10 @@ npm install turbine-orm mysql2
|
|
|
784
784
|
|
|
785
785
|
# SQL Server 2016+ — optional peer
|
|
786
786
|
npm install turbine-orm mssql
|
|
787
|
+
|
|
788
|
+
# PowDB — optional peer; embedded (in-process) or networked transport
|
|
789
|
+
npm install turbine-orm @zvndev/powdb-embedded # in-process
|
|
790
|
+
npm install turbine-orm @zvndev/powdb-client # networked
|
|
787
791
|
```
|
|
788
792
|
|
|
789
793
|
Each engine ships a factory that returns the same `TurbineClient`:
|
|
@@ -813,6 +817,17 @@ import { SCHEMA } from './generated/turbine/metadata.js';
|
|
|
813
817
|
const db = await turbineMssql('mssql://sa:Passw0rd!@localhost:1433/app', SCHEMA);
|
|
814
818
|
```
|
|
815
819
|
|
|
820
|
+
```ts
|
|
821
|
+
// PowDB — async; embedded (in-process) or networked. Schema is code-defined (no introspection).
|
|
822
|
+
import { turbinePowDB } from 'turbine-orm/powdb';
|
|
823
|
+
import { schema } from './schema.js'; // defineSchema({...})
|
|
824
|
+
|
|
825
|
+
// Embedded, in-process — syncMode 'normal' makes writes beat SQLite:
|
|
826
|
+
const db = await turbinePowDB({ embedded: './data', syncMode: 'normal' }, schema);
|
|
827
|
+
// …or networked against a running powdb-server:
|
|
828
|
+
// const db = await turbinePowDB('powdb://127.0.0.1:7070', schema);
|
|
829
|
+
```
|
|
830
|
+
|
|
816
831
|
### Capability matrix
|
|
817
832
|
|
|
818
833
|
Everything is honest about what ports and what doesn't. Features marked **PG-only** throw a typed `UnsupportedFeatureError` (`TURBINE_E017`) on other engines rather than silently degrading.
|
|
@@ -831,6 +846,8 @@ Everything is honest about what ports and what doesn't. Features marked **PG-onl
|
|
|
831
846
|
|
|
832
847
|
**Engine notes:** SQLite uses `RETURNING` (≥ 3.35) just like Postgres. MySQL has no `RETURNING`, so writes re-`SELECT` the affected row and **`createMany` returns `[]`** (the rows ARE inserted — re-query if you need them). SQL Server returns rows via `OUTPUT`/`MERGE`; `DISTINCT ON` is Postgres-only. Only Postgres streams via a true cursor (constant memory); the other engines' `findManyStream` materializes the result then yields it in batches. Optimistic locking throws `OptimisticLockError` on all engines (on MySQL the conflict is detected from the version-checked UPDATE's affected-row count). The `turbine` CLI (`generate`, `migrate`) is currently PostgreSQL-only — point the engine factories at a hand-written or programmatically introspected `SCHEMA`.
|
|
833
848
|
|
|
849
|
+
**PowDB** speaks its own non-SQL query language (PowQL), so it sits outside the SQL matrix above. It uses `reselect` writes with client-assigned UUID PKs, N+1 relation loaders (no `json_agg`), single-writer transactions (no nesting), and `defineSchema` (no introspection). Embedded `syncMode: 'normal'` writes beat SQLite; the networked transport runs the same data over a socket. many-to-many, nested writes, composite keys, cursor streaming, and the Postgres-only trio throw `UnsupportedFeatureError`. Full details: **[turbineorm.dev/engines#powdb](https://turbineorm.dev/engines#powdb)**.
|
|
850
|
+
|
|
834
851
|
Full setup, signatures, and the complete support matrix: **[turbineorm.dev/engines](https://turbineorm.dev/engines)**.
|
|
835
852
|
|
|
836
853
|
## Configuration
|
package/dist/cjs/client.js
CHANGED
|
@@ -120,7 +120,9 @@ class TransactionClient {
|
|
|
120
120
|
// We use a proxy pool that routes queries through the transaction client
|
|
121
121
|
const txPool = this.createTxPool();
|
|
122
122
|
const txOpts = { ...this.queryOptions, _txScoped: true };
|
|
123
|
-
qi =
|
|
123
|
+
qi = txOpts.queryInterfaceFactory
|
|
124
|
+
? txOpts.queryInterfaceFactory(txPool, name, this.schema, this.middlewares, txOpts)
|
|
125
|
+
: new index_js_1.QueryInterface(txPool, name, this.schema, this.middlewares, txOpts);
|
|
124
126
|
this.tableCache.set(name, qi);
|
|
125
127
|
}
|
|
126
128
|
return qi;
|
|
@@ -262,6 +264,11 @@ class TurbineClient {
|
|
|
262
264
|
preparedStatements: envDisablePrepared ? false : (config.preparedStatements ?? !config.pool),
|
|
263
265
|
sqlCache: config.sqlCache ?? true,
|
|
264
266
|
dialect: config.dialect,
|
|
267
|
+
// Non-SQL backends (PowDB) inject a factory that builds their own query
|
|
268
|
+
// interface (PowqlInterface) instead of the SQL QueryInterface. SQL engines
|
|
269
|
+
// never set this, so `table()` keeps constructing `new QueryInterface`.
|
|
270
|
+
queryInterfaceFactory: config
|
|
271
|
+
.queryInterfaceFactory,
|
|
265
272
|
_onQuery: (event) => {
|
|
266
273
|
if (this.queryListeners.size === 0)
|
|
267
274
|
return;
|
|
@@ -413,7 +420,9 @@ class TurbineClient {
|
|
|
413
420
|
table(name) {
|
|
414
421
|
let qi = this.tableCache.get(name);
|
|
415
422
|
if (!qi) {
|
|
416
|
-
qi =
|
|
423
|
+
qi = this.queryOptions?.queryInterfaceFactory
|
|
424
|
+
? this.queryOptions.queryInterfaceFactory(this.pool, name, this.schema, this.middlewares, this.queryOptions)
|
|
425
|
+
: new index_js_1.QueryInterface(this.pool, name, this.schema, this.middlewares, this.queryOptions);
|
|
417
426
|
this.tableCache.set(name, qi);
|
|
418
427
|
}
|
|
419
428
|
return qi;
|
package/dist/cjs/generate.js
CHANGED
|
@@ -508,6 +508,10 @@ function serializeColumn(col) {
|
|
|
508
508
|
`arrayType: '${escSQ(col.arrayType ?? col.pgArrayType)}'`,
|
|
509
509
|
`pgArrayType: '${escSQ(col.pgArrayType)}'`,
|
|
510
510
|
];
|
|
511
|
+
// Emit isGenerated only when set (server-generated serial/identity), so the
|
|
512
|
+
// output stays byte-identical for the common client-default columns.
|
|
513
|
+
if (col.isGenerated)
|
|
514
|
+
parts.push(`isGenerated: true`);
|
|
511
515
|
if (col.maxLength !== undefined)
|
|
512
516
|
parts.push(`maxLength: ${col.maxLength}`);
|
|
513
517
|
return `{ ${parts.join(', ')} }`;
|
package/dist/cjs/introspect.js
CHANGED
|
@@ -35,6 +35,7 @@ const SQL_COLUMNS = `
|
|
|
35
35
|
data_type,
|
|
36
36
|
is_nullable,
|
|
37
37
|
column_default,
|
|
38
|
+
is_identity,
|
|
38
39
|
ordinal_position,
|
|
39
40
|
character_maximum_length
|
|
40
41
|
FROM information_schema.columns
|
|
@@ -170,6 +171,11 @@ async function introspectPostgresCatalog(options) {
|
|
|
170
171
|
(0, schema_js_1.pgTypeToTs)(isArray ? dialectType : baseType, isNullable),
|
|
171
172
|
nullable: isNullable,
|
|
172
173
|
hasDefault: row.column_default !== null,
|
|
174
|
+
// Server-generated = a sequence default (serial/BIGSERIAL → nextval(…))
|
|
175
|
+
// or an IDENTITY column. Distinct from a client-side default expression
|
|
176
|
+
// (gen_random_uuid(), now()), which Turbine must still synthesize.
|
|
177
|
+
isGenerated: (typeof row.column_default === 'string' && row.column_default.includes('nextval(')) ||
|
|
178
|
+
row.is_identity === 'YES',
|
|
173
179
|
isArray,
|
|
174
180
|
arrayType,
|
|
175
181
|
pgArrayType: arrayType,
|