@zenstackhq/runtime 2.4.1 → 2.5.1

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.
Files changed (139) hide show
  1. package/browser/index.js +3 -2
  2. package/browser/index.js.map +1 -1
  3. package/browser/index.mjs +3 -2
  4. package/browser/index.mjs.map +1 -1
  5. package/constants.d.ts +1 -1
  6. package/constants.js +1 -1
  7. package/constants.js.map +1 -1
  8. package/edge.d.ts +1 -7
  9. package/edge.js +1 -7
  10. package/edge.js.map +1 -1
  11. package/enhance-edge.d.ts +1 -0
  12. package/enhance-edge.js +10 -0
  13. package/enhancements/edge/create-enhancement.d.ts +37 -0
  14. package/enhancements/{create-enhancement.js → edge/create-enhancement.js} +2 -2
  15. package/enhancements/edge/create-enhancement.js.map +1 -0
  16. package/enhancements/{default-auth.d.ts → edge/default-auth.d.ts} +2 -1
  17. package/enhancements/{default-auth.js → edge/default-auth.js} +1 -1
  18. package/enhancements/edge/default-auth.js.map +1 -0
  19. package/enhancements/{delegate.d.ts → edge/delegate.d.ts} +1 -1
  20. package/enhancements/{delegate.js → edge/delegate.js} +16 -3
  21. package/enhancements/edge/delegate.js.map +1 -0
  22. package/enhancements/{index.d.ts → edge/index.d.ts} +1 -1
  23. package/enhancements/{index.js → edge/index.js} +1 -1
  24. package/enhancements/edge/index.js.map +1 -0
  25. package/enhancements/edge/logger.js.map +1 -0
  26. package/enhancements/{omit.js → edge/omit.js} +33 -3
  27. package/enhancements/edge/omit.js.map +1 -0
  28. package/enhancements/{password.js → edge/password.js} +2 -2
  29. package/enhancements/edge/password.js.map +1 -0
  30. package/enhancements/edge/policy/check-utils.d.ts +5 -0
  31. package/enhancements/edge/policy/check-utils.js +20 -0
  32. package/enhancements/edge/policy/check-utils.js.map +1 -0
  33. package/enhancements/{policy → edge/policy}/handler.d.ts +24 -9
  34. package/enhancements/{policy → edge/policy}/handler.js +67 -91
  35. package/enhancements/edge/policy/handler.js.map +1 -0
  36. package/enhancements/{policy → edge/policy}/index.d.ts +2 -1
  37. package/enhancements/{policy → edge/policy}/index.js +2 -2
  38. package/enhancements/edge/policy/index.js.map +1 -0
  39. package/enhancements/{policy → edge/policy}/policy-utils.d.ts +5 -5
  40. package/enhancements/{policy → edge/policy}/policy-utils.js +27 -21
  41. package/enhancements/edge/policy/policy-utils.js.map +1 -0
  42. package/enhancements/{promise.d.ts → edge/promise.d.ts} +1 -1
  43. package/enhancements/{promise.js → edge/promise.js} +1 -1
  44. package/enhancements/edge/promise.js.map +1 -0
  45. package/enhancements/{proxy.d.ts → edge/proxy.d.ts} +26 -9
  46. package/enhancements/{proxy.js → edge/proxy.js} +31 -7
  47. package/enhancements/edge/proxy.js.map +1 -0
  48. package/enhancements/{query-utils.d.ts → edge/query-utils.d.ts} +2 -2
  49. package/enhancements/{query-utils.js → edge/query-utils.js} +3 -4
  50. package/enhancements/edge/query-utils.js.map +1 -0
  51. package/enhancements/{types.d.ts → edge/types.d.ts} +5 -12
  52. package/enhancements/{types.js.map → edge/types.js.map} +1 -1
  53. package/enhancements/{utils.d.ts → edge/utils.d.ts} +2 -2
  54. package/enhancements/{utils.js → edge/utils.js} +2 -2
  55. package/enhancements/edge/utils.js.map +1 -0
  56. package/enhancements/{where-visitor.d.ts → edge/where-visitor.d.ts} +1 -1
  57. package/enhancements/{where-visitor.js → edge/where-visitor.js} +1 -1
  58. package/enhancements/edge/where-visitor.js.map +1 -0
  59. package/enhancements/node/create-enhancement.d.ts +37 -0
  60. package/enhancements/node/create-enhancement.js +79 -0
  61. package/enhancements/node/create-enhancement.js.map +1 -0
  62. package/enhancements/node/default-auth.d.ts +8 -0
  63. package/enhancements/node/default-auth.js +129 -0
  64. package/enhancements/node/default-auth.js.map +1 -0
  65. package/enhancements/node/delegate.d.ts +69 -0
  66. package/enhancements/node/delegate.js +1006 -0
  67. package/enhancements/node/delegate.js.map +1 -0
  68. package/enhancements/node/index.d.ts +4 -0
  69. package/enhancements/node/index.js +21 -0
  70. package/enhancements/node/index.js.map +1 -0
  71. package/enhancements/node/logger.d.ts +29 -0
  72. package/enhancements/node/logger.js +65 -0
  73. package/enhancements/node/logger.js.map +1 -0
  74. package/enhancements/node/omit.d.ts +7 -0
  75. package/enhancements/node/omit.js +93 -0
  76. package/enhancements/node/omit.js.map +1 -0
  77. package/enhancements/node/password.d.ts +7 -0
  78. package/enhancements/node/password.js +65 -0
  79. package/enhancements/node/password.js.map +1 -0
  80. package/enhancements/node/policy/check-utils.d.ts +5 -0
  81. package/enhancements/node/policy/check-utils.js +87 -0
  82. package/enhancements/node/policy/check-utils.js.map +1 -0
  83. package/enhancements/node/policy/constraint-solver.js.map +1 -0
  84. package/enhancements/node/policy/handler.d.ts +94 -0
  85. package/enhancements/node/policy/handler.js +1357 -0
  86. package/enhancements/node/policy/handler.js.map +1 -0
  87. package/enhancements/node/policy/index.d.ts +13 -0
  88. package/enhancements/node/policy/index.js +42 -0
  89. package/enhancements/node/policy/index.js.map +1 -0
  90. package/enhancements/node/policy/policy-utils.d.ts +184 -0
  91. package/enhancements/node/policy/policy-utils.js +1296 -0
  92. package/enhancements/node/policy/policy-utils.js.map +1 -0
  93. package/enhancements/node/promise.d.ts +15 -0
  94. package/enhancements/node/promise.js +99 -0
  95. package/enhancements/node/promise.js.map +1 -0
  96. package/enhancements/node/proxy.d.ts +118 -0
  97. package/enhancements/node/proxy.js +267 -0
  98. package/enhancements/node/proxy.js.map +1 -0
  99. package/enhancements/node/query-utils.d.ts +38 -0
  100. package/enhancements/node/query-utils.js +185 -0
  101. package/enhancements/node/query-utils.js.map +1 -0
  102. package/enhancements/node/types.d.ts +224 -0
  103. package/enhancements/node/types.js +3 -0
  104. package/enhancements/node/types.js.map +1 -0
  105. package/enhancements/node/utils.d.ts +11 -0
  106. package/enhancements/node/utils.js +49 -0
  107. package/enhancements/node/utils.js.map +1 -0
  108. package/enhancements/node/where-visitor.d.ts +32 -0
  109. package/enhancements/node/where-visitor.js +86 -0
  110. package/enhancements/node/where-visitor.js.map +1 -0
  111. package/index.d.ts +2 -2
  112. package/index.js +2 -2
  113. package/index.js.map +1 -1
  114. package/package.json +14 -6
  115. package/types.d.ts +62 -0
  116. package/enhancements/create-enhancement.d.ts +0 -78
  117. package/enhancements/create-enhancement.js.map +0 -1
  118. package/enhancements/default-auth.js.map +0 -1
  119. package/enhancements/delegate.js.map +0 -1
  120. package/enhancements/index.js.map +0 -1
  121. package/enhancements/logger.js.map +0 -1
  122. package/enhancements/omit.js.map +0 -1
  123. package/enhancements/password.js.map +0 -1
  124. package/enhancements/policy/constraint-solver.js.map +0 -1
  125. package/enhancements/policy/handler.js.map +0 -1
  126. package/enhancements/policy/index.js.map +0 -1
  127. package/enhancements/policy/policy-utils.js.map +0 -1
  128. package/enhancements/promise.js.map +0 -1
  129. package/enhancements/proxy.js.map +0 -1
  130. package/enhancements/query-utils.js.map +0 -1
  131. package/enhancements/utils.js.map +0 -1
  132. package/enhancements/where-visitor.js.map +0 -1
  133. /package/enhancements/{logger.d.ts → edge/logger.d.ts} +0 -0
  134. /package/enhancements/{logger.js → edge/logger.js} +0 -0
  135. /package/enhancements/{omit.d.ts → edge/omit.d.ts} +0 -0
  136. /package/enhancements/{password.d.ts → edge/password.d.ts} +0 -0
  137. /package/enhancements/{types.js → edge/types.js} +0 -0
  138. /package/enhancements/{policy → node/policy}/constraint-solver.d.ts +0 -0
  139. /package/enhancements/{policy → node/policy}/constraint-solver.js +0 -0
@@ -0,0 +1,1357 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ var __rest = (this && this.__rest) || function (s, e) {
13
+ var t = {};
14
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
15
+ t[p] = s[p];
16
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
17
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
18
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
19
+ t[p[i]] = s[p[i]];
20
+ }
21
+ return t;
22
+ };
23
+ var __importDefault = (this && this.__importDefault) || function (mod) {
24
+ return (mod && mod.__esModule) ? mod : { "default": mod };
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.PolicyProxyHandler = void 0;
28
+ const deepmerge_1 = __importDefault(require("deepmerge"));
29
+ const lower_case_first_1 = require("lower-case-first");
30
+ const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
31
+ const upper_case_first_1 = require("upper-case-first");
32
+ const zod_validation_error_1 = require("zod-validation-error");
33
+ const constants_1 = require("../../../constants");
34
+ const cross_1 = require("../../../cross");
35
+ const logger_1 = require("../logger");
36
+ const promise_1 = require("../promise");
37
+ const query_utils_1 = require("../query-utils");
38
+ const utils_1 = require("../utils");
39
+ const check_utils_1 = require("./check-utils");
40
+ const policy_utils_1 = require("./policy-utils");
41
+ /**
42
+ * Prisma proxy handler for injecting access policy check.
43
+ */
44
+ class PolicyProxyHandler {
45
+ constructor(prisma, model, options, context) {
46
+ this.prisma = prisma;
47
+ this.options = options;
48
+ this.context = context;
49
+ this.logger = new logger_1.Logger(prisma);
50
+ this.model = (0, lower_case_first_1.lowerCaseFirst)(model);
51
+ ({ modelMeta: this.modelMeta, prismaModule: this.prismaModule } = options);
52
+ this.policyUtils = new policy_utils_1.PolicyUtil(prisma, options, context, this.shouldLogQuery);
53
+ this.queryUtils = new query_utils_1.QueryUtils(prisma, options);
54
+ }
55
+ get modelClient() {
56
+ return this.prisma[this.model];
57
+ }
58
+ //#region Find
59
+ // find operations behaves as if the entities that don't match access policies don't exist
60
+ findUnique(args) {
61
+ if (!args) {
62
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
63
+ }
64
+ if (!args.where) {
65
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
66
+ }
67
+ return this.findWithFluent('findUnique', args, () => null);
68
+ }
69
+ findUniqueOrThrow(args) {
70
+ if (!args) {
71
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
72
+ }
73
+ if (!args.where) {
74
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
75
+ }
76
+ return this.findWithFluent('findUniqueOrThrow', args, () => {
77
+ throw this.policyUtils.notFound(this.model);
78
+ });
79
+ }
80
+ findFirst(args) {
81
+ return this.findWithFluent('findFirst', args, () => null);
82
+ }
83
+ findFirstOrThrow(args) {
84
+ return this.findWithFluent('findFirstOrThrow', args, () => {
85
+ throw this.policyUtils.notFound(this.model);
86
+ });
87
+ }
88
+ findMany(args) {
89
+ return (0, promise_1.createDeferredPromise)(() => this.doFind(args, 'findMany', () => []));
90
+ }
91
+ // make a find query promise with fluent API call stubs installed
92
+ findWithFluent(method, args, handleRejection) {
93
+ args = this.policyUtils.safeClone(args);
94
+ return (0, promise_1.createFluentPromise)(() => this.doFind(args, method, handleRejection), args, this.options.modelMeta, this.model);
95
+ }
96
+ doFind(args, actionName, handleRejection) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ const origArgs = args;
99
+ const _args = this.policyUtils.safeClone(args);
100
+ if (!this.policyUtils.injectForRead(this.prisma, this.model, _args)) {
101
+ if (this.shouldLogQuery) {
102
+ this.logger.info(`[policy] \`${actionName}\` ${this.model}: unconditionally denied`);
103
+ }
104
+ return handleRejection();
105
+ }
106
+ this.policyUtils.injectReadCheckSelect(this.model, _args);
107
+ if (this.shouldLogQuery) {
108
+ this.logger.info(`[policy] \`${actionName}\` ${this.model}:\n${(0, utils_1.formatObject)(_args)}`);
109
+ }
110
+ const result = yield this.modelClient[actionName](_args);
111
+ return this.policyUtils.postProcessForRead(result, this.model, origArgs);
112
+ });
113
+ }
114
+ //#endregion
115
+ //#region Create
116
+ create(args) {
117
+ if (!args) {
118
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
119
+ }
120
+ if (!args.data) {
121
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
122
+ }
123
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
124
+ this.policyUtils.tryReject(this.prisma, this.model, 'create');
125
+ const origArgs = args;
126
+ args = this.policyUtils.safeClone(args);
127
+ // static input policy check for top-level create data
128
+ const inputCheck = this.policyUtils.checkInputGuard(this.model, args.data, 'create');
129
+ if (inputCheck === false) {
130
+ throw this.policyUtils.deniedByPolicy(this.model, 'create', undefined, constants_1.CrudFailureReason.ACCESS_POLICY_VIOLATION);
131
+ }
132
+ const hasNestedCreateOrConnect = yield this.hasNestedCreateOrConnect(args);
133
+ const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
134
+ if (
135
+ // MUST check true here since inputCheck can be undefined (meaning static input check not possible)
136
+ inputCheck === true &&
137
+ // simple create: no nested create/connect
138
+ !hasNestedCreateOrConnect) {
139
+ // there's no nested write and we've passed input check, proceed with the create directly
140
+ // validate zod schema if any
141
+ args.data = this.validateCreateInputSchema(this.model, args.data);
142
+ // make a create args only containing data and ID selection
143
+ const createArgs = { data: args.data, select: this.policyUtils.makeIdSelection(this.model) };
144
+ if (this.shouldLogQuery) {
145
+ this.logger.info(`[policy] \`create\` ${this.model}: ${(0, utils_1.formatObject)(createArgs)}`);
146
+ }
147
+ const result = yield tx[this.model].create(createArgs);
148
+ // filter the read-back data
149
+ return this.policyUtils.readBack(tx, this.model, 'create', args, result);
150
+ }
151
+ else {
152
+ // proceed with a complex create and collect post-write checks
153
+ const { result, postWriteChecks } = yield this.doCreate(this.model, args, tx);
154
+ // execute post-write checks
155
+ yield this.runPostWriteChecks(postWriteChecks, tx);
156
+ // filter the read-back data
157
+ return this.policyUtils.readBack(tx, this.model, 'create', origArgs, result);
158
+ }
159
+ }));
160
+ if (error) {
161
+ throw error;
162
+ }
163
+ else {
164
+ return result;
165
+ }
166
+ }));
167
+ }
168
+ // create with nested write
169
+ doCreate(model, args, db) {
170
+ return __awaiter(this, void 0, void 0, function* () {
171
+ // record id fields involved in the nesting context
172
+ const idSelections = [];
173
+ const pushIdFields = (model, context) => {
174
+ const idFields = (0, cross_1.getIdFields)(this.modelMeta, model);
175
+ idSelections.push({
176
+ path: context.nestingPath.map((p) => p.field).filter((f) => !!f),
177
+ ids: idFields.map((f) => f.name),
178
+ });
179
+ };
180
+ // create a string key that uniquely identifies an entity
181
+ const getEntityKey = (model, ids) => `${(0, upper_case_first_1.upperCaseFirst)(model)}#${Object.keys(ids)
182
+ .sort()
183
+ .map((f) => { var _a; return `${f}:${(_a = ids[f]) === null || _a === void 0 ? void 0 : _a.toString()}`; })
184
+ .join('_')}`;
185
+ // record keys of entities that are connected instead of created
186
+ const connectedEntities = new Set();
187
+ // visit the create payload
188
+ const visitor = new cross_1.NestedWriteVisitor(this.modelMeta, {
189
+ create: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
190
+ const validateResult = this.validateCreateInputSchema(model, args);
191
+ if (validateResult !== args) {
192
+ this.policyUtils.replace(args, validateResult);
193
+ }
194
+ pushIdFields(model, context);
195
+ }),
196
+ createMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
197
+ (0, cross_1.enumerate)(args.data).forEach((item) => {
198
+ const r = this.validateCreateInputSchema(model, item);
199
+ if (r !== item) {
200
+ this.policyUtils.replace(item, r);
201
+ }
202
+ });
203
+ pushIdFields(model, context);
204
+ }),
205
+ connectOrCreate: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
206
+ var _a;
207
+ if (!args.where) {
208
+ throw this.policyUtils.validationError(`'where' field is required for connectOrCreate`);
209
+ }
210
+ if (args.create) {
211
+ args.create = this.validateCreateInputSchema(model, args.create);
212
+ }
213
+ const existing = yield this.policyUtils.checkExistence(db, model, args.where);
214
+ if (existing) {
215
+ // connect case
216
+ if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
217
+ const backLinkField = (0, cross_1.resolveField)(this.modelMeta, model, context.field.backLink);
218
+ if (backLinkField === null || backLinkField === void 0 ? void 0 : backLinkField.isRelationOwner) {
219
+ // the target side of relation owns the relation,
220
+ // check if it's updatable
221
+ yield this.policyUtils.checkPolicyForUnique(model, args.where, 'update', db, args);
222
+ }
223
+ }
224
+ this.mergeToParent(context.parent, 'connect', args.where);
225
+ // record the key of connected entities so we can avoid validating them later
226
+ connectedEntities.add(getEntityKey(model, existing));
227
+ }
228
+ else {
229
+ // create case
230
+ pushIdFields(model, context);
231
+ // create a new "create" clause at the parent level
232
+ this.mergeToParent(context.parent, 'create', args.create);
233
+ }
234
+ // remove the connectOrCreate clause
235
+ this.removeFromParent(context.parent, 'connectOrCreate', args);
236
+ // return false to prevent visiting the nested payload
237
+ return false;
238
+ }),
239
+ connect: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
240
+ var _a;
241
+ if (!args || typeof args !== 'object' || Object.keys(args).length === 0) {
242
+ throw this.policyUtils.validationError(`'connect' field must be an non-empty object`);
243
+ }
244
+ if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
245
+ const backLinkField = (0, cross_1.resolveField)(this.modelMeta, model, context.field.backLink);
246
+ if (backLinkField === null || backLinkField === void 0 ? void 0 : backLinkField.isRelationOwner) {
247
+ // check existence
248
+ yield this.policyUtils.checkExistence(db, model, args, true);
249
+ // the target side of relation owns the relation,
250
+ // check if it's updatable
251
+ yield this.policyUtils.checkPolicyForUnique(model, args, 'update', db, args);
252
+ }
253
+ }
254
+ }),
255
+ });
256
+ yield visitor.visit(model, 'create', args);
257
+ // build the final "select" clause including all nested ID fields
258
+ let select = undefined;
259
+ if (idSelections.length > 0) {
260
+ select = {};
261
+ idSelections.forEach(({ path, ids }) => {
262
+ let curr = select;
263
+ for (const p of path) {
264
+ if (!curr[p.name]) {
265
+ curr[p.name] = { select: {} };
266
+ }
267
+ curr = curr[p.name].select;
268
+ }
269
+ Object.assign(curr, ...ids.map((f) => ({ [f]: true })));
270
+ });
271
+ }
272
+ // proceed with the create
273
+ const createArgs = { data: args.data, select };
274
+ if (this.shouldLogQuery) {
275
+ this.logger.info(`[policy] \`create\` ${model}: ${(0, utils_1.formatObject)(createArgs)}`);
276
+ }
277
+ const result = yield db[model].create(createArgs);
278
+ // post create policy check for the top-level and nested creates
279
+ const postCreateChecks = new Map();
280
+ // visit the create result and collect entities that need to be post-checked
281
+ const modelDataVisitor = new cross_1.ModelDataVisitor(this.modelMeta);
282
+ modelDataVisitor.visit(model, result, (model, _data, scalarData) => {
283
+ const key = getEntityKey(model, scalarData);
284
+ // only check if entity is created, not connected
285
+ if (!connectedEntities.has(key) && !postCreateChecks.has(key)) {
286
+ const idFields = this.policyUtils.getIdFieldValues(model, scalarData);
287
+ postCreateChecks.set(key, { model, operation: 'create', uniqueFilter: idFields });
288
+ }
289
+ });
290
+ // return only the ids of the top-level entity
291
+ const ids = this.policyUtils.getEntityIds(model, result);
292
+ return { result: ids, postWriteChecks: [...postCreateChecks.values()] };
293
+ });
294
+ }
295
+ // Checks if the given create payload has nested create or connect
296
+ hasNestedCreateOrConnect(args) {
297
+ return __awaiter(this, void 0, void 0, function* () {
298
+ let hasNestedCreateOrConnect = false;
299
+ const visitor = new cross_1.NestedWriteVisitor(this.modelMeta, {
300
+ create(_model, _args, context) {
301
+ return __awaiter(this, void 0, void 0, function* () {
302
+ if (context.field) {
303
+ hasNestedCreateOrConnect = true;
304
+ return false;
305
+ }
306
+ else {
307
+ return true;
308
+ }
309
+ });
310
+ },
311
+ connect() {
312
+ return __awaiter(this, void 0, void 0, function* () {
313
+ hasNestedCreateOrConnect = true;
314
+ return false;
315
+ });
316
+ },
317
+ connectOrCreate() {
318
+ return __awaiter(this, void 0, void 0, function* () {
319
+ hasNestedCreateOrConnect = true;
320
+ return false;
321
+ });
322
+ },
323
+ createMany() {
324
+ return __awaiter(this, void 0, void 0, function* () {
325
+ hasNestedCreateOrConnect = true;
326
+ return false;
327
+ });
328
+ },
329
+ });
330
+ yield visitor.visit(this.model, 'create', args);
331
+ return hasNestedCreateOrConnect;
332
+ });
333
+ }
334
+ // Validates the given create payload against Zod schema if any
335
+ validateCreateInputSchema(model, data) {
336
+ if (!data) {
337
+ return data;
338
+ }
339
+ return this.policyUtils.validateZodSchema(model, 'create', data, false, (err) => {
340
+ throw this.policyUtils.deniedByPolicy(model, 'create', `input failed validation: ${(0, zod_validation_error_1.fromZodError)(err)}`, constants_1.CrudFailureReason.DATA_VALIDATION_VIOLATION, err);
341
+ });
342
+ }
343
+ createMany(args) {
344
+ if (!args) {
345
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
346
+ }
347
+ if (!args.data) {
348
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
349
+ }
350
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
351
+ this.policyUtils.tryReject(this.prisma, this.model, 'create');
352
+ args = this.policyUtils.safeClone(args);
353
+ // go through create items, statically check input to determine if post-create
354
+ // check is needed, and also validate zod schema
355
+ const needPostCreateCheck = this.validateCreateInput(args);
356
+ if (!needPostCreateCheck) {
357
+ // direct create
358
+ return this.modelClient.createMany(args);
359
+ }
360
+ else {
361
+ // create entities in a transaction with post-create checks
362
+ return this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
363
+ const { result, postWriteChecks } = yield this.doCreateMany(this.model, args, tx);
364
+ // post-create check
365
+ yield this.runPostWriteChecks(postWriteChecks, tx);
366
+ return { count: result.length };
367
+ }));
368
+ }
369
+ }));
370
+ }
371
+ createManyAndReturn(args) {
372
+ if (!args) {
373
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
374
+ }
375
+ if (!args.data) {
376
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
377
+ }
378
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
379
+ var _a;
380
+ this.policyUtils.tryReject(this.prisma, this.model, 'create');
381
+ const origArgs = args;
382
+ args = this.policyUtils.safeClone(args);
383
+ // go through create items, statically check input to determine if post-create
384
+ // check is needed, and also validate zod schema
385
+ const needPostCreateCheck = this.validateCreateInput(args);
386
+ let result;
387
+ if (!needPostCreateCheck) {
388
+ // direct create
389
+ const created = yield this.modelClient.createManyAndReturn(args);
390
+ // process read-back
391
+ result = yield Promise.all(created.map((item) => this.policyUtils.readBack(this.prisma, this.model, 'create', origArgs, item)));
392
+ }
393
+ else {
394
+ // create entities in a transaction with post-create checks
395
+ result = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
396
+ const { result: created, postWriteChecks } = yield this.doCreateMany(this.model, args, tx);
397
+ // post-create check
398
+ yield this.runPostWriteChecks(postWriteChecks, tx);
399
+ // process read-back
400
+ return Promise.all(created.map((item) => this.policyUtils.readBack(tx, this.model, 'create', origArgs, item)));
401
+ }));
402
+ }
403
+ // throw read-back error if any of create result read-back fails
404
+ const error = (_a = result.find((r) => !!r.error)) === null || _a === void 0 ? void 0 : _a.error;
405
+ if (error) {
406
+ throw error;
407
+ }
408
+ else {
409
+ return result.map((r) => r.result);
410
+ }
411
+ }));
412
+ }
413
+ validateCreateInput(args) {
414
+ let needPostCreateCheck = false;
415
+ for (const item of (0, cross_1.enumerate)(args.data)) {
416
+ const validationResult = this.validateCreateInputSchema(this.model, item);
417
+ if (validationResult !== item) {
418
+ this.policyUtils.replace(item, validationResult);
419
+ }
420
+ const inputCheck = this.policyUtils.checkInputGuard(this.model, item, 'create');
421
+ if (inputCheck === false) {
422
+ // unconditionally deny
423
+ throw this.policyUtils.deniedByPolicy(this.model, 'create', undefined, constants_1.CrudFailureReason.ACCESS_POLICY_VIOLATION);
424
+ }
425
+ else if (inputCheck === true) {
426
+ // unconditionally allow
427
+ }
428
+ else if (inputCheck === undefined) {
429
+ // static policy check is not possible, need to do post-create check
430
+ needPostCreateCheck = true;
431
+ }
432
+ }
433
+ return needPostCreateCheck;
434
+ }
435
+ doCreateMany(model, args, db) {
436
+ return __awaiter(this, void 0, void 0, function* () {
437
+ // We can't call the native "createMany" because we can't get back what was created
438
+ // for post-create checks. Instead, do a "create" for each item and collect the results.
439
+ let createResult = yield Promise.all((0, cross_1.enumerate)(args.data).map((item) => __awaiter(this, void 0, void 0, function* () {
440
+ if (args.skipDuplicates) {
441
+ if (yield this.hasDuplicatedUniqueConstraint(model, item, undefined, db)) {
442
+ if (this.shouldLogQuery) {
443
+ this.logger.info(`[policy] \`createMany\` skipping duplicate ${(0, utils_1.formatObject)(item)}`);
444
+ }
445
+ return undefined;
446
+ }
447
+ }
448
+ if (this.shouldLogQuery) {
449
+ this.logger.info(`[policy] \`create\` for \`createMany\` ${model}: ${(0, utils_1.formatObject)(item)}`);
450
+ }
451
+ return yield db[model].create({ select: this.policyUtils.makeIdSelection(model), data: item });
452
+ })));
453
+ // filter undefined values due to skipDuplicates
454
+ createResult = createResult.filter((p) => !!p);
455
+ return {
456
+ result: createResult,
457
+ postWriteChecks: createResult.map((item) => ({
458
+ model,
459
+ operation: 'create',
460
+ uniqueFilter: item,
461
+ })),
462
+ };
463
+ });
464
+ }
465
+ hasDuplicatedUniqueConstraint(model, createData, upstreamQuery, db) {
466
+ return __awaiter(this, void 0, void 0, function* () {
467
+ // check unique constraint conflicts
468
+ // we can't rely on try/catch/ignore constraint violation error: https://github.com/prisma/prisma/issues/20496
469
+ // TODO: for simple cases we should be able to translate it to an `upsert` with empty `update` payload
470
+ var _a;
471
+ // for each unique constraint, check if the input item has all fields set, and if so, check if
472
+ // an entity already exists, and ignore accordingly
473
+ const uniqueConstraints = this.policyUtils.getUniqueConstraints(model);
474
+ for (const constraint of Object.values(uniqueConstraints)) {
475
+ // the unique filter used to check existence
476
+ const uniqueFilter = {};
477
+ // unique constraint fields not covered yet
478
+ const remainingConstraintFields = new Set(constraint.fields);
479
+ // collect constraint fields from the create data
480
+ for (const [k, v] of Object.entries(createData)) {
481
+ if (v === undefined) {
482
+ continue;
483
+ }
484
+ if (remainingConstraintFields.has(k)) {
485
+ uniqueFilter[k] = v;
486
+ remainingConstraintFields.delete(k);
487
+ }
488
+ }
489
+ // collect constraint fields from the upstream query
490
+ if (upstreamQuery) {
491
+ for (const [k, v] of Object.entries(upstreamQuery)) {
492
+ if (v === undefined) {
493
+ continue;
494
+ }
495
+ if (remainingConstraintFields.has(k)) {
496
+ uniqueFilter[k] = v;
497
+ remainingConstraintFields.delete(k);
498
+ continue;
499
+ }
500
+ // check if the upstream query contains a relation field which covers
501
+ // a foreign key field constraint
502
+ const fieldInfo = (0, cross_1.requireField)(this.modelMeta, model, k);
503
+ if (!fieldInfo.isDataModel) {
504
+ // only care about relation fields
505
+ continue;
506
+ }
507
+ // merge the upstream query into the unique filter
508
+ uniqueFilter[k] = v;
509
+ // mark the corresponding foreign key fields as covered
510
+ const fkMapping = (_a = fieldInfo.foreignKeyMapping) !== null && _a !== void 0 ? _a : {};
511
+ for (const fk of Object.values(fkMapping)) {
512
+ remainingConstraintFields.delete(fk);
513
+ }
514
+ }
515
+ }
516
+ if (remainingConstraintFields.size === 0) {
517
+ // all constraint fields set, check existence
518
+ const existing = yield this.policyUtils.checkExistence(db, model, uniqueFilter);
519
+ if (existing) {
520
+ return true;
521
+ }
522
+ }
523
+ }
524
+ return false;
525
+ });
526
+ }
527
+ //#endregion
528
+ //#region Update & Upsert
529
+ // "update" and "upsert" work against unique entity, so we actively rejects the request if the
530
+ // entity fails policy check
531
+ //
532
+ // "updateMany" works against a set of entities, entities not passing policy check are silently
533
+ // ignored
534
+ update(args) {
535
+ if (!args) {
536
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
537
+ }
538
+ if (!args.where) {
539
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
540
+ }
541
+ if (!args.data) {
542
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
543
+ }
544
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
545
+ args = this.policyUtils.safeClone(args);
546
+ const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
547
+ // proceed with nested writes and collect post-write checks
548
+ const { result, postWriteChecks } = yield this.doUpdate(args, tx);
549
+ // post-write check
550
+ yield this.runPostWriteChecks(postWriteChecks, tx);
551
+ // filter the read-back data
552
+ return this.policyUtils.readBack(tx, this.model, 'update', args, result);
553
+ }));
554
+ if (error) {
555
+ throw error;
556
+ }
557
+ else {
558
+ return result;
559
+ }
560
+ }));
561
+ }
562
+ doUpdate(args, db) {
563
+ return __awaiter(this, void 0, void 0, function* () {
564
+ // collected post-update checks
565
+ const postWriteChecks = [];
566
+ // registers a post-update check task
567
+ const _registerPostUpdateCheck = (model, preUpdateLookupFilter, postUpdateLookupFilter) => __awaiter(this, void 0, void 0, function* () {
568
+ // both "post-update" rules and Zod schemas require a post-update check
569
+ if (this.policyUtils.hasAuthGuard(model, 'postUpdate') || this.policyUtils.getZodSchema(model)) {
570
+ // select pre-update field values
571
+ let preValue;
572
+ const preValueSelect = this.policyUtils.getPreValueSelect(model);
573
+ if (preValueSelect && Object.keys(preValueSelect).length > 0) {
574
+ preValue = yield db[model].findFirst({ where: preUpdateLookupFilter, select: preValueSelect });
575
+ }
576
+ postWriteChecks.push({
577
+ model,
578
+ operation: 'postUpdate',
579
+ uniqueFilter: postUpdateLookupFilter,
580
+ preValue,
581
+ });
582
+ }
583
+ });
584
+ // We can't let the native "update" to handle nested "create" because we can't get back what
585
+ // was created for doing post-update checks.
586
+ // Instead, handle nested create inside update as an atomic operation that creates an entire
587
+ // subtree (containing nested creates/connects)
588
+ const _create = (model, args, context) => __awaiter(this, void 0, void 0, function* () {
589
+ var _a;
590
+ let createData = args;
591
+ if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
592
+ // Check if the create payload contains any "unsafe" assignment:
593
+ // assign id or foreign key fields.
594
+ //
595
+ // The reason why we need to do that is Prisma's mutations payload
596
+ // structure has two mutually exclusive forms for safe and unsafe
597
+ // operations. E.g.:
598
+ // - safe: { data: { user: { connect: { id: 1 }} } }
599
+ // - unsafe: { data: { userId: 1 } }
600
+ const unsafe = (0, utils_1.isUnsafeMutate)(model, args, this.modelMeta);
601
+ // handles the connection to upstream entity
602
+ const reversedQuery = this.policyUtils.buildReversedQuery(context, true, unsafe);
603
+ if ((!unsafe || context.field.isRelationOwner) && reversedQuery[context.field.backLink]) {
604
+ // if mutation is safe, or current field owns the relation (so the other side has no fk),
605
+ // and the reverse query contains the back link, then we can build a "connect" with it
606
+ createData = Object.assign(Object.assign({}, createData), { [context.field.backLink]: {
607
+ connect: reversedQuery[context.field.backLink],
608
+ } });
609
+ }
610
+ else {
611
+ // otherwise, the reverse query should be translated to foreign key setting
612
+ // and merged to the create data
613
+ const backLinkField = this.requireBackLink(context.field);
614
+ (0, tiny_invariant_1.default)(backLinkField.foreignKeyMapping);
615
+ // try to extract foreign key values from the reverse query
616
+ let fkValues = Object.values(backLinkField.foreignKeyMapping).reduce((obj, fk) => {
617
+ obj[fk] = reversedQuery[fk];
618
+ return obj;
619
+ }, {});
620
+ if (Object.values(fkValues).every((v) => v !== undefined)) {
621
+ // all foreign key values are available, merge them to the create data
622
+ createData = Object.assign(Object.assign({}, createData), fkValues);
623
+ }
624
+ else {
625
+ // some foreign key values are missing, need to look up the upstream entity,
626
+ // this can happen when the upstream entity doesn't have a unique where clause,
627
+ // for example when it's nested inside a one-to-one update
628
+ const upstreamQuery = {
629
+ where: reversedQuery[backLinkField.name],
630
+ select: this.policyUtils.makeIdSelection(backLinkField.type),
631
+ };
632
+ // fetch the upstream entity
633
+ if (this.shouldLogQuery) {
634
+ this.logger.info(`[policy] \`findUniqueOrThrow\` ${model}: looking up upstream entity of ${backLinkField.type}, ${(0, utils_1.formatObject)(upstreamQuery)}`);
635
+ }
636
+ const upstreamEntity = yield this.prisma[backLinkField.type].findUniqueOrThrow(upstreamQuery);
637
+ // map ids to foreign keys
638
+ fkValues = Object.entries(backLinkField.foreignKeyMapping).reduce((obj, [id, fk]) => {
639
+ obj[fk] = upstreamEntity[id];
640
+ return obj;
641
+ }, {});
642
+ // merge them to the create data
643
+ createData = Object.assign(Object.assign({}, createData), fkValues);
644
+ }
645
+ }
646
+ }
647
+ // proceed with the create and collect post-create checks
648
+ const { postWriteChecks: checks, result } = yield this.doCreate(model, { data: createData }, db);
649
+ postWriteChecks.push(...checks);
650
+ return result;
651
+ });
652
+ const _createMany = (model, args, context) => __awaiter(this, void 0, void 0, function* () {
653
+ for (const item of (0, cross_1.enumerate)(args.data)) {
654
+ if (args.skipDuplicates) {
655
+ // get a reversed query to include fields inherited from upstream mutation,
656
+ // it'll be merged with the create payload for unique constraint checking
657
+ const upstreamQuery = this.policyUtils.buildReversedQuery(context);
658
+ if (yield this.hasDuplicatedUniqueConstraint(model, item, upstreamQuery, db)) {
659
+ if (this.shouldLogQuery) {
660
+ this.logger.info(`[policy] \`createMany\` skipping duplicate ${(0, utils_1.formatObject)(item)}`);
661
+ }
662
+ continue;
663
+ }
664
+ }
665
+ yield _create(model, item, context);
666
+ }
667
+ });
668
+ const _connectDisconnect = (model, args, context, operation) => __awaiter(this, void 0, void 0, function* () {
669
+ var _a;
670
+ if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
671
+ const backLinkField = this.policyUtils.getModelField(model, context.field.backLink);
672
+ if (backLinkField === null || backLinkField === void 0 ? void 0 : backLinkField.isRelationOwner) {
673
+ let uniqueFilter = args;
674
+ if (operation === 'disconnect') {
675
+ // disconnect filter is not unique, need to build a reversed query to
676
+ // locate the entity and use its id fields as unique filter
677
+ const reversedQuery = this.policyUtils.buildReversedQuery(context);
678
+ const found = yield db[model].findUnique({
679
+ where: reversedQuery,
680
+ select: this.policyUtils.makeIdSelection(model),
681
+ });
682
+ uniqueFilter = found && this.policyUtils.getIdFieldValues(model, found);
683
+ }
684
+ // update happens on the related model, require updatable,
685
+ // translate args to foreign keys so field-level policies can be checked
686
+ const checkArgs = {};
687
+ if (args && typeof args === 'object' && backLinkField.foreignKeyMapping) {
688
+ for (const key of Object.keys(args)) {
689
+ const fk = backLinkField.foreignKeyMapping[key];
690
+ if (fk) {
691
+ checkArgs[fk] = args[key];
692
+ }
693
+ }
694
+ }
695
+ // `uniqueFilter` can be undefined if the entity to be disconnected doesn't exist
696
+ if (uniqueFilter) {
697
+ // check for update
698
+ yield this.policyUtils.checkPolicyForUnique(model, uniqueFilter, 'update', db, checkArgs);
699
+ // register post-update check
700
+ yield _registerPostUpdateCheck(model, uniqueFilter, uniqueFilter);
701
+ }
702
+ }
703
+ }
704
+ });
705
+ // visit nested writes
706
+ const visitor = new cross_1.NestedWriteVisitor(this.modelMeta, {
707
+ update: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
708
+ var _a;
709
+ // build a unique query including upstream conditions
710
+ const uniqueFilter = this.policyUtils.buildReversedQuery(context);
711
+ // handle not-found
712
+ const existing = yield this.policyUtils.checkExistence(db, model, uniqueFilter, true);
713
+ // check if the update actually writes to this model
714
+ let thisModelUpdate = false;
715
+ const updatePayload = (_a = args.data) !== null && _a !== void 0 ? _a : args;
716
+ const validatedPayload = this.validateUpdateInputSchema(model, updatePayload);
717
+ if (validatedPayload !== updatePayload) {
718
+ this.policyUtils.replace(updatePayload, validatedPayload);
719
+ }
720
+ if (updatePayload) {
721
+ for (const key of Object.keys(updatePayload)) {
722
+ const field = (0, cross_1.resolveField)(this.modelMeta, model, key);
723
+ if (field) {
724
+ if (!field.isDataModel) {
725
+ // scalar field, require this model to be updatable
726
+ thisModelUpdate = true;
727
+ break;
728
+ }
729
+ else if (field.isRelationOwner) {
730
+ // relation is being updated and this model owns foreign key, require updatable
731
+ thisModelUpdate = true;
732
+ break;
733
+ }
734
+ }
735
+ }
736
+ }
737
+ if (thisModelUpdate) {
738
+ this.policyUtils.tryReject(db, this.model, 'update');
739
+ // check pre-update guard
740
+ yield this.policyUtils.checkPolicyForUnique(model, uniqueFilter, 'update', db, args);
741
+ // handle the case where id fields are updated
742
+ const _args = args;
743
+ const updatePayload = _args.data && typeof _args.data === 'object' ? _args.data : _args;
744
+ const postUpdateIds = this.calculatePostUpdateIds(model, existing, updatePayload);
745
+ // register post-update check
746
+ yield _registerPostUpdateCheck(model, existing, postUpdateIds);
747
+ }
748
+ }),
749
+ updateMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
750
+ // prepare for post-update check
751
+ if (this.policyUtils.hasAuthGuard(model, 'postUpdate') || this.policyUtils.getZodSchema(model)) {
752
+ let select = this.policyUtils.makeIdSelection(model);
753
+ const preValueSelect = this.policyUtils.getPreValueSelect(model);
754
+ if (preValueSelect) {
755
+ select = Object.assign(Object.assign({}, select), preValueSelect);
756
+ }
757
+ const reversedQuery = this.policyUtils.buildReversedQuery(context);
758
+ const currentSetQuery = { select, where: reversedQuery };
759
+ this.policyUtils.injectAuthGuardAsWhere(db, currentSetQuery, model, 'read');
760
+ if (this.shouldLogQuery) {
761
+ this.logger.info(`[policy] \`findMany\` for post update check ${model}:\n${(0, utils_1.formatObject)(currentSetQuery)}`);
762
+ }
763
+ const currentSet = yield db[model].findMany(currentSetQuery);
764
+ postWriteChecks.push(...currentSet.map((preValue) => ({
765
+ model,
766
+ operation: 'postUpdate',
767
+ uniqueFilter: preValue,
768
+ preValue: preValueSelect ? preValue : undefined,
769
+ })));
770
+ }
771
+ args.data = this.validateUpdateInputSchema(model, args.data);
772
+ const updateGuard = this.policyUtils.getAuthGuard(db, model, 'update');
773
+ if (this.policyUtils.isTrue(updateGuard) || this.policyUtils.isFalse(updateGuard)) {
774
+ // injects simple auth guard into where clause
775
+ this.policyUtils.injectAuthGuardAsWhere(db, args, model, 'update');
776
+ }
777
+ else {
778
+ // we have to process `updateMany` separately because the guard may contain
779
+ // filters using relation fields which are not allowed in nested `updateMany`
780
+ const reversedQuery = this.policyUtils.buildReversedQuery(context);
781
+ const updateWhere = this.policyUtils.and(reversedQuery, updateGuard);
782
+ if (this.shouldLogQuery) {
783
+ this.logger.info(`[policy] \`updateMany\` ${model}:\n${(0, utils_1.formatObject)({
784
+ where: updateWhere,
785
+ data: args.data,
786
+ })}`);
787
+ }
788
+ yield db[model].updateMany({ where: updateWhere, data: args.data });
789
+ delete context.parent.updateMany;
790
+ }
791
+ }),
792
+ create: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
793
+ // process the entire create subtree separately
794
+ yield _create(model, args, context);
795
+ // remove it from the update payload
796
+ this.removeFromParent(context.parent, 'create', args);
797
+ // don't visit payload
798
+ return false;
799
+ }),
800
+ createMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
801
+ // process createMany separately
802
+ yield _createMany(model, args, context);
803
+ // remove it from the update payload
804
+ delete context.parent.createMany;
805
+ // don't visit payload
806
+ return false;
807
+ }),
808
+ upsert: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
809
+ // build a unique query including upstream conditions
810
+ const uniqueFilter = this.policyUtils.buildReversedQuery(context);
811
+ // branch based on if the update target exists
812
+ const existing = yield this.policyUtils.checkExistence(db, model, uniqueFilter);
813
+ if (existing) {
814
+ // update case
815
+ // check pre-update guard
816
+ yield this.policyUtils.checkPolicyForUnique(model, existing, 'update', db, args);
817
+ // handle the case where id fields are updated
818
+ const postUpdateIds = this.calculatePostUpdateIds(model, existing, args.update);
819
+ // register post-update check
820
+ yield _registerPostUpdateCheck(model, existing, postUpdateIds);
821
+ // convert upsert to update
822
+ const convertedUpdate = {
823
+ where: args.where,
824
+ data: this.validateUpdateInputSchema(model, args.update),
825
+ };
826
+ this.mergeToParent(context.parent, 'update', convertedUpdate);
827
+ this.removeFromParent(context.parent, 'upsert', args);
828
+ // continue visiting the new payload
829
+ return convertedUpdate;
830
+ }
831
+ else {
832
+ // create case
833
+ // process the entire create subtree separately
834
+ yield _create(model, args.create, context);
835
+ // remove it from the update payload
836
+ this.removeFromParent(context.parent, 'upsert', args);
837
+ // don't visit payload
838
+ return false;
839
+ }
840
+ }),
841
+ connect: (model, args, context) => __awaiter(this, void 0, void 0, function* () { return _connectDisconnect(model, args, context, 'connect'); }),
842
+ connectOrCreate: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
843
+ // the where condition is already unique, so we can use it to check if the target exists
844
+ const existing = yield this.policyUtils.checkExistence(db, model, args.where);
845
+ if (existing) {
846
+ // connect
847
+ yield _connectDisconnect(model, args.where, context, 'connect');
848
+ return true;
849
+ }
850
+ else {
851
+ // create
852
+ const created = yield _create(model, args.create, context);
853
+ const upperContext = context.nestingPath[context.nestingPath.length - 2];
854
+ if ((upperContext === null || upperContext === void 0 ? void 0 : upperContext.where) && context.field) {
855
+ // check if the where clause of the upper context references the id
856
+ // of the connected entity, if so, we need to update it
857
+ this.overrideForeignKeyFields(upperContext.model, upperContext.where, context.field, created);
858
+ }
859
+ // remove the payload from the parent
860
+ this.removeFromParent(context.parent, 'connectOrCreate', args);
861
+ return false;
862
+ }
863
+ }),
864
+ disconnect: (model, args, context) => __awaiter(this, void 0, void 0, function* () { return _connectDisconnect(model, args, context, 'disconnect'); }),
865
+ set: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
866
+ // find the set of items to be replaced
867
+ const reversedQuery = this.policyUtils.buildReversedQuery(context);
868
+ const findCurrSetArgs = {
869
+ select: this.policyUtils.makeIdSelection(model),
870
+ where: reversedQuery,
871
+ };
872
+ if (this.shouldLogQuery) {
873
+ this.logger.info(`[policy] \`findMany\` ${model}:\n${(0, utils_1.formatObject)(findCurrSetArgs)}`);
874
+ }
875
+ const currentSet = yield db[model].findMany(findCurrSetArgs);
876
+ // register current set for update (foreign key)
877
+ yield Promise.all(currentSet.map((item) => _connectDisconnect(model, item, context, 'disconnect')));
878
+ // proceed with connecting the new set
879
+ yield Promise.all((0, cross_1.enumerate)(args).map((item) => _connectDisconnect(model, item, context, 'connect')));
880
+ }),
881
+ delete: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
882
+ // build a unique query including upstream conditions
883
+ const uniqueFilter = this.policyUtils.buildReversedQuery(context);
884
+ // handle not-found
885
+ yield this.policyUtils.checkExistence(db, model, uniqueFilter, true);
886
+ // check delete guard
887
+ yield this.policyUtils.checkPolicyForUnique(model, uniqueFilter, 'delete', db, args);
888
+ }),
889
+ deleteMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
890
+ const guard = yield this.policyUtils.getAuthGuard(db, model, 'delete');
891
+ if (this.policyUtils.isTrue(guard) || this.policyUtils.isFalse(guard)) {
892
+ // inject simple auth guard
893
+ context.parent.deleteMany = this.policyUtils.and(args, guard);
894
+ }
895
+ else {
896
+ // we have to process `deleteMany` separately because the guard may contain
897
+ // filters using relation fields which are not allowed in nested `deleteMany`
898
+ const reversedQuery = this.policyUtils.buildReversedQuery(context);
899
+ const deleteWhere = this.policyUtils.and(reversedQuery, guard);
900
+ if (this.shouldLogQuery) {
901
+ this.logger.info(`[policy] \`deleteMany\` ${model}:\n${(0, utils_1.formatObject)({ where: deleteWhere })}`);
902
+ }
903
+ yield db[model].deleteMany({ where: deleteWhere });
904
+ delete context.parent.deleteMany;
905
+ }
906
+ }),
907
+ });
908
+ yield visitor.visit(this.model, 'update', args);
909
+ // finally proceed with the update
910
+ if (this.shouldLogQuery) {
911
+ this.logger.info(`[policy] \`update\` ${this.model}: ${(0, utils_1.formatObject)(args)}`);
912
+ }
913
+ const result = yield db[this.model].update({
914
+ where: args.where,
915
+ data: args.data,
916
+ select: this.policyUtils.makeIdSelection(this.model),
917
+ });
918
+ return { result, postWriteChecks };
919
+ });
920
+ }
921
+ // calculate id fields used for post-update check given an update payload
922
+ calculatePostUpdateIds(_model, currentIds, updatePayload) {
923
+ const result = this.policyUtils.safeClone(currentIds);
924
+ for (const key of Object.keys(currentIds)) {
925
+ const updateValue = updatePayload[key];
926
+ if (typeof updateValue === 'string' || typeof updateValue === 'number' || typeof updateValue === 'bigint') {
927
+ result[key] = updateValue;
928
+ }
929
+ }
930
+ return result;
931
+ }
932
+ // updates foreign key fields inside `payload` based on relation id fields in `newIds`
933
+ overrideForeignKeyFields(model, payload, relation, newIds) {
934
+ if (!relation.foreignKeyMapping || Object.keys(relation.foreignKeyMapping).length === 0) {
935
+ return;
936
+ }
937
+ // override foreign key values
938
+ for (const [id, fk] of Object.entries(relation.foreignKeyMapping)) {
939
+ if (payload[fk] !== undefined && newIds[id] !== undefined) {
940
+ payload[fk] = newIds[id];
941
+ }
942
+ }
943
+ // deal with compound id fields
944
+ const uniqueConstraints = this.policyUtils.getUniqueConstraints(model);
945
+ for (const [name, constraint] of Object.entries(uniqueConstraints)) {
946
+ if (constraint.fields.length > 1) {
947
+ const target = payload[name];
948
+ if (target) {
949
+ for (const [id, fk] of Object.entries(relation.foreignKeyMapping)) {
950
+ if (target[fk] !== undefined && newIds[id] !== undefined) {
951
+ target[fk] = newIds[id];
952
+ }
953
+ }
954
+ }
955
+ }
956
+ }
957
+ }
958
+ // Validates the given update payload against Zod schema if any
959
+ validateUpdateInputSchema(model, data) {
960
+ if (!data) {
961
+ return data;
962
+ }
963
+ // update payload can contain non-literal fields, like:
964
+ // { x: { increment: 1 } }
965
+ // we should only validate literal fields
966
+ const literalData = Object.entries(data).reduce((acc, [k, v]) => (Object.assign(Object.assign({}, acc), (typeof v !== 'object' ? { [k]: v } : {}))), {});
967
+ const validatedData = this.policyUtils.validateZodSchema(model, 'update', literalData, false, (err) => {
968
+ throw this.policyUtils.deniedByPolicy(model, 'update', `input failed validation: ${(0, zod_validation_error_1.fromZodError)(err)}`, constants_1.CrudFailureReason.DATA_VALIDATION_VIOLATION, err);
969
+ });
970
+ // schema may have transformed field values, use it to overwrite the original data
971
+ return Object.assign(Object.assign({}, data), validatedData);
972
+ }
973
+ updateMany(args) {
974
+ if (!args) {
975
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
976
+ }
977
+ if (!args.data) {
978
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
979
+ }
980
+ return (0, promise_1.createDeferredPromise)(() => {
981
+ this.policyUtils.tryReject(this.prisma, this.model, 'update');
982
+ args = this.policyUtils.safeClone(args);
983
+ this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'update');
984
+ args.data = this.validateUpdateInputSchema(this.model, args.data);
985
+ const entityChecker = this.policyUtils.getEntityChecker(this.model, 'update');
986
+ const canProceedWithoutTransaction =
987
+ // no post-update rules
988
+ !this.policyUtils.hasAuthGuard(this.model, 'postUpdate') &&
989
+ // no Zod schema
990
+ !this.policyUtils.getZodSchema(this.model) &&
991
+ // no entity checker
992
+ !entityChecker;
993
+ if (canProceedWithoutTransaction) {
994
+ // proceed without a transaction
995
+ if (this.shouldLogQuery) {
996
+ this.logger.info(`[policy] \`updateMany\` ${this.model}: ${(0, utils_1.formatObject)(args)}`);
997
+ }
998
+ return this.modelClient.updateMany(args);
999
+ }
1000
+ // collect post-update checks
1001
+ const postWriteChecks = [];
1002
+ return this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
1003
+ // collect pre-update values
1004
+ let select = this.policyUtils.makeIdSelection(this.model);
1005
+ const preValueSelect = this.policyUtils.getPreValueSelect(this.model);
1006
+ if (preValueSelect) {
1007
+ select = Object.assign(Object.assign({}, select), preValueSelect);
1008
+ }
1009
+ // merge selection required for running additional checker
1010
+ const entityChecker = this.policyUtils.getEntityChecker(this.model, 'update');
1011
+ if (entityChecker === null || entityChecker === void 0 ? void 0 : entityChecker.selector) {
1012
+ select = (0, deepmerge_1.default)(select, entityChecker.selector);
1013
+ }
1014
+ const currentSetQuery = { select, where: args.where };
1015
+ this.policyUtils.injectAuthGuardAsWhere(tx, currentSetQuery, this.model, 'update');
1016
+ if (this.shouldLogQuery) {
1017
+ this.logger.info(`[policy] \`findMany\` ${this.model}: ${(0, utils_1.formatObject)(currentSetQuery)}`);
1018
+ }
1019
+ let candidates = yield tx[this.model].findMany(currentSetQuery);
1020
+ if (entityChecker) {
1021
+ // filter candidates with additional checker and build an id filter
1022
+ const r = this.buildIdFilterWithEntityChecker(candidates, entityChecker.func);
1023
+ candidates = r.filteredCandidates;
1024
+ // merge id filter into update's where clause
1025
+ args.where = args.where ? { AND: [args.where, r.idFilter] } : r.idFilter;
1026
+ }
1027
+ postWriteChecks.push(...candidates.map((preValue) => ({
1028
+ model: this.model,
1029
+ operation: 'postUpdate',
1030
+ uniqueFilter: this.policyUtils.getEntityIds(this.model, preValue),
1031
+ preValue: preValueSelect ? preValue : undefined,
1032
+ })));
1033
+ // proceed with the update
1034
+ if (this.shouldLogQuery) {
1035
+ this.logger.info(`[policy] \`updateMany\` in tx for ${this.model}: ${(0, utils_1.formatObject)(args)}`);
1036
+ }
1037
+ const result = yield tx[this.model].updateMany(args);
1038
+ // run post-write checks
1039
+ yield this.runPostWriteChecks(postWriteChecks, tx);
1040
+ return result;
1041
+ }));
1042
+ });
1043
+ }
1044
+ upsert(args) {
1045
+ if (!args) {
1046
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1047
+ }
1048
+ if (!args.where) {
1049
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
1050
+ }
1051
+ if (!args.create) {
1052
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'create field is required in query argument');
1053
+ }
1054
+ if (!args.update) {
1055
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'update field is required in query argument');
1056
+ }
1057
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
1058
+ this.policyUtils.tryReject(this.prisma, this.model, 'create');
1059
+ this.policyUtils.tryReject(this.prisma, this.model, 'update');
1060
+ args = this.policyUtils.safeClone(args);
1061
+ // We can call the native "upsert" because we can't tell if an entity was created or updated
1062
+ // for doing post-write check accordingly. Instead, decompose it into create or update.
1063
+ const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
1064
+ const { where, create, update } = args, rest = __rest(args, ["where", "create", "update"]);
1065
+ const existing = yield this.policyUtils.checkExistence(tx, this.model, where);
1066
+ if (existing) {
1067
+ // update case
1068
+ const { result, postWriteChecks } = yield this.doUpdate(Object.assign({ where: this.policyUtils.composeCompoundUniqueField(this.model, existing), data: update }, rest), tx);
1069
+ yield this.runPostWriteChecks(postWriteChecks, tx);
1070
+ return this.policyUtils.readBack(tx, this.model, 'update', args, result);
1071
+ }
1072
+ else {
1073
+ // create case
1074
+ const { result, postWriteChecks } = yield this.doCreate(this.model, Object.assign({ data: create }, rest), tx);
1075
+ yield this.runPostWriteChecks(postWriteChecks, tx);
1076
+ return this.policyUtils.readBack(tx, this.model, 'create', args, result);
1077
+ }
1078
+ }));
1079
+ if (error) {
1080
+ throw error;
1081
+ }
1082
+ else {
1083
+ return result;
1084
+ }
1085
+ }));
1086
+ }
1087
+ //#endregion
1088
+ //#region Delete
1089
+ // "delete" works against a single entity, and is rejected if the entity fails policy check.
1090
+ // "deleteMany" works against a set of entities, entities that fail policy check are filtered out.
1091
+ delete(args) {
1092
+ if (!args) {
1093
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1094
+ }
1095
+ if (!args.where) {
1096
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
1097
+ }
1098
+ return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
1099
+ this.policyUtils.tryReject(this.prisma, this.model, 'delete');
1100
+ const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
1101
+ // do a read-back before delete
1102
+ const r = yield this.policyUtils.readBack(tx, this.model, 'delete', args, args.where);
1103
+ const error = r.error;
1104
+ const read = r.result;
1105
+ // check existence
1106
+ yield this.policyUtils.checkExistence(tx, this.model, args.where, true);
1107
+ // inject delete guard
1108
+ yield this.policyUtils.checkPolicyForUnique(this.model, args.where, 'delete', tx, args);
1109
+ // proceed with the deletion
1110
+ if (this.shouldLogQuery) {
1111
+ this.logger.info(`[policy] \`delete\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
1112
+ }
1113
+ yield tx[this.model].delete(args);
1114
+ return { result: read, error };
1115
+ }));
1116
+ if (error) {
1117
+ throw error;
1118
+ }
1119
+ else {
1120
+ return result;
1121
+ }
1122
+ }));
1123
+ }
1124
+ deleteMany(args) {
1125
+ return (0, promise_1.createDeferredPromise)(() => {
1126
+ this.policyUtils.tryReject(this.prisma, this.model, 'delete');
1127
+ // inject policy conditions
1128
+ args = this.policyUtils.safeClone(args);
1129
+ this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'delete');
1130
+ const entityChecker = this.policyUtils.getEntityChecker(this.model, 'delete');
1131
+ if (entityChecker) {
1132
+ // additional checker exists, need to run deletion inside a transaction
1133
+ return this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
1134
+ // find the delete candidates, selecting id fields and fields needed for
1135
+ // running the additional checker
1136
+ let candidateSelect = this.policyUtils.makeIdSelection(this.model);
1137
+ if (entityChecker.selector) {
1138
+ candidateSelect = (0, deepmerge_1.default)(candidateSelect, entityChecker.selector);
1139
+ }
1140
+ if (this.shouldLogQuery) {
1141
+ this.logger.info(`[policy] \`findMany\` ${this.model}: ${(0, utils_1.formatObject)({
1142
+ where: args.where,
1143
+ select: candidateSelect,
1144
+ })}`);
1145
+ }
1146
+ const candidates = yield tx[this.model].findMany({ where: args.where, select: candidateSelect });
1147
+ // build a ID filter based on id values filtered by the additional checker
1148
+ const { idFilter } = this.buildIdFilterWithEntityChecker(candidates, entityChecker.func);
1149
+ // merge the ID filter into the where clause
1150
+ args.where = args.where ? { AND: [args.where, idFilter] } : idFilter;
1151
+ // finally, conduct the deletion with the combined where clause
1152
+ if (this.shouldLogQuery) {
1153
+ this.logger.info(`[policy] \`deleteMany\` in tx for ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
1154
+ }
1155
+ return tx[this.model].deleteMany(args);
1156
+ }));
1157
+ }
1158
+ else {
1159
+ // conduct the deletion directly
1160
+ if (this.shouldLogQuery) {
1161
+ this.logger.info(`[policy] \`deleteMany\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
1162
+ }
1163
+ return this.modelClient.deleteMany(args);
1164
+ }
1165
+ });
1166
+ }
1167
+ //#endregion
1168
+ //#region Aggregation
1169
+ aggregate(args) {
1170
+ if (!args) {
1171
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1172
+ }
1173
+ return (0, promise_1.createDeferredPromise)(() => {
1174
+ args = this.policyUtils.safeClone(args);
1175
+ // inject policy conditions
1176
+ this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
1177
+ if (this.shouldLogQuery) {
1178
+ this.logger.info(`[policy] \`aggregate\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
1179
+ }
1180
+ return this.modelClient.aggregate(args);
1181
+ });
1182
+ }
1183
+ groupBy(args) {
1184
+ if (!args) {
1185
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
1186
+ }
1187
+ return (0, promise_1.createDeferredPromise)(() => {
1188
+ args = this.policyUtils.safeClone(args);
1189
+ // inject policy conditions
1190
+ this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
1191
+ if (this.shouldLogQuery) {
1192
+ this.logger.info(`[policy] \`groupBy\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
1193
+ }
1194
+ return this.modelClient.groupBy(args);
1195
+ });
1196
+ }
1197
+ count(args) {
1198
+ return (0, promise_1.createDeferredPromise)(() => {
1199
+ // inject policy conditions
1200
+ args = args ? this.policyUtils.safeClone(args) : {};
1201
+ this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
1202
+ if (this.shouldLogQuery) {
1203
+ this.logger.info(`[policy] \`count\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
1204
+ }
1205
+ return this.modelClient.count(args);
1206
+ });
1207
+ }
1208
+ //#endregion
1209
+ //#region Prisma Pulse
1210
+ subscribe(args) {
1211
+ return this.handleSubscribeStream('subscribe', args);
1212
+ }
1213
+ stream(args) {
1214
+ return this.handleSubscribeStream('stream', args);
1215
+ }
1216
+ handleSubscribeStream(action, args) {
1217
+ return __awaiter(this, void 0, void 0, function* () {
1218
+ if (!args) {
1219
+ // include all
1220
+ args = { create: {}, update: {}, delete: {} };
1221
+ }
1222
+ else {
1223
+ if (typeof args !== 'object') {
1224
+ throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'argument must be an object');
1225
+ }
1226
+ args = this.policyUtils.safeClone(args);
1227
+ }
1228
+ // inject read guard as subscription filter
1229
+ for (const key of ['create', 'update', 'delete']) {
1230
+ if (args[key] === undefined) {
1231
+ continue;
1232
+ }
1233
+ // "update" has an extra layer of "after"
1234
+ const payload = key === 'update' ? args[key].after : args[key];
1235
+ const toInject = { where: payload };
1236
+ this.policyUtils.injectForRead(this.prisma, this.model, toInject);
1237
+ if (key === 'update') {
1238
+ // "update" has an extra layer of "after"
1239
+ args[key].after = toInject.where;
1240
+ }
1241
+ else {
1242
+ args[key] = toInject.where;
1243
+ }
1244
+ }
1245
+ if (this.shouldLogQuery) {
1246
+ this.logger.info(`[policy] \`${action}\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
1247
+ }
1248
+ // Prisma Pulse returns an async iterable, which we need to wrap
1249
+ // and post-process the iteration results
1250
+ const iterable = yield this.modelClient[action](args);
1251
+ return {
1252
+ [Symbol.asyncIterator]: () => {
1253
+ const iter = iterable[Symbol.asyncIterator].bind(iterable)();
1254
+ return {
1255
+ next: () => __awaiter(this, void 0, void 0, function* () {
1256
+ const { done, value } = yield iter.next();
1257
+ let processedValue = value;
1258
+ if (value && 'action' in value) {
1259
+ switch (value.action) {
1260
+ case 'create':
1261
+ if ('created' in value) {
1262
+ processedValue = Object.assign(Object.assign({}, value), { created: this.policyUtils.postProcessForRead(value.created, this.model, {}) });
1263
+ }
1264
+ break;
1265
+ case 'update':
1266
+ if ('before' in value) {
1267
+ processedValue = Object.assign(Object.assign({}, value), { before: this.policyUtils.postProcessForRead(value.before, this.model, {}) });
1268
+ }
1269
+ if ('after' in value) {
1270
+ processedValue = Object.assign(Object.assign({}, value), { after: this.policyUtils.postProcessForRead(value.after, this.model, {}) });
1271
+ }
1272
+ break;
1273
+ case 'delete':
1274
+ if ('deleted' in value) {
1275
+ processedValue = Object.assign(Object.assign({}, value), { deleted: this.policyUtils.postProcessForRead(value.deleted, this.model, {}) });
1276
+ }
1277
+ break;
1278
+ }
1279
+ }
1280
+ return { done, value: processedValue };
1281
+ }),
1282
+ return: () => { var _a; return (_a = iter.return) === null || _a === void 0 ? void 0 : _a.call(iter); },
1283
+ throw: () => { var _a; return (_a = iter.throw) === null || _a === void 0 ? void 0 : _a.call(iter); },
1284
+ };
1285
+ },
1286
+ };
1287
+ });
1288
+ }
1289
+ //#endregion
1290
+ //#region Check
1291
+ /**
1292
+ * Checks if the given operation is possibly allowed by the policy, without querying the database.
1293
+ * @param operation The CRUD operation.
1294
+ * @param fieldValues Extra field value filters to be combined with the policy constraints.
1295
+ */
1296
+ check(args) {
1297
+ return __awaiter(this, void 0, void 0, function* () {
1298
+ return (0, check_utils_1.checkPermission)(this.model, args, this.modelMeta, this.policyUtils, this.prisma, this.prismaModule);
1299
+ });
1300
+ }
1301
+ //#endregion
1302
+ //#region Utils
1303
+ get shouldLogQuery() {
1304
+ var _a;
1305
+ return !!((_a = this.options) === null || _a === void 0 ? void 0 : _a.logPrismaQuery) && this.logger.enabled('info');
1306
+ }
1307
+ runPostWriteChecks(postWriteChecks, db) {
1308
+ return __awaiter(this, void 0, void 0, function* () {
1309
+ yield Promise.all(postWriteChecks.map((_a) => __awaiter(this, [_a], void 0, function* ({ model, operation, uniqueFilter, preValue }) { return this.policyUtils.checkPolicyForUnique(model, uniqueFilter, operation, db, undefined, preValue); })));
1310
+ });
1311
+ }
1312
+ requireBackLink(fieldInfo) {
1313
+ (0, tiny_invariant_1.default)(fieldInfo.backLink, `back link not found for field ${fieldInfo.name}`);
1314
+ return (0, cross_1.requireField)(this.modelMeta, fieldInfo.type, fieldInfo.backLink);
1315
+ }
1316
+ mergeToParent(parent, key, value) {
1317
+ if (parent[key]) {
1318
+ if (Array.isArray(parent[key])) {
1319
+ parent[key].push(value);
1320
+ }
1321
+ else {
1322
+ parent[key] = [parent[key], value];
1323
+ }
1324
+ }
1325
+ else {
1326
+ parent[key] = value;
1327
+ }
1328
+ }
1329
+ removeFromParent(parent, key, data) {
1330
+ if (parent[key] === data) {
1331
+ delete parent[key];
1332
+ }
1333
+ else if (Array.isArray(parent[key])) {
1334
+ const idx = parent[key].indexOf(data);
1335
+ if (idx >= 0) {
1336
+ parent[key].splice(idx, 1);
1337
+ if (parent[key].length === 0) {
1338
+ delete parent[key];
1339
+ }
1340
+ }
1341
+ }
1342
+ }
1343
+ buildIdFilterWithEntityChecker(candidates, entityChecker) {
1344
+ const filteredCandidates = candidates.filter((value) => { var _a; return entityChecker(value, { user: (_a = this.context) === null || _a === void 0 ? void 0 : _a.user }); });
1345
+ const idFields = this.policyUtils.getIdFields(this.model);
1346
+ let idFilter;
1347
+ if (idFields.length === 1) {
1348
+ idFilter = { [idFields[0].name]: { in: filteredCandidates.map((x) => x[idFields[0].name]) } };
1349
+ }
1350
+ else {
1351
+ idFilter = { AND: filteredCandidates.map((x) => this.policyUtils.getIdFieldValues(this.model, x)) };
1352
+ }
1353
+ return { filteredCandidates, idFilter };
1354
+ }
1355
+ }
1356
+ exports.PolicyProxyHandler = PolicyProxyHandler;
1357
+ //# sourceMappingURL=handler.js.map