@zenstackhq/runtime 3.0.0-beta.8 → 3.0.0-beta.9
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/auth-CzM6GLw6.d.cts +524 -0
- package/dist/auth-CzM6GLw6.d.ts +524 -0
- package/dist/index.cjs +1158 -2508
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1185 -6
- package/dist/index.d.ts +1185 -6
- package/dist/index.js +1112 -2469
- package/dist/index.js.map +1 -1
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +2 -0
- package/dist/schema.d.ts +2 -0
- package/package.json +8 -14
- package/dist/errors-DVYyRUGp.d.cts +0 -1545
- package/dist/errors-DVYyRUGp.d.ts +0 -1545
- package/dist/plugins/policy/index.cjs +0 -3478
- package/dist/plugins/policy/index.cjs.map +0 -1
- package/dist/plugins/policy/index.d.cts +0 -42
- package/dist/plugins/policy/index.d.ts +0 -42
- package/dist/plugins/policy/index.js +0 -3441
- package/dist/plugins/policy/index.js.map +0 -1
|
@@ -1,3441 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
|
|
4
|
-
// src/client/client-impl.ts
|
|
5
|
-
import { invariant as invariant11 } from "@zenstackhq/common-helpers";
|
|
6
|
-
import { CompiledQuery, DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, sql as sql9, Transaction } from "kysely";
|
|
7
|
-
|
|
8
|
-
// src/client/crud/operations/aggregate.ts
|
|
9
|
-
import { sql as sql5 } from "kysely";
|
|
10
|
-
import { match as match7 } from "ts-pattern";
|
|
11
|
-
|
|
12
|
-
// src/client/query-utils.ts
|
|
13
|
-
import { invariant } from "@zenstackhq/common-helpers";
|
|
14
|
-
import { match } from "ts-pattern";
|
|
15
|
-
|
|
16
|
-
// src/schema/expression.ts
|
|
17
|
-
var ExpressionUtils = {
|
|
18
|
-
literal: /* @__PURE__ */ __name((value) => {
|
|
19
|
-
return {
|
|
20
|
-
kind: "literal",
|
|
21
|
-
value
|
|
22
|
-
};
|
|
23
|
-
}, "literal"),
|
|
24
|
-
array: /* @__PURE__ */ __name((items) => {
|
|
25
|
-
return {
|
|
26
|
-
kind: "array",
|
|
27
|
-
items
|
|
28
|
-
};
|
|
29
|
-
}, "array"),
|
|
30
|
-
call: /* @__PURE__ */ __name((functionName, args) => {
|
|
31
|
-
return {
|
|
32
|
-
kind: "call",
|
|
33
|
-
function: functionName,
|
|
34
|
-
args
|
|
35
|
-
};
|
|
36
|
-
}, "call"),
|
|
37
|
-
binary: /* @__PURE__ */ __name((left, op, right) => {
|
|
38
|
-
return {
|
|
39
|
-
kind: "binary",
|
|
40
|
-
op,
|
|
41
|
-
left,
|
|
42
|
-
right
|
|
43
|
-
};
|
|
44
|
-
}, "binary"),
|
|
45
|
-
unary: /* @__PURE__ */ __name((op, operand) => {
|
|
46
|
-
return {
|
|
47
|
-
kind: "unary",
|
|
48
|
-
op,
|
|
49
|
-
operand
|
|
50
|
-
};
|
|
51
|
-
}, "unary"),
|
|
52
|
-
field: /* @__PURE__ */ __name((field) => {
|
|
53
|
-
return {
|
|
54
|
-
kind: "field",
|
|
55
|
-
field
|
|
56
|
-
};
|
|
57
|
-
}, "field"),
|
|
58
|
-
member: /* @__PURE__ */ __name((receiver, members) => {
|
|
59
|
-
return {
|
|
60
|
-
kind: "member",
|
|
61
|
-
receiver,
|
|
62
|
-
members
|
|
63
|
-
};
|
|
64
|
-
}, "member"),
|
|
65
|
-
_this: /* @__PURE__ */ __name(() => {
|
|
66
|
-
return {
|
|
67
|
-
kind: "this"
|
|
68
|
-
};
|
|
69
|
-
}, "_this"),
|
|
70
|
-
_null: /* @__PURE__ */ __name(() => {
|
|
71
|
-
return {
|
|
72
|
-
kind: "null"
|
|
73
|
-
};
|
|
74
|
-
}, "_null"),
|
|
75
|
-
and: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
76
|
-
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "&&", exp), expr2);
|
|
77
|
-
}, "and"),
|
|
78
|
-
or: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
79
|
-
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "||", exp), expr2);
|
|
80
|
-
}, "or"),
|
|
81
|
-
not: /* @__PURE__ */ __name((expr2) => {
|
|
82
|
-
return ExpressionUtils.unary("!", expr2);
|
|
83
|
-
}, "not"),
|
|
84
|
-
is: /* @__PURE__ */ __name((value, kind) => {
|
|
85
|
-
return !!value && typeof value === "object" && "kind" in value && value.kind === kind;
|
|
86
|
-
}, "is"),
|
|
87
|
-
isLiteral: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "literal"), "isLiteral"),
|
|
88
|
-
isArray: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "array"), "isArray"),
|
|
89
|
-
isCall: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "call"), "isCall"),
|
|
90
|
-
isNull: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "null"), "isNull"),
|
|
91
|
-
isThis: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "this"), "isThis"),
|
|
92
|
-
isUnary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "unary"), "isUnary"),
|
|
93
|
-
isBinary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "binary"), "isBinary"),
|
|
94
|
-
isField: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "field"), "isField"),
|
|
95
|
-
isMember: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "member"), "isMember"),
|
|
96
|
-
getLiteralValue: /* @__PURE__ */ __name((expr2) => {
|
|
97
|
-
return ExpressionUtils.isLiteral(expr2) ? expr2.value : void 0;
|
|
98
|
-
}, "getLiteralValue")
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// src/client/errors.ts
|
|
102
|
-
var ZenStackError = class extends Error {
|
|
103
|
-
static {
|
|
104
|
-
__name(this, "ZenStackError");
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
var QueryError = class extends ZenStackError {
|
|
108
|
-
static {
|
|
109
|
-
__name(this, "QueryError");
|
|
110
|
-
}
|
|
111
|
-
constructor(message, cause) {
|
|
112
|
-
super(message, {
|
|
113
|
-
cause
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
var InternalError = class extends ZenStackError {
|
|
118
|
-
static {
|
|
119
|
-
__name(this, "InternalError");
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// src/client/query-utils.ts
|
|
124
|
-
function getModel(schema, model) {
|
|
125
|
-
return Object.values(schema.models).find((m) => m.name.toLowerCase() === model.toLowerCase());
|
|
126
|
-
}
|
|
127
|
-
__name(getModel, "getModel");
|
|
128
|
-
function getTypeDef(schema, type) {
|
|
129
|
-
return schema.typeDefs?.[type];
|
|
130
|
-
}
|
|
131
|
-
__name(getTypeDef, "getTypeDef");
|
|
132
|
-
function requireModel(schema, model) {
|
|
133
|
-
const modelDef = getModel(schema, model);
|
|
134
|
-
if (!modelDef) {
|
|
135
|
-
throw new QueryError(`Model "${model}" not found in schema`);
|
|
136
|
-
}
|
|
137
|
-
return modelDef;
|
|
138
|
-
}
|
|
139
|
-
__name(requireModel, "requireModel");
|
|
140
|
-
function getField(schema, model, field) {
|
|
141
|
-
const modelDef = getModel(schema, model);
|
|
142
|
-
return modelDef?.fields[field];
|
|
143
|
-
}
|
|
144
|
-
__name(getField, "getField");
|
|
145
|
-
function requireField(schema, modelOrType, field) {
|
|
146
|
-
const modelDef = getModel(schema, modelOrType);
|
|
147
|
-
if (modelDef) {
|
|
148
|
-
if (!modelDef.fields[field]) {
|
|
149
|
-
throw new QueryError(`Field "${field}" not found in model "${modelOrType}"`);
|
|
150
|
-
} else {
|
|
151
|
-
return modelDef.fields[field];
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
const typeDef = getTypeDef(schema, modelOrType);
|
|
155
|
-
if (typeDef) {
|
|
156
|
-
if (!typeDef.fields[field]) {
|
|
157
|
-
throw new QueryError(`Field "${field}" not found in type "${modelOrType}"`);
|
|
158
|
-
} else {
|
|
159
|
-
return typeDef.fields[field];
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
throw new QueryError(`Model or type "${modelOrType}" not found in schema`);
|
|
163
|
-
}
|
|
164
|
-
__name(requireField, "requireField");
|
|
165
|
-
function requireIdFields(schema, model) {
|
|
166
|
-
const modelDef = requireModel(schema, model);
|
|
167
|
-
const result = modelDef?.idFields;
|
|
168
|
-
if (!result) {
|
|
169
|
-
throw new InternalError(`Model "${model}" does not have ID field(s)`);
|
|
170
|
-
}
|
|
171
|
-
return result;
|
|
172
|
-
}
|
|
173
|
-
__name(requireIdFields, "requireIdFields");
|
|
174
|
-
function getRelationForeignKeyFieldPairs(schema, model, relationField) {
|
|
175
|
-
const fieldDef = requireField(schema, model, relationField);
|
|
176
|
-
if (!fieldDef?.relation) {
|
|
177
|
-
throw new InternalError(`Field "${relationField}" is not a relation`);
|
|
178
|
-
}
|
|
179
|
-
if (fieldDef.relation.fields) {
|
|
180
|
-
if (!fieldDef.relation.references) {
|
|
181
|
-
throw new InternalError(`Relation references not defined for field "${relationField}"`);
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
keyPairs: fieldDef.relation.fields.map((f, i) => ({
|
|
185
|
-
fk: f,
|
|
186
|
-
pk: fieldDef.relation.references[i]
|
|
187
|
-
})),
|
|
188
|
-
ownedByModel: true
|
|
189
|
-
};
|
|
190
|
-
} else {
|
|
191
|
-
if (!fieldDef.relation.opposite) {
|
|
192
|
-
throw new InternalError(`Opposite relation not defined for field "${relationField}"`);
|
|
193
|
-
}
|
|
194
|
-
const oppositeField = requireField(schema, fieldDef.type, fieldDef.relation.opposite);
|
|
195
|
-
if (!oppositeField.relation) {
|
|
196
|
-
throw new InternalError(`Field "${fieldDef.relation.opposite}" is not a relation`);
|
|
197
|
-
}
|
|
198
|
-
if (!oppositeField.relation.fields) {
|
|
199
|
-
throw new InternalError(`Relation fields not defined for field "${relationField}"`);
|
|
200
|
-
}
|
|
201
|
-
if (!oppositeField.relation.references) {
|
|
202
|
-
throw new InternalError(`Relation references not defined for field "${relationField}"`);
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
keyPairs: oppositeField.relation.fields.map((f, i) => ({
|
|
206
|
-
fk: f,
|
|
207
|
-
pk: oppositeField.relation.references[i]
|
|
208
|
-
})),
|
|
209
|
-
ownedByModel: false
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
}
|
|
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");
|
|
224
|
-
function getUniqueFields(schema, model) {
|
|
225
|
-
const modelDef = requireModel(schema, model);
|
|
226
|
-
const result = [];
|
|
227
|
-
for (const [key, value] of Object.entries(modelDef.uniqueFields)) {
|
|
228
|
-
if (typeof value !== "object") {
|
|
229
|
-
throw new InternalError(`Invalid unique field definition for "${key}"`);
|
|
230
|
-
}
|
|
231
|
-
if (typeof value.type === "string") {
|
|
232
|
-
result.push({
|
|
233
|
-
name: key,
|
|
234
|
-
def: requireField(schema, model, key)
|
|
235
|
-
});
|
|
236
|
-
} else {
|
|
237
|
-
result.push({
|
|
238
|
-
name: key,
|
|
239
|
-
defs: Object.fromEntries(Object.keys(value).map((k) => [
|
|
240
|
-
k,
|
|
241
|
-
requireField(schema, model, k)
|
|
242
|
-
]))
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
return result;
|
|
247
|
-
}
|
|
248
|
-
__name(getUniqueFields, "getUniqueFields");
|
|
249
|
-
function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComputedField = true) {
|
|
250
|
-
const fieldDef = requireField(schema, model, field);
|
|
251
|
-
if (!fieldDef.computed) {
|
|
252
|
-
return eb.ref(modelAlias ? `${modelAlias}.${field}` : field);
|
|
253
|
-
} else {
|
|
254
|
-
if (!inlineComputedField) {
|
|
255
|
-
return eb.ref(modelAlias ? `${modelAlias}.${field}` : field);
|
|
256
|
-
}
|
|
257
|
-
let computer;
|
|
258
|
-
if ("computedFields" in options) {
|
|
259
|
-
const computedFields = options.computedFields;
|
|
260
|
-
computer = computedFields?.[model]?.[field];
|
|
261
|
-
}
|
|
262
|
-
if (!computer) {
|
|
263
|
-
throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
|
|
264
|
-
}
|
|
265
|
-
return computer(eb, {
|
|
266
|
-
modelAlias
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
__name(buildFieldRef, "buildFieldRef");
|
|
271
|
-
function isEnum(schema, type) {
|
|
272
|
-
return !!schema.enums?.[type];
|
|
273
|
-
}
|
|
274
|
-
__name(isEnum, "isEnum");
|
|
275
|
-
function buildJoinPairs(schema, model, modelAlias, relationField, relationModelAlias) {
|
|
276
|
-
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(schema, model, relationField);
|
|
277
|
-
return keyPairs.map(({ fk, pk }) => {
|
|
278
|
-
if (ownedByModel) {
|
|
279
|
-
return [
|
|
280
|
-
`${relationModelAlias}.${pk}`,
|
|
281
|
-
`${modelAlias}.${fk}`
|
|
282
|
-
];
|
|
283
|
-
} else {
|
|
284
|
-
return [
|
|
285
|
-
`${relationModelAlias}.${fk}`,
|
|
286
|
-
`${modelAlias}.${pk}`
|
|
287
|
-
];
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
__name(buildJoinPairs, "buildJoinPairs");
|
|
292
|
-
function makeDefaultOrderBy(schema, model) {
|
|
293
|
-
const idFields = requireIdFields(schema, model);
|
|
294
|
-
return idFields.map((f) => ({
|
|
295
|
-
[f]: "asc"
|
|
296
|
-
}));
|
|
297
|
-
}
|
|
298
|
-
__name(makeDefaultOrderBy, "makeDefaultOrderBy");
|
|
299
|
-
function getManyToManyRelation(schema, model, field) {
|
|
300
|
-
const fieldDef = requireField(schema, model, field);
|
|
301
|
-
if (!fieldDef.array || !fieldDef.relation?.opposite) {
|
|
302
|
-
return void 0;
|
|
303
|
-
}
|
|
304
|
-
const oppositeFieldDef = requireField(schema, fieldDef.type, fieldDef.relation.opposite);
|
|
305
|
-
if (oppositeFieldDef.array) {
|
|
306
|
-
const sortedModelNames = [
|
|
307
|
-
model,
|
|
308
|
-
fieldDef.type
|
|
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
|
-
}
|
|
332
|
-
const modelIdFields = requireIdFields(schema, model);
|
|
333
|
-
invariant(modelIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
334
|
-
const otherIdFields = requireIdFields(schema, fieldDef.type);
|
|
335
|
-
invariant(otherIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
336
|
-
return {
|
|
337
|
-
parentFkName: orderedFK[0],
|
|
338
|
-
parentPKName: modelIdFields[0],
|
|
339
|
-
otherModel: fieldDef.type,
|
|
340
|
-
otherField: fieldDef.relation.opposite,
|
|
341
|
-
otherFkName: orderedFK[1],
|
|
342
|
-
otherPKName: otherIdFields[0],
|
|
343
|
-
joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
|
|
344
|
-
};
|
|
345
|
-
} else {
|
|
346
|
-
return void 0;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
__name(getManyToManyRelation, "getManyToManyRelation");
|
|
350
|
-
function flattenCompoundUniqueFilters(schema, model, filter) {
|
|
351
|
-
if (typeof filter !== "object" || !filter) {
|
|
352
|
-
return filter;
|
|
353
|
-
}
|
|
354
|
-
const uniqueFields = getUniqueFields(schema, model);
|
|
355
|
-
const compoundUniques = uniqueFields.filter((u) => "defs" in u);
|
|
356
|
-
if (compoundUniques.length === 0) {
|
|
357
|
-
return filter;
|
|
358
|
-
}
|
|
359
|
-
const result = {};
|
|
360
|
-
for (const [key, value] of Object.entries(filter)) {
|
|
361
|
-
if (compoundUniques.some(({ name }) => name === key)) {
|
|
362
|
-
Object.assign(result, value);
|
|
363
|
-
} else {
|
|
364
|
-
result[key] = value;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return result;
|
|
368
|
-
}
|
|
369
|
-
__name(flattenCompoundUniqueFilters, "flattenCompoundUniqueFilters");
|
|
370
|
-
function ensureArray(value) {
|
|
371
|
-
if (Array.isArray(value)) {
|
|
372
|
-
return value;
|
|
373
|
-
} else {
|
|
374
|
-
return [
|
|
375
|
-
value
|
|
376
|
-
];
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
__name(ensureArray, "ensureArray");
|
|
380
|
-
function getDelegateDescendantModels(schema, model, collected = /* @__PURE__ */ new Set()) {
|
|
381
|
-
const subModels = Object.values(schema.models).filter((m) => m.baseModel === model);
|
|
382
|
-
subModels.forEach((def) => {
|
|
383
|
-
if (!collected.has(def)) {
|
|
384
|
-
collected.add(def);
|
|
385
|
-
getDelegateDescendantModels(schema, def.name, collected);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
return [
|
|
389
|
-
...collected
|
|
390
|
-
];
|
|
391
|
-
}
|
|
392
|
-
__name(getDelegateDescendantModels, "getDelegateDescendantModels");
|
|
393
|
-
function aggregate(eb, expr2, op) {
|
|
394
|
-
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();
|
|
395
|
-
}
|
|
396
|
-
__name(aggregate, "aggregate");
|
|
397
|
-
|
|
398
|
-
// src/client/crud/operations/base.ts
|
|
399
|
-
import { createId } from "@paralleldrive/cuid2";
|
|
400
|
-
import { invariant as invariant5, isPlainObject as isPlainObject3 } from "@zenstackhq/common-helpers";
|
|
401
|
-
import { expressionBuilder as expressionBuilder2, sql as sql4 } from "kysely";
|
|
402
|
-
import { nanoid } from "nanoid";
|
|
403
|
-
import { match as match6 } from "ts-pattern";
|
|
404
|
-
import { ulid } from "ulid";
|
|
405
|
-
import * as uuid from "uuid";
|
|
406
|
-
|
|
407
|
-
// src/utils/clone.ts
|
|
408
|
-
import { isPlainObject } from "@zenstackhq/common-helpers";
|
|
409
|
-
|
|
410
|
-
// src/utils/enumerate.ts
|
|
411
|
-
function enumerate(x) {
|
|
412
|
-
if (x === null || x === void 0) {
|
|
413
|
-
return [];
|
|
414
|
-
} else if (Array.isArray(x)) {
|
|
415
|
-
return x;
|
|
416
|
-
} else {
|
|
417
|
-
return [
|
|
418
|
-
x
|
|
419
|
-
];
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
__name(enumerate, "enumerate");
|
|
423
|
-
|
|
424
|
-
// src/client/constants.ts
|
|
425
|
-
var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
|
|
426
|
-
var LOGICAL_COMBINATORS = [
|
|
427
|
-
"AND",
|
|
428
|
-
"OR",
|
|
429
|
-
"NOT"
|
|
430
|
-
];
|
|
431
|
-
var AGGREGATE_OPERATORS = [
|
|
432
|
-
"_count",
|
|
433
|
-
"_sum",
|
|
434
|
-
"_avg",
|
|
435
|
-
"_min",
|
|
436
|
-
"_max"
|
|
437
|
-
];
|
|
438
|
-
|
|
439
|
-
// src/client/contract.ts
|
|
440
|
-
var CRUD = [
|
|
441
|
-
"create",
|
|
442
|
-
"read",
|
|
443
|
-
"update",
|
|
444
|
-
"delete"
|
|
445
|
-
];
|
|
446
|
-
var CRUD_EXT = [
|
|
447
|
-
...CRUD,
|
|
448
|
-
"post-update"
|
|
449
|
-
];
|
|
450
|
-
|
|
451
|
-
// src/client/crud/dialects/index.ts
|
|
452
|
-
import { match as match5 } from "ts-pattern";
|
|
453
|
-
|
|
454
|
-
// src/client/crud/dialects/postgresql.ts
|
|
455
|
-
import { invariant as invariant3 } from "@zenstackhq/common-helpers";
|
|
456
|
-
import Decimal from "decimal.js";
|
|
457
|
-
import { sql as sql2 } from "kysely";
|
|
458
|
-
import { match as match3 } from "ts-pattern";
|
|
459
|
-
|
|
460
|
-
// src/client/crud/dialects/base-dialect.ts
|
|
461
|
-
import { invariant as invariant2, isPlainObject as isPlainObject2 } from "@zenstackhq/common-helpers";
|
|
462
|
-
import { expressionBuilder, sql } from "kysely";
|
|
463
|
-
import { match as match2, P } from "ts-pattern";
|
|
464
|
-
var BaseCrudDialect = class {
|
|
465
|
-
static {
|
|
466
|
-
__name(this, "BaseCrudDialect");
|
|
467
|
-
}
|
|
468
|
-
schema;
|
|
469
|
-
options;
|
|
470
|
-
constructor(schema, options) {
|
|
471
|
-
this.schema = schema;
|
|
472
|
-
this.options = options;
|
|
473
|
-
}
|
|
474
|
-
transformPrimitive(value, _type, _forArrayField) {
|
|
475
|
-
return value;
|
|
476
|
-
}
|
|
477
|
-
transformOutput(value, _type) {
|
|
478
|
-
return value;
|
|
479
|
-
}
|
|
480
|
-
// #region common query builders
|
|
481
|
-
buildSelectModel(eb, model, modelAlias) {
|
|
482
|
-
const modelDef = requireModel(this.schema, model);
|
|
483
|
-
let result = eb.selectFrom(model === modelAlias ? model : `${model} as ${modelAlias}`);
|
|
484
|
-
let joinBase = modelDef.baseModel;
|
|
485
|
-
while (joinBase) {
|
|
486
|
-
result = this.buildDelegateJoin(model, modelAlias, joinBase, result);
|
|
487
|
-
joinBase = requireModel(this.schema, joinBase).baseModel;
|
|
488
|
-
}
|
|
489
|
-
return result;
|
|
490
|
-
}
|
|
491
|
-
buildFilterSortTake(model, args, query, modelAlias) {
|
|
492
|
-
let result = query;
|
|
493
|
-
if (args.where) {
|
|
494
|
-
result = result.where((eb) => this.buildFilter(eb, model, modelAlias, args?.where));
|
|
495
|
-
}
|
|
496
|
-
let negateOrderBy = false;
|
|
497
|
-
const skip = args.skip;
|
|
498
|
-
let take = args.take;
|
|
499
|
-
if (take !== void 0 && take < 0) {
|
|
500
|
-
negateOrderBy = true;
|
|
501
|
-
take = -take;
|
|
502
|
-
}
|
|
503
|
-
result = this.buildSkipTake(result, skip, take);
|
|
504
|
-
result = this.buildOrderBy(result, model, modelAlias, args.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
505
|
-
if ("distinct" in args && args.distinct) {
|
|
506
|
-
const distinct = ensureArray(args.distinct);
|
|
507
|
-
if (this.supportsDistinctOn) {
|
|
508
|
-
result = result.distinctOn(distinct.map((f) => sql.ref(`${modelAlias}.${f}`)));
|
|
509
|
-
} else {
|
|
510
|
-
throw new QueryError(`"distinct" is not supported by "${this.schema.provider.type}" provider`);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
if (args.cursor) {
|
|
514
|
-
result = this.buildCursorFilter(model, result, args.cursor, args.orderBy, negateOrderBy, modelAlias);
|
|
515
|
-
}
|
|
516
|
-
return result;
|
|
517
|
-
}
|
|
518
|
-
buildFilter(eb, model, modelAlias, where) {
|
|
519
|
-
if (where === true || where === void 0) {
|
|
520
|
-
return this.true(eb);
|
|
521
|
-
}
|
|
522
|
-
if (where === false) {
|
|
523
|
-
return this.false(eb);
|
|
524
|
-
}
|
|
525
|
-
let result = this.true(eb);
|
|
526
|
-
const _where = flattenCompoundUniqueFilters(this.schema, model, where);
|
|
527
|
-
for (const [key, payload] of Object.entries(_where)) {
|
|
528
|
-
if (payload === void 0) {
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
if (key.startsWith("$")) {
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
if (this.isLogicalCombinator(key)) {
|
|
535
|
-
result = this.and(eb, result, this.buildCompositeFilter(eb, model, modelAlias, key, payload));
|
|
536
|
-
continue;
|
|
537
|
-
}
|
|
538
|
-
const fieldDef = requireField(this.schema, model, key);
|
|
539
|
-
if (fieldDef.relation) {
|
|
540
|
-
result = this.and(eb, result, this.buildRelationFilter(eb, model, modelAlias, key, fieldDef, payload));
|
|
541
|
-
} else {
|
|
542
|
-
const fieldRef = this.fieldRef(fieldDef.originModel ?? model, key, eb, fieldDef.originModel ?? modelAlias);
|
|
543
|
-
if (fieldDef.array) {
|
|
544
|
-
result = this.and(eb, result, this.buildArrayFilter(eb, fieldRef, fieldDef, payload));
|
|
545
|
-
} else {
|
|
546
|
-
result = this.and(eb, result, this.buildPrimitiveFilter(eb, fieldRef, fieldDef, payload));
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
if ("$expr" in _where && typeof _where["$expr"] === "function") {
|
|
551
|
-
result = this.and(eb, result, _where["$expr"](eb));
|
|
552
|
-
}
|
|
553
|
-
return result;
|
|
554
|
-
}
|
|
555
|
-
buildCursorFilter(model, query, cursor, orderBy, negateOrderBy, modelAlias) {
|
|
556
|
-
const _orderBy = orderBy ?? makeDefaultOrderBy(this.schema, model);
|
|
557
|
-
const orderByItems = ensureArray(_orderBy).flatMap((obj) => Object.entries(obj));
|
|
558
|
-
const eb = expressionBuilder();
|
|
559
|
-
const subQueryAlias = `${model}$cursor$sub`;
|
|
560
|
-
const cursorFilter = this.buildFilter(eb, model, subQueryAlias, cursor);
|
|
561
|
-
let result = query;
|
|
562
|
-
const filters = [];
|
|
563
|
-
for (let i = orderByItems.length - 1; i >= 0; i--) {
|
|
564
|
-
const andFilters = [];
|
|
565
|
-
for (let j = 0; j <= i; j++) {
|
|
566
|
-
const [field, order] = orderByItems[j];
|
|
567
|
-
const _order = negateOrderBy ? order === "asc" ? "desc" : "asc" : order;
|
|
568
|
-
const op = j === i ? _order === "asc" ? ">=" : "<=" : "=";
|
|
569
|
-
andFilters.push(eb(eb.ref(`${modelAlias}.${field}`), op, this.buildSelectModel(eb, model, subQueryAlias).select(`${subQueryAlias}.${field}`).where(cursorFilter)));
|
|
570
|
-
}
|
|
571
|
-
filters.push(eb.and(andFilters));
|
|
572
|
-
}
|
|
573
|
-
result = result.where((eb2) => eb2.or(filters));
|
|
574
|
-
return result;
|
|
575
|
-
}
|
|
576
|
-
isLogicalCombinator(key) {
|
|
577
|
-
return LOGICAL_COMBINATORS.includes(key);
|
|
578
|
-
}
|
|
579
|
-
buildCompositeFilter(eb, model, modelAlias, key, payload) {
|
|
580
|
-
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();
|
|
581
|
-
}
|
|
582
|
-
buildRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
583
|
-
if (!fieldDef.array) {
|
|
584
|
-
return this.buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, payload);
|
|
585
|
-
} else {
|
|
586
|
-
return this.buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
590
|
-
if (payload === null) {
|
|
591
|
-
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
592
|
-
if (ownedByModel && !fieldDef.originModel) {
|
|
593
|
-
return this.and(eb, ...keyPairs.map(({ fk }) => eb(sql.ref(`${modelAlias}.${fk}`), "is", null)));
|
|
594
|
-
} else {
|
|
595
|
-
return this.buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, {
|
|
596
|
-
is: null
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
const joinAlias = `${modelAlias}$${field}`;
|
|
601
|
-
const joinPairs = buildJoinPairs(
|
|
602
|
-
this.schema,
|
|
603
|
-
model,
|
|
604
|
-
// if field is from a base, use the base model to join
|
|
605
|
-
fieldDef.originModel ?? modelAlias,
|
|
606
|
-
field,
|
|
607
|
-
joinAlias
|
|
608
|
-
);
|
|
609
|
-
const filterResultField = `${field}$filter`;
|
|
610
|
-
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));
|
|
611
|
-
const conditions = [];
|
|
612
|
-
if ("is" in payload || "isNot" in payload) {
|
|
613
|
-
if ("is" in payload) {
|
|
614
|
-
if (payload.is === null) {
|
|
615
|
-
conditions.push(eb(joinSelect, "=", 0));
|
|
616
|
-
} else {
|
|
617
|
-
conditions.push(eb(joinSelect.where(() => this.buildFilter(eb, fieldDef.type, joinAlias, payload.is)), ">", 0));
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
if ("isNot" in payload) {
|
|
621
|
-
if (payload.isNot === null) {
|
|
622
|
-
conditions.push(eb(joinSelect, ">", 0));
|
|
623
|
-
} else {
|
|
624
|
-
conditions.push(this.or(
|
|
625
|
-
eb,
|
|
626
|
-
// is null
|
|
627
|
-
eb(joinSelect, "=", 0),
|
|
628
|
-
// found one that matches the filter
|
|
629
|
-
eb(joinSelect.where(() => this.buildFilter(eb, fieldDef.type, joinAlias, payload.isNot)), "=", 0)
|
|
630
|
-
));
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
} else {
|
|
634
|
-
conditions.push(eb(joinSelect.where(() => this.buildFilter(eb, fieldDef.type, joinAlias, payload)), ">", 0));
|
|
635
|
-
}
|
|
636
|
-
return this.and(eb, ...conditions);
|
|
637
|
-
}
|
|
638
|
-
buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
639
|
-
if (payload === null) {
|
|
640
|
-
return eb(sql.ref(`${modelAlias}.${field}`), "is", null);
|
|
641
|
-
}
|
|
642
|
-
const relationModel = fieldDef.type;
|
|
643
|
-
const relationFilterSelectAlias = `${modelAlias}$${field}$filter`;
|
|
644
|
-
const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
|
|
645
|
-
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
646
|
-
if (m2m) {
|
|
647
|
-
const modelIdFields = requireIdFields(this.schema, model);
|
|
648
|
-
invariant2(modelIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
649
|
-
const relationIdFields = requireIdFields(this.schema, relationModel);
|
|
650
|
-
invariant2(relationIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
651
|
-
return eb2(sql.ref(`${relationFilterSelectAlias}.${relationIdFields[0]}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", sql.ref(`${modelAlias}.${modelIdFields[0]}`)));
|
|
652
|
-
} else {
|
|
653
|
-
const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
654
|
-
let result2 = this.true(eb2);
|
|
655
|
-
for (const { fk, pk } of relationKeyPairs.keyPairs) {
|
|
656
|
-
if (relationKeyPairs.ownedByModel) {
|
|
657
|
-
result2 = this.and(eb2, result2, eb2(sql.ref(`${modelAlias}.${fk}`), "=", sql.ref(`${relationFilterSelectAlias}.${pk}`)));
|
|
658
|
-
} else {
|
|
659
|
-
result2 = this.and(eb2, result2, eb2(sql.ref(`${modelAlias}.${pk}`), "=", sql.ref(`${relationFilterSelectAlias}.${fk}`)));
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
return result2;
|
|
663
|
-
}
|
|
664
|
-
}, "buildPkFkWhereRefs");
|
|
665
|
-
let result = this.true(eb);
|
|
666
|
-
for (const [key, subPayload] of Object.entries(payload)) {
|
|
667
|
-
if (!subPayload) {
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
switch (key) {
|
|
671
|
-
case "some": {
|
|
672
|
-
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));
|
|
673
|
-
break;
|
|
674
|
-
}
|
|
675
|
-
case "every": {
|
|
676
|
-
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));
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
case "none": {
|
|
680
|
-
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));
|
|
681
|
-
break;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
return result;
|
|
686
|
-
}
|
|
687
|
-
buildArrayFilter(eb, fieldRef, fieldDef, payload) {
|
|
688
|
-
const clauses = [];
|
|
689
|
-
const fieldType = fieldDef.type;
|
|
690
|
-
for (const [key, _value] of Object.entries(payload)) {
|
|
691
|
-
if (_value === void 0) {
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
694
|
-
const value = this.transformPrimitive(_value, fieldType, !!fieldDef.array);
|
|
695
|
-
switch (key) {
|
|
696
|
-
case "equals": {
|
|
697
|
-
clauses.push(this.buildLiteralFilter(eb, fieldRef, fieldType, eb.val(value)));
|
|
698
|
-
break;
|
|
699
|
-
}
|
|
700
|
-
case "has": {
|
|
701
|
-
clauses.push(eb(fieldRef, "@>", eb.val([
|
|
702
|
-
value
|
|
703
|
-
])));
|
|
704
|
-
break;
|
|
705
|
-
}
|
|
706
|
-
case "hasEvery": {
|
|
707
|
-
clauses.push(eb(fieldRef, "@>", eb.val(value)));
|
|
708
|
-
break;
|
|
709
|
-
}
|
|
710
|
-
case "hasSome": {
|
|
711
|
-
clauses.push(eb(fieldRef, "&&", eb.val(value)));
|
|
712
|
-
break;
|
|
713
|
-
}
|
|
714
|
-
case "isEmpty": {
|
|
715
|
-
clauses.push(eb(fieldRef, value === true ? "=" : "!=", eb.val([])));
|
|
716
|
-
break;
|
|
717
|
-
}
|
|
718
|
-
default: {
|
|
719
|
-
throw new InternalError(`Invalid array filter key: ${key}`);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
return this.and(eb, ...clauses);
|
|
724
|
-
}
|
|
725
|
-
buildPrimitiveFilter(eb, fieldRef, fieldDef, payload) {
|
|
726
|
-
if (payload === null) {
|
|
727
|
-
return eb(fieldRef, "is", null);
|
|
728
|
-
}
|
|
729
|
-
if (isEnum(this.schema, fieldDef.type)) {
|
|
730
|
-
return this.buildEnumFilter(eb, fieldRef, fieldDef, payload);
|
|
731
|
-
}
|
|
732
|
-
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", () => {
|
|
733
|
-
throw new InternalError("JSON filters are not supported yet");
|
|
734
|
-
}).with("Unsupported", () => {
|
|
735
|
-
throw new QueryError(`Unsupported field cannot be used in filters`);
|
|
736
|
-
}).exhaustive();
|
|
737
|
-
}
|
|
738
|
-
buildLiteralFilter(eb, lhs, type, rhs) {
|
|
739
|
-
return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
|
|
740
|
-
}
|
|
741
|
-
buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
|
|
742
|
-
if (payload === null || !isPlainObject2(payload)) {
|
|
743
|
-
return {
|
|
744
|
-
conditions: [
|
|
745
|
-
this.buildLiteralFilter(eb, lhs, type, payload)
|
|
746
|
-
],
|
|
747
|
-
consumedKeys: []
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
const conditions = [];
|
|
751
|
-
const consumedKeys = [];
|
|
752
|
-
for (const [op, value] of Object.entries(payload)) {
|
|
753
|
-
if (onlyForKeys && !onlyForKeys.includes(op)) {
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
756
|
-
if (excludeKeys.includes(op)) {
|
|
757
|
-
continue;
|
|
758
|
-
}
|
|
759
|
-
const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
|
|
760
|
-
const condition = match2(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
|
|
761
|
-
invariant2(Array.isArray(rhs), "right hand side must be an array");
|
|
762
|
-
if (rhs.length === 0) {
|
|
763
|
-
return this.false(eb);
|
|
764
|
-
} else {
|
|
765
|
-
return eb(lhs, "in", rhs);
|
|
766
|
-
}
|
|
767
|
-
}).with("notIn", () => {
|
|
768
|
-
invariant2(Array.isArray(rhs), "right hand side must be an array");
|
|
769
|
-
if (rhs.length === 0) {
|
|
770
|
-
return this.true(eb);
|
|
771
|
-
} else {
|
|
772
|
-
return eb.not(eb(lhs, "in", rhs));
|
|
773
|
-
}
|
|
774
|
-
}).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) => {
|
|
775
|
-
const innerResult = this.buildStandardFilter(eb, type, value, aggregate(eb, lhs, op2), getRhs, recurse, throwIfInvalid);
|
|
776
|
-
consumedKeys.push(...innerResult.consumedKeys);
|
|
777
|
-
return this.and(eb, ...innerResult.conditions);
|
|
778
|
-
}).otherwise(() => {
|
|
779
|
-
if (throwIfInvalid) {
|
|
780
|
-
throw new QueryError(`Invalid filter key: ${op}`);
|
|
781
|
-
} else {
|
|
782
|
-
return void 0;
|
|
783
|
-
}
|
|
784
|
-
});
|
|
785
|
-
if (condition) {
|
|
786
|
-
conditions.push(condition);
|
|
787
|
-
consumedKeys.push(op);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
return {
|
|
791
|
-
conditions,
|
|
792
|
-
consumedKeys
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
buildStringFilter(eb, fieldRef, payload) {
|
|
796
|
-
let mode;
|
|
797
|
-
if (payload && typeof payload === "object" && "mode" in payload) {
|
|
798
|
-
mode = payload.mode;
|
|
799
|
-
}
|
|
800
|
-
const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload, mode === "insensitive" ? eb.fn("lower", [
|
|
801
|
-
fieldRef
|
|
802
|
-
]) : fieldRef, (value) => this.prepStringCasing(eb, value, mode), (value) => this.buildStringFilter(eb, fieldRef, value));
|
|
803
|
-
if (payload && typeof payload === "object") {
|
|
804
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
805
|
-
if (key === "mode" || consumedKeys.includes(key)) {
|
|
806
|
-
continue;
|
|
807
|
-
}
|
|
808
|
-
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(() => {
|
|
809
|
-
throw new QueryError(`Invalid string filter key: ${key}`);
|
|
810
|
-
});
|
|
811
|
-
if (condition) {
|
|
812
|
-
conditions.push(condition);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return this.and(eb, ...conditions);
|
|
817
|
-
}
|
|
818
|
-
prepStringCasing(eb, value, mode) {
|
|
819
|
-
if (!mode || mode === "default") {
|
|
820
|
-
return value === null ? value : sql.val(value);
|
|
821
|
-
}
|
|
822
|
-
if (typeof value === "string") {
|
|
823
|
-
return eb.fn("lower", [
|
|
824
|
-
sql.val(value)
|
|
825
|
-
]);
|
|
826
|
-
} else if (Array.isArray(value)) {
|
|
827
|
-
return value.map((v) => this.prepStringCasing(eb, v, mode));
|
|
828
|
-
} else {
|
|
829
|
-
return value === null ? null : sql.val(value);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
buildNumberFilter(eb, fieldRef, type, payload) {
|
|
833
|
-
const { conditions } = this.buildStandardFilter(eb, type, payload, fieldRef, (value) => this.transformPrimitive(value, type, false), (value) => this.buildNumberFilter(eb, fieldRef, type, value));
|
|
834
|
-
return this.and(eb, ...conditions);
|
|
835
|
-
}
|
|
836
|
-
buildBooleanFilter(eb, fieldRef, payload) {
|
|
837
|
-
const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, fieldRef, (value) => this.transformPrimitive(value, "Boolean", false), (value) => this.buildBooleanFilter(eb, fieldRef, value), true, [
|
|
838
|
-
"equals",
|
|
839
|
-
"not"
|
|
840
|
-
]);
|
|
841
|
-
return this.and(eb, ...conditions);
|
|
842
|
-
}
|
|
843
|
-
buildDateTimeFilter(eb, fieldRef, payload) {
|
|
844
|
-
const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, fieldRef, (value) => this.transformPrimitive(value, "DateTime", false), (value) => this.buildDateTimeFilter(eb, fieldRef, value), true);
|
|
845
|
-
return this.and(eb, ...conditions);
|
|
846
|
-
}
|
|
847
|
-
buildBytesFilter(eb, fieldRef, payload) {
|
|
848
|
-
const conditions = this.buildStandardFilter(eb, "Bytes", payload, fieldRef, (value) => this.transformPrimitive(value, "Bytes", false), (value) => this.buildBytesFilter(eb, fieldRef, value), true, [
|
|
849
|
-
"equals",
|
|
850
|
-
"in",
|
|
851
|
-
"notIn",
|
|
852
|
-
"not"
|
|
853
|
-
]);
|
|
854
|
-
return this.and(eb, ...conditions.conditions);
|
|
855
|
-
}
|
|
856
|
-
buildEnumFilter(eb, fieldRef, fieldDef, payload) {
|
|
857
|
-
const conditions = this.buildStandardFilter(eb, "String", payload, fieldRef, (value) => value, (value) => this.buildEnumFilter(eb, fieldRef, fieldDef, value), true, [
|
|
858
|
-
"equals",
|
|
859
|
-
"in",
|
|
860
|
-
"notIn",
|
|
861
|
-
"not"
|
|
862
|
-
]);
|
|
863
|
-
return this.and(eb, ...conditions.conditions);
|
|
864
|
-
}
|
|
865
|
-
buildOrderBy(query, model, modelAlias, orderBy, useDefaultIfEmpty, negated) {
|
|
866
|
-
if (!orderBy) {
|
|
867
|
-
if (useDefaultIfEmpty) {
|
|
868
|
-
orderBy = makeDefaultOrderBy(this.schema, model);
|
|
869
|
-
} else {
|
|
870
|
-
return query;
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
let result = query;
|
|
874
|
-
enumerate(orderBy).forEach((orderBy2) => {
|
|
875
|
-
for (const [field, value] of Object.entries(orderBy2)) {
|
|
876
|
-
if (!value) {
|
|
877
|
-
continue;
|
|
878
|
-
}
|
|
879
|
-
if ([
|
|
880
|
-
"_count",
|
|
881
|
-
"_avg",
|
|
882
|
-
"_sum",
|
|
883
|
-
"_min",
|
|
884
|
-
"_max"
|
|
885
|
-
].includes(field)) {
|
|
886
|
-
invariant2(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
887
|
-
for (const [k, v] of Object.entries(value)) {
|
|
888
|
-
invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
889
|
-
result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), sql.raw(this.negateSort(v, negated)));
|
|
890
|
-
}
|
|
891
|
-
continue;
|
|
892
|
-
}
|
|
893
|
-
switch (field) {
|
|
894
|
-
case "_count": {
|
|
895
|
-
invariant2(value && typeof value === "object", 'invalid orderBy value for field "_count"');
|
|
896
|
-
for (const [k, v] of Object.entries(value)) {
|
|
897
|
-
invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
898
|
-
result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), sql.raw(this.negateSort(v, negated)));
|
|
899
|
-
}
|
|
900
|
-
continue;
|
|
901
|
-
}
|
|
902
|
-
default:
|
|
903
|
-
break;
|
|
904
|
-
}
|
|
905
|
-
const fieldDef = requireField(this.schema, model, field);
|
|
906
|
-
if (!fieldDef.relation) {
|
|
907
|
-
const fieldRef = this.fieldRef(model, field, expressionBuilder(), modelAlias);
|
|
908
|
-
if (value === "asc" || value === "desc") {
|
|
909
|
-
result = result.orderBy(fieldRef, this.negateSort(value, negated));
|
|
910
|
-
} else if (value && typeof value === "object" && "nulls" in value && "sort" in value && (value.sort === "asc" || value.sort === "desc") && (value.nulls === "first" || value.nulls === "last")) {
|
|
911
|
-
result = result.orderBy(fieldRef, sql.raw(`${this.negateSort(value.sort, negated)} nulls ${value.nulls}`));
|
|
912
|
-
}
|
|
913
|
-
} else {
|
|
914
|
-
const relationModel = fieldDef.type;
|
|
915
|
-
if (fieldDef.array) {
|
|
916
|
-
if (typeof value !== "object") {
|
|
917
|
-
throw new QueryError(`invalid orderBy value for field "${field}"`);
|
|
918
|
-
}
|
|
919
|
-
if ("_count" in value) {
|
|
920
|
-
invariant2(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
|
|
921
|
-
const sort = this.negateSort(value._count, negated);
|
|
922
|
-
result = result.orderBy((eb) => {
|
|
923
|
-
const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
|
|
924
|
-
let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
|
|
925
|
-
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
926
|
-
subQuery = subQuery.where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(sql.ref(left), "=", sql.ref(right)))));
|
|
927
|
-
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
928
|
-
return subQuery;
|
|
929
|
-
}, sort);
|
|
930
|
-
}
|
|
931
|
-
} else {
|
|
932
|
-
result = result.leftJoin(relationModel, (join) => {
|
|
933
|
-
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, relationModel);
|
|
934
|
-
return join.on((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(sql.ref(left), "=", sql.ref(right)))));
|
|
935
|
-
});
|
|
936
|
-
result = this.buildOrderBy(result, fieldDef.type, relationModel, value, false, negated);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
});
|
|
941
|
-
return result;
|
|
942
|
-
}
|
|
943
|
-
buildSelectAllFields(model, query, omit, modelAlias) {
|
|
944
|
-
const modelDef = requireModel(this.schema, model);
|
|
945
|
-
let result = query;
|
|
946
|
-
for (const field of Object.keys(modelDef.fields)) {
|
|
947
|
-
if (isRelationField(this.schema, model, field)) {
|
|
948
|
-
continue;
|
|
949
|
-
}
|
|
950
|
-
if (omit?.[field] === true) {
|
|
951
|
-
continue;
|
|
952
|
-
}
|
|
953
|
-
result = this.buildSelectField(result, model, modelAlias, field);
|
|
954
|
-
}
|
|
955
|
-
const descendants = getDelegateDescendantModels(this.schema, model);
|
|
956
|
-
for (const subModel of descendants) {
|
|
957
|
-
result = this.buildDelegateJoin(model, modelAlias, subModel.name, result);
|
|
958
|
-
result = result.select((eb) => {
|
|
959
|
-
const jsonObject = {};
|
|
960
|
-
for (const field of Object.keys(subModel.fields)) {
|
|
961
|
-
if (isRelationField(this.schema, subModel.name, field) || isInheritedField(this.schema, subModel.name, field)) {
|
|
962
|
-
continue;
|
|
963
|
-
}
|
|
964
|
-
jsonObject[field] = eb.ref(`${subModel.name}.${field}`);
|
|
965
|
-
}
|
|
966
|
-
return this.buildJsonObject(eb, jsonObject).as(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`);
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
return result;
|
|
970
|
-
}
|
|
971
|
-
buildModelSelect(eb, model, subQueryAlias, payload, selectAllFields) {
|
|
972
|
-
let subQuery = this.buildSelectModel(eb, model, subQueryAlias);
|
|
973
|
-
if (selectAllFields) {
|
|
974
|
-
subQuery = this.buildSelectAllFields(model, subQuery, typeof payload === "object" ? payload?.omit : void 0, subQueryAlias);
|
|
975
|
-
}
|
|
976
|
-
if (payload && typeof payload === "object") {
|
|
977
|
-
subQuery = this.buildFilterSortTake(model, payload, subQuery, subQueryAlias);
|
|
978
|
-
}
|
|
979
|
-
return subQuery;
|
|
980
|
-
}
|
|
981
|
-
buildSelectField(query, model, modelAlias, field) {
|
|
982
|
-
const fieldDef = requireField(this.schema, model, field);
|
|
983
|
-
if (fieldDef.computed) {
|
|
984
|
-
return query.select((eb) => this.fieldRef(model, field, eb, modelAlias).as(field));
|
|
985
|
-
} else if (!fieldDef.originModel) {
|
|
986
|
-
return query.select(sql.ref(`${modelAlias}.${field}`).as(field));
|
|
987
|
-
} else {
|
|
988
|
-
return this.buildSelectField(query, fieldDef.originModel, fieldDef.originModel, field);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
|
|
992
|
-
const idFields = requireIdFields(this.schema, thisModel);
|
|
993
|
-
query = query.leftJoin(otherModelAlias, (qb) => {
|
|
994
|
-
for (const idField of idFields) {
|
|
995
|
-
qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
|
|
996
|
-
}
|
|
997
|
-
return qb;
|
|
998
|
-
});
|
|
999
|
-
return query;
|
|
1000
|
-
}
|
|
1001
|
-
buildCountJson(model, eb, parentAlias, payload) {
|
|
1002
|
-
const modelDef = requireModel(this.schema, model);
|
|
1003
|
-
const toManyRelations = Object.entries(modelDef.fields).filter(([, field]) => field.relation && field.array);
|
|
1004
|
-
const selections = payload === true ? {
|
|
1005
|
-
select: toManyRelations.reduce((acc, [field]) => {
|
|
1006
|
-
acc[field] = true;
|
|
1007
|
-
return acc;
|
|
1008
|
-
}, {})
|
|
1009
|
-
} : payload;
|
|
1010
|
-
const jsonObject = {};
|
|
1011
|
-
for (const [field, value] of Object.entries(selections.select)) {
|
|
1012
|
-
const fieldDef = requireField(this.schema, model, field);
|
|
1013
|
-
const fieldModel = fieldDef.type;
|
|
1014
|
-
let fieldCountQuery;
|
|
1015
|
-
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
1016
|
-
if (m2m) {
|
|
1017
|
-
fieldCountQuery = eb.selectFrom(fieldModel).innerJoin(m2m.joinTable, (join) => join.onRef(`${m2m.joinTable}.${m2m.otherFkName}`, "=", `${fieldModel}.${m2m.otherPKName}`).onRef(`${m2m.joinTable}.${m2m.parentFkName}`, "=", `${parentAlias}.${m2m.parentPKName}`)).select(eb.fn.countAll().as(`_count$${field}`));
|
|
1018
|
-
} else {
|
|
1019
|
-
fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
|
|
1020
|
-
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
|
|
1021
|
-
for (const [left, right] of joinPairs) {
|
|
1022
|
-
fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
|
|
1026
|
-
const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
|
|
1027
|
-
fieldCountQuery = fieldCountQuery.where(filter);
|
|
1028
|
-
}
|
|
1029
|
-
jsonObject[field] = fieldCountQuery;
|
|
1030
|
-
}
|
|
1031
|
-
return this.buildJsonObject(eb, jsonObject);
|
|
1032
|
-
}
|
|
1033
|
-
// #endregion
|
|
1034
|
-
// #region utils
|
|
1035
|
-
negateSort(sort, negated) {
|
|
1036
|
-
return negated ? sort === "asc" ? "desc" : "asc" : sort;
|
|
1037
|
-
}
|
|
1038
|
-
true(eb) {
|
|
1039
|
-
return eb.lit(this.transformPrimitive(true, "Boolean", false));
|
|
1040
|
-
}
|
|
1041
|
-
false(eb) {
|
|
1042
|
-
return eb.lit(this.transformPrimitive(false, "Boolean", false));
|
|
1043
|
-
}
|
|
1044
|
-
isTrue(expression) {
|
|
1045
|
-
const node = expression.toOperationNode();
|
|
1046
|
-
if (node.kind !== "ValueNode") {
|
|
1047
|
-
return false;
|
|
1048
|
-
}
|
|
1049
|
-
return node.value === true || node.value === 1;
|
|
1050
|
-
}
|
|
1051
|
-
isFalse(expression) {
|
|
1052
|
-
const node = expression.toOperationNode();
|
|
1053
|
-
if (node.kind !== "ValueNode") {
|
|
1054
|
-
return false;
|
|
1055
|
-
}
|
|
1056
|
-
return node.value === false || node.value === 0;
|
|
1057
|
-
}
|
|
1058
|
-
and(eb, ...args) {
|
|
1059
|
-
const nonTrueArgs = args.filter((arg) => !this.isTrue(arg));
|
|
1060
|
-
if (nonTrueArgs.length === 0) {
|
|
1061
|
-
return this.true(eb);
|
|
1062
|
-
} else if (nonTrueArgs.length === 1) {
|
|
1063
|
-
return nonTrueArgs[0];
|
|
1064
|
-
} else {
|
|
1065
|
-
return eb.and(nonTrueArgs);
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
or(eb, ...args) {
|
|
1069
|
-
const nonFalseArgs = args.filter((arg) => !this.isFalse(arg));
|
|
1070
|
-
if (nonFalseArgs.length === 0) {
|
|
1071
|
-
return this.false(eb);
|
|
1072
|
-
} else if (nonFalseArgs.length === 1) {
|
|
1073
|
-
return nonFalseArgs[0];
|
|
1074
|
-
} else {
|
|
1075
|
-
return eb.or(nonFalseArgs);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
not(eb, ...args) {
|
|
1079
|
-
return eb.not(this.and(eb, ...args));
|
|
1080
|
-
}
|
|
1081
|
-
fieldRef(model, field, eb, modelAlias, inlineComputedField = true) {
|
|
1082
|
-
return buildFieldRef(this.schema, model, field, this.options, eb, modelAlias, inlineComputedField);
|
|
1083
|
-
}
|
|
1084
|
-
canJoinWithoutNestedSelect(modelDef, payload) {
|
|
1085
|
-
if (modelDef.computedFields) {
|
|
1086
|
-
return false;
|
|
1087
|
-
}
|
|
1088
|
-
if (modelDef.baseModel || modelDef.isDelegate) {
|
|
1089
|
-
return false;
|
|
1090
|
-
}
|
|
1091
|
-
if (typeof payload === "object" && (payload.orderBy || payload.skip !== void 0 || payload.take !== void 0 || payload.cursor || payload.distinct)) {
|
|
1092
|
-
return false;
|
|
1093
|
-
}
|
|
1094
|
-
return true;
|
|
1095
|
-
}
|
|
1096
|
-
};
|
|
1097
|
-
|
|
1098
|
-
// src/client/crud/dialects/postgresql.ts
|
|
1099
|
-
var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
1100
|
-
static {
|
|
1101
|
-
__name(this, "PostgresCrudDialect");
|
|
1102
|
-
}
|
|
1103
|
-
constructor(schema, options) {
|
|
1104
|
-
super(schema, options);
|
|
1105
|
-
}
|
|
1106
|
-
get provider() {
|
|
1107
|
-
return "postgresql";
|
|
1108
|
-
}
|
|
1109
|
-
transformPrimitive(value, type, forArrayField) {
|
|
1110
|
-
if (value === void 0) {
|
|
1111
|
-
return value;
|
|
1112
|
-
}
|
|
1113
|
-
if (Array.isArray(value)) {
|
|
1114
|
-
if (type === "Json" && !forArrayField) {
|
|
1115
|
-
return JSON.stringify(value);
|
|
1116
|
-
} else {
|
|
1117
|
-
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
1118
|
-
}
|
|
1119
|
-
} else {
|
|
1120
|
-
return match3(type).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
transformOutput(value, type) {
|
|
1124
|
-
if (value === null || value === void 0) {
|
|
1125
|
-
return value;
|
|
1126
|
-
}
|
|
1127
|
-
return match3(type).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Decimal", () => this.transformDecimal(value)).otherwise(() => super.transformOutput(value, type));
|
|
1128
|
-
}
|
|
1129
|
-
transformOutputBigInt(value) {
|
|
1130
|
-
if (typeof value === "bigint") {
|
|
1131
|
-
return value;
|
|
1132
|
-
}
|
|
1133
|
-
invariant3(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1134
|
-
return BigInt(value);
|
|
1135
|
-
}
|
|
1136
|
-
transformDecimal(value) {
|
|
1137
|
-
if (value instanceof Decimal) {
|
|
1138
|
-
return value;
|
|
1139
|
-
}
|
|
1140
|
-
invariant3(typeof value === "string" || typeof value === "number" || value instanceof Decimal, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1141
|
-
return new Decimal(value);
|
|
1142
|
-
}
|
|
1143
|
-
transformOutputDate(value) {
|
|
1144
|
-
if (typeof value === "string") {
|
|
1145
|
-
return new Date(value);
|
|
1146
|
-
} else if (value instanceof Date && this.options.fixPostgresTimezone !== false) {
|
|
1147
|
-
return new Date(value.getTime() - value.getTimezoneOffset() * 60 * 1e3);
|
|
1148
|
-
} else {
|
|
1149
|
-
return value;
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
transformOutputBytes(value) {
|
|
1153
|
-
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1154
|
-
}
|
|
1155
|
-
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1156
|
-
const relationResultName = `${parentAlias}$${relationField}`;
|
|
1157
|
-
const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload, relationResultName);
|
|
1158
|
-
return joinedQuery.select(`${relationResultName}.$data as ${relationField}`);
|
|
1159
|
-
}
|
|
1160
|
-
buildRelationJSON(model, qb, relationField, parentAlias, payload, resultName) {
|
|
1161
|
-
const relationFieldDef = requireField(this.schema, model, relationField);
|
|
1162
|
-
const relationModel = relationFieldDef.type;
|
|
1163
|
-
return qb.leftJoinLateral((eb) => {
|
|
1164
|
-
const relationSelectName = `${resultName}$sub`;
|
|
1165
|
-
const relationModelDef = requireModel(this.schema, relationModel);
|
|
1166
|
-
let tbl;
|
|
1167
|
-
if (this.canJoinWithoutNestedSelect(relationModelDef, payload)) {
|
|
1168
|
-
tbl = this.buildModelSelect(eb, relationModel, relationSelectName, payload, false);
|
|
1169
|
-
tbl = this.buildRelationJoinFilter(tbl, model, relationField, relationModel, relationSelectName, parentAlias);
|
|
1170
|
-
} else {
|
|
1171
|
-
tbl = eb.selectFrom(() => {
|
|
1172
|
-
let subQuery = this.buildModelSelect(eb, relationModel, `${relationSelectName}$t`, payload, true);
|
|
1173
|
-
subQuery = this.buildRelationJoinFilter(subQuery, model, relationField, relationModel, `${relationSelectName}$t`, parentAlias);
|
|
1174
|
-
return subQuery.as(relationSelectName);
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
tbl = this.buildRelationObjectSelect(relationModel, relationSelectName, relationFieldDef, tbl, payload, resultName);
|
|
1178
|
-
tbl = this.buildRelationJoins(tbl, relationModel, relationSelectName, payload, resultName);
|
|
1179
|
-
return tbl.as(resultName);
|
|
1180
|
-
}, (join) => join.onTrue());
|
|
1181
|
-
}
|
|
1182
|
-
buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
|
|
1183
|
-
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1184
|
-
if (m2m) {
|
|
1185
|
-
const parentIds = requireIdFields(this.schema, model);
|
|
1186
|
-
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1187
|
-
invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1188
|
-
invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1189
|
-
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}`)));
|
|
1190
|
-
} else {
|
|
1191
|
-
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
|
|
1192
|
-
query = query.where((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(sql2.ref(left), "=", sql2.ref(right)))));
|
|
1193
|
-
}
|
|
1194
|
-
return query;
|
|
1195
|
-
}
|
|
1196
|
-
buildRelationObjectSelect(relationModel, relationModelAlias, relationFieldDef, qb, payload, parentResultName) {
|
|
1197
|
-
qb = qb.select((eb) => {
|
|
1198
|
-
const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
|
|
1199
|
-
if (relationFieldDef.array) {
|
|
1200
|
-
return eb.fn.coalesce(sql2`jsonb_agg(jsonb_build_object(${sql2.join(objArgs)}))`, sql2`'[]'::jsonb`).as("$data");
|
|
1201
|
-
} else {
|
|
1202
|
-
return sql2`jsonb_build_object(${sql2.join(objArgs)})`.as("$data");
|
|
1203
|
-
}
|
|
1204
|
-
});
|
|
1205
|
-
return qb;
|
|
1206
|
-
}
|
|
1207
|
-
buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName) {
|
|
1208
|
-
const relationModelDef = requireModel(this.schema, relationModel);
|
|
1209
|
-
const objArgs = [];
|
|
1210
|
-
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1211
|
-
if (descendantModels.length > 0) {
|
|
1212
|
-
objArgs.push(...descendantModels.map((subModel) => [
|
|
1213
|
-
sql2.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1214
|
-
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1215
|
-
]).flatMap((v) => v));
|
|
1216
|
-
}
|
|
1217
|
-
if (payload === true || !payload.select) {
|
|
1218
|
-
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
1219
|
-
sql2.lit(field),
|
|
1220
|
-
this.fieldRef(relationModel, field, eb, relationModelAlias, false)
|
|
1221
|
-
]).flatMap((v) => v));
|
|
1222
|
-
} else if (payload.select) {
|
|
1223
|
-
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
|
|
1224
|
-
if (field === "_count") {
|
|
1225
|
-
const subJson = this.buildCountJson(relationModel, eb, relationModelAlias, value);
|
|
1226
|
-
return [
|
|
1227
|
-
sql2.lit(field),
|
|
1228
|
-
subJson
|
|
1229
|
-
];
|
|
1230
|
-
} else {
|
|
1231
|
-
const fieldDef = requireField(this.schema, relationModel, field);
|
|
1232
|
-
const fieldValue = fieldDef.relation ? eb.ref(`${parentResultName}$${field}.$data`) : this.fieldRef(relationModel, field, eb, relationModelAlias, false);
|
|
1233
|
-
return [
|
|
1234
|
-
sql2.lit(field),
|
|
1235
|
-
fieldValue
|
|
1236
|
-
];
|
|
1237
|
-
}
|
|
1238
|
-
}).flatMap((v) => v));
|
|
1239
|
-
}
|
|
1240
|
-
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
1241
|
-
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
|
|
1242
|
-
sql2.lit(field),
|
|
1243
|
-
// reference the synthesized JSON field
|
|
1244
|
-
eb.ref(`${parentResultName}$${field}.$data`)
|
|
1245
|
-
]).flatMap((v) => v));
|
|
1246
|
-
}
|
|
1247
|
-
return objArgs;
|
|
1248
|
-
}
|
|
1249
|
-
buildRelationJoins(query, relationModel, relationModelAlias, payload, parentResultName) {
|
|
1250
|
-
let result = query;
|
|
1251
|
-
if (typeof payload === "object") {
|
|
1252
|
-
const selectInclude = payload.include ?? payload.select;
|
|
1253
|
-
if (selectInclude && typeof selectInclude === "object") {
|
|
1254
|
-
Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
|
|
1255
|
-
result = this.buildRelationJSON(relationModel, result, field, relationModelAlias, value, `${parentResultName}$${field}`);
|
|
1256
|
-
});
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
return result;
|
|
1260
|
-
}
|
|
1261
|
-
buildSkipTake(query, skip, take) {
|
|
1262
|
-
if (take !== void 0) {
|
|
1263
|
-
query = query.limit(take);
|
|
1264
|
-
}
|
|
1265
|
-
if (skip !== void 0) {
|
|
1266
|
-
query = query.offset(skip);
|
|
1267
|
-
}
|
|
1268
|
-
return query;
|
|
1269
|
-
}
|
|
1270
|
-
buildJsonObject(eb, value) {
|
|
1271
|
-
return eb.fn("jsonb_build_object", Object.entries(value).flatMap(([key, value2]) => [
|
|
1272
|
-
sql2.lit(key),
|
|
1273
|
-
value2
|
|
1274
|
-
]));
|
|
1275
|
-
}
|
|
1276
|
-
get supportsUpdateWithLimit() {
|
|
1277
|
-
return false;
|
|
1278
|
-
}
|
|
1279
|
-
get supportsDeleteWithLimit() {
|
|
1280
|
-
return false;
|
|
1281
|
-
}
|
|
1282
|
-
get supportsDistinctOn() {
|
|
1283
|
-
return true;
|
|
1284
|
-
}
|
|
1285
|
-
buildArrayLength(eb, array) {
|
|
1286
|
-
return eb.fn("array_length", [
|
|
1287
|
-
array
|
|
1288
|
-
]);
|
|
1289
|
-
}
|
|
1290
|
-
buildArrayLiteralSQL(values) {
|
|
1291
|
-
if (values.length === 0) {
|
|
1292
|
-
return "{}";
|
|
1293
|
-
} else {
|
|
1294
|
-
return `ARRAY[${values.map((v) => typeof v === "string" ? `'${v}'` : v)}]`;
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
get supportInsertWithDefault() {
|
|
1298
|
-
return true;
|
|
1299
|
-
}
|
|
1300
|
-
getFieldSqlType(fieldDef) {
|
|
1301
|
-
if (fieldDef.relation) {
|
|
1302
|
-
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1303
|
-
}
|
|
1304
|
-
let result;
|
|
1305
|
-
if (this.schema.enums?.[fieldDef.type]) {
|
|
1306
|
-
result = "text";
|
|
1307
|
-
} else {
|
|
1308
|
-
result = match3(fieldDef.type).with("String", () => "text").with("Boolean", () => "boolean").with("Int", () => "integer").with("BigInt", () => "bigint").with("Float", () => "double precision").with("Decimal", () => "decimal").with("DateTime", () => "timestamp").with("Bytes", () => "bytea").with("Json", () => "jsonb").otherwise(() => "text");
|
|
1309
|
-
}
|
|
1310
|
-
if (fieldDef.array) {
|
|
1311
|
-
result += "[]";
|
|
1312
|
-
}
|
|
1313
|
-
return result;
|
|
1314
|
-
}
|
|
1315
|
-
getStringCasingBehavior() {
|
|
1316
|
-
return {
|
|
1317
|
-
supportsILike: true,
|
|
1318
|
-
likeCaseSensitive: true
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
};
|
|
1322
|
-
|
|
1323
|
-
// src/client/crud/dialects/sqlite.ts
|
|
1324
|
-
import { invariant as invariant4 } from "@zenstackhq/common-helpers";
|
|
1325
|
-
import Decimal2 from "decimal.js";
|
|
1326
|
-
import { sql as sql3 } from "kysely";
|
|
1327
|
-
import { match as match4 } from "ts-pattern";
|
|
1328
|
-
var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
1329
|
-
static {
|
|
1330
|
-
__name(this, "SqliteCrudDialect");
|
|
1331
|
-
}
|
|
1332
|
-
get provider() {
|
|
1333
|
-
return "sqlite";
|
|
1334
|
-
}
|
|
1335
|
-
transformPrimitive(value, type, _forArrayField) {
|
|
1336
|
-
if (value === void 0) {
|
|
1337
|
-
return value;
|
|
1338
|
-
}
|
|
1339
|
-
if (Array.isArray(value)) {
|
|
1340
|
-
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
1341
|
-
} else {
|
|
1342
|
-
if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1343
|
-
return JSON.stringify(value);
|
|
1344
|
-
} else {
|
|
1345
|
-
return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
transformOutput(value, type) {
|
|
1350
|
-
if (value === null || value === void 0) {
|
|
1351
|
-
return value;
|
|
1352
|
-
} else if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1353
|
-
return this.transformOutputJson(value);
|
|
1354
|
-
} else {
|
|
1355
|
-
return match4(type).with("Boolean", () => this.transformOutputBoolean(value)).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("Decimal", () => this.transformOutputDecimal(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Json", () => this.transformOutputJson(value)).otherwise(() => super.transformOutput(value, type));
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
transformOutputDecimal(value) {
|
|
1359
|
-
if (value instanceof Decimal2) {
|
|
1360
|
-
return value;
|
|
1361
|
-
}
|
|
1362
|
-
invariant4(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1363
|
-
return new Decimal2(value);
|
|
1364
|
-
}
|
|
1365
|
-
transformOutputBigInt(value) {
|
|
1366
|
-
if (typeof value === "bigint") {
|
|
1367
|
-
return value;
|
|
1368
|
-
}
|
|
1369
|
-
invariant4(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1370
|
-
return BigInt(value);
|
|
1371
|
-
}
|
|
1372
|
-
transformOutputBoolean(value) {
|
|
1373
|
-
return !!value;
|
|
1374
|
-
}
|
|
1375
|
-
transformOutputDate(value) {
|
|
1376
|
-
if (typeof value === "number") {
|
|
1377
|
-
return new Date(value);
|
|
1378
|
-
} else if (typeof value === "string") {
|
|
1379
|
-
return new Date(value);
|
|
1380
|
-
} else {
|
|
1381
|
-
return value;
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
transformOutputBytes(value) {
|
|
1385
|
-
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1386
|
-
}
|
|
1387
|
-
transformOutputJson(value) {
|
|
1388
|
-
if (typeof value === "string") {
|
|
1389
|
-
try {
|
|
1390
|
-
return JSON.parse(value);
|
|
1391
|
-
} catch (e) {
|
|
1392
|
-
throw new QueryError("Invalid JSON returned", e);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
return value;
|
|
1396
|
-
}
|
|
1397
|
-
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1398
|
-
return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
|
|
1399
|
-
}
|
|
1400
|
-
buildRelationJSON(model, eb, relationField, parentAlias, payload) {
|
|
1401
|
-
const relationFieldDef = requireField(this.schema, model, relationField);
|
|
1402
|
-
const relationModel = relationFieldDef.type;
|
|
1403
|
-
const relationModelDef = requireModel(this.schema, relationModel);
|
|
1404
|
-
const subQueryName = `${parentAlias}$${relationField}`;
|
|
1405
|
-
let tbl;
|
|
1406
|
-
if (this.canJoinWithoutNestedSelect(relationModelDef, payload)) {
|
|
1407
|
-
tbl = this.buildModelSelect(eb, relationModel, subQueryName, payload, false);
|
|
1408
|
-
tbl = this.buildRelationJoinFilter(tbl, model, relationField, subQueryName, parentAlias);
|
|
1409
|
-
} else {
|
|
1410
|
-
tbl = eb.selectFrom(() => {
|
|
1411
|
-
const selectModelAlias = `${parentAlias}$${relationField}$sub`;
|
|
1412
|
-
let selectModelQuery = this.buildModelSelect(eb, relationModel, selectModelAlias, payload, true);
|
|
1413
|
-
selectModelQuery = this.buildRelationJoinFilter(selectModelQuery, model, relationField, selectModelAlias, parentAlias);
|
|
1414
|
-
return selectModelQuery.as(subQueryName);
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
tbl = tbl.select(() => {
|
|
1418
|
-
const objArgs = [];
|
|
1419
|
-
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1420
|
-
if (descendantModels.length > 0) {
|
|
1421
|
-
objArgs.push(...descendantModels.map((subModel) => [
|
|
1422
|
-
sql3.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1423
|
-
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1424
|
-
]).flatMap((v) => v));
|
|
1425
|
-
}
|
|
1426
|
-
if (payload === true || !payload.select) {
|
|
1427
|
-
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
1428
|
-
sql3.lit(field),
|
|
1429
|
-
this.fieldRef(relationModel, field, eb, subQueryName, false)
|
|
1430
|
-
]).flatMap((v) => v));
|
|
1431
|
-
} else if (payload.select) {
|
|
1432
|
-
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
|
|
1433
|
-
if (field === "_count") {
|
|
1434
|
-
const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
|
|
1435
|
-
return [
|
|
1436
|
-
sql3.lit(field),
|
|
1437
|
-
subJson
|
|
1438
|
-
];
|
|
1439
|
-
} else {
|
|
1440
|
-
const fieldDef = requireField(this.schema, relationModel, field);
|
|
1441
|
-
if (fieldDef.relation) {
|
|
1442
|
-
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
1443
|
-
return [
|
|
1444
|
-
sql3.lit(field),
|
|
1445
|
-
subJson
|
|
1446
|
-
];
|
|
1447
|
-
} else {
|
|
1448
|
-
return [
|
|
1449
|
-
sql3.lit(field),
|
|
1450
|
-
this.fieldRef(relationModel, field, eb, subQueryName, false)
|
|
1451
|
-
];
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
}).flatMap((v) => v));
|
|
1455
|
-
}
|
|
1456
|
-
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
1457
|
-
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
|
|
1458
|
-
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
1459
|
-
return [
|
|
1460
|
-
sql3.lit(field),
|
|
1461
|
-
subJson
|
|
1462
|
-
];
|
|
1463
|
-
}).flatMap((v) => v));
|
|
1464
|
-
}
|
|
1465
|
-
if (relationFieldDef.array) {
|
|
1466
|
-
return eb.fn.coalesce(sql3`json_group_array(json_object(${sql3.join(objArgs)}))`, sql3`json_array()`).as("$data");
|
|
1467
|
-
} else {
|
|
1468
|
-
return sql3`json_object(${sql3.join(objArgs)})`.as("$data");
|
|
1469
|
-
}
|
|
1470
|
-
});
|
|
1471
|
-
return tbl;
|
|
1472
|
-
}
|
|
1473
|
-
buildRelationJoinFilter(selectModelQuery, model, relationField, relationModelAlias, parentAlias) {
|
|
1474
|
-
const fieldDef = requireField(this.schema, model, relationField);
|
|
1475
|
-
const relationModel = fieldDef.type;
|
|
1476
|
-
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1477
|
-
if (m2m) {
|
|
1478
|
-
const parentIds = requireIdFields(this.schema, model);
|
|
1479
|
-
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1480
|
-
invariant4(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1481
|
-
invariant4(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1482
|
-
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}`)));
|
|
1483
|
-
} else {
|
|
1484
|
-
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
1485
|
-
keyPairs.forEach(({ fk, pk }) => {
|
|
1486
|
-
if (ownedByModel) {
|
|
1487
|
-
selectModelQuery = selectModelQuery.whereRef(`${relationModelAlias}.${pk}`, "=", `${parentAlias}.${fk}`);
|
|
1488
|
-
} else {
|
|
1489
|
-
selectModelQuery = selectModelQuery.whereRef(`${relationModelAlias}.${fk}`, "=", `${parentAlias}.${pk}`);
|
|
1490
|
-
}
|
|
1491
|
-
});
|
|
1492
|
-
}
|
|
1493
|
-
return selectModelQuery;
|
|
1494
|
-
}
|
|
1495
|
-
buildSkipTake(query, skip, take) {
|
|
1496
|
-
if (take !== void 0) {
|
|
1497
|
-
query = query.limit(take);
|
|
1498
|
-
}
|
|
1499
|
-
if (skip !== void 0) {
|
|
1500
|
-
query = query.offset(skip);
|
|
1501
|
-
if (take === void 0) {
|
|
1502
|
-
query = query.limit(-1);
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
return query;
|
|
1506
|
-
}
|
|
1507
|
-
buildJsonObject(eb, value) {
|
|
1508
|
-
return eb.fn("json_object", Object.entries(value).flatMap(([key, value2]) => [
|
|
1509
|
-
sql3.lit(key),
|
|
1510
|
-
value2
|
|
1511
|
-
]));
|
|
1512
|
-
}
|
|
1513
|
-
get supportsUpdateWithLimit() {
|
|
1514
|
-
return false;
|
|
1515
|
-
}
|
|
1516
|
-
get supportsDeleteWithLimit() {
|
|
1517
|
-
return false;
|
|
1518
|
-
}
|
|
1519
|
-
get supportsDistinctOn() {
|
|
1520
|
-
return false;
|
|
1521
|
-
}
|
|
1522
|
-
buildArrayLength(eb, array) {
|
|
1523
|
-
return eb.fn("json_array_length", [
|
|
1524
|
-
array
|
|
1525
|
-
]);
|
|
1526
|
-
}
|
|
1527
|
-
buildArrayLiteralSQL(_values) {
|
|
1528
|
-
throw new Error("SQLite does not support array literals");
|
|
1529
|
-
}
|
|
1530
|
-
get supportInsertWithDefault() {
|
|
1531
|
-
return false;
|
|
1532
|
-
}
|
|
1533
|
-
getFieldSqlType(fieldDef) {
|
|
1534
|
-
if (fieldDef.relation) {
|
|
1535
|
-
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1536
|
-
}
|
|
1537
|
-
if (fieldDef.array) {
|
|
1538
|
-
throw new QueryError("SQLite does not support scalar list type");
|
|
1539
|
-
}
|
|
1540
|
-
if (this.schema.enums?.[fieldDef.type]) {
|
|
1541
|
-
return "text";
|
|
1542
|
-
}
|
|
1543
|
-
return match4(fieldDef.type).with("String", () => "text").with("Boolean", () => "integer").with("Int", () => "integer").with("BigInt", () => "integer").with("Float", () => "real").with("Decimal", () => "decimal").with("DateTime", () => "numeric").with("Bytes", () => "blob").with("Json", () => "jsonb").otherwise(() => "text");
|
|
1544
|
-
}
|
|
1545
|
-
getStringCasingBehavior() {
|
|
1546
|
-
return {
|
|
1547
|
-
supportsILike: false,
|
|
1548
|
-
likeCaseSensitive: false
|
|
1549
|
-
};
|
|
1550
|
-
}
|
|
1551
|
-
};
|
|
1552
|
-
|
|
1553
|
-
// src/client/crud/dialects/index.ts
|
|
1554
|
-
function getCrudDialect(schema, options) {
|
|
1555
|
-
return match5(schema.provider.type).with("sqlite", () => new SqliteCrudDialect(schema, options)).with("postgresql", () => new PostgresCrudDialect(schema, options)).exhaustive();
|
|
1556
|
-
}
|
|
1557
|
-
__name(getCrudDialect, "getCrudDialect");
|
|
1558
|
-
|
|
1559
|
-
// src/client/crud/operations/count.ts
|
|
1560
|
-
import { sql as sql6 } from "kysely";
|
|
1561
|
-
|
|
1562
|
-
// src/client/crud/operations/create.ts
|
|
1563
|
-
import { match as match8 } from "ts-pattern";
|
|
1564
|
-
|
|
1565
|
-
// src/client/crud/operations/delete.ts
|
|
1566
|
-
import { match as match9 } from "ts-pattern";
|
|
1567
|
-
|
|
1568
|
-
// src/client/crud/operations/group-by.ts
|
|
1569
|
-
import { expressionBuilder as expressionBuilder3 } from "kysely";
|
|
1570
|
-
import { match as match10 } from "ts-pattern";
|
|
1571
|
-
|
|
1572
|
-
// src/client/crud/operations/update.ts
|
|
1573
|
-
import { match as match11 } from "ts-pattern";
|
|
1574
|
-
|
|
1575
|
-
// src/client/crud/validator.ts
|
|
1576
|
-
import { invariant as invariant6 } from "@zenstackhq/common-helpers";
|
|
1577
|
-
import Decimal3 from "decimal.js";
|
|
1578
|
-
import stableStringify from "json-stable-stringify";
|
|
1579
|
-
import { match as match12, P as P2 } from "ts-pattern";
|
|
1580
|
-
import { z } from "zod";
|
|
1581
|
-
|
|
1582
|
-
// src/utils/zod-utils.ts
|
|
1583
|
-
import { fromError as fromError3 } from "zod-validation-error/v3";
|
|
1584
|
-
import { fromError as fromError4 } from "zod-validation-error/v4";
|
|
1585
|
-
|
|
1586
|
-
// src/client/executor/zenstack-query-executor.ts
|
|
1587
|
-
import { invariant as invariant8 } from "@zenstackhq/common-helpers";
|
|
1588
|
-
import { AndNode, DefaultQueryExecutor, DeleteQueryNode, InsertQueryNode, ReturningNode, SelectionNode as SelectionNode2, SingleConnectionProvider, TableNode as TableNode3, UpdateQueryNode, WhereNode } from "kysely";
|
|
1589
|
-
import { match as match13 } from "ts-pattern";
|
|
1590
|
-
|
|
1591
|
-
// src/client/kysely-utils.ts
|
|
1592
|
-
import { AliasNode, ColumnNode, ReferenceNode, TableNode } from "kysely";
|
|
1593
|
-
function extractFieldName(node) {
|
|
1594
|
-
if (ReferenceNode.is(node) && ColumnNode.is(node.column)) {
|
|
1595
|
-
return node.column.column.name;
|
|
1596
|
-
} else if (ColumnNode.is(node)) {
|
|
1597
|
-
return node.column.name;
|
|
1598
|
-
} else {
|
|
1599
|
-
return void 0;
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
__name(extractFieldName, "extractFieldName");
|
|
1603
|
-
|
|
1604
|
-
// src/client/executor/name-mapper.ts
|
|
1605
|
-
import { invariant as invariant7 } from "@zenstackhq/common-helpers";
|
|
1606
|
-
import { AliasNode as AliasNode2, ColumnNode as ColumnNode2, FromNode, IdentifierNode, OperationNodeTransformer, ReferenceNode as ReferenceNode2, SelectAllNode, SelectionNode, TableNode as TableNode2 } from "kysely";
|
|
1607
|
-
|
|
1608
|
-
// src/client/functions.ts
|
|
1609
|
-
import { invariant as invariant9, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
|
|
1610
|
-
import { sql as sql7, ValueNode } from "kysely";
|
|
1611
|
-
import { match as match14 } from "ts-pattern";
|
|
1612
|
-
|
|
1613
|
-
// src/client/helpers/schema-db-pusher.ts
|
|
1614
|
-
import { invariant as invariant10 } from "@zenstackhq/common-helpers";
|
|
1615
|
-
import { sql as sql8 } from "kysely";
|
|
1616
|
-
import toposort from "toposort";
|
|
1617
|
-
import { match as match15 } from "ts-pattern";
|
|
1618
|
-
|
|
1619
|
-
// src/client/index.ts
|
|
1620
|
-
import { sql as sql10 } from "kysely";
|
|
1621
|
-
|
|
1622
|
-
// src/plugins/policy/errors.ts
|
|
1623
|
-
var RejectedByPolicyReason = /* @__PURE__ */ function(RejectedByPolicyReason2) {
|
|
1624
|
-
RejectedByPolicyReason2["NO_ACCESS"] = "no-access";
|
|
1625
|
-
RejectedByPolicyReason2["CANNOT_READ_BACK"] = "cannot-read-back";
|
|
1626
|
-
RejectedByPolicyReason2["OTHER"] = "other";
|
|
1627
|
-
return RejectedByPolicyReason2;
|
|
1628
|
-
}({});
|
|
1629
|
-
var RejectedByPolicyError = class extends ZenStackError {
|
|
1630
|
-
static {
|
|
1631
|
-
__name(this, "RejectedByPolicyError");
|
|
1632
|
-
}
|
|
1633
|
-
model;
|
|
1634
|
-
reason;
|
|
1635
|
-
constructor(model, reason = "no-access", message) {
|
|
1636
|
-
super(message ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
|
|
1637
|
-
}
|
|
1638
|
-
};
|
|
1639
|
-
|
|
1640
|
-
// src/plugins/policy/functions.ts
|
|
1641
|
-
import { invariant as invariant15 } from "@zenstackhq/common-helpers";
|
|
1642
|
-
import { ExpressionWrapper as ExpressionWrapper2, ValueNode as ValueNode5 } from "kysely";
|
|
1643
|
-
|
|
1644
|
-
// src/plugins/policy/policy-handler.ts
|
|
1645
|
-
import { invariant as invariant14 } from "@zenstackhq/common-helpers";
|
|
1646
|
-
import { AliasNode as AliasNode5, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode4, DeleteQueryNode as DeleteQueryNode2, expressionBuilder as expressionBuilder5, ExpressionWrapper, FromNode as FromNode3, FunctionNode as FunctionNode3, IdentifierNode as IdentifierNode3, InsertQueryNode as InsertQueryNode2, OperationNodeTransformer as OperationNodeTransformer2, OperatorNode as OperatorNode3, ParensNode as ParensNode2, PrimitiveValueListNode, RawNode, ReferenceNode as ReferenceNode5, ReturningNode as ReturningNode2, SelectAllNode as SelectAllNode2, SelectionNode as SelectionNode4, SelectQueryNode as SelectQueryNode2, sql as sql11, TableNode as TableNode6, UpdateQueryNode as UpdateQueryNode2, ValueListNode as ValueListNode2, ValueNode as ValueNode4, ValuesNode, WhereNode as WhereNode3 } from "kysely";
|
|
1647
|
-
import { match as match19 } from "ts-pattern";
|
|
1648
|
-
|
|
1649
|
-
// src/utils/expression-utils.ts
|
|
1650
|
-
import { match as match16 } from "ts-pattern";
|
|
1651
|
-
var ExpressionVisitor = class {
|
|
1652
|
-
static {
|
|
1653
|
-
__name(this, "ExpressionVisitor");
|
|
1654
|
-
}
|
|
1655
|
-
visit(expr2) {
|
|
1656
|
-
match16(expr2).with({
|
|
1657
|
-
kind: "literal"
|
|
1658
|
-
}, (e) => this.visitLiteral(e)).with({
|
|
1659
|
-
kind: "array"
|
|
1660
|
-
}, (e) => this.visitArray(e)).with({
|
|
1661
|
-
kind: "field"
|
|
1662
|
-
}, (e) => this.visitField(e)).with({
|
|
1663
|
-
kind: "member"
|
|
1664
|
-
}, (e) => this.visitMember(e)).with({
|
|
1665
|
-
kind: "binary"
|
|
1666
|
-
}, (e) => this.visitBinary(e)).with({
|
|
1667
|
-
kind: "unary"
|
|
1668
|
-
}, (e) => this.visitUnary(e)).with({
|
|
1669
|
-
kind: "call"
|
|
1670
|
-
}, (e) => this.visitCall(e)).with({
|
|
1671
|
-
kind: "this"
|
|
1672
|
-
}, (e) => this.visitThis(e)).with({
|
|
1673
|
-
kind: "null"
|
|
1674
|
-
}, (e) => this.visitNull(e)).exhaustive();
|
|
1675
|
-
}
|
|
1676
|
-
visitLiteral(_e) {
|
|
1677
|
-
}
|
|
1678
|
-
visitArray(e) {
|
|
1679
|
-
e.items.forEach((item) => this.visit(item));
|
|
1680
|
-
}
|
|
1681
|
-
visitField(_e) {
|
|
1682
|
-
}
|
|
1683
|
-
visitMember(e) {
|
|
1684
|
-
this.visit(e.receiver);
|
|
1685
|
-
}
|
|
1686
|
-
visitBinary(e) {
|
|
1687
|
-
this.visit(e.left);
|
|
1688
|
-
this.visit(e.right);
|
|
1689
|
-
}
|
|
1690
|
-
visitUnary(e) {
|
|
1691
|
-
this.visit(e.operand);
|
|
1692
|
-
}
|
|
1693
|
-
visitCall(e) {
|
|
1694
|
-
e.args?.forEach((arg) => this.visit(arg));
|
|
1695
|
-
}
|
|
1696
|
-
visitThis(_e) {
|
|
1697
|
-
}
|
|
1698
|
-
visitNull(_e) {
|
|
1699
|
-
}
|
|
1700
|
-
};
|
|
1701
|
-
|
|
1702
|
-
// src/utils/default-operation-node-visitor.ts
|
|
1703
|
-
import { OperationNodeVisitor } from "kysely";
|
|
1704
|
-
var DefaultOperationNodeVisitor = class extends OperationNodeVisitor {
|
|
1705
|
-
static {
|
|
1706
|
-
__name(this, "DefaultOperationNodeVisitor");
|
|
1707
|
-
}
|
|
1708
|
-
defaultVisit(node) {
|
|
1709
|
-
Object.values(node).forEach((value) => {
|
|
1710
|
-
if (!value) {
|
|
1711
|
-
return;
|
|
1712
|
-
}
|
|
1713
|
-
if (Array.isArray(value)) {
|
|
1714
|
-
value.forEach((el) => this.defaultVisit(el));
|
|
1715
|
-
}
|
|
1716
|
-
if (typeof value === "object" && "kind" in value && typeof value.kind === "string") {
|
|
1717
|
-
this.visitNode(value);
|
|
1718
|
-
}
|
|
1719
|
-
});
|
|
1720
|
-
}
|
|
1721
|
-
visitSelectQuery(node) {
|
|
1722
|
-
this.defaultVisit(node);
|
|
1723
|
-
}
|
|
1724
|
-
visitSelection(node) {
|
|
1725
|
-
this.defaultVisit(node);
|
|
1726
|
-
}
|
|
1727
|
-
visitColumn(node) {
|
|
1728
|
-
this.defaultVisit(node);
|
|
1729
|
-
}
|
|
1730
|
-
visitAlias(node) {
|
|
1731
|
-
this.defaultVisit(node);
|
|
1732
|
-
}
|
|
1733
|
-
visitTable(node) {
|
|
1734
|
-
this.defaultVisit(node);
|
|
1735
|
-
}
|
|
1736
|
-
visitFrom(node) {
|
|
1737
|
-
this.defaultVisit(node);
|
|
1738
|
-
}
|
|
1739
|
-
visitReference(node) {
|
|
1740
|
-
this.defaultVisit(node);
|
|
1741
|
-
}
|
|
1742
|
-
visitAnd(node) {
|
|
1743
|
-
this.defaultVisit(node);
|
|
1744
|
-
}
|
|
1745
|
-
visitOr(node) {
|
|
1746
|
-
this.defaultVisit(node);
|
|
1747
|
-
}
|
|
1748
|
-
visitValueList(node) {
|
|
1749
|
-
this.defaultVisit(node);
|
|
1750
|
-
}
|
|
1751
|
-
visitParens(node) {
|
|
1752
|
-
this.defaultVisit(node);
|
|
1753
|
-
}
|
|
1754
|
-
visitJoin(node) {
|
|
1755
|
-
this.defaultVisit(node);
|
|
1756
|
-
}
|
|
1757
|
-
visitRaw(node) {
|
|
1758
|
-
this.defaultVisit(node);
|
|
1759
|
-
}
|
|
1760
|
-
visitWhere(node) {
|
|
1761
|
-
this.defaultVisit(node);
|
|
1762
|
-
}
|
|
1763
|
-
visitInsertQuery(node) {
|
|
1764
|
-
this.defaultVisit(node);
|
|
1765
|
-
}
|
|
1766
|
-
visitDeleteQuery(node) {
|
|
1767
|
-
this.defaultVisit(node);
|
|
1768
|
-
}
|
|
1769
|
-
visitReturning(node) {
|
|
1770
|
-
this.defaultVisit(node);
|
|
1771
|
-
}
|
|
1772
|
-
visitCreateTable(node) {
|
|
1773
|
-
this.defaultVisit(node);
|
|
1774
|
-
}
|
|
1775
|
-
visitAddColumn(node) {
|
|
1776
|
-
this.defaultVisit(node);
|
|
1777
|
-
}
|
|
1778
|
-
visitColumnDefinition(node) {
|
|
1779
|
-
this.defaultVisit(node);
|
|
1780
|
-
}
|
|
1781
|
-
visitDropTable(node) {
|
|
1782
|
-
this.defaultVisit(node);
|
|
1783
|
-
}
|
|
1784
|
-
visitOrderBy(node) {
|
|
1785
|
-
this.defaultVisit(node);
|
|
1786
|
-
}
|
|
1787
|
-
visitOrderByItem(node) {
|
|
1788
|
-
this.defaultVisit(node);
|
|
1789
|
-
}
|
|
1790
|
-
visitGroupBy(node) {
|
|
1791
|
-
this.defaultVisit(node);
|
|
1792
|
-
}
|
|
1793
|
-
visitGroupByItem(node) {
|
|
1794
|
-
this.defaultVisit(node);
|
|
1795
|
-
}
|
|
1796
|
-
visitUpdateQuery(node) {
|
|
1797
|
-
this.defaultVisit(node);
|
|
1798
|
-
}
|
|
1799
|
-
visitColumnUpdate(node) {
|
|
1800
|
-
this.defaultVisit(node);
|
|
1801
|
-
}
|
|
1802
|
-
visitLimit(node) {
|
|
1803
|
-
this.defaultVisit(node);
|
|
1804
|
-
}
|
|
1805
|
-
visitOffset(node) {
|
|
1806
|
-
this.defaultVisit(node);
|
|
1807
|
-
}
|
|
1808
|
-
visitOnConflict(node) {
|
|
1809
|
-
this.defaultVisit(node);
|
|
1810
|
-
}
|
|
1811
|
-
visitOnDuplicateKey(node) {
|
|
1812
|
-
this.defaultVisit(node);
|
|
1813
|
-
}
|
|
1814
|
-
visitCheckConstraint(node) {
|
|
1815
|
-
this.defaultVisit(node);
|
|
1816
|
-
}
|
|
1817
|
-
visitDataType(node) {
|
|
1818
|
-
this.defaultVisit(node);
|
|
1819
|
-
}
|
|
1820
|
-
visitSelectAll(node) {
|
|
1821
|
-
this.defaultVisit(node);
|
|
1822
|
-
}
|
|
1823
|
-
visitIdentifier(node) {
|
|
1824
|
-
this.defaultVisit(node);
|
|
1825
|
-
}
|
|
1826
|
-
visitSchemableIdentifier(node) {
|
|
1827
|
-
this.defaultVisit(node);
|
|
1828
|
-
}
|
|
1829
|
-
visitValue(node) {
|
|
1830
|
-
this.defaultVisit(node);
|
|
1831
|
-
}
|
|
1832
|
-
visitPrimitiveValueList(node) {
|
|
1833
|
-
this.defaultVisit(node);
|
|
1834
|
-
}
|
|
1835
|
-
visitOperator(node) {
|
|
1836
|
-
this.defaultVisit(node);
|
|
1837
|
-
}
|
|
1838
|
-
visitCreateIndex(node) {
|
|
1839
|
-
this.defaultVisit(node);
|
|
1840
|
-
}
|
|
1841
|
-
visitDropIndex(node) {
|
|
1842
|
-
this.defaultVisit(node);
|
|
1843
|
-
}
|
|
1844
|
-
visitList(node) {
|
|
1845
|
-
this.defaultVisit(node);
|
|
1846
|
-
}
|
|
1847
|
-
visitPrimaryKeyConstraint(node) {
|
|
1848
|
-
this.defaultVisit(node);
|
|
1849
|
-
}
|
|
1850
|
-
visitUniqueConstraint(node) {
|
|
1851
|
-
this.defaultVisit(node);
|
|
1852
|
-
}
|
|
1853
|
-
visitReferences(node) {
|
|
1854
|
-
this.defaultVisit(node);
|
|
1855
|
-
}
|
|
1856
|
-
visitWith(node) {
|
|
1857
|
-
this.defaultVisit(node);
|
|
1858
|
-
}
|
|
1859
|
-
visitCommonTableExpression(node) {
|
|
1860
|
-
this.defaultVisit(node);
|
|
1861
|
-
}
|
|
1862
|
-
visitCommonTableExpressionName(node) {
|
|
1863
|
-
this.defaultVisit(node);
|
|
1864
|
-
}
|
|
1865
|
-
visitHaving(node) {
|
|
1866
|
-
this.defaultVisit(node);
|
|
1867
|
-
}
|
|
1868
|
-
visitCreateSchema(node) {
|
|
1869
|
-
this.defaultVisit(node);
|
|
1870
|
-
}
|
|
1871
|
-
visitDropSchema(node) {
|
|
1872
|
-
this.defaultVisit(node);
|
|
1873
|
-
}
|
|
1874
|
-
visitAlterTable(node) {
|
|
1875
|
-
this.defaultVisit(node);
|
|
1876
|
-
}
|
|
1877
|
-
visitDropColumn(node) {
|
|
1878
|
-
this.defaultVisit(node);
|
|
1879
|
-
}
|
|
1880
|
-
visitRenameColumn(node) {
|
|
1881
|
-
this.defaultVisit(node);
|
|
1882
|
-
}
|
|
1883
|
-
visitAlterColumn(node) {
|
|
1884
|
-
this.defaultVisit(node);
|
|
1885
|
-
}
|
|
1886
|
-
visitModifyColumn(node) {
|
|
1887
|
-
this.defaultVisit(node);
|
|
1888
|
-
}
|
|
1889
|
-
visitAddConstraint(node) {
|
|
1890
|
-
this.defaultVisit(node);
|
|
1891
|
-
}
|
|
1892
|
-
visitDropConstraint(node) {
|
|
1893
|
-
this.defaultVisit(node);
|
|
1894
|
-
}
|
|
1895
|
-
visitForeignKeyConstraint(node) {
|
|
1896
|
-
this.defaultVisit(node);
|
|
1897
|
-
}
|
|
1898
|
-
visitCreateView(node) {
|
|
1899
|
-
this.defaultVisit(node);
|
|
1900
|
-
}
|
|
1901
|
-
visitDropView(node) {
|
|
1902
|
-
this.defaultVisit(node);
|
|
1903
|
-
}
|
|
1904
|
-
visitGenerated(node) {
|
|
1905
|
-
this.defaultVisit(node);
|
|
1906
|
-
}
|
|
1907
|
-
visitDefaultValue(node) {
|
|
1908
|
-
this.defaultVisit(node);
|
|
1909
|
-
}
|
|
1910
|
-
visitOn(node) {
|
|
1911
|
-
this.defaultVisit(node);
|
|
1912
|
-
}
|
|
1913
|
-
visitValues(node) {
|
|
1914
|
-
this.defaultVisit(node);
|
|
1915
|
-
}
|
|
1916
|
-
visitSelectModifier(node) {
|
|
1917
|
-
this.defaultVisit(node);
|
|
1918
|
-
}
|
|
1919
|
-
visitCreateType(node) {
|
|
1920
|
-
this.defaultVisit(node);
|
|
1921
|
-
}
|
|
1922
|
-
visitDropType(node) {
|
|
1923
|
-
this.defaultVisit(node);
|
|
1924
|
-
}
|
|
1925
|
-
visitExplain(node) {
|
|
1926
|
-
this.defaultVisit(node);
|
|
1927
|
-
}
|
|
1928
|
-
visitDefaultInsertValue(node) {
|
|
1929
|
-
this.defaultVisit(node);
|
|
1930
|
-
}
|
|
1931
|
-
visitAggregateFunction(node) {
|
|
1932
|
-
this.defaultVisit(node);
|
|
1933
|
-
}
|
|
1934
|
-
visitOver(node) {
|
|
1935
|
-
this.defaultVisit(node);
|
|
1936
|
-
}
|
|
1937
|
-
visitPartitionBy(node) {
|
|
1938
|
-
this.defaultVisit(node);
|
|
1939
|
-
}
|
|
1940
|
-
visitPartitionByItem(node) {
|
|
1941
|
-
this.defaultVisit(node);
|
|
1942
|
-
}
|
|
1943
|
-
visitSetOperation(node) {
|
|
1944
|
-
this.defaultVisit(node);
|
|
1945
|
-
}
|
|
1946
|
-
visitBinaryOperation(node) {
|
|
1947
|
-
this.defaultVisit(node);
|
|
1948
|
-
}
|
|
1949
|
-
visitUnaryOperation(node) {
|
|
1950
|
-
this.defaultVisit(node);
|
|
1951
|
-
}
|
|
1952
|
-
visitUsing(node) {
|
|
1953
|
-
this.defaultVisit(node);
|
|
1954
|
-
}
|
|
1955
|
-
visitFunction(node) {
|
|
1956
|
-
this.defaultVisit(node);
|
|
1957
|
-
}
|
|
1958
|
-
visitCase(node) {
|
|
1959
|
-
this.defaultVisit(node);
|
|
1960
|
-
}
|
|
1961
|
-
visitWhen(node) {
|
|
1962
|
-
this.defaultVisit(node);
|
|
1963
|
-
}
|
|
1964
|
-
visitJSONReference(node) {
|
|
1965
|
-
this.defaultVisit(node);
|
|
1966
|
-
}
|
|
1967
|
-
visitJSONPath(node) {
|
|
1968
|
-
this.defaultVisit(node);
|
|
1969
|
-
}
|
|
1970
|
-
visitJSONPathLeg(node) {
|
|
1971
|
-
this.defaultVisit(node);
|
|
1972
|
-
}
|
|
1973
|
-
visitJSONOperatorChain(node) {
|
|
1974
|
-
this.defaultVisit(node);
|
|
1975
|
-
}
|
|
1976
|
-
visitTuple(node) {
|
|
1977
|
-
this.defaultVisit(node);
|
|
1978
|
-
}
|
|
1979
|
-
visitMergeQuery(node) {
|
|
1980
|
-
this.defaultVisit(node);
|
|
1981
|
-
}
|
|
1982
|
-
visitMatched(node) {
|
|
1983
|
-
this.defaultVisit(node);
|
|
1984
|
-
}
|
|
1985
|
-
visitAddIndex(node) {
|
|
1986
|
-
this.defaultVisit(node);
|
|
1987
|
-
}
|
|
1988
|
-
visitCast(node) {
|
|
1989
|
-
this.defaultVisit(node);
|
|
1990
|
-
}
|
|
1991
|
-
visitFetch(node) {
|
|
1992
|
-
this.defaultVisit(node);
|
|
1993
|
-
}
|
|
1994
|
-
visitTop(node) {
|
|
1995
|
-
this.defaultVisit(node);
|
|
1996
|
-
}
|
|
1997
|
-
visitOutput(node) {
|
|
1998
|
-
this.defaultVisit(node);
|
|
1999
|
-
}
|
|
2000
|
-
};
|
|
2001
|
-
|
|
2002
|
-
// src/plugins/policy/column-collector.ts
|
|
2003
|
-
var ColumnCollector = class extends DefaultOperationNodeVisitor {
|
|
2004
|
-
static {
|
|
2005
|
-
__name(this, "ColumnCollector");
|
|
2006
|
-
}
|
|
2007
|
-
columns = [];
|
|
2008
|
-
collect(node) {
|
|
2009
|
-
this.columns = [];
|
|
2010
|
-
this.visitNode(node);
|
|
2011
|
-
return this.columns;
|
|
2012
|
-
}
|
|
2013
|
-
visitColumn(node) {
|
|
2014
|
-
if (!this.columns.includes(node.column.name)) {
|
|
2015
|
-
this.columns.push(node.column.name);
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
};
|
|
2019
|
-
|
|
2020
|
-
// src/plugins/policy/expression-transformer.ts
|
|
2021
|
-
import { invariant as invariant13 } from "@zenstackhq/common-helpers";
|
|
2022
|
-
import { AliasNode as AliasNode4, BinaryOperationNode as BinaryOperationNode2, ColumnNode as ColumnNode3, expressionBuilder as expressionBuilder4, FromNode as FromNode2, FunctionNode as FunctionNode2, IdentifierNode as IdentifierNode2, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode4, SelectionNode as SelectionNode3, SelectQueryNode, TableNode as TableNode5, ValueListNode, ValueNode as ValueNode3, WhereNode as WhereNode2 } from "kysely";
|
|
2023
|
-
import { match as match18 } from "ts-pattern";
|
|
2024
|
-
|
|
2025
|
-
// src/plugins/policy/expression-evaluator.ts
|
|
2026
|
-
import { invariant as invariant12 } from "@zenstackhq/common-helpers";
|
|
2027
|
-
import { match as match17 } from "ts-pattern";
|
|
2028
|
-
var ExpressionEvaluator = class {
|
|
2029
|
-
static {
|
|
2030
|
-
__name(this, "ExpressionEvaluator");
|
|
2031
|
-
}
|
|
2032
|
-
evaluate(expression, context) {
|
|
2033
|
-
const result = match17(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();
|
|
2034
|
-
return result ?? null;
|
|
2035
|
-
}
|
|
2036
|
-
evaluateCall(expr2, context) {
|
|
2037
|
-
if (expr2.function === "auth") {
|
|
2038
|
-
return context.auth;
|
|
2039
|
-
} else {
|
|
2040
|
-
throw new Error(`Unsupported call expression function: ${expr2.function}`);
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
evaluateUnary(expr2, context) {
|
|
2044
|
-
return match17(expr2.op).with("!", () => !this.evaluate(expr2.operand, context)).exhaustive();
|
|
2045
|
-
}
|
|
2046
|
-
evaluateMember(expr2, context) {
|
|
2047
|
-
let val = this.evaluate(expr2.receiver, context);
|
|
2048
|
-
for (const member of expr2.members) {
|
|
2049
|
-
val = val?.[member];
|
|
2050
|
-
}
|
|
2051
|
-
return val;
|
|
2052
|
-
}
|
|
2053
|
-
evaluateLiteral(expr2) {
|
|
2054
|
-
return expr2.value;
|
|
2055
|
-
}
|
|
2056
|
-
evaluateField(expr2, context) {
|
|
2057
|
-
return context.thisValue?.[expr2.field];
|
|
2058
|
-
}
|
|
2059
|
-
evaluateArray(expr2, context) {
|
|
2060
|
-
return expr2.items.map((item) => this.evaluate(item, context));
|
|
2061
|
-
}
|
|
2062
|
-
evaluateBinary(expr2, context) {
|
|
2063
|
-
if (expr2.op === "?" || expr2.op === "!" || expr2.op === "^") {
|
|
2064
|
-
return this.evaluateCollectionPredicate(expr2, context);
|
|
2065
|
-
}
|
|
2066
|
-
const left = this.evaluate(expr2.left, context);
|
|
2067
|
-
const right = this.evaluate(expr2.right, context);
|
|
2068
|
-
return match17(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", () => {
|
|
2069
|
-
const _right = right ?? [];
|
|
2070
|
-
invariant12(Array.isArray(_right), 'expected array for "in" operator');
|
|
2071
|
-
return _right.includes(left);
|
|
2072
|
-
}).exhaustive();
|
|
2073
|
-
}
|
|
2074
|
-
evaluateCollectionPredicate(expr2, context) {
|
|
2075
|
-
const op = expr2.op;
|
|
2076
|
-
invariant12(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
|
|
2077
|
-
const left = this.evaluate(expr2.left, context);
|
|
2078
|
-
if (!left) {
|
|
2079
|
-
return false;
|
|
2080
|
-
}
|
|
2081
|
-
invariant12(Array.isArray(left), "expected array");
|
|
2082
|
-
return match17(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
|
|
2083
|
-
...context,
|
|
2084
|
-
thisValue: item
|
|
2085
|
-
}))).with("!", () => left.every((item) => this.evaluate(expr2.right, {
|
|
2086
|
-
...context,
|
|
2087
|
-
thisValue: item
|
|
2088
|
-
}))).with("^", () => !left.some((item) => this.evaluate(expr2.right, {
|
|
2089
|
-
...context,
|
|
2090
|
-
thisValue: item
|
|
2091
|
-
}))).exhaustive();
|
|
2092
|
-
}
|
|
2093
|
-
};
|
|
2094
|
-
|
|
2095
|
-
// src/plugins/policy/utils.ts
|
|
2096
|
-
import { AliasNode as AliasNode3, AndNode as AndNode2, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode as ReferenceNode3, TableNode as TableNode4, UnaryOperationNode, ValueNode as ValueNode2 } from "kysely";
|
|
2097
|
-
function trueNode(dialect) {
|
|
2098
|
-
return ValueNode2.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
|
|
2099
|
-
}
|
|
2100
|
-
__name(trueNode, "trueNode");
|
|
2101
|
-
function falseNode(dialect) {
|
|
2102
|
-
return ValueNode2.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
|
|
2103
|
-
}
|
|
2104
|
-
__name(falseNode, "falseNode");
|
|
2105
|
-
function isTrueNode(node) {
|
|
2106
|
-
return ValueNode2.is(node) && (node.value === true || node.value === 1);
|
|
2107
|
-
}
|
|
2108
|
-
__name(isTrueNode, "isTrueNode");
|
|
2109
|
-
function isFalseNode(node) {
|
|
2110
|
-
return ValueNode2.is(node) && (node.value === false || node.value === 0);
|
|
2111
|
-
}
|
|
2112
|
-
__name(isFalseNode, "isFalseNode");
|
|
2113
|
-
function conjunction(dialect, nodes) {
|
|
2114
|
-
if (nodes.length === 0) {
|
|
2115
|
-
return trueNode(dialect);
|
|
2116
|
-
}
|
|
2117
|
-
if (nodes.length === 1) {
|
|
2118
|
-
return nodes[0];
|
|
2119
|
-
}
|
|
2120
|
-
if (nodes.some(isFalseNode)) {
|
|
2121
|
-
return falseNode(dialect);
|
|
2122
|
-
}
|
|
2123
|
-
const items = nodes.filter((n) => !isTrueNode(n));
|
|
2124
|
-
if (items.length === 0) {
|
|
2125
|
-
return trueNode(dialect);
|
|
2126
|
-
}
|
|
2127
|
-
return items.reduce((acc, node) => AndNode2.create(wrapParensIf(acc, OrNode.is), wrapParensIf(node, OrNode.is)));
|
|
2128
|
-
}
|
|
2129
|
-
__name(conjunction, "conjunction");
|
|
2130
|
-
function disjunction(dialect, nodes) {
|
|
2131
|
-
if (nodes.length === 0) {
|
|
2132
|
-
return falseNode(dialect);
|
|
2133
|
-
}
|
|
2134
|
-
if (nodes.length === 1) {
|
|
2135
|
-
return nodes[0];
|
|
2136
|
-
}
|
|
2137
|
-
if (nodes.some(isTrueNode)) {
|
|
2138
|
-
return trueNode(dialect);
|
|
2139
|
-
}
|
|
2140
|
-
const items = nodes.filter((n) => !isFalseNode(n));
|
|
2141
|
-
if (items.length === 0) {
|
|
2142
|
-
return falseNode(dialect);
|
|
2143
|
-
}
|
|
2144
|
-
return items.reduce((acc, node) => OrNode.create(wrapParensIf(acc, AndNode2.is), wrapParensIf(node, AndNode2.is)));
|
|
2145
|
-
}
|
|
2146
|
-
__name(disjunction, "disjunction");
|
|
2147
|
-
function logicalNot(dialect, node) {
|
|
2148
|
-
if (isTrueNode(node)) {
|
|
2149
|
-
return falseNode(dialect);
|
|
2150
|
-
}
|
|
2151
|
-
if (isFalseNode(node)) {
|
|
2152
|
-
return trueNode(dialect);
|
|
2153
|
-
}
|
|
2154
|
-
return UnaryOperationNode.create(OperatorNode.create("not"), wrapParensIf(node, (n) => AndNode2.is(n) || OrNode.is(n)));
|
|
2155
|
-
}
|
|
2156
|
-
__name(logicalNot, "logicalNot");
|
|
2157
|
-
function wrapParensIf(node, predicate) {
|
|
2158
|
-
return predicate(node) ? ParensNode.create(node) : node;
|
|
2159
|
-
}
|
|
2160
|
-
__name(wrapParensIf, "wrapParensIf");
|
|
2161
|
-
function buildIsFalse(node, dialect) {
|
|
2162
|
-
if (isFalseNode(node)) {
|
|
2163
|
-
return trueNode(dialect);
|
|
2164
|
-
} else if (isTrueNode(node)) {
|
|
2165
|
-
return falseNode(dialect);
|
|
2166
|
-
}
|
|
2167
|
-
return BinaryOperationNode.create(
|
|
2168
|
-
// coalesce so null is treated as false
|
|
2169
|
-
FunctionNode.create("coalesce", [
|
|
2170
|
-
node,
|
|
2171
|
-
falseNode(dialect)
|
|
2172
|
-
]),
|
|
2173
|
-
OperatorNode.create("="),
|
|
2174
|
-
falseNode(dialect)
|
|
2175
|
-
);
|
|
2176
|
-
}
|
|
2177
|
-
__name(buildIsFalse, "buildIsFalse");
|
|
2178
|
-
function getTableName(node) {
|
|
2179
|
-
if (!node) {
|
|
2180
|
-
return node;
|
|
2181
|
-
}
|
|
2182
|
-
if (TableNode4.is(node)) {
|
|
2183
|
-
return node.table.identifier.name;
|
|
2184
|
-
} else if (AliasNode3.is(node)) {
|
|
2185
|
-
return getTableName(node.node);
|
|
2186
|
-
} else if (ReferenceNode3.is(node) && node.table) {
|
|
2187
|
-
return getTableName(node.table);
|
|
2188
|
-
}
|
|
2189
|
-
return void 0;
|
|
2190
|
-
}
|
|
2191
|
-
__name(getTableName, "getTableName");
|
|
2192
|
-
function isBeforeInvocation(expr2) {
|
|
2193
|
-
return ExpressionUtils.isCall(expr2) && expr2.function === "before";
|
|
2194
|
-
}
|
|
2195
|
-
__name(isBeforeInvocation, "isBeforeInvocation");
|
|
2196
|
-
|
|
2197
|
-
// src/plugins/policy/expression-transformer.ts
|
|
2198
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
2199
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2200
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2201
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2202
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2203
|
-
}
|
|
2204
|
-
__name(_ts_decorate, "_ts_decorate");
|
|
2205
|
-
function _ts_metadata(k, v) {
|
|
2206
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2207
|
-
}
|
|
2208
|
-
__name(_ts_metadata, "_ts_metadata");
|
|
2209
|
-
var expressionHandlers = /* @__PURE__ */ new Map();
|
|
2210
|
-
function expr(kind) {
|
|
2211
|
-
return function(_target, _propertyKey, descriptor) {
|
|
2212
|
-
if (!expressionHandlers.get(kind)) {
|
|
2213
|
-
expressionHandlers.set(kind, descriptor);
|
|
2214
|
-
}
|
|
2215
|
-
return descriptor;
|
|
2216
|
-
};
|
|
2217
|
-
}
|
|
2218
|
-
__name(expr, "expr");
|
|
2219
|
-
var ExpressionTransformer = class {
|
|
2220
|
-
static {
|
|
2221
|
-
__name(this, "ExpressionTransformer");
|
|
2222
|
-
}
|
|
2223
|
-
client;
|
|
2224
|
-
dialect;
|
|
2225
|
-
constructor(client) {
|
|
2226
|
-
this.client = client;
|
|
2227
|
-
this.dialect = getCrudDialect(this.schema, this.clientOptions);
|
|
2228
|
-
}
|
|
2229
|
-
get schema() {
|
|
2230
|
-
return this.client.$schema;
|
|
2231
|
-
}
|
|
2232
|
-
get clientOptions() {
|
|
2233
|
-
return this.client.$options;
|
|
2234
|
-
}
|
|
2235
|
-
get auth() {
|
|
2236
|
-
return this.client.$auth;
|
|
2237
|
-
}
|
|
2238
|
-
get authType() {
|
|
2239
|
-
if (!this.schema.authType) {
|
|
2240
|
-
throw new InternalError('Schema does not have an "authType" specified');
|
|
2241
|
-
}
|
|
2242
|
-
return this.schema.authType;
|
|
2243
|
-
}
|
|
2244
|
-
transform(expression, context) {
|
|
2245
|
-
const handler = expressionHandlers.get(expression.kind);
|
|
2246
|
-
if (!handler) {
|
|
2247
|
-
throw new Error(`Unsupported expression kind: ${expression.kind}`);
|
|
2248
|
-
}
|
|
2249
|
-
return handler.value.call(this, expression, context);
|
|
2250
|
-
}
|
|
2251
|
-
_literal(expr2) {
|
|
2252
|
-
return this.transformValue(expr2.value, typeof expr2.value === "string" ? "String" : typeof expr2.value === "boolean" ? "Boolean" : "Int");
|
|
2253
|
-
}
|
|
2254
|
-
_array(expr2, context) {
|
|
2255
|
-
return ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
|
|
2256
|
-
}
|
|
2257
|
-
_field(expr2, context) {
|
|
2258
|
-
const fieldDef = requireField(this.schema, context.model, expr2.field);
|
|
2259
|
-
if (!fieldDef.relation) {
|
|
2260
|
-
return this.createColumnRef(expr2.field, context);
|
|
2261
|
-
} else {
|
|
2262
|
-
const { memberFilter, memberSelect, ...restContext } = context;
|
|
2263
|
-
const relation = this.transformRelationAccess(expr2.field, fieldDef.type, restContext);
|
|
2264
|
-
return {
|
|
2265
|
-
...relation,
|
|
2266
|
-
where: this.mergeWhere(relation.where, memberFilter),
|
|
2267
|
-
selections: memberSelect ? [
|
|
2268
|
-
memberSelect
|
|
2269
|
-
] : relation.selections
|
|
2270
|
-
};
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
mergeWhere(where, memberFilter) {
|
|
2274
|
-
if (!where) {
|
|
2275
|
-
return WhereNode2.create(memberFilter ?? trueNode(this.dialect));
|
|
2276
|
-
}
|
|
2277
|
-
if (!memberFilter) {
|
|
2278
|
-
return where;
|
|
2279
|
-
}
|
|
2280
|
-
return WhereNode2.create(conjunction(this.dialect, [
|
|
2281
|
-
where.where,
|
|
2282
|
-
memberFilter
|
|
2283
|
-
]));
|
|
2284
|
-
}
|
|
2285
|
-
_null() {
|
|
2286
|
-
return ValueNode3.createImmediate(null);
|
|
2287
|
-
}
|
|
2288
|
-
_binary(expr2, context) {
|
|
2289
|
-
if (expr2.op === "&&") {
|
|
2290
|
-
return conjunction(this.dialect, [
|
|
2291
|
-
this.transform(expr2.left, context),
|
|
2292
|
-
this.transform(expr2.right, context)
|
|
2293
|
-
]);
|
|
2294
|
-
} else if (expr2.op === "||") {
|
|
2295
|
-
return disjunction(this.dialect, [
|
|
2296
|
-
this.transform(expr2.left, context),
|
|
2297
|
-
this.transform(expr2.right, context)
|
|
2298
|
-
]);
|
|
2299
|
-
}
|
|
2300
|
-
if (this.isAuthCall(expr2.left) || this.isAuthCall(expr2.right)) {
|
|
2301
|
-
return this.transformAuthBinary(expr2, context);
|
|
2302
|
-
}
|
|
2303
|
-
const op = expr2.op;
|
|
2304
|
-
if (op === "?" || op === "!" || op === "^") {
|
|
2305
|
-
return this.transformCollectionPredicate(expr2, context);
|
|
2306
|
-
}
|
|
2307
|
-
const { normalizedLeft, normalizedRight } = this.normalizeBinaryOperationOperands(expr2, context);
|
|
2308
|
-
const left = this.transform(normalizedLeft, context);
|
|
2309
|
-
const right = this.transform(normalizedRight, context);
|
|
2310
|
-
if (op === "in") {
|
|
2311
|
-
if (this.isNullNode(left)) {
|
|
2312
|
-
return this.transformValue(false, "Boolean");
|
|
2313
|
-
} else {
|
|
2314
|
-
if (ValueListNode.is(right)) {
|
|
2315
|
-
return BinaryOperationNode2.create(left, OperatorNode2.create("in"), right);
|
|
2316
|
-
} else {
|
|
2317
|
-
return BinaryOperationNode2.create(left, OperatorNode2.create("="), FunctionNode2.create("any", [
|
|
2318
|
-
right
|
|
2319
|
-
]));
|
|
2320
|
-
}
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
if (this.isNullNode(right)) {
|
|
2324
|
-
return this.transformNullCheck(left, expr2.op);
|
|
2325
|
-
} else if (this.isNullNode(left)) {
|
|
2326
|
-
return this.transformNullCheck(right, expr2.op);
|
|
2327
|
-
} else {
|
|
2328
|
-
return BinaryOperationNode2.create(left, this.transformOperator(op), right);
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
transformNullCheck(expr2, operator) {
|
|
2332
|
-
invariant13(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
|
|
2333
|
-
if (ValueNode3.is(expr2)) {
|
|
2334
|
-
if (expr2.value === null) {
|
|
2335
|
-
return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
|
|
2336
|
-
} else {
|
|
2337
|
-
return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
|
|
2338
|
-
}
|
|
2339
|
-
} else {
|
|
2340
|
-
return operator === "==" ? BinaryOperationNode2.create(expr2, OperatorNode2.create("is"), ValueNode3.createImmediate(null)) : BinaryOperationNode2.create(expr2, OperatorNode2.create("is not"), ValueNode3.createImmediate(null));
|
|
2341
|
-
}
|
|
2342
|
-
}
|
|
2343
|
-
normalizeBinaryOperationOperands(expr2, context) {
|
|
2344
|
-
let normalizedLeft = expr2.left;
|
|
2345
|
-
if (this.isRelationField(expr2.left, context.model)) {
|
|
2346
|
-
invariant13(ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
|
|
2347
|
-
const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2348
|
-
invariant13(leftRelDef, "failed to get relation field definition");
|
|
2349
|
-
const idFields = requireIdFields(this.schema, leftRelDef.type);
|
|
2350
|
-
normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
|
|
2351
|
-
}
|
|
2352
|
-
let normalizedRight = expr2.right;
|
|
2353
|
-
if (this.isRelationField(expr2.right, context.model)) {
|
|
2354
|
-
invariant13(ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
|
|
2355
|
-
const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
|
|
2356
|
-
invariant13(rightRelDef, "failed to get relation field definition");
|
|
2357
|
-
const idFields = requireIdFields(this.schema, rightRelDef.type);
|
|
2358
|
-
normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
|
|
2359
|
-
}
|
|
2360
|
-
return {
|
|
2361
|
-
normalizedLeft,
|
|
2362
|
-
normalizedRight
|
|
2363
|
-
};
|
|
2364
|
-
}
|
|
2365
|
-
transformCollectionPredicate(expr2, context) {
|
|
2366
|
-
invariant13(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
|
|
2367
|
-
if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
|
|
2368
|
-
const value = new ExpressionEvaluator().evaluate(expr2, {
|
|
2369
|
-
auth: this.auth
|
|
2370
|
-
});
|
|
2371
|
-
return this.transformValue(value, "Boolean");
|
|
2372
|
-
}
|
|
2373
|
-
invariant13(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
|
|
2374
|
-
let newContextModel;
|
|
2375
|
-
const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2376
|
-
if (fieldDef) {
|
|
2377
|
-
invariant13(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
|
|
2378
|
-
newContextModel = fieldDef.type;
|
|
2379
|
-
} else {
|
|
2380
|
-
invariant13(ExpressionUtils.isMember(expr2.left) && ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
|
|
2381
|
-
const fieldDef2 = requireField(this.schema, context.model, expr2.left.receiver.field);
|
|
2382
|
-
newContextModel = fieldDef2.type;
|
|
2383
|
-
for (const member of expr2.left.members) {
|
|
2384
|
-
const memberDef = requireField(this.schema, newContextModel, member);
|
|
2385
|
-
newContextModel = memberDef.type;
|
|
2386
|
-
}
|
|
2387
|
-
}
|
|
2388
|
-
let predicateFilter = this.transform(expr2.right, {
|
|
2389
|
-
...context,
|
|
2390
|
-
model: newContextModel,
|
|
2391
|
-
alias: void 0
|
|
2392
|
-
});
|
|
2393
|
-
if (expr2.op === "!") {
|
|
2394
|
-
predicateFilter = logicalNot(this.dialect, predicateFilter);
|
|
2395
|
-
}
|
|
2396
|
-
const count = FunctionNode2.create("count", [
|
|
2397
|
-
ValueNode3.createImmediate(1)
|
|
2398
|
-
]);
|
|
2399
|
-
const predicateResult = match18(expr2.op).with("?", () => BinaryOperationNode2.create(count, OperatorNode2.create(">"), ValueNode3.createImmediate(0))).with("!", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode3.createImmediate(0))).with("^", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode3.createImmediate(0))).exhaustive();
|
|
2400
|
-
return this.transform(expr2.left, {
|
|
2401
|
-
...context,
|
|
2402
|
-
memberSelect: SelectionNode3.create(AliasNode4.create(predicateResult, IdentifierNode2.create("$t"))),
|
|
2403
|
-
memberFilter: predicateFilter
|
|
2404
|
-
});
|
|
2405
|
-
}
|
|
2406
|
-
transformAuthBinary(expr2, context) {
|
|
2407
|
-
if (expr2.op !== "==" && expr2.op !== "!=") {
|
|
2408
|
-
throw new QueryError(`Unsupported operator for \`auth()\` in policy of model "${context.model}": ${expr2.op}`);
|
|
2409
|
-
}
|
|
2410
|
-
let authExpr;
|
|
2411
|
-
let other;
|
|
2412
|
-
if (this.isAuthCall(expr2.left)) {
|
|
2413
|
-
authExpr = expr2.left;
|
|
2414
|
-
other = expr2.right;
|
|
2415
|
-
} else {
|
|
2416
|
-
authExpr = expr2.right;
|
|
2417
|
-
other = expr2.left;
|
|
2418
|
-
}
|
|
2419
|
-
if (ExpressionUtils.isNull(other)) {
|
|
2420
|
-
return this.transformValue(expr2.op === "==" ? !this.auth : !!this.auth, "Boolean");
|
|
2421
|
-
} else {
|
|
2422
|
-
const authModel = getModel(this.schema, this.authType);
|
|
2423
|
-
if (!authModel) {
|
|
2424
|
-
throw new QueryError(`Unsupported use of \`auth()\` in policy of model "${context.model}", comparing with \`auth()\` is only possible when auth type is a model`);
|
|
2425
|
-
}
|
|
2426
|
-
const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
|
|
2427
|
-
invariant13(idFields.length > 0, "auth type model must have at least one id field");
|
|
2428
|
-
const conditions = idFields.map((fieldName) => ExpressionUtils.binary(ExpressionUtils.member(authExpr, [
|
|
2429
|
-
fieldName
|
|
2430
|
-
]), "==", this.makeOrAppendMember(other, fieldName)));
|
|
2431
|
-
let result = this.buildAnd(conditions);
|
|
2432
|
-
if (expr2.op === "!=") {
|
|
2433
|
-
result = this.buildLogicalNot(result);
|
|
2434
|
-
}
|
|
2435
|
-
return this.transform(result, context);
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
makeOrAppendMember(other, fieldName) {
|
|
2439
|
-
if (ExpressionUtils.isMember(other)) {
|
|
2440
|
-
return ExpressionUtils.member(other.receiver, [
|
|
2441
|
-
...other.members,
|
|
2442
|
-
fieldName
|
|
2443
|
-
]);
|
|
2444
|
-
} else {
|
|
2445
|
-
return ExpressionUtils.member(other, [
|
|
2446
|
-
fieldName
|
|
2447
|
-
]);
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
transformValue(value, type) {
|
|
2451
|
-
if (value === true) {
|
|
2452
|
-
return trueNode(this.dialect);
|
|
2453
|
-
} else if (value === false) {
|
|
2454
|
-
return falseNode(this.dialect);
|
|
2455
|
-
} else {
|
|
2456
|
-
return ValueNode3.create(this.dialect.transformPrimitive(value, type, false) ?? null);
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
_unary(expr2, context) {
|
|
2460
|
-
invariant13(expr2.op === "!", 'only "!" operator is supported');
|
|
2461
|
-
return logicalNot(this.dialect, this.transform(expr2.operand, context));
|
|
2462
|
-
}
|
|
2463
|
-
transformOperator(op) {
|
|
2464
|
-
const mappedOp = match18(op).with("==", () => "=").otherwise(() => op);
|
|
2465
|
-
return OperatorNode2.create(mappedOp);
|
|
2466
|
-
}
|
|
2467
|
-
_call(expr2, context) {
|
|
2468
|
-
const result = this.transformCall(expr2, context);
|
|
2469
|
-
return result.toOperationNode();
|
|
2470
|
-
}
|
|
2471
|
-
transformCall(expr2, context) {
|
|
2472
|
-
const func = this.getFunctionImpl(expr2.function);
|
|
2473
|
-
if (!func) {
|
|
2474
|
-
throw new QueryError(`Function not implemented: ${expr2.function}`);
|
|
2475
|
-
}
|
|
2476
|
-
const eb = expressionBuilder4();
|
|
2477
|
-
return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
|
|
2478
|
-
client: this.client,
|
|
2479
|
-
dialect: this.dialect,
|
|
2480
|
-
model: context.model,
|
|
2481
|
-
modelAlias: context.alias ?? context.model,
|
|
2482
|
-
operation: context.operation
|
|
2483
|
-
});
|
|
2484
|
-
}
|
|
2485
|
-
getFunctionImpl(functionName) {
|
|
2486
|
-
let func = this.clientOptions.functions?.[functionName];
|
|
2487
|
-
if (!func) {
|
|
2488
|
-
for (const plugin of this.clientOptions.plugins ?? []) {
|
|
2489
|
-
if (plugin.functions?.[functionName]) {
|
|
2490
|
-
func = plugin.functions[functionName];
|
|
2491
|
-
break;
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
return func;
|
|
2496
|
-
}
|
|
2497
|
-
transformCallArg(eb, arg, context) {
|
|
2498
|
-
if (ExpressionUtils.isLiteral(arg)) {
|
|
2499
|
-
return eb.val(arg.value);
|
|
2500
|
-
}
|
|
2501
|
-
if (ExpressionUtils.isField(arg)) {
|
|
2502
|
-
return eb.ref(arg.field);
|
|
2503
|
-
}
|
|
2504
|
-
if (ExpressionUtils.isCall(arg)) {
|
|
2505
|
-
return this.transformCall(arg, context);
|
|
2506
|
-
}
|
|
2507
|
-
if (this.isAuthMember(arg)) {
|
|
2508
|
-
const valNode = this.valueMemberAccess(this.auth, arg, this.authType);
|
|
2509
|
-
return valNode ? eb.val(valNode.value) : eb.val(null);
|
|
2510
|
-
}
|
|
2511
|
-
throw new InternalError(`Unsupported argument expression: ${arg.kind}`);
|
|
2512
|
-
}
|
|
2513
|
-
_member(expr2, context) {
|
|
2514
|
-
if (this.isAuthCall(expr2.receiver)) {
|
|
2515
|
-
return this.valueMemberAccess(this.auth, expr2, this.authType);
|
|
2516
|
-
}
|
|
2517
|
-
if (isBeforeInvocation(expr2.receiver)) {
|
|
2518
|
-
invariant13(context.operation === "post-update", "before() can only be used in post-update policy");
|
|
2519
|
-
invariant13(expr2.members.length === 1, "before() can only be followed by a scalar field access");
|
|
2520
|
-
return ReferenceNode4.create(ColumnNode3.create(expr2.members[0]), TableNode5.create("$before"));
|
|
2521
|
-
}
|
|
2522
|
-
invariant13(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
|
|
2523
|
-
let members = expr2.members;
|
|
2524
|
-
let receiver;
|
|
2525
|
-
const { memberFilter, memberSelect, ...restContext } = context;
|
|
2526
|
-
if (ExpressionUtils.isThis(expr2.receiver)) {
|
|
2527
|
-
if (expr2.members.length === 1) {
|
|
2528
|
-
return this._field(ExpressionUtils.field(expr2.members[0]), context);
|
|
2529
|
-
} else {
|
|
2530
|
-
const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
|
|
2531
|
-
receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
|
|
2532
|
-
members = expr2.members.slice(1);
|
|
2533
|
-
}
|
|
2534
|
-
} else {
|
|
2535
|
-
receiver = this.transform(expr2.receiver, restContext);
|
|
2536
|
-
}
|
|
2537
|
-
invariant13(SelectQueryNode.is(receiver), "expected receiver to be select query");
|
|
2538
|
-
let startType;
|
|
2539
|
-
if (ExpressionUtils.isField(expr2.receiver)) {
|
|
2540
|
-
const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
|
|
2541
|
-
startType = receiverField.type;
|
|
2542
|
-
} else {
|
|
2543
|
-
startType = context.model;
|
|
2544
|
-
}
|
|
2545
|
-
const memberFields = [];
|
|
2546
|
-
let currType = startType;
|
|
2547
|
-
for (const member of members) {
|
|
2548
|
-
const fieldDef = requireField(this.schema, currType, member);
|
|
2549
|
-
memberFields.push({
|
|
2550
|
-
fieldDef,
|
|
2551
|
-
fromModel: currType
|
|
2552
|
-
});
|
|
2553
|
-
currType = fieldDef.type;
|
|
2554
|
-
}
|
|
2555
|
-
let currNode = void 0;
|
|
2556
|
-
for (let i = members.length - 1; i >= 0; i--) {
|
|
2557
|
-
const member = members[i];
|
|
2558
|
-
const { fieldDef, fromModel } = memberFields[i];
|
|
2559
|
-
if (fieldDef.relation) {
|
|
2560
|
-
const relation = this.transformRelationAccess(member, fieldDef.type, {
|
|
2561
|
-
...restContext,
|
|
2562
|
-
model: fromModel,
|
|
2563
|
-
alias: void 0
|
|
2564
|
-
});
|
|
2565
|
-
if (currNode) {
|
|
2566
|
-
currNode = {
|
|
2567
|
-
...relation,
|
|
2568
|
-
selections: [
|
|
2569
|
-
SelectionNode3.create(AliasNode4.create(currNode, IdentifierNode2.create(members[i + 1])))
|
|
2570
|
-
]
|
|
2571
|
-
};
|
|
2572
|
-
} else {
|
|
2573
|
-
currNode = {
|
|
2574
|
-
...relation,
|
|
2575
|
-
where: this.mergeWhere(relation.where, memberFilter),
|
|
2576
|
-
selections: memberSelect ? [
|
|
2577
|
-
memberSelect
|
|
2578
|
-
] : relation.selections
|
|
2579
|
-
};
|
|
2580
|
-
}
|
|
2581
|
-
} else {
|
|
2582
|
-
invariant13(i === members.length - 1, "plain field access must be the last segment");
|
|
2583
|
-
invariant13(!currNode, "plain field access must be the last segment");
|
|
2584
|
-
currNode = ColumnNode3.create(member);
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
return {
|
|
2588
|
-
...receiver,
|
|
2589
|
-
selections: [
|
|
2590
|
-
SelectionNode3.create(AliasNode4.create(currNode, IdentifierNode2.create("$t")))
|
|
2591
|
-
]
|
|
2592
|
-
};
|
|
2593
|
-
}
|
|
2594
|
-
valueMemberAccess(receiver, expr2, receiverType) {
|
|
2595
|
-
if (!receiver) {
|
|
2596
|
-
return ValueNode3.createImmediate(null);
|
|
2597
|
-
}
|
|
2598
|
-
if (expr2.members.length !== 1) {
|
|
2599
|
-
throw new Error(`Only single member access is supported`);
|
|
2600
|
-
}
|
|
2601
|
-
const field = expr2.members[0];
|
|
2602
|
-
const fieldDef = requireField(this.schema, receiverType, field);
|
|
2603
|
-
const fieldValue = receiver[field] ?? null;
|
|
2604
|
-
return this.transformValue(fieldValue, fieldDef.type);
|
|
2605
|
-
}
|
|
2606
|
-
transformRelationAccess(field, relationModel, context) {
|
|
2607
|
-
const m2m = getManyToManyRelation(this.schema, context.model, field);
|
|
2608
|
-
if (m2m) {
|
|
2609
|
-
return this.transformManyToManyRelationAccess(m2m, context);
|
|
2610
|
-
}
|
|
2611
|
-
const fromModel = context.model;
|
|
2612
|
-
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
|
|
2613
|
-
let condition;
|
|
2614
|
-
if (ownedByModel) {
|
|
2615
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode4.create(ColumnNode3.create(fk), TableNode5.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode4.create(ColumnNode3.create(pk), TableNode5.create(relationModel)))));
|
|
2616
|
-
} else {
|
|
2617
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode4.create(ColumnNode3.create(pk), TableNode5.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode4.create(ColumnNode3.create(fk), TableNode5.create(relationModel)))));
|
|
2618
|
-
}
|
|
2619
|
-
return {
|
|
2620
|
-
kind: "SelectQueryNode",
|
|
2621
|
-
from: FromNode2.create([
|
|
2622
|
-
TableNode5.create(relationModel)
|
|
2623
|
-
]),
|
|
2624
|
-
where: WhereNode2.create(condition)
|
|
2625
|
-
};
|
|
2626
|
-
}
|
|
2627
|
-
transformManyToManyRelationAccess(m2m, context) {
|
|
2628
|
-
const eb = expressionBuilder4();
|
|
2629
|
-
const relationQuery = eb.selectFrom(m2m.otherModel).innerJoin(m2m.joinTable, (join) => join.onRef(`${m2m.otherModel}.${m2m.otherPKName}`, "=", `${m2m.joinTable}.${m2m.otherFkName}`).onRef(`${m2m.joinTable}.${m2m.parentFkName}`, "=", `${context.alias ?? context.model}.${m2m.parentPKName}`));
|
|
2630
|
-
return relationQuery.toOperationNode();
|
|
2631
|
-
}
|
|
2632
|
-
createColumnRef(column, context) {
|
|
2633
|
-
return ReferenceNode4.create(ColumnNode3.create(column), TableNode5.create(context.alias ?? context.model));
|
|
2634
|
-
}
|
|
2635
|
-
isAuthCall(value) {
|
|
2636
|
-
return ExpressionUtils.isCall(value) && value.function === "auth";
|
|
2637
|
-
}
|
|
2638
|
-
isAuthMember(expr2) {
|
|
2639
|
-
return ExpressionUtils.isMember(expr2) && this.isAuthCall(expr2.receiver);
|
|
2640
|
-
}
|
|
2641
|
-
isNullNode(node) {
|
|
2642
|
-
return ValueNode3.is(node) && node.value === null;
|
|
2643
|
-
}
|
|
2644
|
-
buildLogicalNot(result) {
|
|
2645
|
-
return ExpressionUtils.unary("!", result);
|
|
2646
|
-
}
|
|
2647
|
-
buildAnd(conditions) {
|
|
2648
|
-
if (conditions.length === 0) {
|
|
2649
|
-
return ExpressionUtils.literal(true);
|
|
2650
|
-
} else if (conditions.length === 1) {
|
|
2651
|
-
return conditions[0];
|
|
2652
|
-
} else {
|
|
2653
|
-
return conditions.reduce((acc, condition) => ExpressionUtils.binary(acc, "&&", condition));
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
isRelationField(expr2, model) {
|
|
2657
|
-
const fieldDef = this.getFieldDefFromFieldRef(expr2, model);
|
|
2658
|
-
return !!fieldDef?.relation;
|
|
2659
|
-
}
|
|
2660
|
-
getFieldDefFromFieldRef(expr2, model) {
|
|
2661
|
-
if (ExpressionUtils.isField(expr2)) {
|
|
2662
|
-
return requireField(this.schema, model, expr2.field);
|
|
2663
|
-
} else if (ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && ExpressionUtils.isThis(expr2.receiver)) {
|
|
2664
|
-
return requireField(this.schema, model, expr2.members[0]);
|
|
2665
|
-
} else {
|
|
2666
|
-
return void 0;
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
};
|
|
2670
|
-
_ts_decorate([
|
|
2671
|
-
expr("literal"),
|
|
2672
|
-
_ts_metadata("design:type", Function),
|
|
2673
|
-
_ts_metadata("design:paramtypes", [
|
|
2674
|
-
typeof LiteralExpression === "undefined" ? Object : LiteralExpression
|
|
2675
|
-
]),
|
|
2676
|
-
_ts_metadata("design:returntype", void 0)
|
|
2677
|
-
], ExpressionTransformer.prototype, "_literal", null);
|
|
2678
|
-
_ts_decorate([
|
|
2679
|
-
expr("array"),
|
|
2680
|
-
_ts_metadata("design:type", Function),
|
|
2681
|
-
_ts_metadata("design:paramtypes", [
|
|
2682
|
-
typeof ArrayExpression === "undefined" ? Object : ArrayExpression,
|
|
2683
|
-
typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
|
|
2684
|
-
]),
|
|
2685
|
-
_ts_metadata("design:returntype", void 0)
|
|
2686
|
-
], ExpressionTransformer.prototype, "_array", null);
|
|
2687
|
-
_ts_decorate([
|
|
2688
|
-
expr("field"),
|
|
2689
|
-
_ts_metadata("design:type", Function),
|
|
2690
|
-
_ts_metadata("design:paramtypes", [
|
|
2691
|
-
typeof FieldExpression === "undefined" ? Object : FieldExpression,
|
|
2692
|
-
typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
|
|
2693
|
-
]),
|
|
2694
|
-
_ts_metadata("design:returntype", void 0)
|
|
2695
|
-
], ExpressionTransformer.prototype, "_field", null);
|
|
2696
|
-
_ts_decorate([
|
|
2697
|
-
expr("null"),
|
|
2698
|
-
_ts_metadata("design:type", Function),
|
|
2699
|
-
_ts_metadata("design:paramtypes", []),
|
|
2700
|
-
_ts_metadata("design:returntype", void 0)
|
|
2701
|
-
], ExpressionTransformer.prototype, "_null", null);
|
|
2702
|
-
_ts_decorate([
|
|
2703
|
-
expr("binary"),
|
|
2704
|
-
_ts_metadata("design:type", Function),
|
|
2705
|
-
_ts_metadata("design:paramtypes", [
|
|
2706
|
-
typeof BinaryExpression === "undefined" ? Object : BinaryExpression,
|
|
2707
|
-
typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
|
|
2708
|
-
]),
|
|
2709
|
-
_ts_metadata("design:returntype", void 0)
|
|
2710
|
-
], ExpressionTransformer.prototype, "_binary", null);
|
|
2711
|
-
_ts_decorate([
|
|
2712
|
-
expr("unary"),
|
|
2713
|
-
_ts_metadata("design:type", Function),
|
|
2714
|
-
_ts_metadata("design:paramtypes", [
|
|
2715
|
-
typeof UnaryExpression === "undefined" ? Object : UnaryExpression,
|
|
2716
|
-
typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
|
|
2717
|
-
]),
|
|
2718
|
-
_ts_metadata("design:returntype", void 0)
|
|
2719
|
-
], ExpressionTransformer.prototype, "_unary", null);
|
|
2720
|
-
_ts_decorate([
|
|
2721
|
-
expr("call"),
|
|
2722
|
-
_ts_metadata("design:type", Function),
|
|
2723
|
-
_ts_metadata("design:paramtypes", [
|
|
2724
|
-
typeof CallExpression === "undefined" ? Object : CallExpression,
|
|
2725
|
-
typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
|
|
2726
|
-
]),
|
|
2727
|
-
_ts_metadata("design:returntype", void 0)
|
|
2728
|
-
], ExpressionTransformer.prototype, "_call", null);
|
|
2729
|
-
_ts_decorate([
|
|
2730
|
-
expr("member"),
|
|
2731
|
-
_ts_metadata("design:type", Function),
|
|
2732
|
-
_ts_metadata("design:paramtypes", [
|
|
2733
|
-
typeof MemberExpression === "undefined" ? Object : MemberExpression,
|
|
2734
|
-
typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
|
|
2735
|
-
]),
|
|
2736
|
-
_ts_metadata("design:returntype", void 0)
|
|
2737
|
-
], ExpressionTransformer.prototype, "_member", null);
|
|
2738
|
-
|
|
2739
|
-
// src/plugins/policy/policy-handler.ts
|
|
2740
|
-
var PolicyHandler = class extends OperationNodeTransformer2 {
|
|
2741
|
-
static {
|
|
2742
|
-
__name(this, "PolicyHandler");
|
|
2743
|
-
}
|
|
2744
|
-
client;
|
|
2745
|
-
dialect;
|
|
2746
|
-
constructor(client) {
|
|
2747
|
-
super(), this.client = client;
|
|
2748
|
-
this.dialect = getCrudDialect(this.client.$schema, this.client.$options);
|
|
2749
|
-
}
|
|
2750
|
-
get kysely() {
|
|
2751
|
-
return this.client.$qb;
|
|
2752
|
-
}
|
|
2753
|
-
async handle(node, proceed) {
|
|
2754
|
-
if (!this.isCrudQueryNode(node)) {
|
|
2755
|
-
throw new RejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
|
|
2756
|
-
}
|
|
2757
|
-
if (!this.isMutationQueryNode(node)) {
|
|
2758
|
-
return proceed(this.transformNode(node));
|
|
2759
|
-
}
|
|
2760
|
-
const { mutationModel } = this.getMutationModel(node);
|
|
2761
|
-
if (InsertQueryNode2.is(node)) {
|
|
2762
|
-
const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
|
|
2763
|
-
let needCheckPreCreate = true;
|
|
2764
|
-
if (!isManyToManyJoinTable) {
|
|
2765
|
-
const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
|
|
2766
|
-
if (constCondition === true) {
|
|
2767
|
-
needCheckPreCreate = false;
|
|
2768
|
-
} else if (constCondition === false) {
|
|
2769
|
-
throw new RejectedByPolicyError(mutationModel);
|
|
2770
|
-
}
|
|
2771
|
-
}
|
|
2772
|
-
if (needCheckPreCreate) {
|
|
2773
|
-
await this.enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed);
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
const hasPostUpdatePolicies = UpdateQueryNode2.is(node) && this.hasPostUpdatePolicies(mutationModel);
|
|
2777
|
-
let beforeUpdateInfo;
|
|
2778
|
-
if (hasPostUpdatePolicies) {
|
|
2779
|
-
beforeUpdateInfo = await this.loadBeforeUpdateEntities(mutationModel, node.where, proceed);
|
|
2780
|
-
}
|
|
2781
|
-
const result = await proceed(this.transformNode(node));
|
|
2782
|
-
if (hasPostUpdatePolicies && result.rows.length > 0) {
|
|
2783
|
-
const idConditions = this.buildIdConditions(mutationModel, result.rows);
|
|
2784
|
-
const postUpdateFilter = this.buildPolicyFilter(mutationModel, void 0, "post-update");
|
|
2785
|
-
const eb = expressionBuilder5();
|
|
2786
|
-
const beforeUpdateTable = beforeUpdateInfo ? {
|
|
2787
|
-
kind: "SelectQueryNode",
|
|
2788
|
-
from: FromNode3.create([
|
|
2789
|
-
ParensNode2.create(ValuesNode.create(beforeUpdateInfo.rows.map((r) => PrimitiveValueListNode.create(beforeUpdateInfo.fields.map((f) => r[f])))))
|
|
2790
|
-
]),
|
|
2791
|
-
selections: beforeUpdateInfo.fields.map((name, index) => {
|
|
2792
|
-
const def = requireField(this.client.$schema, mutationModel, name);
|
|
2793
|
-
const castedColumnRef = sql11`CAST(${eb.ref(`column${index + 1}`)} as ${sql11.raw(this.dialect.getFieldSqlType(def))})`.as(name);
|
|
2794
|
-
return SelectionNode4.create(castedColumnRef.toOperationNode());
|
|
2795
|
-
})
|
|
2796
|
-
} : void 0;
|
|
2797
|
-
const postUpdateQuery = eb.selectFrom(mutationModel).select(() => [
|
|
2798
|
-
eb(eb.fn("COUNT", [
|
|
2799
|
-
eb.lit(1)
|
|
2800
|
-
]), "=", result.rows.length).as("$condition")
|
|
2801
|
-
]).where(() => new ExpressionWrapper(conjunction(this.dialect, [
|
|
2802
|
-
idConditions,
|
|
2803
|
-
postUpdateFilter
|
|
2804
|
-
]))).$if(!!beforeUpdateInfo, (qb) => qb.leftJoin(() => new ExpressionWrapper(beforeUpdateTable).as("$before"), (join) => {
|
|
2805
|
-
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2806
|
-
return idFields.reduce((acc, f) => acc.onRef(`${mutationModel}.${f}`, "=", `$before.${f}`), join);
|
|
2807
|
-
}));
|
|
2808
|
-
const postUpdateResult = await proceed(postUpdateQuery.toOperationNode());
|
|
2809
|
-
if (!postUpdateResult.rows[0]?.$condition) {
|
|
2810
|
-
throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.NO_ACCESS, "some or all updated rows failed to pass post-update policy check");
|
|
2811
|
-
}
|
|
2812
|
-
}
|
|
2813
|
-
if (!node.returning || this.onlyReturningId(node)) {
|
|
2814
|
-
return this.postProcessMutationResult(result, node);
|
|
2815
|
-
} else {
|
|
2816
|
-
const readBackResult = await this.processReadBack(node, result, proceed);
|
|
2817
|
-
if (readBackResult.rows.length !== result.rows.length) {
|
|
2818
|
-
throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
2819
|
-
}
|
|
2820
|
-
return readBackResult;
|
|
2821
|
-
}
|
|
2822
|
-
}
|
|
2823
|
-
// correction to kysely mutation result may be needed because we might have added
|
|
2824
|
-
// returning clause to the query and caused changes to the result shape
|
|
2825
|
-
postProcessMutationResult(result, node) {
|
|
2826
|
-
if (node.returning) {
|
|
2827
|
-
return result;
|
|
2828
|
-
} else {
|
|
2829
|
-
return {
|
|
2830
|
-
...result,
|
|
2831
|
-
rows: [],
|
|
2832
|
-
numAffectedRows: result.numAffectedRows ?? BigInt(result.rows.length)
|
|
2833
|
-
};
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
hasPostUpdatePolicies(model) {
|
|
2837
|
-
const policies = this.getModelPolicies(model, "post-update");
|
|
2838
|
-
return policies.length > 0;
|
|
2839
|
-
}
|
|
2840
|
-
async loadBeforeUpdateEntities(model, where, proceed) {
|
|
2841
|
-
const beforeUpdateAccessFields = this.getFieldsAccessForBeforeUpdatePolicies(model);
|
|
2842
|
-
if (!beforeUpdateAccessFields || beforeUpdateAccessFields.length === 0) {
|
|
2843
|
-
return void 0;
|
|
2844
|
-
}
|
|
2845
|
-
const query = {
|
|
2846
|
-
kind: "SelectQueryNode",
|
|
2847
|
-
from: FromNode3.create([
|
|
2848
|
-
TableNode6.create(model)
|
|
2849
|
-
]),
|
|
2850
|
-
where,
|
|
2851
|
-
selections: [
|
|
2852
|
-
...beforeUpdateAccessFields.map((f) => SelectionNode4.create(ColumnNode4.create(f)))
|
|
2853
|
-
]
|
|
2854
|
-
};
|
|
2855
|
-
const result = await proceed(query);
|
|
2856
|
-
return {
|
|
2857
|
-
fields: beforeUpdateAccessFields,
|
|
2858
|
-
rows: result.rows
|
|
2859
|
-
};
|
|
2860
|
-
}
|
|
2861
|
-
getFieldsAccessForBeforeUpdatePolicies(model) {
|
|
2862
|
-
const policies = this.getModelPolicies(model, "post-update");
|
|
2863
|
-
if (policies.length === 0) {
|
|
2864
|
-
return void 0;
|
|
2865
|
-
}
|
|
2866
|
-
const fields = /* @__PURE__ */ new Set();
|
|
2867
|
-
const fieldCollector = new class extends ExpressionVisitor {
|
|
2868
|
-
visitMember(e) {
|
|
2869
|
-
if (isBeforeInvocation(e.receiver)) {
|
|
2870
|
-
invariant14(e.members.length === 1, "before() can only be followed by a scalar field access");
|
|
2871
|
-
fields.add(e.members[0]);
|
|
2872
|
-
}
|
|
2873
|
-
super.visitMember(e);
|
|
2874
|
-
}
|
|
2875
|
-
}();
|
|
2876
|
-
for (const policy of policies) {
|
|
2877
|
-
fieldCollector.visit(policy.condition);
|
|
2878
|
-
}
|
|
2879
|
-
if (fields.size === 0) {
|
|
2880
|
-
return void 0;
|
|
2881
|
-
}
|
|
2882
|
-
requireIdFields(this.client.$schema, model).forEach((f) => fields.add(f));
|
|
2883
|
-
return Array.from(fields).sort();
|
|
2884
|
-
}
|
|
2885
|
-
// #region overrides
|
|
2886
|
-
transformSelectQuery(node) {
|
|
2887
|
-
let whereNode = this.transformNode(node.where);
|
|
2888
|
-
const policyFilter = this.createPolicyFilterForFrom(node.from);
|
|
2889
|
-
if (policyFilter) {
|
|
2890
|
-
whereNode = WhereNode3.create(whereNode?.where ? conjunction(this.dialect, [
|
|
2891
|
-
whereNode.where,
|
|
2892
|
-
policyFilter
|
|
2893
|
-
]) : policyFilter);
|
|
2894
|
-
}
|
|
2895
|
-
const baseResult = super.transformSelectQuery({
|
|
2896
|
-
...node,
|
|
2897
|
-
where: void 0
|
|
2898
|
-
});
|
|
2899
|
-
return {
|
|
2900
|
-
...baseResult,
|
|
2901
|
-
where: whereNode
|
|
2902
|
-
};
|
|
2903
|
-
}
|
|
2904
|
-
transformJoin(node) {
|
|
2905
|
-
const table = this.extractTableName(node.table);
|
|
2906
|
-
if (!table) {
|
|
2907
|
-
return super.transformJoin(node);
|
|
2908
|
-
}
|
|
2909
|
-
const filter = this.buildPolicyFilter(table.model, table.alias, "read");
|
|
2910
|
-
const nestedSelect = {
|
|
2911
|
-
kind: "SelectQueryNode",
|
|
2912
|
-
from: FromNode3.create([
|
|
2913
|
-
node.table
|
|
2914
|
-
]),
|
|
2915
|
-
selections: [
|
|
2916
|
-
SelectionNode4.createSelectAll()
|
|
2917
|
-
],
|
|
2918
|
-
where: WhereNode3.create(filter)
|
|
2919
|
-
};
|
|
2920
|
-
return {
|
|
2921
|
-
...node,
|
|
2922
|
-
table: AliasNode5.create(ParensNode2.create(nestedSelect), IdentifierNode3.create(table.alias ?? table.model))
|
|
2923
|
-
};
|
|
2924
|
-
}
|
|
2925
|
-
transformInsertQuery(node) {
|
|
2926
|
-
let onConflict = node.onConflict;
|
|
2927
|
-
if (onConflict?.updates) {
|
|
2928
|
-
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2929
|
-
const filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2930
|
-
if (onConflict.updateWhere) {
|
|
2931
|
-
onConflict = {
|
|
2932
|
-
...onConflict,
|
|
2933
|
-
updateWhere: WhereNode3.create(conjunction(this.dialect, [
|
|
2934
|
-
onConflict.updateWhere.where,
|
|
2935
|
-
filter
|
|
2936
|
-
]))
|
|
2937
|
-
};
|
|
2938
|
-
} else {
|
|
2939
|
-
onConflict = {
|
|
2940
|
-
...onConflict,
|
|
2941
|
-
updateWhere: WhereNode3.create(filter)
|
|
2942
|
-
};
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
2945
|
-
const processedNode = onConflict ? {
|
|
2946
|
-
...node,
|
|
2947
|
-
onConflict
|
|
2948
|
-
} : node;
|
|
2949
|
-
const result = super.transformInsertQuery(processedNode);
|
|
2950
|
-
let returning = result.returning;
|
|
2951
|
-
if (returning) {
|
|
2952
|
-
const { mutationModel } = this.getMutationModel(node);
|
|
2953
|
-
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2954
|
-
returning = ReturningNode2.create(idFields.map((f) => SelectionNode4.create(ColumnNode4.create(f))));
|
|
2955
|
-
}
|
|
2956
|
-
return {
|
|
2957
|
-
...result,
|
|
2958
|
-
returning
|
|
2959
|
-
};
|
|
2960
|
-
}
|
|
2961
|
-
transformUpdateQuery(node) {
|
|
2962
|
-
const result = super.transformUpdateQuery(node);
|
|
2963
|
-
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2964
|
-
let filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2965
|
-
if (node.from) {
|
|
2966
|
-
const joinFilter = this.createPolicyFilterForFrom(node.from);
|
|
2967
|
-
if (joinFilter) {
|
|
2968
|
-
filter = conjunction(this.dialect, [
|
|
2969
|
-
filter,
|
|
2970
|
-
joinFilter
|
|
2971
|
-
]);
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
let returning = result.returning;
|
|
2975
|
-
if (returning || this.hasPostUpdatePolicies(mutationModel)) {
|
|
2976
|
-
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2977
|
-
returning = ReturningNode2.create(idFields.map((f) => SelectionNode4.create(ColumnNode4.create(f))));
|
|
2978
|
-
}
|
|
2979
|
-
return {
|
|
2980
|
-
...result,
|
|
2981
|
-
where: WhereNode3.create(result.where ? conjunction(this.dialect, [
|
|
2982
|
-
result.where.where,
|
|
2983
|
-
filter
|
|
2984
|
-
]) : filter),
|
|
2985
|
-
returning
|
|
2986
|
-
};
|
|
2987
|
-
}
|
|
2988
|
-
transformDeleteQuery(node) {
|
|
2989
|
-
const result = super.transformDeleteQuery(node);
|
|
2990
|
-
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2991
|
-
let filter = this.buildPolicyFilter(mutationModel, alias, "delete");
|
|
2992
|
-
if (node.using) {
|
|
2993
|
-
const joinFilter = this.createPolicyFilterForTables(node.using.tables);
|
|
2994
|
-
if (joinFilter) {
|
|
2995
|
-
filter = conjunction(this.dialect, [
|
|
2996
|
-
filter,
|
|
2997
|
-
joinFilter
|
|
2998
|
-
]);
|
|
2999
|
-
}
|
|
3000
|
-
}
|
|
3001
|
-
return {
|
|
3002
|
-
...result,
|
|
3003
|
-
where: WhereNode3.create(result.where ? conjunction(this.dialect, [
|
|
3004
|
-
result.where.where,
|
|
3005
|
-
filter
|
|
3006
|
-
]) : filter)
|
|
3007
|
-
};
|
|
3008
|
-
}
|
|
3009
|
-
// #endregion
|
|
3010
|
-
// #region helpers
|
|
3011
|
-
onlyReturningId(node) {
|
|
3012
|
-
if (!node.returning) {
|
|
3013
|
-
return true;
|
|
3014
|
-
}
|
|
3015
|
-
const { mutationModel } = this.getMutationModel(node);
|
|
3016
|
-
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
3017
|
-
if (node.returning.selections.some((s) => SelectAllNode2.is(s.selection))) {
|
|
3018
|
-
const modelDef = requireModel(this.client.$schema, mutationModel);
|
|
3019
|
-
if (Object.keys(modelDef.fields).some((f) => !idFields.includes(f))) {
|
|
3020
|
-
return false;
|
|
3021
|
-
} else {
|
|
3022
|
-
return true;
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3025
|
-
const collector = new ColumnCollector();
|
|
3026
|
-
const selectedColumns = collector.collect(node.returning);
|
|
3027
|
-
return selectedColumns.every((c) => idFields.includes(c));
|
|
3028
|
-
}
|
|
3029
|
-
async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
|
|
3030
|
-
const fields = node.columns?.map((c) => c.column.name) ?? [];
|
|
3031
|
-
const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
|
|
3032
|
-
[]
|
|
3033
|
-
];
|
|
3034
|
-
for (const values of valueRows) {
|
|
3035
|
-
if (isManyToManyJoinTable) {
|
|
3036
|
-
await this.enforcePreCreatePolicyForManyToManyJoinTable(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
3037
|
-
} else {
|
|
3038
|
-
await this.enforcePreCreatePolicyForOne(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
3039
|
-
}
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
async enforcePreCreatePolicyForManyToManyJoinTable(tableName, fields, values, proceed) {
|
|
3043
|
-
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
3044
|
-
invariant14(m2m);
|
|
3045
|
-
invariant14(fields.includes("A") && fields.includes("B"), "many-to-many join table must have A and B fk fields");
|
|
3046
|
-
const aIndex = fields.indexOf("A");
|
|
3047
|
-
const aNode = values[aIndex];
|
|
3048
|
-
const bIndex = fields.indexOf("B");
|
|
3049
|
-
const bNode = values[bIndex];
|
|
3050
|
-
invariant14(ValueNode4.is(aNode) && ValueNode4.is(bNode), "A and B values must be ValueNode");
|
|
3051
|
-
const aValue = aNode.value;
|
|
3052
|
-
const bValue = bNode.value;
|
|
3053
|
-
invariant14(aValue !== null && aValue !== void 0, "A value cannot be null or undefined");
|
|
3054
|
-
invariant14(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
|
|
3055
|
-
const eb = expressionBuilder5();
|
|
3056
|
-
const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
|
|
3057
|
-
const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new ExpressionWrapper(filterA).as("$t"));
|
|
3058
|
-
const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
|
|
3059
|
-
const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new ExpressionWrapper(filterB).as("$t"));
|
|
3060
|
-
const queryNode = {
|
|
3061
|
-
kind: "SelectQueryNode",
|
|
3062
|
-
selections: [
|
|
3063
|
-
SelectionNode4.create(AliasNode5.create(queryA.toOperationNode(), IdentifierNode3.create("$conditionA"))),
|
|
3064
|
-
SelectionNode4.create(AliasNode5.create(queryB.toOperationNode(), IdentifierNode3.create("$conditionB")))
|
|
3065
|
-
]
|
|
3066
|
-
};
|
|
3067
|
-
const result = await proceed(queryNode);
|
|
3068
|
-
if (!result.rows[0]?.$conditionA) {
|
|
3069
|
-
throw new RejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
|
|
3070
|
-
}
|
|
3071
|
-
if (!result.rows[0]?.$conditionB) {
|
|
3072
|
-
throw new RejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
|
|
3073
|
-
}
|
|
3074
|
-
}
|
|
3075
|
-
async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
|
|
3076
|
-
const allFields = Object.entries(requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
|
|
3077
|
-
const allValues = [];
|
|
3078
|
-
for (const [name, _def] of allFields) {
|
|
3079
|
-
const index = fields.indexOf(name);
|
|
3080
|
-
if (index >= 0) {
|
|
3081
|
-
allValues.push(values[index]);
|
|
3082
|
-
} else {
|
|
3083
|
-
allValues.push(ValueNode4.createImmediate(null));
|
|
3084
|
-
}
|
|
3085
|
-
}
|
|
3086
|
-
const eb = expressionBuilder5();
|
|
3087
|
-
const constTable = {
|
|
3088
|
-
kind: "SelectQueryNode",
|
|
3089
|
-
from: FromNode3.create([
|
|
3090
|
-
AliasNode5.create(ParensNode2.create(ValuesNode.create([
|
|
3091
|
-
ValueListNode2.create(allValues)
|
|
3092
|
-
])), IdentifierNode3.create("$t"))
|
|
3093
|
-
]),
|
|
3094
|
-
selections: allFields.map(([name, def], index) => {
|
|
3095
|
-
const castedColumnRef = sql11`CAST(${eb.ref(`column${index + 1}`)} as ${sql11.raw(this.dialect.getFieldSqlType(def))})`.as(name);
|
|
3096
|
-
return SelectionNode4.create(castedColumnRef.toOperationNode());
|
|
3097
|
-
})
|
|
3098
|
-
};
|
|
3099
|
-
const filter = this.buildPolicyFilter(model, void 0, "create");
|
|
3100
|
-
const preCreateCheck = {
|
|
3101
|
-
kind: "SelectQueryNode",
|
|
3102
|
-
from: FromNode3.create([
|
|
3103
|
-
AliasNode5.create(constTable, IdentifierNode3.create(model))
|
|
3104
|
-
]),
|
|
3105
|
-
selections: [
|
|
3106
|
-
SelectionNode4.create(AliasNode5.create(BinaryOperationNode3.create(FunctionNode3.create("COUNT", [
|
|
3107
|
-
ValueNode4.createImmediate(1)
|
|
3108
|
-
]), OperatorNode3.create(">"), ValueNode4.createImmediate(0)), IdentifierNode3.create("$condition")))
|
|
3109
|
-
],
|
|
3110
|
-
where: WhereNode3.create(filter)
|
|
3111
|
-
};
|
|
3112
|
-
const result = await proceed(preCreateCheck);
|
|
3113
|
-
if (!result.rows[0]?.$condition) {
|
|
3114
|
-
throw new RejectedByPolicyError(model);
|
|
3115
|
-
}
|
|
3116
|
-
}
|
|
3117
|
-
unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
|
|
3118
|
-
if (ValuesNode.is(node)) {
|
|
3119
|
-
return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields, isManyToManyJoinTable));
|
|
3120
|
-
} else if (PrimitiveValueListNode.is(node)) {
|
|
3121
|
-
return [
|
|
3122
|
-
this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
|
|
3123
|
-
];
|
|
3124
|
-
} else {
|
|
3125
|
-
throw new InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
|
|
3129
|
-
invariant14(data.length === fields.length, "data length must match fields length");
|
|
3130
|
-
const result = [];
|
|
3131
|
-
for (let i = 0; i < data.length; i++) {
|
|
3132
|
-
const item = data[i];
|
|
3133
|
-
if (typeof item === "object" && item && "kind" in item) {
|
|
3134
|
-
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
3135
|
-
invariant14(item.kind === "ValueNode", "expecting a ValueNode");
|
|
3136
|
-
result.push({
|
|
3137
|
-
node: ValueNode4.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
|
|
3138
|
-
raw: item.value
|
|
3139
|
-
});
|
|
3140
|
-
} else {
|
|
3141
|
-
let value = item;
|
|
3142
|
-
if (!isImplicitManyToManyJoinTable) {
|
|
3143
|
-
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
3144
|
-
value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
|
|
3145
|
-
}
|
|
3146
|
-
if (Array.isArray(value)) {
|
|
3147
|
-
result.push({
|
|
3148
|
-
node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
|
|
3149
|
-
raw: value
|
|
3150
|
-
});
|
|
3151
|
-
} else {
|
|
3152
|
-
result.push({
|
|
3153
|
-
node: ValueNode4.create(value),
|
|
3154
|
-
raw: value
|
|
3155
|
-
});
|
|
3156
|
-
}
|
|
3157
|
-
}
|
|
3158
|
-
}
|
|
3159
|
-
return result;
|
|
3160
|
-
}
|
|
3161
|
-
tryGetConstantPolicy(model, operation) {
|
|
3162
|
-
const policies = this.getModelPolicies(model, operation);
|
|
3163
|
-
if (!policies.some((p) => p.kind === "allow")) {
|
|
3164
|
-
return false;
|
|
3165
|
-
} else if (
|
|
3166
|
-
// unconditional deny
|
|
3167
|
-
policies.some((p) => p.kind === "deny" && this.isTrueExpr(p.condition))
|
|
3168
|
-
) {
|
|
3169
|
-
return false;
|
|
3170
|
-
} else if (
|
|
3171
|
-
// unconditional allow
|
|
3172
|
-
!policies.some((p) => p.kind === "deny") && policies.some((p) => p.kind === "allow" && this.isTrueExpr(p.condition))
|
|
3173
|
-
) {
|
|
3174
|
-
return true;
|
|
3175
|
-
} else {
|
|
3176
|
-
return void 0;
|
|
3177
|
-
}
|
|
3178
|
-
}
|
|
3179
|
-
isTrueExpr(expr2) {
|
|
3180
|
-
return ExpressionUtils.isLiteral(expr2) && expr2.value === true;
|
|
3181
|
-
}
|
|
3182
|
-
async processReadBack(node, result, proceed) {
|
|
3183
|
-
if (result.rows.length === 0) {
|
|
3184
|
-
return result;
|
|
3185
|
-
}
|
|
3186
|
-
if (!this.isMutationQueryNode(node) || !node.returning) {
|
|
3187
|
-
return result;
|
|
3188
|
-
}
|
|
3189
|
-
const { mutationModel } = this.getMutationModel(node);
|
|
3190
|
-
const idConditions = this.buildIdConditions(mutationModel, result.rows);
|
|
3191
|
-
const policyFilter = this.buildPolicyFilter(mutationModel, void 0, "read");
|
|
3192
|
-
const select = {
|
|
3193
|
-
kind: "SelectQueryNode",
|
|
3194
|
-
from: FromNode3.create([
|
|
3195
|
-
TableNode6.create(mutationModel)
|
|
3196
|
-
]),
|
|
3197
|
-
where: WhereNode3.create(conjunction(this.dialect, [
|
|
3198
|
-
idConditions,
|
|
3199
|
-
policyFilter
|
|
3200
|
-
])),
|
|
3201
|
-
selections: node.returning.selections
|
|
3202
|
-
};
|
|
3203
|
-
const selectResult = await proceed(select);
|
|
3204
|
-
return selectResult;
|
|
3205
|
-
}
|
|
3206
|
-
buildIdConditions(table, rows) {
|
|
3207
|
-
const idFields = requireIdFields(this.client.$schema, table);
|
|
3208
|
-
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(ReferenceNode5.create(ColumnNode4.create(field), TableNode6.create(table)), OperatorNode3.create("="), ValueNode4.create(row[field]))))));
|
|
3209
|
-
}
|
|
3210
|
-
getMutationModel(node) {
|
|
3211
|
-
const r = match19(node).when(InsertQueryNode2.is, (node2) => ({
|
|
3212
|
-
mutationModel: getTableName(node2.into),
|
|
3213
|
-
alias: void 0
|
|
3214
|
-
})).when(UpdateQueryNode2.is, (node2) => {
|
|
3215
|
-
if (!node2.table) {
|
|
3216
|
-
throw new QueryError("Update query must have a table");
|
|
3217
|
-
}
|
|
3218
|
-
const r2 = this.extractTableName(node2.table);
|
|
3219
|
-
return r2 ? {
|
|
3220
|
-
mutationModel: r2.model,
|
|
3221
|
-
alias: r2.alias
|
|
3222
|
-
} : void 0;
|
|
3223
|
-
}).when(DeleteQueryNode2.is, (node2) => {
|
|
3224
|
-
if (node2.from.froms.length !== 1) {
|
|
3225
|
-
throw new QueryError("Only one from table is supported for delete");
|
|
3226
|
-
}
|
|
3227
|
-
const r2 = this.extractTableName(node2.from.froms[0]);
|
|
3228
|
-
return r2 ? {
|
|
3229
|
-
mutationModel: r2.model,
|
|
3230
|
-
alias: r2.alias
|
|
3231
|
-
} : void 0;
|
|
3232
|
-
}).exhaustive();
|
|
3233
|
-
if (!r) {
|
|
3234
|
-
throw new InternalError(`Unable to get table name for query node: ${node}`);
|
|
3235
|
-
}
|
|
3236
|
-
return r;
|
|
3237
|
-
}
|
|
3238
|
-
isCrudQueryNode(node) {
|
|
3239
|
-
return SelectQueryNode2.is(node) || InsertQueryNode2.is(node) || UpdateQueryNode2.is(node) || DeleteQueryNode2.is(node);
|
|
3240
|
-
}
|
|
3241
|
-
isMutationQueryNode(node) {
|
|
3242
|
-
return InsertQueryNode2.is(node) || UpdateQueryNode2.is(node) || DeleteQueryNode2.is(node);
|
|
3243
|
-
}
|
|
3244
|
-
buildPolicyFilter(model, alias, operation) {
|
|
3245
|
-
const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
|
|
3246
|
-
if (m2mFilter) {
|
|
3247
|
-
return m2mFilter;
|
|
3248
|
-
}
|
|
3249
|
-
const policies = this.getModelPolicies(model, operation);
|
|
3250
|
-
const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
3251
|
-
const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
3252
|
-
let combinedPolicy;
|
|
3253
|
-
if (allows.length === 0) {
|
|
3254
|
-
if (operation === "post-update") {
|
|
3255
|
-
combinedPolicy = trueNode(this.dialect);
|
|
3256
|
-
} else {
|
|
3257
|
-
combinedPolicy = falseNode(this.dialect);
|
|
3258
|
-
}
|
|
3259
|
-
} else {
|
|
3260
|
-
combinedPolicy = disjunction(this.dialect, allows);
|
|
3261
|
-
}
|
|
3262
|
-
if (denies.length !== 0) {
|
|
3263
|
-
const combinedDenies = conjunction(this.dialect, denies.map((d) => buildIsFalse(d, this.dialect)));
|
|
3264
|
-
combinedPolicy = conjunction(this.dialect, [
|
|
3265
|
-
combinedPolicy,
|
|
3266
|
-
combinedDenies
|
|
3267
|
-
]);
|
|
3268
|
-
}
|
|
3269
|
-
return combinedPolicy;
|
|
3270
|
-
}
|
|
3271
|
-
extractTableName(node) {
|
|
3272
|
-
if (TableNode6.is(node)) {
|
|
3273
|
-
return {
|
|
3274
|
-
model: node.table.identifier.name
|
|
3275
|
-
};
|
|
3276
|
-
}
|
|
3277
|
-
if (AliasNode5.is(node)) {
|
|
3278
|
-
const inner = this.extractTableName(node.node);
|
|
3279
|
-
if (!inner) {
|
|
3280
|
-
return void 0;
|
|
3281
|
-
}
|
|
3282
|
-
return {
|
|
3283
|
-
model: inner.model,
|
|
3284
|
-
alias: IdentifierNode3.is(node.alias) ? node.alias.name : void 0
|
|
3285
|
-
};
|
|
3286
|
-
} else {
|
|
3287
|
-
return void 0;
|
|
3288
|
-
}
|
|
3289
|
-
}
|
|
3290
|
-
createPolicyFilterForFrom(node) {
|
|
3291
|
-
if (!node) {
|
|
3292
|
-
return void 0;
|
|
3293
|
-
}
|
|
3294
|
-
return this.createPolicyFilterForTables(node.froms);
|
|
3295
|
-
}
|
|
3296
|
-
createPolicyFilterForTables(tables) {
|
|
3297
|
-
return tables.reduce((acc, table) => {
|
|
3298
|
-
const extractResult = this.extractTableName(table);
|
|
3299
|
-
if (extractResult) {
|
|
3300
|
-
const { model, alias } = extractResult;
|
|
3301
|
-
const filter = this.buildPolicyFilter(model, alias, "read");
|
|
3302
|
-
return acc ? conjunction(this.dialect, [
|
|
3303
|
-
acc,
|
|
3304
|
-
filter
|
|
3305
|
-
]) : filter;
|
|
3306
|
-
}
|
|
3307
|
-
return acc;
|
|
3308
|
-
}, void 0);
|
|
3309
|
-
}
|
|
3310
|
-
compilePolicyCondition(model, alias, operation, policy) {
|
|
3311
|
-
return new ExpressionTransformer(this.client).transform(policy.condition, {
|
|
3312
|
-
model,
|
|
3313
|
-
alias,
|
|
3314
|
-
operation
|
|
3315
|
-
});
|
|
3316
|
-
}
|
|
3317
|
-
getModelPolicies(model, operation) {
|
|
3318
|
-
const modelDef = requireModel(this.client.$schema, model);
|
|
3319
|
-
const result = [];
|
|
3320
|
-
const extractOperations = /* @__PURE__ */ __name((expr2) => {
|
|
3321
|
-
invariant14(ExpressionUtils.isLiteral(expr2), "expecting a literal");
|
|
3322
|
-
invariant14(typeof expr2.value === "string", "expecting a string literal");
|
|
3323
|
-
return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
|
|
3324
|
-
}, "extractOperations");
|
|
3325
|
-
if (modelDef.attributes) {
|
|
3326
|
-
result.push(...modelDef.attributes.filter((attr) => attr.name === "@@allow" || attr.name === "@@deny").map((attr) => ({
|
|
3327
|
-
kind: attr.name === "@@allow" ? "allow" : "deny",
|
|
3328
|
-
operations: extractOperations(attr.args[0].value),
|
|
3329
|
-
condition: attr.args[1].value
|
|
3330
|
-
})).filter((policy) => operation !== "post-update" && policy.operations.includes("all") || policy.operations.includes(operation)));
|
|
3331
|
-
}
|
|
3332
|
-
return result;
|
|
3333
|
-
}
|
|
3334
|
-
resolveManyToManyJoinTable(tableName) {
|
|
3335
|
-
for (const model of Object.values(this.client.$schema.models)) {
|
|
3336
|
-
for (const field of Object.values(model.fields)) {
|
|
3337
|
-
const m2m = getManyToManyRelation(this.client.$schema, model.name, field.name);
|
|
3338
|
-
if (m2m?.joinTable === tableName) {
|
|
3339
|
-
const sortedRecord = [
|
|
3340
|
-
{
|
|
3341
|
-
model: model.name,
|
|
3342
|
-
field: field.name
|
|
3343
|
-
},
|
|
3344
|
-
{
|
|
3345
|
-
model: m2m.otherModel,
|
|
3346
|
-
field: m2m.otherField
|
|
3347
|
-
}
|
|
3348
|
-
].sort(this.manyToManySorter);
|
|
3349
|
-
const firstIdFields = requireIdFields(this.client.$schema, sortedRecord[0].model);
|
|
3350
|
-
const secondIdFields = requireIdFields(this.client.$schema, sortedRecord[1].model);
|
|
3351
|
-
invariant14(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
|
|
3352
|
-
return {
|
|
3353
|
-
firstModel: sortedRecord[0].model,
|
|
3354
|
-
firstField: sortedRecord[0].field,
|
|
3355
|
-
firstIdField: firstIdFields[0],
|
|
3356
|
-
secondModel: sortedRecord[1].model,
|
|
3357
|
-
secondField: sortedRecord[1].field,
|
|
3358
|
-
secondIdField: secondIdFields[0]
|
|
3359
|
-
};
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
}
|
|
3363
|
-
return void 0;
|
|
3364
|
-
}
|
|
3365
|
-
manyToManySorter(a, b) {
|
|
3366
|
-
return a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field);
|
|
3367
|
-
}
|
|
3368
|
-
isManyToManyJoinTable(tableName) {
|
|
3369
|
-
return !!this.resolveManyToManyJoinTable(tableName);
|
|
3370
|
-
}
|
|
3371
|
-
getModelPolicyFilterForManyToManyJoinTable(tableName, alias, operation) {
|
|
3372
|
-
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
3373
|
-
if (!m2m) {
|
|
3374
|
-
return void 0;
|
|
3375
|
-
}
|
|
3376
|
-
const checkForOperation = operation === "read" ? "read" : "update";
|
|
3377
|
-
const eb = expressionBuilder5();
|
|
3378
|
-
const joinTable = alias ?? tableName;
|
|
3379
|
-
const aQuery = eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
|
|
3380
|
-
const bQuery = eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
|
|
3381
|
-
return eb.and([
|
|
3382
|
-
aQuery,
|
|
3383
|
-
bQuery
|
|
3384
|
-
]).toOperationNode();
|
|
3385
|
-
}
|
|
3386
|
-
};
|
|
3387
|
-
|
|
3388
|
-
// src/plugins/policy/functions.ts
|
|
3389
|
-
var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, operation }) => {
|
|
3390
|
-
invariant15(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments');
|
|
3391
|
-
const arg1Node = args[0].toOperationNode();
|
|
3392
|
-
const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
|
|
3393
|
-
if (arg2Node) {
|
|
3394
|
-
invariant15(ValueNode5.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
|
|
3395
|
-
invariant15(CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
|
|
3396
|
-
}
|
|
3397
|
-
const fieldName = extractFieldName(arg1Node);
|
|
3398
|
-
invariant15(fieldName, 'Failed to extract field name from the first argument of "check" function');
|
|
3399
|
-
const fieldDef = requireField(client.$schema, model, fieldName);
|
|
3400
|
-
invariant15(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
|
|
3401
|
-
invariant15(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
|
|
3402
|
-
const relationModel = fieldDef.type;
|
|
3403
|
-
const op = arg2Node ? arg2Node.value : operation;
|
|
3404
|
-
const policyHandler = new PolicyHandler(client);
|
|
3405
|
-
const joinPairs = buildJoinPairs(client.$schema, model, modelAlias, fieldName, relationModel);
|
|
3406
|
-
const joinCondition = joinPairs.length === 1 ? eb(eb.ref(joinPairs[0][0]), "=", eb.ref(joinPairs[0][1])) : eb.and(joinPairs.map(([left, right]) => eb(eb.ref(left), "=", eb.ref(right))));
|
|
3407
|
-
const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
|
|
3408
|
-
const result = eb.selectFrom(relationModel).where(joinCondition).select(new ExpressionWrapper2(policyCondition).as("$condition"));
|
|
3409
|
-
return result;
|
|
3410
|
-
}, "check");
|
|
3411
|
-
|
|
3412
|
-
// src/plugins/policy/plugin.ts
|
|
3413
|
-
var PolicyPlugin = class {
|
|
3414
|
-
static {
|
|
3415
|
-
__name(this, "PolicyPlugin");
|
|
3416
|
-
}
|
|
3417
|
-
get id() {
|
|
3418
|
-
return "policy";
|
|
3419
|
-
}
|
|
3420
|
-
get name() {
|
|
3421
|
-
return "Access Policy";
|
|
3422
|
-
}
|
|
3423
|
-
get description() {
|
|
3424
|
-
return "Enforces access policies defined in the schema.";
|
|
3425
|
-
}
|
|
3426
|
-
get functions() {
|
|
3427
|
-
return {
|
|
3428
|
-
check
|
|
3429
|
-
};
|
|
3430
|
-
}
|
|
3431
|
-
onKyselyQuery({ query, client, proceed }) {
|
|
3432
|
-
const handler = new PolicyHandler(client);
|
|
3433
|
-
return handler.handle(query, proceed);
|
|
3434
|
-
}
|
|
3435
|
-
};
|
|
3436
|
-
export {
|
|
3437
|
-
PolicyPlugin,
|
|
3438
|
-
RejectedByPolicyError,
|
|
3439
|
-
RejectedByPolicyReason
|
|
3440
|
-
};
|
|
3441
|
-
//# sourceMappingURL=index.js.map
|