turbine-orm 0.27.1 → 0.28.1
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 -15
- package/dist/cjs/cli/config.js +20 -3
- package/dist/cjs/cli/index.js +273 -71
- package/dist/cjs/cli/mcp.js +788 -0
- package/dist/cjs/cli/migrate.js +95 -20
- package/dist/cjs/cli/studio.js +3 -2
- package/dist/cjs/client.js +267 -34
- package/dist/cjs/dialect.js +2 -0
- package/dist/cjs/errors.js +15 -1
- package/dist/cjs/generate.js +171 -7
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/introspect.js +177 -4
- package/dist/cjs/powdb.js +1 -1
- package/dist/cjs/powql.js +1 -1
- package/dist/cjs/query/batched-loader.js +148 -0
- package/dist/cjs/query/builder.js +763 -401
- package/dist/cjs/query/deferred.js +7 -0
- package/dist/cjs/query/filters.js +251 -0
- package/dist/cjs/schema-builder.js +59 -4
- package/dist/cjs/schema-sql.js +315 -6
- package/dist/cjs/seed.js +66 -0
- package/dist/cli/config.d.ts +9 -2
- package/dist/cli/config.js +19 -3
- package/dist/cli/index.d.ts +52 -1
- package/dist/cli/index.js +272 -74
- package/dist/cli/mcp.d.ts +17 -0
- package/dist/cli/mcp.js +781 -0
- package/dist/cli/migrate.d.ts +37 -0
- package/dist/cli/migrate.js +92 -20
- package/dist/cli/studio.d.ts +3 -2
- package/dist/cli/studio.js +3 -2
- package/dist/client.d.ts +136 -1
- package/dist/client.js +267 -34
- package/dist/dialect.d.ts +17 -0
- package/dist/dialect.js +2 -0
- package/dist/errors.js +15 -1
- package/dist/generate.d.ts +17 -0
- package/dist/generate.js +171 -10
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -0
- package/dist/introspect.d.ts +20 -1
- package/dist/introspect.js +175 -4
- package/dist/powdb.d.ts +1 -1
- package/dist/powdb.js +1 -1
- package/dist/powql.d.ts +1 -1
- package/dist/powql.js +1 -1
- package/dist/query/batched-loader.d.ts +29 -2
- package/dist/query/batched-loader.js +148 -1
- package/dist/query/builder.d.ts +151 -122
- package/dist/query/builder.js +701 -339
- package/dist/query/deferred.d.ts +130 -0
- package/dist/query/deferred.js +6 -0
- package/dist/query/filters.d.ts +120 -0
- package/dist/query/filters.js +232 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/types.d.ts +113 -8
- package/dist/schema-builder.d.ts +73 -8
- package/dist/schema-builder.js +59 -4
- package/dist/schema-sql.d.ts +67 -0
- package/dist/schema-sql.js +310 -6
- package/dist/schema.d.ts +53 -0
- package/dist/seed.d.ts +4 -0
- package/dist/seed.js +63 -0
- package/package.json +4 -5
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm — Where-filter type guards and shape helpers
|
|
4
|
+
*
|
|
5
|
+
* Pure detection / fingerprint utilities used by the query builder's WHERE
|
|
6
|
+
* compiler. Kept out of builder.ts so the class file stays about SQL assembly
|
|
7
|
+
* and execution rather than filter-shape bookkeeping.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.VECTOR_DISTANCE_COMPARATORS = exports.VECTOR_METRIC_OPERATORS = exports.TEXT_SEARCH_KEYS = exports.ARRAY_UNIQUE_KEYS = exports.ARRAY_OPERATOR_KEYS = exports.JSONB_UNIQUE_KEYS = exports.JSONB_OPERATOR_KEYS = exports.UPDATE_OPERATOR_KEYS = void 0;
|
|
11
|
+
exports.isWhereOperator = isWhereOperator;
|
|
12
|
+
exports.isUnmatchedPlainObject = isUnmatchedPlainObject;
|
|
13
|
+
exports.fingerprintOperatorShape = fingerprintOperatorShape;
|
|
14
|
+
exports.assertBindableEqualsOperand = assertBindableEqualsOperand;
|
|
15
|
+
exports.sortedKeys = sortedKeys;
|
|
16
|
+
exports.sortedEntries = sortedEntries;
|
|
17
|
+
exports.isJsonFilter = isJsonFilter;
|
|
18
|
+
exports.findJsonUniqueKey = findJsonUniqueKey;
|
|
19
|
+
exports.isArrayFilter = isArrayFilter;
|
|
20
|
+
exports.findArrayUniqueKey = findArrayUniqueKey;
|
|
21
|
+
exports.isTextSearchFilter = isTextSearchFilter;
|
|
22
|
+
exports.validateTextSearchConfig = validateTextSearchConfig;
|
|
23
|
+
exports.isVectorFilter = isVectorFilter;
|
|
24
|
+
exports.isVectorOrderBy = isVectorOrderBy;
|
|
25
|
+
exports.isOrderBySpec = isOrderBySpec;
|
|
26
|
+
exports.normalizeOrderBy = normalizeOrderBy;
|
|
27
|
+
const errors_js_1 = require("../errors.js");
|
|
28
|
+
const utils_js_1 = require("./utils.js");
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Where-operator detection
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/** Check if a value is a where operator object (has at least one known operator key) */
|
|
33
|
+
function isWhereOperator(value) {
|
|
34
|
+
if (value === null ||
|
|
35
|
+
value === undefined ||
|
|
36
|
+
typeof value !== 'object' ||
|
|
37
|
+
Array.isArray(value) ||
|
|
38
|
+
value instanceof Date) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const keys = Object.keys(value);
|
|
42
|
+
return keys.length > 0 && keys.every((k) => utils_js_1.OPERATOR_KEYS.has(k));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* True for a *plain object literal* that reached an equality fallthrough
|
|
46
|
+
* without matching any known filter shape — the misspelled-operator case.
|
|
47
|
+
* Class instances (Buffer for bytea, Decimal wrappers, ...) are legitimate
|
|
48
|
+
* bind values and return false, as do arrays and Dates.
|
|
49
|
+
*/
|
|
50
|
+
function isUnmatchedPlainObject(value) {
|
|
51
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value) || value instanceof Date)
|
|
52
|
+
return false;
|
|
53
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value))
|
|
54
|
+
return false;
|
|
55
|
+
const proto = Object.getPrototypeOf(value);
|
|
56
|
+
return proto === Object.prototype || proto === null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Fingerprint the SHAPE of a where-operator object. Null-valued `equals` /
|
|
60
|
+
* `not` compile to parameterless `IS NULL` / `IS NOT NULL` (different SQL, no
|
|
61
|
+
* param pushed), so null-ness is part of the shape — without it a cache entry
|
|
62
|
+
* warmed by `{ not: 5 }` would serve `{ not: null }` with a desynced param list.
|
|
63
|
+
*/
|
|
64
|
+
function fingerprintOperatorShape(value) {
|
|
65
|
+
const obj = value;
|
|
66
|
+
const opKeys = Object.keys(obj)
|
|
67
|
+
.filter((k) => k !== 'mode')
|
|
68
|
+
.map((k) => ((k === 'equals' || k === 'not') && obj[k] === null ? `${k}:null` : k))
|
|
69
|
+
.sort();
|
|
70
|
+
const modeStr = value.mode === 'insensitive' ? ':i' : '';
|
|
71
|
+
return `op(${opKeys.join(',')}${modeStr})`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Guard for the value of an `equals` operator reaching the plain-equality
|
|
75
|
+
* operator path. A plain object literal can only legitimately be an equality
|
|
76
|
+
* value on a json/jsonb column — and those route to the JSONB filter branch
|
|
77
|
+
* BEFORE the operator branch, so any plain object that reaches here is a
|
|
78
|
+
* mistake (e.g. `{ equals: { foo: 1 } }` on a text column). Shared by the
|
|
79
|
+
* SQL-build path and the cache-hit param-collect path so a warmed cache can
|
|
80
|
+
* never skip the check.
|
|
81
|
+
*/
|
|
82
|
+
function assertBindableEqualsOperand(value, column) {
|
|
83
|
+
if (!isUnmatchedPlainObject(value))
|
|
84
|
+
return;
|
|
85
|
+
throw new errors_js_1.ValidationError(`[turbine] Plain-object value for operator 'equals' on ${column}: ` +
|
|
86
|
+
`objects are only valid 'equals' values on JSON (json/jsonb) columns, ` +
|
|
87
|
+
`where 'equals' is the JSONB containment filter.`);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Object keys in sorted order, mirroring the canonical order used by every
|
|
91
|
+
* cache fingerprint. The SQL-build and cache-hit param-collect paths MUST
|
|
92
|
+
* enumerate object keys in this exact order: fingerprints sort keys, so two
|
|
93
|
+
* where clauses with the same fields in different insertion order share one
|
|
94
|
+
* cache entry — if build/collect iterated insertion order, the cached SQL's
|
|
95
|
+
* `$N` placeholders would bind the wrong values (cross-tenant-leak class).
|
|
96
|
+
* Array order (OR/AND members) is positional and is never sorted.
|
|
97
|
+
*/
|
|
98
|
+
function sortedKeys(obj) {
|
|
99
|
+
return Object.keys(obj).sort();
|
|
100
|
+
}
|
|
101
|
+
/** {@link sortedKeys}, but yielding `[key, value]` pairs. */
|
|
102
|
+
function sortedEntries(obj) {
|
|
103
|
+
return Object.entries(obj).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
104
|
+
}
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Atomic-update / JSONB / Array / text-search / vector key sets
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
/** Known atomic-update operator keys — used to detect operator objects vs plain JSON values */
|
|
109
|
+
exports.UPDATE_OPERATOR_KEYS = new Set(['set', 'increment', 'decrement', 'multiply', 'divide']);
|
|
110
|
+
/** Known JSONB operator keys */
|
|
111
|
+
exports.JSONB_OPERATOR_KEYS = new Set(['path', 'equals', 'contains', 'hasKey']);
|
|
112
|
+
/**
|
|
113
|
+
* JSONB operator keys that are *unique* to {@link JsonFilter} — they cannot
|
|
114
|
+
* appear in any other where-filter shape, so the presence of one of these is
|
|
115
|
+
* an unambiguous signal that the user meant a JSON filter. Used by the
|
|
116
|
+
* strict-validation path so that `{ contains: 'foo' }` (which is also a valid
|
|
117
|
+
* `WhereOperator` for LIKE) is not misclassified. Note `equals` is NOT in this
|
|
118
|
+
* set: on non-JSON columns it is a plain equality operator (`WhereOperator`),
|
|
119
|
+
* so it must fall through instead of throwing.
|
|
120
|
+
*/
|
|
121
|
+
exports.JSONB_UNIQUE_KEYS = new Set(['path', 'hasKey']);
|
|
122
|
+
/** Check if a value is a JSONB filter object */
|
|
123
|
+
function isJsonFilter(value) {
|
|
124
|
+
if (value === null ||
|
|
125
|
+
value === undefined ||
|
|
126
|
+
typeof value !== 'object' ||
|
|
127
|
+
Array.isArray(value) ||
|
|
128
|
+
value instanceof Date) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
const keys = Object.keys(value);
|
|
132
|
+
return keys.length > 0 && keys.some((k) => exports.JSONB_OPERATOR_KEYS.has(k));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Returns the first JSON-unique key found in `value`, or `null` if none.
|
|
136
|
+
* Used to drive the strict-validation error message.
|
|
137
|
+
*/
|
|
138
|
+
function findJsonUniqueKey(value) {
|
|
139
|
+
for (const k of Object.keys(value)) {
|
|
140
|
+
if (exports.JSONB_UNIQUE_KEYS.has(k))
|
|
141
|
+
return k;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
/** Known Array operator keys */
|
|
146
|
+
exports.ARRAY_OPERATOR_KEYS = new Set(['has', 'hasEvery', 'hasSome', 'isEmpty']);
|
|
147
|
+
/**
|
|
148
|
+
* Array operator keys that are *unique* to {@link ArrayFilter}. None of the
|
|
149
|
+
* array operators currently overlap with `WhereOperator` or `JsonFilter`, so
|
|
150
|
+
* this set equals {@link ARRAY_OPERATOR_KEYS}; it is kept as a separate
|
|
151
|
+
* constant so a future overlap (e.g. a `contains` for arrays) is easy to
|
|
152
|
+
* carve out.
|
|
153
|
+
*/
|
|
154
|
+
exports.ARRAY_UNIQUE_KEYS = new Set(['has', 'hasEvery', 'hasSome', 'isEmpty']);
|
|
155
|
+
/** Check if a value is an Array filter object */
|
|
156
|
+
function isArrayFilter(value) {
|
|
157
|
+
if (value === null ||
|
|
158
|
+
value === undefined ||
|
|
159
|
+
typeof value !== 'object' ||
|
|
160
|
+
Array.isArray(value) ||
|
|
161
|
+
value instanceof Date) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const keys = Object.keys(value);
|
|
165
|
+
return keys.length > 0 && keys.some((k) => exports.ARRAY_OPERATOR_KEYS.has(k));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Returns the first array-unique key found in `value`, or `null` if none.
|
|
169
|
+
* Used to drive the strict-validation error message.
|
|
170
|
+
*/
|
|
171
|
+
function findArrayUniqueKey(value) {
|
|
172
|
+
for (const k of Object.keys(value)) {
|
|
173
|
+
if (exports.ARRAY_UNIQUE_KEYS.has(k))
|
|
174
|
+
return k;
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
/** Known text search operator keys */
|
|
179
|
+
exports.TEXT_SEARCH_KEYS = new Set(['search', 'config']);
|
|
180
|
+
/** Check if a value is a TextSearchFilter object */
|
|
181
|
+
function isTextSearchFilter(value) {
|
|
182
|
+
if (value === null ||
|
|
183
|
+
value === undefined ||
|
|
184
|
+
typeof value !== 'object' ||
|
|
185
|
+
Array.isArray(value) ||
|
|
186
|
+
value instanceof Date) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
const keys = Object.keys(value);
|
|
190
|
+
// Must have 'search' key and only known text search keys
|
|
191
|
+
return keys.includes('search') && keys.every((k) => exports.TEXT_SEARCH_KEYS.has(k));
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Validate a text search config name. Only alphanumeric characters and
|
|
195
|
+
* underscores are allowed to prevent SQL injection via the config parameter.
|
|
196
|
+
*/
|
|
197
|
+
function validateTextSearchConfig(config) {
|
|
198
|
+
return /^[a-zA-Z0-9_]+$/.test(config);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* pgvector distance metric → operator allow-list. This is the ONLY mapping
|
|
202
|
+
* from a user-supplied metric token to a SQL operator; any token not present
|
|
203
|
+
* here is rejected, so a user value can never become an arbitrary operator.
|
|
204
|
+
*
|
|
205
|
+
* - `l2` → `<->` (Euclidean / L2 distance)
|
|
206
|
+
* - `cosine` → `<=>` (cosine distance)
|
|
207
|
+
* - `ip` → `<#>` (negative inner product)
|
|
208
|
+
*/
|
|
209
|
+
exports.VECTOR_METRIC_OPERATORS = {
|
|
210
|
+
l2: '<->',
|
|
211
|
+
cosine: '<=>',
|
|
212
|
+
ip: '<#>',
|
|
213
|
+
};
|
|
214
|
+
/** Comparison keys allowed on a {@link VectorDistanceFilter}. */
|
|
215
|
+
exports.VECTOR_DISTANCE_COMPARATORS = {
|
|
216
|
+
lt: '<',
|
|
217
|
+
lte: '<=',
|
|
218
|
+
gt: '>',
|
|
219
|
+
gte: '>=',
|
|
220
|
+
};
|
|
221
|
+
/** Check if a value is a vector distance WHERE filter: `{ distance: { to, metric } }` */
|
|
222
|
+
function isVectorFilter(value) {
|
|
223
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
const dist = value.distance;
|
|
227
|
+
return (typeof dist === 'object' &&
|
|
228
|
+
dist !== null &&
|
|
229
|
+
!Array.isArray(dist) &&
|
|
230
|
+
'to' in dist &&
|
|
231
|
+
'metric' in dist);
|
|
232
|
+
}
|
|
233
|
+
/** Check if an orderBy value is a vector KNN ordering: `{ distance: { to, metric } }` */
|
|
234
|
+
function isVectorOrderBy(value) {
|
|
235
|
+
return isVectorFilter(value);
|
|
236
|
+
}
|
|
237
|
+
/** Check if an orderBy value is an explicit `{ sort, nulls? }` spec. */
|
|
238
|
+
function isOrderBySpec(value) {
|
|
239
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && 'sort' in value;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Normalize an orderBy value into `{ direction, nulls }`. Accepts a plain
|
|
243
|
+
* direction string or an {@link OrderBySpec}. Used by every ORDER BY compile
|
|
244
|
+
* path (findMany, groupBy, relation inner subqueries).
|
|
245
|
+
*/
|
|
246
|
+
function normalizeOrderBy(value) {
|
|
247
|
+
if (isOrderBySpec(value)) {
|
|
248
|
+
return { dir: value.sort.toLowerCase() === 'desc' ? 'DESC' : 'ASC', nulls: value.nulls };
|
|
249
|
+
}
|
|
250
|
+
return { dir: String(value).toLowerCase() === 'desc' ? 'DESC' : 'ASC' };
|
|
251
|
+
}
|
|
@@ -57,12 +57,34 @@ const TYPE_MAP = {
|
|
|
57
57
|
double: 'DOUBLE PRECISION',
|
|
58
58
|
numeric: 'NUMERIC',
|
|
59
59
|
bytea: 'BYTEA',
|
|
60
|
+
// Sentinels — the real DDL type is derived from `enumName` / `dimensions`
|
|
61
|
+
// in schema-sql.ts, never from these placeholders.
|
|
62
|
+
enum: 'ENUM',
|
|
63
|
+
vector: 'VECTOR',
|
|
60
64
|
};
|
|
61
65
|
/** Convert a user-facing ColumnDef to the internal ColumnConfig */
|
|
62
66
|
function resolveColumn(def) {
|
|
63
67
|
if (!(def.type in TYPE_MAP)) {
|
|
64
68
|
throw new Error(`Invalid column type "${def.type}". Valid types: ${Object.keys(TYPE_MAP).join(', ')}`);
|
|
65
69
|
}
|
|
70
|
+
if (def.type === 'enum' && !def.enumName) {
|
|
71
|
+
throw new Error(`Column of type "enum" requires an "enumName" (the CREATE TYPE name).`);
|
|
72
|
+
}
|
|
73
|
+
if (def.type === 'vector' && (def.dimensions == null || def.dimensions <= 0)) {
|
|
74
|
+
throw new Error(`Column of type "vector" requires a positive "dimensions" count.`);
|
|
75
|
+
}
|
|
76
|
+
// `references` is either the "table.column" string or a { target, onDelete, onUpdate } object.
|
|
77
|
+
let referencesTarget = null;
|
|
78
|
+
let onDelete = null;
|
|
79
|
+
let onUpdate = null;
|
|
80
|
+
if (typeof def.references === 'string') {
|
|
81
|
+
referencesTarget = def.references;
|
|
82
|
+
}
|
|
83
|
+
else if (def.references) {
|
|
84
|
+
referencesTarget = def.references.target;
|
|
85
|
+
onDelete = def.references.onDelete ?? null;
|
|
86
|
+
onUpdate = def.references.onUpdate ?? null;
|
|
87
|
+
}
|
|
66
88
|
return {
|
|
67
89
|
type: TYPE_MAP[def.type],
|
|
68
90
|
isPrimaryKey: def.primaryKey ?? false,
|
|
@@ -70,8 +92,14 @@ function resolveColumn(def) {
|
|
|
70
92
|
isNullable: def.nullable ?? false,
|
|
71
93
|
isUnique: def.unique ?? false,
|
|
72
94
|
defaultValue: def.default ?? null,
|
|
73
|
-
referencesTarget
|
|
95
|
+
referencesTarget,
|
|
74
96
|
maxLength: def.maxLength ?? null,
|
|
97
|
+
onDelete,
|
|
98
|
+
onUpdate,
|
|
99
|
+
enumName: def.enumName ?? null,
|
|
100
|
+
vectorDimensions: def.dimensions ?? null,
|
|
101
|
+
isArray: def.array ?? false,
|
|
102
|
+
check: def.check ?? null,
|
|
75
103
|
};
|
|
76
104
|
}
|
|
77
105
|
/** Check if a value is a TableDef (from legacy table() builder) */
|
|
@@ -97,7 +125,7 @@ function isTableDef(v) {
|
|
|
97
125
|
* });
|
|
98
126
|
* ```
|
|
99
127
|
*/
|
|
100
|
-
function defineSchema(input) {
|
|
128
|
+
function defineSchema(input, options) {
|
|
101
129
|
const tables = {};
|
|
102
130
|
for (const [accessor, value] of Object.entries(input)) {
|
|
103
131
|
// The user-facing key is the camelCase JS accessor; the DDL-facing
|
|
@@ -116,6 +144,7 @@ function defineSchema(input) {
|
|
|
116
144
|
const columns = {};
|
|
117
145
|
let pk;
|
|
118
146
|
let m2m;
|
|
147
|
+
let checks;
|
|
119
148
|
for (const [fieldName, def] of Object.entries(raw)) {
|
|
120
149
|
if (fieldName === 'manyToMany') {
|
|
121
150
|
if (def !== undefined) {
|
|
@@ -126,6 +155,15 @@ function defineSchema(input) {
|
|
|
126
155
|
}
|
|
127
156
|
continue;
|
|
128
157
|
}
|
|
158
|
+
if (fieldName === 'checks') {
|
|
159
|
+
if (def !== undefined) {
|
|
160
|
+
if (!Array.isArray(def)) {
|
|
161
|
+
throw new Error(`Table "${accessor}": "checks" must be an array of { name?, expression } objects`);
|
|
162
|
+
}
|
|
163
|
+
checks = def;
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
129
167
|
if (fieldName === 'primaryKey') {
|
|
130
168
|
// Top-level composite primary key declaration
|
|
131
169
|
if (def !== undefined) {
|
|
@@ -166,10 +204,11 @@ function defineSchema(input) {
|
|
|
166
204
|
columns,
|
|
167
205
|
...(pk && pk.length > 0 ? { primaryKey: pk } : {}),
|
|
168
206
|
...(m2m && m2m.length > 0 ? { manyToMany: m2m } : {}),
|
|
207
|
+
...(checks && checks.length > 0 ? { checks } : {}),
|
|
169
208
|
};
|
|
170
209
|
}
|
|
171
210
|
}
|
|
172
|
-
return { tables };
|
|
211
|
+
return { tables, ...(options?.enums ? { enums: options.enums } : {}) };
|
|
173
212
|
}
|
|
174
213
|
/**
|
|
175
214
|
* Local copy of camelToSnake to avoid a circular import dependency at the
|
|
@@ -193,6 +232,12 @@ class ColumnBuilder {
|
|
|
193
232
|
defaultValue: null,
|
|
194
233
|
referencesTarget: null,
|
|
195
234
|
maxLength: null,
|
|
235
|
+
onDelete: null,
|
|
236
|
+
onUpdate: null,
|
|
237
|
+
enumName: null,
|
|
238
|
+
vectorDimensions: null,
|
|
239
|
+
isArray: false,
|
|
240
|
+
check: null,
|
|
196
241
|
};
|
|
197
242
|
}
|
|
198
243
|
serial() {
|
|
@@ -289,8 +334,18 @@ class ColumnBuilder {
|
|
|
289
334
|
this._config.defaultValue = val;
|
|
290
335
|
return this;
|
|
291
336
|
}
|
|
292
|
-
references(target) {
|
|
337
|
+
references(target, opts) {
|
|
293
338
|
this._config.referencesTarget = target;
|
|
339
|
+
this._config.onDelete = opts?.onDelete ?? null;
|
|
340
|
+
this._config.onUpdate = opts?.onUpdate ?? null;
|
|
341
|
+
return this;
|
|
342
|
+
}
|
|
343
|
+
check(expression) {
|
|
344
|
+
this._config.check = expression;
|
|
345
|
+
return this;
|
|
346
|
+
}
|
|
347
|
+
array() {
|
|
348
|
+
this._config.isArray = true;
|
|
294
349
|
return this;
|
|
295
350
|
}
|
|
296
351
|
build() {
|