@zenstackhq/runtime 2.0.0-beta.4 → 2.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,15 +16,14 @@ export declare class PolicyProxyHandler<DbClient extends DbClientContract> imple
16
16
  private readonly queryUtils;
17
17
  constructor(prisma: DbClient, model: string, options: InternalEnhancementOptions, context?: EnhancementContext | undefined);
18
18
  private get modelClient();
19
- findUnique(args: any): Promise<unknown>;
20
- findUniqueOrThrow(args: any): Promise<unknown>;
21
- findFirst(args?: any): Promise<unknown>;
22
- findFirstOrThrow(args: any): Promise<unknown>;
19
+ findUnique(args: any): Promise<any>;
20
+ findUniqueOrThrow(args: any): Promise<any>;
21
+ findFirst(args?: any): Promise<any>;
22
+ findFirstOrThrow(args: any): Promise<any>;
23
23
  findMany(args?: any): Promise<unknown[]>;
24
- private findWithFluentCallStubs;
24
+ private findWithFluent;
25
+ private addFluentSelect;
25
26
  private doFind;
26
- private fluentCall;
27
- private addFluentFunctions;
28
27
  create(args: any): Promise<unknown>;
29
28
  private doCreate;
30
29
  private hasNestedCreateOrConnect;
@@ -54,7 +53,6 @@ export declare class PolicyProxyHandler<DbClient extends DbClientContract> imple
54
53
  subscribe(args: any): Promise<any>;
55
54
  private get shouldLogQuery();
56
55
  private runPostWriteChecks;
57
- private makeHandler;
58
56
  private requireBackLink;
59
57
  private mergeToParent;
60
58
  private removeFromParent;
@@ -32,10 +32,10 @@ const zod_validation_error_1 = require("zod-validation-error");
32
32
  const constants_1 = require("../../constants");
33
33
  const cross_1 = require("../../cross");
34
34
  const logger_1 = require("../logger");
35
+ const promise_1 = require("../promise");
35
36
  const query_utils_1 = require("../query-utils");
36
37
  const utils_1 = require("../utils");
37
38
  const policy_utils_1 = require("./policy-utils");
38
- const promise_1 = require("./promise");
39
39
  /**
40
40
  * Prisma proxy handler for injecting access policy check.
41
41
  */
@@ -62,7 +62,7 @@ class PolicyProxyHandler {
62
62
  if (!args.where) {
63
63
  throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
64
64
  }
65
- return this.findWithFluentCallStubs(args, 'findUnique', false, () => null);
65
+ return this.findWithFluent('findUnique', args, () => null);
66
66
  }
67
67
  findUniqueOrThrow(args) {
68
68
  if (!args) {
@@ -71,110 +71,60 @@ class PolicyProxyHandler {
71
71
  if (!args.where) {
72
72
  throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
73
73
  }
74
- return this.findWithFluentCallStubs(args, 'findUniqueOrThrow', true, () => {
74
+ return this.findWithFluent('findUniqueOrThrow', args, () => {
75
75
  throw this.policyUtils.notFound(this.model);
76
76
  });
77
77
  }
78
78
  findFirst(args) {
79
- return this.findWithFluentCallStubs(args, 'findFirst', false, () => null);
79
+ return this.findWithFluent('findFirst', args, () => null);
80
80
  }
81
81
  findFirstOrThrow(args) {
82
- return this.findWithFluentCallStubs(args, 'findFirstOrThrow', true, () => {
82
+ return this.findWithFluent('findFirstOrThrow', args, () => {
83
83
  throw this.policyUtils.notFound(this.model);
84
84
  });
85
85
  }
86
86
  findMany(args) {
87
87
  return (0, promise_1.createDeferredPromise)(() => this.doFind(args, 'findMany', () => []));
88
88
  }
89
- // returns a promise for the given find operation, together with function stubs for fluent API calls
90
- findWithFluentCallStubs(args, actionName, resolveRoot, handleRejection) {
91
- // create a deferred promise so it's only evaluated when awaited or .then() is called
92
- const result = (0, promise_1.createDeferredPromise)(() => this.doFind(args, actionName, handleRejection));
93
- this.addFluentFunctions(result, this.model, args === null || args === void 0 ? void 0 : args.where, resolveRoot ? result : undefined);
94
- return result;
89
+ // make a find query promise with fluent API call stubs installed
90
+ findWithFluent(method, args, handleRejection) {
91
+ args = this.policyUtils.clone(args);
92
+ return (0, promise_1.createFluentPromise)(() => this.doFind(args, method, handleRejection), args, this.options.modelMeta, this.model);
93
+ }
94
+ addFluentSelect(args, field, fluentArgs) {
95
+ // overwrite include/select with the fluent field
96
+ delete args.include;
97
+ args.select = { [field]: fluentArgs !== null && fluentArgs !== void 0 ? fluentArgs : true };
95
98
  }
96
99
  doFind(args, actionName, handleRejection) {
97
- const origArgs = args;
98
- const _args = this.policyUtils.clone(args);
99
- if (!this.policyUtils.injectForRead(this.prisma, this.model, _args)) {
100
+ return __awaiter(this, void 0, void 0, function* () {
101
+ const origArgs = args;
102
+ const _args = this.policyUtils.clone(args);
103
+ if (!this.policyUtils.injectForRead(this.prisma, this.model, _args)) {
104
+ if (this.shouldLogQuery) {
105
+ this.logger.info(`[policy] \`${actionName}\` ${this.model}: unconditionally denied`);
106
+ }
107
+ return handleRejection();
108
+ }
109
+ this.policyUtils.injectReadCheckSelect(this.model, _args);
100
110
  if (this.shouldLogQuery) {
101
- this.logger.info(`[policy] \`${actionName}\` ${this.model}: unconditionally denied`);
111
+ this.logger.info(`[policy] \`${actionName}\` ${this.model}:\n${(0, utils_1.formatObject)(_args)}`);
102
112
  }
103
- return handleRejection();
104
- }
105
- this.policyUtils.injectReadCheckSelect(this.model, _args);
106
- if (this.shouldLogQuery) {
107
- this.logger.info(`[policy] \`${actionName}\` ${this.model}:\n${(0, utils_1.formatObject)(_args)}`);
108
- }
109
- return new Promise((resolve, reject) => {
110
- this.modelClient[actionName](_args).then((value) => {
111
- this.policyUtils.postProcessForRead(value, this.model, origArgs);
112
- resolve(value);
113
- }, (err) => reject(err));
113
+ const result = yield this.modelClient[actionName](_args);
114
+ this.policyUtils.postProcessForRead(result, this.model, origArgs);
115
+ return result;
114
116
  });
115
117
  }
116
- // returns a fluent API call function
117
- fluentCall(filter, fieldInfo, rootPromise) {
118
- return (args) => {
119
- args = this.policyUtils.clone(args);
120
- // combine the parent filter with the current one
121
- const backLinkField = this.requireBackLink(fieldInfo);
122
- const condition = backLinkField.isArray
123
- ? { [backLinkField.name]: { some: filter } }
124
- : { [backLinkField.name]: { is: filter } };
125
- args.where = this.policyUtils.and(args.where, condition);
126
- const promise = (0, promise_1.createDeferredPromise)(() => {
127
- // Promise for fetching
128
- const fetchFluent = (resolve, reject) => {
129
- const handler = this.makeHandler(fieldInfo.type);
130
- if (fieldInfo.isArray) {
131
- // fluent call stops here
132
- handler.findMany(args).then((value) => resolve(value), (err) => reject(err));
133
- }
134
- else {
135
- handler.findFirst(args).then((value) => resolve(value), (err) => reject(err));
136
- }
137
- };
138
- return new Promise((resolve, reject) => {
139
- if (rootPromise) {
140
- // if a root promise exists, resolve it before fluent API call,
141
- // so that fluent calls start with `findUniqueOrThrow` and `findFirstOrThrow`
142
- // can throw error properly if the root promise is rejected
143
- rootPromise.then(() => fetchFluent(resolve, reject), (err) => reject(err));
144
- }
145
- else {
146
- fetchFluent(resolve, reject);
147
- }
148
- });
149
- });
150
- if (!fieldInfo.isArray) {
151
- // prepare for a chained fluent API call
152
- this.addFluentFunctions(promise, fieldInfo.type, args.where, rootPromise);
153
- }
154
- return promise;
155
- };
156
- }
157
- // add fluent API functions to the given promise
158
- addFluentFunctions(promise, model, filter, rootPromise) {
159
- const fields = this.policyUtils.getModelFields(model);
160
- if (fields) {
161
- for (const [field, fieldInfo] of Object.entries(fields)) {
162
- if (fieldInfo.isDataModel) {
163
- promise[field] = this.fluentCall(filter, fieldInfo, rootPromise);
164
- }
165
- }
166
- }
167
- }
168
118
  //#endregion
169
119
  //#region Create
170
120
  create(args) {
171
- return __awaiter(this, void 0, void 0, function* () {
172
- if (!args) {
173
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
174
- }
175
- if (!args.data) {
176
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
177
- }
121
+ if (!args) {
122
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
123
+ }
124
+ if (!args.data) {
125
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
126
+ }
127
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
178
128
  this.policyUtils.tryReject(this.prisma, this.model, 'create');
179
129
  const origArgs = args;
180
130
  args = this.policyUtils.clone(args);
@@ -217,7 +167,7 @@ class PolicyProxyHandler {
217
167
  else {
218
168
  return result;
219
169
  }
220
- });
170
+ }));
221
171
  }
222
172
  // create with nested write
223
173
  doCreate(model, args, db) {
@@ -399,13 +349,13 @@ class PolicyProxyHandler {
399
349
  }
400
350
  }
401
351
  createMany(args) {
402
- return __awaiter(this, void 0, void 0, function* () {
403
- if (!args) {
404
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
405
- }
406
- if (!args.data) {
407
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
408
- }
352
+ if (!args) {
353
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
354
+ }
355
+ if (!args.data) {
356
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
357
+ }
358
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
409
359
  this.policyUtils.tryReject(this.prisma, this.model, 'create');
410
360
  args = this.policyUtils.clone(args);
411
361
  // go through create items, statically check input to determine if post-create
@@ -441,7 +391,7 @@ class PolicyProxyHandler {
441
391
  return result;
442
392
  }));
443
393
  }
444
- });
394
+ }));
445
395
  }
446
396
  doCreateMany(model, args, db) {
447
397
  return __awaiter(this, void 0, void 0, function* () {
@@ -543,16 +493,16 @@ class PolicyProxyHandler {
543
493
  // "updateMany" works against a set of entities, entities not passing policy check are silently
544
494
  // ignored
545
495
  update(args) {
546
- return __awaiter(this, void 0, void 0, function* () {
547
- if (!args) {
548
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
549
- }
550
- if (!args.where) {
551
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
552
- }
553
- if (!args.data) {
554
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
555
- }
496
+ if (!args) {
497
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
498
+ }
499
+ if (!args.where) {
500
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
501
+ }
502
+ if (!args.data) {
503
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
504
+ }
505
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
556
506
  args = this.policyUtils.clone(args);
557
507
  const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
558
508
  // proceed with nested writes and collect post-write checks
@@ -568,7 +518,7 @@ class PolicyProxyHandler {
568
518
  else {
569
519
  return result;
570
520
  }
571
- });
521
+ }));
572
522
  }
573
523
  doUpdate(args, db) {
574
524
  return __awaiter(this, void 0, void 0, function* () {
@@ -922,17 +872,17 @@ class PolicyProxyHandler {
922
872
  }
923
873
  }
924
874
  updateMany(args) {
925
- return __awaiter(this, void 0, void 0, function* () {
926
- if (!args) {
927
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
928
- }
929
- if (!args.data) {
930
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
931
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.options, 'query argument is required');
932
- }
933
- if (!args.data) {
934
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.options, 'data field is required in query argument');
935
- }
875
+ if (!args) {
876
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
877
+ }
878
+ if (!args.data) {
879
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
880
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.options, 'query argument is required');
881
+ }
882
+ if (!args.data) {
883
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.options, 'data field is required in query argument');
884
+ }
885
+ return (0, promise_1.createDeferredPromise)(() => {
936
886
  this.policyUtils.tryReject(this.prisma, this.model, 'update');
937
887
  args = this.policyUtils.clone(args);
938
888
  this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'update');
@@ -976,19 +926,19 @@ class PolicyProxyHandler {
976
926
  });
977
927
  }
978
928
  upsert(args) {
979
- return __awaiter(this, void 0, void 0, function* () {
980
- if (!args) {
981
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
982
- }
983
- if (!args.where) {
984
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
985
- }
986
- if (!args.create) {
987
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'create field is required in query argument');
988
- }
989
- if (!args.update) {
990
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'update field is required in query argument');
991
- }
929
+ if (!args) {
930
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
931
+ }
932
+ if (!args.where) {
933
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
934
+ }
935
+ if (!args.create) {
936
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'create field is required in query argument');
937
+ }
938
+ if (!args.update) {
939
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'update field is required in query argument');
940
+ }
941
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
992
942
  this.policyUtils.tryReject(this.prisma, this.model, 'create');
993
943
  this.policyUtils.tryReject(this.prisma, this.model, 'update');
994
944
  args = this.policyUtils.clone(args);
@@ -1016,20 +966,20 @@ class PolicyProxyHandler {
1016
966
  else {
1017
967
  return result;
1018
968
  }
1019
- });
969
+ }));
1020
970
  }
1021
971
  //#endregion
1022
972
  //#region Delete
1023
973
  // "delete" works against a single entity, and is rejected if the entity fails policy check.
1024
974
  // "deleteMany" works against a set of entities, entities that fail policy check are filtered out.
1025
975
  delete(args) {
1026
- return __awaiter(this, void 0, void 0, function* () {
1027
- if (!args) {
1028
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1029
- }
1030
- if (!args.where) {
1031
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
1032
- }
976
+ if (!args) {
977
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
978
+ }
979
+ if (!args.where) {
980
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
981
+ }
982
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
1033
983
  this.policyUtils.tryReject(this.prisma, this.model, 'delete');
1034
984
  const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
1035
985
  // do a read-back before delete
@@ -1053,10 +1003,10 @@ class PolicyProxyHandler {
1053
1003
  else {
1054
1004
  return result;
1055
1005
  }
1056
- });
1006
+ }));
1057
1007
  }
1058
1008
  deleteMany(args) {
1059
- return __awaiter(this, void 0, void 0, function* () {
1009
+ return (0, promise_1.createDeferredPromise)(() => {
1060
1010
  this.policyUtils.tryReject(this.prisma, this.model, 'delete');
1061
1011
  // inject policy conditions
1062
1012
  args = args !== null && args !== void 0 ? args : {};
@@ -1071,10 +1021,10 @@ class PolicyProxyHandler {
1071
1021
  //#endregion
1072
1022
  //#region Aggregation
1073
1023
  aggregate(args) {
1074
- return __awaiter(this, void 0, void 0, function* () {
1075
- if (!args) {
1076
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1077
- }
1024
+ if (!args) {
1025
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1026
+ }
1027
+ return (0, promise_1.createDeferredPromise)(() => {
1078
1028
  args = this.policyUtils.clone(args);
1079
1029
  // inject policy conditions
1080
1030
  this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
@@ -1085,10 +1035,10 @@ class PolicyProxyHandler {
1085
1035
  });
1086
1036
  }
1087
1037
  groupBy(args) {
1088
- return __awaiter(this, void 0, void 0, function* () {
1089
- if (!args) {
1090
- throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1091
- }
1038
+ if (!args) {
1039
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1040
+ }
1041
+ return (0, promise_1.createDeferredPromise)(() => {
1092
1042
  args = this.policyUtils.clone(args);
1093
1043
  // inject policy conditions
1094
1044
  this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
@@ -1099,7 +1049,7 @@ class PolicyProxyHandler {
1099
1049
  });
1100
1050
  }
1101
1051
  count(args) {
1102
- return __awaiter(this, void 0, void 0, function* () {
1052
+ return (0, promise_1.createDeferredPromise)(() => {
1103
1053
  // inject policy conditions
1104
1054
  args = args ? this.policyUtils.clone(args) : {};
1105
1055
  this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
@@ -1112,7 +1062,7 @@ class PolicyProxyHandler {
1112
1062
  //#endregion
1113
1063
  //#region Subscribe (Prisma Pulse)
1114
1064
  subscribe(args) {
1115
- return __awaiter(this, void 0, void 0, function* () {
1065
+ return (0, promise_1.createDeferredPromise)(() => {
1116
1066
  const readGuard = this.policyUtils.getAuthGuard(this.prisma, this.model, 'read');
1117
1067
  if (this.policyUtils.isTrue(readGuard)) {
1118
1068
  // no need to inject
@@ -1164,9 +1114,6 @@ class PolicyProxyHandler {
1164
1114
  yield Promise.all(postWriteChecks.map(({ model, operation, uniqueFilter, preValue }) => __awaiter(this, void 0, void 0, function* () { return this.policyUtils.checkPolicyForUnique(model, uniqueFilter, operation, db, undefined, preValue); })));
1165
1115
  });
1166
1116
  }
1167
- makeHandler(model) {
1168
- return new PolicyProxyHandler(this.prisma, model, this.options, this.context);
1169
- }
1170
1117
  requireBackLink(fieldInfo) {
1171
1118
  (0, tiny_invariant_1.default)(fieldInfo.backLink, `back link not found for field ${fieldInfo.name}`);
1172
1119
  return (0, cross_1.requireField)(this.modelMeta, fieldInfo.type, fieldInfo.backLink);