@zenstackhq/plugin-policy 3.0.0-beta.9 → 3.0.0

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.d.cts CHANGED
@@ -1,14 +1,14 @@
1
1
  import * as kysely from 'kysely';
2
- import * as _zenstackhq_runtime from '@zenstackhq/runtime';
3
- import { RuntimePlugin, OnKyselyQueryArgs } from '@zenstackhq/runtime';
4
- import { SchemaDef } from '@zenstackhq/runtime/schema';
2
+ import * as _zenstackhq_orm from '@zenstackhq/orm';
3
+ import { RuntimePlugin, OnKyselyQueryArgs } from '@zenstackhq/orm';
4
+ import { SchemaDef } from '@zenstackhq/orm/schema';
5
5
 
6
6
  declare class PolicyPlugin<Schema extends SchemaDef> implements RuntimePlugin<Schema> {
7
7
  get id(): string;
8
8
  get name(): string;
9
9
  get description(): string;
10
10
  get functions(): {
11
- check: _zenstackhq_runtime.ZModelFunction<any>;
11
+ check: _zenstackhq_orm.ZModelFunction<any>;
12
12
  };
13
13
  onKyselyQuery({ query, client, proceed }: OnKyselyQueryArgs<Schema>): Promise<kysely.QueryResult<any>>;
14
14
  }
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import * as kysely from 'kysely';
2
- import * as _zenstackhq_runtime from '@zenstackhq/runtime';
3
- import { RuntimePlugin, OnKyselyQueryArgs } from '@zenstackhq/runtime';
4
- import { SchemaDef } from '@zenstackhq/runtime/schema';
2
+ import * as _zenstackhq_orm from '@zenstackhq/orm';
3
+ import { RuntimePlugin, OnKyselyQueryArgs } from '@zenstackhq/orm';
4
+ import { SchemaDef } from '@zenstackhq/orm/schema';
5
5
 
6
6
  declare class PolicyPlugin<Schema extends SchemaDef> implements RuntimePlugin<Schema> {
7
7
  get id(): string;
8
8
  get name(): string;
9
9
  get description(): string;
10
10
  get functions(): {
11
- check: _zenstackhq_runtime.ZModelFunction<any>;
11
+ check: _zenstackhq_orm.ZModelFunction<any>;
12
12
  };
13
13
  onKyselyQuery({ query, client, proceed }: OnKyselyQueryArgs<Schema>): Promise<kysely.QueryResult<any>>;
14
14
  }
package/dist/index.js CHANGED
@@ -3,20 +3,19 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
3
3
 
4
4
  // src/functions.ts
5
5
  import { invariant as invariant4 } from "@zenstackhq/common-helpers";
6
- import { CRUD, QueryUtils as QueryUtils3 } from "@zenstackhq/runtime";
6
+ import { CRUD, QueryUtils as QueryUtils3 } from "@zenstackhq/orm";
7
7
  import { ExpressionWrapper as ExpressionWrapper2, ValueNode as ValueNode4 } from "kysely";
8
8
 
9
9
  // src/policy-handler.ts
10
10
  import { invariant as invariant3 } from "@zenstackhq/common-helpers";
11
- import { getCrudDialect as getCrudDialect2, InternalError as InternalError2, QueryError as QueryError2, QueryUtils as QueryUtils2, RejectedByPolicyError, RejectedByPolicyReason } from "@zenstackhq/runtime";
12
- import { ExpressionUtils as ExpressionUtils4 } from "@zenstackhq/runtime/schema";
13
- import { ExpressionVisitor } from "@zenstackhq/sdk";
11
+ import { getCrudDialect as getCrudDialect2, QueryUtils as QueryUtils2, RejectedByPolicyReason, SchemaUtils as SchemaUtils2 } from "@zenstackhq/orm";
12
+ import { ExpressionUtils as ExpressionUtils4 } from "@zenstackhq/orm/schema";
14
13
  import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode2, DeleteQueryNode, expressionBuilder as expressionBuilder2, ExpressionWrapper, FromNode as FromNode2, FunctionNode as FunctionNode3, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, ParensNode as ParensNode2, PrimitiveValueListNode, RawNode, ReferenceNode as ReferenceNode3, ReturningNode, SelectAllNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, sql, TableNode as TableNode3, UpdateQueryNode, ValueListNode as ValueListNode2, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
15
14
  import { match as match3 } from "ts-pattern";
16
15
 
17
16
  // src/column-collector.ts
18
- import { DefaultOperationNodeVisitor } from "@zenstackhq/sdk";
19
- var ColumnCollector = class extends DefaultOperationNodeVisitor {
17
+ import { KyselyUtils } from "@zenstackhq/orm";
18
+ var ColumnCollector = class extends KyselyUtils.DefaultOperationNodeVisitor {
20
19
  static {
21
20
  __name(this, "ColumnCollector");
22
21
  }
@@ -35,15 +34,15 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
35
34
 
36
35
  // src/expression-transformer.ts
37
36
  import { invariant as invariant2 } from "@zenstackhq/common-helpers";
38
- import { getCrudDialect, InternalError, QueryError, QueryUtils } from "@zenstackhq/runtime";
39
- import { ExpressionUtils as ExpressionUtils3 } from "@zenstackhq/runtime/schema";
37
+ import { getCrudDialect, QueryUtils, SchemaUtils } from "@zenstackhq/orm";
38
+ import { ExpressionUtils as ExpressionUtils3 } from "@zenstackhq/orm/schema";
40
39
  import { AliasNode as AliasNode2, BinaryOperationNode as BinaryOperationNode2, ColumnNode, expressionBuilder, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode2, SelectionNode, SelectQueryNode, TableNode as TableNode2, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
41
40
  import { match as match2 } from "ts-pattern";
42
41
 
43
42
  // src/expression-evaluator.ts
44
43
  import { invariant } from "@zenstackhq/common-helpers";
45
44
  import { match } from "ts-pattern";
46
- import { ExpressionUtils } from "@zenstackhq/runtime/schema";
45
+ import { ExpressionUtils } from "@zenstackhq/orm/schema";
47
46
  var ExpressionEvaluator = class {
48
47
  static {
49
48
  __name(this, "ExpressionEvaluator");
@@ -84,6 +83,12 @@ var ExpressionEvaluator = class {
84
83
  }
85
84
  const left = this.evaluate(expr2.left, context);
86
85
  const right = this.evaluate(expr2.right, context);
86
+ if (![
87
+ "==",
88
+ "!="
89
+ ].includes(expr2.op) && (left === null || right === null)) {
90
+ return null;
91
+ }
87
92
  return 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", () => {
88
93
  const _right = right ?? [];
89
94
  invariant(Array.isArray(_right), 'expected array for "in" operator');
@@ -94,8 +99,8 @@ var ExpressionEvaluator = class {
94
99
  const op = expr2.op;
95
100
  invariant(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
96
101
  const left = this.evaluate(expr2.left, context);
97
- if (!left) {
98
- return false;
102
+ if (left === null || left === void 0) {
103
+ return null;
99
104
  }
100
105
  invariant(Array.isArray(left), "expected array");
101
106
  return match(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
@@ -111,8 +116,16 @@ var ExpressionEvaluator = class {
111
116
  }
112
117
  };
113
118
 
119
+ // src/types.ts
120
+ var CollectionPredicateOperator = [
121
+ "?",
122
+ "!",
123
+ "^"
124
+ ];
125
+
114
126
  // src/utils.ts
115
- import { ExpressionUtils as ExpressionUtils2 } from "@zenstackhq/runtime/schema";
127
+ import { ORMError, ORMErrorReason } from "@zenstackhq/orm";
128
+ import { ExpressionUtils as ExpressionUtils2 } from "@zenstackhq/orm/schema";
116
129
  import { AliasNode, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode, TableNode, UnaryOperationNode, ValueNode } from "kysely";
117
130
  function trueNode(dialect) {
118
131
  return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
@@ -213,6 +226,17 @@ function isBeforeInvocation(expr2) {
213
226
  return ExpressionUtils2.isCall(expr2) && expr2.function === "before";
214
227
  }
215
228
  __name(isBeforeInvocation, "isBeforeInvocation");
229
+ function createRejectedByPolicyError(model, reason, message) {
230
+ const err = new ORMError(ORMErrorReason.REJECTED_BY_POLICY, message ?? "operation is rejected by access policies");
231
+ err.rejectedByPolicyReason = reason;
232
+ err.model = model;
233
+ return err;
234
+ }
235
+ __name(createRejectedByPolicyError, "createRejectedByPolicyError");
236
+ function createUnsupportedError(message) {
237
+ return new ORMError(ORMErrorReason.NOT_SUPPORTED, message);
238
+ }
239
+ __name(createUnsupportedError, "createUnsupportedError");
216
240
 
217
241
  // src/expression-transformer.ts
218
242
  function _ts_decorate(decorators, target, key, desc) {
@@ -257,7 +281,7 @@ var ExpressionTransformer = class {
257
281
  }
258
282
  get authType() {
259
283
  if (!this.schema.authType) {
260
- throw new InternalError('Schema does not have an "authType" specified');
284
+ invariant2(false, 'Schema does not have an "authType" specified');
261
285
  }
262
286
  return this.schema.authType;
263
287
  }
@@ -275,7 +299,11 @@ var ExpressionTransformer = class {
275
299
  return ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
276
300
  }
277
301
  _field(expr2, context) {
278
- const fieldDef = QueryUtils.requireField(this.schema, context.model, expr2.field);
302
+ if (context.contextValue) {
303
+ const fieldDef2 = QueryUtils.requireField(this.schema, context.modelOrType, expr2.field);
304
+ return this.transformValue(context.contextValue[expr2.field], fieldDef2.type);
305
+ }
306
+ const fieldDef = QueryUtils.requireField(this.schema, context.modelOrType, expr2.field);
279
307
  if (!fieldDef.relation) {
280
308
  return this.createColumnRef(expr2.field, context);
281
309
  } else {
@@ -349,30 +377,39 @@ var ExpressionTransformer = class {
349
377
  }
350
378
  }
351
379
  transformNullCheck(expr2, operator) {
352
- invariant2(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
353
- if (ValueNode2.is(expr2)) {
354
- if (expr2.value === null) {
355
- return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
380
+ if (operator === "==" || operator === "!=") {
381
+ if (ValueNode2.is(expr2)) {
382
+ if (expr2.value === null) {
383
+ return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
384
+ } else {
385
+ return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
386
+ }
356
387
  } else {
357
- return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
388
+ return operator === "==" ? BinaryOperationNode2.create(expr2, OperatorNode2.create("is"), ValueNode2.createImmediate(null)) : BinaryOperationNode2.create(expr2, OperatorNode2.create("is not"), ValueNode2.createImmediate(null));
358
389
  }
359
390
  } else {
360
- return operator === "==" ? BinaryOperationNode2.create(expr2, OperatorNode2.create("is"), ValueNode2.createImmediate(null)) : BinaryOperationNode2.create(expr2, OperatorNode2.create("is not"), ValueNode2.createImmediate(null));
391
+ return ValueNode2.createImmediate(null);
361
392
  }
362
393
  }
363
394
  normalizeBinaryOperationOperands(expr2, context) {
395
+ if (context.contextValue) {
396
+ return {
397
+ normalizedLeft: expr2.left,
398
+ normalizedRight: expr2.right
399
+ };
400
+ }
364
401
  let normalizedLeft = expr2.left;
365
- if (this.isRelationField(expr2.left, context.model)) {
402
+ if (this.isRelationField(expr2.left, context.modelOrType)) {
366
403
  invariant2(ExpressionUtils3.isNull(expr2.right), "only null comparison is supported for relation field");
367
- const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
404
+ const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.modelOrType);
368
405
  invariant2(leftRelDef, "failed to get relation field definition");
369
406
  const idFields = QueryUtils.requireIdFields(this.schema, leftRelDef.type);
370
407
  normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
371
408
  }
372
409
  let normalizedRight = expr2.right;
373
- if (this.isRelationField(expr2.right, context.model)) {
410
+ if (this.isRelationField(expr2.right, context.modelOrType)) {
374
411
  invariant2(ExpressionUtils3.isNull(expr2.left), "only null comparison is supported for relation field");
375
- const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
412
+ const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.modelOrType);
376
413
  invariant2(rightRelDef, "failed to get relation field definition");
377
414
  const idFields = QueryUtils.requireIdFields(this.schema, rightRelDef.type);
378
415
  normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
@@ -383,22 +420,30 @@ var ExpressionTransformer = class {
383
420
  };
384
421
  }
385
422
  transformCollectionPredicate(expr2, context) {
386
- invariant2(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
387
- if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
388
- const value = new ExpressionEvaluator().evaluate(expr2, {
423
+ this.ensureCollectionPredicateOperator(expr2.op);
424
+ if (this.isAuthMember(expr2.left) || context.contextValue) {
425
+ invariant2(ExpressionUtils3.isMember(expr2.left) || ExpressionUtils3.isField(expr2.left), "expected member or field expression");
426
+ const evaluator = new ExpressionEvaluator();
427
+ const receiver = evaluator.evaluate(expr2.left, {
428
+ thisValue: context.contextValue,
389
429
  auth: this.auth
390
430
  });
391
- return this.transformValue(value, "Boolean");
431
+ const baseType = this.isAuthMember(expr2.left) ? this.authType : context.modelOrType;
432
+ const memberType = this.getMemberType(baseType, expr2.left);
433
+ return this.transformValueCollectionPredicate(receiver, expr2, {
434
+ ...context,
435
+ modelOrType: memberType
436
+ });
392
437
  }
393
438
  invariant2(ExpressionUtils3.isField(expr2.left) || ExpressionUtils3.isMember(expr2.left), "left operand must be field or member access");
394
439
  let newContextModel;
395
- const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
440
+ const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.modelOrType);
396
441
  if (fieldDef) {
397
442
  invariant2(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
398
443
  newContextModel = fieldDef.type;
399
444
  } else {
400
445
  invariant2(ExpressionUtils3.isMember(expr2.left) && ExpressionUtils3.isField(expr2.left.receiver), "left operand must be member access with field receiver");
401
- const fieldDef2 = QueryUtils.requireField(this.schema, context.model, expr2.left.receiver.field);
446
+ const fieldDef2 = QueryUtils.requireField(this.schema, context.modelOrType, expr2.left.receiver.field);
402
447
  newContextModel = fieldDef2.type;
403
448
  for (const member of expr2.left.members) {
404
449
  const memberDef = QueryUtils.requireField(this.schema, newContextModel, member);
@@ -407,7 +452,7 @@ var ExpressionTransformer = class {
407
452
  }
408
453
  let predicateFilter = this.transform(expr2.right, {
409
454
  ...context,
410
- model: newContextModel,
455
+ modelOrType: newContextModel,
411
456
  alias: void 0
412
457
  });
413
458
  if (expr2.op === "!") {
@@ -423,9 +468,49 @@ var ExpressionTransformer = class {
423
468
  memberFilter: predicateFilter
424
469
  });
425
470
  }
471
+ ensureCollectionPredicateOperator(op) {
472
+ invariant2(CollectionPredicateOperator.includes(op), 'expected "?" or "!" or "^" operator');
473
+ }
474
+ transformValueCollectionPredicate(receiver, expr2, context) {
475
+ if (!receiver) {
476
+ return ValueNode2.createImmediate(null);
477
+ }
478
+ this.ensureCollectionPredicateOperator(expr2.op);
479
+ const visitor = new SchemaUtils.MatchingExpressionVisitor((e) => ExpressionUtils3.isThis(e));
480
+ if (!visitor.find(expr2.right)) {
481
+ const value = new ExpressionEvaluator().evaluate(expr2, {
482
+ auth: this.auth,
483
+ thisValue: context.contextValue
484
+ });
485
+ return this.transformValue(value, "Boolean");
486
+ } else {
487
+ invariant2(Array.isArray(receiver), "array value is expected");
488
+ const components = receiver.map((item) => this.transform(expr2.right, {
489
+ operation: context.operation,
490
+ thisType: context.thisType,
491
+ thisAlias: context.thisAlias,
492
+ modelOrType: context.modelOrType,
493
+ contextValue: item
494
+ }));
495
+ return match2(expr2.op).with("?", () => disjunction(this.dialect, components)).with("!", () => conjunction(this.dialect, components)).with("^", () => logicalNot(this.dialect, disjunction(this.dialect, components))).exhaustive();
496
+ }
497
+ }
498
+ getMemberType(receiverType, expr2) {
499
+ if (ExpressionUtils3.isField(expr2)) {
500
+ const fieldDef = QueryUtils.requireField(this.schema, receiverType, expr2.field);
501
+ return fieldDef.type;
502
+ } else {
503
+ let currType = receiverType;
504
+ for (const member of expr2.members) {
505
+ const fieldDef = QueryUtils.requireField(this.schema, currType, member);
506
+ currType = fieldDef.type;
507
+ }
508
+ return currType;
509
+ }
510
+ }
426
511
  transformAuthBinary(expr2, context) {
427
512
  if (expr2.op !== "==" && expr2.op !== "!=") {
428
- throw new QueryError(`Unsupported operator for \`auth()\` in policy of model "${context.model}": ${expr2.op}`);
513
+ throw createUnsupportedError(`Unsupported operator for \`auth()\` in policy of model "${context.modelOrType}": ${expr2.op}`);
429
514
  }
430
515
  let authExpr;
431
516
  let other;
@@ -441,7 +526,7 @@ var ExpressionTransformer = class {
441
526
  } else {
442
527
  const authModel = QueryUtils.getModel(this.schema, this.authType);
443
528
  if (!authModel) {
444
- 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`);
529
+ throw createUnsupportedError(`Unsupported use of \`auth()\` in policy of model "${context.modelOrType}", comparing with \`auth()\` is only possible when auth type is a model`);
445
530
  }
446
531
  const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
447
532
  invariant2(idFields.length > 0, "auth type model must have at least one id field");
@@ -473,7 +558,12 @@ var ExpressionTransformer = class {
473
558
  } else if (value === false) {
474
559
  return falseNode(this.dialect);
475
560
  } else {
476
- return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
561
+ const transformed = this.dialect.transformPrimitive(value, type, false) ?? null;
562
+ if (!Array.isArray(transformed)) {
563
+ return ValueNode2.createImmediate(transformed);
564
+ } else {
565
+ return ValueNode2.create(transformed);
566
+ }
477
567
  }
478
568
  }
479
569
  _unary(expr2, context) {
@@ -491,14 +581,14 @@ var ExpressionTransformer = class {
491
581
  transformCall(expr2, context) {
492
582
  const func = this.getFunctionImpl(expr2.function);
493
583
  if (!func) {
494
- throw new QueryError(`Function not implemented: ${expr2.function}`);
584
+ throw createUnsupportedError(`Function not implemented: ${expr2.function}`);
495
585
  }
496
586
  const eb = expressionBuilder();
497
587
  return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
498
588
  client: this.client,
499
589
  dialect: this.dialect,
500
- model: context.model,
501
- modelAlias: context.alias ?? context.model,
590
+ model: context.modelOrType,
591
+ modelAlias: context.alias ?? context.modelOrType,
502
592
  operation: context.operation
503
593
  });
504
594
  }
@@ -528,7 +618,7 @@ var ExpressionTransformer = class {
528
618
  const valNode = this.valueMemberAccess(this.auth, arg, this.authType);
529
619
  return valNode ? eb.val(valNode.value) : eb.val(null);
530
620
  }
531
- throw new InternalError(`Unsupported argument expression: ${arg.kind}`);
621
+ throw createUnsupportedError(`Unsupported argument expression: ${arg.kind}`);
532
622
  }
533
623
  _member(expr2, context) {
534
624
  if (this.isAuthCall(expr2.receiver)) {
@@ -545,9 +635,15 @@ var ExpressionTransformer = class {
545
635
  const { memberFilter, memberSelect, ...restContext } = context;
546
636
  if (ExpressionUtils3.isThis(expr2.receiver)) {
547
637
  if (expr2.members.length === 1) {
548
- return this._field(ExpressionUtils3.field(expr2.members[0]), context);
638
+ return this._field(ExpressionUtils3.field(expr2.members[0]), {
639
+ ...context,
640
+ alias: context.thisAlias,
641
+ modelOrType: context.thisType,
642
+ thisType: context.thisType,
643
+ contextValue: void 0
644
+ });
549
645
  } else {
550
- const firstMemberFieldDef = QueryUtils.requireField(this.schema, context.model, expr2.members[0]);
646
+ const firstMemberFieldDef = QueryUtils.requireField(this.schema, context.thisType, expr2.members[0]);
551
647
  receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
552
648
  members = expr2.members.slice(1);
553
649
  }
@@ -557,10 +653,10 @@ var ExpressionTransformer = class {
557
653
  invariant2(SelectQueryNode.is(receiver), "expected receiver to be select query");
558
654
  let startType;
559
655
  if (ExpressionUtils3.isField(expr2.receiver)) {
560
- const receiverField = QueryUtils.requireField(this.schema, context.model, expr2.receiver.field);
656
+ const receiverField = QueryUtils.requireField(this.schema, context.modelOrType, expr2.receiver.field);
561
657
  startType = receiverField.type;
562
658
  } else {
563
- startType = context.model;
659
+ startType = context.thisType;
564
660
  }
565
661
  const memberFields = [];
566
662
  let currType = startType;
@@ -579,7 +675,7 @@ var ExpressionTransformer = class {
579
675
  if (fieldDef.relation) {
580
676
  const relation = this.transformRelationAccess(member, fieldDef.type, {
581
677
  ...restContext,
582
- model: fromModel,
678
+ modelOrType: fromModel,
583
679
  alias: void 0
584
680
  });
585
681
  if (currNode) {
@@ -615,20 +711,29 @@ var ExpressionTransformer = class {
615
711
  if (!receiver) {
616
712
  return ValueNode2.createImmediate(null);
617
713
  }
618
- if (expr2.members.length !== 1) {
619
- throw new Error(`Only single member access is supported`);
714
+ invariant2(expr2.members.length > 0, "member expression must have at least one member");
715
+ let curr = receiver;
716
+ let currType = receiverType;
717
+ for (let i = 0; i < expr2.members.length; i++) {
718
+ const field = expr2.members[i];
719
+ curr = curr?.[field];
720
+ if (curr === void 0) {
721
+ curr = ValueNode2.createImmediate(null);
722
+ break;
723
+ }
724
+ currType = QueryUtils.requireField(this.schema, currType, field).type;
725
+ if (i === expr2.members.length - 1) {
726
+ curr = this.transformValue(curr, currType);
727
+ }
620
728
  }
621
- const field = expr2.members[0];
622
- const fieldDef = QueryUtils.requireField(this.schema, receiverType, field);
623
- const fieldValue = receiver[field] ?? null;
624
- return this.transformValue(fieldValue, fieldDef.type);
729
+ return curr;
625
730
  }
626
731
  transformRelationAccess(field, relationModel, context) {
627
- const m2m = QueryUtils.getManyToManyRelation(this.schema, context.model, field);
732
+ const m2m = QueryUtils.getManyToManyRelation(this.schema, context.modelOrType, field);
628
733
  if (m2m) {
629
734
  return this.transformManyToManyRelationAccess(m2m, context);
630
735
  }
631
- const fromModel = context.model;
736
+ const fromModel = context.modelOrType;
632
737
  const relationFieldDef = QueryUtils.requireField(this.schema, fromModel, field);
633
738
  const { keyPairs, ownedByModel } = QueryUtils.getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
634
739
  let condition;
@@ -653,19 +758,19 @@ var ExpressionTransformer = class {
653
758
  }
654
759
  transformManyToManyRelationAccess(m2m, context) {
655
760
  const eb = expressionBuilder();
656
- 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}`));
761
+ 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.modelOrType}.${m2m.parentPKName}`));
657
762
  return relationQuery.toOperationNode();
658
763
  }
659
764
  createColumnRef(column, context) {
660
- const tableName = context.alias ?? context.model;
765
+ const tableName = context.alias ?? context.modelOrType;
661
766
  if (context.operation === "create") {
662
767
  return ReferenceNode2.create(ColumnNode.create(column), TableNode2.create(tableName));
663
768
  }
664
- const fieldDef = QueryUtils.requireField(this.schema, context.model, column);
665
- if (!fieldDef.originModel || fieldDef.originModel === context.model) {
769
+ const fieldDef = QueryUtils.requireField(this.schema, context.modelOrType, column);
770
+ if (!fieldDef.originModel || fieldDef.originModel === context.modelOrType) {
666
771
  return ReferenceNode2.create(ColumnNode.create(column), TableNode2.create(tableName));
667
772
  }
668
- return this.buildDelegateBaseFieldSelect(context.model, tableName, column, fieldDef.originModel);
773
+ return this.buildDelegateBaseFieldSelect(context.modelOrType, tableName, column, fieldDef.originModel);
669
774
  }
670
775
  buildDelegateBaseFieldSelect(model, modelAlias, field, baseModel) {
671
776
  const idFields = QueryUtils.requireIdFields(this.client.$schema, model);
@@ -707,9 +812,9 @@ var ExpressionTransformer = class {
707
812
  }
708
813
  getFieldDefFromFieldRef(expr2, model) {
709
814
  if (ExpressionUtils3.isField(expr2)) {
710
- return QueryUtils.requireField(this.schema, model, expr2.field);
815
+ return QueryUtils.getField(this.schema, model, expr2.field);
711
816
  } else if (ExpressionUtils3.isMember(expr2) && expr2.members.length === 1 && ExpressionUtils3.isThis(expr2.receiver)) {
712
- return QueryUtils.requireField(this.schema, model, expr2.members[0]);
817
+ return QueryUtils.getField(this.schema, model, expr2.members[0]);
713
818
  } else {
714
819
  return void 0;
715
820
  }
@@ -800,12 +905,13 @@ var PolicyHandler = class extends OperationNodeTransformer {
800
905
  }
801
906
  async handle(node, proceed) {
802
907
  if (!this.isCrudQueryNode(node)) {
803
- throw new RejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
908
+ throw createRejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
804
909
  }
805
910
  if (!this.isMutationQueryNode(node)) {
806
911
  return proceed(this.transformNode(node));
807
912
  }
808
913
  const { mutationModel } = this.getMutationModel(node);
914
+ this.tryRejectNonexistentModel(mutationModel);
809
915
  if (InsertQueryNode.is(node)) {
810
916
  const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
811
917
  let needCheckPreCreate = true;
@@ -814,7 +920,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
814
920
  if (constCondition === true) {
815
921
  needCheckPreCreate = false;
816
922
  } else if (constCondition === false) {
817
- throw new RejectedByPolicyError(mutationModel);
923
+ throw createRejectedByPolicyError(mutationModel, RejectedByPolicyReason.NO_ACCESS);
818
924
  }
819
925
  }
820
926
  if (needCheckPreCreate) {
@@ -834,7 +940,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
834
940
  for (const postRow of result.rows) {
835
941
  const beforeRow = beforeUpdateInfo.rows.find((r) => idFields.every((f) => r[f] === postRow[f]));
836
942
  if (!beforeRow) {
837
- throw new QueryError2("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.");
943
+ throw createRejectedByPolicyError(mutationModel, RejectedByPolicyReason.OTHER, "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.");
838
944
  }
839
945
  }
840
946
  }
@@ -865,7 +971,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
865
971
  }));
866
972
  const postUpdateResult = await proceed(postUpdateQuery.toOperationNode());
867
973
  if (!postUpdateResult.rows[0]?.$condition) {
868
- throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.NO_ACCESS, "some or all updated rows failed to pass post-update policy check");
974
+ throw createRejectedByPolicyError(mutationModel, RejectedByPolicyReason.NO_ACCESS, "some or all updated rows failed to pass post-update policy check");
869
975
  }
870
976
  }
871
977
  if (!node.returning || this.onlyReturningId(node)) {
@@ -873,7 +979,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
873
979
  } else {
874
980
  const readBackResult = await this.processReadBack(node, result, proceed);
875
981
  if (readBackResult.rows.length !== result.rows.length) {
876
- throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
982
+ throw createRejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
877
983
  }
878
984
  return readBackResult;
879
985
  }
@@ -927,7 +1033,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
927
1033
  return void 0;
928
1034
  }
929
1035
  const fields = /* @__PURE__ */ new Set();
930
- const fieldCollector = new class extends ExpressionVisitor {
1036
+ const fieldCollector = new class extends SchemaUtils2.ExpressionVisitor {
931
1037
  visitMember(e) {
932
1038
  if (isBeforeInvocation(e.receiver)) {
933
1039
  invariant3(e.members.length === 1, "before() can only be followed by a scalar field access");
@@ -972,6 +1078,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
972
1078
  if (!table) {
973
1079
  return super.transformJoin(node);
974
1080
  }
1081
+ this.tryRejectNonexistentModel(table.model);
975
1082
  const filter = this.buildPolicyFilter(table.model, table.alias, "read");
976
1083
  const nestedSelect = {
977
1084
  kind: "SelectQueryNode",
@@ -1132,10 +1239,10 @@ var PolicyHandler = class extends OperationNodeTransformer {
1132
1239
  };
1133
1240
  const result = await proceed(queryNode);
1134
1241
  if (!result.rows[0]?.$conditionA) {
1135
- throw new RejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
1242
+ throw createRejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
1136
1243
  }
1137
1244
  if (!result.rows[0]?.$conditionB) {
1138
- throw new RejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
1245
+ throw createRejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
1139
1246
  }
1140
1247
  }
1141
1248
  async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
@@ -1177,7 +1284,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1177
1284
  };
1178
1285
  const result = await proceed(preCreateCheck);
1179
1286
  if (!result.rows[0]?.$condition) {
1180
- throw new RejectedByPolicyError(model);
1287
+ throw createRejectedByPolicyError(model, RejectedByPolicyReason.NO_ACCESS);
1181
1288
  }
1182
1289
  }
1183
1290
  unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
@@ -1188,7 +1295,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1188
1295
  this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
1189
1296
  ];
1190
1297
  } else {
1191
- throw new InternalError2(`Unexpected node kind: ${node.kind} for unwrapping create values`);
1298
+ invariant3(false, `Unexpected node kind: ${node.kind} for unwrapping create values`);
1192
1299
  }
1193
1300
  }
1194
1301
  unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
@@ -1279,7 +1386,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1279
1386
  alias: void 0
1280
1387
  })).when(UpdateQueryNode.is, (node2) => {
1281
1388
  if (!node2.table) {
1282
- throw new QueryError2("Update query must have a table");
1389
+ invariant3(false, "Update query must have a table");
1283
1390
  }
1284
1391
  const r2 = this.extractTableName(node2.table);
1285
1392
  return r2 ? {
@@ -1288,7 +1395,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1288
1395
  } : void 0;
1289
1396
  }).when(DeleteQueryNode.is, (node2) => {
1290
1397
  if (node2.from.froms.length !== 1) {
1291
- throw new QueryError2("Only one from table is supported for delete");
1398
+ throw createUnsupportedError("Only one from table is supported for delete");
1292
1399
  }
1293
1400
  const r2 = this.extractTableName(node2.from.froms[0]);
1294
1401
  return r2 ? {
@@ -1297,7 +1404,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1297
1404
  } : void 0;
1298
1405
  }).exhaustive();
1299
1406
  if (!r) {
1300
- throw new InternalError2(`Unable to get table name for query node: ${node}`);
1407
+ invariant3(false, `Unable to get table name for query node: ${node}`);
1301
1408
  }
1302
1409
  return r;
1303
1410
  }
@@ -1364,6 +1471,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1364
1471
  const extractResult = this.extractTableName(table);
1365
1472
  if (extractResult) {
1366
1473
  const { model, alias } = extractResult;
1474
+ this.tryRejectNonexistentModel(model);
1367
1475
  const filter = this.buildPolicyFilter(model, alias, "read");
1368
1476
  return acc ? conjunction(this.dialect, [
1369
1477
  acc,
@@ -1375,7 +1483,9 @@ var PolicyHandler = class extends OperationNodeTransformer {
1375
1483
  }
1376
1484
  compilePolicyCondition(model, alias, operation, policy) {
1377
1485
  return new ExpressionTransformer(this.client).transform(policy.condition, {
1378
- model,
1486
+ modelOrType: model,
1487
+ thisType: model,
1488
+ thisAlias: alias,
1379
1489
  alias,
1380
1490
  operation
1381
1491
  });
@@ -1449,6 +1559,11 @@ var PolicyHandler = class extends OperationNodeTransformer {
1449
1559
  bQuery
1450
1560
  ]).toOperationNode();
1451
1561
  }
1562
+ tryRejectNonexistentModel(model) {
1563
+ if (!QueryUtils2.hasModel(this.client.$schema, model) && !this.isManyToManyJoinTable(model)) {
1564
+ throw createRejectedByPolicyError(model, RejectedByPolicyReason.NO_ACCESS);
1565
+ }
1566
+ }
1452
1567
  };
1453
1568
 
1454
1569
  // src/functions.ts