@ember-data/store 5.4.0-beta.1 → 5.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,402 +1,17 @@
1
- import { macroCondition, getOwnConfig } from '@embroider/macros';
2
- import { getOwner } from '@ember/application';
3
- import { assert, deprecate, warn } from '@ember/debug';
1
+ import { deprecate, assert, warn } from '@ember/debug';
4
2
  import EmberObject from '@ember/object';
5
- import { tracked } from '@glimmer/tracking';
3
+ import { SkipCache, EnableHydration } from '@warp-drive/core-types/request';
4
+ import { macroCondition, getOwnConfig } from '@embroider/macros';
5
+ import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '@warp-drive/core-types/identifier';
6
6
  import { dasherize } from '@ember/string';
7
+ import { defineSignal, addToTransaction, createSignal, subscribe, createArrayTags, addTransactionCB } from '@ember-data/tracking/-private';
7
8
  import { _backburner } from '@ember/runloop';
8
- import { addToTransaction, subscribe, addTransactionCB } from '@ember-data/tracking/-private';
9
- import { tagForProperty } from '@ember/-internals/metal';
10
- import { dependentKeyCompat } from '@ember/object/compat';
11
- import { dirtyTag } from '@glimmer/validator';
12
- import Ember from 'ember';
13
- function _initializerDefineProperty(target, property, descriptor, context) {
14
- if (!descriptor) return;
15
- Object.defineProperty(target, property, {
16
- enumerable: descriptor.enumerable,
17
- configurable: descriptor.configurable,
18
- writable: descriptor.writable,
19
- value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
20
- });
21
- }
22
- function _classPrivateFieldBase(receiver, privateKey) {
23
- if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
24
- throw new TypeError("attempted to use private field on non-instance");
25
- }
26
- return receiver;
27
- }
28
- var id = 0;
29
- function _classPrivateFieldKey(name) {
30
- return "__private_" + id++ + "_" + name;
31
- }
32
- function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
33
- var desc = {};
34
- Object.keys(descriptor).forEach(function (key) {
35
- desc[key] = descriptor[key];
36
- });
37
- desc.enumerable = !!desc.enumerable;
38
- desc.configurable = !!desc.configurable;
39
- if ('value' in desc || desc.initializer) {
40
- desc.writable = true;
41
- }
42
- desc = decorators.slice().reverse().reduce(function (desc, decorator) {
43
- return decorator(target, property, desc) || desc;
44
- }, desc);
45
- if (context && desc.initializer !== void 0) {
46
- desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
47
- desc.initializer = undefined;
48
- }
49
- if (desc.initializer === void 0) {
50
- Object.defineProperty(target, property, desc);
51
- desc = null;
52
- }
53
- return desc;
54
- }
55
- var _class$2, _descriptor$2, _descriptor2$1, _descriptor3, _descriptor4, _store, _request;
56
- function urlFromLink(link) {
57
- if (typeof link === 'string') return link;
58
- return link.href;
59
- }
60
- let Document = (_class$2 = (_store = /*#__PURE__*/_classPrivateFieldKey("store"), _request = /*#__PURE__*/_classPrivateFieldKey("request"), class Document {
61
- constructor(store, identifier) {
62
- Object.defineProperty(this, _request, {
63
- value: _request2
64
- });
65
- _initializerDefineProperty(this, "links", _descriptor$2, this);
66
- _initializerDefineProperty(this, "data", _descriptor2$1, this);
67
- _initializerDefineProperty(this, "errors", _descriptor3, this);
68
- _initializerDefineProperty(this, "meta", _descriptor4, this);
69
- Object.defineProperty(this, _store, {
70
- writable: true,
71
- value: void 0
72
- });
73
- _classPrivateFieldBase(this, _store)[_store] = store;
74
- this.identifier = identifier;
75
- }
76
- fetch(options = {}) {
77
- assert(`No self link`, this.links?.self);
78
- options.cacheOptions = options.cacheOptions || {};
79
- options.cacheOptions.key = this.identifier?.lid;
80
- return _classPrivateFieldBase(this, _request)[_request]('self', options);
81
- }
82
- next(options) {
83
- return _classPrivateFieldBase(this, _request)[_request]('next', options);
84
- }
85
- prev(options) {
86
- return _classPrivateFieldBase(this, _request)[_request]('prev', options);
87
- }
88
- first(options) {
89
- return _classPrivateFieldBase(this, _request)[_request]('first', options);
90
- }
91
- last(options) {
92
- return _classPrivateFieldBase(this, _request)[_request]('last', options);
93
- }
94
- toJSON() {
95
- const data = {};
96
- data.identifier = this.identifier;
97
- if (this.data !== undefined) {
98
- data.data = this.data;
99
- }
100
- if (this.links !== undefined) {
101
- data.links = this.links;
102
- }
103
- if (this.errors !== undefined) {
104
- data.errors = this.errors;
105
- }
106
- if (this.meta !== undefined) {
107
- data.meta = this.meta;
108
- }
109
- return data;
110
- }
111
- }), (_descriptor$2 = _applyDecoratedDescriptor(_class$2.prototype, "links", [tracked], {
112
- configurable: true,
113
- enumerable: true,
114
- writable: true,
115
- initializer: null
116
- }), _descriptor2$1 = _applyDecoratedDescriptor(_class$2.prototype, "data", [tracked], {
117
- configurable: true,
118
- enumerable: true,
119
- writable: true,
120
- initializer: null
121
- }), _descriptor3 = _applyDecoratedDescriptor(_class$2.prototype, "errors", [tracked], {
122
- configurable: true,
123
- enumerable: true,
124
- writable: true,
125
- initializer: null
126
- }), _descriptor4 = _applyDecoratedDescriptor(_class$2.prototype, "meta", [tracked], {
127
- configurable: true,
128
- enumerable: true,
129
- writable: true,
130
- initializer: null
131
- })), _class$2);
132
- async function _request2(link, options = {}) {
133
- const href = this.links?.[link];
134
- if (!href) {
135
- return null;
136
- }
137
- const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
138
- url: urlFromLink(href)
139
- }));
140
- return response.content;
141
- }
142
- function isErrorDocument(document) {
143
- return 'errors' in document;
144
- }
145
- function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
146
- const {
147
- identifier
148
- } = options;
149
- if (isErrorDocument(document)) {
150
- if (!identifier && !options.shouldHydrate) {
151
- return document;
152
- }
153
- let doc;
154
- if (identifier) {
155
- doc = store._documentCache.get(identifier);
156
- }
157
- if (!doc) {
158
- doc = new Document(store, identifier);
159
- copyDocumentProperties(doc, document);
160
- if (identifier) {
161
- store._documentCache.set(identifier, doc);
162
- }
163
- } else if (!isFromCache) {
164
- doc.data = undefined;
165
- copyDocumentProperties(doc, document);
166
- }
167
- return options.shouldHydrate ? doc : document;
168
- }
169
- if (Array.isArray(document.data)) {
170
- const {
171
- recordArrayManager
172
- } = store;
173
- if (!identifier) {
174
- if (!options.shouldHydrate) {
175
- return document;
176
- }
177
- const data = recordArrayManager.createArray({
178
- type: request.url,
179
- identifiers: document.data,
180
- doc: document,
181
- query: request
182
- });
183
- const doc = new Document(store, null);
184
- doc.data = data;
185
- doc.meta = document.meta;
186
- doc.links = document.links;
187
- return doc;
188
- }
189
- let managed = recordArrayManager._keyedArrays.get(identifier.lid);
190
- if (!managed) {
191
- managed = recordArrayManager.createArray({
192
- type: identifier.lid,
193
- identifiers: document.data,
194
- doc: document
195
- });
196
- recordArrayManager._keyedArrays.set(identifier.lid, managed);
197
- const doc = new Document(store, identifier);
198
- doc.data = managed;
199
- doc.meta = document.meta;
200
- doc.links = document.links;
201
- store._documentCache.set(identifier, doc);
202
- return options.shouldHydrate ? doc : document;
203
- } else {
204
- const doc = store._documentCache.get(identifier);
205
- if (!isFromCache) {
206
- recordArrayManager.populateManagedArray(managed, document.data, document);
207
- doc.data = managed;
208
- doc.meta = document.meta;
209
- doc.links = document.links;
210
- }
211
- return options.shouldHydrate ? doc : document;
212
- }
213
- } else {
214
- if (!identifier && !options.shouldHydrate) {
215
- return document;
216
- }
217
- const data = document.data ? store.peekRecord(document.data) : null;
218
- let doc;
219
- if (identifier) {
220
- doc = store._documentCache.get(identifier);
221
- }
222
- if (!doc) {
223
- doc = new Document(store, identifier);
224
- doc.data = data;
225
- copyDocumentProperties(doc, document);
226
- if (identifier) {
227
- store._documentCache.set(identifier, doc);
228
- }
229
- } else if (!isFromCache) {
230
- doc.data = data;
231
- copyDocumentProperties(doc, document);
232
- }
233
- return options.shouldHydrate ? doc : document;
234
- }
235
- }
236
- const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
237
- function calcShouldFetch(store, request, hasCachedValue, identifier) {
238
- const {
239
- cacheOptions
240
- } = request;
241
- return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier) : false);
242
- }
243
- function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
244
- const {
245
- cacheOptions
246
- } = request;
247
- return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier) : false));
248
- }
249
- function isMutation(request) {
250
- return Boolean(request.op && MUTATION_OPS.has(request.op));
251
- }
252
- function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
253
- const {
254
- store
255
- } = context.request;
256
- const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
257
- let isMut = false;
258
- if (isMutation(context.request)) {
259
- isMut = true;
260
- const record = context.request.data?.record;
261
- assert(`Expected to receive a list of records included in the ${context.request.op} request`, record);
262
- store.cache.willCommit(record, context);
263
- }
264
- const promise = next(context.request).then(document => {
265
- store.requestManager._pending.delete(context.id);
266
- store._enableAsyncFlush = true;
267
- let response;
268
- store._join(() => {
269
- if (isMutation(context.request)) {
270
- response = store.cache.didCommit(context.request.data.record, document);
271
- } else {
272
- response = store.cache.put(document);
273
- }
274
- response = maybeUpdateUiObjects(store, context.request, {
275
- shouldHydrate,
276
- shouldFetch,
277
- shouldBackgroundFetch,
278
- identifier
279
- }, response, false);
280
- });
281
- store._enableAsyncFlush = null;
282
- if (shouldFetch) {
283
- return response;
284
- } else if (shouldBackgroundFetch) {
285
- store.notifications._flush();
286
- }
287
- }, error => {
288
- store.requestManager._pending.delete(context.id);
289
- if (context.request.signal?.aborted) {
290
- throw error;
291
- }
292
- store.requestManager._pending.delete(context.id);
293
- store._enableAsyncFlush = true;
294
- let response;
295
- store._join(() => {
296
- if (isMutation(context.request)) {
297
- // TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
298
- // currently we let the response remain undefiend.
299
- const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
300
- store.cache.commitWasRejected(context.request.data.record, errors);
301
- // re-throw the original error to preserve `errors` property.
302
- throw error;
303
- } else {
304
- response = store.cache.put(error);
305
- response = maybeUpdateUiObjects(store, context.request, {
306
- shouldHydrate,
307
- shouldFetch,
308
- shouldBackgroundFetch,
309
- identifier
310
- }, response, false);
311
- }
312
- });
313
- store._enableAsyncFlush = null;
314
- if (!shouldBackgroundFetch) {
315
- const newError = cloneError(error);
316
- newError.content = response;
317
- throw newError;
318
- } else {
319
- store.notifications._flush();
320
- }
321
- });
322
- if (!isMut) {
323
- return promise;
324
- }
325
- assert(`Expected a mutation`, isMutation(context.request));
326
-
327
- // for mutations we need to enqueue the promise with the requestStateService
328
- return store._requestCache._enqueue(promise, {
329
- data: [{
330
- op: 'saveRecord',
331
- recordIdentifier: context.request.data.record,
332
- options: undefined
333
- }]
334
- });
335
- }
336
- function cloneError(error) {
337
- const cloned = new Error(error.message);
338
- cloned.stack = error.stack;
339
- cloned.error = error.error;
340
- return cloned;
341
- }
342
- const SkipCache = Symbol.for('ember-data:skip-cache');
343
- const EnableHydration = Symbol.for('ember-data:enable-hydration');
344
- const CacheHandler = {
345
- request(context, next) {
346
- // if we have no cache or no cache-key skip cache handling
347
- if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
348
- return next(context.request);
349
- }
350
- const {
351
- store
352
- } = context.request;
353
- const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
354
- const peeked = identifier ? store.cache.peekRequest(identifier) : null;
9
+ import { compat } from '@ember-data/tracking';
355
10
 
356
- // determine if we should skip cache
357
- if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
358
- return fetchContentAndHydrate(next, context, identifier, true, false);
359
- }
360
-
361
- // if we have not skipped cache, determine if we should update behind the scenes
362
- if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
363
- let promise = fetchContentAndHydrate(next, context, identifier, false, true);
364
- store.requestManager._pending.set(context.id, promise);
365
- }
366
- const shouldHydrate = context.request[EnableHydration] || false;
367
- if ('error' in peeked) {
368
- const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
369
- shouldHydrate,
370
- identifier
371
- }, peeked.content, true) : peeked.content;
372
- const newError = cloneError(peeked);
373
- newError.content = content;
374
- throw newError;
375
- }
376
- return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
377
- shouldHydrate,
378
- identifier
379
- }, peeked.content, true) : peeked.content);
380
- }
381
- };
382
- function copyDocumentProperties(target, source) {
383
- if ('links' in source) {
384
- target.links = source.links;
385
- }
386
- if ('meta' in source) {
387
- target.meta = source.meta;
388
- }
389
- if ('errors' in source) {
390
- target.errors = source.errors;
391
- }
392
- }
11
+ /**
12
+ @module @ember-data/store
13
+ */
393
14
 
394
- // Used by the store to normalize IDs entering the store. Despite the fact
395
- // that developers may provide IDs as numbers (e.g., `store.findRecord('person', 1)`),
396
- // it is important that internally we use strings, since IDs may be serialized
397
- // and lose type information. For example, Ember's router may put a record's
398
- // ID into the URL, and if we later try to deserialize that URL and find the
399
- // corresponding record, we will not know if it is a string or a number.
400
15
  function coerceId(id) {
401
16
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_ID)) {
402
17
  let normalized;
@@ -429,14 +44,6 @@ function ensureStringId(id) {
429
44
  assert(`Expected id to be a string or number, received ${String(id)}`, normalized !== null);
430
45
  return normalized;
431
46
  }
432
-
433
- // provided for additional debuggability
434
- const DEBUG_CLIENT_ORIGINATED = Symbol('record-originated-on-client');
435
- const DEBUG_IDENTIFIER_BUCKET = Symbol('identifier-bucket');
436
- const DEBUG_STALE_CACHE_OWNER = Symbol('warpDriveStaleCache');
437
-
438
- // also present in production
439
- const CACHE_OWNER = Symbol('warpDriveCache');
440
47
  function normalizeModelName(type) {
441
48
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_TYPES)) {
442
49
  const result = dasherize(type);
@@ -465,7 +72,7 @@ function installPolyfill() {
465
72
  // we might be able to optimize this by requesting more bytes than we need at a time
466
73
  const rng = function () {
467
74
  // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
468
- let rnds8 = new Uint8Array(16);
75
+ const rnds8 = new Uint8Array(16);
469
76
  if (!CRYPTO.getRandomValues && !isFastBoot) {
470
77
  throw new Error(`Unable to generate bytes for UUID`);
471
78
  }
@@ -481,12 +88,12 @@ function installPolyfill() {
481
88
  byteToHex[i] = (i + 0x100).toString(16).substr(1);
482
89
  }
483
90
  const bytesToUuid = function (buf) {
484
- let bth = byteToHex;
91
+ const bth = byteToHex;
485
92
  // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
486
93
  return [bth[buf[0]], bth[buf[1]], bth[buf[2]], bth[buf[3]], '-', bth[buf[4]], bth[buf[5]], '-', bth[buf[6]], bth[buf[7]], '-', bth[buf[8]], bth[buf[9]], '-', bth[buf[10]], bth[buf[11]], bth[buf[12]], bth[buf[13]], bth[buf[14]], bth[buf[15]]].join('');
487
94
  };
488
95
  CRYPTO.randomUUID = function uuidv4() {
489
- let rnds = rng();
96
+ const rnds = rng();
490
97
 
491
98
  // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
492
99
  rnds[6] = rnds[6] & 0x0f | 0x40;
@@ -540,6 +147,7 @@ function freeze(obj) {
540
147
 
541
148
  // type IdentifierTypeLookup = { all: Set<StableRecordIdentifier>; id: Map<string, StableRecordIdentifier> };
542
149
  // type IdentifiersByType = Map<string, IdentifierTypeLookup>;
150
+
543
151
  let configuredForgetMethod;
544
152
  let configuredGenerationMethod;
545
153
  let configuredResetMethod;
@@ -647,7 +255,8 @@ class IdentifierCache {
647
255
  this._cache = {
648
256
  resources: new Map(),
649
257
  resourcesByType: Object.create(null),
650
- documents: new Map()
258
+ documents: new Map(),
259
+ polymorphicLidBackMap: new Map()
651
260
  };
652
261
  }
653
262
 
@@ -743,7 +352,7 @@ class IdentifierCache {
743
352
  *
744
353
  * @method peekRecordIdentifier
745
354
  * @param resource
746
- * @returns {StableRecordIdentifier | undefined}
355
+ * @return {StableRecordIdentifier | undefined}
747
356
  * @private
748
357
  */
749
358
  peekRecordIdentifier(resource) {
@@ -755,7 +364,7 @@ class IdentifierCache {
755
364
  Returns `null` if the request does not have a `cacheKey` or `url`.
756
365
  @method getOrCreateDocumentIdentifier
757
366
  @param request
758
- @returns {StableDocumentIdentifier | null}
367
+ @return {StableDocumentIdentifier | null}
759
368
  @public
760
369
  */
761
370
  getOrCreateDocumentIdentifier(request) {
@@ -789,7 +398,7 @@ class IdentifierCache {
789
398
  - this referential stability of the object itself is guaranteed
790
399
  @method getOrCreateRecordIdentifier
791
400
  @param resource
792
- @returns {StableRecordIdentifier}
401
+ @return {StableRecordIdentifier}
793
402
  @public
794
403
  */
795
404
  getOrCreateRecordIdentifier(resource) {
@@ -804,12 +413,12 @@ class IdentifierCache {
804
413
  with the signature `generateMethod({ type }, 'record')`.
805
414
  @method createIdentifierForNewRecord
806
415
  @param data
807
- @returns {StableRecordIdentifier}
416
+ @return {StableRecordIdentifier}
808
417
  @public
809
418
  */
810
419
  createIdentifierForNewRecord(data) {
811
- let newLid = this._generate(data, 'record');
812
- let identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
420
+ const newLid = this._generate(data, 'record');
421
+ const identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
813
422
  id: data.id || null,
814
423
  type: data.type,
815
424
  lid: newLid,
@@ -847,7 +456,7 @@ class IdentifierCache {
847
456
  @method updateRecordIdentifier
848
457
  @param identifierObject
849
458
  @param data
850
- @returns {StableRecordIdentifier}
459
+ @return {StableRecordIdentifier}
851
460
  @public
852
461
  */
853
462
  updateRecordIdentifier(identifierObject, data) {
@@ -867,7 +476,7 @@ class IdentifierCache {
867
476
  }
868
477
  }
869
478
  if (existingIdentifier) {
870
- let generatedIdentifier = identifier;
479
+ const generatedIdentifier = identifier;
871
480
  identifier = this._mergeRecordIdentifiers(keyInfo, generatedIdentifier, existingIdentifier, data);
872
481
 
873
482
  // make sure that the `lid` on the data we are processing matches the lid we kept
@@ -879,7 +488,7 @@ class IdentifierCache {
879
488
  console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
880
489
  }
881
490
  }
882
- let id = identifier.id;
491
+ const id = identifier.id;
883
492
  /*#__NOINLINE__*/
884
493
  performRecordIdentifierUpdate(identifier, keyInfo, data, this._update);
885
494
  const newId = identifier.id;
@@ -913,16 +522,32 @@ class IdentifierCache {
913
522
  const kept = this._merge(identifier, existingIdentifier, data);
914
523
  const abandoned = kept === identifier ? existingIdentifier : identifier;
915
524
 
525
+ // get any backreferences before forgetting this identifier, as it will be removed from the cache
526
+ // and we will no longer be able to find them
527
+ const abandonedBackReferences = this._cache.polymorphicLidBackMap.get(abandoned.lid);
528
+ // delete the backreferences for the abandoned identifier so that forgetRecordIdentifier
529
+ // does not try to remove them.
530
+ if (abandonedBackReferences) this._cache.polymorphicLidBackMap.delete(abandoned.lid);
531
+
916
532
  // cleanup the identifier we no longer need
917
533
  this.forgetRecordIdentifier(abandoned);
918
534
 
919
- // ensure a secondary cache entry for this id for the identifier we do keep
920
- // keyOptions.id.set(newId, kept);
535
+ // ensure a secondary cache entry for the original lid for the abandoned identifier
536
+ this._cache.resources.set(abandoned.lid, kept);
921
537
 
922
- // ensure a secondary cache entry for this id for the abandoned identifier's type we do keep
923
- // let baseKeyOptions = getTypeIndex(this._cache.resourcesByType, existingIdentifier.type);
924
- // baseKeyOptions.id.set(newId, kept);
538
+ // backReferences let us know which other identifiers are pointing at this identifier
539
+ // so we can delete them later if we forget this identifier
540
+ const keptBackReferences = this._cache.polymorphicLidBackMap.get(kept.lid) ?? [];
541
+ keptBackReferences.push(abandoned.lid);
925
542
 
543
+ // update the backreferences from the abandoned identifier to be for the kept identifier
544
+ if (abandonedBackReferences) {
545
+ abandonedBackReferences.forEach(lid => {
546
+ keptBackReferences.push(lid);
547
+ this._cache.resources.set(lid, kept);
548
+ });
549
+ }
550
+ this._cache.polymorphicLidBackMap.set(kept.lid, keptBackReferences);
926
551
  return kept;
927
552
  }
928
553
 
@@ -945,6 +570,13 @@ class IdentifierCache {
945
570
  }
946
571
  this._cache.resources.delete(identifier.lid);
947
572
  typeSet.lid.delete(identifier.lid);
573
+ const backReferences = this._cache.polymorphicLidBackMap.get(identifier.lid);
574
+ if (backReferences) {
575
+ backReferences.forEach(lid => {
576
+ this._cache.resources.delete(lid);
577
+ });
578
+ this._cache.polymorphicLidBackMap.delete(identifier.lid);
579
+ }
948
580
  if (macroCondition(getOwnConfig().env.DEBUG)) {
949
581
  identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
950
582
  }
@@ -1035,7 +667,7 @@ function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
1035
667
  } = keyInfo;
1036
668
 
1037
669
  // get the mutable instance behind our proxy wrapper
1038
- let wrapper = identifier;
670
+ const wrapper = identifier;
1039
671
  identifier = DEBUG_MAP.get(wrapper);
1040
672
  if (hasLid(data)) {
1041
673
  const lid = data.lid;
@@ -1124,28 +756,27 @@ function addResourceToCache(cache, identifier) {
1124
756
  typeSet.id.set(identifier.id, identifier);
1125
757
  }
1126
758
  }
1127
- var _class$1, _descriptor$1;
1128
759
 
1129
760
  /**
1130
761
  @module @ember-data/store
1131
762
  */
763
+
1132
764
  /**
1133
765
  @module @ember-data/store
1134
766
  */
767
+
1135
768
  /**
1136
769
  A `RecordReference` is a low-level API that allows users and
1137
770
  addon authors to perform meta-operations on a record.
1138
771
 
1139
772
  @class RecordReference
1140
773
  @public
1141
- @extends Reference
1142
774
  */
1143
- let RecordReference = (_class$1 = class RecordReference {
775
+ class RecordReference {
1144
776
  constructor(store, identifier) {
1145
777
  // unsubscribe token given to us by the notification manager
1146
778
  this.___token = void 0;
1147
779
  this.___identifier = void 0;
1148
- _initializerDefineProperty(this, "_ref", _descriptor$1, this);
1149
780
  this.store = store;
1150
781
  this.___identifier = identifier;
1151
782
  this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
@@ -1313,14 +944,8 @@ let RecordReference = (_class$1 = class RecordReference {
1313
944
  }
1314
945
  assert(`Unable to fetch record of type ${this.type} without an id`);
1315
946
  }
1316
- }, _descriptor$1 = _applyDecoratedDescriptor(_class$1.prototype, "_ref", [tracked], {
1317
- configurable: true,
1318
- enumerable: true,
1319
- writable: true,
1320
- initializer: function () {
1321
- return 0;
1322
- }
1323
- }), _class$1);
947
+ }
948
+ defineSignal(RecordReference.prototype, '_ref');
1324
949
 
1325
950
  /**
1326
951
  @module @ember-data/store
@@ -1351,6 +976,8 @@ class CacheCapabilitiesManager {
1351
976
  if (this._store._cbs) {
1352
977
  this._store._schedule('notify', () => this._flushNotifications());
1353
978
  } else {
979
+ // TODO @runspired determine if relationship mutations should schedule
980
+ // into join/run vs immediate flush
1354
981
  this._flushNotifications();
1355
982
  }
1356
983
  }
@@ -1358,7 +985,7 @@ class CacheCapabilitiesManager {
1358
985
  if (this._willNotify === false) {
1359
986
  return;
1360
987
  }
1361
- let pending = this._pendingNotifies;
988
+ const pending = this._pendingNotifies;
1362
989
  this._pendingNotifies = new Map();
1363
990
  this._willNotify = false;
1364
991
  pending.forEach((set, identifier) => {
@@ -1382,6 +1009,9 @@ class CacheCapabilitiesManager {
1382
1009
  getSchemaDefinitionService() {
1383
1010
  return this._store.getSchemaDefinitionService();
1384
1011
  }
1012
+ get schema() {
1013
+ return this._store.schema;
1014
+ }
1385
1015
  setRecordId(identifier, id) {
1386
1016
  assert(`Expected a stable identifier`, isStableIdentifier(identifier));
1387
1017
  this._store._instanceCache.setRecordId(identifier, id);
@@ -1415,6 +1045,9 @@ function peekCache(instance) {
1415
1045
  }
1416
1046
  return null;
1417
1047
  }
1048
+ function isDestroyable(record) {
1049
+ return Boolean(record && typeof record === 'object' && typeof record.destroy === 'function');
1050
+ }
1418
1051
 
1419
1052
  /**
1420
1053
  @module @ember-data/store
@@ -1442,7 +1075,7 @@ function peekRecordIdentifier(record) {
1442
1075
  @static
1443
1076
  @for @ember-data/store
1444
1077
  @param {Object} record a record instance previously obstained from the store.
1445
- @returns {StableRecordIdentifier}
1078
+ @return {StableRecordIdentifier}
1446
1079
  */
1447
1080
  function recordIdentifierFor(record) {
1448
1081
  assert(`${String(record)} is not a record instantiated by @ember-data/store`, RecordCache.has(record));
@@ -1489,11 +1122,11 @@ class InstanceCache {
1489
1122
  // @ts-expect-error TODO this needs to be fixed
1490
1123
  'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
1491
1124
  }
1492
- let staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1125
+ const staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1493
1126
 
1494
1127
  // check for duplicate entities
1495
- let keptHasRecord = this.__instances.record.has(keptIdentifier);
1496
- let staleHasRecord = this.__instances.record.has(staleIdentifier);
1128
+ const keptHasRecord = this.__instances.record.has(keptIdentifier);
1129
+ const staleHasRecord = this.__instances.record.has(staleIdentifier);
1497
1130
 
1498
1131
  // we cannot merge entities when both have records
1499
1132
  // (this may not be strictly true, we could probably swap the cache data the record points at)
@@ -1547,7 +1180,7 @@ class InstanceCache {
1547
1180
  return record;
1548
1181
  }
1549
1182
  getReference(identifier) {
1550
- let cache = this.__instances.reference;
1183
+ const cache = this.__instances.reference;
1551
1184
  let reference = cache.get(identifier);
1552
1185
  if (!reference) {
1553
1186
  reference = new RecordReference(this.store, identifier);
@@ -1578,7 +1211,7 @@ class InstanceCache {
1578
1211
  }
1579
1212
  disconnect(identifier) {
1580
1213
  const record = this.__instances.record.get(identifier);
1581
- assert('Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying);
1214
+ assert('Cannot destroy record while it is still materialized', !isDestroyable(record) || record.isDestroyed || record.isDestroying);
1582
1215
  this.store._graph?.remove(identifier);
1583
1216
  this.store.identifierCache.forgetRecordIdentifier(identifier);
1584
1217
  removeRecordDataFor(identifier);
@@ -1646,7 +1279,7 @@ class InstanceCache {
1646
1279
  });
1647
1280
  } else {
1648
1281
  const typeCache = cache.resourcesByType;
1649
- let identifiers = typeCache[type]?.lid;
1282
+ const identifiers = typeCache[type]?.lid;
1650
1283
  if (identifiers) {
1651
1284
  identifiers.forEach(identifier => {
1652
1285
  // if (rds.has(identifier)) {
@@ -1664,7 +1297,7 @@ class InstanceCache {
1664
1297
  type,
1665
1298
  lid
1666
1299
  } = identifier;
1667
- let oldId = identifier.id;
1300
+ const oldId = identifier.id;
1668
1301
 
1669
1302
  // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
1670
1303
  assert(`'${type}' was saved to the server, but the response does not have an id and your record does not either.`, !(id === null && oldId === null));
@@ -1683,7 +1316,7 @@ class InstanceCache {
1683
1316
  // eslint-disable-next-line no-console
1684
1317
  console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
1685
1318
  }
1686
- let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1319
+ const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1687
1320
  type,
1688
1321
  id
1689
1322
  });
@@ -1722,13 +1355,13 @@ function resourceIsFullyDeleted(instanceCache, identifier) {
1722
1355
  */
1723
1356
 
1724
1357
  function preloadData(store, identifier, preload) {
1725
- let jsonPayload = {};
1358
+ const jsonPayload = {};
1726
1359
  //TODO(Igor) consider the polymorphic case
1727
1360
  const schemas = store.getSchemaDefinitionService();
1728
1361
  const relationships = schemas.relationshipsDefinitionFor(identifier);
1729
1362
  Object.keys(preload).forEach(key => {
1730
- let preloadValue = preload[key];
1731
- let relationshipMeta = relationships[key];
1363
+ const preloadValue = preload[key];
1364
+ const relationshipMeta = relationships[key];
1732
1365
  if (relationshipMeta) {
1733
1366
  if (!jsonPayload.relationships) {
1734
1367
  jsonPayload.relationships = {};
@@ -1797,7 +1430,7 @@ function getShimClass(store, modelName) {
1797
1430
  }
1798
1431
  function mapFromHash(hash) {
1799
1432
  const map = new Map();
1800
- for (let i in hash) {
1433
+ for (const i in hash) {
1801
1434
  if (Object.prototype.hasOwnProperty.call(hash, i)) {
1802
1435
  map.set(i, hash[i]);
1803
1436
  }
@@ -1812,31 +1445,31 @@ class ShimModelClass {
1812
1445
  this.modelName = modelName;
1813
1446
  }
1814
1447
  get fields() {
1815
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1448
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1816
1449
  type: this.modelName
1817
1450
  });
1818
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1451
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1819
1452
  type: this.modelName
1820
1453
  });
1821
- let fields = new Map();
1454
+ const fields = new Map();
1822
1455
  Object.keys(attrs).forEach(key => fields.set(key, 'attribute'));
1823
1456
  Object.keys(relationships).forEach(key => fields.set(key, relationships[key].kind));
1824
1457
  return fields;
1825
1458
  }
1826
1459
  get attributes() {
1827
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1460
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1828
1461
  type: this.modelName
1829
1462
  });
1830
1463
  return mapFromHash(attrs);
1831
1464
  }
1832
1465
  get relationshipsByName() {
1833
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1466
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1834
1467
  type: this.modelName
1835
1468
  });
1836
1469
  return mapFromHash(relationships);
1837
1470
  }
1838
1471
  eachAttribute(callback, binding) {
1839
- let attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1472
+ const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1840
1473
  type: this.modelName
1841
1474
  });
1842
1475
  Object.keys(attrDefs).forEach(key => {
@@ -1844,7 +1477,7 @@ class ShimModelClass {
1844
1477
  });
1845
1478
  }
1846
1479
  eachRelationship(callback, binding) {
1847
- let relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1480
+ const relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1848
1481
  type: this.modelName
1849
1482
  });
1850
1483
  Object.keys(relationshipDefs).forEach(key => {
@@ -1862,6 +1495,16 @@ class ShimModelClass {
1862
1495
  });
1863
1496
  }
1864
1497
  }
1498
+ function _classPrivateFieldBase(receiver, privateKey) {
1499
+ if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
1500
+ throw new TypeError("attempted to use private field on non-instance");
1501
+ }
1502
+ return receiver;
1503
+ }
1504
+ var id = 0;
1505
+ function _classPrivateFieldKey(name) {
1506
+ return "__private_" + id++ + "_" + name;
1507
+ }
1865
1508
  var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
1866
1509
  /**
1867
1510
  * The CacheManager wraps a Cache enforcing that only
@@ -1888,16 +1531,6 @@ class CacheManager {
1888
1531
  writable: true,
1889
1532
  value: void 0
1890
1533
  });
1891
- /**
1892
- * Query the cache for whether a given resource has been deleted and that deletion
1893
- * has also been persisted.
1894
- *
1895
- * @method isDeletionCommitted
1896
- * @public
1897
- * @param identifier
1898
- * @returns {boolean}
1899
- */
1900
- this.isDel = void 0;
1901
1534
  _classPrivateFieldBase(this, _cache)[_cache] = cache;
1902
1535
  }
1903
1536
 
@@ -1911,7 +1544,7 @@ class CacheManager {
1911
1544
  * semantics, `put` has `replace` semantics similar to
1912
1545
  * the `http` method `PUT`
1913
1546
  *
1914
- * the individually cacheabl
1547
+ * the individually cacheable
1915
1548
  * e resource data it may contain
1916
1549
  * should upsert, but the document data surrounding it should
1917
1550
  * fully replace any existing information
@@ -1924,7 +1557,7 @@ class CacheManager {
1924
1557
  *
1925
1558
  * @method put
1926
1559
  * @param {StructuredDocument} doc
1927
- * @returns {ResourceDocument}
1560
+ * @return {ResourceDocument}
1928
1561
  * @public
1929
1562
  */
1930
1563
  put(doc) {
@@ -1940,7 +1573,7 @@ class CacheManager {
1940
1573
  * @method patch
1941
1574
  * @public
1942
1575
  * @param op the operation to perform
1943
- * @returns {void}
1576
+ * @return {void}
1944
1577
  */
1945
1578
  patch(op) {
1946
1579
  _classPrivateFieldBase(this, _cache)[_cache].patch(op);
@@ -1975,7 +1608,7 @@ class CacheManager {
1975
1608
  * An implementation might want to do this because
1976
1609
  * de-referencing records which read from their own
1977
1610
  * blob is generally safer because the record does
1978
- * not require retainining connections to the Store
1611
+ * not require retaining connections to the Store
1979
1612
  * and Cache to present data on a per-field basis.
1980
1613
  *
1981
1614
  * This generally takes the place of `getAttr` as
@@ -1988,7 +1621,7 @@ class CacheManager {
1988
1621
  * @method peek
1989
1622
  * @public
1990
1623
  * @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
1991
- * @returns {ResourceDocument | ResourceBlob | null} the known resource data
1624
+ * @return {ResourceDocument | ResourceBlob | null} the known resource data
1992
1625
  */
1993
1626
 
1994
1627
  peek(identifier) {
@@ -2001,7 +1634,7 @@ class CacheManager {
2001
1634
  *
2002
1635
  * @method peekRequest
2003
1636
  * @param {StableDocumentIdentifier}
2004
- * @returns {StableDocumentIdentifier | null}
1637
+ * @return {StableDocumentIdentifier | null}
2005
1638
  * @public
2006
1639
  */
2007
1640
  peekRequest(identifier) {
@@ -2016,7 +1649,7 @@ class CacheManager {
2016
1649
  * @param identifier
2017
1650
  * @param data
2018
1651
  * @param hasRecord
2019
- * @returns {void | string[]} if `hasRecord` is true then calculated key changes should be returned
1652
+ * @return {void | string[]} if `hasRecord` is true then calculated key changes should be returned
2020
1653
  */
2021
1654
  upsert(identifier, data, hasRecord) {
2022
1655
  return _classPrivateFieldBase(this, _cache)[_cache].upsert(identifier, data, hasRecord);
@@ -2034,7 +1667,7 @@ class CacheManager {
2034
1667
  *
2035
1668
  * @method fork
2036
1669
  * @public
2037
- * @returns Promise<Cache>
1670
+ * @return Promise<Cache>
2038
1671
  */
2039
1672
  fork() {
2040
1673
  return _classPrivateFieldBase(this, _cache)[_cache].fork();
@@ -2050,7 +1683,7 @@ class CacheManager {
2050
1683
  * @method merge
2051
1684
  * @param {Cache} cache
2052
1685
  * @public
2053
- * @returns Promise<void>
1686
+ * @return Promise<void>
2054
1687
  */
2055
1688
  merge(cache) {
2056
1689
  return _classPrivateFieldBase(this, _cache)[_cache].merge(cache);
@@ -2102,7 +1735,7 @@ class CacheManager {
2102
1735
  * via `cache.hydrate`.
2103
1736
  *
2104
1737
  * @method dump
2105
- * @returns {Promise<ReadableStream>}
1738
+ * @return {Promise<ReadableStream>}
2106
1739
  * @public
2107
1740
  */
2108
1741
  dump() {
@@ -2123,7 +1756,7 @@ class CacheManager {
2123
1756
  *
2124
1757
  * @method hydrate
2125
1758
  * @param {ReadableStream} stream
2126
- * @returns {Promise<void>}
1759
+ * @return {Promise<void>}
2127
1760
  * @public
2128
1761
  */
2129
1762
  hydrate(stream) {
@@ -2137,7 +1770,7 @@ class CacheManager {
2137
1770
  // ================
2138
1771
 
2139
1772
  /**
2140
- * [LIFECYLCE] Signal to the cache that a new record has been instantiated on the client
1773
+ * [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client
2141
1774
  *
2142
1775
  * It returns properties from options that should be set on the record during the create
2143
1776
  * process. This return value behavior is deprecated.
@@ -2211,7 +1844,7 @@ class CacheManager {
2211
1844
  * @public
2212
1845
  * @param identifier
2213
1846
  * @param propertyName
2214
- * @returns {unknown}
1847
+ * @return {unknown}
2215
1848
  */
2216
1849
  getAttr(identifier, propertyName) {
2217
1850
  return _classPrivateFieldBase(this, _cache)[_cache].getAttr(identifier, propertyName);
@@ -2236,7 +1869,7 @@ class CacheManager {
2236
1869
  * @method changedAttrs
2237
1870
  * @public
2238
1871
  * @param identifier
2239
- * @returns
1872
+ * @return
2240
1873
  */
2241
1874
  changedAttrs(identifier) {
2242
1875
  return _classPrivateFieldBase(this, _cache)[_cache].changedAttrs(identifier);
@@ -2248,7 +1881,7 @@ class CacheManager {
2248
1881
  * @method hasChangedAttrs
2249
1882
  * @public
2250
1883
  * @param identifier
2251
- * @returns {boolean}
1884
+ * @return {boolean}
2252
1885
  */
2253
1886
  hasChangedAttrs(identifier) {
2254
1887
  return _classPrivateFieldBase(this, _cache)[_cache].hasChangedAttrs(identifier);
@@ -2260,7 +1893,7 @@ class CacheManager {
2260
1893
  * @method rollbackAttrs
2261
1894
  * @public
2262
1895
  * @param identifier
2263
- * @returns the names of attributes that were restored
1896
+ * @return the names of attributes that were restored
2264
1897
  */
2265
1898
  rollbackAttrs(identifier) {
2266
1899
  return _classPrivateFieldBase(this, _cache)[_cache].rollbackAttrs(identifier);
@@ -2294,7 +1927,7 @@ class CacheManager {
2294
1927
  * @method changedRelationships
2295
1928
  * @public
2296
1929
  * @param {StableRecordIdentifier} identifier
2297
- * @returns {Map<string, RelationshipDiff>}
1930
+ * @return {Map<string, RelationshipDiff>}
2298
1931
  */
2299
1932
  changedRelationships(identifier) {
2300
1933
  return _classPrivateFieldBase(this, _cache)[_cache].changedRelationships(identifier);
@@ -2306,7 +1939,7 @@ class CacheManager {
2306
1939
  * @method hasChangedRelationships
2307
1940
  * @public
2308
1941
  * @param {StableRecordIdentifier} identifier
2309
- * @returns {boolean}
1942
+ * @return {boolean}
2310
1943
  */
2311
1944
  hasChangedRelationships(identifier) {
2312
1945
  return _classPrivateFieldBase(this, _cache)[_cache].hasChangedRelationships(identifier);
@@ -2322,7 +1955,7 @@ class CacheManager {
2322
1955
  * @method rollbackRelationships
2323
1956
  * @public
2324
1957
  * @param {StableRecordIdentifier} identifier
2325
- * @returns {string[]} the names of relationships that were restored
1958
+ * @return {string[]} the names of relationships that were restored
2326
1959
  */
2327
1960
  rollbackRelationships(identifier) {
2328
1961
  return _classPrivateFieldBase(this, _cache)[_cache].rollbackRelationships(identifier);
@@ -2335,7 +1968,7 @@ class CacheManager {
2335
1968
  * @public
2336
1969
  * @param identifier
2337
1970
  * @param propertyName
2338
- * @returns resource relationship object
1971
+ * @return resource relationship object
2339
1972
  */
2340
1973
  getRelationship(identifier, propertyName) {
2341
1974
  return _classPrivateFieldBase(this, _cache)[_cache].getRelationship(identifier, propertyName);
@@ -2363,7 +1996,7 @@ class CacheManager {
2363
1996
  * @method getErrors
2364
1997
  * @public
2365
1998
  * @param identifier
2366
- * @returns
1999
+ * @return
2367
2000
  */
2368
2001
  getErrors(identifier) {
2369
2002
  return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier);
@@ -2375,7 +2008,7 @@ class CacheManager {
2375
2008
  * @method isEmpty
2376
2009
  * @public
2377
2010
  * @param identifier
2378
- * @returns {boolean}
2011
+ * @return {boolean}
2379
2012
  */
2380
2013
  isEmpty(identifier) {
2381
2014
  return _classPrivateFieldBase(this, _cache)[_cache].isEmpty(identifier);
@@ -2388,7 +2021,7 @@ class CacheManager {
2388
2021
  * @method isNew
2389
2022
  * @public
2390
2023
  * @param identifier
2391
- * @returns {boolean}
2024
+ * @return {boolean}
2392
2025
  */
2393
2026
  isNew(identifier) {
2394
2027
  return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier);
@@ -2401,15 +2034,29 @@ class CacheManager {
2401
2034
  * @method isDeleted
2402
2035
  * @public
2403
2036
  * @param identifier
2404
- * @returns {boolean}
2037
+ * @return {boolean}
2405
2038
  */
2406
2039
  isDeleted(identifier) {
2407
2040
  return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier);
2408
2041
  }
2042
+
2043
+ /**
2044
+ * Query the cache for whether a given resource has been deleted and that deletion
2045
+ * has also been persisted.
2046
+ *
2047
+ * @method isDeletionCommitted
2048
+ * @public
2049
+ * @param identifier
2050
+ * @return {boolean}
2051
+ */
2409
2052
  isDeletionCommitted(identifier) {
2410
2053
  return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier);
2411
2054
  }
2412
2055
  }
2056
+
2057
+ /**
2058
+ * @module @ember-data/store
2059
+ */
2413
2060
  let tokenId = 0;
2414
2061
  const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
2415
2062
  function isCacheOperationValue(value) {
@@ -2420,7 +2067,7 @@ function runLoopIsFlushing() {
2420
2067
  return !!_backburner.currentInstance && _backburner._autorun !== true;
2421
2068
  }
2422
2069
  function _unsubscribe(tokens, token, cache) {
2423
- let identifier = tokens.get(token);
2070
+ const identifier = tokens.get(token);
2424
2071
  if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
2425
2072
  if (!identifier) {
2426
2073
  // eslint-disable-next-line no-console
@@ -2479,7 +2126,7 @@ class NotificationManager {
2479
2126
  * @public
2480
2127
  * @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
2481
2128
  * @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
2482
- * @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
2129
+ * @return {UnsubscribeToken} an opaque token to be used with unsubscribe
2483
2130
  */
2484
2131
 
2485
2132
  subscribe(identifier, callback) {
@@ -2489,7 +2136,7 @@ class NotificationManager {
2489
2136
  map = new Map();
2490
2137
  this._cache.set(identifier, map);
2491
2138
  }
2492
- let unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2139
+ const unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2493
2140
  _tokenRef: tokenId++
2494
2141
  } : {};
2495
2142
  map.set(unsubToken, callback);
@@ -2542,7 +2189,7 @@ class NotificationManager {
2542
2189
  this._buffered.set(identifier, buffer);
2543
2190
  }
2544
2191
  buffer.push([value, key]);
2545
- void this._scheduleNotify();
2192
+ this._scheduleNotify();
2546
2193
  }
2547
2194
  return hasSubscribers;
2548
2195
  }
@@ -2584,14 +2231,14 @@ class NotificationManager {
2584
2231
 
2585
2232
  // TODO for documents this will need to switch based on Identifier kind
2586
2233
  if (isCacheOperationValue(value)) {
2587
- let callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2234
+ const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2588
2235
  if (callbackMap) {
2589
2236
  callbackMap.forEach(cb => {
2590
2237
  cb(identifier, value);
2591
2238
  });
2592
2239
  }
2593
2240
  }
2594
- let callbackMap = this._cache.get(identifier);
2241
+ const callbackMap = this._cache.get(identifier);
2595
2242
  if (!callbackMap || !callbackMap.size) {
2596
2243
  return false;
2597
2244
  }
@@ -2607,7 +2254,30 @@ class NotificationManager {
2607
2254
  this._cache.clear();
2608
2255
  }
2609
2256
  }
2610
- var _class, _descriptor, _class3, _descriptor2;
2257
+ function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
2258
+ var desc = {};
2259
+ Object.keys(descriptor).forEach(function (key) {
2260
+ desc[key] = descriptor[key];
2261
+ });
2262
+ desc.enumerable = !!desc.enumerable;
2263
+ desc.configurable = !!desc.configurable;
2264
+ if ('value' in desc || desc.initializer) {
2265
+ desc.writable = true;
2266
+ }
2267
+ desc = decorators.slice().reverse().reduce(function (desc, decorator) {
2268
+ return decorator(target, property, desc) || desc;
2269
+ }, desc);
2270
+ if (context && desc.initializer !== void 0) {
2271
+ desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
2272
+ desc.initializer = undefined;
2273
+ }
2274
+ if (desc.initializer === void 0) {
2275
+ Object.defineProperty(target, property, desc);
2276
+ desc = null;
2277
+ }
2278
+ return desc;
2279
+ }
2280
+ var _class;
2611
2281
  const ARRAY_GETTER_METHODS = new Set([Symbol.iterator, 'concat', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'map', 'reduce', 'reduceRight', 'slice', 'some', 'values']);
2612
2282
  const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
2613
2283
  const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
@@ -2617,21 +2287,16 @@ function isArrayGetter(prop) {
2617
2287
  function isArraySetter(prop) {
2618
2288
  return ARRAY_SETTER_METHODS.has(prop);
2619
2289
  }
2620
- const IDENTIFIER_ARRAY_TAG = Symbol('#tag');
2290
+ function isSelfProp(self, prop) {
2291
+ return prop in self;
2292
+ }
2293
+ const ARRAY_SIGNAL = Symbol('#signal');
2621
2294
  const SOURCE = Symbol('#source');
2622
2295
  const MUTATE = Symbol('#update');
2623
2296
  const NOTIFY = Symbol('#notify');
2624
2297
  const IS_COLLECTION = Symbol.for('Collection');
2625
2298
  function notifyArray(arr) {
2626
- addToTransaction(arr[IDENTIFIER_ARRAY_TAG]);
2627
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
2628
- // @ts-expect-error tagForProperty is mistyped to Tag instead of DirtyableTag
2629
- // eslint-disable-next-line
2630
- dirtyTag(tagForProperty(arr, 'length'));
2631
- // @ts-expect-error tagForProperty is mistyped to Tag instead of DirtyableTag
2632
- // eslint-disable-next-line
2633
- dirtyTag(tagForProperty(arr, '[]'));
2634
- }
2299
+ addToTransaction(arr[ARRAY_SIGNAL]);
2635
2300
  }
2636
2301
  function convertToInt(prop) {
2637
2302
  if (typeof prop === 'symbol') return null;
@@ -2639,29 +2304,6 @@ function convertToInt(prop) {
2639
2304
  if (isNaN(num)) return null;
2640
2305
  return num % 1 === 0 ? num : null;
2641
2306
  }
2642
- let Tag = (_class = class Tag {
2643
- /*
2644
- * whether this was part of a transaction when last mutated
2645
- */
2646
-
2647
- constructor() {
2648
- _initializerDefineProperty(this, "ref", _descriptor, this);
2649
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2650
- const [arr, prop] = arguments;
2651
- this._debug_base = arr.constructor.name + ':' + String(arr.modelName);
2652
- this._debug_prop = prop;
2653
- }
2654
- this.shouldReset = false;
2655
- this.t = false;
2656
- }
2657
- }, _descriptor = _applyDecoratedDescriptor(_class.prototype, "ref", [tracked], {
2658
- configurable: true,
2659
- enumerable: true,
2660
- writable: true,
2661
- initializer: function () {
2662
- return null;
2663
- }
2664
- }), _class);
2665
2307
  function safeForEach(instance, arr, store, callback, target) {
2666
2308
  if (target === undefined) {
2667
2309
  target = null;
@@ -2694,7 +2336,7 @@ function safeForEach(instance, arr, store, callback, target) {
2694
2336
  @class RecordArray
2695
2337
  @public
2696
2338
  */
2697
- let IdentifierArray = (_class3 = class IdentifierArray {
2339
+ let IdentifierArray = (_class = class IdentifierArray {
2698
2340
  [NOTIFY]() {
2699
2341
  notifyArray(this);
2700
2342
  }
@@ -2722,14 +2364,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2722
2364
  set length(value) {
2723
2365
  this[SOURCE].length = value;
2724
2366
  }
2725
-
2726
- // here to support computed chains
2727
- // and {{#each}}
2728
- get '[]'() {
2729
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
2730
- return this;
2731
- }
2732
- }
2733
2367
  constructor(options) {
2734
2368
  /**
2735
2369
  The flag to signal a `RecordArray` is currently loading data.
@@ -2744,7 +2378,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2744
2378
  @public
2745
2379
  @type Boolean
2746
2380
  */
2747
- _initializerDefineProperty(this, "isUpdating", _descriptor2, this);
2748
2381
  this.isLoaded = true;
2749
2382
  this.isDestroying = false;
2750
2383
  this.isDestroyed = false;
@@ -2752,16 +2385,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2752
2385
  this[IS_COLLECTION] = true;
2753
2386
  this[SOURCE] = void 0;
2754
2387
  // eslint-disable-next-line @typescript-eslint/no-this-alias
2755
- let self = this;
2388
+ const self = this;
2756
2389
  this.modelName = options.type;
2757
2390
  this.store = options.store;
2758
2391
  this._manager = options.manager;
2759
2392
  this[SOURCE] = options.identifiers;
2760
- // @ts-expect-error
2761
- this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
2393
+ this[ARRAY_SIGNAL] = createSignal(this, 'length');
2762
2394
  const store = options.store;
2763
2395
  const boundFns = new Map();
2764
- const _TAG = this[IDENTIFIER_ARRAY_TAG];
2396
+ const _SIGNAL = this[ARRAY_SIGNAL];
2765
2397
  const PrivateState = {
2766
2398
  links: options.links || null,
2767
2399
  meta: options.meta || null
@@ -2774,40 +2406,40 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2774
2406
 
2775
2407
  const proxy = new Proxy(this[SOURCE], {
2776
2408
  get(target, prop, receiver) {
2777
- let index = convertToInt(prop);
2778
- if (_TAG.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2409
+ const index = convertToInt(prop);
2410
+ if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2779
2411
  options.manager._syncArray(receiver);
2780
- _TAG.t = false;
2781
- _TAG.shouldReset = false;
2412
+ _SIGNAL.t = false;
2413
+ _SIGNAL.shouldReset = false;
2782
2414
  }
2783
2415
  if (index !== null) {
2784
2416
  const identifier = target[index];
2785
2417
  if (!transaction) {
2786
- subscribe(_TAG);
2418
+ subscribe(_SIGNAL);
2787
2419
  }
2788
2420
  return identifier && store._instanceCache.getRecord(identifier);
2789
2421
  }
2790
- if (prop === 'meta') return subscribe(_TAG), PrivateState.meta;
2791
- if (prop === 'links') return subscribe(_TAG), PrivateState.links;
2792
- if (prop === '[]') return subscribe(_TAG), receiver;
2422
+ if (prop === 'meta') return subscribe(_SIGNAL), PrivateState.meta;
2423
+ if (prop === 'links') return subscribe(_SIGNAL), PrivateState.links;
2424
+ if (prop === '[]') return subscribe(_SIGNAL), receiver;
2793
2425
  if (isArrayGetter(prop)) {
2794
2426
  let fn = boundFns.get(prop);
2795
2427
  if (fn === undefined) {
2796
2428
  if (prop === 'forEach') {
2797
2429
  fn = function () {
2798
- subscribe(_TAG);
2430
+ subscribe(_SIGNAL);
2799
2431
  transaction = true;
2800
- let result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2432
+ const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2801
2433
  transaction = false;
2802
2434
  return result;
2803
2435
  };
2804
2436
  } else {
2805
2437
  fn = function () {
2806
- subscribe(_TAG);
2438
+ subscribe(_SIGNAL);
2807
2439
  // array functions must run through Reflect to work properly
2808
2440
  // binding via other means will not work.
2809
2441
  transaction = true;
2810
- let result = Reflect.apply(target[prop], receiver, arguments);
2442
+ const result = Reflect.apply(target[prop], receiver, arguments);
2811
2443
  transaction = false;
2812
2444
  return result;
2813
2445
  };
@@ -2829,10 +2461,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2829
2461
  const args = Array.prototype.slice.call(arguments);
2830
2462
  assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
2831
2463
  transaction = true;
2832
- let result = Reflect.apply(target[prop], receiver, args);
2833
- self[MUTATE](prop, args, result);
2834
- addToTransaction(_TAG);
2835
- // TODO handle cache updates
2464
+ const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
2836
2465
  transaction = false;
2837
2466
  return result;
2838
2467
  };
@@ -2840,16 +2469,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2840
2469
  }
2841
2470
  return fn;
2842
2471
  }
2843
- if (prop in self) {
2844
- if (prop === NOTIFY || prop === IDENTIFIER_ARRAY_TAG || prop === SOURCE) {
2472
+ if (isSelfProp(self, prop)) {
2473
+ if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
2845
2474
  return self[prop];
2846
2475
  }
2847
2476
  let fn = boundFns.get(prop);
2848
2477
  if (fn) return fn;
2849
- let outcome = self[prop];
2478
+ const outcome = self[prop];
2850
2479
  if (typeof outcome === 'function') {
2851
2480
  fn = function () {
2852
- subscribe(_TAG);
2481
+ subscribe(_SIGNAL);
2853
2482
  // array functions must run through Reflect to work properly
2854
2483
  // binding via other means will not work.
2855
2484
  return Reflect.apply(outcome, receiver, arguments);
@@ -2857,17 +2486,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2857
2486
  boundFns.set(prop, fn);
2858
2487
  return fn;
2859
2488
  }
2860
- return subscribe(_TAG), outcome;
2489
+ return subscribe(_SIGNAL), outcome;
2861
2490
  }
2862
2491
  return target[prop];
2863
2492
  },
2864
- set(target, prop, value) {
2493
+ // FIXME: Should this get a generic like get above?
2494
+ set(target, prop, value, receiver) {
2865
2495
  if (prop === 'length') {
2866
2496
  if (!transaction && value === 0) {
2867
2497
  transaction = true;
2868
- addToTransaction(_TAG);
2869
- Reflect.set(target, prop, value);
2870
- self[MUTATE]('length 0', []);
2498
+ self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
2871
2499
  transaction = false;
2872
2500
  return true;
2873
2501
  } else if (transaction) {
@@ -2884,9 +2512,22 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2884
2512
  PrivateState.meta = value || null;
2885
2513
  return true;
2886
2514
  }
2887
- let index = convertToInt(prop);
2515
+ const index = convertToInt(prop);
2516
+
2517
+ // we do not allow "holey" arrays and so if the index is
2518
+ // greater than length then we will disallow setting it.
2519
+ // however, there is a special case for "unshift" with more than
2520
+ // one item being inserted since current items will be moved to the
2521
+ // new indices first.
2522
+ // we "loosely" detect this by just checking whether we are in
2523
+ // a transaction.
2888
2524
  if (index === null || index > target.length) {
2889
- if (prop in self) {
2525
+ if (index !== null && transaction) {
2526
+ const identifier = recordIdentifierFor(value);
2527
+ assert(`Cannot set index ${index} past the end of the array.`, isStableIdentifier(identifier));
2528
+ target[index] = identifier;
2529
+ return true;
2530
+ } else if (isSelfProp(self, prop)) {
2890
2531
  self[prop] = value;
2891
2532
  return true;
2892
2533
  }
@@ -2896,12 +2537,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2896
2537
  assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
2897
2538
  return false;
2898
2539
  }
2899
- let original = target[index];
2900
- let newIdentifier = extractIdentifierFromRecord$1(value);
2540
+ const original = target[index];
2541
+ const newIdentifier = extractIdentifierFromRecord$1(value);
2901
2542
  target[index] = newIdentifier;
2543
+ assert(`Expected a record`, isStableIdentifier(newIdentifier));
2544
+ // We generate "transactions" whenever a setter method on the array
2545
+ // is called and might bulk update multiple array cells. Fundamentally,
2546
+ // all array operations decompose into individual cell replacements.
2547
+ // e.g. a push is really a "replace cell at next index with new value"
2548
+ // or a splice is "shift all values left/right by X and set out of new
2549
+ // bounds cells to undefined"
2550
+ //
2551
+ // so, if we are in a transaction, then this is not a user generated change
2552
+ // but one generated by a setter method. In this case we want to only apply
2553
+ // the change to the target array and not call the MUTATE method.
2554
+ // If there is no transaction though, then this means the user themselves has
2555
+ // directly changed the value of a specific index and we need to thus generate
2556
+ // a mutation for that change.
2557
+ // e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
2558
+ // a transaction.
2559
+ // while "arr[arr.length] = newVal;" is handled by this replace cell code path.
2902
2560
  if (!transaction) {
2903
- self[MUTATE]('replace cell', [index, original, newIdentifier]);
2904
- addToTransaction(_TAG);
2561
+ self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
2562
+ } else {
2563
+ target[index] = newIdentifier;
2905
2564
  }
2906
2565
  return true;
2907
2566
  },
@@ -2916,12 +2575,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2916
2575
  return IdentifierArray.prototype;
2917
2576
  }
2918
2577
  });
2919
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2920
- const meta = Ember.meta(this);
2921
- meta.addMixin = mixin => {
2922
- assert(`Do not call A() on EmberData RecordArrays`);
2923
- };
2924
- }
2578
+ createArrayTags(proxy, _SIGNAL);
2925
2579
  this[NOTIFY] = this[NOTIFY].bind(proxy);
2926
2580
  return proxy;
2927
2581
  }
@@ -2946,7 +2600,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2946
2600
  return this._updatingPromise;
2947
2601
  }
2948
2602
  this.isUpdating = true;
2949
- let updatingPromise = this._update();
2603
+ const updatingPromise = this._update();
2950
2604
  void updatingPromise.finally(() => {
2951
2605
  this._updatingPromise = null;
2952
2606
  if (this.isDestroying || this.isDestroyed) {
@@ -2985,17 +2639,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2985
2639
  @return {Promise<IdentifierArray>} promise
2986
2640
  */
2987
2641
  save() {
2988
- let promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2642
+ const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2989
2643
  return promise;
2990
2644
  }
2991
- }, (_descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "isUpdating", [tracked], {
2992
- configurable: true,
2645
+ }, _applyDecoratedDescriptor(_class.prototype, "length", [compat], Object.getOwnPropertyDescriptor(_class.prototype, "length"), _class.prototype), _class); // this will error if someone tries to call
2646
+ // A(identifierArray) since it is not configurable
2647
+ // which is preferable to the `meta` override we used
2648
+ // before which required importing all of Ember
2649
+ const desc = {
2993
2650
  enumerable: true,
2994
- writable: true,
2995
- initializer: function () {
2996
- return false;
2651
+ configurable: false,
2652
+ get: function () {
2653
+ return this;
2997
2654
  }
2998
- }), _applyDecoratedDescriptor(_class3.prototype, "length", [dependentKeyCompat], Object.getOwnPropertyDescriptor(_class3.prototype, "length"), _class3.prototype)), _class3);
2655
+ };
2656
+ compat(desc);
2657
+ Object.defineProperty(IdentifierArray.prototype, '[]', desc);
2658
+ defineSignal(IdentifierArray.prototype, 'isUpdating', false);
2999
2659
  class Collection extends IdentifierArray {
3000
2660
  constructor(options) {
3001
2661
  super(options);
@@ -3027,7 +2687,8 @@ class Collection extends IdentifierArray {
3027
2687
  Collection.prototype.query = null;
3028
2688
 
3029
2689
  // Ensure instanceof works correctly
3030
- //Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2690
+ // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2691
+
3031
2692
  function assertRecordPassedToHasMany(record) {
3032
2693
  assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
3033
2694
  try {
@@ -3049,7 +2710,6 @@ function extractIdentifierFromRecord$1(record) {
3049
2710
  /**
3050
2711
  @module @ember-data/store
3051
2712
  */
3052
-
3053
2713
  const FAKE_ARR = {};
3054
2714
  const SLICE_BATCH_SIZE = 1200;
3055
2715
  /**
@@ -3092,7 +2752,7 @@ const SLICE_BATCH_SIZE = 1200;
3092
2752
  */
3093
2753
  function fastPush(target, source) {
3094
2754
  let startLength = 0;
3095
- let newLength = source.length;
2755
+ const newLength = source.length;
3096
2756
  while (newLength - startLength > SLICE_BATCH_SIZE) {
3097
2757
  // eslint-disable-next-line prefer-spread
3098
2758
  target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
@@ -3149,8 +2809,8 @@ class RecordArrayManager {
3149
2809
  */
3150
2810
  liveArrayFor(type) {
3151
2811
  let array = this._live.get(type);
3152
- let identifiers = [];
3153
- let staged = this._staged.get(type);
2812
+ const identifiers = [];
2813
+ const staged = this._staged.get(type);
3154
2814
  if (staged) {
3155
2815
  staged.forEach((value, key) => {
3156
2816
  if (value === 'add') {
@@ -3173,7 +2833,7 @@ class RecordArrayManager {
3173
2833
  return array;
3174
2834
  }
3175
2835
  createArray(config) {
3176
- let options = {
2836
+ const options = {
3177
2837
  type: config.type,
3178
2838
  links: config.doc?.links || null,
3179
2839
  meta: config.doc?.meta || null,
@@ -3184,7 +2844,7 @@ class RecordArrayManager {
3184
2844
  store: this.store,
3185
2845
  manager: this
3186
2846
  };
3187
- let array = new Collection(options);
2847
+ const array = new Collection(options);
3188
2848
  this._managed.add(array);
3189
2849
  this._set.set(array, new Set(options.identifiers || []));
3190
2850
  if (config.identifiers) {
@@ -3196,7 +2856,7 @@ class RecordArrayManager {
3196
2856
  if (array === FAKE_ARR) {
3197
2857
  return;
3198
2858
  }
3199
- let tag = array[IDENTIFIER_ARRAY_TAG];
2859
+ const tag = array[ARRAY_SIGNAL];
3200
2860
  if (!tag.shouldReset) {
3201
2861
  tag.shouldReset = true;
3202
2862
  addTransactionCB(array[NOTIFY]);
@@ -3208,11 +2868,11 @@ class RecordArrayManager {
3208
2868
  if (this.isDestroying || this.isDestroyed) {
3209
2869
  return;
3210
2870
  }
3211
- let liveArray = this._live.get(identifier.type);
2871
+ const liveArray = this._live.get(identifier.type);
3212
2872
  const allPending = this._pending;
3213
- let pending = new Map();
2873
+ const pending = new Map();
3214
2874
  if (includeManaged) {
3215
- let managed = this._identifiers.get(identifier);
2875
+ const managed = this._identifiers.get(identifier);
3216
2876
  if (managed) {
3217
2877
  managed.forEach(arr => {
3218
2878
  let changes = allPending.get(arr);
@@ -3267,10 +2927,10 @@ class RecordArrayManager {
3267
2927
  associate(this._identifiers, array, identifiers);
3268
2928
  }
3269
2929
  identifierAdded(identifier) {
3270
- let changeSets = this._getPendingFor(identifier, false);
2930
+ const changeSets = this._getPendingFor(identifier, false);
3271
2931
  if (changeSets) {
3272
2932
  changeSets.forEach((changes, array) => {
3273
- let existing = changes.get(identifier);
2933
+ const existing = changes.get(identifier);
3274
2934
  if (existing === 'del') {
3275
2935
  changes.delete(identifier);
3276
2936
  } else {
@@ -3281,10 +2941,10 @@ class RecordArrayManager {
3281
2941
  }
3282
2942
  }
3283
2943
  identifierRemoved(identifier) {
3284
- let changeSets = this._getPendingFor(identifier, true, true);
2944
+ const changeSets = this._getPendingFor(identifier, true, true);
3285
2945
  if (changeSets) {
3286
2946
  changeSets.forEach((changes, array) => {
3287
- let existing = changes.get(identifier);
2947
+ const existing = changes.get(identifier);
3288
2948
  if (existing === 'add') {
3289
2949
  changes.delete(identifier);
3290
2950
  } else {
@@ -3295,7 +2955,7 @@ class RecordArrayManager {
3295
2955
  }
3296
2956
  }
3297
2957
  identifierChanged(identifier) {
3298
- let newState = this.store._instanceCache.recordIsLoaded(identifier, true);
2958
+ const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
3299
2959
 
3300
2960
  // if the change matches the most recent direct added/removed
3301
2961
  // state, then we can ignore it
@@ -3322,13 +2982,12 @@ class RecordArrayManager {
3322
2982
  this.clear(false);
3323
2983
  this._live.clear();
3324
2984
  this.isDestroyed = true;
3325
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
3326
2985
  this.store.notifications.unsubscribe(this._subscription);
3327
2986
  }
3328
2987
  }
3329
2988
  function associate(ArraysCache, array, identifiers) {
3330
2989
  for (let i = 0; i < identifiers.length; i++) {
3331
- let identifier = identifiers[i];
2990
+ const identifier = identifiers[i];
3332
2991
  let cache = ArraysCache.get(identifier);
3333
2992
  if (!cache) {
3334
2993
  cache = new Set();
@@ -3343,13 +3002,13 @@ function disassociate(ArraysCache, array, identifiers) {
3343
3002
  }
3344
3003
  }
3345
3004
  function disassociateIdentifier(ArraysCache, array, identifier) {
3346
- let cache = ArraysCache.get(identifier);
3005
+ const cache = ArraysCache.get(identifier);
3347
3006
  if (cache) {
3348
3007
  cache.delete(array);
3349
3008
  }
3350
3009
  }
3351
3010
  function sync(array, changes, arraySet) {
3352
- let state = array[SOURCE];
3011
+ const state = array[SOURCE];
3353
3012
  const adds = [];
3354
3013
  const removes = [];
3355
3014
  changes.forEach((value, key) => {
@@ -3363,13 +3022,13 @@ function sync(array, changes, arraySet) {
3363
3022
  } else {
3364
3023
  if (arraySet.has(key)) {
3365
3024
  removes.push(key);
3025
+ arraySet.delete(key);
3366
3026
  }
3367
3027
  }
3368
3028
  });
3369
3029
  if (removes.length) {
3370
3030
  if (removes.length === state.length) {
3371
3031
  state.length = 0;
3372
- arraySet.clear();
3373
3032
  // changing the reference breaks the Proxy
3374
3033
  // state = array[SOURCE] = [];
3375
3034
  } else {
@@ -3396,6 +3055,9 @@ function sync(array, changes, arraySet) {
3396
3055
  }
3397
3056
  }
3398
3057
 
3058
+ /**
3059
+ * @module @ember-data/store
3060
+ */
3399
3061
  const Touching = Symbol('touching');
3400
3062
  const RequestPromise = Symbol('promise');
3401
3063
  const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
@@ -3423,14 +3085,14 @@ class RequestStateService {
3423
3085
  this._done.delete(identifier);
3424
3086
  }
3425
3087
  _enqueue(promise, queryRequest) {
3426
- let query = queryRequest.data[0];
3088
+ const query = queryRequest.data[0];
3427
3089
  if (hasRecordIdentifier(query)) {
3428
3090
  const identifier = query.recordIdentifier;
3429
- let type = query.op === 'saveRecord' ? 'mutation' : 'query';
3091
+ const type = query.op === 'saveRecord' ? 'mutation' : 'query';
3430
3092
  if (!this._pending.has(identifier)) {
3431
3093
  this._pending.set(identifier, []);
3432
3094
  }
3433
- let request = {
3095
+ const request = {
3434
3096
  state: 'pending',
3435
3097
  request: queryRequest,
3436
3098
  type
@@ -3441,7 +3103,7 @@ class RequestStateService {
3441
3103
  this._triggerSubscriptions(request);
3442
3104
  return promise.then(result => {
3443
3105
  this._dequeue(identifier, request);
3444
- let finalizedRequest = {
3106
+ const finalizedRequest = {
3445
3107
  state: 'fulfilled',
3446
3108
  request: queryRequest,
3447
3109
  type,
@@ -3455,7 +3117,7 @@ class RequestStateService {
3455
3117
  return result;
3456
3118
  }, error => {
3457
3119
  this._dequeue(identifier, request);
3458
- let finalizedRequest = {
3120
+ const finalizedRequest = {
3459
3121
  state: 'rejected',
3460
3122
  request: queryRequest,
3461
3123
  type,
@@ -3504,7 +3166,7 @@ class RequestStateService {
3504
3166
  _addDone(request) {
3505
3167
  request[Touching].forEach(identifier => {
3506
3168
  // TODO add support for multiple
3507
- let requestDataOp = request.request.data[0].op;
3169
+ const requestDataOp = request.request.data[0].op;
3508
3170
  let requests = this._done.get(identifier);
3509
3171
  if (requests) {
3510
3172
  requests = requests.filter(req => {
@@ -3568,7 +3230,7 @@ class RequestStateService {
3568
3230
  * @method getPendingRequestsForRecord
3569
3231
  * @public
3570
3232
  * @param {StableRecordIdentifier} identifier
3571
- * @returns {RequestState[]} an array of request states for any pending requests for the given identifier
3233
+ * @return {RequestState[]} an array of request states for any pending requests for the given identifier
3572
3234
  */
3573
3235
  getPendingRequestsForRecord(identifier) {
3574
3236
  return this._pending.get(identifier) || EMPTY_ARR;
@@ -3580,10 +3242,10 @@ class RequestStateService {
3580
3242
  * @method getLastRequestForRecord
3581
3243
  * @public
3582
3244
  * @param {StableRecordIdentifier} identifier
3583
- * @returns {RequestState | null} the state of the most recent request for the given identifier
3245
+ * @return {RequestState | null} the state of the most recent request for the given identifier
3584
3246
  */
3585
3247
  getLastRequestForRecord(identifier) {
3586
- let requests = this._done.get(identifier);
3248
+ const requests = this._done.get(identifier);
3587
3249
  if (requests) {
3588
3250
  return requests[requests.length - 1];
3589
3251
  }
@@ -3595,7 +3257,7 @@ function isNonEmptyString(str) {
3595
3257
  }
3596
3258
  function constructResource(type, id, lid) {
3597
3259
  if (typeof type === 'object' && type !== null) {
3598
- let resource = type;
3260
+ const resource = type;
3599
3261
  if (isStableIdentifier(resource)) {
3600
3262
  return resource;
3601
3263
  }
@@ -3630,6 +3292,11 @@ function constructResource(type, id, lid) {
3630
3292
  }
3631
3293
  }
3632
3294
 
3295
+ /**
3296
+ @module @ember-data/store
3297
+ */
3298
+ // this import location is deprecated but breaks in 4.8 and older
3299
+
3633
3300
  /**
3634
3301
  * A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
3635
3302
  * and sources of data (such as your API or a local persistence layer)
@@ -3648,7 +3315,10 @@ function constructResource(type, id, lid) {
3648
3315
 
3649
3316
  @class Store
3650
3317
  @public
3651
- */ // @ts-expect-error
3318
+ */
3319
+
3320
+ // @ts-expect-error
3321
+
3652
3322
  class Store extends EmberObject {
3653
3323
  /**
3654
3324
  * Provides access to the NotificationManager associated
@@ -3780,8 +3450,6 @@ class Store extends EmberObject {
3780
3450
  // private
3781
3451
  this._requestCache = new RequestStateService(this);
3782
3452
  this._instanceCache = new InstanceCache(this);
3783
- this._adapterCache = Object.create(null);
3784
- this._serializerCache = Object.create(null);
3785
3453
  this._documentCache = new Map();
3786
3454
  this.isDestroying = false;
3787
3455
  this.isDestroyed = false;
@@ -3839,7 +3507,7 @@ class Store extends EmberObject {
3839
3507
  * that have been initiated for a given identifier.
3840
3508
  *
3841
3509
  * @method getRequestStateService
3842
- * @returns {RequestStateService}
3510
+ * @return {RequestStateService}
3843
3511
  * @public
3844
3512
  */
3845
3513
  getRequestStateService() {
@@ -3864,10 +3532,11 @@ class Store extends EmberObject {
3864
3532
  * inserting the response into the cache and handing
3865
3533
  * back a Future which resolves to a ResponseDocument
3866
3534
  *
3867
- * Resource data is always updated in the cache.
3535
+ * ## Cache Keys
3868
3536
  *
3869
- * Only GET requests have the request result and document
3870
- * cached by default when a cache key is present.
3537
+ * Only GET requests with a url or requests with an explicit
3538
+ * cache key (`cacheOptions.key`) will have the request result
3539
+ * and document cached.
3871
3540
  *
3872
3541
  * The cache key used is `requestConfig.cacheOptions.key`
3873
3542
  * if present, falling back to `requestconfig.url`.
@@ -3878,16 +3547,44 @@ class Store extends EmberObject {
3878
3547
  * via the `POST` method `requestConfig.cacheOptions.key`
3879
3548
  * MUST be supplied for the document to be cached.
3880
3549
  *
3550
+ * ## Requesting Without a Cache Key
3551
+ *
3552
+ * Resource data within the request is always updated in the cache,
3553
+ * regardless of whether a cache key is present for the request.
3554
+ *
3555
+ * ## Fulfilling From Cache
3556
+ *
3557
+ * When a cache-key is determined, the request may fulfill
3558
+ * from cache provided the cache is not stale.
3559
+ *
3560
+ * Cache staleness is determined by the configured LifetimesService
3561
+ * with priority given to the `cacheOptions.reload` and
3562
+ * `cacheOptions.backgroundReload` on the request if present.
3563
+ *
3564
+ * If the cache data has soft expired or the request asks for a background
3565
+ * reload, the request will fulfill from cache if possible and
3566
+ * make a non-blocking request in the background to update the cache.
3567
+ *
3568
+ * If the cache data has hard expired or the request asks for a reload,
3569
+ * the request will not fulfill from cache and will make a blocking
3570
+ * request to update the cache.
3571
+ *
3572
+ * ## The Response
3573
+ *
3574
+ * The primary difference between `requestManager.request` and `store.request`
3575
+ * is that `store.request` will attempt to hydrate the response content into
3576
+ * a response Document containing RecordInstances.
3577
+ *
3881
3578
  * @method request
3882
3579
  * @param {StoreRequestInput} requestConfig
3883
- * @returns {Future}
3580
+ * @return {Future}
3884
3581
  * @public
3885
3582
  */
3886
3583
  request(requestConfig) {
3887
3584
  // we lazily set the cache handler when we issue the first request
3888
3585
  // because constructor doesn't allow for this to run after
3889
3586
  // the user has had the chance to set the prop.
3890
- let opts = {
3587
+ const opts = {
3891
3588
  store: this,
3892
3589
  [EnableHydration]: true
3893
3590
  };
@@ -3939,7 +3636,7 @@ class Store extends EmberObject {
3939
3636
  * @param createRecordArgs
3940
3637
  * @param recordDataFor deprecated use this.cache
3941
3638
  * @param notificationManager deprecated use this.notifications
3942
- * @returns A record instance
3639
+ * @return A record instance
3943
3640
  * @public
3944
3641
  */
3945
3642
 
@@ -4148,8 +3845,8 @@ class Store extends EmberObject {
4148
3845
  // to remove this, we would need to move to a new `async` API.
4149
3846
  let record;
4150
3847
  this._join(() => {
4151
- let normalizedModelName = normalizeModelName(modelName);
4152
- let properties = {
3848
+ const normalizedModelName = normalizeModelName(modelName);
3849
+ const properties = {
4153
3850
  ...inputProperties
4154
3851
  };
4155
3852
 
@@ -4159,7 +3856,7 @@ class Store extends EmberObject {
4159
3856
  // to avoid conflicts.
4160
3857
 
4161
3858
  if (properties.id === null || properties.id === undefined) {
4162
- let adapter = this.adapterFor(modelName, true);
3859
+ const adapter = this.adapterFor?.(modelName, true);
4163
3860
  if (adapter && adapter.generateIdForRecord) {
4164
3861
  properties.id = adapter.generateIdForRecord(this, modelName, properties);
4165
3862
  } else {
@@ -4291,8 +3988,7 @@ class Store extends EmberObject {
4291
3988
  In your adapter you can then access this id without triggering a network request via the
4292
3989
  snapshot:
4293
3990
  ```app/adapters/application.js
4294
- import EmberObject from '@ember/object';
4295
- export default class Adapter extends EmberObject {
3991
+ export default class Adapter {
4296
3992
  findRecord(store, schema, id, snapshot) {
4297
3993
  let type = schema.modelName;
4298
3994
  if (type === 'comment')
@@ -4301,6 +3997,9 @@ class Store extends EmberObject {
4301
3997
  .then(response => response.json())
4302
3998
  }
4303
3999
  }
4000
+ static create() {
4001
+ return new this();
4002
+ }
4304
4003
  }
4305
4004
  ```
4306
4005
  This could also be achieved by supplying the post id to the adapter via the adapterOptions
@@ -4314,9 +4013,8 @@ class Store extends EmberObject {
4314
4013
  }
4315
4014
  ```
4316
4015
  ```app/adapters/application.js
4317
- import EmberObject from '@ember/object';
4318
- export default class Adapter extends EmberObject {
4319
- findRecord(store, schema, id, snapshot) {
4016
+ export default class Adapter {
4017
+ findRecord(store, schema, id, snapshot) {
4320
4018
  let type = schema.modelName;
4321
4019
  if (type === 'comment')
4322
4020
  let postId = snapshot.adapterOptions.post;
@@ -4324,6 +4022,9 @@ class Store extends EmberObject {
4324
4022
  .then(response => response.json())
4325
4023
  }
4326
4024
  }
4025
+ static create() {
4026
+ return new this();
4027
+ }
4327
4028
  }
4328
4029
  ```
4329
4030
  If you have access to the post model you can also pass the model itself to preload:
@@ -4450,9 +4151,8 @@ class Store extends EmberObject {
4450
4151
  }
4451
4152
  ```
4452
4153
  ```app/adapters/application.js
4453
- import EmberObject from '@ember/object';
4454
- export default class Adapter extends EmberObject {
4455
- findRecord(store, schema, id, snapshot) {
4154
+ export default class Adapter {
4155
+ findRecord(store, schema, id, snapshot) {
4456
4156
  let type = schema.modelName;
4457
4157
  if (type === 'post')
4458
4158
  let includes = snapshot.adapterOptions.include;
@@ -4460,6 +4160,9 @@ class Store extends EmberObject {
4460
4160
  .then(response => response.json())
4461
4161
  }
4462
4162
  }
4163
+ static create() {
4164
+ return new this();
4165
+ }
4463
4166
  }
4464
4167
  ```
4465
4168
  In this case, the post's comments would then be available in your template as
@@ -4603,7 +4306,7 @@ class Store extends EmberObject {
4603
4306
  resourceIdentifier = constructResource(type, normalizedId);
4604
4307
  }
4605
4308
  assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
4606
- let identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4309
+ const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4607
4310
  return this._instanceCache.getReference(identifier);
4608
4311
  }
4609
4312
 
@@ -5022,7 +4725,7 @@ class Store extends EmberObject {
5022
4725
  }
5023
4726
  assert(`You need to pass a model name to the store's peekAll method`, modelName);
5024
4727
  assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5025
- let type = normalizeModelName(modelName);
4728
+ const type = normalizeModelName(modelName);
5026
4729
  return this.recordArrayManager.liveArrayFor(type);
5027
4730
  }
5028
4731
 
@@ -5052,7 +4755,7 @@ class Store extends EmberObject {
5052
4755
  this.recordArrayManager.clear();
5053
4756
  this._instanceCache.clear();
5054
4757
  } else {
5055
- let normalizedModelName = normalizeModelName(modelName);
4758
+ const normalizedModelName = normalizeModelName(modelName);
5056
4759
  this._instanceCache.clear(normalizedModelName);
5057
4760
  }
5058
4761
  });
@@ -5193,422 +4896,712 @@ class Store extends EmberObject {
5193
4896
  if (macroCondition(getOwnConfig().env.DEBUG)) {
5194
4897
  assertDestroyingStore(this, 'push');
5195
4898
  }
5196
- let pushed = this._push(data, false);
4899
+ const pushed = this._push(data, false);
5197
4900
  if (Array.isArray(pushed)) {
5198
- let records = pushed.map(identifier => this._instanceCache.getRecord(identifier));
5199
- return records;
4901
+ return pushed.map(identifier => this._instanceCache.getRecord(identifier));
5200
4902
  }
5201
4903
  if (pushed === null) {
5202
4904
  return null;
5203
4905
  }
5204
4906
  return this._instanceCache.getRecord(pushed);
5205
4907
  }
5206
-
4908
+
4909
+ /**
4910
+ Push some data in the form of a json-api document into the store,
4911
+ without creating materialized records.
4912
+ @method _push
4913
+ @private
4914
+ @param {Object} jsonApiDoc
4915
+ @return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
4916
+ */
4917
+ _push(jsonApiDoc, asyncFlush) {
4918
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
4919
+ assertDestroyingStore(this, '_push');
4920
+ }
4921
+ if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
4922
+ try {
4923
+ const data = JSON.parse(JSON.stringify(jsonApiDoc));
4924
+ // eslint-disable-next-line no-console
4925
+ console.log('EmberData | Payload - push', data);
4926
+ } catch (e) {
4927
+ // eslint-disable-next-line no-console
4928
+ console.log('EmberData | Payload - push', jsonApiDoc);
4929
+ }
4930
+ }
4931
+ if (asyncFlush) {
4932
+ this._enableAsyncFlush = true;
4933
+ }
4934
+ let ret;
4935
+ this._join(() => {
4936
+ ret = this.cache.put({
4937
+ content: jsonApiDoc
4938
+ });
4939
+ });
4940
+ this._enableAsyncFlush = null;
4941
+ return 'data' in ret ? ret.data : null;
4942
+ }
4943
+
4944
+ /**
4945
+ * Trigger a save for a Record.
4946
+ *
4947
+ * @method saveRecord
4948
+ * @public
4949
+ * @param {RecordInstance} record
4950
+ * @param options
4951
+ * @return {Promise<RecordInstance>}
4952
+ */
4953
+ saveRecord(record, options = {}) {
4954
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
4955
+ assertDestroyingStore(this, 'saveRecord');
4956
+ }
4957
+ assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
4958
+ const identifier = recordIdentifierFor(record);
4959
+ const cache = this.cache;
4960
+ if (!identifier) {
4961
+ // this commonly means we're disconnected
4962
+ // but just in case we reject here to prevent bad things.
4963
+ return Promise.reject(new Error(`Record Is Disconnected`));
4964
+ }
4965
+ // TODO we used to check if the record was destroyed here
4966
+ assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
4967
+ if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
4968
+ return Promise.resolve(record);
4969
+ }
4970
+ if (!options) {
4971
+ options = {};
4972
+ }
4973
+ let operation = 'updateRecord';
4974
+ if (cache.isNew(identifier)) {
4975
+ operation = 'createRecord';
4976
+ } else if (cache.isDeleted(identifier)) {
4977
+ operation = 'deleteRecord';
4978
+ }
4979
+ const request = {
4980
+ op: operation,
4981
+ data: {
4982
+ options,
4983
+ record: identifier
4984
+ },
4985
+ records: [identifier],
4986
+ cacheOptions: {
4987
+ [SkipCache]: true
4988
+ }
4989
+ };
4990
+
4991
+ // we lie here on the type because legacy doesn't have enough context
4992
+ cache.willCommit(identifier, {
4993
+ request
4994
+ });
4995
+ return this.request(request).then(document => document.content);
4996
+ }
4997
+
4998
+ /**
4999
+ * Instantiation hook allowing applications or addons to configure the store
5000
+ * to utilize a custom Cache implementation.
5001
+ *
5002
+ * This hook should not be called directly by consuming applications or libraries.
5003
+ * Use `Store.cache` to access the Cache instance.
5004
+ *
5005
+ * @method createCache (hook)
5006
+ * @public
5007
+ * @param storeWrapper
5008
+ * @return {Cache}
5009
+ */
5010
+
5011
+ /**
5012
+ * Returns the cache instance associated to this Store, instantiates the Cache
5013
+ * if necessary via `Store.createCache`
5014
+ *
5015
+ * @property {Cache} cache
5016
+ * @public
5017
+ */
5018
+ get cache() {
5019
+ let {
5020
+ cache
5021
+ } = this._instanceCache;
5022
+ if (!cache) {
5023
+ cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5024
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5025
+ cache = new CacheManager(cache);
5026
+ }
5027
+ }
5028
+ return cache;
5029
+ }
5030
+
5031
+ // @ts-expect-error
5032
+ destroy() {
5033
+ if (this.isDestroyed) {
5034
+ // @ember/test-helpers will call destroy multiple times
5035
+ return;
5036
+ }
5037
+ this.isDestroying = true;
5038
+ this._graph?.destroy();
5039
+ this._graph = undefined;
5040
+ this.notifications.destroy();
5041
+ this.recordArrayManager.destroy();
5042
+ this.identifierCache.destroy();
5043
+ this.unloadAll();
5044
+ this.isDestroyed = true;
5045
+ }
5046
+ static create(args) {
5047
+ return new this(args);
5048
+ }
5049
+ }
5050
+ let assertDestroyingStore;
5051
+ let assertDestroyedStoreOnly;
5052
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5053
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5054
+ assertDestroyingStore = function assertDestroyingStore(store, method) {
5055
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5056
+ };
5057
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5058
+ assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5059
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5060
+ };
5061
+ }
5062
+ function isMaybeIdentifier(maybeIdentifier) {
5063
+ return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5064
+ }
5065
+ function normalizeProperties(store, identifier, properties) {
5066
+ // assert here
5067
+ if (properties !== undefined) {
5068
+ if ('id' in properties) {
5069
+ assert(`expected id to be a string or null`, properties.id !== undefined);
5070
+ }
5071
+ assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5072
+ const {
5073
+ type
5074
+ } = identifier;
5075
+
5076
+ // convert relationship Records to RecordDatas before passing to RecordData
5077
+ const defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5078
+ type
5079
+ });
5080
+ if (defs !== null) {
5081
+ const keys = Object.keys(properties);
5082
+ let relationshipValue;
5083
+ for (let i = 0; i < keys.length; i++) {
5084
+ const prop = keys[i];
5085
+ const def = defs[prop];
5086
+ if (def !== undefined) {
5087
+ if (def.kind === 'hasMany') {
5088
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5089
+ assertRecordsPassedToHasMany(properties[prop]);
5090
+ }
5091
+ relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5092
+ } else {
5093
+ relationshipValue = extractIdentifierFromRecord(properties[prop]);
5094
+ }
5095
+ properties[prop] = relationshipValue;
5096
+ }
5097
+ }
5098
+ }
5099
+ }
5100
+ return properties;
5101
+ }
5102
+ function assertRecordsPassedToHasMany(records) {
5103
+ assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5104
+ assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5105
+ return records.every(record => {
5106
+ try {
5107
+ recordIdentifierFor(record);
5108
+ return true;
5109
+ } catch {
5110
+ return false;
5111
+ }
5112
+ });
5113
+ }());
5114
+ }
5115
+ function extractIdentifiersFromRecords(records) {
5116
+ return records.map(record => extractIdentifierFromRecord(record));
5117
+ }
5118
+ function extractIdentifierFromRecord(recordOrPromiseRecord) {
5119
+ if (!recordOrPromiseRecord) {
5120
+ return null;
5121
+ }
5122
+ const extract = recordIdentifierFor;
5123
+ return extract(recordOrPromiseRecord);
5124
+ }
5125
+ function urlFromLink(link) {
5126
+ if (typeof link === 'string') return link;
5127
+ return link.href;
5128
+ }
5129
+
5130
+ /**
5131
+ * A Document is a class that wraps the response content from a request to the API
5132
+ * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
5133
+ * record instances.
5134
+ *
5135
+ * It is not directly instantiated by the user, and its properties should not
5136
+ * be directly modified. Whether individual properties are mutable or not is
5137
+ * determined by the record instance itself.
5138
+ *
5139
+ * @public
5140
+ * @class Document
5141
+ */
5142
+ var _store = /*#__PURE__*/_classPrivateFieldKey("store");
5143
+ var _request = /*#__PURE__*/_classPrivateFieldKey("request");
5144
+ class Document {
5145
+ constructor(store, identifier) {
5146
+ Object.defineProperty(this, _request, {
5147
+ value: _request2
5148
+ });
5149
+ /**
5150
+ * The links object for this document, if any
5151
+ *
5152
+ * e.g.
5153
+ *
5154
+ * ```
5155
+ * {
5156
+ * self: '/articles?page[number]=3',
5157
+ * }
5158
+ * ```
5159
+ *
5160
+ * @property links
5161
+ * @type {object|undefined} - a links object
5162
+ * @public
5163
+ */
5164
+ /**
5165
+ * The primary data for this document, if any.
5166
+ *
5167
+ * If this document has no primary data (e.g. because it is an error document)
5168
+ * this property will be `undefined`.
5169
+ *
5170
+ * For collections this will be an array of record instances,
5171
+ * for single resource requests it will be a single record instance or null.
5172
+ *
5173
+ * @property data
5174
+ * @public
5175
+ * @type {object|Array<object>|null|undefined} - a data object
5176
+ */
5177
+ /**
5178
+ * The errors returned by the API for this request, if any
5179
+ *
5180
+ * @property errors
5181
+ * @public
5182
+ * @type {object|undefined} - an errors object
5183
+ */
5184
+ /**
5185
+ * The meta object for this document, if any
5186
+ *
5187
+ * @property meta
5188
+ * @public
5189
+ * @type {object|undefined} - a meta object
5190
+ */
5191
+ /**
5192
+ * The identifier associated with this document, if any
5193
+ *
5194
+ * @property identifier
5195
+ * @public
5196
+ * @type {StableDocumentIdentifier|null}
5197
+ */
5198
+ Object.defineProperty(this, _store, {
5199
+ writable: true,
5200
+ value: void 0
5201
+ });
5202
+ _classPrivateFieldBase(this, _store)[_store] = store;
5203
+ this.identifier = identifier;
5204
+ }
5207
5205
  /**
5208
- Push some data in the form of a json-api document into the store,
5209
- without creating materialized records.
5210
- @method _push
5211
- @private
5212
- @param {Object} jsonApiDoc
5213
- @return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
5214
- */
5215
- _push(jsonApiDoc, asyncFlush) {
5216
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5217
- assertDestroyingStore(this, '_push');
5218
- }
5219
- if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
5220
- try {
5221
- let data = JSON.parse(JSON.stringify(jsonApiDoc));
5222
- // eslint-disable-next-line no-console
5223
- console.log('EmberData | Payload - push', data);
5224
- } catch (e) {
5225
- // eslint-disable-next-line no-console
5226
- console.log('EmberData | Payload - push', jsonApiDoc);
5227
- }
5228
- }
5229
- if (asyncFlush) {
5230
- this._enableAsyncFlush = true;
5231
- }
5232
- let ret;
5233
- this._join(() => {
5234
- ret = this.cache.put({
5235
- content: jsonApiDoc
5236
- });
5237
- });
5238
- this._enableAsyncFlush = null;
5239
- return 'data' in ret ? ret.data : null;
5206
+ * Fetches the related link for this document, returning a promise that resolves
5207
+ * with the document when the request completes. If no related link is present,
5208
+ * will fallback to the self link if present
5209
+ *
5210
+ * @method fetch
5211
+ * @public
5212
+ * @param {object} options
5213
+ * @return Promise<Document>
5214
+ */
5215
+ fetch(options = {}) {
5216
+ assert(`No self or related link`, this.links?.related || this.links?.self);
5217
+ options.cacheOptions = options.cacheOptions || {};
5218
+ options.cacheOptions.key = this.identifier?.lid;
5219
+ return _classPrivateFieldBase(this, _request)[_request](this.links.related ? 'related' : 'self', options);
5240
5220
  }
5241
5221
 
5242
5222
  /**
5243
- Push some raw data into the store.
5244
- This method can be used both to push in brand new
5245
- records, as well as to update existing records. You
5246
- can push in more than one type of object at once.
5247
- All objects should be in the format expected by the
5248
- serializer.
5249
- ```app/serializers/application.js
5250
- import RESTSerializer from '@ember-data/serializer/rest';
5251
- export default class ApplicationSerializer extends RESTSerializer;
5252
- ```
5253
- ```js
5254
- let pushData = {
5255
- posts: [
5256
- { id: 1, postTitle: "Great post", commentIds: [2] }
5257
- ],
5258
- comments: [
5259
- { id: 2, commentBody: "Insightful comment" }
5260
- ]
5261
- }
5262
- store.pushPayload(pushData);
5263
- ```
5264
- By default, the data will be deserialized using a default
5265
- serializer (the application serializer if it exists).
5266
- Alternatively, `pushPayload` will accept a model type which
5267
- will determine which serializer will process the payload.
5268
- ```app/serializers/application.js
5269
- import RESTSerializer from '@ember-data/serializer/rest';
5270
- export default class ApplicationSerializer extends RESTSerializer;
5271
- ```
5272
- ```app/serializers/post.js
5273
- import JSONSerializer from '@ember-data/serializer/json';
5274
- export default JSONSerializer;
5275
- ```
5276
- ```js
5277
- store.pushPayload(pushData); // Will use the application serializer
5278
- store.pushPayload('post', pushData); // Will use the post serializer
5279
- ```
5280
- @method pushPayload
5281
- @public
5282
- @param {String} modelName Optionally, a model type used to determine which serializer will be used
5283
- @param {Object} inputPayload
5284
- */
5285
- // TODO @runspired @deprecate pushPayload in favor of looking up the serializer
5286
- pushPayload(modelName, inputPayload) {
5287
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5288
- assertDestroyingStore(this, 'pushPayload');
5289
- }
5290
- const payload = inputPayload || modelName;
5291
- const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
5292
- const serializer = this.serializerFor(normalizedModelName);
5293
- assert(`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`, serializer && typeof serializer.pushPayload === 'function');
5294
- serializer.pushPayload(this, payload);
5223
+ * Fetches the next link for this document, returning a promise that resolves
5224
+ * with the new document when the request completes, or null if there is no
5225
+ * next link.
5226
+ *
5227
+ * @method next
5228
+ * @public
5229
+ * @param {object} options
5230
+ * @return Promise<Document | null>
5231
+ */
5232
+ next(options = {}) {
5233
+ return _classPrivateFieldBase(this, _request)[_request]('next', options);
5295
5234
  }
5296
5235
 
5297
5236
  /**
5298
- * Trigger a save for a Record.
5237
+ * Fetches the prev link for this document, returning a promise that resolves
5238
+ * with the new document when the request completes, or null if there is no
5239
+ * prev link.
5299
5240
  *
5300
- * @method saveRecord
5241
+ * @method prev
5301
5242
  * @public
5302
- * @param {RecordInstance} record
5303
- * @param options
5304
- * @returns {Promise<RecordInstance>}
5243
+ * @param {object} options
5244
+ * @return Promise<Document | null>
5305
5245
  */
5306
- saveRecord(record, options = {}) {
5307
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5308
- assertDestroyingStore(this, 'saveRecord');
5309
- }
5310
- assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
5311
- let identifier = recordIdentifierFor(record);
5312
- const cache = this.cache;
5313
- if (!identifier) {
5314
- // this commonly means we're disconnected
5315
- // but just in case we reject here to prevent bad things.
5316
- return Promise.reject(`Record Is Disconnected`);
5317
- }
5318
- // TODO we used to check if the record was destroyed here
5319
- assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
5320
- if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5321
- return Promise.resolve(record);
5322
- }
5323
- if (!options) {
5324
- options = {};
5325
- }
5326
- let operation = 'updateRecord';
5327
- if (cache.isNew(identifier)) {
5328
- operation = 'createRecord';
5329
- } else if (cache.isDeleted(identifier)) {
5330
- operation = 'deleteRecord';
5331
- }
5332
- const request = {
5333
- op: operation,
5334
- data: {
5335
- options,
5336
- record: identifier
5337
- },
5338
- cacheOptions: {
5339
- [SkipCache]: true
5340
- }
5341
- };
5342
-
5343
- // we lie here on the type because legacy doesn't have enough context
5344
- cache.willCommit(identifier, {
5345
- request
5346
- });
5347
- return this.request(request).then(document => document.content);
5246
+ prev(options = {}) {
5247
+ return _classPrivateFieldBase(this, _request)[_request]('prev', options);
5348
5248
  }
5349
5249
 
5350
5250
  /**
5351
- * Instantiation hook allowing applications or addons to configure the store
5352
- * to utilize a custom Cache implementation.
5251
+ * Fetches the first link for this document, returning a promise that resolves
5252
+ * with the new document when the request completes, or null if there is no
5253
+ * first link.
5353
5254
  *
5354
- * This hook should not be called directly by consuming applications or libraries.
5355
- * Use `Store.cache` to access the Cache instance.
5255
+ * @method first
5256
+ * @public
5257
+ * @param {object} options
5258
+ * @return Promise<Document | null>
5259
+ */
5260
+ first(options = {}) {
5261
+ return _classPrivateFieldBase(this, _request)[_request]('first', options);
5262
+ }
5263
+
5264
+ /**
5265
+ * Fetches the last link for this document, returning a promise that resolves
5266
+ * with the new document when the request completes, or null if there is no
5267
+ * last link.
5356
5268
  *
5357
- * @method createCache (hook)
5269
+ * @method last
5358
5270
  * @public
5359
- * @param storeWrapper
5360
- * @returns {Cache}
5271
+ * @param {object} options
5272
+ * @return Promise<Document | null>
5361
5273
  */
5274
+ last(options = {}) {
5275
+ return _classPrivateFieldBase(this, _request)[_request]('last', options);
5276
+ }
5362
5277
 
5363
5278
  /**
5364
- * Returns the cache instance associated to this Store, instantiates the Cache
5365
- * if necessary via `Store.createCache`
5279
+ * Implemented for `JSON.stringify` support.
5366
5280
  *
5367
- * @property {Cache} cache
5281
+ * Returns the JSON representation of the document wrapper.
5282
+ *
5283
+ * This is a shallow serialization, it does not deeply serialize
5284
+ * the document's contents, leaving that to the individual record
5285
+ * instances to determine how to do, if at all.
5286
+ *
5287
+ * @method toJSON
5368
5288
  * @public
5289
+ * @return
5369
5290
  */
5370
- get cache() {
5371
- let {
5372
- cache
5373
- } = this._instanceCache;
5374
- if (!cache) {
5375
- cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5376
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5377
- cache = new CacheManager(cache);
5291
+ toJSON() {
5292
+ const data = {};
5293
+ data.identifier = this.identifier;
5294
+ if (this.data !== undefined) {
5295
+ data.data = this.data;
5296
+ }
5297
+ if (this.links !== undefined) {
5298
+ data.links = this.links;
5299
+ }
5300
+ if (this.errors !== undefined) {
5301
+ data.errors = this.errors;
5302
+ }
5303
+ if (this.meta !== undefined) {
5304
+ data.meta = this.meta;
5305
+ }
5306
+ return data;
5307
+ }
5308
+ }
5309
+ async function _request2(link, options) {
5310
+ const href = this.links?.[link];
5311
+ if (!href) {
5312
+ return null;
5313
+ }
5314
+ options.method = options.method || 'GET';
5315
+ const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
5316
+ url: urlFromLink(href)
5317
+ }));
5318
+ return response.content;
5319
+ }
5320
+ defineSignal(Document.prototype, 'data');
5321
+ defineSignal(Document.prototype, 'links');
5322
+ defineSignal(Document.prototype, 'errors');
5323
+ defineSignal(Document.prototype, 'meta');
5324
+
5325
+ /**
5326
+ * @module @ember-data/store
5327
+ */
5328
+
5329
+ /**
5330
+ * A service which an application may provide to the store via
5331
+ * the store's `lifetimes` property to configure the behavior
5332
+ * of the CacheHandler.
5333
+ *
5334
+ * The default behavior for request lifetimes is to never expire
5335
+ * unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
5336
+ *
5337
+ * Implementing this service allows you to programatically define
5338
+ * when a request should be considered expired.
5339
+ *
5340
+ * @class <Interface> LifetimesService
5341
+ * @public
5342
+ */
5343
+
5344
+ const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
5345
+ function isErrorDocument(document) {
5346
+ return 'errors' in document;
5347
+ }
5348
+ function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
5349
+ const {
5350
+ identifier
5351
+ } = options;
5352
+ if (isErrorDocument(document)) {
5353
+ if (!identifier && !options.shouldHydrate) {
5354
+ return document;
5355
+ }
5356
+ let doc;
5357
+ if (identifier) {
5358
+ doc = store._documentCache.get(identifier);
5359
+ }
5360
+ if (!doc) {
5361
+ doc = new Document(store, identifier);
5362
+ copyDocumentProperties(doc, document);
5363
+ if (identifier) {
5364
+ store._documentCache.set(identifier, doc);
5365
+ }
5366
+ } else if (!isFromCache) {
5367
+ doc.data = undefined;
5368
+ copyDocumentProperties(doc, document);
5369
+ }
5370
+ return options.shouldHydrate ? doc : document;
5371
+ }
5372
+ if (Array.isArray(document.data)) {
5373
+ const {
5374
+ recordArrayManager
5375
+ } = store;
5376
+ if (!identifier) {
5377
+ if (!options.shouldHydrate) {
5378
+ return document;
5379
+ }
5380
+ const data = recordArrayManager.createArray({
5381
+ type: request.url,
5382
+ identifiers: document.data,
5383
+ doc: document,
5384
+ query: request
5385
+ });
5386
+ const doc = new Document(store, null);
5387
+ doc.data = data;
5388
+ doc.meta = document.meta;
5389
+ doc.links = document.links;
5390
+ return doc;
5391
+ }
5392
+ let managed = recordArrayManager._keyedArrays.get(identifier.lid);
5393
+ if (!managed) {
5394
+ managed = recordArrayManager.createArray({
5395
+ type: identifier.lid,
5396
+ identifiers: document.data,
5397
+ doc: document
5398
+ });
5399
+ recordArrayManager._keyedArrays.set(identifier.lid, managed);
5400
+ const doc = new Document(store, identifier);
5401
+ doc.data = managed;
5402
+ doc.meta = document.meta;
5403
+ doc.links = document.links;
5404
+ store._documentCache.set(identifier, doc);
5405
+ return options.shouldHydrate ? doc : document;
5406
+ } else {
5407
+ const doc = store._documentCache.get(identifier);
5408
+ if (!isFromCache) {
5409
+ recordArrayManager.populateManagedArray(managed, document.data, document);
5410
+ doc.data = managed;
5411
+ doc.meta = document.meta;
5412
+ doc.links = document.links;
5413
+ }
5414
+ return options.shouldHydrate ? doc : document;
5415
+ }
5416
+ } else {
5417
+ if (!identifier && !options.shouldHydrate) {
5418
+ return document;
5419
+ }
5420
+ const data = document.data ? store.peekRecord(document.data) : null;
5421
+ let doc;
5422
+ if (identifier) {
5423
+ doc = store._documentCache.get(identifier);
5424
+ }
5425
+ if (!doc) {
5426
+ doc = new Document(store, identifier);
5427
+ doc.data = data;
5428
+ copyDocumentProperties(doc, document);
5429
+ if (identifier) {
5430
+ store._documentCache.set(identifier, doc);
5378
5431
  }
5432
+ } else if (!isFromCache) {
5433
+ doc.data = data;
5434
+ copyDocumentProperties(doc, document);
5379
5435
  }
5380
- return cache;
5436
+ return options.shouldHydrate ? doc : document;
5381
5437
  }
5382
-
5383
- /**
5384
- `normalize` converts a json payload into the normalized form that
5385
- [push](../methods/push?anchor=push) expects.
5386
- Example
5387
- ```js
5388
- socket.on('message', function(message) {
5389
- let modelName = message.model;
5390
- let data = message.data;
5391
- store.push(store.normalize(modelName, data));
5438
+ }
5439
+ function calcShouldFetch(store, request, hasCachedValue, identifier) {
5440
+ const {
5441
+ cacheOptions
5442
+ } = request;
5443
+ return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
5444
+ }
5445
+ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
5446
+ const {
5447
+ cacheOptions
5448
+ } = request;
5449
+ return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false));
5450
+ }
5451
+ function isMutation(request) {
5452
+ return Boolean(request.op && MUTATION_OPS.has(request.op));
5453
+ }
5454
+ function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
5455
+ const {
5456
+ store
5457
+ } = context.request;
5458
+ const shouldHydrate = context.request[EnableHydration] || false;
5459
+ let isMut = false;
5460
+ if (isMutation(context.request)) {
5461
+ isMut = true;
5462
+ // TODO should we handle multiple records in request.records by iteratively calling willCommit for each
5463
+ const record = context.request.data?.record || context.request.records?.[0];
5464
+ assert(`Expected to receive a list of records included in the ${context.request.op} request`, record);
5465
+ store.cache.willCommit(record, context);
5466
+ }
5467
+ if (store.lifetimes?.willRequest) {
5468
+ store.lifetimes.willRequest(context.request, identifier, store);
5469
+ }
5470
+ const promise = next(context.request).then(document => {
5471
+ store.requestManager._pending.delete(context.id);
5472
+ store._enableAsyncFlush = true;
5473
+ let response;
5474
+ store._join(() => {
5475
+ if (isMutation(context.request)) {
5476
+ const record = context.request.data?.record || context.request.records?.[0];
5477
+ response = store.cache.didCommit(record, document);
5478
+ } else {
5479
+ response = store.cache.put(document);
5480
+ }
5481
+ response = maybeUpdateUiObjects(store, context.request, {
5482
+ shouldHydrate,
5483
+ shouldFetch,
5484
+ shouldBackgroundFetch,
5485
+ identifier
5486
+ }, response, false);
5392
5487
  });
5393
- ```
5394
- @method normalize
5395
- @public
5396
- @param {String} modelName The name of the model type for this payload
5397
- @param {Object} payload
5398
- @return {Object} The normalized payload
5399
- */
5400
- // TODO @runspired @deprecate users should call normalize on the associated serializer directly
5401
- normalize(modelName, payload) {
5402
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5403
- assertDestroyingStore(this, 'normalize');
5488
+ store._enableAsyncFlush = null;
5489
+ if (store.lifetimes?.didRequest) {
5490
+ store.lifetimes.didRequest(context.request, document.response, identifier, store);
5404
5491
  }
5405
- assert(`You need to pass a model name to the store's normalize method`, modelName);
5406
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${typeof modelName}`, typeof modelName === 'string');
5407
- const normalizedModelName = normalizeModelName(modelName);
5408
- const serializer = this.serializerFor(normalizedModelName);
5409
- const schema = this.modelFor(normalizedModelName);
5410
- assert(`You must define a normalize method in your serializer in order to call store.normalize`, typeof serializer?.normalize === 'function');
5411
- return serializer.normalize(schema, payload);
5412
- }
5413
-
5414
- /**
5415
- Returns an instance of the adapter for a given type. For
5416
- example, `adapterFor('person')` will return an instance of
5417
- the adapter located at `app/adapters/person.js`
5418
- If no `person` adapter is found, this method will look
5419
- for an `application` adapter (the default adapter for
5420
- your entire application).
5421
- @method adapterFor
5422
- @public
5423
- @param {String} modelName
5424
- @return Adapter
5425
- */
5426
-
5427
- adapterFor(modelName, _allowMissing) {
5428
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5429
- assertDestroyingStore(this, 'adapterFor');
5492
+ if (shouldFetch) {
5493
+ return response;
5494
+ } else if (shouldBackgroundFetch) {
5495
+ store.notifications._flush();
5430
5496
  }
5431
- assert(`You need to pass a model name to the store's adapterFor method`, modelName);
5432
- assert(`Passing classes to store.adapterFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5433
- let normalizedModelName = normalizeModelName(modelName);
5434
- let {
5435
- _adapterCache
5436
- } = this;
5437
- let adapter = _adapterCache[normalizedModelName];
5438
- if (adapter) {
5439
- return adapter;
5497
+ }, error => {
5498
+ store.requestManager._pending.delete(context.id);
5499
+ if (context.request.signal?.aborted) {
5500
+ throw error;
5440
5501
  }
5441
- const owner = getOwner(this);
5442
-
5443
- // name specific adapter
5444
- adapter = owner.lookup(`adapter:${normalizedModelName}`);
5445
- if (adapter !== undefined) {
5446
- _adapterCache[normalizedModelName] = adapter;
5447
- return adapter;
5502
+ store.requestManager._pending.delete(context.id);
5503
+ store._enableAsyncFlush = true;
5504
+ let response;
5505
+ store._join(() => {
5506
+ if (isMutation(context.request)) {
5507
+ // TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
5508
+ // currently we let the response remain undefiend.
5509
+ const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
5510
+ const record = context.request.data?.record || context.request.records?.[0];
5511
+ store.cache.commitWasRejected(record, errors);
5512
+ // re-throw the original error to preserve `errors` property.
5513
+ throw error;
5514
+ } else {
5515
+ response = store.cache.put(error);
5516
+ response = maybeUpdateUiObjects(store, context.request, {
5517
+ shouldHydrate,
5518
+ shouldFetch,
5519
+ shouldBackgroundFetch,
5520
+ identifier
5521
+ }, response, false);
5522
+ }
5523
+ });
5524
+ store._enableAsyncFlush = null;
5525
+ if (identifier && store.lifetimes?.didRequest) {
5526
+ store.lifetimes.didRequest(context.request, error.response, identifier, store);
5448
5527
  }
5449
-
5450
- // no adapter found for the specific name, fallback and check for application adapter
5451
- adapter = _adapterCache.application || owner.lookup('adapter:application');
5452
- if (adapter !== undefined) {
5453
- _adapterCache[normalizedModelName] = adapter;
5454
- _adapterCache.application = adapter;
5455
- return adapter;
5528
+ if (!shouldBackgroundFetch) {
5529
+ const newError = cloneError(error);
5530
+ newError.content = response;
5531
+ throw newError;
5532
+ } else {
5533
+ store.notifications._flush();
5456
5534
  }
5457
- assert(`No adapter was found for '${modelName}' and no 'application' adapter was found as a fallback.`, _allowMissing);
5535
+ });
5536
+ if (!isMut) {
5537
+ return promise;
5458
5538
  }
5539
+ assert(`Expected a mutation`, isMutation(context.request));
5459
5540
 
5460
- /**
5461
- Returns an instance of the serializer for a given type. For
5462
- example, `serializerFor('person')` will return an instance of
5463
- `App.PersonSerializer`.
5464
- If no `App.PersonSerializer` is found, this method will look
5465
- for an `App.ApplicationSerializer` (the default serializer for
5466
- your entire application).
5467
- If a serializer cannot be found on the adapter, it will fall back
5468
- to an instance of `JSONSerializer`.
5469
- @method serializerFor
5470
- @public
5471
- @param {String} modelName the record to serialize
5472
- @return {Serializer}
5473
- */
5474
- serializerFor(modelName) {
5475
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5476
- assertDestroyingStore(this, 'serializerFor');
5477
- }
5478
- assert(`You need to pass a model name to the store's serializerFor method`, modelName);
5479
- assert(`Passing classes to store.serializerFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5480
- let normalizedModelName = normalizeModelName(modelName);
5481
- let {
5482
- _serializerCache
5483
- } = this;
5484
- let serializer = _serializerCache[normalizedModelName];
5485
- if (serializer) {
5486
- return serializer;
5487
- }
5488
-
5489
- // by name
5490
- const owner = getOwner(this);
5491
- serializer = owner.lookup(`serializer:${normalizedModelName}`);
5492
- if (serializer !== undefined) {
5493
- _serializerCache[normalizedModelName] = serializer;
5494
- return serializer;
5541
+ // for mutations we need to enqueue the promise with the requestStateService
5542
+ // TODO should we enque a request per record in records?
5543
+ const record = context.request.data?.record || context.request.records?.[0];
5544
+ return store._requestCache._enqueue(promise, {
5545
+ data: [{
5546
+ op: 'saveRecord',
5547
+ recordIdentifier: record,
5548
+ options: undefined
5549
+ }]
5550
+ });
5551
+ }
5552
+ function cloneError(error) {
5553
+ const cloned = new Error(error.message);
5554
+ cloned.stack = error.stack;
5555
+ cloned.error = error.error;
5556
+ return cloned;
5557
+ }
5558
+ const CacheHandler = {
5559
+ request(context, next) {
5560
+ // if we have no cache or no cache-key skip cache handling
5561
+ if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
5562
+ return next(context.request);
5495
5563
  }
5564
+ const {
5565
+ store
5566
+ } = context.request;
5567
+ const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
5568
+ const peeked = identifier ? store.cache.peekRequest(identifier) : null;
5496
5569
 
5497
- // no serializer found for the specific model, fallback and check for application serializer
5498
- serializer = _serializerCache.application || owner.lookup('serializer:application');
5499
- if (serializer !== undefined) {
5500
- _serializerCache[normalizedModelName] = serializer;
5501
- _serializerCache.application = serializer;
5502
- return serializer;
5570
+ // determine if we should skip cache
5571
+ if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
5572
+ return fetchContentAndHydrate(next, context, identifier, true, false);
5503
5573
  }
5504
- return null;
5505
- }
5506
5574
 
5507
- // @ts-expect-error
5508
- destroy() {
5509
- if (this.isDestroyed) {
5510
- // @ember/test-helpers will call destroy multiple times
5511
- return;
5512
- }
5513
- this.isDestroying = true;
5514
- // enqueue destruction of any adapters/serializers we have created
5515
- for (let adapterName in this._adapterCache) {
5516
- let adapter = this._adapterCache[adapterName];
5517
- if (typeof adapter.destroy === 'function') {
5518
- adapter.destroy();
5519
- }
5575
+ // if we have not skipped cache, determine if we should update behind the scenes
5576
+ if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
5577
+ const promise = fetchContentAndHydrate(next, context, identifier, false, true);
5578
+ store.requestManager._pending.set(context.id, promise);
5520
5579
  }
5521
- for (let serializerName in this._serializerCache) {
5522
- let serializer = this._serializerCache[serializerName];
5523
- if (typeof serializer.destroy === 'function') {
5524
- serializer.destroy();
5525
- }
5580
+ const shouldHydrate = context.request[EnableHydration] || false;
5581
+ if ('error' in peeked) {
5582
+ const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5583
+ shouldHydrate,
5584
+ identifier
5585
+ }, peeked.content, true) : peeked.content;
5586
+ const newError = cloneError(peeked);
5587
+ newError.content = content;
5588
+ throw newError;
5526
5589
  }
5527
- this._graph?.destroy();
5528
- this._graph = undefined;
5529
- this.notifications.destroy();
5530
- this.recordArrayManager.destroy();
5531
- this.identifierCache.destroy();
5532
- this.unloadAll();
5533
- this.isDestroyed = true;
5590
+ return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5591
+ shouldHydrate,
5592
+ identifier
5593
+ }, peeked.content, true) : peeked.content);
5534
5594
  }
5535
- static create(args) {
5536
- return new this(args);
5595
+ };
5596
+ function copyDocumentProperties(target, source) {
5597
+ if ('links' in source) {
5598
+ target.links = source.links;
5537
5599
  }
5538
- }
5539
- let assertDestroyingStore;
5540
- let assertDestroyedStoreOnly;
5541
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5542
- // eslint-disable-next-line @typescript-eslint/no-shadow
5543
- assertDestroyingStore = function assertDestroyingStore(store, method) {
5544
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5545
- };
5546
- // eslint-disable-next-line @typescript-eslint/no-shadow
5547
- assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5548
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5549
- };
5550
- }
5551
- function isMaybeIdentifier(maybeIdentifier) {
5552
- return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5553
- }
5554
- function normalizeProperties(store, identifier, properties) {
5555
- // assert here
5556
- if (properties !== undefined) {
5557
- if ('id' in properties) {
5558
- assert(`expected id to be a string or null`, properties.id !== undefined);
5559
- }
5560
- assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5561
- const {
5562
- type
5563
- } = identifier;
5564
-
5565
- // convert relationship Records to RecordDatas before passing to RecordData
5566
- let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5567
- type
5568
- });
5569
- if (defs !== null) {
5570
- let keys = Object.keys(properties);
5571
- let relationshipValue;
5572
- for (let i = 0; i < keys.length; i++) {
5573
- let prop = keys[i];
5574
- let def = defs[prop];
5575
- if (def !== undefined) {
5576
- if (def.kind === 'hasMany') {
5577
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5578
- assertRecordsPassedToHasMany(properties[prop]);
5579
- }
5580
- relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5581
- } else {
5582
- relationshipValue = extractIdentifierFromRecord(properties[prop]);
5583
- }
5584
- properties[prop] = relationshipValue;
5585
- }
5586
- }
5587
- }
5600
+ if ('meta' in source) {
5601
+ target.meta = source.meta;
5588
5602
  }
5589
- return properties;
5590
- }
5591
- function assertRecordsPassedToHasMany(records) {
5592
- assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5593
- assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5594
- return records.every(record => {
5595
- try {
5596
- recordIdentifierFor(record);
5597
- return true;
5598
- } catch {
5599
- return false;
5600
- }
5601
- });
5602
- }());
5603
- }
5604
- function extractIdentifiersFromRecords(records) {
5605
- return records.map(record => extractIdentifierFromRecord(record));
5606
- }
5607
- function extractIdentifierFromRecord(recordOrPromiseRecord) {
5608
- if (!recordOrPromiseRecord) {
5609
- return null;
5603
+ if ('errors' in source) {
5604
+ target.errors = source.errors;
5610
5605
  }
5611
- const extract = recordIdentifierFor;
5612
- return extract(recordOrPromiseRecord);
5613
5606
  }
5614
- 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, SOURCE as g, IDENTIFIER_ARRAY_TAG as h, isStableIdentifier as i, fastPush as j, removeRecordDataFor as k, setRecordIdentifier as l, StoreMap as m, notifyArray as n, setCacheFor as o, peekCache as p, recordIdentifierFor as r, storeFor as s };
5607
+ export { ARRAY_SIGNAL as A, 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, SOURCE as g, fastPush as h, isStableIdentifier as i, removeRecordDataFor as j, setRecordIdentifier as k, StoreMap as l, setCacheFor as m, notifyArray as n, normalizeModelName as o, peekCache as p, recordIdentifierFor as r, storeFor as s };