@zenstackhq/runtime 3.0.0-beta.4 → 3.0.0-beta.6
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/package.json +11 -8
- package/dist/plugins/policy/plugin.zmodel +0 -33
|
@@ -2,46 +2,50 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
4
|
// src/plugins/policy/errors.ts
|
|
5
|
+
var RejectedByPolicyReason = /* @__PURE__ */ function(RejectedByPolicyReason2) {
|
|
6
|
+
RejectedByPolicyReason2["NO_ACCESS"] = "no-access";
|
|
7
|
+
RejectedByPolicyReason2["CANNOT_READ_BACK"] = "cannot-read-back";
|
|
8
|
+
RejectedByPolicyReason2["OTHER"] = "other";
|
|
9
|
+
return RejectedByPolicyReason2;
|
|
10
|
+
}({});
|
|
5
11
|
var RejectedByPolicyError = class extends Error {
|
|
6
12
|
static {
|
|
7
13
|
__name(this, "RejectedByPolicyError");
|
|
8
14
|
}
|
|
9
15
|
model;
|
|
10
16
|
reason;
|
|
11
|
-
constructor(model, reason) {
|
|
12
|
-
super(
|
|
17
|
+
constructor(model, reason = "no-access", message) {
|
|
18
|
+
super(message ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
|
|
13
19
|
}
|
|
14
20
|
};
|
|
15
21
|
|
|
16
|
-
// src/plugins/policy/
|
|
17
|
-
import { invariant as
|
|
18
|
-
import {
|
|
19
|
-
import { match as match8 } from "ts-pattern";
|
|
20
|
-
|
|
21
|
-
// src/client/crud/dialects/index.ts
|
|
22
|
-
import { match as match5 } from "ts-pattern";
|
|
22
|
+
// src/plugins/policy/functions.ts
|
|
23
|
+
import { invariant as invariant8 } from "@zenstackhq/common-helpers";
|
|
24
|
+
import { ExpressionWrapper as ExpressionWrapper2, ValueNode as ValueNode4 } from "kysely";
|
|
23
25
|
|
|
24
|
-
// src/client/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
|
|
31
|
-
var LOGICAL_COMBINATORS = [
|
|
32
|
-
"AND",
|
|
33
|
-
"OR",
|
|
34
|
-
"NOT"
|
|
35
|
-
];
|
|
36
|
-
var AGGREGATE_OPERATORS = [
|
|
37
|
-
"_count",
|
|
38
|
-
"_sum",
|
|
39
|
-
"_avg",
|
|
40
|
-
"_min",
|
|
41
|
-
"_max"
|
|
26
|
+
// src/client/contract.ts
|
|
27
|
+
var CRUD = [
|
|
28
|
+
"create",
|
|
29
|
+
"read",
|
|
30
|
+
"update",
|
|
31
|
+
"delete"
|
|
42
32
|
];
|
|
43
33
|
|
|
34
|
+
// src/client/kysely-utils.ts
|
|
35
|
+
import { AliasNode, ColumnNode, ReferenceNode, TableNode } from "kysely";
|
|
36
|
+
function extractFieldName(node) {
|
|
37
|
+
if (ReferenceNode.is(node) && ColumnNode.is(node.column)) {
|
|
38
|
+
return node.column.column.name;
|
|
39
|
+
} else if (ColumnNode.is(node)) {
|
|
40
|
+
return node.column.name;
|
|
41
|
+
} else {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
__name(extractFieldName, "extractFieldName");
|
|
46
|
+
|
|
44
47
|
// src/client/query-utils.ts
|
|
48
|
+
import { invariant } from "@zenstackhq/common-helpers";
|
|
45
49
|
import { match } from "ts-pattern";
|
|
46
50
|
|
|
47
51
|
// src/schema/expression.ts
|
|
@@ -188,11 +192,15 @@ function requireField(schema, modelOrType, field) {
|
|
|
188
192
|
throw new QueryError(`Model or type "${modelOrType}" not found in schema`);
|
|
189
193
|
}
|
|
190
194
|
__name(requireField, "requireField");
|
|
191
|
-
function
|
|
195
|
+
function requireIdFields(schema, model) {
|
|
192
196
|
const modelDef = requireModel(schema, model);
|
|
193
|
-
|
|
197
|
+
const result = modelDef?.idFields;
|
|
198
|
+
if (!result) {
|
|
199
|
+
throw new InternalError(`Model "${model}" does not have ID field(s)`);
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
194
202
|
}
|
|
195
|
-
__name(
|
|
203
|
+
__name(requireIdFields, "requireIdFields");
|
|
196
204
|
function getRelationForeignKeyFieldPairs(schema, model, relationField) {
|
|
197
205
|
const fieldDef = requireField(schema, model, relationField);
|
|
198
206
|
if (!fieldDef?.relation) {
|
|
@@ -285,7 +293,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComp
|
|
|
285
293
|
throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
|
|
286
294
|
}
|
|
287
295
|
return computer(eb, {
|
|
288
|
-
|
|
296
|
+
modelAlias
|
|
289
297
|
});
|
|
290
298
|
}
|
|
291
299
|
}
|
|
@@ -312,7 +320,7 @@ function buildJoinPairs(schema, model, modelAlias, relationField, relationModelA
|
|
|
312
320
|
}
|
|
313
321
|
__name(buildJoinPairs, "buildJoinPairs");
|
|
314
322
|
function makeDefaultOrderBy(schema, model) {
|
|
315
|
-
const idFields =
|
|
323
|
+
const idFields = requireIdFields(schema, model);
|
|
316
324
|
return idFields.map((f) => ({
|
|
317
325
|
[f]: "asc"
|
|
318
326
|
}));
|
|
@@ -351,11 +359,17 @@ function getManyToManyRelation(schema, model, field) {
|
|
|
351
359
|
"A"
|
|
352
360
|
];
|
|
353
361
|
}
|
|
362
|
+
const modelIdFields = requireIdFields(schema, model);
|
|
363
|
+
invariant(modelIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
364
|
+
const otherIdFields = requireIdFields(schema, fieldDef.type);
|
|
365
|
+
invariant(otherIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
354
366
|
return {
|
|
355
367
|
parentFkName: orderedFK[0],
|
|
368
|
+
parentPKName: modelIdFields[0],
|
|
356
369
|
otherModel: fieldDef.type,
|
|
357
370
|
otherField: fieldDef.relation.opposite,
|
|
358
371
|
otherFkName: orderedFK[1],
|
|
372
|
+
otherPKName: otherIdFields[0],
|
|
359
373
|
joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
|
|
360
374
|
};
|
|
361
375
|
} else {
|
|
@@ -411,8 +425,37 @@ function aggregate(eb, expr2, op) {
|
|
|
411
425
|
}
|
|
412
426
|
__name(aggregate, "aggregate");
|
|
413
427
|
|
|
428
|
+
// src/plugins/policy/policy-handler.ts
|
|
429
|
+
import { invariant as invariant7 } from "@zenstackhq/common-helpers";
|
|
430
|
+
import { AliasNode as AliasNode4, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode3, DeleteQueryNode, expressionBuilder as expressionBuilder3, ExpressionWrapper, FromNode as FromNode2, FunctionNode as FunctionNode3, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, ParensNode as ParensNode2, PrimitiveValueListNode, RawNode, ReturningNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, sql as sql4, TableNode as TableNode4, UpdateQueryNode, ValueListNode as ValueListNode2, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
|
|
431
|
+
import { match as match8 } from "ts-pattern";
|
|
432
|
+
|
|
433
|
+
// src/client/crud/dialects/index.ts
|
|
434
|
+
import { match as match5 } from "ts-pattern";
|
|
435
|
+
|
|
436
|
+
// src/client/crud/dialects/postgresql.ts
|
|
437
|
+
import { invariant as invariant3 } from "@zenstackhq/common-helpers";
|
|
438
|
+
import Decimal from "decimal.js";
|
|
439
|
+
import { sql as sql2 } from "kysely";
|
|
440
|
+
import { match as match3 } from "ts-pattern";
|
|
441
|
+
|
|
442
|
+
// src/client/constants.ts
|
|
443
|
+
var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
|
|
444
|
+
var LOGICAL_COMBINATORS = [
|
|
445
|
+
"AND",
|
|
446
|
+
"OR",
|
|
447
|
+
"NOT"
|
|
448
|
+
];
|
|
449
|
+
var AGGREGATE_OPERATORS = [
|
|
450
|
+
"_count",
|
|
451
|
+
"_sum",
|
|
452
|
+
"_avg",
|
|
453
|
+
"_min",
|
|
454
|
+
"_max"
|
|
455
|
+
];
|
|
456
|
+
|
|
414
457
|
// src/client/crud/dialects/base-dialect.ts
|
|
415
|
-
import { invariant, isPlainObject } from "@zenstackhq/common-helpers";
|
|
458
|
+
import { invariant as invariant2, isPlainObject } from "@zenstackhq/common-helpers";
|
|
416
459
|
import { expressionBuilder, sql } from "kysely";
|
|
417
460
|
import { match as match2, P } from "ts-pattern";
|
|
418
461
|
|
|
@@ -444,6 +487,9 @@ var BaseCrudDialect = class {
|
|
|
444
487
|
transformPrimitive(value, _type, _forArrayField) {
|
|
445
488
|
return value;
|
|
446
489
|
}
|
|
490
|
+
transformOutput(value, _type) {
|
|
491
|
+
return value;
|
|
492
|
+
}
|
|
447
493
|
// #region common query builders
|
|
448
494
|
buildSelectModel(eb, model, modelAlias) {
|
|
449
495
|
const modelDef = requireModel(this.schema, model);
|
|
@@ -611,9 +657,11 @@ var BaseCrudDialect = class {
|
|
|
611
657
|
const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
|
|
612
658
|
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
613
659
|
if (m2m) {
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
660
|
+
const modelIdFields = requireIdFields(this.schema, model);
|
|
661
|
+
invariant2(modelIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
662
|
+
const relationIdFields = requireIdFields(this.schema, relationModel);
|
|
663
|
+
invariant2(relationIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
664
|
+
return eb2(sql.ref(`${relationFilterSelectAlias}.${relationIdFields[0]}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", sql.ref(`${modelAlias}.${modelIdFields[0]}`)));
|
|
617
665
|
} else {
|
|
618
666
|
const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
619
667
|
let result2 = this.true(eb2);
|
|
@@ -723,14 +771,14 @@ var BaseCrudDialect = class {
|
|
|
723
771
|
}
|
|
724
772
|
const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
|
|
725
773
|
const condition = match2(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
|
|
726
|
-
|
|
774
|
+
invariant2(Array.isArray(rhs), "right hand side must be an array");
|
|
727
775
|
if (rhs.length === 0) {
|
|
728
776
|
return this.false(eb);
|
|
729
777
|
} else {
|
|
730
778
|
return eb(lhs, "in", rhs);
|
|
731
779
|
}
|
|
732
780
|
}).with("notIn", () => {
|
|
733
|
-
|
|
781
|
+
invariant2(Array.isArray(rhs), "right hand side must be an array");
|
|
734
782
|
if (rhs.length === 0) {
|
|
735
783
|
return this.true(eb);
|
|
736
784
|
} else {
|
|
@@ -848,18 +896,18 @@ var BaseCrudDialect = class {
|
|
|
848
896
|
"_min",
|
|
849
897
|
"_max"
|
|
850
898
|
].includes(field)) {
|
|
851
|
-
|
|
899
|
+
invariant2(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
852
900
|
for (const [k, v] of Object.entries(value)) {
|
|
853
|
-
|
|
901
|
+
invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
854
902
|
result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), sql.raw(this.negateSort(v, negated)));
|
|
855
903
|
}
|
|
856
904
|
continue;
|
|
857
905
|
}
|
|
858
906
|
switch (field) {
|
|
859
907
|
case "_count": {
|
|
860
|
-
|
|
908
|
+
invariant2(value && typeof value === "object", 'invalid orderBy value for field "_count"');
|
|
861
909
|
for (const [k, v] of Object.entries(value)) {
|
|
862
|
-
|
|
910
|
+
invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
863
911
|
result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), sql.raw(this.negateSort(v, negated)));
|
|
864
912
|
}
|
|
865
913
|
continue;
|
|
@@ -882,7 +930,7 @@ var BaseCrudDialect = class {
|
|
|
882
930
|
throw new QueryError(`invalid orderBy value for field "${field}"`);
|
|
883
931
|
}
|
|
884
932
|
if ("_count" in value) {
|
|
885
|
-
|
|
933
|
+
invariant2(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
|
|
886
934
|
const sort = this.negateSort(value._count, negated);
|
|
887
935
|
result = result.orderBy((eb) => {
|
|
888
936
|
const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
|
|
@@ -954,7 +1002,7 @@ var BaseCrudDialect = class {
|
|
|
954
1002
|
}
|
|
955
1003
|
}
|
|
956
1004
|
buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
|
|
957
|
-
const idFields =
|
|
1005
|
+
const idFields = requireIdFields(this.schema, thisModel);
|
|
958
1006
|
query = query.leftJoin(otherModelAlias, (qb) => {
|
|
959
1007
|
for (const idField of idFields) {
|
|
960
1008
|
qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
|
|
@@ -976,10 +1024,16 @@ var BaseCrudDialect = class {
|
|
|
976
1024
|
for (const [field, value] of Object.entries(selections.select)) {
|
|
977
1025
|
const fieldDef = requireField(this.schema, model, field);
|
|
978
1026
|
const fieldModel = fieldDef.type;
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
fieldCountQuery =
|
|
1027
|
+
let fieldCountQuery;
|
|
1028
|
+
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
1029
|
+
if (m2m) {
|
|
1030
|
+
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}`));
|
|
1031
|
+
} else {
|
|
1032
|
+
fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
|
|
1033
|
+
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
|
|
1034
|
+
for (const [left, right] of joinPairs) {
|
|
1035
|
+
fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
|
|
1036
|
+
}
|
|
983
1037
|
}
|
|
984
1038
|
if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
|
|
985
1039
|
const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
|
|
@@ -1059,6 +1113,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1059
1113
|
static {
|
|
1060
1114
|
__name(this, "PostgresCrudDialect");
|
|
1061
1115
|
}
|
|
1116
|
+
constructor(schema, options) {
|
|
1117
|
+
super(schema, options);
|
|
1118
|
+
}
|
|
1062
1119
|
get provider() {
|
|
1063
1120
|
return "postgresql";
|
|
1064
1121
|
}
|
|
@@ -1073,8 +1130,40 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1073
1130
|
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
1074
1131
|
}
|
|
1075
1132
|
} else {
|
|
1076
|
-
return match3(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
1133
|
+
return match3(type).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
transformOutput(value, type) {
|
|
1137
|
+
if (value === null || value === void 0) {
|
|
1138
|
+
return value;
|
|
1077
1139
|
}
|
|
1140
|
+
return match3(type).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Decimal", () => this.transformDecimal(value)).otherwise(() => super.transformOutput(value, type));
|
|
1141
|
+
}
|
|
1142
|
+
transformOutputBigInt(value) {
|
|
1143
|
+
if (typeof value === "bigint") {
|
|
1144
|
+
return value;
|
|
1145
|
+
}
|
|
1146
|
+
invariant3(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1147
|
+
return BigInt(value);
|
|
1148
|
+
}
|
|
1149
|
+
transformDecimal(value) {
|
|
1150
|
+
if (value instanceof Decimal) {
|
|
1151
|
+
return value;
|
|
1152
|
+
}
|
|
1153
|
+
invariant3(typeof value === "string" || typeof value === "number" || value instanceof Decimal, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1154
|
+
return new Decimal(value);
|
|
1155
|
+
}
|
|
1156
|
+
transformOutputDate(value) {
|
|
1157
|
+
if (typeof value === "string") {
|
|
1158
|
+
return new Date(value);
|
|
1159
|
+
} else if (value instanceof Date && this.options.fixPostgresTimezone !== false) {
|
|
1160
|
+
return new Date(value.getTime() - value.getTimezoneOffset() * 60 * 1e3);
|
|
1161
|
+
} else {
|
|
1162
|
+
return value;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
transformOutputBytes(value) {
|
|
1166
|
+
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1078
1167
|
}
|
|
1079
1168
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1080
1169
|
const relationResultName = `${parentAlias}$${relationField}`;
|
|
@@ -1106,10 +1195,10 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1106
1195
|
buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
|
|
1107
1196
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1108
1197
|
if (m2m) {
|
|
1109
|
-
const parentIds =
|
|
1110
|
-
const relationIds =
|
|
1111
|
-
|
|
1112
|
-
|
|
1198
|
+
const parentIds = requireIdFields(this.schema, model);
|
|
1199
|
+
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1200
|
+
invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1201
|
+
invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1113
1202
|
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}`)));
|
|
1114
1203
|
} else {
|
|
1115
1204
|
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
|
|
@@ -1221,10 +1310,32 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1221
1310
|
get supportInsertWithDefault() {
|
|
1222
1311
|
return true;
|
|
1223
1312
|
}
|
|
1313
|
+
getFieldSqlType(fieldDef) {
|
|
1314
|
+
if (fieldDef.relation) {
|
|
1315
|
+
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1316
|
+
}
|
|
1317
|
+
let result;
|
|
1318
|
+
if (this.schema.enums?.[fieldDef.type]) {
|
|
1319
|
+
result = "text";
|
|
1320
|
+
} else {
|
|
1321
|
+
result = match3(fieldDef.type).with("String", () => "text").with("Boolean", () => "boolean").with("Int", () => "integer").with("BigInt", () => "bigint").with("Float", () => "double precision").with("Decimal", () => "decimal").with("DateTime", () => "timestamp").with("Bytes", () => "bytea").with("Json", () => "jsonb").otherwise(() => "text");
|
|
1322
|
+
}
|
|
1323
|
+
if (fieldDef.array) {
|
|
1324
|
+
result += "[]";
|
|
1325
|
+
}
|
|
1326
|
+
return result;
|
|
1327
|
+
}
|
|
1328
|
+
getStringCasingBehavior() {
|
|
1329
|
+
return {
|
|
1330
|
+
supportsILike: true,
|
|
1331
|
+
likeCaseSensitive: true
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1224
1334
|
};
|
|
1225
1335
|
|
|
1226
1336
|
// src/client/crud/dialects/sqlite.ts
|
|
1227
|
-
import { invariant as
|
|
1337
|
+
import { invariant as invariant4 } from "@zenstackhq/common-helpers";
|
|
1338
|
+
import Decimal2 from "decimal.js";
|
|
1228
1339
|
import { sql as sql3 } from "kysely";
|
|
1229
1340
|
import { match as match4 } from "ts-pattern";
|
|
1230
1341
|
var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
@@ -1244,9 +1355,57 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1244
1355
|
if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1245
1356
|
return JSON.stringify(value);
|
|
1246
1357
|
} else {
|
|
1247
|
-
return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
1358
|
+
return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
transformOutput(value, type) {
|
|
1363
|
+
if (value === null || value === void 0) {
|
|
1364
|
+
return value;
|
|
1365
|
+
} else if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1366
|
+
return this.transformOutputJson(value);
|
|
1367
|
+
} else {
|
|
1368
|
+
return match4(type).with("Boolean", () => this.transformOutputBoolean(value)).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("Decimal", () => this.transformOutputDecimal(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Json", () => this.transformOutputJson(value)).otherwise(() => super.transformOutput(value, type));
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
transformOutputDecimal(value) {
|
|
1372
|
+
if (value instanceof Decimal2) {
|
|
1373
|
+
return value;
|
|
1374
|
+
}
|
|
1375
|
+
invariant4(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1376
|
+
return new Decimal2(value);
|
|
1377
|
+
}
|
|
1378
|
+
transformOutputBigInt(value) {
|
|
1379
|
+
if (typeof value === "bigint") {
|
|
1380
|
+
return value;
|
|
1381
|
+
}
|
|
1382
|
+
invariant4(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1383
|
+
return BigInt(value);
|
|
1384
|
+
}
|
|
1385
|
+
transformOutputBoolean(value) {
|
|
1386
|
+
return !!value;
|
|
1387
|
+
}
|
|
1388
|
+
transformOutputDate(value) {
|
|
1389
|
+
if (typeof value === "number") {
|
|
1390
|
+
return new Date(value);
|
|
1391
|
+
} else if (typeof value === "string") {
|
|
1392
|
+
return new Date(value);
|
|
1393
|
+
} else {
|
|
1394
|
+
return value;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
transformOutputBytes(value) {
|
|
1398
|
+
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1399
|
+
}
|
|
1400
|
+
transformOutputJson(value) {
|
|
1401
|
+
if (typeof value === "string") {
|
|
1402
|
+
try {
|
|
1403
|
+
return JSON.parse(value);
|
|
1404
|
+
} catch (e) {
|
|
1405
|
+
throw new QueryError("Invalid JSON returned", e);
|
|
1248
1406
|
}
|
|
1249
1407
|
}
|
|
1408
|
+
return value;
|
|
1250
1409
|
}
|
|
1251
1410
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1252
1411
|
return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
|
|
@@ -1329,10 +1488,10 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1329
1488
|
const relationModel = fieldDef.type;
|
|
1330
1489
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1331
1490
|
if (m2m) {
|
|
1332
|
-
const parentIds =
|
|
1333
|
-
const relationIds =
|
|
1334
|
-
|
|
1335
|
-
|
|
1491
|
+
const parentIds = requireIdFields(this.schema, model);
|
|
1492
|
+
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1493
|
+
invariant4(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1494
|
+
invariant4(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1336
1495
|
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}`)));
|
|
1337
1496
|
} else {
|
|
1338
1497
|
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
@@ -1384,6 +1543,24 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1384
1543
|
get supportInsertWithDefault() {
|
|
1385
1544
|
return false;
|
|
1386
1545
|
}
|
|
1546
|
+
getFieldSqlType(fieldDef) {
|
|
1547
|
+
if (fieldDef.relation) {
|
|
1548
|
+
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1549
|
+
}
|
|
1550
|
+
if (fieldDef.array) {
|
|
1551
|
+
throw new QueryError("SQLite does not support scalar list type");
|
|
1552
|
+
}
|
|
1553
|
+
if (this.schema.enums?.[fieldDef.type]) {
|
|
1554
|
+
return "text";
|
|
1555
|
+
}
|
|
1556
|
+
return match4(fieldDef.type).with("String", () => "text").with("Boolean", () => "integer").with("Int", () => "integer").with("BigInt", () => "integer").with("Float", () => "real").with("Decimal", () => "decimal").with("DateTime", () => "numeric").with("Bytes", () => "blob").with("Json", () => "jsonb").otherwise(() => "text");
|
|
1557
|
+
}
|
|
1558
|
+
getStringCasingBehavior() {
|
|
1559
|
+
return {
|
|
1560
|
+
supportsILike: false,
|
|
1561
|
+
likeCaseSensitive: false
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1387
1564
|
};
|
|
1388
1565
|
|
|
1389
1566
|
// src/client/crud/dialects/index.ts
|
|
@@ -1711,12 +1888,12 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
|
|
|
1711
1888
|
};
|
|
1712
1889
|
|
|
1713
1890
|
// src/plugins/policy/expression-transformer.ts
|
|
1714
|
-
import { invariant as
|
|
1715
|
-
import { AliasNode as
|
|
1891
|
+
import { invariant as invariant6 } from "@zenstackhq/common-helpers";
|
|
1892
|
+
import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode2, ColumnNode as ColumnNode2, expressionBuilder as expressionBuilder2, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode3, SelectionNode, SelectQueryNode, TableNode as TableNode3, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
|
|
1716
1893
|
import { match as match7 } from "ts-pattern";
|
|
1717
1894
|
|
|
1718
1895
|
// src/plugins/policy/expression-evaluator.ts
|
|
1719
|
-
import { invariant as
|
|
1896
|
+
import { invariant as invariant5 } from "@zenstackhq/common-helpers";
|
|
1720
1897
|
import { match as match6 } from "ts-pattern";
|
|
1721
1898
|
var ExpressionEvaluator = class {
|
|
1722
1899
|
static {
|
|
@@ -1760,18 +1937,18 @@ var ExpressionEvaluator = class {
|
|
|
1760
1937
|
const right = this.evaluate(expr2.right, context);
|
|
1761
1938
|
return match6(expr2.op).with("==", () => left === right).with("!=", () => left !== right).with(">", () => left > right).with(">=", () => left >= right).with("<", () => left < right).with("<=", () => left <= right).with("&&", () => left && right).with("||", () => left || right).with("in", () => {
|
|
1762
1939
|
const _right = right ?? [];
|
|
1763
|
-
|
|
1940
|
+
invariant5(Array.isArray(_right), 'expected array for "in" operator');
|
|
1764
1941
|
return _right.includes(left);
|
|
1765
1942
|
}).exhaustive();
|
|
1766
1943
|
}
|
|
1767
1944
|
evaluateCollectionPredicate(expr2, context) {
|
|
1768
1945
|
const op = expr2.op;
|
|
1769
|
-
|
|
1946
|
+
invariant5(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
|
|
1770
1947
|
const left = this.evaluate(expr2.left, context);
|
|
1771
1948
|
if (!left) {
|
|
1772
1949
|
return false;
|
|
1773
1950
|
}
|
|
1774
|
-
|
|
1951
|
+
invariant5(Array.isArray(left), "expected array");
|
|
1775
1952
|
return match6(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
|
|
1776
1953
|
...context,
|
|
1777
1954
|
thisValue: item
|
|
@@ -1786,7 +1963,7 @@ var ExpressionEvaluator = class {
|
|
|
1786
1963
|
};
|
|
1787
1964
|
|
|
1788
1965
|
// src/plugins/policy/utils.ts
|
|
1789
|
-
import { AliasNode, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode, TableNode, UnaryOperationNode, ValueNode } from "kysely";
|
|
1966
|
+
import { AliasNode as AliasNode2, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode as ReferenceNode2, TableNode as TableNode2, UnaryOperationNode, ValueNode } from "kysely";
|
|
1790
1967
|
function trueNode(dialect) {
|
|
1791
1968
|
return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
|
|
1792
1969
|
}
|
|
@@ -1872,11 +2049,11 @@ function getTableName(node) {
|
|
|
1872
2049
|
if (!node) {
|
|
1873
2050
|
return node;
|
|
1874
2051
|
}
|
|
1875
|
-
if (
|
|
2052
|
+
if (TableNode2.is(node)) {
|
|
1876
2053
|
return node.table.identifier.name;
|
|
1877
|
-
} else if (
|
|
2054
|
+
} else if (AliasNode2.is(node)) {
|
|
1878
2055
|
return getTableName(node.node);
|
|
1879
|
-
} else if (
|
|
2056
|
+
} else if (ReferenceNode2.is(node) && node.table) {
|
|
1880
2057
|
return getTableName(node.table);
|
|
1881
2058
|
}
|
|
1882
2059
|
return void 0;
|
|
@@ -1909,16 +2086,21 @@ var ExpressionTransformer = class {
|
|
|
1909
2086
|
static {
|
|
1910
2087
|
__name(this, "ExpressionTransformer");
|
|
1911
2088
|
}
|
|
1912
|
-
|
|
1913
|
-
clientOptions;
|
|
1914
|
-
auth;
|
|
2089
|
+
client;
|
|
1915
2090
|
dialect;
|
|
1916
|
-
constructor(
|
|
1917
|
-
this.
|
|
1918
|
-
this.clientOptions = clientOptions;
|
|
1919
|
-
this.auth = auth;
|
|
2091
|
+
constructor(client) {
|
|
2092
|
+
this.client = client;
|
|
1920
2093
|
this.dialect = getCrudDialect(this.schema, this.clientOptions);
|
|
1921
2094
|
}
|
|
2095
|
+
get schema() {
|
|
2096
|
+
return this.client.$schema;
|
|
2097
|
+
}
|
|
2098
|
+
get clientOptions() {
|
|
2099
|
+
return this.client.$options;
|
|
2100
|
+
}
|
|
2101
|
+
get auth() {
|
|
2102
|
+
return this.client.$auth;
|
|
2103
|
+
}
|
|
1922
2104
|
get authType() {
|
|
1923
2105
|
if (!this.schema.authType) {
|
|
1924
2106
|
throw new InternalError('Schema does not have an "authType" specified');
|
|
@@ -1988,8 +2170,9 @@ var ExpressionTransformer = class {
|
|
|
1988
2170
|
if (op === "?" || op === "!" || op === "^") {
|
|
1989
2171
|
return this.transformCollectionPredicate(expr2, context);
|
|
1990
2172
|
}
|
|
1991
|
-
const
|
|
1992
|
-
const
|
|
2173
|
+
const { normalizedLeft, normalizedRight } = this.normalizeBinaryOperationOperands(expr2, context);
|
|
2174
|
+
const left = this.transform(normalizedLeft, context);
|
|
2175
|
+
const right = this.transform(normalizedRight, context);
|
|
1993
2176
|
if (op === "in") {
|
|
1994
2177
|
if (this.isNullNode(left)) {
|
|
1995
2178
|
return this.transformValue(false, "Boolean");
|
|
@@ -2004,29 +2187,65 @@ var ExpressionTransformer = class {
|
|
|
2004
2187
|
}
|
|
2005
2188
|
}
|
|
2006
2189
|
if (this.isNullNode(right)) {
|
|
2007
|
-
return
|
|
2190
|
+
return this.transformNullCheck(left, expr2.op);
|
|
2008
2191
|
} else if (this.isNullNode(left)) {
|
|
2009
|
-
return
|
|
2192
|
+
return this.transformNullCheck(right, expr2.op);
|
|
2193
|
+
} else {
|
|
2194
|
+
return BinaryOperationNode2.create(left, this.transformOperator(op), right);
|
|
2010
2195
|
}
|
|
2011
|
-
|
|
2196
|
+
}
|
|
2197
|
+
transformNullCheck(expr2, operator) {
|
|
2198
|
+
invariant6(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
|
|
2199
|
+
if (ValueNode2.is(expr2)) {
|
|
2200
|
+
if (expr2.value === null) {
|
|
2201
|
+
return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
|
|
2202
|
+
} else {
|
|
2203
|
+
return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
|
|
2204
|
+
}
|
|
2205
|
+
} else {
|
|
2206
|
+
return operator === "==" ? BinaryOperationNode2.create(expr2, OperatorNode2.create("is"), ValueNode2.createImmediate(null)) : BinaryOperationNode2.create(expr2, OperatorNode2.create("is not"), ValueNode2.createImmediate(null));
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
normalizeBinaryOperationOperands(expr2, context) {
|
|
2210
|
+
let normalizedLeft = expr2.left;
|
|
2211
|
+
if (this.isRelationField(expr2.left, context.model)) {
|
|
2212
|
+
invariant6(ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
|
|
2213
|
+
const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2214
|
+
invariant6(leftRelDef, "failed to get relation field definition");
|
|
2215
|
+
const idFields = requireIdFields(this.schema, leftRelDef.type);
|
|
2216
|
+
normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
|
|
2217
|
+
}
|
|
2218
|
+
let normalizedRight = expr2.right;
|
|
2219
|
+
if (this.isRelationField(expr2.right, context.model)) {
|
|
2220
|
+
invariant6(ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
|
|
2221
|
+
const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
|
|
2222
|
+
invariant6(rightRelDef, "failed to get relation field definition");
|
|
2223
|
+
const idFields = requireIdFields(this.schema, rightRelDef.type);
|
|
2224
|
+
normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
|
|
2225
|
+
}
|
|
2226
|
+
return {
|
|
2227
|
+
normalizedLeft,
|
|
2228
|
+
normalizedRight
|
|
2229
|
+
};
|
|
2012
2230
|
}
|
|
2013
2231
|
transformCollectionPredicate(expr2, context) {
|
|
2014
|
-
|
|
2232
|
+
invariant6(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
|
|
2015
2233
|
if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
|
|
2016
2234
|
const value = new ExpressionEvaluator().evaluate(expr2, {
|
|
2017
2235
|
auth: this.auth
|
|
2018
2236
|
});
|
|
2019
2237
|
return this.transformValue(value, "Boolean");
|
|
2020
2238
|
}
|
|
2021
|
-
|
|
2239
|
+
invariant6(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
|
|
2022
2240
|
let newContextModel;
|
|
2023
|
-
|
|
2024
|
-
|
|
2241
|
+
const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2242
|
+
if (fieldDef) {
|
|
2243
|
+
invariant6(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
|
|
2025
2244
|
newContextModel = fieldDef.type;
|
|
2026
2245
|
} else {
|
|
2027
|
-
|
|
2028
|
-
const
|
|
2029
|
-
newContextModel =
|
|
2246
|
+
invariant6(ExpressionUtils.isMember(expr2.left) && ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
|
|
2247
|
+
const fieldDef2 = requireField(this.schema, context.model, expr2.left.receiver.field);
|
|
2248
|
+
newContextModel = fieldDef2.type;
|
|
2030
2249
|
for (const member of expr2.left.members) {
|
|
2031
2250
|
const memberDef = requireField(this.schema, newContextModel, member);
|
|
2032
2251
|
newContextModel = memberDef.type;
|
|
@@ -2046,7 +2265,7 @@ var ExpressionTransformer = class {
|
|
|
2046
2265
|
const predicateResult = match7(expr2.op).with("?", () => BinaryOperationNode2.create(count, OperatorNode2.create(">"), ValueNode2.createImmediate(0))).with("!", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).with("^", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).exhaustive();
|
|
2047
2266
|
return this.transform(expr2.left, {
|
|
2048
2267
|
...context,
|
|
2049
|
-
memberSelect: SelectionNode.create(
|
|
2268
|
+
memberSelect: SelectionNode.create(AliasNode3.create(predicateResult, IdentifierNode.create("$t"))),
|
|
2050
2269
|
memberFilter: predicateFilter
|
|
2051
2270
|
});
|
|
2052
2271
|
}
|
|
@@ -2071,12 +2290,10 @@ var ExpressionTransformer = class {
|
|
|
2071
2290
|
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`);
|
|
2072
2291
|
}
|
|
2073
2292
|
const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
|
|
2074
|
-
|
|
2293
|
+
invariant6(idFields.length > 0, "auth type model must have at least one id field");
|
|
2075
2294
|
const conditions = idFields.map((fieldName) => ExpressionUtils.binary(ExpressionUtils.member(authExpr, [
|
|
2076
2295
|
fieldName
|
|
2077
|
-
]), "==",
|
|
2078
|
-
fieldName
|
|
2079
|
-
])));
|
|
2296
|
+
]), "==", this.makeOrAppendMember(other, fieldName)));
|
|
2080
2297
|
let result = this.buildAnd(conditions);
|
|
2081
2298
|
if (expr2.op === "!=") {
|
|
2082
2299
|
result = this.buildLogicalNot(result);
|
|
@@ -2084,11 +2301,29 @@ var ExpressionTransformer = class {
|
|
|
2084
2301
|
return this.transform(result, context);
|
|
2085
2302
|
}
|
|
2086
2303
|
}
|
|
2304
|
+
makeOrAppendMember(other, fieldName) {
|
|
2305
|
+
if (ExpressionUtils.isMember(other)) {
|
|
2306
|
+
return ExpressionUtils.member(other.receiver, [
|
|
2307
|
+
...other.members,
|
|
2308
|
+
fieldName
|
|
2309
|
+
]);
|
|
2310
|
+
} else {
|
|
2311
|
+
return ExpressionUtils.member(other, [
|
|
2312
|
+
fieldName
|
|
2313
|
+
]);
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2087
2316
|
transformValue(value, type) {
|
|
2088
|
-
|
|
2317
|
+
if (value === true) {
|
|
2318
|
+
return trueNode(this.dialect);
|
|
2319
|
+
} else if (value === false) {
|
|
2320
|
+
return falseNode(this.dialect);
|
|
2321
|
+
} else {
|
|
2322
|
+
return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
|
|
2323
|
+
}
|
|
2089
2324
|
}
|
|
2090
2325
|
_unary(expr2, context) {
|
|
2091
|
-
|
|
2326
|
+
invariant6(expr2.op === "!", 'only "!" operator is supported');
|
|
2092
2327
|
return logicalNot(this.dialect, this.transform(expr2.operand, context));
|
|
2093
2328
|
}
|
|
2094
2329
|
transformOperator(op) {
|
|
@@ -2100,17 +2335,31 @@ var ExpressionTransformer = class {
|
|
|
2100
2335
|
return result.toOperationNode();
|
|
2101
2336
|
}
|
|
2102
2337
|
transformCall(expr2, context) {
|
|
2103
|
-
const func = this.
|
|
2338
|
+
const func = this.getFunctionImpl(expr2.function);
|
|
2104
2339
|
if (!func) {
|
|
2105
2340
|
throw new QueryError(`Function not implemented: ${expr2.function}`);
|
|
2106
2341
|
}
|
|
2107
2342
|
const eb = expressionBuilder2();
|
|
2108
2343
|
return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
|
|
2344
|
+
client: this.client,
|
|
2109
2345
|
dialect: this.dialect,
|
|
2110
2346
|
model: context.model,
|
|
2347
|
+
modelAlias: context.alias ?? context.model,
|
|
2111
2348
|
operation: context.operation
|
|
2112
2349
|
});
|
|
2113
2350
|
}
|
|
2351
|
+
getFunctionImpl(functionName) {
|
|
2352
|
+
let func = this.clientOptions.functions?.[functionName];
|
|
2353
|
+
if (!func) {
|
|
2354
|
+
for (const plugin of this.clientOptions.plugins ?? []) {
|
|
2355
|
+
if (plugin.functions?.[functionName]) {
|
|
2356
|
+
func = plugin.functions[functionName];
|
|
2357
|
+
break;
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
return func;
|
|
2362
|
+
}
|
|
2114
2363
|
transformCallArg(eb, arg, context) {
|
|
2115
2364
|
if (ExpressionUtils.isLiteral(arg)) {
|
|
2116
2365
|
return eb.val(arg.value);
|
|
@@ -2131,23 +2380,22 @@ var ExpressionTransformer = class {
|
|
|
2131
2380
|
if (this.isAuthCall(expr2.receiver)) {
|
|
2132
2381
|
return this.valueMemberAccess(this.auth, expr2, this.authType);
|
|
2133
2382
|
}
|
|
2134
|
-
|
|
2383
|
+
invariant6(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
|
|
2135
2384
|
let members = expr2.members;
|
|
2136
2385
|
let receiver;
|
|
2137
2386
|
const { memberFilter, memberSelect, ...restContext } = context;
|
|
2138
2387
|
if (ExpressionUtils.isThis(expr2.receiver)) {
|
|
2139
2388
|
if (expr2.members.length === 1) {
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2389
|
+
return this._field(ExpressionUtils.field(expr2.members[0]), context);
|
|
2390
|
+
} else {
|
|
2391
|
+
const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
|
|
2392
|
+
receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
|
|
2393
|
+
members = expr2.members.slice(1);
|
|
2143
2394
|
}
|
|
2144
|
-
const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
|
|
2145
|
-
receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
|
|
2146
|
-
members = expr2.members.slice(1);
|
|
2147
2395
|
} else {
|
|
2148
2396
|
receiver = this.transform(expr2.receiver, restContext);
|
|
2149
2397
|
}
|
|
2150
|
-
|
|
2398
|
+
invariant6(SelectQueryNode.is(receiver), "expected receiver to be select query");
|
|
2151
2399
|
let startType;
|
|
2152
2400
|
if (ExpressionUtils.isField(expr2.receiver)) {
|
|
2153
2401
|
const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
|
|
@@ -2176,11 +2424,11 @@ var ExpressionTransformer = class {
|
|
|
2176
2424
|
alias: void 0
|
|
2177
2425
|
});
|
|
2178
2426
|
if (currNode) {
|
|
2179
|
-
|
|
2427
|
+
invariant6(SelectQueryNode.is(currNode), "expected select query node");
|
|
2180
2428
|
currNode = {
|
|
2181
2429
|
...relation,
|
|
2182
2430
|
selections: [
|
|
2183
|
-
SelectionNode.create(
|
|
2431
|
+
SelectionNode.create(AliasNode3.create(currNode, IdentifierNode.create(members[i + 1])))
|
|
2184
2432
|
]
|
|
2185
2433
|
};
|
|
2186
2434
|
} else {
|
|
@@ -2193,15 +2441,15 @@ var ExpressionTransformer = class {
|
|
|
2193
2441
|
};
|
|
2194
2442
|
}
|
|
2195
2443
|
} else {
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
currNode =
|
|
2444
|
+
invariant6(i === members.length - 1, "plain field access must be the last segment");
|
|
2445
|
+
invariant6(!currNode, "plain field access must be the last segment");
|
|
2446
|
+
currNode = ColumnNode2.create(member);
|
|
2199
2447
|
}
|
|
2200
2448
|
}
|
|
2201
2449
|
return {
|
|
2202
2450
|
...receiver,
|
|
2203
2451
|
selections: [
|
|
2204
|
-
SelectionNode.create(
|
|
2452
|
+
SelectionNode.create(AliasNode3.create(currNode, IdentifierNode.create("$t")))
|
|
2205
2453
|
]
|
|
2206
2454
|
};
|
|
2207
2455
|
}
|
|
@@ -2218,24 +2466,33 @@ var ExpressionTransformer = class {
|
|
|
2218
2466
|
return this.transformValue(fieldValue, fieldDef.type);
|
|
2219
2467
|
}
|
|
2220
2468
|
transformRelationAccess(field, relationModel, context) {
|
|
2469
|
+
const m2m = getManyToManyRelation(this.schema, context.model, field);
|
|
2470
|
+
if (m2m) {
|
|
2471
|
+
return this.transformManyToManyRelationAccess(m2m, context);
|
|
2472
|
+
}
|
|
2221
2473
|
const fromModel = context.model;
|
|
2222
2474
|
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
|
|
2223
2475
|
let condition;
|
|
2224
2476
|
if (ownedByModel) {
|
|
2225
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(
|
|
2477
|
+
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode3.create(ColumnNode2.create(fk), TableNode3.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode3.create(ColumnNode2.create(pk), TableNode3.create(relationModel)))));
|
|
2226
2478
|
} else {
|
|
2227
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(
|
|
2479
|
+
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode3.create(ColumnNode2.create(pk), TableNode3.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode3.create(ColumnNode2.create(fk), TableNode3.create(relationModel)))));
|
|
2228
2480
|
}
|
|
2229
2481
|
return {
|
|
2230
2482
|
kind: "SelectQueryNode",
|
|
2231
2483
|
from: FromNode.create([
|
|
2232
|
-
|
|
2484
|
+
TableNode3.create(relationModel)
|
|
2233
2485
|
]),
|
|
2234
2486
|
where: WhereNode.create(condition)
|
|
2235
2487
|
};
|
|
2236
2488
|
}
|
|
2489
|
+
transformManyToManyRelationAccess(m2m, context) {
|
|
2490
|
+
const eb = expressionBuilder2();
|
|
2491
|
+
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}`));
|
|
2492
|
+
return relationQuery.toOperationNode();
|
|
2493
|
+
}
|
|
2237
2494
|
createColumnRef(column, context) {
|
|
2238
|
-
return
|
|
2495
|
+
return ReferenceNode3.create(ColumnNode2.create(column), TableNode3.create(context.alias ?? context.model));
|
|
2239
2496
|
}
|
|
2240
2497
|
isAuthCall(value) {
|
|
2241
2498
|
return ExpressionUtils.isCall(value) && value.function === "auth";
|
|
@@ -2258,6 +2515,19 @@ var ExpressionTransformer = class {
|
|
|
2258
2515
|
return conditions.reduce((acc, condition) => ExpressionUtils.binary(acc, "&&", condition));
|
|
2259
2516
|
}
|
|
2260
2517
|
}
|
|
2518
|
+
isRelationField(expr2, model) {
|
|
2519
|
+
const fieldDef = this.getFieldDefFromFieldRef(expr2, model);
|
|
2520
|
+
return !!fieldDef?.relation;
|
|
2521
|
+
}
|
|
2522
|
+
getFieldDefFromFieldRef(expr2, model) {
|
|
2523
|
+
if (ExpressionUtils.isField(expr2)) {
|
|
2524
|
+
return requireField(this.schema, model, expr2.field);
|
|
2525
|
+
} else if (ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && ExpressionUtils.isThis(expr2.receiver)) {
|
|
2526
|
+
return requireField(this.schema, model, expr2.members[0]);
|
|
2527
|
+
} else {
|
|
2528
|
+
return void 0;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2261
2531
|
};
|
|
2262
2532
|
_ts_decorate([
|
|
2263
2533
|
expr("literal"),
|
|
@@ -2344,86 +2614,249 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2344
2614
|
}
|
|
2345
2615
|
async handle(node, proceed) {
|
|
2346
2616
|
if (!this.isCrudQueryNode(node)) {
|
|
2347
|
-
throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
|
|
2617
|
+
throw new RejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
|
|
2348
2618
|
}
|
|
2349
2619
|
if (!this.isMutationQueryNode(node)) {
|
|
2350
2620
|
return proceed(this.transformNode(node));
|
|
2351
2621
|
}
|
|
2352
|
-
|
|
2353
|
-
const mutationModel = this.getMutationModel(node);
|
|
2622
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2354
2623
|
if (InsertQueryNode.is(node)) {
|
|
2355
|
-
const
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2624
|
+
const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
|
|
2625
|
+
let needCheckPreCreate = true;
|
|
2626
|
+
if (!isManyToManyJoinTable) {
|
|
2627
|
+
const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
|
|
2628
|
+
if (constCondition === true) {
|
|
2629
|
+
needCheckPreCreate = false;
|
|
2630
|
+
} else if (constCondition === false) {
|
|
2631
|
+
throw new RejectedByPolicyError(mutationModel);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
if (needCheckPreCreate) {
|
|
2635
|
+
await this.enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed);
|
|
2360
2636
|
}
|
|
2361
2637
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
await this.enforcePreCreatePolicy(node, proceed);
|
|
2367
|
-
}
|
|
2368
|
-
const transformedNode = this.transformNode(node);
|
|
2369
|
-
const result = await proceed(transformedNode);
|
|
2370
|
-
if (!this.onlyReturningId(node)) {
|
|
2638
|
+
const result = await proceed(this.transformNode(node));
|
|
2639
|
+
if (!node.returning || this.onlyReturningId(node)) {
|
|
2640
|
+
return result;
|
|
2641
|
+
} else {
|
|
2371
2642
|
const readBackResult = await this.processReadBack(node, result, proceed);
|
|
2372
2643
|
if (readBackResult.rows.length !== result.rows.length) {
|
|
2373
|
-
throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
|
|
2644
|
+
throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
2374
2645
|
}
|
|
2375
2646
|
return readBackResult;
|
|
2376
|
-
}
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
// #region overrides
|
|
2650
|
+
transformSelectQuery(node) {
|
|
2651
|
+
let whereNode = this.transformNode(node.where);
|
|
2652
|
+
const policyFilter = this.createPolicyFilterForFrom(node.from);
|
|
2653
|
+
if (policyFilter) {
|
|
2654
|
+
whereNode = WhereNode2.create(whereNode?.where ? conjunction(this.dialect, [
|
|
2655
|
+
whereNode.where,
|
|
2656
|
+
policyFilter
|
|
2657
|
+
]) : policyFilter);
|
|
2658
|
+
}
|
|
2659
|
+
const baseResult = super.transformSelectQuery({
|
|
2660
|
+
...node,
|
|
2661
|
+
where: void 0
|
|
2662
|
+
});
|
|
2663
|
+
return {
|
|
2664
|
+
...baseResult,
|
|
2665
|
+
where: whereNode
|
|
2666
|
+
};
|
|
2667
|
+
}
|
|
2668
|
+
transformJoin(node) {
|
|
2669
|
+
const table = this.extractTableName(node.table);
|
|
2670
|
+
if (!table) {
|
|
2671
|
+
return super.transformJoin(node);
|
|
2672
|
+
}
|
|
2673
|
+
const filter = this.buildPolicyFilter(table.model, table.alias, "read");
|
|
2674
|
+
const nestedSelect = {
|
|
2675
|
+
kind: "SelectQueryNode",
|
|
2676
|
+
from: FromNode2.create([
|
|
2677
|
+
node.table
|
|
2678
|
+
]),
|
|
2679
|
+
selections: [
|
|
2680
|
+
SelectionNode2.createSelectAll()
|
|
2681
|
+
],
|
|
2682
|
+
where: WhereNode2.create(filter)
|
|
2683
|
+
};
|
|
2684
|
+
return {
|
|
2685
|
+
...node,
|
|
2686
|
+
table: AliasNode4.create(ParensNode2.create(nestedSelect), IdentifierNode2.create(table.alias ?? table.model))
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2689
|
+
transformInsertQuery(node) {
|
|
2690
|
+
let onConflict = node.onConflict;
|
|
2691
|
+
if (onConflict?.updates) {
|
|
2692
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2693
|
+
const filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2694
|
+
if (onConflict.updateWhere) {
|
|
2695
|
+
onConflict = {
|
|
2696
|
+
...onConflict,
|
|
2697
|
+
updateWhere: WhereNode2.create(conjunction(this.dialect, [
|
|
2698
|
+
onConflict.updateWhere.where,
|
|
2699
|
+
filter
|
|
2700
|
+
]))
|
|
2701
|
+
};
|
|
2702
|
+
} else {
|
|
2703
|
+
onConflict = {
|
|
2704
|
+
...onConflict,
|
|
2705
|
+
updateWhere: WhereNode2.create(filter)
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
const processedNode = onConflict ? {
|
|
2710
|
+
...node,
|
|
2711
|
+
onConflict
|
|
2712
|
+
} : node;
|
|
2713
|
+
const result = super.transformInsertQuery(processedNode);
|
|
2714
|
+
if (!node.returning) {
|
|
2715
|
+
return result;
|
|
2716
|
+
}
|
|
2717
|
+
if (this.onlyReturningId(node)) {
|
|
2377
2718
|
return result;
|
|
2719
|
+
} else {
|
|
2720
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2721
|
+
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2722
|
+
return {
|
|
2723
|
+
...result,
|
|
2724
|
+
returning: ReturningNode.create(idFields.map((field) => SelectionNode2.create(ColumnNode3.create(field))))
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
transformUpdateQuery(node) {
|
|
2729
|
+
const result = super.transformUpdateQuery(node);
|
|
2730
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2731
|
+
let filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2732
|
+
if (node.from) {
|
|
2733
|
+
const joinFilter = this.createPolicyFilterForFrom(node.from);
|
|
2734
|
+
if (joinFilter) {
|
|
2735
|
+
filter = conjunction(this.dialect, [
|
|
2736
|
+
filter,
|
|
2737
|
+
joinFilter
|
|
2738
|
+
]);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
return {
|
|
2742
|
+
...result,
|
|
2743
|
+
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2744
|
+
result.where.where,
|
|
2745
|
+
filter
|
|
2746
|
+
]) : filter)
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
transformDeleteQuery(node) {
|
|
2750
|
+
const result = super.transformDeleteQuery(node);
|
|
2751
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2752
|
+
let filter = this.buildPolicyFilter(mutationModel, alias, "delete");
|
|
2753
|
+
if (node.using) {
|
|
2754
|
+
const joinFilter = this.createPolicyFilterForTables(node.using.tables);
|
|
2755
|
+
if (joinFilter) {
|
|
2756
|
+
filter = conjunction(this.dialect, [
|
|
2757
|
+
filter,
|
|
2758
|
+
joinFilter
|
|
2759
|
+
]);
|
|
2760
|
+
}
|
|
2378
2761
|
}
|
|
2762
|
+
return {
|
|
2763
|
+
...result,
|
|
2764
|
+
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2765
|
+
result.where.where,
|
|
2766
|
+
filter
|
|
2767
|
+
]) : filter)
|
|
2768
|
+
};
|
|
2379
2769
|
}
|
|
2770
|
+
// #endregion
|
|
2771
|
+
// #region helpers
|
|
2380
2772
|
onlyReturningId(node) {
|
|
2381
2773
|
if (!node.returning) {
|
|
2382
2774
|
return true;
|
|
2383
2775
|
}
|
|
2384
|
-
const
|
|
2776
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2777
|
+
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2385
2778
|
const collector = new ColumnCollector();
|
|
2386
2779
|
const selectedColumns = collector.collect(node.returning);
|
|
2387
2780
|
return selectedColumns.every((c) => idFields.includes(c));
|
|
2388
2781
|
}
|
|
2389
|
-
async enforcePreCreatePolicy(node, proceed) {
|
|
2390
|
-
const model = this.getMutationModel(node);
|
|
2782
|
+
async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
|
|
2391
2783
|
const fields = node.columns?.map((c) => c.column.name) ?? [];
|
|
2392
|
-
const valueRows = node.values ? this.unwrapCreateValueRows(node.values,
|
|
2784
|
+
const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
|
|
2393
2785
|
[]
|
|
2394
2786
|
];
|
|
2395
2787
|
for (const values of valueRows) {
|
|
2396
|
-
|
|
2788
|
+
if (isManyToManyJoinTable) {
|
|
2789
|
+
await this.enforcePreCreatePolicyForManyToManyJoinTable(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
2790
|
+
} else {
|
|
2791
|
+
await this.enforcePreCreatePolicyForOne(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
async enforcePreCreatePolicyForManyToManyJoinTable(tableName, fields, values, proceed) {
|
|
2796
|
+
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
2797
|
+
invariant7(m2m);
|
|
2798
|
+
invariant7(fields.includes("A") && fields.includes("B"), "many-to-many join table must have A and B fk fields");
|
|
2799
|
+
const aIndex = fields.indexOf("A");
|
|
2800
|
+
const aNode = values[aIndex];
|
|
2801
|
+
const bIndex = fields.indexOf("B");
|
|
2802
|
+
const bNode = values[bIndex];
|
|
2803
|
+
invariant7(ValueNode3.is(aNode) && ValueNode3.is(bNode), "A and B values must be ValueNode");
|
|
2804
|
+
const aValue = aNode.value;
|
|
2805
|
+
const bValue = bNode.value;
|
|
2806
|
+
invariant7(aValue !== null && aValue !== void 0, "A value cannot be null or undefined");
|
|
2807
|
+
invariant7(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
|
|
2808
|
+
const eb = expressionBuilder3();
|
|
2809
|
+
const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
|
|
2810
|
+
const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new ExpressionWrapper(filterA).as("$t"));
|
|
2811
|
+
const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
|
|
2812
|
+
const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new ExpressionWrapper(filterB).as("$t"));
|
|
2813
|
+
const queryNode = {
|
|
2814
|
+
kind: "SelectQueryNode",
|
|
2815
|
+
selections: [
|
|
2816
|
+
SelectionNode2.create(AliasNode4.create(queryA.toOperationNode(), IdentifierNode2.create("$conditionA"))),
|
|
2817
|
+
SelectionNode2.create(AliasNode4.create(queryB.toOperationNode(), IdentifierNode2.create("$conditionB")))
|
|
2818
|
+
]
|
|
2819
|
+
};
|
|
2820
|
+
const result = await proceed(queryNode);
|
|
2821
|
+
if (!result.rows[0]?.$conditionA) {
|
|
2822
|
+
throw new RejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
|
|
2823
|
+
}
|
|
2824
|
+
if (!result.rows[0]?.$conditionB) {
|
|
2825
|
+
throw new RejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
|
|
2397
2826
|
}
|
|
2398
2827
|
}
|
|
2399
2828
|
async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
|
|
2400
|
-
const allFields = Object.
|
|
2829
|
+
const allFields = Object.entries(requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
|
|
2401
2830
|
const allValues = [];
|
|
2402
|
-
for (const
|
|
2403
|
-
const index = fields.indexOf(
|
|
2831
|
+
for (const [name, _def] of allFields) {
|
|
2832
|
+
const index = fields.indexOf(name);
|
|
2404
2833
|
if (index >= 0) {
|
|
2405
2834
|
allValues.push(values[index]);
|
|
2406
2835
|
} else {
|
|
2407
2836
|
allValues.push(ValueNode3.createImmediate(null));
|
|
2408
2837
|
}
|
|
2409
2838
|
}
|
|
2839
|
+
const eb = expressionBuilder3();
|
|
2410
2840
|
const constTable = {
|
|
2411
2841
|
kind: "SelectQueryNode",
|
|
2412
2842
|
from: FromNode2.create([
|
|
2413
|
-
|
|
2843
|
+
AliasNode4.create(ParensNode2.create(ValuesNode.create([
|
|
2414
2844
|
ValueListNode2.create(allValues)
|
|
2415
2845
|
])), IdentifierNode2.create("$t"))
|
|
2416
2846
|
]),
|
|
2417
|
-
selections: allFields.map((
|
|
2847
|
+
selections: allFields.map(([name, def], index) => {
|
|
2848
|
+
const castedColumnRef = sql4`CAST(${eb.ref(`column${index + 1}`)} as ${sql4.raw(this.dialect.getFieldSqlType(def))})`.as(name);
|
|
2849
|
+
return SelectionNode2.create(castedColumnRef.toOperationNode());
|
|
2850
|
+
})
|
|
2418
2851
|
};
|
|
2419
2852
|
const filter = this.buildPolicyFilter(model, void 0, "create");
|
|
2420
2853
|
const preCreateCheck = {
|
|
2421
2854
|
kind: "SelectQueryNode",
|
|
2422
2855
|
from: FromNode2.create([
|
|
2423
|
-
|
|
2856
|
+
AliasNode4.create(constTable, IdentifierNode2.create(model))
|
|
2424
2857
|
]),
|
|
2425
2858
|
selections: [
|
|
2426
|
-
SelectionNode2.create(
|
|
2859
|
+
SelectionNode2.create(AliasNode4.create(BinaryOperationNode3.create(FunctionNode3.create("COUNT", [
|
|
2427
2860
|
ValueNode3.createImmediate(1)
|
|
2428
2861
|
]), OperatorNode3.create(">"), ValueNode3.createImmediate(0)), IdentifierNode2.create("$condition")))
|
|
2429
2862
|
],
|
|
@@ -2434,31 +2867,35 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2434
2867
|
throw new RejectedByPolicyError(model);
|
|
2435
2868
|
}
|
|
2436
2869
|
}
|
|
2437
|
-
unwrapCreateValueRows(node, model, fields) {
|
|
2870
|
+
unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
|
|
2438
2871
|
if (ValuesNode.is(node)) {
|
|
2439
|
-
return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields));
|
|
2872
|
+
return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields, isManyToManyJoinTable));
|
|
2440
2873
|
} else if (PrimitiveValueListNode.is(node)) {
|
|
2441
2874
|
return [
|
|
2442
|
-
this.unwrapCreateValueRow(node.values, model, fields)
|
|
2875
|
+
this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
|
|
2443
2876
|
];
|
|
2444
2877
|
} else {
|
|
2445
2878
|
throw new InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
|
|
2446
2879
|
}
|
|
2447
2880
|
}
|
|
2448
|
-
unwrapCreateValueRow(data, model, fields) {
|
|
2449
|
-
|
|
2881
|
+
unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
|
|
2882
|
+
invariant7(data.length === fields.length, "data length must match fields length");
|
|
2450
2883
|
const result = [];
|
|
2451
2884
|
for (let i = 0; i < data.length; i++) {
|
|
2452
2885
|
const item = data[i];
|
|
2453
|
-
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
2454
2886
|
if (typeof item === "object" && item && "kind" in item) {
|
|
2455
|
-
|
|
2887
|
+
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
2888
|
+
invariant7(item.kind === "ValueNode", "expecting a ValueNode");
|
|
2456
2889
|
result.push({
|
|
2457
2890
|
node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
|
|
2458
2891
|
raw: item.value
|
|
2459
2892
|
});
|
|
2460
2893
|
} else {
|
|
2461
|
-
|
|
2894
|
+
let value = item;
|
|
2895
|
+
if (!isImplicitManyToManyJoinTable) {
|
|
2896
|
+
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
2897
|
+
value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
|
|
2898
|
+
}
|
|
2462
2899
|
if (Array.isArray(value)) {
|
|
2463
2900
|
result.push({
|
|
2464
2901
|
node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
|
|
@@ -2502,16 +2939,13 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2502
2939
|
if (!this.isMutationQueryNode(node) || !node.returning) {
|
|
2503
2940
|
return result;
|
|
2504
2941
|
}
|
|
2505
|
-
const
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
}
|
|
2509
|
-
const idConditions = this.buildIdConditions(table, result.rows);
|
|
2510
|
-
const policyFilter = this.buildPolicyFilter(table, void 0, "read");
|
|
2942
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2943
|
+
const idConditions = this.buildIdConditions(mutationModel, result.rows);
|
|
2944
|
+
const policyFilter = this.buildPolicyFilter(mutationModel, void 0, "read");
|
|
2511
2945
|
const select = {
|
|
2512
2946
|
kind: "SelectQueryNode",
|
|
2513
2947
|
from: FromNode2.create([
|
|
2514
|
-
|
|
2948
|
+
TableNode4.create(mutationModel)
|
|
2515
2949
|
]),
|
|
2516
2950
|
where: WhereNode2.create(conjunction(this.dialect, [
|
|
2517
2951
|
idConditions,
|
|
@@ -2523,15 +2957,31 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2523
2957
|
return selectResult;
|
|
2524
2958
|
}
|
|
2525
2959
|
buildIdConditions(table, rows) {
|
|
2526
|
-
const idFields =
|
|
2527
|
-
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(
|
|
2960
|
+
const idFields = requireIdFields(this.client.$schema, table);
|
|
2961
|
+
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(ColumnNode3.create(field), OperatorNode3.create("="), ValueNode3.create(row[field]))))));
|
|
2528
2962
|
}
|
|
2529
2963
|
getMutationModel(node) {
|
|
2530
|
-
const r = match8(node).when(InsertQueryNode.is, (node2) =>
|
|
2964
|
+
const r = match8(node).when(InsertQueryNode.is, (node2) => ({
|
|
2965
|
+
mutationModel: getTableName(node2.into),
|
|
2966
|
+
alias: void 0
|
|
2967
|
+
})).when(UpdateQueryNode.is, (node2) => {
|
|
2968
|
+
if (!node2.table) {
|
|
2969
|
+
throw new QueryError("Update query must have a table");
|
|
2970
|
+
}
|
|
2971
|
+
const r2 = this.extractTableName(node2.table);
|
|
2972
|
+
return r2 ? {
|
|
2973
|
+
mutationModel: r2.model,
|
|
2974
|
+
alias: r2.alias
|
|
2975
|
+
} : void 0;
|
|
2976
|
+
}).when(DeleteQueryNode.is, (node2) => {
|
|
2531
2977
|
if (node2.from.froms.length !== 1) {
|
|
2532
|
-
throw new
|
|
2978
|
+
throw new QueryError("Only one from table is supported for delete");
|
|
2533
2979
|
}
|
|
2534
|
-
|
|
2980
|
+
const r2 = this.extractTableName(node2.from.froms[0]);
|
|
2981
|
+
return r2 ? {
|
|
2982
|
+
mutationModel: r2.model,
|
|
2983
|
+
alias: r2.alias
|
|
2984
|
+
} : void 0;
|
|
2535
2985
|
}).exhaustive();
|
|
2536
2986
|
if (!r) {
|
|
2537
2987
|
throw new InternalError(`Unable to get table name for query node: ${node}`);
|
|
@@ -2545,12 +2995,16 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2545
2995
|
return InsertQueryNode.is(node) || UpdateQueryNode.is(node) || DeleteQueryNode.is(node);
|
|
2546
2996
|
}
|
|
2547
2997
|
buildPolicyFilter(model, alias, operation) {
|
|
2998
|
+
const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
|
|
2999
|
+
if (m2mFilter) {
|
|
3000
|
+
return m2mFilter;
|
|
3001
|
+
}
|
|
2548
3002
|
const policies = this.getModelPolicies(model, operation);
|
|
2549
3003
|
if (policies.length === 0) {
|
|
2550
3004
|
return falseNode(this.dialect);
|
|
2551
3005
|
}
|
|
2552
|
-
const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.
|
|
2553
|
-
const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.
|
|
3006
|
+
const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
3007
|
+
const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
2554
3008
|
let combinedPolicy;
|
|
2555
3009
|
if (allows.length === 0) {
|
|
2556
3010
|
combinedPolicy = falseNode(this.dialect);
|
|
@@ -2566,100 +3020,59 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2566
3020
|
}
|
|
2567
3021
|
return combinedPolicy;
|
|
2568
3022
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
node.from?.froms.forEach((from) => {
|
|
2572
|
-
const extractResult = this.extractTableName(from);
|
|
2573
|
-
if (extractResult) {
|
|
2574
|
-
const { model, alias } = extractResult;
|
|
2575
|
-
const filter = this.buildPolicyFilter(model, alias, "read");
|
|
2576
|
-
whereNode = WhereNode2.create(whereNode?.where ? conjunction(this.dialect, [
|
|
2577
|
-
whereNode.where,
|
|
2578
|
-
filter
|
|
2579
|
-
]) : filter);
|
|
2580
|
-
}
|
|
2581
|
-
});
|
|
2582
|
-
const baseResult = super.transformSelectQuery({
|
|
2583
|
-
...node,
|
|
2584
|
-
where: void 0
|
|
2585
|
-
});
|
|
2586
|
-
return {
|
|
2587
|
-
...baseResult,
|
|
2588
|
-
where: whereNode
|
|
2589
|
-
};
|
|
2590
|
-
}
|
|
2591
|
-
transformInsertQuery(node) {
|
|
2592
|
-
const result = super.transformInsertQuery(node);
|
|
2593
|
-
if (!node.returning) {
|
|
2594
|
-
return result;
|
|
2595
|
-
}
|
|
2596
|
-
if (this.onlyReturningId(node)) {
|
|
2597
|
-
return result;
|
|
2598
|
-
} else {
|
|
2599
|
-
const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
|
|
3023
|
+
extractTableName(node) {
|
|
3024
|
+
if (TableNode4.is(node)) {
|
|
2600
3025
|
return {
|
|
2601
|
-
|
|
2602
|
-
returning: ReturningNode.create(idFields.map((field) => SelectionNode2.create(ColumnNode2.create(field))))
|
|
3026
|
+
model: node.table.identifier.name
|
|
2603
3027
|
};
|
|
2604
3028
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
const result = super.transformUpdateQuery(node);
|
|
2608
|
-
const mutationModel = this.getMutationModel(node);
|
|
2609
|
-
const filter = this.buildPolicyFilter(mutationModel, void 0, "update");
|
|
2610
|
-
return {
|
|
2611
|
-
...result,
|
|
2612
|
-
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2613
|
-
result.where.where,
|
|
2614
|
-
filter
|
|
2615
|
-
]) : filter)
|
|
2616
|
-
};
|
|
2617
|
-
}
|
|
2618
|
-
transformDeleteQuery(node) {
|
|
2619
|
-
const result = super.transformDeleteQuery(node);
|
|
2620
|
-
const mutationModel = this.getMutationModel(node);
|
|
2621
|
-
const filter = this.buildPolicyFilter(mutationModel, void 0, "delete");
|
|
2622
|
-
return {
|
|
2623
|
-
...result,
|
|
2624
|
-
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2625
|
-
result.where.where,
|
|
2626
|
-
filter
|
|
2627
|
-
]) : filter)
|
|
2628
|
-
};
|
|
2629
|
-
}
|
|
2630
|
-
extractTableName(from) {
|
|
2631
|
-
if (TableNode3.is(from)) {
|
|
2632
|
-
return {
|
|
2633
|
-
model: from.table.identifier.name
|
|
2634
|
-
};
|
|
2635
|
-
}
|
|
2636
|
-
if (AliasNode3.is(from)) {
|
|
2637
|
-
const inner = this.extractTableName(from.node);
|
|
3029
|
+
if (AliasNode4.is(node)) {
|
|
3030
|
+
const inner = this.extractTableName(node.node);
|
|
2638
3031
|
if (!inner) {
|
|
2639
3032
|
return void 0;
|
|
2640
3033
|
}
|
|
2641
3034
|
return {
|
|
2642
3035
|
model: inner.model,
|
|
2643
|
-
alias: IdentifierNode2.is(
|
|
3036
|
+
alias: IdentifierNode2.is(node.alias) ? node.alias.name : void 0
|
|
2644
3037
|
};
|
|
2645
3038
|
} else {
|
|
2646
3039
|
return void 0;
|
|
2647
3040
|
}
|
|
2648
3041
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
3042
|
+
createPolicyFilterForFrom(node) {
|
|
3043
|
+
if (!node) {
|
|
3044
|
+
return void 0;
|
|
3045
|
+
}
|
|
3046
|
+
return this.createPolicyFilterForTables(node.froms);
|
|
3047
|
+
}
|
|
3048
|
+
createPolicyFilterForTables(tables) {
|
|
3049
|
+
return tables.reduce((acc, table) => {
|
|
3050
|
+
const extractResult = this.extractTableName(table);
|
|
3051
|
+
if (extractResult) {
|
|
3052
|
+
const { model, alias } = extractResult;
|
|
3053
|
+
const filter = this.buildPolicyFilter(model, alias, "read");
|
|
3054
|
+
return acc ? conjunction(this.dialect, [
|
|
3055
|
+
acc,
|
|
3056
|
+
filter
|
|
3057
|
+
]) : filter;
|
|
3058
|
+
}
|
|
3059
|
+
return acc;
|
|
3060
|
+
}, void 0);
|
|
3061
|
+
}
|
|
3062
|
+
compilePolicyCondition(model, alias, operation, policy) {
|
|
3063
|
+
return new ExpressionTransformer(this.client).transform(policy.condition, {
|
|
2651
3064
|
model,
|
|
2652
3065
|
alias,
|
|
2653
3066
|
operation,
|
|
2654
3067
|
auth: this.client.$auth
|
|
2655
3068
|
});
|
|
2656
3069
|
}
|
|
2657
|
-
getModelPolicies(
|
|
2658
|
-
const modelDef = requireModel(this.client.$schema,
|
|
3070
|
+
getModelPolicies(model, operation) {
|
|
3071
|
+
const modelDef = requireModel(this.client.$schema, model);
|
|
2659
3072
|
const result = [];
|
|
2660
3073
|
const extractOperations = /* @__PURE__ */ __name((expr2) => {
|
|
2661
|
-
|
|
2662
|
-
|
|
3074
|
+
invariant7(ExpressionUtils.isLiteral(expr2), "expecting a literal");
|
|
3075
|
+
invariant7(typeof expr2.value === "string", "expecting a string literal");
|
|
2663
3076
|
return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
|
|
2664
3077
|
}, "extractOperations");
|
|
2665
3078
|
if (modelDef.attributes) {
|
|
@@ -2671,8 +3084,84 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2671
3084
|
}
|
|
2672
3085
|
return result;
|
|
2673
3086
|
}
|
|
3087
|
+
resolveManyToManyJoinTable(tableName) {
|
|
3088
|
+
for (const model of Object.values(this.client.$schema.models)) {
|
|
3089
|
+
for (const field of Object.values(model.fields)) {
|
|
3090
|
+
const m2m = getManyToManyRelation(this.client.$schema, model.name, field.name);
|
|
3091
|
+
if (m2m?.joinTable === tableName) {
|
|
3092
|
+
const sortedRecord = [
|
|
3093
|
+
{
|
|
3094
|
+
model: model.name,
|
|
3095
|
+
field: field.name
|
|
3096
|
+
},
|
|
3097
|
+
{
|
|
3098
|
+
model: m2m.otherModel,
|
|
3099
|
+
field: m2m.otherField
|
|
3100
|
+
}
|
|
3101
|
+
].sort(this.manyToManySorter);
|
|
3102
|
+
const firstIdFields = requireIdFields(this.client.$schema, sortedRecord[0].model);
|
|
3103
|
+
const secondIdFields = requireIdFields(this.client.$schema, sortedRecord[1].model);
|
|
3104
|
+
invariant7(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
|
|
3105
|
+
return {
|
|
3106
|
+
firstModel: sortedRecord[0].model,
|
|
3107
|
+
firstField: sortedRecord[0].field,
|
|
3108
|
+
firstIdField: firstIdFields[0],
|
|
3109
|
+
secondModel: sortedRecord[1].model,
|
|
3110
|
+
secondField: sortedRecord[1].field,
|
|
3111
|
+
secondIdField: secondIdFields[0]
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
return void 0;
|
|
3117
|
+
}
|
|
3118
|
+
manyToManySorter(a, b) {
|
|
3119
|
+
return a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field);
|
|
3120
|
+
}
|
|
3121
|
+
isManyToManyJoinTable(tableName) {
|
|
3122
|
+
return !!this.resolveManyToManyJoinTable(tableName);
|
|
3123
|
+
}
|
|
3124
|
+
getModelPolicyFilterForManyToManyJoinTable(tableName, alias, operation) {
|
|
3125
|
+
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
3126
|
+
if (!m2m) {
|
|
3127
|
+
return void 0;
|
|
3128
|
+
}
|
|
3129
|
+
const checkForOperation = operation === "read" ? "read" : "update";
|
|
3130
|
+
const eb = expressionBuilder3();
|
|
3131
|
+
const joinTable = alias ?? tableName;
|
|
3132
|
+
const aQuery = eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
|
|
3133
|
+
const bQuery = eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
|
|
3134
|
+
return eb.and([
|
|
3135
|
+
aQuery,
|
|
3136
|
+
bQuery
|
|
3137
|
+
]).toOperationNode();
|
|
3138
|
+
}
|
|
2674
3139
|
};
|
|
2675
3140
|
|
|
3141
|
+
// src/plugins/policy/functions.ts
|
|
3142
|
+
var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, operation }) => {
|
|
3143
|
+
invariant8(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments');
|
|
3144
|
+
const arg1Node = args[0].toOperationNode();
|
|
3145
|
+
const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
|
|
3146
|
+
if (arg2Node) {
|
|
3147
|
+
invariant8(ValueNode4.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
|
|
3148
|
+
invariant8(CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
|
|
3149
|
+
}
|
|
3150
|
+
const fieldName = extractFieldName(arg1Node);
|
|
3151
|
+
invariant8(fieldName, 'Failed to extract field name from the first argument of "check" function');
|
|
3152
|
+
const fieldDef = requireField(client.$schema, model, fieldName);
|
|
3153
|
+
invariant8(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
|
|
3154
|
+
invariant8(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
|
|
3155
|
+
const relationModel = fieldDef.type;
|
|
3156
|
+
const op = arg2Node ? arg2Node.value : operation;
|
|
3157
|
+
const policyHandler = new PolicyHandler(client);
|
|
3158
|
+
const joinPairs = buildJoinPairs(client.$schema, model, modelAlias, fieldName, relationModel);
|
|
3159
|
+
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))));
|
|
3160
|
+
const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
|
|
3161
|
+
const result = eb.selectFrom(relationModel).where(joinCondition).select(new ExpressionWrapper2(policyCondition).as("$condition"));
|
|
3162
|
+
return result;
|
|
3163
|
+
}, "check");
|
|
3164
|
+
|
|
2676
3165
|
// src/plugins/policy/plugin.ts
|
|
2677
3166
|
var PolicyPlugin = class {
|
|
2678
3167
|
static {
|
|
@@ -2687,6 +3176,11 @@ var PolicyPlugin = class {
|
|
|
2687
3176
|
get description() {
|
|
2688
3177
|
return "Enforces access policies defined in the schema.";
|
|
2689
3178
|
}
|
|
3179
|
+
get functions() {
|
|
3180
|
+
return {
|
|
3181
|
+
check
|
|
3182
|
+
};
|
|
3183
|
+
}
|
|
2690
3184
|
onKyselyQuery({
|
|
2691
3185
|
query,
|
|
2692
3186
|
client,
|
|
@@ -2703,6 +3197,7 @@ var PolicyPlugin = class {
|
|
|
2703
3197
|
};
|
|
2704
3198
|
export {
|
|
2705
3199
|
PolicyPlugin,
|
|
2706
|
-
RejectedByPolicyError
|
|
3200
|
+
RejectedByPolicyError,
|
|
3201
|
+
RejectedByPolicyReason
|
|
2707
3202
|
};
|
|
2708
3203
|
//# sourceMappingURL=index.js.map
|