turbine-orm 0.32.2 → 0.34.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 +2 -2
- package/dist/cjs/dialect.js +1 -0
- package/dist/cjs/index-advisor.js +0 -0
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/mssql.js +3 -0
- package/dist/cjs/mysql.js +3 -0
- package/dist/cjs/optional-peer-import.cjs +28 -0
- package/dist/cjs/powdb-introspect.js +222 -0
- package/dist/cjs/powdb.js +446 -55
- package/dist/cjs/powql.js +566 -111
- package/dist/cjs/query/builder.js +136 -53
- package/dist/cjs/query/filters.js +4 -4
- package/dist/cjs/schema-builder.js +16 -0
- package/dist/cjs/schema-metadata.js +81 -10
- package/dist/cjs/sqlite.js +2 -0
- package/dist/dialect.d.ts +7 -0
- package/dist/dialect.js +1 -0
- package/dist/index-advisor.d.ts +15 -1
- package/dist/index-advisor.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mssql.js +3 -0
- package/dist/mysql.js +3 -0
- package/dist/optional-peer-import.cjs +28 -0
- package/dist/optional-peer-import.d.cts +19 -0
- package/dist/powdb-introspect.d.ts +84 -0
- package/dist/powdb-introspect.js +219 -0
- package/dist/powdb.d.ts +249 -13
- package/dist/powdb.js +438 -54
- package/dist/powql.d.ts +113 -6
- package/dist/powql.js +568 -113
- package/dist/query/builder.d.ts +11 -0
- package/dist/query/builder.js +136 -53
- package/dist/query/filters.d.ts +3 -3
- package/dist/query/filters.js +4 -4
- package/dist/query/types.d.ts +50 -6
- package/dist/schema-builder.d.ts +46 -1
- package/dist/schema-builder.js +15 -0
- package/dist/schema-metadata.d.ts +13 -7
- package/dist/schema-metadata.js +82 -11
- package/dist/schema.d.ts +25 -0
- package/dist/sqlite.js +2 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -863,7 +863,7 @@ const db = await turbineMssql('mssql://sa:Passw0rd!@localhost:1433/app', SCHEMA)
|
|
|
863
863
|
```
|
|
864
864
|
|
|
865
865
|
```ts
|
|
866
|
-
// PowDB — async; embedded (in-process) or networked. Schema is code-defined
|
|
866
|
+
// PowDB — async; embedded (in-process) or networked. Schema is code-defined.
|
|
867
867
|
import { turbinePowDB } from 'turbine-orm/powdb';
|
|
868
868
|
import { schema } from './schema.js'; // defineSchema({...})
|
|
869
869
|
|
|
@@ -891,7 +891,7 @@ Everything is honest about what ports and what doesn't. Features marked **PG-onl
|
|
|
891
891
|
|
|
892
892
|
**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`.
|
|
893
893
|
|
|
894
|
-
**PowDB** speaks its own non-SQL query language (PowQL), so it sits outside the SQL matrix above. Writes use a trailing **`returning`** keyword (upsert reselects by PK). PKs are server-assigned `auto` ints **or** client UUIDs. Nested relations load client-side (N+1, including many-to-many via the junction — no `json_agg`). Nested writes cover hasMany/hasOne/belongsTo; many-to-many nested writes are not supported. Transactions are single-writer: concurrent `$transaction` calls queue FIFO (bounded by `transactionQueueTimeoutMs`); nested/re-entrant transactions throw typed errors (no savepoints). Schema is code-first via `defineSchema`
|
|
894
|
+
**PowDB** speaks its own non-SQL query language (PowQL), so it sits outside the SQL matrix above. Writes use a trailing **`returning`** keyword (upsert reselects by PK). PKs are server-assigned `auto` ints **or** client UUIDs. Nested relations load client-side (N+1, including many-to-many via the junction — no `json_agg`). Nested writes cover hasMany/hasOne/belongsTo; many-to-many nested writes are not supported. Transactions are single-writer: concurrent `$transaction` calls queue FIFO (bounded by `transactionQueueTimeoutMs`); nested/re-entrant transactions throw typed errors (no savepoints). Schema is code-first via `defineSchema` — `schemaDefToMetadata()` bridges it to any engine that needs runtime metadata, and a programmatic `describe`-based introspector exists since 0.34 (relations excluded). JSON documents are first-class on engine 0.12+: `JsonFilter` where-filters, JSON-path `orderBy`/`groupBy`, doc-field expression indexes, and a lossless native wire (0.13+) that keeps JSON `null`, missing fields, and the string `"null"` distinct. Embedded `syncMode: 'normal'` moves fsync off the commit path; the networked transport runs the same data over a socket. Cursor streaming and the Postgres-only trio (pgvector / LISTEN/NOTIFY / RLS session GUCs) throw `UnsupportedFeatureError`. Full details: **[turbineorm.dev/engines#powdb](https://turbineorm.dev/engines#powdb)**.
|
|
895
895
|
|
|
896
896
|
Full setup, signatures, and the complete support matrix: **[turbineorm.dev/engines](https://turbineorm.dev/engines)**.
|
|
897
897
|
|
package/dist/cjs/dialect.js
CHANGED
|
Binary file
|
package/dist/cjs/index.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.ColumnBuilder = exports.applyManyToManyRelations = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.validateChannel = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UnsupportedFeatureError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
|
|
38
|
-
exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.schemaDefToMetadata = exports.table = exports.defineSchema = exports.column = void 0;
|
|
38
|
+
exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = void 0;
|
|
39
39
|
var index_js_1 = require("./adapters/index.js");
|
|
40
40
|
Object.defineProperty(exports, "alloydb", { enumerable: true, get: function () { return index_js_1.alloydb; } });
|
|
41
41
|
Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return index_js_1.cockroachdb; } });
|
|
@@ -110,6 +110,7 @@ Object.defineProperty(exports, "applyManyToManyRelations", { enumerable: true, g
|
|
|
110
110
|
Object.defineProperty(exports, "ColumnBuilder", { enumerable: true, get: function () { return schema_builder_js_1.ColumnBuilder; } });
|
|
111
111
|
Object.defineProperty(exports, "column", { enumerable: true, get: function () { return schema_builder_js_1.column; } });
|
|
112
112
|
Object.defineProperty(exports, "defineSchema", { enumerable: true, get: function () { return schema_builder_js_1.defineSchema; } });
|
|
113
|
+
Object.defineProperty(exports, "isDocFieldIndexDef", { enumerable: true, get: function () { return schema_builder_js_1.isDocFieldIndexDef; } });
|
|
113
114
|
// Legacy compat (deprecated — use object format with defineSchema)
|
|
114
115
|
Object.defineProperty(exports, "table", { enumerable: true, get: function () { return schema_builder_js_1.table; } });
|
|
115
116
|
// Schema metadata bridge — defineSchema() → SchemaMetadata without a live DB
|
package/dist/cjs/mssql.js
CHANGED
|
@@ -478,6 +478,9 @@ exports.mssqlDialect = {
|
|
|
478
478
|
supportsVector: false,
|
|
479
479
|
supportsListenNotify: false,
|
|
480
480
|
supportsRLS: false,
|
|
481
|
+
// SQL Server has OUTER APPLY, not FROM-clause LATERAL: the lateral pick plan
|
|
482
|
+
// is Postgres-only (out of scope here).
|
|
483
|
+
supportsLateralJoin: false,
|
|
481
484
|
// sp_getapplock / sp_releaseapplock exist (used by a future migrate adapter).
|
|
482
485
|
supportsAdvisoryLock: true,
|
|
483
486
|
// FOR JSON over zero rows is NULL → coalesced in the relation override.
|
package/dist/cjs/mysql.js
CHANGED
|
@@ -379,6 +379,9 @@ exports.mysqlDialect = {
|
|
|
379
379
|
supportsVector: false,
|
|
380
380
|
supportsListenNotify: false,
|
|
381
381
|
supportsRLS: false,
|
|
382
|
+
// MySQL 8.0.14+ supports LATERAL, but the opt-in lateral pick plan stays
|
|
383
|
+
// Postgres-only in this release (flipping it on is a one-line change + tests).
|
|
384
|
+
supportsLateralJoin: false,
|
|
382
385
|
// GET_LOCK / RELEASE_LOCK exist (used by a future migrate adapter).
|
|
383
386
|
supportsAdvisoryLock: true,
|
|
384
387
|
// JSON_ARRAYAGG has no inline ORDER BY argument → force the inner-subquery
|
|
@@ -119,4 +119,32 @@ async function importOptionalPeer(specifier, allowEsmFallback = true) {
|
|
|
119
119
|
return esmCapableCopy(specifier, false);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Merged namespace so callers can reach {@link peerPackageVersion} off the same
|
|
124
|
+
* default import (`importOptionalPeer.peerPackageVersion(...)`). Lives in this
|
|
125
|
+
* `.cts` file for the same reason the dynamic import does: a `.cts` compiles to
|
|
126
|
+
* CommonJS in BOTH build passes, so `require` / `require.resolve` are natively
|
|
127
|
+
* available and `import.meta` is never emitted (which would break the CJS build
|
|
128
|
+
* and crash CJS consumers, see `resolveEmbeddedVersion` in powdb.ts).
|
|
129
|
+
*/
|
|
130
|
+
(function (importOptionalPeer) {
|
|
131
|
+
/**
|
|
132
|
+
* Resolve an optional peer's declared `package.json` version WITHOUT loading
|
|
133
|
+
* the package itself (so an ESM-only peer never trips `require`). `require` is
|
|
134
|
+
* anchored on THIS module's location (inside the published `dist/`), so bare
|
|
135
|
+
* resolution walks up `node_modules` and finds the peer exactly where
|
|
136
|
+
* `import.meta.url` used to point, but it compiles under `module: CommonJS`
|
|
137
|
+
* too. Returns `null` when the peer / its package.json cannot be resolved.
|
|
138
|
+
*/
|
|
139
|
+
function peerPackageVersion(specifier) {
|
|
140
|
+
try {
|
|
141
|
+
const pkg = require(`${specifier}/package.json`);
|
|
142
|
+
return typeof pkg.version === 'string' ? pkg.version : null;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
importOptionalPeer.peerPackageVersion = peerPackageVersion;
|
|
149
|
+
})(importOptionalPeer || (importOptionalPeer = {}));
|
|
122
150
|
module.exports = importOptionalPeer;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm/powdb — `describe`-based introspection.
|
|
4
|
+
*
|
|
5
|
+
* PowDB exposes its catalog through two ordinary rows-returning statements
|
|
6
|
+
* (keywords since engine 0.10):
|
|
7
|
+
* - `schema` → one row per type: `{ name, columns }` (columns = a count).
|
|
8
|
+
* - `describe <T>` / `schema <T>` → one row per column:
|
|
9
|
+
* `{ column, type, nullable, index }` where `type` is a PowQL type name
|
|
10
|
+
* (`str`/`int`/`float`/`bool`/`json`/`datetime`/`uuid`/`bytes`), `nullable`
|
|
11
|
+
* is `"true"`/`"false"`, and `index` is `"unique"` / `"index"` / `""`.
|
|
12
|
+
*
|
|
13
|
+
* {@link introspectPowdbDatabase} turns those into the same {@link SchemaMetadata}
|
|
14
|
+
* shape the SQL introspectors produce, so a code-first PowDB database can be
|
|
15
|
+
* introspected for bootstrap/verification. It is transport-agnostic: the caller
|
|
16
|
+
* supplies an `exec(powql)` that returns row objects **keyed by column name**.
|
|
17
|
+
* - Embedded / owned pool: `exec = async (q) => ({ rows: await db.raw([q]) })`
|
|
18
|
+
* using a live `turbinePowDB` client's `raw` tagged template.
|
|
19
|
+
* - Networked: the raw `@zvndev/powdb-client` returns POSITIONAL rows
|
|
20
|
+
* (`{ columns: string[], rows: string[][] }`), so zip them into records.
|
|
21
|
+
* A bare `(await client.query(q)).rows` would hand this function `string[][]`
|
|
22
|
+
* whose `.name` cell is `undefined` and every table would silently drop out:
|
|
23
|
+
* ```ts
|
|
24
|
+
* const exec = async (q) => {
|
|
25
|
+
* const r = await client.query(q);
|
|
26
|
+
* return { rows: r.rows.map((row) => Object.fromEntries(r.columns.map((c, i) => [c, row[i]]))) };
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
29
|
+
* (A mis-shaped exec is now caught: if `schema` returns rows but none carry
|
|
30
|
+
* a `name`, {@link introspectPowdbDatabase} throws instead of returning an
|
|
31
|
+
* empty schema.)
|
|
32
|
+
*
|
|
33
|
+
* IMPORTANT LIMITATIONS (all documented, none silent):
|
|
34
|
+
* - Relations are ALWAYS `{}`: PowDB has no declared foreign keys, so
|
|
35
|
+
* `describe` cannot report them. The recommended flow for relation-aware
|
|
36
|
+
* metadata is code-first `defineSchema` + `schemaDefToMetadata`; use
|
|
37
|
+
* introspection to bootstrap or verify column shape.
|
|
38
|
+
* - Primary key is a HEURISTIC (`describe` has no PK concept): PowDB marks a
|
|
39
|
+
* PK column as `required unique`, so the first non-nullable `unique` column
|
|
40
|
+
* is chosen (a column named `id` wins ties). A table with no such column
|
|
41
|
+
* yields `primaryKey: []` and a warning; single-row ops on it fail loudly.
|
|
42
|
+
* - `isGenerated` is always `false`: `describe` does not expose PowDB's `auto`
|
|
43
|
+
* modifier, so an introspected int PK is treated as client-supplied unless
|
|
44
|
+
* the caller hand-edits the metadata.
|
|
45
|
+
* - Doc-field expression indexes are INVISIBLE to `describe`, so they never
|
|
46
|
+
* round-trip; only plain `unique`/`index` columns appear in `indexes`.
|
|
47
|
+
* - `datetime` / `uuid` / `bytes` columns map to read-oriented TS types
|
|
48
|
+
* (`Date` / `string` / `Uint8Array`). Turbine never emits those PowQL types
|
|
49
|
+
* on write, so writing to such a column may not round-trip.
|
|
50
|
+
*
|
|
51
|
+
* v1 is a PROGRAMMATIC API (exported from `turbine-orm/powdb`); the CLI's
|
|
52
|
+
* `turbine generate` still defaults to Postgres. Routing a `powdb://` URL
|
|
53
|
+
* through the CLI would additionally need: a `powdbDialect.introspector`
|
|
54
|
+
* wired to a networked `exec`, and `cli/config.ts` teaching the generate
|
|
55
|
+
* funnel to construct a PowDB client instead of a `pg` client for `powdb://`.
|
|
56
|
+
*/
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.introspectPowdbDatabase = introspectPowdbDatabase;
|
|
59
|
+
const errors_js_1 = require("./errors.js");
|
|
60
|
+
const powdb_js_1 = require("./powdb.js");
|
|
61
|
+
const schema_js_1 = require("./schema.js");
|
|
62
|
+
/** Coerce a wire cell to string (legacy wire cells are strings; native cells may be typed). */
|
|
63
|
+
function asString(v) {
|
|
64
|
+
return v === null || v === undefined ? '' : String(v);
|
|
65
|
+
}
|
|
66
|
+
/** Coerce a `describe` `nullable` cell (`"true"`/`"false"` or a native boolean) to a JS boolean. */
|
|
67
|
+
function asBool(v) {
|
|
68
|
+
return v === true || asString(v).toLowerCase() === 'true';
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Map a PowQL type name to the {@link ColumnMetadata} TS/dialect types. The
|
|
72
|
+
* `tsType` drives read coercion (`coerceValue`) and write typing
|
|
73
|
+
* (`powqlColumnType`); `dialectType`/`pgType` carry the PowQL type name so
|
|
74
|
+
* `isFloatColumn` / `isJsonColumn` classify correctly.
|
|
75
|
+
*/
|
|
76
|
+
function mapPowqlType(powqlType) {
|
|
77
|
+
switch (powqlType) {
|
|
78
|
+
case 'int':
|
|
79
|
+
return { tsType: 'number', dialectType: 'int' };
|
|
80
|
+
case 'float':
|
|
81
|
+
return { tsType: 'number', dialectType: 'float' };
|
|
82
|
+
case 'bool':
|
|
83
|
+
return { tsType: 'boolean', dialectType: 'bool' };
|
|
84
|
+
case 'json':
|
|
85
|
+
return { tsType: 'unknown', dialectType: 'json' };
|
|
86
|
+
case 'datetime':
|
|
87
|
+
return { tsType: 'Date', dialectType: 'datetime' };
|
|
88
|
+
case 'uuid':
|
|
89
|
+
return { tsType: 'string', dialectType: 'uuid' };
|
|
90
|
+
case 'bytes':
|
|
91
|
+
return { tsType: 'Uint8Array', dialectType: 'bytes' };
|
|
92
|
+
default:
|
|
93
|
+
// `str` and any unknown future scalar fall back to string.
|
|
94
|
+
return { tsType: 'string', dialectType: 'str' };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Read a live PowDB database into {@link SchemaMetadata} via `schema` +
|
|
99
|
+
* `describe <T>` statements run through the supplied {@link PowdbExec}.
|
|
100
|
+
*
|
|
101
|
+
* @param exec Rows-returning executor (embedded `db.raw` or networked `client.query`).
|
|
102
|
+
* @param options `include`/`exclude` table filters.
|
|
103
|
+
*/
|
|
104
|
+
async function introspectPowdbDatabase(exec, options = {}) {
|
|
105
|
+
// Gate on the engine's introspection capability (>= 0.10) when the caller
|
|
106
|
+
// knows the version, so a pre-0.10 engine gets a typed E017 hint instead of
|
|
107
|
+
// an opaque `unexpected token schema` parse error.
|
|
108
|
+
if (options.capabilities) {
|
|
109
|
+
(0, powdb_js_1.requireCapability)(options.capabilities, 'introspection', 'PowDB `describe` introspection');
|
|
110
|
+
}
|
|
111
|
+
// ----- Types (one row per table, columns `name`, `columns`) -----
|
|
112
|
+
const schemaRows = (await exec('schema')).rows;
|
|
113
|
+
let tableNames = schemaRows.map((r) => asString(r.name)).filter((n) => n.length > 0);
|
|
114
|
+
// A mis-shaped `exec` (e.g. the raw client's positional `string[][]` rows
|
|
115
|
+
// passed straight through) yields rows whose `name` cell is `undefined`, so
|
|
116
|
+
// every table filters out and the schema comes back silently empty. Refuse
|
|
117
|
+
// that instead of losing data: real rows must carry a `name`.
|
|
118
|
+
if (schemaRows.length > 0 && tableNames.length === 0) {
|
|
119
|
+
throw new errors_js_1.ValidationError(`[turbine] PowDB introspection: the \`schema\` statement returned ${schemaRows.length} row(s) but none carried a ` +
|
|
120
|
+
'`name` cell. The `exec` you supplied likely returns POSITIONAL rows (string[][]) rather than records keyed by ' +
|
|
121
|
+
'column name; zip `columns` with each row (see introspectPowdbDatabase docs).');
|
|
122
|
+
}
|
|
123
|
+
if (options.include?.length) {
|
|
124
|
+
const inc = new Set(options.include);
|
|
125
|
+
tableNames = tableNames.filter((t) => inc.has(t));
|
|
126
|
+
}
|
|
127
|
+
if (options.exclude?.length) {
|
|
128
|
+
const exc = new Set(options.exclude);
|
|
129
|
+
tableNames = tableNames.filter((t) => !exc.has(t));
|
|
130
|
+
}
|
|
131
|
+
const tables = {};
|
|
132
|
+
for (const tableName of tableNames) {
|
|
133
|
+
// `describe` needs the table name in bare-identifier position → quote it so
|
|
134
|
+
// a reserved-word / non-bare table name (`order`) does not become a parse
|
|
135
|
+
// error.
|
|
136
|
+
const describeRows = (await exec(`describe ${(0, powdb_js_1.quotePowqlIdent)(tableName)}`)).rows.map((r) => ({
|
|
137
|
+
column: asString(r.column),
|
|
138
|
+
type: asString(r.type),
|
|
139
|
+
nullable: asBool(r.nullable),
|
|
140
|
+
index: asString(r.index),
|
|
141
|
+
}));
|
|
142
|
+
const columns = [];
|
|
143
|
+
const columnMap = {};
|
|
144
|
+
const reverseColumnMap = {};
|
|
145
|
+
const dateColumns = new Set();
|
|
146
|
+
const dialectTypes = {};
|
|
147
|
+
const pgTypes = {};
|
|
148
|
+
const allColumns = [];
|
|
149
|
+
const uniqueColumns = [];
|
|
150
|
+
const indexes = [];
|
|
151
|
+
// PK heuristic candidates: non-nullable `unique` columns.
|
|
152
|
+
const pkCandidates = [];
|
|
153
|
+
for (const row of describeRows) {
|
|
154
|
+
const name = row.column;
|
|
155
|
+
const field = (0, schema_js_1.snakeToCamel)(name);
|
|
156
|
+
const { tsType, dialectType } = mapPowqlType(row.type);
|
|
157
|
+
const nullable = row.nullable;
|
|
158
|
+
const finalTs = nullable ? `${tsType} | null` : tsType;
|
|
159
|
+
const col = {
|
|
160
|
+
name,
|
|
161
|
+
field,
|
|
162
|
+
dialectType,
|
|
163
|
+
pgType: dialectType,
|
|
164
|
+
tsType: finalTs,
|
|
165
|
+
nullable,
|
|
166
|
+
// `describe` reports neither defaults nor the `auto` modifier.
|
|
167
|
+
hasDefault: false,
|
|
168
|
+
isGenerated: false,
|
|
169
|
+
isArray: false,
|
|
170
|
+
arrayType: undefined,
|
|
171
|
+
pgArrayType: 'text[]',
|
|
172
|
+
};
|
|
173
|
+
columns.push(col);
|
|
174
|
+
columnMap[field] = name;
|
|
175
|
+
reverseColumnMap[name] = field;
|
|
176
|
+
allColumns.push(name);
|
|
177
|
+
dialectTypes[name] = dialectType;
|
|
178
|
+
pgTypes[name] = dialectType;
|
|
179
|
+
if (dialectType === 'datetime')
|
|
180
|
+
dateColumns.add(name);
|
|
181
|
+
if (row.index === 'unique') {
|
|
182
|
+
uniqueColumns.push([name]);
|
|
183
|
+
if (!nullable)
|
|
184
|
+
pkCandidates.push(name);
|
|
185
|
+
}
|
|
186
|
+
if (row.index === 'unique' || row.index === 'index') {
|
|
187
|
+
indexes.push({
|
|
188
|
+
name: `${tableName}_${name}_idx`,
|
|
189
|
+
columns: [name],
|
|
190
|
+
unique: row.index === 'unique',
|
|
191
|
+
definition: `${row.index === 'unique' ? 'unique ' : ''}index on ${tableName}(${name})`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Primary key: first non-nullable unique column, preferring one named `id`.
|
|
196
|
+
let primaryKey = [];
|
|
197
|
+
if (pkCandidates.length > 0) {
|
|
198
|
+
primaryKey = [pkCandidates.includes('id') ? 'id' : pkCandidates[0]];
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
console.warn(`[turbine] PowDB introspection: table "${tableName}" has no non-nullable unique column; ` +
|
|
202
|
+
'primaryKey is [] (single-row operations will fail). Supply a primary key via code-first ' +
|
|
203
|
+
'`defineSchema` metadata if this table needs findUnique/update/delete by id.');
|
|
204
|
+
}
|
|
205
|
+
tables[tableName] = {
|
|
206
|
+
name: tableName,
|
|
207
|
+
columns,
|
|
208
|
+
columnMap,
|
|
209
|
+
reverseColumnMap,
|
|
210
|
+
dateColumns,
|
|
211
|
+
dialectTypes,
|
|
212
|
+
pgTypes,
|
|
213
|
+
allColumns,
|
|
214
|
+
primaryKey,
|
|
215
|
+
uniqueColumns,
|
|
216
|
+
// PowDB has no declared foreign keys → no relations from introspection.
|
|
217
|
+
relations: {},
|
|
218
|
+
indexes,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return { tables, enums: {} };
|
|
222
|
+
}
|