ember-data-model-fragments 7.0.3 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-plan.json +16 -4
- package/CHANGELOG.md +29 -0
- package/README.md +95 -31
- package/addon/array/fragment.js +19 -0
- package/addon/array/stateful.js +9 -45
- package/addon/attributes/array.js +55 -46
- package/addon/attributes/fragment-array.js +58 -49
- package/addon/attributes/fragment-owner.js +2 -1
- package/addon/attributes/fragment.js +61 -49
- package/addon/cache/fragment-cache.js +323 -24
- package/addon/cache/fragment-record-data-proxy.js +3 -1
- package/addon/cache/fragment-state-manager.js +283 -70
- package/addon/ext.js +52 -216
- package/addon/fragment.js +122 -48
- package/addon/index.js +15 -4
- package/addon/schema-service.js +190 -0
- package/addon/serializer.js +21 -0
- package/addon/serializers/fragment.js +85 -0
- package/addon/serializers/json-api.js +85 -0
- package/addon/serializers/rest.js +83 -0
- package/addon/serializers/utils.js +253 -0
- package/addon/store.js +301 -0
- package/addon/transforms/fragment.js +3 -7
- package/addon/util/fragment-cache.js +59 -0
- package/package.json +76 -65
- package/addon/record-data.js +0 -1131
- package/app/initializers/model-fragments.js +0 -11
- package/record-data.d.ts +0 -4
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
2
|
import { typeOf } from '@ember/utils';
|
|
3
3
|
import { isArray } from '@ember/array';
|
|
4
|
-
import { diffArray } from '@ember-data/model/-private';
|
|
5
4
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
5
|
+
import { dependencySatisfies, macroCondition } from '@embroider/macros';
|
|
6
6
|
import { getActualFragmentType, isFragment } from '../fragment';
|
|
7
7
|
import isInstanceOfType from '../util/instance-of-type';
|
|
8
|
+
import fragmentCacheFor from '../util/fragment-cache';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Simple array diff implementation.
|
|
12
|
+
* Returns an object with `firstChangeIndex` indicating where the arrays first differ.
|
|
13
|
+
* Returns `{ firstChangeIndex: null }` if arrays are identical.
|
|
14
|
+
*
|
|
15
|
+
* @param {Array} oldArray
|
|
16
|
+
* @param {Array} newArray
|
|
17
|
+
* @returns {Object} Object with firstChangeIndex property
|
|
18
|
+
*/
|
|
19
|
+
function diffArray(oldArray, newArray) {
|
|
20
|
+
if (oldArray === newArray) {
|
|
21
|
+
return { firstChangeIndex: null };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!oldArray || !newArray) {
|
|
25
|
+
return { firstChangeIndex: 0 };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (oldArray.length !== newArray.length) {
|
|
29
|
+
return { firstChangeIndex: Math.min(oldArray.length, newArray.length) };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < oldArray.length; i++) {
|
|
33
|
+
if (oldArray[i] !== newArray[i]) {
|
|
34
|
+
return { firstChangeIndex: i };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { firstChangeIndex: null };
|
|
39
|
+
}
|
|
8
40
|
|
|
9
41
|
/**
|
|
10
42
|
* Behavior for single fragment attributes
|
|
@@ -429,6 +461,14 @@ export default class FragmentStateManager {
|
|
|
429
461
|
return this.__storeWrapper._store;
|
|
430
462
|
}
|
|
431
463
|
|
|
464
|
+
get cache() {
|
|
465
|
+
return fragmentCacheFor(this.store);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
get innerCache() {
|
|
469
|
+
return this.cache.__innerCache;
|
|
470
|
+
}
|
|
471
|
+
|
|
432
472
|
_identifierFor(fragmentOrRecord) {
|
|
433
473
|
// Use recordIdentifierFor for records/fragments that already exist
|
|
434
474
|
// This is the correct way to get an identifier from an instantiated record
|
|
@@ -441,36 +481,53 @@ export default class FragmentStateManager {
|
|
|
441
481
|
|
|
442
482
|
_getBehaviors(identifier) {
|
|
443
483
|
let behaviors = this.__behaviors.get(identifier.lid);
|
|
444
|
-
if (
|
|
445
|
-
behaviors
|
|
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
|
-
|
|
484
|
+
if (behaviors) {
|
|
485
|
+
return behaviors;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
behaviors = Object.create(null);
|
|
489
|
+
|
|
490
|
+
const definitions = this.__storeWrapper
|
|
491
|
+
.getSchemaDefinitionService()
|
|
492
|
+
.attributesDefinitionFor(identifier);
|
|
493
|
+
|
|
494
|
+
for (const [key, definition] of Object.entries(definitions)) {
|
|
495
|
+
// Support both metadata formats:
|
|
496
|
+
// - ember-data 4.12: definition.isFragment (direct property on meta)
|
|
497
|
+
// - ember-data 4.13: definition.options.isFragment (transformed by FragmentSchemaService)
|
|
498
|
+
const isFragmentAttr =
|
|
499
|
+
definition.isFragment || definition.options?.isFragment;
|
|
500
|
+
|
|
501
|
+
if (!isFragmentAttr) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Get the fragment kind from the appropriate location:
|
|
506
|
+
// - ember-data 4.12: definition.kind (original location)
|
|
507
|
+
// - ember-data 4.13: definition.options.fragmentKind (preserved by FragmentSchemaService)
|
|
508
|
+
const fragmentKind = definition.options?.fragmentKind || definition.kind;
|
|
509
|
+
|
|
510
|
+
switch (fragmentKind) {
|
|
511
|
+
case 'fragment-array':
|
|
512
|
+
behaviors[key] = new FragmentArrayBehavior(
|
|
513
|
+
this,
|
|
514
|
+
identifier,
|
|
515
|
+
definition,
|
|
516
|
+
);
|
|
517
|
+
break;
|
|
518
|
+
case 'fragment':
|
|
519
|
+
behaviors[key] = new FragmentBehavior(this, identifier, definition);
|
|
520
|
+
break;
|
|
521
|
+
case 'array':
|
|
522
|
+
behaviors[key] = new ArrayBehavior(this, identifier, definition);
|
|
523
|
+
break;
|
|
524
|
+
default:
|
|
525
|
+
assert(`Unsupported fragment type: ${fragmentKind}`);
|
|
526
|
+
break;
|
|
471
527
|
}
|
|
472
|
-
this.__behaviors.set(identifier.lid, behaviors);
|
|
473
528
|
}
|
|
529
|
+
|
|
530
|
+
this.__behaviors.set(identifier.lid, behaviors);
|
|
474
531
|
return behaviors;
|
|
475
532
|
}
|
|
476
533
|
|
|
@@ -621,6 +678,119 @@ export default class FragmentStateManager {
|
|
|
621
678
|
});
|
|
622
679
|
}
|
|
623
680
|
|
|
681
|
+
/**
|
|
682
|
+
* Returns true if the given attribute value is, or contains, an instantiated
|
|
683
|
+
* Fragment. Used by FragmentCache.clientDidCreate to route fragment-instance
|
|
684
|
+
* initialization (from `store.createRecord(type, { key: fragmentInstance })`)
|
|
685
|
+
* through the adopt-fragment path instead of pushFragmentData (which only
|
|
686
|
+
* accepts raw object/null canonical data).
|
|
687
|
+
*/
|
|
688
|
+
valueContainsFragmentInstance(value) {
|
|
689
|
+
if (value == null) {
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
if (isFragment(value)) {
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
if (isArray(value)) {
|
|
696
|
+
return value.some((item) => isFragment(item));
|
|
697
|
+
}
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Adopt an existing fragment instance (or array of fragment instances) for the
|
|
703
|
+
* given owner identifier + key. This is used by the client-created path
|
|
704
|
+
* (`createRecord(...props)`) when consumers pass already-instantiated
|
|
705
|
+
* fragments rather than raw object data.
|
|
706
|
+
*
|
|
707
|
+
* Returns the canonical fragment identifier value to be stored in the
|
|
708
|
+
* fragment data map (a single identifier for `fragment` kind, an array of
|
|
709
|
+
* identifiers for `fragment-array` kind).
|
|
710
|
+
*
|
|
711
|
+
* Asserts when the provided value does not match the fragment kind/type
|
|
712
|
+
* declared on the owner's schema.
|
|
713
|
+
*/
|
|
714
|
+
adoptFragmentForKey(ownerIdentifier, key, value) {
|
|
715
|
+
const behaviors = this._getBehaviors(ownerIdentifier);
|
|
716
|
+
const behavior = behaviors[key];
|
|
717
|
+
assert(
|
|
718
|
+
`Attribute '${key}' for model '${ownerIdentifier.type}' must be a fragment`,
|
|
719
|
+
behavior != null,
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
const definition = behavior.definition;
|
|
723
|
+
const isArrayBehavior = behavior instanceof FragmentArrayBehavior;
|
|
724
|
+
const isSingleFragmentBehavior = behavior instanceof FragmentBehavior;
|
|
725
|
+
|
|
726
|
+
// Only single-fragment and fragment-array behaviors support adopting
|
|
727
|
+
// fragment instances. Plain array behavior should never receive a
|
|
728
|
+
// fragment instance.
|
|
729
|
+
if (!isSingleFragmentBehavior && !isArrayBehavior) {
|
|
730
|
+
return undefined;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (isSingleFragmentBehavior) {
|
|
734
|
+
if (value === null) {
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
if (!isFragment(value)) {
|
|
738
|
+
return undefined;
|
|
739
|
+
}
|
|
740
|
+
assert(
|
|
741
|
+
`The value provided for fragment attribute '${key}' must be a '${definition.modelName}' fragment`,
|
|
742
|
+
isInstanceOfType(
|
|
743
|
+
this.__storeWrapper._store.modelFor(definition.modelName),
|
|
744
|
+
value,
|
|
745
|
+
),
|
|
746
|
+
);
|
|
747
|
+
const fragmentIdentifier = this._identifierFor(value);
|
|
748
|
+
this.setFragmentOwner(fragmentIdentifier, ownerIdentifier, key);
|
|
749
|
+
return fragmentIdentifier;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// fragment-array behavior
|
|
753
|
+
if (value === null) {
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
if (!isArray(value)) {
|
|
757
|
+
return undefined;
|
|
758
|
+
}
|
|
759
|
+
// Only treat the value as fragment-instance input if at least one entry is
|
|
760
|
+
// a fragment instance. Otherwise let the regular pushData path handle it
|
|
761
|
+
// (raw object array).
|
|
762
|
+
if (!value.some((item) => isFragment(item))) {
|
|
763
|
+
return undefined;
|
|
764
|
+
}
|
|
765
|
+
return value.map((item) => {
|
|
766
|
+
if (isFragment(item)) {
|
|
767
|
+
assert(
|
|
768
|
+
`The value provided for fragment array attribute '${key}' can only include '${definition.modelName}' fragments`,
|
|
769
|
+
isInstanceOfType(
|
|
770
|
+
this.__storeWrapper._store.modelFor(definition.modelName),
|
|
771
|
+
item,
|
|
772
|
+
),
|
|
773
|
+
);
|
|
774
|
+
const fragmentIdentifier = this._identifierFor(item);
|
|
775
|
+
this.setFragmentOwner(fragmentIdentifier, ownerIdentifier, key);
|
|
776
|
+
return fragmentIdentifier;
|
|
777
|
+
}
|
|
778
|
+
// Raw object entry mixed in with fragments - create a new fragment for it.
|
|
779
|
+
return this._newFragmentIdentifier(ownerIdentifier, definition, item);
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Set canonical fragment data for a key on an owner identifier. Used by the
|
|
785
|
+
* client-created path after adopting fragment instances so subsequent
|
|
786
|
+
* getFragment/getCurrentState calls find the canonical value without going
|
|
787
|
+
* through pushData (which only accepts raw object/null canonical data).
|
|
788
|
+
*/
|
|
789
|
+
setCanonicalFragmentValue(identifier, key, value) {
|
|
790
|
+
const fragmentData = this._getFragmentDataMap(identifier);
|
|
791
|
+
fragmentData[key] = value;
|
|
792
|
+
}
|
|
793
|
+
|
|
624
794
|
_newFragmentIdentifierForKey(identifier, key, attributes) {
|
|
625
795
|
const behaviors = this._getBehaviors(identifier);
|
|
626
796
|
const behavior = behaviors[key];
|
|
@@ -645,27 +815,21 @@ export default class FragmentStateManager {
|
|
|
645
815
|
const fragmentIdentifier =
|
|
646
816
|
this.store.identifierCache.createIdentifierForNewRecord({ type });
|
|
647
817
|
this.setFragmentOwner(fragmentIdentifier, ownerIdentifier, definition.name);
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
this.store.cache.__innerCache.clientDidCreate(fragmentIdentifier, {});
|
|
651
|
-
// Push the attributes to the inner cache
|
|
818
|
+
this.innerCache.clientDidCreate(fragmentIdentifier, {});
|
|
819
|
+
|
|
652
820
|
if (attributes) {
|
|
653
|
-
this.
|
|
654
|
-
fragmentIdentifier,
|
|
655
|
-
{ attributes },
|
|
656
|
-
false,
|
|
657
|
-
);
|
|
821
|
+
this.innerCache.upsert(fragmentIdentifier, { attributes }, false);
|
|
658
822
|
}
|
|
823
|
+
|
|
659
824
|
// Process any nested fragment attributes
|
|
660
825
|
this.pushFragmentData(fragmentIdentifier, { attributes }, false);
|
|
661
826
|
return fragmentIdentifier;
|
|
662
827
|
}
|
|
663
828
|
|
|
664
829
|
hasChangedAttributes(identifier) {
|
|
665
|
-
// Check both fragment state and inner cache state
|
|
666
830
|
return (
|
|
667
831
|
this.hasChangedFragments(identifier) ||
|
|
668
|
-
this.
|
|
832
|
+
this.innerCache.hasChangedAttrs(identifier)
|
|
669
833
|
);
|
|
670
834
|
}
|
|
671
835
|
|
|
@@ -675,7 +839,7 @@ export default class FragmentStateManager {
|
|
|
675
839
|
// Explicitly return boolean to ensure false instead of undefined
|
|
676
840
|
return Boolean(
|
|
677
841
|
(fragments && Object.keys(fragments).length > 0) ||
|
|
678
|
-
|
|
842
|
+
(inFlight && Object.keys(inFlight).length > 0),
|
|
679
843
|
);
|
|
680
844
|
}
|
|
681
845
|
|
|
@@ -684,14 +848,16 @@ export default class FragmentStateManager {
|
|
|
684
848
|
const definitions = this.__storeWrapper
|
|
685
849
|
.getSchemaDefinitionService()
|
|
686
850
|
.attributesDefinitionFor(identifier);
|
|
687
|
-
const cache = this.
|
|
851
|
+
const cache = this.innerCache;
|
|
688
852
|
|
|
689
853
|
// Get changed attrs from inner cache to find original values for dirty attrs
|
|
690
854
|
const changedAttrs = cache.changedAttrs(identifier);
|
|
691
855
|
|
|
692
856
|
// Get canonical values for all attributes
|
|
693
857
|
for (const [key, definition] of Object.entries(definitions)) {
|
|
694
|
-
|
|
858
|
+
const isFragmentAttr =
|
|
859
|
+
definition.isFragment || definition.options?.isFragment;
|
|
860
|
+
if (isFragmentAttr) {
|
|
695
861
|
// Fragment attributes - use behavior's canonicalState
|
|
696
862
|
const behaviors = this._getBehaviors(identifier);
|
|
697
863
|
const behavior = behaviors[key];
|
|
@@ -723,14 +889,16 @@ export default class FragmentStateManager {
|
|
|
723
889
|
|
|
724
890
|
// Get current values for all attributes
|
|
725
891
|
for (const [key, definition] of Object.entries(definitions)) {
|
|
726
|
-
|
|
892
|
+
const isFragmentAttr =
|
|
893
|
+
definition.isFragment || definition.options?.isFragment;
|
|
894
|
+
if (isFragmentAttr) {
|
|
727
895
|
// Fragment attributes - use behavior's currentState
|
|
728
896
|
const behaviors = this._getBehaviors(identifier);
|
|
729
897
|
const behavior = behaviors[key];
|
|
730
898
|
result[key] = behavior.currentState(this.getFragment(identifier, key));
|
|
731
899
|
} else {
|
|
732
900
|
// Regular attributes - get from inner cache
|
|
733
|
-
const cache = this.
|
|
901
|
+
const cache = this.innerCache;
|
|
734
902
|
result[key] = cache.getAttr(identifier, key);
|
|
735
903
|
}
|
|
736
904
|
}
|
|
@@ -796,7 +964,7 @@ export default class FragmentStateManager {
|
|
|
796
964
|
return changedKeys;
|
|
797
965
|
}
|
|
798
966
|
|
|
799
|
-
pushFragmentData(identifier, data, calculateChange) {
|
|
967
|
+
pushFragmentData(identifier, data, calculateChange, shouldNotify = true) {
|
|
800
968
|
let changedFragmentKeys;
|
|
801
969
|
const behaviors = this._getBehaviors(identifier);
|
|
802
970
|
const subFragmentsToProcess = [];
|
|
@@ -823,23 +991,28 @@ export default class FragmentStateManager {
|
|
|
823
991
|
newCanonicalFragments[key] = behavior.pushData(current, canonical);
|
|
824
992
|
});
|
|
825
993
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
}
|
|
994
|
+
// Always calculate which keys changed so we can notify observers
|
|
995
|
+
changedFragmentKeys = this._changedFragmentKeys(
|
|
996
|
+
identifier,
|
|
997
|
+
newCanonicalFragments,
|
|
998
|
+
);
|
|
832
999
|
|
|
833
1000
|
Object.assign(fragmentData, newCanonicalFragments);
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
1001
|
+
|
|
1002
|
+
if (shouldNotify) {
|
|
1003
|
+
// Notify the storeWrapper that fragment attributes changed when this is
|
|
1004
|
+
// an update to existing state. For clientDidCreate initialization we
|
|
1005
|
+
// skip notification so initial props don't look like post-consumption
|
|
1006
|
+
// mutations during first render in WarpDrive 5.8.
|
|
1007
|
+
changedFragmentKeys.forEach((key) => {
|
|
1008
|
+
this.__storeWrapper.notifyChange(identifier, 'attributes', key);
|
|
1009
|
+
const arrayCache = this.__fragmentArrayCache.get(identifier.lid);
|
|
1010
|
+
arrayCache?.[key]?.notify();
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
840
1013
|
}
|
|
841
1014
|
|
|
842
|
-
return changedFragmentKeys || [];
|
|
1015
|
+
return calculateChange ? changedFragmentKeys || [] : [];
|
|
843
1016
|
}
|
|
844
1017
|
|
|
845
1018
|
willCommitFragments(identifier) {
|
|
@@ -968,6 +1141,16 @@ export default class FragmentStateManager {
|
|
|
968
1141
|
}
|
|
969
1142
|
|
|
970
1143
|
unloadFragments(identifier) {
|
|
1144
|
+
// Guard against store being destroyed during teardown
|
|
1145
|
+
if (this.store.isDestroying || this.store.isDestroyed) {
|
|
1146
|
+
// Just clear our internal data structures
|
|
1147
|
+
this.__fragments.delete(identifier.lid);
|
|
1148
|
+
this.__inFlightFragments.delete(identifier.lid);
|
|
1149
|
+
this.__fragmentData.delete(identifier.lid);
|
|
1150
|
+
this.__fragmentArrayCache.delete(identifier.lid);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
971
1154
|
const behaviors = this._getBehaviors(identifier);
|
|
972
1155
|
const fragments = this.__fragments.get(identifier.lid) || {};
|
|
973
1156
|
const inFlight = this.__inFlightFragments.get(identifier.lid) || {};
|
|
@@ -1059,14 +1242,38 @@ export default class FragmentStateManager {
|
|
|
1059
1242
|
|
|
1060
1243
|
_notifyStateChange(identifier, key) {
|
|
1061
1244
|
this.__storeWrapper.notifyChange(identifier, 'attributes', key);
|
|
1245
|
+
if (macroCondition(dependencySatisfies('ember-data', '<5.8.0'))) {
|
|
1246
|
+
// 5.8+ already wires cache notifications into reactivity, and directly notifying
|
|
1247
|
+
// the record can trip mutation-after-consumption assertions during initial render.
|
|
1248
|
+
if (key) {
|
|
1249
|
+
try {
|
|
1250
|
+
const record = this.store._instanceCache.getRecord(identifier);
|
|
1251
|
+
if (record && typeof record.notifyPropertyChange === 'function') {
|
|
1252
|
+
record.notifyPropertyChange(key);
|
|
1253
|
+
// Also clear any cached value by directly removing from Ember's cache
|
|
1254
|
+
// This is needed because computed setters cache their return value
|
|
1255
|
+
// and notifyPropertyChange alone may not clear it in all Ember versions
|
|
1256
|
+
const meta = record.constructor.metaForProperty?.(key);
|
|
1257
|
+
if (meta) {
|
|
1258
|
+
// Force the computed property to re-evaluate by clearing its cache entry
|
|
1259
|
+
const cache = record['__ember_meta__']?.peekCache?.(key);
|
|
1260
|
+
if (cache !== undefined) {
|
|
1261
|
+
record['__ember_meta__']?.deleteFromCache?.(key);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
} catch {
|
|
1266
|
+
// Record may not be instantiated yet
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1062
1270
|
}
|
|
1063
1271
|
|
|
1064
1272
|
// Fragment lifecycle methods using cache API directly
|
|
1065
1273
|
_fragmentPushData(identifier, data) {
|
|
1066
1274
|
// Push data to the cache for the fragment
|
|
1067
1275
|
if (data?.attributes) {
|
|
1068
|
-
|
|
1069
|
-
const cache = this.store.cache.__innerCache;
|
|
1276
|
+
const cache = this.innerCache;
|
|
1070
1277
|
cache.upsert(identifier, data, false);
|
|
1071
1278
|
// Notify that attributes changed so computed properties are invalidated
|
|
1072
1279
|
for (const key of Object.keys(data.attributes)) {
|
|
@@ -1079,14 +1286,16 @@ export default class FragmentStateManager {
|
|
|
1079
1286
|
|
|
1080
1287
|
_fragmentWillCommit(identifier) {
|
|
1081
1288
|
// Capture the current attribute values before commit - these are what will be committed
|
|
1082
|
-
const innerCache = this.
|
|
1289
|
+
const innerCache = this.innerCache;
|
|
1083
1290
|
const definitions = this.__storeWrapper
|
|
1084
1291
|
.getSchemaDefinitionService()
|
|
1085
1292
|
.attributesDefinitionFor(identifier);
|
|
1086
1293
|
|
|
1087
1294
|
const inFlightValues = {};
|
|
1088
1295
|
for (const [key, definition] of Object.entries(definitions)) {
|
|
1089
|
-
|
|
1296
|
+
const isFragmentAttr =
|
|
1297
|
+
definition.isFragment || definition.options?.isFragment;
|
|
1298
|
+
if (!isFragmentAttr) {
|
|
1090
1299
|
// getAttr returns dirty value if exists, else canonical
|
|
1091
1300
|
inFlightValues[key] = innerCache.getAttr(identifier, key);
|
|
1092
1301
|
}
|
|
@@ -1095,7 +1304,7 @@ export default class FragmentStateManager {
|
|
|
1095
1304
|
|
|
1096
1305
|
// Signal to cache that fragment is being committed
|
|
1097
1306
|
this.willCommitFragments(identifier);
|
|
1098
|
-
|
|
1307
|
+
innerCache.willCommit(identifier);
|
|
1099
1308
|
}
|
|
1100
1309
|
|
|
1101
1310
|
_fragmentDidCommit(identifier, data) {
|
|
@@ -1113,7 +1322,7 @@ export default class FragmentStateManager {
|
|
|
1113
1322
|
// 4. Rollback to clear in-flight state
|
|
1114
1323
|
// 5. Upsert the committed values as new canonical
|
|
1115
1324
|
// 6. Re-apply any new dirty changes that were made during in-flight
|
|
1116
|
-
const innerCache = this.
|
|
1325
|
+
const innerCache = this.innerCache;
|
|
1117
1326
|
|
|
1118
1327
|
// Get schema for non-fragment attributes
|
|
1119
1328
|
const definitions = this.__storeWrapper
|
|
@@ -1127,7 +1336,9 @@ export default class FragmentStateManager {
|
|
|
1127
1336
|
// Get current values (may include new dirty changes made during in-flight)
|
|
1128
1337
|
const currentValues = {};
|
|
1129
1338
|
for (const [key, definition] of Object.entries(definitions)) {
|
|
1130
|
-
|
|
1339
|
+
const isFragmentAttr =
|
|
1340
|
+
definition.isFragment || definition.options?.isFragment;
|
|
1341
|
+
if (!isFragmentAttr) {
|
|
1131
1342
|
currentValues[key] = innerCache.getAttr(identifier, key);
|
|
1132
1343
|
}
|
|
1133
1344
|
}
|
|
@@ -1140,7 +1351,9 @@ export default class FragmentStateManager {
|
|
|
1140
1351
|
const newDirtyAttrs = {};
|
|
1141
1352
|
|
|
1142
1353
|
for (const [key, definition] of Object.entries(definitions)) {
|
|
1143
|
-
|
|
1354
|
+
const isFragmentAttr =
|
|
1355
|
+
definition.isFragment || definition.options?.isFragment;
|
|
1356
|
+
if (isFragmentAttr) continue;
|
|
1144
1357
|
|
|
1145
1358
|
// Determine the new canonical value
|
|
1146
1359
|
const canonicalValue =
|
|
@@ -1199,13 +1412,13 @@ export default class FragmentStateManager {
|
|
|
1199
1412
|
_fragmentRollbackAttributes(identifier) {
|
|
1200
1413
|
// Rollback fragment attributes
|
|
1201
1414
|
this.rollbackFragments(identifier);
|
|
1202
|
-
this.
|
|
1415
|
+
this.innerCache.rollbackAttrs(identifier);
|
|
1203
1416
|
}
|
|
1204
1417
|
|
|
1205
1418
|
_fragmentCommitWasRejected(identifier) {
|
|
1206
1419
|
// Signal that commit was rejected
|
|
1207
1420
|
this.commitWasRejectedFragments(identifier);
|
|
1208
|
-
this.
|
|
1421
|
+
this.innerCache.commitWasRejected(identifier);
|
|
1209
1422
|
}
|
|
1210
1423
|
|
|
1211
1424
|
_fragmentUnloadRecord(identifier) {
|
|
@@ -1223,7 +1436,7 @@ export default class FragmentStateManager {
|
|
|
1223
1436
|
// Fragment may already be unloaded or destroyed
|
|
1224
1437
|
// Fall back to just clearing the inner cache
|
|
1225
1438
|
try {
|
|
1226
|
-
this.
|
|
1439
|
+
this.innerCache.unloadRecord(identifier);
|
|
1227
1440
|
} catch {
|
|
1228
1441
|
// May already be unloaded
|
|
1229
1442
|
}
|