@ember-data/store 4.12.0-beta.6 → 4.12.0-beta.8

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.
@@ -35,6 +35,111 @@ import ObjectProxy from '@ember/object/proxy';
35
35
  function normalizeModelName$1(modelName) {
36
36
  return dasherize(modelName);
37
37
  }
38
+ function getHydratedContent(store, request, document) {
39
+ if (Array.isArray(document.data)) {
40
+ const {
41
+ lid
42
+ } = document;
43
+ const {
44
+ recordArrayManager
45
+ } = store;
46
+ if (!lid) {
47
+ return recordArrayManager.createArray({
48
+ identifiers: document.data,
49
+ doc: document,
50
+ query: request
51
+ });
52
+ }
53
+ let managed = recordArrayManager._keyedArrays.get(lid);
54
+ if (!managed) {
55
+ managed = recordArrayManager.createArray({
56
+ identifiers: document.data,
57
+ doc: document
58
+ });
59
+ recordArrayManager._keyedArrays.set(lid, managed);
60
+ } else {
61
+ recordArrayManager.populateManagedArray(managed, document.data, document);
62
+ }
63
+ return managed;
64
+ } else {
65
+ return Object.assign({}, document, {
66
+ data: document.data ? store.peekRecord(document.data) : null
67
+ });
68
+ }
69
+ }
70
+ function calcShouldFetch(store, request, hasCachedValue, identifier) {
71
+ const {
72
+ cacheOptions
73
+ } = request;
74
+ return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier) : false);
75
+ }
76
+ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
77
+ const {
78
+ cacheOptions
79
+ } = request;
80
+ return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier) : false));
81
+ }
82
+ function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetch) {
83
+ const {
84
+ store
85
+ } = context.request;
86
+ const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
87
+ return next(context.request).then(document => {
88
+ store._enableAsyncFlush = true;
89
+ let response;
90
+ store._join(() => {
91
+ response = store.cache.put(document);
92
+ if (shouldFetch && shouldHydrate) {
93
+ response = getHydratedContent(store, context.request, response);
94
+ }
95
+ });
96
+ store._enableAsyncFlush = null;
97
+ if (shouldFetch) {
98
+ return response;
99
+ }
100
+ }, error => {
101
+ store._enableAsyncFlush = true;
102
+ store._join(() => {
103
+ store.cache.put(error);
104
+ });
105
+ store._enableAsyncFlush = null;
106
+
107
+ // TODO @runspired this is probably not the right thing to throw so make sure we add a test
108
+ if (!shouldBackgroundFetch) {
109
+ throw error;
110
+ }
111
+ });
112
+ }
113
+ const SkipCache = Symbol.for('ember-data:skip-cache');
114
+ const EnableHydration = Symbol.for('ember-data:enable-hydration');
115
+ const CacheHandler = {
116
+ request(context, next) {
117
+ // if we have no cache or no cache-key skip cache handling
118
+ if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
119
+ return next(context.request);
120
+ }
121
+ const {
122
+ store
123
+ } = context.request;
124
+ const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
125
+ const peeked = identifier ? store.cache.peekRequest(identifier) : null;
126
+
127
+ // determine if we should skip cache
128
+ if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
129
+ return fetchContentAndHydrate(next, context, true, false);
130
+ }
131
+
132
+ // if we have not skipped cache, determine if we should update behind the scenes
133
+ if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
134
+ void fetchContentAndHydrate(next, context, false, true);
135
+ }
136
+ if ('error' in peeked) {
137
+ throw peeked.error;
138
+ }
139
+ const shouldHydrate = context.request[EnableHydration] || false;
140
+ return Promise.resolve(shouldHydrate ? getHydratedContent(store, context.request, peeked.content) : peeked.content);
141
+ }
142
+ };
38
143
 
39
144
  /*
40
145
  * Returns the Cache instance associated with a given
@@ -145,9 +250,13 @@ function installPolyfill() {
145
250
  @module @ember-data/store
146
251
  */
147
252
  const IDENTIFIERS = new Set();
253
+ const DOCUMENTS = new Set();
148
254
  function isStableIdentifier(identifier) {
149
255
  return IDENTIFIERS.has(identifier);
150
256
  }
257
+ function isDocumentIdentifier(identifier) {
258
+ return DOCUMENTS.has(identifier);
259
+ }
151
260
  const isFastBoot = typeof FastBoot !== 'undefined';
152
261
  const _crypto = isFastBoot ? FastBoot.require('crypto') : window.crypto;
153
262
  if (macroCondition(getOwnConfig().polyfillUUID)) {
@@ -180,20 +289,31 @@ function setIdentifierResetMethod(method) {
180
289
  configuredResetMethod = method;
181
290
  }
182
291
  function defaultGenerationMethod(data, bucket) {
183
- if (isNonEmptyString(data.lid)) {
184
- return data.lid;
185
- }
186
- if (data.id !== undefined) {
187
- let {
188
- type,
189
- id
190
- } = data;
191
- // TODO: add test for id not a string
192
- if (isNonEmptyString(coerceId(id))) {
193
- return `@lid:${normalizeModelName$1(type)}-${id}`;
292
+ if (bucket === 'record') {
293
+ if (isNonEmptyString(data.lid)) {
294
+ return data.lid;
194
295
  }
296
+ if (data.id !== undefined) {
297
+ let {
298
+ type,
299
+ id
300
+ } = data;
301
+ // TODO: add test for id not a string
302
+ if (isNonEmptyString(coerceId(id))) {
303
+ return `@lid:${normalizeModelName$1(type)}-${id}`;
304
+ }
305
+ }
306
+ return uuidv4();
307
+ } else if (bucket === 'document') {
308
+ if (!data.url) {
309
+ return null;
310
+ }
311
+ if (!data.method || data.method.toUpperCase() === 'GET') {
312
+ return data.url;
313
+ }
314
+ return null;
195
315
  }
196
- return uuidv4();
316
+ assert(`Unknown bucket ${bucket}`, false);
197
317
  }
198
318
  function defaultEmptyCallback(...args) {}
199
319
  let DEBUG_MAP;
@@ -218,7 +338,8 @@ class IdentifierCache {
218
338
  constructor() {
219
339
  this._cache = {
220
340
  lids: new Map(),
221
- types: Object.create(null)
341
+ types: Object.create(null),
342
+ documents: new Map()
222
343
  };
223
344
  // we cache the user configuredGenerationMethod at init because it must
224
345
  // be configured prior and is not allowed to be changed
@@ -392,6 +513,36 @@ class IdentifierCache {
392
513
  return this._getRecordIdentifier(resource, false);
393
514
  }
394
515
 
516
+ /**
517
+ Returns the DocumentIdentifier for the given Request, creates one if it does not yet exist.
518
+ Returns `null` if the request does not have a `cacheKey` or `url`.
519
+ @method getOrCreateDocumentIdentifier
520
+ @param request
521
+ @returns {StableDocumentIdentifier | null}
522
+ @public
523
+ */
524
+ getOrCreateDocumentIdentifier(request) {
525
+ let cacheKey = request.cacheOptions?.key;
526
+ if (!cacheKey) {
527
+ cacheKey = this._generate(request, 'document');
528
+ }
529
+ if (!cacheKey) {
530
+ return null;
531
+ }
532
+ let identifier = this._cache.documents.get(cacheKey);
533
+ if (identifier === undefined) {
534
+ identifier = {
535
+ lid: cacheKey
536
+ };
537
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
538
+ Object.freeze(identifier);
539
+ }
540
+ DOCUMENTS.add(identifier);
541
+ this._cache.documents.set(cacheKey, identifier);
542
+ }
543
+ return identifier;
544
+ }
545
+
395
546
  /**
396
547
  Returns the Identifier for the given Resource, creates one if it does not yet exist.
397
548
  Specifically this means that we:
@@ -559,6 +710,9 @@ class IdentifierCache {
559
710
  }
560
711
  }
561
712
  destroy() {
713
+ this._cache.documents.forEach(identifier => {
714
+ DOCUMENTS.delete(identifier);
715
+ });
562
716
  this._reset();
563
717
  }
564
718
  }
@@ -727,9 +881,9 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
727
881
  return desc;
728
882
  }
729
883
  let tokenId = 0;
730
- const CacheOperations$1 = new Set(['added', 'removed', 'state', 'updated']);
884
+ const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
731
885
  function isCacheOperationValue(value) {
732
- return CacheOperations$1.has(value);
886
+ return CacheOperations.has(value);
733
887
  }
734
888
  function runLoopIsFlushing() {
735
889
  //@ts-expect-error
@@ -792,8 +946,9 @@ class NotificationManager {
792
946
  * @param {NotificationCallback} callback
793
947
  * @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
794
948
  */
949
+
795
950
  subscribe(identifier, callback) {
796
- assert(`Expected to receive a stable Identifier to subscribe to`, identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier));
951
+ assert(`Expected to receive a stable Identifier to subscribe to`, identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
797
952
  let map = Cache.get(identifier);
798
953
  if (!map) {
799
954
  map = new Map();
@@ -833,7 +988,7 @@ class NotificationManager {
833
988
 
834
989
  notify(identifier, value, key) {
835
990
  assert(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key || ''}'.`, !key || value === 'attributes' || value === 'relationships');
836
- if (!isStableIdentifier(identifier)) {
991
+ if (!isStableIdentifier(identifier) && !isDocumentIdentifier(identifier)) {
837
992
  if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
838
993
  // eslint-disable-next-line no-console
839
994
  console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key || ''}' with, but ${String(identifier)} is not in the cache`, identifier);
@@ -842,7 +997,7 @@ class NotificationManager {
842
997
  }
843
998
  if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
844
999
  // eslint-disable-next-line no-console
845
- console.log(`Buffering Notify: ${String(identifier)}\t${value}\t${key || ''}`);
1000
+ console.log(`Buffering Notify: ${String(identifier.lid)}\t${value}\t${key || ''}`);
846
1001
  }
847
1002
  const hasSubscribers = Boolean(Cache.get(identifier)?.size);
848
1003
  if (isCacheOperationValue(value) || hasSubscribers) {
@@ -894,7 +1049,7 @@ class NotificationManager {
894
1049
 
895
1050
  // TODO for documents this will need to switch based on Identifier kind
896
1051
  if (isCacheOperationValue(value)) {
897
- let callbackMap = Cache.get('resource');
1052
+ let callbackMap = Cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
898
1053
  if (callbackMap) {
899
1054
  callbackMap.forEach(cb => {
900
1055
  cb(identifier, value);
@@ -2273,7 +2428,7 @@ class LegacyWrapper {
2273
2428
  });
2274
2429
  }
2275
2430
  notifyChange(identifier, namespace, key) {
2276
- assert(`Expected a stable identifier`, isStableIdentifier(identifier));
2431
+ assert(`Expected a stable identifier`, isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
2277
2432
 
2278
2433
  // TODO do we still get value from this?
2279
2434
  if (namespace === 'relationships' && key) {
@@ -2540,7 +2695,7 @@ class V2CacheStoreWrapper {
2540
2695
  });
2541
2696
  }
2542
2697
  notifyChange(identifier, namespace, key) {
2543
- assert(`Expected a stable identifier`, isStableIdentifier(identifier));
2698
+ assert(`Expected a stable identifier`, isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
2544
2699
 
2545
2700
  // TODO do we still get value from this?
2546
2701
  if (namespace === 'relationships' && key) {
@@ -4712,75 +4867,18 @@ class Store {
4712
4867
  */
4713
4868
 
4714
4869
  /**
4715
- * Provides access to the IdentifierCache instance
4716
- * for this store.
4717
- *
4718
- * The IdentifierCache can be used to generate or
4719
- * retrieve a stable unique identifier for any resource.
4720
- *
4721
- * @property {IdentifierCache} identifierCache
4722
- * @public
4723
- */
4724
-
4725
- /**
4726
- * Provides access to the requestManager instance associated
4727
- * with this Store instance.
4728
- *
4729
- * When using `ember-data` this property is automatically
4730
- * set to an instance of `RequestManager`. When not using `ember-data`
4731
- * you must configure this property yourself, either by declaring
4732
- * it as a service or by initializing it.
4733
- *
4734
- * ```ts
4735
- * import Store, { CacheHandler } from '@ember-data/store';
4736
- * import RequestManager from '@ember-data/request';
4737
- * import Fetch from '@ember/data/request/fetch';
4738
- *
4739
- * class extends Store {
4740
- * constructor() {
4741
- * super(...arguments);
4742
- * this.requestManager = new RequestManager();
4743
- * this.requestManager.use([Fetch]);
4744
- * this.requestManager.useCache(CacheHandler);
4745
- * }
4746
- * }
4747
- * ```
4748
- *
4749
- * @public
4750
- * @property {RequestManager} requestManager
4751
- */
4752
-
4753
- /**
4754
- * A Property which an App may set to provide a Lifetimes Service
4755
- * to control when a cached request becomes stale.
4756
- *
4757
- * Note, when defined, these methods will only be invoked if `key` `url` and `method`
4758
- * are all present.
4759
- *
4760
- * `isSoftExpired` will only be invoked if `isHardExpired` returns `false`.
4761
- *
4762
- * ```ts
4763
- * store.lifetimes = {
4764
- * // make the request and ignore the current cache state
4765
- * isHardExpired(key: string, url: string, method?: HTTPMethod): boolean {
4766
- * return false;
4767
- * }
4870
+ * Provides access to the SchemaService instance
4871
+ * for this Store instance.
4768
4872
  *
4769
- * // make the request in the background if true, return cache state
4770
- * isSoftExpired(key: string, url: string, method: HTTPMethod): boolean {
4771
- * return false;
4772
- * }
4773
- * }
4774
- * ```
4873
+ * The SchemaService can be used to query for
4874
+ * information about the schema of a resource.
4775
4875
  *
4876
+ * @property {SchemaService} schema
4776
4877
  * @public
4777
- * @property {LivetimesService|undefined} lifetimes
4778
4878
  */
4779
-
4780
- // Private
4781
-
4782
- // DEBUG-only properties
4783
-
4879
+ get schema() {
4880
+ return this.getSchemaDefinitionService();
4881
+ }
4784
4882
  /**
4785
4883
  @method init
4786
4884
  @private
@@ -4889,7 +4987,8 @@ class Store {
4889
4987
  // because constructor doesn't allow for this to run after
4890
4988
  // the user has had the chance to set the prop.
4891
4989
  let opts = {
4892
- store: this
4990
+ store: this,
4991
+ [EnableHydration]: true
4893
4992
  };
4894
4993
  if (macroCondition(getOwnConfig().env.TESTING)) {
4895
4994
  if (this.DISABLE_WAITER) {
@@ -4992,25 +5091,27 @@ class Store {
4992
5091
  */
4993
5092
  getSchemaDefinitionService() {
4994
5093
  if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
4995
- if (!this._schemaDefinitionService) {
5094
+ if (!this._schema) {
4996
5095
  // it is potentially a mistake for the RFC to have not enabled chaining these services, though highlander rule is nice.
4997
5096
  // what ember-m3 did via private API to allow both worlds to interop would be much much harder using this.
4998
- this._schemaDefinitionService = new DSModelSchemaDefinitionService(this);
5097
+ this._schema = new DSModelSchemaDefinitionService(this);
4999
5098
  }
5000
5099
  }
5001
- assert(`You must registerSchemaDefinitionService with the store to use custom model classes`, this._schemaDefinitionService);
5002
- return this._schemaDefinitionService;
5100
+ assert(`You must registerSchemaDefinitionService with the store to use custom model classes`, this._schema);
5101
+ return this._schema;
5003
5102
  }
5004
5103
 
5005
5104
  /**
5006
- * Allows an app to register a custom SchemaDefinitionService
5105
+ * DEPRECATED - Use `registerSchema` instead.
5106
+ *
5107
+ * Allows an app to register a custom SchemaService
5007
5108
  * for use when information about a resource's schema needs
5008
5109
  * to be queried.
5009
5110
  *
5010
5111
  * This method can only be called more than once, but only one schema
5011
5112
  * definition service may exist. Therefore if you wish to chain services
5012
5113
  * you must lookup the existing service and close over it with the new
5013
- * service by calling `getSchemaDefinitionService` prior to registration.
5114
+ * service by accessing `store.schema` prior to registration.
5014
5115
  *
5015
5116
  * For Example:
5016
5117
  *
@@ -5043,18 +5144,73 @@ class Store {
5043
5144
  * constructor(...args) {
5044
5145
  * super(...args);
5045
5146
  *
5046
- * const schema = this.getSchemaDefinitionService();
5147
+ * const schema = this.schema;
5047
5148
  * this.registerSchemaDefinitionService(new SchemaDelegator(schema));
5048
5149
  * }
5049
5150
  * }
5050
5151
  * ```
5051
5152
  *
5052
5153
  * @method registerSchemaDefinitionService
5053
- * @param {SchemaDefinitionService} schema
5154
+ * @param {SchemaService} schema
5155
+ * @deprecated
5054
5156
  * @public
5055
5157
  */
5056
5158
  registerSchemaDefinitionService(schema) {
5057
- this._schemaDefinitionService = schema;
5159
+ this._schema = schema;
5160
+ }
5161
+ /**
5162
+ * Allows an app to register a custom SchemaService
5163
+ * for use when information about a resource's schema needs
5164
+ * to be queried.
5165
+ *
5166
+ * This method can only be called more than once, but only one schema
5167
+ * definition service may exist. Therefore if you wish to chain services
5168
+ * you must lookup the existing service and close over it with the new
5169
+ * service by accessing `store.schema` prior to registration.
5170
+ *
5171
+ * For Example:
5172
+ *
5173
+ * ```ts
5174
+ * import Store from '@ember-data/store';
5175
+ *
5176
+ * class SchemaDelegator {
5177
+ * constructor(schema) {
5178
+ * this._schema = schema;
5179
+ * }
5180
+ *
5181
+ * doesTypeExist(type: string): boolean {
5182
+ * if (AbstractSchemas.has(type)) {
5183
+ * return true;
5184
+ * }
5185
+ * return this._schema.doesTypeExist(type);
5186
+ * }
5187
+ *
5188
+ * attributesDefinitionFor(identifier: RecordIdentifier | { type: string }): AttributesSchema {
5189
+ * return this._schema.attributesDefinitionFor(identifier);
5190
+ * }
5191
+ *
5192
+ * relationshipsDefinitionFor(identifier: RecordIdentifier | { type: string }): RelationshipsSchema {
5193
+ * const schema = AbstractSchemas.get(identifier.type);
5194
+ * return schema || this._schema.relationshipsDefinitionFor(identifier);
5195
+ * }
5196
+ * }
5197
+ *
5198
+ * export default class extends Store {
5199
+ * constructor(...args) {
5200
+ * super(...args);
5201
+ *
5202
+ * const schema = this.schema;
5203
+ * this.registerSchema(new SchemaDelegator(schema));
5204
+ * }
5205
+ * }
5206
+ * ```
5207
+ *
5208
+ * @method registerSchema
5209
+ * @param {SchemaService} schema
5210
+ * @public
5211
+ */
5212
+ registerSchema(schema) {
5213
+ this._schema = schema;
5058
5214
  }
5059
5215
 
5060
5216
  /**
@@ -5585,6 +5741,9 @@ class Store {
5585
5741
  data: {
5586
5742
  record: identifier,
5587
5743
  options
5744
+ },
5745
+ cacheOptions: {
5746
+ [SkipCache]: true
5588
5747
  }
5589
5748
  });
5590
5749
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
@@ -5799,6 +5958,9 @@ class Store {
5799
5958
  type: normalizeModelName$1(modelName),
5800
5959
  query,
5801
5960
  options: options || {}
5961
+ },
5962
+ cacheOptions: {
5963
+ [SkipCache]: true
5802
5964
  }
5803
5965
  });
5804
5966
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
@@ -5901,6 +6063,9 @@ class Store {
5901
6063
  type: normalizeModelName$1(modelName),
5902
6064
  query,
5903
6065
  options: options || {}
6066
+ },
6067
+ cacheOptions: {
6068
+ [SkipCache]: true
5904
6069
  }
5905
6070
  });
5906
6071
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
@@ -6071,6 +6236,9 @@ class Store {
6071
6236
  data: {
6072
6237
  type: normalizeModelName$1(modelName),
6073
6238
  options: options || {}
6239
+ },
6240
+ cacheOptions: {
6241
+ [SkipCache]: true
6074
6242
  }
6075
6243
  });
6076
6244
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
@@ -6459,6 +6627,9 @@ class Store {
6459
6627
  data: {
6460
6628
  options,
6461
6629
  record: identifier
6630
+ },
6631
+ cacheOptions: {
6632
+ [SkipCache]: true
6462
6633
  }
6463
6634
  }).then(document => document.content);
6464
6635
  }
@@ -6798,127 +6969,6 @@ function secretInit(record, cache, identifier, store) {
6798
6969
  StoreMap.set(record, store);
6799
6970
  setCacheFor(record, cache);
6800
6971
  }
6801
- const CacheOperations = new Set(['findRecord', 'findAll', 'query', 'queryRecord', 'findBelongsTo', 'findHasMany', 'updateRecord', 'createRecord', 'deleteRecord']);
6802
- function getHydratedContent(store, request, document) {
6803
- if (!request.op || !CacheOperations.has(request.op)) {
6804
- return document;
6805
- }
6806
- if (Array.isArray(document.data)) {
6807
- const {
6808
- lid
6809
- } = document;
6810
- const {
6811
- recordArrayManager
6812
- } = store;
6813
- if (!lid) {
6814
- return recordArrayManager.createArray({
6815
- identifiers: document.data,
6816
- doc: document,
6817
- query: request
6818
- });
6819
- }
6820
- let managed = recordArrayManager._keyedArrays.get(lid);
6821
- if (!managed) {
6822
- managed = recordArrayManager.createArray({
6823
- identifiers: document.data,
6824
- doc: document
6825
- });
6826
- recordArrayManager._keyedArrays.set(lid, managed);
6827
- } else {
6828
- recordArrayManager.populateManagedArray(managed, document.data, document);
6829
- }
6830
- return managed;
6831
- } else {
6832
- switch (request.op) {
6833
- case 'findBelongsTo':
6834
- case 'queryRecord':
6835
- case 'findRecord':
6836
- return document.data ? store.peekRecord(document.data) : null;
6837
- default:
6838
- return document.data;
6839
- }
6840
- }
6841
- }
6842
- function calcShouldFetch(store, request, hasCachedValue, lid) {
6843
- const {
6844
- cacheOptions,
6845
- url,
6846
- method
6847
- } = request;
6848
- return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && lid && url && method ? store.lifetimes.isHardExpired(lid, url, method) : false);
6849
- }
6850
- function calcShouldBackgroundFetch(store, request, willFetch, lid) {
6851
- const {
6852
- cacheOptions,
6853
- url,
6854
- method
6855
- } = request;
6856
- return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && lid && url && method ? store.lifetimes.isSoftExpired(lid, url, method) : false));
6857
- }
6858
- function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetch) {
6859
- const {
6860
- store
6861
- } = context.request;
6862
- return next(context.request).then(document => {
6863
- store._enableAsyncFlush = true;
6864
- let response;
6865
- store._join(() => {
6866
- response = store.cache.put(document);
6867
- if (shouldFetch) {
6868
- response = getHydratedContent(store, context.request, response);
6869
- }
6870
- });
6871
- store._enableAsyncFlush = null;
6872
- if (shouldFetch) {
6873
- return response;
6874
- }
6875
- }, error => {
6876
- store._enableAsyncFlush = true;
6877
- store._join(() => {
6878
- store.cache.put(error);
6879
- });
6880
- store._enableAsyncFlush = null;
6881
-
6882
- // TODO @runspired this is probably not the right thing to throw so make sure we add a test
6883
- if (!shouldBackgroundFetch) {
6884
- throw error;
6885
- }
6886
- });
6887
- }
6888
- const CacheHandler = {
6889
- request(context, next) {
6890
- // if we are a legacy request or did not originate from the store, skip cache handling
6891
- if (!context.request.store || context.request.op && CacheOperations.has(context.request.op) && !context.request.url) {
6892
- return next(context.request);
6893
- }
6894
- const {
6895
- store
6896
- } = context.request;
6897
- const {
6898
- cacheOptions,
6899
- url,
6900
- method
6901
- } = context.request;
6902
- const lid = cacheOptions?.key || method === 'GET' && url ? url : null;
6903
- const peeked = lid ? store.cache.peekRequest({
6904
- lid
6905
- }) : null;
6906
-
6907
- // determine if we should skip cache
6908
- if (calcShouldFetch(store, context.request, !!peeked, lid)) {
6909
- return fetchContentAndHydrate(next, context, true, false);
6910
- }
6911
-
6912
- // if we have not skipped cache, determine if we should update behind the scenes
6913
- if (calcShouldBackgroundFetch(store, context.request, false, lid)) {
6914
- void fetchContentAndHydrate(next, context, false, true);
6915
- }
6916
- if ('error' in peeked) {
6917
- throw peeked.error;
6918
- }
6919
- return Promise.resolve(getHydratedContent(store, context.request, peeked.content));
6920
- }
6921
- };
6922
6972
  function normalizeModelName(modelName) {
6923
6973
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_HELPERS)) {
6924
6974
  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, {