@zenstackhq/runtime 1.2.1 → 1.3.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/cross/index.d.mts CHANGED
@@ -1,3 +1,19 @@
1
+ /**
2
+ * Callback for @see ModelDataVisitor.
3
+ */
4
+ type ModelDataVisitorCallback = (model: string, data: any, scalarData: any) => void;
5
+ /**
6
+ * Visitor that traverses data returned by a Prisma query.
7
+ */
8
+ declare class ModelDataVisitor {
9
+ private modelMeta;
10
+ constructor(modelMeta: ModelMeta);
11
+ /**
12
+ * Visits the given model data.
13
+ */
14
+ visit(model: string, data: any, callback: ModelDataVisitorCallback): void;
15
+ }
16
+
1
17
  /**
2
18
  * Runtime information of a data model or field attribute
3
19
  */
@@ -23,23 +39,23 @@ type FieldInfo = {
23
39
  /**
24
40
  * If the field is an ID field or part of a multi-field ID
25
41
  */
26
- isId: boolean;
42
+ isId?: boolean;
27
43
  /**
28
44
  * If the field type is a data model (or an optional/array of data model)
29
45
  */
30
- isDataModel: boolean;
46
+ isDataModel?: boolean;
31
47
  /**
32
48
  * If the field is an array
33
49
  */
34
- isArray: boolean;
50
+ isArray?: boolean;
35
51
  /**
36
52
  * If the field is optional
37
53
  */
38
- isOptional: boolean;
54
+ isOptional?: boolean;
39
55
  /**
40
56
  * Attributes on the field
41
57
  */
42
- attributes: RuntimeAttribute[];
58
+ attributes?: RuntimeAttribute[];
43
59
  /**
44
60
  * If the field is a relation field, the field name of the reverse side of the relation
45
61
  */
@@ -47,11 +63,11 @@ type FieldInfo = {
47
63
  /**
48
64
  * If the field is the owner side of a relation
49
65
  */
50
- isRelationOwner: boolean;
66
+ isRelationOwner?: boolean;
51
67
  /**
52
68
  * If the field is a foreign key field
53
69
  */
54
- isForeignKey: boolean;
70
+ isForeignKey?: boolean;
55
71
  /**
56
72
  * Mapping from foreign key field names to relation field names
57
73
  */
@@ -95,6 +111,21 @@ declare function resolveField(modelMeta: ModelMeta, model: string, field: string
95
111
  */
96
112
  declare function getFields(modelMeta: ModelMeta, model: string): Record<string, FieldInfo>;
97
113
 
114
+ /**
115
+ * Tries to apply a mutation to a query result.
116
+ *
117
+ * @param queryModel the model of the query
118
+ * @param queryOp the operation of the query
119
+ * @param queryData the result data of the query
120
+ * @param mutationModel the model of the mutation
121
+ * @param mutationOp the operation of the mutation
122
+ * @param mutationArgs the arguments of the mutation
123
+ * @param modelMeta the model metadata
124
+ * @param logging whether to log the mutation application
125
+ * @returns the updated query data if the mutation is applicable, otherwise undefined
126
+ */
127
+ declare function applyMutation(queryModel: string, queryOp: string, queryData: any, mutationModel: string, mutationOp: PrismaWriteActionType, mutationArgs: any, modelMeta: ModelMeta, logging: boolean): Promise<any>;
128
+
98
129
  type NestedReadVisitorCallback = {
99
130
  field?: (model: string, field: FieldInfo | undefined, kind: 'include' | 'select' | undefined, args: unknown) => void | boolean;
100
131
  };
@@ -225,5 +256,6 @@ declare function enumerate<T>(x: Enumerable<T>): T[];
225
256
  * Zip two arrays or scalars.
226
257
  */
227
258
  declare function zip<T1, T2>(x: Enumerable<T1>, y: Enumerable<T2>): Array<[T1, T2]>;
259
+ declare function getIdFields(modelMeta: ModelMeta, model: string, throwIfNotFound?: boolean): FieldInfo[];
228
260
 
229
- export { Enumerable, FieldInfo, MaybePromise, ModelMeta, NestedReadVisitor, NestedReadVisitorCallback, NestedWriteVisitor, NestedWriteVisitorContext, NestedWriterVisitorCallback, PrismaWriteActionType, PrismaWriteActions, RuntimeAttribute, UniqueConstraint, enumerate, getFields, getModelFields, getMutatedModels, getReadModels, resolveField, zip };
261
+ export { Enumerable, FieldInfo, MaybePromise, ModelDataVisitor, ModelDataVisitorCallback, ModelMeta, NestedReadVisitor, NestedReadVisitorCallback, NestedWriteVisitor, NestedWriteVisitorContext, NestedWriterVisitorCallback, PrismaWriteActionType, PrismaWriteActions, RuntimeAttribute, UniqueConstraint, applyMutation, enumerate, getFields, getIdFields, getModelFields, getMutatedModels, getReadModels, resolveField, zip };
package/cross/index.d.ts CHANGED
@@ -1,3 +1,19 @@
1
+ /**
2
+ * Callback for @see ModelDataVisitor.
3
+ */
4
+ type ModelDataVisitorCallback = (model: string, data: any, scalarData: any) => void;
5
+ /**
6
+ * Visitor that traverses data returned by a Prisma query.
7
+ */
8
+ declare class ModelDataVisitor {
9
+ private modelMeta;
10
+ constructor(modelMeta: ModelMeta);
11
+ /**
12
+ * Visits the given model data.
13
+ */
14
+ visit(model: string, data: any, callback: ModelDataVisitorCallback): void;
15
+ }
16
+
1
17
  /**
2
18
  * Runtime information of a data model or field attribute
3
19
  */
@@ -23,23 +39,23 @@ type FieldInfo = {
23
39
  /**
24
40
  * If the field is an ID field or part of a multi-field ID
25
41
  */
26
- isId: boolean;
42
+ isId?: boolean;
27
43
  /**
28
44
  * If the field type is a data model (or an optional/array of data model)
29
45
  */
30
- isDataModel: boolean;
46
+ isDataModel?: boolean;
31
47
  /**
32
48
  * If the field is an array
33
49
  */
34
- isArray: boolean;
50
+ isArray?: boolean;
35
51
  /**
36
52
  * If the field is optional
37
53
  */
38
- isOptional: boolean;
54
+ isOptional?: boolean;
39
55
  /**
40
56
  * Attributes on the field
41
57
  */
42
- attributes: RuntimeAttribute[];
58
+ attributes?: RuntimeAttribute[];
43
59
  /**
44
60
  * If the field is a relation field, the field name of the reverse side of the relation
45
61
  */
@@ -47,11 +63,11 @@ type FieldInfo = {
47
63
  /**
48
64
  * If the field is the owner side of a relation
49
65
  */
50
- isRelationOwner: boolean;
66
+ isRelationOwner?: boolean;
51
67
  /**
52
68
  * If the field is a foreign key field
53
69
  */
54
- isForeignKey: boolean;
70
+ isForeignKey?: boolean;
55
71
  /**
56
72
  * Mapping from foreign key field names to relation field names
57
73
  */
@@ -95,6 +111,21 @@ declare function resolveField(modelMeta: ModelMeta, model: string, field: string
95
111
  */
96
112
  declare function getFields(modelMeta: ModelMeta, model: string): Record<string, FieldInfo>;
97
113
 
114
+ /**
115
+ * Tries to apply a mutation to a query result.
116
+ *
117
+ * @param queryModel the model of the query
118
+ * @param queryOp the operation of the query
119
+ * @param queryData the result data of the query
120
+ * @param mutationModel the model of the mutation
121
+ * @param mutationOp the operation of the mutation
122
+ * @param mutationArgs the arguments of the mutation
123
+ * @param modelMeta the model metadata
124
+ * @param logging whether to log the mutation application
125
+ * @returns the updated query data if the mutation is applicable, otherwise undefined
126
+ */
127
+ declare function applyMutation(queryModel: string, queryOp: string, queryData: any, mutationModel: string, mutationOp: PrismaWriteActionType, mutationArgs: any, modelMeta: ModelMeta, logging: boolean): Promise<any>;
128
+
98
129
  type NestedReadVisitorCallback = {
99
130
  field?: (model: string, field: FieldInfo | undefined, kind: 'include' | 'select' | undefined, args: unknown) => void | boolean;
100
131
  };
@@ -225,5 +256,6 @@ declare function enumerate<T>(x: Enumerable<T>): T[];
225
256
  * Zip two arrays or scalars.
226
257
  */
227
258
  declare function zip<T1, T2>(x: Enumerable<T1>, y: Enumerable<T2>): Array<[T1, T2]>;
259
+ declare function getIdFields(modelMeta: ModelMeta, model: string, throwIfNotFound?: boolean): FieldInfo[];
228
260
 
229
- export { Enumerable, FieldInfo, MaybePromise, ModelMeta, NestedReadVisitor, NestedReadVisitorCallback, NestedWriteVisitor, NestedWriteVisitorContext, NestedWriterVisitorCallback, PrismaWriteActionType, PrismaWriteActions, RuntimeAttribute, UniqueConstraint, enumerate, getFields, getModelFields, getMutatedModels, getReadModels, resolveField, zip };
261
+ export { Enumerable, FieldInfo, MaybePromise, ModelDataVisitor, ModelDataVisitorCallback, ModelMeta, NestedReadVisitor, NestedReadVisitorCallback, NestedWriteVisitor, NestedWriteVisitorContext, NestedWriterVisitorCallback, PrismaWriteActionType, PrismaWriteActions, RuntimeAttribute, UniqueConstraint, applyMutation, enumerate, getFields, getIdFields, getModelFields, getMutatedModels, getReadModels, resolveField, zip };
package/cross/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __defProps = Object.defineProperties;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
7
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
8
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
+ var __getProtoOf = Object.getPrototypeOf;
8
10
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
11
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
12
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -32,6 +34,14 @@ var __copyProps = (to, from, except, desc) => {
32
34
  }
33
35
  return to;
34
36
  };
37
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
38
+ // If the importer is in node compatibility mode or this is not an ESM
39
+ // file that has been converted to a CommonJS file using a Babel-
40
+ // compatible transform (i.e. "__esModule" has not been set), then set
41
+ // "default" to the CommonJS "module.exports" for node compatibility.
42
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
43
+ mod
44
+ ));
35
45
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
46
  var __async = (__this, __arguments, generator) => {
37
47
  return new Promise((resolve, reject) => {
@@ -57,11 +67,14 @@ var __async = (__this, __arguments, generator) => {
57
67
  // src/cross/index.ts
58
68
  var cross_exports = {};
59
69
  __export(cross_exports, {
70
+ ModelDataVisitor: () => ModelDataVisitor,
60
71
  NestedReadVisitor: () => NestedReadVisitor,
61
72
  NestedWriteVisitor: () => NestedWriteVisitor,
62
73
  PrismaWriteActions: () => PrismaWriteActions,
74
+ applyMutation: () => applyMutation,
63
75
  enumerate: () => enumerate,
64
76
  getFields: () => getFields,
77
+ getIdFields: () => getIdFields,
65
78
  getModelFields: () => getModelFields,
66
79
  getMutatedModels: () => getMutatedModels,
67
80
  getReadModels: () => getReadModels,
@@ -70,6 +83,37 @@ __export(cross_exports, {
70
83
  });
71
84
  module.exports = __toCommonJS(cross_exports);
72
85
 
86
+ // src/cross/model-data-visitor.ts
87
+ var ModelDataVisitor = class {
88
+ constructor(modelMeta) {
89
+ this.modelMeta = modelMeta;
90
+ }
91
+ /**
92
+ * Visits the given model data.
93
+ */
94
+ visit(model, data, callback) {
95
+ if (!data || typeof data !== "object") {
96
+ return;
97
+ }
98
+ const scalarData = {};
99
+ const subTasks = [];
100
+ for (const [k, v] of Object.entries(data)) {
101
+ const field = resolveField(this.modelMeta, model, k);
102
+ if (field && field.isDataModel) {
103
+ if (field.isArray && Array.isArray(v)) {
104
+ subTasks.push(...v.map((item) => ({ model: field.type, data: item })));
105
+ } else {
106
+ subTasks.push({ model: field.type, data: v });
107
+ }
108
+ } else {
109
+ scalarData[k] = v;
110
+ }
111
+ }
112
+ callback(model, data, scalarData);
113
+ subTasks.forEach(({ model: model2, data: data2 }) => this.visit(model2, data2, callback));
114
+ }
115
+ };
116
+
73
117
  // src/cross/model-meta.ts
74
118
  var import_lower_case_first = require("lower-case-first");
75
119
  function resolveField(modelMeta, model, field) {
@@ -80,6 +124,177 @@ function getFields(modelMeta, model) {
80
124
  return modelMeta.fields[(0, import_lower_case_first.lowerCaseFirst)(model)];
81
125
  }
82
126
 
127
+ // src/cross/mutator.ts
128
+ var import_uuid = require("uuid");
129
+ var import_deepcopy = __toESM(require("deepcopy"));
130
+ function applyMutation(queryModel, queryOp, queryData, mutationModel, mutationOp, mutationArgs, modelMeta, logging) {
131
+ return __async(this, null, function* () {
132
+ if (["count", "aggregate", "groupBy"].includes(queryOp)) {
133
+ return void 0;
134
+ }
135
+ let resultData = queryData;
136
+ let updated = false;
137
+ const visitor = new NestedWriteVisitor(modelMeta, {
138
+ create: (model, args) => {
139
+ if (model === queryModel) {
140
+ const r = createMutate(queryModel, queryOp, resultData, args, modelMeta, logging);
141
+ if (r) {
142
+ resultData = r;
143
+ updated = true;
144
+ }
145
+ }
146
+ },
147
+ createMany: (model, args) => {
148
+ if (model === queryModel && (args == null ? void 0 : args.data)) {
149
+ for (const oneArg of enumerate(args.data)) {
150
+ const r = createMutate(queryModel, queryOp, resultData, oneArg, modelMeta, logging);
151
+ if (r) {
152
+ resultData = r;
153
+ updated = true;
154
+ }
155
+ }
156
+ }
157
+ },
158
+ update: (model, args) => {
159
+ const r = updateMutate(queryModel, resultData, model, args, modelMeta, logging);
160
+ if (r) {
161
+ resultData = r;
162
+ updated = true;
163
+ }
164
+ },
165
+ delete: (model, args) => {
166
+ const r = deleteMutate(queryModel, resultData, model, args, modelMeta, logging);
167
+ if (r) {
168
+ resultData = r;
169
+ updated = true;
170
+ }
171
+ }
172
+ });
173
+ yield visitor.visit(mutationModel, mutationOp, mutationArgs);
174
+ return updated ? resultData : void 0;
175
+ });
176
+ }
177
+ function createMutate(queryModel, queryOp, currentData, newData, modelMeta, logging) {
178
+ if (!newData) {
179
+ return void 0;
180
+ }
181
+ if (queryOp !== "findMany") {
182
+ return void 0;
183
+ }
184
+ const modelFields = getFields(modelMeta, queryModel);
185
+ if (!modelFields) {
186
+ return void 0;
187
+ }
188
+ const insert = {};
189
+ const newDataFields = Object.keys(newData);
190
+ Object.entries(modelFields).forEach(([name, field]) => {
191
+ var _a, _b, _c, _d;
192
+ if (field.isDataModel) {
193
+ return;
194
+ }
195
+ if (newDataFields.includes(name)) {
196
+ insert[name] = newData[name];
197
+ } else {
198
+ const defaultAttr = (_a = field.attributes) == null ? void 0 : _a.find((attr) => attr.name === "@default");
199
+ if (field.type === "DateTime") {
200
+ if (defaultAttr || ((_b = field.attributes) == null ? void 0 : _b.some((attr) => attr.name === "@updatedAt"))) {
201
+ insert[name] = /* @__PURE__ */ new Date();
202
+ }
203
+ } else if (((_d = (_c = defaultAttr == null ? void 0 : defaultAttr.args) == null ? void 0 : _c[0]) == null ? void 0 : _d.value) !== void 0) {
204
+ insert[name] = defaultAttr.args[0].value;
205
+ }
206
+ }
207
+ });
208
+ const idFields = getIdFields(modelMeta, queryModel, false);
209
+ idFields.forEach((f) => {
210
+ if (insert[f.name] === void 0) {
211
+ if (f.type === "Int" || f.type === "BigInt") {
212
+ const currMax = Math.max(
213
+ ...[...currentData].map((item) => {
214
+ const idv = parseInt(item[f.name]);
215
+ return isNaN(idv) ? 0 : idv;
216
+ })
217
+ );
218
+ insert[f.name] = currMax + 1;
219
+ } else {
220
+ insert[f.name] = (0, import_uuid.v4)();
221
+ }
222
+ }
223
+ });
224
+ insert.$optimistic = true;
225
+ if (logging) {
226
+ console.log(`Optimistic create for ${queryModel}:`, insert);
227
+ }
228
+ return [insert, ...currentData];
229
+ }
230
+ function updateMutate(queryModel, currentData, mutateModel, mutateArgs, modelMeta, logging) {
231
+ if (!currentData) {
232
+ return void 0;
233
+ }
234
+ if (!(mutateArgs == null ? void 0 : mutateArgs.where) || !(mutateArgs == null ? void 0 : mutateArgs.data)) {
235
+ return void 0;
236
+ }
237
+ let updated = false;
238
+ for (const item of enumerate(currentData)) {
239
+ const visitor = new ModelDataVisitor(modelMeta);
240
+ visitor.visit(queryModel, item, (model, _data, scalarData) => {
241
+ if (model === mutateModel && idFieldsMatch(model, scalarData, mutateArgs.where, modelMeta)) {
242
+ Object.keys(item).forEach((k) => {
243
+ if (mutateArgs.data[k] !== void 0) {
244
+ item[k] = mutateArgs.data[k];
245
+ }
246
+ });
247
+ item.$optimistic = true;
248
+ updated = true;
249
+ if (logging) {
250
+ console.log(`Optimistic update for ${queryModel}:`, item);
251
+ }
252
+ }
253
+ });
254
+ }
255
+ return updated ? (0, import_deepcopy.default)(currentData) : void 0;
256
+ }
257
+ function deleteMutate(queryModel, currentData, mutateModel, mutateArgs, modelMeta, logging) {
258
+ if (!currentData || !mutateArgs) {
259
+ return void 0;
260
+ }
261
+ if (queryModel !== mutateModel) {
262
+ return void 0;
263
+ }
264
+ let updated = false;
265
+ let result = currentData;
266
+ if (Array.isArray(currentData)) {
267
+ for (const item of currentData) {
268
+ if (idFieldsMatch(mutateModel, item, mutateArgs, modelMeta)) {
269
+ result = result.filter((x) => x !== item);
270
+ updated = true;
271
+ if (logging) {
272
+ console.log(`Optimistic delete for ${queryModel}:`, item);
273
+ }
274
+ }
275
+ }
276
+ } else {
277
+ if (idFieldsMatch(mutateModel, currentData, mutateArgs, modelMeta)) {
278
+ result = null;
279
+ updated = true;
280
+ if (logging) {
281
+ console.log(`Optimistic delete for ${queryModel}:`, currentData);
282
+ }
283
+ }
284
+ }
285
+ return updated ? result : void 0;
286
+ }
287
+ function idFieldsMatch(model, x, y, modelMeta) {
288
+ if (!x || !y || typeof x !== "object" || typeof y !== "object") {
289
+ return false;
290
+ }
291
+ const idFields = getIdFields(modelMeta, model, false);
292
+ if (idFields.length === 0) {
293
+ return false;
294
+ }
295
+ return idFields.every((f) => x[f.name] === y[f.name]);
296
+ }
297
+
83
298
  // src/cross/nested-read-visitor.ts
84
299
  var NestedReadVisitor = class {
85
300
  constructor(modelMeta, callback) {
@@ -139,6 +354,7 @@ var PrismaWriteActions = [
139
354
  ];
140
355
 
141
356
  // src/cross/utils.ts
357
+ var import_lower_case_first2 = require("lower-case-first");
142
358
  function getModelFields(data) {
143
359
  return data ? Object.keys(data) : [];
144
360
  }
@@ -167,6 +383,21 @@ function zip(x, y) {
167
383
  return [[x, y]];
168
384
  }
169
385
  }
386
+ function getIdFields(modelMeta, model, throwIfNotFound = false) {
387
+ let fields = modelMeta.fields[(0, import_lower_case_first2.lowerCaseFirst)(model)];
388
+ if (!fields) {
389
+ if (throwIfNotFound) {
390
+ throw new Error(`Unable to load fields for ${model}`);
391
+ } else {
392
+ fields = {};
393
+ }
394
+ }
395
+ const result = Object.values(fields).filter((f) => f.isId);
396
+ if (result.length === 0 && throwIfNotFound) {
397
+ throw new Error(`model ${model} does not have an id field`);
398
+ }
399
+ return result;
400
+ }
170
401
 
171
402
  // src/cross/nested-write-visitor.ts
172
403
  var NestedWriteVisitor = class {
@@ -385,7 +616,7 @@ var NestedWriteVisitor = class {
385
616
  };
386
617
 
387
618
  // src/cross/query-analyzer.ts
388
- var import_lower_case_first2 = require("lower-case-first");
619
+ var import_lower_case_first3 = require("lower-case-first");
389
620
  function getReadModels(model, modelMeta, args) {
390
621
  const result = /* @__PURE__ */ new Set();
391
622
  result.add(model);
@@ -439,7 +670,7 @@ function collectDeleteCascades(model, modelMeta, result, visited) {
439
670
  return;
440
671
  }
441
672
  visited.add(model);
442
- const cascades = modelMeta.deleteCascade[(0, import_lower_case_first2.lowerCaseFirst)(model)];
673
+ const cascades = modelMeta.deleteCascade[(0, import_lower_case_first3.lowerCaseFirst)(model)];
443
674
  if (!cascades) {
444
675
  return;
445
676
  }
@@ -450,11 +681,14 @@ function collectDeleteCascades(model, modelMeta, result, visited) {
450
681
  }
451
682
  // Annotate the CommonJS export names for ESM import in node:
452
683
  0 && (module.exports = {
684
+ ModelDataVisitor,
453
685
  NestedReadVisitor,
454
686
  NestedWriteVisitor,
455
687
  PrismaWriteActions,
688
+ applyMutation,
456
689
  enumerate,
457
690
  getFields,
691
+ getIdFields,
458
692
  getModelFields,
459
693
  getMutatedModels,
460
694
  getReadModels,