@warp-drive/json-api 5.9.0-beta.0 → 5.9.0-beta.2
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/CHANGELOG.md +109 -0
- package/README.md +1 -1
- package/dist/index.d.ts +515 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2421 -3120
- package/dist/index.js.map +1 -0
- package/dist/unpkg/dev/index.js +2477 -3225
- package/dist/unpkg/dev/index.js.map +1 -0
- package/dist/unpkg/dev-deprecated/index.js +2452 -3186
- package/dist/unpkg/dev-deprecated/index.js.map +1 -0
- package/dist/unpkg/prod/index.js +1264 -1815
- package/dist/unpkg/prod/index.js.map +1 -0
- package/dist/unpkg/prod-deprecated/index.js +1264 -1815
- package/dist/unpkg/prod-deprecated/index.js.map +1 -0
- package/package.json +19 -23
- package/declarations/-private/cache.d.ts +0 -454
- package/declarations/-private/validate-document-fields.d.ts +0 -3
- package/declarations/-private/validator/1.1/7.1_top-level-document-members.d.ts +0 -1
- package/declarations/-private/validator/1.1/7.2_resource-objects.d.ts +0 -1
- package/declarations/-private/validator/1.1/links.d.ts +0 -1
- package/declarations/-private/validator/index.d.ts +0 -4
- package/declarations/-private/validator/utils.d.ts +0 -75
- package/declarations/index.d.ts +0 -5
package/dist/unpkg/prod/index.js
CHANGED
|
@@ -1,1884 +1,1333 @@
|
|
|
1
|
-
import { graphFor,
|
|
2
|
-
import { assertPrivateCapabilities,
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import { graphFor, isBelongsTo, peekGraph } from "@warp-drive/core/graph/-private";
|
|
2
|
+
import { assertPrivateCapabilities, isRequestKey, isResourceKey } from "@warp-drive/core/store/-private";
|
|
3
|
+
import "fuse.js";
|
|
4
|
+
import "json-to-ast";
|
|
5
5
|
|
|
6
|
+
//#region src/-private/validator/utils.ts
|
|
6
7
|
function isMetaDocument(doc) {
|
|
7
|
-
|
|
8
|
+
return !(doc instanceof Error) && doc.content && !("data" in doc.content) && !("included" in doc.content) && "meta" in doc.content;
|
|
8
9
|
}
|
|
9
10
|
function isErrorDocument(doc) {
|
|
10
|
-
|
|
11
|
+
return doc instanceof Error;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/-private/cache.ts
|
|
13
16
|
function isImplicit(relationship) {
|
|
14
|
-
|
|
17
|
+
return relationship.definition.isImplicit;
|
|
15
18
|
}
|
|
16
|
-
const EMPTY_ITERATOR = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
};
|
|
19
|
+
const EMPTY_ITERATOR = { iterator() {
|
|
20
|
+
return { next() {
|
|
21
|
+
return {
|
|
22
|
+
done: true,
|
|
23
|
+
value: void 0
|
|
24
|
+
};
|
|
25
|
+
} };
|
|
26
|
+
} };
|
|
28
27
|
function makeCache() {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
return {
|
|
29
|
+
id: null,
|
|
30
|
+
remoteAttrs: null,
|
|
31
|
+
localAttrs: null,
|
|
32
|
+
defaultAttrs: null,
|
|
33
|
+
inflightAttrs: null,
|
|
34
|
+
changes: null,
|
|
35
|
+
errors: null,
|
|
36
|
+
isNew: false,
|
|
37
|
+
isDeleted: false,
|
|
38
|
+
isDeletionCommitted: false
|
|
39
|
+
};
|
|
41
40
|
}
|
|
42
|
-
|
|
43
41
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
// since we initiaize the value as basePath as a clone of the value at the remote basePath
|
|
944
|
-
// then in theory we can use JSON.stringify to compare the two values as key insertion order
|
|
945
|
-
// ought to be consistent.
|
|
946
|
-
// we try/catch this because users have a habit of doing "Bad Things"TM wherein the cache contains
|
|
947
|
-
// stateful values that are not JSON serializable correctly such as Dates.
|
|
948
|
-
// in the case that we error, we fallback to not removing the local value
|
|
949
|
-
// so that any changes we don't understand are preserved. Thse objects would then sometimes
|
|
950
|
-
// appear to be dirty unnecessarily, and for folks that open an issue we can guide them
|
|
951
|
-
// to make their cache data less stateful.
|
|
952
|
-
} else if (cached.localAttrs) {
|
|
953
|
-
try {
|
|
954
|
-
if (!existing) {
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
const existingStr = JSON.stringify(existing);
|
|
958
|
-
const newStr = JSON.stringify(cached.localAttrs[basePath]);
|
|
959
|
-
if (existingStr !== newStr) {
|
|
960
|
-
delete cached.localAttrs[basePath];
|
|
961
|
-
delete cached.changes[basePath];
|
|
962
|
-
}
|
|
963
|
-
} catch {
|
|
964
|
-
// noop
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
this._capabilities.notifyChange(identifier, 'attributes', basePath);
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
/**
|
|
971
|
-
* Query the cache for the changed attributes of a resource.
|
|
972
|
-
*
|
|
973
|
-
* @category Resource Data
|
|
974
|
-
* @public
|
|
975
|
-
* @return `{ '<field>': ['<old>', '<new>'] }`
|
|
976
|
-
*/
|
|
977
|
-
changedAttrs(identifier) {
|
|
978
|
-
const cached = this.__peek(identifier, false);
|
|
979
|
-
|
|
980
|
-
// in Prod we try to recover when accessing something that
|
|
981
|
-
// doesn't exist
|
|
982
|
-
if (!cached) {
|
|
983
|
-
return Object.create(null);
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// TODO freeze in dev
|
|
987
|
-
return cached.changes || Object.create(null);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
/**
|
|
991
|
-
* Query the cache for whether any mutated attributes exist
|
|
992
|
-
*
|
|
993
|
-
* @category Resource Data
|
|
994
|
-
* @public
|
|
995
|
-
*/
|
|
996
|
-
hasChangedAttrs(identifier) {
|
|
997
|
-
const cached = this.__peek(identifier, true);
|
|
998
|
-
|
|
999
|
-
// in Prod we try to recover when accessing something that
|
|
1000
|
-
// doesn't exist
|
|
1001
|
-
if (!cached) {
|
|
1002
|
-
return false;
|
|
1003
|
-
}
|
|
1004
|
-
return cached.inflightAttrs !== null && Object.keys(cached.inflightAttrs).length > 0 || cached.localAttrs !== null && Object.keys(cached.localAttrs).length > 0;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
/**
|
|
1008
|
-
* Tell the cache to discard any uncommitted mutations to attributes
|
|
1009
|
-
*
|
|
1010
|
-
* This method is a candidate to become a mutation
|
|
1011
|
-
*
|
|
1012
|
-
* @category Resource Data
|
|
1013
|
-
* @public
|
|
1014
|
-
* @return the names of fields that were restored
|
|
1015
|
-
*/
|
|
1016
|
-
rollbackAttrs(identifier) {
|
|
1017
|
-
const cached = this.__peek(identifier, false);
|
|
1018
|
-
let dirtyKeys;
|
|
1019
|
-
cached.isDeleted = false;
|
|
1020
|
-
if (cached.localAttrs !== null) {
|
|
1021
|
-
dirtyKeys = Object.keys(cached.localAttrs);
|
|
1022
|
-
cached.localAttrs = null;
|
|
1023
|
-
cached.changes = null;
|
|
1024
|
-
}
|
|
1025
|
-
if (cached.isNew) {
|
|
1026
|
-
// > Note: Graph removal handled by unloadRecord
|
|
1027
|
-
cached.isDeletionCommitted = true;
|
|
1028
|
-
cached.isDeleted = true;
|
|
1029
|
-
cached.isNew = false;
|
|
1030
|
-
}
|
|
1031
|
-
cached.inflightAttrs = null;
|
|
1032
|
-
cached.defaultAttrs = null;
|
|
1033
|
-
if (cached.errors) {
|
|
1034
|
-
cached.errors = null;
|
|
1035
|
-
this._capabilities.notifyChange(identifier, 'errors', null);
|
|
1036
|
-
}
|
|
1037
|
-
this._capabilities.notifyChange(identifier, 'state', null);
|
|
1038
|
-
if (dirtyKeys && dirtyKeys.length) {
|
|
1039
|
-
notifyAttributes(this._capabilities, identifier, new Set(dirtyKeys));
|
|
1040
|
-
}
|
|
1041
|
-
return dirtyKeys || [];
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* Query the cache for the changes to relationships of a resource.
|
|
1046
|
-
*
|
|
1047
|
-
* Returns a map of relationship names to RelationshipDiff objects.
|
|
1048
|
-
*
|
|
1049
|
-
* ```ts
|
|
1050
|
-
* type RelationshipDiff =
|
|
1051
|
-
| {
|
|
1052
|
-
kind: 'collection';
|
|
1053
|
-
remoteState: ResourceKey[];
|
|
1054
|
-
additions: Set<ResourceKey>;
|
|
1055
|
-
removals: Set<ResourceKey>;
|
|
1056
|
-
localState: ResourceKey[];
|
|
1057
|
-
reordered: boolean;
|
|
1058
|
-
}
|
|
1059
|
-
| {
|
|
1060
|
-
kind: 'resource';
|
|
1061
|
-
remoteState: ResourceKey | null;
|
|
1062
|
-
localState: ResourceKey | null;
|
|
1063
|
-
};
|
|
1064
|
-
```
|
|
1065
|
-
*
|
|
1066
|
-
* @category Resource Data
|
|
1067
|
-
* @public
|
|
1068
|
-
*/
|
|
1069
|
-
changedRelationships(identifier) {
|
|
1070
|
-
return this.__graph.getChanged(identifier);
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
/**
|
|
1074
|
-
* Query the cache for whether any mutated relationships exist
|
|
1075
|
-
*
|
|
1076
|
-
* @category Resource Data
|
|
1077
|
-
* @public
|
|
1078
|
-
*/
|
|
1079
|
-
hasChangedRelationships(identifier) {
|
|
1080
|
-
return this.__graph.hasChanged(identifier);
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
/**
|
|
1084
|
-
* Tell the cache to discard any uncommitted mutations to relationships.
|
|
1085
|
-
*
|
|
1086
|
-
* This will also discard the change on any appropriate inverses.
|
|
1087
|
-
*
|
|
1088
|
-
* This method is a candidate to become a mutation
|
|
1089
|
-
*
|
|
1090
|
-
* @category Resource Data
|
|
1091
|
-
* @public
|
|
1092
|
-
* @return the names of relationships that were restored
|
|
1093
|
-
*/
|
|
1094
|
-
rollbackRelationships(identifier) {
|
|
1095
|
-
assertPrivateCapabilities(this._capabilities);
|
|
1096
|
-
let result;
|
|
1097
|
-
this._capabilities._store._join(() => {
|
|
1098
|
-
result = this.__graph.rollback(identifier);
|
|
1099
|
-
});
|
|
1100
|
-
return result;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
/**
|
|
1104
|
-
* Query the cache for the current state of a relationship property
|
|
1105
|
-
*
|
|
1106
|
-
* @category Resource Data
|
|
1107
|
-
* @public
|
|
1108
|
-
* @return resource relationship object
|
|
1109
|
-
*/
|
|
1110
|
-
getRelationship(identifier, field) {
|
|
1111
|
-
return this.__graph.getData(identifier, field);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
/**
|
|
1115
|
-
* Query the cache for the remote state of a relationship property
|
|
1116
|
-
*
|
|
1117
|
-
* @category Resource Data
|
|
1118
|
-
* @public
|
|
1119
|
-
* @return resource relationship object
|
|
1120
|
-
*/
|
|
1121
|
-
getRemoteRelationship(identifier, field) {
|
|
1122
|
-
return this.__graph.getRemoteData(identifier, field);
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
////////// ============== //////////
|
|
1126
|
-
////////// Resource State //////////
|
|
1127
|
-
////////// ============== //////////
|
|
1128
|
-
|
|
1129
|
-
/**
|
|
1130
|
-
* Update the cache state for the given resource to be marked
|
|
1131
|
-
* as locally deleted, or remove such a mark.
|
|
1132
|
-
*
|
|
1133
|
-
* This method is a candidate to become a mutation
|
|
1134
|
-
*
|
|
1135
|
-
* @category Resource State
|
|
1136
|
-
* @public
|
|
1137
|
-
*/
|
|
1138
|
-
setIsDeleted(identifier, isDeleted) {
|
|
1139
|
-
const cached = this.__peek(identifier, false);
|
|
1140
|
-
cached.isDeleted = isDeleted;
|
|
1141
|
-
// > Note: Graph removal for isNew handled by unloadRecord
|
|
1142
|
-
this._capabilities.notifyChange(identifier, 'state', null);
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
/**
|
|
1146
|
-
* Query the cache for any validation errors applicable to the given resource.
|
|
1147
|
-
*
|
|
1148
|
-
* @category Resource State
|
|
1149
|
-
* @public
|
|
1150
|
-
*/
|
|
1151
|
-
getErrors(identifier) {
|
|
1152
|
-
return this.__peek(identifier, true).errors || [];
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
/**
|
|
1156
|
-
* Query the cache for whether a given resource has any available data
|
|
1157
|
-
*
|
|
1158
|
-
* @category Resource State
|
|
1159
|
-
* @public
|
|
1160
|
-
*/
|
|
1161
|
-
isEmpty(identifier) {
|
|
1162
|
-
const cached = this.__safePeek(identifier, true);
|
|
1163
|
-
return cached ? cached.remoteAttrs === null && cached.inflightAttrs === null && cached.localAttrs === null : true;
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
/**
|
|
1167
|
-
* Query the cache for whether a given resource was created locally and not
|
|
1168
|
-
* yet persisted.
|
|
1169
|
-
*
|
|
1170
|
-
* @category Resource State
|
|
1171
|
-
* @public
|
|
1172
|
-
*/
|
|
1173
|
-
isNew(identifier) {
|
|
1174
|
-
// TODO can we assert here?
|
|
1175
|
-
return this.__safePeek(identifier, true)?.isNew || false;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
/**
|
|
1179
|
-
* Query the cache for whether a given resource is marked as deleted (but not
|
|
1180
|
-
* necessarily persisted yet).
|
|
1181
|
-
*
|
|
1182
|
-
* @category Resource State
|
|
1183
|
-
* @public
|
|
1184
|
-
*/
|
|
1185
|
-
isDeleted(identifier) {
|
|
1186
|
-
// TODO can we assert here?
|
|
1187
|
-
return this.__safePeek(identifier, true)?.isDeleted || false;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
/**
|
|
1191
|
-
* Query the cache for whether a given resource has been deleted and that deletion
|
|
1192
|
-
* has also been persisted.
|
|
1193
|
-
*
|
|
1194
|
-
* @category Resource State
|
|
1195
|
-
* @public
|
|
1196
|
-
*/
|
|
1197
|
-
isDeletionCommitted(identifier) {
|
|
1198
|
-
// TODO can we assert here?
|
|
1199
|
-
return this.__safePeek(identifier, true)?.isDeletionCommitted || false;
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
/**
|
|
1203
|
-
* Private method used to populate an entry for the identifier
|
|
1204
|
-
*
|
|
1205
|
-
* @internal
|
|
1206
|
-
*/
|
|
1207
|
-
_createCache(identifier) {
|
|
1208
|
-
const cache = makeCache();
|
|
1209
|
-
this.__cache.set(identifier, cache);
|
|
1210
|
-
return cache;
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
/**
|
|
1214
|
-
* Peek whether we have cached resource data matching the identifier
|
|
1215
|
-
* without asserting if the resource data is missing.
|
|
1216
|
-
*
|
|
1217
|
-
* @internal
|
|
1218
|
-
*/
|
|
1219
|
-
__safePeek(identifier, allowDestroyed) {
|
|
1220
|
-
let resource = this.__cache.get(identifier);
|
|
1221
|
-
if (!resource && allowDestroyed) {
|
|
1222
|
-
resource = this.__destroyedCache.get(identifier);
|
|
1223
|
-
}
|
|
1224
|
-
return resource;
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
/**
|
|
1228
|
-
* Peek whether we have cached resource data matching the identifier
|
|
1229
|
-
* Asserts if the resource data is missing.
|
|
1230
|
-
*
|
|
1231
|
-
* @internal
|
|
1232
|
-
*/
|
|
1233
|
-
__peek(identifier, allowDestroyed) {
|
|
1234
|
-
const resource = this.__safePeek(identifier, allowDestroyed);
|
|
1235
|
-
return resource;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { JSONAPICache } from '@warp-drive/json-api';
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* A {@link Cache} implementation tuned for [{json:api}](https://jsonapi.org/)
|
|
47
|
+
*
|
|
48
|
+
* @categoryDescription Cache Management
|
|
49
|
+
* APIs for primary cache management functionality
|
|
50
|
+
* @categoryDescription Cache Forking
|
|
51
|
+
* APIs that support Cache Forking
|
|
52
|
+
* @categoryDescription SSR Support
|
|
53
|
+
* APIs that support SSR functionality
|
|
54
|
+
* @categoryDescription Resource Lifecycle
|
|
55
|
+
* APIs that support management of resource data
|
|
56
|
+
* @categoryDescription Resource Data
|
|
57
|
+
* APIs that support granular field level management of resource data
|
|
58
|
+
* @categoryDescription Resource State
|
|
59
|
+
* APIs that support managing Resource states
|
|
60
|
+
*
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
var JSONAPICache = class {
|
|
64
|
+
/**
|
|
65
|
+
* The Cache Version that this implementation implements.
|
|
66
|
+
*
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
/** @internal */
|
|
70
|
+
/** @internal */
|
|
71
|
+
/** @internal */
|
|
72
|
+
/** @internal */
|
|
73
|
+
/** @internal */
|
|
74
|
+
constructor(capabilities) {
|
|
75
|
+
this.version = "2";
|
|
76
|
+
this._capabilities = capabilities;
|
|
77
|
+
this.__cache = /* @__PURE__ */ new Map();
|
|
78
|
+
this.__graph = graphFor(capabilities);
|
|
79
|
+
this.__destroyedCache = /* @__PURE__ */ new Map();
|
|
80
|
+
this.__documents = /* @__PURE__ */ new Map();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Cache the response to a request
|
|
84
|
+
*
|
|
85
|
+
* Implements `Cache.put`.
|
|
86
|
+
*
|
|
87
|
+
* Expects a StructuredDocument whose `content` member is a JsonApiDocument.
|
|
88
|
+
*
|
|
89
|
+
* ```js
|
|
90
|
+
* cache.put({
|
|
91
|
+
* request: { url: 'https://api.example.com/v1/user/1' },
|
|
92
|
+
* content: {
|
|
93
|
+
* data: {
|
|
94
|
+
* type: 'user',
|
|
95
|
+
* id: '1',
|
|
96
|
+
* attributes: {
|
|
97
|
+
* name: 'Chris'
|
|
98
|
+
* }
|
|
99
|
+
* }
|
|
100
|
+
* }
|
|
101
|
+
* })
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* > **Note**
|
|
105
|
+
* > The nested `content` and `data` members are not a mistake. This is because
|
|
106
|
+
* > there are two separate concepts involved here, the `StructuredDocument` which contains
|
|
107
|
+
* > the context of a given Request that has been issued with the returned contents as its
|
|
108
|
+
* > `content` property, and a `JSON:API Document` which is the json contents returned by
|
|
109
|
+
* > this endpoint and which uses its `data` property to signify which resources are the
|
|
110
|
+
* > primary resources associated with the request.
|
|
111
|
+
*
|
|
112
|
+
* StructuredDocument's with urls will be cached as full documents with
|
|
113
|
+
* associated resource membership order and contents preserved but linked
|
|
114
|
+
* into the cache.
|
|
115
|
+
*
|
|
116
|
+
* @category Cache Management
|
|
117
|
+
* @public
|
|
118
|
+
*/
|
|
119
|
+
put(doc) {
|
|
120
|
+
if (isErrorDocument(doc)) return this._putDocument(doc, void 0, void 0);
|
|
121
|
+
else if (isMetaDocument(doc)) return this._putDocument(doc, void 0, void 0);
|
|
122
|
+
const jsonApiDoc = doc.content;
|
|
123
|
+
const included = jsonApiDoc.included;
|
|
124
|
+
let i, length;
|
|
125
|
+
const { cacheKeyManager } = this._capabilities;
|
|
126
|
+
if (included) for (i = 0, length = included.length; i < length; i++) included[i] = putOne(this, cacheKeyManager, included[i]);
|
|
127
|
+
if (Array.isArray(jsonApiDoc.data)) {
|
|
128
|
+
length = jsonApiDoc.data.length;
|
|
129
|
+
const identifiers = [];
|
|
130
|
+
for (i = 0; i < length; i++) identifiers.push(putOne(this, cacheKeyManager, jsonApiDoc.data[i]));
|
|
131
|
+
return this._putDocument(doc, identifiers, included);
|
|
132
|
+
}
|
|
133
|
+
if (jsonApiDoc.data === null) return this._putDocument(doc, null, included);
|
|
134
|
+
const identifier = putOne(this, cacheKeyManager, jsonApiDoc.data);
|
|
135
|
+
return this._putDocument(doc, identifier, included);
|
|
136
|
+
}
|
|
137
|
+
/** @internal */
|
|
138
|
+
/** @internal */
|
|
139
|
+
/** @internal */
|
|
140
|
+
/** @internal */
|
|
141
|
+
/** @internal */
|
|
142
|
+
_putDocument(doc, data, included) {
|
|
143
|
+
const resourceDocument = isErrorDocument(doc) ? fromStructuredError(doc) : fromBaseDocument(doc);
|
|
144
|
+
if (data !== void 0) resourceDocument.data = data;
|
|
145
|
+
if (included !== void 0) resourceDocument.included = included;
|
|
146
|
+
const request = doc.request;
|
|
147
|
+
const identifier = request ? this._capabilities.cacheKeyManager.getOrCreateDocumentIdentifier(request) : null;
|
|
148
|
+
if (identifier) {
|
|
149
|
+
resourceDocument.lid = identifier.lid;
|
|
150
|
+
doc.content = resourceDocument;
|
|
151
|
+
const hasExisting = this.__documents.has(identifier.lid);
|
|
152
|
+
this.__documents.set(identifier.lid, doc);
|
|
153
|
+
this._capabilities.notifyChange(identifier, hasExisting ? "updated" : "added", null);
|
|
154
|
+
}
|
|
155
|
+
if (doc.request?.op === "findHasMany") {
|
|
156
|
+
const parentIdentifier = doc.request.options?.identifier;
|
|
157
|
+
const parentField = doc.request.options?.field;
|
|
158
|
+
if (parentField && parentIdentifier) this.__graph.push({
|
|
159
|
+
op: "updateRelationship",
|
|
160
|
+
record: parentIdentifier,
|
|
161
|
+
field: parentField.name,
|
|
162
|
+
value: resourceDocument
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return resourceDocument;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Update the "remote" or "canonical" (persisted) state of the Cache
|
|
169
|
+
* by merging new information into the existing state.
|
|
170
|
+
*
|
|
171
|
+
* @category Cache Management
|
|
172
|
+
* @public
|
|
173
|
+
* @param op the operation or list of operations to perform
|
|
174
|
+
*/
|
|
175
|
+
patch(op) {
|
|
176
|
+
if (Array.isArray(op)) {
|
|
177
|
+
assertPrivateCapabilities(this._capabilities);
|
|
178
|
+
this._capabilities._store._join(() => {
|
|
179
|
+
for (const operation of op) patchCache(this, operation);
|
|
180
|
+
});
|
|
181
|
+
} else patchCache(this, op);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Update the "local" or "current" (unpersisted) state of the Cache
|
|
185
|
+
*
|
|
186
|
+
* @category Cache Management
|
|
187
|
+
* @public
|
|
188
|
+
*/
|
|
189
|
+
mutate(mutation) {
|
|
190
|
+
this.__graph.update(mutation, false);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Peek resource data from the Cache.
|
|
194
|
+
*
|
|
195
|
+
* In development, if the return value
|
|
196
|
+
* is JSON the return value
|
|
197
|
+
* will be deep-cloned and deep-frozen
|
|
198
|
+
* to prevent mutation thereby enforcing cache
|
|
199
|
+
* Immutability.
|
|
200
|
+
*
|
|
201
|
+
* This form of peek is useful for implementations
|
|
202
|
+
* that want to feed raw-data from cache to the UI
|
|
203
|
+
* or which want to interact with a blob of data
|
|
204
|
+
* directly from the presentation cache.
|
|
205
|
+
*
|
|
206
|
+
* An implementation might want to do this because
|
|
207
|
+
* de-referencing records which read from their own
|
|
208
|
+
* blob is generally safer because the record does
|
|
209
|
+
* not require retainining connections to the Store
|
|
210
|
+
* and Cache to present data on a per-field basis.
|
|
211
|
+
*
|
|
212
|
+
* This generally takes the place of `getAttr` as
|
|
213
|
+
* an API and may even take the place of `getRelationship`
|
|
214
|
+
* depending on implementation specifics, though this
|
|
215
|
+
* latter usage is less recommended due to the advantages
|
|
216
|
+
* of the Graph handling necessary entanglements and
|
|
217
|
+
* notifications for relational data.
|
|
218
|
+
*
|
|
219
|
+
* :::warning
|
|
220
|
+
* It is not recommended to use the return value as
|
|
221
|
+
* a serialized representation of the resource both
|
|
222
|
+
* due to it containing local mutations and because
|
|
223
|
+
* it may contain additional fields not recognized
|
|
224
|
+
* by the {json:api} API implementation such as `lid` and
|
|
225
|
+
* the various internal WarpDrive bookkeeping fields.
|
|
226
|
+
* :::
|
|
227
|
+
*
|
|
228
|
+
* @category Cache Management
|
|
229
|
+
* @public
|
|
230
|
+
*/
|
|
231
|
+
peek(identifier) {
|
|
232
|
+
if (isResourceKey(identifier)) {
|
|
233
|
+
const peeked = this.__safePeek(identifier, false);
|
|
234
|
+
if (!peeked) return null;
|
|
235
|
+
const { type, id, lid } = identifier;
|
|
236
|
+
const attributes = structuredClone(Object.assign({}, peeked.remoteAttrs, peeked.inflightAttrs, peeked.localAttrs));
|
|
237
|
+
const relationships = {};
|
|
238
|
+
const rels = this.__graph.identifiers.get(identifier);
|
|
239
|
+
if (rels) Object.keys(rels).forEach((key) => {
|
|
240
|
+
if (rels[key].definition.isImplicit) return;
|
|
241
|
+
else relationships[key] = this.__graph.getData(identifier, key);
|
|
242
|
+
});
|
|
243
|
+
assertPrivateCapabilities(this._capabilities);
|
|
244
|
+
const store = this._capabilities._store;
|
|
245
|
+
getCacheFields(this, identifier).forEach((attr, key) => {
|
|
246
|
+
if (key in attributes && attributes[key] !== void 0) return;
|
|
247
|
+
const defaultValue = getDefaultValue(attr, identifier, store);
|
|
248
|
+
if (defaultValue !== void 0) attributes[key] = defaultValue;
|
|
249
|
+
});
|
|
250
|
+
return {
|
|
251
|
+
type,
|
|
252
|
+
id,
|
|
253
|
+
lid,
|
|
254
|
+
attributes,
|
|
255
|
+
relationships
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const document = this.peekRequest(identifier);
|
|
259
|
+
if (document) {
|
|
260
|
+
if ("content" in document) return document.content;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Peek the remote resource data from the Cache.
|
|
266
|
+
*
|
|
267
|
+
* @category Cache Management
|
|
268
|
+
* @public
|
|
269
|
+
*/
|
|
270
|
+
peekRemoteState(identifier) {
|
|
271
|
+
if (isResourceKey(identifier)) {
|
|
272
|
+
const peeked = this.__safePeek(identifier, false);
|
|
273
|
+
if (!peeked) return null;
|
|
274
|
+
const { type, id, lid } = identifier;
|
|
275
|
+
const attributes = structuredClone(peeked.remoteAttrs);
|
|
276
|
+
const relationships = {};
|
|
277
|
+
const rels = this.__graph.identifiers.get(identifier);
|
|
278
|
+
if (rels) Object.keys(rels).forEach((key) => {
|
|
279
|
+
if (rels[key].definition.isImplicit) return;
|
|
280
|
+
else relationships[key] = structuredClone(this.__graph.getData(identifier, key));
|
|
281
|
+
});
|
|
282
|
+
assertPrivateCapabilities(this._capabilities);
|
|
283
|
+
const store = this._capabilities._store;
|
|
284
|
+
getCacheFields(this, identifier).forEach((attr, key) => {
|
|
285
|
+
if (key in attributes && attributes[key] !== void 0) return;
|
|
286
|
+
const defaultValue = getDefaultValue(attr, identifier, store);
|
|
287
|
+
if (defaultValue !== void 0) attributes[key] = defaultValue;
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
type,
|
|
291
|
+
id,
|
|
292
|
+
lid,
|
|
293
|
+
attributes,
|
|
294
|
+
relationships
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
const document = this.peekRequest(identifier);
|
|
298
|
+
if (document) {
|
|
299
|
+
if ("content" in document) return document.content;
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Peek the Cache for the existing request data associated with
|
|
305
|
+
* a cacheable request.
|
|
306
|
+
*
|
|
307
|
+
* This is effectively the reverse of `put` for a request in
|
|
308
|
+
* that it will return the the request, response, and content
|
|
309
|
+
* whereas `peek` will return just the `content`.
|
|
310
|
+
*
|
|
311
|
+
* @category Cache Management
|
|
312
|
+
* @public
|
|
313
|
+
*/
|
|
314
|
+
peekRequest(identifier) {
|
|
315
|
+
return this.__documents.get(identifier.lid) || null;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Push resource data from a remote source into the cache for this identifier
|
|
319
|
+
*
|
|
320
|
+
* @category Cache Management
|
|
321
|
+
* @public
|
|
322
|
+
* @return if `calculateChanges` is true then calculated key changes should be returned
|
|
323
|
+
*/
|
|
324
|
+
upsert(identifier, data, calculateChanges) {
|
|
325
|
+
assertPrivateCapabilities(this._capabilities);
|
|
326
|
+
const store = this._capabilities._store;
|
|
327
|
+
if (!store._cbs) {
|
|
328
|
+
let result = void 0;
|
|
329
|
+
store._run(() => {
|
|
330
|
+
result = cacheUpsert(this, identifier, data, calculateChanges);
|
|
331
|
+
});
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
return cacheUpsert(this, identifier, data, calculateChanges);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Create a fork of the cache from the current state.
|
|
338
|
+
*
|
|
339
|
+
* Applications should typically not call this method themselves,
|
|
340
|
+
* preferring instead to fork at the Store level, which will
|
|
341
|
+
* utilize this method to fork the cache.
|
|
342
|
+
*
|
|
343
|
+
* @category Cache Forking
|
|
344
|
+
* @private
|
|
345
|
+
*/
|
|
346
|
+
fork() {
|
|
347
|
+
throw new Error(`Not Implemented`);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Merge a fork back into a parent Cache.
|
|
351
|
+
*
|
|
352
|
+
* Applications should typically not call this method themselves,
|
|
353
|
+
* preferring instead to merge at the Store level, which will
|
|
354
|
+
* utilize this method to merge the caches.
|
|
355
|
+
*
|
|
356
|
+
* @category Cache Forking
|
|
357
|
+
* @private
|
|
358
|
+
*/
|
|
359
|
+
merge(_cache) {
|
|
360
|
+
throw new Error(`Not Implemented`);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Generate the list of changes applied to all
|
|
364
|
+
* record in the store.
|
|
365
|
+
*
|
|
366
|
+
* Each individual resource or document that has
|
|
367
|
+
* been mutated should be described as an individual
|
|
368
|
+
* `Change` entry in the returned array.
|
|
369
|
+
*
|
|
370
|
+
* A `Change` is described by an object containing up to
|
|
371
|
+
* three properties: (1) the `identifier` of the entity that
|
|
372
|
+
* changed; (2) the `op` code of that change being one of
|
|
373
|
+
* `upsert` or `remove`, and if the op is `upsert` a `patch`
|
|
374
|
+
* containing the data to merge into the cache for the given
|
|
375
|
+
* entity.
|
|
376
|
+
*
|
|
377
|
+
* This `patch` is opaque to the Store but should be understood
|
|
378
|
+
* by the Cache and may expect to be utilized by an Adapter
|
|
379
|
+
* when generating data during a `save` operation.
|
|
380
|
+
*
|
|
381
|
+
* It is generally recommended that the `patch` contain only
|
|
382
|
+
* the updated state, ignoring fields that are unchanged
|
|
383
|
+
*
|
|
384
|
+
* ```ts
|
|
385
|
+
* interface Change {
|
|
386
|
+
* identifier: ResourceKey | RequestKey;
|
|
387
|
+
* op: 'upsert' | 'remove';
|
|
388
|
+
* patch?: unknown;
|
|
389
|
+
* }
|
|
390
|
+
* ```
|
|
391
|
+
*
|
|
392
|
+
* @category Cache Forking
|
|
393
|
+
* @private
|
|
394
|
+
*/
|
|
395
|
+
diff() {
|
|
396
|
+
throw new Error(`Not Implemented`);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Serialize the entire contents of the Cache into a Stream
|
|
400
|
+
* which may be fed back into a new instance of the same Cache
|
|
401
|
+
* via `cache.hydrate`.
|
|
402
|
+
*
|
|
403
|
+
* @category SSR Support
|
|
404
|
+
* @private
|
|
405
|
+
*/
|
|
406
|
+
dump() {
|
|
407
|
+
throw new Error(`Not Implemented`);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* hydrate a Cache from a Stream with content previously serialized
|
|
411
|
+
* from another instance of the same Cache, resolving when hydration
|
|
412
|
+
* is complete.
|
|
413
|
+
*
|
|
414
|
+
* This method should expect to be called both in the context of restoring
|
|
415
|
+
* the Cache during application rehydration after SSR **AND** at unknown
|
|
416
|
+
* times during the lifetime of an already booted application when it is
|
|
417
|
+
* desired to bulk-load additional information into the cache. This latter
|
|
418
|
+
* behavior supports optimizing pre/fetching of data for route transitions
|
|
419
|
+
* via data-only SSR modes.
|
|
420
|
+
*
|
|
421
|
+
* @category SSR Support
|
|
422
|
+
* @private
|
|
423
|
+
*/
|
|
424
|
+
hydrate(stream) {
|
|
425
|
+
throw new Error("Not Implemented");
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client
|
|
429
|
+
*
|
|
430
|
+
* It returns properties from options that should be set on the record during the create
|
|
431
|
+
* process. This return value behavior is deprecated.
|
|
432
|
+
*
|
|
433
|
+
* @category Resource Lifecycle
|
|
434
|
+
* @public
|
|
435
|
+
*/
|
|
436
|
+
clientDidCreate(identifier, options) {
|
|
437
|
+
const cached = this._createCache(identifier);
|
|
438
|
+
cached.isNew = true;
|
|
439
|
+
const createOptions = {};
|
|
440
|
+
if (options !== void 0) {
|
|
441
|
+
const fields = getCacheFields(this, identifier);
|
|
442
|
+
const graph = this.__graph;
|
|
443
|
+
const propertyNames = Object.keys(options);
|
|
444
|
+
for (let i = 0; i < propertyNames.length; i++) {
|
|
445
|
+
const name = propertyNames[i];
|
|
446
|
+
const propertyValue = options[name];
|
|
447
|
+
if (name === "id") continue;
|
|
448
|
+
const fieldType = fields.get(name);
|
|
449
|
+
const kind = fieldType !== void 0 ? "kind" in fieldType ? fieldType.kind : "attribute" : null;
|
|
450
|
+
let relationship;
|
|
451
|
+
switch (kind) {
|
|
452
|
+
case "attribute":
|
|
453
|
+
this.setAttr(identifier, name, propertyValue);
|
|
454
|
+
createOptions[name] = propertyValue;
|
|
455
|
+
break;
|
|
456
|
+
case "belongsTo":
|
|
457
|
+
this.mutate({
|
|
458
|
+
op: "replaceRelatedRecord",
|
|
459
|
+
field: name,
|
|
460
|
+
record: identifier,
|
|
461
|
+
value: propertyValue
|
|
462
|
+
});
|
|
463
|
+
relationship = graph.get(identifier, name);
|
|
464
|
+
relationship.state.hasReceivedData = true;
|
|
465
|
+
relationship.state.isEmpty = false;
|
|
466
|
+
break;
|
|
467
|
+
case "hasMany":
|
|
468
|
+
this.mutate({
|
|
469
|
+
op: "replaceRelatedRecords",
|
|
470
|
+
field: name,
|
|
471
|
+
record: identifier,
|
|
472
|
+
value: propertyValue
|
|
473
|
+
});
|
|
474
|
+
relationship = graph.get(identifier, name);
|
|
475
|
+
relationship.state.hasReceivedData = true;
|
|
476
|
+
relationship.state.isEmpty = false;
|
|
477
|
+
break;
|
|
478
|
+
default: createOptions[name] = propertyValue;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
this._capabilities.notifyChange(identifier, "added", null);
|
|
483
|
+
return createOptions;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* [LIFECYCLE] Signals to the cache that a resource
|
|
487
|
+
* will be part of a save transaction.
|
|
488
|
+
*
|
|
489
|
+
* @category Resource Lifecycle
|
|
490
|
+
* @public
|
|
491
|
+
*/
|
|
492
|
+
willCommit(identifier, _context) {
|
|
493
|
+
if (Array.isArray(identifier)) for (const key of identifier) willCommit(this, key);
|
|
494
|
+
else willCommit(this, identifier);
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* [LIFECYCLE] Signals to the cache that a resource
|
|
498
|
+
* was successfully updated as part of a save transaction.
|
|
499
|
+
*
|
|
500
|
+
* @category Resource Lifecycle
|
|
501
|
+
* @public
|
|
502
|
+
*/
|
|
503
|
+
didCommit(committedIdentifier, result) {
|
|
504
|
+
const payload = result ? result.content : null;
|
|
505
|
+
const operation = result?.request?.op ?? null;
|
|
506
|
+
const data = payload && payload.data;
|
|
507
|
+
const responseIsCollection = Array.isArray(data);
|
|
508
|
+
const hasMultipleIdentifiers = Array.isArray(committedIdentifier) && committedIdentifier.length > 1;
|
|
509
|
+
if (Array.isArray(committedIdentifier)) {
|
|
510
|
+
if (responseIsCollection) for (let i = 0; i < committedIdentifier.length; i++) {
|
|
511
|
+
const identifier = committedIdentifier[i];
|
|
512
|
+
didCommit(this, identifier, data[i] ?? null, operation);
|
|
513
|
+
}
|
|
514
|
+
else for (let i = 0; i < committedIdentifier.length; i++) {
|
|
515
|
+
const identifier = committedIdentifier[i];
|
|
516
|
+
didCommit(this, identifier, i === 0 ? data : null, operation);
|
|
517
|
+
}
|
|
518
|
+
} else didCommit(this, committedIdentifier, data, operation);
|
|
519
|
+
const included = payload && payload.included;
|
|
520
|
+
const { cacheKeyManager } = this._capabilities;
|
|
521
|
+
if (included) for (let i = 0, length = included.length; i < length; i++) putOne(this, cacheKeyManager, included[i]);
|
|
522
|
+
return hasMultipleIdentifiers && responseIsCollection ? { data: committedIdentifier } : { data: Array.isArray(committedIdentifier) ? committedIdentifier[0] : committedIdentifier };
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* [LIFECYCLE] Signals to the cache that a resource
|
|
526
|
+
* was update via a save transaction failed.
|
|
527
|
+
*
|
|
528
|
+
* @category Resource Lifecycle
|
|
529
|
+
* @public
|
|
530
|
+
*/
|
|
531
|
+
commitWasRejected(identifier, errors) {
|
|
532
|
+
if (Array.isArray(identifier)) {
|
|
533
|
+
for (let i = 0; i < identifier.length; i++) commitDidError(this, identifier[i], errors && i === 0 ? errors : null);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
return commitDidError(this, identifier, errors || null);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* [LIFECYCLE] Signals to the cache that all data for a resource
|
|
540
|
+
* should be cleared.
|
|
541
|
+
*
|
|
542
|
+
* This method is a candidate to become a mutation
|
|
543
|
+
*
|
|
544
|
+
* @category Resource Lifecycle
|
|
545
|
+
* @public
|
|
546
|
+
*/
|
|
547
|
+
unloadRecord(identifier) {
|
|
548
|
+
const storeWrapper = this._capabilities;
|
|
549
|
+
if (!this.__cache.has(identifier)) {
|
|
550
|
+
peekGraph(storeWrapper)?.unload(identifier);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const removeFromRecordArray = !this.isDeletionCommitted(identifier);
|
|
554
|
+
let removed = false;
|
|
555
|
+
const cached = this.__peek(identifier, false);
|
|
556
|
+
if (cached.isNew || cached.isDeletionCommitted) peekGraph(storeWrapper)?.push({
|
|
557
|
+
op: "deleteRecord",
|
|
558
|
+
record: identifier,
|
|
559
|
+
isNew: cached.isNew
|
|
560
|
+
});
|
|
561
|
+
else peekGraph(storeWrapper)?.unload(identifier);
|
|
562
|
+
cached.localAttrs = null;
|
|
563
|
+
cached.remoteAttrs = null;
|
|
564
|
+
cached.defaultAttrs = null;
|
|
565
|
+
cached.inflightAttrs = null;
|
|
566
|
+
const relatedIdentifiers = _allRelatedIdentifiers(storeWrapper, identifier);
|
|
567
|
+
if (areAllModelsUnloaded(storeWrapper, relatedIdentifiers)) for (let i = 0; i < relatedIdentifiers.length; ++i) {
|
|
568
|
+
const relatedIdentifier = relatedIdentifiers[i];
|
|
569
|
+
storeWrapper.notifyChange(relatedIdentifier, "removed", null);
|
|
570
|
+
removed = true;
|
|
571
|
+
storeWrapper.disconnectRecord(relatedIdentifier);
|
|
572
|
+
}
|
|
573
|
+
this.__cache.delete(identifier);
|
|
574
|
+
this.__destroyedCache.set(identifier, cached);
|
|
575
|
+
if (this.__destroyedCache.size === 1) setTimeout(() => {
|
|
576
|
+
this.__destroyedCache.clear();
|
|
577
|
+
}, 100);
|
|
578
|
+
if (!removed && removeFromRecordArray) storeWrapper.notifyChange(identifier, "removed", null);
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Retrieve the data for an attribute from the cache
|
|
582
|
+
* with local mutations applied.
|
|
583
|
+
*
|
|
584
|
+
* @category Resource Data
|
|
585
|
+
* @public
|
|
586
|
+
*/
|
|
587
|
+
getAttr(identifier, attr) {
|
|
588
|
+
const isSimplePath = !Array.isArray(attr) || attr.length === 1;
|
|
589
|
+
if (Array.isArray(attr) && attr.length === 1) attr = attr[0];
|
|
590
|
+
if (isSimplePath) {
|
|
591
|
+
const attribute = attr;
|
|
592
|
+
const cached = this.__peek(identifier, true);
|
|
593
|
+
if (!cached) return;
|
|
594
|
+
if (cached.localAttrs && attribute in cached.localAttrs) return cached.localAttrs[attribute];
|
|
595
|
+
else if (cached.inflightAttrs && attribute in cached.inflightAttrs) return cached.inflightAttrs[attribute];
|
|
596
|
+
else if (cached.remoteAttrs && attribute in cached.remoteAttrs) return cached.remoteAttrs[attribute];
|
|
597
|
+
else if (cached.defaultAttrs && attribute in cached.defaultAttrs) return cached.defaultAttrs[attribute];
|
|
598
|
+
else {
|
|
599
|
+
const attrSchema = getCacheFields(this, identifier).get(attribute);
|
|
600
|
+
assertPrivateCapabilities(this._capabilities);
|
|
601
|
+
const defaultValue = getDefaultValue(attrSchema, identifier, this._capabilities._store);
|
|
602
|
+
if (schemaHasLegacyDefaultValueFn(attrSchema)) {
|
|
603
|
+
cached.defaultAttrs = cached.defaultAttrs || Object.create(null);
|
|
604
|
+
cached.defaultAttrs[attribute] = defaultValue;
|
|
605
|
+
}
|
|
606
|
+
return defaultValue;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
const path = attr;
|
|
610
|
+
const cached = this.__peek(identifier, true);
|
|
611
|
+
const basePath = path[0];
|
|
612
|
+
let current = cached.localAttrs && basePath in cached.localAttrs ? cached.localAttrs[basePath] : void 0;
|
|
613
|
+
if (current === void 0) current = cached.inflightAttrs && basePath in cached.inflightAttrs ? cached.inflightAttrs[basePath] : void 0;
|
|
614
|
+
if (current === void 0) current = cached.remoteAttrs && basePath in cached.remoteAttrs ? cached.remoteAttrs[basePath] : void 0;
|
|
615
|
+
if (current === void 0) return;
|
|
616
|
+
for (let i = 1; i < path.length; i++) {
|
|
617
|
+
current = current[path[i]];
|
|
618
|
+
if (current === void 0) return;
|
|
619
|
+
}
|
|
620
|
+
return current;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Retrieve the remote data for an attribute from the cache
|
|
624
|
+
*
|
|
625
|
+
* @category Resource Data
|
|
626
|
+
* @public
|
|
627
|
+
*/
|
|
628
|
+
getRemoteAttr(identifier, attr) {
|
|
629
|
+
const isSimplePath = !Array.isArray(attr) || attr.length === 1;
|
|
630
|
+
if (Array.isArray(attr) && attr.length === 1) attr = attr[0];
|
|
631
|
+
if (isSimplePath) {
|
|
632
|
+
const attribute = attr;
|
|
633
|
+
const cached = this.__peek(identifier, true);
|
|
634
|
+
if (!cached) return;
|
|
635
|
+
if (cached.remoteAttrs && attribute in cached.remoteAttrs) return cached.remoteAttrs[attribute];
|
|
636
|
+
else if (cached.defaultAttrs && attribute in cached.defaultAttrs) return cached.defaultAttrs[attribute];
|
|
637
|
+
else {
|
|
638
|
+
const attrSchema = getCacheFields(this, identifier).get(attribute);
|
|
639
|
+
assertPrivateCapabilities(this._capabilities);
|
|
640
|
+
const defaultValue = getDefaultValue(attrSchema, identifier, this._capabilities._store);
|
|
641
|
+
if (schemaHasLegacyDefaultValueFn(attrSchema)) {
|
|
642
|
+
cached.defaultAttrs = cached.defaultAttrs || Object.create(null);
|
|
643
|
+
cached.defaultAttrs[attribute] = defaultValue;
|
|
644
|
+
}
|
|
645
|
+
return defaultValue;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const path = attr;
|
|
649
|
+
const cached = this.__peek(identifier, true);
|
|
650
|
+
const basePath = path[0];
|
|
651
|
+
let current = cached.remoteAttrs && basePath in cached.remoteAttrs ? cached.remoteAttrs[basePath] : void 0;
|
|
652
|
+
if (current === void 0) return;
|
|
653
|
+
for (let i = 1; i < path.length; i++) {
|
|
654
|
+
current = current[path[i]];
|
|
655
|
+
if (current === void 0) return;
|
|
656
|
+
}
|
|
657
|
+
return current;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Mutate the data for an attribute in the cache
|
|
661
|
+
*
|
|
662
|
+
* This method is a candidate to become a mutation
|
|
663
|
+
*
|
|
664
|
+
* @category Resource Data
|
|
665
|
+
* @public
|
|
666
|
+
*/
|
|
667
|
+
setAttr(identifier, attr, value) {
|
|
668
|
+
const isSimplePath = !Array.isArray(attr) || attr.length === 1;
|
|
669
|
+
if (Array.isArray(attr) && attr.length === 1) attr = attr[0];
|
|
670
|
+
if (isSimplePath) {
|
|
671
|
+
const cached = this.__peek(identifier, false);
|
|
672
|
+
const currentAttr = attr;
|
|
673
|
+
const existing = cached.inflightAttrs && currentAttr in cached.inflightAttrs ? cached.inflightAttrs[currentAttr] : cached.remoteAttrs && currentAttr in cached.remoteAttrs ? cached.remoteAttrs[currentAttr] : void 0;
|
|
674
|
+
if (existing !== value) {
|
|
675
|
+
cached.localAttrs = cached.localAttrs || Object.create(null);
|
|
676
|
+
cached.localAttrs[currentAttr] = value;
|
|
677
|
+
cached.changes = cached.changes || Object.create(null);
|
|
678
|
+
cached.changes[currentAttr] = [existing, value];
|
|
679
|
+
} else if (cached.localAttrs) {
|
|
680
|
+
delete cached.localAttrs[currentAttr];
|
|
681
|
+
delete cached.changes[currentAttr];
|
|
682
|
+
}
|
|
683
|
+
if (cached.defaultAttrs && currentAttr in cached.defaultAttrs) delete cached.defaultAttrs[currentAttr];
|
|
684
|
+
this._capabilities.notifyChange(identifier, "attributes", currentAttr, "local");
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
const path = attr;
|
|
688
|
+
const cached = this.__peek(identifier, false);
|
|
689
|
+
const basePath = path[0];
|
|
690
|
+
const existing = cached.inflightAttrs && basePath in cached.inflightAttrs ? cached.inflightAttrs[basePath] : cached.remoteAttrs && basePath in cached.remoteAttrs ? cached.remoteAttrs[basePath] : void 0;
|
|
691
|
+
let existingAttr;
|
|
692
|
+
if (existing) {
|
|
693
|
+
existingAttr = existing[path[1]];
|
|
694
|
+
for (let i = 2; i < path.length; i++) existingAttr = existingAttr[path[i]];
|
|
695
|
+
}
|
|
696
|
+
if (existingAttr !== value) {
|
|
697
|
+
cached.localAttrs = cached.localAttrs || Object.create(null);
|
|
698
|
+
cached.localAttrs[basePath] = cached.localAttrs[basePath] || structuredClone(existing);
|
|
699
|
+
cached.changes = cached.changes || Object.create(null);
|
|
700
|
+
let currentLocal = cached.localAttrs[basePath];
|
|
701
|
+
let nextLink = 1;
|
|
702
|
+
while (nextLink < path.length - 1) currentLocal = currentLocal[path[nextLink++]];
|
|
703
|
+
currentLocal[path[nextLink]] = value;
|
|
704
|
+
cached.changes[basePath] = [existing, cached.localAttrs[basePath]];
|
|
705
|
+
} else if (cached.localAttrs) try {
|
|
706
|
+
if (!existing) return;
|
|
707
|
+
if (JSON.stringify(existing) !== JSON.stringify(cached.localAttrs[basePath])) {
|
|
708
|
+
delete cached.localAttrs[basePath];
|
|
709
|
+
delete cached.changes[basePath];
|
|
710
|
+
}
|
|
711
|
+
} catch {}
|
|
712
|
+
this._capabilities.notifyChange(identifier, "attributes", basePath, "local");
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Query the cache for the changed attributes of a resource.
|
|
716
|
+
*
|
|
717
|
+
* @category Resource Data
|
|
718
|
+
* @public
|
|
719
|
+
* @return `{ '<field>': ['<old>', '<new>'] }`
|
|
720
|
+
*/
|
|
721
|
+
changedAttrs(identifier) {
|
|
722
|
+
const cached = this.__peek(identifier, false);
|
|
723
|
+
if (!cached) return Object.create(null);
|
|
724
|
+
return cached.changes || Object.create(null);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Query the cache for whether any mutated attributes exist
|
|
728
|
+
*
|
|
729
|
+
* @category Resource Data
|
|
730
|
+
* @public
|
|
731
|
+
*/
|
|
732
|
+
hasChangedAttrs(identifier) {
|
|
733
|
+
const cached = this.__peek(identifier, true);
|
|
734
|
+
if (!cached) return false;
|
|
735
|
+
return cached.inflightAttrs !== null && Object.keys(cached.inflightAttrs).length > 0 || cached.localAttrs !== null && Object.keys(cached.localAttrs).length > 0;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Tell the cache to discard any uncommitted mutations to attributes
|
|
739
|
+
*
|
|
740
|
+
* This method is a candidate to become a mutation
|
|
741
|
+
*
|
|
742
|
+
* @category Resource Data
|
|
743
|
+
* @public
|
|
744
|
+
* @return the names of fields that were restored
|
|
745
|
+
*/
|
|
746
|
+
rollbackAttrs(identifier) {
|
|
747
|
+
const cached = this.__peek(identifier, false);
|
|
748
|
+
let dirtyKeys;
|
|
749
|
+
cached.isDeleted = false;
|
|
750
|
+
if (cached.localAttrs !== null) {
|
|
751
|
+
dirtyKeys = Object.keys(cached.localAttrs);
|
|
752
|
+
cached.localAttrs = null;
|
|
753
|
+
cached.changes = null;
|
|
754
|
+
}
|
|
755
|
+
if (cached.isNew) {
|
|
756
|
+
cached.isDeletionCommitted = true;
|
|
757
|
+
cached.isDeleted = true;
|
|
758
|
+
cached.isNew = false;
|
|
759
|
+
}
|
|
760
|
+
cached.inflightAttrs = null;
|
|
761
|
+
cached.defaultAttrs = null;
|
|
762
|
+
if (cached.errors) {
|
|
763
|
+
cached.errors = null;
|
|
764
|
+
this._capabilities.notifyChange(identifier, "errors", null);
|
|
765
|
+
}
|
|
766
|
+
this._capabilities.notifyChange(identifier, "state", null);
|
|
767
|
+
if (dirtyKeys && dirtyKeys.length) this._capabilities.notifyChange(identifier, "attributes", new Set(dirtyKeys));
|
|
768
|
+
return dirtyKeys || [];
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Query the cache for the changes to relationships of a resource.
|
|
772
|
+
*
|
|
773
|
+
* Returns a map of relationship names to RelationshipDiff objects.
|
|
774
|
+
*
|
|
775
|
+
* ```ts
|
|
776
|
+
* type RelationshipDiff =
|
|
777
|
+
| {
|
|
778
|
+
kind: 'collection';
|
|
779
|
+
remoteState: ResourceKey[];
|
|
780
|
+
additions: Set<ResourceKey>;
|
|
781
|
+
removals: Set<ResourceKey>;
|
|
782
|
+
localState: ResourceKey[];
|
|
783
|
+
reordered: boolean;
|
|
784
|
+
}
|
|
785
|
+
| {
|
|
786
|
+
kind: 'resource';
|
|
787
|
+
remoteState: ResourceKey | null;
|
|
788
|
+
localState: ResourceKey | null;
|
|
789
|
+
};
|
|
790
|
+
```
|
|
791
|
+
*
|
|
792
|
+
* @category Resource Data
|
|
793
|
+
* @public
|
|
794
|
+
*/
|
|
795
|
+
changedRelationships(identifier) {
|
|
796
|
+
return this.__graph.getChanged(identifier);
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Query the cache for whether any mutated relationships exist
|
|
800
|
+
*
|
|
801
|
+
* @category Resource Data
|
|
802
|
+
* @public
|
|
803
|
+
*/
|
|
804
|
+
hasChangedRelationships(identifier) {
|
|
805
|
+
return this.__graph.hasChanged(identifier);
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Tell the cache to discard any uncommitted mutations to relationships.
|
|
809
|
+
*
|
|
810
|
+
* This will also discard the change on any appropriate inverses.
|
|
811
|
+
*
|
|
812
|
+
* This method is a candidate to become a mutation
|
|
813
|
+
*
|
|
814
|
+
* @category Resource Data
|
|
815
|
+
* @public
|
|
816
|
+
* @return the names of relationships that were restored
|
|
817
|
+
*/
|
|
818
|
+
rollbackRelationships(identifier) {
|
|
819
|
+
assertPrivateCapabilities(this._capabilities);
|
|
820
|
+
let result;
|
|
821
|
+
this._capabilities._store._join(() => {
|
|
822
|
+
result = this.__graph.rollback(identifier);
|
|
823
|
+
});
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Query the cache for the current state of a relationship property
|
|
828
|
+
*
|
|
829
|
+
* @category Resource Data
|
|
830
|
+
* @public
|
|
831
|
+
* @return resource relationship object
|
|
832
|
+
*/
|
|
833
|
+
getRelationship(identifier, field) {
|
|
834
|
+
return this.__graph.getData(identifier, field);
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Query the cache for the remote state of a relationship property
|
|
838
|
+
*
|
|
839
|
+
* @category Resource Data
|
|
840
|
+
* @public
|
|
841
|
+
* @return resource relationship object
|
|
842
|
+
*/
|
|
843
|
+
getRemoteRelationship(identifier, field) {
|
|
844
|
+
return this.__graph.getRemoteData(identifier, field);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Update the cache state for the given resource to be marked
|
|
848
|
+
* as locally deleted, or remove such a mark.
|
|
849
|
+
*
|
|
850
|
+
* This method is a candidate to become a mutation
|
|
851
|
+
*
|
|
852
|
+
* @category Resource State
|
|
853
|
+
* @public
|
|
854
|
+
*/
|
|
855
|
+
setIsDeleted(identifier, isDeleted) {
|
|
856
|
+
const cached = this.__peek(identifier, false);
|
|
857
|
+
cached.isDeleted = isDeleted;
|
|
858
|
+
this._capabilities.notifyChange(identifier, "state", null);
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Query the cache for any validation errors applicable to the given resource.
|
|
862
|
+
*
|
|
863
|
+
* @category Resource State
|
|
864
|
+
* @public
|
|
865
|
+
*/
|
|
866
|
+
getErrors(identifier) {
|
|
867
|
+
return this.__peek(identifier, true).errors || [];
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Query the cache for whether a given resource has any available data
|
|
871
|
+
*
|
|
872
|
+
* @category Resource State
|
|
873
|
+
* @public
|
|
874
|
+
*/
|
|
875
|
+
isEmpty(identifier) {
|
|
876
|
+
const cached = this.__safePeek(identifier, true);
|
|
877
|
+
return cached ? cached.remoteAttrs === null && cached.inflightAttrs === null && cached.localAttrs === null : true;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Query the cache for whether a given resource was created locally and not
|
|
881
|
+
* yet persisted.
|
|
882
|
+
*
|
|
883
|
+
* @category Resource State
|
|
884
|
+
* @public
|
|
885
|
+
*/
|
|
886
|
+
isNew(identifier) {
|
|
887
|
+
return this.__safePeek(identifier, true)?.isNew || false;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Query the cache for whether a given resource is marked as deleted (but not
|
|
891
|
+
* necessarily persisted yet).
|
|
892
|
+
*
|
|
893
|
+
* @category Resource State
|
|
894
|
+
* @public
|
|
895
|
+
*/
|
|
896
|
+
isDeleted(identifier) {
|
|
897
|
+
return this.__safePeek(identifier, true)?.isDeleted || false;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Query the cache for whether a given resource has been deleted and that deletion
|
|
901
|
+
* has also been persisted.
|
|
902
|
+
*
|
|
903
|
+
* @category Resource State
|
|
904
|
+
* @public
|
|
905
|
+
*/
|
|
906
|
+
isDeletionCommitted(identifier) {
|
|
907
|
+
return this.__safePeek(identifier, true)?.isDeletionCommitted || false;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Private method used to populate an entry for the identifier
|
|
911
|
+
*
|
|
912
|
+
* @internal
|
|
913
|
+
*/
|
|
914
|
+
_createCache(identifier) {
|
|
915
|
+
const cache = makeCache();
|
|
916
|
+
this.__cache.set(identifier, cache);
|
|
917
|
+
return cache;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Peek whether we have cached resource data matching the identifier
|
|
921
|
+
* without asserting if the resource data is missing.
|
|
922
|
+
*
|
|
923
|
+
* @internal
|
|
924
|
+
*/
|
|
925
|
+
__safePeek(identifier, allowDestroyed) {
|
|
926
|
+
let resource = this.__cache.get(identifier);
|
|
927
|
+
if (!resource && allowDestroyed) resource = this.__destroyedCache.get(identifier);
|
|
928
|
+
return resource;
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Peek whether we have cached resource data matching the identifier
|
|
932
|
+
* Asserts if the resource data is missing.
|
|
933
|
+
*
|
|
934
|
+
* @internal
|
|
935
|
+
*/
|
|
936
|
+
__peek(identifier, allowDestroyed) {
|
|
937
|
+
return this.__safePeek(identifier, allowDestroyed);
|
|
938
|
+
}
|
|
939
|
+
};
|
|
1238
940
|
function addResourceToDocument(cache, op) {
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
} else {
|
|
1267
|
-
// for collections, because we allow duplicates we are always changed.
|
|
1268
|
-
shouldNotify = true;
|
|
1269
|
-
content.data.push(op.value);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
// notify
|
|
1275
|
-
if (shouldNotify) cache._capabilities.notifyChange(op.record, 'updated', null);
|
|
1276
|
-
return;
|
|
1277
|
-
}
|
|
1278
|
-
content.included = content.included || [];
|
|
1279
|
-
if (Array.isArray(op.value)) {
|
|
1280
|
-
content.included = content.included.concat(op.value);
|
|
1281
|
-
} else {
|
|
1282
|
-
content.included.push(op.value);
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
// we don't notify in the included case because this is not reactively
|
|
1286
|
-
// exposed. We should possibly consider doing so though for subscribers
|
|
941
|
+
const { content } = cache.__documents.get(op.record.lid);
|
|
942
|
+
if (op.field === "data") {
|
|
943
|
+
let shouldNotify;
|
|
944
|
+
if (!Array.isArray(content.data)) {
|
|
945
|
+
shouldNotify = content.data !== op.value;
|
|
946
|
+
if (shouldNotify) content.data = op.value;
|
|
947
|
+
} else if (Array.isArray(op.value)) {
|
|
948
|
+
if (op.index !== void 0) {
|
|
949
|
+
shouldNotify = true;
|
|
950
|
+
content.data.splice(op.index, 0, ...op.value);
|
|
951
|
+
} else {
|
|
952
|
+
shouldNotify = true;
|
|
953
|
+
content.data.push(...op.value);
|
|
954
|
+
}
|
|
955
|
+
} else if (op.index !== void 0) {
|
|
956
|
+
shouldNotify = true;
|
|
957
|
+
content.data.splice(op.index, 0, op.value);
|
|
958
|
+
} else {
|
|
959
|
+
shouldNotify = true;
|
|
960
|
+
content.data.push(op.value);
|
|
961
|
+
}
|
|
962
|
+
if (shouldNotify) cache._capabilities.notifyChange(op.record, "updated", null);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
content.included = content.included || [];
|
|
966
|
+
if (Array.isArray(op.value)) content.included = content.included.concat(op.value);
|
|
967
|
+
else content.included.push(op.value);
|
|
1287
968
|
}
|
|
1288
969
|
function removeResourceFromDocument(cache, op) {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
// notify
|
|
1326
|
-
if (shouldNotify) cache._capabilities.notifyChange(op.record, 'updated', null);
|
|
1327
|
-
} else {
|
|
1328
|
-
content.included = content.included || [];
|
|
1329
|
-
const toRemove = Array.isArray(op.value) ? op.value : [op.value];
|
|
1330
|
-
for (const identifier of toRemove) {
|
|
1331
|
-
const index = content.included.indexOf(identifier);
|
|
1332
|
-
if (index !== -1) {
|
|
1333
|
-
content.included.splice(index, 1);
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
// we don't notify in the included case because this is not reactively
|
|
1338
|
-
// exposed. We should possibly consider doing so though for subscribers
|
|
1339
|
-
}
|
|
970
|
+
const { content } = cache.__documents.get(op.record.lid);
|
|
971
|
+
if (op.field === "data") {
|
|
972
|
+
let shouldNotify = false;
|
|
973
|
+
if (!Array.isArray(content.data)) {
|
|
974
|
+
shouldNotify = content.data === op.value;
|
|
975
|
+
if (shouldNotify) content.data = null;
|
|
976
|
+
} else {
|
|
977
|
+
const toRemove = Array.isArray(op.value) ? op.value : [op.value];
|
|
978
|
+
for (let i = 0; i < toRemove.length; i++) {
|
|
979
|
+
const value = toRemove[i];
|
|
980
|
+
if (op.index !== void 0) {
|
|
981
|
+
const index = op.index < content.data.length && content.data[op.index] === value ? op.index : content.data.indexOf(value);
|
|
982
|
+
if (index !== -1) {
|
|
983
|
+
shouldNotify = true;
|
|
984
|
+
content.data.splice(index, 1);
|
|
985
|
+
}
|
|
986
|
+
} else {
|
|
987
|
+
const index = content.data.indexOf(value);
|
|
988
|
+
if (index !== -1) {
|
|
989
|
+
shouldNotify = true;
|
|
990
|
+
content.data.splice(index, 1);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (shouldNotify) cache._capabilities.notifyChange(op.record, "updated", null);
|
|
996
|
+
} else {
|
|
997
|
+
content.included = content.included || [];
|
|
998
|
+
const toRemove = Array.isArray(op.value) ? op.value : [op.value];
|
|
999
|
+
for (const identifier of toRemove) {
|
|
1000
|
+
const index = content.included.indexOf(identifier);
|
|
1001
|
+
if (index !== -1) content.included.splice(index, 1);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1340
1004
|
}
|
|
1341
1005
|
function areAllModelsUnloaded(wrapper, identifiers) {
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
}
|
|
1348
|
-
return true;
|
|
1006
|
+
for (let i = 0; i < identifiers.length; ++i) {
|
|
1007
|
+
const identifier = identifiers[i];
|
|
1008
|
+
if (wrapper.hasRecord(identifier)) return false;
|
|
1009
|
+
}
|
|
1010
|
+
return true;
|
|
1349
1011
|
}
|
|
1350
1012
|
function getLocalState(rel) {
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
}
|
|
1354
|
-
return rel.additions ? [...rel.additions] : [];
|
|
1013
|
+
if (isBelongsTo(rel)) return rel.localState ? [rel.localState] : [];
|
|
1014
|
+
return rel.additions ? [...rel.additions] : [];
|
|
1355
1015
|
}
|
|
1356
1016
|
function getRemoteState(rel) {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
}
|
|
1360
|
-
return rel.remoteState;
|
|
1017
|
+
if (isBelongsTo(rel)) return rel.remoteState ? [rel.remoteState] : [];
|
|
1018
|
+
return rel.remoteState;
|
|
1361
1019
|
}
|
|
1362
1020
|
function schemaHasLegacyDefaultValueFn(schema) {
|
|
1363
|
-
|
|
1364
|
-
|
|
1021
|
+
if (!schema) return false;
|
|
1022
|
+
return hasLegacyDefaultValueFn(schema.options);
|
|
1365
1023
|
}
|
|
1366
1024
|
function hasLegacyDefaultValueFn(options) {
|
|
1367
|
-
|
|
1025
|
+
return !!options && typeof options.defaultValue === "function";
|
|
1368
1026
|
}
|
|
1369
1027
|
function getDefaultValue(schema, identifier, store) {
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
if (hasLegacyDefaultValueFn(options)) {
|
|
1380
|
-
// If anyone opens an issue for args not working right, we'll restore + deprecate it via a Proxy
|
|
1381
|
-
// that lazily instantiates the record. We don't want to provide any args here
|
|
1382
|
-
// because in a non @ember-data/model world they don't make sense.
|
|
1383
|
-
return options.defaultValue();
|
|
1384
|
-
// legacy support for defaultValues that are primitives
|
|
1385
|
-
} else if (options && 'defaultValue' in options) {
|
|
1386
|
-
const defaultValue = options.defaultValue;
|
|
1387
|
-
return defaultValue;
|
|
1388
|
-
|
|
1389
|
-
// new style transforms
|
|
1390
|
-
} else if (schema.kind !== 'attribute' && schema.type) {
|
|
1391
|
-
const transform = store.schema.transformation(schema);
|
|
1392
|
-
if (transform?.defaultValue) {
|
|
1393
|
-
return transform.defaultValue(options || null, identifier);
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
function notifyAttributes(storeWrapper, identifier, keys) {
|
|
1398
|
-
if (!keys) {
|
|
1399
|
-
storeWrapper.notifyChange(identifier, 'attributes', null);
|
|
1400
|
-
return;
|
|
1401
|
-
}
|
|
1402
|
-
for (const key of keys) {
|
|
1403
|
-
storeWrapper.notifyChange(identifier, 'attributes', key);
|
|
1404
|
-
}
|
|
1028
|
+
const options = schema?.options;
|
|
1029
|
+
if (!schema || !options && !schema.type) return;
|
|
1030
|
+
if (schema.kind !== "attribute" && schema.kind !== "field") return;
|
|
1031
|
+
if (hasLegacyDefaultValueFn(options)) return options.defaultValue();
|
|
1032
|
+
else if (options && "defaultValue" in options) return options.defaultValue;
|
|
1033
|
+
else if (schema.kind !== "attribute" && schema.type) {
|
|
1034
|
+
const transform = store.schema.transformation(schema);
|
|
1035
|
+
if (transform?.defaultValue) return transform.defaultValue(options || null, identifier);
|
|
1036
|
+
}
|
|
1405
1037
|
}
|
|
1406
|
-
|
|
1407
|
-
/*
|
|
1408
|
-
TODO @deprecate IGOR DAVID
|
|
1409
|
-
There seems to be a potential bug here, where we will return keys that are not
|
|
1410
|
-
in the schema
|
|
1411
|
-
*/
|
|
1412
1038
|
function calculateChangedKeys(cached, updates, fields) {
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
// this attribute. We never override this value when merging
|
|
1427
|
-
// updates from the backend so we should not sent a change
|
|
1428
|
-
// notification if the server value differs from the original.
|
|
1429
|
-
if (localAttrs && localAttrs[key] !== undefined) {
|
|
1430
|
-
continue;
|
|
1431
|
-
}
|
|
1432
|
-
if (original[key] !== value) {
|
|
1433
|
-
changedKeys.add(key);
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
return changedKeys;
|
|
1039
|
+
const changedKeys = /* @__PURE__ */ new Set();
|
|
1040
|
+
const keys = Object.keys(updates);
|
|
1041
|
+
const length = keys.length;
|
|
1042
|
+
const localAttrs = cached.localAttrs;
|
|
1043
|
+
const original = Object.assign(Object.create(null), cached.remoteAttrs, cached.inflightAttrs);
|
|
1044
|
+
for (let i = 0; i < length; i++) {
|
|
1045
|
+
const key = keys[i];
|
|
1046
|
+
if (!fields.has(key)) continue;
|
|
1047
|
+
const value = updates[key];
|
|
1048
|
+
if (localAttrs && localAttrs[key] !== void 0) continue;
|
|
1049
|
+
if (original[key] !== value) changedKeys.add(key);
|
|
1050
|
+
}
|
|
1051
|
+
return changedKeys;
|
|
1437
1052
|
}
|
|
1438
1053
|
function cacheIsEmpty(cached) {
|
|
1439
|
-
|
|
1054
|
+
return !cached || cached.remoteAttrs === null && cached.inflightAttrs === null && cached.localAttrs === null;
|
|
1440
1055
|
}
|
|
1441
1056
|
function _isEmpty(peeked) {
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
const isEmpty = cacheIsEmpty(peeked);
|
|
1448
|
-
return (!isNew || isDeleted) && isEmpty;
|
|
1057
|
+
if (!peeked) return true;
|
|
1058
|
+
const isNew = peeked.isNew;
|
|
1059
|
+
const isDeleted = peeked.isDeleted;
|
|
1060
|
+
const isEmpty = cacheIsEmpty(peeked);
|
|
1061
|
+
return (!isNew || isDeleted) && isEmpty;
|
|
1449
1062
|
}
|
|
1450
1063
|
function recordIsLoaded(cached, filterDeleted = false) {
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
// if we are new we must consider ourselves loaded
|
|
1458
|
-
if (isNew) {
|
|
1459
|
-
return !cached.isDeleted;
|
|
1460
|
-
}
|
|
1461
|
-
// even if we have a past request, if we are now empty we are not loaded
|
|
1462
|
-
// typically this is true after an unloadRecord call
|
|
1463
|
-
|
|
1464
|
-
// if we are not empty, not new && we have a fulfilled request then we are loaded
|
|
1465
|
-
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
1466
|
-
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
1467
|
-
// in that it does not account for unloading.
|
|
1468
|
-
return filterDeleted && cached.isDeletionCommitted ? false : !isEmpty;
|
|
1064
|
+
if (!cached) return false;
|
|
1065
|
+
const isNew = cached.isNew;
|
|
1066
|
+
const isEmpty = cacheIsEmpty(cached);
|
|
1067
|
+
if (isNew) return !cached.isDeleted;
|
|
1068
|
+
return filterDeleted && cached.isDeletionCommitted ? false : !isEmpty;
|
|
1469
1069
|
}
|
|
1470
1070
|
function _isLoading(peeked, capabilities, identifier) {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
const req = capabilities._store.getRequestStateService();
|
|
1475
|
-
// const fulfilled = req.getLastRequestForRecord(identifier);
|
|
1476
|
-
const isLoaded = recordIsLoaded(peeked);
|
|
1477
|
-
return !isLoaded &&
|
|
1478
|
-
// fulfilled === null &&
|
|
1479
|
-
req.getPendingRequestsForRecord(identifier).some(r => r.type === 'query');
|
|
1071
|
+
assertPrivateCapabilities(capabilities);
|
|
1072
|
+
const req = capabilities._store.getRequestStateService();
|
|
1073
|
+
return !recordIsLoaded(peeked) && req.getPendingRequestsForRecord(identifier).some((r) => r.type === "query");
|
|
1480
1074
|
}
|
|
1481
1075
|
function setupRelationships(graph, fields, identifier, data) {
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1076
|
+
for (const name in data.relationships) {
|
|
1077
|
+
const relationshipData = data.relationships[name];
|
|
1078
|
+
const field = fields.get(name);
|
|
1079
|
+
if (!relationshipData || !field || !isRelationship(field)) continue;
|
|
1080
|
+
graph.push({
|
|
1081
|
+
op: "updateRelationship",
|
|
1082
|
+
record: identifier,
|
|
1083
|
+
field: name,
|
|
1084
|
+
value: relationshipData
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1495
1087
|
}
|
|
1496
1088
|
function isRelationship(field) {
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
} = field;
|
|
1500
|
-
return kind === 'hasMany' || kind === 'belongsTo' || kind === 'resource' || kind === 'collection';
|
|
1089
|
+
const { kind } = field;
|
|
1090
|
+
return kind === "hasMany" || kind === "belongsTo" || kind === "resource" || kind === "collection";
|
|
1501
1091
|
}
|
|
1502
1092
|
function patchLocalAttributes(cached, changedRemoteKeys) {
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
// if the local change is committed, then
|
|
1523
|
-
// the remoteKeyChange is no longer relevant
|
|
1524
|
-
changedRemoteKeys?.delete(attr);
|
|
1525
|
-
delete localAttrs[attr];
|
|
1526
|
-
delete changes[attr];
|
|
1527
|
-
}
|
|
1528
|
-
if (defaultAttrs && attr in defaultAttrs) {
|
|
1529
|
-
delete defaultAttrs[attr];
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
return hasAppliedPatch;
|
|
1093
|
+
const { localAttrs, remoteAttrs, inflightAttrs, defaultAttrs, changes } = cached;
|
|
1094
|
+
if (!localAttrs) {
|
|
1095
|
+
cached.changes = null;
|
|
1096
|
+
return false;
|
|
1097
|
+
}
|
|
1098
|
+
let hasAppliedPatch = false;
|
|
1099
|
+
const mutatedKeys = Object.keys(localAttrs);
|
|
1100
|
+
for (let i = 0, length = mutatedKeys.length; i < length; i++) {
|
|
1101
|
+
const attr = mutatedKeys[i];
|
|
1102
|
+
if ((inflightAttrs && attr in inflightAttrs ? inflightAttrs[attr] : remoteAttrs && attr in remoteAttrs ? remoteAttrs[attr] : void 0) === localAttrs[attr]) {
|
|
1103
|
+
hasAppliedPatch = true;
|
|
1104
|
+
changedRemoteKeys?.delete(attr);
|
|
1105
|
+
delete localAttrs[attr];
|
|
1106
|
+
delete changes[attr];
|
|
1107
|
+
}
|
|
1108
|
+
if (defaultAttrs && attr in defaultAttrs) delete defaultAttrs[attr];
|
|
1109
|
+
}
|
|
1110
|
+
return hasAppliedPatch;
|
|
1533
1111
|
}
|
|
1534
1112
|
function putOne(cache, identifiers, resource) {
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
}
|
|
1541
|
-
cache.upsert(identifier, resource, cache._capabilities.hasRecord(identifier));
|
|
1542
|
-
// even if the identifier was not "existing" before, it is now
|
|
1543
|
-
return identifier;
|
|
1113
|
+
let identifier = identifiers.peekResourceKey(resource);
|
|
1114
|
+
if (identifier) identifier = identifiers.updateRecordIdentifier(identifier, resource);
|
|
1115
|
+
else identifier = identifiers.getOrCreateRecordIdentifier(resource);
|
|
1116
|
+
cache.upsert(identifier, resource, cache._capabilities.hasRecord(identifier));
|
|
1117
|
+
return identifier;
|
|
1544
1118
|
}
|
|
1545
|
-
|
|
1546
|
-
/*
|
|
1547
|
-
Iterates over the set of internal models reachable from `this` across exactly one
|
|
1548
|
-
relationship.
|
|
1549
|
-
*/
|
|
1550
1119
|
function _directlyRelatedIdentifiersIterable(storeWrapper, originating) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
iterator() {
|
|
1586
|
-
return {
|
|
1587
|
-
next: () => {
|
|
1588
|
-
const value = findNext();
|
|
1589
|
-
return {
|
|
1590
|
-
value,
|
|
1591
|
-
done: value === undefined
|
|
1592
|
-
};
|
|
1593
|
-
}
|
|
1594
|
-
};
|
|
1595
|
-
}
|
|
1596
|
-
};
|
|
1120
|
+
const initializedRelationships = peekGraph(storeWrapper)?.identifiers.get(originating);
|
|
1121
|
+
if (!initializedRelationships) return EMPTY_ITERATOR;
|
|
1122
|
+
const initializedRelationshipsArr = [];
|
|
1123
|
+
Object.keys(initializedRelationships).forEach((key) => {
|
|
1124
|
+
const rel = initializedRelationships[key];
|
|
1125
|
+
if (rel && !isImplicit(rel)) initializedRelationshipsArr.push(rel);
|
|
1126
|
+
});
|
|
1127
|
+
let i = 0;
|
|
1128
|
+
let j = 0;
|
|
1129
|
+
let k = 0;
|
|
1130
|
+
const findNext = () => {
|
|
1131
|
+
while (i < initializedRelationshipsArr.length) {
|
|
1132
|
+
while (j < 2) {
|
|
1133
|
+
const relatedIdentifiers = j === 0 ? getLocalState(initializedRelationshipsArr[i]) : getRemoteState(initializedRelationshipsArr[i]);
|
|
1134
|
+
while (k < relatedIdentifiers.length) {
|
|
1135
|
+
const relatedIdentifier = relatedIdentifiers[k++];
|
|
1136
|
+
if (relatedIdentifier !== null) return relatedIdentifier;
|
|
1137
|
+
}
|
|
1138
|
+
k = 0;
|
|
1139
|
+
j++;
|
|
1140
|
+
}
|
|
1141
|
+
j = 0;
|
|
1142
|
+
i++;
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
return { iterator() {
|
|
1146
|
+
return { next: () => {
|
|
1147
|
+
const value = findNext();
|
|
1148
|
+
return {
|
|
1149
|
+
value,
|
|
1150
|
+
done: value === void 0
|
|
1151
|
+
};
|
|
1152
|
+
} };
|
|
1153
|
+
} };
|
|
1597
1154
|
}
|
|
1598
|
-
|
|
1599
|
-
/*
|
|
1600
|
-
Computes the set of Identifiers reachable from this Identifier.
|
|
1601
|
-
|
|
1602
|
-
Reachability is determined over the relationship graph (ie a graph where
|
|
1603
|
-
nodes are identifiers and edges are belongs to or has many
|
|
1604
|
-
relationships).
|
|
1605
|
-
|
|
1606
|
-
Returns an array including `this` and all identifiers reachable
|
|
1607
|
-
from `this.identifier`.
|
|
1608
|
-
*/
|
|
1609
1155
|
function _allRelatedIdentifiers(storeWrapper, originating) {
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1156
|
+
const array = [];
|
|
1157
|
+
const queue = [];
|
|
1158
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1159
|
+
queue.push(originating);
|
|
1160
|
+
while (queue.length > 0) {
|
|
1161
|
+
const identifier = queue.shift();
|
|
1162
|
+
array.push(identifier);
|
|
1163
|
+
seen.add(identifier);
|
|
1164
|
+
const iterator = _directlyRelatedIdentifiersIterable(storeWrapper, originating).iterator();
|
|
1165
|
+
for (let obj = iterator.next(); !obj.done; obj = iterator.next()) {
|
|
1166
|
+
const relatedIdentifier = obj.value;
|
|
1167
|
+
if (relatedIdentifier && !seen.has(relatedIdentifier)) {
|
|
1168
|
+
seen.add(relatedIdentifier);
|
|
1169
|
+
queue.push(relatedIdentifier);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
return array;
|
|
1628
1174
|
}
|
|
1629
1175
|
function fromBaseDocument(doc) {
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
}
|
|
1635
|
-
return resourceDocument;
|
|
1176
|
+
const resourceDocument = {};
|
|
1177
|
+
const jsonApiDoc = doc.content;
|
|
1178
|
+
if (jsonApiDoc) copyLinksAndMeta(resourceDocument, jsonApiDoc);
|
|
1179
|
+
return resourceDocument;
|
|
1636
1180
|
}
|
|
1637
1181
|
function fromStructuredError(doc) {
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
errorDoc.errors = [{
|
|
1647
|
-
title: doc.message
|
|
1648
|
-
}];
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
return errorDoc;
|
|
1182
|
+
const errorDoc = {};
|
|
1183
|
+
if (doc.content) {
|
|
1184
|
+
copyLinksAndMeta(errorDoc, doc.content);
|
|
1185
|
+
if ("errors" in doc.content) errorDoc.errors = doc.content.errors;
|
|
1186
|
+
else if (typeof doc.error === "object" && "errors" in doc.error) errorDoc.errors = doc.error.errors;
|
|
1187
|
+
else errorDoc.errors = [{ title: doc.message }];
|
|
1188
|
+
}
|
|
1189
|
+
return errorDoc;
|
|
1652
1190
|
}
|
|
1653
1191
|
function copyLinksAndMeta(target, source) {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
}
|
|
1657
|
-
if ('meta' in source) {
|
|
1658
|
-
target.meta = source.meta;
|
|
1659
|
-
}
|
|
1192
|
+
if ("links" in source) target.links = source.links;
|
|
1193
|
+
if ("meta" in source) target.meta = source.meta;
|
|
1660
1194
|
}
|
|
1661
1195
|
function cacheUpsert(cache, identifier, data, calculateChanges) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
if (!isUpdate) {
|
|
1687
|
-
cache._capabilities.notifyChange(identifier, 'added', null);
|
|
1688
|
-
}
|
|
1689
|
-
if (data.id) {
|
|
1690
|
-
cached.id = data.id;
|
|
1691
|
-
}
|
|
1692
|
-
if (data.relationships) {
|
|
1693
|
-
setupRelationships(cache.__graph, fields, identifier, data);
|
|
1694
|
-
}
|
|
1695
|
-
if (changedKeys?.size) {
|
|
1696
|
-
notifyAttributes(cache._capabilities, identifier, changedKeys);
|
|
1697
|
-
}
|
|
1698
|
-
return changedKeys?.size ? Array.from(changedKeys) : undefined;
|
|
1196
|
+
let changedKeys;
|
|
1197
|
+
const peeked = cache.__safePeek(identifier, false);
|
|
1198
|
+
const existed = !!peeked;
|
|
1199
|
+
const cached = peeked || cache._createCache(identifier);
|
|
1200
|
+
const isLoading = _isLoading(peeked, cache._capabilities, identifier) || !recordIsLoaded(peeked);
|
|
1201
|
+
const isUpdate = !_isEmpty(peeked) && !isLoading;
|
|
1202
|
+
if (cached.isNew) {
|
|
1203
|
+
cached.isNew = false;
|
|
1204
|
+
cache._capabilities.notifyChange(identifier, "identity", null);
|
|
1205
|
+
cache._capabilities.notifyChange(identifier, "state", null);
|
|
1206
|
+
}
|
|
1207
|
+
const fields = getCacheFields(cache, identifier);
|
|
1208
|
+
if (calculateChanges && existed && data.attributes) changedKeys = calculateChangedKeys(cached, data.attributes, fields);
|
|
1209
|
+
cached.remoteAttrs = Object.assign(cached.remoteAttrs || Object.create(null), data.attributes);
|
|
1210
|
+
if (cached.localAttrs) {
|
|
1211
|
+
if (patchLocalAttributes(cached, changedKeys)) cache._capabilities.notifyChange(identifier, "state", null);
|
|
1212
|
+
}
|
|
1213
|
+
if (!isUpdate) cache._capabilities.notifyChange(identifier, "added", null);
|
|
1214
|
+
if (data.id) cached.id = data.id;
|
|
1215
|
+
if (data.relationships) setupRelationships(cache.__graph, fields, identifier, data);
|
|
1216
|
+
if (changedKeys?.size) cache._capabilities.notifyChange(identifier, "attributes", changedKeys);
|
|
1217
|
+
return changedKeys?.size ? Array.from(changedKeys) : void 0;
|
|
1699
1218
|
}
|
|
1700
1219
|
function patchCache(Cache, op) {
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
break;
|
|
1748
|
-
}
|
|
1749
|
-
case 'remove':
|
|
1750
|
-
{
|
|
1751
|
-
if (isRecord) {
|
|
1752
|
-
if ('field' in op) {
|
|
1753
|
-
Cache.__graph.push(op);
|
|
1754
|
-
} else {
|
|
1755
|
-
const cached = Cache.__safePeek(op.record, false);
|
|
1756
|
-
if (cached) {
|
|
1757
|
-
cached.isDeleted = true;
|
|
1758
|
-
cached.isDeletionCommitted = true;
|
|
1759
|
-
Cache.unloadRecord(op.record);
|
|
1760
|
-
} else {
|
|
1761
|
-
peekGraph(Cache._capabilities)?.push({
|
|
1762
|
-
op: 'deleteRecord',
|
|
1763
|
-
record: op.record,
|
|
1764
|
-
isNew: false
|
|
1765
|
-
});
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
} else {
|
|
1769
|
-
if ('field' in op) {
|
|
1770
|
-
removeResourceFromDocument(Cache, op);
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
break;
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1220
|
+
const isRecord = isResourceKey(op.record);
|
|
1221
|
+
!isRecord && isRequestKey(op.record);
|
|
1222
|
+
switch (op.op) {
|
|
1223
|
+
case "mergeIdentifiers": {
|
|
1224
|
+
const cache = Cache.__cache.get(op.record);
|
|
1225
|
+
if (cache) {
|
|
1226
|
+
Cache.__cache.set(op.value, cache);
|
|
1227
|
+
Cache.__cache.delete(op.record);
|
|
1228
|
+
}
|
|
1229
|
+
Cache.__graph.update(op, true);
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
case "update":
|
|
1233
|
+
if (isRecord) {
|
|
1234
|
+
if ("field" in op) {
|
|
1235
|
+
if (isRelationship(getCacheFields(Cache, op.record).get(op.field))) Cache.__graph.push(op);
|
|
1236
|
+
else Cache.upsert(op.record, {
|
|
1237
|
+
type: op.record.type,
|
|
1238
|
+
id: op.record.id,
|
|
1239
|
+
attributes: { [op.field]: op.value }
|
|
1240
|
+
}, Cache._capabilities.hasRecord(op.record));
|
|
1241
|
+
} else Cache.upsert(op.record, op.value, Cache._capabilities.hasRecord(op.record));
|
|
1242
|
+
}
|
|
1243
|
+
break;
|
|
1244
|
+
case "add":
|
|
1245
|
+
if (isRecord) {
|
|
1246
|
+
if ("field" in op) Cache.__graph.push(op);
|
|
1247
|
+
else Cache.upsert(op.record, op.value, Cache._capabilities.hasRecord(op.record));
|
|
1248
|
+
} else addResourceToDocument(Cache, op);
|
|
1249
|
+
break;
|
|
1250
|
+
case "remove": if (isRecord) {
|
|
1251
|
+
if ("field" in op) Cache.__graph.push(op);
|
|
1252
|
+
else {
|
|
1253
|
+
const cached = Cache.__safePeek(op.record, false);
|
|
1254
|
+
if (cached) {
|
|
1255
|
+
cached.isDeleted = true;
|
|
1256
|
+
cached.isDeletionCommitted = true;
|
|
1257
|
+
Cache.unloadRecord(op.record);
|
|
1258
|
+
} else peekGraph(Cache._capabilities)?.push({
|
|
1259
|
+
op: "deleteRecord",
|
|
1260
|
+
record: op.record,
|
|
1261
|
+
isNew: false
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
} else if ("field" in op) removeResourceFromDocument(Cache, op);
|
|
1265
|
+
}
|
|
1776
1266
|
}
|
|
1777
1267
|
function getCacheFields(cache, identifier) {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
// the model schema service cannot process fields that are not cache fields
|
|
1786
|
-
return cache._capabilities.schema.fields(identifier);
|
|
1268
|
+
if (cache._capabilities.schema.cacheFields) {
|
|
1269
|
+
const result = cache._capabilities.schema.cacheFields(identifier);
|
|
1270
|
+
if (result) return result;
|
|
1271
|
+
}
|
|
1272
|
+
return cache._capabilities.schema.fields(identifier);
|
|
1787
1273
|
}
|
|
1788
1274
|
function commitDidError(cache, identifier, errors) {
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
cached.inflightAttrs = null;
|
|
1801
|
-
}
|
|
1802
|
-
if (errors) {
|
|
1803
|
-
cached.errors = errors;
|
|
1804
|
-
}
|
|
1805
|
-
cache._capabilities.notifyChange(identifier, 'errors', null);
|
|
1275
|
+
const cached = cache.__peek(identifier, false);
|
|
1276
|
+
if (cached.inflightAttrs) {
|
|
1277
|
+
const keys = Object.keys(cached.inflightAttrs);
|
|
1278
|
+
if (keys.length > 0) {
|
|
1279
|
+
const attrs = cached.localAttrs = cached.localAttrs || Object.create(null);
|
|
1280
|
+
for (let i = 0; i < keys.length; i++) if (attrs[keys[i]] === void 0) attrs[keys[i]] = cached.inflightAttrs[keys[i]];
|
|
1281
|
+
}
|
|
1282
|
+
cached.inflightAttrs = null;
|
|
1283
|
+
}
|
|
1284
|
+
if (errors) cached.errors = errors;
|
|
1285
|
+
cache._capabilities.notifyChange(identifier, "errors", null);
|
|
1806
1286
|
}
|
|
1807
1287
|
function didCommit(cache, committedIdentifier, data, op) {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
cached.inflightAttrs = null;
|
|
1843
|
-
patchLocalAttributes(cached, changedKeys);
|
|
1844
|
-
if (cached.errors) {
|
|
1845
|
-
cached.errors = null;
|
|
1846
|
-
cache._capabilities.notifyChange(identifier, 'errors', null);
|
|
1847
|
-
}
|
|
1848
|
-
if (changedKeys?.size) notifyAttributes(cache._capabilities, identifier, changedKeys);
|
|
1849
|
-
cache._capabilities.notifyChange(identifier, 'state', null);
|
|
1288
|
+
if (!data) {}
|
|
1289
|
+
const { cacheKeyManager } = cache._capabilities;
|
|
1290
|
+
const existingId = committedIdentifier.id;
|
|
1291
|
+
const identifier = op !== "deleteRecord" && data ? cacheKeyManager.updateRecordIdentifier(committedIdentifier, data) : committedIdentifier;
|
|
1292
|
+
const cached = cache.__peek(identifier, false);
|
|
1293
|
+
if (cached.isDeleted || op === "deleteRecord") {
|
|
1294
|
+
cache.__graph.push({
|
|
1295
|
+
op: "deleteRecord",
|
|
1296
|
+
record: identifier,
|
|
1297
|
+
isNew: false
|
|
1298
|
+
});
|
|
1299
|
+
cached.isDeleted = true;
|
|
1300
|
+
cached.isDeletionCommitted = true;
|
|
1301
|
+
cache._capabilities.notifyChange(identifier, "removed", null);
|
|
1302
|
+
}
|
|
1303
|
+
const fields = getCacheFields(cache, identifier);
|
|
1304
|
+
cached.isNew = false;
|
|
1305
|
+
let newCanonicalAttributes;
|
|
1306
|
+
if (data) {
|
|
1307
|
+
if (data.id && !cached.id) cached.id = data.id;
|
|
1308
|
+
if (identifier === committedIdentifier && identifier.id !== existingId) cache._capabilities.notifyChange(identifier, "identity", null);
|
|
1309
|
+
if (data.relationships) setupRelationships(cache.__graph, fields, identifier, data);
|
|
1310
|
+
newCanonicalAttributes = data.attributes;
|
|
1311
|
+
}
|
|
1312
|
+
const changedKeys = newCanonicalAttributes && calculateChangedKeys(cached, newCanonicalAttributes, fields);
|
|
1313
|
+
cached.remoteAttrs = Object.assign(cached.remoteAttrs || Object.create(null), cached.inflightAttrs, newCanonicalAttributes);
|
|
1314
|
+
cached.inflightAttrs = null;
|
|
1315
|
+
patchLocalAttributes(cached, changedKeys);
|
|
1316
|
+
if (cached.errors) {
|
|
1317
|
+
cached.errors = null;
|
|
1318
|
+
cache._capabilities.notifyChange(identifier, "errors", null);
|
|
1319
|
+
}
|
|
1320
|
+
if (changedKeys?.size) cache._capabilities.notifyChange(identifier, "attributes", changedKeys);
|
|
1321
|
+
cache._capabilities.notifyChange(identifier, "state", null);
|
|
1850
1322
|
}
|
|
1851
1323
|
function willCommit(cache, identifier) {
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
attempts to lose the least information.
|
|
1858
|
-
If we were to clear inflightAttrs, previous requests
|
|
1859
|
-
would not be able to use it during their didCommit.
|
|
1860
|
-
If we upsert inflightattrs, previous requests incorrectly
|
|
1861
|
-
see more recent inflight changes as part of their own and
|
|
1862
|
-
will incorrectly mark the new state as the correct remote state.
|
|
1863
|
-
We choose this latter behavior to avoid accidentally removing
|
|
1864
|
-
earlier changes.
|
|
1865
|
-
If apps do not want this behavior they can either
|
|
1866
|
-
- chain save requests serially vs allowing concurrent saves
|
|
1867
|
-
- move to using a request handler that caches the inflight state
|
|
1868
|
-
on a per-request basis
|
|
1869
|
-
- change their save requests to only send a "PATCH" instead of a "PUT"
|
|
1870
|
-
so that only latest changes are involved in each request, and then also
|
|
1871
|
-
ensure that the API or their handler reflects only those changes back
|
|
1872
|
-
for upsert into the cache.
|
|
1873
|
-
*/
|
|
1874
|
-
if (cached.inflightAttrs) {
|
|
1875
|
-
if (cached.localAttrs) {
|
|
1876
|
-
Object.assign(cached.inflightAttrs, cached.localAttrs);
|
|
1877
|
-
}
|
|
1878
|
-
} else {
|
|
1879
|
-
cached.inflightAttrs = cached.localAttrs;
|
|
1880
|
-
}
|
|
1881
|
-
cached.localAttrs = null;
|
|
1324
|
+
const cached = cache.__peek(identifier, false);
|
|
1325
|
+
if (cached.inflightAttrs) {
|
|
1326
|
+
if (cached.localAttrs) Object.assign(cached.inflightAttrs, cached.localAttrs);
|
|
1327
|
+
} else cached.inflightAttrs = cached.localAttrs;
|
|
1328
|
+
cached.localAttrs = null;
|
|
1882
1329
|
}
|
|
1883
1330
|
|
|
1331
|
+
//#endregion
|
|
1884
1332
|
export { JSONAPICache };
|
|
1333
|
+
//# sourceMappingURL=index.js.map
|