@ember-data/store 5.4.0-alpha.3 → 5.4.0-alpha.31

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.
Files changed (76) hide show
  1. package/README.md +8 -4
  2. package/addon/-private.js +1 -1
  3. package/addon/{store-service-0a03d2a4.js → cache-handler-oB00-31L.js} +1165 -1014
  4. package/addon/cache-handler-oB00-31L.js.map +1 -0
  5. package/addon/index.js +1 -1
  6. package/addon/index.js.map +1 -1
  7. package/package.json +62 -41
  8. package/unstable-preview-types/-private/cache-handler.d.ts +99 -0
  9. package/unstable-preview-types/-private/cache-handler.d.ts.map +1 -0
  10. package/unstable-preview-types/-private/caches/cache-utils.d.ts +9 -0
  11. package/unstable-preview-types/-private/caches/cache-utils.d.ts.map +1 -0
  12. package/unstable-preview-types/-private/caches/identifier-cache.d.ts +180 -0
  13. package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -0
  14. package/unstable-preview-types/-private/caches/instance-cache.d.ts +61 -0
  15. package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -0
  16. package/unstable-preview-types/-private/caches/resource-utils.d.ts +10 -0
  17. package/unstable-preview-types/-private/caches/resource-utils.d.ts.map +1 -0
  18. package/unstable-preview-types/-private/document.d.ts +144 -0
  19. package/unstable-preview-types/-private/document.d.ts.map +1 -0
  20. package/unstable-preview-types/-private/index.d.ts +16 -0
  21. package/unstable-preview-types/-private/index.d.ts.map +1 -0
  22. package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts +177 -0
  23. package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts.map +1 -0
  24. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts +17 -0
  25. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts.map +1 -0
  26. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts +27 -0
  27. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts.map +1 -0
  28. package/unstable-preview-types/-private/managers/cache-manager.d.ts +440 -0
  29. package/unstable-preview-types/-private/managers/cache-manager.d.ts.map +1 -0
  30. package/unstable-preview-types/-private/managers/notification-manager.d.ts +96 -0
  31. package/unstable-preview-types/-private/managers/notification-manager.d.ts.map +1 -0
  32. package/unstable-preview-types/-private/managers/record-array-manager.d.ts +95 -0
  33. package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -0
  34. package/unstable-preview-types/-private/network/request-cache.d.ts +107 -0
  35. package/unstable-preview-types/-private/network/request-cache.d.ts.map +1 -0
  36. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +135 -0
  37. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -0
  38. package/unstable-preview-types/-private/store-service.d.ts +1552 -0
  39. package/unstable-preview-types/-private/store-service.d.ts.map +1 -0
  40. package/unstable-preview-types/-private/utils/coerce-id.d.ts +8 -0
  41. package/unstable-preview-types/-private/utils/coerce-id.d.ts.map +1 -0
  42. package/unstable-preview-types/-private/utils/construct-resource.d.ts +8 -0
  43. package/unstable-preview-types/-private/utils/construct-resource.d.ts.map +1 -0
  44. package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts +5 -0
  45. package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts.map +1 -0
  46. package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts +2 -0
  47. package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts.map +1 -0
  48. package/unstable-preview-types/-private/utils/normalize-model-name.d.ts +2 -0
  49. package/unstable-preview-types/-private/utils/normalize-model-name.d.ts.map +1 -0
  50. package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts +2 -0
  51. package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts.map +1 -0
  52. package/unstable-preview-types/-private.d.ts +2 -0
  53. package/unstable-preview-types/-private.d.ts.map +1 -0
  54. package/unstable-preview-types/-types/overview.d.ts +19 -0
  55. package/unstable-preview-types/-types/overview.d.ts.map +1 -0
  56. package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts +105 -0
  57. package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts.map +1 -0
  58. package/unstable-preview-types/-types/q/cache.d.ts +45 -0
  59. package/unstable-preview-types/-types/q/cache.d.ts.map +1 -0
  60. package/unstable-preview-types/-types/q/ds-model.d.ts +13 -0
  61. package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -0
  62. package/unstable-preview-types/-types/q/identifier.d.ts +169 -0
  63. package/unstable-preview-types/-types/q/identifier.d.ts.map +1 -0
  64. package/unstable-preview-types/-types/q/promise-proxies.d.ts +2 -0
  65. package/unstable-preview-types/-types/q/promise-proxies.d.ts.map +1 -0
  66. package/unstable-preview-types/-types/q/record-data-json-api.d.ts +34 -0
  67. package/unstable-preview-types/-types/q/record-data-json-api.d.ts.map +1 -0
  68. package/unstable-preview-types/-types/q/record-instance.d.ts +27 -0
  69. package/unstable-preview-types/-types/q/record-instance.d.ts.map +1 -0
  70. package/unstable-preview-types/-types/q/schema-service.d.ts +212 -0
  71. package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -0
  72. package/unstable-preview-types/-types/q/store.d.ts +15 -0
  73. package/unstable-preview-types/-types/q/store.d.ts.map +1 -0
  74. package/unstable-preview-types/index.d.ts +185 -0
  75. package/unstable-preview-types/index.d.ts.map +1 -0
  76. package/addon/store-service-0a03d2a4.js.map +0 -1
@@ -1,365 +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 { _backburner } from '@ember/runloop';
6
- 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';
7
6
  import { dasherize } from '@ember/string';
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
- function calcShouldFetch(store, request, hasCachedValue, identifier) {
237
- const {
238
- cacheOptions
239
- } = request;
240
- return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier) : false);
241
- }
242
- function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
243
- const {
244
- cacheOptions
245
- } = request;
246
- return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier) : false));
247
- }
248
- function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
249
- const {
250
- store
251
- } = context.request;
252
- const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
253
- return next(context.request).then(document => {
254
- store.requestManager._pending.delete(context.id);
255
- store._enableAsyncFlush = true;
256
- let response;
257
- store._join(() => {
258
- response = store.cache.put(document);
259
- response = maybeUpdateUiObjects(store, context.request, {
260
- shouldHydrate,
261
- shouldFetch,
262
- shouldBackgroundFetch,
263
- identifier
264
- }, response, false);
265
- });
266
- store._enableAsyncFlush = null;
267
- if (shouldFetch) {
268
- return response;
269
- } else if (shouldBackgroundFetch) {
270
- store.notifications._flush();
271
- }
272
- }, error => {
273
- store.requestManager._pending.delete(context.id);
274
- if (context.request.signal?.aborted) {
275
- throw error;
276
- }
277
- store.requestManager._pending.delete(context.id);
278
- store._enableAsyncFlush = true;
279
- let response;
280
- store._join(() => {
281
- response = store.cache.put(error);
282
- response = maybeUpdateUiObjects(store, context.request, {
283
- shouldHydrate,
284
- shouldFetch,
285
- shouldBackgroundFetch,
286
- identifier
287
- }, response, false);
288
- });
289
- store._enableAsyncFlush = null;
290
- if (!shouldBackgroundFetch) {
291
- const newError = cloneError(error);
292
- newError.content = response;
293
- throw newError;
294
- } else {
295
- store.notifications._flush();
296
- }
297
- });
298
- }
299
- function cloneError(error) {
300
- const cloned = new Error(error.message);
301
- cloned.stack = error.stack;
302
- cloned.error = error.error;
303
- return cloned;
304
- }
305
- const SkipCache = Symbol.for('ember-data:skip-cache');
306
- const EnableHydration = Symbol.for('ember-data:enable-hydration');
307
- const CacheHandler = {
308
- request(context, next) {
309
- // if we have no cache or no cache-key skip cache handling
310
- if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
311
- return next(context.request);
312
- }
313
- const {
314
- store
315
- } = context.request;
316
- const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
317
- const peeked = identifier ? store.cache.peekRequest(identifier) : null;
318
-
319
- // determine if we should skip cache
320
- if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
321
- return fetchContentAndHydrate(next, context, identifier, true, false);
322
- }
7
+ import { defineSignal, addToTransaction, createSignal, subscribe, createArrayTags, addTransactionCB } from '@ember-data/tracking/-private';
8
+ import { _backburner } from '@ember/runloop';
9
+ import { compat } from '@ember-data/tracking';
323
10
 
324
- // if we have not skipped cache, determine if we should update behind the scenes
325
- if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
326
- let promise = fetchContentAndHydrate(next, context, identifier, false, true);
327
- store.requestManager._pending.set(context.id, promise);
328
- }
329
- const shouldHydrate = context.request[EnableHydration] || false;
330
- if ('error' in peeked) {
331
- const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
332
- shouldHydrate,
333
- identifier
334
- }, peeked.content, true) : peeked.content;
335
- const newError = cloneError(peeked);
336
- newError.content = content;
337
- throw newError;
338
- }
339
- return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
340
- shouldHydrate,
341
- identifier
342
- }, peeked.content, true) : peeked.content);
343
- }
344
- };
345
- function copyDocumentProperties(target, source) {
346
- if ('links' in source) {
347
- target.links = source.links;
348
- }
349
- if ('meta' in source) {
350
- target.meta = source.meta;
351
- }
352
- if ('errors' in source) {
353
- target.errors = source.errors;
354
- }
355
- }
11
+ /**
12
+ @module @ember-data/store
13
+ */
356
14
 
357
- // Used by the store to normalize IDs entering the store. Despite the fact
358
- // that developers may provide IDs as numbers (e.g., `store.findRecord('person', 1)`),
359
- // it is important that internally we use strings, since IDs may be serialized
360
- // and lose type information. For example, Ember's router may put a record's
361
- // ID into the URL, and if we later try to deserialize that URL and find the
362
- // corresponding record, we will not know if it is a string or a number.
363
15
  function coerceId(id) {
364
16
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_ID)) {
365
17
  let normalized;
@@ -392,10 +44,6 @@ function ensureStringId(id) {
392
44
  assert(`Expected id to be a string or number, received ${String(id)}`, normalized !== null);
393
45
  return normalized;
394
46
  }
395
-
396
- // provided for additional debuggability
397
- const DEBUG_CLIENT_ORIGINATED = Symbol('record-originated-on-client');
398
- const DEBUG_IDENTIFIER_BUCKET = Symbol('identifier-bucket');
399
47
  function normalizeModelName(type) {
400
48
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_TYPES)) {
401
49
  const result = dasherize(type);
@@ -424,7 +72,7 @@ function installPolyfill() {
424
72
  // we might be able to optimize this by requesting more bytes than we need at a time
425
73
  const rng = function () {
426
74
  // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
427
- let rnds8 = new Uint8Array(16);
75
+ const rnds8 = new Uint8Array(16);
428
76
  if (!CRYPTO.getRandomValues && !isFastBoot) {
429
77
  throw new Error(`Unable to generate bytes for UUID`);
430
78
  }
@@ -440,12 +88,12 @@ function installPolyfill() {
440
88
  byteToHex[i] = (i + 0x100).toString(16).substr(1);
441
89
  }
442
90
  const bytesToUuid = function (buf) {
443
- let bth = byteToHex;
91
+ const bth = byteToHex;
444
92
  // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
445
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('');
446
94
  };
447
95
  CRYPTO.randomUUID = function uuidv4() {
448
- let rnds = rng();
96
+ const rnds = rng();
449
97
 
450
98
  // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
451
99
  rnds[6] = rnds[6] & 0x0f | 0x40;
@@ -476,7 +124,7 @@ function hasType(resource) {
476
124
  const IDENTIFIERS = new Set();
477
125
  const DOCUMENTS = new Set();
478
126
  function isStableIdentifier(identifier) {
479
- return IDENTIFIERS.has(identifier);
127
+ return identifier[CACHE_OWNER] !== undefined || IDENTIFIERS.has(identifier);
480
128
  }
481
129
  function isDocumentIdentifier(identifier) {
482
130
  return DOCUMENTS.has(identifier);
@@ -499,6 +147,7 @@ function freeze(obj) {
499
147
 
500
148
  // type IdentifierTypeLookup = { all: Set<StableRecordIdentifier>; id: Map<string, StableRecordIdentifier> };
501
149
  // type IdentifiersByType = Map<string, IdentifierTypeLookup>;
150
+
502
151
  let configuredForgetMethod;
503
152
  let configuredGenerationMethod;
504
153
  let configuredResetMethod;
@@ -519,6 +168,7 @@ function setIdentifierResetMethod(method) {
519
168
  // Map<type, Map<id, lid>>
520
169
 
521
170
  const NEW_IDENTIFIERS = new Map();
171
+ let IDENTIFIER_CACHE_ID = 0;
522
172
  function updateTypeIdMapping(typeMap, identifier, id) {
523
173
  let idMap = typeMap.get(identifier.type);
524
174
  if (!idMap) {
@@ -601,10 +251,12 @@ class IdentifierCache {
601
251
  this._merge = defaultMergeMethod;
602
252
  this._keyInfoForResource = defaultKeyInfoMethod;
603
253
  this._isDefaultConfig = !configuredGenerationMethod;
254
+ this._id = IDENTIFIER_CACHE_ID++;
604
255
  this._cache = {
605
256
  resources: new Map(),
606
257
  resourcesByType: Object.create(null),
607
- documents: new Map()
258
+ documents: new Map(),
259
+ polymorphicLidBackMap: new Map()
608
260
  };
609
261
  }
610
262
 
@@ -658,7 +310,7 @@ class IdentifierCache {
658
310
  console.log(`Identifiers: ${lid ? 'no ' : ''}lid ${lid ? lid + ' ' : ''}determined for resource`, resource);
659
311
  }
660
312
  let identifier = /*#__NOINLINE__*/getIdentifierFromLid(this._cache, lid, resource);
661
- if (identifier !== undefined) {
313
+ if (identifier !== null) {
662
314
  if (macroCondition(getOwnConfig().debug.LOG_IDENTIFIERS)) {
663
315
  // eslint-disable-next-line no-console
664
316
  console.groupEnd();
@@ -676,11 +328,13 @@ class IdentifierCache {
676
328
  // if we still don't have an identifier, time to generate one
677
329
  if (shouldGenerate === 2) {
678
330
  resource.lid = lid;
331
+ resource[CACHE_OWNER] = this._id;
679
332
  identifier = /*#__NOINLINE__*/makeStableRecordIdentifier(resource, 'record', false);
680
333
  } else {
681
334
  // we lie a bit here as a memory optimization
682
335
  const keyInfo = this._keyInfoForResource(resource, null);
683
336
  keyInfo.lid = lid;
337
+ keyInfo[CACHE_OWNER] = this._id;
684
338
  identifier = /*#__NOINLINE__*/makeStableRecordIdentifier(keyInfo, 'record', false);
685
339
  }
686
340
  addResourceToCache(this._cache, identifier);
@@ -698,7 +352,7 @@ class IdentifierCache {
698
352
  *
699
353
  * @method peekRecordIdentifier
700
354
  * @param resource
701
- * @returns {StableRecordIdentifier | undefined}
355
+ * @return {StableRecordIdentifier | undefined}
702
356
  * @private
703
357
  */
704
358
  peekRecordIdentifier(resource) {
@@ -710,7 +364,7 @@ class IdentifierCache {
710
364
  Returns `null` if the request does not have a `cacheKey` or `url`.
711
365
  @method getOrCreateDocumentIdentifier
712
366
  @param request
713
- @returns {StableDocumentIdentifier | null}
367
+ @return {StableDocumentIdentifier | null}
714
368
  @public
715
369
  */
716
370
  getOrCreateDocumentIdentifier(request) {
@@ -744,7 +398,7 @@ class IdentifierCache {
744
398
  - this referential stability of the object itself is guaranteed
745
399
  @method getOrCreateRecordIdentifier
746
400
  @param resource
747
- @returns {StableRecordIdentifier}
401
+ @return {StableRecordIdentifier}
748
402
  @public
749
403
  */
750
404
  getOrCreateRecordIdentifier(resource) {
@@ -759,15 +413,16 @@ class IdentifierCache {
759
413
  with the signature `generateMethod({ type }, 'record')`.
760
414
  @method createIdentifierForNewRecord
761
415
  @param data
762
- @returns {StableRecordIdentifier}
416
+ @return {StableRecordIdentifier}
763
417
  @public
764
418
  */
765
419
  createIdentifierForNewRecord(data) {
766
- let newLid = this._generate(data, 'record');
767
- let identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
420
+ const newLid = this._generate(data, 'record');
421
+ const identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
768
422
  id: data.id || null,
769
423
  type: data.type,
770
- lid: newLid
424
+ lid: newLid,
425
+ [CACHE_OWNER]: this._id
771
426
  }, 'record', true);
772
427
 
773
428
  // populate our unique table
@@ -801,7 +456,7 @@ class IdentifierCache {
801
456
  @method updateRecordIdentifier
802
457
  @param identifierObject
803
458
  @param data
804
- @returns {StableRecordIdentifier}
459
+ @return {StableRecordIdentifier}
805
460
  @public
806
461
  */
807
462
  updateRecordIdentifier(identifierObject, data) {
@@ -821,7 +476,7 @@ class IdentifierCache {
821
476
  }
822
477
  }
823
478
  if (existingIdentifier) {
824
- let generatedIdentifier = identifier;
479
+ const generatedIdentifier = identifier;
825
480
  identifier = this._mergeRecordIdentifiers(keyInfo, generatedIdentifier, existingIdentifier, data);
826
481
 
827
482
  // make sure that the `lid` on the data we are processing matches the lid we kept
@@ -833,7 +488,7 @@ class IdentifierCache {
833
488
  console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
834
489
  }
835
490
  }
836
- let id = identifier.id;
491
+ const id = identifier.id;
837
492
  /*#__NOINLINE__*/
838
493
  performRecordIdentifierUpdate(identifier, keyInfo, data, this._update);
839
494
  const newId = identifier.id;
@@ -867,16 +522,32 @@ class IdentifierCache {
867
522
  const kept = this._merge(identifier, existingIdentifier, data);
868
523
  const abandoned = kept === identifier ? existingIdentifier : identifier;
869
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
+
870
532
  // cleanup the identifier we no longer need
871
533
  this.forgetRecordIdentifier(abandoned);
872
534
 
873
- // ensure a secondary cache entry for this id for the identifier we do keep
874
- // 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);
875
537
 
876
- // ensure a secondary cache entry for this id for the abandoned identifier's type we do keep
877
- // let baseKeyOptions = getTypeIndex(this._cache.resourcesByType, existingIdentifier.type);
878
- // 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);
879
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);
880
551
  return kept;
881
552
  }
882
553
 
@@ -899,6 +570,17 @@ class IdentifierCache {
899
570
  }
900
571
  this._cache.resources.delete(identifier.lid);
901
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
+ }
580
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
581
+ identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
582
+ }
583
+ identifier[CACHE_OWNER] = undefined;
902
584
  IDENTIFIERS.delete(identifier);
903
585
  this._forget(identifier, 'record');
904
586
  if (macroCondition(getOwnConfig().debug.LOG_IDENTIFIERS)) {
@@ -929,15 +611,33 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
929
611
  get type() {
930
612
  return recordIdentifier.type;
931
613
  },
932
- toString() {
614
+ get [CACHE_OWNER]() {
615
+ return recordIdentifier[CACHE_OWNER];
616
+ },
617
+ set [CACHE_OWNER](value) {
618
+ recordIdentifier[CACHE_OWNER] = value;
619
+ },
620
+ get [DEBUG_STALE_CACHE_OWNER]() {
621
+ return recordIdentifier[DEBUG_STALE_CACHE_OWNER];
622
+ },
623
+ set [DEBUG_STALE_CACHE_OWNER](value) {
624
+ recordIdentifier[DEBUG_STALE_CACHE_OWNER] = value;
625
+ }
626
+ };
627
+ Object.defineProperty(wrapper, 'toString', {
628
+ enumerable: false,
629
+ value: () => {
933
630
  const {
934
631
  type,
935
632
  id,
936
633
  lid
937
634
  } = recordIdentifier;
938
635
  return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
939
- },
940
- toJSON() {
636
+ }
637
+ });
638
+ Object.defineProperty(wrapper, 'toJSON', {
639
+ enumerable: false,
640
+ value: () => {
941
641
  const {
942
642
  type,
943
643
  id,
@@ -949,7 +649,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
949
649
  lid
950
650
  };
951
651
  }
952
- };
652
+ });
953
653
  wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
954
654
  wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
955
655
  IDENTIFIERS.add(wrapper);
@@ -967,7 +667,7 @@ function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
967
667
  } = keyInfo;
968
668
 
969
669
  // get the mutable instance behind our proxy wrapper
970
- let wrapper = identifier;
670
+ const wrapper = identifier;
971
671
  identifier = DEBUG_MAP.get(wrapper);
972
672
  if (hasLid(data)) {
973
673
  const lid = data.lid;
@@ -1022,7 +722,7 @@ function detectMerge(cache, keyInfo, identifier, data) {
1022
722
  // we trigger a merge of the identifiers
1023
723
  // though probably we should just throw an error here
1024
724
  if (id !== null && id === newId && newType === type && hasLid(data) && data.lid !== lid) {
1025
- return cache.resources.get(data.lid) || false;
725
+ return getIdentifierFromLid(cache, data.lid, data) || false;
1026
726
 
1027
727
  // If the lids are the same, and ids are the same, but types are different we should trigger a merge of the identifiers
1028
728
  } else if (id !== null && id === newId && newType && newType !== type && hasLid(data) && data.lid === lid) {
@@ -1039,7 +739,7 @@ function getIdentifierFromLid(cache, lid, resource) {
1039
739
  // eslint-disable-next-line no-console
1040
740
  console.log(`Identifiers: cache ${identifier ? 'HIT' : 'MISS'} - Non-Stable ${lid}`, resource);
1041
741
  }
1042
- return identifier;
742
+ return identifier || null;
1043
743
  }
1044
744
  function addResourceToCache(cache, identifier) {
1045
745
  cache.resources.set(identifier.lid, identifier);
@@ -1056,28 +756,27 @@ function addResourceToCache(cache, identifier) {
1056
756
  typeSet.id.set(identifier.id, identifier);
1057
757
  }
1058
758
  }
1059
- var _class$1, _descriptor$1;
1060
759
 
1061
760
  /**
1062
761
  @module @ember-data/store
1063
762
  */
763
+
1064
764
  /**
1065
765
  @module @ember-data/store
1066
766
  */
767
+
1067
768
  /**
1068
769
  A `RecordReference` is a low-level API that allows users and
1069
770
  addon authors to perform meta-operations on a record.
1070
771
 
1071
772
  @class RecordReference
1072
773
  @public
1073
- @extends Reference
1074
774
  */
1075
- let RecordReference = (_class$1 = class RecordReference {
775
+ class RecordReference {
1076
776
  constructor(store, identifier) {
1077
777
  // unsubscribe token given to us by the notification manager
1078
778
  this.___token = void 0;
1079
779
  this.___identifier = void 0;
1080
- _initializerDefineProperty(this, "_ref", _descriptor$1, this);
1081
780
  this.store = store;
1082
781
  this.___identifier = identifier;
1083
782
  this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
@@ -1245,14 +944,8 @@ let RecordReference = (_class$1 = class RecordReference {
1245
944
  }
1246
945
  assert(`Unable to fetch record of type ${this.type} without an id`);
1247
946
  }
1248
- }, _descriptor$1 = _applyDecoratedDescriptor(_class$1.prototype, "_ref", [tracked], {
1249
- configurable: true,
1250
- enumerable: true,
1251
- writable: true,
1252
- initializer: function () {
1253
- return 0;
1254
- }
1255
- }), _class$1);
947
+ }
948
+ defineSignal(RecordReference.prototype, '_ref');
1256
949
 
1257
950
  /**
1258
951
  @module @ember-data/store
@@ -1283,6 +976,8 @@ class CacheCapabilitiesManager {
1283
976
  if (this._store._cbs) {
1284
977
  this._store._schedule('notify', () => this._flushNotifications());
1285
978
  } else {
979
+ // TODO @runspired determine if relationship mutations should schedule
980
+ // into join/run vs immediate flush
1286
981
  this._flushNotifications();
1287
982
  }
1288
983
  }
@@ -1290,7 +985,7 @@ class CacheCapabilitiesManager {
1290
985
  if (this._willNotify === false) {
1291
986
  return;
1292
987
  }
1293
- let pending = this._pendingNotifies;
988
+ const pending = this._pendingNotifies;
1294
989
  this._pendingNotifies = new Map();
1295
990
  this._willNotify = false;
1296
991
  pending.forEach((set, identifier) => {
@@ -1314,6 +1009,9 @@ class CacheCapabilitiesManager {
1314
1009
  getSchemaDefinitionService() {
1315
1010
  return this._store.getSchemaDefinitionService();
1316
1011
  }
1012
+ get schema() {
1013
+ return this._store.schema;
1014
+ }
1317
1015
  setRecordId(identifier, id) {
1318
1016
  assert(`Expected a stable identifier`, isStableIdentifier(identifier));
1319
1017
  this._store._instanceCache.setRecordId(identifier, id);
@@ -1347,6 +1045,9 @@ function peekCache(instance) {
1347
1045
  }
1348
1046
  return null;
1349
1047
  }
1048
+ function isDestroyable(record) {
1049
+ return Boolean(record && typeof record === 'object' && typeof record.destroy === 'function');
1050
+ }
1350
1051
 
1351
1052
  /**
1352
1053
  @module @ember-data/store
@@ -1374,8 +1075,9 @@ function peekRecordIdentifier(record) {
1374
1075
  @static
1375
1076
  @for @ember-data/store
1376
1077
  @param {Object} record a record instance previously obstained from the store.
1377
- @returns {StableRecordIdentifier}
1078
+ @return {StableRecordIdentifier}
1378
1079
  */
1080
+
1379
1081
  function recordIdentifierFor(record) {
1380
1082
  assert(`${String(record)} is not a record instantiated by @ember-data/store`, RecordCache.has(record));
1381
1083
  return RecordCache.get(record);
@@ -1421,11 +1123,11 @@ class InstanceCache {
1421
1123
  // @ts-expect-error TODO this needs to be fixed
1422
1124
  'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
1423
1125
  }
1424
- let staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1126
+ const staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1425
1127
 
1426
1128
  // check for duplicate entities
1427
- let keptHasRecord = this.__instances.record.has(keptIdentifier);
1428
- let staleHasRecord = this.__instances.record.has(staleIdentifier);
1129
+ const keptHasRecord = this.__instances.record.has(keptIdentifier);
1130
+ const staleHasRecord = this.__instances.record.has(staleIdentifier);
1429
1131
 
1430
1132
  // we cannot merge entities when both have records
1431
1133
  // (this may not be strictly true, we could probably swap the cache data the record points at)
@@ -1479,7 +1181,7 @@ class InstanceCache {
1479
1181
  return record;
1480
1182
  }
1481
1183
  getReference(identifier) {
1482
- let cache = this.__instances.reference;
1184
+ const cache = this.__instances.reference;
1483
1185
  let reference = cache.get(identifier);
1484
1186
  if (!reference) {
1485
1187
  reference = new RecordReference(this.store, identifier);
@@ -1510,7 +1212,7 @@ class InstanceCache {
1510
1212
  }
1511
1213
  disconnect(identifier) {
1512
1214
  const record = this.__instances.record.get(identifier);
1513
- assert('Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying);
1215
+ assert('Cannot destroy record while it is still materialized', !isDestroyable(record) || record.isDestroyed || record.isDestroying);
1514
1216
  this.store._graph?.remove(identifier);
1515
1217
  this.store.identifierCache.forgetRecordIdentifier(identifier);
1516
1218
  removeRecordDataFor(identifier);
@@ -1578,7 +1280,7 @@ class InstanceCache {
1578
1280
  });
1579
1281
  } else {
1580
1282
  const typeCache = cache.resourcesByType;
1581
- let identifiers = typeCache[type]?.lid;
1283
+ const identifiers = typeCache[type]?.lid;
1582
1284
  if (identifiers) {
1583
1285
  identifiers.forEach(identifier => {
1584
1286
  // if (rds.has(identifier)) {
@@ -1596,7 +1298,7 @@ class InstanceCache {
1596
1298
  type,
1597
1299
  lid
1598
1300
  } = identifier;
1599
- let oldId = identifier.id;
1301
+ const oldId = identifier.id;
1600
1302
 
1601
1303
  // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
1602
1304
  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));
@@ -1615,7 +1317,7 @@ class InstanceCache {
1615
1317
  // eslint-disable-next-line no-console
1616
1318
  console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
1617
1319
  }
1618
- let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1320
+ const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1619
1321
  type,
1620
1322
  id
1621
1323
  });
@@ -1654,13 +1356,13 @@ function resourceIsFullyDeleted(instanceCache, identifier) {
1654
1356
  */
1655
1357
 
1656
1358
  function preloadData(store, identifier, preload) {
1657
- let jsonPayload = {};
1359
+ const jsonPayload = {};
1658
1360
  //TODO(Igor) consider the polymorphic case
1659
1361
  const schemas = store.getSchemaDefinitionService();
1660
1362
  const relationships = schemas.relationshipsDefinitionFor(identifier);
1661
1363
  Object.keys(preload).forEach(key => {
1662
- let preloadValue = preload[key];
1663
- let relationshipMeta = relationships[key];
1364
+ const preloadValue = preload[key];
1365
+ const relationshipMeta = relationships[key];
1664
1366
  if (relationshipMeta) {
1665
1367
  if (!jsonPayload.relationships) {
1666
1368
  jsonPayload.relationships = {};
@@ -1729,7 +1431,7 @@ function getShimClass(store, modelName) {
1729
1431
  }
1730
1432
  function mapFromHash(hash) {
1731
1433
  const map = new Map();
1732
- for (let i in hash) {
1434
+ for (const i in hash) {
1733
1435
  if (Object.prototype.hasOwnProperty.call(hash, i)) {
1734
1436
  map.set(i, hash[i]);
1735
1437
  }
@@ -1744,31 +1446,31 @@ class ShimModelClass {
1744
1446
  this.modelName = modelName;
1745
1447
  }
1746
1448
  get fields() {
1747
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1449
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1748
1450
  type: this.modelName
1749
1451
  });
1750
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1452
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1751
1453
  type: this.modelName
1752
1454
  });
1753
- let fields = new Map();
1455
+ const fields = new Map();
1754
1456
  Object.keys(attrs).forEach(key => fields.set(key, 'attribute'));
1755
1457
  Object.keys(relationships).forEach(key => fields.set(key, relationships[key].kind));
1756
1458
  return fields;
1757
1459
  }
1758
1460
  get attributes() {
1759
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1461
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1760
1462
  type: this.modelName
1761
1463
  });
1762
1464
  return mapFromHash(attrs);
1763
1465
  }
1764
1466
  get relationshipsByName() {
1765
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1467
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1766
1468
  type: this.modelName
1767
1469
  });
1768
1470
  return mapFromHash(relationships);
1769
1471
  }
1770
1472
  eachAttribute(callback, binding) {
1771
- let attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1473
+ const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1772
1474
  type: this.modelName
1773
1475
  });
1774
1476
  Object.keys(attrDefs).forEach(key => {
@@ -1776,7 +1478,7 @@ class ShimModelClass {
1776
1478
  });
1777
1479
  }
1778
1480
  eachRelationship(callback, binding) {
1779
- let relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1481
+ const relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1780
1482
  type: this.modelName
1781
1483
  });
1782
1484
  Object.keys(relationshipDefs).forEach(key => {
@@ -1794,6 +1496,16 @@ class ShimModelClass {
1794
1496
  });
1795
1497
  }
1796
1498
  }
1499
+ function _classPrivateFieldBase(receiver, privateKey) {
1500
+ if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
1501
+ throw new TypeError("attempted to use private field on non-instance");
1502
+ }
1503
+ return receiver;
1504
+ }
1505
+ var id = 0;
1506
+ function _classPrivateFieldKey(name) {
1507
+ return "__private_" + id++ + "_" + name;
1508
+ }
1797
1509
  var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
1798
1510
  /**
1799
1511
  * The CacheManager wraps a Cache enforcing that only
@@ -1820,16 +1532,6 @@ class CacheManager {
1820
1532
  writable: true,
1821
1533
  value: void 0
1822
1534
  });
1823
- /**
1824
- * Query the cache for whether a given resource has been deleted and that deletion
1825
- * has also been persisted.
1826
- *
1827
- * @method isDeletionCommitted
1828
- * @public
1829
- * @param identifier
1830
- * @returns {boolean}
1831
- */
1832
- this.isDel = void 0;
1833
1535
  _classPrivateFieldBase(this, _cache)[_cache] = cache;
1834
1536
  }
1835
1537
 
@@ -1843,7 +1545,7 @@ class CacheManager {
1843
1545
  * semantics, `put` has `replace` semantics similar to
1844
1546
  * the `http` method `PUT`
1845
1547
  *
1846
- * the individually cacheabl
1548
+ * the individually cacheable
1847
1549
  * e resource data it may contain
1848
1550
  * should upsert, but the document data surrounding it should
1849
1551
  * fully replace any existing information
@@ -1856,7 +1558,7 @@ class CacheManager {
1856
1558
  *
1857
1559
  * @method put
1858
1560
  * @param {StructuredDocument} doc
1859
- * @returns {ResourceDocument}
1561
+ * @return {ResourceDocument}
1860
1562
  * @public
1861
1563
  */
1862
1564
  put(doc) {
@@ -1872,7 +1574,7 @@ class CacheManager {
1872
1574
  * @method patch
1873
1575
  * @public
1874
1576
  * @param op the operation to perform
1875
- * @returns {void}
1577
+ * @return {void}
1876
1578
  */
1877
1579
  patch(op) {
1878
1580
  _classPrivateFieldBase(this, _cache)[_cache].patch(op);
@@ -1907,7 +1609,7 @@ class CacheManager {
1907
1609
  * An implementation might want to do this because
1908
1610
  * de-referencing records which read from their own
1909
1611
  * blob is generally safer because the record does
1910
- * not require retainining connections to the Store
1612
+ * not require retaining connections to the Store
1911
1613
  * and Cache to present data on a per-field basis.
1912
1614
  *
1913
1615
  * This generally takes the place of `getAttr` as
@@ -1920,7 +1622,7 @@ class CacheManager {
1920
1622
  * @method peek
1921
1623
  * @public
1922
1624
  * @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
1923
- * @returns {ResourceDocument | ResourceBlob | null} the known resource data
1625
+ * @return {ResourceDocument | ResourceBlob | null} the known resource data
1924
1626
  */
1925
1627
 
1926
1628
  peek(identifier) {
@@ -1933,7 +1635,7 @@ class CacheManager {
1933
1635
  *
1934
1636
  * @method peekRequest
1935
1637
  * @param {StableDocumentIdentifier}
1936
- * @returns {StableDocumentIdentifier | null}
1638
+ * @return {StableDocumentIdentifier | null}
1937
1639
  * @public
1938
1640
  */
1939
1641
  peekRequest(identifier) {
@@ -1948,7 +1650,7 @@ class CacheManager {
1948
1650
  * @param identifier
1949
1651
  * @param data
1950
1652
  * @param hasRecord
1951
- * @returns {void | string[]} if `hasRecord` is true then calculated key changes should be returned
1653
+ * @return {void | string[]} if `hasRecord` is true then calculated key changes should be returned
1952
1654
  */
1953
1655
  upsert(identifier, data, hasRecord) {
1954
1656
  return _classPrivateFieldBase(this, _cache)[_cache].upsert(identifier, data, hasRecord);
@@ -1966,7 +1668,7 @@ class CacheManager {
1966
1668
  *
1967
1669
  * @method fork
1968
1670
  * @public
1969
- * @returns Promise<Cache>
1671
+ * @return Promise<Cache>
1970
1672
  */
1971
1673
  fork() {
1972
1674
  return _classPrivateFieldBase(this, _cache)[_cache].fork();
@@ -1982,7 +1684,7 @@ class CacheManager {
1982
1684
  * @method merge
1983
1685
  * @param {Cache} cache
1984
1686
  * @public
1985
- * @returns Promise<void>
1687
+ * @return Promise<void>
1986
1688
  */
1987
1689
  merge(cache) {
1988
1690
  return _classPrivateFieldBase(this, _cache)[_cache].merge(cache);
@@ -2034,7 +1736,7 @@ class CacheManager {
2034
1736
  * via `cache.hydrate`.
2035
1737
  *
2036
1738
  * @method dump
2037
- * @returns {Promise<ReadableStream>}
1739
+ * @return {Promise<ReadableStream>}
2038
1740
  * @public
2039
1741
  */
2040
1742
  dump() {
@@ -2055,7 +1757,7 @@ class CacheManager {
2055
1757
  *
2056
1758
  * @method hydrate
2057
1759
  * @param {ReadableStream} stream
2058
- * @returns {Promise<void>}
1760
+ * @return {Promise<void>}
2059
1761
  * @public
2060
1762
  */
2061
1763
  hydrate(stream) {
@@ -2069,7 +1771,7 @@ class CacheManager {
2069
1771
  // ================
2070
1772
 
2071
1773
  /**
2072
- * [LIFECYLCE] Signal to the cache that a new record has been instantiated on the client
1774
+ * [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client
2073
1775
  *
2074
1776
  * It returns properties from options that should be set on the record during the create
2075
1777
  * process. This return value behavior is deprecated.
@@ -2143,7 +1845,7 @@ class CacheManager {
2143
1845
  * @public
2144
1846
  * @param identifier
2145
1847
  * @param propertyName
2146
- * @returns {unknown}
1848
+ * @return {unknown}
2147
1849
  */
2148
1850
  getAttr(identifier, propertyName) {
2149
1851
  return _classPrivateFieldBase(this, _cache)[_cache].getAttr(identifier, propertyName);
@@ -2168,7 +1870,7 @@ class CacheManager {
2168
1870
  * @method changedAttrs
2169
1871
  * @public
2170
1872
  * @param identifier
2171
- * @returns
1873
+ * @return
2172
1874
  */
2173
1875
  changedAttrs(identifier) {
2174
1876
  return _classPrivateFieldBase(this, _cache)[_cache].changedAttrs(identifier);
@@ -2180,7 +1882,7 @@ class CacheManager {
2180
1882
  * @method hasChangedAttrs
2181
1883
  * @public
2182
1884
  * @param identifier
2183
- * @returns {boolean}
1885
+ * @return {boolean}
2184
1886
  */
2185
1887
  hasChangedAttrs(identifier) {
2186
1888
  return _classPrivateFieldBase(this, _cache)[_cache].hasChangedAttrs(identifier);
@@ -2192,7 +1894,7 @@ class CacheManager {
2192
1894
  * @method rollbackAttrs
2193
1895
  * @public
2194
1896
  * @param identifier
2195
- * @returns the names of attributes that were restored
1897
+ * @return the names of attributes that were restored
2196
1898
  */
2197
1899
  rollbackAttrs(identifier) {
2198
1900
  return _classPrivateFieldBase(this, _cache)[_cache].rollbackAttrs(identifier);
@@ -2201,6 +1903,65 @@ class CacheManager {
2201
1903
  // Relationships
2202
1904
  // =============
2203
1905
 
1906
+ /**
1907
+ * Query the cache for the changes to relationships of a resource.
1908
+ *
1909
+ * Returns a map of relationship names to RelationshipDiff objects.
1910
+ *
1911
+ * ```ts
1912
+ * type RelationshipDiff =
1913
+ | {
1914
+ kind: 'collection';
1915
+ remoteState: StableRecordIdentifier[];
1916
+ additions: Set<StableRecordIdentifier>;
1917
+ removals: Set<StableRecordIdentifier>;
1918
+ localState: StableRecordIdentifier[];
1919
+ reordered: boolean;
1920
+ }
1921
+ | {
1922
+ kind: 'resource';
1923
+ remoteState: StableRecordIdentifier | null;
1924
+ localState: StableRecordIdentifier | null;
1925
+ };
1926
+ ```
1927
+ *
1928
+ * @method changedRelationships
1929
+ * @public
1930
+ * @param {StableRecordIdentifier} identifier
1931
+ * @return {Map<string, RelationshipDiff>}
1932
+ */
1933
+ changedRelationships(identifier) {
1934
+ return _classPrivateFieldBase(this, _cache)[_cache].changedRelationships(identifier);
1935
+ }
1936
+
1937
+ /**
1938
+ * Query the cache for whether any mutated attributes exist
1939
+ *
1940
+ * @method hasChangedRelationships
1941
+ * @public
1942
+ * @param {StableRecordIdentifier} identifier
1943
+ * @return {boolean}
1944
+ */
1945
+ hasChangedRelationships(identifier) {
1946
+ return _classPrivateFieldBase(this, _cache)[_cache].hasChangedRelationships(identifier);
1947
+ }
1948
+
1949
+ /**
1950
+ * Tell the cache to discard any uncommitted mutations to relationships.
1951
+ *
1952
+ * This will also discard the change on any appropriate inverses.
1953
+ *
1954
+ * This method is a candidate to become a mutation
1955
+ *
1956
+ * @method rollbackRelationships
1957
+ * @public
1958
+ * @param {StableRecordIdentifier} identifier
1959
+ * @return {string[]} the names of relationships that were restored
1960
+ */
1961
+ rollbackRelationships(identifier) {
1962
+ return _classPrivateFieldBase(this, _cache)[_cache].rollbackRelationships(identifier);
1963
+ }
1964
+
2204
1965
  /**
2205
1966
  * Query the cache for the current state of a relationship property
2206
1967
  *
@@ -2208,7 +1969,7 @@ class CacheManager {
2208
1969
  * @public
2209
1970
  * @param identifier
2210
1971
  * @param propertyName
2211
- * @returns resource relationship object
1972
+ * @return resource relationship object
2212
1973
  */
2213
1974
  getRelationship(identifier, propertyName) {
2214
1975
  return _classPrivateFieldBase(this, _cache)[_cache].getRelationship(identifier, propertyName);
@@ -2236,7 +1997,7 @@ class CacheManager {
2236
1997
  * @method getErrors
2237
1998
  * @public
2238
1999
  * @param identifier
2239
- * @returns
2000
+ * @return
2240
2001
  */
2241
2002
  getErrors(identifier) {
2242
2003
  return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier);
@@ -2248,7 +2009,7 @@ class CacheManager {
2248
2009
  * @method isEmpty
2249
2010
  * @public
2250
2011
  * @param identifier
2251
- * @returns {boolean}
2012
+ * @return {boolean}
2252
2013
  */
2253
2014
  isEmpty(identifier) {
2254
2015
  return _classPrivateFieldBase(this, _cache)[_cache].isEmpty(identifier);
@@ -2261,7 +2022,7 @@ class CacheManager {
2261
2022
  * @method isNew
2262
2023
  * @public
2263
2024
  * @param identifier
2264
- * @returns {boolean}
2025
+ * @return {boolean}
2265
2026
  */
2266
2027
  isNew(identifier) {
2267
2028
  return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier);
@@ -2274,15 +2035,29 @@ class CacheManager {
2274
2035
  * @method isDeleted
2275
2036
  * @public
2276
2037
  * @param identifier
2277
- * @returns {boolean}
2038
+ * @return {boolean}
2278
2039
  */
2279
2040
  isDeleted(identifier) {
2280
2041
  return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier);
2281
2042
  }
2043
+
2044
+ /**
2045
+ * Query the cache for whether a given resource has been deleted and that deletion
2046
+ * has also been persisted.
2047
+ *
2048
+ * @method isDeletionCommitted
2049
+ * @public
2050
+ * @param identifier
2051
+ * @return {boolean}
2052
+ */
2282
2053
  isDeletionCommitted(identifier) {
2283
2054
  return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier);
2284
2055
  }
2285
2056
  }
2057
+
2058
+ /**
2059
+ * @module @ember-data/store
2060
+ */
2286
2061
  let tokenId = 0;
2287
2062
  const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
2288
2063
  function isCacheOperationValue(value) {
@@ -2293,7 +2068,7 @@ function runLoopIsFlushing() {
2293
2068
  return !!_backburner.currentInstance && _backburner._autorun !== true;
2294
2069
  }
2295
2070
  function _unsubscribe(tokens, token, cache) {
2296
- let identifier = tokens.get(token);
2071
+ const identifier = tokens.get(token);
2297
2072
  if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
2298
2073
  if (!identifier) {
2299
2074
  // eslint-disable-next-line no-console
@@ -2352,7 +2127,7 @@ class NotificationManager {
2352
2127
  * @public
2353
2128
  * @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
2354
2129
  * @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
2355
- * @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
2130
+ * @return {UnsubscribeToken} an opaque token to be used with unsubscribe
2356
2131
  */
2357
2132
 
2358
2133
  subscribe(identifier, callback) {
@@ -2362,7 +2137,7 @@ class NotificationManager {
2362
2137
  map = new Map();
2363
2138
  this._cache.set(identifier, map);
2364
2139
  }
2365
- let unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2140
+ const unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2366
2141
  _tokenRef: tokenId++
2367
2142
  } : {};
2368
2143
  map.set(unsubToken, callback);
@@ -2415,7 +2190,7 @@ class NotificationManager {
2415
2190
  this._buffered.set(identifier, buffer);
2416
2191
  }
2417
2192
  buffer.push([value, key]);
2418
- void this._scheduleNotify();
2193
+ this._scheduleNotify();
2419
2194
  }
2420
2195
  return hasSubscribers;
2421
2196
  }
@@ -2457,14 +2232,14 @@ class NotificationManager {
2457
2232
 
2458
2233
  // TODO for documents this will need to switch based on Identifier kind
2459
2234
  if (isCacheOperationValue(value)) {
2460
- let callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2235
+ const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2461
2236
  if (callbackMap) {
2462
2237
  callbackMap.forEach(cb => {
2463
2238
  cb(identifier, value);
2464
2239
  });
2465
2240
  }
2466
2241
  }
2467
- let callbackMap = this._cache.get(identifier);
2242
+ const callbackMap = this._cache.get(identifier);
2468
2243
  if (!callbackMap || !callbackMap.size) {
2469
2244
  return false;
2470
2245
  }
@@ -2480,7 +2255,30 @@ class NotificationManager {
2480
2255
  this._cache.clear();
2481
2256
  }
2482
2257
  }
2483
- var _class, _descriptor, _class3, _descriptor2;
2258
+ function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
2259
+ var desc = {};
2260
+ Object.keys(descriptor).forEach(function (key) {
2261
+ desc[key] = descriptor[key];
2262
+ });
2263
+ desc.enumerable = !!desc.enumerable;
2264
+ desc.configurable = !!desc.configurable;
2265
+ if ('value' in desc || desc.initializer) {
2266
+ desc.writable = true;
2267
+ }
2268
+ desc = decorators.slice().reverse().reduce(function (desc, decorator) {
2269
+ return decorator(target, property, desc) || desc;
2270
+ }, desc);
2271
+ if (context && desc.initializer !== void 0) {
2272
+ desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
2273
+ desc.initializer = undefined;
2274
+ }
2275
+ if (desc.initializer === void 0) {
2276
+ Object.defineProperty(target, property, desc);
2277
+ desc = null;
2278
+ }
2279
+ return desc;
2280
+ }
2281
+ var _class;
2484
2282
  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']);
2485
2283
  const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
2486
2284
  const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
@@ -2490,19 +2288,16 @@ function isArrayGetter(prop) {
2490
2288
  function isArraySetter(prop) {
2491
2289
  return ARRAY_SETTER_METHODS.has(prop);
2492
2290
  }
2493
- const IDENTIFIER_ARRAY_TAG = Symbol('#tag');
2291
+ function isSelfProp(self, prop) {
2292
+ return prop in self;
2293
+ }
2294
+ const ARRAY_SIGNAL = Symbol('#signal');
2494
2295
  const SOURCE = Symbol('#source');
2495
2296
  const MUTATE = Symbol('#update');
2496
2297
  const NOTIFY = Symbol('#notify');
2497
2298
  const IS_COLLECTION = Symbol.for('Collection');
2498
2299
  function notifyArray(arr) {
2499
- addToTransaction(arr[IDENTIFIER_ARRAY_TAG]);
2500
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
2501
- // eslint-disable-next-line
2502
- dirtyTag(tagForProperty(arr, 'length'));
2503
- // eslint-disable-next-line
2504
- dirtyTag(tagForProperty(arr, '[]'));
2505
- }
2300
+ addToTransaction(arr[ARRAY_SIGNAL]);
2506
2301
  }
2507
2302
  function convertToInt(prop) {
2508
2303
  if (typeof prop === 'symbol') return null;
@@ -2510,29 +2305,6 @@ function convertToInt(prop) {
2510
2305
  if (isNaN(num)) return null;
2511
2306
  return num % 1 === 0 ? num : null;
2512
2307
  }
2513
- let Tag = (_class = class Tag {
2514
- /*
2515
- * whether this was part of a transaction when last mutated
2516
- */
2517
-
2518
- constructor() {
2519
- _initializerDefineProperty(this, "ref", _descriptor, this);
2520
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2521
- const [arr, prop] = arguments;
2522
- this._debug_base = arr.constructor.name + ':' + String(arr.modelName);
2523
- this._debug_prop = prop;
2524
- }
2525
- this.shouldReset = false;
2526
- this.t = false;
2527
- }
2528
- }, _descriptor = _applyDecoratedDescriptor(_class.prototype, "ref", [tracked], {
2529
- configurable: true,
2530
- enumerable: true,
2531
- writable: true,
2532
- initializer: function () {
2533
- return null;
2534
- }
2535
- }), _class);
2536
2308
  function safeForEach(instance, arr, store, callback, target) {
2537
2309
  if (target === undefined) {
2538
2310
  target = null;
@@ -2565,7 +2337,7 @@ function safeForEach(instance, arr, store, callback, target) {
2565
2337
  @class RecordArray
2566
2338
  @public
2567
2339
  */
2568
- let IdentifierArray = (_class3 = class IdentifierArray {
2340
+ let IdentifierArray = (_class = class IdentifierArray {
2569
2341
  [NOTIFY]() {
2570
2342
  notifyArray(this);
2571
2343
  }
@@ -2593,14 +2365,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2593
2365
  set length(value) {
2594
2366
  this[SOURCE].length = value;
2595
2367
  }
2596
-
2597
- // here to support computed chains
2598
- // and {{#each}}
2599
- get '[]'() {
2600
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
2601
- return this;
2602
- }
2603
- }
2604
2368
  constructor(options) {
2605
2369
  /**
2606
2370
  The flag to signal a `RecordArray` is currently loading data.
@@ -2615,7 +2379,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2615
2379
  @public
2616
2380
  @type Boolean
2617
2381
  */
2618
- _initializerDefineProperty(this, "isUpdating", _descriptor2, this);
2619
2382
  this.isLoaded = true;
2620
2383
  this.isDestroying = false;
2621
2384
  this.isDestroyed = false;
@@ -2623,16 +2386,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2623
2386
  this[IS_COLLECTION] = true;
2624
2387
  this[SOURCE] = void 0;
2625
2388
  // eslint-disable-next-line @typescript-eslint/no-this-alias
2626
- let self = this;
2389
+ const self = this;
2627
2390
  this.modelName = options.type;
2628
2391
  this.store = options.store;
2629
2392
  this._manager = options.manager;
2630
2393
  this[SOURCE] = options.identifiers;
2631
- // @ts-expect-error
2632
- this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
2394
+ this[ARRAY_SIGNAL] = createSignal(this, 'length');
2633
2395
  const store = options.store;
2634
2396
  const boundFns = new Map();
2635
- const _TAG = this[IDENTIFIER_ARRAY_TAG];
2397
+ const _SIGNAL = this[ARRAY_SIGNAL];
2636
2398
  const PrivateState = {
2637
2399
  links: options.links || null,
2638
2400
  meta: options.meta || null
@@ -2645,40 +2407,40 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2645
2407
 
2646
2408
  const proxy = new Proxy(this[SOURCE], {
2647
2409
  get(target, prop, receiver) {
2648
- let index = convertToInt(prop);
2649
- if (_TAG.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2410
+ const index = convertToInt(prop);
2411
+ if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2650
2412
  options.manager._syncArray(receiver);
2651
- _TAG.t = false;
2652
- _TAG.shouldReset = false;
2413
+ _SIGNAL.t = false;
2414
+ _SIGNAL.shouldReset = false;
2653
2415
  }
2654
2416
  if (index !== null) {
2655
2417
  const identifier = target[index];
2656
2418
  if (!transaction) {
2657
- subscribe(_TAG);
2419
+ subscribe(_SIGNAL);
2658
2420
  }
2659
2421
  return identifier && store._instanceCache.getRecord(identifier);
2660
2422
  }
2661
- if (prop === 'meta') return subscribe(_TAG), PrivateState.meta;
2662
- if (prop === 'links') return subscribe(_TAG), PrivateState.links;
2663
- if (prop === '[]') return subscribe(_TAG), receiver;
2423
+ if (prop === 'meta') return subscribe(_SIGNAL), PrivateState.meta;
2424
+ if (prop === 'links') return subscribe(_SIGNAL), PrivateState.links;
2425
+ if (prop === '[]') return subscribe(_SIGNAL), receiver;
2664
2426
  if (isArrayGetter(prop)) {
2665
2427
  let fn = boundFns.get(prop);
2666
2428
  if (fn === undefined) {
2667
2429
  if (prop === 'forEach') {
2668
2430
  fn = function () {
2669
- subscribe(_TAG);
2431
+ subscribe(_SIGNAL);
2670
2432
  transaction = true;
2671
- let result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2433
+ const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2672
2434
  transaction = false;
2673
2435
  return result;
2674
2436
  };
2675
2437
  } else {
2676
2438
  fn = function () {
2677
- subscribe(_TAG);
2439
+ subscribe(_SIGNAL);
2678
2440
  // array functions must run through Reflect to work properly
2679
2441
  // binding via other means will not work.
2680
2442
  transaction = true;
2681
- let result = Reflect.apply(target[prop], receiver, arguments);
2443
+ const result = Reflect.apply(target[prop], receiver, arguments);
2682
2444
  transaction = false;
2683
2445
  return result;
2684
2446
  };
@@ -2700,10 +2462,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2700
2462
  const args = Array.prototype.slice.call(arguments);
2701
2463
  assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
2702
2464
  transaction = true;
2703
- let result = Reflect.apply(target[prop], receiver, args);
2704
- self[MUTATE](prop, args, result);
2705
- addToTransaction(_TAG);
2706
- // TODO handle cache updates
2465
+ const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
2707
2466
  transaction = false;
2708
2467
  return result;
2709
2468
  };
@@ -2711,16 +2470,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2711
2470
  }
2712
2471
  return fn;
2713
2472
  }
2714
- if (prop in self) {
2715
- if (prop === NOTIFY || prop === IDENTIFIER_ARRAY_TAG || prop === SOURCE) {
2473
+ if (isSelfProp(self, prop)) {
2474
+ if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
2716
2475
  return self[prop];
2717
2476
  }
2718
2477
  let fn = boundFns.get(prop);
2719
2478
  if (fn) return fn;
2720
- let outcome = self[prop];
2479
+ const outcome = self[prop];
2721
2480
  if (typeof outcome === 'function') {
2722
2481
  fn = function () {
2723
- subscribe(_TAG);
2482
+ subscribe(_SIGNAL);
2724
2483
  // array functions must run through Reflect to work properly
2725
2484
  // binding via other means will not work.
2726
2485
  return Reflect.apply(outcome, receiver, arguments);
@@ -2728,17 +2487,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2728
2487
  boundFns.set(prop, fn);
2729
2488
  return fn;
2730
2489
  }
2731
- return subscribe(_TAG), outcome;
2490
+ return subscribe(_SIGNAL), outcome;
2732
2491
  }
2733
2492
  return target[prop];
2734
2493
  },
2735
- set(target, prop, value) {
2494
+ // FIXME: Should this get a generic like get above?
2495
+ set(target, prop, value, receiver) {
2736
2496
  if (prop === 'length') {
2737
2497
  if (!transaction && value === 0) {
2738
2498
  transaction = true;
2739
- addToTransaction(_TAG);
2740
- Reflect.set(target, prop, value);
2741
- self[MUTATE]('length 0', []);
2499
+ self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
2742
2500
  transaction = false;
2743
2501
  return true;
2744
2502
  } else if (transaction) {
@@ -2755,9 +2513,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2755
2513
  PrivateState.meta = value || null;
2756
2514
  return true;
2757
2515
  }
2758
- let index = convertToInt(prop);
2516
+ const index = convertToInt(prop);
2517
+
2518
+ // we do not allow "holey" arrays and so if the index is
2519
+ // greater than length then we will disallow setting it.
2520
+ // however, there is a special case for "unshift" with more than
2521
+ // one item being inserted since current items will be moved to the
2522
+ // new indices first.
2523
+ // we "loosely" detect this by just checking whether we are in
2524
+ // a transaction.
2759
2525
  if (index === null || index > target.length) {
2760
- if (prop in self) {
2526
+ if (index !== null && transaction) {
2527
+ const identifier = recordIdentifierFor(value);
2528
+ assert(`Cannot set index ${index} past the end of the array.`, isStableIdentifier(identifier));
2529
+ target[index] = identifier;
2530
+ return true;
2531
+ } else if (isSelfProp(self, prop)) {
2532
+ // @ts-expect-error not all properties are indeces and we can't safely cast
2761
2533
  self[prop] = value;
2762
2534
  return true;
2763
2535
  }
@@ -2767,12 +2539,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2767
2539
  assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
2768
2540
  return false;
2769
2541
  }
2770
- let original = target[index];
2771
- let newIdentifier = extractIdentifierFromRecord$1(value);
2542
+ const original = target[index];
2543
+ const newIdentifier = extractIdentifierFromRecord$1(value);
2772
2544
  target[index] = newIdentifier;
2545
+ assert(`Expected a record`, isStableIdentifier(newIdentifier));
2546
+ // We generate "transactions" whenever a setter method on the array
2547
+ // is called and might bulk update multiple array cells. Fundamentally,
2548
+ // all array operations decompose into individual cell replacements.
2549
+ // e.g. a push is really a "replace cell at next index with new value"
2550
+ // or a splice is "shift all values left/right by X and set out of new
2551
+ // bounds cells to undefined"
2552
+ //
2553
+ // so, if we are in a transaction, then this is not a user generated change
2554
+ // but one generated by a setter method. In this case we want to only apply
2555
+ // the change to the target array and not call the MUTATE method.
2556
+ // If there is no transaction though, then this means the user themselves has
2557
+ // directly changed the value of a specific index and we need to thus generate
2558
+ // a mutation for that change.
2559
+ // e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
2560
+ // a transaction.
2561
+ // while "arr[arr.length] = newVal;" is handled by this replace cell code path.
2773
2562
  if (!transaction) {
2774
- self[MUTATE]('replace cell', [index, original, newIdentifier]);
2775
- addToTransaction(_TAG);
2563
+ self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
2564
+ } else {
2565
+ target[index] = newIdentifier;
2776
2566
  }
2777
2567
  return true;
2778
2568
  },
@@ -2787,12 +2577,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2787
2577
  return IdentifierArray.prototype;
2788
2578
  }
2789
2579
  });
2790
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2791
- const meta = Ember.meta(this);
2792
- meta.hasMixin = mixin => {
2793
- assert(`Do not call A() on EmberData RecordArrays`);
2794
- };
2795
- }
2580
+ createArrayTags(proxy, _SIGNAL);
2796
2581
  this[NOTIFY] = this[NOTIFY].bind(proxy);
2797
2582
  return proxy;
2798
2583
  }
@@ -2817,8 +2602,8 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2817
2602
  return this._updatingPromise;
2818
2603
  }
2819
2604
  this.isUpdating = true;
2820
- let updatingPromise = this._update();
2821
- updatingPromise.finally(() => {
2605
+ const updatingPromise = this._update();
2606
+ void updatingPromise.finally(() => {
2822
2607
  this._updatingPromise = null;
2823
2608
  if (this.isDestroying || this.isDestroyed) {
2824
2609
  return;
@@ -2835,6 +2620,10 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2835
2620
  */
2836
2621
  _update() {
2837
2622
  assert(`_update cannot be used with this array`, this.modelName);
2623
+ // @ts-expect-error typescript is unable to handle the complexity of
2624
+ // T = unknown, modelName = string
2625
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
2626
+ // both being valid options to pass through here.
2838
2627
  return this.store.findAll(this.modelName, {
2839
2628
  reload: true
2840
2629
  });
@@ -2856,17 +2645,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2856
2645
  @return {Promise<IdentifierArray>} promise
2857
2646
  */
2858
2647
  save() {
2859
- let promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2648
+ const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2860
2649
  return promise;
2861
2650
  }
2862
- }, (_descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "isUpdating", [tracked], {
2863
- configurable: true,
2651
+ }, _applyDecoratedDescriptor(_class.prototype, "length", [compat], Object.getOwnPropertyDescriptor(_class.prototype, "length"), _class.prototype), _class); // this will error if someone tries to call
2652
+ // A(identifierArray) since it is not configurable
2653
+ // which is preferable to the `meta` override we used
2654
+ // before which required importing all of Ember
2655
+ const desc = {
2864
2656
  enumerable: true,
2865
- writable: true,
2866
- initializer: function () {
2867
- return false;
2657
+ configurable: false,
2658
+ get: function () {
2659
+ return this;
2868
2660
  }
2869
- }), _applyDecoratedDescriptor(_class3.prototype, "length", [dependentKeyCompat], Object.getOwnPropertyDescriptor(_class3.prototype, "length"), _class3.prototype)), _class3);
2661
+ };
2662
+ compat(desc);
2663
+ Object.defineProperty(IdentifierArray.prototype, '[]', desc);
2664
+ defineSignal(IdentifierArray.prototype, 'isUpdating', false);
2870
2665
  class Collection extends IdentifierArray {
2871
2666
  constructor(options) {
2872
2667
  super(options);
@@ -2883,6 +2678,10 @@ class Collection extends IdentifierArray {
2883
2678
  // TODO save options from initial request?
2884
2679
  assert(`update cannot be used with this array`, this.modelName);
2885
2680
  assert(`update cannot be used with no query`, query);
2681
+ // @ts-expect-error typescript is unable to handle the complexity of
2682
+ // T = unknown, modelName = string
2683
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
2684
+ // both being valid options to pass through here.
2886
2685
  const promise = store.query(this.modelName, query, {
2887
2686
  _recordArray: this
2888
2687
  });
@@ -2898,7 +2697,8 @@ class Collection extends IdentifierArray {
2898
2697
  Collection.prototype.query = null;
2899
2698
 
2900
2699
  // Ensure instanceof works correctly
2901
- //Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2700
+ // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2701
+
2902
2702
  function assertRecordPassedToHasMany(record) {
2903
2703
  assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
2904
2704
  try {
@@ -2920,7 +2720,6 @@ function extractIdentifierFromRecord$1(record) {
2920
2720
  /**
2921
2721
  @module @ember-data/store
2922
2722
  */
2923
-
2924
2723
  const FAKE_ARR = {};
2925
2724
  const SLICE_BATCH_SIZE = 1200;
2926
2725
  /**
@@ -2963,7 +2762,7 @@ const SLICE_BATCH_SIZE = 1200;
2963
2762
  */
2964
2763
  function fastPush(target, source) {
2965
2764
  let startLength = 0;
2966
- let newLength = source.length;
2765
+ const newLength = source.length;
2967
2766
  while (newLength - startLength > SLICE_BATCH_SIZE) {
2968
2767
  // eslint-disable-next-line prefer-spread
2969
2768
  target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
@@ -3020,8 +2819,8 @@ class RecordArrayManager {
3020
2819
  */
3021
2820
  liveArrayFor(type) {
3022
2821
  let array = this._live.get(type);
3023
- let identifiers = [];
3024
- let staged = this._staged.get(type);
2822
+ const identifiers = [];
2823
+ const staged = this._staged.get(type);
3025
2824
  if (staged) {
3026
2825
  staged.forEach((value, key) => {
3027
2826
  if (value === 'add') {
@@ -3044,7 +2843,7 @@ class RecordArrayManager {
3044
2843
  return array;
3045
2844
  }
3046
2845
  createArray(config) {
3047
- let options = {
2846
+ const options = {
3048
2847
  type: config.type,
3049
2848
  links: config.doc?.links || null,
3050
2849
  meta: config.doc?.meta || null,
@@ -3055,7 +2854,7 @@ class RecordArrayManager {
3055
2854
  store: this.store,
3056
2855
  manager: this
3057
2856
  };
3058
- let array = new Collection(options);
2857
+ const array = new Collection(options);
3059
2858
  this._managed.add(array);
3060
2859
  this._set.set(array, new Set(options.identifiers || []));
3061
2860
  if (config.identifiers) {
@@ -3067,7 +2866,7 @@ class RecordArrayManager {
3067
2866
  if (array === FAKE_ARR) {
3068
2867
  return;
3069
2868
  }
3070
- let tag = array[IDENTIFIER_ARRAY_TAG];
2869
+ const tag = array[ARRAY_SIGNAL];
3071
2870
  if (!tag.shouldReset) {
3072
2871
  tag.shouldReset = true;
3073
2872
  addTransactionCB(array[NOTIFY]);
@@ -3079,11 +2878,11 @@ class RecordArrayManager {
3079
2878
  if (this.isDestroying || this.isDestroyed) {
3080
2879
  return;
3081
2880
  }
3082
- let liveArray = this._live.get(identifier.type);
2881
+ const liveArray = this._live.get(identifier.type);
3083
2882
  const allPending = this._pending;
3084
- let pending = new Map();
2883
+ const pending = new Map();
3085
2884
  if (includeManaged) {
3086
- let managed = this._identifiers.get(identifier);
2885
+ const managed = this._identifiers.get(identifier);
3087
2886
  if (managed) {
3088
2887
  managed.forEach(arr => {
3089
2888
  let changes = allPending.get(arr);
@@ -3138,10 +2937,10 @@ class RecordArrayManager {
3138
2937
  associate(this._identifiers, array, identifiers);
3139
2938
  }
3140
2939
  identifierAdded(identifier) {
3141
- let changeSets = this._getPendingFor(identifier, false);
2940
+ const changeSets = this._getPendingFor(identifier, false);
3142
2941
  if (changeSets) {
3143
2942
  changeSets.forEach((changes, array) => {
3144
- let existing = changes.get(identifier);
2943
+ const existing = changes.get(identifier);
3145
2944
  if (existing === 'del') {
3146
2945
  changes.delete(identifier);
3147
2946
  } else {
@@ -3152,10 +2951,10 @@ class RecordArrayManager {
3152
2951
  }
3153
2952
  }
3154
2953
  identifierRemoved(identifier) {
3155
- let changeSets = this._getPendingFor(identifier, true, true);
2954
+ const changeSets = this._getPendingFor(identifier, true, true);
3156
2955
  if (changeSets) {
3157
2956
  changeSets.forEach((changes, array) => {
3158
- let existing = changes.get(identifier);
2957
+ const existing = changes.get(identifier);
3159
2958
  if (existing === 'add') {
3160
2959
  changes.delete(identifier);
3161
2960
  } else {
@@ -3166,7 +2965,7 @@ class RecordArrayManager {
3166
2965
  }
3167
2966
  }
3168
2967
  identifierChanged(identifier) {
3169
- let newState = this.store._instanceCache.recordIsLoaded(identifier, true);
2968
+ const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
3170
2969
 
3171
2970
  // if the change matches the most recent direct added/removed
3172
2971
  // state, then we can ignore it
@@ -3193,13 +2992,12 @@ class RecordArrayManager {
3193
2992
  this.clear(false);
3194
2993
  this._live.clear();
3195
2994
  this.isDestroyed = true;
3196
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
3197
2995
  this.store.notifications.unsubscribe(this._subscription);
3198
2996
  }
3199
2997
  }
3200
2998
  function associate(ArraysCache, array, identifiers) {
3201
2999
  for (let i = 0; i < identifiers.length; i++) {
3202
- let identifier = identifiers[i];
3000
+ const identifier = identifiers[i];
3203
3001
  let cache = ArraysCache.get(identifier);
3204
3002
  if (!cache) {
3205
3003
  cache = new Set();
@@ -3214,13 +3012,13 @@ function disassociate(ArraysCache, array, identifiers) {
3214
3012
  }
3215
3013
  }
3216
3014
  function disassociateIdentifier(ArraysCache, array, identifier) {
3217
- let cache = ArraysCache.get(identifier);
3015
+ const cache = ArraysCache.get(identifier);
3218
3016
  if (cache) {
3219
3017
  cache.delete(array);
3220
3018
  }
3221
3019
  }
3222
3020
  function sync(array, changes, arraySet) {
3223
- let state = array[SOURCE];
3021
+ const state = array[SOURCE];
3224
3022
  const adds = [];
3225
3023
  const removes = [];
3226
3024
  changes.forEach((value, key) => {
@@ -3234,13 +3032,13 @@ function sync(array, changes, arraySet) {
3234
3032
  } else {
3235
3033
  if (arraySet.has(key)) {
3236
3034
  removes.push(key);
3035
+ arraySet.delete(key);
3237
3036
  }
3238
3037
  }
3239
3038
  });
3240
3039
  if (removes.length) {
3241
3040
  if (removes.length === state.length) {
3242
3041
  state.length = 0;
3243
- arraySet.clear();
3244
3042
  // changing the reference breaks the Proxy
3245
3043
  // state = array[SOURCE] = [];
3246
3044
  } else {
@@ -3267,6 +3065,9 @@ function sync(array, changes, arraySet) {
3267
3065
  }
3268
3066
  }
3269
3067
 
3068
+ /**
3069
+ * @module @ember-data/store
3070
+ */
3270
3071
  const Touching = Symbol('touching');
3271
3072
  const RequestPromise = Symbol('promise');
3272
3073
  const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
@@ -3294,14 +3095,14 @@ class RequestStateService {
3294
3095
  this._done.delete(identifier);
3295
3096
  }
3296
3097
  _enqueue(promise, queryRequest) {
3297
- let query = queryRequest.data[0];
3098
+ const query = queryRequest.data[0];
3298
3099
  if (hasRecordIdentifier(query)) {
3299
3100
  const identifier = query.recordIdentifier;
3300
- let type = query.op === 'saveRecord' ? 'mutation' : 'query';
3101
+ const type = query.op === 'saveRecord' ? 'mutation' : 'query';
3301
3102
  if (!this._pending.has(identifier)) {
3302
3103
  this._pending.set(identifier, []);
3303
3104
  }
3304
- let request = {
3105
+ const request = {
3305
3106
  state: 'pending',
3306
3107
  request: queryRequest,
3307
3108
  type
@@ -3312,7 +3113,7 @@ class RequestStateService {
3312
3113
  this._triggerSubscriptions(request);
3313
3114
  return promise.then(result => {
3314
3115
  this._dequeue(identifier, request);
3315
- let finalizedRequest = {
3116
+ const finalizedRequest = {
3316
3117
  state: 'fulfilled',
3317
3118
  request: queryRequest,
3318
3119
  type,
@@ -3326,7 +3127,7 @@ class RequestStateService {
3326
3127
  return result;
3327
3128
  }, error => {
3328
3129
  this._dequeue(identifier, request);
3329
- let finalizedRequest = {
3130
+ const finalizedRequest = {
3330
3131
  state: 'rejected',
3331
3132
  request: queryRequest,
3332
3133
  type,
@@ -3375,7 +3176,7 @@ class RequestStateService {
3375
3176
  _addDone(request) {
3376
3177
  request[Touching].forEach(identifier => {
3377
3178
  // TODO add support for multiple
3378
- let requestDataOp = request.request.data[0].op;
3179
+ const requestDataOp = request.request.data[0].op;
3379
3180
  let requests = this._done.get(identifier);
3380
3181
  if (requests) {
3381
3182
  requests = requests.filter(req => {
@@ -3439,7 +3240,7 @@ class RequestStateService {
3439
3240
  * @method getPendingRequestsForRecord
3440
3241
  * @public
3441
3242
  * @param {StableRecordIdentifier} identifier
3442
- * @returns {RequestState[]} an array of request states for any pending requests for the given identifier
3243
+ * @return {RequestState[]} an array of request states for any pending requests for the given identifier
3443
3244
  */
3444
3245
  getPendingRequestsForRecord(identifier) {
3445
3246
  return this._pending.get(identifier) || EMPTY_ARR;
@@ -3451,10 +3252,10 @@ class RequestStateService {
3451
3252
  * @method getLastRequestForRecord
3452
3253
  * @public
3453
3254
  * @param {StableRecordIdentifier} identifier
3454
- * @returns {RequestState | null} the state of the most recent request for the given identifier
3255
+ * @return {RequestState | null} the state of the most recent request for the given identifier
3455
3256
  */
3456
3257
  getLastRequestForRecord(identifier) {
3457
- let requests = this._done.get(identifier);
3258
+ const requests = this._done.get(identifier);
3458
3259
  if (requests) {
3459
3260
  return requests[requests.length - 1];
3460
3261
  }
@@ -3466,7 +3267,7 @@ function isNonEmptyString(str) {
3466
3267
  }
3467
3268
  function constructResource(type, id, lid) {
3468
3269
  if (typeof type === 'object' && type !== null) {
3469
- let resource = type;
3270
+ const resource = type;
3470
3271
  if (isStableIdentifier(resource)) {
3471
3272
  return resource;
3472
3273
  }
@@ -3501,6 +3302,29 @@ function constructResource(type, id, lid) {
3501
3302
  }
3502
3303
  }
3503
3304
 
3305
+ /**
3306
+ @module @ember-data/store
3307
+ */
3308
+ // this import location is deprecated but breaks in 4.8 and older
3309
+
3310
+ /**
3311
+ * Currently only records that extend object can be created via
3312
+ * store.createRecord. This is a limitation of the current API,
3313
+ * but can be worked around by creating a new identifier, running
3314
+ * the cache.clientDidCreate method, and then peeking the record
3315
+ * for the identifier.
3316
+ *
3317
+ * To assign primary key to a record during creation, only `id` will
3318
+ * work correctly for `store.createRecord`, other primary key may be
3319
+ * handled by updating the record after creation or using the flow
3320
+ * described above.
3321
+ *
3322
+ * TODO: These are limitations we want to (and can) address. If you
3323
+ * have need of lifting these limitations, please open an issue.
3324
+ *
3325
+ * @typedoc
3326
+ */
3327
+
3504
3328
  /**
3505
3329
  * A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
3506
3330
  * and sources of data (such as your API or a local persistence layer)
@@ -3519,7 +3343,10 @@ function constructResource(type, id, lid) {
3519
3343
 
3520
3344
  @class Store
3521
3345
  @public
3522
- */ // @ts-expect-error
3346
+ */
3347
+
3348
+ // @ts-expect-error
3349
+
3523
3350
  class Store extends EmberObject {
3524
3351
  /**
3525
3352
  * Provides access to the NotificationManager associated
@@ -3651,8 +3478,6 @@ class Store extends EmberObject {
3651
3478
  // private
3652
3479
  this._requestCache = new RequestStateService(this);
3653
3480
  this._instanceCache = new InstanceCache(this);
3654
- this._adapterCache = Object.create(null);
3655
- this._serializerCache = Object.create(null);
3656
3481
  this._documentCache = new Map();
3657
3482
  this.isDestroying = false;
3658
3483
  this.isDestroyed = false;
@@ -3710,7 +3535,7 @@ class Store extends EmberObject {
3710
3535
  * that have been initiated for a given identifier.
3711
3536
  *
3712
3537
  * @method getRequestStateService
3713
- * @returns {RequestStateService}
3538
+ * @return {RequestStateService}
3714
3539
  * @public
3715
3540
  */
3716
3541
  getRequestStateService() {
@@ -3735,10 +3560,11 @@ class Store extends EmberObject {
3735
3560
  * inserting the response into the cache and handing
3736
3561
  * back a Future which resolves to a ResponseDocument
3737
3562
  *
3738
- * Resource data is always updated in the cache.
3563
+ * ## Cache Keys
3739
3564
  *
3740
- * Only GET requests have the request result and document
3741
- * cached by default when a cache key is present.
3565
+ * Only GET requests with a url or requests with an explicit
3566
+ * cache key (`cacheOptions.key`) will have the request result
3567
+ * and document cached.
3742
3568
  *
3743
3569
  * The cache key used is `requestConfig.cacheOptions.key`
3744
3570
  * if present, falling back to `requestconfig.url`.
@@ -3749,16 +3575,44 @@ class Store extends EmberObject {
3749
3575
  * via the `POST` method `requestConfig.cacheOptions.key`
3750
3576
  * MUST be supplied for the document to be cached.
3751
3577
  *
3578
+ * ## Requesting Without a Cache Key
3579
+ *
3580
+ * Resource data within the request is always updated in the cache,
3581
+ * regardless of whether a cache key is present for the request.
3582
+ *
3583
+ * ## Fulfilling From Cache
3584
+ *
3585
+ * When a cache-key is determined, the request may fulfill
3586
+ * from cache provided the cache is not stale.
3587
+ *
3588
+ * Cache staleness is determined by the configured LifetimesService
3589
+ * with priority given to the `cacheOptions.reload` and
3590
+ * `cacheOptions.backgroundReload` on the request if present.
3591
+ *
3592
+ * If the cache data has soft expired or the request asks for a background
3593
+ * reload, the request will fulfill from cache if possible and
3594
+ * make a non-blocking request in the background to update the cache.
3595
+ *
3596
+ * If the cache data has hard expired or the request asks for a reload,
3597
+ * the request will not fulfill from cache and will make a blocking
3598
+ * request to update the cache.
3599
+ *
3600
+ * ## The Response
3601
+ *
3602
+ * The primary difference between `requestManager.request` and `store.request`
3603
+ * is that `store.request` will attempt to hydrate the response content into
3604
+ * a response Document containing RecordInstances.
3605
+ *
3752
3606
  * @method request
3753
3607
  * @param {StoreRequestInput} requestConfig
3754
- * @returns {Future}
3608
+ * @return {Future}
3755
3609
  * @public
3756
3610
  */
3757
3611
  request(requestConfig) {
3758
3612
  // we lazily set the cache handler when we issue the first request
3759
3613
  // because constructor doesn't allow for this to run after
3760
3614
  // the user has had the chance to set the prop.
3761
- let opts = {
3615
+ const opts = {
3762
3616
  store: this,
3763
3617
  [EnableHydration]: true
3764
3618
  };
@@ -3810,7 +3664,7 @@ class Store extends EmberObject {
3810
3664
  * @param createRecordArgs
3811
3665
  * @param recordDataFor deprecated use this.cache
3812
3666
  * @param notificationManager deprecated use this.notifications
3813
- * @returns A record instance
3667
+ * @return A record instance
3814
3668
  * @public
3815
3669
  */
3816
3670
 
@@ -3953,7 +3807,7 @@ class Store extends EmberObject {
3953
3807
  }
3954
3808
 
3955
3809
  /**
3956
- Returns the schema for a particular `modelName`.
3810
+ Returns the schema for a particular resource type (modelName).
3957
3811
  When used with Model from @ember-data/model the return is the model class,
3958
3812
  but this is not guaranteed.
3959
3813
  If looking to query attribute or relationship information it is
@@ -3967,7 +3821,7 @@ class Store extends EmberObject {
3967
3821
  for example.
3968
3822
  @method modelFor
3969
3823
  @public
3970
- @param {String} type
3824
+ @param {string} type
3971
3825
  @return {ModelSchema}
3972
3826
  */
3973
3827
  // TODO @deprecate in favor of schema APIs, requires adapter/serializer overhaul or replacement
@@ -4000,17 +3854,18 @@ class Store extends EmberObject {
4000
3854
  ```
4001
3855
  @method createRecord
4002
3856
  @public
4003
- @param {String} modelName
3857
+ @param {String} type the name of the resource
4004
3858
  @param {Object} inputProperties a hash of properties to set on the
4005
3859
  newly created record.
4006
3860
  @return {Model} record
4007
3861
  */
4008
- createRecord(modelName, inputProperties) {
3862
+
3863
+ createRecord(type, inputProperties) {
4009
3864
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4010
3865
  assertDestroyingStore(this, 'createRecord');
4011
3866
  }
4012
- assert(`You need to pass a model name to the store's createRecord method`, modelName);
4013
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
3867
+ assert(`You need to pass a model name to the store's createRecord method`, type);
3868
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4014
3869
 
4015
3870
  // This is wrapped in a `run.join` so that in test environments users do not need to manually wrap
4016
3871
  // calls to `createRecord`. The run loop usage here is because we batch the joining and updating
@@ -4018,43 +3873,40 @@ class Store extends EmberObject {
4018
3873
  //
4019
3874
  // to remove this, we would need to move to a new `async` API.
4020
3875
  let record;
4021
- _backburner.join(() => {
4022
- this._join(() => {
4023
- let normalizedModelName = normalizeModelName(modelName);
4024
- let properties = {
4025
- ...inputProperties
4026
- };
4027
-
4028
- // If the passed properties do not include a primary key,
4029
- // give the adapter an opportunity to generate one. Typically,
4030
- // client-side ID generators will use something like uuid.js
4031
- // to avoid conflicts.
4032
-
4033
- if (properties.id === null || properties.id === undefined) {
4034
- let adapter = this.adapterFor(modelName);
4035
- if (adapter && adapter.generateIdForRecord) {
4036
- properties.id = adapter.generateIdForRecord(this, modelName, properties);
4037
- } else {
4038
- properties.id = null;
4039
- }
4040
- }
3876
+ this._join(() => {
3877
+ const normalizedModelName = normalizeModelName(type);
3878
+ const properties = {
3879
+ ...inputProperties
3880
+ };
4041
3881
 
4042
- // Coerce ID to a string
4043
- properties.id = coerceId(properties.id);
4044
- const resource = {
4045
- type: normalizedModelName,
4046
- id: properties.id
4047
- };
4048
- if (resource.id) {
4049
- const identifier = this.identifierCache.peekRecordIdentifier(resource);
4050
- assert(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`, !identifier);
3882
+ // If the passed properties do not include a primary key,
3883
+ // give the adapter an opportunity to generate one. Typically,
3884
+ // client-side ID generators will use something like uuid.js
3885
+ // to avoid conflicts.
3886
+ let id = null;
3887
+ if (properties.id === null || properties.id === undefined) {
3888
+ const adapter = this.adapterFor?.(normalizedModelName, true);
3889
+ if (adapter && adapter.generateIdForRecord) {
3890
+ id = properties.id = coerceId(adapter.generateIdForRecord(this, normalizedModelName, properties));
3891
+ } else {
3892
+ id = properties.id = null;
4051
3893
  }
4052
- const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
4053
- const cache = this.cache;
4054
- const createOptions = normalizeProperties(this, identifier, properties);
4055
- const resultProps = cache.clientDidCreate(identifier, createOptions);
4056
- record = this._instanceCache.getRecord(identifier, resultProps);
4057
- });
3894
+ } else {
3895
+ id = properties.id = coerceId(properties.id);
3896
+ }
3897
+ const resource = {
3898
+ type: normalizedModelName,
3899
+ id
3900
+ };
3901
+ if (resource.id) {
3902
+ const identifier = this.identifierCache.peekRecordIdentifier(resource);
3903
+ assert(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`, !identifier);
3904
+ }
3905
+ const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
3906
+ const cache = this.cache;
3907
+ const createOptions = normalizeProperties(this, identifier, properties);
3908
+ const resultProps = cache.clientDidCreate(identifier, createOptions);
3909
+ record = this._instanceCache.getRecord(identifier, resultProps);
4058
3910
  });
4059
3911
  return record;
4060
3912
  }
@@ -4070,7 +3922,7 @@ class Store extends EmberObject {
4070
3922
  ```
4071
3923
  @method deleteRecord
4072
3924
  @public
4073
- @param {Model} record
3925
+ @param {unknown} record
4074
3926
  */
4075
3927
  deleteRecord(record) {
4076
3928
  if (macroCondition(getOwnConfig().env.DEBUG)) {
@@ -4082,9 +3934,7 @@ class Store extends EmberObject {
4082
3934
  this._join(() => {
4083
3935
  cache.setIsDeleted(identifier, true);
4084
3936
  if (cache.isNew(identifier)) {
4085
- _backburner.join(() => {
4086
- this._instanceCache.unloadRecord(identifier);
4087
- });
3937
+ this._instanceCache.unloadRecord(identifier);
4088
3938
  }
4089
3939
  });
4090
3940
  }
@@ -4166,8 +4016,7 @@ class Store extends EmberObject {
4166
4016
  In your adapter you can then access this id without triggering a network request via the
4167
4017
  snapshot:
4168
4018
  ```app/adapters/application.js
4169
- import EmberObject from '@ember/object';
4170
- export default class Adapter extends EmberObject {
4019
+ export default class Adapter {
4171
4020
  findRecord(store, schema, id, snapshot) {
4172
4021
  let type = schema.modelName;
4173
4022
  if (type === 'comment')
@@ -4176,6 +4025,9 @@ class Store extends EmberObject {
4176
4025
  .then(response => response.json())
4177
4026
  }
4178
4027
  }
4028
+ static create() {
4029
+ return new this();
4030
+ }
4179
4031
  }
4180
4032
  ```
4181
4033
  This could also be achieved by supplying the post id to the adapter via the adapterOptions
@@ -4189,9 +4041,8 @@ class Store extends EmberObject {
4189
4041
  }
4190
4042
  ```
4191
4043
  ```app/adapters/application.js
4192
- import EmberObject from '@ember/object';
4193
- export default class Adapter extends EmberObject {
4194
- findRecord(store, schema, id, snapshot) {
4044
+ export default class Adapter {
4045
+ findRecord(store, schema, id, snapshot) {
4195
4046
  let type = schema.modelName;
4196
4047
  if (type === 'comment')
4197
4048
  let postId = snapshot.adapterOptions.post;
@@ -4199,6 +4050,9 @@ class Store extends EmberObject {
4199
4050
  .then(response => response.json())
4200
4051
  }
4201
4052
  }
4053
+ static create() {
4054
+ return new this();
4055
+ }
4202
4056
  }
4203
4057
  ```
4204
4058
  If you have access to the post model you can also pass the model itself to preload:
@@ -4325,9 +4179,8 @@ class Store extends EmberObject {
4325
4179
  }
4326
4180
  ```
4327
4181
  ```app/adapters/application.js
4328
- import EmberObject from '@ember/object';
4329
- export default class Adapter extends EmberObject {
4330
- findRecord(store, schema, id, snapshot) {
4182
+ export default class Adapter {
4183
+ findRecord(store, schema, id, snapshot) {
4331
4184
  let type = schema.modelName;
4332
4185
  if (type === 'post')
4333
4186
  let includes = snapshot.adapterOptions.include;
@@ -4335,6 +4188,9 @@ class Store extends EmberObject {
4335
4188
  .then(response => response.json())
4336
4189
  }
4337
4190
  }
4191
+ static create() {
4192
+ return new this();
4193
+ }
4338
4194
  }
4339
4195
  ```
4340
4196
  In this case, the post's comments would then be available in your template as
@@ -4388,7 +4244,7 @@ class Store extends EmberObject {
4388
4244
  @since 1.13.0
4389
4245
  @method findRecord
4390
4246
  @public
4391
- @param {String|object} modelName - either a string representing the modelName or a ResourceIdentifier object containing both the type (a string) and the id (a string) for the record or an lid (a string) of an existing record
4247
+ @param {String|object} type - either a string representing the name of the resource or a ResourceIdentifier object containing both the type (a string) and the id (a string) for the record or an lid (a string) of an existing record
4392
4248
  @param {(String|Integer|Object)} id - optional object with options for the request only if the first param is a ResourceIdentifier, else the string id of the record to be retrieved
4393
4249
  @param {Object} [options] - if the first param is a string this will be the optional options for the request. See examples for available options.
4394
4250
  @return {Promise} promise
@@ -4478,7 +4334,7 @@ class Store extends EmberObject {
4478
4334
  resourceIdentifier = constructResource(type, normalizedId);
4479
4335
  }
4480
4336
  assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
4481
- let identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4337
+ const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4482
4338
  return this._instanceCache.getReference(identifier);
4483
4339
  }
4484
4340
 
@@ -4573,24 +4429,25 @@ class Store extends EmberObject {
4573
4429
  @since 1.13.0
4574
4430
  @method query
4575
4431
  @public
4576
- @param {String} modelName
4577
- @param {any} query an opaque query to be used by the adapter
4432
+ @param {String} type the name of the resource
4433
+ @param {object} query a query to be used by the adapter
4578
4434
  @param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.query
4579
4435
  @return {Promise} promise
4580
4436
  */
4581
- query(modelName, query, options) {
4437
+
4438
+ query(type, query, options = {}) {
4582
4439
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4583
4440
  assertDestroyingStore(this, 'query');
4584
4441
  }
4585
- assert(`You need to pass a model name to the store's query method`, modelName);
4442
+ assert(`You need to pass a model name to the store's query method`, type);
4586
4443
  assert(`You need to pass a query hash to the store's query method`, query);
4587
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
4444
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4588
4445
  const promise = this.request({
4589
4446
  op: 'query',
4590
4447
  data: {
4591
- type: normalizeModelName(modelName),
4448
+ type: normalizeModelName(type),
4592
4449
  query,
4593
- options: options || {}
4450
+ options: options
4594
4451
  },
4595
4452
  cacheOptions: {
4596
4453
  [SkipCache]: true
@@ -4848,20 +4705,21 @@ class Store extends EmberObject {
4848
4705
  @since 1.13.0
4849
4706
  @method findAll
4850
4707
  @public
4851
- @param {String} modelName
4852
- @param {Object} options
4708
+ @param {string} type the name of the resource
4709
+ @param {object} options
4853
4710
  @return {Promise} promise
4854
4711
  */
4855
- findAll(modelName, options = {}) {
4712
+
4713
+ findAll(type, options = {}) {
4856
4714
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4857
4715
  assertDestroyingStore(this, 'findAll');
4858
4716
  }
4859
- assert(`You need to pass a model name to the store's findAll method`, modelName);
4860
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
4717
+ assert(`You need to pass a model name to the store's findAll method`, type);
4718
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4861
4719
  const promise = this.request({
4862
4720
  op: 'findAll',
4863
4721
  data: {
4864
- type: normalizeModelName(modelName),
4722
+ type: normalizeModelName(type),
4865
4723
  options: options || {}
4866
4724
  },
4867
4725
  cacheOptions: {
@@ -4888,17 +4746,17 @@ class Store extends EmberObject {
4888
4746
  @since 1.13.0
4889
4747
  @method peekAll
4890
4748
  @public
4891
- @param {String} modelName
4749
+ @param {string} type the name of the resource
4892
4750
  @return {RecordArray}
4893
4751
  */
4894
- peekAll(modelName) {
4752
+
4753
+ peekAll(type) {
4895
4754
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4896
4755
  assertDestroyingStore(this, 'peekAll');
4897
4756
  }
4898
- assert(`You need to pass a model name to the store's peekAll method`, modelName);
4899
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
4900
- let type = normalizeModelName(modelName);
4901
- return this.recordArrayManager.liveArrayFor(type);
4757
+ assert(`You need to pass a model name to the store's peekAll method`, type);
4758
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4759
+ return this.recordArrayManager.liveArrayFor(normalizeModelName(type));
4902
4760
  }
4903
4761
 
4904
4762
  /**
@@ -4910,16 +4768,17 @@ class Store extends EmberObject {
4910
4768
  store.unloadAll('post');
4911
4769
  ```
4912
4770
  @method unloadAll
4771
+ @param {string} type the name of the resource
4913
4772
  @public
4914
- @param {String} modelName
4915
4773
  */
4916
- unloadAll(modelName) {
4774
+
4775
+ unloadAll(type) {
4917
4776
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4918
4777
  assertDestroyedStoreOnly(this, 'unloadAll');
4919
4778
  }
4920
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(modelName)}`, !modelName || typeof modelName === 'string');
4779
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`, !type || typeof type === 'string');
4921
4780
  this._join(() => {
4922
- if (modelName === undefined) {
4781
+ if (type === undefined) {
4923
4782
  // destroy the graph before unloadAll
4924
4783
  // since then we avoid churning relationships
4925
4784
  // during unload
@@ -4927,8 +4786,7 @@ class Store extends EmberObject {
4927
4786
  this.recordArrayManager.clear();
4928
4787
  this._instanceCache.clear();
4929
4788
  } else {
4930
- let normalizedModelName = normalizeModelName(modelName);
4931
- this._instanceCache.clear(normalizedModelName);
4789
+ this._instanceCache.clear(normalizeModelName(type));
4932
4790
  }
4933
4791
  });
4934
4792
  }
@@ -5068,10 +4926,9 @@ class Store extends EmberObject {
5068
4926
  if (macroCondition(getOwnConfig().env.DEBUG)) {
5069
4927
  assertDestroyingStore(this, 'push');
5070
4928
  }
5071
- let pushed = this._push(data, false);
4929
+ const pushed = this._push(data, false);
5072
4930
  if (Array.isArray(pushed)) {
5073
- let records = pushed.map(identifier => this._instanceCache.getRecord(identifier));
5074
- return records;
4931
+ return pushed.map(identifier => this._instanceCache.getRecord(identifier));
5075
4932
  }
5076
4933
  if (pushed === null) {
5077
4934
  return null;
@@ -5093,7 +4950,7 @@ class Store extends EmberObject {
5093
4950
  }
5094
4951
  if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
5095
4952
  try {
5096
- let data = JSON.parse(JSON.stringify(jsonApiDoc));
4953
+ const data = JSON.parse(JSON.stringify(jsonApiDoc));
5097
4954
  // eslint-disable-next-line no-console
5098
4955
  console.log('EmberData | Payload - push', data);
5099
4956
  } catch (e) {
@@ -5101,388 +4958,682 @@ class Store extends EmberObject {
5101
4958
  console.log('EmberData | Payload - push', jsonApiDoc);
5102
4959
  }
5103
4960
  }
5104
- if (asyncFlush) {
5105
- this._enableAsyncFlush = true;
5106
- }
5107
- let ret;
5108
- this._join(() => {
5109
- ret = this.cache.put({
5110
- content: jsonApiDoc
5111
- });
4961
+ if (asyncFlush) {
4962
+ this._enableAsyncFlush = true;
4963
+ }
4964
+ let ret;
4965
+ this._join(() => {
4966
+ ret = this.cache.put({
4967
+ content: jsonApiDoc
4968
+ });
4969
+ });
4970
+ this._enableAsyncFlush = null;
4971
+ return 'data' in ret ? ret.data : null;
4972
+ }
4973
+
4974
+ /**
4975
+ * Trigger a save for a Record.
4976
+ *
4977
+ * Returns a promise resolving with the same record when the save is complete.
4978
+ *
4979
+ * @method saveRecord
4980
+ * @public
4981
+ * @param {unknown} record
4982
+ * @param options
4983
+ * @return {Promise<record>}
4984
+ */
4985
+ saveRecord(record, options = {}) {
4986
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
4987
+ assertDestroyingStore(this, 'saveRecord');
4988
+ }
4989
+ assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
4990
+ const identifier = recordIdentifierFor(record);
4991
+ const cache = this.cache;
4992
+ if (!identifier) {
4993
+ // this commonly means we're disconnected
4994
+ // but just in case we reject here to prevent bad things.
4995
+ return Promise.reject(new Error(`Record Is Disconnected`));
4996
+ }
4997
+ // TODO we used to check if the record was destroyed here
4998
+ assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
4999
+ if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5000
+ return Promise.resolve(record);
5001
+ }
5002
+ if (!options) {
5003
+ options = {};
5004
+ }
5005
+ let operation = 'updateRecord';
5006
+ if (cache.isNew(identifier)) {
5007
+ operation = 'createRecord';
5008
+ } else if (cache.isDeleted(identifier)) {
5009
+ operation = 'deleteRecord';
5010
+ }
5011
+ const request = {
5012
+ op: operation,
5013
+ data: {
5014
+ options,
5015
+ record: identifier
5016
+ },
5017
+ records: [identifier],
5018
+ cacheOptions: {
5019
+ [SkipCache]: true
5020
+ }
5021
+ };
5022
+
5023
+ // we lie here on the type because legacy doesn't have enough context
5024
+ cache.willCommit(identifier, {
5025
+ request
5026
+ });
5027
+ return this.request(request).then(document => document.content);
5028
+ }
5029
+
5030
+ /**
5031
+ * Instantiation hook allowing applications or addons to configure the store
5032
+ * to utilize a custom Cache implementation.
5033
+ *
5034
+ * This hook should not be called directly by consuming applications or libraries.
5035
+ * Use `Store.cache` to access the Cache instance.
5036
+ *
5037
+ * @method createCache (hook)
5038
+ * @public
5039
+ * @param storeWrapper
5040
+ * @return {Cache}
5041
+ */
5042
+
5043
+ /**
5044
+ * Returns the cache instance associated to this Store, instantiates the Cache
5045
+ * if necessary via `Store.createCache`
5046
+ *
5047
+ * @property {Cache} cache
5048
+ * @public
5049
+ */
5050
+ get cache() {
5051
+ let {
5052
+ cache
5053
+ } = this._instanceCache;
5054
+ if (!cache) {
5055
+ cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5056
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5057
+ cache = new CacheManager(cache);
5058
+ }
5059
+ }
5060
+ return cache;
5061
+ }
5062
+
5063
+ // @ts-expect-error
5064
+ destroy() {
5065
+ if (this.isDestroyed) {
5066
+ // @ember/test-helpers will call destroy multiple times
5067
+ return;
5068
+ }
5069
+ this.isDestroying = true;
5070
+ this._graph?.destroy();
5071
+ this._graph = undefined;
5072
+ this.notifications.destroy();
5073
+ this.recordArrayManager.destroy();
5074
+ this.identifierCache.destroy();
5075
+ this.unloadAll();
5076
+ this.isDestroyed = true;
5077
+ }
5078
+ static create(args) {
5079
+ return new this(args);
5080
+ }
5081
+ }
5082
+ let assertDestroyingStore;
5083
+ let assertDestroyedStoreOnly;
5084
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5085
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5086
+ assertDestroyingStore = function assertDestroyingStore(store, method) {
5087
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5088
+ };
5089
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5090
+ assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5091
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5092
+ };
5093
+ }
5094
+ function isMaybeIdentifier(maybeIdentifier) {
5095
+ return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5096
+ }
5097
+ function normalizeProperties(store, identifier, properties) {
5098
+ // assert here
5099
+ if (properties !== undefined) {
5100
+ if ('id' in properties) {
5101
+ assert(`expected id to be a string or null`, properties.id !== undefined);
5102
+ }
5103
+ assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5104
+ const {
5105
+ type
5106
+ } = identifier;
5107
+
5108
+ // convert relationship Records to RecordDatas before passing to RecordData
5109
+ const defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5110
+ type
5111
+ });
5112
+ if (defs !== null) {
5113
+ const keys = Object.keys(properties);
5114
+ let relationshipValue;
5115
+ for (let i = 0; i < keys.length; i++) {
5116
+ const prop = keys[i];
5117
+ const def = defs[prop];
5118
+ if (def !== undefined) {
5119
+ if (def.kind === 'hasMany') {
5120
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5121
+ assertRecordsPassedToHasMany(properties[prop]);
5122
+ }
5123
+ relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5124
+ } else {
5125
+ relationshipValue = extractIdentifierFromRecord(properties[prop]);
5126
+ }
5127
+ properties[prop] = relationshipValue;
5128
+ }
5129
+ }
5130
+ }
5131
+ }
5132
+ return properties;
5133
+ }
5134
+ function assertRecordsPassedToHasMany(records) {
5135
+ assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5136
+ assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5137
+ return records.every(record => {
5138
+ try {
5139
+ recordIdentifierFor(record);
5140
+ return true;
5141
+ } catch {
5142
+ return false;
5143
+ }
5112
5144
  });
5113
- this._enableAsyncFlush = null;
5114
- return 'data' in ret ? ret.data : null;
5145
+ }());
5146
+ }
5147
+ function extractIdentifiersFromRecords(records) {
5148
+ return records.map(record => extractIdentifierFromRecord(record));
5149
+ }
5150
+ function extractIdentifierFromRecord(recordOrPromiseRecord) {
5151
+ if (!recordOrPromiseRecord) {
5152
+ return null;
5115
5153
  }
5154
+ const extract = recordIdentifierFor;
5155
+ return extract(recordOrPromiseRecord);
5156
+ }
5157
+ function urlFromLink(link) {
5158
+ if (typeof link === 'string') return link;
5159
+ return link.href;
5160
+ }
5116
5161
 
5162
+ /**
5163
+ * A Document is a class that wraps the response content from a request to the API
5164
+ * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
5165
+ * record instances.
5166
+ *
5167
+ * It is not directly instantiated by the user, and its properties should not
5168
+ * be directly modified. Whether individual properties are mutable or not is
5169
+ * determined by the record instance itself.
5170
+ *
5171
+ * @public
5172
+ * @class Document
5173
+ */
5174
+ var _store = /*#__PURE__*/_classPrivateFieldKey("store");
5175
+ var _request = /*#__PURE__*/_classPrivateFieldKey("request");
5176
+ class Document {
5177
+ constructor(store, identifier) {
5178
+ Object.defineProperty(this, _request, {
5179
+ value: _request2
5180
+ });
5181
+ /**
5182
+ * The links object for this document, if any
5183
+ *
5184
+ * e.g.
5185
+ *
5186
+ * ```
5187
+ * {
5188
+ * self: '/articles?page[number]=3',
5189
+ * }
5190
+ * ```
5191
+ *
5192
+ * @property links
5193
+ * @type {object|undefined} - a links object
5194
+ * @public
5195
+ */
5196
+ /**
5197
+ * The primary data for this document, if any.
5198
+ *
5199
+ * If this document has no primary data (e.g. because it is an error document)
5200
+ * this property will be `undefined`.
5201
+ *
5202
+ * For collections this will be an array of record instances,
5203
+ * for single resource requests it will be a single record instance or null.
5204
+ *
5205
+ * @property data
5206
+ * @public
5207
+ * @type {object|Array<object>|null|undefined} - a data object
5208
+ */
5209
+ /**
5210
+ * The errors returned by the API for this request, if any
5211
+ *
5212
+ * @property errors
5213
+ * @public
5214
+ * @type {object|undefined} - an errors object
5215
+ */
5216
+ /**
5217
+ * The meta object for this document, if any
5218
+ *
5219
+ * @property meta
5220
+ * @public
5221
+ * @type {object|undefined} - a meta object
5222
+ */
5223
+ /**
5224
+ * The identifier associated with this document, if any
5225
+ *
5226
+ * @property identifier
5227
+ * @public
5228
+ * @type {StableDocumentIdentifier|null}
5229
+ */
5230
+ Object.defineProperty(this, _store, {
5231
+ writable: true,
5232
+ value: void 0
5233
+ });
5234
+ _classPrivateFieldBase(this, _store)[_store] = store;
5235
+ this.identifier = identifier;
5236
+ }
5117
5237
  /**
5118
- Push some raw data into the store.
5119
- This method can be used both to push in brand new
5120
- records, as well as to update existing records. You
5121
- can push in more than one type of object at once.
5122
- All objects should be in the format expected by the
5123
- serializer.
5124
- ```app/serializers/application.js
5125
- import RESTSerializer from '@ember-data/serializer/rest';
5126
- export default class ApplicationSerializer extends RESTSerializer;
5127
- ```
5128
- ```js
5129
- let pushData = {
5130
- posts: [
5131
- { id: 1, postTitle: "Great post", commentIds: [2] }
5132
- ],
5133
- comments: [
5134
- { id: 2, commentBody: "Insightful comment" }
5135
- ]
5136
- }
5137
- store.pushPayload(pushData);
5138
- ```
5139
- By default, the data will be deserialized using a default
5140
- serializer (the application serializer if it exists).
5141
- Alternatively, `pushPayload` will accept a model type which
5142
- will determine which serializer will process the payload.
5143
- ```app/serializers/application.js
5144
- import RESTSerializer from '@ember-data/serializer/rest';
5145
- export default class ApplicationSerializer extends RESTSerializer;
5146
- ```
5147
- ```app/serializers/post.js
5148
- import JSONSerializer from '@ember-data/serializer/json';
5149
- export default JSONSerializer;
5150
- ```
5151
- ```js
5152
- store.pushPayload(pushData); // Will use the application serializer
5153
- store.pushPayload('post', pushData); // Will use the post serializer
5154
- ```
5155
- @method pushPayload
5156
- @public
5157
- @param {String} modelName Optionally, a model type used to determine which serializer will be used
5158
- @param {Object} inputPayload
5159
- */
5160
- // TODO @runspired @deprecate pushPayload in favor of looking up the serializer
5161
- pushPayload(modelName, inputPayload) {
5162
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5163
- assertDestroyingStore(this, 'pushPayload');
5164
- }
5165
- const payload = inputPayload || modelName;
5166
- const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
5167
- const serializer = this.serializerFor(normalizedModelName);
5168
- assert(`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`, serializer && typeof serializer.pushPayload === 'function');
5169
- serializer.pushPayload(this, payload);
5238
+ * Fetches the related link for this document, returning a promise that resolves
5239
+ * with the document when the request completes. If no related link is present,
5240
+ * will fallback to the self link if present
5241
+ *
5242
+ * @method fetch
5243
+ * @public
5244
+ * @param {object} options
5245
+ * @return Promise<Document>
5246
+ */
5247
+ fetch(options = {}) {
5248
+ assert(`No self or related link`, this.links?.related || this.links?.self);
5249
+ options.cacheOptions = options.cacheOptions || {};
5250
+ options.cacheOptions.key = this.identifier?.lid;
5251
+ return _classPrivateFieldBase(this, _request)[_request](this.links.related ? 'related' : 'self', options);
5170
5252
  }
5171
5253
 
5172
5254
  /**
5173
- * Trigger a save for a Record.
5255
+ * Fetches the next link for this document, returning a promise that resolves
5256
+ * with the new document when the request completes, or null if there is no
5257
+ * next link.
5174
5258
  *
5175
- * @method saveRecord
5259
+ * @method next
5176
5260
  * @public
5177
- * @param {RecordInstance} record
5178
- * @param options
5179
- * @returns {Promise<RecordInstance>}
5261
+ * @param {object} options
5262
+ * @return Promise<Document | null>
5180
5263
  */
5181
- saveRecord(record, options = {}) {
5182
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5183
- assertDestroyingStore(this, 'saveRecord');
5184
- }
5185
- assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
5186
- let identifier = recordIdentifierFor(record);
5187
- const cache = this.cache;
5188
- if (!identifier) {
5189
- // this commonly means we're disconnected
5190
- // but just in case we reject here to prevent bad things.
5191
- return Promise.reject(`Record Is Disconnected`);
5192
- }
5193
- // TODO we used to check if the record was destroyed here
5194
- assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
5195
- if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5196
- return Promise.resolve(record);
5197
- }
5198
- if (!options) {
5199
- options = {};
5200
- }
5201
- let operation = 'updateRecord';
5202
- if (cache.isNew(identifier)) {
5203
- operation = 'createRecord';
5204
- } else if (cache.isDeleted(identifier)) {
5205
- operation = 'deleteRecord';
5206
- }
5207
- const request = {
5208
- op: operation,
5209
- data: {
5210
- options,
5211
- record: identifier
5212
- },
5213
- cacheOptions: {
5214
- [SkipCache]: true
5215
- }
5216
- };
5264
+ next(options = {}) {
5265
+ return _classPrivateFieldBase(this, _request)[_request]('next', options);
5266
+ }
5217
5267
 
5218
- // we lie here on the type because legacy doesn't have enough context
5219
- cache.willCommit(identifier, {
5220
- request
5221
- });
5222
- return this.request(request).then(document => document.content);
5268
+ /**
5269
+ * Fetches the prev link for this document, returning a promise that resolves
5270
+ * with the new document when the request completes, or null if there is no
5271
+ * prev link.
5272
+ *
5273
+ * @method prev
5274
+ * @public
5275
+ * @param {object} options
5276
+ * @return Promise<Document | null>
5277
+ */
5278
+ prev(options = {}) {
5279
+ return _classPrivateFieldBase(this, _request)[_request]('prev', options);
5223
5280
  }
5224
5281
 
5225
5282
  /**
5226
- * Instantiation hook allowing applications or addons to configure the store
5227
- * to utilize a custom Cache implementation.
5283
+ * Fetches the first link for this document, returning a promise that resolves
5284
+ * with the new document when the request completes, or null if there is no
5285
+ * first link.
5228
5286
  *
5229
- * This hook should not be called directly by consuming applications or libraries.
5230
- * Use `Store.cache` to access the Cache instance.
5287
+ * @method first
5288
+ * @public
5289
+ * @param {object} options
5290
+ * @return Promise<Document | null>
5291
+ */
5292
+ first(options = {}) {
5293
+ return _classPrivateFieldBase(this, _request)[_request]('first', options);
5294
+ }
5295
+
5296
+ /**
5297
+ * Fetches the last link for this document, returning a promise that resolves
5298
+ * with the new document when the request completes, or null if there is no
5299
+ * last link.
5231
5300
  *
5232
- * @method createCache (hook)
5301
+ * @method last
5233
5302
  * @public
5234
- * @param storeWrapper
5235
- * @returns {Cache}
5303
+ * @param {object} options
5304
+ * @return Promise<Document | null>
5236
5305
  */
5306
+ last(options = {}) {
5307
+ return _classPrivateFieldBase(this, _request)[_request]('last', options);
5308
+ }
5237
5309
 
5238
5310
  /**
5239
- * Returns the cache instance associated to this Store, instantiates the Cache
5240
- * if necessary via `Store.createCache`
5311
+ * Implemented for `JSON.stringify` support.
5241
5312
  *
5242
- * @property {Cache} cache
5313
+ * Returns the JSON representation of the document wrapper.
5314
+ *
5315
+ * This is a shallow serialization, it does not deeply serialize
5316
+ * the document's contents, leaving that to the individual record
5317
+ * instances to determine how to do, if at all.
5318
+ *
5319
+ * @method toJSON
5243
5320
  * @public
5321
+ * @return
5244
5322
  */
5245
- get cache() {
5246
- let {
5247
- cache
5248
- } = this._instanceCache;
5249
- if (!cache) {
5250
- cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5251
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5252
- cache = new CacheManager(cache);
5253
- }
5323
+ toJSON() {
5324
+ const data = {};
5325
+ data.identifier = this.identifier;
5326
+ if (this.data !== undefined) {
5327
+ data.data = this.data;
5254
5328
  }
5255
- return cache;
5329
+ if (this.links !== undefined) {
5330
+ data.links = this.links;
5331
+ }
5332
+ if (this.errors !== undefined) {
5333
+ data.errors = this.errors;
5334
+ }
5335
+ if (this.meta !== undefined) {
5336
+ data.meta = this.meta;
5337
+ }
5338
+ return data;
5339
+ }
5340
+ }
5341
+ async function _request2(link, options) {
5342
+ const href = this.links?.[link];
5343
+ if (!href) {
5344
+ return null;
5256
5345
  }
5346
+ options.method = options.method || 'GET';
5347
+ const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
5348
+ url: urlFromLink(href)
5349
+ }));
5350
+ return response.content;
5351
+ }
5352
+ defineSignal(Document.prototype, 'data');
5353
+ defineSignal(Document.prototype, 'links');
5354
+ defineSignal(Document.prototype, 'errors');
5355
+ defineSignal(Document.prototype, 'meta');
5257
5356
 
5258
- /**
5259
- `normalize` converts a json payload into the normalized form that
5260
- [push](../methods/push?anchor=push) expects.
5261
- Example
5262
- ```js
5263
- socket.on('message', function(message) {
5264
- let modelName = message.model;
5265
- let data = message.data;
5266
- store.push(store.normalize(modelName, data));
5267
- });
5268
- ```
5269
- @method normalize
5270
- @public
5271
- @param {String} modelName The name of the model type for this payload
5272
- @param {Object} payload
5273
- @return {Object} The normalized payload
5274
- */
5275
- // TODO @runspired @deprecate users should call normalize on the associated serializer directly
5276
- normalize(modelName, payload) {
5277
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5278
- assertDestroyingStore(this, 'normalize');
5357
+ /**
5358
+ * @module @ember-data/store
5359
+ */
5360
+
5361
+ /**
5362
+ * A service which an application may provide to the store via
5363
+ * the store's `lifetimes` property to configure the behavior
5364
+ * of the CacheHandler.
5365
+ *
5366
+ * The default behavior for request lifetimes is to never expire
5367
+ * unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
5368
+ *
5369
+ * Implementing this service allows you to programatically define
5370
+ * when a request should be considered expired.
5371
+ *
5372
+ * @class <Interface> LifetimesService
5373
+ * @public
5374
+ */
5375
+
5376
+ const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
5377
+ function isErrorDocument(document) {
5378
+ return 'errors' in document;
5379
+ }
5380
+ function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
5381
+ const {
5382
+ identifier
5383
+ } = options;
5384
+ if (isErrorDocument(document)) {
5385
+ if (!identifier && !options.shouldHydrate) {
5386
+ return document;
5387
+ }
5388
+ let doc;
5389
+ if (identifier) {
5390
+ doc = store._documentCache.get(identifier);
5391
+ }
5392
+ if (!doc) {
5393
+ doc = new Document(store, identifier);
5394
+ copyDocumentProperties(doc, document);
5395
+ if (identifier) {
5396
+ store._documentCache.set(identifier, doc);
5397
+ }
5398
+ } else if (!isFromCache) {
5399
+ doc.data = undefined;
5400
+ copyDocumentProperties(doc, document);
5401
+ }
5402
+ return options.shouldHydrate ? doc : document;
5403
+ }
5404
+ if (Array.isArray(document.data)) {
5405
+ const {
5406
+ recordArrayManager
5407
+ } = store;
5408
+ if (!identifier) {
5409
+ if (!options.shouldHydrate) {
5410
+ return document;
5411
+ }
5412
+ const data = recordArrayManager.createArray({
5413
+ type: request.url,
5414
+ identifiers: document.data,
5415
+ doc: document,
5416
+ query: request
5417
+ });
5418
+ const doc = new Document(store, null);
5419
+ doc.data = data;
5420
+ doc.meta = document.meta;
5421
+ doc.links = document.links;
5422
+ return doc;
5279
5423
  }
5280
- assert(`You need to pass a model name to the store's normalize method`, modelName);
5281
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${typeof modelName}`, typeof modelName === 'string');
5282
- const normalizedModelName = normalizeModelName(modelName);
5283
- const serializer = this.serializerFor(normalizedModelName);
5284
- const schema = this.modelFor(normalizedModelName);
5285
- assert(`You must define a normalize method in your serializer in order to call store.normalize`, typeof serializer?.normalize === 'function');
5286
- return serializer.normalize(schema, payload);
5287
- }
5288
-
5289
- /**
5290
- Returns an instance of the adapter for a given type. For
5291
- example, `adapterFor('person')` will return an instance of
5292
- the adapter located at `app/adapters/person.js`
5293
- If no `person` adapter is found, this method will look
5294
- for an `application` adapter (the default adapter for
5295
- your entire application).
5296
- @method adapterFor
5297
- @public
5298
- @param {String} modelName
5299
- @return Adapter
5300
- */
5301
- adapterFor(modelName) {
5302
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5303
- assertDestroyingStore(this, 'adapterFor');
5424
+ let managed = recordArrayManager._keyedArrays.get(identifier.lid);
5425
+ if (!managed) {
5426
+ managed = recordArrayManager.createArray({
5427
+ type: identifier.lid,
5428
+ identifiers: document.data,
5429
+ doc: document
5430
+ });
5431
+ recordArrayManager._keyedArrays.set(identifier.lid, managed);
5432
+ const doc = new Document(store, identifier);
5433
+ doc.data = managed;
5434
+ doc.meta = document.meta;
5435
+ doc.links = document.links;
5436
+ store._documentCache.set(identifier, doc);
5437
+ return options.shouldHydrate ? doc : document;
5438
+ } else {
5439
+ const doc = store._documentCache.get(identifier);
5440
+ if (!isFromCache) {
5441
+ recordArrayManager.populateManagedArray(managed, document.data, document);
5442
+ doc.data = managed;
5443
+ doc.meta = document.meta;
5444
+ doc.links = document.links;
5445
+ }
5446
+ return options.shouldHydrate ? doc : document;
5304
5447
  }
5305
- assert(`You need to pass a model name to the store's adapterFor method`, modelName);
5306
- assert(`Passing classes to store.adapterFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5307
- let normalizedModelName = normalizeModelName(modelName);
5308
- let {
5309
- _adapterCache
5310
- } = this;
5311
- let adapter = _adapterCache[normalizedModelName];
5312
- if (adapter) {
5313
- return adapter;
5448
+ } else {
5449
+ if (!identifier && !options.shouldHydrate) {
5450
+ return document;
5314
5451
  }
5315
- const owner = getOwner(this);
5316
-
5317
- // name specific adapter
5318
- adapter = owner.lookup(`adapter:${normalizedModelName}`);
5319
- if (adapter !== undefined) {
5320
- _adapterCache[normalizedModelName] = adapter;
5321
- return adapter;
5452
+ const data = document.data ? store.peekRecord(document.data) : null;
5453
+ let doc;
5454
+ if (identifier) {
5455
+ doc = store._documentCache.get(identifier);
5322
5456
  }
5323
-
5324
- // no adapter found for the specific name, fallback and check for application adapter
5325
- adapter = _adapterCache.application || owner.lookup('adapter:application');
5326
- if (adapter !== undefined) {
5327
- _adapterCache[normalizedModelName] = adapter;
5328
- _adapterCache.application = adapter;
5329
- return adapter;
5457
+ if (!doc) {
5458
+ doc = new Document(store, identifier);
5459
+ doc.data = data;
5460
+ copyDocumentProperties(doc, document);
5461
+ if (identifier) {
5462
+ store._documentCache.set(identifier, doc);
5463
+ }
5464
+ } else if (!isFromCache) {
5465
+ doc.data = data;
5466
+ copyDocumentProperties(doc, document);
5330
5467
  }
5331
- assert(`No adapter was found for '${modelName}' and no 'application' adapter was found as a fallback.`);
5468
+ return options.shouldHydrate ? doc : document;
5332
5469
  }
5333
-
5334
- /**
5335
- Returns an instance of the serializer for a given type. For
5336
- example, `serializerFor('person')` will return an instance of
5337
- `App.PersonSerializer`.
5338
- If no `App.PersonSerializer` is found, this method will look
5339
- for an `App.ApplicationSerializer` (the default serializer for
5340
- your entire application).
5341
- If a serializer cannot be found on the adapter, it will fall back
5342
- to an instance of `JSONSerializer`.
5343
- @method serializerFor
5344
- @public
5345
- @param {String} modelName the record to serialize
5346
- @return {Serializer}
5347
- */
5348
- serializerFor(modelName) {
5349
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5350
- assertDestroyingStore(this, 'serializerFor');
5351
- }
5352
- assert(`You need to pass a model name to the store's serializerFor method`, modelName);
5353
- assert(`Passing classes to store.serializerFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5354
- let normalizedModelName = normalizeModelName(modelName);
5355
- let {
5356
- _serializerCache
5357
- } = this;
5358
- let serializer = _serializerCache[normalizedModelName];
5359
- if (serializer) {
5360
- return serializer;
5361
- }
5362
-
5363
- // by name
5364
- const owner = getOwner(this);
5365
- serializer = owner.lookup(`serializer:${normalizedModelName}`);
5366
- if (serializer !== undefined) {
5367
- _serializerCache[normalizedModelName] = serializer;
5368
- return serializer;
5470
+ }
5471
+ function calcShouldFetch(store, request, hasCachedValue, identifier) {
5472
+ const {
5473
+ cacheOptions
5474
+ } = request;
5475
+ return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
5476
+ }
5477
+ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
5478
+ const {
5479
+ cacheOptions
5480
+ } = request;
5481
+ return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false));
5482
+ }
5483
+ function isMutation(request) {
5484
+ return Boolean(request.op && MUTATION_OPS.has(request.op));
5485
+ }
5486
+ function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
5487
+ const {
5488
+ store
5489
+ } = context.request;
5490
+ const shouldHydrate = context.request[EnableHydration] || false;
5491
+ let isMut = false;
5492
+ if (isMutation(context.request)) {
5493
+ isMut = true;
5494
+ // TODO should we handle multiple records in request.records by iteratively calling willCommit for each
5495
+ const record = context.request.data?.record || context.request.records?.[0];
5496
+ assert(`Expected to receive a list of records included in the ${context.request.op} request`, record);
5497
+ store.cache.willCommit(record, context);
5498
+ }
5499
+ if (store.lifetimes?.willRequest) {
5500
+ store.lifetimes.willRequest(context.request, identifier, store);
5501
+ }
5502
+ const promise = next(context.request).then(document => {
5503
+ store.requestManager._pending.delete(context.id);
5504
+ store._enableAsyncFlush = true;
5505
+ let response;
5506
+ store._join(() => {
5507
+ if (isMutation(context.request)) {
5508
+ const record = context.request.data?.record || context.request.records?.[0];
5509
+ response = store.cache.didCommit(record, document);
5510
+ } else {
5511
+ response = store.cache.put(document);
5512
+ }
5513
+ response = maybeUpdateUiObjects(store, context.request, {
5514
+ shouldHydrate,
5515
+ shouldFetch,
5516
+ shouldBackgroundFetch,
5517
+ identifier
5518
+ }, response, false);
5519
+ });
5520
+ store._enableAsyncFlush = null;
5521
+ if (store.lifetimes?.didRequest) {
5522
+ store.lifetimes.didRequest(context.request, document.response, identifier, store);
5369
5523
  }
5370
-
5371
- // no serializer found for the specific model, fallback and check for application serializer
5372
- serializer = _serializerCache.application || owner.lookup('serializer:application');
5373
- if (serializer !== undefined) {
5374
- _serializerCache[normalizedModelName] = serializer;
5375
- _serializerCache.application = serializer;
5376
- return serializer;
5524
+ if (shouldFetch) {
5525
+ return response;
5526
+ } else if (shouldBackgroundFetch) {
5527
+ store.notifications._flush();
5377
5528
  }
5378
- return null;
5379
- }
5380
-
5381
- // @ts-expect-error
5382
- destroy() {
5383
- if (this.isDestroyed) {
5384
- // @ember/test-helpers will call destroy multiple times
5385
- return;
5529
+ }, error => {
5530
+ store.requestManager._pending.delete(context.id);
5531
+ if (context.request.signal?.aborted) {
5532
+ throw error;
5386
5533
  }
5387
- this.isDestroying = true;
5388
- // enqueue destruction of any adapters/serializers we have created
5389
- for (let adapterName in this._adapterCache) {
5390
- let adapter = this._adapterCache[adapterName];
5391
- if (typeof adapter.destroy === 'function') {
5392
- adapter.destroy();
5534
+ store.requestManager._pending.delete(context.id);
5535
+ store._enableAsyncFlush = true;
5536
+ let response;
5537
+ store._join(() => {
5538
+ if (isMutation(context.request)) {
5539
+ // TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
5540
+ // currently we let the response remain undefiend.
5541
+ const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
5542
+ const record = context.request.data?.record || context.request.records?.[0];
5543
+ store.cache.commitWasRejected(record, errors);
5544
+ // re-throw the original error to preserve `errors` property.
5545
+ throw error;
5546
+ } else {
5547
+ response = store.cache.put(error);
5548
+ response = maybeUpdateUiObjects(store, context.request, {
5549
+ shouldHydrate,
5550
+ shouldFetch,
5551
+ shouldBackgroundFetch,
5552
+ identifier
5553
+ }, response, false);
5393
5554
  }
5555
+ });
5556
+ store._enableAsyncFlush = null;
5557
+ if (identifier && store.lifetimes?.didRequest) {
5558
+ store.lifetimes.didRequest(context.request, error.response, identifier, store);
5394
5559
  }
5395
- for (let serializerName in this._serializerCache) {
5396
- let serializer = this._serializerCache[serializerName];
5397
- if (typeof serializer.destroy === 'function') {
5398
- serializer.destroy();
5399
- }
5560
+ if (!shouldBackgroundFetch) {
5561
+ const newError = cloneError(error);
5562
+ newError.content = response;
5563
+ throw newError;
5564
+ } else {
5565
+ store.notifications._flush();
5400
5566
  }
5401
- this._graph?.destroy();
5402
- this._graph = undefined;
5403
- this.notifications.destroy();
5404
- this.recordArrayManager.destroy();
5405
- this.identifierCache.destroy();
5406
- this.unloadAll();
5407
- this.isDestroyed = true;
5408
- }
5409
- static create(args) {
5410
- return new this(args);
5567
+ });
5568
+ if (!isMut) {
5569
+ return promise;
5411
5570
  }
5571
+ assert(`Expected a mutation`, isMutation(context.request));
5572
+
5573
+ // for mutations we need to enqueue the promise with the requestStateService
5574
+ // TODO should we enque a request per record in records?
5575
+ const record = context.request.data?.record || context.request.records?.[0];
5576
+ return store._requestCache._enqueue(promise, {
5577
+ data: [{
5578
+ op: 'saveRecord',
5579
+ recordIdentifier: record,
5580
+ options: undefined
5581
+ }]
5582
+ });
5412
5583
  }
5413
- let assertDestroyingStore;
5414
- let assertDestroyedStoreOnly;
5415
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5416
- // eslint-disable-next-line @typescript-eslint/no-shadow
5417
- assertDestroyingStore = function assertDestroyingStore(store, method) {
5418
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5419
- };
5420
- // eslint-disable-next-line @typescript-eslint/no-shadow
5421
- assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5422
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5423
- };
5424
- }
5425
- function isMaybeIdentifier(maybeIdentifier) {
5426
- return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5584
+ function cloneError(error) {
5585
+ const cloned = new Error(error.message);
5586
+ cloned.stack = error.stack;
5587
+ cloned.error = error.error;
5588
+ return cloned;
5427
5589
  }
5428
- function normalizeProperties(store, identifier, properties) {
5429
- // assert here
5430
- if (properties !== undefined) {
5431
- if ('id' in properties) {
5432
- assert(`expected id to be a string or null`, properties.id !== undefined);
5590
+ const CacheHandler = {
5591
+ request(context, next) {
5592
+ // if we have no cache or no cache-key skip cache handling
5593
+ if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
5594
+ return next(context.request);
5433
5595
  }
5434
- assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5435
5596
  const {
5436
- type
5437
- } = identifier;
5597
+ store
5598
+ } = context.request;
5599
+ const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
5600
+ const peeked = identifier ? store.cache.peekRequest(identifier) : null;
5438
5601
 
5439
- // convert relationship Records to RecordDatas before passing to RecordData
5440
- let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5441
- type
5442
- });
5443
- if (defs !== null) {
5444
- let keys = Object.keys(properties);
5445
- let relationshipValue;
5446
- for (let i = 0; i < keys.length; i++) {
5447
- let prop = keys[i];
5448
- let def = defs[prop];
5449
- if (def !== undefined) {
5450
- if (def.kind === 'hasMany') {
5451
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5452
- assertRecordsPassedToHasMany(properties[prop]);
5453
- }
5454
- relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5455
- } else {
5456
- relationshipValue = extractIdentifierFromRecord(properties[prop]);
5457
- }
5458
- properties[prop] = relationshipValue;
5459
- }
5460
- }
5602
+ // determine if we should skip cache
5603
+ if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
5604
+ return fetchContentAndHydrate(next, context, identifier, true, false);
5605
+ }
5606
+
5607
+ // if we have not skipped cache, determine if we should update behind the scenes
5608
+ if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
5609
+ const promise = fetchContentAndHydrate(next, context, identifier, false, true);
5610
+ store.requestManager._pending.set(context.id, promise);
5611
+ }
5612
+ const shouldHydrate = context.request[EnableHydration] || false;
5613
+ if ('error' in peeked) {
5614
+ const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5615
+ shouldHydrate,
5616
+ identifier
5617
+ }, peeked.content, true) : peeked.content;
5618
+ const newError = cloneError(peeked);
5619
+ newError.content = content;
5620
+ throw newError;
5461
5621
  }
5622
+ return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5623
+ shouldHydrate,
5624
+ identifier
5625
+ }, peeked.content, true) : peeked.content);
5462
5626
  }
5463
- return properties;
5464
- }
5465
- function assertRecordsPassedToHasMany(records) {
5466
- assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5467
- assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5468
- return records.every(record => {
5469
- try {
5470
- recordIdentifierFor(record);
5471
- return true;
5472
- } catch {
5473
- return false;
5474
- }
5475
- });
5476
- }());
5477
- }
5478
- function extractIdentifiersFromRecords(records) {
5479
- return records.map(record => extractIdentifierFromRecord(record));
5480
- }
5481
- function extractIdentifierFromRecord(recordOrPromiseRecord) {
5482
- if (!recordOrPromiseRecord) {
5483
- return null;
5627
+ };
5628
+ function copyDocumentProperties(target, source) {
5629
+ if ('links' in source) {
5630
+ target.links = source.links;
5631
+ }
5632
+ if ('meta' in source) {
5633
+ target.meta = source.meta;
5634
+ }
5635
+ if ('errors' in source) {
5636
+ target.errors = source.errors;
5484
5637
  }
5485
- const extract = recordIdentifierFor;
5486
- return extract(recordOrPromiseRecord);
5487
5638
  }
5488
- 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 };
5639
+ 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 };