@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.
@@ -1,10 +1,8 @@
1
- import { macroCondition, getOwnConfig, isDevelopingApp, dependencySatisfies, importSync } from '@embroider/macros';
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$1(obj) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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: createded identifier ${String(identifier)} for newly generated resource`, data);
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(isDevelopingApp())) {
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$1(wrapper);
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(isDevelopingApp())) {
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(isDevelopingApp()) ? {
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
- // deactivated type signature overloads because pass-through was failing to match any. Bring back if possible.
794
- // notify(identifier: StableRecordIdentifier, value: 'attributes' | 'relationships', key?: string): boolean;
795
- // notify(identifier: StableRecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'state'): boolean;
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(`Notifying: ${String(identifier)}\t${value}\t${key}`);
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
- var _store = /*#__PURE__*/_classPrivateFieldKey("store");
1029
- var _recordData = /*#__PURE__*/_classPrivateFieldKey("recordData");
1030
- var _identifier = /*#__PURE__*/_classPrivateFieldKey("identifier");
1031
- var _isDeprecated = /*#__PURE__*/_classPrivateFieldKey("isDeprecated");
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
- * enforcing that only the public API surface area
1035
- * is exposed.
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, _recordData)[_recordData].version || '1';
1209
+ return _classPrivateFieldBase(this, _cache)[_cache].version || '1';
1075
1210
  }
1076
- constructor(store, _recordData2, identifier) {
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, _recordData, {
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, _recordData)[_recordData] = _recordData2;
1229
+ _classPrivateFieldBase(this, _cache)[_cache] = _cache2;
1095
1230
  _classPrivateFieldBase(this, _identifier)[_identifier] = identifier;
1096
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](_recordData2)) {
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
- * Retrieve the identifier for this v1 recordData
1247
+ * Cache the response to a request
1113
1248
  *
1114
- * DEPRECATED Caches should not be assumed to be 1:1 with resources
1249
+ * Unlike `store.push` which has UPSERT
1250
+ * semantics, `put` has `replace` semantics similar to
1251
+ * the `http` method `PUT`
1115
1252
  *
1116
- * @method getResourceIdentifier
1117
- * @public
1118
- * @deprecated
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
- * @method pushData
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
- pushData(identifier, data, hasRecord) {
1135
- const recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1136
- // called by something V1
1137
- if (!isStableIdentifier(identifier)) {
1138
- data = identifier;
1139
- hasRecord = data;
1140
- identifier = _classPrivateFieldBase(this, _identifier)[_identifier];
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 recordData.pushData(identifier, data, hasRecord);
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 sync
1287
+ * @method patch
1155
1288
  * @public
1156
1289
  * @param op the operation to perform
1157
1290
  * @returns {void}
1158
1291
  */
1159
- sync(op) {
1160
- const recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1161
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1292
+ patch(op) {
1293
+ const cache = _classPrivateFieldBase(this, _cache)[_cache];
1294
+ if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
1162
1295
  return;
1163
1296
  }
1164
- recordData.sync(op);
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 update
1304
+ * @method mutate
1172
1305
  * @public
1173
- * @param operation
1306
+ * @param mutation
1174
1307
  */
1175
- // isCollection is only needed for interop with v1 cache
1176
- update(operation, isResource) {
1177
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](_classPrivateFieldBase(this, _recordData)[_recordData])) {
1178
- const cache = _classPrivateFieldBase(this, _store)[_store]._instanceCache;
1179
- switch (operation.op) {
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
- _classPrivateFieldBase(this, _recordData)[_recordData].addToHasMany(operation.field, operation.value.map(i => cache.getRecordData(i)), operation.index);
1315
+ cache.addToHasMany(mutation.field, mutation.value.map(i => instanceCache.getResourceCache(i)), mutation.index);
1182
1316
  return;
1183
1317
  case 'removeFromRelatedRecords':
1184
- _classPrivateFieldBase(this, _recordData)[_recordData].removeFromHasMany(operation.field, operation.value.map(i => cache.getRecordData(i)));
1318
+ cache.removeFromHasMany(mutation.field, mutation.value.map(i => instanceCache.getResourceCache(i)));
1185
1319
  return;
1186
1320
  case 'replaceRelatedRecords':
1187
- _classPrivateFieldBase(this, _recordData)[_recordData].setDirtyHasMany(operation.field, operation.value.map(i => cache.getRecordData(i)));
1321
+ cache.setDirtyHasMany(mutation.field, mutation.value.map(i => instanceCache.getResourceCache(i)));
1188
1322
  return;
1189
1323
  case 'replaceRelatedRecord':
1190
1324
  if (isResource) {
1191
- _classPrivateFieldBase(this, _recordData)[_recordData].setDirtyBelongsTo(operation.field, operation.value ? cache.getRecordData(operation.value) : null);
1325
+ cache.setDirtyBelongsTo(mutation.field, mutation.value ? instanceCache.getResourceCache(mutation.value) : null);
1192
1326
  return;
1193
1327
  }
1194
- _classPrivateFieldBase(this, _recordData)[_recordData].removeFromHasMany(operation.field, [cache.getRecordData(operation.prior)]);
1195
- _classPrivateFieldBase(this, _recordData)[_recordData].addToHasMany(operation.field, [cache.getRecordData(operation.value)], operation.index);
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
- _classPrivateFieldBase(this, _recordData)[_recordData].update(operation);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1603
+ const cache = _classPrivateFieldBase(this, _cache)[_cache];
1225
1604
 
1226
1605
  // TODO deprecate return value
1227
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1228
- recordData.clientDidCreate();
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 recordData._initRecordCreateOptions(options);
1609
+ return cache._initRecordCreateOptions(options);
1231
1610
  } else {
1232
- return recordData.clientDidCreate(identifier, options);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1249
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1250
- return recordData._initRecordCreateOptions(options);
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, _recordData)[_recordData].willCommit(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1282
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.didCommit(data) : recordData.didCommit(identifier, data);
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, _recordData)[_recordData].commitWasRejected(identifier || _classPrivateFieldBase(this, _identifier)[_identifier], errors);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1308
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1309
- recordData.unloadRecord();
1686
+ const cache = _classPrivateFieldBase(this, _cache)[_cache];
1687
+ if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
1688
+ cache.unloadRecord();
1310
1689
  } else {
1311
- recordData.unloadRecord(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
1690
+ cache.unloadRecord(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
1312
1691
  }
1313
1692
  }
1314
1693
 
1315
- // Attrs
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1334
- return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.getAttr(propertyName) : recordData.getAttr(identifier, propertyName);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1348
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.setDirtyAttribute(propertyName, value) : recordData.setAttr(identifier, propertyName, value);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1365
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.setDirtyAttribute(propertyName, value) : recordData.setAttr(_classPrivateFieldBase(this, _identifier)[_identifier], propertyName, value);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1381
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1382
- return recordData.changedAttributes();
1759
+ const cache = _classPrivateFieldBase(this, _cache)[_cache];
1760
+ if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
1761
+ return cache.changedAttributes();
1383
1762
  }
1384
- return recordData.changedAttrs(_classPrivateFieldBase(this, _identifier)[_identifier]);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1398
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1399
- return recordData.changedAttributes();
1776
+ const cache = _classPrivateFieldBase(this, _cache)[_cache];
1777
+ if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
1778
+ return cache.changedAttributes();
1400
1779
  }
1401
- return recordData.changedAttrs(identifier);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1416
- return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.hasChangedAttributes() : recordData.hasChangedAttrs(_classPrivateFieldBase(this, _identifier)[_identifier]);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1429
- return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.hasChangedAttributes() : recordData.hasChangedAttrs(identifier);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1444
- return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.rollbackAttributes() : recordData.rollbackAttrs(_classPrivateFieldBase(this, _identifier)[_identifier]);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1457
- return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.rollbackAttributes() : recordData.rollbackAttrs(identifier);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1480
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1858
+ const cache = _classPrivateFieldBase(this, _cache)[_cache];
1859
+ if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](cache)) {
1481
1860
  let isBelongsTo = !isCollection;
1482
- return isBelongsTo ? recordData.getBelongsTo(propertyName) : recordData.getHasMany(propertyName);
1861
+ return isBelongsTo ? cache.getBelongsTo(propertyName) : cache.getHasMany(propertyName);
1483
1862
  }
1484
- return recordData.getRelationship(identifier, propertyName);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1500
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1501
- return recordData.getBelongsTo(propertyName);
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 recordData.getRelationship(identifier, propertyName);
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1521
- if (_classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData)) {
1522
- return recordData.getHasMany(propertyName);
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 recordData.getRelationship(identifier, propertyName);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1542
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.setDirtyBelongsTo(propertyName, value) : recordData.update({
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1567
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.addToHasMany(propertyName, value, idx) : recordData.update({
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1589
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.removeFromHasMany(propertyName, value) : recordData.update({
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
- let recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1610
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.setDirtyHasMany(propertyName, value) : recordData.update({
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1636
- _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.setIsDeleted(isDeleted) : recordData.setIsDeleted(identifier, isDeleted);
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, _recordData)[_recordData].getErrors(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
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 recordData = _classPrivateFieldBase(this, _recordData)[_recordData];
1661
- return _classPrivateFieldBase(this, _isDeprecated)[_isDeprecated](recordData) ? recordData.isEmpty?.(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]) || false : recordData.isEmpty(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
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, _recordData)[_recordData].isNew(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
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, _recordData)[_recordData].isDeleted(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
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, _recordData)[_recordData].isDeletionCommitted(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
2079
+ return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier || _classPrivateFieldBase(this, _identifier)[_identifier]);
1701
2080
  }
1702
2081
  }
1703
- function _isDeprecated2(recordData) {
1704
- let version = recordData.version || '1';
2082
+ function _isDeprecated2(cache) {
2083
+ let version = cache.version || '1';
1705
2084
  return version !== this.version;
1706
2085
  }
1707
- var _recordDatas = /*#__PURE__*/_classPrivateFieldKey("recordDatas");
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, _recordDatas, {
2090
+ Object.defineProperty(this, _cache3, {
1716
2091
  writable: true,
1717
2092
  value: void 0
1718
2093
  });
1719
- _classPrivateFieldBase(this, _recordDatas)[_recordDatas] = new Map();
2094
+ _classPrivateFieldBase(this, _cache3)[_cache3] = cache;
1720
2095
  }
1721
- _addRecordData(identifier, recordData) {
1722
- _classPrivateFieldBase(this, _recordDatas)[_recordDatas].set(identifier, recordData);
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
- pushData(identifier, data, hasRecord) {
1728
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).pushData(identifier, data, hasRecord);
2124
+ upsert(identifier, data, hasRecord) {
2125
+ return _classPrivateFieldBase(this, _cache3)[_cache3].upsert(identifier, data, hasRecord);
1729
2126
  }
1730
- sync(op) {
1731
- _classPrivateFieldBase(this, _recordData3)[_recordData3](op.record).sync(op);
2127
+ patch(op) {
2128
+ _classPrivateFieldBase(this, _cache3)[_cache3].patch(op);
1732
2129
  }
1733
2130
  clientDidCreate(identifier, options) {
1734
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).clientDidCreate(identifier, options);
2131
+ return _classPrivateFieldBase(this, _cache3)[_cache3].clientDidCreate(identifier, options);
1735
2132
  }
1736
2133
  willCommit(identifier) {
1737
- _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).willCommit(identifier);
2134
+ _classPrivateFieldBase(this, _cache3)[_cache3].willCommit(identifier);
1738
2135
  }
1739
2136
  didCommit(identifier, data) {
1740
- _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).didCommit(identifier, data);
2137
+ _classPrivateFieldBase(this, _cache3)[_cache3].didCommit(identifier, data);
1741
2138
  }
1742
2139
  commitWasRejected(identifier, errors) {
1743
- _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).commitWasRejected(identifier, errors);
2140
+ _classPrivateFieldBase(this, _cache3)[_cache3].commitWasRejected(identifier, errors);
1744
2141
  }
1745
2142
  unloadRecord(identifier) {
1746
- _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).unloadRecord(identifier);
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, _recordData3)[_recordData3](identifier).getAttr(identifier, propertyName);
2150
+ return _classPrivateFieldBase(this, _cache3)[_cache3].getAttr(identifier, propertyName);
1754
2151
  }
1755
2152
  setAttr(identifier, propertyName, value) {
1756
- _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).setAttr(identifier, propertyName, value);
2153
+ _classPrivateFieldBase(this, _cache3)[_cache3].setAttr(identifier, propertyName, value);
1757
2154
  }
1758
2155
  changedAttrs(identifier) {
1759
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).changedAttrs(identifier);
2156
+ return _classPrivateFieldBase(this, _cache3)[_cache3].changedAttrs(identifier);
1760
2157
  }
1761
2158
  hasChangedAttrs(identifier) {
1762
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).hasChangedAttrs(identifier);
2159
+ return _classPrivateFieldBase(this, _cache3)[_cache3].hasChangedAttrs(identifier);
1763
2160
  }
1764
2161
  rollbackAttrs(identifier) {
1765
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).rollbackAttrs(identifier);
2162
+ return _classPrivateFieldBase(this, _cache3)[_cache3].rollbackAttrs(identifier);
1766
2163
  }
1767
2164
  getRelationship(identifier, propertyName) {
1768
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).getRelationship(identifier, propertyName);
2165
+ return _classPrivateFieldBase(this, _cache3)[_cache3].getRelationship(identifier, propertyName);
1769
2166
  }
1770
- update(operation) {
1771
- _classPrivateFieldBase(this, _recordData3)[_recordData3](operation.record).update(operation);
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, _recordData3)[_recordData3](identifier).setIsDeleted(identifier, isDeleted);
2175
+ _classPrivateFieldBase(this, _cache3)[_cache3].setIsDeleted(identifier, isDeleted);
1779
2176
  }
1780
2177
  getErrors(identifier) {
1781
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).getErrors(identifier);
2178
+ return _classPrivateFieldBase(this, _cache3)[_cache3].getErrors(identifier);
1782
2179
  }
1783
2180
  isEmpty(identifier) {
1784
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).isEmpty(identifier);
2181
+ return _classPrivateFieldBase(this, _cache3)[_cache3].isEmpty(identifier);
1785
2182
  }
1786
2183
  isNew(identifier) {
1787
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).isNew(identifier);
2184
+ return _classPrivateFieldBase(this, _cache3)[_cache3].isNew(identifier);
1788
2185
  }
1789
2186
  isDeleted(identifier) {
1790
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).isDeleted(identifier);
2187
+ return _classPrivateFieldBase(this, _cache3)[_cache3].isDeleted(identifier);
1791
2188
  }
1792
2189
  isDeletionCommitted(identifier) {
1793
- return _classPrivateFieldBase(this, _recordData3)[_recordData3](identifier).isDeletionCommitted(identifier);
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 recordData = this._store._instanceCache.getRecordData(identifier);
2033
- if (!id && !lid) {
2034
- recordData.clientDidCreate(identifier);
2035
- this._store.recordArrayManager.identifierAdded(identifier);
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 recordData;
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.getRecordData(identifier);
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
- The unique RecordIdentifier associated with this Snapshot.
2222
- @property identifier
2223
- @public
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(isDevelopingApp())) {
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
- recordData: new Map(),
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
- this.__recordDataFor = resource => {
2733
- // TODO enforce strict
2734
- const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource);
2735
- return this.getRecordData(identifier);
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 keptRecordData = this.__instances.recordData.get(keptIdentifier) || null;
2750
- let staleRecordData = this.__instances.recordData.get(staleIdentifier) || null;
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 recordData the record points at)
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 recordData = keptRecordData || staleRecordData;
2765
- if (recordData) {
2766
- recordData.sync({
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(dependencySatisfies("@ember-data/json-api", "*"))) {
2772
- // TODO notify cache always, this requires it always being a singleton
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 (staleRecordData === null) {
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 recordData = this.getRecordData(identifier);
2809
- record = this.store.instantiateRecord(identifier, properties || {}, this.__recordDataFor, this.store.notifications);
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
- setRecordDataFor(record, recordData);
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
- getRecordData(identifier) {
2822
- let recordData = this.__instances.recordData.get(identifier);
2823
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1CACHE_STORE_APIS)) {
2824
- if (!recordData && this.store.createRecordDataFor.length > 2) {
2825
- deprecate(`Store.createRecordDataFor(<type>, <id>, <lid>, <storeWrapper>) has been deprecated in favor of Store.createRecordDataFor(<identifier>, <storeWrapper>)`, false, {
2826
- id: 'ember-data:deprecate-v1cache-store-apis',
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
- if (!recordData) {
2845
- let recordDataInstance = this.store.createRecordDataFor(identifier, this._storeWrapper);
2846
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA)) {
2847
- recordData = new NonSingletonCacheManager(this.store, recordDataInstance, identifier);
2848
- } else {
2849
- if (macroCondition(isDevelopingApp())) {
2850
- recordData = this.__cacheManager = this.__cacheManager || new SingletonCacheManager();
2851
- recordData._addRecordData(identifier, recordDataInstance);
2852
- } else {
2853
- recordData = recordDataInstance;
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
- setRecordDataFor(identifier, recordData);
2857
- this.__instances.recordData.set(identifier, recordData);
2858
- if (macroCondition(getOwnConfig().debug.LOG_INSTANCE_CACHE)) {
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
- return recordData;
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 recordData = this.__instances.recordData.get(identifier);
2876
- if (!recordData) {
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 = recordData.isNew(identifier);
2880
- const isEmpty = recordData.isEmpty(identifier);
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 !recordData.isDeleted(identifier);
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 && recordData.isDeletionCommitted(identifier) ? false : !isEmpty;
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(dependencySatisfies("@ember-data/graph", "*"))) {
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(isDevelopingApp())) {
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 recordData = this.__instances.recordData.get(identifier);
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
- let removeFromRecordArray = true;
2958
- if (recordData) {
2959
- removeFromRecordArray = !recordData.isDeletionCommitted(identifier);
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._fetchManager.clearEntries(identifier);
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 typeCache = this.store.identifierCache._cache.types;
2917
+ const cache = this.store.identifierCache._cache;
2981
2918
  if (type === undefined) {
2982
- this.__instances.recordData.forEach((value, identifier) => {
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.recordData;
2927
+ // const rds = this.__instances.resourceCache;
2988
2928
  if (identifiers) {
2989
2929
  identifiers.forEach(identifier => {
2990
- if (rds.has(identifier)) {
2991
- this.unloadRecord(identifier);
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 recordData if needed ?
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 this should move into something coordinating operations
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 recordData = this.getRecordData(identifier);
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
- recordData.pushData(identifier, data, hasRecord);
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 _recordDataIsFullDeleted(identifier, recordData) {
3097
- return recordData.isDeletionCommitted(identifier) || recordData.isNew(identifier) && recordData.isDeleted(identifier);
3013
+ function _resourceIsFullDeleted(identifier, cache) {
3014
+ return cache.isDeletionCommitted(identifier) || cache.isNew(identifier) && cache.isDeleted(identifier);
3098
3015
  }
3099
- function recordDataIsFullyDeleted(cache, identifier) {
3100
- let recordData = cache.__instances.recordData.get(identifier);
3101
- return !recordData || _recordDataIsFullDeleted(identifier, recordData);
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.getRecordData(identifier).pushData(identifier, jsonPayload);
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(cache, identifier) {
3168
- const recordData = cache.__instances.recordData.get(identifier);
3169
- if (!recordData) {
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 = recordData.isNew(identifier);
3173
- const isDeleted = recordData.isDeleted(identifier);
3174
- const isEmpty = recordData.isEmpty(identifier);
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
- RecordDataForIdentifierCache.clear();
3110
+ CacheForIdentifierCache.clear();
3189
3111
  }
3190
3112
  let _modelForMixin;
3191
- if (macroCondition(dependencySatisfies("@ember-data/model", "*"))) {
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(dependencySatisfies("@ember-data/model", "*"))) {
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, label) {
3358
+ function _promiseObject(promise) {
3437
3359
  return PromiseObjectProxy.create({
3438
- promise: resolve(promise, label)
3360
+ promise
3439
3361
  });
3440
3362
  }
3441
- function _promiseArray(promise, label) {
3363
+ function _promiseArray(promise) {
3442
3364
  return PromiseArray.create({
3443
- promise: resolve(promise, label)
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(!isDevelopingApp())) {
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(!isDevelopingApp())) {
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(isDevelopingApp())) {
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, 'clear', 'length = 0');
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
- function freeze(obj) {
5466
- if (typeof Object.freeze === 'function') {
5467
- return Object.freeze(obj);
5468
- }
5469
- return obj;
5470
- }
5471
- /**
5472
- The store contains all of the data for records loaded from the server.
5473
- It is also responsible for creating instances of `Model` that wrap
5474
- the individual data for a record, so that they can be bound to in your
5475
- Handlebars templates.
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
- [createRecord](../methods/createRecord?anchor=createRecord) is used for creating new
5516
- records on the client side. This will return a new record in the
5517
- `created.uncommitted` state. In order to persist this record to the
5518
- backend, you will need to call `record.save()`.
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
- [push](../methods/push?anchor=push) is used to notify Ember Data's store of new or
5521
- updated records that exist in the backend. This will return a record
5522
- in the `loaded.saved` state. The primary use-case for `store#push` is
5523
- to notify Ember Data about record updates (full or partial) that happen
5524
- outside of the normal adapter methods (for example
5525
- [SSE](http://dev.w3.org/html5/eventsource/) or [Web
5526
- Sockets](http://www.w3.org/TR/2009/WD-websockets-20091222/)).
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
- [pushPayload](../methods/pushPayload?anchor=pushPayload) is a convenience wrapper for
5529
- `store#push` that will deserialize payloads if the
5530
- Serializer implements a `pushPayload` method.
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
- Note: When creating a new record using any of the above methods
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
- @main @ember-data/store
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._fetchManager = new FetchManager(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._fetchManager.requestCache;
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, recordDataFor, notificationManager) {
5676
- if (macroCondition(dependencySatisfies("@ember-data/model", "*"))) {
4940
+ instantiateRecord(identifier, createRecordArgs) {
4941
+ if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
5677
4942
  let modelName = identifier.type;
5678
- let recordData = this._instanceCache.getRecordData(identifier);
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
- recordData,
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(dependencySatisfies("@ember-data/model", "*"))) {
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(dependencySatisfies("@ember-data/model", "*"))) {
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(isDevelopingApp())) {
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(dependencySatisfies("@ember-data/model", "*"))) {
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(isDevelopingApp())) {
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 recordData = this._instanceCache.getRecordData(identifier);
5908
- const createOptions = normalizeProperties(this, identifier, properties, recordData.managedVersion === '1');
5909
- const resultProps = recordData.clientDidCreate(identifier, createOptions);
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(isDevelopingApp())) {
5195
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5932
5196
  assertDestroyingStore(this, 'deleteRecord');
5933
5197
  }
5934
5198
  const identifier = peekRecordIdentifier(record);
5935
- const recordData = identifier && this._instanceCache.peek({
5199
+ const cache = identifier && (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._instanceCache.peek({
5936
5200
  identifier,
5937
- bucket: 'recordData'
5938
- });
5939
- assert(`expected a recordData instance to exist for the record`, recordData);
5201
+ bucket: 'resourceCache'
5202
+ }) : this.cache);
5203
+ assert(`expected a cache instance to exist for the record`, cache);
5940
5204
  this._join(() => {
5941
- recordData.setIsDeleted(identifier, true);
5942
- if (recordData.isNew(identifier)) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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
- // if not loaded start loading
6315
- if (!this._instanceCache.recordIsLoaded(identifier)) {
6316
- promise = this._instanceCache._fetchDataIfNeededForIdentifier(identifier, options);
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 promiseRecord(this, promise);
5590
+ return promiseObject(promise.then(document => {
5591
+ return document.content;
5592
+ }));
6343
5593
  }
6344
- return promise.then(identifier => this.peekRecord(identifier));
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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
- let adapterOptionsWrapper = {};
6544
- if (options && options.adapterOptions) {
6545
- adapterOptionsWrapper.adapterOptions = options.adapterOptions;
6546
- }
6547
- let recordArray = options?._recordArray || null;
6548
- let normalizedModelName = normalizeModelName$1(modelName);
6549
- let adapter = this.adapterFor(normalizedModelName);
6550
- assert(`You tried to load a query but you have no adapter (for ${modelName})`, adapter);
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(queryPromise);
5804
+ return promiseArray(promise.then(document => document.content));
6555
5805
  }
6556
- return queryPromise;
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(isDevelopingApp())) {
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
- let normalizedModelName = normalizeModelName$1(modelName);
6648
- let adapter = this.adapterFor(normalizedModelName);
6649
- let adapterOptionsWrapper = {};
6650
- if (options && options.adapterOptions) {
6651
- adapterOptionsWrapper.adapterOptions = options.adapterOptions;
6652
- }
6653
- assert(`You tried to make a query but you have no adapter (for ${normalizedModelName})`, adapter);
6654
- assert(`You tried to make a query but your adapter does not implement 'queryRecord'`, typeof adapter.queryRecord === 'function');
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(identifier => identifier && this.peekRecord(identifier)));
5906
+ return promiseObject(promise.then(document => document.content));
6658
5907
  }
6659
- return promise.then(identifier => identifier && this.peekRecord(identifier));
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(isDevelopingApp())) {
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
- let normalizedModelName = normalizeModelName$1(modelName);
6820
- let array = this.peekAll(normalizedModelName);
6821
- let fetch;
6822
- let adapter = this.adapterFor(normalizedModelName);
6823
- assert(`You tried to load all records but you have no adapter (for ${normalizedModelName})`, adapter);
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(fetch);
6076
+ return promiseArray(promise.then(document => document.content));
6848
6077
  }
6849
- return fetch;
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(dependencySatisfies("@ember-data/graph", "*"))) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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
- let included = jsonApiDoc.included;
7092
- let i, length;
7093
- if (included) {
7094
- for (i = 0, length = included.length; i < length; i++) {
7095
- this._instanceCache.loadData(included[i]);
7096
- }
7097
- }
7098
- if (Array.isArray(jsonApiDoc.data)) {
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
- return ret;
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(isDevelopingApp())) {
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
- return this._instanceCache.createSnapshot(recordIdentifierFor(record)).serialize(options);
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
- let recordData = identifier && this._instanceCache.peek({
6429
+ const cache = identifier && (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._instanceCache.peek({
7201
6430
  identifier,
7202
- bucket: 'recordData'
7203
- });
7204
- if (!recordData) {
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}`, recordData && this._instanceCache.recordIsLoaded(identifier));
7211
- if (recordDataIsFullyDeleted(this._instanceCache, identifier)) {
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
- recordData.willCommit(identifier);
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 (recordData.isNew(identifier)) {
6451
+ if (cache.isNew(identifier)) {
7223
6452
  operation = 'createRecord';
7224
- } else if (recordData.isDeleted(identifier)) {
6453
+ } else if (cache.isDeleted(identifier)) {
7225
6454
  operation = 'deleteRecord';
7226
6455
  }
7227
- const saveOptions = Object.assign({
7228
- [SaveOp]: operation
7229
- }, options);
7230
- let fetchManagerPromise = this._fetchManager.scheduleSave(identifier, saveOptions);
7231
- return fetchManagerPromise.then(payload => {
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 RecordData implementation.
6467
+ * to utilize a custom Cache implementation.
7295
6468
  *
7296
- * @method createRecordDataFor (hook)
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
- createRecordDataFor(identifier, storeWrapper) {
7302
- if (macroCondition(dependencySatisfies("@ember-data/json-api", "*"))) {
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/-private').Cache;
6480
+ _Cache = importSync('@ember-data/json-api').default;
7310
6481
  }
7311
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1CACHE_STORE_APIS)) {
7312
- if (arguments.length === 4) {
7313
- deprecate(`Store.createRecordDataFor(<type>, <id>, <lid>, <storeWrapper>) has been deprecated in favor of Store.createRecordDataFor(<identifier>, <storeWrapper>)`, false, {
7314
- id: 'ember-data:deprecate-v1cache-store-apis',
7315
- for: 'ember-data',
7316
- until: '5.0',
7317
- since: {
7318
- enabled: '4.7',
7319
- available: '4.7'
7320
- }
7321
- });
7322
- identifier = this.identifierCache.getOrCreateRecordIdentifier({
7323
- type: arguments[0],
7324
- id: arguments[1],
7325
- lid: arguments[2]
7326
- });
7327
- storeWrapper = arguments[3];
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
- assert(`Expected store.createRecordDataFor to be implemented but it wasn't`);
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(isDevelopingApp())) {
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(dependencySatisfies("@ember-data/graph", "*"))) {
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(isDevelopingApp())) {
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(!dependencySatisfies("@ember-data/model", "*"))) {
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(isDevelopingApp())) {
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 ? recordDataFor : recordIdentifierFor;
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, recordData, identifier, store) {
6795
+ function secretInit(record, cache, identifier, store) {
7677
6796
  setRecordIdentifier(record, identifier);
7678
6797
  StoreMap.set(record, store);
7679
- setRecordDataFor(record, recordData);
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 { Collection as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, Snapshot as a, setIdentifierGenerationMethod as b, setIdentifierUpdateMethod as c, setIdentifierForgetMethod as d, setIdentifierResetMethod as e, coerceId as f, notifyArray as g, SOURCE as h, isStableIdentifier as i, IDENTIFIER_ARRAY_TAG as j, fastPush as k, SnapshotRecordArray as l, recordDataFor as m, normalizeModelName as n, removeRecordDataFor as o, recordIdentifierFor as r, storeFor as s };
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 };