@zenstackhq/plugin-policy 3.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1556 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var src_exports = {};
23
+ __export(src_exports, {
24
+ PolicyPlugin: () => PolicyPlugin
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+
28
+ // src/functions.ts
29
+ var import_common_helpers4 = require("@zenstackhq/common-helpers");
30
+ var import_runtime8 = require("@zenstackhq/runtime");
31
+ var import_kysely4 = require("kysely");
32
+
33
+ // src/policy-handler.ts
34
+ var import_common_helpers3 = require("@zenstackhq/common-helpers");
35
+ var import_runtime6 = require("@zenstackhq/runtime");
36
+ var import_schema4 = require("@zenstackhq/runtime/schema");
37
+ var import_sdk2 = require("@zenstackhq/sdk");
38
+ var import_kysely3 = require("kysely");
39
+ var import_ts_pattern3 = require("ts-pattern");
40
+
41
+ // src/column-collector.ts
42
+ var import_sdk = require("@zenstackhq/sdk");
43
+ var ColumnCollector = class extends import_sdk.DefaultOperationNodeVisitor {
44
+ static {
45
+ __name(this, "ColumnCollector");
46
+ }
47
+ columns = [];
48
+ collect(node) {
49
+ this.columns = [];
50
+ this.visitNode(node);
51
+ return this.columns;
52
+ }
53
+ visitColumn(node) {
54
+ if (!this.columns.includes(node.column.name)) {
55
+ this.columns.push(node.column.name);
56
+ }
57
+ }
58
+ };
59
+
60
+ // src/expression-transformer.ts
61
+ var import_common_helpers2 = require("@zenstackhq/common-helpers");
62
+ var import_runtime4 = require("@zenstackhq/runtime");
63
+ var import_schema3 = require("@zenstackhq/runtime/schema");
64
+ var import_kysely2 = require("kysely");
65
+ var import_ts_pattern2 = require("ts-pattern");
66
+
67
+ // src/expression-evaluator.ts
68
+ var import_common_helpers = require("@zenstackhq/common-helpers");
69
+ var import_ts_pattern = require("ts-pattern");
70
+ var import_schema = require("@zenstackhq/runtime/schema");
71
+ var ExpressionEvaluator = class {
72
+ static {
73
+ __name(this, "ExpressionEvaluator");
74
+ }
75
+ evaluate(expression, context) {
76
+ const result = (0, import_ts_pattern.match)(expression).when(import_schema.ExpressionUtils.isArray, (expr2) => this.evaluateArray(expr2, context)).when(import_schema.ExpressionUtils.isBinary, (expr2) => this.evaluateBinary(expr2, context)).when(import_schema.ExpressionUtils.isField, (expr2) => this.evaluateField(expr2, context)).when(import_schema.ExpressionUtils.isLiteral, (expr2) => this.evaluateLiteral(expr2)).when(import_schema.ExpressionUtils.isMember, (expr2) => this.evaluateMember(expr2, context)).when(import_schema.ExpressionUtils.isUnary, (expr2) => this.evaluateUnary(expr2, context)).when(import_schema.ExpressionUtils.isCall, (expr2) => this.evaluateCall(expr2, context)).when(import_schema.ExpressionUtils.isThis, () => context.thisValue).when(import_schema.ExpressionUtils.isNull, () => null).exhaustive();
77
+ return result ?? null;
78
+ }
79
+ evaluateCall(expr2, context) {
80
+ if (expr2.function === "auth") {
81
+ return context.auth;
82
+ } else {
83
+ throw new Error(`Unsupported call expression function: ${expr2.function}`);
84
+ }
85
+ }
86
+ evaluateUnary(expr2, context) {
87
+ return (0, import_ts_pattern.match)(expr2.op).with("!", () => !this.evaluate(expr2.operand, context)).exhaustive();
88
+ }
89
+ evaluateMember(expr2, context) {
90
+ let val = this.evaluate(expr2.receiver, context);
91
+ for (const member of expr2.members) {
92
+ val = val?.[member];
93
+ }
94
+ return val;
95
+ }
96
+ evaluateLiteral(expr2) {
97
+ return expr2.value;
98
+ }
99
+ evaluateField(expr2, context) {
100
+ return context.thisValue?.[expr2.field];
101
+ }
102
+ evaluateArray(expr2, context) {
103
+ return expr2.items.map((item) => this.evaluate(item, context));
104
+ }
105
+ evaluateBinary(expr2, context) {
106
+ if (expr2.op === "?" || expr2.op === "!" || expr2.op === "^") {
107
+ return this.evaluateCollectionPredicate(expr2, context);
108
+ }
109
+ const left = this.evaluate(expr2.left, context);
110
+ const right = this.evaluate(expr2.right, context);
111
+ return (0, import_ts_pattern.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", () => {
112
+ const _right = right ?? [];
113
+ (0, import_common_helpers.invariant)(Array.isArray(_right), 'expected array for "in" operator');
114
+ return _right.includes(left);
115
+ }).exhaustive();
116
+ }
117
+ evaluateCollectionPredicate(expr2, context) {
118
+ const op = expr2.op;
119
+ (0, import_common_helpers.invariant)(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
120
+ const left = this.evaluate(expr2.left, context);
121
+ if (!left) {
122
+ return false;
123
+ }
124
+ (0, import_common_helpers.invariant)(Array.isArray(left), "expected array");
125
+ return (0, import_ts_pattern.match)(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
126
+ ...context,
127
+ thisValue: item
128
+ }))).with("!", () => left.every((item) => this.evaluate(expr2.right, {
129
+ ...context,
130
+ thisValue: item
131
+ }))).with("^", () => !left.some((item) => this.evaluate(expr2.right, {
132
+ ...context,
133
+ thisValue: item
134
+ }))).exhaustive();
135
+ }
136
+ };
137
+
138
+ // src/utils.ts
139
+ var import_schema2 = require("@zenstackhq/runtime/schema");
140
+ var import_kysely = require("kysely");
141
+ function trueNode(dialect) {
142
+ return import_kysely.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
143
+ }
144
+ __name(trueNode, "trueNode");
145
+ function falseNode(dialect) {
146
+ return import_kysely.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
147
+ }
148
+ __name(falseNode, "falseNode");
149
+ function isTrueNode(node) {
150
+ return import_kysely.ValueNode.is(node) && (node.value === true || node.value === 1);
151
+ }
152
+ __name(isTrueNode, "isTrueNode");
153
+ function isFalseNode(node) {
154
+ return import_kysely.ValueNode.is(node) && (node.value === false || node.value === 0);
155
+ }
156
+ __name(isFalseNode, "isFalseNode");
157
+ function conjunction(dialect, nodes) {
158
+ if (nodes.length === 0) {
159
+ return trueNode(dialect);
160
+ }
161
+ if (nodes.length === 1) {
162
+ return nodes[0];
163
+ }
164
+ if (nodes.some(isFalseNode)) {
165
+ return falseNode(dialect);
166
+ }
167
+ const items = nodes.filter((n) => !isTrueNode(n));
168
+ if (items.length === 0) {
169
+ return trueNode(dialect);
170
+ }
171
+ return items.reduce((acc, node) => import_kysely.AndNode.create(wrapParensIf(acc, import_kysely.OrNode.is), wrapParensIf(node, import_kysely.OrNode.is)));
172
+ }
173
+ __name(conjunction, "conjunction");
174
+ function disjunction(dialect, nodes) {
175
+ if (nodes.length === 0) {
176
+ return falseNode(dialect);
177
+ }
178
+ if (nodes.length === 1) {
179
+ return nodes[0];
180
+ }
181
+ if (nodes.some(isTrueNode)) {
182
+ return trueNode(dialect);
183
+ }
184
+ const items = nodes.filter((n) => !isFalseNode(n));
185
+ if (items.length === 0) {
186
+ return falseNode(dialect);
187
+ }
188
+ return items.reduce((acc, node) => import_kysely.OrNode.create(wrapParensIf(acc, import_kysely.AndNode.is), wrapParensIf(node, import_kysely.AndNode.is)));
189
+ }
190
+ __name(disjunction, "disjunction");
191
+ function logicalNot(dialect, node) {
192
+ if (isTrueNode(node)) {
193
+ return falseNode(dialect);
194
+ }
195
+ if (isFalseNode(node)) {
196
+ return trueNode(dialect);
197
+ }
198
+ return import_kysely.UnaryOperationNode.create(import_kysely.OperatorNode.create("not"), wrapParensIf(node, (n) => import_kysely.AndNode.is(n) || import_kysely.OrNode.is(n)));
199
+ }
200
+ __name(logicalNot, "logicalNot");
201
+ function wrapParensIf(node, predicate) {
202
+ return predicate(node) ? import_kysely.ParensNode.create(node) : node;
203
+ }
204
+ __name(wrapParensIf, "wrapParensIf");
205
+ function buildIsFalse(node, dialect) {
206
+ if (isFalseNode(node)) {
207
+ return trueNode(dialect);
208
+ } else if (isTrueNode(node)) {
209
+ return falseNode(dialect);
210
+ }
211
+ return import_kysely.BinaryOperationNode.create(
212
+ // coalesce so null is treated as false
213
+ import_kysely.FunctionNode.create("coalesce", [
214
+ node,
215
+ falseNode(dialect)
216
+ ]),
217
+ import_kysely.OperatorNode.create("="),
218
+ falseNode(dialect)
219
+ );
220
+ }
221
+ __name(buildIsFalse, "buildIsFalse");
222
+ function getTableName(node) {
223
+ if (!node) {
224
+ return node;
225
+ }
226
+ if (import_kysely.TableNode.is(node)) {
227
+ return node.table.identifier.name;
228
+ } else if (import_kysely.AliasNode.is(node)) {
229
+ return getTableName(node.node);
230
+ } else if (import_kysely.ReferenceNode.is(node) && node.table) {
231
+ return getTableName(node.table);
232
+ }
233
+ return void 0;
234
+ }
235
+ __name(getTableName, "getTableName");
236
+ function isBeforeInvocation(expr2) {
237
+ return import_schema2.ExpressionUtils.isCall(expr2) && expr2.function === "before";
238
+ }
239
+ __name(isBeforeInvocation, "isBeforeInvocation");
240
+
241
+ // src/expression-transformer.ts
242
+ function _ts_decorate(decorators, target, key, desc) {
243
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
244
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
245
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
246
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
247
+ }
248
+ __name(_ts_decorate, "_ts_decorate");
249
+ function _ts_metadata(k, v) {
250
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
251
+ }
252
+ __name(_ts_metadata, "_ts_metadata");
253
+ var expressionHandlers = /* @__PURE__ */ new Map();
254
+ function expr(kind) {
255
+ return function(_target, _propertyKey, descriptor) {
256
+ if (!expressionHandlers.get(kind)) {
257
+ expressionHandlers.set(kind, descriptor);
258
+ }
259
+ return descriptor;
260
+ };
261
+ }
262
+ __name(expr, "expr");
263
+ var ExpressionTransformer = class {
264
+ static {
265
+ __name(this, "ExpressionTransformer");
266
+ }
267
+ client;
268
+ dialect;
269
+ constructor(client) {
270
+ this.client = client;
271
+ this.dialect = (0, import_runtime4.getCrudDialect)(this.schema, this.clientOptions);
272
+ }
273
+ get schema() {
274
+ return this.client.$schema;
275
+ }
276
+ get clientOptions() {
277
+ return this.client.$options;
278
+ }
279
+ get auth() {
280
+ return this.client.$auth;
281
+ }
282
+ get authType() {
283
+ if (!this.schema.authType) {
284
+ throw new import_runtime4.InternalError('Schema does not have an "authType" specified');
285
+ }
286
+ return this.schema.authType;
287
+ }
288
+ transform(expression, context) {
289
+ const handler = expressionHandlers.get(expression.kind);
290
+ if (!handler) {
291
+ throw new Error(`Unsupported expression kind: ${expression.kind}`);
292
+ }
293
+ return handler.value.call(this, expression, context);
294
+ }
295
+ _literal(expr2) {
296
+ return this.transformValue(expr2.value, typeof expr2.value === "string" ? "String" : typeof expr2.value === "boolean" ? "Boolean" : "Int");
297
+ }
298
+ _array(expr2, context) {
299
+ return import_kysely2.ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
300
+ }
301
+ _field(expr2, context) {
302
+ const fieldDef = import_runtime4.QueryUtils.requireField(this.schema, context.model, expr2.field);
303
+ if (!fieldDef.relation) {
304
+ return this.createColumnRef(expr2.field, context);
305
+ } else {
306
+ const { memberFilter, memberSelect, ...restContext } = context;
307
+ const relation = this.transformRelationAccess(expr2.field, fieldDef.type, restContext);
308
+ return {
309
+ ...relation,
310
+ where: this.mergeWhere(relation.where, memberFilter),
311
+ selections: memberSelect ? [
312
+ memberSelect
313
+ ] : relation.selections
314
+ };
315
+ }
316
+ }
317
+ mergeWhere(where, memberFilter) {
318
+ if (!where) {
319
+ return import_kysely2.WhereNode.create(memberFilter ?? trueNode(this.dialect));
320
+ }
321
+ if (!memberFilter) {
322
+ return where;
323
+ }
324
+ return import_kysely2.WhereNode.create(conjunction(this.dialect, [
325
+ where.where,
326
+ memberFilter
327
+ ]));
328
+ }
329
+ _null() {
330
+ return import_kysely2.ValueNode.createImmediate(null);
331
+ }
332
+ _binary(expr2, context) {
333
+ if (expr2.op === "&&") {
334
+ return conjunction(this.dialect, [
335
+ this.transform(expr2.left, context),
336
+ this.transform(expr2.right, context)
337
+ ]);
338
+ } else if (expr2.op === "||") {
339
+ return disjunction(this.dialect, [
340
+ this.transform(expr2.left, context),
341
+ this.transform(expr2.right, context)
342
+ ]);
343
+ }
344
+ if (this.isAuthCall(expr2.left) || this.isAuthCall(expr2.right)) {
345
+ return this.transformAuthBinary(expr2, context);
346
+ }
347
+ const op = expr2.op;
348
+ if (op === "?" || op === "!" || op === "^") {
349
+ return this.transformCollectionPredicate(expr2, context);
350
+ }
351
+ const { normalizedLeft, normalizedRight } = this.normalizeBinaryOperationOperands(expr2, context);
352
+ const left = this.transform(normalizedLeft, context);
353
+ const right = this.transform(normalizedRight, context);
354
+ if (op === "in") {
355
+ if (this.isNullNode(left)) {
356
+ return this.transformValue(false, "Boolean");
357
+ } else {
358
+ if (import_kysely2.ValueListNode.is(right)) {
359
+ return import_kysely2.BinaryOperationNode.create(left, import_kysely2.OperatorNode.create("in"), right);
360
+ } else {
361
+ return import_kysely2.BinaryOperationNode.create(left, import_kysely2.OperatorNode.create("="), import_kysely2.FunctionNode.create("any", [
362
+ right
363
+ ]));
364
+ }
365
+ }
366
+ }
367
+ if (this.isNullNode(right)) {
368
+ return this.transformNullCheck(left, expr2.op);
369
+ } else if (this.isNullNode(left)) {
370
+ return this.transformNullCheck(right, expr2.op);
371
+ } else {
372
+ return import_kysely2.BinaryOperationNode.create(left, this.transformOperator(op), right);
373
+ }
374
+ }
375
+ transformNullCheck(expr2, operator) {
376
+ (0, import_common_helpers2.invariant)(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
377
+ if (import_kysely2.ValueNode.is(expr2)) {
378
+ if (expr2.value === null) {
379
+ return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
380
+ } else {
381
+ return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
382
+ }
383
+ } else {
384
+ return operator === "==" ? import_kysely2.BinaryOperationNode.create(expr2, import_kysely2.OperatorNode.create("is"), import_kysely2.ValueNode.createImmediate(null)) : import_kysely2.BinaryOperationNode.create(expr2, import_kysely2.OperatorNode.create("is not"), import_kysely2.ValueNode.createImmediate(null));
385
+ }
386
+ }
387
+ normalizeBinaryOperationOperands(expr2, context) {
388
+ let normalizedLeft = expr2.left;
389
+ if (this.isRelationField(expr2.left, context.model)) {
390
+ (0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
391
+ const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
392
+ (0, import_common_helpers2.invariant)(leftRelDef, "failed to get relation field definition");
393
+ const idFields = import_runtime4.QueryUtils.requireIdFields(this.schema, leftRelDef.type);
394
+ normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
395
+ }
396
+ let normalizedRight = expr2.right;
397
+ if (this.isRelationField(expr2.right, context.model)) {
398
+ (0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
399
+ const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
400
+ (0, import_common_helpers2.invariant)(rightRelDef, "failed to get relation field definition");
401
+ const idFields = import_runtime4.QueryUtils.requireIdFields(this.schema, rightRelDef.type);
402
+ normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
403
+ }
404
+ return {
405
+ normalizedLeft,
406
+ normalizedRight
407
+ };
408
+ }
409
+ transformCollectionPredicate(expr2, context) {
410
+ (0, import_common_helpers2.invariant)(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
411
+ if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
412
+ const value = new ExpressionEvaluator().evaluate(expr2, {
413
+ auth: this.auth
414
+ });
415
+ return this.transformValue(value, "Boolean");
416
+ }
417
+ (0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isField(expr2.left) || import_schema3.ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
418
+ let newContextModel;
419
+ const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
420
+ if (fieldDef) {
421
+ (0, import_common_helpers2.invariant)(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
422
+ newContextModel = fieldDef.type;
423
+ } else {
424
+ (0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isMember(expr2.left) && import_schema3.ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
425
+ const fieldDef2 = import_runtime4.QueryUtils.requireField(this.schema, context.model, expr2.left.receiver.field);
426
+ newContextModel = fieldDef2.type;
427
+ for (const member of expr2.left.members) {
428
+ const memberDef = import_runtime4.QueryUtils.requireField(this.schema, newContextModel, member);
429
+ newContextModel = memberDef.type;
430
+ }
431
+ }
432
+ let predicateFilter = this.transform(expr2.right, {
433
+ ...context,
434
+ model: newContextModel,
435
+ alias: void 0
436
+ });
437
+ if (expr2.op === "!") {
438
+ predicateFilter = logicalNot(this.dialect, predicateFilter);
439
+ }
440
+ const count = import_kysely2.FunctionNode.create("count", [
441
+ import_kysely2.ValueNode.createImmediate(1)
442
+ ]);
443
+ const predicateResult = (0, import_ts_pattern2.match)(expr2.op).with("?", () => import_kysely2.BinaryOperationNode.create(count, import_kysely2.OperatorNode.create(">"), import_kysely2.ValueNode.createImmediate(0))).with("!", () => import_kysely2.BinaryOperationNode.create(count, import_kysely2.OperatorNode.create("="), import_kysely2.ValueNode.createImmediate(0))).with("^", () => import_kysely2.BinaryOperationNode.create(count, import_kysely2.OperatorNode.create("="), import_kysely2.ValueNode.createImmediate(0))).exhaustive();
444
+ return this.transform(expr2.left, {
445
+ ...context,
446
+ memberSelect: import_kysely2.SelectionNode.create(import_kysely2.AliasNode.create(predicateResult, import_kysely2.IdentifierNode.create("$t"))),
447
+ memberFilter: predicateFilter
448
+ });
449
+ }
450
+ transformAuthBinary(expr2, context) {
451
+ if (expr2.op !== "==" && expr2.op !== "!=") {
452
+ throw new import_runtime4.QueryError(`Unsupported operator for \`auth()\` in policy of model "${context.model}": ${expr2.op}`);
453
+ }
454
+ let authExpr;
455
+ let other;
456
+ if (this.isAuthCall(expr2.left)) {
457
+ authExpr = expr2.left;
458
+ other = expr2.right;
459
+ } else {
460
+ authExpr = expr2.right;
461
+ other = expr2.left;
462
+ }
463
+ if (import_schema3.ExpressionUtils.isNull(other)) {
464
+ return this.transformValue(expr2.op === "==" ? !this.auth : !!this.auth, "Boolean");
465
+ } else {
466
+ const authModel = import_runtime4.QueryUtils.getModel(this.schema, this.authType);
467
+ if (!authModel) {
468
+ throw new import_runtime4.QueryError(`Unsupported use of \`auth()\` in policy of model "${context.model}", comparing with \`auth()\` is only possible when auth type is a model`);
469
+ }
470
+ const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
471
+ (0, import_common_helpers2.invariant)(idFields.length > 0, "auth type model must have at least one id field");
472
+ const conditions = idFields.map((fieldName) => import_schema3.ExpressionUtils.binary(import_schema3.ExpressionUtils.member(authExpr, [
473
+ fieldName
474
+ ]), "==", this.makeOrAppendMember(other, fieldName)));
475
+ let result = this.buildAnd(conditions);
476
+ if (expr2.op === "!=") {
477
+ result = this.buildLogicalNot(result);
478
+ }
479
+ return this.transform(result, context);
480
+ }
481
+ }
482
+ makeOrAppendMember(other, fieldName) {
483
+ if (import_schema3.ExpressionUtils.isMember(other)) {
484
+ return import_schema3.ExpressionUtils.member(other.receiver, [
485
+ ...other.members,
486
+ fieldName
487
+ ]);
488
+ } else {
489
+ return import_schema3.ExpressionUtils.member(other, [
490
+ fieldName
491
+ ]);
492
+ }
493
+ }
494
+ transformValue(value, type) {
495
+ if (value === true) {
496
+ return trueNode(this.dialect);
497
+ } else if (value === false) {
498
+ return falseNode(this.dialect);
499
+ } else {
500
+ return import_kysely2.ValueNode.create(this.dialect.transformPrimitive(value, type, false) ?? null);
501
+ }
502
+ }
503
+ _unary(expr2, context) {
504
+ (0, import_common_helpers2.invariant)(expr2.op === "!", 'only "!" operator is supported');
505
+ return logicalNot(this.dialect, this.transform(expr2.operand, context));
506
+ }
507
+ transformOperator(op) {
508
+ const mappedOp = (0, import_ts_pattern2.match)(op).with("==", () => "=").otherwise(() => op);
509
+ return import_kysely2.OperatorNode.create(mappedOp);
510
+ }
511
+ _call(expr2, context) {
512
+ const result = this.transformCall(expr2, context);
513
+ return result.toOperationNode();
514
+ }
515
+ transformCall(expr2, context) {
516
+ const func = this.getFunctionImpl(expr2.function);
517
+ if (!func) {
518
+ throw new import_runtime4.QueryError(`Function not implemented: ${expr2.function}`);
519
+ }
520
+ const eb = (0, import_kysely2.expressionBuilder)();
521
+ return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
522
+ client: this.client,
523
+ dialect: this.dialect,
524
+ model: context.model,
525
+ modelAlias: context.alias ?? context.model,
526
+ operation: context.operation
527
+ });
528
+ }
529
+ getFunctionImpl(functionName) {
530
+ let func = this.clientOptions.functions?.[functionName];
531
+ if (!func) {
532
+ for (const plugin of this.clientOptions.plugins ?? []) {
533
+ if (plugin.functions?.[functionName]) {
534
+ func = plugin.functions[functionName];
535
+ break;
536
+ }
537
+ }
538
+ }
539
+ return func;
540
+ }
541
+ transformCallArg(eb, arg, context) {
542
+ if (import_schema3.ExpressionUtils.isLiteral(arg)) {
543
+ return eb.val(arg.value);
544
+ }
545
+ if (import_schema3.ExpressionUtils.isField(arg)) {
546
+ return eb.ref(arg.field);
547
+ }
548
+ if (import_schema3.ExpressionUtils.isCall(arg)) {
549
+ return this.transformCall(arg, context);
550
+ }
551
+ if (this.isAuthMember(arg)) {
552
+ const valNode = this.valueMemberAccess(this.auth, arg, this.authType);
553
+ return valNode ? eb.val(valNode.value) : eb.val(null);
554
+ }
555
+ throw new import_runtime4.InternalError(`Unsupported argument expression: ${arg.kind}`);
556
+ }
557
+ _member(expr2, context) {
558
+ if (this.isAuthCall(expr2.receiver)) {
559
+ return this.valueMemberAccess(this.auth, expr2, this.authType);
560
+ }
561
+ if (isBeforeInvocation(expr2.receiver)) {
562
+ (0, import_common_helpers2.invariant)(context.operation === "post-update", "before() can only be used in post-update policy");
563
+ (0, import_common_helpers2.invariant)(expr2.members.length === 1, "before() can only be followed by a scalar field access");
564
+ return import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(expr2.members[0]), import_kysely2.TableNode.create("$before"));
565
+ }
566
+ (0, import_common_helpers2.invariant)(import_schema3.ExpressionUtils.isField(expr2.receiver) || import_schema3.ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
567
+ let members = expr2.members;
568
+ let receiver;
569
+ const { memberFilter, memberSelect, ...restContext } = context;
570
+ if (import_schema3.ExpressionUtils.isThis(expr2.receiver)) {
571
+ if (expr2.members.length === 1) {
572
+ return this._field(import_schema3.ExpressionUtils.field(expr2.members[0]), context);
573
+ } else {
574
+ const firstMemberFieldDef = import_runtime4.QueryUtils.requireField(this.schema, context.model, expr2.members[0]);
575
+ receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
576
+ members = expr2.members.slice(1);
577
+ }
578
+ } else {
579
+ receiver = this.transform(expr2.receiver, restContext);
580
+ }
581
+ (0, import_common_helpers2.invariant)(import_kysely2.SelectQueryNode.is(receiver), "expected receiver to be select query");
582
+ let startType;
583
+ if (import_schema3.ExpressionUtils.isField(expr2.receiver)) {
584
+ const receiverField = import_runtime4.QueryUtils.requireField(this.schema, context.model, expr2.receiver.field);
585
+ startType = receiverField.type;
586
+ } else {
587
+ startType = context.model;
588
+ }
589
+ const memberFields = [];
590
+ let currType = startType;
591
+ for (const member of members) {
592
+ const fieldDef = import_runtime4.QueryUtils.requireField(this.schema, currType, member);
593
+ memberFields.push({
594
+ fieldDef,
595
+ fromModel: currType
596
+ });
597
+ currType = fieldDef.type;
598
+ }
599
+ let currNode = void 0;
600
+ for (let i = members.length - 1; i >= 0; i--) {
601
+ const member = members[i];
602
+ const { fieldDef, fromModel } = memberFields[i];
603
+ if (fieldDef.relation) {
604
+ const relation = this.transformRelationAccess(member, fieldDef.type, {
605
+ ...restContext,
606
+ model: fromModel,
607
+ alias: void 0
608
+ });
609
+ if (currNode) {
610
+ currNode = {
611
+ ...relation,
612
+ selections: [
613
+ import_kysely2.SelectionNode.create(import_kysely2.AliasNode.create(currNode, import_kysely2.IdentifierNode.create(members[i + 1])))
614
+ ]
615
+ };
616
+ } else {
617
+ currNode = {
618
+ ...relation,
619
+ where: this.mergeWhere(relation.where, memberFilter),
620
+ selections: memberSelect ? [
621
+ memberSelect
622
+ ] : relation.selections
623
+ };
624
+ }
625
+ } else {
626
+ (0, import_common_helpers2.invariant)(i === members.length - 1, "plain field access must be the last segment");
627
+ (0, import_common_helpers2.invariant)(!currNode, "plain field access must be the last segment");
628
+ currNode = import_kysely2.ColumnNode.create(member);
629
+ }
630
+ }
631
+ return {
632
+ ...receiver,
633
+ selections: [
634
+ import_kysely2.SelectionNode.create(import_kysely2.AliasNode.create(currNode, import_kysely2.IdentifierNode.create("$t")))
635
+ ]
636
+ };
637
+ }
638
+ valueMemberAccess(receiver, expr2, receiverType) {
639
+ if (!receiver) {
640
+ return import_kysely2.ValueNode.createImmediate(null);
641
+ }
642
+ if (expr2.members.length !== 1) {
643
+ throw new Error(`Only single member access is supported`);
644
+ }
645
+ const field = expr2.members[0];
646
+ const fieldDef = import_runtime4.QueryUtils.requireField(this.schema, receiverType, field);
647
+ const fieldValue = receiver[field] ?? null;
648
+ return this.transformValue(fieldValue, fieldDef.type);
649
+ }
650
+ transformRelationAccess(field, relationModel, context) {
651
+ const m2m = import_runtime4.QueryUtils.getManyToManyRelation(this.schema, context.model, field);
652
+ if (m2m) {
653
+ return this.transformManyToManyRelationAccess(m2m, context);
654
+ }
655
+ const fromModel = context.model;
656
+ const relationFieldDef = import_runtime4.QueryUtils.requireField(this.schema, fromModel, field);
657
+ const { keyPairs, ownedByModel } = import_runtime4.QueryUtils.getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
658
+ let condition;
659
+ if (ownedByModel) {
660
+ condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => {
661
+ let fkRef = import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(fk), import_kysely2.TableNode.create(context.alias ?? fromModel));
662
+ if (relationFieldDef.originModel && relationFieldDef.originModel !== fromModel) {
663
+ fkRef = this.buildDelegateBaseFieldSelect(fromModel, context.alias ?? fromModel, fk, relationFieldDef.originModel);
664
+ }
665
+ return import_kysely2.BinaryOperationNode.create(fkRef, import_kysely2.OperatorNode.create("="), import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(pk), import_kysely2.TableNode.create(relationModel)));
666
+ }));
667
+ } else {
668
+ condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => import_kysely2.BinaryOperationNode.create(import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(pk), import_kysely2.TableNode.create(context.alias ?? fromModel)), import_kysely2.OperatorNode.create("="), import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(fk), import_kysely2.TableNode.create(relationModel)))));
669
+ }
670
+ return {
671
+ kind: "SelectQueryNode",
672
+ from: import_kysely2.FromNode.create([
673
+ import_kysely2.TableNode.create(relationModel)
674
+ ]),
675
+ where: import_kysely2.WhereNode.create(condition)
676
+ };
677
+ }
678
+ transformManyToManyRelationAccess(m2m, context) {
679
+ const eb = (0, import_kysely2.expressionBuilder)();
680
+ 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}`));
681
+ return relationQuery.toOperationNode();
682
+ }
683
+ createColumnRef(column, context) {
684
+ const tableName = context.alias ?? context.model;
685
+ if (context.operation === "create") {
686
+ return import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(column), import_kysely2.TableNode.create(tableName));
687
+ }
688
+ const fieldDef = import_runtime4.QueryUtils.requireField(this.schema, context.model, column);
689
+ if (!fieldDef.originModel || fieldDef.originModel === context.model) {
690
+ return import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(column), import_kysely2.TableNode.create(tableName));
691
+ }
692
+ return this.buildDelegateBaseFieldSelect(context.model, tableName, column, fieldDef.originModel);
693
+ }
694
+ buildDelegateBaseFieldSelect(model, modelAlias, field, baseModel) {
695
+ const idFields = import_runtime4.QueryUtils.requireIdFields(this.client.$schema, model);
696
+ return {
697
+ kind: "SelectQueryNode",
698
+ from: import_kysely2.FromNode.create([
699
+ import_kysely2.TableNode.create(baseModel)
700
+ ]),
701
+ selections: [
702
+ import_kysely2.SelectionNode.create(import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(field), import_kysely2.TableNode.create(baseModel)))
703
+ ],
704
+ where: import_kysely2.WhereNode.create(conjunction(this.dialect, idFields.map((idField) => import_kysely2.BinaryOperationNode.create(import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(idField), import_kysely2.TableNode.create(baseModel)), import_kysely2.OperatorNode.create("="), import_kysely2.ReferenceNode.create(import_kysely2.ColumnNode.create(idField), import_kysely2.TableNode.create(modelAlias))))))
705
+ };
706
+ }
707
+ isAuthCall(value) {
708
+ return import_schema3.ExpressionUtils.isCall(value) && value.function === "auth";
709
+ }
710
+ isAuthMember(expr2) {
711
+ return import_schema3.ExpressionUtils.isMember(expr2) && this.isAuthCall(expr2.receiver);
712
+ }
713
+ isNullNode(node) {
714
+ return import_kysely2.ValueNode.is(node) && node.value === null;
715
+ }
716
+ buildLogicalNot(result) {
717
+ return import_schema3.ExpressionUtils.unary("!", result);
718
+ }
719
+ buildAnd(conditions) {
720
+ if (conditions.length === 0) {
721
+ return import_schema3.ExpressionUtils.literal(true);
722
+ } else if (conditions.length === 1) {
723
+ return conditions[0];
724
+ } else {
725
+ return conditions.reduce((acc, condition) => import_schema3.ExpressionUtils.binary(acc, "&&", condition));
726
+ }
727
+ }
728
+ isRelationField(expr2, model) {
729
+ const fieldDef = this.getFieldDefFromFieldRef(expr2, model);
730
+ return !!fieldDef?.relation;
731
+ }
732
+ getFieldDefFromFieldRef(expr2, model) {
733
+ if (import_schema3.ExpressionUtils.isField(expr2)) {
734
+ return import_runtime4.QueryUtils.requireField(this.schema, model, expr2.field);
735
+ } else if (import_schema3.ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && import_schema3.ExpressionUtils.isThis(expr2.receiver)) {
736
+ return import_runtime4.QueryUtils.requireField(this.schema, model, expr2.members[0]);
737
+ } else {
738
+ return void 0;
739
+ }
740
+ }
741
+ };
742
+ _ts_decorate([
743
+ expr("literal"),
744
+ _ts_metadata("design:type", Function),
745
+ _ts_metadata("design:paramtypes", [
746
+ typeof LiteralExpression === "undefined" ? Object : LiteralExpression
747
+ ]),
748
+ _ts_metadata("design:returntype", void 0)
749
+ ], ExpressionTransformer.prototype, "_literal", null);
750
+ _ts_decorate([
751
+ expr("array"),
752
+ _ts_metadata("design:type", Function),
753
+ _ts_metadata("design:paramtypes", [
754
+ typeof ArrayExpression === "undefined" ? Object : ArrayExpression,
755
+ typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
756
+ ]),
757
+ _ts_metadata("design:returntype", void 0)
758
+ ], ExpressionTransformer.prototype, "_array", null);
759
+ _ts_decorate([
760
+ expr("field"),
761
+ _ts_metadata("design:type", Function),
762
+ _ts_metadata("design:paramtypes", [
763
+ typeof FieldExpression === "undefined" ? Object : FieldExpression,
764
+ typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
765
+ ]),
766
+ _ts_metadata("design:returntype", void 0)
767
+ ], ExpressionTransformer.prototype, "_field", null);
768
+ _ts_decorate([
769
+ expr("null"),
770
+ _ts_metadata("design:type", Function),
771
+ _ts_metadata("design:paramtypes", []),
772
+ _ts_metadata("design:returntype", void 0)
773
+ ], ExpressionTransformer.prototype, "_null", null);
774
+ _ts_decorate([
775
+ expr("binary"),
776
+ _ts_metadata("design:type", Function),
777
+ _ts_metadata("design:paramtypes", [
778
+ typeof BinaryExpression === "undefined" ? Object : BinaryExpression,
779
+ typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
780
+ ]),
781
+ _ts_metadata("design:returntype", void 0)
782
+ ], ExpressionTransformer.prototype, "_binary", null);
783
+ _ts_decorate([
784
+ expr("unary"),
785
+ _ts_metadata("design:type", Function),
786
+ _ts_metadata("design:paramtypes", [
787
+ typeof UnaryExpression === "undefined" ? Object : UnaryExpression,
788
+ typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
789
+ ]),
790
+ _ts_metadata("design:returntype", void 0)
791
+ ], ExpressionTransformer.prototype, "_unary", null);
792
+ _ts_decorate([
793
+ expr("call"),
794
+ _ts_metadata("design:type", Function),
795
+ _ts_metadata("design:paramtypes", [
796
+ typeof CallExpression === "undefined" ? Object : CallExpression,
797
+ typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
798
+ ]),
799
+ _ts_metadata("design:returntype", void 0)
800
+ ], ExpressionTransformer.prototype, "_call", null);
801
+ _ts_decorate([
802
+ expr("member"),
803
+ _ts_metadata("design:type", Function),
804
+ _ts_metadata("design:paramtypes", [
805
+ typeof MemberExpression === "undefined" ? Object : MemberExpression,
806
+ typeof ExpressionTransformerContext === "undefined" ? Object : ExpressionTransformerContext
807
+ ]),
808
+ _ts_metadata("design:returntype", void 0)
809
+ ], ExpressionTransformer.prototype, "_member", null);
810
+
811
+ // src/policy-handler.ts
812
+ var PolicyHandler = class extends import_kysely3.OperationNodeTransformer {
813
+ static {
814
+ __name(this, "PolicyHandler");
815
+ }
816
+ client;
817
+ dialect;
818
+ constructor(client) {
819
+ super(), this.client = client;
820
+ this.dialect = (0, import_runtime6.getCrudDialect)(this.client.$schema, this.client.$options);
821
+ }
822
+ get kysely() {
823
+ return this.client.$qb;
824
+ }
825
+ async handle(node, proceed) {
826
+ if (!this.isCrudQueryNode(node)) {
827
+ throw new import_runtime6.RejectedByPolicyError(void 0, import_runtime6.RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
828
+ }
829
+ if (!this.isMutationQueryNode(node)) {
830
+ return proceed(this.transformNode(node));
831
+ }
832
+ const { mutationModel } = this.getMutationModel(node);
833
+ if (import_kysely3.InsertQueryNode.is(node)) {
834
+ const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
835
+ let needCheckPreCreate = true;
836
+ if (!isManyToManyJoinTable) {
837
+ const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
838
+ if (constCondition === true) {
839
+ needCheckPreCreate = false;
840
+ } else if (constCondition === false) {
841
+ throw new import_runtime6.RejectedByPolicyError(mutationModel);
842
+ }
843
+ }
844
+ if (needCheckPreCreate) {
845
+ await this.enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed);
846
+ }
847
+ }
848
+ const hasPostUpdatePolicies = import_kysely3.UpdateQueryNode.is(node) && this.hasPostUpdatePolicies(mutationModel);
849
+ let beforeUpdateInfo;
850
+ if (hasPostUpdatePolicies) {
851
+ beforeUpdateInfo = await this.loadBeforeUpdateEntities(mutationModel, node.where, proceed);
852
+ }
853
+ const result = await proceed(this.transformNode(node));
854
+ if (hasPostUpdatePolicies && result.rows.length > 0) {
855
+ if (beforeUpdateInfo) {
856
+ (0, import_common_helpers3.invariant)(beforeUpdateInfo.rows.length === result.rows.length);
857
+ const idFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
858
+ for (const postRow of result.rows) {
859
+ const beforeRow = beforeUpdateInfo.rows.find((r) => idFields.every((f) => r[f] === postRow[f]));
860
+ if (!beforeRow) {
861
+ throw new import_runtime6.QueryError("Before-update and after-update rows do not match by id. If you have post-update policies on a model, updating id fields is not supported.");
862
+ }
863
+ }
864
+ }
865
+ const idConditions = this.buildIdConditions(mutationModel, result.rows);
866
+ const postUpdateFilter = this.buildPolicyFilter(mutationModel, void 0, "post-update");
867
+ const eb = (0, import_kysely3.expressionBuilder)();
868
+ const beforeUpdateTable = beforeUpdateInfo ? {
869
+ kind: "SelectQueryNode",
870
+ from: import_kysely3.FromNode.create([
871
+ import_kysely3.ParensNode.create(import_kysely3.ValuesNode.create(beforeUpdateInfo.rows.map((r) => import_kysely3.PrimitiveValueListNode.create(beforeUpdateInfo.fields.map((f) => r[f])))))
872
+ ]),
873
+ selections: beforeUpdateInfo.fields.map((name, index) => {
874
+ const def = import_runtime6.QueryUtils.requireField(this.client.$schema, mutationModel, name);
875
+ const castedColumnRef = import_kysely3.sql`CAST(${eb.ref(`column${index + 1}`)} as ${import_kysely3.sql.raw(this.dialect.getFieldSqlType(def))})`.as(name);
876
+ return import_kysely3.SelectionNode.create(castedColumnRef.toOperationNode());
877
+ })
878
+ } : void 0;
879
+ const postUpdateQuery = eb.selectFrom(mutationModel).select(() => [
880
+ eb(eb.fn("COUNT", [
881
+ eb.lit(1)
882
+ ]), "=", result.rows.length).as("$condition")
883
+ ]).where(() => new import_kysely3.ExpressionWrapper(conjunction(this.dialect, [
884
+ idConditions,
885
+ postUpdateFilter
886
+ ]))).$if(!!beforeUpdateInfo, (qb) => qb.leftJoin(() => new import_kysely3.ExpressionWrapper(beforeUpdateTable).as("$before"), (join) => {
887
+ const idFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
888
+ return idFields.reduce((acc, f) => acc.onRef(`${mutationModel}.${f}`, "=", `$before.${f}`), join);
889
+ }));
890
+ const postUpdateResult = await proceed(postUpdateQuery.toOperationNode());
891
+ if (!postUpdateResult.rows[0]?.$condition) {
892
+ throw new import_runtime6.RejectedByPolicyError(mutationModel, import_runtime6.RejectedByPolicyReason.NO_ACCESS, "some or all updated rows failed to pass post-update policy check");
893
+ }
894
+ }
895
+ if (!node.returning || this.onlyReturningId(node)) {
896
+ return this.postProcessMutationResult(result, node);
897
+ } else {
898
+ const readBackResult = await this.processReadBack(node, result, proceed);
899
+ if (readBackResult.rows.length !== result.rows.length) {
900
+ throw new import_runtime6.RejectedByPolicyError(mutationModel, import_runtime6.RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
901
+ }
902
+ return readBackResult;
903
+ }
904
+ }
905
+ // correction to kysely mutation result may be needed because we might have added
906
+ // returning clause to the query and caused changes to the result shape
907
+ postProcessMutationResult(result, node) {
908
+ if (node.returning) {
909
+ return result;
910
+ } else {
911
+ return {
912
+ ...result,
913
+ rows: [],
914
+ numAffectedRows: result.numAffectedRows ?? BigInt(result.rows.length)
915
+ };
916
+ }
917
+ }
918
+ hasPostUpdatePolicies(model) {
919
+ const policies = this.getModelPolicies(model, "post-update");
920
+ return policies.length > 0;
921
+ }
922
+ async loadBeforeUpdateEntities(model, where, proceed) {
923
+ const beforeUpdateAccessFields = this.getFieldsAccessForBeforeUpdatePolicies(model);
924
+ if (!beforeUpdateAccessFields || beforeUpdateAccessFields.length === 0) {
925
+ return void 0;
926
+ }
927
+ const policyFilter = this.buildPolicyFilter(model, model, "update");
928
+ const combinedFilter = where ? conjunction(this.dialect, [
929
+ where.where,
930
+ policyFilter
931
+ ]) : policyFilter;
932
+ const query = {
933
+ kind: "SelectQueryNode",
934
+ from: import_kysely3.FromNode.create([
935
+ import_kysely3.TableNode.create(model)
936
+ ]),
937
+ where: import_kysely3.WhereNode.create(combinedFilter),
938
+ selections: [
939
+ ...beforeUpdateAccessFields.map((f) => import_kysely3.SelectionNode.create(import_kysely3.ColumnNode.create(f)))
940
+ ]
941
+ };
942
+ const result = await proceed(query);
943
+ return {
944
+ fields: beforeUpdateAccessFields,
945
+ rows: result.rows
946
+ };
947
+ }
948
+ getFieldsAccessForBeforeUpdatePolicies(model) {
949
+ const policies = this.getModelPolicies(model, "post-update");
950
+ if (policies.length === 0) {
951
+ return void 0;
952
+ }
953
+ const fields = /* @__PURE__ */ new Set();
954
+ const fieldCollector = new class extends import_sdk2.ExpressionVisitor {
955
+ visitMember(e) {
956
+ if (isBeforeInvocation(e.receiver)) {
957
+ (0, import_common_helpers3.invariant)(e.members.length === 1, "before() can only be followed by a scalar field access");
958
+ fields.add(e.members[0]);
959
+ }
960
+ super.visitMember(e);
961
+ }
962
+ }();
963
+ for (const policy of policies) {
964
+ fieldCollector.visit(policy.condition);
965
+ }
966
+ if (fields.size === 0) {
967
+ return void 0;
968
+ }
969
+ import_runtime6.QueryUtils.requireIdFields(this.client.$schema, model).forEach((f) => fields.add(f));
970
+ return Array.from(fields).sort();
971
+ }
972
+ // #region overrides
973
+ transformSelectQuery(node) {
974
+ if (!node.from) {
975
+ return super.transformSelectQuery(node);
976
+ }
977
+ let whereNode = this.transformNode(node.where);
978
+ const policyFilter = this.createPolicyFilterForFrom(node.from);
979
+ if (policyFilter) {
980
+ whereNode = import_kysely3.WhereNode.create(whereNode?.where ? conjunction(this.dialect, [
981
+ whereNode.where,
982
+ policyFilter
983
+ ]) : policyFilter);
984
+ }
985
+ const baseResult = super.transformSelectQuery({
986
+ ...node,
987
+ where: void 0
988
+ });
989
+ return {
990
+ ...baseResult,
991
+ where: whereNode
992
+ };
993
+ }
994
+ transformJoin(node) {
995
+ const table = this.extractTableName(node.table);
996
+ if (!table) {
997
+ return super.transformJoin(node);
998
+ }
999
+ const filter = this.buildPolicyFilter(table.model, table.alias, "read");
1000
+ const nestedSelect = {
1001
+ kind: "SelectQueryNode",
1002
+ from: import_kysely3.FromNode.create([
1003
+ node.table
1004
+ ]),
1005
+ selections: [
1006
+ import_kysely3.SelectionNode.createSelectAll()
1007
+ ],
1008
+ where: import_kysely3.WhereNode.create(filter)
1009
+ };
1010
+ return {
1011
+ ...node,
1012
+ table: import_kysely3.AliasNode.create(import_kysely3.ParensNode.create(nestedSelect), import_kysely3.IdentifierNode.create(table.alias ?? table.model))
1013
+ };
1014
+ }
1015
+ transformInsertQuery(node) {
1016
+ let onConflict = node.onConflict;
1017
+ if (onConflict?.updates) {
1018
+ const { mutationModel, alias } = this.getMutationModel(node);
1019
+ const filter = this.buildPolicyFilter(mutationModel, alias, "update");
1020
+ if (onConflict.updateWhere) {
1021
+ onConflict = {
1022
+ ...onConflict,
1023
+ updateWhere: import_kysely3.WhereNode.create(conjunction(this.dialect, [
1024
+ onConflict.updateWhere.where,
1025
+ filter
1026
+ ]))
1027
+ };
1028
+ } else {
1029
+ onConflict = {
1030
+ ...onConflict,
1031
+ updateWhere: import_kysely3.WhereNode.create(filter)
1032
+ };
1033
+ }
1034
+ }
1035
+ const processedNode = onConflict ? {
1036
+ ...node,
1037
+ onConflict
1038
+ } : node;
1039
+ const result = super.transformInsertQuery(processedNode);
1040
+ let returning = result.returning;
1041
+ if (returning) {
1042
+ const { mutationModel } = this.getMutationModel(node);
1043
+ const idFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
1044
+ returning = import_kysely3.ReturningNode.create(idFields.map((f) => import_kysely3.SelectionNode.create(import_kysely3.ColumnNode.create(f))));
1045
+ }
1046
+ return {
1047
+ ...result,
1048
+ returning
1049
+ };
1050
+ }
1051
+ transformUpdateQuery(node) {
1052
+ const result = super.transformUpdateQuery(node);
1053
+ const { mutationModel, alias } = this.getMutationModel(node);
1054
+ let filter = this.buildPolicyFilter(mutationModel, alias, "update");
1055
+ if (node.from) {
1056
+ const joinFilter = this.createPolicyFilterForFrom(node.from);
1057
+ if (joinFilter) {
1058
+ filter = conjunction(this.dialect, [
1059
+ filter,
1060
+ joinFilter
1061
+ ]);
1062
+ }
1063
+ }
1064
+ let returning = result.returning;
1065
+ if (returning || this.hasPostUpdatePolicies(mutationModel)) {
1066
+ const idFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
1067
+ returning = import_kysely3.ReturningNode.create(idFields.map((f) => import_kysely3.SelectionNode.create(import_kysely3.ColumnNode.create(f))));
1068
+ }
1069
+ return {
1070
+ ...result,
1071
+ where: import_kysely3.WhereNode.create(result.where ? conjunction(this.dialect, [
1072
+ result.where.where,
1073
+ filter
1074
+ ]) : filter),
1075
+ returning
1076
+ };
1077
+ }
1078
+ transformDeleteQuery(node) {
1079
+ const result = super.transformDeleteQuery(node);
1080
+ const { mutationModel, alias } = this.getMutationModel(node);
1081
+ let filter = this.buildPolicyFilter(mutationModel, alias, "delete");
1082
+ if (node.using) {
1083
+ const joinFilter = this.createPolicyFilterForTables(node.using.tables);
1084
+ if (joinFilter) {
1085
+ filter = conjunction(this.dialect, [
1086
+ filter,
1087
+ joinFilter
1088
+ ]);
1089
+ }
1090
+ }
1091
+ return {
1092
+ ...result,
1093
+ where: import_kysely3.WhereNode.create(result.where ? conjunction(this.dialect, [
1094
+ result.where.where,
1095
+ filter
1096
+ ]) : filter)
1097
+ };
1098
+ }
1099
+ // #endregion
1100
+ // #region helpers
1101
+ onlyReturningId(node) {
1102
+ if (!node.returning) {
1103
+ return true;
1104
+ }
1105
+ const { mutationModel } = this.getMutationModel(node);
1106
+ const idFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, mutationModel);
1107
+ if (node.returning.selections.some((s) => import_kysely3.SelectAllNode.is(s.selection))) {
1108
+ const modelDef = import_runtime6.QueryUtils.requireModel(this.client.$schema, mutationModel);
1109
+ if (Object.keys(modelDef.fields).some((f) => !idFields.includes(f))) {
1110
+ return false;
1111
+ } else {
1112
+ return true;
1113
+ }
1114
+ }
1115
+ const collector = new ColumnCollector();
1116
+ const selectedColumns = collector.collect(node.returning);
1117
+ return selectedColumns.every((c) => idFields.includes(c));
1118
+ }
1119
+ async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
1120
+ const fields = node.columns?.map((c) => c.column.name) ?? [];
1121
+ const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
1122
+ []
1123
+ ];
1124
+ for (const values of valueRows) {
1125
+ if (isManyToManyJoinTable) {
1126
+ await this.enforcePreCreatePolicyForManyToManyJoinTable(mutationModel, fields, values.map((v) => v.node), proceed);
1127
+ } else {
1128
+ await this.enforcePreCreatePolicyForOne(mutationModel, fields, values.map((v) => v.node), proceed);
1129
+ }
1130
+ }
1131
+ }
1132
+ async enforcePreCreatePolicyForManyToManyJoinTable(tableName, fields, values, proceed) {
1133
+ const m2m = this.resolveManyToManyJoinTable(tableName);
1134
+ (0, import_common_helpers3.invariant)(m2m);
1135
+ (0, import_common_helpers3.invariant)(fields.includes("A") && fields.includes("B"), "many-to-many join table must have A and B fk fields");
1136
+ const aIndex = fields.indexOf("A");
1137
+ const aNode = values[aIndex];
1138
+ const bIndex = fields.indexOf("B");
1139
+ const bNode = values[bIndex];
1140
+ (0, import_common_helpers3.invariant)(import_kysely3.ValueNode.is(aNode) && import_kysely3.ValueNode.is(bNode), "A and B values must be ValueNode");
1141
+ const aValue = aNode.value;
1142
+ const bValue = bNode.value;
1143
+ (0, import_common_helpers3.invariant)(aValue !== null && aValue !== void 0, "A value cannot be null or undefined");
1144
+ (0, import_common_helpers3.invariant)(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
1145
+ const eb = (0, import_kysely3.expressionBuilder)();
1146
+ const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
1147
+ const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new import_kysely3.ExpressionWrapper(filterA).as("$t"));
1148
+ const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
1149
+ const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new import_kysely3.ExpressionWrapper(filterB).as("$t"));
1150
+ const queryNode = {
1151
+ kind: "SelectQueryNode",
1152
+ selections: [
1153
+ import_kysely3.SelectionNode.create(import_kysely3.AliasNode.create(queryA.toOperationNode(), import_kysely3.IdentifierNode.create("$conditionA"))),
1154
+ import_kysely3.SelectionNode.create(import_kysely3.AliasNode.create(queryB.toOperationNode(), import_kysely3.IdentifierNode.create("$conditionB")))
1155
+ ]
1156
+ };
1157
+ const result = await proceed(queryNode);
1158
+ if (!result.rows[0]?.$conditionA) {
1159
+ throw new import_runtime6.RejectedByPolicyError(m2m.firstModel, import_runtime6.RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
1160
+ }
1161
+ if (!result.rows[0]?.$conditionB) {
1162
+ throw new import_runtime6.RejectedByPolicyError(m2m.secondModel, import_runtime6.RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
1163
+ }
1164
+ }
1165
+ async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
1166
+ const allFields = Object.entries(import_runtime6.QueryUtils.requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
1167
+ const allValues = [];
1168
+ for (const [name, _def] of allFields) {
1169
+ const index = fields.indexOf(name);
1170
+ if (index >= 0) {
1171
+ allValues.push(values[index]);
1172
+ } else {
1173
+ allValues.push(import_kysely3.ValueNode.createImmediate(null));
1174
+ }
1175
+ }
1176
+ const eb = (0, import_kysely3.expressionBuilder)();
1177
+ const constTable = {
1178
+ kind: "SelectQueryNode",
1179
+ from: import_kysely3.FromNode.create([
1180
+ import_kysely3.AliasNode.create(import_kysely3.ParensNode.create(import_kysely3.ValuesNode.create([
1181
+ import_kysely3.ValueListNode.create(allValues)
1182
+ ])), import_kysely3.IdentifierNode.create("$t"))
1183
+ ]),
1184
+ selections: allFields.map(([name, def], index) => {
1185
+ const castedColumnRef = import_kysely3.sql`CAST(${eb.ref(`column${index + 1}`)} as ${import_kysely3.sql.raw(this.dialect.getFieldSqlType(def))})`.as(name);
1186
+ return import_kysely3.SelectionNode.create(castedColumnRef.toOperationNode());
1187
+ })
1188
+ };
1189
+ const filter = this.buildPolicyFilter(model, void 0, "create");
1190
+ const preCreateCheck = {
1191
+ kind: "SelectQueryNode",
1192
+ from: import_kysely3.FromNode.create([
1193
+ import_kysely3.AliasNode.create(constTable, import_kysely3.IdentifierNode.create(model))
1194
+ ]),
1195
+ selections: [
1196
+ import_kysely3.SelectionNode.create(import_kysely3.AliasNode.create(import_kysely3.BinaryOperationNode.create(import_kysely3.FunctionNode.create("COUNT", [
1197
+ import_kysely3.ValueNode.createImmediate(1)
1198
+ ]), import_kysely3.OperatorNode.create(">"), import_kysely3.ValueNode.createImmediate(0)), import_kysely3.IdentifierNode.create("$condition")))
1199
+ ],
1200
+ where: import_kysely3.WhereNode.create(filter)
1201
+ };
1202
+ const result = await proceed(preCreateCheck);
1203
+ if (!result.rows[0]?.$condition) {
1204
+ throw new import_runtime6.RejectedByPolicyError(model);
1205
+ }
1206
+ }
1207
+ unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
1208
+ if (import_kysely3.ValuesNode.is(node)) {
1209
+ return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields, isManyToManyJoinTable));
1210
+ } else if (import_kysely3.PrimitiveValueListNode.is(node)) {
1211
+ return [
1212
+ this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
1213
+ ];
1214
+ } else {
1215
+ throw new import_runtime6.InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
1216
+ }
1217
+ }
1218
+ unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
1219
+ (0, import_common_helpers3.invariant)(data.length === fields.length, "data length must match fields length");
1220
+ const result = [];
1221
+ for (let i = 0; i < data.length; i++) {
1222
+ const item = data[i];
1223
+ if (typeof item === "object" && item && "kind" in item) {
1224
+ const fieldDef = import_runtime6.QueryUtils.requireField(this.client.$schema, model, fields[i]);
1225
+ (0, import_common_helpers3.invariant)(item.kind === "ValueNode", "expecting a ValueNode");
1226
+ result.push({
1227
+ node: import_kysely3.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
1228
+ raw: item.value
1229
+ });
1230
+ } else {
1231
+ let value = item;
1232
+ if (!isImplicitManyToManyJoinTable) {
1233
+ const fieldDef = import_runtime6.QueryUtils.requireField(this.client.$schema, model, fields[i]);
1234
+ value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
1235
+ }
1236
+ if (Array.isArray(value)) {
1237
+ result.push({
1238
+ node: import_kysely3.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
1239
+ raw: value
1240
+ });
1241
+ } else {
1242
+ result.push({
1243
+ node: import_kysely3.ValueNode.create(value),
1244
+ raw: value
1245
+ });
1246
+ }
1247
+ }
1248
+ }
1249
+ return result;
1250
+ }
1251
+ tryGetConstantPolicy(model, operation) {
1252
+ const policies = this.getModelPolicies(model, operation);
1253
+ if (!policies.some((p) => p.kind === "allow")) {
1254
+ return false;
1255
+ } else if (
1256
+ // unconditional deny
1257
+ policies.some((p) => p.kind === "deny" && this.isTrueExpr(p.condition))
1258
+ ) {
1259
+ return false;
1260
+ } else if (
1261
+ // unconditional allow
1262
+ !policies.some((p) => p.kind === "deny") && policies.some((p) => p.kind === "allow" && this.isTrueExpr(p.condition))
1263
+ ) {
1264
+ return true;
1265
+ } else {
1266
+ return void 0;
1267
+ }
1268
+ }
1269
+ isTrueExpr(expr2) {
1270
+ return import_schema4.ExpressionUtils.isLiteral(expr2) && expr2.value === true;
1271
+ }
1272
+ async processReadBack(node, result, proceed) {
1273
+ if (result.rows.length === 0) {
1274
+ return result;
1275
+ }
1276
+ if (!this.isMutationQueryNode(node) || !node.returning) {
1277
+ return result;
1278
+ }
1279
+ const { mutationModel } = this.getMutationModel(node);
1280
+ const idConditions = this.buildIdConditions(mutationModel, result.rows);
1281
+ const policyFilter = this.buildPolicyFilter(mutationModel, void 0, "read");
1282
+ const select = {
1283
+ kind: "SelectQueryNode",
1284
+ from: import_kysely3.FromNode.create([
1285
+ import_kysely3.TableNode.create(mutationModel)
1286
+ ]),
1287
+ where: import_kysely3.WhereNode.create(conjunction(this.dialect, [
1288
+ idConditions,
1289
+ policyFilter
1290
+ ])),
1291
+ selections: node.returning.selections
1292
+ };
1293
+ const selectResult = await proceed(select);
1294
+ return selectResult;
1295
+ }
1296
+ buildIdConditions(table, rows) {
1297
+ const idFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, table);
1298
+ return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => import_kysely3.BinaryOperationNode.create(import_kysely3.ReferenceNode.create(import_kysely3.ColumnNode.create(field), import_kysely3.TableNode.create(table)), import_kysely3.OperatorNode.create("="), import_kysely3.ValueNode.create(row[field]))))));
1299
+ }
1300
+ getMutationModel(node) {
1301
+ const r = (0, import_ts_pattern3.match)(node).when(import_kysely3.InsertQueryNode.is, (node2) => ({
1302
+ mutationModel: getTableName(node2.into),
1303
+ alias: void 0
1304
+ })).when(import_kysely3.UpdateQueryNode.is, (node2) => {
1305
+ if (!node2.table) {
1306
+ throw new import_runtime6.QueryError("Update query must have a table");
1307
+ }
1308
+ const r2 = this.extractTableName(node2.table);
1309
+ return r2 ? {
1310
+ mutationModel: r2.model,
1311
+ alias: r2.alias
1312
+ } : void 0;
1313
+ }).when(import_kysely3.DeleteQueryNode.is, (node2) => {
1314
+ if (node2.from.froms.length !== 1) {
1315
+ throw new import_runtime6.QueryError("Only one from table is supported for delete");
1316
+ }
1317
+ const r2 = this.extractTableName(node2.from.froms[0]);
1318
+ return r2 ? {
1319
+ mutationModel: r2.model,
1320
+ alias: r2.alias
1321
+ } : void 0;
1322
+ }).exhaustive();
1323
+ if (!r) {
1324
+ throw new import_runtime6.InternalError(`Unable to get table name for query node: ${node}`);
1325
+ }
1326
+ return r;
1327
+ }
1328
+ isCrudQueryNode(node) {
1329
+ return import_kysely3.SelectQueryNode.is(node) || import_kysely3.InsertQueryNode.is(node) || import_kysely3.UpdateQueryNode.is(node) || import_kysely3.DeleteQueryNode.is(node);
1330
+ }
1331
+ isMutationQueryNode(node) {
1332
+ return import_kysely3.InsertQueryNode.is(node) || import_kysely3.UpdateQueryNode.is(node) || import_kysely3.DeleteQueryNode.is(node);
1333
+ }
1334
+ buildPolicyFilter(model, alias, operation) {
1335
+ const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
1336
+ if (m2mFilter) {
1337
+ return m2mFilter;
1338
+ }
1339
+ const policies = this.getModelPolicies(model, operation);
1340
+ const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
1341
+ const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
1342
+ let combinedPolicy;
1343
+ if (allows.length === 0) {
1344
+ if (operation === "post-update") {
1345
+ combinedPolicy = trueNode(this.dialect);
1346
+ } else {
1347
+ combinedPolicy = falseNode(this.dialect);
1348
+ }
1349
+ } else {
1350
+ combinedPolicy = disjunction(this.dialect, allows);
1351
+ }
1352
+ if (denies.length !== 0) {
1353
+ const combinedDenies = conjunction(this.dialect, denies.map((d) => buildIsFalse(d, this.dialect)));
1354
+ combinedPolicy = conjunction(this.dialect, [
1355
+ combinedPolicy,
1356
+ combinedDenies
1357
+ ]);
1358
+ }
1359
+ return combinedPolicy;
1360
+ }
1361
+ extractTableName(node) {
1362
+ if (import_kysely3.TableNode.is(node)) {
1363
+ return {
1364
+ model: node.table.identifier.name
1365
+ };
1366
+ }
1367
+ if (import_kysely3.AliasNode.is(node)) {
1368
+ const inner = this.extractTableName(node.node);
1369
+ if (!inner) {
1370
+ return void 0;
1371
+ }
1372
+ return {
1373
+ model: inner.model,
1374
+ alias: import_kysely3.IdentifierNode.is(node.alias) ? node.alias.name : void 0
1375
+ };
1376
+ } else {
1377
+ return void 0;
1378
+ }
1379
+ }
1380
+ createPolicyFilterForFrom(node) {
1381
+ if (!node) {
1382
+ return void 0;
1383
+ }
1384
+ return this.createPolicyFilterForTables(node.froms);
1385
+ }
1386
+ createPolicyFilterForTables(tables) {
1387
+ return tables.reduce((acc, table) => {
1388
+ const extractResult = this.extractTableName(table);
1389
+ if (extractResult) {
1390
+ const { model, alias } = extractResult;
1391
+ const filter = this.buildPolicyFilter(model, alias, "read");
1392
+ return acc ? conjunction(this.dialect, [
1393
+ acc,
1394
+ filter
1395
+ ]) : filter;
1396
+ }
1397
+ return acc;
1398
+ }, void 0);
1399
+ }
1400
+ compilePolicyCondition(model, alias, operation, policy) {
1401
+ return new ExpressionTransformer(this.client).transform(policy.condition, {
1402
+ model,
1403
+ alias,
1404
+ operation
1405
+ });
1406
+ }
1407
+ getModelPolicies(model, operation) {
1408
+ const modelDef = import_runtime6.QueryUtils.requireModel(this.client.$schema, model);
1409
+ const result = [];
1410
+ const extractOperations = /* @__PURE__ */ __name((expr2) => {
1411
+ (0, import_common_helpers3.invariant)(import_schema4.ExpressionUtils.isLiteral(expr2), "expecting a literal");
1412
+ (0, import_common_helpers3.invariant)(typeof expr2.value === "string", "expecting a string literal");
1413
+ return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
1414
+ }, "extractOperations");
1415
+ if (modelDef.attributes) {
1416
+ result.push(...modelDef.attributes.filter((attr) => attr.name === "@@allow" || attr.name === "@@deny").map((attr) => ({
1417
+ kind: attr.name === "@@allow" ? "allow" : "deny",
1418
+ operations: extractOperations(attr.args[0].value),
1419
+ condition: attr.args[1].value
1420
+ })).filter((policy) => operation !== "post-update" && policy.operations.includes("all") || policy.operations.includes(operation)));
1421
+ }
1422
+ return result;
1423
+ }
1424
+ resolveManyToManyJoinTable(tableName) {
1425
+ for (const model of Object.values(this.client.$schema.models)) {
1426
+ for (const field of Object.values(model.fields)) {
1427
+ const m2m = import_runtime6.QueryUtils.getManyToManyRelation(this.client.$schema, model.name, field.name);
1428
+ if (m2m?.joinTable === tableName) {
1429
+ const sortedRecord = [
1430
+ {
1431
+ model: model.name,
1432
+ field: field.name
1433
+ },
1434
+ {
1435
+ model: m2m.otherModel,
1436
+ field: m2m.otherField
1437
+ }
1438
+ ].sort(this.manyToManySorter);
1439
+ const firstIdFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, sortedRecord[0].model);
1440
+ const secondIdFields = import_runtime6.QueryUtils.requireIdFields(this.client.$schema, sortedRecord[1].model);
1441
+ (0, import_common_helpers3.invariant)(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
1442
+ return {
1443
+ firstModel: sortedRecord[0].model,
1444
+ firstField: sortedRecord[0].field,
1445
+ firstIdField: firstIdFields[0],
1446
+ secondModel: sortedRecord[1].model,
1447
+ secondField: sortedRecord[1].field,
1448
+ secondIdField: secondIdFields[0]
1449
+ };
1450
+ }
1451
+ }
1452
+ }
1453
+ return void 0;
1454
+ }
1455
+ manyToManySorter(a, b) {
1456
+ return a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field);
1457
+ }
1458
+ isManyToManyJoinTable(tableName) {
1459
+ return !!this.resolveManyToManyJoinTable(tableName);
1460
+ }
1461
+ getModelPolicyFilterForManyToManyJoinTable(tableName, alias, operation) {
1462
+ const m2m = this.resolveManyToManyJoinTable(tableName);
1463
+ if (!m2m) {
1464
+ return void 0;
1465
+ }
1466
+ const checkForOperation = operation === "read" ? "read" : "update";
1467
+ const eb = (0, import_kysely3.expressionBuilder)();
1468
+ const joinTable = alias ?? tableName;
1469
+ const aQuery = eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new import_kysely3.ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
1470
+ const bQuery = eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new import_kysely3.ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
1471
+ return eb.and([
1472
+ aQuery,
1473
+ bQuery
1474
+ ]).toOperationNode();
1475
+ }
1476
+ };
1477
+
1478
+ // src/functions.ts
1479
+ var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, operation }) => {
1480
+ (0, import_common_helpers4.invariant)(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments');
1481
+ const arg1Node = args[0].toOperationNode();
1482
+ const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
1483
+ if (arg2Node) {
1484
+ (0, import_common_helpers4.invariant)(import_kysely4.ValueNode.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
1485
+ (0, import_common_helpers4.invariant)(import_runtime8.CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
1486
+ }
1487
+ const fieldName = import_runtime8.QueryUtils.extractFieldName(arg1Node);
1488
+ (0, import_common_helpers4.invariant)(fieldName, 'Failed to extract field name from the first argument of "check" function');
1489
+ const fieldDef = import_runtime8.QueryUtils.requireField(client.$schema, model, fieldName);
1490
+ (0, import_common_helpers4.invariant)(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
1491
+ (0, import_common_helpers4.invariant)(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
1492
+ const relationModel = fieldDef.type;
1493
+ const joinConditions = [];
1494
+ const fkInfo = import_runtime8.QueryUtils.getRelationForeignKeyFieldPairs(client.$schema, model, fieldName);
1495
+ const idFields = import_runtime8.QueryUtils.requireIdFields(client.$schema, model);
1496
+ const buildBaseSelect = /* @__PURE__ */ __name((baseModel, field) => {
1497
+ return eb.selectFrom(baseModel).select(field).where(eb.and(idFields.map((idField) => eb(eb.ref(`${fieldDef.originModel}.${idField}`), "=", eb.ref(`${modelAlias}.${idField}`)))));
1498
+ }, "buildBaseSelect");
1499
+ if (fkInfo.ownedByModel) {
1500
+ joinConditions.push(...fkInfo.keyPairs.map(({ fk, pk }) => {
1501
+ let fkRef;
1502
+ if (fieldDef.originModel && fieldDef.originModel !== model) {
1503
+ fkRef = buildBaseSelect(fieldDef.originModel, fk);
1504
+ } else {
1505
+ fkRef = eb.ref(`${modelAlias}.${fk}`);
1506
+ }
1507
+ return eb(fkRef, "=", eb.ref(`${relationModel}.${pk}`));
1508
+ }));
1509
+ } else {
1510
+ joinConditions.push(...fkInfo.keyPairs.map(({ fk, pk }) => {
1511
+ let pkRef;
1512
+ if (fieldDef.originModel && fieldDef.originModel !== model) {
1513
+ pkRef = buildBaseSelect(fieldDef.originModel, pk);
1514
+ } else {
1515
+ pkRef = eb.ref(`${modelAlias}.${pk}`);
1516
+ }
1517
+ return eb(pkRef, "=", eb.ref(`${relationModel}.${fk}`));
1518
+ }));
1519
+ }
1520
+ const joinCondition = joinConditions.length === 1 ? joinConditions[0] : eb.and(joinConditions);
1521
+ const policyHandler = new PolicyHandler(client);
1522
+ const op = arg2Node ? arg2Node.value : operation;
1523
+ const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
1524
+ const result = eb.selectFrom(relationModel).where(joinCondition).select(new import_kysely4.ExpressionWrapper(policyCondition).as("$condition"));
1525
+ return result;
1526
+ }, "check");
1527
+
1528
+ // src/plugin.ts
1529
+ var PolicyPlugin = class {
1530
+ static {
1531
+ __name(this, "PolicyPlugin");
1532
+ }
1533
+ get id() {
1534
+ return "policy";
1535
+ }
1536
+ get name() {
1537
+ return "Access Policy";
1538
+ }
1539
+ get description() {
1540
+ return "Enforces access policies defined in the schema.";
1541
+ }
1542
+ get functions() {
1543
+ return {
1544
+ check
1545
+ };
1546
+ }
1547
+ onKyselyQuery({ query, client, proceed }) {
1548
+ const handler = new PolicyHandler(client);
1549
+ return handler.handle(query, proceed);
1550
+ }
1551
+ };
1552
+ // Annotate the CommonJS export names for ESM import in node:
1553
+ 0 && (module.exports = {
1554
+ PolicyPlugin
1555
+ });
1556
+ //# sourceMappingURL=index.cjs.map