@zenstackhq/runtime 3.0.0-alpha.3 → 3.0.0-alpha.31
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-0F-AwA7Z.d.cts → contract-5Wcmlo5v.d.cts} +1006 -811
- package/dist/{contract-0F-AwA7Z.d.ts → contract-5Wcmlo5v.d.ts} +1006 -811
- 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 +2216 -1130
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -8
- package/dist/index.d.ts +30 -8
- package/dist/index.js +2188 -1108
- package/dist/index.js.map +1 -1
- package/dist/plugins/{policy.cjs → policy/index.cjs} +578 -310
- package/dist/plugins/policy/index.cjs.map +1 -0
- package/dist/plugins/{policy.d.ts → policy/index.d.cts} +2 -4
- package/dist/plugins/{policy.d.cts → policy/index.d.ts} +2 -4
- package/dist/plugins/{policy.js → policy/index.js} +553 -275
- package/dist/plugins/policy/index.js.map +1 -0
- package/dist/plugins/policy/plugin.zmodel +33 -0
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js.map +1 -1
- package/package.json +30 -28
- package/dist/client.cjs +0 -6084
- package/dist/client.cjs.map +0 -1
- package/dist/client.d.cts +0 -19
- package/dist/client.d.ts +0 -19
- package/dist/client.js +0 -6050
- package/dist/client.js.map +0 -1
- package/dist/plugins/policy.cjs.map +0 -1
- package/dist/plugins/policy.js.map +0 -1
|
@@ -14,34 +14,130 @@ var RejectedByPolicyError = class extends Error {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// src/plugins/policy/policy-handler.ts
|
|
17
|
+
import { invariant as invariant6 } from "@zenstackhq/common-helpers";
|
|
17
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";
|
|
18
|
-
import
|
|
19
|
-
import { match as match7 } from "ts-pattern";
|
|
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
|
+
import { invariant as invariant2 } from "@zenstackhq/common-helpers";
|
|
25
26
|
import { sql as sql2 } from "kysely";
|
|
26
|
-
import
|
|
27
|
-
|
|
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
|
+
};
|
|
28
125
|
|
|
29
126
|
// src/client/errors.ts
|
|
30
127
|
var QueryError = class extends Error {
|
|
31
128
|
static {
|
|
32
129
|
__name(this, "QueryError");
|
|
33
130
|
}
|
|
34
|
-
constructor(message) {
|
|
35
|
-
super(message
|
|
131
|
+
constructor(message, cause) {
|
|
132
|
+
super(message, {
|
|
133
|
+
cause
|
|
134
|
+
});
|
|
36
135
|
}
|
|
37
136
|
};
|
|
38
137
|
var InternalError = class extends Error {
|
|
39
138
|
static {
|
|
40
139
|
__name(this, "InternalError");
|
|
41
140
|
}
|
|
42
|
-
constructor(message) {
|
|
43
|
-
super(message);
|
|
44
|
-
}
|
|
45
141
|
};
|
|
46
142
|
|
|
47
143
|
// src/client/query-utils.ts
|
|
@@ -52,7 +148,7 @@ __name(getModel, "getModel");
|
|
|
52
148
|
function requireModel(schema, model) {
|
|
53
149
|
const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
|
|
54
150
|
if (!matchedName) {
|
|
55
|
-
throw new QueryError(`Model "${model}" not found`);
|
|
151
|
+
throw new QueryError(`Model "${model}" not found in schema`);
|
|
56
152
|
}
|
|
57
153
|
return schema.models[matchedName];
|
|
58
154
|
}
|
|
@@ -115,6 +211,16 @@ function getRelationForeignKeyFieldPairs(schema, model, relationField) {
|
|
|
115
211
|
}
|
|
116
212
|
}
|
|
117
213
|
__name(getRelationForeignKeyFieldPairs, "getRelationForeignKeyFieldPairs");
|
|
214
|
+
function isRelationField(schema, model, field) {
|
|
215
|
+
const fieldDef = getField(schema, model, field);
|
|
216
|
+
return !!fieldDef?.relation;
|
|
217
|
+
}
|
|
218
|
+
__name(isRelationField, "isRelationField");
|
|
219
|
+
function isInheritedField(schema, model, field) {
|
|
220
|
+
const fieldDef = getField(schema, model, field);
|
|
221
|
+
return !!fieldDef?.originModel;
|
|
222
|
+
}
|
|
223
|
+
__name(isInheritedField, "isInheritedField");
|
|
118
224
|
function getUniqueFields(schema, model) {
|
|
119
225
|
const modelDef = requireModel(schema, model);
|
|
120
226
|
const result = [];
|
|
@@ -140,20 +246,25 @@ function getUniqueFields(schema, model) {
|
|
|
140
246
|
return result;
|
|
141
247
|
}
|
|
142
248
|
__name(getUniqueFields, "getUniqueFields");
|
|
143
|
-
function buildFieldRef(schema, model, field, options, eb, modelAlias) {
|
|
249
|
+
function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComputedField = true) {
|
|
144
250
|
const fieldDef = requireField(schema, model, field);
|
|
145
251
|
if (!fieldDef.computed) {
|
|
146
252
|
return eb.ref(modelAlias ? `${modelAlias}.${field}` : field);
|
|
147
253
|
} else {
|
|
254
|
+
if (!inlineComputedField) {
|
|
255
|
+
return eb.ref(modelAlias ? `${modelAlias}.${field}` : field);
|
|
256
|
+
}
|
|
148
257
|
let computer;
|
|
149
258
|
if ("computedFields" in options) {
|
|
150
259
|
const computedFields = options.computedFields;
|
|
151
260
|
computer = computedFields?.[model]?.[field];
|
|
152
261
|
}
|
|
153
262
|
if (!computer) {
|
|
154
|
-
throw new QueryError(`Computed field "${field}" implementation not provided`);
|
|
263
|
+
throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
|
|
155
264
|
}
|
|
156
|
-
return computer(eb
|
|
265
|
+
return computer(eb, {
|
|
266
|
+
currentModel: modelAlias
|
|
267
|
+
});
|
|
157
268
|
}
|
|
158
269
|
}
|
|
159
270
|
__name(buildFieldRef, "buildFieldRef");
|
|
@@ -196,11 +307,33 @@ function getManyToManyRelation(schema, model, field) {
|
|
|
196
307
|
model,
|
|
197
308
|
fieldDef.type
|
|
198
309
|
].sort();
|
|
310
|
+
let orderedFK;
|
|
311
|
+
if (model !== fieldDef.type) {
|
|
312
|
+
orderedFK = sortedModelNames[0] === model ? [
|
|
313
|
+
"A",
|
|
314
|
+
"B"
|
|
315
|
+
] : [
|
|
316
|
+
"B",
|
|
317
|
+
"A"
|
|
318
|
+
];
|
|
319
|
+
} else {
|
|
320
|
+
const sortedFieldNames = [
|
|
321
|
+
field,
|
|
322
|
+
oppositeFieldDef.name
|
|
323
|
+
].sort();
|
|
324
|
+
orderedFK = sortedFieldNames[0] === field ? [
|
|
325
|
+
"A",
|
|
326
|
+
"B"
|
|
327
|
+
] : [
|
|
328
|
+
"B",
|
|
329
|
+
"A"
|
|
330
|
+
];
|
|
331
|
+
}
|
|
199
332
|
return {
|
|
200
|
-
parentFkName:
|
|
333
|
+
parentFkName: orderedFK[0],
|
|
201
334
|
otherModel: fieldDef.type,
|
|
202
335
|
otherField: fieldDef.relation.opposite,
|
|
203
|
-
otherFkName:
|
|
336
|
+
otherFkName: orderedFK[1],
|
|
204
337
|
joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
|
|
205
338
|
};
|
|
206
339
|
} else {
|
|
@@ -228,11 +361,38 @@ function flattenCompoundUniqueFilters(schema, model, filter) {
|
|
|
228
361
|
return result;
|
|
229
362
|
}
|
|
230
363
|
__name(flattenCompoundUniqueFilters, "flattenCompoundUniqueFilters");
|
|
364
|
+
function ensureArray(value) {
|
|
365
|
+
if (Array.isArray(value)) {
|
|
366
|
+
return value;
|
|
367
|
+
} else {
|
|
368
|
+
return [
|
|
369
|
+
value
|
|
370
|
+
];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
__name(ensureArray, "ensureArray");
|
|
374
|
+
function getDelegateDescendantModels(schema, model, collected = /* @__PURE__ */ new Set()) {
|
|
375
|
+
const subModels = Object.values(schema.models).filter((m) => m.baseModel === model);
|
|
376
|
+
subModels.forEach((def) => {
|
|
377
|
+
if (!collected.has(def)) {
|
|
378
|
+
collected.add(def);
|
|
379
|
+
getDelegateDescendantModels(schema, def.name, collected);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
return [
|
|
383
|
+
...collected
|
|
384
|
+
];
|
|
385
|
+
}
|
|
386
|
+
__name(getDelegateDescendantModels, "getDelegateDescendantModels");
|
|
387
|
+
function aggregate(eb, expr2, op) {
|
|
388
|
+
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();
|
|
389
|
+
}
|
|
390
|
+
__name(aggregate, "aggregate");
|
|
231
391
|
|
|
232
392
|
// src/client/crud/dialects/base.ts
|
|
233
|
-
import {
|
|
234
|
-
import
|
|
235
|
-
import { match, P } from "ts-pattern";
|
|
393
|
+
import { invariant, isPlainObject } from "@zenstackhq/common-helpers";
|
|
394
|
+
import { expressionBuilder, sql } from "kysely";
|
|
395
|
+
import { match as match2, P } from "ts-pattern";
|
|
236
396
|
|
|
237
397
|
// src/utils/enumerate.ts
|
|
238
398
|
function enumerate(x) {
|
|
@@ -249,7 +409,6 @@ function enumerate(x) {
|
|
|
249
409
|
__name(enumerate, "enumerate");
|
|
250
410
|
|
|
251
411
|
// src/client/crud/dialects/base.ts
|
|
252
|
-
import { isPlainObject } from "is-plain-object";
|
|
253
412
|
var BaseCrudDialect = class {
|
|
254
413
|
static {
|
|
255
414
|
__name(this, "BaseCrudDialect");
|
|
@@ -260,9 +419,47 @@ var BaseCrudDialect = class {
|
|
|
260
419
|
this.schema = schema;
|
|
261
420
|
this.options = options;
|
|
262
421
|
}
|
|
263
|
-
transformPrimitive(value, _type) {
|
|
422
|
+
transformPrimitive(value, _type, _forArrayField) {
|
|
264
423
|
return value;
|
|
265
424
|
}
|
|
425
|
+
// #region common query builders
|
|
426
|
+
buildSelectModel(eb, model, modelAlias) {
|
|
427
|
+
const modelDef = requireModel(this.schema, model);
|
|
428
|
+
let result = eb.selectFrom(model === modelAlias ? model : `${model} as ${modelAlias}`);
|
|
429
|
+
let joinBase = modelDef.baseModel;
|
|
430
|
+
while (joinBase) {
|
|
431
|
+
result = this.buildDelegateJoin(model, modelAlias, joinBase, result);
|
|
432
|
+
joinBase = requireModel(this.schema, joinBase).baseModel;
|
|
433
|
+
}
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
buildFilterSortTake(model, args, query, modelAlias) {
|
|
437
|
+
let result = query;
|
|
438
|
+
if (args.where) {
|
|
439
|
+
result = result.where((eb) => this.buildFilter(eb, model, modelAlias, args?.where));
|
|
440
|
+
}
|
|
441
|
+
let negateOrderBy = false;
|
|
442
|
+
const skip = args.skip;
|
|
443
|
+
let take = args.take;
|
|
444
|
+
if (take !== void 0 && take < 0) {
|
|
445
|
+
negateOrderBy = true;
|
|
446
|
+
take = -take;
|
|
447
|
+
}
|
|
448
|
+
result = this.buildSkipTake(result, skip, take);
|
|
449
|
+
result = this.buildOrderBy(result, model, modelAlias, args.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
450
|
+
if ("distinct" in args && args.distinct) {
|
|
451
|
+
const distinct = ensureArray(args.distinct);
|
|
452
|
+
if (this.supportsDistinctOn) {
|
|
453
|
+
result = result.distinctOn(distinct.map((f) => sql.ref(`${modelAlias}.${f}`)));
|
|
454
|
+
} else {
|
|
455
|
+
throw new QueryError(`"distinct" is not supported by "${this.schema.provider.type}" provider`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (args.cursor) {
|
|
459
|
+
result = this.buildCursorFilter(model, result, args.cursor, args.orderBy, negateOrderBy, modelAlias);
|
|
460
|
+
}
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
266
463
|
buildFilter(eb, model, modelAlias, where) {
|
|
267
464
|
if (where === true || where === void 0) {
|
|
268
465
|
return this.true(eb);
|
|
@@ -279,17 +476,20 @@ var BaseCrudDialect = class {
|
|
|
279
476
|
if (key.startsWith("$")) {
|
|
280
477
|
continue;
|
|
281
478
|
}
|
|
282
|
-
if (key
|
|
479
|
+
if (this.isLogicalCombinator(key)) {
|
|
283
480
|
result = this.and(eb, result, this.buildCompositeFilter(eb, model, modelAlias, key, payload));
|
|
284
481
|
continue;
|
|
285
482
|
}
|
|
286
483
|
const fieldDef = requireField(this.schema, model, key);
|
|
287
484
|
if (fieldDef.relation) {
|
|
288
485
|
result = this.and(eb, result, this.buildRelationFilter(eb, model, modelAlias, key, fieldDef, payload));
|
|
289
|
-
} else if (fieldDef.array) {
|
|
290
|
-
result = this.and(eb, result, this.buildArrayFilter(eb, model, modelAlias, key, fieldDef, payload));
|
|
291
486
|
} else {
|
|
292
|
-
|
|
487
|
+
const fieldRef = this.fieldRef(fieldDef.originModel ?? model, key, eb, fieldDef.originModel ?? modelAlias);
|
|
488
|
+
if (fieldDef.array) {
|
|
489
|
+
result = this.and(eb, result, this.buildArrayFilter(eb, fieldRef, fieldDef, payload));
|
|
490
|
+
} else {
|
|
491
|
+
result = this.and(eb, result, this.buildPrimitiveFilter(eb, fieldRef, fieldDef, payload));
|
|
492
|
+
}
|
|
293
493
|
}
|
|
294
494
|
}
|
|
295
495
|
if ("$expr" in _where && typeof _where["$expr"] === "function") {
|
|
@@ -297,8 +497,32 @@ var BaseCrudDialect = class {
|
|
|
297
497
|
}
|
|
298
498
|
return result;
|
|
299
499
|
}
|
|
500
|
+
buildCursorFilter(model, query, cursor, orderBy, negateOrderBy, modelAlias) {
|
|
501
|
+
const _orderBy = orderBy ?? makeDefaultOrderBy(this.schema, model);
|
|
502
|
+
const orderByItems = ensureArray(_orderBy).flatMap((obj) => Object.entries(obj));
|
|
503
|
+
const eb = expressionBuilder();
|
|
504
|
+
const subQueryAlias = `${model}$cursor$sub`;
|
|
505
|
+
const cursorFilter = this.buildFilter(eb, model, subQueryAlias, cursor);
|
|
506
|
+
let result = query;
|
|
507
|
+
const filters = [];
|
|
508
|
+
for (let i = orderByItems.length - 1; i >= 0; i--) {
|
|
509
|
+
const andFilters = [];
|
|
510
|
+
for (let j = 0; j <= i; j++) {
|
|
511
|
+
const [field, order] = orderByItems[j];
|
|
512
|
+
const _order = negateOrderBy ? order === "asc" ? "desc" : "asc" : order;
|
|
513
|
+
const op = j === i ? _order === "asc" ? ">=" : "<=" : "=";
|
|
514
|
+
andFilters.push(eb(eb.ref(`${modelAlias}.${field}`), op, this.buildSelectModel(eb, model, subQueryAlias).select(`${subQueryAlias}.${field}`).where(cursorFilter)));
|
|
515
|
+
}
|
|
516
|
+
filters.push(eb.and(andFilters));
|
|
517
|
+
}
|
|
518
|
+
result = result.where((eb2) => eb2.or(filters));
|
|
519
|
+
return result;
|
|
520
|
+
}
|
|
521
|
+
isLogicalCombinator(key) {
|
|
522
|
+
return LOGICAL_COMBINATORS.includes(key);
|
|
523
|
+
}
|
|
300
524
|
buildCompositeFilter(eb, model, modelAlias, key, payload) {
|
|
301
|
-
return
|
|
525
|
+
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();
|
|
302
526
|
}
|
|
303
527
|
buildRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
304
528
|
if (!fieldDef.array) {
|
|
@@ -307,19 +531,26 @@ var BaseCrudDialect = class {
|
|
|
307
531
|
return this.buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload);
|
|
308
532
|
}
|
|
309
533
|
}
|
|
310
|
-
buildToOneRelationFilter(eb, model,
|
|
534
|
+
buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
311
535
|
if (payload === null) {
|
|
312
536
|
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
313
|
-
if (ownedByModel) {
|
|
314
|
-
return this.and(eb, ...keyPairs.map(({ fk }) => eb(sql.ref(`${
|
|
537
|
+
if (ownedByModel && !fieldDef.originModel) {
|
|
538
|
+
return this.and(eb, ...keyPairs.map(({ fk }) => eb(sql.ref(`${modelAlias}.${fk}`), "is", null)));
|
|
315
539
|
} else {
|
|
316
|
-
return this.buildToOneRelationFilter(eb, model,
|
|
540
|
+
return this.buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, {
|
|
317
541
|
is: null
|
|
318
542
|
});
|
|
319
543
|
}
|
|
320
544
|
}
|
|
321
|
-
const joinAlias = `${
|
|
322
|
-
const joinPairs = buildJoinPairs(
|
|
545
|
+
const joinAlias = `${modelAlias}$${field}`;
|
|
546
|
+
const joinPairs = buildJoinPairs(
|
|
547
|
+
this.schema,
|
|
548
|
+
model,
|
|
549
|
+
// if field is from a base, use the base model to join
|
|
550
|
+
fieldDef.originModel ?? modelAlias,
|
|
551
|
+
field,
|
|
552
|
+
joinAlias
|
|
553
|
+
);
|
|
323
554
|
const filterResultField = `${field}$filter`;
|
|
324
555
|
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));
|
|
325
556
|
const conditions = [];
|
|
@@ -349,25 +580,26 @@ var BaseCrudDialect = class {
|
|
|
349
580
|
}
|
|
350
581
|
return this.and(eb, ...conditions);
|
|
351
582
|
}
|
|
352
|
-
buildToManyRelationFilter(eb, model,
|
|
583
|
+
buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
353
584
|
if (payload === null) {
|
|
354
|
-
return eb(sql.ref(`${
|
|
585
|
+
return eb(sql.ref(`${modelAlias}.${field}`), "is", null);
|
|
355
586
|
}
|
|
356
587
|
const relationModel = fieldDef.type;
|
|
588
|
+
const relationFilterSelectAlias = `${modelAlias}$${field}$filter`;
|
|
357
589
|
const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
|
|
358
590
|
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
359
591
|
if (m2m) {
|
|
360
592
|
const modelIdField = getIdFields(this.schema, model)[0];
|
|
361
593
|
const relationIdField = getIdFields(this.schema, relationModel)[0];
|
|
362
|
-
return eb2(sql.ref(`${
|
|
594
|
+
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}`)));
|
|
363
595
|
} else {
|
|
364
596
|
const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
365
597
|
let result2 = this.true(eb2);
|
|
366
598
|
for (const { fk, pk } of relationKeyPairs.keyPairs) {
|
|
367
599
|
if (relationKeyPairs.ownedByModel) {
|
|
368
|
-
result2 = this.and(eb2, result2, eb2(sql.ref(`${
|
|
600
|
+
result2 = this.and(eb2, result2, eb2(sql.ref(`${modelAlias}.${fk}`), "=", sql.ref(`${relationFilterSelectAlias}.${pk}`)));
|
|
369
601
|
} else {
|
|
370
|
-
result2 = this.and(eb2, result2, eb2(sql.ref(`${
|
|
602
|
+
result2 = this.and(eb2, result2, eb2(sql.ref(`${modelAlias}.${pk}`), "=", sql.ref(`${relationFilterSelectAlias}.${fk}`)));
|
|
371
603
|
}
|
|
372
604
|
}
|
|
373
605
|
return result2;
|
|
@@ -380,30 +612,29 @@ var BaseCrudDialect = class {
|
|
|
380
612
|
}
|
|
381
613
|
switch (key) {
|
|
382
614
|
case "some": {
|
|
383
|
-
result = this.and(eb, result, eb(
|
|
615
|
+
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));
|
|
384
616
|
break;
|
|
385
617
|
}
|
|
386
618
|
case "every": {
|
|
387
|
-
result = this.and(eb, result, eb(
|
|
619
|
+
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));
|
|
388
620
|
break;
|
|
389
621
|
}
|
|
390
622
|
case "none": {
|
|
391
|
-
result = this.and(eb, result, eb(
|
|
623
|
+
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));
|
|
392
624
|
break;
|
|
393
625
|
}
|
|
394
626
|
}
|
|
395
627
|
}
|
|
396
628
|
return result;
|
|
397
629
|
}
|
|
398
|
-
buildArrayFilter(eb,
|
|
630
|
+
buildArrayFilter(eb, fieldRef, fieldDef, payload) {
|
|
399
631
|
const clauses = [];
|
|
400
632
|
const fieldType = fieldDef.type;
|
|
401
|
-
const fieldRef = buildFieldRef(this.schema, model, field, this.options, eb, modelAlias);
|
|
402
633
|
for (const [key, _value] of Object.entries(payload)) {
|
|
403
634
|
if (_value === void 0) {
|
|
404
635
|
continue;
|
|
405
636
|
}
|
|
406
|
-
const value = this.transformPrimitive(_value, fieldType);
|
|
637
|
+
const value = this.transformPrimitive(_value, fieldType, !!fieldDef.array);
|
|
407
638
|
switch (key) {
|
|
408
639
|
case "equals": {
|
|
409
640
|
clauses.push(this.buildLiteralFilter(eb, fieldRef, fieldType, eb.val(value)));
|
|
@@ -434,19 +665,23 @@ var BaseCrudDialect = class {
|
|
|
434
665
|
}
|
|
435
666
|
return this.and(eb, ...clauses);
|
|
436
667
|
}
|
|
437
|
-
buildPrimitiveFilter(eb,
|
|
668
|
+
buildPrimitiveFilter(eb, fieldRef, fieldDef, payload) {
|
|
438
669
|
if (payload === null) {
|
|
439
|
-
return eb(
|
|
670
|
+
return eb(fieldRef, "is", null);
|
|
440
671
|
}
|
|
441
672
|
if (isEnum(this.schema, fieldDef.type)) {
|
|
442
|
-
return this.buildEnumFilter(eb,
|
|
673
|
+
return this.buildEnumFilter(eb, fieldRef, fieldDef, payload);
|
|
443
674
|
}
|
|
444
|
-
return
|
|
675
|
+
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", () => {
|
|
676
|
+
throw new InternalError("JSON filters are not supported yet");
|
|
677
|
+
}).with("Unsupported", () => {
|
|
678
|
+
throw new QueryError(`Unsupported field cannot be used in filters`);
|
|
679
|
+
}).exhaustive();
|
|
445
680
|
}
|
|
446
681
|
buildLiteralFilter(eb, lhs, type, rhs) {
|
|
447
|
-
return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type) : rhs);
|
|
682
|
+
return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
|
|
448
683
|
}
|
|
449
|
-
buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
|
|
684
|
+
buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
|
|
450
685
|
if (payload === null || !isPlainObject(payload)) {
|
|
451
686
|
return {
|
|
452
687
|
conditions: [
|
|
@@ -461,8 +696,11 @@ var BaseCrudDialect = class {
|
|
|
461
696
|
if (onlyForKeys && !onlyForKeys.includes(op)) {
|
|
462
697
|
continue;
|
|
463
698
|
}
|
|
699
|
+
if (excludeKeys.includes(op)) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
464
702
|
const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
|
|
465
|
-
const condition =
|
|
703
|
+
const condition = match2(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
|
|
466
704
|
invariant(Array.isArray(rhs), "right hand side must be an array");
|
|
467
705
|
if (rhs.length === 0) {
|
|
468
706
|
return this.false(eb);
|
|
@@ -476,7 +714,11 @@ var BaseCrudDialect = class {
|
|
|
476
714
|
} else {
|
|
477
715
|
return eb.not(eb(lhs, "in", rhs));
|
|
478
716
|
}
|
|
479
|
-
}).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))).
|
|
717
|
+
}).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) => {
|
|
718
|
+
const innerResult = this.buildStandardFilter(eb, type, value, aggregate(eb, lhs, op2), getRhs, recurse, throwIfInvalid);
|
|
719
|
+
consumedKeys.push(...innerResult.consumedKeys);
|
|
720
|
+
return this.and(eb, ...innerResult.conditions);
|
|
721
|
+
}).otherwise(() => {
|
|
480
722
|
if (throwIfInvalid) {
|
|
481
723
|
throw new QueryError(`Invalid filter key: ${op}`);
|
|
482
724
|
} else {
|
|
@@ -493,24 +735,21 @@ var BaseCrudDialect = class {
|
|
|
493
735
|
consumedKeys
|
|
494
736
|
};
|
|
495
737
|
}
|
|
496
|
-
buildStringFilter(eb,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if (payload && typeof payload === "object" && "mode" in payload && payload.mode === "insensitive") {
|
|
501
|
-
insensitive = true;
|
|
502
|
-
fieldRef = eb.fn("lower", [
|
|
503
|
-
fieldRef
|
|
504
|
-
]);
|
|
738
|
+
buildStringFilter(eb, fieldRef, payload) {
|
|
739
|
+
let mode;
|
|
740
|
+
if (payload && typeof payload === "object" && "mode" in payload) {
|
|
741
|
+
mode = payload.mode;
|
|
505
742
|
}
|
|
506
|
-
const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload,
|
|
743
|
+
const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload, mode === "insensitive" ? eb.fn("lower", [
|
|
744
|
+
fieldRef
|
|
745
|
+
]) : fieldRef, (value) => this.prepStringCasing(eb, value, mode), (value) => this.buildStringFilter(eb, fieldRef, value));
|
|
507
746
|
if (payload && typeof payload === "object") {
|
|
508
747
|
for (const [key, value] of Object.entries(payload)) {
|
|
509
748
|
if (key === "mode" || consumedKeys.includes(key)) {
|
|
510
749
|
continue;
|
|
511
750
|
}
|
|
512
|
-
const condition =
|
|
513
|
-
throw new
|
|
751
|
+
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(() => {
|
|
752
|
+
throw new QueryError(`Invalid string filter key: ${key}`);
|
|
514
753
|
});
|
|
515
754
|
if (condition) {
|
|
516
755
|
conditions.push(condition);
|
|
@@ -519,34 +758,37 @@ var BaseCrudDialect = class {
|
|
|
519
758
|
}
|
|
520
759
|
return this.and(eb, ...conditions);
|
|
521
760
|
}
|
|
522
|
-
prepStringCasing(eb, value,
|
|
761
|
+
prepStringCasing(eb, value, mode) {
|
|
762
|
+
if (!mode || mode === "default") {
|
|
763
|
+
return value === null ? value : sql.val(value);
|
|
764
|
+
}
|
|
523
765
|
if (typeof value === "string") {
|
|
524
|
-
return
|
|
525
|
-
sql.
|
|
526
|
-
])
|
|
766
|
+
return eb.fn("lower", [
|
|
767
|
+
sql.val(value)
|
|
768
|
+
]);
|
|
527
769
|
} else if (Array.isArray(value)) {
|
|
528
|
-
return value.map((v) => this.prepStringCasing(eb, v,
|
|
770
|
+
return value.map((v) => this.prepStringCasing(eb, v, mode));
|
|
529
771
|
} else {
|
|
530
|
-
return value === null ? null : sql.
|
|
772
|
+
return value === null ? null : sql.val(value);
|
|
531
773
|
}
|
|
532
774
|
}
|
|
533
|
-
buildNumberFilter(eb,
|
|
534
|
-
const { conditions } = this.buildStandardFilter(eb, type, payload,
|
|
775
|
+
buildNumberFilter(eb, fieldRef, type, payload) {
|
|
776
|
+
const { conditions } = this.buildStandardFilter(eb, type, payload, fieldRef, (value) => this.transformPrimitive(value, type, false), (value) => this.buildNumberFilter(eb, fieldRef, type, value));
|
|
535
777
|
return this.and(eb, ...conditions);
|
|
536
778
|
}
|
|
537
|
-
buildBooleanFilter(eb,
|
|
538
|
-
const { conditions } = this.buildStandardFilter(eb, "Boolean", payload,
|
|
779
|
+
buildBooleanFilter(eb, fieldRef, payload) {
|
|
780
|
+
const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, fieldRef, (value) => this.transformPrimitive(value, "Boolean", false), (value) => this.buildBooleanFilter(eb, fieldRef, value), true, [
|
|
539
781
|
"equals",
|
|
540
782
|
"not"
|
|
541
783
|
]);
|
|
542
784
|
return this.and(eb, ...conditions);
|
|
543
785
|
}
|
|
544
|
-
buildDateTimeFilter(eb,
|
|
545
|
-
const { conditions } = this.buildStandardFilter(eb, "DateTime", payload,
|
|
786
|
+
buildDateTimeFilter(eb, fieldRef, payload) {
|
|
787
|
+
const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, fieldRef, (value) => this.transformPrimitive(value, "DateTime", false), (value) => this.buildDateTimeFilter(eb, fieldRef, value), true);
|
|
546
788
|
return this.and(eb, ...conditions);
|
|
547
789
|
}
|
|
548
|
-
buildBytesFilter(eb,
|
|
549
|
-
const conditions = this.buildStandardFilter(eb, "Bytes", payload,
|
|
790
|
+
buildBytesFilter(eb, fieldRef, payload) {
|
|
791
|
+
const conditions = this.buildStandardFilter(eb, "Bytes", payload, fieldRef, (value) => this.transformPrimitive(value, "Bytes", false), (value) => this.buildBytesFilter(eb, fieldRef, value), true, [
|
|
550
792
|
"equals",
|
|
551
793
|
"in",
|
|
552
794
|
"notIn",
|
|
@@ -554,8 +796,8 @@ var BaseCrudDialect = class {
|
|
|
554
796
|
]);
|
|
555
797
|
return this.and(eb, ...conditions.conditions);
|
|
556
798
|
}
|
|
557
|
-
buildEnumFilter(eb,
|
|
558
|
-
const conditions = this.buildStandardFilter(eb, "String", payload,
|
|
799
|
+
buildEnumFilter(eb, fieldRef, fieldDef, payload) {
|
|
800
|
+
const conditions = this.buildStandardFilter(eb, "String", payload, fieldRef, (value) => value, (value) => this.buildEnumFilter(eb, fieldRef, fieldDef, value), true, [
|
|
559
801
|
"equals",
|
|
560
802
|
"in",
|
|
561
803
|
"notIn",
|
|
@@ -587,9 +829,7 @@ var BaseCrudDialect = class {
|
|
|
587
829
|
invariant(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
588
830
|
for (const [k, v] of Object.entries(value)) {
|
|
589
831
|
invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
590
|
-
result = result.orderBy((eb) => eb.
|
|
591
|
-
sql.ref(k)
|
|
592
|
-
]), sql.raw(this.negateSort(v, negated)));
|
|
832
|
+
result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), sql.raw(this.negateSort(v, negated)));
|
|
593
833
|
}
|
|
594
834
|
continue;
|
|
595
835
|
}
|
|
@@ -598,7 +838,7 @@ var BaseCrudDialect = class {
|
|
|
598
838
|
invariant(value && typeof value === "object", 'invalid orderBy value for field "_count"');
|
|
599
839
|
for (const [k, v] of Object.entries(value)) {
|
|
600
840
|
invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
601
|
-
result = result.orderBy((eb) => eb.fn.count(
|
|
841
|
+
result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), sql.raw(this.negateSort(v, negated)));
|
|
602
842
|
}
|
|
603
843
|
continue;
|
|
604
844
|
}
|
|
@@ -607,10 +847,11 @@ var BaseCrudDialect = class {
|
|
|
607
847
|
}
|
|
608
848
|
const fieldDef = requireField(this.schema, model, field);
|
|
609
849
|
if (!fieldDef.relation) {
|
|
850
|
+
const fieldRef = this.fieldRef(model, field, expressionBuilder(), modelAlias);
|
|
610
851
|
if (value === "asc" || value === "desc") {
|
|
611
|
-
result = result.orderBy(
|
|
852
|
+
result = result.orderBy(fieldRef, this.negateSort(value, negated));
|
|
612
853
|
} else if (value && typeof value === "object" && "nulls" in value && "sort" in value && (value.sort === "asc" || value.sort === "desc") && (value.nulls === "first" || value.nulls === "last")) {
|
|
613
|
-
result = result.orderBy(
|
|
854
|
+
result = result.orderBy(fieldRef, sql.raw(`${this.negateSort(value.sort, negated)} nulls ${value.nulls}`));
|
|
614
855
|
}
|
|
615
856
|
} else {
|
|
616
857
|
const relationModel = fieldDef.type;
|
|
@@ -622,8 +863,9 @@ var BaseCrudDialect = class {
|
|
|
622
863
|
invariant(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
|
|
623
864
|
const sort = this.negateSort(value._count, negated);
|
|
624
865
|
result = result.orderBy((eb) => {
|
|
625
|
-
|
|
626
|
-
|
|
866
|
+
const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
|
|
867
|
+
let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
|
|
868
|
+
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
627
869
|
subQuery = subQuery.where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(sql.ref(left), "=", sql.ref(right)))));
|
|
628
870
|
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
629
871
|
return subQuery;
|
|
@@ -641,14 +883,90 @@ var BaseCrudDialect = class {
|
|
|
641
883
|
});
|
|
642
884
|
return result;
|
|
643
885
|
}
|
|
886
|
+
buildSelectAllFields(model, query, omit, modelAlias) {
|
|
887
|
+
const modelDef = requireModel(this.schema, model);
|
|
888
|
+
let result = query;
|
|
889
|
+
for (const field of Object.keys(modelDef.fields)) {
|
|
890
|
+
if (isRelationField(this.schema, model, field)) {
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (omit?.[field] === true) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
result = this.buildSelectField(result, model, modelAlias, field);
|
|
897
|
+
}
|
|
898
|
+
const descendants = getDelegateDescendantModels(this.schema, model);
|
|
899
|
+
for (const subModel of descendants) {
|
|
900
|
+
result = this.buildDelegateJoin(model, modelAlias, subModel.name, result);
|
|
901
|
+
result = result.select((eb) => {
|
|
902
|
+
const jsonObject = {};
|
|
903
|
+
for (const field of Object.keys(subModel.fields)) {
|
|
904
|
+
if (isRelationField(this.schema, subModel.name, field) || isInheritedField(this.schema, subModel.name, field)) {
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
jsonObject[field] = eb.ref(`${subModel.name}.${field}`);
|
|
908
|
+
}
|
|
909
|
+
return this.buildJsonObject(eb, jsonObject).as(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`);
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
return result;
|
|
913
|
+
}
|
|
914
|
+
buildSelectField(query, model, modelAlias, field) {
|
|
915
|
+
const fieldDef = requireField(this.schema, model, field);
|
|
916
|
+
if (fieldDef.computed) {
|
|
917
|
+
return query.select((eb) => this.fieldRef(model, field, eb, modelAlias).as(field));
|
|
918
|
+
} else if (!fieldDef.originModel) {
|
|
919
|
+
return query.select(sql.ref(`${modelAlias}.${field}`).as(field));
|
|
920
|
+
} else {
|
|
921
|
+
return this.buildSelectField(query, fieldDef.originModel, fieldDef.originModel, field);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
|
|
925
|
+
const idFields = getIdFields(this.schema, thisModel);
|
|
926
|
+
query = query.leftJoin(otherModelAlias, (qb) => {
|
|
927
|
+
for (const idField of idFields) {
|
|
928
|
+
qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
|
|
929
|
+
}
|
|
930
|
+
return qb;
|
|
931
|
+
});
|
|
932
|
+
return query;
|
|
933
|
+
}
|
|
934
|
+
buildCountJson(model, eb, parentAlias, payload) {
|
|
935
|
+
const modelDef = requireModel(this.schema, model);
|
|
936
|
+
const toManyRelations = Object.entries(modelDef.fields).filter(([, field]) => field.relation && field.array);
|
|
937
|
+
const selections = payload === true ? {
|
|
938
|
+
select: toManyRelations.reduce((acc, [field]) => {
|
|
939
|
+
acc[field] = true;
|
|
940
|
+
return acc;
|
|
941
|
+
}, {})
|
|
942
|
+
} : payload;
|
|
943
|
+
const jsonObject = {};
|
|
944
|
+
for (const [field, value] of Object.entries(selections.select)) {
|
|
945
|
+
const fieldDef = requireField(this.schema, model, field);
|
|
946
|
+
const fieldModel = fieldDef.type;
|
|
947
|
+
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
|
|
948
|
+
let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
|
|
949
|
+
for (const [left, right] of joinPairs) {
|
|
950
|
+
fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
|
|
951
|
+
}
|
|
952
|
+
if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
|
|
953
|
+
const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
|
|
954
|
+
fieldCountQuery = fieldCountQuery.where(filter);
|
|
955
|
+
}
|
|
956
|
+
jsonObject[field] = fieldCountQuery;
|
|
957
|
+
}
|
|
958
|
+
return this.buildJsonObject(eb, jsonObject);
|
|
959
|
+
}
|
|
960
|
+
// #endregion
|
|
961
|
+
// #region utils
|
|
644
962
|
negateSort(sort, negated) {
|
|
645
963
|
return negated ? sort === "asc" ? "desc" : "asc" : sort;
|
|
646
964
|
}
|
|
647
965
|
true(eb) {
|
|
648
|
-
return eb.lit(this.transformPrimitive(true, "Boolean"));
|
|
966
|
+
return eb.lit(this.transformPrimitive(true, "Boolean", false));
|
|
649
967
|
}
|
|
650
968
|
false(eb) {
|
|
651
|
-
return eb.lit(this.transformPrimitive(false, "Boolean"));
|
|
969
|
+
return eb.lit(this.transformPrimitive(false, "Boolean", false));
|
|
652
970
|
}
|
|
653
971
|
isTrue(expression) {
|
|
654
972
|
const node = expression.toOperationNode();
|
|
@@ -687,6 +1005,9 @@ var BaseCrudDialect = class {
|
|
|
687
1005
|
not(eb, ...args) {
|
|
688
1006
|
return eb.not(this.and(eb, ...args));
|
|
689
1007
|
}
|
|
1008
|
+
fieldRef(model, field, eb, modelAlias, inlineComputedField = true) {
|
|
1009
|
+
return buildFieldRef(this.schema, model, field, this.options, eb, modelAlias, inlineComputedField);
|
|
1010
|
+
}
|
|
690
1011
|
};
|
|
691
1012
|
|
|
692
1013
|
// src/client/crud/dialects/postgresql.ts
|
|
@@ -697,14 +1018,18 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
697
1018
|
get provider() {
|
|
698
1019
|
return "postgresql";
|
|
699
1020
|
}
|
|
700
|
-
transformPrimitive(value, type) {
|
|
1021
|
+
transformPrimitive(value, type, forArrayField) {
|
|
701
1022
|
if (value === void 0) {
|
|
702
1023
|
return value;
|
|
703
1024
|
}
|
|
704
1025
|
if (Array.isArray(value)) {
|
|
705
|
-
|
|
1026
|
+
if (type === "Json" && !forArrayField) {
|
|
1027
|
+
return JSON.stringify(value);
|
|
1028
|
+
} else {
|
|
1029
|
+
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
1030
|
+
}
|
|
706
1031
|
} else {
|
|
707
|
-
return
|
|
1032
|
+
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);
|
|
708
1033
|
}
|
|
709
1034
|
}
|
|
710
1035
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
@@ -717,21 +1042,12 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
717
1042
|
return qb.leftJoinLateral((eb) => {
|
|
718
1043
|
const joinTableName = `${parentName}$${relationField}`;
|
|
719
1044
|
let result = eb.selectFrom(`${relationModel} as ${joinTableName}`);
|
|
1045
|
+
const subQueryAlias = `${relationModel}$${relationField}$sub`;
|
|
720
1046
|
result = eb.selectFrom(() => {
|
|
721
|
-
let subQuery =
|
|
1047
|
+
let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
|
|
1048
|
+
subQuery = this.buildSelectAllFields(relationModel, subQuery, typeof payload === "object" ? payload?.omit : void 0, subQueryAlias);
|
|
722
1049
|
if (payload && typeof payload === "object") {
|
|
723
|
-
|
|
724
|
-
subQuery = subQuery.where((eb2) => this.buildFilter(eb2, relationModel, relationModel, payload.where));
|
|
725
|
-
}
|
|
726
|
-
const skip = payload.skip;
|
|
727
|
-
let take = payload.take;
|
|
728
|
-
let negateOrderBy = false;
|
|
729
|
-
if (take !== void 0 && take < 0) {
|
|
730
|
-
negateOrderBy = true;
|
|
731
|
-
take = -take;
|
|
732
|
-
}
|
|
733
|
-
subQuery = this.buildSkipTake(subQuery, skip, take);
|
|
734
|
-
subQuery = this.buildOrderBy(subQuery, relationModel, relationModel, payload.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
1050
|
+
subQuery = this.buildFilterSortTake(relationModel, payload, subQuery, subQueryAlias);
|
|
735
1051
|
}
|
|
736
1052
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
737
1053
|
if (m2m) {
|
|
@@ -739,21 +1055,21 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
739
1055
|
const relationIds = getIdFields(this.schema, relationModel);
|
|
740
1056
|
invariant2(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
741
1057
|
invariant2(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
742
|
-
subQuery = subQuery.where(eb(eb.ref(`${
|
|
1058
|
+
subQuery = subQuery.where(eb(eb.ref(`${subQueryAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentName}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
743
1059
|
} else {
|
|
744
|
-
const joinPairs = buildJoinPairs(this.schema, model, parentName, relationField,
|
|
1060
|
+
const joinPairs = buildJoinPairs(this.schema, model, parentName, relationField, subQueryAlias);
|
|
745
1061
|
subQuery = subQuery.where((eb2) => this.and(eb2, ...joinPairs.map(([left, right]) => eb2(sql2.ref(left), "=", sql2.ref(right)))));
|
|
746
1062
|
}
|
|
747
1063
|
return subQuery.as(joinTableName);
|
|
748
1064
|
});
|
|
749
|
-
result = this.buildRelationObjectSelect(relationModel, relationField, relationFieldDef, result, payload, parentName);
|
|
1065
|
+
result = this.buildRelationObjectSelect(relationModel, joinTableName, relationField, relationFieldDef, result, payload, parentName);
|
|
750
1066
|
result = this.buildRelationJoins(relationModel, relationField, result, payload, parentName);
|
|
751
1067
|
return result.as(joinTableName);
|
|
752
1068
|
}, (join) => join.onTrue());
|
|
753
1069
|
}
|
|
754
|
-
buildRelationObjectSelect(relationModel, relationField, relationFieldDef, qb, payload, parentName) {
|
|
1070
|
+
buildRelationObjectSelect(relationModel, relationModelAlias, relationField, relationFieldDef, qb, payload, parentName) {
|
|
755
1071
|
qb = qb.select((eb) => {
|
|
756
|
-
const objArgs = this.buildRelationObjectArgs(relationModel, relationField, eb, payload, parentName);
|
|
1072
|
+
const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, relationField, eb, payload, parentName);
|
|
757
1073
|
if (relationFieldDef.array) {
|
|
758
1074
|
return eb.fn.coalesce(sql2`jsonb_agg(jsonb_build_object(${sql2.join(objArgs)}))`, sql2`'[]'::jsonb`).as("$j");
|
|
759
1075
|
} else {
|
|
@@ -762,34 +1078,57 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
762
1078
|
});
|
|
763
1079
|
return qb;
|
|
764
1080
|
}
|
|
765
|
-
buildRelationObjectArgs(relationModel, relationField, eb, payload,
|
|
1081
|
+
buildRelationObjectArgs(relationModel, relationModelAlias, relationField, eb, payload, parentAlias) {
|
|
766
1082
|
const relationModelDef = requireModel(this.schema, relationModel);
|
|
767
1083
|
const objArgs = [];
|
|
1084
|
+
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1085
|
+
if (descendantModels.length > 0) {
|
|
1086
|
+
objArgs.push(...descendantModels.map((subModel) => [
|
|
1087
|
+
sql2.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1088
|
+
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1089
|
+
]).flatMap((v) => v));
|
|
1090
|
+
}
|
|
768
1091
|
if (payload === true || !payload.select) {
|
|
769
1092
|
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
770
1093
|
sql2.lit(field),
|
|
771
|
-
|
|
1094
|
+
this.fieldRef(relationModel, field, eb, relationModelAlias, false)
|
|
772
1095
|
]).flatMap((v) => v));
|
|
773
1096
|
} else if (payload.select) {
|
|
774
|
-
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) =>
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
1097
|
+
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
|
|
1098
|
+
if (field === "_count") {
|
|
1099
|
+
const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
|
|
1100
|
+
return [
|
|
1101
|
+
sql2.lit(field),
|
|
1102
|
+
subJson
|
|
1103
|
+
];
|
|
1104
|
+
} else {
|
|
1105
|
+
const fieldDef = requireField(this.schema, relationModel, field);
|
|
1106
|
+
const fieldValue = fieldDef.relation ? eb.ref(`${parentAlias}$${relationField}$${field}.$j`) : this.fieldRef(relationModel, field, eb, void 0, false);
|
|
1107
|
+
return [
|
|
1108
|
+
sql2.lit(field),
|
|
1109
|
+
fieldValue
|
|
1110
|
+
];
|
|
1111
|
+
}
|
|
1112
|
+
}).flatMap((v) => v));
|
|
778
1113
|
}
|
|
779
1114
|
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
780
1115
|
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
|
|
781
1116
|
sql2.lit(field),
|
|
782
|
-
|
|
1117
|
+
// reference the synthesized JSON field
|
|
1118
|
+
eb.ref(`${parentAlias}$${relationField}$${field}.$j`)
|
|
783
1119
|
]).flatMap((v) => v));
|
|
784
1120
|
}
|
|
785
1121
|
return objArgs;
|
|
786
1122
|
}
|
|
787
|
-
buildRelationJoins(
|
|
1123
|
+
buildRelationJoins(relationModel, relationField, qb, payload, parentName) {
|
|
788
1124
|
let result = qb;
|
|
789
|
-
if (typeof payload === "object"
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
1125
|
+
if (typeof payload === "object") {
|
|
1126
|
+
const selectInclude = payload.include ?? payload.select;
|
|
1127
|
+
if (selectInclude && typeof selectInclude === "object") {
|
|
1128
|
+
Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
|
|
1129
|
+
result = this.buildRelationJSON(relationModel, result, field, `${parentName}$${relationField}`, value);
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
793
1132
|
}
|
|
794
1133
|
return result;
|
|
795
1134
|
}
|
|
@@ -829,12 +1168,15 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
829
1168
|
return `ARRAY[${values.map((v) => typeof v === "string" ? `'${v}'` : v)}]`;
|
|
830
1169
|
}
|
|
831
1170
|
}
|
|
1171
|
+
get supportInsertWithDefault() {
|
|
1172
|
+
return true;
|
|
1173
|
+
}
|
|
832
1174
|
};
|
|
833
1175
|
|
|
834
1176
|
// src/client/crud/dialects/sqlite.ts
|
|
1177
|
+
import { invariant as invariant3 } from "@zenstackhq/common-helpers";
|
|
835
1178
|
import { sql as sql3 } from "kysely";
|
|
836
|
-
import
|
|
837
|
-
import { match as match3 } from "ts-pattern";
|
|
1179
|
+
import { match as match4 } from "ts-pattern";
|
|
838
1180
|
var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
839
1181
|
static {
|
|
840
1182
|
__name(this, "SqliteCrudDialect");
|
|
@@ -842,39 +1184,34 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
842
1184
|
get provider() {
|
|
843
1185
|
return "sqlite";
|
|
844
1186
|
}
|
|
845
|
-
transformPrimitive(value, type) {
|
|
1187
|
+
transformPrimitive(value, type, _forArrayField) {
|
|
846
1188
|
if (value === void 0) {
|
|
847
1189
|
return value;
|
|
848
1190
|
}
|
|
849
1191
|
if (Array.isArray(value)) {
|
|
850
|
-
return value.map((v) => this.transformPrimitive(v, type));
|
|
1192
|
+
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
851
1193
|
} else {
|
|
852
|
-
|
|
1194
|
+
if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1195
|
+
return JSON.stringify(value);
|
|
1196
|
+
} else {
|
|
1197
|
+
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);
|
|
1198
|
+
}
|
|
853
1199
|
}
|
|
854
1200
|
}
|
|
855
1201
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
856
1202
|
return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
|
|
857
1203
|
}
|
|
858
|
-
buildRelationJSON(model, eb, relationField,
|
|
1204
|
+
buildRelationJSON(model, eb, relationField, parentAlias, payload) {
|
|
859
1205
|
const relationFieldDef = requireField(this.schema, model, relationField);
|
|
860
1206
|
const relationModel = relationFieldDef.type;
|
|
861
1207
|
const relationModelDef = requireModel(this.schema, relationModel);
|
|
862
|
-
const subQueryName = `${
|
|
1208
|
+
const subQueryName = `${parentAlias}$${relationField}`;
|
|
863
1209
|
let tbl = eb.selectFrom(() => {
|
|
864
|
-
|
|
1210
|
+
const subQueryAlias = `${parentAlias}$${relationField}$sub`;
|
|
1211
|
+
let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
|
|
1212
|
+
subQuery = this.buildSelectAllFields(relationModel, subQuery, typeof payload === "object" ? payload?.omit : void 0, subQueryAlias);
|
|
865
1213
|
if (payload && typeof payload === "object") {
|
|
866
|
-
|
|
867
|
-
subQuery = subQuery.where((eb2) => this.buildFilter(eb2, relationModel, relationModel, payload.where));
|
|
868
|
-
}
|
|
869
|
-
const skip = payload.skip;
|
|
870
|
-
let take = payload.take;
|
|
871
|
-
let negateOrderBy = false;
|
|
872
|
-
if (take !== void 0 && take < 0) {
|
|
873
|
-
negateOrderBy = true;
|
|
874
|
-
take = -take;
|
|
875
|
-
}
|
|
876
|
-
subQuery = this.buildSkipTake(subQuery, skip, take);
|
|
877
|
-
subQuery = this.buildOrderBy(subQuery, relationModel, relationModel, payload.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
1214
|
+
subQuery = this.buildFilterSortTake(relationModel, payload, subQuery, subQueryAlias);
|
|
878
1215
|
}
|
|
879
1216
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
880
1217
|
if (m2m) {
|
|
@@ -882,14 +1219,14 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
882
1219
|
const relationIds = getIdFields(this.schema, relationModel);
|
|
883
1220
|
invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
884
1221
|
invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
885
|
-
subQuery = subQuery.where(eb(eb.ref(`${
|
|
1222
|
+
subQuery = subQuery.where(eb(eb.ref(`${subQueryAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
886
1223
|
} else {
|
|
887
1224
|
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
888
1225
|
keyPairs.forEach(({ fk, pk }) => {
|
|
889
1226
|
if (ownedByModel) {
|
|
890
|
-
subQuery = subQuery.whereRef(`${
|
|
1227
|
+
subQuery = subQuery.whereRef(`${subQueryAlias}.${pk}`, "=", `${parentAlias}.${fk}`);
|
|
891
1228
|
} else {
|
|
892
|
-
subQuery = subQuery.whereRef(`${
|
|
1229
|
+
subQuery = subQuery.whereRef(`${subQueryAlias}.${fk}`, "=", `${parentAlias}.${pk}`);
|
|
893
1230
|
}
|
|
894
1231
|
});
|
|
895
1232
|
}
|
|
@@ -897,31 +1234,46 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
897
1234
|
});
|
|
898
1235
|
tbl = tbl.select(() => {
|
|
899
1236
|
const objArgs = [];
|
|
1237
|
+
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1238
|
+
if (descendantModels.length > 0) {
|
|
1239
|
+
objArgs.push(...descendantModels.map((subModel) => [
|
|
1240
|
+
sql3.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1241
|
+
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1242
|
+
]).flatMap((v) => v));
|
|
1243
|
+
}
|
|
900
1244
|
if (payload === true || !payload.select) {
|
|
901
1245
|
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
902
1246
|
sql3.lit(field),
|
|
903
|
-
|
|
1247
|
+
this.fieldRef(relationModel, field, eb, void 0, false)
|
|
904
1248
|
]).flatMap((v) => v));
|
|
905
1249
|
} else if (payload.select) {
|
|
906
1250
|
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentName}$${relationField}`, value);
|
|
1251
|
+
if (field === "_count") {
|
|
1252
|
+
const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
|
|
910
1253
|
return [
|
|
911
1254
|
sql3.lit(field),
|
|
912
1255
|
subJson
|
|
913
1256
|
];
|
|
914
1257
|
} else {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1258
|
+
const fieldDef = requireField(this.schema, relationModel, field);
|
|
1259
|
+
if (fieldDef.relation) {
|
|
1260
|
+
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
1261
|
+
return [
|
|
1262
|
+
sql3.lit(field),
|
|
1263
|
+
subJson
|
|
1264
|
+
];
|
|
1265
|
+
} else {
|
|
1266
|
+
return [
|
|
1267
|
+
sql3.lit(field),
|
|
1268
|
+
this.fieldRef(relationModel, field, eb, void 0, false)
|
|
1269
|
+
];
|
|
1270
|
+
}
|
|
919
1271
|
}
|
|
920
1272
|
}).flatMap((v) => v));
|
|
921
1273
|
}
|
|
922
1274
|
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
923
1275
|
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
|
|
924
|
-
const subJson = this.buildRelationJSON(relationModel, eb, field, `${
|
|
1276
|
+
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
925
1277
|
return [
|
|
926
1278
|
sql3.lit(field),
|
|
927
1279
|
subJson
|
|
@@ -971,93 +1323,17 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
971
1323
|
buildArrayLiteralSQL(_values) {
|
|
972
1324
|
throw new Error("SQLite does not support array literals");
|
|
973
1325
|
}
|
|
1326
|
+
get supportInsertWithDefault() {
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
974
1329
|
};
|
|
975
1330
|
|
|
976
1331
|
// src/client/crud/dialects/index.ts
|
|
977
1332
|
function getCrudDialect(schema, options) {
|
|
978
|
-
return
|
|
1333
|
+
return match5(schema.provider.type).with("sqlite", () => new SqliteCrudDialect(schema, options)).with("postgresql", () => new PostgresCrudDialect(schema, options)).exhaustive();
|
|
979
1334
|
}
|
|
980
1335
|
__name(getCrudDialect, "getCrudDialect");
|
|
981
1336
|
|
|
982
|
-
// src/schema/expression.ts
|
|
983
|
-
var ExpressionUtils = {
|
|
984
|
-
literal: /* @__PURE__ */ __name((value) => {
|
|
985
|
-
return {
|
|
986
|
-
kind: "literal",
|
|
987
|
-
value
|
|
988
|
-
};
|
|
989
|
-
}, "literal"),
|
|
990
|
-
array: /* @__PURE__ */ __name((items) => {
|
|
991
|
-
return {
|
|
992
|
-
kind: "array",
|
|
993
|
-
items
|
|
994
|
-
};
|
|
995
|
-
}, "array"),
|
|
996
|
-
call: /* @__PURE__ */ __name((functionName, args) => {
|
|
997
|
-
return {
|
|
998
|
-
kind: "call",
|
|
999
|
-
function: functionName,
|
|
1000
|
-
args
|
|
1001
|
-
};
|
|
1002
|
-
}, "call"),
|
|
1003
|
-
binary: /* @__PURE__ */ __name((left, op, right) => {
|
|
1004
|
-
return {
|
|
1005
|
-
kind: "binary",
|
|
1006
|
-
op,
|
|
1007
|
-
left,
|
|
1008
|
-
right
|
|
1009
|
-
};
|
|
1010
|
-
}, "binary"),
|
|
1011
|
-
unary: /* @__PURE__ */ __name((op, operand) => {
|
|
1012
|
-
return {
|
|
1013
|
-
kind: "unary",
|
|
1014
|
-
op,
|
|
1015
|
-
operand
|
|
1016
|
-
};
|
|
1017
|
-
}, "unary"),
|
|
1018
|
-
field: /* @__PURE__ */ __name((field) => {
|
|
1019
|
-
return {
|
|
1020
|
-
kind: "field",
|
|
1021
|
-
field
|
|
1022
|
-
};
|
|
1023
|
-
}, "field"),
|
|
1024
|
-
member: /* @__PURE__ */ __name((receiver, members) => {
|
|
1025
|
-
return {
|
|
1026
|
-
kind: "member",
|
|
1027
|
-
receiver,
|
|
1028
|
-
members
|
|
1029
|
-
};
|
|
1030
|
-
}, "member"),
|
|
1031
|
-
_this: /* @__PURE__ */ __name(() => {
|
|
1032
|
-
return {
|
|
1033
|
-
kind: "this"
|
|
1034
|
-
};
|
|
1035
|
-
}, "_this"),
|
|
1036
|
-
_null: /* @__PURE__ */ __name(() => {
|
|
1037
|
-
return {
|
|
1038
|
-
kind: "null"
|
|
1039
|
-
};
|
|
1040
|
-
}, "_null"),
|
|
1041
|
-
and: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
1042
|
-
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "&&", exp), expr2);
|
|
1043
|
-
}, "and"),
|
|
1044
|
-
or: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
1045
|
-
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "||", exp), expr2);
|
|
1046
|
-
}, "or"),
|
|
1047
|
-
is: /* @__PURE__ */ __name((value, kind) => {
|
|
1048
|
-
return !!value && typeof value === "object" && "kind" in value && value.kind === kind;
|
|
1049
|
-
}, "is"),
|
|
1050
|
-
isLiteral: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "literal"), "isLiteral"),
|
|
1051
|
-
isArray: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "array"), "isArray"),
|
|
1052
|
-
isCall: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "call"), "isCall"),
|
|
1053
|
-
isNull: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "null"), "isNull"),
|
|
1054
|
-
isThis: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "this"), "isThis"),
|
|
1055
|
-
isUnary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "unary"), "isUnary"),
|
|
1056
|
-
isBinary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "binary"), "isBinary"),
|
|
1057
|
-
isField: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "field"), "isField"),
|
|
1058
|
-
isMember: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "member"), "isMember")
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
1337
|
// src/utils/default-operation-node-visitor.ts
|
|
1062
1338
|
import { OperationNodeVisitor } from "kysely";
|
|
1063
1339
|
var DefaultOperationNodeVisitor = class extends OperationNodeVisitor {
|
|
@@ -1377,19 +1653,19 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
|
|
|
1377
1653
|
};
|
|
1378
1654
|
|
|
1379
1655
|
// src/plugins/policy/expression-transformer.ts
|
|
1380
|
-
import {
|
|
1381
|
-
import
|
|
1382
|
-
import { match as
|
|
1656
|
+
import { invariant as invariant5 } from "@zenstackhq/common-helpers";
|
|
1657
|
+
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";
|
|
1658
|
+
import { match as match7 } from "ts-pattern";
|
|
1383
1659
|
|
|
1384
1660
|
// src/plugins/policy/expression-evaluator.ts
|
|
1385
|
-
import invariant4 from "
|
|
1386
|
-
import { match as
|
|
1661
|
+
import { invariant as invariant4 } from "@zenstackhq/common-helpers";
|
|
1662
|
+
import { match as match6 } from "ts-pattern";
|
|
1387
1663
|
var ExpressionEvaluator = class {
|
|
1388
1664
|
static {
|
|
1389
1665
|
__name(this, "ExpressionEvaluator");
|
|
1390
1666
|
}
|
|
1391
1667
|
evaluate(expression, context) {
|
|
1392
|
-
const result =
|
|
1668
|
+
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();
|
|
1393
1669
|
return result ?? null;
|
|
1394
1670
|
}
|
|
1395
1671
|
evaluateCall(expr2, context) {
|
|
@@ -1400,7 +1676,7 @@ var ExpressionEvaluator = class {
|
|
|
1400
1676
|
}
|
|
1401
1677
|
}
|
|
1402
1678
|
evaluateUnary(expr2, context) {
|
|
1403
|
-
return
|
|
1679
|
+
return match6(expr2.op).with("!", () => !this.evaluate(expr2.operand, context)).exhaustive();
|
|
1404
1680
|
}
|
|
1405
1681
|
evaluateMember(expr2, context) {
|
|
1406
1682
|
let val = this.evaluate(expr2.receiver, context);
|
|
@@ -1424,7 +1700,7 @@ var ExpressionEvaluator = class {
|
|
|
1424
1700
|
}
|
|
1425
1701
|
const left = this.evaluate(expr2.left, context);
|
|
1426
1702
|
const right = this.evaluate(expr2.right, context);
|
|
1427
|
-
return
|
|
1703
|
+
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", () => {
|
|
1428
1704
|
const _right = right ?? [];
|
|
1429
1705
|
invariant4(Array.isArray(_right), 'expected array for "in" operator');
|
|
1430
1706
|
return _right.includes(left);
|
|
@@ -1438,7 +1714,7 @@ var ExpressionEvaluator = class {
|
|
|
1438
1714
|
return false;
|
|
1439
1715
|
}
|
|
1440
1716
|
invariant4(Array.isArray(left), "expected array");
|
|
1441
|
-
return
|
|
1717
|
+
return match6(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
|
|
1442
1718
|
...context,
|
|
1443
1719
|
thisValue: item
|
|
1444
1720
|
}))).with("!", () => left.every((item) => this.evaluate(expr2.right, {
|
|
@@ -1454,11 +1730,11 @@ var ExpressionEvaluator = class {
|
|
|
1454
1730
|
// src/plugins/policy/utils.ts
|
|
1455
1731
|
import { AliasNode, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode, TableNode, UnaryOperationNode, ValueNode } from "kysely";
|
|
1456
1732
|
function trueNode(dialect) {
|
|
1457
|
-
return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean"));
|
|
1733
|
+
return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
|
|
1458
1734
|
}
|
|
1459
1735
|
__name(trueNode, "trueNode");
|
|
1460
1736
|
function falseNode(dialect) {
|
|
1461
|
-
return ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean"));
|
|
1737
|
+
return ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
|
|
1462
1738
|
}
|
|
1463
1739
|
__name(falseNode, "falseNode");
|
|
1464
1740
|
function isTrueNode(node) {
|
|
@@ -1692,7 +1968,7 @@ var ExpressionTransformer = class {
|
|
|
1692
1968
|
const count = FunctionNode2.create("count", [
|
|
1693
1969
|
ValueNode2.createImmediate(1)
|
|
1694
1970
|
]);
|
|
1695
|
-
const predicateResult =
|
|
1971
|
+
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();
|
|
1696
1972
|
return this.transform(expr2.left, {
|
|
1697
1973
|
...context,
|
|
1698
1974
|
memberSelect: SelectionNode.create(AliasNode2.create(predicateResult, IdentifierNode.create("$t"))),
|
|
@@ -1716,14 +1992,14 @@ var ExpressionTransformer = class {
|
|
|
1716
1992
|
}
|
|
1717
1993
|
}
|
|
1718
1994
|
transformValue(value, type) {
|
|
1719
|
-
return ValueNode2.create(this.dialect.transformPrimitive(value, type) ?? null);
|
|
1995
|
+
return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
|
|
1720
1996
|
}
|
|
1721
1997
|
_unary(expr2, context) {
|
|
1722
1998
|
invariant5(expr2.op === "!", 'only "!" operator is supported');
|
|
1723
1999
|
return BinaryOperationNode2.create(this.transform(expr2.operand, context), this.transformOperator("!="), trueNode(this.dialect));
|
|
1724
2000
|
}
|
|
1725
2001
|
transformOperator(op) {
|
|
1726
|
-
const mappedOp =
|
|
2002
|
+
const mappedOp = match7(op).with("==", () => "=").otherwise(() => op);
|
|
1727
2003
|
return OperatorNode2.create(mappedOp);
|
|
1728
2004
|
}
|
|
1729
2005
|
_call(expr2, context) {
|
|
@@ -1735,7 +2011,7 @@ var ExpressionTransformer = class {
|
|
|
1735
2011
|
if (!func) {
|
|
1736
2012
|
throw new QueryError(`Function not implemented: ${expr2.function}`);
|
|
1737
2013
|
}
|
|
1738
|
-
const eb =
|
|
2014
|
+
const eb = expressionBuilder2();
|
|
1739
2015
|
return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
|
|
1740
2016
|
dialect: this.dialect,
|
|
1741
2017
|
model: context.model,
|
|
@@ -1959,7 +2235,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
1959
2235
|
get kysely() {
|
|
1960
2236
|
return this.client.$qb;
|
|
1961
2237
|
}
|
|
1962
|
-
async handle(node, proceed
|
|
2238
|
+
async handle(node, proceed) {
|
|
1963
2239
|
if (!this.isCrudQueryNode(node)) {
|
|
1964
2240
|
throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
|
|
1965
2241
|
}
|
|
@@ -1979,27 +2255,20 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
1979
2255
|
if (!mutationRequiresTransaction && !node.returning) {
|
|
1980
2256
|
return proceed(this.transformNode(node));
|
|
1981
2257
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
const
|
|
1989
|
-
if (
|
|
1990
|
-
|
|
1991
|
-
if (readBackResult.rows.length !== result2.rows.length) {
|
|
1992
|
-
readBackError = true;
|
|
1993
|
-
}
|
|
1994
|
-
return readBackResult;
|
|
1995
|
-
} else {
|
|
1996
|
-
return result2;
|
|
2258
|
+
if (InsertQueryNode.is(node)) {
|
|
2259
|
+
await this.enforcePreCreatePolicy(node, proceed);
|
|
2260
|
+
}
|
|
2261
|
+
const transformedNode = this.transformNode(node);
|
|
2262
|
+
const result = await proceed(transformedNode);
|
|
2263
|
+
if (!this.onlyReturningId(node)) {
|
|
2264
|
+
const readBackResult = await this.processReadBack(node, result, proceed);
|
|
2265
|
+
if (readBackResult.rows.length !== result.rows.length) {
|
|
2266
|
+
throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
|
|
1997
2267
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2268
|
+
return readBackResult;
|
|
2269
|
+
} else {
|
|
2270
|
+
return result;
|
|
2001
2271
|
}
|
|
2002
|
-
return result;
|
|
2003
2272
|
}
|
|
2004
2273
|
onlyReturningId(node) {
|
|
2005
2274
|
if (!node.returning) {
|
|
@@ -2060,11 +2329,11 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2060
2329
|
if (typeof item === "object" && item && "kind" in item) {
|
|
2061
2330
|
invariant6(item.kind === "ValueNode", "expecting a ValueNode");
|
|
2062
2331
|
result.push({
|
|
2063
|
-
node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type)),
|
|
2332
|
+
node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
|
|
2064
2333
|
raw: item.value
|
|
2065
2334
|
});
|
|
2066
2335
|
} else {
|
|
2067
|
-
const value = this.dialect.transformPrimitive(item, fieldDef.type);
|
|
2336
|
+
const value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
|
|
2068
2337
|
if (Array.isArray(value)) {
|
|
2069
2338
|
result.push({
|
|
2070
2339
|
node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
|
|
@@ -2133,7 +2402,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2133
2402
|
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(ColumnNode2.create(field), OperatorNode3.create("="), ValueNode3.create(row[field]))))));
|
|
2134
2403
|
}
|
|
2135
2404
|
getMutationModel(node) {
|
|
2136
|
-
const r =
|
|
2405
|
+
const r = match8(node).when(InsertQueryNode.is, (node2) => getTableName(node2.into)).when(UpdateQueryNode.is, (node2) => getTableName(node2.table)).when(DeleteQueryNode.is, (node2) => {
|
|
2137
2406
|
if (node2.from.froms.length !== 1) {
|
|
2138
2407
|
throw new InternalError("Only one from table is supported for delete");
|
|
2139
2408
|
}
|
|
@@ -2295,13 +2564,22 @@ var PolicyPlugin = class {
|
|
|
2295
2564
|
get description() {
|
|
2296
2565
|
return "Enforces access policies defined in the schema.";
|
|
2297
2566
|
}
|
|
2298
|
-
onKyselyQuery({
|
|
2567
|
+
onKyselyQuery({
|
|
2568
|
+
query,
|
|
2569
|
+
client,
|
|
2570
|
+
proceed
|
|
2571
|
+
/*, transaction*/
|
|
2572
|
+
}) {
|
|
2299
2573
|
const handler = new PolicyHandler(client);
|
|
2300
|
-
return handler.handle(
|
|
2574
|
+
return handler.handle(
|
|
2575
|
+
query,
|
|
2576
|
+
proceed
|
|
2577
|
+
/*, transaction*/
|
|
2578
|
+
);
|
|
2301
2579
|
}
|
|
2302
2580
|
};
|
|
2303
2581
|
export {
|
|
2304
2582
|
PolicyPlugin,
|
|
2305
2583
|
RejectedByPolicyError
|
|
2306
2584
|
};
|
|
2307
|
-
//# sourceMappingURL=
|
|
2585
|
+
//# sourceMappingURL=index.js.map
|