@ember-data/store 5.4.0-beta.0 → 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,19 +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
- // eslint-disable-next-line
2629
- dirtyTag(tagForProperty(arr, 'length'));
2630
- // eslint-disable-next-line
2631
- dirtyTag(tagForProperty(arr, '[]'));
2632
- }
2299
+ addToTransaction(arr[ARRAY_SIGNAL]);
2633
2300
  }
2634
2301
  function convertToInt(prop) {
2635
2302
  if (typeof prop === 'symbol') return null;
@@ -2637,29 +2304,6 @@ function convertToInt(prop) {
2637
2304
  if (isNaN(num)) return null;
2638
2305
  return num % 1 === 0 ? num : null;
2639
2306
  }
2640
- let Tag = (_class = class Tag {
2641
- /*
2642
- * whether this was part of a transaction when last mutated
2643
- */
2644
-
2645
- constructor() {
2646
- _initializerDefineProperty(this, "ref", _descriptor, this);
2647
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2648
- const [arr, prop] = arguments;
2649
- this._debug_base = arr.constructor.name + ':' + String(arr.modelName);
2650
- this._debug_prop = prop;
2651
- }
2652
- this.shouldReset = false;
2653
- this.t = false;
2654
- }
2655
- }, _descriptor = _applyDecoratedDescriptor(_class.prototype, "ref", [tracked], {
2656
- configurable: true,
2657
- enumerable: true,
2658
- writable: true,
2659
- initializer: function () {
2660
- return null;
2661
- }
2662
- }), _class);
2663
2307
  function safeForEach(instance, arr, store, callback, target) {
2664
2308
  if (target === undefined) {
2665
2309
  target = null;
@@ -2692,7 +2336,7 @@ function safeForEach(instance, arr, store, callback, target) {
2692
2336
  @class RecordArray
2693
2337
  @public
2694
2338
  */
2695
- let IdentifierArray = (_class3 = class IdentifierArray {
2339
+ let IdentifierArray = (_class = class IdentifierArray {
2696
2340
  [NOTIFY]() {
2697
2341
  notifyArray(this);
2698
2342
  }
@@ -2720,14 +2364,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2720
2364
  set length(value) {
2721
2365
  this[SOURCE].length = value;
2722
2366
  }
2723
-
2724
- // here to support computed chains
2725
- // and {{#each}}
2726
- get '[]'() {
2727
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
2728
- return this;
2729
- }
2730
- }
2731
2367
  constructor(options) {
2732
2368
  /**
2733
2369
  The flag to signal a `RecordArray` is currently loading data.
@@ -2742,7 +2378,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2742
2378
  @public
2743
2379
  @type Boolean
2744
2380
  */
2745
- _initializerDefineProperty(this, "isUpdating", _descriptor2, this);
2746
2381
  this.isLoaded = true;
2747
2382
  this.isDestroying = false;
2748
2383
  this.isDestroyed = false;
@@ -2750,16 +2385,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2750
2385
  this[IS_COLLECTION] = true;
2751
2386
  this[SOURCE] = void 0;
2752
2387
  // eslint-disable-next-line @typescript-eslint/no-this-alias
2753
- let self = this;
2388
+ const self = this;
2754
2389
  this.modelName = options.type;
2755
2390
  this.store = options.store;
2756
2391
  this._manager = options.manager;
2757
2392
  this[SOURCE] = options.identifiers;
2758
- // @ts-expect-error
2759
- this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
2393
+ this[ARRAY_SIGNAL] = createSignal(this, 'length');
2760
2394
  const store = options.store;
2761
2395
  const boundFns = new Map();
2762
- const _TAG = this[IDENTIFIER_ARRAY_TAG];
2396
+ const _SIGNAL = this[ARRAY_SIGNAL];
2763
2397
  const PrivateState = {
2764
2398
  links: options.links || null,
2765
2399
  meta: options.meta || null
@@ -2772,40 +2406,40 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2772
2406
 
2773
2407
  const proxy = new Proxy(this[SOURCE], {
2774
2408
  get(target, prop, receiver) {
2775
- let index = convertToInt(prop);
2776
- 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))) {
2777
2411
  options.manager._syncArray(receiver);
2778
- _TAG.t = false;
2779
- _TAG.shouldReset = false;
2412
+ _SIGNAL.t = false;
2413
+ _SIGNAL.shouldReset = false;
2780
2414
  }
2781
2415
  if (index !== null) {
2782
2416
  const identifier = target[index];
2783
2417
  if (!transaction) {
2784
- subscribe(_TAG);
2418
+ subscribe(_SIGNAL);
2785
2419
  }
2786
2420
  return identifier && store._instanceCache.getRecord(identifier);
2787
2421
  }
2788
- if (prop === 'meta') return subscribe(_TAG), PrivateState.meta;
2789
- if (prop === 'links') return subscribe(_TAG), PrivateState.links;
2790
- 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;
2791
2425
  if (isArrayGetter(prop)) {
2792
2426
  let fn = boundFns.get(prop);
2793
2427
  if (fn === undefined) {
2794
2428
  if (prop === 'forEach') {
2795
2429
  fn = function () {
2796
- subscribe(_TAG);
2430
+ subscribe(_SIGNAL);
2797
2431
  transaction = true;
2798
- let result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2432
+ const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2799
2433
  transaction = false;
2800
2434
  return result;
2801
2435
  };
2802
2436
  } else {
2803
2437
  fn = function () {
2804
- subscribe(_TAG);
2438
+ subscribe(_SIGNAL);
2805
2439
  // array functions must run through Reflect to work properly
2806
2440
  // binding via other means will not work.
2807
2441
  transaction = true;
2808
- let result = Reflect.apply(target[prop], receiver, arguments);
2442
+ const result = Reflect.apply(target[prop], receiver, arguments);
2809
2443
  transaction = false;
2810
2444
  return result;
2811
2445
  };
@@ -2827,10 +2461,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2827
2461
  const args = Array.prototype.slice.call(arguments);
2828
2462
  assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
2829
2463
  transaction = true;
2830
- let result = Reflect.apply(target[prop], receiver, args);
2831
- self[MUTATE](prop, args, result);
2832
- addToTransaction(_TAG);
2833
- // TODO handle cache updates
2464
+ const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
2834
2465
  transaction = false;
2835
2466
  return result;
2836
2467
  };
@@ -2838,16 +2469,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2838
2469
  }
2839
2470
  return fn;
2840
2471
  }
2841
- if (prop in self) {
2842
- if (prop === NOTIFY || prop === IDENTIFIER_ARRAY_TAG || prop === SOURCE) {
2472
+ if (isSelfProp(self, prop)) {
2473
+ if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
2843
2474
  return self[prop];
2844
2475
  }
2845
2476
  let fn = boundFns.get(prop);
2846
2477
  if (fn) return fn;
2847
- let outcome = self[prop];
2478
+ const outcome = self[prop];
2848
2479
  if (typeof outcome === 'function') {
2849
2480
  fn = function () {
2850
- subscribe(_TAG);
2481
+ subscribe(_SIGNAL);
2851
2482
  // array functions must run through Reflect to work properly
2852
2483
  // binding via other means will not work.
2853
2484
  return Reflect.apply(outcome, receiver, arguments);
@@ -2855,17 +2486,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2855
2486
  boundFns.set(prop, fn);
2856
2487
  return fn;
2857
2488
  }
2858
- return subscribe(_TAG), outcome;
2489
+ return subscribe(_SIGNAL), outcome;
2859
2490
  }
2860
2491
  return target[prop];
2861
2492
  },
2862
- set(target, prop, value) {
2493
+ // FIXME: Should this get a generic like get above?
2494
+ set(target, prop, value, receiver) {
2863
2495
  if (prop === 'length') {
2864
2496
  if (!transaction && value === 0) {
2865
2497
  transaction = true;
2866
- addToTransaction(_TAG);
2867
- Reflect.set(target, prop, value);
2868
- self[MUTATE]('length 0', []);
2498
+ self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
2869
2499
  transaction = false;
2870
2500
  return true;
2871
2501
  } else if (transaction) {
@@ -2882,9 +2512,22 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2882
2512
  PrivateState.meta = value || null;
2883
2513
  return true;
2884
2514
  }
2885
- 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.
2886
2524
  if (index === null || index > target.length) {
2887
- 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)) {
2888
2531
  self[prop] = value;
2889
2532
  return true;
2890
2533
  }
@@ -2894,12 +2537,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2894
2537
  assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
2895
2538
  return false;
2896
2539
  }
2897
- let original = target[index];
2898
- let newIdentifier = extractIdentifierFromRecord$1(value);
2540
+ const original = target[index];
2541
+ const newIdentifier = extractIdentifierFromRecord$1(value);
2899
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.
2900
2560
  if (!transaction) {
2901
- self[MUTATE]('replace cell', [index, original, newIdentifier]);
2902
- addToTransaction(_TAG);
2561
+ self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
2562
+ } else {
2563
+ target[index] = newIdentifier;
2903
2564
  }
2904
2565
  return true;
2905
2566
  },
@@ -2914,12 +2575,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2914
2575
  return IdentifierArray.prototype;
2915
2576
  }
2916
2577
  });
2917
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2918
- const meta = Ember.meta(this);
2919
- meta.addMixin = mixin => {
2920
- assert(`Do not call A() on EmberData RecordArrays`);
2921
- };
2922
- }
2578
+ createArrayTags(proxy, _SIGNAL);
2923
2579
  this[NOTIFY] = this[NOTIFY].bind(proxy);
2924
2580
  return proxy;
2925
2581
  }
@@ -2944,7 +2600,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2944
2600
  return this._updatingPromise;
2945
2601
  }
2946
2602
  this.isUpdating = true;
2947
- let updatingPromise = this._update();
2603
+ const updatingPromise = this._update();
2948
2604
  void updatingPromise.finally(() => {
2949
2605
  this._updatingPromise = null;
2950
2606
  if (this.isDestroying || this.isDestroyed) {
@@ -2983,17 +2639,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2983
2639
  @return {Promise<IdentifierArray>} promise
2984
2640
  */
2985
2641
  save() {
2986
- 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);
2987
2643
  return promise;
2988
2644
  }
2989
- }, (_descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "isUpdating", [tracked], {
2990
- 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 = {
2991
2650
  enumerable: true,
2992
- writable: true,
2993
- initializer: function () {
2994
- return false;
2651
+ configurable: false,
2652
+ get: function () {
2653
+ return this;
2995
2654
  }
2996
- }), _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);
2997
2659
  class Collection extends IdentifierArray {
2998
2660
  constructor(options) {
2999
2661
  super(options);
@@ -3025,7 +2687,8 @@ class Collection extends IdentifierArray {
3025
2687
  Collection.prototype.query = null;
3026
2688
 
3027
2689
  // Ensure instanceof works correctly
3028
- //Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2690
+ // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2691
+
3029
2692
  function assertRecordPassedToHasMany(record) {
3030
2693
  assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
3031
2694
  try {
@@ -3047,7 +2710,6 @@ function extractIdentifierFromRecord$1(record) {
3047
2710
  /**
3048
2711
  @module @ember-data/store
3049
2712
  */
3050
-
3051
2713
  const FAKE_ARR = {};
3052
2714
  const SLICE_BATCH_SIZE = 1200;
3053
2715
  /**
@@ -3090,7 +2752,7 @@ const SLICE_BATCH_SIZE = 1200;
3090
2752
  */
3091
2753
  function fastPush(target, source) {
3092
2754
  let startLength = 0;
3093
- let newLength = source.length;
2755
+ const newLength = source.length;
3094
2756
  while (newLength - startLength > SLICE_BATCH_SIZE) {
3095
2757
  // eslint-disable-next-line prefer-spread
3096
2758
  target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
@@ -3147,8 +2809,8 @@ class RecordArrayManager {
3147
2809
  */
3148
2810
  liveArrayFor(type) {
3149
2811
  let array = this._live.get(type);
3150
- let identifiers = [];
3151
- let staged = this._staged.get(type);
2812
+ const identifiers = [];
2813
+ const staged = this._staged.get(type);
3152
2814
  if (staged) {
3153
2815
  staged.forEach((value, key) => {
3154
2816
  if (value === 'add') {
@@ -3171,7 +2833,7 @@ class RecordArrayManager {
3171
2833
  return array;
3172
2834
  }
3173
2835
  createArray(config) {
3174
- let options = {
2836
+ const options = {
3175
2837
  type: config.type,
3176
2838
  links: config.doc?.links || null,
3177
2839
  meta: config.doc?.meta || null,
@@ -3182,7 +2844,7 @@ class RecordArrayManager {
3182
2844
  store: this.store,
3183
2845
  manager: this
3184
2846
  };
3185
- let array = new Collection(options);
2847
+ const array = new Collection(options);
3186
2848
  this._managed.add(array);
3187
2849
  this._set.set(array, new Set(options.identifiers || []));
3188
2850
  if (config.identifiers) {
@@ -3194,7 +2856,7 @@ class RecordArrayManager {
3194
2856
  if (array === FAKE_ARR) {
3195
2857
  return;
3196
2858
  }
3197
- let tag = array[IDENTIFIER_ARRAY_TAG];
2859
+ const tag = array[ARRAY_SIGNAL];
3198
2860
  if (!tag.shouldReset) {
3199
2861
  tag.shouldReset = true;
3200
2862
  addTransactionCB(array[NOTIFY]);
@@ -3206,11 +2868,11 @@ class RecordArrayManager {
3206
2868
  if (this.isDestroying || this.isDestroyed) {
3207
2869
  return;
3208
2870
  }
3209
- let liveArray = this._live.get(identifier.type);
2871
+ const liveArray = this._live.get(identifier.type);
3210
2872
  const allPending = this._pending;
3211
- let pending = new Map();
2873
+ const pending = new Map();
3212
2874
  if (includeManaged) {
3213
- let managed = this._identifiers.get(identifier);
2875
+ const managed = this._identifiers.get(identifier);
3214
2876
  if (managed) {
3215
2877
  managed.forEach(arr => {
3216
2878
  let changes = allPending.get(arr);
@@ -3265,10 +2927,10 @@ class RecordArrayManager {
3265
2927
  associate(this._identifiers, array, identifiers);
3266
2928
  }
3267
2929
  identifierAdded(identifier) {
3268
- let changeSets = this._getPendingFor(identifier, false);
2930
+ const changeSets = this._getPendingFor(identifier, false);
3269
2931
  if (changeSets) {
3270
2932
  changeSets.forEach((changes, array) => {
3271
- let existing = changes.get(identifier);
2933
+ const existing = changes.get(identifier);
3272
2934
  if (existing === 'del') {
3273
2935
  changes.delete(identifier);
3274
2936
  } else {
@@ -3279,10 +2941,10 @@ class RecordArrayManager {
3279
2941
  }
3280
2942
  }
3281
2943
  identifierRemoved(identifier) {
3282
- let changeSets = this._getPendingFor(identifier, true, true);
2944
+ const changeSets = this._getPendingFor(identifier, true, true);
3283
2945
  if (changeSets) {
3284
2946
  changeSets.forEach((changes, array) => {
3285
- let existing = changes.get(identifier);
2947
+ const existing = changes.get(identifier);
3286
2948
  if (existing === 'add') {
3287
2949
  changes.delete(identifier);
3288
2950
  } else {
@@ -3293,7 +2955,7 @@ class RecordArrayManager {
3293
2955
  }
3294
2956
  }
3295
2957
  identifierChanged(identifier) {
3296
- let newState = this.store._instanceCache.recordIsLoaded(identifier, true);
2958
+ const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
3297
2959
 
3298
2960
  // if the change matches the most recent direct added/removed
3299
2961
  // state, then we can ignore it
@@ -3320,13 +2982,12 @@ class RecordArrayManager {
3320
2982
  this.clear(false);
3321
2983
  this._live.clear();
3322
2984
  this.isDestroyed = true;
3323
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
3324
2985
  this.store.notifications.unsubscribe(this._subscription);
3325
2986
  }
3326
2987
  }
3327
2988
  function associate(ArraysCache, array, identifiers) {
3328
2989
  for (let i = 0; i < identifiers.length; i++) {
3329
- let identifier = identifiers[i];
2990
+ const identifier = identifiers[i];
3330
2991
  let cache = ArraysCache.get(identifier);
3331
2992
  if (!cache) {
3332
2993
  cache = new Set();
@@ -3341,13 +3002,13 @@ function disassociate(ArraysCache, array, identifiers) {
3341
3002
  }
3342
3003
  }
3343
3004
  function disassociateIdentifier(ArraysCache, array, identifier) {
3344
- let cache = ArraysCache.get(identifier);
3005
+ const cache = ArraysCache.get(identifier);
3345
3006
  if (cache) {
3346
3007
  cache.delete(array);
3347
3008
  }
3348
3009
  }
3349
3010
  function sync(array, changes, arraySet) {
3350
- let state = array[SOURCE];
3011
+ const state = array[SOURCE];
3351
3012
  const adds = [];
3352
3013
  const removes = [];
3353
3014
  changes.forEach((value, key) => {
@@ -3361,13 +3022,13 @@ function sync(array, changes, arraySet) {
3361
3022
  } else {
3362
3023
  if (arraySet.has(key)) {
3363
3024
  removes.push(key);
3025
+ arraySet.delete(key);
3364
3026
  }
3365
3027
  }
3366
3028
  });
3367
3029
  if (removes.length) {
3368
3030
  if (removes.length === state.length) {
3369
3031
  state.length = 0;
3370
- arraySet.clear();
3371
3032
  // changing the reference breaks the Proxy
3372
3033
  // state = array[SOURCE] = [];
3373
3034
  } else {
@@ -3394,6 +3055,9 @@ function sync(array, changes, arraySet) {
3394
3055
  }
3395
3056
  }
3396
3057
 
3058
+ /**
3059
+ * @module @ember-data/store
3060
+ */
3397
3061
  const Touching = Symbol('touching');
3398
3062
  const RequestPromise = Symbol('promise');
3399
3063
  const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
@@ -3421,14 +3085,14 @@ class RequestStateService {
3421
3085
  this._done.delete(identifier);
3422
3086
  }
3423
3087
  _enqueue(promise, queryRequest) {
3424
- let query = queryRequest.data[0];
3088
+ const query = queryRequest.data[0];
3425
3089
  if (hasRecordIdentifier(query)) {
3426
3090
  const identifier = query.recordIdentifier;
3427
- let type = query.op === 'saveRecord' ? 'mutation' : 'query';
3091
+ const type = query.op === 'saveRecord' ? 'mutation' : 'query';
3428
3092
  if (!this._pending.has(identifier)) {
3429
3093
  this._pending.set(identifier, []);
3430
3094
  }
3431
- let request = {
3095
+ const request = {
3432
3096
  state: 'pending',
3433
3097
  request: queryRequest,
3434
3098
  type
@@ -3439,7 +3103,7 @@ class RequestStateService {
3439
3103
  this._triggerSubscriptions(request);
3440
3104
  return promise.then(result => {
3441
3105
  this._dequeue(identifier, request);
3442
- let finalizedRequest = {
3106
+ const finalizedRequest = {
3443
3107
  state: 'fulfilled',
3444
3108
  request: queryRequest,
3445
3109
  type,
@@ -3453,7 +3117,7 @@ class RequestStateService {
3453
3117
  return result;
3454
3118
  }, error => {
3455
3119
  this._dequeue(identifier, request);
3456
- let finalizedRequest = {
3120
+ const finalizedRequest = {
3457
3121
  state: 'rejected',
3458
3122
  request: queryRequest,
3459
3123
  type,
@@ -3502,7 +3166,7 @@ class RequestStateService {
3502
3166
  _addDone(request) {
3503
3167
  request[Touching].forEach(identifier => {
3504
3168
  // TODO add support for multiple
3505
- let requestDataOp = request.request.data[0].op;
3169
+ const requestDataOp = request.request.data[0].op;
3506
3170
  let requests = this._done.get(identifier);
3507
3171
  if (requests) {
3508
3172
  requests = requests.filter(req => {
@@ -3566,7 +3230,7 @@ class RequestStateService {
3566
3230
  * @method getPendingRequestsForRecord
3567
3231
  * @public
3568
3232
  * @param {StableRecordIdentifier} identifier
3569
- * @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
3570
3234
  */
3571
3235
  getPendingRequestsForRecord(identifier) {
3572
3236
  return this._pending.get(identifier) || EMPTY_ARR;
@@ -3578,10 +3242,10 @@ class RequestStateService {
3578
3242
  * @method getLastRequestForRecord
3579
3243
  * @public
3580
3244
  * @param {StableRecordIdentifier} identifier
3581
- * @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
3582
3246
  */
3583
3247
  getLastRequestForRecord(identifier) {
3584
- let requests = this._done.get(identifier);
3248
+ const requests = this._done.get(identifier);
3585
3249
  if (requests) {
3586
3250
  return requests[requests.length - 1];
3587
3251
  }
@@ -3593,7 +3257,7 @@ function isNonEmptyString(str) {
3593
3257
  }
3594
3258
  function constructResource(type, id, lid) {
3595
3259
  if (typeof type === 'object' && type !== null) {
3596
- let resource = type;
3260
+ const resource = type;
3597
3261
  if (isStableIdentifier(resource)) {
3598
3262
  return resource;
3599
3263
  }
@@ -3628,6 +3292,11 @@ function constructResource(type, id, lid) {
3628
3292
  }
3629
3293
  }
3630
3294
 
3295
+ /**
3296
+ @module @ember-data/store
3297
+ */
3298
+ // this import location is deprecated but breaks in 4.8 and older
3299
+
3631
3300
  /**
3632
3301
  * A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
3633
3302
  * and sources of data (such as your API or a local persistence layer)
@@ -3646,7 +3315,10 @@ function constructResource(type, id, lid) {
3646
3315
 
3647
3316
  @class Store
3648
3317
  @public
3649
- */ // @ts-expect-error
3318
+ */
3319
+
3320
+ // @ts-expect-error
3321
+
3650
3322
  class Store extends EmberObject {
3651
3323
  /**
3652
3324
  * Provides access to the NotificationManager associated
@@ -3778,8 +3450,6 @@ class Store extends EmberObject {
3778
3450
  // private
3779
3451
  this._requestCache = new RequestStateService(this);
3780
3452
  this._instanceCache = new InstanceCache(this);
3781
- this._adapterCache = Object.create(null);
3782
- this._serializerCache = Object.create(null);
3783
3453
  this._documentCache = new Map();
3784
3454
  this.isDestroying = false;
3785
3455
  this.isDestroyed = false;
@@ -3837,7 +3507,7 @@ class Store extends EmberObject {
3837
3507
  * that have been initiated for a given identifier.
3838
3508
  *
3839
3509
  * @method getRequestStateService
3840
- * @returns {RequestStateService}
3510
+ * @return {RequestStateService}
3841
3511
  * @public
3842
3512
  */
3843
3513
  getRequestStateService() {
@@ -3862,10 +3532,11 @@ class Store extends EmberObject {
3862
3532
  * inserting the response into the cache and handing
3863
3533
  * back a Future which resolves to a ResponseDocument
3864
3534
  *
3865
- * Resource data is always updated in the cache.
3535
+ * ## Cache Keys
3866
3536
  *
3867
- * Only GET requests have the request result and document
3868
- * 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.
3869
3540
  *
3870
3541
  * The cache key used is `requestConfig.cacheOptions.key`
3871
3542
  * if present, falling back to `requestconfig.url`.
@@ -3876,16 +3547,44 @@ class Store extends EmberObject {
3876
3547
  * via the `POST` method `requestConfig.cacheOptions.key`
3877
3548
  * MUST be supplied for the document to be cached.
3878
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
+ *
3879
3578
  * @method request
3880
3579
  * @param {StoreRequestInput} requestConfig
3881
- * @returns {Future}
3580
+ * @return {Future}
3882
3581
  * @public
3883
3582
  */
3884
3583
  request(requestConfig) {
3885
3584
  // we lazily set the cache handler when we issue the first request
3886
3585
  // because constructor doesn't allow for this to run after
3887
3586
  // the user has had the chance to set the prop.
3888
- let opts = {
3587
+ const opts = {
3889
3588
  store: this,
3890
3589
  [EnableHydration]: true
3891
3590
  };
@@ -3937,7 +3636,7 @@ class Store extends EmberObject {
3937
3636
  * @param createRecordArgs
3938
3637
  * @param recordDataFor deprecated use this.cache
3939
3638
  * @param notificationManager deprecated use this.notifications
3940
- * @returns A record instance
3639
+ * @return A record instance
3941
3640
  * @public
3942
3641
  */
3943
3642
 
@@ -4146,8 +3845,8 @@ class Store extends EmberObject {
4146
3845
  // to remove this, we would need to move to a new `async` API.
4147
3846
  let record;
4148
3847
  this._join(() => {
4149
- let normalizedModelName = normalizeModelName(modelName);
4150
- let properties = {
3848
+ const normalizedModelName = normalizeModelName(modelName);
3849
+ const properties = {
4151
3850
  ...inputProperties
4152
3851
  };
4153
3852
 
@@ -4157,7 +3856,7 @@ class Store extends EmberObject {
4157
3856
  // to avoid conflicts.
4158
3857
 
4159
3858
  if (properties.id === null || properties.id === undefined) {
4160
- let adapter = this.adapterFor(modelName, true);
3859
+ const adapter = this.adapterFor?.(modelName, true);
4161
3860
  if (adapter && adapter.generateIdForRecord) {
4162
3861
  properties.id = adapter.generateIdForRecord(this, modelName, properties);
4163
3862
  } else {
@@ -4289,8 +3988,7 @@ class Store extends EmberObject {
4289
3988
  In your adapter you can then access this id without triggering a network request via the
4290
3989
  snapshot:
4291
3990
  ```app/adapters/application.js
4292
- import EmberObject from '@ember/object';
4293
- export default class Adapter extends EmberObject {
3991
+ export default class Adapter {
4294
3992
  findRecord(store, schema, id, snapshot) {
4295
3993
  let type = schema.modelName;
4296
3994
  if (type === 'comment')
@@ -4299,6 +3997,9 @@ class Store extends EmberObject {
4299
3997
  .then(response => response.json())
4300
3998
  }
4301
3999
  }
4000
+ static create() {
4001
+ return new this();
4002
+ }
4302
4003
  }
4303
4004
  ```
4304
4005
  This could also be achieved by supplying the post id to the adapter via the adapterOptions
@@ -4312,9 +4013,8 @@ class Store extends EmberObject {
4312
4013
  }
4313
4014
  ```
4314
4015
  ```app/adapters/application.js
4315
- import EmberObject from '@ember/object';
4316
- export default class Adapter extends EmberObject {
4317
- findRecord(store, schema, id, snapshot) {
4016
+ export default class Adapter {
4017
+ findRecord(store, schema, id, snapshot) {
4318
4018
  let type = schema.modelName;
4319
4019
  if (type === 'comment')
4320
4020
  let postId = snapshot.adapterOptions.post;
@@ -4322,6 +4022,9 @@ class Store extends EmberObject {
4322
4022
  .then(response => response.json())
4323
4023
  }
4324
4024
  }
4025
+ static create() {
4026
+ return new this();
4027
+ }
4325
4028
  }
4326
4029
  ```
4327
4030
  If you have access to the post model you can also pass the model itself to preload:
@@ -4448,9 +4151,8 @@ class Store extends EmberObject {
4448
4151
  }
4449
4152
  ```
4450
4153
  ```app/adapters/application.js
4451
- import EmberObject from '@ember/object';
4452
- export default class Adapter extends EmberObject {
4453
- findRecord(store, schema, id, snapshot) {
4154
+ export default class Adapter {
4155
+ findRecord(store, schema, id, snapshot) {
4454
4156
  let type = schema.modelName;
4455
4157
  if (type === 'post')
4456
4158
  let includes = snapshot.adapterOptions.include;
@@ -4458,6 +4160,9 @@ class Store extends EmberObject {
4458
4160
  .then(response => response.json())
4459
4161
  }
4460
4162
  }
4163
+ static create() {
4164
+ return new this();
4165
+ }
4461
4166
  }
4462
4167
  ```
4463
4168
  In this case, the post's comments would then be available in your template as
@@ -4601,7 +4306,7 @@ class Store extends EmberObject {
4601
4306
  resourceIdentifier = constructResource(type, normalizedId);
4602
4307
  }
4603
4308
  assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
4604
- let identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4309
+ const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4605
4310
  return this._instanceCache.getReference(identifier);
4606
4311
  }
4607
4312
 
@@ -5020,7 +4725,7 @@ class Store extends EmberObject {
5020
4725
  }
5021
4726
  assert(`You need to pass a model name to the store's peekAll method`, modelName);
5022
4727
  assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5023
- let type = normalizeModelName(modelName);
4728
+ const type = normalizeModelName(modelName);
5024
4729
  return this.recordArrayManager.liveArrayFor(type);
5025
4730
  }
5026
4731
 
@@ -5050,7 +4755,7 @@ class Store extends EmberObject {
5050
4755
  this.recordArrayManager.clear();
5051
4756
  this._instanceCache.clear();
5052
4757
  } else {
5053
- let normalizedModelName = normalizeModelName(modelName);
4758
+ const normalizedModelName = normalizeModelName(modelName);
5054
4759
  this._instanceCache.clear(normalizedModelName);
5055
4760
  }
5056
4761
  });
@@ -5191,422 +4896,712 @@ class Store extends EmberObject {
5191
4896
  if (macroCondition(getOwnConfig().env.DEBUG)) {
5192
4897
  assertDestroyingStore(this, 'push');
5193
4898
  }
5194
- let pushed = this._push(data, false);
4899
+ const pushed = this._push(data, false);
5195
4900
  if (Array.isArray(pushed)) {
5196
- let records = pushed.map(identifier => this._instanceCache.getRecord(identifier));
5197
- return records;
4901
+ return pushed.map(identifier => this._instanceCache.getRecord(identifier));
5198
4902
  }
5199
4903
  if (pushed === null) {
5200
4904
  return null;
5201
4905
  }
5202
4906
  return this._instanceCache.getRecord(pushed);
5203
4907
  }
5204
-
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
+ }
5205
5205
  /**
5206
- Push some data in the form of a json-api document into the store,
5207
- without creating materialized records.
5208
- @method _push
5209
- @private
5210
- @param {Object} jsonApiDoc
5211
- @return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
5212
- */
5213
- _push(jsonApiDoc, asyncFlush) {
5214
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5215
- assertDestroyingStore(this, '_push');
5216
- }
5217
- if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
5218
- try {
5219
- let data = JSON.parse(JSON.stringify(jsonApiDoc));
5220
- // eslint-disable-next-line no-console
5221
- console.log('EmberData | Payload - push', data);
5222
- } catch (e) {
5223
- // eslint-disable-next-line no-console
5224
- console.log('EmberData | Payload - push', jsonApiDoc);
5225
- }
5226
- }
5227
- if (asyncFlush) {
5228
- this._enableAsyncFlush = true;
5229
- }
5230
- let ret;
5231
- this._join(() => {
5232
- ret = this.cache.put({
5233
- content: jsonApiDoc
5234
- });
5235
- });
5236
- this._enableAsyncFlush = null;
5237
- 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);
5238
5220
  }
5239
5221
 
5240
5222
  /**
5241
- Push some raw data into the store.
5242
- This method can be used both to push in brand new
5243
- records, as well as to update existing records. You
5244
- can push in more than one type of object at once.
5245
- All objects should be in the format expected by the
5246
- serializer.
5247
- ```app/serializers/application.js
5248
- import RESTSerializer from '@ember-data/serializer/rest';
5249
- export default class ApplicationSerializer extends RESTSerializer;
5250
- ```
5251
- ```js
5252
- let pushData = {
5253
- posts: [
5254
- { id: 1, postTitle: "Great post", commentIds: [2] }
5255
- ],
5256
- comments: [
5257
- { id: 2, commentBody: "Insightful comment" }
5258
- ]
5259
- }
5260
- store.pushPayload(pushData);
5261
- ```
5262
- By default, the data will be deserialized using a default
5263
- serializer (the application serializer if it exists).
5264
- Alternatively, `pushPayload` will accept a model type which
5265
- will determine which serializer will process the payload.
5266
- ```app/serializers/application.js
5267
- import RESTSerializer from '@ember-data/serializer/rest';
5268
- export default class ApplicationSerializer extends RESTSerializer;
5269
- ```
5270
- ```app/serializers/post.js
5271
- import JSONSerializer from '@ember-data/serializer/json';
5272
- export default JSONSerializer;
5273
- ```
5274
- ```js
5275
- store.pushPayload(pushData); // Will use the application serializer
5276
- store.pushPayload('post', pushData); // Will use the post serializer
5277
- ```
5278
- @method pushPayload
5279
- @public
5280
- @param {String} modelName Optionally, a model type used to determine which serializer will be used
5281
- @param {Object} inputPayload
5282
- */
5283
- // TODO @runspired @deprecate pushPayload in favor of looking up the serializer
5284
- pushPayload(modelName, inputPayload) {
5285
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5286
- assertDestroyingStore(this, 'pushPayload');
5287
- }
5288
- const payload = inputPayload || modelName;
5289
- const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
5290
- const serializer = this.serializerFor(normalizedModelName);
5291
- assert(`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`, serializer && typeof serializer.pushPayload === 'function');
5292
- 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);
5293
5234
  }
5294
5235
 
5295
5236
  /**
5296
- * 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.
5297
5240
  *
5298
- * @method saveRecord
5241
+ * @method prev
5299
5242
  * @public
5300
- * @param {RecordInstance} record
5301
- * @param options
5302
- * @returns {Promise<RecordInstance>}
5243
+ * @param {object} options
5244
+ * @return Promise<Document | null>
5303
5245
  */
5304
- saveRecord(record, options = {}) {
5305
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5306
- assertDestroyingStore(this, 'saveRecord');
5307
- }
5308
- assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
5309
- let identifier = recordIdentifierFor(record);
5310
- const cache = this.cache;
5311
- if (!identifier) {
5312
- // this commonly means we're disconnected
5313
- // but just in case we reject here to prevent bad things.
5314
- return Promise.reject(`Record Is Disconnected`);
5315
- }
5316
- // TODO we used to check if the record was destroyed here
5317
- assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
5318
- if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5319
- return Promise.resolve(record);
5320
- }
5321
- if (!options) {
5322
- options = {};
5323
- }
5324
- let operation = 'updateRecord';
5325
- if (cache.isNew(identifier)) {
5326
- operation = 'createRecord';
5327
- } else if (cache.isDeleted(identifier)) {
5328
- operation = 'deleteRecord';
5329
- }
5330
- const request = {
5331
- op: operation,
5332
- data: {
5333
- options,
5334
- record: identifier
5335
- },
5336
- cacheOptions: {
5337
- [SkipCache]: true
5338
- }
5339
- };
5340
-
5341
- // we lie here on the type because legacy doesn't have enough context
5342
- cache.willCommit(identifier, {
5343
- request
5344
- });
5345
- return this.request(request).then(document => document.content);
5246
+ prev(options = {}) {
5247
+ return _classPrivateFieldBase(this, _request)[_request]('prev', options);
5346
5248
  }
5347
5249
 
5348
5250
  /**
5349
- * Instantiation hook allowing applications or addons to configure the store
5350
- * 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.
5351
5254
  *
5352
- * This hook should not be called directly by consuming applications or libraries.
5353
- * 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.
5354
5268
  *
5355
- * @method createCache (hook)
5269
+ * @method last
5356
5270
  * @public
5357
- * @param storeWrapper
5358
- * @returns {Cache}
5271
+ * @param {object} options
5272
+ * @return Promise<Document | null>
5359
5273
  */
5274
+ last(options = {}) {
5275
+ return _classPrivateFieldBase(this, _request)[_request]('last', options);
5276
+ }
5360
5277
 
5361
5278
  /**
5362
- * Returns the cache instance associated to this Store, instantiates the Cache
5363
- * if necessary via `Store.createCache`
5279
+ * Implemented for `JSON.stringify` support.
5364
5280
  *
5365
- * @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
5366
5288
  * @public
5289
+ * @return
5367
5290
  */
5368
- get cache() {
5369
- let {
5370
- cache
5371
- } = this._instanceCache;
5372
- if (!cache) {
5373
- cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5374
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5375
- 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);
5376
5431
  }
5432
+ } else if (!isFromCache) {
5433
+ doc.data = data;
5434
+ copyDocumentProperties(doc, document);
5377
5435
  }
5378
- return cache;
5436
+ return options.shouldHydrate ? doc : document;
5379
5437
  }
5380
-
5381
- /**
5382
- `normalize` converts a json payload into the normalized form that
5383
- [push](../methods/push?anchor=push) expects.
5384
- Example
5385
- ```js
5386
- socket.on('message', function(message) {
5387
- let modelName = message.model;
5388
- let data = message.data;
5389
- 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);
5390
5487
  });
5391
- ```
5392
- @method normalize
5393
- @public
5394
- @param {String} modelName The name of the model type for this payload
5395
- @param {Object} payload
5396
- @return {Object} The normalized payload
5397
- */
5398
- // TODO @runspired @deprecate users should call normalize on the associated serializer directly
5399
- normalize(modelName, payload) {
5400
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5401
- assertDestroyingStore(this, 'normalize');
5488
+ store._enableAsyncFlush = null;
5489
+ if (store.lifetimes?.didRequest) {
5490
+ store.lifetimes.didRequest(context.request, document.response, identifier, store);
5402
5491
  }
5403
- assert(`You need to pass a model name to the store's normalize method`, modelName);
5404
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${typeof modelName}`, typeof modelName === 'string');
5405
- const normalizedModelName = normalizeModelName(modelName);
5406
- const serializer = this.serializerFor(normalizedModelName);
5407
- const schema = this.modelFor(normalizedModelName);
5408
- assert(`You must define a normalize method in your serializer in order to call store.normalize`, typeof serializer?.normalize === 'function');
5409
- return serializer.normalize(schema, payload);
5410
- }
5411
-
5412
- /**
5413
- Returns an instance of the adapter for a given type. For
5414
- example, `adapterFor('person')` will return an instance of
5415
- the adapter located at `app/adapters/person.js`
5416
- If no `person` adapter is found, this method will look
5417
- for an `application` adapter (the default adapter for
5418
- your entire application).
5419
- @method adapterFor
5420
- @public
5421
- @param {String} modelName
5422
- @return Adapter
5423
- */
5424
-
5425
- adapterFor(modelName, _allowMissing) {
5426
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5427
- assertDestroyingStore(this, 'adapterFor');
5492
+ if (shouldFetch) {
5493
+ return response;
5494
+ } else if (shouldBackgroundFetch) {
5495
+ store.notifications._flush();
5428
5496
  }
5429
- assert(`You need to pass a model name to the store's adapterFor method`, modelName);
5430
- assert(`Passing classes to store.adapterFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5431
- let normalizedModelName = normalizeModelName(modelName);
5432
- let {
5433
- _adapterCache
5434
- } = this;
5435
- let adapter = _adapterCache[normalizedModelName];
5436
- if (adapter) {
5437
- return adapter;
5497
+ }, error => {
5498
+ store.requestManager._pending.delete(context.id);
5499
+ if (context.request.signal?.aborted) {
5500
+ throw error;
5438
5501
  }
5439
- const owner = getOwner(this);
5440
-
5441
- // name specific adapter
5442
- adapter = owner.lookup(`adapter:${normalizedModelName}`);
5443
- if (adapter !== undefined) {
5444
- _adapterCache[normalizedModelName] = adapter;
5445
- 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);
5446
5527
  }
5447
-
5448
- // no adapter found for the specific name, fallback and check for application adapter
5449
- adapter = _adapterCache.application || owner.lookup('adapter:application');
5450
- if (adapter !== undefined) {
5451
- _adapterCache[normalizedModelName] = adapter;
5452
- _adapterCache.application = adapter;
5453
- return adapter;
5528
+ if (!shouldBackgroundFetch) {
5529
+ const newError = cloneError(error);
5530
+ newError.content = response;
5531
+ throw newError;
5532
+ } else {
5533
+ store.notifications._flush();
5454
5534
  }
5455
- assert(`No adapter was found for '${modelName}' and no 'application' adapter was found as a fallback.`, _allowMissing);
5535
+ });
5536
+ if (!isMut) {
5537
+ return promise;
5456
5538
  }
5539
+ assert(`Expected a mutation`, isMutation(context.request));
5457
5540
 
5458
- /**
5459
- Returns an instance of the serializer for a given type. For
5460
- example, `serializerFor('person')` will return an instance of
5461
- `App.PersonSerializer`.
5462
- If no `App.PersonSerializer` is found, this method will look
5463
- for an `App.ApplicationSerializer` (the default serializer for
5464
- your entire application).
5465
- If a serializer cannot be found on the adapter, it will fall back
5466
- to an instance of `JSONSerializer`.
5467
- @method serializerFor
5468
- @public
5469
- @param {String} modelName the record to serialize
5470
- @return {Serializer}
5471
- */
5472
- serializerFor(modelName) {
5473
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5474
- assertDestroyingStore(this, 'serializerFor');
5475
- }
5476
- assert(`You need to pass a model name to the store's serializerFor method`, modelName);
5477
- assert(`Passing classes to store.serializerFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5478
- let normalizedModelName = normalizeModelName(modelName);
5479
- let {
5480
- _serializerCache
5481
- } = this;
5482
- let serializer = _serializerCache[normalizedModelName];
5483
- if (serializer) {
5484
- return serializer;
5485
- }
5486
-
5487
- // by name
5488
- const owner = getOwner(this);
5489
- serializer = owner.lookup(`serializer:${normalizedModelName}`);
5490
- if (serializer !== undefined) {
5491
- _serializerCache[normalizedModelName] = serializer;
5492
- 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);
5493
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;
5494
5569
 
5495
- // no serializer found for the specific model, fallback and check for application serializer
5496
- serializer = _serializerCache.application || owner.lookup('serializer:application');
5497
- if (serializer !== undefined) {
5498
- _serializerCache[normalizedModelName] = serializer;
5499
- _serializerCache.application = serializer;
5500
- 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);
5501
5573
  }
5502
- return null;
5503
- }
5504
5574
 
5505
- // @ts-expect-error
5506
- destroy() {
5507
- if (this.isDestroyed) {
5508
- // @ember/test-helpers will call destroy multiple times
5509
- return;
5510
- }
5511
- this.isDestroying = true;
5512
- // enqueue destruction of any adapters/serializers we have created
5513
- for (let adapterName in this._adapterCache) {
5514
- let adapter = this._adapterCache[adapterName];
5515
- if (typeof adapter.destroy === 'function') {
5516
- adapter.destroy();
5517
- }
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);
5518
5579
  }
5519
- for (let serializerName in this._serializerCache) {
5520
- let serializer = this._serializerCache[serializerName];
5521
- if (typeof serializer.destroy === 'function') {
5522
- serializer.destroy();
5523
- }
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;
5524
5589
  }
5525
- this._graph?.destroy();
5526
- this._graph = undefined;
5527
- this.notifications.destroy();
5528
- this.recordArrayManager.destroy();
5529
- this.identifierCache.destroy();
5530
- this.unloadAll();
5531
- this.isDestroyed = true;
5590
+ return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5591
+ shouldHydrate,
5592
+ identifier
5593
+ }, peeked.content, true) : peeked.content);
5532
5594
  }
5533
- static create(args) {
5534
- return new this(args);
5595
+ };
5596
+ function copyDocumentProperties(target, source) {
5597
+ if ('links' in source) {
5598
+ target.links = source.links;
5535
5599
  }
5536
- }
5537
- let assertDestroyingStore;
5538
- let assertDestroyedStoreOnly;
5539
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5540
- // eslint-disable-next-line @typescript-eslint/no-shadow
5541
- assertDestroyingStore = function assertDestroyingStore(store, method) {
5542
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5543
- };
5544
- // eslint-disable-next-line @typescript-eslint/no-shadow
5545
- assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5546
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5547
- };
5548
- }
5549
- function isMaybeIdentifier(maybeIdentifier) {
5550
- return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5551
- }
5552
- function normalizeProperties(store, identifier, properties) {
5553
- // assert here
5554
- if (properties !== undefined) {
5555
- if ('id' in properties) {
5556
- assert(`expected id to be a string or null`, properties.id !== undefined);
5557
- }
5558
- assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5559
- const {
5560
- type
5561
- } = identifier;
5562
-
5563
- // convert relationship Records to RecordDatas before passing to RecordData
5564
- let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5565
- type
5566
- });
5567
- if (defs !== null) {
5568
- let keys = Object.keys(properties);
5569
- let relationshipValue;
5570
- for (let i = 0; i < keys.length; i++) {
5571
- let prop = keys[i];
5572
- let def = defs[prop];
5573
- if (def !== undefined) {
5574
- if (def.kind === 'hasMany') {
5575
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5576
- assertRecordsPassedToHasMany(properties[prop]);
5577
- }
5578
- relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5579
- } else {
5580
- relationshipValue = extractIdentifierFromRecord(properties[prop]);
5581
- }
5582
- properties[prop] = relationshipValue;
5583
- }
5584
- }
5585
- }
5600
+ if ('meta' in source) {
5601
+ target.meta = source.meta;
5586
5602
  }
5587
- return properties;
5588
- }
5589
- function assertRecordsPassedToHasMany(records) {
5590
- assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5591
- assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5592
- return records.every(record => {
5593
- try {
5594
- recordIdentifierFor(record);
5595
- return true;
5596
- } catch {
5597
- return false;
5598
- }
5599
- });
5600
- }());
5601
- }
5602
- function extractIdentifiersFromRecords(records) {
5603
- return records.map(record => extractIdentifierFromRecord(record));
5604
- }
5605
- function extractIdentifierFromRecord(recordOrPromiseRecord) {
5606
- if (!recordOrPromiseRecord) {
5607
- return null;
5603
+ if ('errors' in source) {
5604
+ target.errors = source.errors;
5608
5605
  }
5609
- const extract = recordIdentifierFor;
5610
- return extract(recordOrPromiseRecord);
5611
5606
  }
5612
- 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 };