@zenstackhq/runtime 3.0.0-beta.4 → 3.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{contract-hoS-Sd87.d.cts → contract-BJce14-p.d.cts} +54 -12
- package/dist/{contract-hoS-Sd87.d.ts → contract-BJce14-p.d.ts} +54 -12
- package/dist/index.cjs +1211 -690
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1049 -528
- package/dist/index.js.map +1 -1
- package/dist/plugins/policy/index.cjs +835 -329
- package/dist/plugins/policy/index.cjs.map +1 -1
- package/dist/plugins/policy/index.d.cts +24 -4
- package/dist/plugins/policy/index.d.ts +24 -4
- package/dist/plugins/policy/index.js +736 -241
- package/dist/plugins/policy/index.js.map +1 -1
- package/dist/plugins/policy/plugin.zmodel +10 -0
- package/package.json +11 -8
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
9
|
var __export = (target, all) => {
|
|
@@ -16,57 +18,70 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/plugins/policy/index.ts
|
|
22
32
|
var policy_exports = {};
|
|
23
33
|
__export(policy_exports, {
|
|
24
34
|
PolicyPlugin: () => PolicyPlugin,
|
|
25
|
-
RejectedByPolicyError: () => RejectedByPolicyError
|
|
35
|
+
RejectedByPolicyError: () => RejectedByPolicyError,
|
|
36
|
+
RejectedByPolicyReason: () => RejectedByPolicyReason
|
|
26
37
|
});
|
|
27
38
|
module.exports = __toCommonJS(policy_exports);
|
|
28
39
|
|
|
29
40
|
// src/plugins/policy/errors.ts
|
|
41
|
+
var RejectedByPolicyReason = /* @__PURE__ */ function(RejectedByPolicyReason2) {
|
|
42
|
+
RejectedByPolicyReason2["NO_ACCESS"] = "no-access";
|
|
43
|
+
RejectedByPolicyReason2["CANNOT_READ_BACK"] = "cannot-read-back";
|
|
44
|
+
RejectedByPolicyReason2["OTHER"] = "other";
|
|
45
|
+
return RejectedByPolicyReason2;
|
|
46
|
+
}({});
|
|
30
47
|
var RejectedByPolicyError = class extends Error {
|
|
31
48
|
static {
|
|
32
49
|
__name(this, "RejectedByPolicyError");
|
|
33
50
|
}
|
|
34
51
|
model;
|
|
35
52
|
reason;
|
|
36
|
-
constructor(model, reason) {
|
|
37
|
-
super(
|
|
53
|
+
constructor(model, reason = "no-access", message) {
|
|
54
|
+
super(message ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
|
|
38
55
|
}
|
|
39
56
|
};
|
|
40
57
|
|
|
41
|
-
// src/plugins/policy/
|
|
42
|
-
var
|
|
43
|
-
var
|
|
44
|
-
var import_ts_pattern8 = require("ts-pattern");
|
|
45
|
-
|
|
46
|
-
// src/client/crud/dialects/index.ts
|
|
47
|
-
var import_ts_pattern5 = require("ts-pattern");
|
|
48
|
-
|
|
49
|
-
// src/client/crud/dialects/postgresql.ts
|
|
50
|
-
var import_common_helpers2 = require("@zenstackhq/common-helpers");
|
|
51
|
-
var import_kysely2 = require("kysely");
|
|
52
|
-
var import_ts_pattern3 = require("ts-pattern");
|
|
58
|
+
// src/plugins/policy/functions.ts
|
|
59
|
+
var import_common_helpers8 = require("@zenstackhq/common-helpers");
|
|
60
|
+
var import_kysely9 = require("kysely");
|
|
53
61
|
|
|
54
|
-
// src/client/
|
|
55
|
-
var
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
];
|
|
61
|
-
var AGGREGATE_OPERATORS = [
|
|
62
|
-
"_count",
|
|
63
|
-
"_sum",
|
|
64
|
-
"_avg",
|
|
65
|
-
"_min",
|
|
66
|
-
"_max"
|
|
62
|
+
// src/client/contract.ts
|
|
63
|
+
var CRUD = [
|
|
64
|
+
"create",
|
|
65
|
+
"read",
|
|
66
|
+
"update",
|
|
67
|
+
"delete"
|
|
67
68
|
];
|
|
68
69
|
|
|
70
|
+
// src/client/kysely-utils.ts
|
|
71
|
+
var import_kysely = require("kysely");
|
|
72
|
+
function extractFieldName(node) {
|
|
73
|
+
if (import_kysely.ReferenceNode.is(node) && import_kysely.ColumnNode.is(node.column)) {
|
|
74
|
+
return node.column.column.name;
|
|
75
|
+
} else if (import_kysely.ColumnNode.is(node)) {
|
|
76
|
+
return node.column.name;
|
|
77
|
+
} else {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
__name(extractFieldName, "extractFieldName");
|
|
82
|
+
|
|
69
83
|
// src/client/query-utils.ts
|
|
84
|
+
var import_common_helpers = require("@zenstackhq/common-helpers");
|
|
70
85
|
var import_ts_pattern = require("ts-pattern");
|
|
71
86
|
|
|
72
87
|
// src/schema/expression.ts
|
|
@@ -213,11 +228,15 @@ function requireField(schema, modelOrType, field) {
|
|
|
213
228
|
throw new QueryError(`Model or type "${modelOrType}" not found in schema`);
|
|
214
229
|
}
|
|
215
230
|
__name(requireField, "requireField");
|
|
216
|
-
function
|
|
231
|
+
function requireIdFields(schema, model) {
|
|
217
232
|
const modelDef = requireModel(schema, model);
|
|
218
|
-
|
|
233
|
+
const result = modelDef?.idFields;
|
|
234
|
+
if (!result) {
|
|
235
|
+
throw new InternalError(`Model "${model}" does not have ID field(s)`);
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
219
238
|
}
|
|
220
|
-
__name(
|
|
239
|
+
__name(requireIdFields, "requireIdFields");
|
|
221
240
|
function getRelationForeignKeyFieldPairs(schema, model, relationField) {
|
|
222
241
|
const fieldDef = requireField(schema, model, relationField);
|
|
223
242
|
if (!fieldDef?.relation) {
|
|
@@ -310,7 +329,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComp
|
|
|
310
329
|
throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
|
|
311
330
|
}
|
|
312
331
|
return computer(eb, {
|
|
313
|
-
|
|
332
|
+
modelAlias
|
|
314
333
|
});
|
|
315
334
|
}
|
|
316
335
|
}
|
|
@@ -337,7 +356,7 @@ function buildJoinPairs(schema, model, modelAlias, relationField, relationModelA
|
|
|
337
356
|
}
|
|
338
357
|
__name(buildJoinPairs, "buildJoinPairs");
|
|
339
358
|
function makeDefaultOrderBy(schema, model) {
|
|
340
|
-
const idFields =
|
|
359
|
+
const idFields = requireIdFields(schema, model);
|
|
341
360
|
return idFields.map((f) => ({
|
|
342
361
|
[f]: "asc"
|
|
343
362
|
}));
|
|
@@ -376,11 +395,17 @@ function getManyToManyRelation(schema, model, field) {
|
|
|
376
395
|
"A"
|
|
377
396
|
];
|
|
378
397
|
}
|
|
398
|
+
const modelIdFields = requireIdFields(schema, model);
|
|
399
|
+
(0, import_common_helpers.invariant)(modelIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
400
|
+
const otherIdFields = requireIdFields(schema, fieldDef.type);
|
|
401
|
+
(0, import_common_helpers.invariant)(otherIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
379
402
|
return {
|
|
380
403
|
parentFkName: orderedFK[0],
|
|
404
|
+
parentPKName: modelIdFields[0],
|
|
381
405
|
otherModel: fieldDef.type,
|
|
382
406
|
otherField: fieldDef.relation.opposite,
|
|
383
407
|
otherFkName: orderedFK[1],
|
|
408
|
+
otherPKName: otherIdFields[0],
|
|
384
409
|
joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
|
|
385
410
|
};
|
|
386
411
|
} else {
|
|
@@ -436,9 +461,38 @@ function aggregate(eb, expr2, op) {
|
|
|
436
461
|
}
|
|
437
462
|
__name(aggregate, "aggregate");
|
|
438
463
|
|
|
464
|
+
// src/plugins/policy/policy-handler.ts
|
|
465
|
+
var import_common_helpers7 = require("@zenstackhq/common-helpers");
|
|
466
|
+
var import_kysely8 = require("kysely");
|
|
467
|
+
var import_ts_pattern8 = require("ts-pattern");
|
|
468
|
+
|
|
469
|
+
// src/client/crud/dialects/index.ts
|
|
470
|
+
var import_ts_pattern5 = require("ts-pattern");
|
|
471
|
+
|
|
472
|
+
// src/client/crud/dialects/postgresql.ts
|
|
473
|
+
var import_common_helpers3 = require("@zenstackhq/common-helpers");
|
|
474
|
+
var import_decimal = __toESM(require("decimal.js"), 1);
|
|
475
|
+
var import_kysely3 = require("kysely");
|
|
476
|
+
var import_ts_pattern3 = require("ts-pattern");
|
|
477
|
+
|
|
478
|
+
// src/client/constants.ts
|
|
479
|
+
var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
|
|
480
|
+
var LOGICAL_COMBINATORS = [
|
|
481
|
+
"AND",
|
|
482
|
+
"OR",
|
|
483
|
+
"NOT"
|
|
484
|
+
];
|
|
485
|
+
var AGGREGATE_OPERATORS = [
|
|
486
|
+
"_count",
|
|
487
|
+
"_sum",
|
|
488
|
+
"_avg",
|
|
489
|
+
"_min",
|
|
490
|
+
"_max"
|
|
491
|
+
];
|
|
492
|
+
|
|
439
493
|
// src/client/crud/dialects/base-dialect.ts
|
|
440
|
-
var
|
|
441
|
-
var
|
|
494
|
+
var import_common_helpers2 = require("@zenstackhq/common-helpers");
|
|
495
|
+
var import_kysely2 = require("kysely");
|
|
442
496
|
var import_ts_pattern2 = require("ts-pattern");
|
|
443
497
|
|
|
444
498
|
// src/utils/enumerate.ts
|
|
@@ -469,6 +523,9 @@ var BaseCrudDialect = class {
|
|
|
469
523
|
transformPrimitive(value, _type, _forArrayField) {
|
|
470
524
|
return value;
|
|
471
525
|
}
|
|
526
|
+
transformOutput(value, _type) {
|
|
527
|
+
return value;
|
|
528
|
+
}
|
|
472
529
|
// #region common query builders
|
|
473
530
|
buildSelectModel(eb, model, modelAlias) {
|
|
474
531
|
const modelDef = requireModel(this.schema, model);
|
|
@@ -497,7 +554,7 @@ var BaseCrudDialect = class {
|
|
|
497
554
|
if ("distinct" in args && args.distinct) {
|
|
498
555
|
const distinct = ensureArray(args.distinct);
|
|
499
556
|
if (this.supportsDistinctOn) {
|
|
500
|
-
result = result.distinctOn(distinct.map((f) =>
|
|
557
|
+
result = result.distinctOn(distinct.map((f) => import_kysely2.sql.ref(`${modelAlias}.${f}`)));
|
|
501
558
|
} else {
|
|
502
559
|
throw new QueryError(`"distinct" is not supported by "${this.schema.provider.type}" provider`);
|
|
503
560
|
}
|
|
@@ -547,7 +604,7 @@ var BaseCrudDialect = class {
|
|
|
547
604
|
buildCursorFilter(model, query, cursor, orderBy, negateOrderBy, modelAlias) {
|
|
548
605
|
const _orderBy = orderBy ?? makeDefaultOrderBy(this.schema, model);
|
|
549
606
|
const orderByItems = ensureArray(_orderBy).flatMap((obj) => Object.entries(obj));
|
|
550
|
-
const eb = (0,
|
|
607
|
+
const eb = (0, import_kysely2.expressionBuilder)();
|
|
551
608
|
const subQueryAlias = `${model}$cursor$sub`;
|
|
552
609
|
const cursorFilter = this.buildFilter(eb, model, subQueryAlias, cursor);
|
|
553
610
|
let result = query;
|
|
@@ -582,7 +639,7 @@ var BaseCrudDialect = class {
|
|
|
582
639
|
if (payload === null) {
|
|
583
640
|
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
584
641
|
if (ownedByModel && !fieldDef.originModel) {
|
|
585
|
-
return this.and(eb, ...keyPairs.map(({ fk }) => eb(
|
|
642
|
+
return this.and(eb, ...keyPairs.map(({ fk }) => eb(import_kysely2.sql.ref(`${modelAlias}.${fk}`), "is", null)));
|
|
586
643
|
} else {
|
|
587
644
|
return this.buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, {
|
|
588
645
|
is: null
|
|
@@ -599,7 +656,7 @@ var BaseCrudDialect = class {
|
|
|
599
656
|
joinAlias
|
|
600
657
|
);
|
|
601
658
|
const filterResultField = `${field}$filter`;
|
|
602
|
-
const joinSelect = eb.selectFrom(`${fieldDef.type} as ${joinAlias}`).where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(
|
|
659
|
+
const joinSelect = eb.selectFrom(`${fieldDef.type} as ${joinAlias}`).where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right))))).select(() => eb.fn.count(eb.lit(1)).as(filterResultField));
|
|
603
660
|
const conditions = [];
|
|
604
661
|
if ("is" in payload || "isNot" in payload) {
|
|
605
662
|
if ("is" in payload) {
|
|
@@ -629,24 +686,26 @@ var BaseCrudDialect = class {
|
|
|
629
686
|
}
|
|
630
687
|
buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
|
|
631
688
|
if (payload === null) {
|
|
632
|
-
return eb(
|
|
689
|
+
return eb(import_kysely2.sql.ref(`${modelAlias}.${field}`), "is", null);
|
|
633
690
|
}
|
|
634
691
|
const relationModel = fieldDef.type;
|
|
635
692
|
const relationFilterSelectAlias = `${modelAlias}$${field}$filter`;
|
|
636
693
|
const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
|
|
637
694
|
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
638
695
|
if (m2m) {
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
696
|
+
const modelIdFields = requireIdFields(this.schema, model);
|
|
697
|
+
(0, import_common_helpers2.invariant)(modelIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
698
|
+
const relationIdFields = requireIdFields(this.schema, relationModel);
|
|
699
|
+
(0, import_common_helpers2.invariant)(relationIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
700
|
+
return eb2(import_kysely2.sql.ref(`${relationFilterSelectAlias}.${relationIdFields[0]}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(import_kysely2.sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", import_kysely2.sql.ref(`${modelAlias}.${modelIdFields[0]}`)));
|
|
642
701
|
} else {
|
|
643
702
|
const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
644
703
|
let result2 = this.true(eb2);
|
|
645
704
|
for (const { fk, pk } of relationKeyPairs.keyPairs) {
|
|
646
705
|
if (relationKeyPairs.ownedByModel) {
|
|
647
|
-
result2 = this.and(eb2, result2, eb2(
|
|
706
|
+
result2 = this.and(eb2, result2, eb2(import_kysely2.sql.ref(`${modelAlias}.${fk}`), "=", import_kysely2.sql.ref(`${relationFilterSelectAlias}.${pk}`)));
|
|
648
707
|
} else {
|
|
649
|
-
result2 = this.and(eb2, result2, eb2(
|
|
708
|
+
result2 = this.and(eb2, result2, eb2(import_kysely2.sql.ref(`${modelAlias}.${pk}`), "=", import_kysely2.sql.ref(`${relationFilterSelectAlias}.${fk}`)));
|
|
650
709
|
}
|
|
651
710
|
}
|
|
652
711
|
return result2;
|
|
@@ -729,7 +788,7 @@ var BaseCrudDialect = class {
|
|
|
729
788
|
return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
|
|
730
789
|
}
|
|
731
790
|
buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
|
|
732
|
-
if (payload === null || !(0,
|
|
791
|
+
if (payload === null || !(0, import_common_helpers2.isPlainObject)(payload)) {
|
|
733
792
|
return {
|
|
734
793
|
conditions: [
|
|
735
794
|
this.buildLiteralFilter(eb, lhs, type, payload)
|
|
@@ -748,14 +807,14 @@ var BaseCrudDialect = class {
|
|
|
748
807
|
}
|
|
749
808
|
const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
|
|
750
809
|
const condition = (0, import_ts_pattern2.match)(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
|
|
751
|
-
(0,
|
|
810
|
+
(0, import_common_helpers2.invariant)(Array.isArray(rhs), "right hand side must be an array");
|
|
752
811
|
if (rhs.length === 0) {
|
|
753
812
|
return this.false(eb);
|
|
754
813
|
} else {
|
|
755
814
|
return eb(lhs, "in", rhs);
|
|
756
815
|
}
|
|
757
816
|
}).with("notIn", () => {
|
|
758
|
-
(0,
|
|
817
|
+
(0, import_common_helpers2.invariant)(Array.isArray(rhs), "right hand side must be an array");
|
|
759
818
|
if (rhs.length === 0) {
|
|
760
819
|
return this.true(eb);
|
|
761
820
|
} else {
|
|
@@ -795,7 +854,7 @@ var BaseCrudDialect = class {
|
|
|
795
854
|
if (key === "mode" || consumedKeys.includes(key)) {
|
|
796
855
|
continue;
|
|
797
856
|
}
|
|
798
|
-
const condition = (0, import_ts_pattern2.match)(key).with("contains", () => mode === "insensitive" ? eb(fieldRef, "ilike",
|
|
857
|
+
const condition = (0, import_ts_pattern2.match)(key).with("contains", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely2.sql.val(`%${value}%`)) : eb(fieldRef, "like", import_kysely2.sql.val(`%${value}%`))).with("startsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely2.sql.val(`${value}%`)) : eb(fieldRef, "like", import_kysely2.sql.val(`${value}%`))).with("endsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely2.sql.val(`%${value}`)) : eb(fieldRef, "like", import_kysely2.sql.val(`%${value}`))).otherwise(() => {
|
|
799
858
|
throw new QueryError(`Invalid string filter key: ${key}`);
|
|
800
859
|
});
|
|
801
860
|
if (condition) {
|
|
@@ -807,16 +866,16 @@ var BaseCrudDialect = class {
|
|
|
807
866
|
}
|
|
808
867
|
prepStringCasing(eb, value, mode) {
|
|
809
868
|
if (!mode || mode === "default") {
|
|
810
|
-
return value === null ? value :
|
|
869
|
+
return value === null ? value : import_kysely2.sql.val(value);
|
|
811
870
|
}
|
|
812
871
|
if (typeof value === "string") {
|
|
813
872
|
return eb.fn("lower", [
|
|
814
|
-
|
|
873
|
+
import_kysely2.sql.val(value)
|
|
815
874
|
]);
|
|
816
875
|
} else if (Array.isArray(value)) {
|
|
817
876
|
return value.map((v) => this.prepStringCasing(eb, v, mode));
|
|
818
877
|
} else {
|
|
819
|
-
return value === null ? null :
|
|
878
|
+
return value === null ? null : import_kysely2.sql.val(value);
|
|
820
879
|
}
|
|
821
880
|
}
|
|
822
881
|
buildNumberFilter(eb, fieldRef, type, payload) {
|
|
@@ -873,19 +932,19 @@ var BaseCrudDialect = class {
|
|
|
873
932
|
"_min",
|
|
874
933
|
"_max"
|
|
875
934
|
].includes(field)) {
|
|
876
|
-
(0,
|
|
935
|
+
(0, import_common_helpers2.invariant)(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
877
936
|
for (const [k, v] of Object.entries(value)) {
|
|
878
|
-
(0,
|
|
879
|
-
result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field),
|
|
937
|
+
(0, import_common_helpers2.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
938
|
+
result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), import_kysely2.sql.raw(this.negateSort(v, negated)));
|
|
880
939
|
}
|
|
881
940
|
continue;
|
|
882
941
|
}
|
|
883
942
|
switch (field) {
|
|
884
943
|
case "_count": {
|
|
885
|
-
(0,
|
|
944
|
+
(0, import_common_helpers2.invariant)(value && typeof value === "object", 'invalid orderBy value for field "_count"');
|
|
886
945
|
for (const [k, v] of Object.entries(value)) {
|
|
887
|
-
(0,
|
|
888
|
-
result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)),
|
|
946
|
+
(0, import_common_helpers2.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
947
|
+
result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), import_kysely2.sql.raw(this.negateSort(v, negated)));
|
|
889
948
|
}
|
|
890
949
|
continue;
|
|
891
950
|
}
|
|
@@ -894,11 +953,11 @@ var BaseCrudDialect = class {
|
|
|
894
953
|
}
|
|
895
954
|
const fieldDef = requireField(this.schema, model, field);
|
|
896
955
|
if (!fieldDef.relation) {
|
|
897
|
-
const fieldRef = this.fieldRef(model, field, (0,
|
|
956
|
+
const fieldRef = this.fieldRef(model, field, (0, import_kysely2.expressionBuilder)(), modelAlias);
|
|
898
957
|
if (value === "asc" || value === "desc") {
|
|
899
958
|
result = result.orderBy(fieldRef, this.negateSort(value, negated));
|
|
900
959
|
} else if (value && typeof value === "object" && "nulls" in value && "sort" in value && (value.sort === "asc" || value.sort === "desc") && (value.nulls === "first" || value.nulls === "last")) {
|
|
901
|
-
result = result.orderBy(fieldRef,
|
|
960
|
+
result = result.orderBy(fieldRef, import_kysely2.sql.raw(`${this.negateSort(value.sort, negated)} nulls ${value.nulls}`));
|
|
902
961
|
}
|
|
903
962
|
} else {
|
|
904
963
|
const relationModel = fieldDef.type;
|
|
@@ -907,13 +966,13 @@ var BaseCrudDialect = class {
|
|
|
907
966
|
throw new QueryError(`invalid orderBy value for field "${field}"`);
|
|
908
967
|
}
|
|
909
968
|
if ("_count" in value) {
|
|
910
|
-
(0,
|
|
969
|
+
(0, import_common_helpers2.invariant)(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
|
|
911
970
|
const sort = this.negateSort(value._count, negated);
|
|
912
971
|
result = result.orderBy((eb) => {
|
|
913
972
|
const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
|
|
914
973
|
let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
|
|
915
974
|
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
|
|
916
|
-
subQuery = subQuery.where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(
|
|
975
|
+
subQuery = subQuery.where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right)))));
|
|
917
976
|
subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
|
|
918
977
|
return subQuery;
|
|
919
978
|
}, sort);
|
|
@@ -921,7 +980,7 @@ var BaseCrudDialect = class {
|
|
|
921
980
|
} else {
|
|
922
981
|
result = result.leftJoin(relationModel, (join) => {
|
|
923
982
|
const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, relationModel);
|
|
924
|
-
return join.on((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(
|
|
983
|
+
return join.on((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right)))));
|
|
925
984
|
});
|
|
926
985
|
result = this.buildOrderBy(result, fieldDef.type, relationModel, value, false, negated);
|
|
927
986
|
}
|
|
@@ -973,13 +1032,13 @@ var BaseCrudDialect = class {
|
|
|
973
1032
|
if (fieldDef.computed) {
|
|
974
1033
|
return query.select((eb) => this.fieldRef(model, field, eb, modelAlias).as(field));
|
|
975
1034
|
} else if (!fieldDef.originModel) {
|
|
976
|
-
return query.select(
|
|
1035
|
+
return query.select(import_kysely2.sql.ref(`${modelAlias}.${field}`).as(field));
|
|
977
1036
|
} else {
|
|
978
1037
|
return this.buildSelectField(query, fieldDef.originModel, fieldDef.originModel, field);
|
|
979
1038
|
}
|
|
980
1039
|
}
|
|
981
1040
|
buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
|
|
982
|
-
const idFields =
|
|
1041
|
+
const idFields = requireIdFields(this.schema, thisModel);
|
|
983
1042
|
query = query.leftJoin(otherModelAlias, (qb) => {
|
|
984
1043
|
for (const idField of idFields) {
|
|
985
1044
|
qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
|
|
@@ -1001,10 +1060,16 @@ var BaseCrudDialect = class {
|
|
|
1001
1060
|
for (const [field, value] of Object.entries(selections.select)) {
|
|
1002
1061
|
const fieldDef = requireField(this.schema, model, field);
|
|
1003
1062
|
const fieldModel = fieldDef.type;
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
fieldCountQuery =
|
|
1063
|
+
let fieldCountQuery;
|
|
1064
|
+
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
1065
|
+
if (m2m) {
|
|
1066
|
+
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}`));
|
|
1067
|
+
} else {
|
|
1068
|
+
fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
|
|
1069
|
+
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
|
|
1070
|
+
for (const [left, right] of joinPairs) {
|
|
1071
|
+
fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
|
|
1072
|
+
}
|
|
1008
1073
|
}
|
|
1009
1074
|
if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
|
|
1010
1075
|
const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
|
|
@@ -1084,6 +1149,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1084
1149
|
static {
|
|
1085
1150
|
__name(this, "PostgresCrudDialect");
|
|
1086
1151
|
}
|
|
1152
|
+
constructor(schema, options) {
|
|
1153
|
+
super(schema, options);
|
|
1154
|
+
}
|
|
1087
1155
|
get provider() {
|
|
1088
1156
|
return "postgresql";
|
|
1089
1157
|
}
|
|
@@ -1098,9 +1166,41 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1098
1166
|
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
1099
1167
|
}
|
|
1100
1168
|
} else {
|
|
1101
|
-
return (0, import_ts_pattern3.match)(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
1169
|
+
return (0, import_ts_pattern3.match)(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);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
transformOutput(value, type) {
|
|
1173
|
+
if (value === null || value === void 0) {
|
|
1174
|
+
return value;
|
|
1175
|
+
}
|
|
1176
|
+
return (0, import_ts_pattern3.match)(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));
|
|
1177
|
+
}
|
|
1178
|
+
transformOutputBigInt(value) {
|
|
1179
|
+
if (typeof value === "bigint") {
|
|
1180
|
+
return value;
|
|
1181
|
+
}
|
|
1182
|
+
(0, import_common_helpers3.invariant)(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1183
|
+
return BigInt(value);
|
|
1184
|
+
}
|
|
1185
|
+
transformDecimal(value) {
|
|
1186
|
+
if (value instanceof import_decimal.default) {
|
|
1187
|
+
return value;
|
|
1188
|
+
}
|
|
1189
|
+
(0, import_common_helpers3.invariant)(typeof value === "string" || typeof value === "number" || value instanceof import_decimal.default, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1190
|
+
return new import_decimal.default(value);
|
|
1191
|
+
}
|
|
1192
|
+
transformOutputDate(value) {
|
|
1193
|
+
if (typeof value === "string") {
|
|
1194
|
+
return new Date(value);
|
|
1195
|
+
} else if (value instanceof Date && this.options.fixPostgresTimezone !== false) {
|
|
1196
|
+
return new Date(value.getTime() - value.getTimezoneOffset() * 60 * 1e3);
|
|
1197
|
+
} else {
|
|
1198
|
+
return value;
|
|
1102
1199
|
}
|
|
1103
1200
|
}
|
|
1201
|
+
transformOutputBytes(value) {
|
|
1202
|
+
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1203
|
+
}
|
|
1104
1204
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1105
1205
|
const relationResultName = `${parentAlias}$${relationField}`;
|
|
1106
1206
|
const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload, relationResultName);
|
|
@@ -1131,14 +1231,14 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1131
1231
|
buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
|
|
1132
1232
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1133
1233
|
if (m2m) {
|
|
1134
|
-
const parentIds =
|
|
1135
|
-
const relationIds =
|
|
1136
|
-
(0,
|
|
1137
|
-
(0,
|
|
1234
|
+
const parentIds = requireIdFields(this.schema, model);
|
|
1235
|
+
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1236
|
+
(0, import_common_helpers3.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1237
|
+
(0, import_common_helpers3.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1138
1238
|
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}`)));
|
|
1139
1239
|
} else {
|
|
1140
1240
|
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
|
|
1141
|
-
query = query.where((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(
|
|
1241
|
+
query = query.where((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely3.sql.ref(left), "=", import_kysely3.sql.ref(right)))));
|
|
1142
1242
|
}
|
|
1143
1243
|
return query;
|
|
1144
1244
|
}
|
|
@@ -1146,9 +1246,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1146
1246
|
qb = qb.select((eb) => {
|
|
1147
1247
|
const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
|
|
1148
1248
|
if (relationFieldDef.array) {
|
|
1149
|
-
return eb.fn.coalesce(
|
|
1249
|
+
return eb.fn.coalesce(import_kysely3.sql`jsonb_agg(jsonb_build_object(${import_kysely3.sql.join(objArgs)}))`, import_kysely3.sql`'[]'::jsonb`).as("$data");
|
|
1150
1250
|
} else {
|
|
1151
|
-
return
|
|
1251
|
+
return import_kysely3.sql`jsonb_build_object(${import_kysely3.sql.join(objArgs)})`.as("$data");
|
|
1152
1252
|
}
|
|
1153
1253
|
});
|
|
1154
1254
|
return qb;
|
|
@@ -1159,13 +1259,13 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1159
1259
|
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1160
1260
|
if (descendantModels.length > 0) {
|
|
1161
1261
|
objArgs.push(...descendantModels.map((subModel) => [
|
|
1162
|
-
|
|
1262
|
+
import_kysely3.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1163
1263
|
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1164
1264
|
]).flatMap((v) => v));
|
|
1165
1265
|
}
|
|
1166
1266
|
if (payload === true || !payload.select) {
|
|
1167
1267
|
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
1168
|
-
|
|
1268
|
+
import_kysely3.sql.lit(field),
|
|
1169
1269
|
this.fieldRef(relationModel, field, eb, relationModelAlias, false)
|
|
1170
1270
|
]).flatMap((v) => v));
|
|
1171
1271
|
} else if (payload.select) {
|
|
@@ -1173,14 +1273,14 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1173
1273
|
if (field === "_count") {
|
|
1174
1274
|
const subJson = this.buildCountJson(relationModel, eb, relationModelAlias, value);
|
|
1175
1275
|
return [
|
|
1176
|
-
|
|
1276
|
+
import_kysely3.sql.lit(field),
|
|
1177
1277
|
subJson
|
|
1178
1278
|
];
|
|
1179
1279
|
} else {
|
|
1180
1280
|
const fieldDef = requireField(this.schema, relationModel, field);
|
|
1181
1281
|
const fieldValue = fieldDef.relation ? eb.ref(`${parentResultName}$${field}.$data`) : this.fieldRef(relationModel, field, eb, relationModelAlias, false);
|
|
1182
1282
|
return [
|
|
1183
|
-
|
|
1283
|
+
import_kysely3.sql.lit(field),
|
|
1184
1284
|
fieldValue
|
|
1185
1285
|
];
|
|
1186
1286
|
}
|
|
@@ -1188,7 +1288,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1188
1288
|
}
|
|
1189
1289
|
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
1190
1290
|
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
|
|
1191
|
-
|
|
1291
|
+
import_kysely3.sql.lit(field),
|
|
1192
1292
|
// reference the synthesized JSON field
|
|
1193
1293
|
eb.ref(`${parentResultName}$${field}.$data`)
|
|
1194
1294
|
]).flatMap((v) => v));
|
|
@@ -1218,7 +1318,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1218
1318
|
}
|
|
1219
1319
|
buildJsonObject(eb, value) {
|
|
1220
1320
|
return eb.fn("jsonb_build_object", Object.entries(value).flatMap(([key, value2]) => [
|
|
1221
|
-
|
|
1321
|
+
import_kysely3.sql.lit(key),
|
|
1222
1322
|
value2
|
|
1223
1323
|
]));
|
|
1224
1324
|
}
|
|
@@ -1246,11 +1346,33 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1246
1346
|
get supportInsertWithDefault() {
|
|
1247
1347
|
return true;
|
|
1248
1348
|
}
|
|
1349
|
+
getFieldSqlType(fieldDef) {
|
|
1350
|
+
if (fieldDef.relation) {
|
|
1351
|
+
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1352
|
+
}
|
|
1353
|
+
let result;
|
|
1354
|
+
if (this.schema.enums?.[fieldDef.type]) {
|
|
1355
|
+
result = "text";
|
|
1356
|
+
} else {
|
|
1357
|
+
result = (0, import_ts_pattern3.match)(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");
|
|
1358
|
+
}
|
|
1359
|
+
if (fieldDef.array) {
|
|
1360
|
+
result += "[]";
|
|
1361
|
+
}
|
|
1362
|
+
return result;
|
|
1363
|
+
}
|
|
1364
|
+
getStringCasingBehavior() {
|
|
1365
|
+
return {
|
|
1366
|
+
supportsILike: true,
|
|
1367
|
+
likeCaseSensitive: true
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1249
1370
|
};
|
|
1250
1371
|
|
|
1251
1372
|
// src/client/crud/dialects/sqlite.ts
|
|
1252
|
-
var
|
|
1253
|
-
var
|
|
1373
|
+
var import_common_helpers4 = require("@zenstackhq/common-helpers");
|
|
1374
|
+
var import_decimal2 = __toESM(require("decimal.js"), 1);
|
|
1375
|
+
var import_kysely4 = require("kysely");
|
|
1254
1376
|
var import_ts_pattern4 = require("ts-pattern");
|
|
1255
1377
|
var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
1256
1378
|
static {
|
|
@@ -1269,9 +1391,57 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1269
1391
|
if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1270
1392
|
return JSON.stringify(value);
|
|
1271
1393
|
} else {
|
|
1272
|
-
return (0, import_ts_pattern4.match)(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
1394
|
+
return (0, import_ts_pattern4.match)(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);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
transformOutput(value, type) {
|
|
1399
|
+
if (value === null || value === void 0) {
|
|
1400
|
+
return value;
|
|
1401
|
+
} else if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1402
|
+
return this.transformOutputJson(value);
|
|
1403
|
+
} else {
|
|
1404
|
+
return (0, import_ts_pattern4.match)(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));
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
transformOutputDecimal(value) {
|
|
1408
|
+
if (value instanceof import_decimal2.default) {
|
|
1409
|
+
return value;
|
|
1410
|
+
}
|
|
1411
|
+
(0, import_common_helpers4.invariant)(typeof value === "string" || typeof value === "number" || value instanceof import_decimal2.default, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1412
|
+
return new import_decimal2.default(value);
|
|
1413
|
+
}
|
|
1414
|
+
transformOutputBigInt(value) {
|
|
1415
|
+
if (typeof value === "bigint") {
|
|
1416
|
+
return value;
|
|
1417
|
+
}
|
|
1418
|
+
(0, import_common_helpers4.invariant)(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1419
|
+
return BigInt(value);
|
|
1420
|
+
}
|
|
1421
|
+
transformOutputBoolean(value) {
|
|
1422
|
+
return !!value;
|
|
1423
|
+
}
|
|
1424
|
+
transformOutputDate(value) {
|
|
1425
|
+
if (typeof value === "number") {
|
|
1426
|
+
return new Date(value);
|
|
1427
|
+
} else if (typeof value === "string") {
|
|
1428
|
+
return new Date(value);
|
|
1429
|
+
} else {
|
|
1430
|
+
return value;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
transformOutputBytes(value) {
|
|
1434
|
+
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1435
|
+
}
|
|
1436
|
+
transformOutputJson(value) {
|
|
1437
|
+
if (typeof value === "string") {
|
|
1438
|
+
try {
|
|
1439
|
+
return JSON.parse(value);
|
|
1440
|
+
} catch (e) {
|
|
1441
|
+
throw new QueryError("Invalid JSON returned", e);
|
|
1273
1442
|
}
|
|
1274
1443
|
}
|
|
1444
|
+
return value;
|
|
1275
1445
|
}
|
|
1276
1446
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1277
1447
|
return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
|
|
@@ -1298,13 +1468,13 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1298
1468
|
const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
|
|
1299
1469
|
if (descendantModels.length > 0) {
|
|
1300
1470
|
objArgs.push(...descendantModels.map((subModel) => [
|
|
1301
|
-
|
|
1471
|
+
import_kysely4.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
|
|
1302
1472
|
eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
|
|
1303
1473
|
]).flatMap((v) => v));
|
|
1304
1474
|
}
|
|
1305
1475
|
if (payload === true || !payload.select) {
|
|
1306
1476
|
objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
|
|
1307
|
-
|
|
1477
|
+
import_kysely4.sql.lit(field),
|
|
1308
1478
|
this.fieldRef(relationModel, field, eb, subQueryName, false)
|
|
1309
1479
|
]).flatMap((v) => v));
|
|
1310
1480
|
} else if (payload.select) {
|
|
@@ -1312,7 +1482,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1312
1482
|
if (field === "_count") {
|
|
1313
1483
|
const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
|
|
1314
1484
|
return [
|
|
1315
|
-
|
|
1485
|
+
import_kysely4.sql.lit(field),
|
|
1316
1486
|
subJson
|
|
1317
1487
|
];
|
|
1318
1488
|
} else {
|
|
@@ -1320,12 +1490,12 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1320
1490
|
if (fieldDef.relation) {
|
|
1321
1491
|
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
1322
1492
|
return [
|
|
1323
|
-
|
|
1493
|
+
import_kysely4.sql.lit(field),
|
|
1324
1494
|
subJson
|
|
1325
1495
|
];
|
|
1326
1496
|
} else {
|
|
1327
1497
|
return [
|
|
1328
|
-
|
|
1498
|
+
import_kysely4.sql.lit(field),
|
|
1329
1499
|
this.fieldRef(relationModel, field, eb, subQueryName, false)
|
|
1330
1500
|
];
|
|
1331
1501
|
}
|
|
@@ -1336,15 +1506,15 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1336
1506
|
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
|
|
1337
1507
|
const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
|
|
1338
1508
|
return [
|
|
1339
|
-
|
|
1509
|
+
import_kysely4.sql.lit(field),
|
|
1340
1510
|
subJson
|
|
1341
1511
|
];
|
|
1342
1512
|
}).flatMap((v) => v));
|
|
1343
1513
|
}
|
|
1344
1514
|
if (relationFieldDef.array) {
|
|
1345
|
-
return eb.fn.coalesce(
|
|
1515
|
+
return eb.fn.coalesce(import_kysely4.sql`json_group_array(json_object(${import_kysely4.sql.join(objArgs)}))`, import_kysely4.sql`json_array()`).as("$data");
|
|
1346
1516
|
} else {
|
|
1347
|
-
return
|
|
1517
|
+
return import_kysely4.sql`json_object(${import_kysely4.sql.join(objArgs)})`.as("$data");
|
|
1348
1518
|
}
|
|
1349
1519
|
});
|
|
1350
1520
|
return tbl;
|
|
@@ -1354,10 +1524,10 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1354
1524
|
const relationModel = fieldDef.type;
|
|
1355
1525
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1356
1526
|
if (m2m) {
|
|
1357
|
-
const parentIds =
|
|
1358
|
-
const relationIds =
|
|
1359
|
-
(0,
|
|
1360
|
-
(0,
|
|
1527
|
+
const parentIds = requireIdFields(this.schema, model);
|
|
1528
|
+
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1529
|
+
(0, import_common_helpers4.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1530
|
+
(0, import_common_helpers4.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1361
1531
|
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}`)));
|
|
1362
1532
|
} else {
|
|
1363
1533
|
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
@@ -1385,7 +1555,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1385
1555
|
}
|
|
1386
1556
|
buildJsonObject(eb, value) {
|
|
1387
1557
|
return eb.fn("json_object", Object.entries(value).flatMap(([key, value2]) => [
|
|
1388
|
-
|
|
1558
|
+
import_kysely4.sql.lit(key),
|
|
1389
1559
|
value2
|
|
1390
1560
|
]));
|
|
1391
1561
|
}
|
|
@@ -1409,6 +1579,24 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1409
1579
|
get supportInsertWithDefault() {
|
|
1410
1580
|
return false;
|
|
1411
1581
|
}
|
|
1582
|
+
getFieldSqlType(fieldDef) {
|
|
1583
|
+
if (fieldDef.relation) {
|
|
1584
|
+
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1585
|
+
}
|
|
1586
|
+
if (fieldDef.array) {
|
|
1587
|
+
throw new QueryError("SQLite does not support scalar list type");
|
|
1588
|
+
}
|
|
1589
|
+
if (this.schema.enums?.[fieldDef.type]) {
|
|
1590
|
+
return "text";
|
|
1591
|
+
}
|
|
1592
|
+
return (0, import_ts_pattern4.match)(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");
|
|
1593
|
+
}
|
|
1594
|
+
getStringCasingBehavior() {
|
|
1595
|
+
return {
|
|
1596
|
+
supportsILike: false,
|
|
1597
|
+
likeCaseSensitive: false
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1412
1600
|
};
|
|
1413
1601
|
|
|
1414
1602
|
// src/client/crud/dialects/index.ts
|
|
@@ -1418,8 +1606,8 @@ function getCrudDialect(schema, options) {
|
|
|
1418
1606
|
__name(getCrudDialect, "getCrudDialect");
|
|
1419
1607
|
|
|
1420
1608
|
// src/utils/default-operation-node-visitor.ts
|
|
1421
|
-
var
|
|
1422
|
-
var DefaultOperationNodeVisitor = class extends
|
|
1609
|
+
var import_kysely5 = require("kysely");
|
|
1610
|
+
var DefaultOperationNodeVisitor = class extends import_kysely5.OperationNodeVisitor {
|
|
1423
1611
|
static {
|
|
1424
1612
|
__name(this, "DefaultOperationNodeVisitor");
|
|
1425
1613
|
}
|
|
@@ -1736,12 +1924,12 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
|
|
|
1736
1924
|
};
|
|
1737
1925
|
|
|
1738
1926
|
// src/plugins/policy/expression-transformer.ts
|
|
1739
|
-
var
|
|
1740
|
-
var
|
|
1927
|
+
var import_common_helpers6 = require("@zenstackhq/common-helpers");
|
|
1928
|
+
var import_kysely7 = require("kysely");
|
|
1741
1929
|
var import_ts_pattern7 = require("ts-pattern");
|
|
1742
1930
|
|
|
1743
1931
|
// src/plugins/policy/expression-evaluator.ts
|
|
1744
|
-
var
|
|
1932
|
+
var import_common_helpers5 = require("@zenstackhq/common-helpers");
|
|
1745
1933
|
var import_ts_pattern6 = require("ts-pattern");
|
|
1746
1934
|
var ExpressionEvaluator = class {
|
|
1747
1935
|
static {
|
|
@@ -1785,18 +1973,18 @@ var ExpressionEvaluator = class {
|
|
|
1785
1973
|
const right = this.evaluate(expr2.right, context);
|
|
1786
1974
|
return (0, import_ts_pattern6.match)(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", () => {
|
|
1787
1975
|
const _right = right ?? [];
|
|
1788
|
-
(0,
|
|
1976
|
+
(0, import_common_helpers5.invariant)(Array.isArray(_right), 'expected array for "in" operator');
|
|
1789
1977
|
return _right.includes(left);
|
|
1790
1978
|
}).exhaustive();
|
|
1791
1979
|
}
|
|
1792
1980
|
evaluateCollectionPredicate(expr2, context) {
|
|
1793
1981
|
const op = expr2.op;
|
|
1794
|
-
(0,
|
|
1982
|
+
(0, import_common_helpers5.invariant)(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
|
|
1795
1983
|
const left = this.evaluate(expr2.left, context);
|
|
1796
1984
|
if (!left) {
|
|
1797
1985
|
return false;
|
|
1798
1986
|
}
|
|
1799
|
-
(0,
|
|
1987
|
+
(0, import_common_helpers5.invariant)(Array.isArray(left), "expected array");
|
|
1800
1988
|
return (0, import_ts_pattern6.match)(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
|
|
1801
1989
|
...context,
|
|
1802
1990
|
thisValue: item
|
|
@@ -1811,21 +1999,21 @@ var ExpressionEvaluator = class {
|
|
|
1811
1999
|
};
|
|
1812
2000
|
|
|
1813
2001
|
// src/plugins/policy/utils.ts
|
|
1814
|
-
var
|
|
2002
|
+
var import_kysely6 = require("kysely");
|
|
1815
2003
|
function trueNode(dialect) {
|
|
1816
|
-
return
|
|
2004
|
+
return import_kysely6.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
|
|
1817
2005
|
}
|
|
1818
2006
|
__name(trueNode, "trueNode");
|
|
1819
2007
|
function falseNode(dialect) {
|
|
1820
|
-
return
|
|
2008
|
+
return import_kysely6.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
|
|
1821
2009
|
}
|
|
1822
2010
|
__name(falseNode, "falseNode");
|
|
1823
2011
|
function isTrueNode(node) {
|
|
1824
|
-
return
|
|
2012
|
+
return import_kysely6.ValueNode.is(node) && (node.value === true || node.value === 1);
|
|
1825
2013
|
}
|
|
1826
2014
|
__name(isTrueNode, "isTrueNode");
|
|
1827
2015
|
function isFalseNode(node) {
|
|
1828
|
-
return
|
|
2016
|
+
return import_kysely6.ValueNode.is(node) && (node.value === false || node.value === 0);
|
|
1829
2017
|
}
|
|
1830
2018
|
__name(isFalseNode, "isFalseNode");
|
|
1831
2019
|
function conjunction(dialect, nodes) {
|
|
@@ -1842,7 +2030,7 @@ function conjunction(dialect, nodes) {
|
|
|
1842
2030
|
if (items.length === 0) {
|
|
1843
2031
|
return trueNode(dialect);
|
|
1844
2032
|
}
|
|
1845
|
-
return items.reduce((acc, node) =>
|
|
2033
|
+
return items.reduce((acc, node) => import_kysely6.AndNode.create(wrapParensIf(acc, import_kysely6.OrNode.is), wrapParensIf(node, import_kysely6.OrNode.is)));
|
|
1846
2034
|
}
|
|
1847
2035
|
__name(conjunction, "conjunction");
|
|
1848
2036
|
function disjunction(dialect, nodes) {
|
|
@@ -1859,7 +2047,7 @@ function disjunction(dialect, nodes) {
|
|
|
1859
2047
|
if (items.length === 0) {
|
|
1860
2048
|
return falseNode(dialect);
|
|
1861
2049
|
}
|
|
1862
|
-
return items.reduce((acc, node) =>
|
|
2050
|
+
return items.reduce((acc, node) => import_kysely6.OrNode.create(wrapParensIf(acc, import_kysely6.AndNode.is), wrapParensIf(node, import_kysely6.AndNode.is)));
|
|
1863
2051
|
}
|
|
1864
2052
|
__name(disjunction, "disjunction");
|
|
1865
2053
|
function logicalNot(dialect, node) {
|
|
@@ -1869,11 +2057,11 @@ function logicalNot(dialect, node) {
|
|
|
1869
2057
|
if (isFalseNode(node)) {
|
|
1870
2058
|
return trueNode(dialect);
|
|
1871
2059
|
}
|
|
1872
|
-
return
|
|
2060
|
+
return import_kysely6.UnaryOperationNode.create(import_kysely6.OperatorNode.create("not"), wrapParensIf(node, (n) => import_kysely6.AndNode.is(n) || import_kysely6.OrNode.is(n)));
|
|
1873
2061
|
}
|
|
1874
2062
|
__name(logicalNot, "logicalNot");
|
|
1875
2063
|
function wrapParensIf(node, predicate) {
|
|
1876
|
-
return predicate(node) ?
|
|
2064
|
+
return predicate(node) ? import_kysely6.ParensNode.create(node) : node;
|
|
1877
2065
|
}
|
|
1878
2066
|
__name(wrapParensIf, "wrapParensIf");
|
|
1879
2067
|
function buildIsFalse(node, dialect) {
|
|
@@ -1882,13 +2070,13 @@ function buildIsFalse(node, dialect) {
|
|
|
1882
2070
|
} else if (isTrueNode(node)) {
|
|
1883
2071
|
return falseNode(dialect);
|
|
1884
2072
|
}
|
|
1885
|
-
return
|
|
2073
|
+
return import_kysely6.BinaryOperationNode.create(
|
|
1886
2074
|
// coalesce so null is treated as false
|
|
1887
|
-
|
|
2075
|
+
import_kysely6.FunctionNode.create("coalesce", [
|
|
1888
2076
|
node,
|
|
1889
2077
|
falseNode(dialect)
|
|
1890
2078
|
]),
|
|
1891
|
-
|
|
2079
|
+
import_kysely6.OperatorNode.create("="),
|
|
1892
2080
|
falseNode(dialect)
|
|
1893
2081
|
);
|
|
1894
2082
|
}
|
|
@@ -1897,11 +2085,11 @@ function getTableName(node) {
|
|
|
1897
2085
|
if (!node) {
|
|
1898
2086
|
return node;
|
|
1899
2087
|
}
|
|
1900
|
-
if (
|
|
2088
|
+
if (import_kysely6.TableNode.is(node)) {
|
|
1901
2089
|
return node.table.identifier.name;
|
|
1902
|
-
} else if (
|
|
2090
|
+
} else if (import_kysely6.AliasNode.is(node)) {
|
|
1903
2091
|
return getTableName(node.node);
|
|
1904
|
-
} else if (
|
|
2092
|
+
} else if (import_kysely6.ReferenceNode.is(node) && node.table) {
|
|
1905
2093
|
return getTableName(node.table);
|
|
1906
2094
|
}
|
|
1907
2095
|
return void 0;
|
|
@@ -1934,16 +2122,21 @@ var ExpressionTransformer = class {
|
|
|
1934
2122
|
static {
|
|
1935
2123
|
__name(this, "ExpressionTransformer");
|
|
1936
2124
|
}
|
|
1937
|
-
|
|
1938
|
-
clientOptions;
|
|
1939
|
-
auth;
|
|
2125
|
+
client;
|
|
1940
2126
|
dialect;
|
|
1941
|
-
constructor(
|
|
1942
|
-
this.
|
|
1943
|
-
this.clientOptions = clientOptions;
|
|
1944
|
-
this.auth = auth;
|
|
2127
|
+
constructor(client) {
|
|
2128
|
+
this.client = client;
|
|
1945
2129
|
this.dialect = getCrudDialect(this.schema, this.clientOptions);
|
|
1946
2130
|
}
|
|
2131
|
+
get schema() {
|
|
2132
|
+
return this.client.$schema;
|
|
2133
|
+
}
|
|
2134
|
+
get clientOptions() {
|
|
2135
|
+
return this.client.$options;
|
|
2136
|
+
}
|
|
2137
|
+
get auth() {
|
|
2138
|
+
return this.client.$auth;
|
|
2139
|
+
}
|
|
1947
2140
|
get authType() {
|
|
1948
2141
|
if (!this.schema.authType) {
|
|
1949
2142
|
throw new InternalError('Schema does not have an "authType" specified');
|
|
@@ -1961,7 +2154,7 @@ var ExpressionTransformer = class {
|
|
|
1961
2154
|
return this.transformValue(expr2.value, typeof expr2.value === "string" ? "String" : typeof expr2.value === "boolean" ? "Boolean" : "Int");
|
|
1962
2155
|
}
|
|
1963
2156
|
_array(expr2, context) {
|
|
1964
|
-
return
|
|
2157
|
+
return import_kysely7.ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
|
|
1965
2158
|
}
|
|
1966
2159
|
_field(expr2, context) {
|
|
1967
2160
|
const fieldDef = requireField(this.schema, context.model, expr2.field);
|
|
@@ -1981,18 +2174,18 @@ var ExpressionTransformer = class {
|
|
|
1981
2174
|
}
|
|
1982
2175
|
mergeWhere(where, memberFilter) {
|
|
1983
2176
|
if (!where) {
|
|
1984
|
-
return
|
|
2177
|
+
return import_kysely7.WhereNode.create(memberFilter ?? trueNode(this.dialect));
|
|
1985
2178
|
}
|
|
1986
2179
|
if (!memberFilter) {
|
|
1987
2180
|
return where;
|
|
1988
2181
|
}
|
|
1989
|
-
return
|
|
2182
|
+
return import_kysely7.WhereNode.create(conjunction(this.dialect, [
|
|
1990
2183
|
where.where,
|
|
1991
2184
|
memberFilter
|
|
1992
2185
|
]));
|
|
1993
2186
|
}
|
|
1994
2187
|
_null() {
|
|
1995
|
-
return
|
|
2188
|
+
return import_kysely7.ValueNode.createImmediate(null);
|
|
1996
2189
|
}
|
|
1997
2190
|
_binary(expr2, context) {
|
|
1998
2191
|
if (expr2.op === "&&") {
|
|
@@ -2013,45 +2206,82 @@ var ExpressionTransformer = class {
|
|
|
2013
2206
|
if (op === "?" || op === "!" || op === "^") {
|
|
2014
2207
|
return this.transformCollectionPredicate(expr2, context);
|
|
2015
2208
|
}
|
|
2016
|
-
const
|
|
2017
|
-
const
|
|
2209
|
+
const { normalizedLeft, normalizedRight } = this.normalizeBinaryOperationOperands(expr2, context);
|
|
2210
|
+
const left = this.transform(normalizedLeft, context);
|
|
2211
|
+
const right = this.transform(normalizedRight, context);
|
|
2018
2212
|
if (op === "in") {
|
|
2019
2213
|
if (this.isNullNode(left)) {
|
|
2020
2214
|
return this.transformValue(false, "Boolean");
|
|
2021
2215
|
} else {
|
|
2022
|
-
if (
|
|
2023
|
-
return
|
|
2216
|
+
if (import_kysely7.ValueListNode.is(right)) {
|
|
2217
|
+
return import_kysely7.BinaryOperationNode.create(left, import_kysely7.OperatorNode.create("in"), right);
|
|
2024
2218
|
} else {
|
|
2025
|
-
return
|
|
2219
|
+
return import_kysely7.BinaryOperationNode.create(left, import_kysely7.OperatorNode.create("="), import_kysely7.FunctionNode.create("any", [
|
|
2026
2220
|
right
|
|
2027
2221
|
]));
|
|
2028
2222
|
}
|
|
2029
2223
|
}
|
|
2030
2224
|
}
|
|
2031
2225
|
if (this.isNullNode(right)) {
|
|
2032
|
-
return
|
|
2226
|
+
return this.transformNullCheck(left, expr2.op);
|
|
2033
2227
|
} else if (this.isNullNode(left)) {
|
|
2034
|
-
return
|
|
2228
|
+
return this.transformNullCheck(right, expr2.op);
|
|
2229
|
+
} else {
|
|
2230
|
+
return import_kysely7.BinaryOperationNode.create(left, this.transformOperator(op), right);
|
|
2035
2231
|
}
|
|
2036
|
-
|
|
2232
|
+
}
|
|
2233
|
+
transformNullCheck(expr2, operator) {
|
|
2234
|
+
(0, import_common_helpers6.invariant)(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
|
|
2235
|
+
if (import_kysely7.ValueNode.is(expr2)) {
|
|
2236
|
+
if (expr2.value === null) {
|
|
2237
|
+
return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
|
|
2238
|
+
} else {
|
|
2239
|
+
return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
|
|
2240
|
+
}
|
|
2241
|
+
} else {
|
|
2242
|
+
return operator === "==" ? import_kysely7.BinaryOperationNode.create(expr2, import_kysely7.OperatorNode.create("is"), import_kysely7.ValueNode.createImmediate(null)) : import_kysely7.BinaryOperationNode.create(expr2, import_kysely7.OperatorNode.create("is not"), import_kysely7.ValueNode.createImmediate(null));
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
normalizeBinaryOperationOperands(expr2, context) {
|
|
2246
|
+
let normalizedLeft = expr2.left;
|
|
2247
|
+
if (this.isRelationField(expr2.left, context.model)) {
|
|
2248
|
+
(0, import_common_helpers6.invariant)(ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
|
|
2249
|
+
const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2250
|
+
(0, import_common_helpers6.invariant)(leftRelDef, "failed to get relation field definition");
|
|
2251
|
+
const idFields = requireIdFields(this.schema, leftRelDef.type);
|
|
2252
|
+
normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
|
|
2253
|
+
}
|
|
2254
|
+
let normalizedRight = expr2.right;
|
|
2255
|
+
if (this.isRelationField(expr2.right, context.model)) {
|
|
2256
|
+
(0, import_common_helpers6.invariant)(ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
|
|
2257
|
+
const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
|
|
2258
|
+
(0, import_common_helpers6.invariant)(rightRelDef, "failed to get relation field definition");
|
|
2259
|
+
const idFields = requireIdFields(this.schema, rightRelDef.type);
|
|
2260
|
+
normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
|
|
2261
|
+
}
|
|
2262
|
+
return {
|
|
2263
|
+
normalizedLeft,
|
|
2264
|
+
normalizedRight
|
|
2265
|
+
};
|
|
2037
2266
|
}
|
|
2038
2267
|
transformCollectionPredicate(expr2, context) {
|
|
2039
|
-
(0,
|
|
2268
|
+
(0, import_common_helpers6.invariant)(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
|
|
2040
2269
|
if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
|
|
2041
2270
|
const value = new ExpressionEvaluator().evaluate(expr2, {
|
|
2042
2271
|
auth: this.auth
|
|
2043
2272
|
});
|
|
2044
2273
|
return this.transformValue(value, "Boolean");
|
|
2045
2274
|
}
|
|
2046
|
-
(0,
|
|
2275
|
+
(0, import_common_helpers6.invariant)(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
|
|
2047
2276
|
let newContextModel;
|
|
2048
|
-
|
|
2049
|
-
|
|
2277
|
+
const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2278
|
+
if (fieldDef) {
|
|
2279
|
+
(0, import_common_helpers6.invariant)(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
|
|
2050
2280
|
newContextModel = fieldDef.type;
|
|
2051
2281
|
} else {
|
|
2052
|
-
(0,
|
|
2053
|
-
const
|
|
2054
|
-
newContextModel =
|
|
2282
|
+
(0, import_common_helpers6.invariant)(ExpressionUtils.isMember(expr2.left) && ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
|
|
2283
|
+
const fieldDef2 = requireField(this.schema, context.model, expr2.left.receiver.field);
|
|
2284
|
+
newContextModel = fieldDef2.type;
|
|
2055
2285
|
for (const member of expr2.left.members) {
|
|
2056
2286
|
const memberDef = requireField(this.schema, newContextModel, member);
|
|
2057
2287
|
newContextModel = memberDef.type;
|
|
@@ -2065,13 +2295,13 @@ var ExpressionTransformer = class {
|
|
|
2065
2295
|
if (expr2.op === "!") {
|
|
2066
2296
|
predicateFilter = logicalNot(this.dialect, predicateFilter);
|
|
2067
2297
|
}
|
|
2068
|
-
const count =
|
|
2069
|
-
|
|
2298
|
+
const count = import_kysely7.FunctionNode.create("count", [
|
|
2299
|
+
import_kysely7.ValueNode.createImmediate(1)
|
|
2070
2300
|
]);
|
|
2071
|
-
const predicateResult = (0, import_ts_pattern7.match)(expr2.op).with("?", () =>
|
|
2301
|
+
const predicateResult = (0, import_ts_pattern7.match)(expr2.op).with("?", () => import_kysely7.BinaryOperationNode.create(count, import_kysely7.OperatorNode.create(">"), import_kysely7.ValueNode.createImmediate(0))).with("!", () => import_kysely7.BinaryOperationNode.create(count, import_kysely7.OperatorNode.create("="), import_kysely7.ValueNode.createImmediate(0))).with("^", () => import_kysely7.BinaryOperationNode.create(count, import_kysely7.OperatorNode.create("="), import_kysely7.ValueNode.createImmediate(0))).exhaustive();
|
|
2072
2302
|
return this.transform(expr2.left, {
|
|
2073
2303
|
...context,
|
|
2074
|
-
memberSelect:
|
|
2304
|
+
memberSelect: import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(predicateResult, import_kysely7.IdentifierNode.create("$t"))),
|
|
2075
2305
|
memberFilter: predicateFilter
|
|
2076
2306
|
});
|
|
2077
2307
|
}
|
|
@@ -2096,12 +2326,10 @@ var ExpressionTransformer = class {
|
|
|
2096
2326
|
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`);
|
|
2097
2327
|
}
|
|
2098
2328
|
const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
|
|
2099
|
-
(0,
|
|
2329
|
+
(0, import_common_helpers6.invariant)(idFields.length > 0, "auth type model must have at least one id field");
|
|
2100
2330
|
const conditions = idFields.map((fieldName) => ExpressionUtils.binary(ExpressionUtils.member(authExpr, [
|
|
2101
2331
|
fieldName
|
|
2102
|
-
]), "==",
|
|
2103
|
-
fieldName
|
|
2104
|
-
])));
|
|
2332
|
+
]), "==", this.makeOrAppendMember(other, fieldName)));
|
|
2105
2333
|
let result = this.buildAnd(conditions);
|
|
2106
2334
|
if (expr2.op === "!=") {
|
|
2107
2335
|
result = this.buildLogicalNot(result);
|
|
@@ -2109,33 +2337,65 @@ var ExpressionTransformer = class {
|
|
|
2109
2337
|
return this.transform(result, context);
|
|
2110
2338
|
}
|
|
2111
2339
|
}
|
|
2340
|
+
makeOrAppendMember(other, fieldName) {
|
|
2341
|
+
if (ExpressionUtils.isMember(other)) {
|
|
2342
|
+
return ExpressionUtils.member(other.receiver, [
|
|
2343
|
+
...other.members,
|
|
2344
|
+
fieldName
|
|
2345
|
+
]);
|
|
2346
|
+
} else {
|
|
2347
|
+
return ExpressionUtils.member(other, [
|
|
2348
|
+
fieldName
|
|
2349
|
+
]);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2112
2352
|
transformValue(value, type) {
|
|
2113
|
-
|
|
2353
|
+
if (value === true) {
|
|
2354
|
+
return trueNode(this.dialect);
|
|
2355
|
+
} else if (value === false) {
|
|
2356
|
+
return falseNode(this.dialect);
|
|
2357
|
+
} else {
|
|
2358
|
+
return import_kysely7.ValueNode.create(this.dialect.transformPrimitive(value, type, false) ?? null);
|
|
2359
|
+
}
|
|
2114
2360
|
}
|
|
2115
2361
|
_unary(expr2, context) {
|
|
2116
|
-
(0,
|
|
2362
|
+
(0, import_common_helpers6.invariant)(expr2.op === "!", 'only "!" operator is supported');
|
|
2117
2363
|
return logicalNot(this.dialect, this.transform(expr2.operand, context));
|
|
2118
2364
|
}
|
|
2119
2365
|
transformOperator(op) {
|
|
2120
2366
|
const mappedOp = (0, import_ts_pattern7.match)(op).with("==", () => "=").otherwise(() => op);
|
|
2121
|
-
return
|
|
2367
|
+
return import_kysely7.OperatorNode.create(mappedOp);
|
|
2122
2368
|
}
|
|
2123
2369
|
_call(expr2, context) {
|
|
2124
2370
|
const result = this.transformCall(expr2, context);
|
|
2125
2371
|
return result.toOperationNode();
|
|
2126
2372
|
}
|
|
2127
2373
|
transformCall(expr2, context) {
|
|
2128
|
-
const func = this.
|
|
2374
|
+
const func = this.getFunctionImpl(expr2.function);
|
|
2129
2375
|
if (!func) {
|
|
2130
2376
|
throw new QueryError(`Function not implemented: ${expr2.function}`);
|
|
2131
2377
|
}
|
|
2132
|
-
const eb = (0,
|
|
2378
|
+
const eb = (0, import_kysely7.expressionBuilder)();
|
|
2133
2379
|
return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
|
|
2380
|
+
client: this.client,
|
|
2134
2381
|
dialect: this.dialect,
|
|
2135
2382
|
model: context.model,
|
|
2383
|
+
modelAlias: context.alias ?? context.model,
|
|
2136
2384
|
operation: context.operation
|
|
2137
2385
|
});
|
|
2138
2386
|
}
|
|
2387
|
+
getFunctionImpl(functionName) {
|
|
2388
|
+
let func = this.clientOptions.functions?.[functionName];
|
|
2389
|
+
if (!func) {
|
|
2390
|
+
for (const plugin of this.clientOptions.plugins ?? []) {
|
|
2391
|
+
if (plugin.functions?.[functionName]) {
|
|
2392
|
+
func = plugin.functions[functionName];
|
|
2393
|
+
break;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
return func;
|
|
2398
|
+
}
|
|
2139
2399
|
transformCallArg(eb, arg, context) {
|
|
2140
2400
|
if (ExpressionUtils.isLiteral(arg)) {
|
|
2141
2401
|
return eb.val(arg.value);
|
|
@@ -2156,23 +2416,22 @@ var ExpressionTransformer = class {
|
|
|
2156
2416
|
if (this.isAuthCall(expr2.receiver)) {
|
|
2157
2417
|
return this.valueMemberAccess(this.auth, expr2, this.authType);
|
|
2158
2418
|
}
|
|
2159
|
-
(0,
|
|
2419
|
+
(0, import_common_helpers6.invariant)(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
|
|
2160
2420
|
let members = expr2.members;
|
|
2161
2421
|
let receiver;
|
|
2162
2422
|
const { memberFilter, memberSelect, ...restContext } = context;
|
|
2163
2423
|
if (ExpressionUtils.isThis(expr2.receiver)) {
|
|
2164
2424
|
if (expr2.members.length === 1) {
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2425
|
+
return this._field(ExpressionUtils.field(expr2.members[0]), context);
|
|
2426
|
+
} else {
|
|
2427
|
+
const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
|
|
2428
|
+
receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
|
|
2429
|
+
members = expr2.members.slice(1);
|
|
2168
2430
|
}
|
|
2169
|
-
const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
|
|
2170
|
-
receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
|
|
2171
|
-
members = expr2.members.slice(1);
|
|
2172
2431
|
} else {
|
|
2173
2432
|
receiver = this.transform(expr2.receiver, restContext);
|
|
2174
2433
|
}
|
|
2175
|
-
(0,
|
|
2434
|
+
(0, import_common_helpers6.invariant)(import_kysely7.SelectQueryNode.is(receiver), "expected receiver to be select query");
|
|
2176
2435
|
let startType;
|
|
2177
2436
|
if (ExpressionUtils.isField(expr2.receiver)) {
|
|
2178
2437
|
const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
|
|
@@ -2201,11 +2460,11 @@ var ExpressionTransformer = class {
|
|
|
2201
2460
|
alias: void 0
|
|
2202
2461
|
});
|
|
2203
2462
|
if (currNode) {
|
|
2204
|
-
(0,
|
|
2463
|
+
(0, import_common_helpers6.invariant)(import_kysely7.SelectQueryNode.is(currNode), "expected select query node");
|
|
2205
2464
|
currNode = {
|
|
2206
2465
|
...relation,
|
|
2207
2466
|
selections: [
|
|
2208
|
-
|
|
2467
|
+
import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(currNode, import_kysely7.IdentifierNode.create(members[i + 1])))
|
|
2209
2468
|
]
|
|
2210
2469
|
};
|
|
2211
2470
|
} else {
|
|
@@ -2218,21 +2477,21 @@ var ExpressionTransformer = class {
|
|
|
2218
2477
|
};
|
|
2219
2478
|
}
|
|
2220
2479
|
} else {
|
|
2221
|
-
(0,
|
|
2222
|
-
(0,
|
|
2223
|
-
currNode =
|
|
2480
|
+
(0, import_common_helpers6.invariant)(i === members.length - 1, "plain field access must be the last segment");
|
|
2481
|
+
(0, import_common_helpers6.invariant)(!currNode, "plain field access must be the last segment");
|
|
2482
|
+
currNode = import_kysely7.ColumnNode.create(member);
|
|
2224
2483
|
}
|
|
2225
2484
|
}
|
|
2226
2485
|
return {
|
|
2227
2486
|
...receiver,
|
|
2228
2487
|
selections: [
|
|
2229
|
-
|
|
2488
|
+
import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(currNode, import_kysely7.IdentifierNode.create("$t")))
|
|
2230
2489
|
]
|
|
2231
2490
|
};
|
|
2232
2491
|
}
|
|
2233
2492
|
valueMemberAccess(receiver, expr2, receiverType) {
|
|
2234
2493
|
if (!receiver) {
|
|
2235
|
-
return
|
|
2494
|
+
return import_kysely7.ValueNode.createImmediate(null);
|
|
2236
2495
|
}
|
|
2237
2496
|
if (expr2.members.length !== 1) {
|
|
2238
2497
|
throw new Error(`Only single member access is supported`);
|
|
@@ -2243,24 +2502,33 @@ var ExpressionTransformer = class {
|
|
|
2243
2502
|
return this.transformValue(fieldValue, fieldDef.type);
|
|
2244
2503
|
}
|
|
2245
2504
|
transformRelationAccess(field, relationModel, context) {
|
|
2505
|
+
const m2m = getManyToManyRelation(this.schema, context.model, field);
|
|
2506
|
+
if (m2m) {
|
|
2507
|
+
return this.transformManyToManyRelationAccess(m2m, context);
|
|
2508
|
+
}
|
|
2246
2509
|
const fromModel = context.model;
|
|
2247
2510
|
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
|
|
2248
2511
|
let condition;
|
|
2249
2512
|
if (ownedByModel) {
|
|
2250
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) =>
|
|
2513
|
+
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => import_kysely7.BinaryOperationNode.create(import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(fk), import_kysely7.TableNode.create(context.alias ?? fromModel)), import_kysely7.OperatorNode.create("="), import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(pk), import_kysely7.TableNode.create(relationModel)))));
|
|
2251
2514
|
} else {
|
|
2252
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) =>
|
|
2515
|
+
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => import_kysely7.BinaryOperationNode.create(import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(pk), import_kysely7.TableNode.create(context.alias ?? fromModel)), import_kysely7.OperatorNode.create("="), import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(fk), import_kysely7.TableNode.create(relationModel)))));
|
|
2253
2516
|
}
|
|
2254
2517
|
return {
|
|
2255
2518
|
kind: "SelectQueryNode",
|
|
2256
|
-
from:
|
|
2257
|
-
|
|
2519
|
+
from: import_kysely7.FromNode.create([
|
|
2520
|
+
import_kysely7.TableNode.create(relationModel)
|
|
2258
2521
|
]),
|
|
2259
|
-
where:
|
|
2522
|
+
where: import_kysely7.WhereNode.create(condition)
|
|
2260
2523
|
};
|
|
2261
2524
|
}
|
|
2525
|
+
transformManyToManyRelationAccess(m2m, context) {
|
|
2526
|
+
const eb = (0, import_kysely7.expressionBuilder)();
|
|
2527
|
+
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}`));
|
|
2528
|
+
return relationQuery.toOperationNode();
|
|
2529
|
+
}
|
|
2262
2530
|
createColumnRef(column, context) {
|
|
2263
|
-
return
|
|
2531
|
+
return import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(column), import_kysely7.TableNode.create(context.alias ?? context.model));
|
|
2264
2532
|
}
|
|
2265
2533
|
isAuthCall(value) {
|
|
2266
2534
|
return ExpressionUtils.isCall(value) && value.function === "auth";
|
|
@@ -2269,7 +2537,7 @@ var ExpressionTransformer = class {
|
|
|
2269
2537
|
return ExpressionUtils.isMember(expr2) && this.isAuthCall(expr2.receiver);
|
|
2270
2538
|
}
|
|
2271
2539
|
isNullNode(node) {
|
|
2272
|
-
return
|
|
2540
|
+
return import_kysely7.ValueNode.is(node) && node.value === null;
|
|
2273
2541
|
}
|
|
2274
2542
|
buildLogicalNot(result) {
|
|
2275
2543
|
return ExpressionUtils.unary("!", result);
|
|
@@ -2283,6 +2551,19 @@ var ExpressionTransformer = class {
|
|
|
2283
2551
|
return conditions.reduce((acc, condition) => ExpressionUtils.binary(acc, "&&", condition));
|
|
2284
2552
|
}
|
|
2285
2553
|
}
|
|
2554
|
+
isRelationField(expr2, model) {
|
|
2555
|
+
const fieldDef = this.getFieldDefFromFieldRef(expr2, model);
|
|
2556
|
+
return !!fieldDef?.relation;
|
|
2557
|
+
}
|
|
2558
|
+
getFieldDefFromFieldRef(expr2, model) {
|
|
2559
|
+
if (ExpressionUtils.isField(expr2)) {
|
|
2560
|
+
return requireField(this.schema, model, expr2.field);
|
|
2561
|
+
} else if (ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && ExpressionUtils.isThis(expr2.receiver)) {
|
|
2562
|
+
return requireField(this.schema, model, expr2.members[0]);
|
|
2563
|
+
} else {
|
|
2564
|
+
return void 0;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2286
2567
|
};
|
|
2287
2568
|
_ts_decorate([
|
|
2288
2569
|
expr("literal"),
|
|
@@ -2354,7 +2635,7 @@ _ts_decorate([
|
|
|
2354
2635
|
], ExpressionTransformer.prototype, "_member", null);
|
|
2355
2636
|
|
|
2356
2637
|
// src/plugins/policy/policy-handler.ts
|
|
2357
|
-
var PolicyHandler = class extends
|
|
2638
|
+
var PolicyHandler = class extends import_kysely8.OperationNodeTransformer {
|
|
2358
2639
|
static {
|
|
2359
2640
|
__name(this, "PolicyHandler");
|
|
2360
2641
|
}
|
|
@@ -2369,129 +2650,296 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2369
2650
|
}
|
|
2370
2651
|
async handle(node, proceed) {
|
|
2371
2652
|
if (!this.isCrudQueryNode(node)) {
|
|
2372
|
-
throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
|
|
2653
|
+
throw new RejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
|
|
2373
2654
|
}
|
|
2374
2655
|
if (!this.isMutationQueryNode(node)) {
|
|
2375
2656
|
return proceed(this.transformNode(node));
|
|
2376
2657
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
if (
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2658
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2659
|
+
if (import_kysely8.InsertQueryNode.is(node)) {
|
|
2660
|
+
const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
|
|
2661
|
+
let needCheckPreCreate = true;
|
|
2662
|
+
if (!isManyToManyJoinTable) {
|
|
2663
|
+
const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
|
|
2664
|
+
if (constCondition === true) {
|
|
2665
|
+
needCheckPreCreate = false;
|
|
2666
|
+
} else if (constCondition === false) {
|
|
2667
|
+
throw new RejectedByPolicyError(mutationModel);
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
if (needCheckPreCreate) {
|
|
2671
|
+
await this.enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed);
|
|
2385
2672
|
}
|
|
2386
2673
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
await this.enforcePreCreatePolicy(node, proceed);
|
|
2392
|
-
}
|
|
2393
|
-
const transformedNode = this.transformNode(node);
|
|
2394
|
-
const result = await proceed(transformedNode);
|
|
2395
|
-
if (!this.onlyReturningId(node)) {
|
|
2674
|
+
const result = await proceed(this.transformNode(node));
|
|
2675
|
+
if (!node.returning || this.onlyReturningId(node)) {
|
|
2676
|
+
return result;
|
|
2677
|
+
} else {
|
|
2396
2678
|
const readBackResult = await this.processReadBack(node, result, proceed);
|
|
2397
2679
|
if (readBackResult.rows.length !== result.rows.length) {
|
|
2398
|
-
throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
|
|
2680
|
+
throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
2399
2681
|
}
|
|
2400
2682
|
return readBackResult;
|
|
2401
|
-
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
// #region overrides
|
|
2686
|
+
transformSelectQuery(node) {
|
|
2687
|
+
let whereNode = this.transformNode(node.where);
|
|
2688
|
+
const policyFilter = this.createPolicyFilterForFrom(node.from);
|
|
2689
|
+
if (policyFilter) {
|
|
2690
|
+
whereNode = import_kysely8.WhereNode.create(whereNode?.where ? conjunction(this.dialect, [
|
|
2691
|
+
whereNode.where,
|
|
2692
|
+
policyFilter
|
|
2693
|
+
]) : policyFilter);
|
|
2694
|
+
}
|
|
2695
|
+
const baseResult = super.transformSelectQuery({
|
|
2696
|
+
...node,
|
|
2697
|
+
where: void 0
|
|
2698
|
+
});
|
|
2699
|
+
return {
|
|
2700
|
+
...baseResult,
|
|
2701
|
+
where: whereNode
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2704
|
+
transformJoin(node) {
|
|
2705
|
+
const table = this.extractTableName(node.table);
|
|
2706
|
+
if (!table) {
|
|
2707
|
+
return super.transformJoin(node);
|
|
2708
|
+
}
|
|
2709
|
+
const filter = this.buildPolicyFilter(table.model, table.alias, "read");
|
|
2710
|
+
const nestedSelect = {
|
|
2711
|
+
kind: "SelectQueryNode",
|
|
2712
|
+
from: import_kysely8.FromNode.create([
|
|
2713
|
+
node.table
|
|
2714
|
+
]),
|
|
2715
|
+
selections: [
|
|
2716
|
+
import_kysely8.SelectionNode.createSelectAll()
|
|
2717
|
+
],
|
|
2718
|
+
where: import_kysely8.WhereNode.create(filter)
|
|
2719
|
+
};
|
|
2720
|
+
return {
|
|
2721
|
+
...node,
|
|
2722
|
+
table: import_kysely8.AliasNode.create(import_kysely8.ParensNode.create(nestedSelect), import_kysely8.IdentifierNode.create(table.alias ?? table.model))
|
|
2723
|
+
};
|
|
2724
|
+
}
|
|
2725
|
+
transformInsertQuery(node) {
|
|
2726
|
+
let onConflict = node.onConflict;
|
|
2727
|
+
if (onConflict?.updates) {
|
|
2728
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2729
|
+
const filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2730
|
+
if (onConflict.updateWhere) {
|
|
2731
|
+
onConflict = {
|
|
2732
|
+
...onConflict,
|
|
2733
|
+
updateWhere: import_kysely8.WhereNode.create(conjunction(this.dialect, [
|
|
2734
|
+
onConflict.updateWhere.where,
|
|
2735
|
+
filter
|
|
2736
|
+
]))
|
|
2737
|
+
};
|
|
2738
|
+
} else {
|
|
2739
|
+
onConflict = {
|
|
2740
|
+
...onConflict,
|
|
2741
|
+
updateWhere: import_kysely8.WhereNode.create(filter)
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
const processedNode = onConflict ? {
|
|
2746
|
+
...node,
|
|
2747
|
+
onConflict
|
|
2748
|
+
} : node;
|
|
2749
|
+
const result = super.transformInsertQuery(processedNode);
|
|
2750
|
+
if (!node.returning) {
|
|
2751
|
+
return result;
|
|
2752
|
+
}
|
|
2753
|
+
if (this.onlyReturningId(node)) {
|
|
2402
2754
|
return result;
|
|
2755
|
+
} else {
|
|
2756
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2757
|
+
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2758
|
+
return {
|
|
2759
|
+
...result,
|
|
2760
|
+
returning: import_kysely8.ReturningNode.create(idFields.map((field) => import_kysely8.SelectionNode.create(import_kysely8.ColumnNode.create(field))))
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
transformUpdateQuery(node) {
|
|
2765
|
+
const result = super.transformUpdateQuery(node);
|
|
2766
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2767
|
+
let filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2768
|
+
if (node.from) {
|
|
2769
|
+
const joinFilter = this.createPolicyFilterForFrom(node.from);
|
|
2770
|
+
if (joinFilter) {
|
|
2771
|
+
filter = conjunction(this.dialect, [
|
|
2772
|
+
filter,
|
|
2773
|
+
joinFilter
|
|
2774
|
+
]);
|
|
2775
|
+
}
|
|
2403
2776
|
}
|
|
2777
|
+
return {
|
|
2778
|
+
...result,
|
|
2779
|
+
where: import_kysely8.WhereNode.create(result.where ? conjunction(this.dialect, [
|
|
2780
|
+
result.where.where,
|
|
2781
|
+
filter
|
|
2782
|
+
]) : filter)
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
transformDeleteQuery(node) {
|
|
2786
|
+
const result = super.transformDeleteQuery(node);
|
|
2787
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2788
|
+
let filter = this.buildPolicyFilter(mutationModel, alias, "delete");
|
|
2789
|
+
if (node.using) {
|
|
2790
|
+
const joinFilter = this.createPolicyFilterForTables(node.using.tables);
|
|
2791
|
+
if (joinFilter) {
|
|
2792
|
+
filter = conjunction(this.dialect, [
|
|
2793
|
+
filter,
|
|
2794
|
+
joinFilter
|
|
2795
|
+
]);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
return {
|
|
2799
|
+
...result,
|
|
2800
|
+
where: import_kysely8.WhereNode.create(result.where ? conjunction(this.dialect, [
|
|
2801
|
+
result.where.where,
|
|
2802
|
+
filter
|
|
2803
|
+
]) : filter)
|
|
2804
|
+
};
|
|
2404
2805
|
}
|
|
2806
|
+
// #endregion
|
|
2807
|
+
// #region helpers
|
|
2405
2808
|
onlyReturningId(node) {
|
|
2406
2809
|
if (!node.returning) {
|
|
2407
2810
|
return true;
|
|
2408
2811
|
}
|
|
2409
|
-
const
|
|
2812
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2813
|
+
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2410
2814
|
const collector = new ColumnCollector();
|
|
2411
2815
|
const selectedColumns = collector.collect(node.returning);
|
|
2412
2816
|
return selectedColumns.every((c) => idFields.includes(c));
|
|
2413
2817
|
}
|
|
2414
|
-
async enforcePreCreatePolicy(node, proceed) {
|
|
2415
|
-
const model = this.getMutationModel(node);
|
|
2818
|
+
async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
|
|
2416
2819
|
const fields = node.columns?.map((c) => c.column.name) ?? [];
|
|
2417
|
-
const valueRows = node.values ? this.unwrapCreateValueRows(node.values,
|
|
2820
|
+
const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
|
|
2418
2821
|
[]
|
|
2419
2822
|
];
|
|
2420
2823
|
for (const values of valueRows) {
|
|
2421
|
-
|
|
2824
|
+
if (isManyToManyJoinTable) {
|
|
2825
|
+
await this.enforcePreCreatePolicyForManyToManyJoinTable(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
2826
|
+
} else {
|
|
2827
|
+
await this.enforcePreCreatePolicyForOne(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
async enforcePreCreatePolicyForManyToManyJoinTable(tableName, fields, values, proceed) {
|
|
2832
|
+
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
2833
|
+
(0, import_common_helpers7.invariant)(m2m);
|
|
2834
|
+
(0, import_common_helpers7.invariant)(fields.includes("A") && fields.includes("B"), "many-to-many join table must have A and B fk fields");
|
|
2835
|
+
const aIndex = fields.indexOf("A");
|
|
2836
|
+
const aNode = values[aIndex];
|
|
2837
|
+
const bIndex = fields.indexOf("B");
|
|
2838
|
+
const bNode = values[bIndex];
|
|
2839
|
+
(0, import_common_helpers7.invariant)(import_kysely8.ValueNode.is(aNode) && import_kysely8.ValueNode.is(bNode), "A and B values must be ValueNode");
|
|
2840
|
+
const aValue = aNode.value;
|
|
2841
|
+
const bValue = bNode.value;
|
|
2842
|
+
(0, import_common_helpers7.invariant)(aValue !== null && aValue !== void 0, "A value cannot be null or undefined");
|
|
2843
|
+
(0, import_common_helpers7.invariant)(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
|
|
2844
|
+
const eb = (0, import_kysely8.expressionBuilder)();
|
|
2845
|
+
const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
|
|
2846
|
+
const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new import_kysely8.ExpressionWrapper(filterA).as("$t"));
|
|
2847
|
+
const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
|
|
2848
|
+
const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new import_kysely8.ExpressionWrapper(filterB).as("$t"));
|
|
2849
|
+
const queryNode = {
|
|
2850
|
+
kind: "SelectQueryNode",
|
|
2851
|
+
selections: [
|
|
2852
|
+
import_kysely8.SelectionNode.create(import_kysely8.AliasNode.create(queryA.toOperationNode(), import_kysely8.IdentifierNode.create("$conditionA"))),
|
|
2853
|
+
import_kysely8.SelectionNode.create(import_kysely8.AliasNode.create(queryB.toOperationNode(), import_kysely8.IdentifierNode.create("$conditionB")))
|
|
2854
|
+
]
|
|
2855
|
+
};
|
|
2856
|
+
const result = await proceed(queryNode);
|
|
2857
|
+
if (!result.rows[0]?.$conditionA) {
|
|
2858
|
+
throw new RejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
|
|
2859
|
+
}
|
|
2860
|
+
if (!result.rows[0]?.$conditionB) {
|
|
2861
|
+
throw new RejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
|
|
2422
2862
|
}
|
|
2423
2863
|
}
|
|
2424
2864
|
async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
|
|
2425
|
-
const allFields = Object.
|
|
2865
|
+
const allFields = Object.entries(requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
|
|
2426
2866
|
const allValues = [];
|
|
2427
|
-
for (const
|
|
2428
|
-
const index = fields.indexOf(
|
|
2867
|
+
for (const [name, _def] of allFields) {
|
|
2868
|
+
const index = fields.indexOf(name);
|
|
2429
2869
|
if (index >= 0) {
|
|
2430
2870
|
allValues.push(values[index]);
|
|
2431
2871
|
} else {
|
|
2432
|
-
allValues.push(
|
|
2872
|
+
allValues.push(import_kysely8.ValueNode.createImmediate(null));
|
|
2433
2873
|
}
|
|
2434
2874
|
}
|
|
2875
|
+
const eb = (0, import_kysely8.expressionBuilder)();
|
|
2435
2876
|
const constTable = {
|
|
2436
2877
|
kind: "SelectQueryNode",
|
|
2437
|
-
from:
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
])),
|
|
2878
|
+
from: import_kysely8.FromNode.create([
|
|
2879
|
+
import_kysely8.AliasNode.create(import_kysely8.ParensNode.create(import_kysely8.ValuesNode.create([
|
|
2880
|
+
import_kysely8.ValueListNode.create(allValues)
|
|
2881
|
+
])), import_kysely8.IdentifierNode.create("$t"))
|
|
2441
2882
|
]),
|
|
2442
|
-
selections: allFields.map((
|
|
2883
|
+
selections: allFields.map(([name, def], index) => {
|
|
2884
|
+
const castedColumnRef = import_kysely8.sql`CAST(${eb.ref(`column${index + 1}`)} as ${import_kysely8.sql.raw(this.dialect.getFieldSqlType(def))})`.as(name);
|
|
2885
|
+
return import_kysely8.SelectionNode.create(castedColumnRef.toOperationNode());
|
|
2886
|
+
})
|
|
2443
2887
|
};
|
|
2444
2888
|
const filter = this.buildPolicyFilter(model, void 0, "create");
|
|
2445
2889
|
const preCreateCheck = {
|
|
2446
2890
|
kind: "SelectQueryNode",
|
|
2447
|
-
from:
|
|
2448
|
-
|
|
2891
|
+
from: import_kysely8.FromNode.create([
|
|
2892
|
+
import_kysely8.AliasNode.create(constTable, import_kysely8.IdentifierNode.create(model))
|
|
2449
2893
|
]),
|
|
2450
2894
|
selections: [
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
]),
|
|
2895
|
+
import_kysely8.SelectionNode.create(import_kysely8.AliasNode.create(import_kysely8.BinaryOperationNode.create(import_kysely8.FunctionNode.create("COUNT", [
|
|
2896
|
+
import_kysely8.ValueNode.createImmediate(1)
|
|
2897
|
+
]), import_kysely8.OperatorNode.create(">"), import_kysely8.ValueNode.createImmediate(0)), import_kysely8.IdentifierNode.create("$condition")))
|
|
2454
2898
|
],
|
|
2455
|
-
where:
|
|
2899
|
+
where: import_kysely8.WhereNode.create(filter)
|
|
2456
2900
|
};
|
|
2457
2901
|
const result = await proceed(preCreateCheck);
|
|
2458
2902
|
if (!result.rows[0]?.$condition) {
|
|
2459
2903
|
throw new RejectedByPolicyError(model);
|
|
2460
2904
|
}
|
|
2461
2905
|
}
|
|
2462
|
-
unwrapCreateValueRows(node, model, fields) {
|
|
2463
|
-
if (
|
|
2464
|
-
return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields));
|
|
2465
|
-
} else if (
|
|
2906
|
+
unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
|
|
2907
|
+
if (import_kysely8.ValuesNode.is(node)) {
|
|
2908
|
+
return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields, isManyToManyJoinTable));
|
|
2909
|
+
} else if (import_kysely8.PrimitiveValueListNode.is(node)) {
|
|
2466
2910
|
return [
|
|
2467
|
-
this.unwrapCreateValueRow(node.values, model, fields)
|
|
2911
|
+
this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
|
|
2468
2912
|
];
|
|
2469
2913
|
} else {
|
|
2470
2914
|
throw new InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
|
|
2471
2915
|
}
|
|
2472
2916
|
}
|
|
2473
|
-
unwrapCreateValueRow(data, model, fields) {
|
|
2474
|
-
(0,
|
|
2917
|
+
unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
|
|
2918
|
+
(0, import_common_helpers7.invariant)(data.length === fields.length, "data length must match fields length");
|
|
2475
2919
|
const result = [];
|
|
2476
2920
|
for (let i = 0; i < data.length; i++) {
|
|
2477
2921
|
const item = data[i];
|
|
2478
|
-
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
2479
2922
|
if (typeof item === "object" && item && "kind" in item) {
|
|
2480
|
-
|
|
2923
|
+
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
2924
|
+
(0, import_common_helpers7.invariant)(item.kind === "ValueNode", "expecting a ValueNode");
|
|
2481
2925
|
result.push({
|
|
2482
|
-
node:
|
|
2926
|
+
node: import_kysely8.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
|
|
2483
2927
|
raw: item.value
|
|
2484
2928
|
});
|
|
2485
2929
|
} else {
|
|
2486
|
-
|
|
2930
|
+
let value = item;
|
|
2931
|
+
if (!isImplicitManyToManyJoinTable) {
|
|
2932
|
+
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
2933
|
+
value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
|
|
2934
|
+
}
|
|
2487
2935
|
if (Array.isArray(value)) {
|
|
2488
2936
|
result.push({
|
|
2489
|
-
node:
|
|
2937
|
+
node: import_kysely8.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
|
|
2490
2938
|
raw: value
|
|
2491
2939
|
});
|
|
2492
2940
|
} else {
|
|
2493
2941
|
result.push({
|
|
2494
|
-
node:
|
|
2942
|
+
node: import_kysely8.ValueNode.create(value),
|
|
2495
2943
|
raw: value
|
|
2496
2944
|
});
|
|
2497
2945
|
}
|
|
@@ -2527,18 +2975,15 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2527
2975
|
if (!this.isMutationQueryNode(node) || !node.returning) {
|
|
2528
2976
|
return result;
|
|
2529
2977
|
}
|
|
2530
|
-
const
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
}
|
|
2534
|
-
const idConditions = this.buildIdConditions(table, result.rows);
|
|
2535
|
-
const policyFilter = this.buildPolicyFilter(table, void 0, "read");
|
|
2978
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2979
|
+
const idConditions = this.buildIdConditions(mutationModel, result.rows);
|
|
2980
|
+
const policyFilter = this.buildPolicyFilter(mutationModel, void 0, "read");
|
|
2536
2981
|
const select = {
|
|
2537
2982
|
kind: "SelectQueryNode",
|
|
2538
|
-
from:
|
|
2539
|
-
|
|
2983
|
+
from: import_kysely8.FromNode.create([
|
|
2984
|
+
import_kysely8.TableNode.create(mutationModel)
|
|
2540
2985
|
]),
|
|
2541
|
-
where:
|
|
2986
|
+
where: import_kysely8.WhereNode.create(conjunction(this.dialect, [
|
|
2542
2987
|
idConditions,
|
|
2543
2988
|
policyFilter
|
|
2544
2989
|
])),
|
|
@@ -2548,15 +2993,31 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2548
2993
|
return selectResult;
|
|
2549
2994
|
}
|
|
2550
2995
|
buildIdConditions(table, rows) {
|
|
2551
|
-
const idFields =
|
|
2552
|
-
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) =>
|
|
2996
|
+
const idFields = requireIdFields(this.client.$schema, table);
|
|
2997
|
+
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => import_kysely8.BinaryOperationNode.create(import_kysely8.ColumnNode.create(field), import_kysely8.OperatorNode.create("="), import_kysely8.ValueNode.create(row[field]))))));
|
|
2553
2998
|
}
|
|
2554
2999
|
getMutationModel(node) {
|
|
2555
|
-
const r = (0, import_ts_pattern8.match)(node).when(
|
|
3000
|
+
const r = (0, import_ts_pattern8.match)(node).when(import_kysely8.InsertQueryNode.is, (node2) => ({
|
|
3001
|
+
mutationModel: getTableName(node2.into),
|
|
3002
|
+
alias: void 0
|
|
3003
|
+
})).when(import_kysely8.UpdateQueryNode.is, (node2) => {
|
|
3004
|
+
if (!node2.table) {
|
|
3005
|
+
throw new QueryError("Update query must have a table");
|
|
3006
|
+
}
|
|
3007
|
+
const r2 = this.extractTableName(node2.table);
|
|
3008
|
+
return r2 ? {
|
|
3009
|
+
mutationModel: r2.model,
|
|
3010
|
+
alias: r2.alias
|
|
3011
|
+
} : void 0;
|
|
3012
|
+
}).when(import_kysely8.DeleteQueryNode.is, (node2) => {
|
|
2556
3013
|
if (node2.from.froms.length !== 1) {
|
|
2557
|
-
throw new
|
|
3014
|
+
throw new QueryError("Only one from table is supported for delete");
|
|
2558
3015
|
}
|
|
2559
|
-
|
|
3016
|
+
const r2 = this.extractTableName(node2.from.froms[0]);
|
|
3017
|
+
return r2 ? {
|
|
3018
|
+
mutationModel: r2.model,
|
|
3019
|
+
alias: r2.alias
|
|
3020
|
+
} : void 0;
|
|
2560
3021
|
}).exhaustive();
|
|
2561
3022
|
if (!r) {
|
|
2562
3023
|
throw new InternalError(`Unable to get table name for query node: ${node}`);
|
|
@@ -2564,18 +3025,22 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2564
3025
|
return r;
|
|
2565
3026
|
}
|
|
2566
3027
|
isCrudQueryNode(node) {
|
|
2567
|
-
return
|
|
3028
|
+
return import_kysely8.SelectQueryNode.is(node) || import_kysely8.InsertQueryNode.is(node) || import_kysely8.UpdateQueryNode.is(node) || import_kysely8.DeleteQueryNode.is(node);
|
|
2568
3029
|
}
|
|
2569
3030
|
isMutationQueryNode(node) {
|
|
2570
|
-
return
|
|
3031
|
+
return import_kysely8.InsertQueryNode.is(node) || import_kysely8.UpdateQueryNode.is(node) || import_kysely8.DeleteQueryNode.is(node);
|
|
2571
3032
|
}
|
|
2572
3033
|
buildPolicyFilter(model, alias, operation) {
|
|
3034
|
+
const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
|
|
3035
|
+
if (m2mFilter) {
|
|
3036
|
+
return m2mFilter;
|
|
3037
|
+
}
|
|
2573
3038
|
const policies = this.getModelPolicies(model, operation);
|
|
2574
3039
|
if (policies.length === 0) {
|
|
2575
3040
|
return falseNode(this.dialect);
|
|
2576
3041
|
}
|
|
2577
|
-
const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.
|
|
2578
|
-
const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.
|
|
3042
|
+
const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
3043
|
+
const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
2579
3044
|
let combinedPolicy;
|
|
2580
3045
|
if (allows.length === 0) {
|
|
2581
3046
|
combinedPolicy = falseNode(this.dialect);
|
|
@@ -2591,100 +3056,59 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2591
3056
|
}
|
|
2592
3057
|
return combinedPolicy;
|
|
2593
3058
|
}
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
node.from?.froms.forEach((from) => {
|
|
2597
|
-
const extractResult = this.extractTableName(from);
|
|
2598
|
-
if (extractResult) {
|
|
2599
|
-
const { model, alias } = extractResult;
|
|
2600
|
-
const filter = this.buildPolicyFilter(model, alias, "read");
|
|
2601
|
-
whereNode = import_kysely7.WhereNode.create(whereNode?.where ? conjunction(this.dialect, [
|
|
2602
|
-
whereNode.where,
|
|
2603
|
-
filter
|
|
2604
|
-
]) : filter);
|
|
2605
|
-
}
|
|
2606
|
-
});
|
|
2607
|
-
const baseResult = super.transformSelectQuery({
|
|
2608
|
-
...node,
|
|
2609
|
-
where: void 0
|
|
2610
|
-
});
|
|
2611
|
-
return {
|
|
2612
|
-
...baseResult,
|
|
2613
|
-
where: whereNode
|
|
2614
|
-
};
|
|
2615
|
-
}
|
|
2616
|
-
transformInsertQuery(node) {
|
|
2617
|
-
const result = super.transformInsertQuery(node);
|
|
2618
|
-
if (!node.returning) {
|
|
2619
|
-
return result;
|
|
2620
|
-
}
|
|
2621
|
-
if (this.onlyReturningId(node)) {
|
|
2622
|
-
return result;
|
|
2623
|
-
} else {
|
|
2624
|
-
const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
|
|
2625
|
-
return {
|
|
2626
|
-
...result,
|
|
2627
|
-
returning: import_kysely7.ReturningNode.create(idFields.map((field) => import_kysely7.SelectionNode.create(import_kysely7.ColumnNode.create(field))))
|
|
2628
|
-
};
|
|
2629
|
-
}
|
|
2630
|
-
}
|
|
2631
|
-
transformUpdateQuery(node) {
|
|
2632
|
-
const result = super.transformUpdateQuery(node);
|
|
2633
|
-
const mutationModel = this.getMutationModel(node);
|
|
2634
|
-
const filter = this.buildPolicyFilter(mutationModel, void 0, "update");
|
|
2635
|
-
return {
|
|
2636
|
-
...result,
|
|
2637
|
-
where: import_kysely7.WhereNode.create(result.where ? conjunction(this.dialect, [
|
|
2638
|
-
result.where.where,
|
|
2639
|
-
filter
|
|
2640
|
-
]) : filter)
|
|
2641
|
-
};
|
|
2642
|
-
}
|
|
2643
|
-
transformDeleteQuery(node) {
|
|
2644
|
-
const result = super.transformDeleteQuery(node);
|
|
2645
|
-
const mutationModel = this.getMutationModel(node);
|
|
2646
|
-
const filter = this.buildPolicyFilter(mutationModel, void 0, "delete");
|
|
2647
|
-
return {
|
|
2648
|
-
...result,
|
|
2649
|
-
where: import_kysely7.WhereNode.create(result.where ? conjunction(this.dialect, [
|
|
2650
|
-
result.where.where,
|
|
2651
|
-
filter
|
|
2652
|
-
]) : filter)
|
|
2653
|
-
};
|
|
2654
|
-
}
|
|
2655
|
-
extractTableName(from) {
|
|
2656
|
-
if (import_kysely7.TableNode.is(from)) {
|
|
3059
|
+
extractTableName(node) {
|
|
3060
|
+
if (import_kysely8.TableNode.is(node)) {
|
|
2657
3061
|
return {
|
|
2658
|
-
model:
|
|
3062
|
+
model: node.table.identifier.name
|
|
2659
3063
|
};
|
|
2660
3064
|
}
|
|
2661
|
-
if (
|
|
2662
|
-
const inner = this.extractTableName(
|
|
3065
|
+
if (import_kysely8.AliasNode.is(node)) {
|
|
3066
|
+
const inner = this.extractTableName(node.node);
|
|
2663
3067
|
if (!inner) {
|
|
2664
3068
|
return void 0;
|
|
2665
3069
|
}
|
|
2666
3070
|
return {
|
|
2667
3071
|
model: inner.model,
|
|
2668
|
-
alias:
|
|
3072
|
+
alias: import_kysely8.IdentifierNode.is(node.alias) ? node.alias.name : void 0
|
|
2669
3073
|
};
|
|
2670
3074
|
} else {
|
|
2671
3075
|
return void 0;
|
|
2672
3076
|
}
|
|
2673
3077
|
}
|
|
2674
|
-
|
|
2675
|
-
|
|
3078
|
+
createPolicyFilterForFrom(node) {
|
|
3079
|
+
if (!node) {
|
|
3080
|
+
return void 0;
|
|
3081
|
+
}
|
|
3082
|
+
return this.createPolicyFilterForTables(node.froms);
|
|
3083
|
+
}
|
|
3084
|
+
createPolicyFilterForTables(tables) {
|
|
3085
|
+
return tables.reduce((acc, table) => {
|
|
3086
|
+
const extractResult = this.extractTableName(table);
|
|
3087
|
+
if (extractResult) {
|
|
3088
|
+
const { model, alias } = extractResult;
|
|
3089
|
+
const filter = this.buildPolicyFilter(model, alias, "read");
|
|
3090
|
+
return acc ? conjunction(this.dialect, [
|
|
3091
|
+
acc,
|
|
3092
|
+
filter
|
|
3093
|
+
]) : filter;
|
|
3094
|
+
}
|
|
3095
|
+
return acc;
|
|
3096
|
+
}, void 0);
|
|
3097
|
+
}
|
|
3098
|
+
compilePolicyCondition(model, alias, operation, policy) {
|
|
3099
|
+
return new ExpressionTransformer(this.client).transform(policy.condition, {
|
|
2676
3100
|
model,
|
|
2677
3101
|
alias,
|
|
2678
3102
|
operation,
|
|
2679
3103
|
auth: this.client.$auth
|
|
2680
3104
|
});
|
|
2681
3105
|
}
|
|
2682
|
-
getModelPolicies(
|
|
2683
|
-
const modelDef = requireModel(this.client.$schema,
|
|
3106
|
+
getModelPolicies(model, operation) {
|
|
3107
|
+
const modelDef = requireModel(this.client.$schema, model);
|
|
2684
3108
|
const result = [];
|
|
2685
3109
|
const extractOperations = /* @__PURE__ */ __name((expr2) => {
|
|
2686
|
-
(0,
|
|
2687
|
-
(0,
|
|
3110
|
+
(0, import_common_helpers7.invariant)(ExpressionUtils.isLiteral(expr2), "expecting a literal");
|
|
3111
|
+
(0, import_common_helpers7.invariant)(typeof expr2.value === "string", "expecting a string literal");
|
|
2688
3112
|
return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
|
|
2689
3113
|
}, "extractOperations");
|
|
2690
3114
|
if (modelDef.attributes) {
|
|
@@ -2696,8 +3120,84 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2696
3120
|
}
|
|
2697
3121
|
return result;
|
|
2698
3122
|
}
|
|
3123
|
+
resolveManyToManyJoinTable(tableName) {
|
|
3124
|
+
for (const model of Object.values(this.client.$schema.models)) {
|
|
3125
|
+
for (const field of Object.values(model.fields)) {
|
|
3126
|
+
const m2m = getManyToManyRelation(this.client.$schema, model.name, field.name);
|
|
3127
|
+
if (m2m?.joinTable === tableName) {
|
|
3128
|
+
const sortedRecord = [
|
|
3129
|
+
{
|
|
3130
|
+
model: model.name,
|
|
3131
|
+
field: field.name
|
|
3132
|
+
},
|
|
3133
|
+
{
|
|
3134
|
+
model: m2m.otherModel,
|
|
3135
|
+
field: m2m.otherField
|
|
3136
|
+
}
|
|
3137
|
+
].sort(this.manyToManySorter);
|
|
3138
|
+
const firstIdFields = requireIdFields(this.client.$schema, sortedRecord[0].model);
|
|
3139
|
+
const secondIdFields = requireIdFields(this.client.$schema, sortedRecord[1].model);
|
|
3140
|
+
(0, import_common_helpers7.invariant)(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
|
|
3141
|
+
return {
|
|
3142
|
+
firstModel: sortedRecord[0].model,
|
|
3143
|
+
firstField: sortedRecord[0].field,
|
|
3144
|
+
firstIdField: firstIdFields[0],
|
|
3145
|
+
secondModel: sortedRecord[1].model,
|
|
3146
|
+
secondField: sortedRecord[1].field,
|
|
3147
|
+
secondIdField: secondIdFields[0]
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
return void 0;
|
|
3153
|
+
}
|
|
3154
|
+
manyToManySorter(a, b) {
|
|
3155
|
+
return a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field);
|
|
3156
|
+
}
|
|
3157
|
+
isManyToManyJoinTable(tableName) {
|
|
3158
|
+
return !!this.resolveManyToManyJoinTable(tableName);
|
|
3159
|
+
}
|
|
3160
|
+
getModelPolicyFilterForManyToManyJoinTable(tableName, alias, operation) {
|
|
3161
|
+
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
3162
|
+
if (!m2m) {
|
|
3163
|
+
return void 0;
|
|
3164
|
+
}
|
|
3165
|
+
const checkForOperation = operation === "read" ? "read" : "update";
|
|
3166
|
+
const eb = (0, import_kysely8.expressionBuilder)();
|
|
3167
|
+
const joinTable = alias ?? tableName;
|
|
3168
|
+
const aQuery = eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new import_kysely8.ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
|
|
3169
|
+
const bQuery = eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new import_kysely8.ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
|
|
3170
|
+
return eb.and([
|
|
3171
|
+
aQuery,
|
|
3172
|
+
bQuery
|
|
3173
|
+
]).toOperationNode();
|
|
3174
|
+
}
|
|
2699
3175
|
};
|
|
2700
3176
|
|
|
3177
|
+
// src/plugins/policy/functions.ts
|
|
3178
|
+
var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, operation }) => {
|
|
3179
|
+
(0, import_common_helpers8.invariant)(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments');
|
|
3180
|
+
const arg1Node = args[0].toOperationNode();
|
|
3181
|
+
const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
|
|
3182
|
+
if (arg2Node) {
|
|
3183
|
+
(0, import_common_helpers8.invariant)(import_kysely9.ValueNode.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
|
|
3184
|
+
(0, import_common_helpers8.invariant)(CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
|
|
3185
|
+
}
|
|
3186
|
+
const fieldName = extractFieldName(arg1Node);
|
|
3187
|
+
(0, import_common_helpers8.invariant)(fieldName, 'Failed to extract field name from the first argument of "check" function');
|
|
3188
|
+
const fieldDef = requireField(client.$schema, model, fieldName);
|
|
3189
|
+
(0, import_common_helpers8.invariant)(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
|
|
3190
|
+
(0, import_common_helpers8.invariant)(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
|
|
3191
|
+
const relationModel = fieldDef.type;
|
|
3192
|
+
const op = arg2Node ? arg2Node.value : operation;
|
|
3193
|
+
const policyHandler = new PolicyHandler(client);
|
|
3194
|
+
const joinPairs = buildJoinPairs(client.$schema, model, modelAlias, fieldName, relationModel);
|
|
3195
|
+
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))));
|
|
3196
|
+
const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
|
|
3197
|
+
const result = eb.selectFrom(relationModel).where(joinCondition).select(new import_kysely9.ExpressionWrapper(policyCondition).as("$condition"));
|
|
3198
|
+
return result;
|
|
3199
|
+
}, "check");
|
|
3200
|
+
|
|
2701
3201
|
// src/plugins/policy/plugin.ts
|
|
2702
3202
|
var PolicyPlugin = class {
|
|
2703
3203
|
static {
|
|
@@ -2712,6 +3212,11 @@ var PolicyPlugin = class {
|
|
|
2712
3212
|
get description() {
|
|
2713
3213
|
return "Enforces access policies defined in the schema.";
|
|
2714
3214
|
}
|
|
3215
|
+
get functions() {
|
|
3216
|
+
return {
|
|
3217
|
+
check
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
2715
3220
|
onKyselyQuery({
|
|
2716
3221
|
query,
|
|
2717
3222
|
client,
|
|
@@ -2729,6 +3234,7 @@ var PolicyPlugin = class {
|
|
|
2729
3234
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2730
3235
|
0 && (module.exports = {
|
|
2731
3236
|
PolicyPlugin,
|
|
2732
|
-
RejectedByPolicyError
|
|
3237
|
+
RejectedByPolicyError,
|
|
3238
|
+
RejectedByPolicyReason
|
|
2733
3239
|
});
|
|
2734
3240
|
//# sourceMappingURL=index.cjs.map
|