@zenstackhq/runtime 3.0.0-alpha.9 → 3.0.0-beta.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/dist/{contract-BiU0iYAh.d.cts → contract-CusA0mQO.d.cts} +388 -297
- package/dist/{contract-BiU0iYAh.d.ts → contract-CusA0mQO.d.ts} +388 -297
- package/dist/helpers.cjs +31 -0
- package/dist/helpers.cjs.map +1 -0
- package/dist/helpers.d.cts +1 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +6 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.cjs +2197 -1203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -5
- package/dist/index.d.ts +4 -5
- package/dist/index.js +2120 -1127
- package/dist/index.js.map +1 -1
- package/dist/plugins/{policy.cjs → policy/index.cjs} +565 -272
- package/dist/plugins/policy/index.cjs.map +1 -0
- package/dist/plugins/{policy.d.ts → policy/index.d.cts} +1 -1
- package/dist/plugins/{policy.d.cts → policy/index.d.ts} +1 -1
- package/dist/plugins/{policy.js → policy/index.js} +568 -275
- package/dist/plugins/policy/index.js.map +1 -0
- package/dist/plugins/policy/plugin.zmodel +33 -0
- package/dist/schema.cjs +4 -1
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +1 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +4 -1
- package/dist/schema.js.map +1 -1
- package/package.json +28 -14
- package/dist/plugins/policy.cjs.map +0 -1
- package/dist/plugins/policy.js.map +0 -1
|
@@ -16,15 +16,115 @@ var RejectedByPolicyError = class extends Error {
|
|
|
16
16
|
// src/plugins/policy/policy-handler.ts
|
|
17
17
|
import { invariant as invariant6 } from "@zenstackhq/common-helpers";
|
|
18
18
|
import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode2, DeleteQueryNode, FromNode as FromNode2, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, PrimitiveValueListNode, RawNode, ReturningNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, TableNode as TableNode3, UpdateQueryNode, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
|
|
19
|
-
import { match as
|
|
19
|
+
import { match as match8 } from "ts-pattern";
|
|
20
20
|
|
|
21
21
|
// src/client/crud/dialects/index.ts
|
|
22
|
-
import { match as
|
|
22
|
+
import { match as match5 } from "ts-pattern";
|
|
23
23
|
|
|
24
24
|
// src/client/crud/dialects/postgresql.ts
|
|
25
25
|
import { invariant as invariant2 } from "@zenstackhq/common-helpers";
|
|
26
26
|
import { sql as sql2 } from "kysely";
|
|
27
|
-
import { match as
|
|
27
|
+
import { match as match3 } from "ts-pattern";
|
|
28
|
+
|
|
29
|
+
// src/client/constants.ts
|
|
30
|
+
var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
|
|
31
|
+
var LOGICAL_COMBINATORS = [
|
|
32
|
+
"AND",
|
|
33
|
+
"OR",
|
|
34
|
+
"NOT"
|
|
35
|
+
];
|
|
36
|
+
var AGGREGATE_OPERATORS = [
|
|
37
|
+
"_count",
|
|
38
|
+
"_sum",
|
|
39
|
+
"_avg",
|
|
40
|
+
"_min",
|
|
41
|
+
"_max"
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// src/client/query-utils.ts
|
|
45
|
+
import { match } from "ts-pattern";
|
|
46
|
+
|
|
47
|
+
// src/schema/expression.ts
|
|
48
|
+
var ExpressionUtils = {
|
|
49
|
+
literal: /* @__PURE__ */ __name((value) => {
|
|
50
|
+
return {
|
|
51
|
+
kind: "literal",
|
|
52
|
+
value
|
|
53
|
+
};
|
|
54
|
+
}, "literal"),
|
|
55
|
+
array: /* @__PURE__ */ __name((items) => {
|
|
56
|
+
return {
|
|
57
|
+
kind: "array",
|
|
58
|
+
items
|
|
59
|
+
};
|
|
60
|
+
}, "array"),
|
|
61
|
+
call: /* @__PURE__ */ __name((functionName, args) => {
|
|
62
|
+
return {
|
|
63
|
+
kind: "call",
|
|
64
|
+
function: functionName,
|
|
65
|
+
args
|
|
66
|
+
};
|
|
67
|
+
}, "call"),
|
|
68
|
+
binary: /* @__PURE__ */ __name((left, op, right) => {
|
|
69
|
+
return {
|
|
70
|
+
kind: "binary",
|
|
71
|
+
op,
|
|
72
|
+
left,
|
|
73
|
+
right
|
|
74
|
+
};
|
|
75
|
+
}, "binary"),
|
|
76
|
+
unary: /* @__PURE__ */ __name((op, operand) => {
|
|
77
|
+
return {
|
|
78
|
+
kind: "unary",
|
|
79
|
+
op,
|
|
80
|
+
operand
|
|
81
|
+
};
|
|
82
|
+
}, "unary"),
|
|
83
|
+
field: /* @__PURE__ */ __name((field) => {
|
|
84
|
+
return {
|
|
85
|
+
kind: "field",
|
|
86
|
+
field
|
|
87
|
+
};
|
|
88
|
+
}, "field"),
|
|
89
|
+
member: /* @__PURE__ */ __name((receiver, members) => {
|
|
90
|
+
return {
|
|
91
|
+
kind: "member",
|
|
92
|
+
receiver,
|
|
93
|
+
members
|
|
94
|
+
};
|
|
95
|
+
}, "member"),
|
|
96
|
+
_this: /* @__PURE__ */ __name(() => {
|
|
97
|
+
return {
|
|
98
|
+
kind: "this"
|
|
99
|
+
};
|
|
100
|
+
}, "_this"),
|
|
101
|
+
_null: /* @__PURE__ */ __name(() => {
|
|
102
|
+
return {
|
|
103
|
+
kind: "null"
|
|
104
|
+
};
|
|
105
|
+
}, "_null"),
|
|
106
|
+
and: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
107
|
+
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "&&", exp), expr2);
|
|
108
|
+
}, "and"),
|
|
109
|
+
or: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
110
|
+
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "||", exp), expr2);
|
|
111
|
+
}, "or"),
|
|
112
|
+
is: /* @__PURE__ */ __name((value, kind) => {
|
|
113
|
+
return !!value && typeof value === "object" && "kind" in value && value.kind === kind;
|
|
114
|
+
}, "is"),
|
|
115
|
+
isLiteral: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "literal"), "isLiteral"),
|
|
116
|
+
isArray: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "array"), "isArray"),
|
|
117
|
+
isCall: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "call"), "isCall"),
|
|
118
|
+
isNull: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "null"), "isNull"),
|
|
119
|
+
isThis: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "this"), "isThis"),
|
|
120
|
+
isUnary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "unary"), "isUnary"),
|
|
121
|
+
isBinary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "binary"), "isBinary"),
|
|
122
|
+
isField: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "field"), "isField"),
|
|
123
|
+
isMember: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "member"), "isMember"),
|
|
124
|
+
getLiteralValue: /* @__PURE__ */ __name((expr2) => {
|
|
125
|
+
return ExpressionUtils.isLiteral(expr2) ? expr2.value : void 0;
|
|
126
|
+
}, "getLiteralValue")
|
|
127
|
+
};
|
|
28
128
|
|
|
29
129
|
// src/client/errors.ts
|
|
30
130
|
var QueryError = class extends Error {
|
|
@@ -41,9 +141,6 @@ var InternalError = class extends Error {
|
|
|
41
141
|
static {
|
|
42
142
|
__name(this, "InternalError");
|
|
43
143
|
}
|
|
44
|
-
constructor(message) {
|
|
45
|
-
super(message);
|
|
46
|
-
}
|
|
47
144
|
};
|
|
48
145
|
|
|
49
146
|
// src/client/query-utils.ts
|
|
@@ -118,10 +215,15 @@ function getRelationForeignKeyFieldPairs(schema, model, relationField) {
|
|
|
118
215
|
}
|
|
119
216
|
__name(getRelationForeignKeyFieldPairs, "getRelationForeignKeyFieldPairs");
|
|
120
217
|
function isRelationField(schema, model, field) {
|
|
121
|
-
const fieldDef =
|
|
122
|
-
return !!fieldDef
|
|
218
|
+
const fieldDef = getField(schema, model, field);
|
|
219
|
+
return !!fieldDef?.relation;
|
|
123
220
|
}
|
|
124
221
|
__name(isRelationField, "isRelationField");
|
|
222
|
+
function isInheritedField(schema, model, field) {
|
|
223
|
+
const fieldDef = getField(schema, model, field);
|
|
224
|
+
return !!fieldDef?.originModel;
|
|
225
|
+
}
|
|
226
|
+
__name(isInheritedField, "isInheritedField");
|
|
125
227
|
function getUniqueFields(schema, model) {
|
|
126
228
|
const modelDef = requireModel(schema, model);
|
|
127
229
|
const result = [];
|
|
@@ -147,11 +249,14 @@ function getUniqueFields(schema, model) {
|
|
|
147
249
|
return result;
|
|
148
250
|
}
|
|
149
251
|
__name(getUniqueFields, "getUniqueFields");
|
|
150
|
-
function buildFieldRef(schema, model, field, options, eb, modelAlias) {
|
|
252
|
+
function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComputedField = true) {
|
|
151
253
|
const fieldDef = requireField(schema, model, field);
|
|
152
254
|
if (!fieldDef.computed) {
|
|
153
255
|
return eb.ref(modelAlias ? `${modelAlias}.${field}` : field);
|
|
154
256
|
} else {
|
|
257
|
+
if (!inlineComputedField) {
|
|
258
|
+
return eb.ref(modelAlias ? `${modelAlias}.${field}` : field);
|
|
259
|
+
}
|
|
155
260
|
let computer;
|
|
156
261
|
if ("computedFields" in options) {
|
|
157
262
|
const computedFields = options.computedFields;
|
|
@@ -160,7 +265,9 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias) {
|
|
|
160
265
|
if (!computer) {
|
|
161
266
|
throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
|
|
162
267
|
}
|
|
163
|
-
return computer(eb
|
|
268
|
+
return computer(eb, {
|
|
269
|
+
currentModel: modelAlias
|
|
270
|
+
});
|
|
164
271
|
}
|
|
165
272
|
}
|
|
166
273
|
__name(buildFieldRef, "buildFieldRef");
|
|
@@ -203,11 +310,33 @@ function getManyToManyRelation(schema, model, field) {
|
|
|
203
310
|
model,
|
|
204
311
|
fieldDef.type
|
|
205
312
|
].sort();
|
|
313
|
+
let orderedFK;
|
|
314
|
+
if (model !== fieldDef.type) {
|
|
315
|
+
orderedFK = sortedModelNames[0] === model ? [
|
|
316
|
+
"A",
|
|
317
|
+
"B"
|
|
318
|
+
] : [
|
|
319
|
+
"B",
|
|
320
|
+
"A"
|
|
321
|
+
];
|
|
322
|
+
} else {
|
|
323
|
+
const sortedFieldNames = [
|
|
324
|
+
field,
|
|
325
|
+
oppositeFieldDef.name
|
|
326
|
+
].sort();
|
|
327
|
+
orderedFK = sortedFieldNames[0] === field ? [
|
|
328
|
+
"A",
|
|
329
|
+
"B"
|
|
330
|
+
] : [
|
|
331
|
+
"B",
|
|
332
|
+
"A"
|
|
333
|
+
];
|
|
334
|
+
}
|
|
206
335
|
return {
|
|
207
|
-
parentFkName:
|
|
336
|
+
parentFkName: orderedFK[0],
|
|
208
337
|
otherModel: fieldDef.type,
|
|
209
338
|
otherField: fieldDef.relation.opposite,
|
|
210
|
-
otherFkName:
|
|
339
|
+
otherFkName: orderedFK[1],
|
|
211
340
|
joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
|
|
212
341
|
};
|
|
213
342
|
} else {
|
|
@@ -235,11 +364,38 @@ function flattenCompoundUniqueFilters(schema, model, filter) {
|
|
|
235
364
|
return result;
|
|
236
365
|
}
|
|
237
366
|
__name(flattenCompoundUniqueFilters, "flattenCompoundUniqueFilters");
|
|
367
|
+
function ensureArray(value) {
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
return value;
|
|
370
|
+
} else {
|
|
371
|
+
return [
|
|
372
|
+
value
|
|
373
|
+
];
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
__name(ensureArray, "ensureArray");
|
|
377
|
+
function getDelegateDescendantModels(schema, model, collected = /* @__PURE__ */ new Set()) {
|
|
378
|
+
const subModels = Object.values(schema.models).filter((m) => m.baseModel === model);
|
|
379
|
+
subModels.forEach((def) => {
|
|
380
|
+
if (!collected.has(def)) {
|
|
381
|
+
collected.add(def);
|
|
382
|
+
getDelegateDescendantModels(schema, def.name, collected);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
return [
|
|
386
|
+
...collected
|
|
387
|
+
];
|
|
388
|
+
}
|
|
389
|
+
__name(getDelegateDescendantModels, "getDelegateDescendantModels");
|
|
390
|
+
function aggregate(eb, expr2, op) {
|
|
391
|
+
return match(op).with("_count", () => eb.fn.count(expr2)).with("_sum", () => eb.fn.sum(expr2)).with("_avg", () => eb.fn.avg(expr2)).with("_min", () => eb.fn.min(expr2)).with("_max", () => eb.fn.max(expr2)).exhaustive();
|
|
392
|
+
}
|
|
393
|
+
__name(aggregate, "aggregate");
|
|
238
394
|
|
|
239
395
|
// src/client/crud/dialects/base.ts
|
|
240
396
|
import { invariant, isPlainObject } from "@zenstackhq/common-helpers";
|
|
241
|
-
import { sql } from "kysely";
|
|
242
|
-
import { match, P } from "ts-pattern";
|
|
397
|
+
import { expressionBuilder, sql } from "kysely";
|
|
398
|
+
import { match as match2, P } from "ts-pattern";
|
|
243
399
|
|
|
244
400
|
// src/utils/enumerate.ts
|
|
245
401
|
function enumerate(x) {
|
|
@@ -269,6 +425,44 @@ var BaseCrudDialect = class {
|
|
|
269
425
|
transformPrimitive(value, _type, _forArrayField) {
|
|
270
426
|
return value;
|
|
271
427
|
}
|
|
428
|
+
// #region common query builders
|
|
429
|
+
buildSelectModel(eb, model, modelAlias) {
|
|
430
|
+
const modelDef = requireModel(this.schema, model);
|
|
431
|
+
let result = eb.selectFrom(model === modelAlias ? model : `${model} as ${modelAlias}`);
|
|
432
|
+
let joinBase = modelDef.baseModel;
|
|
433
|
+
while (joinBase) {
|
|
434
|
+
result = this.buildDelegateJoin(model, modelAlias, joinBase, result);
|
|
435
|
+
joinBase = requireModel(this.schema, joinBase).baseModel;
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
buildFilterSortTake(model, args, query, modelAlias) {
|
|
440
|
+
let result = query;
|
|
441
|
+
if (args.where) {
|
|
442
|
+
result = result.where((eb) => this.buildFilter(eb, model, modelAlias, args?.where));
|
|
443
|
+
}
|
|
444
|
+
let negateOrderBy = false;
|
|
445
|
+
const skip = args.skip;
|
|
446
|
+
let take = args.take;
|
|
447
|
+
if (take !== void 0 && take < 0) {
|
|
448
|
+
negateOrderBy = true;
|
|
449
|
+
take = -take;
|
|
450
|
+
}
|
|
451
|
+
result = this.buildSkipTake(result, skip, take);
|
|
452
|
+
result = this.buildOrderBy(result, model, modelAlias, args.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
453
|
+
if ("distinct" in args && args.distinct) {
|
|
454
|
+
const distinct = ensureArray(args.distinct);
|
|
455
|
+
if (this.supportsDistinctOn) {
|
|
456
|
+
result = result.distinctOn(distinct.map((f) => sql.ref(`${modelAlias}.${f}`)));
|
|
457
|
+
} else {
|
|
458
|
+
throw new QueryError(`"distinct" is not supported by "${this.schema.provider.type}" provider`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (args.cursor) {
|
|
462
|
+
result = this.buildCursorFilter(model, result, args.cursor, args.orderBy, negateOrderBy, modelAlias);
|
|
463
|
+
}
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
272
466
|
buildFilter(eb, model, modelAlias, where) {
|
|
273
467
|
if (where === true || where === void 0) {
|
|
274
468
|
return this.true(eb);
|
|
@@ -285,17 +479,20 @@ var BaseCrudDialect = class {
|
|
|
285
479
|
if (key.startsWith("$")) {
|
|
286
480
|
continue;
|
|
287
481
|
}
|
|
288
|
-
if (key
|
|
482
|
+
if (this.isLogicalCombinator(key)) {
|
|
289
483
|
result = this.and(eb, result, this.buildCompositeFilter(eb, model, modelAlias, key, payload));
|
|
290
484
|
continue;
|
|
291
485
|
}
|
|
292
486
|
const fieldDef = requireField(this.schema, model, key);
|
|
293
487
|
if (fieldDef.relation) {
|
|
294
488
|
result = this.and(eb, result, this.buildRelationFilter(eb, model, modelAlias, key, fieldDef, payload));
|
|
295
|
-
} else if (fieldDef.array) {
|
|
296
|
-
result = this.and(eb, result, this.buildArrayFilter(eb, model, modelAlias, key, fieldDef, payload));
|
|
297
489
|
} else {
|
|
298
|
-
|
|
490
|
+
const fieldRef = this.fieldRef(fieldDef.originModel ?? model, key, eb, fieldDef.originModel ?? modelAlias);
|
|
491
|
+
if (fieldDef.array) {
|
|
492
|
+
result = this.and(eb, result, this.buildArrayFilter(eb, fieldRef, fieldDef, payload));
|
|
493
|
+
} else {
|
|
494
|
+
result = this.and(eb, result, this.buildPrimitiveFilter(eb, fieldRef, fieldDef, payload));
|
|
495
|
+
}
|
|
299
496
|
}
|
|
300
497
|
}
|
|
301
498
|
if ("$expr" in _where && typeof _where["$expr"] === "function") {
|
|
@@ -303,8 +500,32 @@ var BaseCrudDialect = class {
|
|
|
303
500
|
}
|
|
304
501
|
return result;
|
|
305
502
|
}
|
|
503
|
+
buildCursorFilter(model, query, cursor, orderBy, negateOrderBy, modelAlias) {
|
|
504
|
+
const _orderBy = orderBy ?? makeDefaultOrderBy(this.schema, model);
|
|
505
|
+
const orderByItems = ensureArray(_orderBy).flatMap((obj) => Object.entries(obj));
|
|
506
|
+
const eb = expressionBuilder();
|
|
507
|
+
const subQueryAlias = `${model}$cursor$sub`;
|
|
508
|
+
const cursorFilter = this.buildFilter(eb, model, subQueryAlias, cursor);
|
|
509
|
+
let result = query;
|
|
510
|
+
const filters = [];
|
|
511
|
+
for (let i = orderByItems.length - 1; i >= 0; i--) {
|
|
512
|
+
const andFilters = [];
|
|
513
|
+
for (let j = 0; j <= i; j++) {
|
|
514
|
+
const [field, order] = orderByItems[j];
|
|
515
|
+
const _order = negateOrderBy ? order === "asc" ? "desc" : "asc" : order;
|
|
516
|
+
const op = j === i ? _order === "asc" ? ">=" : "<=" : "=";
|
|
517
|
+
andFilters.push(eb(eb.ref(`${modelAlias}.${field}`), op, this.buildSelectModel(eb, model, subQueryAlias).select(`${subQueryAlias}.${field}`).where(cursorFilter)));
|
|
518
|
+
}
|
|
519
|
+
filters.push(eb.and(andFilters));
|
|
520
|
+
}
|
|
521
|
+
result = result.where((eb2) => eb2.or(filters));
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
524
|
+
isLogicalCombinator(key) {
|
|
525
|
+
return LOGICAL_COMBINATORS.includes(key);
|
|
526
|
+
}
|
|
306
527
|
buildCompositeFilter(eb, model, modelAlias, key, payload) {
|
|
307
|
-
return
|
|
528
|
+
return match2(key).with("AND", () => this.and(eb, ...enumerate(payload).map((subPayload) => this.buildFilter(eb, model, modelAlias, subPayload)))).with("OR", () => this.or(eb, ...enumerate(payload).map((subPayload) => this.buildFilter(eb, model, modelAlias, subPayload)))).with("NOT", () => eb.not(this.buildCompositeFilter(eb, model, modelAlias, "AND", payload))).exhaustive();
|
|
308
529
|
}
|
|
309
530
|
buildRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
310
531
|
if (!fieldDef.array) {
|
|
@@ -313,19 +534,26 @@ var BaseCrudDialect = class {
|
|
|
313
534
|
return this.buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload);
|
|
314
535
|
}
|
|
315
536
|
}
|
|
316
|
-
buildToOneRelationFilter(eb, model,
|
|
537
|
+
buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
317
538
|
if (payload === null) {
|
|
318
539
|
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
319
|
-
if (ownedByModel) {
|
|
320
|
-
return this.and(eb, ...keyPairs.map(({ fk }) => eb(sql.ref(`${
|
|
540
|
+
if (ownedByModel && !fieldDef.originModel) {
|
|
541
|
+
return this.and(eb, ...keyPairs.map(({ fk }) => eb(sql.ref(`${modelAlias}.${fk}`), "is", null)));
|
|
321
542
|
} else {
|
|
322
|
-
return this.buildToOneRelationFilter(eb, model,
|
|
543
|
+
return this.buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, {
|
|
323
544
|
is: null
|
|
324
545
|
});
|
|
325
546
|
}
|
|
326
547
|
}
|
|
327
|
-
const joinAlias = `${
|
|
328
|
-
const joinPairs = buildJoinPairs(
|
|
548
|
+
const joinAlias = `${modelAlias}$${field}`;
|
|
549
|
+
const joinPairs = buildJoinPairs(
|
|
550
|
+
this.schema,
|
|
551
|
+
model,
|
|
552
|
+
// if field is from a base, use the base model to join
|
|
553
|
+
fieldDef.originModel ?? modelAlias,
|
|
554
|
+
field,
|
|
555
|
+
joinAlias
|
|
556
|
+
);
|
|
329
557
|
const filterResultField = `${field}$filter`;
|
|
330
558
|
const joinSelect = eb.selectFrom(`${fieldDef.type} as ${joinAlias}`).where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(sql.ref(left), "=", sql.ref(right))))).select(() => eb.fn.count(eb.lit(1)).as(filterResultField));
|
|
331
559
|
const conditions = [];
|
|
@@ -355,25 +583,26 @@ var BaseCrudDialect = class {
|
|
|
355
583
|
}
|
|
356
584
|
return this.and(eb, ...conditions);
|
|
357
585
|
}
|
|
358
|
-
buildToManyRelationFilter(eb, model,
|
|
586
|
+
buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
359
587
|
if (payload === null) {
|
|
360
|
-
return eb(sql.ref(`${
|
|
588
|
+
return eb(sql.ref(`${modelAlias}.${field}`), "is", null);
|
|
361
589
|
}
|
|
362
590
|
const relationModel = fieldDef.type;
|
|
591
|
+
const relationFilterSelectAlias = `${modelAlias}$${field}$filter`;
|
|
363
592
|
const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
|
|
364
593
|
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
365
594
|
if (m2m) {
|
|
366
595
|
const modelIdField = getIdFields(this.schema, model)[0];
|
|
367
596
|
const relationIdField = getIdFields(this.schema, relationModel)[0];
|
|
368
|
-
return eb2(sql.ref(`${
|
|
597
|
+
return eb2(sql.ref(`${relationFilterSelectAlias}.${relationIdField}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", sql.ref(`${modelAlias}.${modelIdField}`)));
|
|
369
598
|
} else {
|
|
370
599
|
const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
371
600
|
let result2 = this.true(eb2);
|
|
372
601
|
for (const { fk, pk } of relationKeyPairs.keyPairs) {
|
|
373
602
|
if (relationKeyPairs.ownedByModel) {
|
|
374
|
-
result2 = this.and(eb2, result2, eb2(sql.ref(`${
|
|
603
|
+
result2 = this.and(eb2, result2, eb2(sql.ref(`${modelAlias}.${fk}`), "=", sql.ref(`${relationFilterSelectAlias}.${pk}`)));
|
|
375
604
|
} else {
|
|
376
|
-
result2 = this.and(eb2, result2, eb2(sql.ref(`${
|
|
605
|
+
result2 = this.and(eb2, result2, eb2(sql.ref(`${modelAlias}.${pk}`), "=", sql.ref(`${relationFilterSelectAlias}.${fk}`)));
|
|
377
606
|
}
|
|
378
607
|
}
|
|
379
608
|
return result2;
|
|
@@ -386,25 +615,24 @@ var BaseCrudDialect = class {
|
|
|
386
615
|
}
|
|
387
616
|
switch (key) {
|
|
388
617
|
case "some": {
|
|
389
|
-
result = this.and(eb, result, eb(
|
|
618
|
+
result = this.and(eb, result, eb(this.buildSelectModel(eb, relationModel, relationFilterSelectAlias).select((eb1) => eb1.fn.count(eb1.lit(1)).as("$count")).where(buildPkFkWhereRefs(eb)).where((eb1) => this.buildFilter(eb1, relationModel, relationFilterSelectAlias, subPayload)), ">", 0));
|
|
390
619
|
break;
|
|
391
620
|
}
|
|
392
621
|
case "every": {
|
|
393
|
-
result = this.and(eb, result, eb(
|
|
622
|
+
result = this.and(eb, result, eb(this.buildSelectModel(eb, relationModel, relationFilterSelectAlias).select((eb1) => eb1.fn.count(eb1.lit(1)).as("$count")).where(buildPkFkWhereRefs(eb)).where((eb1) => eb1.not(this.buildFilter(eb1, relationModel, relationFilterSelectAlias, subPayload))), "=", 0));
|
|
394
623
|
break;
|
|
395
624
|
}
|
|
396
625
|
case "none": {
|
|
397
|
-
result = this.and(eb, result, eb(
|
|
626
|
+
result = this.and(eb, result, eb(this.buildSelectModel(eb, relationModel, relationFilterSelectAlias).select((eb1) => eb1.fn.count(eb1.lit(1)).as("$count")).where(buildPkFkWhereRefs(eb)).where((eb1) => this.buildFilter(eb1, relationModel, relationFilterSelectAlias, subPayload)), "=", 0));
|
|
398
627
|
break;
|
|
399
628
|
}
|
|
400
629
|
}
|
|
401
630
|
}
|
|
402
631
|
return result;
|
|
403
632
|
}
|
|
404
|
-
buildArrayFilter(eb,
|
|
633
|
+
buildArrayFilter(eb, fieldRef, fieldDef, payload) {
|
|
405
634
|
const clauses = [];
|
|
406
635
|
const fieldType = fieldDef.type;
|
|
407
|
-
const fieldRef = buildFieldRef(this.schema, model, field, this.options, eb, modelAlias);
|
|
408
636
|
for (const [key, _value] of Object.entries(payload)) {
|
|
409
637
|
if (_value === void 0) {
|
|
410
638
|
continue;
|
|
@@ -440,14 +668,14 @@ var BaseCrudDialect = class {
|
|
|
440
668
|
}
|
|
441
669
|
return this.and(eb, ...clauses);
|
|
442
670
|
}
|
|
443
|
-
buildPrimitiveFilter(eb,
|
|
671
|
+
buildPrimitiveFilter(eb, fieldRef, fieldDef, payload) {
|
|
444
672
|
if (payload === null) {
|
|
445
|
-
return eb(
|
|
673
|
+
return eb(fieldRef, "is", null);
|
|
446
674
|
}
|
|
447
675
|
if (isEnum(this.schema, fieldDef.type)) {
|
|
448
|
-
return this.buildEnumFilter(eb,
|
|
676
|
+
return this.buildEnumFilter(eb, fieldRef, fieldDef, payload);
|
|
449
677
|
}
|
|
450
|
-
return
|
|
678
|
+
return match2(fieldDef.type).with("String", () => this.buildStringFilter(eb, fieldRef, payload)).with(P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(eb, fieldRef, type, payload)).with("Boolean", () => this.buildBooleanFilter(eb, fieldRef, payload)).with("DateTime", () => this.buildDateTimeFilter(eb, fieldRef, payload)).with("Bytes", () => this.buildBytesFilter(eb, fieldRef, payload)).with("Json", () => {
|
|
451
679
|
throw new InternalError("JSON filters are not supported yet");
|
|
452
680
|
}).with("Unsupported", () => {
|
|
453
681
|
throw new QueryError(`Unsupported field cannot be used in filters`);
|
|
@@ -456,7 +684,7 @@ var BaseCrudDialect = class {
|
|
|
456
684
|
buildLiteralFilter(eb, lhs, type, rhs) {
|
|
457
685
|
return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
|
|
458
686
|
}
|
|
459
|
-
buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
|
|
687
|
+
buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
|
|
460
688
|
if (payload === null || !isPlainObject(payload)) {
|
|
461
689
|
return {
|
|
462
690
|
conditions: [
|
|
@@ -471,8 +699,11 @@ var BaseCrudDialect = class {
|
|
|
471
699
|
if (onlyForKeys && !onlyForKeys.includes(op)) {
|
|
472
700
|
continue;
|
|
473
701
|
}
|
|
702
|
+
if (excludeKeys.includes(op)) {
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
474
705
|
const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
|
|
475
|
-
const condition =
|
|
706
|
+
const condition = match2(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
|
|
476
707
|
invariant(Array.isArray(rhs), "right hand side must be an array");
|
|
477
708
|
if (rhs.length === 0) {
|
|
478
709
|
return this.false(eb);
|
|
@@ -486,7 +717,11 @@ var BaseCrudDialect = class {
|
|
|
486
717
|
} else {
|
|
487
718
|
return eb.not(eb(lhs, "in", rhs));
|
|
488
719
|
}
|
|
489
|
-
}).with("lt", () => eb(lhs, "<", rhs)).with("lte", () => eb(lhs, "<=", rhs)).with("gt", () => eb(lhs, ">", rhs)).with("gte", () => eb(lhs, ">=", rhs)).with("not", () => eb.not(recurse(value))).
|
|
720
|
+
}).with("lt", () => eb(lhs, "<", rhs)).with("lte", () => eb(lhs, "<=", rhs)).with("gt", () => eb(lhs, ">", rhs)).with("gte", () => eb(lhs, ">=", rhs)).with("not", () => eb.not(recurse(value))).with(P.union(...AGGREGATE_OPERATORS), (op2) => {
|
|
721
|
+
const innerResult = this.buildStandardFilter(eb, type, value, aggregate(eb, lhs, op2), getRhs, recurse, throwIfInvalid);
|
|
722
|
+
consumedKeys.push(...innerResult.consumedKeys);
|
|
723
|
+
return this.and(eb, ...innerResult.conditions);
|
|
724
|
+
}).otherwise(() => {
|
|
490
725
|
if (throwIfInvalid) {
|
|
491
726
|
throw new QueryError(`Invalid filter key: ${op}`);
|
|
492
727
|
} else {
|
|
@@ -503,24 +738,21 @@ var BaseCrudDialect = class {
|
|
|
503
738
|
consumedKeys
|
|
504
739
|
};
|
|
505
740
|
}
|
|
506
|
-
buildStringFilter(eb,
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if (payload && typeof payload === "object" && "mode" in payload && payload.mode === "insensitive") {
|
|
511
|
-
insensitive = true;
|
|
512
|
-
fieldRef = eb.fn("lower", [
|
|
513
|
-
fieldRef
|
|
514
|
-
]);
|
|
741
|
+
buildStringFilter(eb, fieldRef, payload) {
|
|
742
|
+
let mode;
|
|
743
|
+
if (payload && typeof payload === "object" && "mode" in payload) {
|
|
744
|
+
mode = payload.mode;
|
|
515
745
|
}
|
|
516
|
-
const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload,
|
|
746
|
+
const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload, mode === "insensitive" ? eb.fn("lower", [
|
|
747
|
+
fieldRef
|
|
748
|
+
]) : fieldRef, (value) => this.prepStringCasing(eb, value, mode), (value) => this.buildStringFilter(eb, fieldRef, value));
|
|
517
749
|
if (payload && typeof payload === "object") {
|
|
518
750
|
for (const [key, value] of Object.entries(payload)) {
|
|
519
751
|
if (key === "mode" || consumedKeys.includes(key)) {
|
|
520
752
|
continue;
|
|
521
753
|
}
|
|
522
|
-
const condition =
|
|
523
|
-
throw new
|
|
754
|
+
const condition = match2(key).with("contains", () => mode === "insensitive" ? eb(fieldRef, "ilike", sql.val(`%${value}%`)) : eb(fieldRef, "like", sql.val(`%${value}%`))).with("startsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", sql.val(`${value}%`)) : eb(fieldRef, "like", sql.val(`${value}%`))).with("endsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", sql.val(`%${value}`)) : eb(fieldRef, "like", sql.val(`%${value}`))).otherwise(() => {
|
|
755
|
+
throw new QueryError(`Invalid string filter key: ${key}`);
|
|
524
756
|
});
|
|
525
757
|
if (condition) {
|
|
526
758
|
conditions.push(condition);
|
|
@@ -529,34 +761,37 @@ var BaseCrudDialect = class {
|
|
|
529
761
|
}
|
|
530
762
|
return this.and(eb, ...conditions);
|
|
531
763
|
}
|
|
532
|
-
prepStringCasing(eb, value,
|
|
764
|
+
prepStringCasing(eb, value, mode) {
|
|
765
|
+
if (!mode || mode === "default") {
|
|
766
|
+
return value === null ? value : sql.val(value);
|
|
767
|
+
}
|
|
533
768
|
if (typeof value === "string") {
|
|
534
|
-
return
|
|
535
|
-
sql.
|
|
536
|
-
])
|
|
769
|
+
return eb.fn("lower", [
|
|
770
|
+
sql.val(value)
|
|
771
|
+
]);
|
|
537
772
|
} else if (Array.isArray(value)) {
|
|
538
|
-
return value.map((v) => this.prepStringCasing(eb, v,
|
|
773
|
+
return value.map((v) => this.prepStringCasing(eb, v, mode));
|
|
539
774
|
} else {
|
|
540
|
-
return value === null ? null : sql.
|
|
775
|
+
return value === null ? null : sql.val(value);
|
|
541
776
|
}
|
|
542
777
|
}
|
|
543
|
-
buildNumberFilter(eb,
|
|
544
|
-
const { conditions } = this.buildStandardFilter(eb, type, payload,
|
|
778
|
+
buildNumberFilter(eb, fieldRef, type, payload) {
|
|
779
|
+
const { conditions } = this.buildStandardFilter(eb, type, payload, fieldRef, (value) => this.transformPrimitive(value, type, false), (value) => this.buildNumberFilter(eb, fieldRef, type, value));
|
|
545
780
|
return this.and(eb, ...conditions);
|
|
546
781
|
}
|
|
547
|
-
buildBooleanFilter(eb,
|
|
548
|
-
const { conditions } = this.buildStandardFilter(eb, "Boolean", payload,
|
|
782
|
+
buildBooleanFilter(eb, fieldRef, payload) {
|
|
783
|
+
const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, fieldRef, (value) => this.transformPrimitive(value, "Boolean", false), (value) => this.buildBooleanFilter(eb, fieldRef, value), true, [
|
|
549
784
|
"equals",
|
|
550
785
|
"not"
|
|
551
786
|
]);
|
|
552
787
|
return this.and(eb, ...conditions);
|
|
553
788
|
}
|
|
554
|
-
buildDateTimeFilter(eb,
|
|
555
|
-
const { conditions } = this.buildStandardFilter(eb, "DateTime", payload,
|
|
789
|
+
buildDateTimeFilter(eb, fieldRef, payload) {
|
|
790
|
+
const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, fieldRef, (value) => this.transformPrimitive(value, "DateTime", false), (value) => this.buildDateTimeFilter(eb, fieldRef, value), true);
|
|
556
791
|
return this.and(eb, ...conditions);
|
|
557
792
|
}
|
|
558
|
-
buildBytesFilter(eb,
|
|
559
|
-
const conditions = this.buildStandardFilter(eb, "Bytes", payload,
|
|
793
|
+
buildBytesFilter(eb, fieldRef, payload) {
|
|
794
|
+
const conditions = this.buildStandardFilter(eb, "Bytes", payload, fieldRef, (value) => this.transformPrimitive(value, "Bytes", false), (value) => this.buildBytesFilter(eb, fieldRef, value), true, [
|
|
560
795
|
"equals",
|
|
561
796
|
"in",
|
|
562
797
|
"notIn",
|
|
@@ -564,8 +799,8 @@ var BaseCrudDialect = class {
|
|
|
564
799
|
]);
|
|
565
800
|
return this.and(eb, ...conditions.conditions);
|
|
566
801
|
}
|
|
567
|
-
buildEnumFilter(eb,
|
|
568
|
-
const conditions = this.buildStandardFilter(eb, "String", payload,
|
|
802
|
+
buildEnumFilter(eb, fieldRef, fieldDef, payload) {
|
|
803
|
+
const conditions = this.buildStandardFilter(eb, "String", payload, fieldRef, (value) => value, (value) => this.buildEnumFilter(eb, fieldRef, fieldDef, value), true, [
|
|
569
804
|
"equals",
|
|
570
805
|
"in",
|
|
571
806
|
"notIn",
|
|
@@ -597,9 +832,7 @@ var BaseCrudDialect = class {
|
|
|
597
832
|
invariant(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
598
833
|
for (const [k, v] of Object.entries(value)) {
|
|
599
834
|
invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
600
|
-
result = result.orderBy((eb) => eb.
|
|
601
|
-
sql.ref(k)
|
|
602
|
-
]), sql.raw(this.negateSort(v, negated)));
|
|
835
|
+
result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), sql.raw(this.negateSort(v, negated)));
|
|
603
836
|
}
|
|
604
837
|
continue;
|
|
605
838
|
}
|
|
@@ -608,7 +841,7 @@ var BaseCrudDialect = class {
|
|
|
608
841
|
invariant(value && typeof value === "object", 'invalid orderBy value for field "_count"');
|
|
609
842
|
for (const [k, v] of Object.entries(value)) {
|
|
610
843
|
invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
611
|
-
result = result.orderBy((eb) => eb.fn.count(
|
|
844
|
+
result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), sql.raw(this.negateSort(v, negated)));
|
|
612
845
|
}
|
|
613
846
|
continue;
|
|
614
847
|
}
|
|
@@ -617,10 +850,11 @@ var BaseCrudDialect = class {
|
|
|
617
850
|
}
|
|
618
851
|
const fieldDef = requireField(this.schema, model, field);
|
|
619
852
|
if (!fieldDef.relation) {
|
|
853
|
+
const fieldRef = this.fieldRef(model, field, expressionBuilder(), modelAlias);
|
|
620
854
|
if (value === "asc" || value === "desc") {
|
|
621
|
-
result = result.orderBy(
|
|
855
|
+
result = result.orderBy(fieldRef, this.negateSort(value, negated));
|
|
622
856
|
} else if (value && typeof value === "object" && "nulls" in value && "sort" in value && (value.sort === "asc" || value.sort === "desc") && (value.nulls === "first" || value.nulls === "last")) {
|
|
623
|
-
result = result.orderBy(
|
|
857
|
+
result = result.orderBy(fieldRef, sql.raw(`${this.negateSort(value.sort, negated)} nulls ${value.nulls}`));
|
|
624
858
|
}
|
|
625
859
|
} else {
|
|
626
860
|
const relationModel = fieldDef.type;
|
|
@@ -632,8 +866,9 @@ var BaseCrudDialect = class {
|
|
|
632
866
|
invariant(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
|
|
633
867
|
const sort = this.negateSort(value._count, negated);
|
|
634
868
|
result = result.orderBy((eb) => {
|
|
635
|
-
|
|
636
|
-
|
|
869
|
+
const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
|
|
870
|
+
let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
|
|
871
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
637
872
|
subQuery = subQuery.where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(sql.ref(left), "=", sql.ref(right)))));
|
|
638
873
|
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
639
874
|
return subQuery;
|
|
@@ -651,6 +886,92 @@ var BaseCrudDialect = class {
|
|
|
651
886
|
});
|
|
652
887
|
return result;
|
|
653
888
|
}
|
|
889
|
+
buildSelectAllFields(model, query, omit, modelAlias) {
|
|
890
|
+
const modelDef = requireModel(this.schema, model);
|
|
891
|
+
let result = query;
|
|
892
|
+
for (const field of Object.keys(modelDef.fields)) {
|
|
893
|
+
if (isRelationField(this.schema, model, field)) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
if (omit?.[field] === true) {
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
result = this.buildSelectField(result, model, modelAlias, field);
|
|
900
|
+
}
|
|
901
|
+
const descendants = getDelegateDescendantModels(this.schema, model);
|
|
902
|
+
for (const subModel of descendants) {
|
|
903
|
+
result = this.buildDelegateJoin(model, modelAlias, subModel.name, result);
|
|
904
|
+
result = result.select((eb) => {
|
|
905
|
+
const jsonObject = {};
|
|
906
|
+
for (const field of Object.keys(subModel.fields)) {
|
|
907
|
+
if (isRelationField(this.schema, subModel.name, field) || isInheritedField(this.schema, subModel.name, field)) {
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
jsonObject[field] = eb.ref(`${subModel.name}.${field}`);
|
|
911
|
+
}
|
|
912
|
+
return this.buildJsonObject(eb, jsonObject).as(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
return result;
|
|
916
|
+
}
|
|
917
|
+
buildModelSelect(eb, model, subQueryAlias, payload, selectAllFields) {
|
|
918
|
+
let subQuery = this.buildSelectModel(eb, model, subQueryAlias);
|
|
919
|
+
if (selectAllFields) {
|
|
920
|
+
subQuery = this.buildSelectAllFields(model, subQuery, typeof payload === "object" ? payload?.omit : void 0, subQueryAlias);
|
|
921
|
+
}
|
|
922
|
+
if (payload && typeof payload === "object") {
|
|
923
|
+
subQuery = this.buildFilterSortTake(model, payload, subQuery, subQueryAlias);
|
|
924
|
+
}
|
|
925
|
+
return subQuery;
|
|
926
|
+
}
|
|
927
|
+
buildSelectField(query, model, modelAlias, field) {
|
|
928
|
+
const fieldDef = requireField(this.schema, model, field);
|
|
929
|
+
if (fieldDef.computed) {
|
|
930
|
+
return query.select((eb) => this.fieldRef(model, field, eb, modelAlias).as(field));
|
|
931
|
+
} else if (!fieldDef.originModel) {
|
|
932
|
+
return query.select(sql.ref(`${modelAlias}.${field}`).as(field));
|
|
933
|
+
} else {
|
|
934
|
+
return this.buildSelectField(query, fieldDef.originModel, fieldDef.originModel, field);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
|
|
938
|
+
const idFields = getIdFields(this.schema, thisModel);
|
|
939
|
+
query = query.leftJoin(otherModelAlias, (qb) => {
|
|
940
|
+
for (const idField of idFields) {
|
|
941
|
+
qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
|
|
942
|
+
}
|
|
943
|
+
return qb;
|
|
944
|
+
});
|
|
945
|
+
return query;
|
|
946
|
+
}
|
|
947
|
+
buildCountJson(model, eb, parentAlias, payload) {
|
|
948
|
+
const modelDef = requireModel(this.schema, model);
|
|
949
|
+
const toManyRelations = Object.entries(modelDef.fields).filter(([, field]) => field.relation && field.array);
|
|
950
|
+
const selections = payload === true ? {
|
|
951
|
+
select: toManyRelations.reduce((acc, [field]) => {
|
|
952
|
+
acc[field] = true;
|
|
953
|
+
return acc;
|
|
954
|
+
}, {})
|
|
955
|
+
} : payload;
|
|
956
|
+
const jsonObject = {};
|
|
957
|
+
for (const [field, value] of Object.entries(selections.select)) {
|
|
958
|
+
const fieldDef = requireField(this.schema, model, field);
|
|
959
|
+
const fieldModel = fieldDef.type;
|
|
960
|
+
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
|
|
961
|
+
let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
|
|
962
|
+
for (const [left, right] of joinPairs) {
|
|
963
|
+
fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
|
|
964
|
+
}
|
|
965
|
+
if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
|
|
966
|
+
const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
|
|
967
|
+
fieldCountQuery = fieldCountQuery.where(filter);
|
|
968
|
+
}
|
|
969
|
+
jsonObject[field] = fieldCountQuery;
|
|
970
|
+
}
|
|
971
|
+
return this.buildJsonObject(eb, jsonObject);
|
|
972
|
+
}
|
|
973
|
+
// #endregion
|
|
974
|
+
// #region utils
|
|
654
975
|
negateSort(sort, negated) {
|
|
655
976
|
return negated ? sort === "asc" ? "desc" : "asc" : sort;
|
|
656
977
|
}
|
|
@@ -697,6 +1018,21 @@ var BaseCrudDialect = class {
|
|
|
697
1018
|
not(eb, ...args) {
|
|
698
1019
|
return eb.not(this.and(eb, ...args));
|
|
699
1020
|
}
|
|
1021
|
+
fieldRef(model, field, eb, modelAlias, inlineComputedField = true) {
|
|
1022
|
+
return buildFieldRef(this.schema, model, field, this.options, eb, modelAlias, inlineComputedField);
|
|
1023
|
+
}
|
|
1024
|
+
canJoinWithoutNestedSelect(modelDef, payload) {
|
|
1025
|
+
if (modelDef.computedFields) {
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
if (modelDef.baseModel || modelDef.isDelegate) {
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
if (typeof payload === "object" && (payload.orderBy || payload.skip !== void 0 || payload.take !== void 0 || payload.cursor || payload.distinct)) {
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
return true;
|
|
1035
|
+
}
|
|
700
1036
|
};
|
|
701
1037
|
|
|
702
1038
|
// src/client/crud/dialects/postgresql.ts
|
|
@@ -718,98 +1054,110 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
718
1054
|
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
719
1055
|
}
|
|
720
1056
|
} else {
|
|
721
|
-
return
|
|
1057
|
+
return match3(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
722
1058
|
}
|
|
723
1059
|
}
|
|
724
1060
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
725
|
-
const
|
|
726
|
-
|
|
1061
|
+
const relationResultName = `${parentAlias}$${relationField}`;
|
|
1062
|
+
const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload, relationResultName);
|
|
1063
|
+
return joinedQuery.select(`${relationResultName}.$data as ${relationField}`);
|
|
727
1064
|
}
|
|
728
|
-
buildRelationJSON(model, qb, relationField,
|
|
1065
|
+
buildRelationJSON(model, qb, relationField, parentAlias, payload, resultName) {
|
|
729
1066
|
const relationFieldDef = requireField(this.schema, model, relationField);
|
|
730
1067
|
const relationModel = relationFieldDef.type;
|
|
731
1068
|
return qb.leftJoinLateral((eb) => {
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
subQuery = this.buildOrderBy(subQuery, relationModel, relationModel, payload.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
749
|
-
}
|
|
750
|
-
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
751
|
-
if (m2m) {
|
|
752
|
-
const parentIds = getIdFields(this.schema, model);
|
|
753
|
-
const relationIds = getIdFields(this.schema, relationModel);
|
|
754
|
-
invariant2(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
755
|
-
invariant2(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
756
|
-
subQuery = subQuery.where(eb(eb.ref(`${relationModel}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentName}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
757
|
-
} else {
|
|
758
|
-
const joinPairs = buildJoinPairs(this.schema, model, parentName, relationField, relationModel);
|
|
759
|
-
subQuery = subQuery.where((eb2) => this.and(eb2, ...joinPairs.map(([left, right]) => eb2(sql2.ref(left), "=", sql2.ref(right)))));
|
|
760
|
-
}
|
|
761
|
-
return subQuery.as(joinTableName);
|
|
762
|
-
});
|
|
763
|
-
result = this.buildRelationObjectSelect(relationModel, relationField, relationFieldDef, result, payload, parentName);
|
|
764
|
-
result = this.buildRelationJoins(relationModel, relationField, result, payload, parentName);
|
|
765
|
-
return result.as(joinTableName);
|
|
1069
|
+
const relationSelectName = `${resultName}$sub`;
|
|
1070
|
+
const relationModelDef = requireModel(this.schema, relationModel);
|
|
1071
|
+
let tbl;
|
|
1072
|
+
if (this.canJoinWithoutNestedSelect(relationModelDef, payload)) {
|
|
1073
|
+
tbl = this.buildModelSelect(eb, relationModel, relationSelectName, payload, false);
|
|
1074
|
+
tbl = this.buildRelationJoinFilter(tbl, model, relationField, relationModel, relationSelectName, parentAlias);
|
|
1075
|
+
} else {
|
|
1076
|
+
tbl = eb.selectFrom(() => {
|
|
1077
|
+
let subQuery = this.buildModelSelect(eb, relationModel, `${relationSelectName}$t`, payload, true);
|
|
1078
|
+
subQuery = this.buildRelationJoinFilter(subQuery, model, relationField, relationModel, `${relationSelectName}$t`, parentAlias);
|
|
1079
|
+
return subQuery.as(relationSelectName);
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
tbl = this.buildRelationObjectSelect(relationModel, relationSelectName, relationFieldDef, tbl, payload, resultName);
|
|
1083
|
+
tbl = this.buildRelationJoins(tbl, relationModel, relationSelectName, payload, resultName);
|
|
1084
|
+
return tbl.as(resultName);
|
|
766
1085
|
}, (join) => join.onTrue());
|
|
767
1086
|
}
|
|
768
|
-
|
|
1087
|
+
buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
|
|
1088
|
+
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1089
|
+
if (m2m) {
|
|
1090
|
+
const parentIds = getIdFields(this.schema, model);
|
|
1091
|
+
const relationIds = getIdFields(this.schema, relationModel);
|
|
1092
|
+
invariant2(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1093
|
+
invariant2(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1094
|
+
query = query.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
1095
|
+
} else {
|
|
1096
|
+
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
|
|
1097
|
+
query = query.where((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(sql2.ref(left), "=", sql2.ref(right)))));
|
|
1098
|
+
}
|
|
1099
|
+
return query;
|
|
1100
|
+
}
|
|
1101
|
+
buildRelationObjectSelect(relationModel, relationModelAlias, relationFieldDef, qb, payload, parentResultName) {
|
|
769
1102
|
qb = qb.select((eb) => {
|
|
770
|
-
const objArgs = this.buildRelationObjectArgs(relationModel,
|
|
1103
|
+
const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
|
|
771
1104
|
if (relationFieldDef.array) {
|
|
772
|
-
return eb.fn.coalesce(sql2`jsonb_agg(jsonb_build_object(${sql2.join(objArgs)}))`, sql2`'[]'::jsonb`).as("$
|
|
1105
|
+
return eb.fn.coalesce(sql2`jsonb_agg(jsonb_build_object(${sql2.join(objArgs)}))`, sql2`'[]'::jsonb`).as("$data");
|
|
773
1106
|
} else {
|
|
774
|
-
return sql2`jsonb_build_object(${sql2.join(objArgs)})`.as("$
|
|
1107
|
+
return sql2`jsonb_build_object(${sql2.join(objArgs)})`.as("$data");
|
|
775
1108
|
}
|
|
776
1109
|
});
|
|
777
1110
|
return qb;
|
|
778
1111
|
}
|
|
779
|
-
buildRelationObjectArgs(relationModel,
|
|
1112
|
+
buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName) {
|
|
780
1113
|
const relationModelDef = requireModel(this.schema, relationModel);
|
|
781
1114
|
const objArgs = [];
|
|
1115
|
+
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1116
|
+
if (descendantModels.length > 0) {
|
|
1117
|
+
objArgs.push(...descendantModels.map((subModel) => [
|
|
1118
|
+
sql2.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1119
|
+
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1120
|
+
]).flatMap((v) => v));
|
|
1121
|
+
}
|
|
782
1122
|
if (payload === true || !payload.select) {
|
|
783
1123
|
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
784
1124
|
sql2.lit(field),
|
|
785
|
-
|
|
1125
|
+
this.fieldRef(relationModel, field, eb, relationModelAlias, false)
|
|
786
1126
|
]).flatMap((v) => v));
|
|
787
1127
|
} else if (payload.select) {
|
|
788
|
-
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1128
|
+
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
|
|
1129
|
+
if (field === "_count") {
|
|
1130
|
+
const subJson = this.buildCountJson(relationModel, eb, relationModelAlias, value);
|
|
1131
|
+
return [
|
|
1132
|
+
sql2.lit(field),
|
|
1133
|
+
subJson
|
|
1134
|
+
];
|
|
1135
|
+
} else {
|
|
1136
|
+
const fieldDef = requireField(this.schema, relationModel, field);
|
|
1137
|
+
const fieldValue = fieldDef.relation ? eb.ref(`${parentResultName}$${field}.$data`) : this.fieldRef(relationModel, field, eb, relationModelAlias, false);
|
|
1138
|
+
return [
|
|
1139
|
+
sql2.lit(field),
|
|
1140
|
+
fieldValue
|
|
1141
|
+
];
|
|
1142
|
+
}
|
|
795
1143
|
}).flatMap((v) => v));
|
|
796
1144
|
}
|
|
797
1145
|
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
798
1146
|
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
|
|
799
1147
|
sql2.lit(field),
|
|
800
1148
|
// reference the synthesized JSON field
|
|
801
|
-
eb.ref(`${
|
|
1149
|
+
eb.ref(`${parentResultName}$${field}.$data`)
|
|
802
1150
|
]).flatMap((v) => v));
|
|
803
1151
|
}
|
|
804
1152
|
return objArgs;
|
|
805
1153
|
}
|
|
806
|
-
buildRelationJoins(
|
|
807
|
-
let result =
|
|
1154
|
+
buildRelationJoins(query, relationModel, relationModelAlias, payload, parentResultName) {
|
|
1155
|
+
let result = query;
|
|
808
1156
|
if (typeof payload === "object") {
|
|
809
1157
|
const selectInclude = payload.include ?? payload.select;
|
|
810
1158
|
if (selectInclude && typeof selectInclude === "object") {
|
|
811
1159
|
Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
|
|
812
|
-
result = this.buildRelationJSON(relationModel, result, field, `${
|
|
1160
|
+
result = this.buildRelationJSON(relationModel, result, field, relationModelAlias, value, `${parentResultName}$${field}`);
|
|
813
1161
|
});
|
|
814
1162
|
}
|
|
815
1163
|
}
|
|
@@ -851,12 +1199,15 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
851
1199
|
return `ARRAY[${values.map((v) => typeof v === "string" ? `'${v}'` : v)}]`;
|
|
852
1200
|
}
|
|
853
1201
|
}
|
|
1202
|
+
get supportInsertWithDefault() {
|
|
1203
|
+
return true;
|
|
1204
|
+
}
|
|
854
1205
|
};
|
|
855
1206
|
|
|
856
1207
|
// src/client/crud/dialects/sqlite.ts
|
|
857
1208
|
import { invariant as invariant3 } from "@zenstackhq/common-helpers";
|
|
858
1209
|
import { sql as sql3 } from "kysely";
|
|
859
|
-
import { match as
|
|
1210
|
+
import { match as match4 } from "ts-pattern";
|
|
860
1211
|
var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
861
1212
|
static {
|
|
862
1213
|
__name(this, "SqliteCrudDialect");
|
|
@@ -871,79 +1222,75 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
871
1222
|
if (Array.isArray(value)) {
|
|
872
1223
|
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
873
1224
|
} else {
|
|
874
|
-
|
|
1225
|
+
if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1226
|
+
return JSON.stringify(value);
|
|
1227
|
+
} else {
|
|
1228
|
+
return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
1229
|
+
}
|
|
875
1230
|
}
|
|
876
1231
|
}
|
|
877
1232
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
878
1233
|
return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
|
|
879
1234
|
}
|
|
880
|
-
buildRelationJSON(model, eb, relationField,
|
|
1235
|
+
buildRelationJSON(model, eb, relationField, parentAlias, payload) {
|
|
881
1236
|
const relationFieldDef = requireField(this.schema, model, relationField);
|
|
882
1237
|
const relationModel = relationFieldDef.type;
|
|
883
1238
|
const relationModelDef = requireModel(this.schema, relationModel);
|
|
884
|
-
const subQueryName = `${
|
|
885
|
-
let tbl
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
const
|
|
892
|
-
let
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
}
|
|
898
|
-
subQuery = this.buildSkipTake(subQuery, skip, take);
|
|
899
|
-
subQuery = this.buildOrderBy(subQuery, relationModel, relationModel, payload.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
900
|
-
}
|
|
901
|
-
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
902
|
-
if (m2m) {
|
|
903
|
-
const parentIds = getIdFields(this.schema, model);
|
|
904
|
-
const relationIds = getIdFields(this.schema, relationModel);
|
|
905
|
-
invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
906
|
-
invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
907
|
-
subQuery = subQuery.where(eb(eb.ref(`${relationModel}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentName}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
908
|
-
} else {
|
|
909
|
-
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
910
|
-
keyPairs.forEach(({ fk, pk }) => {
|
|
911
|
-
if (ownedByModel) {
|
|
912
|
-
subQuery = subQuery.whereRef(`${relationModel}.${pk}`, "=", `${parentName}.${fk}`);
|
|
913
|
-
} else {
|
|
914
|
-
subQuery = subQuery.whereRef(`${relationModel}.${fk}`, "=", `${parentName}.${pk}`);
|
|
915
|
-
}
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
return subQuery.as(subQueryName);
|
|
919
|
-
});
|
|
1239
|
+
const subQueryName = `${parentAlias}$${relationField}`;
|
|
1240
|
+
let tbl;
|
|
1241
|
+
if (this.canJoinWithoutNestedSelect(relationModelDef, payload)) {
|
|
1242
|
+
tbl = this.buildModelSelect(eb, relationModel, subQueryName, payload, false);
|
|
1243
|
+
tbl = this.buildRelationJoinFilter(tbl, model, relationField, subQueryName, parentAlias);
|
|
1244
|
+
} else {
|
|
1245
|
+
tbl = eb.selectFrom(() => {
|
|
1246
|
+
const selectModelAlias = `${parentAlias}$${relationField}$sub`;
|
|
1247
|
+
let selectModelQuery = this.buildModelSelect(eb, relationModel, selectModelAlias, payload, true);
|
|
1248
|
+
selectModelQuery = this.buildRelationJoinFilter(selectModelQuery, model, relationField, selectModelAlias, parentAlias);
|
|
1249
|
+
return selectModelQuery.as(subQueryName);
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
920
1252
|
tbl = tbl.select(() => {
|
|
921
1253
|
const objArgs = [];
|
|
1254
|
+
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1255
|
+
if (descendantModels.length > 0) {
|
|
1256
|
+
objArgs.push(...descendantModels.map((subModel) => [
|
|
1257
|
+
sql3.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1258
|
+
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1259
|
+
]).flatMap((v) => v));
|
|
1260
|
+
}
|
|
922
1261
|
if (payload === true || !payload.select) {
|
|
923
1262
|
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
924
1263
|
sql3.lit(field),
|
|
925
|
-
|
|
1264
|
+
this.fieldRef(relationModel, field, eb, subQueryName, false)
|
|
926
1265
|
]).flatMap((v) => v));
|
|
927
1266
|
} else if (payload.select) {
|
|
928
1267
|
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentName}$${relationField}`, value);
|
|
1268
|
+
if (field === "_count") {
|
|
1269
|
+
const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
|
|
932
1270
|
return [
|
|
933
1271
|
sql3.lit(field),
|
|
934
1272
|
subJson
|
|
935
1273
|
];
|
|
936
1274
|
} else {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1275
|
+
const fieldDef = requireField(this.schema, relationModel, field);
|
|
1276
|
+
if (fieldDef.relation) {
|
|
1277
|
+
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
1278
|
+
return [
|
|
1279
|
+
sql3.lit(field),
|
|
1280
|
+
subJson
|
|
1281
|
+
];
|
|
1282
|
+
} else {
|
|
1283
|
+
return [
|
|
1284
|
+
sql3.lit(field),
|
|
1285
|
+
this.fieldRef(relationModel, field, eb, subQueryName, false)
|
|
1286
|
+
];
|
|
1287
|
+
}
|
|
941
1288
|
}
|
|
942
1289
|
}).flatMap((v) => v));
|
|
943
1290
|
}
|
|
944
1291
|
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
945
1292
|
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
|
|
946
|
-
const subJson = this.buildRelationJSON(relationModel, eb, field, `${
|
|
1293
|
+
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
947
1294
|
return [
|
|
948
1295
|
sql3.lit(field),
|
|
949
1296
|
subJson
|
|
@@ -951,13 +1298,35 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
951
1298
|
}).flatMap((v) => v));
|
|
952
1299
|
}
|
|
953
1300
|
if (relationFieldDef.array) {
|
|
954
|
-
return eb.fn.coalesce(sql3`json_group_array(json_object(${sql3.join(objArgs)}))`, sql3`json_array()`).as("$
|
|
1301
|
+
return eb.fn.coalesce(sql3`json_group_array(json_object(${sql3.join(objArgs)}))`, sql3`json_array()`).as("$data");
|
|
955
1302
|
} else {
|
|
956
|
-
return sql3`json_object(${sql3.join(objArgs)})`.as("data");
|
|
1303
|
+
return sql3`json_object(${sql3.join(objArgs)})`.as("$data");
|
|
957
1304
|
}
|
|
958
1305
|
});
|
|
959
1306
|
return tbl;
|
|
960
1307
|
}
|
|
1308
|
+
buildRelationJoinFilter(selectModelQuery, model, relationField, relationModelAlias, parentAlias) {
|
|
1309
|
+
const fieldDef = requireField(this.schema, model, relationField);
|
|
1310
|
+
const relationModel = fieldDef.type;
|
|
1311
|
+
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1312
|
+
if (m2m) {
|
|
1313
|
+
const parentIds = getIdFields(this.schema, model);
|
|
1314
|
+
const relationIds = getIdFields(this.schema, relationModel);
|
|
1315
|
+
invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1316
|
+
invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1317
|
+
selectModelQuery = selectModelQuery.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
1318
|
+
} else {
|
|
1319
|
+
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
1320
|
+
keyPairs.forEach(({ fk, pk }) => {
|
|
1321
|
+
if (ownedByModel) {
|
|
1322
|
+
selectModelQuery = selectModelQuery.whereRef(`${relationModelAlias}.${pk}`, "=", `${parentAlias}.${fk}`);
|
|
1323
|
+
} else {
|
|
1324
|
+
selectModelQuery = selectModelQuery.whereRef(`${relationModelAlias}.${fk}`, "=", `${parentAlias}.${pk}`);
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
return selectModelQuery;
|
|
1329
|
+
}
|
|
961
1330
|
buildSkipTake(query, skip, take) {
|
|
962
1331
|
if (take !== void 0) {
|
|
963
1332
|
query = query.limit(take);
|
|
@@ -993,93 +1362,17 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
993
1362
|
buildArrayLiteralSQL(_values) {
|
|
994
1363
|
throw new Error("SQLite does not support array literals");
|
|
995
1364
|
}
|
|
1365
|
+
get supportInsertWithDefault() {
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
996
1368
|
};
|
|
997
1369
|
|
|
998
1370
|
// src/client/crud/dialects/index.ts
|
|
999
1371
|
function getCrudDialect(schema, options) {
|
|
1000
|
-
return
|
|
1372
|
+
return match5(schema.provider.type).with("sqlite", () => new SqliteCrudDialect(schema, options)).with("postgresql", () => new PostgresCrudDialect(schema, options)).exhaustive();
|
|
1001
1373
|
}
|
|
1002
1374
|
__name(getCrudDialect, "getCrudDialect");
|
|
1003
1375
|
|
|
1004
|
-
// src/schema/expression.ts
|
|
1005
|
-
var ExpressionUtils = {
|
|
1006
|
-
literal: /* @__PURE__ */ __name((value) => {
|
|
1007
|
-
return {
|
|
1008
|
-
kind: "literal",
|
|
1009
|
-
value
|
|
1010
|
-
};
|
|
1011
|
-
}, "literal"),
|
|
1012
|
-
array: /* @__PURE__ */ __name((items) => {
|
|
1013
|
-
return {
|
|
1014
|
-
kind: "array",
|
|
1015
|
-
items
|
|
1016
|
-
};
|
|
1017
|
-
}, "array"),
|
|
1018
|
-
call: /* @__PURE__ */ __name((functionName, args) => {
|
|
1019
|
-
return {
|
|
1020
|
-
kind: "call",
|
|
1021
|
-
function: functionName,
|
|
1022
|
-
args
|
|
1023
|
-
};
|
|
1024
|
-
}, "call"),
|
|
1025
|
-
binary: /* @__PURE__ */ __name((left, op, right) => {
|
|
1026
|
-
return {
|
|
1027
|
-
kind: "binary",
|
|
1028
|
-
op,
|
|
1029
|
-
left,
|
|
1030
|
-
right
|
|
1031
|
-
};
|
|
1032
|
-
}, "binary"),
|
|
1033
|
-
unary: /* @__PURE__ */ __name((op, operand) => {
|
|
1034
|
-
return {
|
|
1035
|
-
kind: "unary",
|
|
1036
|
-
op,
|
|
1037
|
-
operand
|
|
1038
|
-
};
|
|
1039
|
-
}, "unary"),
|
|
1040
|
-
field: /* @__PURE__ */ __name((field) => {
|
|
1041
|
-
return {
|
|
1042
|
-
kind: "field",
|
|
1043
|
-
field
|
|
1044
|
-
};
|
|
1045
|
-
}, "field"),
|
|
1046
|
-
member: /* @__PURE__ */ __name((receiver, members) => {
|
|
1047
|
-
return {
|
|
1048
|
-
kind: "member",
|
|
1049
|
-
receiver,
|
|
1050
|
-
members
|
|
1051
|
-
};
|
|
1052
|
-
}, "member"),
|
|
1053
|
-
_this: /* @__PURE__ */ __name(() => {
|
|
1054
|
-
return {
|
|
1055
|
-
kind: "this"
|
|
1056
|
-
};
|
|
1057
|
-
}, "_this"),
|
|
1058
|
-
_null: /* @__PURE__ */ __name(() => {
|
|
1059
|
-
return {
|
|
1060
|
-
kind: "null"
|
|
1061
|
-
};
|
|
1062
|
-
}, "_null"),
|
|
1063
|
-
and: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
1064
|
-
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "&&", exp), expr2);
|
|
1065
|
-
}, "and"),
|
|
1066
|
-
or: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
1067
|
-
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "||", exp), expr2);
|
|
1068
|
-
}, "or"),
|
|
1069
|
-
is: /* @__PURE__ */ __name((value, kind) => {
|
|
1070
|
-
return !!value && typeof value === "object" && "kind" in value && value.kind === kind;
|
|
1071
|
-
}, "is"),
|
|
1072
|
-
isLiteral: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "literal"), "isLiteral"),
|
|
1073
|
-
isArray: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "array"), "isArray"),
|
|
1074
|
-
isCall: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "call"), "isCall"),
|
|
1075
|
-
isNull: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "null"), "isNull"),
|
|
1076
|
-
isThis: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "this"), "isThis"),
|
|
1077
|
-
isUnary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "unary"), "isUnary"),
|
|
1078
|
-
isBinary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "binary"), "isBinary"),
|
|
1079
|
-
isField: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "field"), "isField"),
|
|
1080
|
-
isMember: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "member"), "isMember")
|
|
1081
|
-
};
|
|
1082
|
-
|
|
1083
1376
|
// src/utils/default-operation-node-visitor.ts
|
|
1084
1377
|
import { OperationNodeVisitor } from "kysely";
|
|
1085
1378
|
var DefaultOperationNodeVisitor = class extends OperationNodeVisitor {
|
|
@@ -1400,18 +1693,18 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
|
|
|
1400
1693
|
|
|
1401
1694
|
// src/plugins/policy/expression-transformer.ts
|
|
1402
1695
|
import { invariant as invariant5 } from "@zenstackhq/common-helpers";
|
|
1403
|
-
import { AliasNode as AliasNode2, BinaryOperationNode as BinaryOperationNode2, ColumnNode, expressionBuilder, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode2, SelectionNode, SelectQueryNode, TableNode as TableNode2, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
|
|
1404
|
-
import { match as
|
|
1696
|
+
import { AliasNode as AliasNode2, BinaryOperationNode as BinaryOperationNode2, ColumnNode, expressionBuilder as expressionBuilder2, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode2, SelectionNode, SelectQueryNode, TableNode as TableNode2, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
|
|
1697
|
+
import { match as match7 } from "ts-pattern";
|
|
1405
1698
|
|
|
1406
1699
|
// src/plugins/policy/expression-evaluator.ts
|
|
1407
1700
|
import { invariant as invariant4 } from "@zenstackhq/common-helpers";
|
|
1408
|
-
import { match as
|
|
1701
|
+
import { match as match6 } from "ts-pattern";
|
|
1409
1702
|
var ExpressionEvaluator = class {
|
|
1410
1703
|
static {
|
|
1411
1704
|
__name(this, "ExpressionEvaluator");
|
|
1412
1705
|
}
|
|
1413
1706
|
evaluate(expression, context) {
|
|
1414
|
-
const result =
|
|
1707
|
+
const result = match6(expression).when(ExpressionUtils.isArray, (expr2) => this.evaluateArray(expr2, context)).when(ExpressionUtils.isBinary, (expr2) => this.evaluateBinary(expr2, context)).when(ExpressionUtils.isField, (expr2) => this.evaluateField(expr2, context)).when(ExpressionUtils.isLiteral, (expr2) => this.evaluateLiteral(expr2)).when(ExpressionUtils.isMember, (expr2) => this.evaluateMember(expr2, context)).when(ExpressionUtils.isUnary, (expr2) => this.evaluateUnary(expr2, context)).when(ExpressionUtils.isCall, (expr2) => this.evaluateCall(expr2, context)).when(ExpressionUtils.isThis, () => context.thisValue).when(ExpressionUtils.isNull, () => null).exhaustive();
|
|
1415
1708
|
return result ?? null;
|
|
1416
1709
|
}
|
|
1417
1710
|
evaluateCall(expr2, context) {
|
|
@@ -1422,7 +1715,7 @@ var ExpressionEvaluator = class {
|
|
|
1422
1715
|
}
|
|
1423
1716
|
}
|
|
1424
1717
|
evaluateUnary(expr2, context) {
|
|
1425
|
-
return
|
|
1718
|
+
return match6(expr2.op).with("!", () => !this.evaluate(expr2.operand, context)).exhaustive();
|
|
1426
1719
|
}
|
|
1427
1720
|
evaluateMember(expr2, context) {
|
|
1428
1721
|
let val = this.evaluate(expr2.receiver, context);
|
|
@@ -1446,7 +1739,7 @@ var ExpressionEvaluator = class {
|
|
|
1446
1739
|
}
|
|
1447
1740
|
const left = this.evaluate(expr2.left, context);
|
|
1448
1741
|
const right = this.evaluate(expr2.right, context);
|
|
1449
|
-
return
|
|
1742
|
+
return match6(expr2.op).with("==", () => left === right).with("!=", () => left !== right).with(">", () => left > right).with(">=", () => left >= right).with("<", () => left < right).with("<=", () => left <= right).with("&&", () => left && right).with("||", () => left || right).with("in", () => {
|
|
1450
1743
|
const _right = right ?? [];
|
|
1451
1744
|
invariant4(Array.isArray(_right), 'expected array for "in" operator');
|
|
1452
1745
|
return _right.includes(left);
|
|
@@ -1460,7 +1753,7 @@ var ExpressionEvaluator = class {
|
|
|
1460
1753
|
return false;
|
|
1461
1754
|
}
|
|
1462
1755
|
invariant4(Array.isArray(left), "expected array");
|
|
1463
|
-
return
|
|
1756
|
+
return match6(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
|
|
1464
1757
|
...context,
|
|
1465
1758
|
thisValue: item
|
|
1466
1759
|
}))).with("!", () => left.every((item) => this.evaluate(expr2.right, {
|
|
@@ -1714,7 +2007,7 @@ var ExpressionTransformer = class {
|
|
|
1714
2007
|
const count = FunctionNode2.create("count", [
|
|
1715
2008
|
ValueNode2.createImmediate(1)
|
|
1716
2009
|
]);
|
|
1717
|
-
const predicateResult =
|
|
2010
|
+
const predicateResult = match7(expr2.op).with("?", () => BinaryOperationNode2.create(count, OperatorNode2.create(">"), ValueNode2.createImmediate(0))).with("!", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).with("^", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).exhaustive();
|
|
1718
2011
|
return this.transform(expr2.left, {
|
|
1719
2012
|
...context,
|
|
1720
2013
|
memberSelect: SelectionNode.create(AliasNode2.create(predicateResult, IdentifierNode.create("$t"))),
|
|
@@ -1745,7 +2038,7 @@ var ExpressionTransformer = class {
|
|
|
1745
2038
|
return BinaryOperationNode2.create(this.transform(expr2.operand, context), this.transformOperator("!="), trueNode(this.dialect));
|
|
1746
2039
|
}
|
|
1747
2040
|
transformOperator(op) {
|
|
1748
|
-
const mappedOp =
|
|
2041
|
+
const mappedOp = match7(op).with("==", () => "=").otherwise(() => op);
|
|
1749
2042
|
return OperatorNode2.create(mappedOp);
|
|
1750
2043
|
}
|
|
1751
2044
|
_call(expr2, context) {
|
|
@@ -1757,7 +2050,7 @@ var ExpressionTransformer = class {
|
|
|
1757
2050
|
if (!func) {
|
|
1758
2051
|
throw new QueryError(`Function not implemented: ${expr2.function}`);
|
|
1759
2052
|
}
|
|
1760
|
-
const eb =
|
|
2053
|
+
const eb = expressionBuilder2();
|
|
1761
2054
|
return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
|
|
1762
2055
|
dialect: this.dialect,
|
|
1763
2056
|
model: context.model,
|
|
@@ -2148,7 +2441,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2148
2441
|
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(ColumnNode2.create(field), OperatorNode3.create("="), ValueNode3.create(row[field]))))));
|
|
2149
2442
|
}
|
|
2150
2443
|
getMutationModel(node) {
|
|
2151
|
-
const r =
|
|
2444
|
+
const r = match8(node).when(InsertQueryNode.is, (node2) => getTableName(node2.into)).when(UpdateQueryNode.is, (node2) => getTableName(node2.table)).when(DeleteQueryNode.is, (node2) => {
|
|
2152
2445
|
if (node2.from.froms.length !== 1) {
|
|
2153
2446
|
throw new InternalError("Only one from table is supported for delete");
|
|
2154
2447
|
}
|
|
@@ -2328,4 +2621,4 @@ export {
|
|
|
2328
2621
|
PolicyPlugin,
|
|
2329
2622
|
RejectedByPolicyError
|
|
2330
2623
|
};
|
|
2331
|
-
//# sourceMappingURL=
|
|
2624
|
+
//# sourceMappingURL=index.js.map
|