@zenstackhq/client-helpers 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,822 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/constants.ts
5
+ var DEFAULT_QUERY_ENDPOINT = "/api/model";
6
+
7
+ // src/logging.ts
8
+ function log(logger, message) {
9
+ if (typeof logger === "function") {
10
+ logger(message);
11
+ } else if (logger) {
12
+ console.log(message);
13
+ }
14
+ }
15
+ __name(log, "log");
16
+
17
+ // src/nested-read-visitor.ts
18
+ var NestedReadVisitor = class {
19
+ static {
20
+ __name(this, "NestedReadVisitor");
21
+ }
22
+ schema;
23
+ callback;
24
+ constructor(schema, callback) {
25
+ this.schema = schema;
26
+ this.callback = callback;
27
+ }
28
+ doVisit(model, field, kind, args) {
29
+ if (this.callback.field) {
30
+ const r = this.callback.field(model, field, kind, args);
31
+ if (r === false) {
32
+ return;
33
+ }
34
+ }
35
+ if (!args || typeof args !== "object") {
36
+ return;
37
+ }
38
+ let selectInclude;
39
+ let nextKind;
40
+ if (args.select) {
41
+ selectInclude = args.select;
42
+ nextKind = "select";
43
+ } else if (args.include) {
44
+ selectInclude = args.include;
45
+ nextKind = "include";
46
+ }
47
+ if (selectInclude && typeof selectInclude === "object") {
48
+ for (const [k, v] of Object.entries(selectInclude)) {
49
+ if (k === "_count" && typeof v === "object" && v) {
50
+ this.doVisit(model, field, kind, v);
51
+ } else {
52
+ const field2 = this.schema.models[model]?.fields[k];
53
+ if (field2) {
54
+ this.doVisit(field2.type, field2, nextKind, v);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+ visit(model, args) {
61
+ this.doVisit(model, void 0, void 0, args);
62
+ }
63
+ };
64
+
65
+ // src/nested-write-visitor.ts
66
+ import { enumerate } from "@zenstackhq/common-helpers";
67
+
68
+ // src/types.ts
69
+ var ORMWriteActions = [
70
+ "create",
71
+ "createMany",
72
+ "createManyAndReturn",
73
+ "connectOrCreate",
74
+ "update",
75
+ "updateMany",
76
+ "updateManyAndReturn",
77
+ "upsert",
78
+ "connect",
79
+ "disconnect",
80
+ "set",
81
+ "delete",
82
+ "deleteMany"
83
+ ];
84
+
85
+ // src/nested-write-visitor.ts
86
+ var NestedWriteVisitor = class {
87
+ static {
88
+ __name(this, "NestedWriteVisitor");
89
+ }
90
+ schema;
91
+ callback;
92
+ constructor(schema, callback) {
93
+ this.schema = schema;
94
+ this.callback = callback;
95
+ }
96
+ isWriteAction(value) {
97
+ return ORMWriteActions.includes(value);
98
+ }
99
+ /**
100
+ * Start visiting
101
+ *
102
+ * @see NestedWriteVisitorCallback
103
+ */
104
+ async visit(model, action, args) {
105
+ if (!args) {
106
+ return;
107
+ }
108
+ let topData = args;
109
+ switch (action) {
110
+ // create has its data wrapped in 'data' field
111
+ case "create":
112
+ topData = topData.data;
113
+ break;
114
+ case "delete":
115
+ case "deleteMany":
116
+ topData = topData.where;
117
+ break;
118
+ }
119
+ await this.doVisit(model, action, topData, void 0, void 0, []);
120
+ }
121
+ async doVisit(model, action, data, parent, field, nestingPath) {
122
+ if (!data) {
123
+ return;
124
+ }
125
+ const toplevel = field == void 0;
126
+ const context = {
127
+ parent,
128
+ field,
129
+ nestingPath: [
130
+ ...nestingPath
131
+ ]
132
+ };
133
+ const pushNewContext = /* @__PURE__ */ __name((field2, model2, where, unique = false) => {
134
+ return {
135
+ ...context,
136
+ nestingPath: [
137
+ ...context.nestingPath,
138
+ {
139
+ field: field2,
140
+ model: model2,
141
+ where,
142
+ unique
143
+ }
144
+ ]
145
+ };
146
+ }, "pushNewContext");
147
+ switch (action) {
148
+ case "create":
149
+ for (const item of this.enumerateReverse(data)) {
150
+ const newContext = pushNewContext(field, model, {});
151
+ let callbackResult;
152
+ if (this.callback.create) {
153
+ callbackResult = await this.callback.create(model, item, newContext);
154
+ }
155
+ if (callbackResult !== false) {
156
+ const subPayload = typeof callbackResult === "object" ? callbackResult : item;
157
+ await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
158
+ }
159
+ }
160
+ break;
161
+ case "createMany":
162
+ case "createManyAndReturn":
163
+ {
164
+ const newContext = pushNewContext(field, model, {});
165
+ let callbackResult;
166
+ if (this.callback.createMany) {
167
+ callbackResult = await this.callback.createMany(model, data, newContext);
168
+ }
169
+ if (callbackResult !== false) {
170
+ const subPayload = typeof callbackResult === "object" ? callbackResult : data.data;
171
+ await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
172
+ }
173
+ }
174
+ break;
175
+ case "connectOrCreate":
176
+ for (const item of this.enumerateReverse(data)) {
177
+ const newContext = pushNewContext(field, model, item.where);
178
+ let callbackResult;
179
+ if (this.callback.connectOrCreate) {
180
+ callbackResult = await this.callback.connectOrCreate(model, item, newContext);
181
+ }
182
+ if (callbackResult !== false) {
183
+ const subPayload = typeof callbackResult === "object" ? callbackResult : item.create;
184
+ await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
185
+ }
186
+ }
187
+ break;
188
+ case "connect":
189
+ if (this.callback.connect) {
190
+ for (const item of this.enumerateReverse(data)) {
191
+ const newContext = pushNewContext(field, model, item, true);
192
+ await this.callback.connect(model, item, newContext);
193
+ }
194
+ }
195
+ break;
196
+ case "disconnect":
197
+ if (this.callback.disconnect) {
198
+ for (const item of this.enumerateReverse(data)) {
199
+ const newContext = pushNewContext(field, model, item, typeof item === "object");
200
+ await this.callback.disconnect(model, item, newContext);
201
+ }
202
+ }
203
+ break;
204
+ case "set":
205
+ if (this.callback.set) {
206
+ for (const item of this.enumerateReverse(data)) {
207
+ const newContext = pushNewContext(field, model, item, true);
208
+ await this.callback.set(model, item, newContext);
209
+ }
210
+ }
211
+ break;
212
+ case "update":
213
+ for (const item of this.enumerateReverse(data)) {
214
+ const newContext = pushNewContext(field, model, item.where);
215
+ let callbackResult;
216
+ if (this.callback.update) {
217
+ callbackResult = await this.callback.update(model, item, newContext);
218
+ }
219
+ if (callbackResult !== false) {
220
+ const subPayload = typeof callbackResult === "object" ? callbackResult : typeof item.data === "object" ? item.data : item;
221
+ await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
222
+ }
223
+ }
224
+ break;
225
+ case "updateMany":
226
+ case "updateManyAndReturn":
227
+ for (const item of this.enumerateReverse(data)) {
228
+ const newContext = pushNewContext(field, model, item.where);
229
+ let callbackResult;
230
+ if (this.callback.updateMany) {
231
+ callbackResult = await this.callback.updateMany(model, item, newContext);
232
+ }
233
+ if (callbackResult !== false) {
234
+ const subPayload = typeof callbackResult === "object" ? callbackResult : item;
235
+ await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
236
+ }
237
+ }
238
+ break;
239
+ case "upsert": {
240
+ for (const item of this.enumerateReverse(data)) {
241
+ const newContext = pushNewContext(field, model, item.where);
242
+ let callbackResult;
243
+ if (this.callback.upsert) {
244
+ callbackResult = await this.callback.upsert(model, item, newContext);
245
+ }
246
+ if (callbackResult !== false) {
247
+ if (typeof callbackResult === "object") {
248
+ await this.visitSubPayload(model, action, callbackResult, newContext.nestingPath);
249
+ } else {
250
+ await this.visitSubPayload(model, action, item.create, newContext.nestingPath);
251
+ await this.visitSubPayload(model, action, item.update, newContext.nestingPath);
252
+ }
253
+ }
254
+ }
255
+ break;
256
+ }
257
+ case "delete": {
258
+ if (this.callback.delete) {
259
+ for (const item of this.enumerateReverse(data)) {
260
+ const newContext = pushNewContext(field, model, toplevel ? item.where : item);
261
+ await this.callback.delete(model, item, newContext);
262
+ }
263
+ }
264
+ break;
265
+ }
266
+ case "deleteMany":
267
+ if (this.callback.deleteMany) {
268
+ for (const item of this.enumerateReverse(data)) {
269
+ const newContext = pushNewContext(field, model, toplevel ? item.where : item);
270
+ await this.callback.deleteMany(model, item, newContext);
271
+ }
272
+ }
273
+ break;
274
+ default: {
275
+ throw new Error(`unhandled action type ${action}`);
276
+ }
277
+ }
278
+ }
279
+ async visitSubPayload(model, action, payload, nestingPath) {
280
+ for (const item of enumerate(payload)) {
281
+ if (!item || typeof item !== "object") {
282
+ continue;
283
+ }
284
+ for (const field of Object.keys(item)) {
285
+ const fieldDef = this.schema.models[model]?.fields[field];
286
+ if (!fieldDef) {
287
+ continue;
288
+ }
289
+ if (fieldDef.relation) {
290
+ if (item[field]) {
291
+ for (const [subAction, subData] of Object.entries(item[field])) {
292
+ if (this.isWriteAction(subAction) && subData) {
293
+ await this.doVisit(fieldDef.type, subAction, subData, item[field], fieldDef, [
294
+ ...nestingPath
295
+ ]);
296
+ }
297
+ }
298
+ }
299
+ } else {
300
+ if (this.callback.field) {
301
+ await this.callback.field(fieldDef, action, item[field], {
302
+ parent: item,
303
+ nestingPath,
304
+ field: fieldDef
305
+ });
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ // enumerate a (possible) array in reverse order, so that the enumeration
312
+ // callback can safely delete the current item
313
+ *enumerateReverse(data) {
314
+ if (Array.isArray(data)) {
315
+ for (let i = data.length - 1; i >= 0; i--) {
316
+ yield data[i];
317
+ }
318
+ } else {
319
+ yield data;
320
+ }
321
+ }
322
+ };
323
+
324
+ // src/query-analysis.ts
325
+ function getReadModels(model, schema, args) {
326
+ const result = /* @__PURE__ */ new Set();
327
+ result.add(model);
328
+ const visitor = new NestedReadVisitor(schema, {
329
+ field: /* @__PURE__ */ __name((model2) => {
330
+ result.add(model2);
331
+ return true;
332
+ }, "field")
333
+ });
334
+ visitor.visit(model, args);
335
+ return [
336
+ ...result
337
+ ];
338
+ }
339
+ __name(getReadModels, "getReadModels");
340
+ async function getMutatedModels(model, operation, mutationArgs, schema) {
341
+ const result = /* @__PURE__ */ new Set();
342
+ result.add(model);
343
+ if (mutationArgs) {
344
+ const addModel = /* @__PURE__ */ __name((model2) => void result.add(model2), "addModel");
345
+ const addCascades = /* @__PURE__ */ __name((model2) => {
346
+ const cascades = /* @__PURE__ */ new Set();
347
+ const visited = /* @__PURE__ */ new Set();
348
+ collectDeleteCascades(model2, schema, cascades, visited);
349
+ cascades.forEach((m) => addModel(m));
350
+ }, "addCascades");
351
+ const visitor = new NestedWriteVisitor(schema, {
352
+ create: addModel,
353
+ createMany: addModel,
354
+ connectOrCreate: addModel,
355
+ connect: addModel,
356
+ disconnect: addModel,
357
+ set: addModel,
358
+ update: addModel,
359
+ updateMany: addModel,
360
+ upsert: addModel,
361
+ delete: /* @__PURE__ */ __name((model2) => {
362
+ addModel(model2);
363
+ addCascades(model2);
364
+ }, "delete"),
365
+ deleteMany: /* @__PURE__ */ __name((model2) => {
366
+ addModel(model2);
367
+ addCascades(model2);
368
+ }, "deleteMany")
369
+ });
370
+ await visitor.visit(model, operation, mutationArgs);
371
+ }
372
+ result.forEach((m) => {
373
+ getBaseRecursively(m, schema, result);
374
+ });
375
+ return [
376
+ ...result
377
+ ];
378
+ }
379
+ __name(getMutatedModels, "getMutatedModels");
380
+ function collectDeleteCascades(model, schema, result, visited) {
381
+ if (visited.has(model)) {
382
+ return;
383
+ }
384
+ visited.add(model);
385
+ const modelDef = schema.models[model];
386
+ if (!modelDef) {
387
+ return;
388
+ }
389
+ for (const [modelName, modelDef2] of Object.entries(schema.models)) {
390
+ if (!modelDef2) {
391
+ continue;
392
+ }
393
+ for (const fieldDef of Object.values(modelDef2.fields)) {
394
+ if (fieldDef.relation?.onDelete === "Cascade" && fieldDef.type === model) {
395
+ if (!result.has(modelName)) {
396
+ result.add(modelName);
397
+ }
398
+ collectDeleteCascades(modelName, schema, result, visited);
399
+ }
400
+ }
401
+ }
402
+ }
403
+ __name(collectDeleteCascades, "collectDeleteCascades");
404
+ function getBaseRecursively(model, schema, result) {
405
+ const modelDef = schema.models[model];
406
+ if (!modelDef) {
407
+ return;
408
+ }
409
+ if (modelDef.baseModel) {
410
+ result.add(modelDef.baseModel);
411
+ getBaseRecursively(modelDef.baseModel, schema, result);
412
+ }
413
+ }
414
+ __name(getBaseRecursively, "getBaseRecursively");
415
+
416
+ // src/invalidation.ts
417
+ function createInvalidator(model, operation, schema, invalidator, logging) {
418
+ return async (...args) => {
419
+ const [_, variables] = args;
420
+ const predicate = await getInvalidationPredicate(model, operation, variables, schema, logging);
421
+ await invalidator(predicate);
422
+ };
423
+ }
424
+ __name(createInvalidator, "createInvalidator");
425
+ async function getInvalidationPredicate(model, operation, mutationArgs, schema, logging) {
426
+ const mutatedModels = await getMutatedModels(model, operation, mutationArgs, schema);
427
+ return ({ model: model2, args }) => {
428
+ if (mutatedModels.includes(model2)) {
429
+ if (logging) {
430
+ log(logging, `Marking "${model2}" query for invalidation due to mutation "${operation}", query args: ${JSON.stringify(args)}`);
431
+ }
432
+ return true;
433
+ }
434
+ if (args) {
435
+ if (findNestedRead(model2, mutatedModels, schema, args)) {
436
+ if (logging) {
437
+ log(logging, `Marking "${model2}" query for invalidation due to mutation "${operation}", query args: ${JSON.stringify(args)}`);
438
+ }
439
+ return true;
440
+ }
441
+ }
442
+ return false;
443
+ };
444
+ }
445
+ __name(getInvalidationPredicate, "getInvalidationPredicate");
446
+ function findNestedRead(visitingModel, targetModels, schema, args) {
447
+ const modelsRead = getReadModels(visitingModel, schema, args);
448
+ return targetModels.some((m) => modelsRead.includes(m));
449
+ }
450
+ __name(findNestedRead, "findNestedRead");
451
+
452
+ // src/mutator.ts
453
+ import { clone, enumerate as enumerate2, invariant, zip } from "@zenstackhq/common-helpers";
454
+ async function applyMutation(queryModel, queryOp, queryData, mutationModel, mutationOp, mutationArgs, schema, logging) {
455
+ if (!queryData || typeof queryData !== "object" && !Array.isArray(queryData)) {
456
+ return void 0;
457
+ }
458
+ if (!queryOp.startsWith("find")) {
459
+ return void 0;
460
+ }
461
+ return await doApplyMutation(queryModel, queryData, mutationModel, mutationOp, mutationArgs, schema, logging);
462
+ }
463
+ __name(applyMutation, "applyMutation");
464
+ async function doApplyMutation(queryModel, queryData, mutationModel, mutationOp, mutationArgs, schema, logging) {
465
+ let resultData = queryData;
466
+ let updated = false;
467
+ const visitor = new NestedWriteVisitor(schema, {
468
+ create: /* @__PURE__ */ __name((model, args) => {
469
+ if (model === queryModel && Array.isArray(resultData)) {
470
+ const r = createMutate(queryModel, resultData, args, schema, logging);
471
+ if (r) {
472
+ resultData = r;
473
+ updated = true;
474
+ }
475
+ }
476
+ }, "create"),
477
+ createMany: /* @__PURE__ */ __name((model, args) => {
478
+ if (model === queryModel && args?.data && Array.isArray(resultData)) {
479
+ for (const oneArg of enumerate2(args.data)) {
480
+ const r = createMutate(queryModel, resultData, oneArg, schema, logging);
481
+ if (r) {
482
+ resultData = r;
483
+ updated = true;
484
+ }
485
+ }
486
+ }
487
+ }, "createMany"),
488
+ update: /* @__PURE__ */ __name((model, args) => {
489
+ if (model === queryModel && !Array.isArray(resultData)) {
490
+ const r = updateMutate(queryModel, resultData, model, args, schema, logging);
491
+ if (r) {
492
+ resultData = r;
493
+ updated = true;
494
+ }
495
+ }
496
+ }, "update"),
497
+ upsert: /* @__PURE__ */ __name((model, args) => {
498
+ if (model === queryModel && args?.where && args?.create && args?.update) {
499
+ const r = upsertMutate(queryModel, resultData, model, args, schema, logging);
500
+ if (r) {
501
+ resultData = r;
502
+ updated = true;
503
+ }
504
+ }
505
+ }, "upsert"),
506
+ delete: /* @__PURE__ */ __name((model, args) => {
507
+ if (model === queryModel) {
508
+ const r = deleteMutate(queryModel, resultData, model, args, schema, logging);
509
+ if (r) {
510
+ resultData = r;
511
+ updated = true;
512
+ }
513
+ }
514
+ }, "delete")
515
+ });
516
+ await visitor.visit(mutationModel, mutationOp, mutationArgs);
517
+ const modelFields = schema.models[queryModel]?.fields;
518
+ invariant(modelFields, `Model ${queryModel} not found in schema`);
519
+ if (Array.isArray(resultData)) {
520
+ let arrayCloned = false;
521
+ for (let i = 0; i < resultData.length; i++) {
522
+ const item = resultData[i];
523
+ if (!item || typeof item !== "object" || item.$optimistic) {
524
+ continue;
525
+ }
526
+ const r = await doApplyMutation(queryModel, item, mutationModel, mutationOp, mutationArgs, schema, logging);
527
+ if (r && typeof r === "object") {
528
+ if (!arrayCloned) {
529
+ resultData = [
530
+ ...resultData
531
+ ];
532
+ arrayCloned = true;
533
+ }
534
+ resultData[i] = r;
535
+ updated = true;
536
+ }
537
+ }
538
+ } else if (resultData !== null && typeof resultData === "object") {
539
+ const currentData = {
540
+ ...resultData
541
+ };
542
+ for (const [key, value] of Object.entries(currentData)) {
543
+ const fieldDef = modelFields[key];
544
+ if (!fieldDef?.relation) {
545
+ continue;
546
+ }
547
+ const r = await doApplyMutation(fieldDef.type, value, mutationModel, mutationOp, mutationArgs, schema, logging);
548
+ if (r && typeof r === "object") {
549
+ resultData = {
550
+ ...resultData,
551
+ [key]: r
552
+ };
553
+ updated = true;
554
+ }
555
+ }
556
+ }
557
+ return updated ? resultData : void 0;
558
+ }
559
+ __name(doApplyMutation, "doApplyMutation");
560
+ function createMutate(queryModel, currentData, newData, schema, logging) {
561
+ if (!newData) {
562
+ return void 0;
563
+ }
564
+ const modelFields = schema.models[queryModel]?.fields;
565
+ if (!modelFields) {
566
+ return void 0;
567
+ }
568
+ const insert = {};
569
+ const newDataFields = Object.keys(newData);
570
+ Object.entries(modelFields).forEach(([name, field]) => {
571
+ if (field.relation && newData[name]) {
572
+ assignForeignKeyFields(field, insert, newData[name]);
573
+ return;
574
+ }
575
+ if (newDataFields.includes(name)) {
576
+ insert[name] = clone(newData[name]);
577
+ } else {
578
+ const defaultAttr = field.attributes?.find((attr) => attr.name === "@default");
579
+ if (field.type === "DateTime") {
580
+ if (defaultAttr || field.attributes?.some((attr) => attr.name === "@updatedAt")) {
581
+ insert[name] = /* @__PURE__ */ new Date();
582
+ return;
583
+ }
584
+ }
585
+ const defaultArg = defaultAttr?.args?.[0]?.value;
586
+ if (defaultArg?.kind === "literal") {
587
+ insert[name] = defaultArg.value;
588
+ }
589
+ }
590
+ });
591
+ const idFields = getIdFields(schema, queryModel);
592
+ idFields.forEach((f) => {
593
+ if (insert[f.name] === void 0) {
594
+ if (f.type === "Int" || f.type === "BigInt") {
595
+ const currMax = Array.isArray(currentData) ? Math.max(...[
596
+ ...currentData
597
+ ].map((item) => {
598
+ const idv = parseInt(item[f.name]);
599
+ return isNaN(idv) ? 0 : idv;
600
+ })) : 0;
601
+ insert[f.name] = currMax + 1;
602
+ } else {
603
+ insert[f.name] = crypto.randomUUID();
604
+ }
605
+ }
606
+ });
607
+ insert.$optimistic = true;
608
+ if (logging) {
609
+ log(logging, `Applying optimistic create for ${queryModel}: ${JSON.stringify(insert)}`);
610
+ }
611
+ return [
612
+ insert,
613
+ ...Array.isArray(currentData) ? currentData : []
614
+ ];
615
+ }
616
+ __name(createMutate, "createMutate");
617
+ function updateMutate(queryModel, currentData, mutateModel, mutateArgs, schema, logging) {
618
+ if (!currentData || typeof currentData !== "object") {
619
+ return void 0;
620
+ }
621
+ if (!mutateArgs?.where || typeof mutateArgs.where !== "object") {
622
+ return void 0;
623
+ }
624
+ if (!mutateArgs?.data || typeof mutateArgs.data !== "object") {
625
+ return void 0;
626
+ }
627
+ if (!idFieldsMatch(mutateModel, currentData, mutateArgs.where, schema)) {
628
+ return void 0;
629
+ }
630
+ const modelFields = schema.models[queryModel]?.fields;
631
+ if (!modelFields) {
632
+ return void 0;
633
+ }
634
+ let updated = false;
635
+ let resultData = currentData;
636
+ for (const [key, value] of Object.entries(mutateArgs.data)) {
637
+ const fieldInfo = modelFields[key];
638
+ if (!fieldInfo) {
639
+ continue;
640
+ }
641
+ if (fieldInfo.relation && !value?.connect) {
642
+ continue;
643
+ }
644
+ if (!updated) {
645
+ resultData = {
646
+ ...currentData
647
+ };
648
+ }
649
+ if (fieldInfo.relation) {
650
+ assignForeignKeyFields(fieldInfo, resultData, value);
651
+ } else {
652
+ resultData[key] = clone(value);
653
+ }
654
+ resultData.$optimistic = true;
655
+ updated = true;
656
+ if (logging) {
657
+ log(logging, `Applying optimistic update for ${queryModel}: ${JSON.stringify(resultData)}`);
658
+ }
659
+ }
660
+ return updated ? resultData : void 0;
661
+ }
662
+ __name(updateMutate, "updateMutate");
663
+ function upsertMutate(queryModel, currentData, model, args, schema, logging) {
664
+ let updated = false;
665
+ let resultData = currentData;
666
+ if (Array.isArray(resultData)) {
667
+ const foundIndex = resultData.findIndex((x) => idFieldsMatch(model, x, args.where, schema));
668
+ if (foundIndex >= 0) {
669
+ const updateResult = updateMutate(queryModel, resultData[foundIndex], model, {
670
+ where: args.where,
671
+ data: args.update
672
+ }, schema, logging);
673
+ if (updateResult) {
674
+ resultData = [
675
+ ...resultData.slice(0, foundIndex),
676
+ updateResult,
677
+ ...resultData.slice(foundIndex + 1)
678
+ ];
679
+ updated = true;
680
+ }
681
+ } else {
682
+ const createResult = createMutate(queryModel, resultData, args.create, schema, logging);
683
+ if (createResult) {
684
+ resultData = createResult;
685
+ updated = true;
686
+ }
687
+ }
688
+ } else {
689
+ const updateResult = updateMutate(queryModel, resultData, model, {
690
+ where: args.where,
691
+ data: args.update
692
+ }, schema, logging);
693
+ if (updateResult) {
694
+ resultData = updateResult;
695
+ updated = true;
696
+ }
697
+ }
698
+ return updated ? resultData : void 0;
699
+ }
700
+ __name(upsertMutate, "upsertMutate");
701
+ function deleteMutate(queryModel, currentData, mutateModel, mutateArgs, schema, logging) {
702
+ if (!currentData || !mutateArgs) {
703
+ return void 0;
704
+ }
705
+ if (queryModel !== mutateModel) {
706
+ return void 0;
707
+ }
708
+ let updated = false;
709
+ let result = currentData;
710
+ if (Array.isArray(currentData)) {
711
+ for (const item of currentData) {
712
+ if (idFieldsMatch(mutateModel, item, mutateArgs, schema)) {
713
+ result = result.filter((x) => x !== item);
714
+ updated = true;
715
+ if (logging) {
716
+ log(logging, `Applying optimistic delete for ${queryModel}: ${JSON.stringify(item)}`);
717
+ }
718
+ }
719
+ }
720
+ } else {
721
+ if (idFieldsMatch(mutateModel, currentData, mutateArgs, schema)) {
722
+ result = null;
723
+ updated = true;
724
+ if (logging) {
725
+ log(logging, `Applying optimistic delete for ${queryModel}: ${JSON.stringify(currentData)}`);
726
+ }
727
+ }
728
+ }
729
+ return updated ? result : void 0;
730
+ }
731
+ __name(deleteMutate, "deleteMutate");
732
+ function idFieldsMatch(model, x, y, schema) {
733
+ if (!x || !y || typeof x !== "object" || typeof y !== "object") {
734
+ return false;
735
+ }
736
+ const idFields = getIdFields(schema, model);
737
+ if (idFields.length === 0) {
738
+ return false;
739
+ }
740
+ return idFields.every((f) => x[f.name] === y[f.name]);
741
+ }
742
+ __name(idFieldsMatch, "idFieldsMatch");
743
+ function assignForeignKeyFields(field, resultData, mutationData) {
744
+ if (!mutationData?.connect) {
745
+ return;
746
+ }
747
+ if (!field.relation?.fields || !field.relation.references) {
748
+ return;
749
+ }
750
+ for (const [idField, fkField] of zip(field.relation.references, field.relation.fields)) {
751
+ if (idField in mutationData.connect) {
752
+ resultData[fkField] = mutationData.connect[idField];
753
+ }
754
+ }
755
+ }
756
+ __name(assignForeignKeyFields, "assignForeignKeyFields");
757
+ function getIdFields(schema, model) {
758
+ return (schema.models[model]?.idFields ?? []).map((f) => schema.models[model].fields[f]);
759
+ }
760
+ __name(getIdFields, "getIdFields");
761
+
762
+ // src/optimistic.ts
763
+ function createOptimisticUpdater(model, operation, schema, options, getAllQueries, logging) {
764
+ return async (...args) => {
765
+ const [mutationArgs] = args;
766
+ for (const queryInfo of getAllQueries()) {
767
+ const logInfo = JSON.stringify({
768
+ model: queryInfo.model,
769
+ operation: queryInfo.operation,
770
+ args: queryInfo.args
771
+ });
772
+ if (!queryInfo.optimisticUpdate) {
773
+ if (logging) {
774
+ log(logging, `Skipping optimistic update for ${logInfo} due to opt-out`);
775
+ }
776
+ continue;
777
+ }
778
+ if (options.optimisticDataProvider) {
779
+ const providerResult = await options.optimisticDataProvider({
780
+ queryModel: queryInfo.model,
781
+ queryOperation: queryInfo.operation,
782
+ queryArgs: queryInfo.args,
783
+ currentData: queryInfo.data,
784
+ mutationArgs
785
+ });
786
+ if (providerResult?.kind === "Skip") {
787
+ if (logging) {
788
+ log(logging, `Skipping optimistic updating due to provider result: ${logInfo}`);
789
+ }
790
+ continue;
791
+ } else if (providerResult?.kind === "Update") {
792
+ if (logging) {
793
+ log(logging, `Optimistically updating due to provider result: ${logInfo}`);
794
+ }
795
+ queryInfo.updateData(providerResult.data, true);
796
+ continue;
797
+ }
798
+ }
799
+ const mutatedData = await applyMutation(queryInfo.model, queryInfo.operation, queryInfo.data, model, operation, mutationArgs, schema, logging);
800
+ if (mutatedData !== void 0) {
801
+ if (logging) {
802
+ log(logging, `Optimistically updating due to mutation "${model}.${operation}": ${logInfo}`);
803
+ }
804
+ queryInfo.updateData(mutatedData, true);
805
+ }
806
+ }
807
+ };
808
+ }
809
+ __name(createOptimisticUpdater, "createOptimisticUpdater");
810
+ export {
811
+ DEFAULT_QUERY_ENDPOINT,
812
+ NestedReadVisitor,
813
+ NestedWriteVisitor,
814
+ ORMWriteActions,
815
+ applyMutation,
816
+ createInvalidator,
817
+ createOptimisticUpdater,
818
+ getMutatedModels,
819
+ getReadModels,
820
+ log
821
+ };
822
+ //# sourceMappingURL=index.js.map