@ember-data/store 4.12.0-beta.3 → 4.12.0-beta.5
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/README.md +165 -16
- package/addon/-private.js +1 -1
- package/addon/{index-bebb407b.js → index-123f3e7e.js} +1248 -2032
- package/addon/index-123f3e7e.js.map +1 -0
- package/addon/index.js +1 -1
- package/addon-main.js +18 -15
- package/package.json +14 -13
- package/addon/index-bebb407b.js.map +0 -1
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { macroCondition, getOwnConfig,
|
|
1
|
+
import { macroCondition, getOwnConfig, importSync } from '@embroider/macros';
|
|
2
2
|
import { assert, warn, deprecate } from '@ember/debug';
|
|
3
3
|
import { dasherize } from '@ember/string';
|
|
4
4
|
import { getOwner, setOwner } from '@ember/application';
|
|
5
5
|
import { _backburner } from '@ember/runloop';
|
|
6
|
-
import { registerWaiter, unregisterWaiter } from '@ember/test';
|
|
7
|
-
import RSVP, { resolve, Promise as Promise$1, reject } from 'rsvp';
|
|
8
6
|
import { tracked } from '@glimmer/tracking';
|
|
9
7
|
import { subscribe, addToTransaction, addTransactionCB } from '@ember-data/tracking/-private';
|
|
10
8
|
import { tagForProperty } from '@ember/-internals/metal';
|
|
@@ -38,6 +36,26 @@ function normalizeModelName$1(modelName) {
|
|
|
38
36
|
return dasherize(modelName);
|
|
39
37
|
}
|
|
40
38
|
|
|
39
|
+
/*
|
|
40
|
+
* Returns the Cache instance associated with a given
|
|
41
|
+
* Model or Identifier
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const CacheForIdentifierCache = new Map();
|
|
45
|
+
function setCacheFor(identifier, cache) {
|
|
46
|
+
assert(`Illegal set of identifier`, !CacheForIdentifierCache.has(identifier) || CacheForIdentifierCache.get(identifier) === cache);
|
|
47
|
+
CacheForIdentifierCache.set(identifier, cache);
|
|
48
|
+
}
|
|
49
|
+
function removeRecordDataFor(identifier) {
|
|
50
|
+
CacheForIdentifierCache.delete(identifier);
|
|
51
|
+
}
|
|
52
|
+
function peekCache(instance) {
|
|
53
|
+
if (CacheForIdentifierCache.has(instance)) {
|
|
54
|
+
return CacheForIdentifierCache.get(instance);
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
41
59
|
/**
|
|
42
60
|
@module @ember-data/store
|
|
43
61
|
*/
|
|
@@ -139,7 +157,7 @@ function uuidv4() {
|
|
|
139
157
|
assert('crypto.randomUUID needs to be avaliable. Some browsers incorrectly disallow it in insecure contexts. You maybe want to enable the polyfill: https://github.com/emberjs/data#randomuuid-polyfill', _crypto.randomUUID);
|
|
140
158
|
return _crypto.randomUUID();
|
|
141
159
|
}
|
|
142
|
-
function freeze
|
|
160
|
+
function freeze(obj) {
|
|
143
161
|
if (typeof Object.freeze === 'function') {
|
|
144
162
|
return Object.freeze(obj);
|
|
145
163
|
}
|
|
@@ -179,7 +197,7 @@ function defaultGenerationMethod(data, bucket) {
|
|
|
179
197
|
}
|
|
180
198
|
function defaultEmptyCallback(...args) {}
|
|
181
199
|
let DEBUG_MAP;
|
|
182
|
-
if (macroCondition(
|
|
200
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
183
201
|
DEBUG_MAP = new WeakMap();
|
|
184
202
|
}
|
|
185
203
|
|
|
@@ -233,7 +251,7 @@ class IdentifierCache {
|
|
|
233
251
|
_getRecordIdentifier(resource, shouldGenerate = false) {
|
|
234
252
|
// short circuit if we're already the stable version
|
|
235
253
|
if (isStableIdentifier(resource)) {
|
|
236
|
-
if (macroCondition(
|
|
254
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
237
255
|
// TODO should we instead just treat this case as a new generation skipping the short circuit?
|
|
238
256
|
if (!this._cache.lids.has(resource.lid) || this._cache.lids.get(resource.lid) !== resource) {
|
|
239
257
|
throw new Error(`The supplied identifier ${resource} does not belong to this store instance`);
|
|
@@ -307,7 +325,7 @@ class IdentifierCache {
|
|
|
307
325
|
identifier = makeStableRecordIdentifier(id, type, newLid, 'record', false);
|
|
308
326
|
|
|
309
327
|
// populate our unique table
|
|
310
|
-
if (macroCondition(
|
|
328
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
311
329
|
// realistically if you hit this it means you changed `type` :/
|
|
312
330
|
// TODO consider how to handle type change assertions more gracefully
|
|
313
331
|
if (this._cache.lids.has(identifier.lid)) {
|
|
@@ -386,6 +404,7 @@ class IdentifierCache {
|
|
|
386
404
|
@returns {StableRecordIdentifier}
|
|
387
405
|
@public
|
|
388
406
|
*/
|
|
407
|
+
|
|
389
408
|
getOrCreateRecordIdentifier(resource) {
|
|
390
409
|
return this._getRecordIdentifier(resource, true);
|
|
391
410
|
}
|
|
@@ -407,7 +426,7 @@ class IdentifierCache {
|
|
|
407
426
|
let keyOptions = getTypeIndex(this._cache.types, data.type);
|
|
408
427
|
|
|
409
428
|
// populate our unique table
|
|
410
|
-
if (macroCondition(
|
|
429
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
411
430
|
if (this._cache.lids.has(identifier.lid)) {
|
|
412
431
|
throw new Error(`The lid generated for the new record is not unique as it matches an existing identifier`);
|
|
413
432
|
}
|
|
@@ -421,7 +440,7 @@ class IdentifierCache {
|
|
|
421
440
|
}
|
|
422
441
|
if (macroCondition(getOwnConfig().debug.LOG_IDENTIFIERS)) {
|
|
423
442
|
// eslint-disable-next-line no-console
|
|
424
|
-
console.log(`Identifiers:
|
|
443
|
+
console.log(`Identifiers: created identifier ${String(identifier)} for newly generated resource`, data);
|
|
425
444
|
}
|
|
426
445
|
return identifier;
|
|
427
446
|
}
|
|
@@ -561,7 +580,7 @@ function makeStableRecordIdentifier(id, type, lid, bucket, clientOriginated = fa
|
|
|
561
580
|
type
|
|
562
581
|
};
|
|
563
582
|
IDENTIFIERS.add(recordIdentifier);
|
|
564
|
-
if (macroCondition(
|
|
583
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
565
584
|
// we enforce immutability in dev
|
|
566
585
|
// but preserve our ability to do controlled updates to the reference
|
|
567
586
|
let wrapper = {
|
|
@@ -599,13 +618,13 @@ function makeStableRecordIdentifier(id, type, lid, bucket, clientOriginated = fa
|
|
|
599
618
|
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
|
|
600
619
|
IDENTIFIERS.add(wrapper);
|
|
601
620
|
DEBUG_MAP.set(wrapper, recordIdentifier);
|
|
602
|
-
wrapper = freeze
|
|
621
|
+
wrapper = freeze(wrapper);
|
|
603
622
|
return wrapper;
|
|
604
623
|
}
|
|
605
624
|
return recordIdentifier;
|
|
606
625
|
}
|
|
607
626
|
function performRecordIdentifierUpdate(identifier, data, updateFn) {
|
|
608
|
-
if (macroCondition(
|
|
627
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
609
628
|
let {
|
|
610
629
|
lid
|
|
611
630
|
} = data;
|
|
@@ -708,6 +727,14 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
|
|
|
708
727
|
return desc;
|
|
709
728
|
}
|
|
710
729
|
let tokenId = 0;
|
|
730
|
+
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
731
|
+
function isCacheOperationValue(value) {
|
|
732
|
+
return CacheOperations.has(value);
|
|
733
|
+
}
|
|
734
|
+
function runLoopIsFlushing() {
|
|
735
|
+
//@ts-expect-error
|
|
736
|
+
return !!_backburner.currentInstance && _backburner._autorun !== true;
|
|
737
|
+
}
|
|
711
738
|
const Cache = new Map();
|
|
712
739
|
const Tokens = new Map();
|
|
713
740
|
// TODO this isn't importable anyway, remove and use a map on the manager?
|
|
@@ -725,6 +752,7 @@ function unsubscribe(token) {
|
|
|
725
752
|
map?.delete(token);
|
|
726
753
|
}
|
|
727
754
|
}
|
|
755
|
+
|
|
728
756
|
/*
|
|
729
757
|
Currently only support a single callback per identifier
|
|
730
758
|
*/
|
|
@@ -743,6 +771,8 @@ class NotificationManager {
|
|
|
743
771
|
constructor(store) {
|
|
744
772
|
this.store = store;
|
|
745
773
|
this.isDestroyed = false;
|
|
774
|
+
this._buffered = new Map();
|
|
775
|
+
this._hasFlush = false;
|
|
746
776
|
}
|
|
747
777
|
|
|
748
778
|
/**
|
|
@@ -763,13 +793,13 @@ class NotificationManager {
|
|
|
763
793
|
* @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
|
|
764
794
|
*/
|
|
765
795
|
subscribe(identifier, callback) {
|
|
766
|
-
assert(`Expected to receive a stable Identifier to subscribe to`, isStableIdentifier(identifier));
|
|
796
|
+
assert(`Expected to receive a stable Identifier to subscribe to`, identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier));
|
|
767
797
|
let map = Cache.get(identifier);
|
|
768
798
|
if (!map) {
|
|
769
799
|
map = new Map();
|
|
770
800
|
Cache.set(identifier, map);
|
|
771
801
|
}
|
|
772
|
-
let unsubToken = macroCondition(
|
|
802
|
+
let unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
|
|
773
803
|
_tokenRef: tokenId++
|
|
774
804
|
} : {};
|
|
775
805
|
map.set(unsubToken, callback);
|
|
@@ -790,27 +820,93 @@ class NotificationManager {
|
|
|
790
820
|
}
|
|
791
821
|
}
|
|
792
822
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
823
|
+
/**
|
|
824
|
+
* Custom Caches and Application Code should not call this method directly.
|
|
825
|
+
*
|
|
826
|
+
* @method notify
|
|
827
|
+
* @param identifier
|
|
828
|
+
* @param value
|
|
829
|
+
* @param key
|
|
830
|
+
* @return {Boolean} whether a notification was delivered to any subscribers
|
|
831
|
+
* @private
|
|
832
|
+
*/
|
|
833
|
+
|
|
796
834
|
notify(identifier, value, key) {
|
|
797
|
-
assert(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key}'.`, !key || value === 'attributes' || value === 'relationships');
|
|
835
|
+
assert(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key || ''}'.`, !key || value === 'attributes' || value === 'relationships');
|
|
798
836
|
if (!isStableIdentifier(identifier)) {
|
|
799
837
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
800
838
|
// eslint-disable-next-line no-console
|
|
801
|
-
console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key}' with, but ${String(identifier)} is not in the cache`, identifier);
|
|
839
|
+
console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key || ''}' with, but ${String(identifier)} is not in the cache`, identifier);
|
|
802
840
|
}
|
|
803
841
|
return false;
|
|
804
842
|
}
|
|
805
843
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
806
844
|
// eslint-disable-next-line no-console
|
|
807
|
-
console.log(`
|
|
845
|
+
console.log(`Buffering Notify: ${String(identifier)}\t${value}\t${key || ''}`);
|
|
846
|
+
}
|
|
847
|
+
const hasSubscribers = Boolean(Cache.get(identifier)?.size);
|
|
848
|
+
if (isCacheOperationValue(value) || hasSubscribers) {
|
|
849
|
+
let buffer = this._buffered.get(identifier);
|
|
850
|
+
if (!buffer) {
|
|
851
|
+
buffer = [];
|
|
852
|
+
this._buffered.set(identifier, buffer);
|
|
853
|
+
}
|
|
854
|
+
buffer.push([value, key]);
|
|
855
|
+
void this._scheduleNotify();
|
|
856
|
+
}
|
|
857
|
+
return hasSubscribers;
|
|
858
|
+
}
|
|
859
|
+
_onNextFlush(cb) {
|
|
860
|
+
this._onFlushCB = cb;
|
|
861
|
+
}
|
|
862
|
+
_scheduleNotify() {
|
|
863
|
+
const asyncFlush = this.store._enableAsyncFlush;
|
|
864
|
+
if (this._hasFlush) {
|
|
865
|
+
if (asyncFlush !== false && !runLoopIsFlushing()) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
if (asyncFlush && !runLoopIsFlushing()) {
|
|
870
|
+
this._hasFlush = true;
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
this._flush();
|
|
874
|
+
}
|
|
875
|
+
_flush() {
|
|
876
|
+
if (this._buffered.size) {
|
|
877
|
+
this._buffered.forEach((states, identifier) => {
|
|
878
|
+
states.forEach(args => {
|
|
879
|
+
// @ts-expect-error
|
|
880
|
+
this._flushNotification(identifier, args[0], args[1]);
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
this._buffered = new Map();
|
|
884
|
+
}
|
|
885
|
+
this._hasFlush = false;
|
|
886
|
+
this._onFlushCB?.();
|
|
887
|
+
this._onFlushCB = undefined;
|
|
888
|
+
}
|
|
889
|
+
_flushNotification(identifier, value, key) {
|
|
890
|
+
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
891
|
+
// eslint-disable-next-line no-console
|
|
892
|
+
console.log(`Notifying: ${String(identifier)}\t${value}\t${key || ''}`);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// TODO for documents this will need to switch based on Identifier kind
|
|
896
|
+
if (isCacheOperationValue(value)) {
|
|
897
|
+
let callbackMap = Cache.get('resource');
|
|
898
|
+
if (callbackMap) {
|
|
899
|
+
callbackMap.forEach(cb => {
|
|
900
|
+
cb(identifier, value);
|
|
901
|
+
});
|
|
902
|
+
}
|
|
808
903
|
}
|
|
809
904
|
let callbackMap = Cache.get(identifier);
|
|
810
905
|
if (!callbackMap || !callbackMap.size) {
|
|
811
906
|
return false;
|
|
812
907
|
}
|
|
813
908
|
callbackMap.forEach(cb => {
|
|
909
|
+
// @ts-expect-error overload doesn't narrow within body
|
|
814
910
|
cb(identifier, value, key);
|
|
815
911
|
});
|
|
816
912
|
return true;
|
|
@@ -942,7 +1038,7 @@ let RecordReference = (_class$2 = class RecordReference {
|
|
|
942
1038
|
*/
|
|
943
1039
|
push(objectOrPromise) {
|
|
944
1040
|
// TODO @deprecate pushing unresolved payloads
|
|
945
|
-
return resolve(objectOrPromise).then(data => {
|
|
1041
|
+
return Promise.resolve(objectOrPromise).then(data => {
|
|
946
1042
|
return this.store.push(data);
|
|
947
1043
|
});
|
|
948
1044
|
}
|
|
@@ -1025,14 +1121,49 @@ var id = 0;
|
|
|
1025
1121
|
function _classPrivateFieldKey(name) {
|
|
1026
1122
|
return "__private_" + id++ + "_" + name;
|
|
1027
1123
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1124
|
+
function legacyCachePut(store, doc) {
|
|
1125
|
+
const jsonApiDoc = doc.content;
|
|
1126
|
+
let ret;
|
|
1127
|
+
store._join(() => {
|
|
1128
|
+
let included = jsonApiDoc.included;
|
|
1129
|
+
let i, length;
|
|
1130
|
+
if (included) {
|
|
1131
|
+
for (i = 0, length = included.length; i < length; i++) {
|
|
1132
|
+
store._instanceCache.loadData(included[i]);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
if (Array.isArray(jsonApiDoc.data)) {
|
|
1136
|
+
length = jsonApiDoc.data.length;
|
|
1137
|
+
let identifiers = [];
|
|
1138
|
+
for (i = 0; i < length; i++) {
|
|
1139
|
+
identifiers.push(store._instanceCache.loadData(jsonApiDoc.data[i]));
|
|
1140
|
+
}
|
|
1141
|
+
ret = {
|
|
1142
|
+
data: identifiers
|
|
1143
|
+
};
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
if (jsonApiDoc.data === null) {
|
|
1147
|
+
ret = {
|
|
1148
|
+
data: null
|
|
1149
|
+
};
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
assert(`Expected an object in the 'data' property in a call to 'push', but was ${typeof jsonApiDoc.data}`, typeof jsonApiDoc.data === 'object');
|
|
1153
|
+
ret = {
|
|
1154
|
+
data: store._instanceCache.loadData(jsonApiDoc.data)
|
|
1155
|
+
};
|
|
1156
|
+
return;
|
|
1157
|
+
});
|
|
1158
|
+
return ret;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1032
1161
|
/**
|
|
1033
|
-
* The CacheManager wraps a Cache
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
1162
|
+
* The CacheManager wraps a Cache enforcing that only
|
|
1163
|
+
* the public API surface area is exposed.
|
|
1164
|
+
*
|
|
1165
|
+
* Hence, it is the value of `Store.cache`, wrapping
|
|
1166
|
+
* the cache instance returned by `Store.createCache`.
|
|
1036
1167
|
*
|
|
1037
1168
|
* This class is the the return value of both the
|
|
1038
1169
|
* `recordDataFor` function supplied to the store
|
|
@@ -1069,11 +1200,15 @@ var _isDeprecated = /*#__PURE__*/_classPrivateFieldKey("isDeprecated");
|
|
|
1069
1200
|
* @class CacheManager
|
|
1070
1201
|
* @public
|
|
1071
1202
|
*/
|
|
1203
|
+
var _store = /*#__PURE__*/_classPrivateFieldKey("store");
|
|
1204
|
+
var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
|
|
1205
|
+
var _identifier = /*#__PURE__*/_classPrivateFieldKey("identifier");
|
|
1206
|
+
var _isDeprecated = /*#__PURE__*/_classPrivateFieldKey("isDeprecated");
|
|
1072
1207
|
class NonSingletonCacheManager {
|
|
1073
1208
|
get managedVersion() {
|
|
1074
|
-
return _classPrivateFieldBase(this,
|
|
1209
|
+
return _classPrivateFieldBase(this, _cache)[_cache].version || '1';
|
|
1075
1210
|
}
|
|
1076
|
-
constructor(store,
|
|
1211
|
+
constructor(store, _cache2, identifier) {
|
|
1077
1212
|
Object.defineProperty(this, _isDeprecated, {
|
|
1078
1213
|
value: _isDeprecated2
|
|
1079
1214
|
});
|
|
@@ -1082,7 +1217,7 @@ class NonSingletonCacheManager {
|
|
|
1082
1217
|
writable: true,
|
|
1083
1218
|
value: void 0
|
|
1084
1219
|
});
|
|
1085
|
-
Object.defineProperty(this,
|
|
1220
|
+
Object.defineProperty(this, _cache, {
|
|
1086
1221
|
writable: true,
|
|
1087
1222
|
value: void 0
|
|
1088
1223
|
});
|
|
@@ -1091,9 +1226,9 @@ class NonSingletonCacheManager {
|
|
|
1091
1226
|
value: void 0
|
|
1092
1227
|
});
|
|
1093
1228
|
_classPrivateFieldBase(this, _store)[_store] = store;
|
|
1094
|
-
_classPrivateFieldBase(this,
|
|
1229
|
+
_classPrivateFieldBase(this, _cache)[_cache] = _cache2;
|
|
1095
1230
|
_classPrivateFieldBase(this, _identifier)[_identifier] = identifier;
|
|
1096
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1231
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](_cache2)) {
|
|
1097
1232
|
deprecate(`This RecordData uses the deprecated V1 RecordData Spec. Upgrade to V2 to maintain compatibility.`, false, {
|
|
1098
1233
|
id: 'ember-data:deprecate-v1-cache',
|
|
1099
1234
|
until: '5.0',
|
|
@@ -1105,44 +1240,42 @@ class NonSingletonCacheManager {
|
|
|
1105
1240
|
});
|
|
1106
1241
|
}
|
|
1107
1242
|
}
|
|
1108
|
-
// Cache
|
|
1109
|
-
//
|
|
1243
|
+
// Cache Management
|
|
1244
|
+
// ================
|
|
1110
1245
|
|
|
1111
1246
|
/**
|
|
1112
|
-
*
|
|
1247
|
+
* Cache the response to a request
|
|
1113
1248
|
*
|
|
1114
|
-
*
|
|
1249
|
+
* Unlike `store.push` which has UPSERT
|
|
1250
|
+
* semantics, `put` has `replace` semantics similar to
|
|
1251
|
+
* the `http` method `PUT`
|
|
1115
1252
|
*
|
|
1116
|
-
*
|
|
1117
|
-
*
|
|
1118
|
-
*
|
|
1119
|
-
|
|
1120
|
-
getResourceIdentifier() {
|
|
1121
|
-
return _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
/**
|
|
1125
|
-
* Push resource data from a remote source into the cache for this identifier
|
|
1253
|
+
* the individually cacheabl
|
|
1254
|
+
* e resource data it may contain
|
|
1255
|
+
* should upsert, but the document data surrounding it should
|
|
1256
|
+
* fully replace any existing information
|
|
1126
1257
|
*
|
|
1127
|
-
*
|
|
1258
|
+
* Note that in order to support inserting arbitrary data
|
|
1259
|
+
* to the cache that did not originate from a request `put`
|
|
1260
|
+
* should expect to sometimes encounter a document with only
|
|
1261
|
+
* a `content` member and therefor must not assume the existence
|
|
1262
|
+
* of `request` and `response` on the document.
|
|
1263
|
+
*
|
|
1264
|
+
* @method put
|
|
1265
|
+
* @param {StructuredDocument} doc
|
|
1266
|
+
* @returns {ResourceDocument}
|
|
1128
1267
|
* @public
|
|
1129
|
-
* @param identifier
|
|
1130
|
-
* @param data
|
|
1131
|
-
* @param hasRecord
|
|
1132
|
-
* @returns {void | string[]} if `hasRecord` is true then calculated key changes should be returned
|
|
1133
1268
|
*/
|
|
1134
|
-
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
|
|
1143
|
-
return recordData.pushData(data, hasRecord);
|
|
1269
|
+
put(doc) {
|
|
1270
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1271
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1272
|
+
if (doc instanceof Error) {
|
|
1273
|
+
// in legacy we don't know how to handle this
|
|
1274
|
+
throw doc;
|
|
1275
|
+
}
|
|
1276
|
+
return legacyCachePut(_classPrivateFieldBase(this, _store)[_store], doc);
|
|
1144
1277
|
}
|
|
1145
|
-
return
|
|
1278
|
+
return cache.put(doc);
|
|
1146
1279
|
}
|
|
1147
1280
|
|
|
1148
1281
|
/**
|
|
@@ -1151,48 +1284,49 @@ class NonSingletonCacheManager {
|
|
|
1151
1284
|
* Note: currently the only valid operation is a MergeOperation
|
|
1152
1285
|
* which occurs when a collision of identifiers is detected.
|
|
1153
1286
|
*
|
|
1154
|
-
* @method
|
|
1287
|
+
* @method patch
|
|
1155
1288
|
* @public
|
|
1156
1289
|
* @param op the operation to perform
|
|
1157
1290
|
* @returns {void}
|
|
1158
1291
|
*/
|
|
1159
|
-
|
|
1160
|
-
const
|
|
1161
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1292
|
+
patch(op) {
|
|
1293
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1294
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1162
1295
|
return;
|
|
1163
1296
|
}
|
|
1164
|
-
|
|
1297
|
+
cache.patch(op);
|
|
1165
1298
|
}
|
|
1166
1299
|
|
|
1167
1300
|
/**
|
|
1168
1301
|
* Update resource data with a local mutation. Currently supports operations
|
|
1169
1302
|
* on relationships only.
|
|
1170
1303
|
*
|
|
1171
|
-
* @method
|
|
1304
|
+
* @method mutate
|
|
1172
1305
|
* @public
|
|
1173
|
-
* @param
|
|
1306
|
+
* @param mutation
|
|
1174
1307
|
*/
|
|
1175
|
-
//
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1308
|
+
// isResource is only needed for interop with v1 cache
|
|
1309
|
+
mutate(mutation, isResource) {
|
|
1310
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1311
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1312
|
+
const instanceCache = _classPrivateFieldBase(this, _store)[_store]._instanceCache;
|
|
1313
|
+
switch (mutation.op) {
|
|
1180
1314
|
case 'addToRelatedRecords':
|
|
1181
|
-
|
|
1315
|
+
cache.addToHasMany(mutation.field, mutation.value.map(i => instanceCache.getResourceCache(i)), mutation.index);
|
|
1182
1316
|
return;
|
|
1183
1317
|
case 'removeFromRelatedRecords':
|
|
1184
|
-
|
|
1318
|
+
cache.removeFromHasMany(mutation.field, mutation.value.map(i => instanceCache.getResourceCache(i)));
|
|
1185
1319
|
return;
|
|
1186
1320
|
case 'replaceRelatedRecords':
|
|
1187
|
-
|
|
1321
|
+
cache.setDirtyHasMany(mutation.field, mutation.value.map(i => instanceCache.getResourceCache(i)));
|
|
1188
1322
|
return;
|
|
1189
1323
|
case 'replaceRelatedRecord':
|
|
1190
1324
|
if (isResource) {
|
|
1191
|
-
|
|
1325
|
+
cache.setDirtyBelongsTo(mutation.field, mutation.value ? instanceCache.getResourceCache(mutation.value) : null);
|
|
1192
1326
|
return;
|
|
1193
1327
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1328
|
+
cache.removeFromHasMany(mutation.field, [instanceCache.getResourceCache(mutation.prior)]);
|
|
1329
|
+
cache.addToHasMany(mutation.field, [instanceCache.getResourceCache(mutation.value)], mutation.index);
|
|
1196
1330
|
return;
|
|
1197
1331
|
case 'sortRelatedRecords':
|
|
1198
1332
|
return;
|
|
@@ -1200,10 +1334,255 @@ class NonSingletonCacheManager {
|
|
|
1200
1334
|
return;
|
|
1201
1335
|
}
|
|
1202
1336
|
} else {
|
|
1203
|
-
|
|
1337
|
+
cache.mutate(mutation);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/**
|
|
1342
|
+
* Peek resource data from the Cache.
|
|
1343
|
+
*
|
|
1344
|
+
* In development, if the return value
|
|
1345
|
+
* is JSON the return value
|
|
1346
|
+
* will be deep-cloned and deep-frozen
|
|
1347
|
+
* to prevent mutation thereby enforcing cache
|
|
1348
|
+
* Immutability.
|
|
1349
|
+
*
|
|
1350
|
+
* This form of peek is useful for implementations
|
|
1351
|
+
* that want to feed raw-data from cache to the UI
|
|
1352
|
+
* or which want to interact with a blob of data
|
|
1353
|
+
* directly from the presentation cache.
|
|
1354
|
+
*
|
|
1355
|
+
* An implementation might want to do this because
|
|
1356
|
+
* de-referencing records which read from their own
|
|
1357
|
+
* blob is generally safer because the record does
|
|
1358
|
+
* not require retainining connections to the Store
|
|
1359
|
+
* and Cache to present data on a per-field basis.
|
|
1360
|
+
*
|
|
1361
|
+
* This generally takes the place of `getAttr` as
|
|
1362
|
+
* an API and may even take the place of `getRelationship`
|
|
1363
|
+
* depending on implementation specifics, though this
|
|
1364
|
+
* latter usage is less recommended due to the advantages
|
|
1365
|
+
* of the Graph handling necessary entanglements and
|
|
1366
|
+
* notifications for relational data.
|
|
1367
|
+
*
|
|
1368
|
+
* @method peek
|
|
1369
|
+
* @public
|
|
1370
|
+
* @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
|
|
1371
|
+
* @returns {ResourceDocument | ResourceBlob | null} the known resource data
|
|
1372
|
+
*/
|
|
1373
|
+
|
|
1374
|
+
peek(identifier) {
|
|
1375
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1376
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1377
|
+
throw new Error(`Expected cache to implement peek`);
|
|
1378
|
+
}
|
|
1379
|
+
return cache.peek(identifier);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* Peek the Cache for the existing request data associated with
|
|
1384
|
+
* a cacheable request
|
|
1385
|
+
*
|
|
1386
|
+
* @method peekRequest
|
|
1387
|
+
* @param {StableDocumentIdentifier}
|
|
1388
|
+
* @returns {StableDocumentIdentifier | null}
|
|
1389
|
+
* @public
|
|
1390
|
+
*/
|
|
1391
|
+
peekRequest(identifier) {
|
|
1392
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1393
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1394
|
+
throw new Error(`Expected cache to implement peekRequest`);
|
|
1395
|
+
}
|
|
1396
|
+
return cache.peekRequest(identifier);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/**
|
|
1400
|
+
* Push resource data from a remote source into the cache for this identifier
|
|
1401
|
+
*
|
|
1402
|
+
* @method upsert
|
|
1403
|
+
* @public
|
|
1404
|
+
* @param identifier
|
|
1405
|
+
* @param data
|
|
1406
|
+
* @param hasRecord
|
|
1407
|
+
* @returns {void | string[]} if `hasRecord` is true then calculated key changes should be returned
|
|
1408
|
+
*/
|
|
1409
|
+
upsert(identifier, data, hasRecord) {
|
|
1410
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1411
|
+
// called by something V1
|
|
1412
|
+
if (!isStableIdentifier(identifier)) {
|
|
1413
|
+
data = identifier;
|
|
1414
|
+
hasRecord = data;
|
|
1415
|
+
identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1416
|
+
}
|
|
1417
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1418
|
+
return cache.pushData(data, hasRecord);
|
|
1419
|
+
}
|
|
1420
|
+
return cache.upsert(identifier, data, hasRecord);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// Cache Forking Support
|
|
1424
|
+
// =====================
|
|
1425
|
+
|
|
1426
|
+
/**
|
|
1427
|
+
* Create a fork of the cache from the current state.
|
|
1428
|
+
*
|
|
1429
|
+
* Applications should typically not call this method themselves,
|
|
1430
|
+
* preferring instead to fork at the Store level, which will
|
|
1431
|
+
* utilize this method to fork the cache.
|
|
1432
|
+
*
|
|
1433
|
+
* @method fork
|
|
1434
|
+
* @public
|
|
1435
|
+
* @returns Promise<Cache>
|
|
1436
|
+
*/
|
|
1437
|
+
fork() {
|
|
1438
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1439
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1440
|
+
throw new Error(`Expected cache to implement fork`);
|
|
1441
|
+
}
|
|
1442
|
+
return cache.fork();
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Merge a fork back into a parent Cache.
|
|
1447
|
+
*
|
|
1448
|
+
* Applications should typically not call this method themselves,
|
|
1449
|
+
* preferring instead to merge at the Store level, which will
|
|
1450
|
+
* utilize this method to merge the caches.
|
|
1451
|
+
*
|
|
1452
|
+
* @method merge
|
|
1453
|
+
* @param {Cache} cache
|
|
1454
|
+
* @public
|
|
1455
|
+
* @returns Promise<void>
|
|
1456
|
+
*/
|
|
1457
|
+
merge(updates) {
|
|
1458
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1459
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1460
|
+
throw new Error(`Expected cache to implement merge`);
|
|
1461
|
+
}
|
|
1462
|
+
return cache.merge(updates);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/**
|
|
1466
|
+
* Generate the list of changes applied to all
|
|
1467
|
+
* record in the store.
|
|
1468
|
+
*
|
|
1469
|
+
* Each individual resource or document that has
|
|
1470
|
+
* been mutated should be described as an individual
|
|
1471
|
+
* `Change` entry in the returned array.
|
|
1472
|
+
*
|
|
1473
|
+
* A `Change` is described by an object containing up to
|
|
1474
|
+
* three properties: (1) the `identifier` of the entity that
|
|
1475
|
+
* changed; (2) the `op` code of that change being one of
|
|
1476
|
+
* `upsert` or `remove`, and if the op is `upsert` a `patch`
|
|
1477
|
+
* containing the data to merge into the cache for the given
|
|
1478
|
+
* entity.
|
|
1479
|
+
*
|
|
1480
|
+
* This `patch` is opaque to the Store but should be understood
|
|
1481
|
+
* by the Cache and may expect to be utilized by an Adapter
|
|
1482
|
+
* when generating data during a `save` operation.
|
|
1483
|
+
*
|
|
1484
|
+
* It is generally recommended that the `patch` contain only
|
|
1485
|
+
* the updated state, ignoring fields that are unchanged
|
|
1486
|
+
*
|
|
1487
|
+
* ```ts
|
|
1488
|
+
* interface Change {
|
|
1489
|
+
* identifier: StableRecordIdentifier | StableDocumentIdentifier;
|
|
1490
|
+
* op: 'upsert' | 'remove';
|
|
1491
|
+
* patch?: unknown;
|
|
1492
|
+
* }
|
|
1493
|
+
* ```
|
|
1494
|
+
*
|
|
1495
|
+
* @method diff
|
|
1496
|
+
* @public
|
|
1497
|
+
*/
|
|
1498
|
+
diff() {
|
|
1499
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1500
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1501
|
+
throw new Error(`Expected cache to implement diff`);
|
|
1502
|
+
}
|
|
1503
|
+
return cache.diff();
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// SSR Support
|
|
1507
|
+
// ===========
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* Serialize the entire contents of the Cache into a Stream
|
|
1511
|
+
* which may be fed back into a new instance of the same Cache
|
|
1512
|
+
* via `cache.hydrate`.
|
|
1513
|
+
*
|
|
1514
|
+
* @method dump
|
|
1515
|
+
* @returns {Promise<ReadableStream>}
|
|
1516
|
+
* @public
|
|
1517
|
+
*/
|
|
1518
|
+
dump() {
|
|
1519
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1520
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1521
|
+
throw new Error(`Expected cache to implement dump`);
|
|
1522
|
+
}
|
|
1523
|
+
return cache.dump();
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
/**
|
|
1527
|
+
* hydrate a Cache from a Stream with content previously serialized
|
|
1528
|
+
* from another instance of the same Cache, resolving when hydration
|
|
1529
|
+
* is complete.
|
|
1530
|
+
*
|
|
1531
|
+
* This method should expect to be called both in the context of restoring
|
|
1532
|
+
* the Cache during application rehydration after SSR **AND** at unknown
|
|
1533
|
+
* times during the lifetime of an already booted application when it is
|
|
1534
|
+
* desired to bulk-load additional information into the cache. This latter
|
|
1535
|
+
* behavior supports optimizing pre/fetching of data for route transitions
|
|
1536
|
+
* via data-only SSR modes.
|
|
1537
|
+
*
|
|
1538
|
+
* @method hydrate
|
|
1539
|
+
* @param {ReadableStream} stream
|
|
1540
|
+
* @returns {Promise<void>}
|
|
1541
|
+
* @public
|
|
1542
|
+
*/
|
|
1543
|
+
hydrate(stream) {
|
|
1544
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1545
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1546
|
+
throw new Error(`Expected cache to implement hydrate`);
|
|
1204
1547
|
}
|
|
1548
|
+
return cache.hydrate(stream);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// Cache
|
|
1552
|
+
// =====
|
|
1553
|
+
|
|
1554
|
+
/**
|
|
1555
|
+
* Retrieve the identifier for this v1 cache
|
|
1556
|
+
*
|
|
1557
|
+
* DEPRECATED Caches should not be assumed to be 1:1 with resources
|
|
1558
|
+
*
|
|
1559
|
+
* @method getResourceIdentifier
|
|
1560
|
+
* @public
|
|
1561
|
+
* @deprecated
|
|
1562
|
+
*/
|
|
1563
|
+
getResourceIdentifier() {
|
|
1564
|
+
return _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
* Push resource data from a remote source into the cache for this identifier
|
|
1569
|
+
*
|
|
1570
|
+
* DEPRECATED Use upsert. Caches should not be assumed to be 1:1 with resources
|
|
1571
|
+
*
|
|
1572
|
+
* @method pushData
|
|
1573
|
+
* @param data
|
|
1574
|
+
* @param hasRecord
|
|
1575
|
+
* @returns {void | string[]} if `hasRecord` is true then calculated key changes should be returned
|
|
1576
|
+
* @public
|
|
1577
|
+
* @deprecated
|
|
1578
|
+
*/
|
|
1579
|
+
pushData(data, hasRecord) {
|
|
1580
|
+
return this.upsert(_classPrivateFieldBase(this, _identifier)[_identifier], data, hasRecord);
|
|
1205
1581
|
}
|
|
1206
1582
|
|
|
1583
|
+
// Resource Support
|
|
1584
|
+
// ================
|
|
1585
|
+
|
|
1207
1586
|
/**
|
|
1208
1587
|
* [LIFECYLCE] Signal to the cache that a new record has been instantiated on the client
|
|
1209
1588
|
*
|
|
@@ -1221,15 +1600,15 @@ class NonSingletonCacheManager {
|
|
|
1221
1600
|
options = identifier;
|
|
1222
1601
|
identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1223
1602
|
}
|
|
1224
|
-
|
|
1603
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1225
1604
|
|
|
1226
1605
|
// TODO deprecate return value
|
|
1227
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1228
|
-
|
|
1606
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1607
|
+
cache.clientDidCreate();
|
|
1229
1608
|
// if a V2 is calling a V1 we need to call both methods
|
|
1230
|
-
return
|
|
1609
|
+
return cache._initRecordCreateOptions(options);
|
|
1231
1610
|
} else {
|
|
1232
|
-
return
|
|
1611
|
+
return cache.clientDidCreate(identifier, options);
|
|
1233
1612
|
}
|
|
1234
1613
|
}
|
|
1235
1614
|
|
|
@@ -1245,9 +1624,9 @@ class NonSingletonCacheManager {
|
|
|
1245
1624
|
* @param options
|
|
1246
1625
|
*/
|
|
1247
1626
|
_initRecordCreateOptions(options) {
|
|
1248
|
-
|
|
1249
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1250
|
-
return
|
|
1627
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1628
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1629
|
+
return cache._initRecordCreateOptions(options);
|
|
1251
1630
|
}
|
|
1252
1631
|
}
|
|
1253
1632
|
|
|
@@ -1260,7 +1639,7 @@ class NonSingletonCacheManager {
|
|
|
1260
1639
|
* @param identifier
|
|
1261
1640
|
*/
|
|
1262
1641
|
willCommit(identifier) {
|
|
1263
|
-
_classPrivateFieldBase(this,
|
|
1642
|
+
_classPrivateFieldBase(this, _cache)[_cache].willCommit(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1264
1643
|
}
|
|
1265
1644
|
|
|
1266
1645
|
/**
|
|
@@ -1278,8 +1657,8 @@ class NonSingletonCacheManager {
|
|
|
1278
1657
|
data = identifier;
|
|
1279
1658
|
identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1280
1659
|
}
|
|
1281
|
-
|
|
1282
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1660
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1661
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.didCommit(data) : cache.didCommit(identifier, data);
|
|
1283
1662
|
}
|
|
1284
1663
|
|
|
1285
1664
|
/**
|
|
@@ -1292,7 +1671,7 @@ class NonSingletonCacheManager {
|
|
|
1292
1671
|
* @param errors
|
|
1293
1672
|
*/
|
|
1294
1673
|
commitWasRejected(identifier, errors) {
|
|
1295
|
-
_classPrivateFieldBase(this,
|
|
1674
|
+
_classPrivateFieldBase(this, _cache)[_cache].commitWasRejected(identifier || _classPrivateFieldBase(this, _identifier)[_identifier], errors);
|
|
1296
1675
|
}
|
|
1297
1676
|
|
|
1298
1677
|
/**
|
|
@@ -1304,16 +1683,16 @@ class NonSingletonCacheManager {
|
|
|
1304
1683
|
* @param identifier
|
|
1305
1684
|
*/
|
|
1306
1685
|
unloadRecord(identifier) {
|
|
1307
|
-
const
|
|
1308
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1309
|
-
|
|
1686
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1687
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1688
|
+
cache.unloadRecord();
|
|
1310
1689
|
} else {
|
|
1311
|
-
|
|
1690
|
+
cache.unloadRecord(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1312
1691
|
}
|
|
1313
1692
|
}
|
|
1314
1693
|
|
|
1315
|
-
//
|
|
1316
|
-
//
|
|
1694
|
+
// Granular Resource Data APIs
|
|
1695
|
+
// ===========================
|
|
1317
1696
|
|
|
1318
1697
|
/**
|
|
1319
1698
|
* Retrieve the data for an attribute from the cache
|
|
@@ -1330,8 +1709,8 @@ class NonSingletonCacheManager {
|
|
|
1330
1709
|
propertyName = identifier;
|
|
1331
1710
|
identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1332
1711
|
}
|
|
1333
|
-
|
|
1334
|
-
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1712
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1713
|
+
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.getAttr(propertyName) : cache.getAttr(identifier, propertyName);
|
|
1335
1714
|
}
|
|
1336
1715
|
|
|
1337
1716
|
/**
|
|
@@ -1344,8 +1723,8 @@ class NonSingletonCacheManager {
|
|
|
1344
1723
|
* @param value
|
|
1345
1724
|
*/
|
|
1346
1725
|
setAttr(identifier, propertyName, value) {
|
|
1347
|
-
|
|
1348
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1726
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1727
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.setDirtyAttribute(propertyName, value) : cache.setAttr(identifier, propertyName, value);
|
|
1349
1728
|
}
|
|
1350
1729
|
|
|
1351
1730
|
/**
|
|
@@ -1361,8 +1740,8 @@ class NonSingletonCacheManager {
|
|
|
1361
1740
|
* @param value
|
|
1362
1741
|
*/
|
|
1363
1742
|
setDirtyAttribute(propertyName, value) {
|
|
1364
|
-
|
|
1365
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1743
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1744
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.setDirtyAttribute(propertyName, value) : cache.setAttr(_classPrivateFieldBase(this, _identifier)[_identifier], propertyName, value);
|
|
1366
1745
|
}
|
|
1367
1746
|
|
|
1368
1747
|
/**
|
|
@@ -1377,11 +1756,11 @@ class NonSingletonCacheManager {
|
|
|
1377
1756
|
* @returns
|
|
1378
1757
|
*/
|
|
1379
1758
|
changedAttributes() {
|
|
1380
|
-
const
|
|
1381
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1382
|
-
return
|
|
1759
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1760
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1761
|
+
return cache.changedAttributes();
|
|
1383
1762
|
}
|
|
1384
|
-
return
|
|
1763
|
+
return cache.changedAttrs(_classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1385
1764
|
}
|
|
1386
1765
|
|
|
1387
1766
|
/**
|
|
@@ -1394,11 +1773,11 @@ class NonSingletonCacheManager {
|
|
|
1394
1773
|
* @returns
|
|
1395
1774
|
*/
|
|
1396
1775
|
changedAttrs(identifier) {
|
|
1397
|
-
const
|
|
1398
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1399
|
-
return
|
|
1776
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1777
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1778
|
+
return cache.changedAttributes();
|
|
1400
1779
|
}
|
|
1401
|
-
return
|
|
1780
|
+
return cache.changedAttrs(identifier);
|
|
1402
1781
|
}
|
|
1403
1782
|
|
|
1404
1783
|
/**
|
|
@@ -1412,8 +1791,8 @@ class NonSingletonCacheManager {
|
|
|
1412
1791
|
* @returns {boolean}
|
|
1413
1792
|
*/
|
|
1414
1793
|
hasChangedAttributes() {
|
|
1415
|
-
const
|
|
1416
|
-
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1794
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1795
|
+
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.hasChangedAttributes() : cache.hasChangedAttrs(_classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1417
1796
|
}
|
|
1418
1797
|
|
|
1419
1798
|
/**
|
|
@@ -1425,8 +1804,8 @@ class NonSingletonCacheManager {
|
|
|
1425
1804
|
* @returns {boolean}
|
|
1426
1805
|
*/
|
|
1427
1806
|
hasChangedAttrs(identifier) {
|
|
1428
|
-
const
|
|
1429
|
-
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1807
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1808
|
+
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.hasChangedAttributes() : cache.hasChangedAttrs(identifier);
|
|
1430
1809
|
}
|
|
1431
1810
|
|
|
1432
1811
|
/**
|
|
@@ -1440,8 +1819,8 @@ class NonSingletonCacheManager {
|
|
|
1440
1819
|
* @returns
|
|
1441
1820
|
*/
|
|
1442
1821
|
rollbackAttributes() {
|
|
1443
|
-
const
|
|
1444
|
-
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1822
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1823
|
+
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.rollbackAttributes() : cache.rollbackAttrs(_classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1445
1824
|
}
|
|
1446
1825
|
|
|
1447
1826
|
/**
|
|
@@ -1453,8 +1832,8 @@ class NonSingletonCacheManager {
|
|
|
1453
1832
|
* @returns the names of attributes that were restored
|
|
1454
1833
|
*/
|
|
1455
1834
|
rollbackAttrs(identifier) {
|
|
1456
|
-
const
|
|
1457
|
-
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1835
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1836
|
+
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.rollbackAttributes() : cache.rollbackAttrs(identifier);
|
|
1458
1837
|
}
|
|
1459
1838
|
|
|
1460
1839
|
// Relationships
|
|
@@ -1476,12 +1855,12 @@ class NonSingletonCacheManager {
|
|
|
1476
1855
|
* @returns resource relationship object
|
|
1477
1856
|
*/
|
|
1478
1857
|
getRelationship(identifier, propertyName, isCollection = false) {
|
|
1479
|
-
|
|
1480
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1858
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1859
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1481
1860
|
let isBelongsTo = !isCollection;
|
|
1482
|
-
return isBelongsTo ?
|
|
1861
|
+
return isBelongsTo ? cache.getBelongsTo(propertyName) : cache.getHasMany(propertyName);
|
|
1483
1862
|
}
|
|
1484
|
-
return
|
|
1863
|
+
return cache.getRelationship(identifier, propertyName);
|
|
1485
1864
|
}
|
|
1486
1865
|
|
|
1487
1866
|
/**
|
|
@@ -1496,12 +1875,12 @@ class NonSingletonCacheManager {
|
|
|
1496
1875
|
* @returns single resource relationship object
|
|
1497
1876
|
*/
|
|
1498
1877
|
getBelongsTo(propertyName) {
|
|
1499
|
-
|
|
1500
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1501
|
-
return
|
|
1878
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1879
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1880
|
+
return cache.getBelongsTo(propertyName);
|
|
1502
1881
|
} else {
|
|
1503
1882
|
let identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1504
|
-
return
|
|
1883
|
+
return cache.getRelationship(identifier, propertyName);
|
|
1505
1884
|
}
|
|
1506
1885
|
}
|
|
1507
1886
|
|
|
@@ -1517,12 +1896,12 @@ class NonSingletonCacheManager {
|
|
|
1517
1896
|
* @returns single resource relationship object
|
|
1518
1897
|
*/
|
|
1519
1898
|
getHasMany(propertyName) {
|
|
1520
|
-
|
|
1521
|
-
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1522
|
-
return
|
|
1899
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1900
|
+
if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
|
|
1901
|
+
return cache.getHasMany(propertyName);
|
|
1523
1902
|
} else {
|
|
1524
1903
|
let identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1525
|
-
return
|
|
1904
|
+
return cache.getRelationship(identifier, propertyName);
|
|
1526
1905
|
}
|
|
1527
1906
|
}
|
|
1528
1907
|
|
|
@@ -1538,8 +1917,8 @@ class NonSingletonCacheManager {
|
|
|
1538
1917
|
* @param value
|
|
1539
1918
|
*/
|
|
1540
1919
|
setDirtyBelongsTo(propertyName, value) {
|
|
1541
|
-
const
|
|
1542
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1920
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1921
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.setDirtyBelongsTo(propertyName, value) : cache.mutate({
|
|
1543
1922
|
op: 'replaceRelatedRecord',
|
|
1544
1923
|
record: _classPrivateFieldBase(this, _identifier)[_identifier],
|
|
1545
1924
|
field: propertyName,
|
|
@@ -1563,8 +1942,8 @@ class NonSingletonCacheManager {
|
|
|
1563
1942
|
*/
|
|
1564
1943
|
addToHasMany(propertyName, value, idx) {
|
|
1565
1944
|
const identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1566
|
-
const
|
|
1567
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1945
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1946
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.addToHasMany(propertyName, value, idx) : cache.mutate({
|
|
1568
1947
|
op: 'addToRelatedRecords',
|
|
1569
1948
|
field: propertyName,
|
|
1570
1949
|
record: identifier,
|
|
@@ -1585,8 +1964,8 @@ class NonSingletonCacheManager {
|
|
|
1585
1964
|
*/
|
|
1586
1965
|
removeFromHasMany(propertyName, value) {
|
|
1587
1966
|
const identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1588
|
-
const
|
|
1589
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1967
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1968
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.removeFromHasMany(propertyName, value) : cache.mutate({
|
|
1590
1969
|
op: 'removeFromRelatedRecords',
|
|
1591
1970
|
record: identifier,
|
|
1592
1971
|
field: propertyName,
|
|
@@ -1606,8 +1985,8 @@ class NonSingletonCacheManager {
|
|
|
1606
1985
|
* @param value
|
|
1607
1986
|
*/
|
|
1608
1987
|
setDirtyHasMany(propertyName, value) {
|
|
1609
|
-
|
|
1610
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
1988
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
1989
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.setDirtyHasMany(propertyName, value) : cache.mutate({
|
|
1611
1990
|
op: 'replaceRelatedRecords',
|
|
1612
1991
|
record: _classPrivateFieldBase(this, _identifier)[_identifier],
|
|
1613
1992
|
field: propertyName,
|
|
@@ -1615,8 +1994,8 @@ class NonSingletonCacheManager {
|
|
|
1615
1994
|
});
|
|
1616
1995
|
}
|
|
1617
1996
|
|
|
1618
|
-
// State
|
|
1619
|
-
//
|
|
1997
|
+
// Resource State
|
|
1998
|
+
// ===============
|
|
1620
1999
|
|
|
1621
2000
|
/**
|
|
1622
2001
|
* Update the cache state for the given resource to be marked as locally deleted,
|
|
@@ -1632,8 +2011,8 @@ class NonSingletonCacheManager {
|
|
|
1632
2011
|
isDeleted = identifier;
|
|
1633
2012
|
identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
|
|
1634
2013
|
}
|
|
1635
|
-
const
|
|
1636
|
-
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
2014
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
2015
|
+
_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.setIsDeleted(isDeleted) : cache.setIsDeleted(identifier, isDeleted);
|
|
1637
2016
|
}
|
|
1638
2017
|
|
|
1639
2018
|
/**
|
|
@@ -1645,7 +2024,7 @@ class NonSingletonCacheManager {
|
|
|
1645
2024
|
* @returns
|
|
1646
2025
|
*/
|
|
1647
2026
|
getErrors(identifier) {
|
|
1648
|
-
return _classPrivateFieldBase(this,
|
|
2027
|
+
return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1649
2028
|
}
|
|
1650
2029
|
|
|
1651
2030
|
/**
|
|
@@ -1657,8 +2036,8 @@ class NonSingletonCacheManager {
|
|
|
1657
2036
|
* @returns {boolean}
|
|
1658
2037
|
*/
|
|
1659
2038
|
isEmpty(identifier) {
|
|
1660
|
-
const
|
|
1661
|
-
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](
|
|
2039
|
+
const cache = _classPrivateFieldBase(this, _cache)[_cache];
|
|
2040
|
+
return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache) ? cache.isEmpty?.(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]) || false : cache.isEmpty(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1662
2041
|
}
|
|
1663
2042
|
|
|
1664
2043
|
/**
|
|
@@ -1671,7 +2050,7 @@ class NonSingletonCacheManager {
|
|
|
1671
2050
|
* @returns {boolean}
|
|
1672
2051
|
*/
|
|
1673
2052
|
isNew(identifier) {
|
|
1674
|
-
return _classPrivateFieldBase(this,
|
|
2053
|
+
return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1675
2054
|
}
|
|
1676
2055
|
|
|
1677
2056
|
/**
|
|
@@ -1684,7 +2063,7 @@ class NonSingletonCacheManager {
|
|
|
1684
2063
|
* @returns {boolean}
|
|
1685
2064
|
*/
|
|
1686
2065
|
isDeleted(identifier) {
|
|
1687
|
-
return _classPrivateFieldBase(this,
|
|
2066
|
+
return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1688
2067
|
}
|
|
1689
2068
|
|
|
1690
2069
|
/**
|
|
@@ -1697,106 +2076,120 @@ class NonSingletonCacheManager {
|
|
|
1697
2076
|
* @returns {boolean}
|
|
1698
2077
|
*/
|
|
1699
2078
|
isDeletionCommitted(identifier) {
|
|
1700
|
-
return _classPrivateFieldBase(this,
|
|
2079
|
+
return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
|
|
1701
2080
|
}
|
|
1702
2081
|
}
|
|
1703
|
-
function _isDeprecated2(
|
|
1704
|
-
let version =
|
|
2082
|
+
function _isDeprecated2(cache) {
|
|
2083
|
+
let version = cache.version || '1';
|
|
1705
2084
|
return version !== this.version;
|
|
1706
2085
|
}
|
|
1707
|
-
var
|
|
1708
|
-
var _recordData3 = /*#__PURE__*/_classPrivateFieldKey("recordData");
|
|
2086
|
+
var _cache3 = /*#__PURE__*/_classPrivateFieldKey("cache");
|
|
1709
2087
|
class SingletonCacheManager {
|
|
1710
|
-
constructor() {
|
|
1711
|
-
Object.defineProperty(this, _recordData3, {
|
|
1712
|
-
value: _recordData4
|
|
1713
|
-
});
|
|
2088
|
+
constructor(cache) {
|
|
1714
2089
|
this.version = '2';
|
|
1715
|
-
Object.defineProperty(this,
|
|
2090
|
+
Object.defineProperty(this, _cache3, {
|
|
1716
2091
|
writable: true,
|
|
1717
2092
|
value: void 0
|
|
1718
2093
|
});
|
|
1719
|
-
_classPrivateFieldBase(this,
|
|
2094
|
+
_classPrivateFieldBase(this, _cache3)[_cache3] = cache;
|
|
1720
2095
|
}
|
|
1721
|
-
|
|
1722
|
-
_classPrivateFieldBase(this,
|
|
2096
|
+
put(doc) {
|
|
2097
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].put(doc);
|
|
1723
2098
|
}
|
|
2099
|
+
peek(identifier) {
|
|
2100
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].peek(identifier);
|
|
2101
|
+
}
|
|
2102
|
+
peekRequest(identifier) {
|
|
2103
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].peekRequest(identifier);
|
|
2104
|
+
}
|
|
2105
|
+
fork() {
|
|
2106
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].fork();
|
|
2107
|
+
}
|
|
2108
|
+
merge(cache) {
|
|
2109
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].merge(cache);
|
|
2110
|
+
}
|
|
2111
|
+
diff() {
|
|
2112
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].diff();
|
|
2113
|
+
}
|
|
2114
|
+
dump() {
|
|
2115
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].dump();
|
|
2116
|
+
}
|
|
2117
|
+
hydrate(stream) {
|
|
2118
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].hydrate(stream);
|
|
2119
|
+
}
|
|
2120
|
+
|
|
1724
2121
|
// Cache
|
|
1725
2122
|
// =====
|
|
1726
2123
|
|
|
1727
|
-
|
|
1728
|
-
return _classPrivateFieldBase(this,
|
|
2124
|
+
upsert(identifier, data, hasRecord) {
|
|
2125
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].upsert(identifier, data, hasRecord);
|
|
1729
2126
|
}
|
|
1730
|
-
|
|
1731
|
-
_classPrivateFieldBase(this,
|
|
2127
|
+
patch(op) {
|
|
2128
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].patch(op);
|
|
1732
2129
|
}
|
|
1733
2130
|
clientDidCreate(identifier, options) {
|
|
1734
|
-
return _classPrivateFieldBase(this,
|
|
2131
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].clientDidCreate(identifier, options);
|
|
1735
2132
|
}
|
|
1736
2133
|
willCommit(identifier) {
|
|
1737
|
-
_classPrivateFieldBase(this,
|
|
2134
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].willCommit(identifier);
|
|
1738
2135
|
}
|
|
1739
2136
|
didCommit(identifier, data) {
|
|
1740
|
-
_classPrivateFieldBase(this,
|
|
2137
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].didCommit(identifier, data);
|
|
1741
2138
|
}
|
|
1742
2139
|
commitWasRejected(identifier, errors) {
|
|
1743
|
-
_classPrivateFieldBase(this,
|
|
2140
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].commitWasRejected(identifier, errors);
|
|
1744
2141
|
}
|
|
1745
2142
|
unloadRecord(identifier) {
|
|
1746
|
-
_classPrivateFieldBase(this,
|
|
2143
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].unloadRecord(identifier);
|
|
1747
2144
|
}
|
|
1748
2145
|
|
|
1749
2146
|
// Attrs
|
|
1750
2147
|
// =====
|
|
1751
2148
|
|
|
1752
2149
|
getAttr(identifier, propertyName) {
|
|
1753
|
-
return _classPrivateFieldBase(this,
|
|
2150
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].getAttr(identifier, propertyName);
|
|
1754
2151
|
}
|
|
1755
2152
|
setAttr(identifier, propertyName, value) {
|
|
1756
|
-
_classPrivateFieldBase(this,
|
|
2153
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].setAttr(identifier, propertyName, value);
|
|
1757
2154
|
}
|
|
1758
2155
|
changedAttrs(identifier) {
|
|
1759
|
-
return _classPrivateFieldBase(this,
|
|
2156
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].changedAttrs(identifier);
|
|
1760
2157
|
}
|
|
1761
2158
|
hasChangedAttrs(identifier) {
|
|
1762
|
-
return _classPrivateFieldBase(this,
|
|
2159
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].hasChangedAttrs(identifier);
|
|
1763
2160
|
}
|
|
1764
2161
|
rollbackAttrs(identifier) {
|
|
1765
|
-
return _classPrivateFieldBase(this,
|
|
2162
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].rollbackAttrs(identifier);
|
|
1766
2163
|
}
|
|
1767
2164
|
getRelationship(identifier, propertyName) {
|
|
1768
|
-
return _classPrivateFieldBase(this,
|
|
2165
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].getRelationship(identifier, propertyName);
|
|
1769
2166
|
}
|
|
1770
|
-
|
|
1771
|
-
_classPrivateFieldBase(this,
|
|
2167
|
+
mutate(mutation) {
|
|
2168
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].mutate(mutation);
|
|
1772
2169
|
}
|
|
1773
2170
|
|
|
1774
2171
|
// State
|
|
1775
2172
|
// =============
|
|
1776
2173
|
|
|
1777
2174
|
setIsDeleted(identifier, isDeleted) {
|
|
1778
|
-
_classPrivateFieldBase(this,
|
|
2175
|
+
_classPrivateFieldBase(this, _cache3)[_cache3].setIsDeleted(identifier, isDeleted);
|
|
1779
2176
|
}
|
|
1780
2177
|
getErrors(identifier) {
|
|
1781
|
-
return _classPrivateFieldBase(this,
|
|
2178
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].getErrors(identifier);
|
|
1782
2179
|
}
|
|
1783
2180
|
isEmpty(identifier) {
|
|
1784
|
-
return _classPrivateFieldBase(this,
|
|
2181
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].isEmpty(identifier);
|
|
1785
2182
|
}
|
|
1786
2183
|
isNew(identifier) {
|
|
1787
|
-
return _classPrivateFieldBase(this,
|
|
2184
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].isNew(identifier);
|
|
1788
2185
|
}
|
|
1789
2186
|
isDeleted(identifier) {
|
|
1790
|
-
return _classPrivateFieldBase(this,
|
|
2187
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].isDeleted(identifier);
|
|
1791
2188
|
}
|
|
1792
2189
|
isDeletionCommitted(identifier) {
|
|
1793
|
-
return _classPrivateFieldBase(this,
|
|
2190
|
+
return _classPrivateFieldBase(this, _cache3)[_cache3].isDeletionCommitted(identifier);
|
|
1794
2191
|
}
|
|
1795
2192
|
}
|
|
1796
|
-
function _recordData4(identifier) {
|
|
1797
|
-
assert(`No RecordData Yet Exists!`, _classPrivateFieldBase(this, _recordDatas)[_recordDatas].has(identifier));
|
|
1798
|
-
return _classPrivateFieldBase(this, _recordDatas)[_recordDatas].get(identifier);
|
|
1799
|
-
}
|
|
1800
2193
|
function constructResource(type, id, lid) {
|
|
1801
2194
|
if (typeof type === 'object' && type !== null) {
|
|
1802
2195
|
let resource = type;
|
|
@@ -1887,10 +2280,9 @@ class LegacyWrapper {
|
|
|
1887
2280
|
this._scheduleNotification(identifier, key);
|
|
1888
2281
|
return;
|
|
1889
2282
|
}
|
|
2283
|
+
|
|
2284
|
+
// @ts-expect-error
|
|
1890
2285
|
this._store.notifications.notify(identifier, namespace, key);
|
|
1891
|
-
if (namespace === 'state') {
|
|
1892
|
-
this._store.recordArrayManager.identifierChanged(identifier);
|
|
1893
|
-
}
|
|
1894
2286
|
}
|
|
1895
2287
|
notifyErrorsChange(type, id, lid) {
|
|
1896
2288
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1CACHE_STORE_APIS)) {
|
|
@@ -2006,7 +2398,6 @@ class LegacyWrapper {
|
|
|
2006
2398
|
const resource = constructResource(type, id, lid);
|
|
2007
2399
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
2008
2400
|
this._store.notifications.notify(identifier, 'state');
|
|
2009
|
-
this._store.recordArrayManager.identifierChanged(identifier);
|
|
2010
2401
|
}
|
|
2011
2402
|
recordDataFor(type, id, lid) {
|
|
2012
2403
|
let identifier;
|
|
@@ -2029,12 +2420,14 @@ class LegacyWrapper {
|
|
|
2029
2420
|
assert(`Expected a stable identifier`, isStableIdentifier(type));
|
|
2030
2421
|
identifier = type;
|
|
2031
2422
|
}
|
|
2032
|
-
const
|
|
2033
|
-
if (
|
|
2034
|
-
|
|
2035
|
-
|
|
2423
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK) ? this._store._instanceCache.getResourceCache(identifier) : this._store.cache;
|
|
2424
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1CACHE_STORE_APIS)) {
|
|
2425
|
+
if (!id && !lid && typeof type === 'string') {
|
|
2426
|
+
cache.clientDidCreate(identifier);
|
|
2427
|
+
this._store.recordArrayManager.identifierAdded(identifier);
|
|
2428
|
+
}
|
|
2036
2429
|
}
|
|
2037
|
-
return
|
|
2430
|
+
return cache;
|
|
2038
2431
|
}
|
|
2039
2432
|
setRecordId(type, id, lid) {
|
|
2040
2433
|
let identifier;
|
|
@@ -2154,17 +2547,27 @@ class V2CacheStoreWrapper {
|
|
|
2154
2547
|
this._scheduleNotification(identifier, key);
|
|
2155
2548
|
return;
|
|
2156
2549
|
}
|
|
2550
|
+
|
|
2551
|
+
// @ts-expect-error
|
|
2157
2552
|
this._store.notifications.notify(identifier, namespace, key);
|
|
2158
|
-
if (namespace === 'state') {
|
|
2159
|
-
this._store.recordArrayManager.identifierChanged(identifier);
|
|
2160
|
-
}
|
|
2161
2553
|
}
|
|
2162
2554
|
getSchemaDefinitionService() {
|
|
2163
2555
|
return this._store.getSchemaDefinitionService();
|
|
2164
2556
|
}
|
|
2165
2557
|
recordDataFor(identifier) {
|
|
2558
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK)) {
|
|
2559
|
+
deprecate(`StoreWrapper.recordDataFor is deprecated. With Singleton Cache, this method is no longer needed as the caller is its own cache reference.`, false, {
|
|
2560
|
+
for: '@ember-data/store',
|
|
2561
|
+
id: 'ember-data:deprecate-record-data-for',
|
|
2562
|
+
since: {
|
|
2563
|
+
available: '4.10',
|
|
2564
|
+
enabled: '4.10'
|
|
2565
|
+
},
|
|
2566
|
+
until: '5.0'
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2166
2569
|
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
2167
|
-
return this._store._instanceCache.
|
|
2570
|
+
return macroCondition(getOwnConfig().deprecations.DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK) ? this._store._instanceCache.getResourceCache(identifier) : void 0;
|
|
2168
2571
|
}
|
|
2169
2572
|
setRecordId(identifier, id) {
|
|
2170
2573
|
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
@@ -2183,496 +2586,24 @@ class V2CacheStoreWrapper {
|
|
|
2183
2586
|
}
|
|
2184
2587
|
}
|
|
2185
2588
|
const CacheStoreWrapper = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1CACHE_STORE_APIS) ? LegacyWrapper : V2CacheStoreWrapper;
|
|
2589
|
+
let _peekGraph;
|
|
2590
|
+
if (macroCondition(getOwnConfig().packages.HAS_GRAPH_PACKAGE)) {
|
|
2591
|
+
let __peekGraph;
|
|
2592
|
+
_peekGraph = wrapper => {
|
|
2593
|
+
let a = importSync('@ember-data/graph/-private').peekGraph;
|
|
2594
|
+
__peekGraph = __peekGraph || a;
|
|
2595
|
+
return __peekGraph(wrapper);
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2186
2598
|
|
|
2187
2599
|
/**
|
|
2188
2600
|
@module @ember-data/store
|
|
2189
2601
|
*/
|
|
2190
|
-
/**
|
|
2191
|
-
Snapshot is not directly instantiable.
|
|
2192
|
-
Instances are provided to a consuming application's
|
|
2193
|
-
adapters and serializers for certain requests.
|
|
2194
|
-
|
|
2195
|
-
@class Snapshot
|
|
2196
|
-
@public
|
|
2197
|
-
*/
|
|
2198
|
-
class Snapshot {
|
|
2199
|
-
/**
|
|
2200
|
-
* @method constructor
|
|
2201
|
-
* @constructor
|
|
2202
|
-
* @private
|
|
2203
|
-
* @param options
|
|
2204
|
-
* @param identifier
|
|
2205
|
-
* @param _store
|
|
2206
|
-
*/
|
|
2207
|
-
constructor(options, identifier, store) {
|
|
2208
|
-
this._store = store;
|
|
2209
|
-
this.__attributes = null;
|
|
2210
|
-
this._belongsToRelationships = Object.create(null);
|
|
2211
|
-
this._belongsToIds = Object.create(null);
|
|
2212
|
-
this._hasManyRelationships = Object.create(null);
|
|
2213
|
-
this._hasManyIds = Object.create(null);
|
|
2214
|
-
const hasRecord = !!store._instanceCache.peek({
|
|
2215
|
-
identifier,
|
|
2216
|
-
bucket: 'record'
|
|
2217
|
-
});
|
|
2218
|
-
this.modelName = identifier.type;
|
|
2219
2602
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
@type {StableRecordIdentifier}
|
|
2225
|
-
*/
|
|
2226
|
-
this.identifier = identifier;
|
|
2227
|
-
|
|
2228
|
-
/*
|
|
2229
|
-
If the we do not yet have a record, then we are
|
|
2230
|
-
likely a snapshot being provided to a find request, so we
|
|
2231
|
-
populate __attributes lazily. Else, to preserve the "moment
|
|
2232
|
-
in time" in which a snapshot is created, we greedily grab
|
|
2233
|
-
the values.
|
|
2234
|
-
*/
|
|
2235
|
-
if (hasRecord) {
|
|
2236
|
-
this._attributes;
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
|
-
/**
|
|
2240
|
-
The id of the snapshot's underlying record
|
|
2241
|
-
Example
|
|
2242
|
-
```javascript
|
|
2243
|
-
// store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' });
|
|
2244
|
-
postSnapshot.id; // => '1'
|
|
2245
|
-
```
|
|
2246
|
-
@property id
|
|
2247
|
-
@type {String}
|
|
2248
|
-
@public
|
|
2249
|
-
*/
|
|
2250
|
-
this.id = identifier.id;
|
|
2251
|
-
|
|
2252
|
-
/**
|
|
2253
|
-
A hash of adapter options
|
|
2254
|
-
@property adapterOptions
|
|
2255
|
-
@type {Object}
|
|
2256
|
-
@public
|
|
2257
|
-
*/
|
|
2258
|
-
this.adapterOptions = options.adapterOptions;
|
|
2259
|
-
|
|
2260
|
-
/**
|
|
2261
|
-
If `include` was passed to the options hash for the request, the value
|
|
2262
|
-
would be available here.
|
|
2263
|
-
@property include
|
|
2264
|
-
@type {String|Array}
|
|
2265
|
-
@public
|
|
2266
|
-
*/
|
|
2267
|
-
this.include = options.include;
|
|
2268
|
-
|
|
2269
|
-
/**
|
|
2270
|
-
The name of the type of the underlying record for this snapshot, as a string.
|
|
2271
|
-
@property modelName
|
|
2272
|
-
@type {String}
|
|
2273
|
-
@public
|
|
2274
|
-
*/
|
|
2275
|
-
this.modelName = identifier.type;
|
|
2276
|
-
if (hasRecord) {
|
|
2277
|
-
this._changedAttributes = this._store._instanceCache.getRecordData(identifier).changedAttrs(identifier);
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
/**
|
|
2282
|
-
The underlying record for this snapshot. Can be used to access methods and
|
|
2283
|
-
properties defined on the record.
|
|
2284
|
-
Example
|
|
2285
|
-
```javascript
|
|
2286
|
-
let json = snapshot.record.toJSON();
|
|
2287
|
-
```
|
|
2288
|
-
@property record
|
|
2289
|
-
@type {Model}
|
|
2290
|
-
@public
|
|
2291
|
-
*/
|
|
2292
|
-
get record() {
|
|
2293
|
-
return this._store._instanceCache.getRecord(this.identifier);
|
|
2294
|
-
}
|
|
2295
|
-
get _attributes() {
|
|
2296
|
-
if (this.__attributes !== null) {
|
|
2297
|
-
return this.__attributes;
|
|
2298
|
-
}
|
|
2299
|
-
let attributes = this.__attributes = Object.create(null);
|
|
2300
|
-
const {
|
|
2301
|
-
identifier
|
|
2302
|
-
} = this;
|
|
2303
|
-
let attrs = Object.keys(this._store.getSchemaDefinitionService().attributesDefinitionFor(identifier));
|
|
2304
|
-
let recordData = this._store._instanceCache.getRecordData(identifier);
|
|
2305
|
-
attrs.forEach(keyName => {
|
|
2306
|
-
attributes[keyName] = recordData.getAttr(identifier, keyName);
|
|
2307
|
-
});
|
|
2308
|
-
return attributes;
|
|
2309
|
-
}
|
|
2310
|
-
|
|
2311
|
-
/**
|
|
2312
|
-
The type of the underlying record for this snapshot, as a Model.
|
|
2313
|
-
@property type
|
|
2314
|
-
@public
|
|
2315
|
-
@deprecated
|
|
2316
|
-
@type {Model}
|
|
2317
|
-
*/
|
|
2318
|
-
|
|
2319
|
-
get isNew() {
|
|
2320
|
-
const recordData = this._store._instanceCache.peek({
|
|
2321
|
-
identifier: this.identifier,
|
|
2322
|
-
bucket: 'recordData'
|
|
2323
|
-
});
|
|
2324
|
-
return recordData?.isNew(this.identifier) || false;
|
|
2325
|
-
}
|
|
2326
|
-
|
|
2327
|
-
/**
|
|
2328
|
-
Returns the value of an attribute.
|
|
2329
|
-
Example
|
|
2330
|
-
```javascript
|
|
2331
|
-
// store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' });
|
|
2332
|
-
postSnapshot.attr('author'); // => 'Tomster'
|
|
2333
|
-
postSnapshot.attr('title'); // => 'Ember.js rocks'
|
|
2334
|
-
```
|
|
2335
|
-
Note: Values are loaded eagerly and cached when the snapshot is created.
|
|
2336
|
-
@method attr
|
|
2337
|
-
@param {String} keyName
|
|
2338
|
-
@return {Object} The attribute value or undefined
|
|
2339
|
-
@public
|
|
2340
|
-
*/
|
|
2341
|
-
attr(keyName) {
|
|
2342
|
-
if (keyName in this._attributes) {
|
|
2343
|
-
return this._attributes[keyName];
|
|
2344
|
-
}
|
|
2345
|
-
assert(`Model '${this.identifier}' has no attribute named '${keyName}' defined.`, false);
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
/**
|
|
2349
|
-
Returns all attributes and their corresponding values.
|
|
2350
|
-
Example
|
|
2351
|
-
```javascript
|
|
2352
|
-
// store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' });
|
|
2353
|
-
postSnapshot.attributes(); // => { author: 'Tomster', title: 'Ember.js rocks' }
|
|
2354
|
-
```
|
|
2355
|
-
@method attributes
|
|
2356
|
-
@return {Object} All attributes of the current snapshot
|
|
2357
|
-
@public
|
|
2358
|
-
*/
|
|
2359
|
-
attributes() {
|
|
2360
|
-
return {
|
|
2361
|
-
...this._attributes
|
|
2362
|
-
};
|
|
2363
|
-
}
|
|
2364
|
-
|
|
2365
|
-
/**
|
|
2366
|
-
Returns all changed attributes and their old and new values.
|
|
2367
|
-
Example
|
|
2368
|
-
```javascript
|
|
2369
|
-
// store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' });
|
|
2370
|
-
postModel.set('title', 'Ember.js rocks!');
|
|
2371
|
-
postSnapshot.changedAttributes(); // => { title: ['Ember.js rocks', 'Ember.js rocks!'] }
|
|
2372
|
-
```
|
|
2373
|
-
@method changedAttributes
|
|
2374
|
-
@return {Object} All changed attributes of the current snapshot
|
|
2375
|
-
@public
|
|
2376
|
-
*/
|
|
2377
|
-
changedAttributes() {
|
|
2378
|
-
let changedAttributes = Object.create(null);
|
|
2379
|
-
if (!this._changedAttributes) {
|
|
2380
|
-
return changedAttributes;
|
|
2381
|
-
}
|
|
2382
|
-
let changedAttributeKeys = Object.keys(this._changedAttributes);
|
|
2383
|
-
for (let i = 0, length = changedAttributeKeys.length; i < length; i++) {
|
|
2384
|
-
let key = changedAttributeKeys[i];
|
|
2385
|
-
changedAttributes[key] = this._changedAttributes[key].slice();
|
|
2386
|
-
}
|
|
2387
|
-
return changedAttributes;
|
|
2388
|
-
}
|
|
2389
|
-
|
|
2390
|
-
/**
|
|
2391
|
-
Returns the current value of a belongsTo relationship.
|
|
2392
|
-
`belongsTo` takes an optional hash of options as a second parameter,
|
|
2393
|
-
currently supported options are:
|
|
2394
|
-
- `id`: set to `true` if you only want the ID of the related record to be
|
|
2395
|
-
returned.
|
|
2396
|
-
Example
|
|
2397
|
-
```javascript
|
|
2398
|
-
// store.push('post', { id: 1, title: 'Hello World' });
|
|
2399
|
-
// store.createRecord('comment', { body: 'Lorem ipsum', post: post });
|
|
2400
|
-
commentSnapshot.belongsTo('post'); // => Snapshot
|
|
2401
|
-
commentSnapshot.belongsTo('post', { id: true }); // => '1'
|
|
2402
|
-
// store.push('comment', { id: 1, body: 'Lorem ipsum' });
|
|
2403
|
-
commentSnapshot.belongsTo('post'); // => undefined
|
|
2404
|
-
```
|
|
2405
|
-
Calling `belongsTo` will return a new Snapshot as long as there's any known
|
|
2406
|
-
data for the relationship available, such as an ID. If the relationship is
|
|
2407
|
-
known but unset, `belongsTo` will return `null`. If the contents of the
|
|
2408
|
-
relationship is unknown `belongsTo` will return `undefined`.
|
|
2409
|
-
Note: Relationships are loaded lazily and cached upon first access.
|
|
2410
|
-
@method belongsTo
|
|
2411
|
-
@param {String} keyName
|
|
2412
|
-
@param {Object} [options]
|
|
2413
|
-
@public
|
|
2414
|
-
@return {(Snapshot|String|null|undefined)} A snapshot or ID of a known
|
|
2415
|
-
relationship or null if the relationship is known but unset. undefined
|
|
2416
|
-
will be returned if the contents of the relationship is unknown.
|
|
2417
|
-
*/
|
|
2418
|
-
belongsTo(keyName, options) {
|
|
2419
|
-
let returnModeIsId = !!(options && options.id);
|
|
2420
|
-
let result;
|
|
2421
|
-
let store = this._store;
|
|
2422
|
-
if (returnModeIsId === true && keyName in this._belongsToIds) {
|
|
2423
|
-
return this._belongsToIds[keyName];
|
|
2424
|
-
}
|
|
2425
|
-
if (returnModeIsId === false && keyName in this._belongsToRelationships) {
|
|
2426
|
-
return this._belongsToRelationships[keyName];
|
|
2427
|
-
}
|
|
2428
|
-
let relationshipMeta = store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
2429
|
-
type: this.modelName
|
|
2430
|
-
})[keyName];
|
|
2431
|
-
assert(`Model '${this.identifier}' has no belongsTo relationship named '${keyName}' defined.`, relationshipMeta && relationshipMeta.kind === 'belongsTo');
|
|
2432
|
-
|
|
2433
|
-
// TODO @runspired it seems this code branch would not work with CUSTOM_MODEL_CLASSes
|
|
2434
|
-
// this check is not a regression in behavior because relationships don't currently
|
|
2435
|
-
// function without access to intimate API contracts between RecordData and Model.
|
|
2436
|
-
// This is a requirement we should fix as soon as the relationship layer does not require
|
|
2437
|
-
// this intimate API usage.
|
|
2438
|
-
if (macroCondition(!dependencySatisfies("@ember-data/json-api", "*"))) {
|
|
2439
|
-
assert(`snapshot.belongsTo only supported when using the package @ember-data/json-api`);
|
|
2440
|
-
}
|
|
2441
|
-
const graphFor = importSync('@ember-data/graph/-private').graphFor;
|
|
2442
|
-
const {
|
|
2443
|
-
identifier
|
|
2444
|
-
} = this;
|
|
2445
|
-
const relationship = graphFor(this._store).get(identifier, keyName);
|
|
2446
|
-
assert(`You looked up the ${keyName} belongsTo relationship for { type: ${identifier.type}, id: ${identifier.id}, lid: ${identifier.lid} but no such relationship was found.`, relationship);
|
|
2447
|
-
assert(`You looked up the ${keyName} belongsTo relationship for { type: ${identifier.type}, id: ${identifier.id}, lid: ${identifier.lid} but that relationship is a hasMany.`, relationship.definition.kind === 'belongsTo');
|
|
2448
|
-
let value = relationship.getData();
|
|
2449
|
-
let data = value && value.data;
|
|
2450
|
-
let inverseIdentifier = data ? store.identifierCache.getOrCreateRecordIdentifier(data) : null;
|
|
2451
|
-
if (value && value.data !== undefined) {
|
|
2452
|
-
if (inverseIdentifier && !store._instanceCache.getRecordData(inverseIdentifier).isDeleted(inverseIdentifier)) {
|
|
2453
|
-
if (returnModeIsId) {
|
|
2454
|
-
result = inverseIdentifier.id;
|
|
2455
|
-
} else {
|
|
2456
|
-
result = store._instanceCache.createSnapshot(inverseIdentifier);
|
|
2457
|
-
}
|
|
2458
|
-
} else {
|
|
2459
|
-
result = null;
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
if (returnModeIsId) {
|
|
2463
|
-
this._belongsToIds[keyName] = result;
|
|
2464
|
-
} else {
|
|
2465
|
-
this._belongsToRelationships[keyName] = result;
|
|
2466
|
-
}
|
|
2467
|
-
return result;
|
|
2468
|
-
}
|
|
2469
|
-
|
|
2470
|
-
/**
|
|
2471
|
-
Returns the current value of a hasMany relationship.
|
|
2472
|
-
`hasMany` takes an optional hash of options as a second parameter,
|
|
2473
|
-
currently supported options are:
|
|
2474
|
-
- `ids`: set to `true` if you only want the IDs of the related records to be
|
|
2475
|
-
returned.
|
|
2476
|
-
Example
|
|
2477
|
-
```javascript
|
|
2478
|
-
// store.push('post', { id: 1, title: 'Hello World', comments: [2, 3] });
|
|
2479
|
-
postSnapshot.hasMany('comments'); // => [Snapshot, Snapshot]
|
|
2480
|
-
postSnapshot.hasMany('comments', { ids: true }); // => ['2', '3']
|
|
2481
|
-
// store.push('post', { id: 1, title: 'Hello World' });
|
|
2482
|
-
postSnapshot.hasMany('comments'); // => undefined
|
|
2483
|
-
```
|
|
2484
|
-
Note: Relationships are loaded lazily and cached upon first access.
|
|
2485
|
-
@method hasMany
|
|
2486
|
-
@param {String} keyName
|
|
2487
|
-
@param {Object} [options]
|
|
2488
|
-
@public
|
|
2489
|
-
@return {(Array|undefined)} An array of snapshots or IDs of a known
|
|
2490
|
-
relationship or an empty array if the relationship is known but unset.
|
|
2491
|
-
undefined will be returned if the contents of the relationship is unknown.
|
|
2492
|
-
*/
|
|
2493
|
-
hasMany(keyName, options) {
|
|
2494
|
-
let returnModeIsIds = !!(options && options.ids);
|
|
2495
|
-
let results;
|
|
2496
|
-
let cachedIds = this._hasManyIds[keyName];
|
|
2497
|
-
let cachedSnapshots = this._hasManyRelationships[keyName];
|
|
2498
|
-
if (returnModeIsIds === true && keyName in this._hasManyIds) {
|
|
2499
|
-
return cachedIds;
|
|
2500
|
-
}
|
|
2501
|
-
if (returnModeIsIds === false && keyName in this._hasManyRelationships) {
|
|
2502
|
-
return cachedSnapshots;
|
|
2503
|
-
}
|
|
2504
|
-
let store = this._store;
|
|
2505
|
-
let relationshipMeta = store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
2506
|
-
type: this.modelName
|
|
2507
|
-
})[keyName];
|
|
2508
|
-
assert(`Model '${this.identifier}' has no hasMany relationship named '${keyName}' defined.`, relationshipMeta && relationshipMeta.kind === 'hasMany');
|
|
2509
|
-
|
|
2510
|
-
// TODO @runspired it seems this code branch would not work with CUSTOM_MODEL_CLASSes
|
|
2511
|
-
// this check is not a regression in behavior because relationships don't currently
|
|
2512
|
-
// function without access to intimate API contracts between RecordData and Model.
|
|
2513
|
-
// This is a requirement we should fix as soon as the relationship layer does not require
|
|
2514
|
-
// this intimate API usage.
|
|
2515
|
-
if (macroCondition(!dependencySatisfies("@ember-data/json-api", "*"))) {
|
|
2516
|
-
assert(`snapshot.hasMany only supported when using the package @ember-data/json-api`);
|
|
2517
|
-
}
|
|
2518
|
-
const graphFor = importSync('@ember-data/graph/-private').graphFor;
|
|
2519
|
-
const {
|
|
2520
|
-
identifier
|
|
2521
|
-
} = this;
|
|
2522
|
-
const relationship = graphFor(this._store).get(identifier, keyName);
|
|
2523
|
-
assert(`You looked up the ${keyName} hasMany relationship for { type: ${identifier.type}, id: ${identifier.id}, lid: ${identifier.lid} but no such relationship was found.`, relationship);
|
|
2524
|
-
assert(`You looked up the ${keyName} hasMany relationship for { type: ${identifier.type}, id: ${identifier.id}, lid: ${identifier.lid} but that relationship is a belongsTo.`, relationship.definition.kind === 'hasMany');
|
|
2525
|
-
let value = relationship.getData();
|
|
2526
|
-
if (value.data) {
|
|
2527
|
-
results = [];
|
|
2528
|
-
value.data.forEach(member => {
|
|
2529
|
-
let inverseIdentifier = store.identifierCache.getOrCreateRecordIdentifier(member);
|
|
2530
|
-
if (!store._instanceCache.getRecordData(inverseIdentifier).isDeleted(inverseIdentifier)) {
|
|
2531
|
-
if (returnModeIsIds) {
|
|
2532
|
-
results.push(inverseIdentifier.id);
|
|
2533
|
-
} else {
|
|
2534
|
-
results.push(store._instanceCache.createSnapshot(inverseIdentifier));
|
|
2535
|
-
}
|
|
2536
|
-
}
|
|
2537
|
-
});
|
|
2538
|
-
}
|
|
2539
|
-
|
|
2540
|
-
// we assign even if `undefined` so that we don't reprocess the relationship
|
|
2541
|
-
// on next access. This works with the `keyName in` checks above.
|
|
2542
|
-
if (returnModeIsIds) {
|
|
2543
|
-
this._hasManyIds[keyName] = results;
|
|
2544
|
-
} else {
|
|
2545
|
-
this._hasManyRelationships[keyName] = results;
|
|
2546
|
-
}
|
|
2547
|
-
return results;
|
|
2548
|
-
}
|
|
2549
|
-
|
|
2550
|
-
/**
|
|
2551
|
-
Iterates through all the attributes of the model, calling the passed
|
|
2552
|
-
function on each attribute.
|
|
2553
|
-
Example
|
|
2554
|
-
```javascript
|
|
2555
|
-
snapshot.eachAttribute(function(name, meta) {
|
|
2556
|
-
// ...
|
|
2557
|
-
});
|
|
2558
|
-
```
|
|
2559
|
-
@method eachAttribute
|
|
2560
|
-
@param {Function} callback the callback to execute
|
|
2561
|
-
@param {Object} [binding] the value to which the callback's `this` should be bound
|
|
2562
|
-
@public
|
|
2563
|
-
*/
|
|
2564
|
-
eachAttribute(callback, binding) {
|
|
2565
|
-
let attrDefs = this._store.getSchemaDefinitionService().attributesDefinitionFor(this.identifier);
|
|
2566
|
-
Object.keys(attrDefs).forEach(key => {
|
|
2567
|
-
callback.call(binding, key, attrDefs[key]);
|
|
2568
|
-
});
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
/**
|
|
2572
|
-
Iterates through all the relationships of the model, calling the passed
|
|
2573
|
-
function on each relationship.
|
|
2574
|
-
Example
|
|
2575
|
-
```javascript
|
|
2576
|
-
snapshot.eachRelationship(function(name, relationship) {
|
|
2577
|
-
// ...
|
|
2578
|
-
});
|
|
2579
|
-
```
|
|
2580
|
-
@method eachRelationship
|
|
2581
|
-
@param {Function} callback the callback to execute
|
|
2582
|
-
@param {Object} [binding] the value to which the callback's `this` should be bound
|
|
2583
|
-
@public
|
|
2584
|
-
*/
|
|
2585
|
-
eachRelationship(callback, binding) {
|
|
2586
|
-
let relationshipDefs = this._store.getSchemaDefinitionService().relationshipsDefinitionFor(this.identifier);
|
|
2587
|
-
Object.keys(relationshipDefs).forEach(key => {
|
|
2588
|
-
callback.call(binding, key, relationshipDefs[key]);
|
|
2589
|
-
});
|
|
2590
|
-
}
|
|
2591
|
-
|
|
2592
|
-
/**
|
|
2593
|
-
Serializes the snapshot using the serializer for the model.
|
|
2594
|
-
Example
|
|
2595
|
-
```app/adapters/application.js
|
|
2596
|
-
import Adapter from '@ember-data/adapter';
|
|
2597
|
-
export default Adapter.extend({
|
|
2598
|
-
createRecord(store, type, snapshot) {
|
|
2599
|
-
let data = snapshot.serialize({ includeId: true });
|
|
2600
|
-
let url = `/${type.modelName}`;
|
|
2601
|
-
return fetch(url, {
|
|
2602
|
-
method: 'POST',
|
|
2603
|
-
body: data,
|
|
2604
|
-
}).then((response) => response.json())
|
|
2605
|
-
}
|
|
2606
|
-
});
|
|
2607
|
-
```
|
|
2608
|
-
@method serialize
|
|
2609
|
-
@param {Object} options
|
|
2610
|
-
@return {Object} an object whose values are primitive JSON values only
|
|
2611
|
-
@public
|
|
2612
|
-
*/
|
|
2613
|
-
serialize(options) {
|
|
2614
|
-
const serializer = this._store.serializerFor(this.modelName);
|
|
2615
|
-
assert(`Cannot serialize record, no serializer found`, serializer);
|
|
2616
|
-
return serializer.serialize(this, options);
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS)) {
|
|
2620
|
-
Object.defineProperty(Snapshot.prototype, 'type', {
|
|
2621
|
-
get() {
|
|
2622
|
-
deprecate(`Using Snapshot.type to access the ModelClass for a record is deprecated. Use store.modelFor(<modelName>) instead.`, false, {
|
|
2623
|
-
id: 'ember-data:deprecate-snapshot-model-class-access',
|
|
2624
|
-
until: '5.0',
|
|
2625
|
-
for: 'ember-data',
|
|
2626
|
-
since: {
|
|
2627
|
-
available: '4.5.0',
|
|
2628
|
-
enabled: '4.5.0'
|
|
2629
|
-
}
|
|
2630
|
-
});
|
|
2631
|
-
return this._store.modelFor(this.identifier.type);
|
|
2632
|
-
}
|
|
2633
|
-
});
|
|
2634
|
-
}
|
|
2635
|
-
function assertIdentifierHasId(identifier) {
|
|
2636
|
-
assert(`Attempted to schedule a fetch for a record without an id.`, identifier.id !== null);
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
/*
|
|
2640
|
-
* Returns the RecordData instance associated with a given
|
|
2641
|
-
* Model or Identifier
|
|
2642
|
-
*/
|
|
2643
|
-
|
|
2644
|
-
const RecordDataForIdentifierCache = new Map();
|
|
2645
|
-
function setRecordDataFor(identifier, recordData) {
|
|
2646
|
-
assert(`Illegal set of identifier`, !RecordDataForIdentifierCache.has(identifier) || RecordDataForIdentifierCache.get(identifier) === recordData);
|
|
2647
|
-
RecordDataForIdentifierCache.set(identifier, recordData);
|
|
2648
|
-
}
|
|
2649
|
-
function removeRecordDataFor(identifier) {
|
|
2650
|
-
RecordDataForIdentifierCache.delete(identifier);
|
|
2651
|
-
}
|
|
2652
|
-
function recordDataFor(instance) {
|
|
2653
|
-
if (RecordDataForIdentifierCache.has(instance)) {
|
|
2654
|
-
return RecordDataForIdentifierCache.get(instance);
|
|
2655
|
-
}
|
|
2656
|
-
return null;
|
|
2657
|
-
}
|
|
2658
|
-
let _peekGraph;
|
|
2659
|
-
if (macroCondition(dependencySatisfies("@ember-data/graph", "*"))) {
|
|
2660
|
-
let __peekGraph;
|
|
2661
|
-
_peekGraph = wrapper => {
|
|
2662
|
-
let a = importSync('@ember-data/graph/-private').peekGraph;
|
|
2663
|
-
__peekGraph = __peekGraph || a;
|
|
2664
|
-
return __peekGraph(wrapper);
|
|
2665
|
-
};
|
|
2666
|
-
}
|
|
2667
|
-
|
|
2668
|
-
/**
|
|
2669
|
-
@module @ember-data/store
|
|
2670
|
-
*/
|
|
2671
|
-
|
|
2672
|
-
const RecordCache = new Map();
|
|
2673
|
-
function peekRecordIdentifier(record) {
|
|
2674
|
-
return RecordCache.get(record);
|
|
2675
|
-
}
|
|
2603
|
+
const RecordCache = new Map();
|
|
2604
|
+
function peekRecordIdentifier(record) {
|
|
2605
|
+
return RecordCache.get(record);
|
|
2606
|
+
}
|
|
2676
2607
|
|
|
2677
2608
|
/**
|
|
2678
2609
|
Retrieves the unique referentially-stable [RecordIdentifier](/ember-data/release/classes/StableRecordIdentifier)
|
|
@@ -2698,7 +2629,7 @@ function recordIdentifierFor(record) {
|
|
|
2698
2629
|
return RecordCache.get(record);
|
|
2699
2630
|
}
|
|
2700
2631
|
function setRecordIdentifier(record, identifier) {
|
|
2701
|
-
if (macroCondition(
|
|
2632
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
2702
2633
|
if (RecordCache.has(record) && RecordCache.get(record) !== identifier) {
|
|
2703
2634
|
throw new Error(`${String(record)} was already assigned an identifier`);
|
|
2704
2635
|
}
|
|
@@ -2724,16 +2655,18 @@ class InstanceCache {
|
|
|
2724
2655
|
constructor(store) {
|
|
2725
2656
|
this.__instances = {
|
|
2726
2657
|
record: new Map(),
|
|
2727
|
-
|
|
2658
|
+
resourceCache: new Map(),
|
|
2728
2659
|
reference: new WeakMap()
|
|
2729
2660
|
};
|
|
2730
2661
|
this.store = store;
|
|
2731
2662
|
this._storeWrapper = new CacheStoreWrapper(this.store);
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2663
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK)) {
|
|
2664
|
+
this.__cacheFor = resource => {
|
|
2665
|
+
// TODO enforce strict
|
|
2666
|
+
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
2667
|
+
return this.getResourceCache(identifier);
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2737
2670
|
store.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => {
|
|
2738
2671
|
let keptIdentifier = identifier;
|
|
2739
2672
|
if (identifier.id !== matchedIdentifier.id) {
|
|
@@ -2746,11 +2679,11 @@ class InstanceCache {
|
|
|
2746
2679
|
// check for duplicate entities
|
|
2747
2680
|
let keptHasRecord = this.__instances.record.has(keptIdentifier);
|
|
2748
2681
|
let staleHasRecord = this.__instances.record.has(staleIdentifier);
|
|
2749
|
-
let
|
|
2750
|
-
let
|
|
2682
|
+
let keptResourceCache = this.__instances.resourceCache.get(keptIdentifier) || null;
|
|
2683
|
+
let staleResourceCache = this.__instances.resourceCache.get(staleIdentifier) || null;
|
|
2751
2684
|
|
|
2752
2685
|
// we cannot merge entities when both have records
|
|
2753
|
-
// (this may not be strictly true, we could probably swap the
|
|
2686
|
+
// (this may not be strictly true, we could probably swap the cache data the record points at)
|
|
2754
2687
|
if (keptHasRecord && staleHasRecord) {
|
|
2755
2688
|
// TODO we probably don't need to throw these errors anymore
|
|
2756
2689
|
// we can probably just "swap" what data source the abandoned
|
|
@@ -2761,23 +2694,27 @@ class InstanceCache {
|
|
|
2761
2694
|
}
|
|
2762
2695
|
assert(`Failed to update the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${identifier.lid})' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${String(matchedIdentifier.id)} (${String(matchedIdentifier.lid)})'`);
|
|
2763
2696
|
}
|
|
2764
|
-
let
|
|
2765
|
-
if (
|
|
2766
|
-
|
|
2697
|
+
let resourceCache = keptResourceCache || staleResourceCache;
|
|
2698
|
+
if (resourceCache) {
|
|
2699
|
+
resourceCache.patch({
|
|
2700
|
+
op: 'mergeIdentifiers',
|
|
2701
|
+
record: staleIdentifier,
|
|
2702
|
+
value: keptIdentifier
|
|
2703
|
+
});
|
|
2704
|
+
} else if (macroCondition(!getOwnConfig().deprecations.DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK)) {
|
|
2705
|
+
this.store.cache.patch({
|
|
2767
2706
|
op: 'mergeIdentifiers',
|
|
2768
2707
|
record: staleIdentifier,
|
|
2769
2708
|
value: keptIdentifier
|
|
2770
2709
|
});
|
|
2771
|
-
} else if (macroCondition(
|
|
2772
|
-
|
|
2773
|
-
// and not ever specific to one record-data
|
|
2774
|
-
this.store.__private_singleton_recordData?.sync({
|
|
2710
|
+
} else if (macroCondition(getOwnConfig().packages.HAS_JSON_API_PACKAGE)) {
|
|
2711
|
+
this.store.cache.patch({
|
|
2775
2712
|
op: 'mergeIdentifiers',
|
|
2776
2713
|
record: staleIdentifier,
|
|
2777
2714
|
value: keptIdentifier
|
|
2778
2715
|
});
|
|
2779
2716
|
}
|
|
2780
|
-
if (
|
|
2717
|
+
if (staleResourceCache === null) {
|
|
2781
2718
|
return keptIdentifier;
|
|
2782
2719
|
}
|
|
2783
2720
|
|
|
@@ -2805,10 +2742,27 @@ class InstanceCache {
|
|
|
2805
2742
|
let record = this.__instances.record.get(identifier);
|
|
2806
2743
|
if (!record) {
|
|
2807
2744
|
assert(`Cannot create a new record instance while the store is being destroyed`, !this.store.isDestroying && !this.store.isDestroyed);
|
|
2808
|
-
const
|
|
2809
|
-
|
|
2745
|
+
const cache = this.getResourceCache(identifier);
|
|
2746
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_INSTANTIATE_RECORD_ARGS)) {
|
|
2747
|
+
if (this.store.instantiateRecord.length > 2) {
|
|
2748
|
+
deprecate(`Expected store.instantiateRecord to have an arity of 2. recordDataFor and notificationManager args have been deprecated.`, false, {
|
|
2749
|
+
for: '@ember-data/store',
|
|
2750
|
+
id: 'ember-data:deprecate-instantiate-record-args',
|
|
2751
|
+
since: {
|
|
2752
|
+
available: '4.12',
|
|
2753
|
+
enabled: '4.12'
|
|
2754
|
+
},
|
|
2755
|
+
until: '5.0'
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
record = this.store.instantiateRecord(identifier, properties || {},
|
|
2759
|
+
// @ts-expect-error
|
|
2760
|
+
this.__cacheFor, this.store.notifications);
|
|
2761
|
+
} else {
|
|
2762
|
+
record = this.store.instantiateRecord(identifier, properties || {});
|
|
2763
|
+
}
|
|
2810
2764
|
setRecordIdentifier(record, identifier);
|
|
2811
|
-
|
|
2765
|
+
setCacheFor(record, cache);
|
|
2812
2766
|
StoreMap.set(record, this.store);
|
|
2813
2767
|
this.__instances.record.set(identifier, record);
|
|
2814
2768
|
if (macroCondition(getOwnConfig().debug.LOG_INSTANCE_CACHE)) {
|
|
@@ -2818,49 +2772,49 @@ class InstanceCache {
|
|
|
2818
2772
|
}
|
|
2819
2773
|
return record;
|
|
2820
2774
|
}
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
for: 'ember-data',
|
|
2828
|
-
until: '5.0',
|
|
2829
|
-
since: {
|
|
2830
|
-
enabled: '4.7',
|
|
2831
|
-
available: '4.7'
|
|
2832
|
-
}
|
|
2833
|
-
});
|
|
2834
|
-
let recordDataInstance = this.store.createRecordDataFor(identifier.type, identifier.id,
|
|
2835
|
-
// @ts-expect-error
|
|
2836
|
-
identifier.lid, this._storeWrapper);
|
|
2837
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA)) {
|
|
2838
|
-
recordData = new NonSingletonCacheManager(this.store, recordDataInstance, identifier);
|
|
2839
|
-
} else {
|
|
2840
|
-
recordData = this.__cacheManager = this.__cacheManager || new NonSingletonCacheManager(this.store, recordDataInstance, identifier);
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2775
|
+
getResourceCache(identifier) {
|
|
2776
|
+
if (macroCondition(!getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA)) {
|
|
2777
|
+
const cache = this.store.cache;
|
|
2778
|
+
setCacheFor(identifier, cache);
|
|
2779
|
+
this.__instances.resourceCache.set(identifier, cache);
|
|
2780
|
+
return cache;
|
|
2843
2781
|
}
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2782
|
+
let cache = this.__instances.resourceCache.get(identifier);
|
|
2783
|
+
if (cache) {
|
|
2784
|
+
return cache;
|
|
2785
|
+
}
|
|
2786
|
+
if (this.store.createRecordDataFor) {
|
|
2787
|
+
deprecate(`Store.createRecordDataFor(<type>, <id>, <lid>, <storeWrapper>) has been deprecated in favor of Store.createCache(<storeWrapper>)`, false, {
|
|
2788
|
+
id: 'ember-data:deprecate-v1-cache',
|
|
2789
|
+
for: 'ember-data',
|
|
2790
|
+
until: '5.0',
|
|
2791
|
+
since: {
|
|
2792
|
+
enabled: '4.12',
|
|
2793
|
+
available: '4.12'
|
|
2794
|
+
}
|
|
2795
|
+
});
|
|
2796
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1CACHE_STORE_APIS)) {
|
|
2797
|
+
if (this.store.createRecordDataFor.length > 2) {
|
|
2798
|
+
let cacheInstance = this.store.createRecordDataFor(identifier.type, identifier.id,
|
|
2799
|
+
// @ts-expect-error
|
|
2800
|
+
identifier.lid, this._storeWrapper);
|
|
2801
|
+
cache = new NonSingletonCacheManager(this.store, cacheInstance, identifier);
|
|
2854
2802
|
}
|
|
2855
2803
|
}
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
// eslint-disable-next-line no-console
|
|
2860
|
-
console.log(`InstanceCache: created RecordData for ${String(identifier)}`);
|
|
2804
|
+
if (!cache) {
|
|
2805
|
+
let cacheInstance = this.store.createRecordDataFor(identifier, this._storeWrapper);
|
|
2806
|
+
cache = cacheInstance.version === '2' ? cacheInstance : new NonSingletonCacheManager(this.store, cacheInstance, identifier);
|
|
2861
2807
|
}
|
|
2808
|
+
} else {
|
|
2809
|
+
cache = this.store.cache;
|
|
2862
2810
|
}
|
|
2863
|
-
|
|
2811
|
+
setCacheFor(identifier, cache);
|
|
2812
|
+
this.__instances.resourceCache.set(identifier, cache);
|
|
2813
|
+
if (macroCondition(getOwnConfig().debug.LOG_INSTANCE_CACHE)) {
|
|
2814
|
+
// eslint-disable-next-line no-console
|
|
2815
|
+
console.log(`InstanceCache: created Cache for ${String(identifier)}`);
|
|
2816
|
+
}
|
|
2817
|
+
return cache;
|
|
2864
2818
|
}
|
|
2865
2819
|
getReference(identifier) {
|
|
2866
2820
|
let cache = this.__instances.reference;
|
|
@@ -2872,16 +2826,16 @@ class InstanceCache {
|
|
|
2872
2826
|
return reference;
|
|
2873
2827
|
}
|
|
2874
2828
|
recordIsLoaded(identifier, filterDeleted = false) {
|
|
2875
|
-
const
|
|
2876
|
-
if (!
|
|
2829
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this.__instances.resourceCache.get(identifier) : this.cache;
|
|
2830
|
+
if (!cache) {
|
|
2877
2831
|
return false;
|
|
2878
2832
|
}
|
|
2879
|
-
const isNew =
|
|
2880
|
-
const isEmpty =
|
|
2833
|
+
const isNew = cache.isNew(identifier);
|
|
2834
|
+
const isEmpty = cache.isEmpty(identifier);
|
|
2881
2835
|
|
|
2882
2836
|
// if we are new we must consider ourselves loaded
|
|
2883
2837
|
if (isNew) {
|
|
2884
|
-
return !
|
|
2838
|
+
return !cache.isDeleted(identifier);
|
|
2885
2839
|
}
|
|
2886
2840
|
// even if we have a past request, if we are now empty we are not loaded
|
|
2887
2841
|
// typically this is true after an unloadRecord call
|
|
@@ -2890,43 +2844,28 @@ class InstanceCache {
|
|
|
2890
2844
|
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
2891
2845
|
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
2892
2846
|
// in that it does not account for unloading.
|
|
2893
|
-
return filterDeleted &&
|
|
2894
|
-
|
|
2895
|
-
/*
|
|
2896
|
-
const req = this.store.getRequestStateService();
|
|
2897
|
-
const fulfilled = req.getLastRequestForRecord(identifier);
|
|
2898
|
-
const isLocallyLoaded = !isEmpty;
|
|
2899
|
-
const isLoading =
|
|
2900
|
-
!isLocallyLoaded &&
|
|
2901
|
-
fulfilled === null &&
|
|
2902
|
-
req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query');
|
|
2903
|
-
if (isEmpty || (filterDeleted && recordData.isDeletionCommitted(identifier)) || isLoading) {
|
|
2904
|
-
return false;
|
|
2905
|
-
}
|
|
2906
|
-
return true;
|
|
2907
|
-
*/
|
|
2908
|
-
}
|
|
2909
|
-
|
|
2910
|
-
createSnapshot(identifier, options = {}) {
|
|
2911
|
-
return new Snapshot(options, identifier, this.store);
|
|
2847
|
+
return filterDeleted && cache.isDeletionCommitted(identifier) ? false : !isEmpty;
|
|
2912
2848
|
}
|
|
2913
2849
|
disconnect(identifier) {
|
|
2914
2850
|
const record = this.__instances.record.get(identifier);
|
|
2915
2851
|
assert('Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying);
|
|
2916
|
-
if (macroCondition(
|
|
2852
|
+
if (macroCondition(getOwnConfig().packages.HAS_GRAPH_PACKAGE)) {
|
|
2917
2853
|
let graph = _peekGraph(this.store);
|
|
2918
2854
|
if (graph) {
|
|
2919
2855
|
graph.remove(identifier);
|
|
2920
2856
|
}
|
|
2921
2857
|
}
|
|
2922
2858
|
this.store.identifierCache.forgetRecordIdentifier(identifier);
|
|
2859
|
+
this.__instances.resourceCache.delete(identifier);
|
|
2860
|
+
removeRecordDataFor(identifier);
|
|
2861
|
+
this.store._requestCache._clearEntries(identifier);
|
|
2923
2862
|
if (macroCondition(getOwnConfig().debug.LOG_INSTANCE_CACHE)) {
|
|
2924
2863
|
// eslint-disable-next-line no-console
|
|
2925
2864
|
console.log(`InstanceCache: disconnected ${String(identifier)}`);
|
|
2926
2865
|
}
|
|
2927
2866
|
}
|
|
2928
2867
|
unloadRecord(identifier) {
|
|
2929
|
-
if (macroCondition(
|
|
2868
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
2930
2869
|
const requests = this.store.getRequestStateService().getPendingRequestsForRecord(identifier);
|
|
2931
2870
|
if (requests.some(req => {
|
|
2932
2871
|
return req.type === 'mutation';
|
|
@@ -2942,7 +2881,7 @@ class InstanceCache {
|
|
|
2942
2881
|
// TODO is this join still necessary?
|
|
2943
2882
|
this.store._join(() => {
|
|
2944
2883
|
const record = this.__instances.record.get(identifier);
|
|
2945
|
-
const
|
|
2884
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this.__instances.resourceCache.get(identifier) : this.cache;
|
|
2946
2885
|
if (record) {
|
|
2947
2886
|
this.store.teardownRecord(record);
|
|
2948
2887
|
this.__instances.record.delete(identifier);
|
|
@@ -2954,20 +2893,18 @@ class InstanceCache {
|
|
|
2954
2893
|
console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
|
|
2955
2894
|
}
|
|
2956
2895
|
}
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
recordData.unloadRecord(identifier);
|
|
2961
|
-
this.__instances.recordData.delete(identifier);
|
|
2896
|
+
if (cache) {
|
|
2897
|
+
cache.unloadRecord(identifier);
|
|
2898
|
+
this.__instances.resourceCache.delete(identifier);
|
|
2962
2899
|
removeRecordDataFor(identifier);
|
|
2900
|
+
if (macroCondition(getOwnConfig().debug.LOG_INSTANCE_CACHE)) {
|
|
2901
|
+
// eslint-disable-next-line no-console
|
|
2902
|
+
console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
|
|
2903
|
+
}
|
|
2963
2904
|
} else {
|
|
2964
|
-
removeFromRecordArray = false;
|
|
2965
2905
|
this.disconnect(identifier);
|
|
2966
2906
|
}
|
|
2967
|
-
this.store.
|
|
2968
|
-
if (removeFromRecordArray) {
|
|
2969
|
-
this.store.recordArrayManager.identifierRemoved(identifier);
|
|
2970
|
-
}
|
|
2907
|
+
this.store._requestCache._clearEntries(identifier);
|
|
2971
2908
|
if (macroCondition(getOwnConfig().debug.LOG_INSTANCE_CACHE)) {
|
|
2972
2909
|
// eslint-disable-next-line no-console
|
|
2973
2910
|
console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
|
|
@@ -2977,43 +2914,28 @@ class InstanceCache {
|
|
|
2977
2914
|
});
|
|
2978
2915
|
}
|
|
2979
2916
|
clear(type) {
|
|
2980
|
-
const
|
|
2917
|
+
const cache = this.store.identifierCache._cache;
|
|
2981
2918
|
if (type === undefined) {
|
|
2982
|
-
|
|
2919
|
+
// it would be cool if we could just de-ref cache here
|
|
2920
|
+
// but probably would require WeakRef models to do so.
|
|
2921
|
+
cache.lids.forEach(identifier => {
|
|
2983
2922
|
this.unloadRecord(identifier);
|
|
2984
2923
|
});
|
|
2985
2924
|
} else {
|
|
2925
|
+
const typeCache = cache.types;
|
|
2986
2926
|
let identifiers = typeCache[type]?.lid;
|
|
2987
|
-
const rds = this.__instances.
|
|
2927
|
+
// const rds = this.__instances.resourceCache;
|
|
2988
2928
|
if (identifiers) {
|
|
2989
2929
|
identifiers.forEach(identifier => {
|
|
2990
|
-
if (rds.has(identifier)) {
|
|
2991
|
-
|
|
2992
|
-
}
|
|
2930
|
+
// if (rds.has(identifier)) {
|
|
2931
|
+
this.unloadRecord(identifier);
|
|
2932
|
+
// }
|
|
2993
2933
|
// TODO we don't remove the identifier, should we?
|
|
2994
2934
|
});
|
|
2995
2935
|
}
|
|
2996
2936
|
}
|
|
2997
2937
|
}
|
|
2998
2938
|
|
|
2999
|
-
// TODO this should move into the network layer
|
|
3000
|
-
_fetchDataIfNeededForIdentifier(identifier, options = {}) {
|
|
3001
|
-
// pre-loading will change the isEmpty value
|
|
3002
|
-
const isEmpty = _isEmpty(this, identifier);
|
|
3003
|
-
const isLoading = _isLoading(this, identifier);
|
|
3004
|
-
let promise;
|
|
3005
|
-
if (isEmpty) {
|
|
3006
|
-
assertIdentifierHasId(identifier);
|
|
3007
|
-
promise = this.store._fetchManager.scheduleFetch(identifier, options);
|
|
3008
|
-
} else if (isLoading) {
|
|
3009
|
-
promise = this.store._fetchManager.getPendingFetch(identifier, options);
|
|
3010
|
-
assert(`Expected to find a pending request for a record in the loading state, but found none`, promise);
|
|
3011
|
-
} else {
|
|
3012
|
-
promise = resolve(identifier);
|
|
3013
|
-
}
|
|
3014
|
-
return promise;
|
|
3015
|
-
}
|
|
3016
|
-
|
|
3017
2939
|
// TODO this should move into something coordinating operations
|
|
3018
2940
|
setRecordId(identifier, id) {
|
|
3019
2941
|
const {
|
|
@@ -3052,12 +2974,13 @@ class InstanceCache {
|
|
|
3052
2974
|
});
|
|
3053
2975
|
}
|
|
3054
2976
|
|
|
3055
|
-
// TODO update
|
|
2977
|
+
// TODO update resource cache if needed ?
|
|
3056
2978
|
// TODO handle consequences of identifier merge for notifications
|
|
3057
2979
|
this.store.notifications.notify(identifier, 'identity');
|
|
3058
2980
|
}
|
|
3059
2981
|
|
|
3060
|
-
// TODO
|
|
2982
|
+
// TODO ths should be wrapped in a deprecation flag since cache.put
|
|
2983
|
+
// handles this the rest of the time
|
|
3061
2984
|
loadData(data) {
|
|
3062
2985
|
let modelName = data.type;
|
|
3063
2986
|
assert(`You must include an 'id' for ${modelName} in an object passed to 'push'`, data.id !== null && data.id !== undefined && data.id !== '');
|
|
@@ -3081,24 +3004,18 @@ class InstanceCache {
|
|
|
3081
3004
|
} else {
|
|
3082
3005
|
identifier = this.store.identifierCache.getOrCreateRecordIdentifier(data);
|
|
3083
3006
|
}
|
|
3084
|
-
const
|
|
3085
|
-
if (recordData.isNew(identifier)) {
|
|
3086
|
-
this.store.notifications.notify(identifier, 'identity');
|
|
3087
|
-
}
|
|
3007
|
+
const cache = this.getResourceCache(identifier);
|
|
3088
3008
|
const hasRecord = this.__instances.record.has(identifier);
|
|
3089
|
-
|
|
3090
|
-
if (!isUpdate) {
|
|
3091
|
-
this.store.recordArrayManager.identifierAdded(identifier);
|
|
3092
|
-
}
|
|
3009
|
+
cache.upsert(identifier, data, hasRecord);
|
|
3093
3010
|
return identifier;
|
|
3094
3011
|
}
|
|
3095
3012
|
}
|
|
3096
|
-
function
|
|
3097
|
-
return
|
|
3013
|
+
function _resourceIsFullDeleted(identifier, cache) {
|
|
3014
|
+
return cache.isDeletionCommitted(identifier) || cache.isNew(identifier) && cache.isDeleted(identifier);
|
|
3098
3015
|
}
|
|
3099
|
-
function
|
|
3100
|
-
|
|
3101
|
-
return !
|
|
3016
|
+
function resourceIsFullyDeleted(instanceCache, identifier) {
|
|
3017
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? instanceCache.__instances.resourceCache.get(identifier) : instanceCache.cache;
|
|
3018
|
+
return !cache || _resourceIsFullDeleted(identifier, cache);
|
|
3102
3019
|
}
|
|
3103
3020
|
|
|
3104
3021
|
/*
|
|
@@ -3133,7 +3050,12 @@ function preloadData(store, identifier, preload) {
|
|
|
3133
3050
|
jsonPayload.attributes[key] = preloadValue;
|
|
3134
3051
|
}
|
|
3135
3052
|
});
|
|
3136
|
-
store._instanceCache.
|
|
3053
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? store._instanceCache.getResourceCache(identifier) : store.cache;
|
|
3054
|
+
const hasRecord = Boolean(store._instanceCache.peek({
|
|
3055
|
+
identifier,
|
|
3056
|
+
bucket: 'record'
|
|
3057
|
+
}));
|
|
3058
|
+
cache.upsert(identifier, jsonPayload, hasRecord);
|
|
3137
3059
|
}
|
|
3138
3060
|
function preloadRelationship(schema, preloadValue) {
|
|
3139
3061
|
const relatedType = schema.type;
|
|
@@ -3164,14 +3086,14 @@ function _convertPreloadRelationshipToJSON(value, type) {
|
|
|
3164
3086
|
// and allow identifiers to be used
|
|
3165
3087
|
return recordIdentifierFor(value);
|
|
3166
3088
|
}
|
|
3167
|
-
function _isEmpty(
|
|
3168
|
-
const
|
|
3169
|
-
if (!
|
|
3089
|
+
function _isEmpty(instanceCache, identifier) {
|
|
3090
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? instanceCache.__instances.resourceCache.get(identifier) : instanceCache.cache;
|
|
3091
|
+
if (!cache) {
|
|
3170
3092
|
return true;
|
|
3171
3093
|
}
|
|
3172
|
-
const isNew =
|
|
3173
|
-
const isDeleted =
|
|
3174
|
-
const isEmpty =
|
|
3094
|
+
const isNew = cache.isNew(identifier);
|
|
3095
|
+
const isDeleted = cache.isDeleted(identifier);
|
|
3096
|
+
const isEmpty = cache.isEmpty(identifier);
|
|
3175
3097
|
return (!isNew || isDeleted) && isEmpty;
|
|
3176
3098
|
}
|
|
3177
3099
|
function _isLoading(cache, identifier) {
|
|
@@ -3185,10 +3107,10 @@ function _isLoading(cache, identifier) {
|
|
|
3185
3107
|
function _clearCaches() {
|
|
3186
3108
|
RecordCache.clear();
|
|
3187
3109
|
StoreMap.clear();
|
|
3188
|
-
|
|
3110
|
+
CacheForIdentifierCache.clear();
|
|
3189
3111
|
}
|
|
3190
3112
|
let _modelForMixin;
|
|
3191
|
-
if (macroCondition(
|
|
3113
|
+
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
3192
3114
|
let _found;
|
|
3193
3115
|
_modelForMixin = function () {
|
|
3194
3116
|
if (!_found) {
|
|
@@ -3276,7 +3198,7 @@ function getModelFactory(store, cache, normalizedModelName) {
|
|
|
3276
3198
|
if (!factory) {
|
|
3277
3199
|
let owner = getOwner(store);
|
|
3278
3200
|
factory = owner.factoryFor(`model:${normalizedModelName}`);
|
|
3279
|
-
if (macroCondition(
|
|
3201
|
+
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
3280
3202
|
if (!factory) {
|
|
3281
3203
|
//Support looking up mixins as base types for polymorphic relationships
|
|
3282
3204
|
factory = _modelForMixin(store, normalizedModelName);
|
|
@@ -3433,14 +3355,14 @@ let PromiseArray = (_dec = reads('content.meta'), (_class$1 = class PromiseArray
|
|
|
3433
3355
|
writable: true,
|
|
3434
3356
|
initializer: null
|
|
3435
3357
|
}), _class$1));
|
|
3436
|
-
function _promiseObject(promise
|
|
3358
|
+
function _promiseObject(promise) {
|
|
3437
3359
|
return PromiseObjectProxy.create({
|
|
3438
|
-
promise
|
|
3360
|
+
promise
|
|
3439
3361
|
});
|
|
3440
3362
|
}
|
|
3441
|
-
function _promiseArray(promise
|
|
3363
|
+
function _promiseArray(promise) {
|
|
3442
3364
|
return PromiseArray.create({
|
|
3443
|
-
promise
|
|
3365
|
+
promise
|
|
3444
3366
|
});
|
|
3445
3367
|
}
|
|
3446
3368
|
|
|
@@ -3451,7 +3373,7 @@ const PROXIED_ARRAY_PROPS = ['length', '[]', 'firstObject', 'lastObject', 'meta'
|
|
|
3451
3373
|
const PROXIED_OBJECT_PROPS = ['content', 'isPending', 'isSettled', 'isRejected', 'isFulfilled', 'promise', 'reason'];
|
|
3452
3374
|
function promiseArray(promise) {
|
|
3453
3375
|
const promiseObjectProxy = _promiseArray(promise);
|
|
3454
|
-
if (macroCondition(!
|
|
3376
|
+
if (macroCondition(!getOwnConfig().env.DEBUG)) {
|
|
3455
3377
|
return promiseObjectProxy;
|
|
3456
3378
|
}
|
|
3457
3379
|
const handler = {
|
|
@@ -3488,7 +3410,7 @@ function promiseArray(promise) {
|
|
|
3488
3410
|
const ProxySymbolString = String(Symbol.for('PROXY_CONTENT'));
|
|
3489
3411
|
function promiseObject(promise) {
|
|
3490
3412
|
const promiseObjectProxy = _promiseObject(promise);
|
|
3491
|
-
if (macroCondition(!
|
|
3413
|
+
if (macroCondition(!getOwnConfig().env.DEBUG)) {
|
|
3492
3414
|
return promiseObjectProxy;
|
|
3493
3415
|
}
|
|
3494
3416
|
const handler = {
|
|
@@ -3544,6 +3466,7 @@ const IDENTIFIER_ARRAY_TAG = Symbol('#tag');
|
|
|
3544
3466
|
const SOURCE = Symbol('#source');
|
|
3545
3467
|
const MUTATE = Symbol('#update');
|
|
3546
3468
|
const NOTIFY = Symbol('#notify');
|
|
3469
|
+
const IS_COLLECTION = Symbol.for('Collection');
|
|
3547
3470
|
function notifyArray(arr) {
|
|
3548
3471
|
arr[IDENTIFIER_ARRAY_TAG].ref = null;
|
|
3549
3472
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
@@ -3664,6 +3587,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
3664
3587
|
this.isDestroying = false;
|
|
3665
3588
|
this.isDestroyed = false;
|
|
3666
3589
|
this._updatingPromise = null;
|
|
3590
|
+
this[IS_COLLECTION] = true;
|
|
3667
3591
|
this[IDENTIFIER_ARRAY_TAG] = new Tag();
|
|
3668
3592
|
this[SOURCE] = void 0;
|
|
3669
3593
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
@@ -3856,7 +3780,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
3856
3780
|
}
|
|
3857
3781
|
return false;
|
|
3858
3782
|
};
|
|
3859
|
-
} else if (macroCondition(
|
|
3783
|
+
} else if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
3860
3784
|
const meta = Ember.meta(this);
|
|
3861
3785
|
meta.hasMixin = mixin => {
|
|
3862
3786
|
assert(`Do not call A() on EmberData RecordArrays`);
|
|
@@ -3903,6 +3827,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
3903
3827
|
is finished.
|
|
3904
3828
|
*/
|
|
3905
3829
|
_update() {
|
|
3830
|
+
assert(`_update cannot be used with this array`, this.modelName);
|
|
3906
3831
|
return this.store.findAll(this.modelName, {
|
|
3907
3832
|
reload: true
|
|
3908
3833
|
});
|
|
@@ -3973,6 +3898,8 @@ class Collection extends IdentifierArray {
|
|
|
3973
3898
|
} = this;
|
|
3974
3899
|
|
|
3975
3900
|
// TODO save options from initial request?
|
|
3901
|
+
assert(`update cannot be used with this array`, this.modelName);
|
|
3902
|
+
assert(`update cannot be used with no query`, query);
|
|
3976
3903
|
const promise = store.query(this.modelName, query, {
|
|
3977
3904
|
_recordArray: this
|
|
3978
3905
|
});
|
|
@@ -4113,7 +4040,7 @@ if (macroCondition(getOwnConfig().deprecations.DEPRECATE_ARRAY_LIKE)) {
|
|
|
4113
4040
|
return this;
|
|
4114
4041
|
};
|
|
4115
4042
|
IdentifierArray.prototype.setObjects = function (objects) {
|
|
4116
|
-
deprecateArrayLike(this.DEPRECATED_CLASS_NAME, '
|
|
4043
|
+
deprecateArrayLike(this.DEPRECATED_CLASS_NAME, 'setObjects', '`arr.length = 0; arr.push(objects);`');
|
|
4117
4044
|
assert(`${this.DEPRECATED_CLASS_NAME}.setObjects expects to receive an array as its argument`, Array.isArray(objects));
|
|
4118
4045
|
this.splice(0, this.length);
|
|
4119
4046
|
this.push(...objects);
|
|
@@ -4368,6 +4295,15 @@ class RecordArrayManager {
|
|
|
4368
4295
|
this._pending = new Map();
|
|
4369
4296
|
this._staged = new Map();
|
|
4370
4297
|
this._identifiers = RecordArraysCache;
|
|
4298
|
+
this._subscription = this.store.notifications.subscribe('resource', (identifier, type) => {
|
|
4299
|
+
if (type === 'added') {
|
|
4300
|
+
this.identifierAdded(identifier);
|
|
4301
|
+
} else if (type === 'removed') {
|
|
4302
|
+
this.identifierRemoved(identifier);
|
|
4303
|
+
} else if (type === 'state') {
|
|
4304
|
+
this.identifierChanged(identifier);
|
|
4305
|
+
}
|
|
4306
|
+
});
|
|
4371
4307
|
}
|
|
4372
4308
|
_syncArray(array) {
|
|
4373
4309
|
const pending = this._pending.get(array);
|
|
@@ -4549,6 +4485,8 @@ class RecordArrayManager {
|
|
|
4549
4485
|
this.clear();
|
|
4550
4486
|
this._live.clear();
|
|
4551
4487
|
this.isDestroyed = true;
|
|
4488
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
4489
|
+
this.store.notifications.unsubscribe(this._subscription);
|
|
4552
4490
|
}
|
|
4553
4491
|
}
|
|
4554
4492
|
function associate(array, identifiers) {
|
|
@@ -4616,125 +4554,22 @@ function sync(array, changes) {
|
|
|
4616
4554
|
}
|
|
4617
4555
|
}
|
|
4618
4556
|
|
|
4619
|
-
/**
|
|
4620
|
-
@module @ember-data/store
|
|
4621
|
-
*/
|
|
4622
|
-
|
|
4623
|
-
function _bind(fn, ...args) {
|
|
4624
|
-
return function () {
|
|
4625
|
-
return fn.apply(undefined, args);
|
|
4626
|
-
};
|
|
4627
|
-
}
|
|
4628
|
-
function _guard(promise, test) {
|
|
4629
|
-
let guarded = promise.finally(() => {
|
|
4630
|
-
if (!test()) {
|
|
4631
|
-
guarded._subscribers.length = 0;
|
|
4632
|
-
}
|
|
4633
|
-
});
|
|
4634
|
-
return guarded;
|
|
4635
|
-
}
|
|
4636
|
-
function _objectIsAlive(object) {
|
|
4637
|
-
return !(object.isDestroyed || object.isDestroying);
|
|
4638
|
-
}
|
|
4639
|
-
function guardDestroyedStore(promise, store, label) {
|
|
4640
|
-
let token;
|
|
4641
|
-
if (macroCondition(isDevelopingApp())) {
|
|
4642
|
-
token = store._trackAsyncRequestStart(label);
|
|
4643
|
-
}
|
|
4644
|
-
let wrapperPromise = resolve(promise, label).then(_v => {
|
|
4645
|
-
if (!_objectIsAlive(store)) {
|
|
4646
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_RSVP_PROMISE)) {
|
|
4647
|
-
deprecate(`A Promise did not resolve by the time the store was destroyed. This will error in a future release.`, false, {
|
|
4648
|
-
id: 'ember-data:rsvp-unresolved-async',
|
|
4649
|
-
until: '5.0',
|
|
4650
|
-
for: '@ember-data/store',
|
|
4651
|
-
since: {
|
|
4652
|
-
available: '4.5',
|
|
4653
|
-
enabled: '4.5'
|
|
4654
|
-
}
|
|
4655
|
-
});
|
|
4656
|
-
}
|
|
4657
|
-
}
|
|
4658
|
-
return promise;
|
|
4659
|
-
});
|
|
4660
|
-
return _guard(wrapperPromise, () => {
|
|
4661
|
-
if (macroCondition(isDevelopingApp())) {
|
|
4662
|
-
store._trackAsyncRequestEnd(token);
|
|
4663
|
-
}
|
|
4664
|
-
return _objectIsAlive(store);
|
|
4665
|
-
});
|
|
4666
|
-
}
|
|
4667
|
-
|
|
4668
|
-
/**
|
|
4669
|
-
This is a helper method that validates a JSON API top-level document
|
|
4670
|
-
|
|
4671
|
-
The format of a document is described here:
|
|
4672
|
-
http://jsonapi.org/format/#document-top-level
|
|
4673
|
-
|
|
4674
|
-
@internal
|
|
4675
|
-
*/
|
|
4676
|
-
function validateDocumentStructure(doc) {
|
|
4677
|
-
if (macroCondition(isDevelopingApp())) {
|
|
4678
|
-
let errors = [];
|
|
4679
|
-
if (!doc || typeof doc !== 'object') {
|
|
4680
|
-
errors.push('Top level of a JSON API document must be an object');
|
|
4681
|
-
} else {
|
|
4682
|
-
if (!('data' in doc) && !('errors' in doc) && !('meta' in doc)) {
|
|
4683
|
-
errors.push('One or more of the following keys must be present: "data", "errors", "meta".');
|
|
4684
|
-
} else {
|
|
4685
|
-
if ('data' in doc && 'errors' in doc) {
|
|
4686
|
-
errors.push('Top level keys "errors" and "data" cannot both be present in a JSON API document');
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
if ('data' in doc) {
|
|
4690
|
-
if (!(doc.data === null || Array.isArray(doc.data) || typeof doc.data === 'object')) {
|
|
4691
|
-
errors.push('data must be null, an object, or an array');
|
|
4692
|
-
}
|
|
4693
|
-
}
|
|
4694
|
-
if ('meta' in doc) {
|
|
4695
|
-
if (typeof doc.meta !== 'object') {
|
|
4696
|
-
errors.push('meta must be an object');
|
|
4697
|
-
}
|
|
4698
|
-
}
|
|
4699
|
-
if ('errors' in doc) {
|
|
4700
|
-
if (!Array.isArray(doc.errors)) {
|
|
4701
|
-
errors.push('errors must be an array');
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
4704
|
-
if ('links' in doc) {
|
|
4705
|
-
if (typeof doc.links !== 'object') {
|
|
4706
|
-
errors.push('links must be an object');
|
|
4707
|
-
}
|
|
4708
|
-
}
|
|
4709
|
-
if ('jsonapi' in doc) {
|
|
4710
|
-
if (typeof doc.jsonapi !== 'object') {
|
|
4711
|
-
errors.push('jsonapi must be an object');
|
|
4712
|
-
}
|
|
4713
|
-
}
|
|
4714
|
-
if ('included' in doc) {
|
|
4715
|
-
if (typeof doc.included !== 'object') {
|
|
4716
|
-
errors.push('included must be an array');
|
|
4717
|
-
}
|
|
4718
|
-
}
|
|
4719
|
-
}
|
|
4720
|
-
assert(`Response must be normalized to a valid JSON API document:\n\t* ${errors.join('\n\t* ')}`, errors.length === 0);
|
|
4721
|
-
}
|
|
4722
|
-
}
|
|
4723
|
-
function normalizeResponseHelper(serializer, store, modelClass, payload, id, requestType) {
|
|
4724
|
-
let normalizedResponse = serializer ? serializer.normalizeResponse(store, modelClass, payload, id, requestType) : payload;
|
|
4725
|
-
validateDocumentStructure(normalizedResponse);
|
|
4726
|
-
return normalizedResponse;
|
|
4727
|
-
}
|
|
4728
4557
|
const Touching = Symbol('touching');
|
|
4729
4558
|
const RequestPromise = Symbol('promise');
|
|
4730
4559
|
function hasRecordIdentifier(op) {
|
|
4731
4560
|
return 'recordIdentifier' in op;
|
|
4732
4561
|
}
|
|
4733
4562
|
class RequestCache {
|
|
4734
|
-
constructor() {
|
|
4563
|
+
constructor(store) {
|
|
4735
4564
|
this._pending = Object.create(null);
|
|
4736
4565
|
this._done = new Map();
|
|
4737
4566
|
this._subscriptions = Object.create(null);
|
|
4567
|
+
this._toFlush = [];
|
|
4568
|
+
this._store = void 0;
|
|
4569
|
+
this._store = store;
|
|
4570
|
+
}
|
|
4571
|
+
_clearEntries(identifier) {
|
|
4572
|
+
this._done.delete(identifier);
|
|
4738
4573
|
}
|
|
4739
4574
|
enqueue(promise, queryRequest) {
|
|
4740
4575
|
let query = queryRequest.data[0];
|
|
@@ -4753,7 +4588,7 @@ class RequestCache {
|
|
|
4753
4588
|
request[RequestPromise] = promise;
|
|
4754
4589
|
this._pending[lid].push(request);
|
|
4755
4590
|
this._triggerSubscriptions(request);
|
|
4756
|
-
promise.then(result => {
|
|
4591
|
+
return promise.then(result => {
|
|
4757
4592
|
this._dequeue(lid, request);
|
|
4758
4593
|
let finalizedRequest = {
|
|
4759
4594
|
state: 'fulfilled',
|
|
@@ -4766,6 +4601,7 @@ class RequestCache {
|
|
|
4766
4601
|
finalizedRequest[Touching] = request[Touching];
|
|
4767
4602
|
this._addDone(finalizedRequest);
|
|
4768
4603
|
this._triggerSubscriptions(finalizedRequest);
|
|
4604
|
+
return result;
|
|
4769
4605
|
}, error => {
|
|
4770
4606
|
this._dequeue(lid, request);
|
|
4771
4607
|
let finalizedRequest = {
|
|
@@ -4779,10 +4615,30 @@ class RequestCache {
|
|
|
4779
4615
|
finalizedRequest[Touching] = request[Touching];
|
|
4780
4616
|
this._addDone(finalizedRequest);
|
|
4781
4617
|
this._triggerSubscriptions(finalizedRequest);
|
|
4618
|
+
throw error;
|
|
4782
4619
|
});
|
|
4783
4620
|
}
|
|
4621
|
+
assert(`Expected a well formed query`);
|
|
4784
4622
|
}
|
|
4785
4623
|
_triggerSubscriptions(req) {
|
|
4624
|
+
if (req.state === 'pending') {
|
|
4625
|
+
this._flushRequest(req);
|
|
4626
|
+
return;
|
|
4627
|
+
}
|
|
4628
|
+
this._toFlush.push(req);
|
|
4629
|
+
if (this._toFlush.length === 1) {
|
|
4630
|
+
this._store.notifications._onNextFlush(() => {
|
|
4631
|
+
this._flush();
|
|
4632
|
+
});
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
_flush() {
|
|
4636
|
+
this._toFlush.forEach(req => {
|
|
4637
|
+
this._flushRequest(req);
|
|
4638
|
+
});
|
|
4639
|
+
this._toFlush = [];
|
|
4640
|
+
}
|
|
4641
|
+
_flushRequest(req) {
|
|
4786
4642
|
req[Touching].forEach(identifier => {
|
|
4787
4643
|
if (this._subscriptions[identifier.lid]) {
|
|
4788
4644
|
this._subscriptions[identifier.lid].forEach(callback => callback(req));
|
|
@@ -4823,636 +4679,16 @@ class RequestCache {
|
|
|
4823
4679
|
getPendingRequestsForRecord(identifier) {
|
|
4824
4680
|
if (this._pending[identifier.lid]) {
|
|
4825
4681
|
return this._pending[identifier.lid];
|
|
4826
|
-
}
|
|
4827
|
-
return [];
|
|
4828
|
-
}
|
|
4829
|
-
getLastRequestForRecord(identifier) {
|
|
4830
|
-
let requests = this._done.get(identifier);
|
|
4831
|
-
if (requests) {
|
|
4832
|
-
return requests[requests.length - 1];
|
|
4833
|
-
}
|
|
4834
|
-
return null;
|
|
4835
|
-
}
|
|
4836
|
-
}
|
|
4837
|
-
|
|
4838
|
-
/**
|
|
4839
|
-
* @module @ember-data/store
|
|
4840
|
-
*/
|
|
4841
|
-
function payloadIsNotBlank$1(adapterPayload) {
|
|
4842
|
-
if (Array.isArray(adapterPayload)) {
|
|
4843
|
-
return true;
|
|
4844
|
-
} else {
|
|
4845
|
-
return Object.keys(adapterPayload || {}).length !== 0;
|
|
4846
|
-
}
|
|
4847
|
-
}
|
|
4848
|
-
const SaveOp = Symbol('SaveOp');
|
|
4849
|
-
/**
|
|
4850
|
-
* Manages the state of network requests initiated by the store
|
|
4851
|
-
*
|
|
4852
|
-
* @class FetchManager
|
|
4853
|
-
* @private
|
|
4854
|
-
*/
|
|
4855
|
-
class FetchManager {
|
|
4856
|
-
// saves which are pending in the runloop
|
|
4857
|
-
|
|
4858
|
-
// fetches pending in the runloop, waiting to be coalesced
|
|
4859
|
-
|
|
4860
|
-
constructor(store) {
|
|
4861
|
-
this._store = store;
|
|
4862
|
-
// used to keep track of all the find requests that need to be coalesced
|
|
4863
|
-
this._pendingFetch = new Map();
|
|
4864
|
-
this._pendingSave = [];
|
|
4865
|
-
this.requestCache = new RequestCache();
|
|
4866
|
-
this.isDestroyed = false;
|
|
4867
|
-
}
|
|
4868
|
-
clearEntries(identifier) {
|
|
4869
|
-
this.requestCache._done.delete(identifier);
|
|
4870
|
-
}
|
|
4871
|
-
|
|
4872
|
-
/**
|
|
4873
|
-
This method is called by `record.save`, and gets passed a
|
|
4874
|
-
resolver for the promise that `record.save` returns.
|
|
4875
|
-
It schedules saving to happen at the end of the run loop.
|
|
4876
|
-
@internal
|
|
4877
|
-
*/
|
|
4878
|
-
scheduleSave(identifier, options) {
|
|
4879
|
-
let promiseLabel = 'DS: Model#save ' + this;
|
|
4880
|
-
let resolver = RSVP.defer(promiseLabel);
|
|
4881
|
-
let query = {
|
|
4882
|
-
op: 'saveRecord',
|
|
4883
|
-
recordIdentifier: identifier,
|
|
4884
|
-
options
|
|
4885
|
-
};
|
|
4886
|
-
let queryRequest = {
|
|
4887
|
-
data: [query]
|
|
4888
|
-
};
|
|
4889
|
-
let snapshot = new Snapshot(options, identifier, this._store);
|
|
4890
|
-
let pendingSaveItem = {
|
|
4891
|
-
snapshot: snapshot,
|
|
4892
|
-
resolver: resolver,
|
|
4893
|
-
identifier,
|
|
4894
|
-
options,
|
|
4895
|
-
queryRequest
|
|
4896
|
-
};
|
|
4897
|
-
this._pendingSave.push(pendingSaveItem);
|
|
4898
|
-
_backburner.scheduleOnce('actions', this, this._flushPendingSaves);
|
|
4899
|
-
this.requestCache.enqueue(resolver.promise, pendingSaveItem.queryRequest);
|
|
4900
|
-
return resolver.promise;
|
|
4901
|
-
}
|
|
4902
|
-
|
|
4903
|
-
/**
|
|
4904
|
-
This method is called at the end of the run loop, and
|
|
4905
|
-
flushes any records passed into `scheduleSave`
|
|
4906
|
-
@method flushPendingSave
|
|
4907
|
-
@internal
|
|
4908
|
-
*/
|
|
4909
|
-
_flushPendingSaves() {
|
|
4910
|
-
const store = this._store;
|
|
4911
|
-
let pending = this._pendingSave.slice();
|
|
4912
|
-
this._pendingSave = [];
|
|
4913
|
-
for (let i = 0, j = pending.length; i < j; i++) {
|
|
4914
|
-
let pendingItem = pending[i];
|
|
4915
|
-
_flushPendingSave(store, pendingItem);
|
|
4916
|
-
}
|
|
4917
|
-
}
|
|
4918
|
-
scheduleFetch(identifier, options) {
|
|
4919
|
-
// TODO Probably the store should pass in the query object
|
|
4920
|
-
let shouldTrace = isDevelopingApp() && this._store.generateStackTracesForTrackedRequests;
|
|
4921
|
-
let query = {
|
|
4922
|
-
op: 'findRecord',
|
|
4923
|
-
recordIdentifier: identifier,
|
|
4924
|
-
options
|
|
4925
|
-
};
|
|
4926
|
-
let queryRequest = {
|
|
4927
|
-
data: [query]
|
|
4928
|
-
};
|
|
4929
|
-
let pendingFetch = this.getPendingFetch(identifier, options);
|
|
4930
|
-
if (pendingFetch) {
|
|
4931
|
-
return pendingFetch;
|
|
4932
|
-
}
|
|
4933
|
-
let id = identifier.id;
|
|
4934
|
-
let modelName = identifier.type;
|
|
4935
|
-
let resolver = RSVP.defer(`Fetching ${modelName}' with id: ${id}`);
|
|
4936
|
-
let pendingFetchItem = {
|
|
4937
|
-
identifier,
|
|
4938
|
-
resolver,
|
|
4939
|
-
options,
|
|
4940
|
-
queryRequest
|
|
4941
|
-
};
|
|
4942
|
-
if (macroCondition(isDevelopingApp())) {
|
|
4943
|
-
if (shouldTrace) {
|
|
4944
|
-
let trace;
|
|
4945
|
-
try {
|
|
4946
|
-
throw new Error(`Trace Origin for scheduled fetch for ${modelName}:${id}.`);
|
|
4947
|
-
} catch (e) {
|
|
4948
|
-
trace = e;
|
|
4949
|
-
}
|
|
4950
|
-
|
|
4951
|
-
// enable folks to discover the origin of this findRecord call when
|
|
4952
|
-
// debugging. Ideally we would have a tracked queue for requests with
|
|
4953
|
-
// labels or local IDs that could be used to merge this trace with
|
|
4954
|
-
// the trace made available when we detect an async leak
|
|
4955
|
-
pendingFetchItem.trace = trace;
|
|
4956
|
-
}
|
|
4957
|
-
}
|
|
4958
|
-
let resolverPromise = resolver.promise;
|
|
4959
|
-
const store = this._store;
|
|
4960
|
-
const isLoading = !store._instanceCache.recordIsLoaded(identifier); // we don't use isLoading directly because we are the request
|
|
4961
|
-
|
|
4962
|
-
const promise = resolverPromise.then(payload => {
|
|
4963
|
-
// ensure that regardless of id returned we assign to the correct record
|
|
4964
|
-
if (payload.data && !Array.isArray(payload.data)) {
|
|
4965
|
-
payload.data.lid = identifier.lid;
|
|
4966
|
-
}
|
|
4967
|
-
|
|
4968
|
-
// additional data received in the payload
|
|
4969
|
-
// may result in the merging of identifiers (and thus records)
|
|
4970
|
-
let potentiallyNewIm = store._push(payload);
|
|
4971
|
-
if (potentiallyNewIm && !Array.isArray(potentiallyNewIm)) {
|
|
4972
|
-
return potentiallyNewIm;
|
|
4973
|
-
}
|
|
4974
|
-
return identifier;
|
|
4975
|
-
}, error => {
|
|
4976
|
-
const recordData = store._instanceCache.peek({
|
|
4977
|
-
identifier,
|
|
4978
|
-
bucket: 'recordData'
|
|
4979
|
-
});
|
|
4980
|
-
if (!recordData || recordData.isEmpty(identifier) || isLoading) {
|
|
4981
|
-
let isReleasable = true;
|
|
4982
|
-
if (macroCondition(dependencySatisfies("@ember-data/graph", "*"))) {
|
|
4983
|
-
if (!recordData) {
|
|
4984
|
-
const graphFor = importSync('@ember-data/graph/-private').graphFor;
|
|
4985
|
-
const graph = graphFor(store);
|
|
4986
|
-
isReleasable = graph.isReleasable(identifier);
|
|
4987
|
-
if (!isReleasable) {
|
|
4988
|
-
graph.unload(identifier, true);
|
|
4989
|
-
}
|
|
4990
|
-
}
|
|
4991
|
-
}
|
|
4992
|
-
if (recordData || isReleasable) {
|
|
4993
|
-
store._instanceCache.unloadRecord(identifier);
|
|
4994
|
-
}
|
|
4995
|
-
}
|
|
4996
|
-
throw error;
|
|
4997
|
-
});
|
|
4998
|
-
if (this._pendingFetch.size === 0) {
|
|
4999
|
-
_backburner.schedule('actions', this, this.flushAllPendingFetches);
|
|
5000
|
-
}
|
|
5001
|
-
let fetches = this._pendingFetch;
|
|
5002
|
-
if (!fetches.has(modelName)) {
|
|
5003
|
-
fetches.set(modelName, []);
|
|
5004
|
-
}
|
|
5005
|
-
fetches.get(modelName).push(pendingFetchItem);
|
|
5006
|
-
pendingFetchItem.promise = promise;
|
|
5007
|
-
this.requestCache.enqueue(resolverPromise, pendingFetchItem.queryRequest);
|
|
5008
|
-
return promise;
|
|
5009
|
-
}
|
|
5010
|
-
getPendingFetch(identifier, options) {
|
|
5011
|
-
let pendingFetches = this._pendingFetch.get(identifier.type);
|
|
5012
|
-
|
|
5013
|
-
// We already have a pending fetch for this
|
|
5014
|
-
if (pendingFetches) {
|
|
5015
|
-
let matchingPendingFetch = pendingFetches.find(fetch => fetch.identifier === identifier && isSameRequest(options, fetch.options));
|
|
5016
|
-
if (matchingPendingFetch) {
|
|
5017
|
-
return matchingPendingFetch.promise;
|
|
5018
|
-
}
|
|
5019
|
-
}
|
|
5020
|
-
}
|
|
5021
|
-
flushAllPendingFetches() {
|
|
5022
|
-
if (this.isDestroyed) {
|
|
5023
|
-
return;
|
|
5024
|
-
}
|
|
5025
|
-
const store = this._store;
|
|
5026
|
-
this._pendingFetch.forEach((fetchItem, type) => _flushPendingFetchForType(store, fetchItem, type));
|
|
5027
|
-
this._pendingFetch.clear();
|
|
5028
|
-
}
|
|
5029
|
-
destroy() {
|
|
5030
|
-
this.isDestroyed = true;
|
|
5031
|
-
}
|
|
5032
|
-
}
|
|
5033
|
-
|
|
5034
|
-
// this function helps resolve whether we have a pending request that we should use instead
|
|
5035
|
-
function isSameRequest(options = {}, existingOptions = {}) {
|
|
5036
|
-
let includedMatches = !options.include || options.include === existingOptions.include;
|
|
5037
|
-
let adapterOptionsMatches = options.adapterOptions === existingOptions.adapterOptions;
|
|
5038
|
-
return includedMatches && adapterOptionsMatches;
|
|
5039
|
-
}
|
|
5040
|
-
function _findMany(store, adapter, modelName, snapshots) {
|
|
5041
|
-
let modelClass = store.modelFor(modelName); // `adapter.findMany` gets the modelClass still
|
|
5042
|
-
const ids = snapshots.map(s => s.id);
|
|
5043
|
-
assert(`Cannot fetch a record without an id`, ids.every(v => v !== null));
|
|
5044
|
-
assert(`Expected this adapter to implement findMany for coalescing`, adapter.findMany);
|
|
5045
|
-
let promise = adapter.findMany(store, modelClass, ids, snapshots);
|
|
5046
|
-
let label = `DS: Handle Adapter#findMany of '${modelName}'`;
|
|
5047
|
-
if (promise === undefined) {
|
|
5048
|
-
throw new Error('adapter.findMany returned undefined, this was very likely a mistake');
|
|
5049
|
-
}
|
|
5050
|
-
promise = guardDestroyedStore(promise, store, label);
|
|
5051
|
-
return promise.then(adapterPayload => {
|
|
5052
|
-
assert(`You made a 'findMany' request for '${modelName}' records with ids '[${ids}]', but the adapter's response did not have any data`, !!payloadIsNotBlank$1(adapterPayload));
|
|
5053
|
-
let serializer = store.serializerFor(modelName);
|
|
5054
|
-
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findMany');
|
|
5055
|
-
return payload;
|
|
5056
|
-
});
|
|
5057
|
-
}
|
|
5058
|
-
function rejectFetchedItems(fetchMap, snapshots, error) {
|
|
5059
|
-
for (let i = 0, l = snapshots.length; i < l; i++) {
|
|
5060
|
-
let snapshot = snapshots[i];
|
|
5061
|
-
let pair = fetchMap.get(snapshot);
|
|
5062
|
-
if (pair) {
|
|
5063
|
-
pair.resolver.reject(error || new Error(`Expected: '<${snapshot.modelName}:${snapshot.id}>' to be present in the adapter provided payload, but it was not found.`));
|
|
5064
|
-
}
|
|
5065
|
-
}
|
|
5066
|
-
}
|
|
5067
|
-
function handleFoundRecords(store, fetchMap, snapshots, coalescedPayload) {
|
|
5068
|
-
/*
|
|
5069
|
-
It is possible that the same ID is included multiple times
|
|
5070
|
-
via multiple snapshots. This happens when more than one
|
|
5071
|
-
options hash was supplied, each of which must be uniquely
|
|
5072
|
-
accounted for.
|
|
5073
|
-
However, since we can't map from response to a specific
|
|
5074
|
-
options object, we resolve all snapshots by id with
|
|
5075
|
-
the first response we see.
|
|
5076
|
-
*/
|
|
5077
|
-
let snapshotsById = new Map();
|
|
5078
|
-
for (let i = 0; i < snapshots.length; i++) {
|
|
5079
|
-
let id = snapshots[i].id;
|
|
5080
|
-
let snapshotGroup = snapshotsById.get(id);
|
|
5081
|
-
if (!snapshotGroup) {
|
|
5082
|
-
snapshotGroup = [];
|
|
5083
|
-
snapshotsById.set(id, snapshotGroup);
|
|
5084
|
-
}
|
|
5085
|
-
snapshotGroup.push(snapshots[i]);
|
|
5086
|
-
}
|
|
5087
|
-
const included = Array.isArray(coalescedPayload.included) ? coalescedPayload.included : [];
|
|
5088
|
-
|
|
5089
|
-
// resolve found records
|
|
5090
|
-
let resources = coalescedPayload.data;
|
|
5091
|
-
for (let i = 0, l = resources.length; i < l; i++) {
|
|
5092
|
-
let resource = resources[i];
|
|
5093
|
-
let snapshotGroup = snapshotsById.get(resource.id);
|
|
5094
|
-
snapshotsById.delete(resource.id);
|
|
5095
|
-
if (!snapshotGroup) {
|
|
5096
|
-
// TODO consider whether this should be a deprecation/assertion
|
|
5097
|
-
included.push(resource);
|
|
5098
|
-
} else {
|
|
5099
|
-
snapshotGroup.forEach(snapshot => {
|
|
5100
|
-
let pair = fetchMap.get(snapshot);
|
|
5101
|
-
let resolver = pair.resolver;
|
|
5102
|
-
resolver.resolve({
|
|
5103
|
-
data: resource
|
|
5104
|
-
});
|
|
5105
|
-
});
|
|
5106
|
-
}
|
|
5107
|
-
}
|
|
5108
|
-
if (included.length > 0) {
|
|
5109
|
-
store._push({
|
|
5110
|
-
data: null,
|
|
5111
|
-
included
|
|
5112
|
-
});
|
|
5113
|
-
}
|
|
5114
|
-
if (snapshotsById.size === 0) {
|
|
5115
|
-
return;
|
|
5116
|
-
}
|
|
5117
|
-
|
|
5118
|
-
// reject missing records
|
|
5119
|
-
let rejected = [];
|
|
5120
|
-
snapshotsById.forEach(snapshots => {
|
|
5121
|
-
rejected.push(...snapshots);
|
|
5122
|
-
});
|
|
5123
|
-
warn('Ember Data expected to find records with the following ids in the adapter response from findMany but they were missing: [ "' + [...snapshotsById.values()].map(r => r[0].id).join('", "') + '" ]', {
|
|
5124
|
-
id: 'ds.store.missing-records-from-adapter'
|
|
5125
|
-
});
|
|
5126
|
-
rejectFetchedItems(fetchMap, rejected);
|
|
5127
|
-
}
|
|
5128
|
-
function _fetchRecord(store, fetchItem) {
|
|
5129
|
-
let identifier = fetchItem.identifier;
|
|
5130
|
-
let modelName = identifier.type;
|
|
5131
|
-
let adapter = store.adapterFor(modelName);
|
|
5132
|
-
assert(`You tried to find a record but you have no adapter (for ${modelName})`, adapter);
|
|
5133
|
-
assert(`You tried to find a record but your adapter (for ${modelName}) does not implement 'findRecord'`, typeof adapter.findRecord === 'function');
|
|
5134
|
-
let snapshot = new Snapshot(fetchItem.options, identifier, store);
|
|
5135
|
-
let klass = store.modelFor(identifier.type);
|
|
5136
|
-
let id = identifier.id;
|
|
5137
|
-
let label = `DS: Handle Adapter#findRecord of '${modelName}' with id: '${id}'`;
|
|
5138
|
-
let promise = guardDestroyedStore(resolve().then(() => {
|
|
5139
|
-
return adapter.findRecord(store, klass, identifier.id, snapshot);
|
|
5140
|
-
}), store, label).then(adapterPayload => {
|
|
5141
|
-
assert(`You made a 'findRecord' request for a '${modelName}' with id '${id}', but the adapter's response did not have any data`, !!payloadIsNotBlank$1(adapterPayload));
|
|
5142
|
-
let serializer = store.serializerFor(modelName);
|
|
5143
|
-
let payload = normalizeResponseHelper(serializer, store, klass, adapterPayload, id, 'findRecord');
|
|
5144
|
-
assert(`Ember Data expected the primary data returned from a 'findRecord' response to be an object but instead it found an array.`, !Array.isArray(payload.data));
|
|
5145
|
-
assert(`The 'findRecord' request for ${modelName}:${id} resolved indicating success but contained no primary data. To indicate a 404 not found you should either reject the promise returned by the adapter's findRecord method or throw a NotFoundError.`, 'data' in payload && payload.data !== null && typeof payload.data === 'object');
|
|
5146
|
-
warn(`You requested a record of type '${modelName}' with id '${id}' but the adapter returned a payload with primary data having an id of '${payload.data.id}'. Use 'store.findRecord()' when the requested id is the same as the one returned by the adapter. In other cases use 'store.queryRecord()' instead.`, coerceId(payload.data.id) === coerceId(id), {
|
|
5147
|
-
id: 'ds.store.findRecord.id-mismatch'
|
|
5148
|
-
});
|
|
5149
|
-
return payload;
|
|
5150
|
-
});
|
|
5151
|
-
fetchItem.resolver.resolve(promise);
|
|
5152
|
-
}
|
|
5153
|
-
function _processCoalescedGroup(store, fetchMap, group, adapter, modelName) {
|
|
5154
|
-
if (group.length > 1) {
|
|
5155
|
-
_findMany(store, adapter, modelName, group).then(payloads => {
|
|
5156
|
-
handleFoundRecords(store, fetchMap, group, payloads);
|
|
5157
|
-
}).catch(error => {
|
|
5158
|
-
rejectFetchedItems(fetchMap, group, error);
|
|
5159
|
-
});
|
|
5160
|
-
} else if (group.length === 1) {
|
|
5161
|
-
_fetchRecord(store, fetchMap.get(group[0]));
|
|
5162
|
-
} else {
|
|
5163
|
-
assert("You cannot return an empty array from adapter's method groupRecordsForFindMany", false);
|
|
5164
|
-
}
|
|
5165
|
-
}
|
|
5166
|
-
function _flushPendingFetchForType(store, pendingFetchItems, modelName) {
|
|
5167
|
-
let adapter = store.adapterFor(modelName);
|
|
5168
|
-
let shouldCoalesce = !!adapter.findMany && adapter.coalesceFindRequests;
|
|
5169
|
-
let totalItems = pendingFetchItems.length;
|
|
5170
|
-
if (shouldCoalesce) {
|
|
5171
|
-
let snapshots = new Array(totalItems);
|
|
5172
|
-
let fetchMap = new Map();
|
|
5173
|
-
for (let i = 0; i < totalItems; i++) {
|
|
5174
|
-
let fetchItem = pendingFetchItems[i];
|
|
5175
|
-
snapshots[i] = new Snapshot(fetchItem.options, fetchItem.identifier, store);
|
|
5176
|
-
fetchMap.set(snapshots[i], fetchItem);
|
|
5177
|
-
}
|
|
5178
|
-
let groups;
|
|
5179
|
-
if (adapter.groupRecordsForFindMany) {
|
|
5180
|
-
groups = adapter.groupRecordsForFindMany(store, snapshots);
|
|
5181
|
-
} else {
|
|
5182
|
-
groups = [snapshots];
|
|
5183
|
-
}
|
|
5184
|
-
for (let i = 0, l = groups.length; i < l; i++) {
|
|
5185
|
-
_processCoalescedGroup(store, fetchMap, groups[i], adapter, modelName);
|
|
5186
|
-
}
|
|
5187
|
-
} else {
|
|
5188
|
-
for (let i = 0; i < totalItems; i++) {
|
|
5189
|
-
_fetchRecord(store, pendingFetchItems[i]);
|
|
5190
|
-
}
|
|
5191
|
-
}
|
|
5192
|
-
}
|
|
5193
|
-
function _flushPendingSave(store, pending) {
|
|
5194
|
-
const {
|
|
5195
|
-
snapshot,
|
|
5196
|
-
resolver,
|
|
5197
|
-
identifier,
|
|
5198
|
-
options
|
|
5199
|
-
} = pending;
|
|
5200
|
-
const adapter = store.adapterFor(identifier.type);
|
|
5201
|
-
const operation = options[SaveOp];
|
|
5202
|
-
let modelName = snapshot.modelName;
|
|
5203
|
-
let modelClass = store.modelFor(modelName);
|
|
5204
|
-
const record = store._instanceCache.getRecord(identifier);
|
|
5205
|
-
assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter);
|
|
5206
|
-
assert(`You tried to update a record but your adapter (for ${modelName}) does not implement '${operation}'`, typeof adapter[operation] === 'function');
|
|
5207
|
-
let promise = resolve().then(() => adapter[operation](store, modelClass, snapshot));
|
|
5208
|
-
let serializer = store.serializerFor(modelName);
|
|
5209
|
-
let label = `DS: Extract and notify about ${operation} completion of ${identifier}`;
|
|
5210
|
-
assert(`Your adapter's '${operation}' method must return a value, but it returned 'undefined'`, promise !== undefined);
|
|
5211
|
-
promise = _guard(guardDestroyedStore(promise, store, label), _bind(_objectIsAlive, record)).then(adapterPayload => {
|
|
5212
|
-
if (!_objectIsAlive(record)) {
|
|
5213
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_RSVP_PROMISE)) {
|
|
5214
|
-
deprecate(`A Promise while saving ${modelName} did not resolve by the time your model was destroyed. This will error in a future release.`, false, {
|
|
5215
|
-
id: 'ember-data:rsvp-unresolved-async',
|
|
5216
|
-
until: '5.0',
|
|
5217
|
-
for: '@ember-data/store',
|
|
5218
|
-
since: {
|
|
5219
|
-
available: '4.5',
|
|
5220
|
-
enabled: '4.5'
|
|
5221
|
-
}
|
|
5222
|
-
});
|
|
5223
|
-
}
|
|
5224
|
-
}
|
|
5225
|
-
if (adapterPayload) {
|
|
5226
|
-
return normalizeResponseHelper(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
|
|
5227
|
-
}
|
|
5228
|
-
});
|
|
5229
|
-
resolver.resolve(promise);
|
|
5230
|
-
}
|
|
5231
|
-
|
|
5232
|
-
/**
|
|
5233
|
-
SnapshotRecordArray is not directly instantiable.
|
|
5234
|
-
Instances are provided to consuming application's
|
|
5235
|
-
adapters for certain requests.
|
|
5236
|
-
|
|
5237
|
-
@class SnapshotRecordArray
|
|
5238
|
-
@public
|
|
5239
|
-
*/
|
|
5240
|
-
class SnapshotRecordArray {
|
|
5241
|
-
/**
|
|
5242
|
-
SnapshotRecordArray is not directly instantiable.
|
|
5243
|
-
Instances are provided to consuming application's
|
|
5244
|
-
adapters and serializers for certain requests.
|
|
5245
|
-
@method constructor
|
|
5246
|
-
@private
|
|
5247
|
-
@constructor
|
|
5248
|
-
@param {RecordArray} recordArray
|
|
5249
|
-
@param options
|
|
5250
|
-
*/
|
|
5251
|
-
constructor(store, recordArray, options = {}) {
|
|
5252
|
-
this.__store = store;
|
|
5253
|
-
/**
|
|
5254
|
-
An array of snapshots
|
|
5255
|
-
@private
|
|
5256
|
-
@property _snapshots
|
|
5257
|
-
@type {Array}
|
|
5258
|
-
*/
|
|
5259
|
-
this._snapshots = null;
|
|
5260
|
-
|
|
5261
|
-
/**
|
|
5262
|
-
An array of records
|
|
5263
|
-
@private
|
|
5264
|
-
@property _recordArray
|
|
5265
|
-
@type {Array}
|
|
5266
|
-
*/
|
|
5267
|
-
this._recordArray = recordArray;
|
|
5268
|
-
|
|
5269
|
-
/**
|
|
5270
|
-
Number of records in the array
|
|
5271
|
-
Example
|
|
5272
|
-
```app/adapters/post.js
|
|
5273
|
-
import JSONAPIAdapter from '@ember-data/adapter/json-api';
|
|
5274
|
-
export default class PostAdapter extends JSONAPIAdapter {
|
|
5275
|
-
shouldReloadAll(store, snapshotRecordArray) {
|
|
5276
|
-
return !snapshotRecordArray.length;
|
|
5277
|
-
}
|
|
5278
|
-
});
|
|
5279
|
-
```
|
|
5280
|
-
@property length
|
|
5281
|
-
@public
|
|
5282
|
-
@type {Number}
|
|
5283
|
-
*/
|
|
5284
|
-
this.length = recordArray.length; // deal with computedProperty shennanigans
|
|
5285
|
-
|
|
5286
|
-
/**
|
|
5287
|
-
A hash of adapter options passed into the store method for this request.
|
|
5288
|
-
Example
|
|
5289
|
-
```app/adapters/post.js
|
|
5290
|
-
import MyCustomAdapter from './custom-adapter';
|
|
5291
|
-
export default class PostAdapter extends MyCustomAdapter {
|
|
5292
|
-
findAll(store, type, sinceToken, snapshotRecordArray) {
|
|
5293
|
-
if (snapshotRecordArray.adapterOptions.subscribe) {
|
|
5294
|
-
// ...
|
|
5295
|
-
}
|
|
5296
|
-
// ...
|
|
5297
|
-
}
|
|
5298
|
-
}
|
|
5299
|
-
```
|
|
5300
|
-
@property adapterOptions
|
|
5301
|
-
@public
|
|
5302
|
-
@type {Object}
|
|
5303
|
-
*/
|
|
5304
|
-
this.adapterOptions = options.adapterOptions;
|
|
5305
|
-
|
|
5306
|
-
/**
|
|
5307
|
-
The relationships to include for this request.
|
|
5308
|
-
Example
|
|
5309
|
-
```app/adapters/application.js
|
|
5310
|
-
import Adapter from '@ember-data/adapter';
|
|
5311
|
-
export default class ApplicationAdapter extends Adapter {
|
|
5312
|
-
findAll(store, type, snapshotRecordArray) {
|
|
5313
|
-
let url = `/${type.modelName}?include=${encodeURIComponent(snapshotRecordArray.include)}`;
|
|
5314
|
-
return fetch(url).then((response) => response.json())
|
|
5315
|
-
}
|
|
5316
|
-
}
|
|
5317
|
-
```
|
|
5318
|
-
@property include
|
|
5319
|
-
@public
|
|
5320
|
-
@type {String|Array}
|
|
5321
|
-
*/
|
|
5322
|
-
this.include = options.include;
|
|
5323
|
-
}
|
|
5324
|
-
|
|
5325
|
-
/**
|
|
5326
|
-
The type of the underlying records for the snapshots in the array, as a Model
|
|
5327
|
-
@property type
|
|
5328
|
-
@deprecated
|
|
5329
|
-
@public
|
|
5330
|
-
@type {Model}
|
|
5331
|
-
*/
|
|
5332
|
-
|
|
5333
|
-
/**
|
|
5334
|
-
The modelName of the underlying records for the snapshots in the array, as a Model
|
|
5335
|
-
@property modelName
|
|
5336
|
-
@public
|
|
5337
|
-
@type {Model}
|
|
5338
|
-
*/
|
|
5339
|
-
get modelName() {
|
|
5340
|
-
return this._recordArray.modelName;
|
|
5341
|
-
}
|
|
5342
|
-
|
|
5343
|
-
/**
|
|
5344
|
-
Get snapshots of the underlying record array
|
|
5345
|
-
Example
|
|
5346
|
-
```app/adapters/post.js
|
|
5347
|
-
import JSONAPIAdapter from '@ember-data/adapter/json-api';
|
|
5348
|
-
export default class PostAdapter extends JSONAPIAdapter {
|
|
5349
|
-
shouldReloadAll(store, snapshotArray) {
|
|
5350
|
-
let snapshots = snapshotArray.snapshots();
|
|
5351
|
-
return snapshots.any(function(ticketSnapshot) {
|
|
5352
|
-
let timeDiff = moment().diff(ticketSnapshot.attr('lastAccessedAt'), 'minutes');
|
|
5353
|
-
if (timeDiff > 20) {
|
|
5354
|
-
return true;
|
|
5355
|
-
} else {
|
|
5356
|
-
return false;
|
|
5357
|
-
}
|
|
5358
|
-
});
|
|
5359
|
-
}
|
|
5360
|
-
}
|
|
5361
|
-
```
|
|
5362
|
-
@method snapshots
|
|
5363
|
-
@public
|
|
5364
|
-
@return {Array} Array of snapshots
|
|
5365
|
-
*/
|
|
5366
|
-
snapshots() {
|
|
5367
|
-
if (this._snapshots !== null) {
|
|
5368
|
-
return this._snapshots;
|
|
5369
|
-
}
|
|
5370
|
-
const {
|
|
5371
|
-
_instanceCache
|
|
5372
|
-
} = this.__store;
|
|
5373
|
-
this._snapshots = this._recordArray[SOURCE].map(identifier => _instanceCache.createSnapshot(identifier));
|
|
5374
|
-
return this._snapshots;
|
|
5375
|
-
}
|
|
5376
|
-
}
|
|
5377
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS)) {
|
|
5378
|
-
Object.defineProperty(SnapshotRecordArray.prototype, 'type', {
|
|
5379
|
-
get() {
|
|
5380
|
-
deprecate(`Using SnapshotRecordArray.type to access the ModelClass for a record is deprecated. Use store.modelFor(<modelName>) instead.`, false, {
|
|
5381
|
-
id: 'ember-data:deprecate-snapshot-model-class-access',
|
|
5382
|
-
until: '5.0',
|
|
5383
|
-
for: 'ember-data',
|
|
5384
|
-
since: {
|
|
5385
|
-
available: '4.5.0',
|
|
5386
|
-
enabled: '4.5.0'
|
|
5387
|
-
}
|
|
5388
|
-
});
|
|
5389
|
-
return this._recordArray.type;
|
|
5390
|
-
}
|
|
5391
|
-
});
|
|
5392
|
-
}
|
|
5393
|
-
|
|
5394
|
-
/**
|
|
5395
|
-
@module @ember-data/store
|
|
5396
|
-
*/
|
|
5397
|
-
|
|
5398
|
-
function payloadIsNotBlank(adapterPayload) {
|
|
5399
|
-
if (Array.isArray(adapterPayload)) {
|
|
5400
|
-
return true;
|
|
5401
|
-
} else {
|
|
5402
|
-
return Object.keys(adapterPayload || {}).length;
|
|
4682
|
+
}
|
|
4683
|
+
return [];
|
|
4684
|
+
}
|
|
4685
|
+
getLastRequestForRecord(identifier) {
|
|
4686
|
+
let requests = this._done.get(identifier);
|
|
4687
|
+
if (requests) {
|
|
4688
|
+
return requests[requests.length - 1];
|
|
4689
|
+
}
|
|
4690
|
+
return null;
|
|
5403
4691
|
}
|
|
5404
|
-
}
|
|
5405
|
-
function _findAll(adapter, store, modelName, options, snapshotArray) {
|
|
5406
|
-
let modelClass = store.modelFor(modelName); // adapter.findAll depends on the class
|
|
5407
|
-
let recordArray = store.peekAll(modelName);
|
|
5408
|
-
snapshotArray = snapshotArray || new SnapshotRecordArray(store, recordArray, options);
|
|
5409
|
-
let promise = Promise$1.resolve().then(() => adapter.findAll(store, modelClass, null, snapshotArray));
|
|
5410
|
-
let label = 'DS: Handle Adapter#findAll of ' + modelClass;
|
|
5411
|
-
promise = guardDestroyedStore(promise, store, label);
|
|
5412
|
-
return promise.then(adapterPayload => {
|
|
5413
|
-
assert(`You made a 'findAll' request for '${modelName}' records, but the adapter's response did not have any data`, payloadIsNotBlank(adapterPayload));
|
|
5414
|
-
let serializer = store.serializerFor(modelName);
|
|
5415
|
-
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findAll');
|
|
5416
|
-
store._push(payload);
|
|
5417
|
-
recordArray.isUpdating = false;
|
|
5418
|
-
return recordArray;
|
|
5419
|
-
}, null, 'DS: Extract payload of findAll ${modelName}');
|
|
5420
|
-
}
|
|
5421
|
-
function _query(adapter, store, modelName, query, recordArray, options) {
|
|
5422
|
-
let modelClass = store.modelFor(modelName); // adapter.query needs the class
|
|
5423
|
-
|
|
5424
|
-
// TODO @deprecate RecordArrays being passed to Adapters
|
|
5425
|
-
recordArray = recordArray || store.recordArrayManager.createArray({
|
|
5426
|
-
type: modelName,
|
|
5427
|
-
query
|
|
5428
|
-
});
|
|
5429
|
-
let promise = Promise$1.resolve().then(() => adapter.query(store, modelClass, query, recordArray, options));
|
|
5430
|
-
let label = `DS: Handle Adapter#query of ${modelName}`;
|
|
5431
|
-
promise = guardDestroyedStore(promise, store, label);
|
|
5432
|
-
return promise.then(adapterPayload => {
|
|
5433
|
-
let serializer = store.serializerFor(modelName);
|
|
5434
|
-
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'query');
|
|
5435
|
-
let identifiers = store._push(payload);
|
|
5436
|
-
assert('The response to store.query is expected to be an array but it was a single record. Please wrap your response in an array or use `store.queryRecord` to query for a single record.', Array.isArray(identifiers));
|
|
5437
|
-
store.recordArrayManager.populateManagedArray(recordArray, identifiers, payload);
|
|
5438
|
-
return recordArray;
|
|
5439
|
-
}, null, `DS: Extract payload of query ${modelName}`);
|
|
5440
|
-
}
|
|
5441
|
-
function _queryRecord(adapter, store, modelName, query, options) {
|
|
5442
|
-
let modelClass = store.modelFor(modelName); // adapter.queryRecord needs the class
|
|
5443
|
-
let promise = Promise$1.resolve().then(() => adapter.queryRecord(store, modelClass, query, options));
|
|
5444
|
-
let label = `DS: Handle Adapter#queryRecord of ${modelName}`;
|
|
5445
|
-
promise = guardDestroyedStore(promise, store, label);
|
|
5446
|
-
return promise.then(adapterPayload => {
|
|
5447
|
-
let serializer = store.serializerFor(modelName);
|
|
5448
|
-
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'queryRecord');
|
|
5449
|
-
assert(`Expected the primary data returned by the serializer for a 'queryRecord' response to be a single object or null but instead it was an array.`, !Array.isArray(payload.data));
|
|
5450
|
-
return store._push(payload);
|
|
5451
|
-
}, null, `DS: Extract payload of queryRecord ${modelName}`);
|
|
5452
|
-
}
|
|
5453
|
-
function promiseRecord(store, promise) {
|
|
5454
|
-
let toReturn = promise.then(identifier => store.peekRecord(identifier));
|
|
5455
|
-
return promiseObject(toReturn);
|
|
5456
4692
|
}
|
|
5457
4693
|
|
|
5458
4694
|
/**
|
|
@@ -5462,155 +4698,110 @@ function promiseRecord(store, promise) {
|
|
|
5462
4698
|
// hello world
|
|
5463
4699
|
|
|
5464
4700
|
let _Cache;
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
Define your application's store like this:
|
|
5478
|
-
|
|
5479
|
-
```app/services/store.js
|
|
5480
|
-
import Store from '@ember-data/store';
|
|
5481
|
-
|
|
5482
|
-
export default class MyStore extends Store {}
|
|
5483
|
-
```
|
|
5484
|
-
|
|
5485
|
-
Most Ember.js applications will only have a single `Store` that is
|
|
5486
|
-
automatically created by their `Ember.Application`.
|
|
5487
|
-
|
|
5488
|
-
You can retrieve models from the store in several ways. To retrieve a record
|
|
5489
|
-
for a specific id, use `Store`'s `findRecord()` method:
|
|
5490
|
-
|
|
5491
|
-
```javascript
|
|
5492
|
-
store.findRecord('person', 123).then(function (person) {
|
|
5493
|
-
});
|
|
5494
|
-
```
|
|
5495
|
-
|
|
5496
|
-
By default, the store will talk to your backend using a standard
|
|
5497
|
-
REST mechanism. You can customize how the store talks to your
|
|
5498
|
-
backend by specifying a custom adapter:
|
|
5499
|
-
|
|
5500
|
-
```app/adapters/application.js
|
|
5501
|
-
import Adapter from '@ember-data/adapter';
|
|
5502
|
-
|
|
5503
|
-
export default class ApplicationAdapter extends Adapter {
|
|
5504
|
-
}
|
|
5505
|
-
```
|
|
5506
|
-
|
|
5507
|
-
You can learn more about writing a custom adapter by reading the `Adapter`
|
|
5508
|
-
documentation.
|
|
5509
|
-
|
|
5510
|
-
### Store createRecord() vs. push() vs. pushPayload()
|
|
5511
|
-
|
|
5512
|
-
The store provides multiple ways to create new record objects. They have
|
|
5513
|
-
some subtle differences in their use which are detailed below:
|
|
4701
|
+
class Store {
|
|
4702
|
+
/**
|
|
4703
|
+
* Provides access to the NotificationManager associated
|
|
4704
|
+
* with this Store instance.
|
|
4705
|
+
*
|
|
4706
|
+
* The NotificationManager can be used to subscribe to
|
|
4707
|
+
* changes to the cache.
|
|
4708
|
+
*
|
|
4709
|
+
* @property {NotificationManager} notifications
|
|
4710
|
+
* @public
|
|
4711
|
+
*/
|
|
5514
4712
|
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
4713
|
+
/**
|
|
4714
|
+
* Provides access to the IdentifierCache instance
|
|
4715
|
+
* for this store.
|
|
4716
|
+
*
|
|
4717
|
+
* The IdentifierCache can be used to generate or
|
|
4718
|
+
* retrieve a stable unique identifier for any resource.
|
|
4719
|
+
*
|
|
4720
|
+
* @property {IdentifierCache} identifierCache
|
|
4721
|
+
* @public
|
|
4722
|
+
*/
|
|
5519
4723
|
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
4724
|
+
/**
|
|
4725
|
+
* Provides access to the requestManager instance associated
|
|
4726
|
+
* with this Store instance.
|
|
4727
|
+
*
|
|
4728
|
+
* When using `ember-data` this property is automatically
|
|
4729
|
+
* set to an instance of `RequestManager`. When not using `ember-data`
|
|
4730
|
+
* you must configure this property yourself, either by declaring
|
|
4731
|
+
* it as a service or by initializing it.
|
|
4732
|
+
*
|
|
4733
|
+
* ```ts
|
|
4734
|
+
* import Store, { CacheHandler } from '@ember-data/store';
|
|
4735
|
+
* import RequestManager from '@ember-data/request';
|
|
4736
|
+
* import Fetch from '@ember/data/request/fetch';
|
|
4737
|
+
*
|
|
4738
|
+
* class extends Store {
|
|
4739
|
+
* constructor() {
|
|
4740
|
+
* super(...arguments);
|
|
4741
|
+
* this.requestManager = new RequestManager();
|
|
4742
|
+
* this.requestManager.use([Fetch]);
|
|
4743
|
+
* this.requestManager.useCache(CacheHandler);
|
|
4744
|
+
* }
|
|
4745
|
+
* }
|
|
4746
|
+
* ```
|
|
4747
|
+
*
|
|
4748
|
+
* @public
|
|
4749
|
+
* @property {RequestManager} requestManager
|
|
4750
|
+
*/
|
|
5527
4751
|
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
4752
|
+
/**
|
|
4753
|
+
* A Property which an App may set to provide a Lifetimes Service
|
|
4754
|
+
* to control when a cached request becomes stale.
|
|
4755
|
+
*
|
|
4756
|
+
* Note, when defined, these methods will only be invoked if `key` `url` and `method`
|
|
4757
|
+
* are all present.
|
|
4758
|
+
*
|
|
4759
|
+
* `isSoftExpired` will only be invoked if `isHardExpired` returns `false`.
|
|
4760
|
+
*
|
|
4761
|
+
* ```ts
|
|
4762
|
+
* store.lifetimes = {
|
|
4763
|
+
* // make the request and ignore the current cache state
|
|
4764
|
+
* isHardExpired(key: string, url: string, method?: HTTPMethod): boolean {
|
|
4765
|
+
* return false;
|
|
4766
|
+
* }
|
|
4767
|
+
*
|
|
4768
|
+
* // make the request in the background if true, return cache state
|
|
4769
|
+
* isSoftExpired(key: string, url: string, method: HTTPMethod): boolean {
|
|
4770
|
+
* return false;
|
|
4771
|
+
* }
|
|
4772
|
+
* }
|
|
4773
|
+
* ```
|
|
4774
|
+
*
|
|
4775
|
+
* @public
|
|
4776
|
+
* @property {LivetimesService|undefined} lifetimes
|
|
4777
|
+
*/
|
|
5531
4778
|
|
|
5532
|
-
|
|
5533
|
-
Ember Data will update `RecordArray`s such as those returned by
|
|
5534
|
-
`store#peekAll()` or `store#findAll()`. This means any
|
|
5535
|
-
data bindings or computed properties that depend on the RecordArray
|
|
5536
|
-
will automatically be synced to include the new or updated record
|
|
5537
|
-
values.
|
|
4779
|
+
// Private
|
|
5538
4780
|
|
|
5539
|
-
|
|
5540
|
-
@class Store
|
|
5541
|
-
@public
|
|
5542
|
-
@extends Ember.Service
|
|
5543
|
-
*/
|
|
4781
|
+
// DEBUG-only properties
|
|
5544
4782
|
|
|
5545
|
-
class Store {
|
|
5546
4783
|
/**
|
|
5547
4784
|
@method init
|
|
5548
4785
|
@private
|
|
5549
4786
|
*/
|
|
5550
4787
|
constructor(createArgs) {
|
|
5551
|
-
this.__private_singleton_recordData = void 0;
|
|
5552
4788
|
this.isDestroying = false;
|
|
5553
4789
|
this.isDestroyed = false;
|
|
5554
4790
|
Object.assign(this, createArgs);
|
|
5555
|
-
|
|
5556
|
-
/**
|
|
5557
|
-
* Provides access to the IdentifierCache instance
|
|
5558
|
-
* for this store.
|
|
5559
|
-
*
|
|
5560
|
-
* The IdentifierCache can be used to generate or
|
|
5561
|
-
* retrieve a stable unique identifier for any resource.
|
|
5562
|
-
*
|
|
5563
|
-
* @property {IdentifierCache} identifierCache
|
|
5564
|
-
* @public
|
|
5565
|
-
*/
|
|
5566
4791
|
this.identifierCache = new IdentifierCache();
|
|
4792
|
+
this.notifications = new NotificationManager(this);
|
|
5567
4793
|
|
|
5568
4794
|
// private but maybe useful to be here, somewhat intimate
|
|
5569
4795
|
this.recordArrayManager = new RecordArrayManager({
|
|
5570
4796
|
store: this
|
|
5571
4797
|
});
|
|
5572
|
-
this.notifications = new NotificationManager(this);
|
|
5573
4798
|
|
|
5574
4799
|
// private
|
|
5575
|
-
this.
|
|
4800
|
+
this._requestCache = new RequestCache(this);
|
|
5576
4801
|
this._instanceCache = new InstanceCache(this);
|
|
5577
4802
|
this._adapterCache = Object.create(null);
|
|
5578
4803
|
this._serializerCache = Object.create(null);
|
|
5579
4804
|
this._modelFactoryCache = Object.create(null);
|
|
5580
|
-
if (macroCondition(isDevelopingApp())) {
|
|
5581
|
-
if (this.generateStackTracesForTrackedRequests === undefined) {
|
|
5582
|
-
this.generateStackTracesForTrackedRequests = false;
|
|
5583
|
-
}
|
|
5584
|
-
this._trackedAsyncRequests = [];
|
|
5585
|
-
this._trackAsyncRequestStart = label => {
|
|
5586
|
-
let trace = 'set `store.generateStackTracesForTrackedRequests = true;` to get a detailed trace for where this request originated';
|
|
5587
|
-
if (this.generateStackTracesForTrackedRequests) {
|
|
5588
|
-
try {
|
|
5589
|
-
throw new Error(`EmberData TrackedRequest: ${label}`);
|
|
5590
|
-
} catch (e) {
|
|
5591
|
-
trace = e;
|
|
5592
|
-
}
|
|
5593
|
-
}
|
|
5594
|
-
let token = freeze({
|
|
5595
|
-
label,
|
|
5596
|
-
trace
|
|
5597
|
-
});
|
|
5598
|
-
this._trackedAsyncRequests.push(token);
|
|
5599
|
-
return token;
|
|
5600
|
-
};
|
|
5601
|
-
this._trackAsyncRequestEnd = token => {
|
|
5602
|
-
let index = this._trackedAsyncRequests.indexOf(token);
|
|
5603
|
-
if (index === -1) {
|
|
5604
|
-
throw new Error(`Attempted to end tracking for the following request but it was not being tracked:\n${token}`);
|
|
5605
|
-
}
|
|
5606
|
-
this._trackedAsyncRequests.splice(index, 1);
|
|
5607
|
-
};
|
|
5608
|
-
this.__asyncWaiter = () => {
|
|
5609
|
-
let tracked = this._trackedAsyncRequests;
|
|
5610
|
-
return this.DISABLE_WAITER || tracked.length === 0;
|
|
5611
|
-
};
|
|
5612
|
-
registerWaiter(this.__asyncWaiter);
|
|
5613
|
-
}
|
|
5614
4805
|
}
|
|
5615
4806
|
_run(cb) {
|
|
5616
4807
|
assert(`EmberData should never encounter a nested run`, !this._cbs);
|
|
@@ -5652,7 +4843,81 @@ class Store {
|
|
|
5652
4843
|
* @public
|
|
5653
4844
|
*/
|
|
5654
4845
|
getRequestStateService() {
|
|
5655
|
-
return this.
|
|
4846
|
+
return this._requestCache;
|
|
4847
|
+
}
|
|
4848
|
+
_getAllPending() {
|
|
4849
|
+
if (macroCondition(getOwnConfig().env.TESTING)) {
|
|
4850
|
+
const all = [];
|
|
4851
|
+
const pending = this._requestCache._pending;
|
|
4852
|
+
const lids = Object.keys(pending);
|
|
4853
|
+
lids.forEach(lid => {
|
|
4854
|
+
all.push(...pending[lid].map(v => v[RequestPromise]));
|
|
4855
|
+
});
|
|
4856
|
+
const promise = Promise.allSettled(all);
|
|
4857
|
+
promise.length = all.length;
|
|
4858
|
+
return promise;
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4862
|
+
/**
|
|
4863
|
+
* Issue a request via the configured RequestManager,
|
|
4864
|
+
* inserting the response into the cache and handing
|
|
4865
|
+
* back a Future which resolves to a ResponseDocument
|
|
4866
|
+
*
|
|
4867
|
+
* Resource data is always updated in the cache.
|
|
4868
|
+
*
|
|
4869
|
+
* Only GET requests have the request result and document
|
|
4870
|
+
* cached by default when a cache key is present.
|
|
4871
|
+
*
|
|
4872
|
+
* The cache key used is `requestConfig.cacheOptions.key`
|
|
4873
|
+
* if present, falling back to `requestconfig.url`.
|
|
4874
|
+
*
|
|
4875
|
+
* Params are not serialized as part of the cache-key, so
|
|
4876
|
+
* either ensure they are already in the url or utilize
|
|
4877
|
+
* `requestConfig.cacheOptions.key`. For queries issued
|
|
4878
|
+
* via the `POST` method `requestConfig.cacheOptions.key`
|
|
4879
|
+
* MUST be supplied for the document to be cached.
|
|
4880
|
+
*
|
|
4881
|
+
* @method request
|
|
4882
|
+
* @param {StoreRequestInfo} requestConfig
|
|
4883
|
+
* @returns {Future}
|
|
4884
|
+
* @public
|
|
4885
|
+
*/
|
|
4886
|
+
request(requestConfig) {
|
|
4887
|
+
// we lazily set the cache handler when we issue the first request
|
|
4888
|
+
// because constructor doesn't allow for this to run after
|
|
4889
|
+
// the user has had the chance to set the prop.
|
|
4890
|
+
let opts = {
|
|
4891
|
+
store: this
|
|
4892
|
+
};
|
|
4893
|
+
if (macroCondition(getOwnConfig().env.TESTING)) {
|
|
4894
|
+
if (this.DISABLE_WAITER) {
|
|
4895
|
+
opts.disableTestWaiter = typeof requestConfig.disableTestWaiter === 'boolean' ? requestConfig.disableTestWaiter : true;
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
if (macroCondition(getOwnConfig().debug.LOG_REQUESTS)) {
|
|
4899
|
+
let options;
|
|
4900
|
+
try {
|
|
4901
|
+
options = JSON.parse(JSON.stringify(requestConfig));
|
|
4902
|
+
} catch {
|
|
4903
|
+
options = requestConfig;
|
|
4904
|
+
}
|
|
4905
|
+
// eslint-disable-next-line no-console
|
|
4906
|
+
console.log(`request: [[START]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`, options);
|
|
4907
|
+
}
|
|
4908
|
+
const future = this.requestManager.request(Object.assign(requestConfig, opts));
|
|
4909
|
+
future.onFinalize(() => {
|
|
4910
|
+
if (macroCondition(getOwnConfig().debug.LOG_REQUESTS)) {
|
|
4911
|
+
// eslint-disable-next-line no-console
|
|
4912
|
+
console.log(`request: [[FINALIZE]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`);
|
|
4913
|
+
}
|
|
4914
|
+
// skip flush for legacy belongsTo
|
|
4915
|
+
if (requestConfig.op === 'findBelongsTo' && !requestConfig.url) {
|
|
4916
|
+
return;
|
|
4917
|
+
}
|
|
4918
|
+
this.notifications._flush();
|
|
4919
|
+
});
|
|
4920
|
+
return future;
|
|
5656
4921
|
}
|
|
5657
4922
|
|
|
5658
4923
|
/**
|
|
@@ -5667,22 +4932,22 @@ class Store {
|
|
|
5667
4932
|
* @method instantiateRecord (hook)
|
|
5668
4933
|
* @param identifier
|
|
5669
4934
|
* @param createRecordArgs
|
|
5670
|
-
* @param recordDataFor
|
|
5671
|
-
* @param notificationManager
|
|
4935
|
+
* @param recordDataFor deprecated use this.cache
|
|
4936
|
+
* @param notificationManager deprecated use this.notifications
|
|
5672
4937
|
* @returns A record instance
|
|
5673
4938
|
* @public
|
|
5674
4939
|
*/
|
|
5675
|
-
instantiateRecord(identifier, createRecordArgs
|
|
5676
|
-
if (macroCondition(
|
|
4940
|
+
instantiateRecord(identifier, createRecordArgs) {
|
|
4941
|
+
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
5677
4942
|
let modelName = identifier.type;
|
|
5678
|
-
|
|
4943
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._instanceCache.getResourceCache(identifier) : this.cache;
|
|
5679
4944
|
// TODO deprecate allowing unknown args setting
|
|
5680
4945
|
let createOptions = {
|
|
5681
4946
|
_createProps: createRecordArgs,
|
|
5682
4947
|
// TODO @deprecate consider deprecating accessing record properties during init which the below is necessary for
|
|
5683
4948
|
_secretInit: {
|
|
5684
4949
|
identifier,
|
|
5685
|
-
|
|
4950
|
+
cache,
|
|
5686
4951
|
store: this,
|
|
5687
4952
|
cb: secretInit
|
|
5688
4953
|
}
|
|
@@ -5706,7 +4971,7 @@ class Store {
|
|
|
5706
4971
|
* @param record
|
|
5707
4972
|
*/
|
|
5708
4973
|
teardownRecord(record) {
|
|
5709
|
-
if (macroCondition(
|
|
4974
|
+
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
5710
4975
|
assert(`expected to receive an instance of DSModel. If using a custom model make sure you implement teardownRecord`, 'destroy' in record);
|
|
5711
4976
|
record.destroy();
|
|
5712
4977
|
} else {
|
|
@@ -5725,7 +4990,7 @@ class Store {
|
|
|
5725
4990
|
* @public
|
|
5726
4991
|
*/
|
|
5727
4992
|
getSchemaDefinitionService() {
|
|
5728
|
-
if (macroCondition(
|
|
4993
|
+
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
5729
4994
|
if (!this._schemaDefinitionService) {
|
|
5730
4995
|
// it is potentially a mistake for the RFC to have not enabled chaining these services, though highlander rule is nice.
|
|
5731
4996
|
// what ember-m3 did via private API to allow both worlds to interop would be much much harder using this.
|
|
@@ -5812,12 +5077,12 @@ class Store {
|
|
|
5812
5077
|
// TODO @deprecate in favor of schema APIs, requires adapter/serializer overhaul or replacement
|
|
5813
5078
|
|
|
5814
5079
|
modelFor(modelName) {
|
|
5815
|
-
if (macroCondition(
|
|
5080
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5816
5081
|
assertDestroyedStoreOnly(this, 'modelFor');
|
|
5817
5082
|
}
|
|
5818
5083
|
assert(`You need to pass a model name to the store's modelFor method`, modelName);
|
|
5819
5084
|
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
|
|
5820
|
-
if (macroCondition(
|
|
5085
|
+
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
5821
5086
|
let normalizedModelName = normalizeModelName$1(modelName);
|
|
5822
5087
|
let maybeFactory = getModelFactory(this, this._modelFactoryCache, normalizedModelName);
|
|
5823
5088
|
|
|
@@ -5860,7 +5125,7 @@ class Store {
|
|
|
5860
5125
|
@return {Model} record
|
|
5861
5126
|
*/
|
|
5862
5127
|
createRecord(modelName, inputProperties) {
|
|
5863
|
-
if (macroCondition(
|
|
5128
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5864
5129
|
assertDestroyingStore(this, 'createRecord');
|
|
5865
5130
|
}
|
|
5866
5131
|
assert(`You need to pass a model name to the store's createRecord method`, modelName);
|
|
@@ -5904,10 +5169,9 @@ class Store {
|
|
|
5904
5169
|
assert(`The id ${properties.id} has already been used with another '${normalizedModelName}' record.`, !identifier);
|
|
5905
5170
|
}
|
|
5906
5171
|
const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
|
|
5907
|
-
const
|
|
5908
|
-
const createOptions = normalizeProperties(this, identifier, properties,
|
|
5909
|
-
const resultProps =
|
|
5910
|
-
this.recordArrayManager.identifierAdded(identifier);
|
|
5172
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._instanceCache.getResourceCache(identifier) : this.cache;
|
|
5173
|
+
const createOptions = normalizeProperties(this, identifier, properties, cache.managedVersion === '1');
|
|
5174
|
+
const resultProps = cache.clientDidCreate(identifier, createOptions);
|
|
5911
5175
|
record = this._instanceCache.getRecord(identifier, resultProps);
|
|
5912
5176
|
});
|
|
5913
5177
|
});
|
|
@@ -5928,18 +5192,18 @@ class Store {
|
|
|
5928
5192
|
@param {Model} record
|
|
5929
5193
|
*/
|
|
5930
5194
|
deleteRecord(record) {
|
|
5931
|
-
if (macroCondition(
|
|
5195
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5932
5196
|
assertDestroyingStore(this, 'deleteRecord');
|
|
5933
5197
|
}
|
|
5934
5198
|
const identifier = peekRecordIdentifier(record);
|
|
5935
|
-
const
|
|
5199
|
+
const cache = identifier && (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._instanceCache.peek({
|
|
5936
5200
|
identifier,
|
|
5937
|
-
bucket: '
|
|
5938
|
-
});
|
|
5939
|
-
assert(`expected a
|
|
5201
|
+
bucket: 'resourceCache'
|
|
5202
|
+
}) : this.cache);
|
|
5203
|
+
assert(`expected a cache instance to exist for the record`, cache);
|
|
5940
5204
|
this._join(() => {
|
|
5941
|
-
|
|
5942
|
-
if (
|
|
5205
|
+
cache.setIsDeleted(identifier, true);
|
|
5206
|
+
if (cache.isNew(identifier)) {
|
|
5943
5207
|
_backburner.join(() => {
|
|
5944
5208
|
this._instanceCache.unloadRecord(identifier);
|
|
5945
5209
|
});
|
|
@@ -5961,7 +5225,7 @@ class Store {
|
|
|
5961
5225
|
@param {Model} record
|
|
5962
5226
|
*/
|
|
5963
5227
|
unloadRecord(record) {
|
|
5964
|
-
if (macroCondition(
|
|
5228
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5965
5229
|
assertDestroyingStore(this, 'unloadRecord');
|
|
5966
5230
|
}
|
|
5967
5231
|
const identifier = peekRecordIdentifier(record);
|
|
@@ -5980,7 +5244,7 @@ class Store {
|
|
|
5980
5244
|
@private
|
|
5981
5245
|
*/
|
|
5982
5246
|
find(modelName, id, options) {
|
|
5983
|
-
if (macroCondition(
|
|
5247
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5984
5248
|
assertDestroyingStore(this, 'find');
|
|
5985
5249
|
}
|
|
5986
5250
|
// The default `model` hook in Route calls `find(modelName, id)`,
|
|
@@ -6290,7 +5554,7 @@ class Store {
|
|
|
6290
5554
|
*/
|
|
6291
5555
|
|
|
6292
5556
|
findRecord(resource, id, options) {
|
|
6293
|
-
if (macroCondition(
|
|
5557
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6294
5558
|
assertDestroyingStore(this, 'findRecord');
|
|
6295
5559
|
}
|
|
6296
5560
|
assert(`You need to pass a modelName or resource identifier as the first argument to the store's findRecord method`, resource);
|
|
@@ -6303,45 +5567,33 @@ class Store {
|
|
|
6303
5567
|
resource = constructResource(type, normalizedId);
|
|
6304
5568
|
}
|
|
6305
5569
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
6306
|
-
let promise;
|
|
6307
5570
|
options = options || {};
|
|
6308
5571
|
if (options.preload) {
|
|
5572
|
+
// force reload if we preload to ensure we don't resolve the promise
|
|
5573
|
+
// until we are complete, else we will end up background-reloading
|
|
5574
|
+
// even for initial load.
|
|
5575
|
+
if (!this._instanceCache.recordIsLoaded(identifier)) {
|
|
5576
|
+
options.reload = true;
|
|
5577
|
+
}
|
|
6309
5578
|
this._join(() => {
|
|
6310
5579
|
preloadData(this, identifier, options.preload);
|
|
6311
5580
|
});
|
|
6312
5581
|
}
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
// Refetch if the reload option is passed
|
|
6319
|
-
} else if (options.reload) {
|
|
6320
|
-
assertIdentifierHasId(identifier);
|
|
6321
|
-
promise = this._fetchManager.scheduleFetch(identifier, options);
|
|
6322
|
-
} else {
|
|
6323
|
-
let snapshot = null;
|
|
6324
|
-
let adapter = this.adapterFor(identifier.type);
|
|
6325
|
-
|
|
6326
|
-
// Refetch the record if the adapter thinks the record is stale
|
|
6327
|
-
if (typeof options.reload === 'undefined' && adapter.shouldReloadRecord && adapter.shouldReloadRecord(this, snapshot = this._instanceCache.createSnapshot(identifier, options))) {
|
|
6328
|
-
assertIdentifierHasId(identifier);
|
|
6329
|
-
promise = this._fetchManager.scheduleFetch(identifier, options);
|
|
6330
|
-
} else {
|
|
6331
|
-
// Trigger the background refetch if backgroundReload option is passed
|
|
6332
|
-
if (options.backgroundReload !== false && (options.backgroundReload || !adapter.shouldBackgroundReloadRecord || adapter.shouldBackgroundReloadRecord(this, snapshot = snapshot || this._instanceCache.createSnapshot(identifier, options)))) {
|
|
6333
|
-
assertIdentifierHasId(identifier);
|
|
6334
|
-
this._fetchManager.scheduleFetch(identifier, options);
|
|
6335
|
-
}
|
|
6336
|
-
|
|
6337
|
-
// Return the cached record
|
|
6338
|
-
promise = resolve(identifier);
|
|
5582
|
+
const promise = this.request({
|
|
5583
|
+
op: 'findRecord',
|
|
5584
|
+
data: {
|
|
5585
|
+
record: identifier,
|
|
5586
|
+
options
|
|
6339
5587
|
}
|
|
6340
|
-
}
|
|
5588
|
+
});
|
|
6341
5589
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
6342
|
-
return
|
|
5590
|
+
return promiseObject(promise.then(document => {
|
|
5591
|
+
return document.content;
|
|
5592
|
+
}));
|
|
6343
5593
|
}
|
|
6344
|
-
return promise.then(
|
|
5594
|
+
return promise.then(document => {
|
|
5595
|
+
return document.content;
|
|
5596
|
+
});
|
|
6345
5597
|
}
|
|
6346
5598
|
|
|
6347
5599
|
/**
|
|
@@ -6375,7 +5627,7 @@ class Store {
|
|
|
6375
5627
|
*/
|
|
6376
5628
|
// TODO @deprecate getReference (and references generally)
|
|
6377
5629
|
getReference(resource, id) {
|
|
6378
|
-
if (macroCondition(
|
|
5630
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6379
5631
|
assertDestroyingStore(this, 'getReference');
|
|
6380
5632
|
}
|
|
6381
5633
|
let resourceIdentifier;
|
|
@@ -6434,7 +5686,7 @@ class Store {
|
|
|
6434
5686
|
// this is basically an "are we not empty" query.
|
|
6435
5687
|
return isLoaded ? this._instanceCache.getRecord(stableIdentifier) : null;
|
|
6436
5688
|
}
|
|
6437
|
-
if (macroCondition(
|
|
5689
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6438
5690
|
assertDestroyingStore(this, 'peekRecord');
|
|
6439
5691
|
}
|
|
6440
5692
|
assert(`You need to pass a model name to the store's peekRecord method`, identifier);
|
|
@@ -6479,7 +5731,7 @@ class Store {
|
|
|
6479
5731
|
until: '5.0',
|
|
6480
5732
|
for: 'ember-data'
|
|
6481
5733
|
});
|
|
6482
|
-
if (macroCondition(
|
|
5734
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6483
5735
|
assertDestroyingStore(this, 'hasRecordForId');
|
|
6484
5736
|
}
|
|
6485
5737
|
assert(`You need to pass a model name to the store's hasRecordForId method`, modelName);
|
|
@@ -6534,26 +5786,24 @@ class Store {
|
|
|
6534
5786
|
@return {Promise} promise
|
|
6535
5787
|
*/
|
|
6536
5788
|
query(modelName, query, options) {
|
|
6537
|
-
if (macroCondition(
|
|
5789
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6538
5790
|
assertDestroyingStore(this, 'query');
|
|
6539
5791
|
}
|
|
6540
5792
|
assert(`You need to pass a model name to the store's query method`, modelName);
|
|
6541
5793
|
assert(`You need to pass a query hash to the store's query method`, query);
|
|
6542
5794
|
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
assert(`You tried to load a query but your adapter does not implement 'query'`, typeof adapter.query === 'function');
|
|
6552
|
-
let queryPromise = _query(adapter, this, normalizedModelName, query, recordArray, adapterOptionsWrapper);
|
|
5795
|
+
const promise = this.request({
|
|
5796
|
+
op: 'query',
|
|
5797
|
+
data: {
|
|
5798
|
+
type: normalizeModelName$1(modelName),
|
|
5799
|
+
query,
|
|
5800
|
+
options: options || {}
|
|
5801
|
+
}
|
|
5802
|
+
});
|
|
6553
5803
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
6554
|
-
return promiseArray(
|
|
5804
|
+
return promiseArray(promise.then(document => document.content));
|
|
6555
5805
|
}
|
|
6556
|
-
return
|
|
5806
|
+
return promise.then(document => document.content);
|
|
6557
5807
|
}
|
|
6558
5808
|
|
|
6559
5809
|
/**
|
|
@@ -6638,25 +5888,24 @@ class Store {
|
|
|
6638
5888
|
@return {Promise} promise which resolves with the found record or `null`
|
|
6639
5889
|
*/
|
|
6640
5890
|
queryRecord(modelName, query, options) {
|
|
6641
|
-
if (macroCondition(
|
|
5891
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6642
5892
|
assertDestroyingStore(this, 'queryRecord');
|
|
6643
5893
|
}
|
|
6644
5894
|
assert(`You need to pass a model name to the store's queryRecord method`, modelName);
|
|
6645
5895
|
assert(`You need to pass a query hash to the store's queryRecord method`, query);
|
|
6646
5896
|
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
const promise = _queryRecord(adapter, this, normalizedModelName, query, adapterOptionsWrapper);
|
|
5897
|
+
const promise = this.request({
|
|
5898
|
+
op: 'queryRecord',
|
|
5899
|
+
data: {
|
|
5900
|
+
type: normalizeModelName$1(modelName),
|
|
5901
|
+
query,
|
|
5902
|
+
options: options || {}
|
|
5903
|
+
}
|
|
5904
|
+
});
|
|
6656
5905
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
6657
|
-
return promiseObject(promise.then(
|
|
5906
|
+
return promiseObject(promise.then(document => document.content));
|
|
6658
5907
|
}
|
|
6659
|
-
return promise.then(
|
|
5908
|
+
return promise.then(document => document.content);
|
|
6660
5909
|
}
|
|
6661
5910
|
|
|
6662
5911
|
/**
|
|
@@ -6811,42 +6060,22 @@ class Store {
|
|
|
6811
6060
|
@return {Promise} promise
|
|
6812
6061
|
*/
|
|
6813
6062
|
findAll(modelName, options = {}) {
|
|
6814
|
-
if (macroCondition(
|
|
6063
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6815
6064
|
assertDestroyingStore(this, 'findAll');
|
|
6816
6065
|
}
|
|
6817
6066
|
assert(`You need to pass a model name to the store's findAll method`, modelName);
|
|
6818
6067
|
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
assert(`You tried to load all records but your adapter does not implement 'findAll'`, typeof adapter.findAll === 'function');
|
|
6825
|
-
if (options.reload) {
|
|
6826
|
-
array.isUpdating = true;
|
|
6827
|
-
fetch = _findAll(adapter, this, normalizedModelName, options);
|
|
6828
|
-
} else {
|
|
6829
|
-
let snapshotArray = new SnapshotRecordArray(this, array, options);
|
|
6830
|
-
if (options.reload !== false) {
|
|
6831
|
-
if (adapter.shouldReloadAll && adapter.shouldReloadAll(this, snapshotArray) || !adapter.shouldReloadAll && snapshotArray.length === 0) {
|
|
6832
|
-
array.isUpdating = true;
|
|
6833
|
-
fetch = _findAll(adapter, this, modelName, options, snapshotArray);
|
|
6834
|
-
}
|
|
6835
|
-
}
|
|
6836
|
-
if (!fetch) {
|
|
6837
|
-
if (options.backgroundReload === false) {
|
|
6838
|
-
fetch = resolve(array);
|
|
6839
|
-
} else if (options.backgroundReload || !adapter.shouldBackgroundReloadAll || adapter.shouldBackgroundReloadAll(this, snapshotArray)) {
|
|
6840
|
-
array.isUpdating = true;
|
|
6841
|
-
_findAll(adapter, this, modelName, options, snapshotArray);
|
|
6842
|
-
}
|
|
6843
|
-
fetch = resolve(array);
|
|
6068
|
+
const promise = this.request({
|
|
6069
|
+
op: 'findAll',
|
|
6070
|
+
data: {
|
|
6071
|
+
type: normalizeModelName$1(modelName),
|
|
6072
|
+
options: options || {}
|
|
6844
6073
|
}
|
|
6845
|
-
}
|
|
6074
|
+
});
|
|
6846
6075
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
6847
|
-
return promiseArray(
|
|
6076
|
+
return promiseArray(promise.then(document => document.content));
|
|
6848
6077
|
}
|
|
6849
|
-
return
|
|
6078
|
+
return promise.then(document => document.content);
|
|
6850
6079
|
}
|
|
6851
6080
|
|
|
6852
6081
|
/**
|
|
@@ -6870,7 +6099,7 @@ class Store {
|
|
|
6870
6099
|
@return {RecordArray}
|
|
6871
6100
|
*/
|
|
6872
6101
|
peekAll(modelName) {
|
|
6873
|
-
if (macroCondition(
|
|
6102
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6874
6103
|
assertDestroyingStore(this, 'peekAll');
|
|
6875
6104
|
}
|
|
6876
6105
|
assert(`You need to pass a model name to the store's peekAll method`, modelName);
|
|
@@ -6892,7 +6121,7 @@ class Store {
|
|
|
6892
6121
|
@param {String} modelName
|
|
6893
6122
|
*/
|
|
6894
6123
|
unloadAll(modelName) {
|
|
6895
|
-
if (macroCondition(
|
|
6124
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6896
6125
|
assertDestroyedStoreOnly(this, 'unloadAll');
|
|
6897
6126
|
}
|
|
6898
6127
|
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, !modelName || typeof modelName === 'string');
|
|
@@ -6901,7 +6130,7 @@ class Store {
|
|
|
6901
6130
|
// destroy the graph before unloadAll
|
|
6902
6131
|
// since then we avoid churning relationships
|
|
6903
6132
|
// during unload
|
|
6904
|
-
if (macroCondition(
|
|
6133
|
+
if (macroCondition(getOwnConfig().packages.HAS_GRAPH_PACKAGE)) {
|
|
6905
6134
|
const peekGraph = importSync('@ember-data/graph/-private').peekGraph;
|
|
6906
6135
|
let graph = peekGraph(this);
|
|
6907
6136
|
if (graph) {
|
|
@@ -7050,10 +6279,10 @@ class Store {
|
|
|
7050
6279
|
*/
|
|
7051
6280
|
|
|
7052
6281
|
push(data) {
|
|
7053
|
-
if (macroCondition(
|
|
6282
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7054
6283
|
assertDestroyingStore(this, 'push');
|
|
7055
6284
|
}
|
|
7056
|
-
let pushed = this._push(data);
|
|
6285
|
+
let pushed = this._push(data, false);
|
|
7057
6286
|
if (Array.isArray(pushed)) {
|
|
7058
6287
|
let records = pushed.map(identifier => this._instanceCache.getRecord(identifier));
|
|
7059
6288
|
return records;
|
|
@@ -7072,8 +6301,8 @@ class Store {
|
|
|
7072
6301
|
@param {Object} jsonApiDoc
|
|
7073
6302
|
@return {StableRecordIdentifier|Array<StableRecordIdentifier>} identifiers for the primary records that had data loaded
|
|
7074
6303
|
*/
|
|
7075
|
-
_push(jsonApiDoc) {
|
|
7076
|
-
if (macroCondition(
|
|
6304
|
+
_push(jsonApiDoc, asyncFlush) {
|
|
6305
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7077
6306
|
assertDestroyingStore(this, '_push');
|
|
7078
6307
|
}
|
|
7079
6308
|
if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
|
|
@@ -7086,33 +6315,23 @@ class Store {
|
|
|
7086
6315
|
console.log('EmberData | Payload - push', jsonApiDoc);
|
|
7087
6316
|
}
|
|
7088
6317
|
}
|
|
6318
|
+
if (asyncFlush) {
|
|
6319
|
+
this._enableAsyncFlush = true;
|
|
6320
|
+
}
|
|
7089
6321
|
let ret;
|
|
7090
6322
|
this._join(() => {
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
length = jsonApiDoc.data.length;
|
|
7100
|
-
let identifiers = new Array(length);
|
|
7101
|
-
for (i = 0; i < length; i++) {
|
|
7102
|
-
identifiers[i] = this._instanceCache.loadData(jsonApiDoc.data[i]);
|
|
7103
|
-
}
|
|
7104
|
-
ret = identifiers;
|
|
7105
|
-
return;
|
|
7106
|
-
}
|
|
7107
|
-
if (jsonApiDoc.data === null) {
|
|
7108
|
-
ret = null;
|
|
7109
|
-
return;
|
|
6323
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA)) {
|
|
6324
|
+
ret = legacyCachePut(this, {
|
|
6325
|
+
content: jsonApiDoc
|
|
6326
|
+
});
|
|
6327
|
+
} else {
|
|
6328
|
+
ret = this.cache.put({
|
|
6329
|
+
content: jsonApiDoc
|
|
6330
|
+
});
|
|
7110
6331
|
}
|
|
7111
|
-
assert(`Expected an object in the 'data' property in a call to 'push' for ${jsonApiDoc.type}, but was ${typeof jsonApiDoc.data}`, typeof jsonApiDoc.data === 'object');
|
|
7112
|
-
ret = this._instanceCache.loadData(jsonApiDoc.data);
|
|
7113
|
-
return;
|
|
7114
6332
|
});
|
|
7115
|
-
|
|
6333
|
+
this._enableAsyncFlush = null;
|
|
6334
|
+
return ret.data;
|
|
7116
6335
|
}
|
|
7117
6336
|
|
|
7118
6337
|
/**
|
|
@@ -7160,7 +6379,7 @@ class Store {
|
|
|
7160
6379
|
*/
|
|
7161
6380
|
// TODO @runspired @deprecate pushPayload in favor of looking up the serializer
|
|
7162
6381
|
pushPayload(modelName, inputPayload) {
|
|
7163
|
-
if (macroCondition(
|
|
6382
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7164
6383
|
assertDestroyingStore(this, 'pushPayload');
|
|
7165
6384
|
}
|
|
7166
6385
|
let serializer;
|
|
@@ -7182,7 +6401,14 @@ class Store {
|
|
|
7182
6401
|
// TODO @runspired @deprecate records should implement their own serialization if desired
|
|
7183
6402
|
serializeRecord(record, options) {
|
|
7184
6403
|
// TODO we used to check if the record was destroyed here
|
|
7185
|
-
|
|
6404
|
+
if (macroCondition(getOwnConfig().packages.HAS_COMPAT_PACKAGE)) {
|
|
6405
|
+
if (!this._fetchManager) {
|
|
6406
|
+
const FetchManager = importSync('@ember-data/legacy-compat/-private').FetchManager;
|
|
6407
|
+
this._fetchManager = new FetchManager(this);
|
|
6408
|
+
}
|
|
6409
|
+
return this._fetchManager.createSnapshot(recordIdentifierFor(record)).serialize(options);
|
|
6410
|
+
}
|
|
6411
|
+
assert(`Store.serializeRecord is only available when utilizing @ember-data/legacy-compat for legacy compatibility`);
|
|
7186
6412
|
}
|
|
7187
6413
|
|
|
7188
6414
|
/**
|
|
@@ -7195,23 +6421,26 @@ class Store {
|
|
|
7195
6421
|
* @returns {Promise<RecordInstance>}
|
|
7196
6422
|
*/
|
|
7197
6423
|
saveRecord(record, options = {}) {
|
|
6424
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6425
|
+
assertDestroyingStore(this, 'saveRecord');
|
|
6426
|
+
}
|
|
7198
6427
|
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
|
|
7199
6428
|
let identifier = recordIdentifierFor(record);
|
|
7200
|
-
|
|
6429
|
+
const cache = identifier && (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._instanceCache.peek({
|
|
7201
6430
|
identifier,
|
|
7202
|
-
bucket: '
|
|
7203
|
-
});
|
|
7204
|
-
if (!
|
|
6431
|
+
bucket: 'resourceCache'
|
|
6432
|
+
}) : this.cache);
|
|
6433
|
+
if (!cache) {
|
|
7205
6434
|
// this commonly means we're disconnected
|
|
7206
6435
|
// but just in case we reject here to prevent bad things.
|
|
7207
|
-
return reject(`Record Is Disconnected`);
|
|
6436
|
+
return Promise.reject(`Record Is Disconnected`);
|
|
7208
6437
|
}
|
|
7209
6438
|
// TODO we used to check if the record was destroyed here
|
|
7210
|
-
assert(`Cannot initiate a save request for an unloaded record: ${identifier}`,
|
|
7211
|
-
if (
|
|
7212
|
-
return resolve(record);
|
|
6439
|
+
assert(`Cannot initiate a save request for an unloaded record: ${identifier}`, cache && this._instanceCache.recordIsLoaded(identifier));
|
|
6440
|
+
if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
|
|
6441
|
+
return Promise.resolve(record);
|
|
7213
6442
|
}
|
|
7214
|
-
|
|
6443
|
+
cache.willCommit(identifier);
|
|
7215
6444
|
if (isDSModel(record)) {
|
|
7216
6445
|
record.errors.clear();
|
|
7217
6446
|
}
|
|
@@ -7219,121 +6448,76 @@ class Store {
|
|
|
7219
6448
|
options = {};
|
|
7220
6449
|
}
|
|
7221
6450
|
let operation = 'updateRecord';
|
|
7222
|
-
if (
|
|
6451
|
+
if (cache.isNew(identifier)) {
|
|
7223
6452
|
operation = 'createRecord';
|
|
7224
|
-
} else if (
|
|
6453
|
+
} else if (cache.isDeleted(identifier)) {
|
|
7225
6454
|
operation = 'deleteRecord';
|
|
7226
6455
|
}
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
|
|
7233
|
-
try {
|
|
7234
|
-
let data = payload ? JSON.parse(JSON.stringify(payload)) : payload;
|
|
7235
|
-
// eslint-disable-next-line no-console
|
|
7236
|
-
console.log(`EmberData | Payload - ${operation}`, data);
|
|
7237
|
-
} catch (e) {
|
|
7238
|
-
// eslint-disable-next-line no-console
|
|
7239
|
-
console.log(`EmberData | Payload - ${operation}`, payload);
|
|
7240
|
-
}
|
|
6456
|
+
return this.request({
|
|
6457
|
+
op: operation,
|
|
6458
|
+
data: {
|
|
6459
|
+
options,
|
|
6460
|
+
record: identifier
|
|
7241
6461
|
}
|
|
7242
|
-
|
|
7243
|
-
// TODO @runspired re-evaluate the below claim now that
|
|
7244
|
-
// the save request pipeline is more streamlined.
|
|
7245
|
-
Note to future spelunkers hoping to optimize.
|
|
7246
|
-
We rely on this `run` to create a run loop if needed
|
|
7247
|
-
that `store._push` and `store.saveRecord` will both share.
|
|
7248
|
-
We use `join` because it is often the case that we
|
|
7249
|
-
have an outer run loop available still from the first
|
|
7250
|
-
call to `store._push`;
|
|
7251
|
-
*/
|
|
7252
|
-
this._join(() => {
|
|
7253
|
-
if (macroCondition(isDevelopingApp())) {
|
|
7254
|
-
assertDestroyingStore(this, 'saveRecord');
|
|
7255
|
-
}
|
|
7256
|
-
let data = payload && payload.data;
|
|
7257
|
-
if (!data) {
|
|
7258
|
-
assert(`Your ${identifier.type} record was saved to the server, but the response does not have an id and no id has been set client side. Records must have ids. Please update the server response to provide an id in the response or generate the id on the client side either before saving the record or while normalizing the response.`, identifier.id);
|
|
7259
|
-
}
|
|
7260
|
-
const cache = this.identifierCache;
|
|
7261
|
-
let actualIdentifier = identifier;
|
|
7262
|
-
if (operation !== 'deleteRecord' && data) {
|
|
7263
|
-
actualIdentifier = cache.updateRecordIdentifier(identifier, data);
|
|
7264
|
-
}
|
|
7265
|
-
|
|
7266
|
-
//We first make sure the primary data has been updated
|
|
7267
|
-
const recordData = this._instanceCache.getRecordData(actualIdentifier);
|
|
7268
|
-
recordData.didCommit(identifier, data);
|
|
7269
|
-
if (operation === 'deleteRecord') {
|
|
7270
|
-
this.recordArrayManager.identifierRemoved(actualIdentifier);
|
|
7271
|
-
}
|
|
7272
|
-
if (payload && payload.included) {
|
|
7273
|
-
this._push({
|
|
7274
|
-
data: null,
|
|
7275
|
-
included: payload.included
|
|
7276
|
-
});
|
|
7277
|
-
}
|
|
7278
|
-
});
|
|
7279
|
-
return record;
|
|
7280
|
-
}).catch(e => {
|
|
7281
|
-
let err = e;
|
|
7282
|
-
if (!e) {
|
|
7283
|
-
err = new Error(`Unknown Error Occurred During Request`);
|
|
7284
|
-
} else if (typeof e === 'string') {
|
|
7285
|
-
err = new Error(e);
|
|
7286
|
-
}
|
|
7287
|
-
adapterDidInvalidate(this, identifier, err);
|
|
7288
|
-
throw err;
|
|
7289
|
-
});
|
|
6462
|
+
}).then(document => document.content);
|
|
7290
6463
|
}
|
|
7291
6464
|
|
|
7292
6465
|
/**
|
|
7293
6466
|
* Instantiation hook allowing applications or addons to configure the store
|
|
7294
|
-
* to utilize a custom
|
|
6467
|
+
* to utilize a custom Cache implementation.
|
|
7295
6468
|
*
|
|
7296
|
-
*
|
|
6469
|
+
* This hook should not be called directly by consuming applications or libraries.
|
|
6470
|
+
* Use `Store.cache` to access the Cache instance.
|
|
6471
|
+
*
|
|
6472
|
+
* @method createCache (hook)
|
|
7297
6473
|
* @public
|
|
7298
|
-
* @param identifier
|
|
7299
6474
|
* @param storeWrapper
|
|
6475
|
+
* @returns {Cache}
|
|
7300
6476
|
*/
|
|
7301
|
-
|
|
7302
|
-
if (macroCondition(
|
|
7303
|
-
// we can't greedily use require as this causes
|
|
7304
|
-
// a cycle we can't easily fix (or clearly pin point) at present.
|
|
7305
|
-
//
|
|
7306
|
-
// it can be reproduced in partner tests by running
|
|
7307
|
-
// node ./scripts/packages-for-commit.js && pnpm test-external:ember-observer
|
|
6477
|
+
createCache(storeWrapper) {
|
|
6478
|
+
if (macroCondition(getOwnConfig().packages.HAS_JSON_API_PACKAGE)) {
|
|
7308
6479
|
if (_Cache === undefined) {
|
|
7309
|
-
_Cache = importSync('@ember-data/json-api
|
|
6480
|
+
_Cache = importSync('@ember-data/json-api').default;
|
|
7310
6481
|
}
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
6482
|
+
return new _Cache(storeWrapper);
|
|
6483
|
+
}
|
|
6484
|
+
assert(`Expected store.createCache to be implemented but it wasn't`);
|
|
6485
|
+
}
|
|
6486
|
+
|
|
6487
|
+
/**
|
|
6488
|
+
* Returns the cache instance associated to this Store, instantiates the Cache
|
|
6489
|
+
* if necessary via `Store.createCache`
|
|
6490
|
+
*
|
|
6491
|
+
* @property {Cache} cache
|
|
6492
|
+
* @public
|
|
6493
|
+
*/
|
|
6494
|
+
get cache() {
|
|
6495
|
+
let {
|
|
6496
|
+
cache
|
|
6497
|
+
} = this._instanceCache;
|
|
6498
|
+
if (!cache) {
|
|
6499
|
+
cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
|
|
6500
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
6501
|
+
cache = new SingletonCacheManager(cache);
|
|
7329
6502
|
}
|
|
7330
|
-
this.__private_singleton_recordData = this.__private_singleton_recordData || new _Cache(storeWrapper);
|
|
7331
|
-
this.__private_singleton_recordData.createCache(identifier);
|
|
7332
|
-
return this.__private_singleton_recordData;
|
|
7333
6503
|
}
|
|
7334
|
-
|
|
6504
|
+
return cache;
|
|
7335
6505
|
}
|
|
7336
6506
|
|
|
6507
|
+
/**
|
|
6508
|
+
* [DEPRECATED] use Store.createCache
|
|
6509
|
+
*
|
|
6510
|
+
* Instantiation hook allowing applications or addons to configure the store
|
|
6511
|
+
* to utilize a custom RecordData implementation.
|
|
6512
|
+
*
|
|
6513
|
+
* @method createRecordDataFor (hook)
|
|
6514
|
+
* @deprecated
|
|
6515
|
+
* @public
|
|
6516
|
+
* @param identifier
|
|
6517
|
+
* @param storeWrapper
|
|
6518
|
+
* @returns {Cache}
|
|
6519
|
+
*/
|
|
6520
|
+
|
|
7337
6521
|
/**
|
|
7338
6522
|
`normalize` converts a json payload into the normalized form that
|
|
7339
6523
|
[push](../methods/push?anchor=push) expects.
|
|
@@ -7353,7 +6537,7 @@ class Store {
|
|
|
7353
6537
|
*/
|
|
7354
6538
|
// TODO @runspired @deprecate users should call normalize on the associated serializer directly
|
|
7355
6539
|
normalize(modelName, payload) {
|
|
7356
|
-
if (macroCondition(
|
|
6540
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7357
6541
|
assertDestroyingStore(this, 'normalize');
|
|
7358
6542
|
}
|
|
7359
6543
|
assert(`You need to pass a model name to the store's normalize method`, modelName);
|
|
@@ -7378,7 +6562,7 @@ class Store {
|
|
|
7378
6562
|
@return Adapter
|
|
7379
6563
|
*/
|
|
7380
6564
|
adapterFor(modelName) {
|
|
7381
|
-
if (macroCondition(
|
|
6565
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7382
6566
|
assertDestroyingStore(this, 'adapterFor');
|
|
7383
6567
|
}
|
|
7384
6568
|
assert(`You need to pass a model name to the store's adapterFor method`, modelName);
|
|
@@ -7444,7 +6628,7 @@ class Store {
|
|
|
7444
6628
|
@return {Serializer}
|
|
7445
6629
|
*/
|
|
7446
6630
|
serializerFor(modelName) {
|
|
7447
|
-
if (macroCondition(
|
|
6631
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7448
6632
|
assertDestroyingStore(this, 'serializerFor');
|
|
7449
6633
|
}
|
|
7450
6634
|
assert(`You need to pass a model name to the store's serializerFor method`, modelName);
|
|
@@ -7494,7 +6678,7 @@ class Store {
|
|
|
7494
6678
|
serializer.destroy();
|
|
7495
6679
|
}
|
|
7496
6680
|
}
|
|
7497
|
-
if (macroCondition(
|
|
6681
|
+
if (macroCondition(getOwnConfig().packages.HAS_GRAPH_PACKAGE)) {
|
|
7498
6682
|
const peekGraph = importSync('@ember-data/graph/-private').peekGraph;
|
|
7499
6683
|
let graph = peekGraph(this);
|
|
7500
6684
|
if (graph) {
|
|
@@ -7504,14 +6688,6 @@ class Store {
|
|
|
7504
6688
|
this.recordArrayManager.destroy();
|
|
7505
6689
|
this.identifierCache.destroy();
|
|
7506
6690
|
this.unloadAll();
|
|
7507
|
-
if (macroCondition(isDevelopingApp())) {
|
|
7508
|
-
unregisterWaiter(this.__asyncWaiter);
|
|
7509
|
-
let tracked = this._trackedAsyncRequests;
|
|
7510
|
-
let isSettled = tracked.length === 0;
|
|
7511
|
-
if (!isSettled) {
|
|
7512
|
-
throw new Error('Async Request leaks detected. Add a breakpoint here and set `store.generateStackTracesForTrackedRequests = true;`to inspect traces for leak origins:\n\t - ' + tracked.map(o => o.label).join('\n\t - '));
|
|
7513
|
-
}
|
|
7514
|
-
}
|
|
7515
6691
|
this.isDestroyed = true;
|
|
7516
6692
|
}
|
|
7517
6693
|
static create(args) {
|
|
@@ -7520,7 +6696,7 @@ class Store {
|
|
|
7520
6696
|
}
|
|
7521
6697
|
let assertDestroyingStore;
|
|
7522
6698
|
let assertDestroyedStoreOnly;
|
|
7523
|
-
if (macroCondition(
|
|
6699
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7524
6700
|
assertDestroyingStore = function assertDestroyedStore(store, method) {
|
|
7525
6701
|
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
|
|
7526
6702
|
};
|
|
@@ -7532,68 +6708,11 @@ function isMaybeIdentifier(maybeIdentifier) {
|
|
|
7532
6708
|
return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
|
|
7533
6709
|
}
|
|
7534
6710
|
function isDSModel(record) {
|
|
7535
|
-
if (macroCondition(!
|
|
6711
|
+
if (macroCondition(!getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
7536
6712
|
return false;
|
|
7537
6713
|
}
|
|
7538
6714
|
return !!record && 'constructor' in record && 'isModel' in record.constructor && record.constructor.isModel === true;
|
|
7539
6715
|
}
|
|
7540
|
-
function adapterDidInvalidate(store, identifier, error) {
|
|
7541
|
-
if (error && error.isAdapterError === true && error.code === 'InvalidError') {
|
|
7542
|
-
let serializer = store.serializerFor(identifier.type);
|
|
7543
|
-
|
|
7544
|
-
// TODO @deprecate extractErrors being called
|
|
7545
|
-
// TODO remove extractErrors from the default serializers.
|
|
7546
|
-
if (serializer && typeof serializer.extractErrors === 'function') {
|
|
7547
|
-
let errorsHash = serializer.extractErrors(store, store.modelFor(identifier.type), error, identifier.id);
|
|
7548
|
-
error.errors = errorsHashToArray(errorsHash);
|
|
7549
|
-
}
|
|
7550
|
-
}
|
|
7551
|
-
const recordData = store._instanceCache.getRecordData(identifier);
|
|
7552
|
-
if (error.errors) {
|
|
7553
|
-
assert(`Expected the RecordData implementation for ${identifier} to have a getErrors(identifier) method for retreiving errors.`, typeof recordData.getErrors === 'function');
|
|
7554
|
-
let jsonApiErrors = error.errors;
|
|
7555
|
-
if (jsonApiErrors.length === 0) {
|
|
7556
|
-
jsonApiErrors = [{
|
|
7557
|
-
title: 'Invalid Error',
|
|
7558
|
-
detail: '',
|
|
7559
|
-
source: {
|
|
7560
|
-
pointer: '/data'
|
|
7561
|
-
}
|
|
7562
|
-
}];
|
|
7563
|
-
}
|
|
7564
|
-
recordData.commitWasRejected(identifier, jsonApiErrors);
|
|
7565
|
-
} else {
|
|
7566
|
-
recordData.commitWasRejected(identifier);
|
|
7567
|
-
}
|
|
7568
|
-
}
|
|
7569
|
-
function makeArray(value) {
|
|
7570
|
-
return Array.isArray(value) ? value : [value];
|
|
7571
|
-
}
|
|
7572
|
-
const PRIMARY_ATTRIBUTE_KEY = 'base';
|
|
7573
|
-
function errorsHashToArray(errors) {
|
|
7574
|
-
const out = [];
|
|
7575
|
-
if (errors) {
|
|
7576
|
-
Object.keys(errors).forEach(key => {
|
|
7577
|
-
let messages = makeArray(errors[key]);
|
|
7578
|
-
for (let i = 0; i < messages.length; i++) {
|
|
7579
|
-
let title = 'Invalid Attribute';
|
|
7580
|
-
let pointer = `/data/attributes/${key}`;
|
|
7581
|
-
if (key === PRIMARY_ATTRIBUTE_KEY) {
|
|
7582
|
-
title = 'Invalid Document';
|
|
7583
|
-
pointer = `/data`;
|
|
7584
|
-
}
|
|
7585
|
-
out.push({
|
|
7586
|
-
title: title,
|
|
7587
|
-
detail: messages[i],
|
|
7588
|
-
source: {
|
|
7589
|
-
pointer: pointer
|
|
7590
|
-
}
|
|
7591
|
-
});
|
|
7592
|
-
}
|
|
7593
|
-
});
|
|
7594
|
-
}
|
|
7595
|
-
return out;
|
|
7596
|
-
}
|
|
7597
6716
|
function normalizeProperties(store, identifier, properties, isForV1 = false) {
|
|
7598
6717
|
// assert here
|
|
7599
6718
|
if (properties !== undefined) {
|
|
@@ -7617,7 +6736,7 @@ function normalizeProperties(store, identifier, properties, isForV1 = false) {
|
|
|
7617
6736
|
let def = defs[prop];
|
|
7618
6737
|
if (def !== undefined) {
|
|
7619
6738
|
if (def.kind === 'hasMany') {
|
|
7620
|
-
if (macroCondition(
|
|
6739
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
7621
6740
|
assertRecordsPassedToHasMany(properties[prop]);
|
|
7622
6741
|
}
|
|
7623
6742
|
relationshipValue = extractIdentifiersFromRecords(properties[prop], isForV1);
|
|
@@ -7651,7 +6770,7 @@ function extractIdentifierFromRecord(recordOrPromiseRecord, isForV1 = false) {
|
|
|
7651
6770
|
if (!recordOrPromiseRecord) {
|
|
7652
6771
|
return null;
|
|
7653
6772
|
}
|
|
7654
|
-
const extract = isForV1 ?
|
|
6773
|
+
const extract = isForV1 ? peekCache : recordIdentifierFor;
|
|
7655
6774
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
7656
6775
|
if (isPromiseRecord(recordOrPromiseRecord)) {
|
|
7657
6776
|
let content = recordOrPromiseRecord.content;
|
|
@@ -7673,11 +6792,108 @@ function extractIdentifierFromRecord(recordOrPromiseRecord, isForV1 = false) {
|
|
|
7673
6792
|
function isPromiseRecord(record) {
|
|
7674
6793
|
return !!record.then;
|
|
7675
6794
|
}
|
|
7676
|
-
function secretInit(record,
|
|
6795
|
+
function secretInit(record, cache, identifier, store) {
|
|
7677
6796
|
setRecordIdentifier(record, identifier);
|
|
7678
6797
|
StoreMap.set(record, store);
|
|
7679
|
-
|
|
6798
|
+
setCacheFor(record, cache);
|
|
6799
|
+
}
|
|
6800
|
+
function getHydratedContent(store, request, document) {
|
|
6801
|
+
if (Array.isArray(document.data)) {
|
|
6802
|
+
const {
|
|
6803
|
+
lid
|
|
6804
|
+
} = document;
|
|
6805
|
+
const {
|
|
6806
|
+
recordArrayManager
|
|
6807
|
+
} = store;
|
|
6808
|
+
if (!lid) {
|
|
6809
|
+
return recordArrayManager.createArray({
|
|
6810
|
+
identifiers: document.data,
|
|
6811
|
+
doc: document,
|
|
6812
|
+
query: request
|
|
6813
|
+
});
|
|
6814
|
+
}
|
|
6815
|
+
let managed = recordArrayManager._keyedArrays.get(lid);
|
|
6816
|
+
if (!managed) {
|
|
6817
|
+
managed = recordArrayManager.createArray({
|
|
6818
|
+
identifiers: document.data,
|
|
6819
|
+
doc: document
|
|
6820
|
+
});
|
|
6821
|
+
recordArrayManager._keyedArrays.set(lid, managed);
|
|
6822
|
+
} else {
|
|
6823
|
+
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
6824
|
+
}
|
|
6825
|
+
return managed;
|
|
6826
|
+
} else {
|
|
6827
|
+
return document.data ? store.peekRecord(document.data) : null;
|
|
6828
|
+
}
|
|
6829
|
+
}
|
|
6830
|
+
function calcShouldFetch(store, request, hasCachedValue, lid) {
|
|
6831
|
+
const {
|
|
6832
|
+
cacheOptions,
|
|
6833
|
+
url,
|
|
6834
|
+
method
|
|
6835
|
+
} = request;
|
|
6836
|
+
return cacheOptions?.reload || !hasCachedValue || store.lifetimes && lid && url && method ? store.lifetimes.isHardExpired(lid, url, method) : false;
|
|
6837
|
+
}
|
|
6838
|
+
function calcShouldBackgroundFetch(store, request, willFetch, lid) {
|
|
6839
|
+
const {
|
|
6840
|
+
cacheOptions,
|
|
6841
|
+
url,
|
|
6842
|
+
method
|
|
6843
|
+
} = request;
|
|
6844
|
+
return !willFetch && (cacheOptions?.backgroundReload || store.lifetimes && lid && url && method ? store.lifetimes.isSoftExpired(lid, url, method) : false);
|
|
6845
|
+
}
|
|
6846
|
+
function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetch) {
|
|
6847
|
+
const {
|
|
6848
|
+
store
|
|
6849
|
+
} = context.request;
|
|
6850
|
+
return next(context.request).then(document => {
|
|
6851
|
+
const response = store.cache.put(document);
|
|
6852
|
+
if (shouldFetch) {
|
|
6853
|
+
return getHydratedContent(store, context.request, response);
|
|
6854
|
+
}
|
|
6855
|
+
}, error => {
|
|
6856
|
+
store.cache.put(error);
|
|
6857
|
+
// TODO @runspired this is probably not the right thing to throw so make sure we add a test
|
|
6858
|
+
if (!shouldBackgroundFetch) {
|
|
6859
|
+
throw error;
|
|
6860
|
+
}
|
|
6861
|
+
});
|
|
7680
6862
|
}
|
|
6863
|
+
const CacheHandler = {
|
|
6864
|
+
request(context, next) {
|
|
6865
|
+
// if we are a legacy request or did not originate from the store, skip cache handling
|
|
6866
|
+
if (!context.request.store || context.request.op && !context.request.url) {
|
|
6867
|
+
return next(context.request);
|
|
6868
|
+
}
|
|
6869
|
+
const {
|
|
6870
|
+
store
|
|
6871
|
+
} = context.request;
|
|
6872
|
+
const {
|
|
6873
|
+
cacheOptions,
|
|
6874
|
+
url,
|
|
6875
|
+
method
|
|
6876
|
+
} = context.request;
|
|
6877
|
+
const lid = cacheOptions?.key || method === 'GET' && url ? url : null;
|
|
6878
|
+
const peeked = lid ? store.cache.peekRequest({
|
|
6879
|
+
lid
|
|
6880
|
+
}) : null;
|
|
6881
|
+
|
|
6882
|
+
// determine if we should skip cache
|
|
6883
|
+
if (calcShouldFetch(store, context.request, !!peeked, lid)) {
|
|
6884
|
+
return fetchContentAndHydrate(next, context, true, false);
|
|
6885
|
+
}
|
|
6886
|
+
|
|
6887
|
+
// if we have not skipped cache, determine if we should update behind the scenes
|
|
6888
|
+
if (calcShouldBackgroundFetch(store, context.request, false, lid)) {
|
|
6889
|
+
void fetchContentAndHydrate(next, context, false, true);
|
|
6890
|
+
}
|
|
6891
|
+
if ('error' in peeked) {
|
|
6892
|
+
throw peeked.error;
|
|
6893
|
+
}
|
|
6894
|
+
return Promise.resolve(getHydratedContent(store, context.request, peeked.content));
|
|
6895
|
+
}
|
|
6896
|
+
};
|
|
7681
6897
|
function normalizeModelName(modelName) {
|
|
7682
6898
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_HELPERS)) {
|
|
7683
6899
|
deprecate(`the helper function normalizeModelName is deprecated. You should use model names that are already normalized, or use string helpers of your own. This function is primarily an alias for dasherize from @ember/string.`, false, {
|
|
@@ -7693,4 +6909,4 @@ function normalizeModelName(modelName) {
|
|
|
7693
6909
|
}
|
|
7694
6910
|
assert(`normalizeModelName support has been removed`);
|
|
7695
6911
|
}
|
|
7696
|
-
export {
|
|
6912
|
+
export { CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, coerceId as e, Collection as f, notifyArray as g, SOURCE as h, isStableIdentifier as i, IDENTIFIER_ARRAY_TAG as j, fastPush as k, removeRecordDataFor as l, normalizeModelName as n, peekCache as p, recordIdentifierFor as r, storeFor as s };
|