@ember-data/store 5.4.0-alpha.5 → 5.4.0-alpha.51

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 (77) hide show
  1. package/README.md +8 -4
  2. package/addon/-private.js +1 -1
  3. package/addon/{store-service-aa7f91c0.js → cache-handler-BGVZPfAA.js} +1247 -1042
  4. package/addon/cache-handler-BGVZPfAA.js.map +1 -0
  5. package/addon/index.js +1 -1
  6. package/addon/index.js.map +1 -1
  7. package/addon-main.js +1 -0
  8. package/package.json +64 -43
  9. package/unstable-preview-types/-private/cache-handler.d.ts +141 -0
  10. package/unstable-preview-types/-private/cache-handler.d.ts.map +1 -0
  11. package/unstable-preview-types/-private/caches/cache-utils.d.ts +11 -0
  12. package/unstable-preview-types/-private/caches/cache-utils.d.ts.map +1 -0
  13. package/unstable-preview-types/-private/caches/identifier-cache.d.ts +182 -0
  14. package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -0
  15. package/unstable-preview-types/-private/caches/instance-cache.d.ts +63 -0
  16. package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -0
  17. package/unstable-preview-types/-private/caches/resource-utils.d.ts +12 -0
  18. package/unstable-preview-types/-private/caches/resource-utils.d.ts.map +1 -0
  19. package/unstable-preview-types/-private/document.d.ts +146 -0
  20. package/unstable-preview-types/-private/document.d.ts.map +1 -0
  21. package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts +179 -0
  22. package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts.map +1 -0
  23. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts +19 -0
  24. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts.map +1 -0
  25. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts +29 -0
  26. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts.map +1 -0
  27. package/unstable-preview-types/-private/managers/cache-manager.d.ts +442 -0
  28. package/unstable-preview-types/-private/managers/cache-manager.d.ts.map +1 -0
  29. package/unstable-preview-types/-private/managers/notification-manager.d.ts +98 -0
  30. package/unstable-preview-types/-private/managers/notification-manager.d.ts.map +1 -0
  31. package/unstable-preview-types/-private/managers/record-array-manager.d.ts +97 -0
  32. package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -0
  33. package/unstable-preview-types/-private/network/request-cache.d.ts +109 -0
  34. package/unstable-preview-types/-private/network/request-cache.d.ts.map +1 -0
  35. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +133 -0
  36. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -0
  37. package/unstable-preview-types/-private/record-arrays/native-proxy-type-fix.d.ts +118 -0
  38. package/unstable-preview-types/-private/record-arrays/native-proxy-type-fix.d.ts.map +1 -0
  39. package/unstable-preview-types/-private/store-service.d.ts +1554 -0
  40. package/unstable-preview-types/-private/store-service.d.ts.map +1 -0
  41. package/unstable-preview-types/-private/utils/coerce-id.d.ts +10 -0
  42. package/unstable-preview-types/-private/utils/coerce-id.d.ts.map +1 -0
  43. package/unstable-preview-types/-private/utils/construct-resource.d.ts +10 -0
  44. package/unstable-preview-types/-private/utils/construct-resource.d.ts.map +1 -0
  45. package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts +7 -0
  46. package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts.map +1 -0
  47. package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts +4 -0
  48. package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts.map +1 -0
  49. package/unstable-preview-types/-private/utils/normalize-model-name.d.ts +4 -0
  50. package/unstable-preview-types/-private/utils/normalize-model-name.d.ts.map +1 -0
  51. package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts +4 -0
  52. package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts.map +1 -0
  53. package/unstable-preview-types/-private.d.ts +20 -0
  54. package/unstable-preview-types/-private.d.ts.map +1 -0
  55. package/unstable-preview-types/-types/overview.d.ts +21 -0
  56. package/unstable-preview-types/-types/overview.d.ts.map +1 -0
  57. package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts +107 -0
  58. package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts.map +1 -0
  59. package/unstable-preview-types/-types/q/cache.d.ts +47 -0
  60. package/unstable-preview-types/-types/q/cache.d.ts.map +1 -0
  61. package/unstable-preview-types/-types/q/ds-model.d.ts +15 -0
  62. package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -0
  63. package/unstable-preview-types/-types/q/identifier.d.ts +171 -0
  64. package/unstable-preview-types/-types/q/identifier.d.ts.map +1 -0
  65. package/unstable-preview-types/-types/q/promise-proxies.d.ts +4 -0
  66. package/unstable-preview-types/-types/q/promise-proxies.d.ts.map +1 -0
  67. package/unstable-preview-types/-types/q/record-data-json-api.d.ts +36 -0
  68. package/unstable-preview-types/-types/q/record-data-json-api.d.ts.map +1 -0
  69. package/unstable-preview-types/-types/q/record-instance.d.ts +29 -0
  70. package/unstable-preview-types/-types/q/record-instance.d.ts.map +1 -0
  71. package/unstable-preview-types/-types/q/schema-service.d.ts +214 -0
  72. package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -0
  73. package/unstable-preview-types/-types/q/store.d.ts +17 -0
  74. package/unstable-preview-types/-types/q/store.d.ts.map +1 -0
  75. package/unstable-preview-types/index.d.ts +220 -0
  76. package/unstable-preview-types/index.d.ts.map +1 -0
  77. package/addon/store-service-aa7f91c0.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,14 +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
- const DEBUG_STALE_CACHE_OWNER = Symbol('warpDriveStaleCache');
400
-
401
- // also present in production
402
- const CACHE_OWNER = Symbol('warpDriveCache');
403
47
  function normalizeModelName(type) {
404
48
  if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_TYPES)) {
405
49
  const result = dasherize(type);
@@ -428,7 +72,7 @@ function installPolyfill() {
428
72
  // we might be able to optimize this by requesting more bytes than we need at a time
429
73
  const rng = function () {
430
74
  // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
431
- let rnds8 = new Uint8Array(16);
75
+ const rnds8 = new Uint8Array(16);
432
76
  if (!CRYPTO.getRandomValues && !isFastBoot) {
433
77
  throw new Error(`Unable to generate bytes for UUID`);
434
78
  }
@@ -444,12 +88,12 @@ function installPolyfill() {
444
88
  byteToHex[i] = (i + 0x100).toString(16).substr(1);
445
89
  }
446
90
  const bytesToUuid = function (buf) {
447
- let bth = byteToHex;
91
+ const bth = byteToHex;
448
92
  // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
449
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('');
450
94
  };
451
95
  CRYPTO.randomUUID = function uuidv4() {
452
- let rnds = rng();
96
+ const rnds = rng();
453
97
 
454
98
  // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
455
99
  rnds[6] = rnds[6] & 0x0f | 0x40;
@@ -503,6 +147,7 @@ function freeze(obj) {
503
147
 
504
148
  // type IdentifierTypeLookup = { all: Set<StableRecordIdentifier>; id: Map<string, StableRecordIdentifier> };
505
149
  // type IdentifiersByType = Map<string, IdentifierTypeLookup>;
150
+
506
151
  let configuredForgetMethod;
507
152
  let configuredGenerationMethod;
508
153
  let configuredResetMethod;
@@ -610,7 +255,8 @@ class IdentifierCache {
610
255
  this._cache = {
611
256
  resources: new Map(),
612
257
  resourcesByType: Object.create(null),
613
- documents: new Map()
258
+ documents: new Map(),
259
+ polymorphicLidBackMap: new Map()
614
260
  };
615
261
  }
616
262
 
@@ -664,7 +310,7 @@ class IdentifierCache {
664
310
  console.log(`Identifiers: ${lid ? 'no ' : ''}lid ${lid ? lid + ' ' : ''}determined for resource`, resource);
665
311
  }
666
312
  let identifier = /*#__NOINLINE__*/getIdentifierFromLid(this._cache, lid, resource);
667
- if (identifier !== undefined) {
313
+ if (identifier !== null) {
668
314
  if (macroCondition(getOwnConfig().debug.LOG_IDENTIFIERS)) {
669
315
  // eslint-disable-next-line no-console
670
316
  console.groupEnd();
@@ -706,7 +352,7 @@ class IdentifierCache {
706
352
  *
707
353
  * @method peekRecordIdentifier
708
354
  * @param resource
709
- * @returns {StableRecordIdentifier | undefined}
355
+ * @return {StableRecordIdentifier | undefined}
710
356
  * @private
711
357
  */
712
358
  peekRecordIdentifier(resource) {
@@ -718,7 +364,7 @@ class IdentifierCache {
718
364
  Returns `null` if the request does not have a `cacheKey` or `url`.
719
365
  @method getOrCreateDocumentIdentifier
720
366
  @param request
721
- @returns {StableDocumentIdentifier | null}
367
+ @return {StableDocumentIdentifier | null}
722
368
  @public
723
369
  */
724
370
  getOrCreateDocumentIdentifier(request) {
@@ -752,7 +398,7 @@ class IdentifierCache {
752
398
  - this referential stability of the object itself is guaranteed
753
399
  @method getOrCreateRecordIdentifier
754
400
  @param resource
755
- @returns {StableRecordIdentifier}
401
+ @return {StableRecordIdentifier}
756
402
  @public
757
403
  */
758
404
  getOrCreateRecordIdentifier(resource) {
@@ -767,12 +413,12 @@ class IdentifierCache {
767
413
  with the signature `generateMethod({ type }, 'record')`.
768
414
  @method createIdentifierForNewRecord
769
415
  @param data
770
- @returns {StableRecordIdentifier}
416
+ @return {StableRecordIdentifier}
771
417
  @public
772
418
  */
773
419
  createIdentifierForNewRecord(data) {
774
- let newLid = this._generate(data, 'record');
775
- let identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
420
+ const newLid = this._generate(data, 'record');
421
+ const identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
776
422
  id: data.id || null,
777
423
  type: data.type,
778
424
  lid: newLid,
@@ -810,7 +456,7 @@ class IdentifierCache {
810
456
  @method updateRecordIdentifier
811
457
  @param identifierObject
812
458
  @param data
813
- @returns {StableRecordIdentifier}
459
+ @return {StableRecordIdentifier}
814
460
  @public
815
461
  */
816
462
  updateRecordIdentifier(identifierObject, data) {
@@ -830,7 +476,7 @@ class IdentifierCache {
830
476
  }
831
477
  }
832
478
  if (existingIdentifier) {
833
- let generatedIdentifier = identifier;
479
+ const generatedIdentifier = identifier;
834
480
  identifier = this._mergeRecordIdentifiers(keyInfo, generatedIdentifier, existingIdentifier, data);
835
481
 
836
482
  // make sure that the `lid` on the data we are processing matches the lid we kept
@@ -842,7 +488,7 @@ class IdentifierCache {
842
488
  console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
843
489
  }
844
490
  }
845
- let id = identifier.id;
491
+ const id = identifier.id;
846
492
  /*#__NOINLINE__*/
847
493
  performRecordIdentifierUpdate(identifier, keyInfo, data, this._update);
848
494
  const newId = identifier.id;
@@ -876,16 +522,32 @@ class IdentifierCache {
876
522
  const kept = this._merge(identifier, existingIdentifier, data);
877
523
  const abandoned = kept === identifier ? existingIdentifier : identifier;
878
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
+
879
532
  // cleanup the identifier we no longer need
880
533
  this.forgetRecordIdentifier(abandoned);
881
534
 
882
- // ensure a secondary cache entry for this id for the identifier we do keep
883
- // 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);
884
537
 
885
- // ensure a secondary cache entry for this id for the abandoned identifier's type we do keep
886
- // let baseKeyOptions = getTypeIndex(this._cache.resourcesByType, existingIdentifier.type);
887
- // 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);
888
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);
889
551
  return kept;
890
552
  }
891
553
 
@@ -908,6 +570,13 @@ class IdentifierCache {
908
570
  }
909
571
  this._cache.resources.delete(identifier.lid);
910
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
+ }
911
580
  if (macroCondition(getOwnConfig().env.DEBUG)) {
912
581
  identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
913
582
  }
@@ -953,17 +622,22 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
953
622
  },
954
623
  set [DEBUG_STALE_CACHE_OWNER](value) {
955
624
  recordIdentifier[DEBUG_STALE_CACHE_OWNER] = value;
956
- },
957
- // @ts-expect-error debug only
958
- toString() {
625
+ }
626
+ };
627
+ Object.defineProperty(wrapper, 'toString', {
628
+ enumerable: false,
629
+ value: () => {
959
630
  const {
960
631
  type,
961
632
  id,
962
633
  lid
963
634
  } = recordIdentifier;
964
635
  return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
965
- },
966
- toJSON() {
636
+ }
637
+ });
638
+ Object.defineProperty(wrapper, 'toJSON', {
639
+ enumerable: false,
640
+ value: () => {
967
641
  const {
968
642
  type,
969
643
  id,
@@ -975,7 +649,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
975
649
  lid
976
650
  };
977
651
  }
978
- };
652
+ });
979
653
  wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
980
654
  wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
981
655
  IDENTIFIERS.add(wrapper);
@@ -993,7 +667,7 @@ function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
993
667
  } = keyInfo;
994
668
 
995
669
  // get the mutable instance behind our proxy wrapper
996
- let wrapper = identifier;
670
+ const wrapper = identifier;
997
671
  identifier = DEBUG_MAP.get(wrapper);
998
672
  if (hasLid(data)) {
999
673
  const lid = data.lid;
@@ -1048,7 +722,7 @@ function detectMerge(cache, keyInfo, identifier, data) {
1048
722
  // we trigger a merge of the identifiers
1049
723
  // though probably we should just throw an error here
1050
724
  if (id !== null && id === newId && newType === type && hasLid(data) && data.lid !== lid) {
1051
- return cache.resources.get(data.lid) || false;
725
+ return getIdentifierFromLid(cache, data.lid, data) || false;
1052
726
 
1053
727
  // If the lids are the same, and ids are the same, but types are different we should trigger a merge of the identifiers
1054
728
  } else if (id !== null && id === newId && newType && newType !== type && hasLid(data) && data.lid === lid) {
@@ -1065,7 +739,7 @@ function getIdentifierFromLid(cache, lid, resource) {
1065
739
  // eslint-disable-next-line no-console
1066
740
  console.log(`Identifiers: cache ${identifier ? 'HIT' : 'MISS'} - Non-Stable ${lid}`, resource);
1067
741
  }
1068
- return identifier;
742
+ return identifier || null;
1069
743
  }
1070
744
  function addResourceToCache(cache, identifier) {
1071
745
  cache.resources.set(identifier.lid, identifier);
@@ -1082,28 +756,27 @@ function addResourceToCache(cache, identifier) {
1082
756
  typeSet.id.set(identifier.id, identifier);
1083
757
  }
1084
758
  }
1085
- var _class$1, _descriptor$1;
1086
759
 
1087
760
  /**
1088
761
  @module @ember-data/store
1089
762
  */
763
+
1090
764
  /**
1091
765
  @module @ember-data/store
1092
766
  */
767
+
1093
768
  /**
1094
769
  A `RecordReference` is a low-level API that allows users and
1095
770
  addon authors to perform meta-operations on a record.
1096
771
 
1097
772
  @class RecordReference
1098
773
  @public
1099
- @extends Reference
1100
774
  */
1101
- let RecordReference = (_class$1 = class RecordReference {
775
+ class RecordReference {
1102
776
  constructor(store, identifier) {
1103
777
  // unsubscribe token given to us by the notification manager
1104
778
  this.___token = void 0;
1105
779
  this.___identifier = void 0;
1106
- _initializerDefineProperty(this, "_ref", _descriptor$1, this);
1107
780
  this.store = store;
1108
781
  this.___identifier = identifier;
1109
782
  this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
@@ -1271,14 +944,8 @@ let RecordReference = (_class$1 = class RecordReference {
1271
944
  }
1272
945
  assert(`Unable to fetch record of type ${this.type} without an id`);
1273
946
  }
1274
- }, _descriptor$1 = _applyDecoratedDescriptor(_class$1.prototype, "_ref", [tracked], {
1275
- configurable: true,
1276
- enumerable: true,
1277
- writable: true,
1278
- initializer: function () {
1279
- return 0;
1280
- }
1281
- }), _class$1);
947
+ }
948
+ defineSignal(RecordReference.prototype, '_ref');
1282
949
 
1283
950
  /**
1284
951
  @module @ember-data/store
@@ -1309,6 +976,8 @@ class CacheCapabilitiesManager {
1309
976
  if (this._store._cbs) {
1310
977
  this._store._schedule('notify', () => this._flushNotifications());
1311
978
  } else {
979
+ // TODO @runspired determine if relationship mutations should schedule
980
+ // into join/run vs immediate flush
1312
981
  this._flushNotifications();
1313
982
  }
1314
983
  }
@@ -1316,7 +985,7 @@ class CacheCapabilitiesManager {
1316
985
  if (this._willNotify === false) {
1317
986
  return;
1318
987
  }
1319
- let pending = this._pendingNotifies;
988
+ const pending = this._pendingNotifies;
1320
989
  this._pendingNotifies = new Map();
1321
990
  this._willNotify = false;
1322
991
  pending.forEach((set, identifier) => {
@@ -1340,6 +1009,9 @@ class CacheCapabilitiesManager {
1340
1009
  getSchemaDefinitionService() {
1341
1010
  return this._store.getSchemaDefinitionService();
1342
1011
  }
1012
+ get schema() {
1013
+ return this._store.schema;
1014
+ }
1343
1015
  setRecordId(identifier, id) {
1344
1016
  assert(`Expected a stable identifier`, isStableIdentifier(identifier));
1345
1017
  this._store._instanceCache.setRecordId(identifier, id);
@@ -1373,6 +1045,9 @@ function peekCache(instance) {
1373
1045
  }
1374
1046
  return null;
1375
1047
  }
1048
+ function isDestroyable(record) {
1049
+ return Boolean(record && typeof record === 'object' && typeof record.destroy === 'function');
1050
+ }
1376
1051
 
1377
1052
  /**
1378
1053
  @module @ember-data/store
@@ -1400,8 +1075,9 @@ function peekRecordIdentifier(record) {
1400
1075
  @static
1401
1076
  @for @ember-data/store
1402
1077
  @param {Object} record a record instance previously obstained from the store.
1403
- @returns {StableRecordIdentifier}
1078
+ @return {StableRecordIdentifier}
1404
1079
  */
1080
+
1405
1081
  function recordIdentifierFor(record) {
1406
1082
  assert(`${String(record)} is not a record instantiated by @ember-data/store`, RecordCache.has(record));
1407
1083
  return RecordCache.get(record);
@@ -1447,11 +1123,11 @@ class InstanceCache {
1447
1123
  // @ts-expect-error TODO this needs to be fixed
1448
1124
  'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
1449
1125
  }
1450
- let staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1126
+ const staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1451
1127
 
1452
1128
  // check for duplicate entities
1453
- let keptHasRecord = this.__instances.record.has(keptIdentifier);
1454
- let staleHasRecord = this.__instances.record.has(staleIdentifier);
1129
+ const keptHasRecord = this.__instances.record.has(keptIdentifier);
1130
+ const staleHasRecord = this.__instances.record.has(staleIdentifier);
1455
1131
 
1456
1132
  // we cannot merge entities when both have records
1457
1133
  // (this may not be strictly true, we could probably swap the cache data the record points at)
@@ -1505,7 +1181,7 @@ class InstanceCache {
1505
1181
  return record;
1506
1182
  }
1507
1183
  getReference(identifier) {
1508
- let cache = this.__instances.reference;
1184
+ const cache = this.__instances.reference;
1509
1185
  let reference = cache.get(identifier);
1510
1186
  if (!reference) {
1511
1187
  reference = new RecordReference(this.store, identifier);
@@ -1536,7 +1212,7 @@ class InstanceCache {
1536
1212
  }
1537
1213
  disconnect(identifier) {
1538
1214
  const record = this.__instances.record.get(identifier);
1539
- 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);
1540
1216
  this.store._graph?.remove(identifier);
1541
1217
  this.store.identifierCache.forgetRecordIdentifier(identifier);
1542
1218
  removeRecordDataFor(identifier);
@@ -1604,7 +1280,7 @@ class InstanceCache {
1604
1280
  });
1605
1281
  } else {
1606
1282
  const typeCache = cache.resourcesByType;
1607
- let identifiers = typeCache[type]?.lid;
1283
+ const identifiers = typeCache[type]?.lid;
1608
1284
  if (identifiers) {
1609
1285
  identifiers.forEach(identifier => {
1610
1286
  // if (rds.has(identifier)) {
@@ -1622,7 +1298,7 @@ class InstanceCache {
1622
1298
  type,
1623
1299
  lid
1624
1300
  } = identifier;
1625
- let oldId = identifier.id;
1301
+ const oldId = identifier.id;
1626
1302
 
1627
1303
  // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
1628
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));
@@ -1641,7 +1317,7 @@ class InstanceCache {
1641
1317
  // eslint-disable-next-line no-console
1642
1318
  console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
1643
1319
  }
1644
- let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1320
+ const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1645
1321
  type,
1646
1322
  id
1647
1323
  });
@@ -1680,13 +1356,13 @@ function resourceIsFullyDeleted(instanceCache, identifier) {
1680
1356
  */
1681
1357
 
1682
1358
  function preloadData(store, identifier, preload) {
1683
- let jsonPayload = {};
1359
+ const jsonPayload = {};
1684
1360
  //TODO(Igor) consider the polymorphic case
1685
1361
  const schemas = store.getSchemaDefinitionService();
1686
1362
  const relationships = schemas.relationshipsDefinitionFor(identifier);
1687
1363
  Object.keys(preload).forEach(key => {
1688
- let preloadValue = preload[key];
1689
- let relationshipMeta = relationships[key];
1364
+ const preloadValue = preload[key];
1365
+ const relationshipMeta = relationships[key];
1690
1366
  if (relationshipMeta) {
1691
1367
  if (!jsonPayload.relationships) {
1692
1368
  jsonPayload.relationships = {};
@@ -1755,7 +1431,7 @@ function getShimClass(store, modelName) {
1755
1431
  }
1756
1432
  function mapFromHash(hash) {
1757
1433
  const map = new Map();
1758
- for (let i in hash) {
1434
+ for (const i in hash) {
1759
1435
  if (Object.prototype.hasOwnProperty.call(hash, i)) {
1760
1436
  map.set(i, hash[i]);
1761
1437
  }
@@ -1770,31 +1446,31 @@ class ShimModelClass {
1770
1446
  this.modelName = modelName;
1771
1447
  }
1772
1448
  get fields() {
1773
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1449
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1774
1450
  type: this.modelName
1775
1451
  });
1776
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1452
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1777
1453
  type: this.modelName
1778
1454
  });
1779
- let fields = new Map();
1455
+ const fields = new Map();
1780
1456
  Object.keys(attrs).forEach(key => fields.set(key, 'attribute'));
1781
1457
  Object.keys(relationships).forEach(key => fields.set(key, relationships[key].kind));
1782
1458
  return fields;
1783
1459
  }
1784
1460
  get attributes() {
1785
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1461
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1786
1462
  type: this.modelName
1787
1463
  });
1788
1464
  return mapFromHash(attrs);
1789
1465
  }
1790
1466
  get relationshipsByName() {
1791
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1467
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1792
1468
  type: this.modelName
1793
1469
  });
1794
1470
  return mapFromHash(relationships);
1795
1471
  }
1796
1472
  eachAttribute(callback, binding) {
1797
- let attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1473
+ const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1798
1474
  type: this.modelName
1799
1475
  });
1800
1476
  Object.keys(attrDefs).forEach(key => {
@@ -1802,7 +1478,7 @@ class ShimModelClass {
1802
1478
  });
1803
1479
  }
1804
1480
  eachRelationship(callback, binding) {
1805
- let relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1481
+ const relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1806
1482
  type: this.modelName
1807
1483
  });
1808
1484
  Object.keys(relationshipDefs).forEach(key => {
@@ -1820,6 +1496,16 @@ class ShimModelClass {
1820
1496
  });
1821
1497
  }
1822
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
+ }
1823
1509
  var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
1824
1510
  /**
1825
1511
  * The CacheManager wraps a Cache enforcing that only
@@ -1846,16 +1532,6 @@ class CacheManager {
1846
1532
  writable: true,
1847
1533
  value: void 0
1848
1534
  });
1849
- /**
1850
- * Query the cache for whether a given resource has been deleted and that deletion
1851
- * has also been persisted.
1852
- *
1853
- * @method isDeletionCommitted
1854
- * @public
1855
- * @param identifier
1856
- * @returns {boolean}
1857
- */
1858
- this.isDel = void 0;
1859
1535
  _classPrivateFieldBase(this, _cache)[_cache] = cache;
1860
1536
  }
1861
1537
 
@@ -1869,7 +1545,7 @@ class CacheManager {
1869
1545
  * semantics, `put` has `replace` semantics similar to
1870
1546
  * the `http` method `PUT`
1871
1547
  *
1872
- * the individually cacheabl
1548
+ * the individually cacheable
1873
1549
  * e resource data it may contain
1874
1550
  * should upsert, but the document data surrounding it should
1875
1551
  * fully replace any existing information
@@ -1882,7 +1558,7 @@ class CacheManager {
1882
1558
  *
1883
1559
  * @method put
1884
1560
  * @param {StructuredDocument} doc
1885
- * @returns {ResourceDocument}
1561
+ * @return {ResourceDocument}
1886
1562
  * @public
1887
1563
  */
1888
1564
  put(doc) {
@@ -1898,7 +1574,7 @@ class CacheManager {
1898
1574
  * @method patch
1899
1575
  * @public
1900
1576
  * @param op the operation to perform
1901
- * @returns {void}
1577
+ * @return {void}
1902
1578
  */
1903
1579
  patch(op) {
1904
1580
  _classPrivateFieldBase(this, _cache)[_cache].patch(op);
@@ -1933,7 +1609,7 @@ class CacheManager {
1933
1609
  * An implementation might want to do this because
1934
1610
  * de-referencing records which read from their own
1935
1611
  * blob is generally safer because the record does
1936
- * not require retainining connections to the Store
1612
+ * not require retaining connections to the Store
1937
1613
  * and Cache to present data on a per-field basis.
1938
1614
  *
1939
1615
  * This generally takes the place of `getAttr` as
@@ -1946,7 +1622,7 @@ class CacheManager {
1946
1622
  * @method peek
1947
1623
  * @public
1948
1624
  * @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
1949
- * @returns {ResourceDocument | ResourceBlob | null} the known resource data
1625
+ * @return {ResourceDocument | ResourceBlob | null} the known resource data
1950
1626
  */
1951
1627
 
1952
1628
  peek(identifier) {
@@ -1959,7 +1635,7 @@ class CacheManager {
1959
1635
  *
1960
1636
  * @method peekRequest
1961
1637
  * @param {StableDocumentIdentifier}
1962
- * @returns {StableDocumentIdentifier | null}
1638
+ * @return {StableDocumentIdentifier | null}
1963
1639
  * @public
1964
1640
  */
1965
1641
  peekRequest(identifier) {
@@ -1974,7 +1650,7 @@ class CacheManager {
1974
1650
  * @param identifier
1975
1651
  * @param data
1976
1652
  * @param hasRecord
1977
- * @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
1978
1654
  */
1979
1655
  upsert(identifier, data, hasRecord) {
1980
1656
  return _classPrivateFieldBase(this, _cache)[_cache].upsert(identifier, data, hasRecord);
@@ -1992,7 +1668,7 @@ class CacheManager {
1992
1668
  *
1993
1669
  * @method fork
1994
1670
  * @public
1995
- * @returns Promise<Cache>
1671
+ * @return Promise<Cache>
1996
1672
  */
1997
1673
  fork() {
1998
1674
  return _classPrivateFieldBase(this, _cache)[_cache].fork();
@@ -2008,7 +1684,7 @@ class CacheManager {
2008
1684
  * @method merge
2009
1685
  * @param {Cache} cache
2010
1686
  * @public
2011
- * @returns Promise<void>
1687
+ * @return Promise<void>
2012
1688
  */
2013
1689
  merge(cache) {
2014
1690
  return _classPrivateFieldBase(this, _cache)[_cache].merge(cache);
@@ -2060,7 +1736,7 @@ class CacheManager {
2060
1736
  * via `cache.hydrate`.
2061
1737
  *
2062
1738
  * @method dump
2063
- * @returns {Promise<ReadableStream>}
1739
+ * @return {Promise<ReadableStream>}
2064
1740
  * @public
2065
1741
  */
2066
1742
  dump() {
@@ -2081,7 +1757,7 @@ class CacheManager {
2081
1757
  *
2082
1758
  * @method hydrate
2083
1759
  * @param {ReadableStream} stream
2084
- * @returns {Promise<void>}
1760
+ * @return {Promise<void>}
2085
1761
  * @public
2086
1762
  */
2087
1763
  hydrate(stream) {
@@ -2095,7 +1771,7 @@ class CacheManager {
2095
1771
  // ================
2096
1772
 
2097
1773
  /**
2098
- * [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
2099
1775
  *
2100
1776
  * It returns properties from options that should be set on the record during the create
2101
1777
  * process. This return value behavior is deprecated.
@@ -2169,7 +1845,7 @@ class CacheManager {
2169
1845
  * @public
2170
1846
  * @param identifier
2171
1847
  * @param propertyName
2172
- * @returns {unknown}
1848
+ * @return {unknown}
2173
1849
  */
2174
1850
  getAttr(identifier, propertyName) {
2175
1851
  return _classPrivateFieldBase(this, _cache)[_cache].getAttr(identifier, propertyName);
@@ -2194,7 +1870,7 @@ class CacheManager {
2194
1870
  * @method changedAttrs
2195
1871
  * @public
2196
1872
  * @param identifier
2197
- * @returns
1873
+ * @return
2198
1874
  */
2199
1875
  changedAttrs(identifier) {
2200
1876
  return _classPrivateFieldBase(this, _cache)[_cache].changedAttrs(identifier);
@@ -2206,7 +1882,7 @@ class CacheManager {
2206
1882
  * @method hasChangedAttrs
2207
1883
  * @public
2208
1884
  * @param identifier
2209
- * @returns {boolean}
1885
+ * @return {boolean}
2210
1886
  */
2211
1887
  hasChangedAttrs(identifier) {
2212
1888
  return _classPrivateFieldBase(this, _cache)[_cache].hasChangedAttrs(identifier);
@@ -2218,7 +1894,7 @@ class CacheManager {
2218
1894
  * @method rollbackAttrs
2219
1895
  * @public
2220
1896
  * @param identifier
2221
- * @returns the names of attributes that were restored
1897
+ * @return the names of attributes that were restored
2222
1898
  */
2223
1899
  rollbackAttrs(identifier) {
2224
1900
  return _classPrivateFieldBase(this, _cache)[_cache].rollbackAttrs(identifier);
@@ -2227,6 +1903,65 @@ class CacheManager {
2227
1903
  // Relationships
2228
1904
  // =============
2229
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
+
2230
1965
  /**
2231
1966
  * Query the cache for the current state of a relationship property
2232
1967
  *
@@ -2234,7 +1969,7 @@ class CacheManager {
2234
1969
  * @public
2235
1970
  * @param identifier
2236
1971
  * @param propertyName
2237
- * @returns resource relationship object
1972
+ * @return resource relationship object
2238
1973
  */
2239
1974
  getRelationship(identifier, propertyName) {
2240
1975
  return _classPrivateFieldBase(this, _cache)[_cache].getRelationship(identifier, propertyName);
@@ -2262,7 +1997,7 @@ class CacheManager {
2262
1997
  * @method getErrors
2263
1998
  * @public
2264
1999
  * @param identifier
2265
- * @returns
2000
+ * @return
2266
2001
  */
2267
2002
  getErrors(identifier) {
2268
2003
  return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier);
@@ -2274,7 +2009,7 @@ class CacheManager {
2274
2009
  * @method isEmpty
2275
2010
  * @public
2276
2011
  * @param identifier
2277
- * @returns {boolean}
2012
+ * @return {boolean}
2278
2013
  */
2279
2014
  isEmpty(identifier) {
2280
2015
  return _classPrivateFieldBase(this, _cache)[_cache].isEmpty(identifier);
@@ -2287,7 +2022,7 @@ class CacheManager {
2287
2022
  * @method isNew
2288
2023
  * @public
2289
2024
  * @param identifier
2290
- * @returns {boolean}
2025
+ * @return {boolean}
2291
2026
  */
2292
2027
  isNew(identifier) {
2293
2028
  return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier);
@@ -2300,15 +2035,29 @@ class CacheManager {
2300
2035
  * @method isDeleted
2301
2036
  * @public
2302
2037
  * @param identifier
2303
- * @returns {boolean}
2038
+ * @return {boolean}
2304
2039
  */
2305
2040
  isDeleted(identifier) {
2306
2041
  return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier);
2307
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
+ */
2308
2053
  isDeletionCommitted(identifier) {
2309
2054
  return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier);
2310
2055
  }
2311
2056
  }
2057
+
2058
+ /**
2059
+ * @module @ember-data/store
2060
+ */
2312
2061
  let tokenId = 0;
2313
2062
  const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
2314
2063
  function isCacheOperationValue(value) {
@@ -2319,7 +2068,7 @@ function runLoopIsFlushing() {
2319
2068
  return !!_backburner.currentInstance && _backburner._autorun !== true;
2320
2069
  }
2321
2070
  function _unsubscribe(tokens, token, cache) {
2322
- let identifier = tokens.get(token);
2071
+ const identifier = tokens.get(token);
2323
2072
  if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
2324
2073
  if (!identifier) {
2325
2074
  // eslint-disable-next-line no-console
@@ -2378,7 +2127,7 @@ class NotificationManager {
2378
2127
  * @public
2379
2128
  * @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
2380
2129
  * @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
2381
- * @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
2130
+ * @return {UnsubscribeToken} an opaque token to be used with unsubscribe
2382
2131
  */
2383
2132
 
2384
2133
  subscribe(identifier, callback) {
@@ -2388,7 +2137,7 @@ class NotificationManager {
2388
2137
  map = new Map();
2389
2138
  this._cache.set(identifier, map);
2390
2139
  }
2391
- let unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2140
+ const unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2392
2141
  _tokenRef: tokenId++
2393
2142
  } : {};
2394
2143
  map.set(unsubToken, callback);
@@ -2441,7 +2190,7 @@ class NotificationManager {
2441
2190
  this._buffered.set(identifier, buffer);
2442
2191
  }
2443
2192
  buffer.push([value, key]);
2444
- void this._scheduleNotify();
2193
+ this._scheduleNotify();
2445
2194
  }
2446
2195
  return hasSubscribers;
2447
2196
  }
@@ -2483,14 +2232,14 @@ class NotificationManager {
2483
2232
 
2484
2233
  // TODO for documents this will need to switch based on Identifier kind
2485
2234
  if (isCacheOperationValue(value)) {
2486
- let callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2235
+ const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2487
2236
  if (callbackMap) {
2488
2237
  callbackMap.forEach(cb => {
2489
2238
  cb(identifier, value);
2490
2239
  });
2491
2240
  }
2492
2241
  }
2493
- let callbackMap = this._cache.get(identifier);
2242
+ const callbackMap = this._cache.get(identifier);
2494
2243
  if (!callbackMap || !callbackMap.size) {
2495
2244
  return false;
2496
2245
  }
@@ -2506,7 +2255,40 @@ class NotificationManager {
2506
2255
  this._cache.clear();
2507
2256
  }
2508
2257
  }
2509
- 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
+
2282
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2283
+ /*
2284
+ We redefine Proxy because the native Proxy type treats the `target` and
2285
+ `receiver` as the same type incorrectly.
2286
+
2287
+ We ported this from Typescript's own Proxy types on 3/10/2024.
2288
+ */
2289
+
2290
+ const NativeProxy = Proxy;
2291
+ var _class;
2510
2292
  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']);
2511
2293
  const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
2512
2294
  const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
@@ -2516,19 +2298,16 @@ function isArrayGetter(prop) {
2516
2298
  function isArraySetter(prop) {
2517
2299
  return ARRAY_SETTER_METHODS.has(prop);
2518
2300
  }
2519
- const IDENTIFIER_ARRAY_TAG = Symbol('#tag');
2301
+ function isSelfProp(self, prop) {
2302
+ return prop in self;
2303
+ }
2304
+ const ARRAY_SIGNAL = Symbol('#signal');
2520
2305
  const SOURCE = Symbol('#source');
2521
2306
  const MUTATE = Symbol('#update');
2522
2307
  const NOTIFY = Symbol('#notify');
2523
2308
  const IS_COLLECTION = Symbol.for('Collection');
2524
2309
  function notifyArray(arr) {
2525
- addToTransaction(arr[IDENTIFIER_ARRAY_TAG]);
2526
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
2527
- // eslint-disable-next-line
2528
- dirtyTag(tagForProperty(arr, 'length'));
2529
- // eslint-disable-next-line
2530
- dirtyTag(tagForProperty(arr, '[]'));
2531
- }
2310
+ addToTransaction(arr[ARRAY_SIGNAL]);
2532
2311
  }
2533
2312
  function convertToInt(prop) {
2534
2313
  if (typeof prop === 'symbol') return null;
@@ -2536,29 +2315,6 @@ function convertToInt(prop) {
2536
2315
  if (isNaN(num)) return null;
2537
2316
  return num % 1 === 0 ? num : null;
2538
2317
  }
2539
- let Tag = (_class = class Tag {
2540
- /*
2541
- * whether this was part of a transaction when last mutated
2542
- */
2543
-
2544
- constructor() {
2545
- _initializerDefineProperty(this, "ref", _descriptor, this);
2546
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2547
- const [arr, prop] = arguments;
2548
- this._debug_base = arr.constructor.name + ':' + String(arr.modelName);
2549
- this._debug_prop = prop;
2550
- }
2551
- this.shouldReset = false;
2552
- this.t = false;
2553
- }
2554
- }, _descriptor = _applyDecoratedDescriptor(_class.prototype, "ref", [tracked], {
2555
- configurable: true,
2556
- enumerable: true,
2557
- writable: true,
2558
- initializer: function () {
2559
- return null;
2560
- }
2561
- }), _class);
2562
2318
  function safeForEach(instance, arr, store, callback, target) {
2563
2319
  if (target === undefined) {
2564
2320
  target = null;
@@ -2591,7 +2347,7 @@ function safeForEach(instance, arr, store, callback, target) {
2591
2347
  @class RecordArray
2592
2348
  @public
2593
2349
  */
2594
- let IdentifierArray = (_class3 = class IdentifierArray {
2350
+ let IdentifierArray = (_class = class IdentifierArray {
2595
2351
  [NOTIFY]() {
2596
2352
  notifyArray(this);
2597
2353
  }
@@ -2619,14 +2375,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2619
2375
  set length(value) {
2620
2376
  this[SOURCE].length = value;
2621
2377
  }
2622
-
2623
- // here to support computed chains
2624
- // and {{#each}}
2625
- get '[]'() {
2626
- if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
2627
- return this;
2628
- }
2629
- }
2630
2378
  constructor(options) {
2631
2379
  /**
2632
2380
  The flag to signal a `RecordArray` is currently loading data.
@@ -2641,7 +2389,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2641
2389
  @public
2642
2390
  @type Boolean
2643
2391
  */
2644
- _initializerDefineProperty(this, "isUpdating", _descriptor2, this);
2645
2392
  this.isLoaded = true;
2646
2393
  this.isDestroying = false;
2647
2394
  this.isDestroyed = false;
@@ -2649,16 +2396,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2649
2396
  this[IS_COLLECTION] = true;
2650
2397
  this[SOURCE] = void 0;
2651
2398
  // eslint-disable-next-line @typescript-eslint/no-this-alias
2652
- let self = this;
2399
+ const self = this;
2653
2400
  this.modelName = options.type;
2654
2401
  this.store = options.store;
2655
2402
  this._manager = options.manager;
2656
2403
  this[SOURCE] = options.identifiers;
2657
- // @ts-expect-error
2658
- this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
2404
+ this[ARRAY_SIGNAL] = createSignal(this, 'length');
2659
2405
  const store = options.store;
2660
2406
  const boundFns = new Map();
2661
- const _TAG = this[IDENTIFIER_ARRAY_TAG];
2407
+ const _SIGNAL = this[ARRAY_SIGNAL];
2662
2408
  const PrivateState = {
2663
2409
  links: options.links || null,
2664
2410
  meta: options.meta || null
@@ -2669,42 +2415,42 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2669
2415
  // we track all mutations within the call
2670
2416
  // and forward them as one
2671
2417
 
2672
- const proxy = new Proxy(this[SOURCE], {
2418
+ const proxy = new NativeProxy(this[SOURCE], {
2673
2419
  get(target, prop, receiver) {
2674
- let index = convertToInt(prop);
2675
- if (_TAG.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2420
+ const index = convertToInt(prop);
2421
+ if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2676
2422
  options.manager._syncArray(receiver);
2677
- _TAG.t = false;
2678
- _TAG.shouldReset = false;
2423
+ _SIGNAL.t = false;
2424
+ _SIGNAL.shouldReset = false;
2679
2425
  }
2680
2426
  if (index !== null) {
2681
2427
  const identifier = target[index];
2682
2428
  if (!transaction) {
2683
- subscribe(_TAG);
2429
+ subscribe(_SIGNAL);
2684
2430
  }
2685
2431
  return identifier && store._instanceCache.getRecord(identifier);
2686
2432
  }
2687
- if (prop === 'meta') return subscribe(_TAG), PrivateState.meta;
2688
- if (prop === 'links') return subscribe(_TAG), PrivateState.links;
2689
- if (prop === '[]') return subscribe(_TAG), receiver;
2433
+ if (prop === 'meta') return subscribe(_SIGNAL), PrivateState.meta;
2434
+ if (prop === 'links') return subscribe(_SIGNAL), PrivateState.links;
2435
+ if (prop === '[]') return subscribe(_SIGNAL), receiver;
2690
2436
  if (isArrayGetter(prop)) {
2691
2437
  let fn = boundFns.get(prop);
2692
2438
  if (fn === undefined) {
2693
2439
  if (prop === 'forEach') {
2694
2440
  fn = function () {
2695
- subscribe(_TAG);
2441
+ subscribe(_SIGNAL);
2696
2442
  transaction = true;
2697
- let result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2443
+ const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2698
2444
  transaction = false;
2699
2445
  return result;
2700
2446
  };
2701
2447
  } else {
2702
2448
  fn = function () {
2703
- subscribe(_TAG);
2449
+ subscribe(_SIGNAL);
2704
2450
  // array functions must run through Reflect to work properly
2705
2451
  // binding via other means will not work.
2706
2452
  transaction = true;
2707
- let result = Reflect.apply(target[prop], receiver, arguments);
2453
+ const result = Reflect.apply(target[prop], receiver, arguments);
2708
2454
  transaction = false;
2709
2455
  return result;
2710
2456
  };
@@ -2726,10 +2472,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2726
2472
  const args = Array.prototype.slice.call(arguments);
2727
2473
  assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
2728
2474
  transaction = true;
2729
- let result = Reflect.apply(target[prop], receiver, args);
2730
- self[MUTATE](prop, args, result);
2731
- addToTransaction(_TAG);
2732
- // TODO handle cache updates
2475
+ const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
2733
2476
  transaction = false;
2734
2477
  return result;
2735
2478
  };
@@ -2737,16 +2480,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2737
2480
  }
2738
2481
  return fn;
2739
2482
  }
2740
- if (prop in self) {
2741
- if (prop === NOTIFY || prop === IDENTIFIER_ARRAY_TAG || prop === SOURCE) {
2483
+ if (isSelfProp(self, prop)) {
2484
+ if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
2742
2485
  return self[prop];
2743
2486
  }
2744
2487
  let fn = boundFns.get(prop);
2745
2488
  if (fn) return fn;
2746
- let outcome = self[prop];
2489
+ const outcome = self[prop];
2747
2490
  if (typeof outcome === 'function') {
2748
2491
  fn = function () {
2749
- subscribe(_TAG);
2492
+ subscribe(_SIGNAL);
2750
2493
  // array functions must run through Reflect to work properly
2751
2494
  // binding via other means will not work.
2752
2495
  return Reflect.apply(outcome, receiver, arguments);
@@ -2754,17 +2497,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2754
2497
  boundFns.set(prop, fn);
2755
2498
  return fn;
2756
2499
  }
2757
- return subscribe(_TAG), outcome;
2500
+ return subscribe(_SIGNAL), outcome;
2758
2501
  }
2759
2502
  return target[prop];
2760
2503
  },
2761
- set(target, prop, value) {
2504
+ // FIXME: Should this get a generic like get above?
2505
+ set(target, prop, value, receiver) {
2762
2506
  if (prop === 'length') {
2763
2507
  if (!transaction && value === 0) {
2764
2508
  transaction = true;
2765
- addToTransaction(_TAG);
2766
- Reflect.set(target, prop, value);
2767
- self[MUTATE]('length 0', []);
2509
+ self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
2768
2510
  transaction = false;
2769
2511
  return true;
2770
2512
  } else if (transaction) {
@@ -2781,9 +2523,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2781
2523
  PrivateState.meta = value || null;
2782
2524
  return true;
2783
2525
  }
2784
- let index = convertToInt(prop);
2526
+ const index = convertToInt(prop);
2527
+
2528
+ // we do not allow "holey" arrays and so if the index is
2529
+ // greater than length then we will disallow setting it.
2530
+ // however, there is a special case for "unshift" with more than
2531
+ // one item being inserted since current items will be moved to the
2532
+ // new indices first.
2533
+ // we "loosely" detect this by just checking whether we are in
2534
+ // a transaction.
2785
2535
  if (index === null || index > target.length) {
2786
- if (prop in self) {
2536
+ if (index !== null && transaction) {
2537
+ const identifier = recordIdentifierFor(value);
2538
+ assert(`Cannot set index ${index} past the end of the array.`, isStableIdentifier(identifier));
2539
+ target[index] = identifier;
2540
+ return true;
2541
+ } else if (isSelfProp(self, prop)) {
2542
+ // @ts-expect-error not all properties are indeces and we can't safely cast
2787
2543
  self[prop] = value;
2788
2544
  return true;
2789
2545
  }
@@ -2793,12 +2549,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2793
2549
  assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
2794
2550
  return false;
2795
2551
  }
2796
- let original = target[index];
2797
- let newIdentifier = extractIdentifierFromRecord$1(value);
2552
+ const original = target[index];
2553
+ const newIdentifier = extractIdentifierFromRecord$1(value);
2798
2554
  target[index] = newIdentifier;
2555
+ assert(`Expected a record`, isStableIdentifier(newIdentifier));
2556
+ // We generate "transactions" whenever a setter method on the array
2557
+ // is called and might bulk update multiple array cells. Fundamentally,
2558
+ // all array operations decompose into individual cell replacements.
2559
+ // e.g. a push is really a "replace cell at next index with new value"
2560
+ // or a splice is "shift all values left/right by X and set out of new
2561
+ // bounds cells to undefined"
2562
+ //
2563
+ // so, if we are in a transaction, then this is not a user generated change
2564
+ // but one generated by a setter method. In this case we want to only apply
2565
+ // the change to the target array and not call the MUTATE method.
2566
+ // If there is no transaction though, then this means the user themselves has
2567
+ // directly changed the value of a specific index and we need to thus generate
2568
+ // a mutation for that change.
2569
+ // e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
2570
+ // a transaction.
2571
+ // while "arr[arr.length] = newVal;" is handled by this replace cell code path.
2799
2572
  if (!transaction) {
2800
- self[MUTATE]('replace cell', [index, original, newIdentifier]);
2801
- addToTransaction(_TAG);
2573
+ self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
2574
+ } else {
2575
+ target[index] = newIdentifier;
2802
2576
  }
2803
2577
  return true;
2804
2578
  },
@@ -2813,12 +2587,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2813
2587
  return IdentifierArray.prototype;
2814
2588
  }
2815
2589
  });
2816
- if (macroCondition(getOwnConfig().env.DEBUG)) {
2817
- const meta = Ember.meta(this);
2818
- meta.hasMixin = mixin => {
2819
- assert(`Do not call A() on EmberData RecordArrays`);
2820
- };
2821
- }
2590
+ createArrayTags(proxy, _SIGNAL);
2822
2591
  this[NOTIFY] = this[NOTIFY].bind(proxy);
2823
2592
  return proxy;
2824
2593
  }
@@ -2843,7 +2612,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2843
2612
  return this._updatingPromise;
2844
2613
  }
2845
2614
  this.isUpdating = true;
2846
- let updatingPromise = this._update();
2615
+ const updatingPromise = this._update();
2847
2616
  void updatingPromise.finally(() => {
2848
2617
  this._updatingPromise = null;
2849
2618
  if (this.isDestroying || this.isDestroyed) {
@@ -2861,6 +2630,10 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2861
2630
  */
2862
2631
  _update() {
2863
2632
  assert(`_update cannot be used with this array`, this.modelName);
2633
+ // @ts-expect-error typescript is unable to handle the complexity of
2634
+ // T = unknown, modelName = string
2635
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
2636
+ // both being valid options to pass through here.
2864
2637
  return this.store.findAll(this.modelName, {
2865
2638
  reload: true
2866
2639
  });
@@ -2882,17 +2655,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2882
2655
  @return {Promise<IdentifierArray>} promise
2883
2656
  */
2884
2657
  save() {
2885
- let promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2658
+ const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2886
2659
  return promise;
2887
2660
  }
2888
- }, (_descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "isUpdating", [tracked], {
2889
- configurable: true,
2661
+ }, _applyDecoratedDescriptor(_class.prototype, "length", [compat], Object.getOwnPropertyDescriptor(_class.prototype, "length"), _class.prototype), _class); // this will error if someone tries to call
2662
+ // A(identifierArray) since it is not configurable
2663
+ // which is preferable to the `meta` override we used
2664
+ // before which required importing all of Ember
2665
+ const desc = {
2890
2666
  enumerable: true,
2891
- writable: true,
2892
- initializer: function () {
2893
- return false;
2667
+ configurable: false,
2668
+ get: function () {
2669
+ return this;
2894
2670
  }
2895
- }), _applyDecoratedDescriptor(_class3.prototype, "length", [dependentKeyCompat], Object.getOwnPropertyDescriptor(_class3.prototype, "length"), _class3.prototype)), _class3);
2671
+ };
2672
+ compat(desc);
2673
+ Object.defineProperty(IdentifierArray.prototype, '[]', desc);
2674
+ defineSignal(IdentifierArray.prototype, 'isUpdating', false);
2896
2675
  class Collection extends IdentifierArray {
2897
2676
  constructor(options) {
2898
2677
  super(options);
@@ -2909,6 +2688,10 @@ class Collection extends IdentifierArray {
2909
2688
  // TODO save options from initial request?
2910
2689
  assert(`update cannot be used with this array`, this.modelName);
2911
2690
  assert(`update cannot be used with no query`, query);
2691
+ // @ts-expect-error typescript is unable to handle the complexity of
2692
+ // T = unknown, modelName = string
2693
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
2694
+ // both being valid options to pass through here.
2912
2695
  const promise = store.query(this.modelName, query, {
2913
2696
  _recordArray: this
2914
2697
  });
@@ -2924,7 +2707,8 @@ class Collection extends IdentifierArray {
2924
2707
  Collection.prototype.query = null;
2925
2708
 
2926
2709
  // Ensure instanceof works correctly
2927
- //Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2710
+ // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2711
+
2928
2712
  function assertRecordPassedToHasMany(record) {
2929
2713
  assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
2930
2714
  try {
@@ -2946,7 +2730,6 @@ function extractIdentifierFromRecord$1(record) {
2946
2730
  /**
2947
2731
  @module @ember-data/store
2948
2732
  */
2949
-
2950
2733
  const FAKE_ARR = {};
2951
2734
  const SLICE_BATCH_SIZE = 1200;
2952
2735
  /**
@@ -2989,7 +2772,7 @@ const SLICE_BATCH_SIZE = 1200;
2989
2772
  */
2990
2773
  function fastPush(target, source) {
2991
2774
  let startLength = 0;
2992
- let newLength = source.length;
2775
+ const newLength = source.length;
2993
2776
  while (newLength - startLength > SLICE_BATCH_SIZE) {
2994
2777
  // eslint-disable-next-line prefer-spread
2995
2778
  target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
@@ -3046,8 +2829,8 @@ class RecordArrayManager {
3046
2829
  */
3047
2830
  liveArrayFor(type) {
3048
2831
  let array = this._live.get(type);
3049
- let identifiers = [];
3050
- let staged = this._staged.get(type);
2832
+ const identifiers = [];
2833
+ const staged = this._staged.get(type);
3051
2834
  if (staged) {
3052
2835
  staged.forEach((value, key) => {
3053
2836
  if (value === 'add') {
@@ -3070,7 +2853,7 @@ class RecordArrayManager {
3070
2853
  return array;
3071
2854
  }
3072
2855
  createArray(config) {
3073
- let options = {
2856
+ const options = {
3074
2857
  type: config.type,
3075
2858
  links: config.doc?.links || null,
3076
2859
  meta: config.doc?.meta || null,
@@ -3081,7 +2864,7 @@ class RecordArrayManager {
3081
2864
  store: this.store,
3082
2865
  manager: this
3083
2866
  };
3084
- let array = new Collection(options);
2867
+ const array = new Collection(options);
3085
2868
  this._managed.add(array);
3086
2869
  this._set.set(array, new Set(options.identifiers || []));
3087
2870
  if (config.identifiers) {
@@ -3093,7 +2876,7 @@ class RecordArrayManager {
3093
2876
  if (array === FAKE_ARR) {
3094
2877
  return;
3095
2878
  }
3096
- let tag = array[IDENTIFIER_ARRAY_TAG];
2879
+ const tag = array[ARRAY_SIGNAL];
3097
2880
  if (!tag.shouldReset) {
3098
2881
  tag.shouldReset = true;
3099
2882
  addTransactionCB(array[NOTIFY]);
@@ -3105,11 +2888,11 @@ class RecordArrayManager {
3105
2888
  if (this.isDestroying || this.isDestroyed) {
3106
2889
  return;
3107
2890
  }
3108
- let liveArray = this._live.get(identifier.type);
2891
+ const liveArray = this._live.get(identifier.type);
3109
2892
  const allPending = this._pending;
3110
- let pending = new Map();
2893
+ const pending = new Map();
3111
2894
  if (includeManaged) {
3112
- let managed = this._identifiers.get(identifier);
2895
+ const managed = this._identifiers.get(identifier);
3113
2896
  if (managed) {
3114
2897
  managed.forEach(arr => {
3115
2898
  let changes = allPending.get(arr);
@@ -3164,10 +2947,10 @@ class RecordArrayManager {
3164
2947
  associate(this._identifiers, array, identifiers);
3165
2948
  }
3166
2949
  identifierAdded(identifier) {
3167
- let changeSets = this._getPendingFor(identifier, false);
2950
+ const changeSets = this._getPendingFor(identifier, false);
3168
2951
  if (changeSets) {
3169
2952
  changeSets.forEach((changes, array) => {
3170
- let existing = changes.get(identifier);
2953
+ const existing = changes.get(identifier);
3171
2954
  if (existing === 'del') {
3172
2955
  changes.delete(identifier);
3173
2956
  } else {
@@ -3178,10 +2961,10 @@ class RecordArrayManager {
3178
2961
  }
3179
2962
  }
3180
2963
  identifierRemoved(identifier) {
3181
- let changeSets = this._getPendingFor(identifier, true, true);
2964
+ const changeSets = this._getPendingFor(identifier, true, true);
3182
2965
  if (changeSets) {
3183
2966
  changeSets.forEach((changes, array) => {
3184
- let existing = changes.get(identifier);
2967
+ const existing = changes.get(identifier);
3185
2968
  if (existing === 'add') {
3186
2969
  changes.delete(identifier);
3187
2970
  } else {
@@ -3192,7 +2975,7 @@ class RecordArrayManager {
3192
2975
  }
3193
2976
  }
3194
2977
  identifierChanged(identifier) {
3195
- let newState = this.store._instanceCache.recordIsLoaded(identifier, true);
2978
+ const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
3196
2979
 
3197
2980
  // if the change matches the most recent direct added/removed
3198
2981
  // state, then we can ignore it
@@ -3219,13 +3002,12 @@ class RecordArrayManager {
3219
3002
  this.clear(false);
3220
3003
  this._live.clear();
3221
3004
  this.isDestroyed = true;
3222
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
3223
3005
  this.store.notifications.unsubscribe(this._subscription);
3224
3006
  }
3225
3007
  }
3226
3008
  function associate(ArraysCache, array, identifiers) {
3227
3009
  for (let i = 0; i < identifiers.length; i++) {
3228
- let identifier = identifiers[i];
3010
+ const identifier = identifiers[i];
3229
3011
  let cache = ArraysCache.get(identifier);
3230
3012
  if (!cache) {
3231
3013
  cache = new Set();
@@ -3240,13 +3022,13 @@ function disassociate(ArraysCache, array, identifiers) {
3240
3022
  }
3241
3023
  }
3242
3024
  function disassociateIdentifier(ArraysCache, array, identifier) {
3243
- let cache = ArraysCache.get(identifier);
3025
+ const cache = ArraysCache.get(identifier);
3244
3026
  if (cache) {
3245
3027
  cache.delete(array);
3246
3028
  }
3247
3029
  }
3248
3030
  function sync(array, changes, arraySet) {
3249
- let state = array[SOURCE];
3031
+ const state = array[SOURCE];
3250
3032
  const adds = [];
3251
3033
  const removes = [];
3252
3034
  changes.forEach((value, key) => {
@@ -3260,13 +3042,13 @@ function sync(array, changes, arraySet) {
3260
3042
  } else {
3261
3043
  if (arraySet.has(key)) {
3262
3044
  removes.push(key);
3045
+ arraySet.delete(key);
3263
3046
  }
3264
3047
  }
3265
3048
  });
3266
3049
  if (removes.length) {
3267
3050
  if (removes.length === state.length) {
3268
3051
  state.length = 0;
3269
- arraySet.clear();
3270
3052
  // changing the reference breaks the Proxy
3271
3053
  // state = array[SOURCE] = [];
3272
3054
  } else {
@@ -3293,6 +3075,9 @@ function sync(array, changes, arraySet) {
3293
3075
  }
3294
3076
  }
3295
3077
 
3078
+ /**
3079
+ * @module @ember-data/store
3080
+ */
3296
3081
  const Touching = Symbol('touching');
3297
3082
  const RequestPromise = Symbol('promise');
3298
3083
  const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
@@ -3320,14 +3105,14 @@ class RequestStateService {
3320
3105
  this._done.delete(identifier);
3321
3106
  }
3322
3107
  _enqueue(promise, queryRequest) {
3323
- let query = queryRequest.data[0];
3108
+ const query = queryRequest.data[0];
3324
3109
  if (hasRecordIdentifier(query)) {
3325
3110
  const identifier = query.recordIdentifier;
3326
- let type = query.op === 'saveRecord' ? 'mutation' : 'query';
3111
+ const type = query.op === 'saveRecord' ? 'mutation' : 'query';
3327
3112
  if (!this._pending.has(identifier)) {
3328
3113
  this._pending.set(identifier, []);
3329
3114
  }
3330
- let request = {
3115
+ const request = {
3331
3116
  state: 'pending',
3332
3117
  request: queryRequest,
3333
3118
  type
@@ -3338,7 +3123,7 @@ class RequestStateService {
3338
3123
  this._triggerSubscriptions(request);
3339
3124
  return promise.then(result => {
3340
3125
  this._dequeue(identifier, request);
3341
- let finalizedRequest = {
3126
+ const finalizedRequest = {
3342
3127
  state: 'fulfilled',
3343
3128
  request: queryRequest,
3344
3129
  type,
@@ -3352,7 +3137,7 @@ class RequestStateService {
3352
3137
  return result;
3353
3138
  }, error => {
3354
3139
  this._dequeue(identifier, request);
3355
- let finalizedRequest = {
3140
+ const finalizedRequest = {
3356
3141
  state: 'rejected',
3357
3142
  request: queryRequest,
3358
3143
  type,
@@ -3401,7 +3186,7 @@ class RequestStateService {
3401
3186
  _addDone(request) {
3402
3187
  request[Touching].forEach(identifier => {
3403
3188
  // TODO add support for multiple
3404
- let requestDataOp = request.request.data[0].op;
3189
+ const requestDataOp = request.request.data[0].op;
3405
3190
  let requests = this._done.get(identifier);
3406
3191
  if (requests) {
3407
3192
  requests = requests.filter(req => {
@@ -3465,7 +3250,7 @@ class RequestStateService {
3465
3250
  * @method getPendingRequestsForRecord
3466
3251
  * @public
3467
3252
  * @param {StableRecordIdentifier} identifier
3468
- * @returns {RequestState[]} an array of request states for any pending requests for the given identifier
3253
+ * @return {RequestState[]} an array of request states for any pending requests for the given identifier
3469
3254
  */
3470
3255
  getPendingRequestsForRecord(identifier) {
3471
3256
  return this._pending.get(identifier) || EMPTY_ARR;
@@ -3477,10 +3262,10 @@ class RequestStateService {
3477
3262
  * @method getLastRequestForRecord
3478
3263
  * @public
3479
3264
  * @param {StableRecordIdentifier} identifier
3480
- * @returns {RequestState | null} the state of the most recent request for the given identifier
3265
+ * @return {RequestState | null} the state of the most recent request for the given identifier
3481
3266
  */
3482
3267
  getLastRequestForRecord(identifier) {
3483
- let requests = this._done.get(identifier);
3268
+ const requests = this._done.get(identifier);
3484
3269
  if (requests) {
3485
3270
  return requests[requests.length - 1];
3486
3271
  }
@@ -3492,7 +3277,7 @@ function isNonEmptyString(str) {
3492
3277
  }
3493
3278
  function constructResource(type, id, lid) {
3494
3279
  if (typeof type === 'object' && type !== null) {
3495
- let resource = type;
3280
+ const resource = type;
3496
3281
  if (isStableIdentifier(resource)) {
3497
3282
  return resource;
3498
3283
  }
@@ -3527,6 +3312,29 @@ function constructResource(type, id, lid) {
3527
3312
  }
3528
3313
  }
3529
3314
 
3315
+ /**
3316
+ @module @ember-data/store
3317
+ */
3318
+ // this import location is deprecated but breaks in 4.8 and older
3319
+
3320
+ /**
3321
+ * Currently only records that extend object can be created via
3322
+ * store.createRecord. This is a limitation of the current API,
3323
+ * but can be worked around by creating a new identifier, running
3324
+ * the cache.clientDidCreate method, and then peeking the record
3325
+ * for the identifier.
3326
+ *
3327
+ * To assign primary key to a record during creation, only `id` will
3328
+ * work correctly for `store.createRecord`, other primary key may be
3329
+ * handled by updating the record after creation or using the flow
3330
+ * described above.
3331
+ *
3332
+ * TODO: These are limitations we want to (and can) address. If you
3333
+ * have need of lifting these limitations, please open an issue.
3334
+ *
3335
+ * @typedoc
3336
+ */
3337
+
3530
3338
  /**
3531
3339
  * A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
3532
3340
  * and sources of data (such as your API or a local persistence layer)
@@ -3545,7 +3353,10 @@ function constructResource(type, id, lid) {
3545
3353
 
3546
3354
  @class Store
3547
3355
  @public
3548
- */ // @ts-expect-error
3356
+ */
3357
+
3358
+ // @ts-expect-error
3359
+
3549
3360
  class Store extends EmberObject {
3550
3361
  /**
3551
3362
  * Provides access to the NotificationManager associated
@@ -3677,8 +3488,6 @@ class Store extends EmberObject {
3677
3488
  // private
3678
3489
  this._requestCache = new RequestStateService(this);
3679
3490
  this._instanceCache = new InstanceCache(this);
3680
- this._adapterCache = Object.create(null);
3681
- this._serializerCache = Object.create(null);
3682
3491
  this._documentCache = new Map();
3683
3492
  this.isDestroying = false;
3684
3493
  this.isDestroyed = false;
@@ -3736,7 +3545,7 @@ class Store extends EmberObject {
3736
3545
  * that have been initiated for a given identifier.
3737
3546
  *
3738
3547
  * @method getRequestStateService
3739
- * @returns {RequestStateService}
3548
+ * @return {RequestStateService}
3740
3549
  * @public
3741
3550
  */
3742
3551
  getRequestStateService() {
@@ -3761,10 +3570,11 @@ class Store extends EmberObject {
3761
3570
  * inserting the response into the cache and handing
3762
3571
  * back a Future which resolves to a ResponseDocument
3763
3572
  *
3764
- * Resource data is always updated in the cache.
3573
+ * ## Cache Keys
3765
3574
  *
3766
- * Only GET requests have the request result and document
3767
- * cached by default when a cache key is present.
3575
+ * Only GET requests with a url or requests with an explicit
3576
+ * cache key (`cacheOptions.key`) will have the request result
3577
+ * and document cached.
3768
3578
  *
3769
3579
  * The cache key used is `requestConfig.cacheOptions.key`
3770
3580
  * if present, falling back to `requestconfig.url`.
@@ -3775,16 +3585,44 @@ class Store extends EmberObject {
3775
3585
  * via the `POST` method `requestConfig.cacheOptions.key`
3776
3586
  * MUST be supplied for the document to be cached.
3777
3587
  *
3588
+ * ## Requesting Without a Cache Key
3589
+ *
3590
+ * Resource data within the request is always updated in the cache,
3591
+ * regardless of whether a cache key is present for the request.
3592
+ *
3593
+ * ## Fulfilling From Cache
3594
+ *
3595
+ * When a cache-key is determined, the request may fulfill
3596
+ * from cache provided the cache is not stale.
3597
+ *
3598
+ * Cache staleness is determined by the configured LifetimesService
3599
+ * with priority given to the `cacheOptions.reload` and
3600
+ * `cacheOptions.backgroundReload` on the request if present.
3601
+ *
3602
+ * If the cache data has soft expired or the request asks for a background
3603
+ * reload, the request will fulfill from cache if possible and
3604
+ * make a non-blocking request in the background to update the cache.
3605
+ *
3606
+ * If the cache data has hard expired or the request asks for a reload,
3607
+ * the request will not fulfill from cache and will make a blocking
3608
+ * request to update the cache.
3609
+ *
3610
+ * ## The Response
3611
+ *
3612
+ * The primary difference between `requestManager.request` and `store.request`
3613
+ * is that `store.request` will attempt to hydrate the response content into
3614
+ * a response Document containing RecordInstances.
3615
+ *
3778
3616
  * @method request
3779
3617
  * @param {StoreRequestInput} requestConfig
3780
- * @returns {Future}
3618
+ * @return {Future}
3781
3619
  * @public
3782
3620
  */
3783
3621
  request(requestConfig) {
3784
3622
  // we lazily set the cache handler when we issue the first request
3785
3623
  // because constructor doesn't allow for this to run after
3786
3624
  // the user has had the chance to set the prop.
3787
- let opts = {
3625
+ const opts = {
3788
3626
  store: this,
3789
3627
  [EnableHydration]: true
3790
3628
  };
@@ -3836,7 +3674,7 @@ class Store extends EmberObject {
3836
3674
  * @param createRecordArgs
3837
3675
  * @param recordDataFor deprecated use this.cache
3838
3676
  * @param notificationManager deprecated use this.notifications
3839
- * @returns A record instance
3677
+ * @return A record instance
3840
3678
  * @public
3841
3679
  */
3842
3680
 
@@ -3979,7 +3817,7 @@ class Store extends EmberObject {
3979
3817
  }
3980
3818
 
3981
3819
  /**
3982
- Returns the schema for a particular `modelName`.
3820
+ Returns the schema for a particular resource type (modelName).
3983
3821
  When used with Model from @ember-data/model the return is the model class,
3984
3822
  but this is not guaranteed.
3985
3823
  If looking to query attribute or relationship information it is
@@ -3993,7 +3831,7 @@ class Store extends EmberObject {
3993
3831
  for example.
3994
3832
  @method modelFor
3995
3833
  @public
3996
- @param {String} type
3834
+ @param {string} type
3997
3835
  @return {ModelSchema}
3998
3836
  */
3999
3837
  // TODO @deprecate in favor of schema APIs, requires adapter/serializer overhaul or replacement
@@ -4026,17 +3864,18 @@ class Store extends EmberObject {
4026
3864
  ```
4027
3865
  @method createRecord
4028
3866
  @public
4029
- @param {String} modelName
3867
+ @param {String} type the name of the resource
4030
3868
  @param {Object} inputProperties a hash of properties to set on the
4031
3869
  newly created record.
4032
3870
  @return {Model} record
4033
3871
  */
4034
- createRecord(modelName, inputProperties) {
3872
+
3873
+ createRecord(type, inputProperties) {
4035
3874
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4036
3875
  assertDestroyingStore(this, 'createRecord');
4037
3876
  }
4038
- assert(`You need to pass a model name to the store's createRecord method`, modelName);
4039
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
3877
+ assert(`You need to pass a model name to the store's createRecord method`, type);
3878
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4040
3879
 
4041
3880
  // This is wrapped in a `run.join` so that in test environments users do not need to manually wrap
4042
3881
  // calls to `createRecord`. The run loop usage here is because we batch the joining and updating
@@ -4044,43 +3883,40 @@ class Store extends EmberObject {
4044
3883
  //
4045
3884
  // to remove this, we would need to move to a new `async` API.
4046
3885
  let record;
4047
- _backburner.join(() => {
4048
- this._join(() => {
4049
- let normalizedModelName = normalizeModelName(modelName);
4050
- let properties = {
4051
- ...inputProperties
4052
- };
4053
-
4054
- // If the passed properties do not include a primary key,
4055
- // give the adapter an opportunity to generate one. Typically,
4056
- // client-side ID generators will use something like uuid.js
4057
- // to avoid conflicts.
4058
-
4059
- if (properties.id === null || properties.id === undefined) {
4060
- let adapter = this.adapterFor(modelName);
4061
- if (adapter && adapter.generateIdForRecord) {
4062
- properties.id = adapter.generateIdForRecord(this, modelName, properties);
4063
- } else {
4064
- properties.id = null;
4065
- }
4066
- }
3886
+ this._join(() => {
3887
+ const normalizedModelName = normalizeModelName(type);
3888
+ const properties = {
3889
+ ...inputProperties
3890
+ };
4067
3891
 
4068
- // Coerce ID to a string
4069
- properties.id = coerceId(properties.id);
4070
- const resource = {
4071
- type: normalizedModelName,
4072
- id: properties.id
4073
- };
4074
- if (resource.id) {
4075
- const identifier = this.identifierCache.peekRecordIdentifier(resource);
4076
- assert(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`, !identifier);
3892
+ // If the passed properties do not include a primary key,
3893
+ // give the adapter an opportunity to generate one. Typically,
3894
+ // client-side ID generators will use something like uuid.js
3895
+ // to avoid conflicts.
3896
+ let id = null;
3897
+ if (properties.id === null || properties.id === undefined) {
3898
+ const adapter = this.adapterFor?.(normalizedModelName, true);
3899
+ if (adapter && adapter.generateIdForRecord) {
3900
+ id = properties.id = coerceId(adapter.generateIdForRecord(this, normalizedModelName, properties));
3901
+ } else {
3902
+ id = properties.id = null;
4077
3903
  }
4078
- const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
4079
- const cache = this.cache;
4080
- const createOptions = normalizeProperties(this, identifier, properties);
4081
- const resultProps = cache.clientDidCreate(identifier, createOptions);
4082
- record = this._instanceCache.getRecord(identifier, resultProps);
4083
- });
3904
+ } else {
3905
+ id = properties.id = coerceId(properties.id);
3906
+ }
3907
+ const resource = {
3908
+ type: normalizedModelName,
3909
+ id
3910
+ };
3911
+ if (resource.id) {
3912
+ const identifier = this.identifierCache.peekRecordIdentifier(resource);
3913
+ assert(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`, !identifier);
3914
+ }
3915
+ const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
3916
+ const cache = this.cache;
3917
+ const createOptions = normalizeProperties(this, identifier, properties);
3918
+ const resultProps = cache.clientDidCreate(identifier, createOptions);
3919
+ record = this._instanceCache.getRecord(identifier, resultProps);
4084
3920
  });
4085
3921
  return record;
4086
3922
  }
@@ -4096,7 +3932,7 @@ class Store extends EmberObject {
4096
3932
  ```
4097
3933
  @method deleteRecord
4098
3934
  @public
4099
- @param {Model} record
3935
+ @param {unknown} record
4100
3936
  */
4101
3937
  deleteRecord(record) {
4102
3938
  if (macroCondition(getOwnConfig().env.DEBUG)) {
@@ -4108,9 +3944,7 @@ class Store extends EmberObject {
4108
3944
  this._join(() => {
4109
3945
  cache.setIsDeleted(identifier, true);
4110
3946
  if (cache.isNew(identifier)) {
4111
- _backburner.join(() => {
4112
- this._instanceCache.unloadRecord(identifier);
4113
- });
3947
+ this._instanceCache.unloadRecord(identifier);
4114
3948
  }
4115
3949
  });
4116
3950
  }
@@ -4192,8 +4026,7 @@ class Store extends EmberObject {
4192
4026
  In your adapter you can then access this id without triggering a network request via the
4193
4027
  snapshot:
4194
4028
  ```app/adapters/application.js
4195
- import EmberObject from '@ember/object';
4196
- export default class Adapter extends EmberObject {
4029
+ export default class Adapter {
4197
4030
  findRecord(store, schema, id, snapshot) {
4198
4031
  let type = schema.modelName;
4199
4032
  if (type === 'comment')
@@ -4202,6 +4035,9 @@ class Store extends EmberObject {
4202
4035
  .then(response => response.json())
4203
4036
  }
4204
4037
  }
4038
+ static create() {
4039
+ return new this();
4040
+ }
4205
4041
  }
4206
4042
  ```
4207
4043
  This could also be achieved by supplying the post id to the adapter via the adapterOptions
@@ -4215,9 +4051,8 @@ class Store extends EmberObject {
4215
4051
  }
4216
4052
  ```
4217
4053
  ```app/adapters/application.js
4218
- import EmberObject from '@ember/object';
4219
- export default class Adapter extends EmberObject {
4220
- findRecord(store, schema, id, snapshot) {
4054
+ export default class Adapter {
4055
+ findRecord(store, schema, id, snapshot) {
4221
4056
  let type = schema.modelName;
4222
4057
  if (type === 'comment')
4223
4058
  let postId = snapshot.adapterOptions.post;
@@ -4225,6 +4060,9 @@ class Store extends EmberObject {
4225
4060
  .then(response => response.json())
4226
4061
  }
4227
4062
  }
4063
+ static create() {
4064
+ return new this();
4065
+ }
4228
4066
  }
4229
4067
  ```
4230
4068
  If you have access to the post model you can also pass the model itself to preload:
@@ -4351,9 +4189,8 @@ class Store extends EmberObject {
4351
4189
  }
4352
4190
  ```
4353
4191
  ```app/adapters/application.js
4354
- import EmberObject from '@ember/object';
4355
- export default class Adapter extends EmberObject {
4356
- findRecord(store, schema, id, snapshot) {
4192
+ export default class Adapter {
4193
+ findRecord(store, schema, id, snapshot) {
4357
4194
  let type = schema.modelName;
4358
4195
  if (type === 'post')
4359
4196
  let includes = snapshot.adapterOptions.include;
@@ -4361,6 +4198,9 @@ class Store extends EmberObject {
4361
4198
  .then(response => response.json())
4362
4199
  }
4363
4200
  }
4201
+ static create() {
4202
+ return new this();
4203
+ }
4364
4204
  }
4365
4205
  ```
4366
4206
  In this case, the post's comments would then be available in your template as
@@ -4414,7 +4254,7 @@ class Store extends EmberObject {
4414
4254
  @since 1.13.0
4415
4255
  @method findRecord
4416
4256
  @public
4417
- @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
4257
+ @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
4418
4258
  @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
4419
4259
  @param {Object} [options] - if the first param is a string this will be the optional options for the request. See examples for available options.
4420
4260
  @return {Promise} promise
@@ -4504,7 +4344,7 @@ class Store extends EmberObject {
4504
4344
  resourceIdentifier = constructResource(type, normalizedId);
4505
4345
  }
4506
4346
  assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
4507
- let identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4347
+ const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4508
4348
  return this._instanceCache.getReference(identifier);
4509
4349
  }
4510
4350
 
@@ -4599,24 +4439,25 @@ class Store extends EmberObject {
4599
4439
  @since 1.13.0
4600
4440
  @method query
4601
4441
  @public
4602
- @param {String} modelName
4603
- @param {any} query an opaque query to be used by the adapter
4442
+ @param {String} type the name of the resource
4443
+ @param {object} query a query to be used by the adapter
4604
4444
  @param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.query
4605
4445
  @return {Promise} promise
4606
4446
  */
4607
- query(modelName, query, options) {
4447
+
4448
+ query(type, query, options = {}) {
4608
4449
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4609
4450
  assertDestroyingStore(this, 'query');
4610
4451
  }
4611
- assert(`You need to pass a model name to the store's query method`, modelName);
4452
+ assert(`You need to pass a model name to the store's query method`, type);
4612
4453
  assert(`You need to pass a query hash to the store's query method`, query);
4613
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
4454
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4614
4455
  const promise = this.request({
4615
4456
  op: 'query',
4616
4457
  data: {
4617
- type: normalizeModelName(modelName),
4458
+ type: normalizeModelName(type),
4618
4459
  query,
4619
- options: options || {}
4460
+ options: options
4620
4461
  },
4621
4462
  cacheOptions: {
4622
4463
  [SkipCache]: true
@@ -4874,20 +4715,21 @@ class Store extends EmberObject {
4874
4715
  @since 1.13.0
4875
4716
  @method findAll
4876
4717
  @public
4877
- @param {String} modelName
4878
- @param {Object} options
4718
+ @param {string} type the name of the resource
4719
+ @param {object} options
4879
4720
  @return {Promise} promise
4880
4721
  */
4881
- findAll(modelName, options = {}) {
4722
+
4723
+ findAll(type, options = {}) {
4882
4724
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4883
4725
  assertDestroyingStore(this, 'findAll');
4884
4726
  }
4885
- assert(`You need to pass a model name to the store's findAll method`, modelName);
4886
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
4727
+ assert(`You need to pass a model name to the store's findAll method`, type);
4728
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4887
4729
  const promise = this.request({
4888
4730
  op: 'findAll',
4889
4731
  data: {
4890
- type: normalizeModelName(modelName),
4732
+ type: normalizeModelName(type),
4891
4733
  options: options || {}
4892
4734
  },
4893
4735
  cacheOptions: {
@@ -4914,17 +4756,17 @@ class Store extends EmberObject {
4914
4756
  @since 1.13.0
4915
4757
  @method peekAll
4916
4758
  @public
4917
- @param {String} modelName
4759
+ @param {string} type the name of the resource
4918
4760
  @return {RecordArray}
4919
4761
  */
4920
- peekAll(modelName) {
4762
+
4763
+ peekAll(type) {
4921
4764
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4922
4765
  assertDestroyingStore(this, 'peekAll');
4923
4766
  }
4924
- assert(`You need to pass a model name to the store's peekAll method`, modelName);
4925
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
4926
- let type = normalizeModelName(modelName);
4927
- return this.recordArrayManager.liveArrayFor(type);
4767
+ assert(`You need to pass a model name to the store's peekAll method`, type);
4768
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4769
+ return this.recordArrayManager.liveArrayFor(normalizeModelName(type));
4928
4770
  }
4929
4771
 
4930
4772
  /**
@@ -4936,16 +4778,17 @@ class Store extends EmberObject {
4936
4778
  store.unloadAll('post');
4937
4779
  ```
4938
4780
  @method unloadAll
4781
+ @param {string} type the name of the resource
4939
4782
  @public
4940
- @param {String} modelName
4941
4783
  */
4942
- unloadAll(modelName) {
4784
+
4785
+ unloadAll(type) {
4943
4786
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4944
4787
  assertDestroyedStoreOnly(this, 'unloadAll');
4945
4788
  }
4946
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(modelName)}`, !modelName || typeof modelName === 'string');
4789
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`, !type || typeof type === 'string');
4947
4790
  this._join(() => {
4948
- if (modelName === undefined) {
4791
+ if (type === undefined) {
4949
4792
  // destroy the graph before unloadAll
4950
4793
  // since then we avoid churning relationships
4951
4794
  // during unload
@@ -4953,8 +4796,7 @@ class Store extends EmberObject {
4953
4796
  this.recordArrayManager.clear();
4954
4797
  this._instanceCache.clear();
4955
4798
  } else {
4956
- let normalizedModelName = normalizeModelName(modelName);
4957
- this._instanceCache.clear(normalizedModelName);
4799
+ this._instanceCache.clear(normalizeModelName(type));
4958
4800
  }
4959
4801
  });
4960
4802
  }
@@ -5094,421 +4936,784 @@ class Store extends EmberObject {
5094
4936
  if (macroCondition(getOwnConfig().env.DEBUG)) {
5095
4937
  assertDestroyingStore(this, 'push');
5096
4938
  }
5097
- let pushed = this._push(data, false);
4939
+ const pushed = this._push(data, false);
5098
4940
  if (Array.isArray(pushed)) {
5099
- let records = pushed.map(identifier => this._instanceCache.getRecord(identifier));
5100
- return records;
4941
+ return pushed.map(identifier => this._instanceCache.getRecord(identifier));
5101
4942
  }
5102
4943
  if (pushed === null) {
5103
4944
  return null;
5104
4945
  }
5105
4946
  return this._instanceCache.getRecord(pushed);
5106
4947
  }
5107
-
4948
+
4949
+ /**
4950
+ Push some data in the form of a json-api document into the store,
4951
+ without creating materialized records.
4952
+ @method _push
4953
+ @private
4954
+ @param {Object} jsonApiDoc
4955
+ @return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
4956
+ */
4957
+ _push(jsonApiDoc, asyncFlush) {
4958
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
4959
+ assertDestroyingStore(this, '_push');
4960
+ }
4961
+ if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
4962
+ try {
4963
+ const data = JSON.parse(JSON.stringify(jsonApiDoc));
4964
+ // eslint-disable-next-line no-console
4965
+ console.log('EmberData | Payload - push', data);
4966
+ } catch (e) {
4967
+ // eslint-disable-next-line no-console
4968
+ console.log('EmberData | Payload - push', jsonApiDoc);
4969
+ }
4970
+ }
4971
+ if (asyncFlush) {
4972
+ this._enableAsyncFlush = true;
4973
+ }
4974
+ let ret;
4975
+ this._join(() => {
4976
+ ret = this.cache.put({
4977
+ content: jsonApiDoc
4978
+ });
4979
+ });
4980
+ this._enableAsyncFlush = null;
4981
+ return 'data' in ret ? ret.data : null;
4982
+ }
4983
+
4984
+ /**
4985
+ * Trigger a save for a Record.
4986
+ *
4987
+ * Returns a promise resolving with the same record when the save is complete.
4988
+ *
4989
+ * @method saveRecord
4990
+ * @public
4991
+ * @param {unknown} record
4992
+ * @param options
4993
+ * @return {Promise<record>}
4994
+ */
4995
+ saveRecord(record, options = {}) {
4996
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
4997
+ assertDestroyingStore(this, 'saveRecord');
4998
+ }
4999
+ assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
5000
+ const identifier = recordIdentifierFor(record);
5001
+ const cache = this.cache;
5002
+ if (!identifier) {
5003
+ // this commonly means we're disconnected
5004
+ // but just in case we reject here to prevent bad things.
5005
+ return Promise.reject(new Error(`Record Is Disconnected`));
5006
+ }
5007
+ // TODO we used to check if the record was destroyed here
5008
+ assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
5009
+ if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5010
+ return Promise.resolve(record);
5011
+ }
5012
+ if (!options) {
5013
+ options = {};
5014
+ }
5015
+ let operation = 'updateRecord';
5016
+ if (cache.isNew(identifier)) {
5017
+ operation = 'createRecord';
5018
+ } else if (cache.isDeleted(identifier)) {
5019
+ operation = 'deleteRecord';
5020
+ }
5021
+ const request = {
5022
+ op: operation,
5023
+ data: {
5024
+ options,
5025
+ record: identifier
5026
+ },
5027
+ records: [identifier],
5028
+ cacheOptions: {
5029
+ [SkipCache]: true
5030
+ }
5031
+ };
5032
+
5033
+ // we lie here on the type because legacy doesn't have enough context
5034
+ cache.willCommit(identifier, {
5035
+ request
5036
+ });
5037
+ return this.request(request).then(document => document.content);
5038
+ }
5039
+
5040
+ /**
5041
+ * Instantiation hook allowing applications or addons to configure the store
5042
+ * to utilize a custom Cache implementation.
5043
+ *
5044
+ * This hook should not be called directly by consuming applications or libraries.
5045
+ * Use `Store.cache` to access the Cache instance.
5046
+ *
5047
+ * @method createCache (hook)
5048
+ * @public
5049
+ * @param storeWrapper
5050
+ * @return {Cache}
5051
+ */
5052
+
5053
+ /**
5054
+ * Returns the cache instance associated to this Store, instantiates the Cache
5055
+ * if necessary via `Store.createCache`
5056
+ *
5057
+ * @property {Cache} cache
5058
+ * @public
5059
+ */
5060
+ get cache() {
5061
+ let {
5062
+ cache
5063
+ } = this._instanceCache;
5064
+ if (!cache) {
5065
+ cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5066
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5067
+ cache = new CacheManager(cache);
5068
+ }
5069
+ }
5070
+ return cache;
5071
+ }
5072
+
5073
+ // @ts-expect-error
5074
+ destroy() {
5075
+ if (this.isDestroyed) {
5076
+ // @ember/test-helpers will call destroy multiple times
5077
+ return;
5078
+ }
5079
+ this.isDestroying = true;
5080
+ this._graph?.destroy();
5081
+ this._graph = undefined;
5082
+ this.notifications.destroy();
5083
+ this.recordArrayManager.destroy();
5084
+ this.identifierCache.destroy();
5085
+ this.unloadAll();
5086
+ this.isDestroyed = true;
5087
+ }
5088
+ static create(args) {
5089
+ return new this(args);
5090
+ }
5091
+ }
5092
+ let assertDestroyingStore;
5093
+ let assertDestroyedStoreOnly;
5094
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5095
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5096
+ assertDestroyingStore = function assertDestroyingStore(store, method) {
5097
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5098
+ };
5099
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5100
+ assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5101
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5102
+ };
5103
+ }
5104
+ function isMaybeIdentifier(maybeIdentifier) {
5105
+ return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5106
+ }
5107
+ function normalizeProperties(store, identifier, properties) {
5108
+ // assert here
5109
+ if (properties !== undefined) {
5110
+ if ('id' in properties) {
5111
+ assert(`expected id to be a string or null`, properties.id !== undefined);
5112
+ }
5113
+ assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5114
+ const {
5115
+ type
5116
+ } = identifier;
5117
+
5118
+ // convert relationship Records to RecordDatas before passing to RecordData
5119
+ const defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5120
+ type
5121
+ });
5122
+ if (defs !== null) {
5123
+ const keys = Object.keys(properties);
5124
+ let relationshipValue;
5125
+ for (let i = 0; i < keys.length; i++) {
5126
+ const prop = keys[i];
5127
+ const def = defs[prop];
5128
+ if (def !== undefined) {
5129
+ if (def.kind === 'hasMany') {
5130
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5131
+ assertRecordsPassedToHasMany(properties[prop]);
5132
+ }
5133
+ relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5134
+ } else {
5135
+ relationshipValue = extractIdentifierFromRecord(properties[prop]);
5136
+ }
5137
+ properties[prop] = relationshipValue;
5138
+ }
5139
+ }
5140
+ }
5141
+ }
5142
+ return properties;
5143
+ }
5144
+ function assertRecordsPassedToHasMany(records) {
5145
+ assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5146
+ assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5147
+ return records.every(record => {
5148
+ try {
5149
+ recordIdentifierFor(record);
5150
+ return true;
5151
+ } catch {
5152
+ return false;
5153
+ }
5154
+ });
5155
+ }());
5156
+ }
5157
+ function extractIdentifiersFromRecords(records) {
5158
+ return records.map(record => extractIdentifierFromRecord(record));
5159
+ }
5160
+ function extractIdentifierFromRecord(recordOrPromiseRecord) {
5161
+ if (!recordOrPromiseRecord) {
5162
+ return null;
5163
+ }
5164
+ const extract = recordIdentifierFor;
5165
+ return extract(recordOrPromiseRecord);
5166
+ }
5167
+ function urlFromLink(link) {
5168
+ if (typeof link === 'string') return link;
5169
+ return link.href;
5170
+ }
5171
+
5172
+ /**
5173
+ * A Document is a class that wraps the response content from a request to the API
5174
+ * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
5175
+ * record instances.
5176
+ *
5177
+ * It is not directly instantiated by the user, and its properties should not
5178
+ * be directly modified. Whether individual properties are mutable or not is
5179
+ * determined by the record instance itself.
5180
+ *
5181
+ * @public
5182
+ * @class Document
5183
+ */
5184
+ var _store = /*#__PURE__*/_classPrivateFieldKey("store");
5185
+ var _request = /*#__PURE__*/_classPrivateFieldKey("request");
5186
+ class Document {
5187
+ constructor(store, identifier) {
5188
+ Object.defineProperty(this, _request, {
5189
+ value: _request2
5190
+ });
5191
+ /**
5192
+ * The links object for this document, if any
5193
+ *
5194
+ * e.g.
5195
+ *
5196
+ * ```
5197
+ * {
5198
+ * self: '/articles?page[number]=3',
5199
+ * }
5200
+ * ```
5201
+ *
5202
+ * @property links
5203
+ * @type {object|undefined} - a links object
5204
+ * @public
5205
+ */
5206
+ /**
5207
+ * The primary data for this document, if any.
5208
+ *
5209
+ * If this document has no primary data (e.g. because it is an error document)
5210
+ * this property will be `undefined`.
5211
+ *
5212
+ * For collections this will be an array of record instances,
5213
+ * for single resource requests it will be a single record instance or null.
5214
+ *
5215
+ * @property data
5216
+ * @public
5217
+ * @type {object|Array<object>|null|undefined} - a data object
5218
+ */
5219
+ /**
5220
+ * The errors returned by the API for this request, if any
5221
+ *
5222
+ * @property errors
5223
+ * @public
5224
+ * @type {object|undefined} - an errors object
5225
+ */
5226
+ /**
5227
+ * The meta object for this document, if any
5228
+ *
5229
+ * @property meta
5230
+ * @public
5231
+ * @type {object|undefined} - a meta object
5232
+ */
5233
+ /**
5234
+ * The identifier associated with this document, if any
5235
+ *
5236
+ * @property identifier
5237
+ * @public
5238
+ * @type {StableDocumentIdentifier|null}
5239
+ */
5240
+ Object.defineProperty(this, _store, {
5241
+ writable: true,
5242
+ value: void 0
5243
+ });
5244
+ _classPrivateFieldBase(this, _store)[_store] = store;
5245
+ this.identifier = identifier;
5246
+ }
5108
5247
  /**
5109
- Push some data in the form of a json-api document into the store,
5110
- without creating materialized records.
5111
- @method _push
5112
- @private
5113
- @param {Object} jsonApiDoc
5114
- @return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
5115
- */
5116
- _push(jsonApiDoc, asyncFlush) {
5117
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5118
- assertDestroyingStore(this, '_push');
5119
- }
5120
- if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
5121
- try {
5122
- let data = JSON.parse(JSON.stringify(jsonApiDoc));
5123
- // eslint-disable-next-line no-console
5124
- console.log('EmberData | Payload - push', data);
5125
- } catch (e) {
5126
- // eslint-disable-next-line no-console
5127
- console.log('EmberData | Payload - push', jsonApiDoc);
5128
- }
5129
- }
5130
- if (asyncFlush) {
5131
- this._enableAsyncFlush = true;
5132
- }
5133
- let ret;
5134
- this._join(() => {
5135
- ret = this.cache.put({
5136
- content: jsonApiDoc
5137
- });
5138
- });
5139
- this._enableAsyncFlush = null;
5140
- return 'data' in ret ? ret.data : null;
5248
+ * Fetches the related link for this document, returning a promise that resolves
5249
+ * with the document when the request completes. If no related link is present,
5250
+ * will fallback to the self link if present
5251
+ *
5252
+ * @method fetch
5253
+ * @public
5254
+ * @param {object} options
5255
+ * @return Promise<Document>
5256
+ */
5257
+ fetch(options = {}) {
5258
+ assert(`No self or related link`, this.links?.related || this.links?.self);
5259
+ options.cacheOptions = options.cacheOptions || {};
5260
+ options.cacheOptions.key = this.identifier?.lid;
5261
+ return _classPrivateFieldBase(this, _request)[_request](this.links.related ? 'related' : 'self', options);
5141
5262
  }
5142
5263
 
5143
5264
  /**
5144
- Push some raw data into the store.
5145
- This method can be used both to push in brand new
5146
- records, as well as to update existing records. You
5147
- can push in more than one type of object at once.
5148
- All objects should be in the format expected by the
5149
- serializer.
5150
- ```app/serializers/application.js
5151
- import RESTSerializer from '@ember-data/serializer/rest';
5152
- export default class ApplicationSerializer extends RESTSerializer;
5153
- ```
5154
- ```js
5155
- let pushData = {
5156
- posts: [
5157
- { id: 1, postTitle: "Great post", commentIds: [2] }
5158
- ],
5159
- comments: [
5160
- { id: 2, commentBody: "Insightful comment" }
5161
- ]
5162
- }
5163
- store.pushPayload(pushData);
5164
- ```
5165
- By default, the data will be deserialized using a default
5166
- serializer (the application serializer if it exists).
5167
- Alternatively, `pushPayload` will accept a model type which
5168
- will determine which serializer will process the payload.
5169
- ```app/serializers/application.js
5170
- import RESTSerializer from '@ember-data/serializer/rest';
5171
- export default class ApplicationSerializer extends RESTSerializer;
5172
- ```
5173
- ```app/serializers/post.js
5174
- import JSONSerializer from '@ember-data/serializer/json';
5175
- export default JSONSerializer;
5176
- ```
5177
- ```js
5178
- store.pushPayload(pushData); // Will use the application serializer
5179
- store.pushPayload('post', pushData); // Will use the post serializer
5180
- ```
5181
- @method pushPayload
5182
- @public
5183
- @param {String} modelName Optionally, a model type used to determine which serializer will be used
5184
- @param {Object} inputPayload
5185
- */
5186
- // TODO @runspired @deprecate pushPayload in favor of looking up the serializer
5187
- pushPayload(modelName, inputPayload) {
5188
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5189
- assertDestroyingStore(this, 'pushPayload');
5190
- }
5191
- const payload = inputPayload || modelName;
5192
- const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
5193
- const serializer = this.serializerFor(normalizedModelName);
5194
- assert(`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`, serializer && typeof serializer.pushPayload === 'function');
5195
- serializer.pushPayload(this, payload);
5265
+ * Fetches the next link for this document, returning a promise that resolves
5266
+ * with the new document when the request completes, or null if there is no
5267
+ * next link.
5268
+ *
5269
+ * @method next
5270
+ * @public
5271
+ * @param {object} options
5272
+ * @return Promise<Document | null>
5273
+ */
5274
+ next(options = {}) {
5275
+ return _classPrivateFieldBase(this, _request)[_request]('next', options);
5196
5276
  }
5197
5277
 
5198
5278
  /**
5199
- * Trigger a save for a Record.
5279
+ * Fetches the prev link for this document, returning a promise that resolves
5280
+ * with the new document when the request completes, or null if there is no
5281
+ * prev link.
5200
5282
  *
5201
- * @method saveRecord
5283
+ * @method prev
5202
5284
  * @public
5203
- * @param {RecordInstance} record
5204
- * @param options
5205
- * @returns {Promise<RecordInstance>}
5285
+ * @param {object} options
5286
+ * @return Promise<Document | null>
5206
5287
  */
5207
- saveRecord(record, options = {}) {
5208
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5209
- assertDestroyingStore(this, 'saveRecord');
5210
- }
5211
- assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
5212
- let identifier = recordIdentifierFor(record);
5213
- const cache = this.cache;
5214
- if (!identifier) {
5215
- // this commonly means we're disconnected
5216
- // but just in case we reject here to prevent bad things.
5217
- return Promise.reject(`Record Is Disconnected`);
5218
- }
5219
- // TODO we used to check if the record was destroyed here
5220
- assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
5221
- if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5222
- return Promise.resolve(record);
5223
- }
5224
- if (!options) {
5225
- options = {};
5226
- }
5227
- let operation = 'updateRecord';
5228
- if (cache.isNew(identifier)) {
5229
- operation = 'createRecord';
5230
- } else if (cache.isDeleted(identifier)) {
5231
- operation = 'deleteRecord';
5232
- }
5233
- const request = {
5234
- op: operation,
5235
- data: {
5236
- options,
5237
- record: identifier
5238
- },
5239
- cacheOptions: {
5240
- [SkipCache]: true
5241
- }
5242
- };
5243
-
5244
- // we lie here on the type because legacy doesn't have enough context
5245
- cache.willCommit(identifier, {
5246
- request
5247
- });
5248
- return this.request(request).then(document => document.content);
5288
+ prev(options = {}) {
5289
+ return _classPrivateFieldBase(this, _request)[_request]('prev', options);
5249
5290
  }
5250
5291
 
5251
5292
  /**
5252
- * Instantiation hook allowing applications or addons to configure the store
5253
- * to utilize a custom Cache implementation.
5293
+ * Fetches the first link for this document, returning a promise that resolves
5294
+ * with the new document when the request completes, or null if there is no
5295
+ * first link.
5254
5296
  *
5255
- * This hook should not be called directly by consuming applications or libraries.
5256
- * Use `Store.cache` to access the Cache instance.
5297
+ * @method first
5298
+ * @public
5299
+ * @param {object} options
5300
+ * @return Promise<Document | null>
5301
+ */
5302
+ first(options = {}) {
5303
+ return _classPrivateFieldBase(this, _request)[_request]('first', options);
5304
+ }
5305
+
5306
+ /**
5307
+ * Fetches the last link for this document, returning a promise that resolves
5308
+ * with the new document when the request completes, or null if there is no
5309
+ * last link.
5257
5310
  *
5258
- * @method createCache (hook)
5311
+ * @method last
5259
5312
  * @public
5260
- * @param storeWrapper
5261
- * @returns {Cache}
5313
+ * @param {object} options
5314
+ * @return Promise<Document | null>
5262
5315
  */
5316
+ last(options = {}) {
5317
+ return _classPrivateFieldBase(this, _request)[_request]('last', options);
5318
+ }
5263
5319
 
5264
5320
  /**
5265
- * Returns the cache instance associated to this Store, instantiates the Cache
5266
- * if necessary via `Store.createCache`
5321
+ * Implemented for `JSON.stringify` support.
5267
5322
  *
5268
- * @property {Cache} cache
5323
+ * Returns the JSON representation of the document wrapper.
5324
+ *
5325
+ * This is a shallow serialization, it does not deeply serialize
5326
+ * the document's contents, leaving that to the individual record
5327
+ * instances to determine how to do, if at all.
5328
+ *
5329
+ * @method toJSON
5269
5330
  * @public
5331
+ * @return
5270
5332
  */
5271
- get cache() {
5272
- let {
5273
- cache
5274
- } = this._instanceCache;
5275
- if (!cache) {
5276
- cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5277
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5278
- cache = new CacheManager(cache);
5333
+ toJSON() {
5334
+ const data = {};
5335
+ data.identifier = this.identifier;
5336
+ if (this.data !== undefined) {
5337
+ data.data = this.data;
5338
+ }
5339
+ if (this.links !== undefined) {
5340
+ data.links = this.links;
5341
+ }
5342
+ if (this.errors !== undefined) {
5343
+ data.errors = this.errors;
5344
+ }
5345
+ if (this.meta !== undefined) {
5346
+ data.meta = this.meta;
5347
+ }
5348
+ return data;
5349
+ }
5350
+ }
5351
+ async function _request2(link, options) {
5352
+ const href = this.links?.[link];
5353
+ if (!href) {
5354
+ return null;
5355
+ }
5356
+ options.method = options.method || 'GET';
5357
+ const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
5358
+ url: urlFromLink(href)
5359
+ }));
5360
+ return response.content;
5361
+ }
5362
+ defineSignal(Document.prototype, 'data');
5363
+ defineSignal(Document.prototype, 'links');
5364
+ defineSignal(Document.prototype, 'errors');
5365
+ defineSignal(Document.prototype, 'meta');
5366
+
5367
+ /**
5368
+ * @module @ember-data/store
5369
+ */
5370
+
5371
+ /**
5372
+ * A service which an application may provide to the store via
5373
+ * the store's `lifetimes` property to configure the behavior
5374
+ * of the CacheHandler.
5375
+ *
5376
+ * The default behavior for request lifetimes is to never expire
5377
+ * unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
5378
+ *
5379
+ * Implementing this service allows you to programatically define
5380
+ * when a request should be considered expired.
5381
+ *
5382
+ * @class <Interface> LifetimesService
5383
+ * @public
5384
+ */
5385
+
5386
+ const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
5387
+ function isErrorDocument(document) {
5388
+ return 'errors' in document;
5389
+ }
5390
+ function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
5391
+ const {
5392
+ identifier
5393
+ } = options;
5394
+ if (!document) {
5395
+ assert(`The CacheHandler expected response content but none was found`, !options.shouldHydrate);
5396
+ return document;
5397
+ }
5398
+ if (isErrorDocument(document)) {
5399
+ if (!identifier && !options.shouldHydrate) {
5400
+ return document;
5401
+ }
5402
+ let doc;
5403
+ if (identifier) {
5404
+ doc = store._documentCache.get(identifier);
5405
+ }
5406
+ if (!doc) {
5407
+ doc = new Document(store, identifier);
5408
+ copyDocumentProperties(doc, document);
5409
+ if (identifier) {
5410
+ store._documentCache.set(identifier, doc);
5411
+ }
5412
+ } else if (!isFromCache) {
5413
+ doc.data = undefined;
5414
+ copyDocumentProperties(doc, document);
5415
+ }
5416
+ return options.shouldHydrate ? doc : document;
5417
+ }
5418
+ if (Array.isArray(document.data)) {
5419
+ const {
5420
+ recordArrayManager
5421
+ } = store;
5422
+ if (!identifier) {
5423
+ if (!options.shouldHydrate) {
5424
+ return document;
5425
+ }
5426
+ const data = recordArrayManager.createArray({
5427
+ type: request.url,
5428
+ identifiers: document.data,
5429
+ doc: document,
5430
+ query: request
5431
+ });
5432
+ const doc = new Document(store, null);
5433
+ doc.data = data;
5434
+ doc.meta = document.meta;
5435
+ doc.links = document.links;
5436
+ return doc;
5437
+ }
5438
+ let managed = recordArrayManager._keyedArrays.get(identifier.lid);
5439
+ if (!managed) {
5440
+ managed = recordArrayManager.createArray({
5441
+ type: identifier.lid,
5442
+ identifiers: document.data,
5443
+ doc: document
5444
+ });
5445
+ recordArrayManager._keyedArrays.set(identifier.lid, managed);
5446
+ const doc = new Document(store, identifier);
5447
+ doc.data = managed;
5448
+ doc.meta = document.meta;
5449
+ doc.links = document.links;
5450
+ store._documentCache.set(identifier, doc);
5451
+ return options.shouldHydrate ? doc : document;
5452
+ } else {
5453
+ const doc = store._documentCache.get(identifier);
5454
+ if (!isFromCache) {
5455
+ recordArrayManager.populateManagedArray(managed, document.data, document);
5456
+ doc.data = managed;
5457
+ doc.meta = document.meta;
5458
+ doc.links = document.links;
5279
5459
  }
5460
+ return options.shouldHydrate ? doc : document;
5280
5461
  }
5281
- return cache;
5282
- }
5283
-
5284
- /**
5285
- `normalize` converts a json payload into the normalized form that
5286
- [push](../methods/push?anchor=push) expects.
5287
- Example
5288
- ```js
5289
- socket.on('message', function(message) {
5290
- let modelName = message.model;
5291
- let data = message.data;
5292
- store.push(store.normalize(modelName, data));
5293
- });
5294
- ```
5295
- @method normalize
5296
- @public
5297
- @param {String} modelName The name of the model type for this payload
5298
- @param {Object} payload
5299
- @return {Object} The normalized payload
5300
- */
5301
- // TODO @runspired @deprecate users should call normalize on the associated serializer directly
5302
- normalize(modelName, payload) {
5303
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5304
- assertDestroyingStore(this, 'normalize');
5305
- }
5306
- assert(`You need to pass a model name to the store's normalize method`, modelName);
5307
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${typeof modelName}`, typeof modelName === 'string');
5308
- const normalizedModelName = normalizeModelName(modelName);
5309
- const serializer = this.serializerFor(normalizedModelName);
5310
- const schema = this.modelFor(normalizedModelName);
5311
- assert(`You must define a normalize method in your serializer in order to call store.normalize`, typeof serializer?.normalize === 'function');
5312
- return serializer.normalize(schema, payload);
5313
- }
5314
-
5315
- /**
5316
- Returns an instance of the adapter for a given type. For
5317
- example, `adapterFor('person')` will return an instance of
5318
- the adapter located at `app/adapters/person.js`
5319
- If no `person` adapter is found, this method will look
5320
- for an `application` adapter (the default adapter for
5321
- your entire application).
5322
- @method adapterFor
5323
- @public
5324
- @param {String} modelName
5325
- @return Adapter
5326
- */
5327
- adapterFor(modelName) {
5328
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5329
- assertDestroyingStore(this, 'adapterFor');
5330
- }
5331
- assert(`You need to pass a model name to the store's adapterFor method`, modelName);
5332
- assert(`Passing classes to store.adapterFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5333
- let normalizedModelName = normalizeModelName(modelName);
5334
- let {
5335
- _adapterCache
5336
- } = this;
5337
- let adapter = _adapterCache[normalizedModelName];
5338
- if (adapter) {
5339
- return adapter;
5462
+ } else {
5463
+ if (!identifier && !options.shouldHydrate) {
5464
+ return document;
5340
5465
  }
5341
- const owner = getOwner(this);
5342
-
5343
- // name specific adapter
5344
- adapter = owner.lookup(`adapter:${normalizedModelName}`);
5345
- if (adapter !== undefined) {
5346
- _adapterCache[normalizedModelName] = adapter;
5347
- return adapter;
5466
+ const data = document.data ? store.peekRecord(document.data) : null;
5467
+ let doc;
5468
+ if (identifier) {
5469
+ doc = store._documentCache.get(identifier);
5348
5470
  }
5349
-
5350
- // no adapter found for the specific name, fallback and check for application adapter
5351
- adapter = _adapterCache.application || owner.lookup('adapter:application');
5352
- if (adapter !== undefined) {
5353
- _adapterCache[normalizedModelName] = adapter;
5354
- _adapterCache.application = adapter;
5355
- return adapter;
5471
+ if (!doc) {
5472
+ doc = new Document(store, identifier);
5473
+ doc.data = data;
5474
+ copyDocumentProperties(doc, document);
5475
+ if (identifier) {
5476
+ store._documentCache.set(identifier, doc);
5477
+ }
5478
+ } else if (!isFromCache) {
5479
+ doc.data = data;
5480
+ copyDocumentProperties(doc, document);
5356
5481
  }
5357
- assert(`No adapter was found for '${modelName}' and no 'application' adapter was found as a fallback.`);
5482
+ return options.shouldHydrate ? doc : document;
5358
5483
  }
5359
-
5360
- /**
5361
- Returns an instance of the serializer for a given type. For
5362
- example, `serializerFor('person')` will return an instance of
5363
- `App.PersonSerializer`.
5364
- If no `App.PersonSerializer` is found, this method will look
5365
- for an `App.ApplicationSerializer` (the default serializer for
5366
- your entire application).
5367
- If a serializer cannot be found on the adapter, it will fall back
5368
- to an instance of `JSONSerializer`.
5369
- @method serializerFor
5370
- @public
5371
- @param {String} modelName the record to serialize
5372
- @return {Serializer}
5373
- */
5374
- serializerFor(modelName) {
5375
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5376
- assertDestroyingStore(this, 'serializerFor');
5377
- }
5378
- assert(`You need to pass a model name to the store's serializerFor method`, modelName);
5379
- assert(`Passing classes to store.serializerFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
5380
- let normalizedModelName = normalizeModelName(modelName);
5381
- let {
5382
- _serializerCache
5383
- } = this;
5384
- let serializer = _serializerCache[normalizedModelName];
5385
- if (serializer) {
5386
- return serializer;
5484
+ }
5485
+ function calcShouldFetch(store, request, hasCachedValue, identifier) {
5486
+ const {
5487
+ cacheOptions
5488
+ } = request;
5489
+ return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
5490
+ }
5491
+ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
5492
+ const {
5493
+ cacheOptions
5494
+ } = request;
5495
+ return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false));
5496
+ }
5497
+ function isMutation(request) {
5498
+ return Boolean(request.op && MUTATION_OPS.has(request.op));
5499
+ }
5500
+ function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
5501
+ const {
5502
+ store
5503
+ } = context.request;
5504
+ const shouldHydrate = context.request[EnableHydration] || false;
5505
+ let isMut = false;
5506
+ if (isMutation(context.request)) {
5507
+ isMut = true;
5508
+ // TODO should we handle multiple records in request.records by iteratively calling willCommit for each
5509
+ const record = context.request.data?.record || context.request.records?.[0];
5510
+ assert(`Expected to receive a list of records included in the ${context.request.op} request`, record || !shouldHydrate);
5511
+ if (record) {
5512
+ store.cache.willCommit(record, context);
5387
5513
  }
5388
-
5389
- // by name
5390
- const owner = getOwner(this);
5391
- serializer = owner.lookup(`serializer:${normalizedModelName}`);
5392
- if (serializer !== undefined) {
5393
- _serializerCache[normalizedModelName] = serializer;
5394
- return serializer;
5514
+ }
5515
+ if (store.lifetimes?.willRequest) {
5516
+ store.lifetimes.willRequest(context.request, identifier, store);
5517
+ }
5518
+ const promise = next(context.request).then(document => {
5519
+ store.requestManager._pending.delete(context.id);
5520
+ store._enableAsyncFlush = true;
5521
+ let response;
5522
+ store._join(() => {
5523
+ if (isMutation(context.request)) {
5524
+ const record = context.request.data?.record || context.request.records?.[0];
5525
+ if (record) {
5526
+ response = store.cache.didCommit(record, document);
5527
+
5528
+ // a mutation combined with a 204 has no cache impact when no known records were involved
5529
+ // a createRecord with a 201 with an empty response and no known records should similarly
5530
+ // have no cache impact
5531
+ } else if (isCacheAffecting(document)) {
5532
+ response = store.cache.put(document);
5533
+ }
5534
+ } else {
5535
+ response = store.cache.put(document);
5536
+ }
5537
+ response = maybeUpdateUiObjects(store, context.request, {
5538
+ shouldHydrate,
5539
+ shouldFetch,
5540
+ shouldBackgroundFetch,
5541
+ identifier
5542
+ }, response, false);
5543
+ });
5544
+ store._enableAsyncFlush = null;
5545
+ if (store.lifetimes?.didRequest) {
5546
+ store.lifetimes.didRequest(context.request, document.response, identifier, store);
5395
5547
  }
5396
-
5397
- // no serializer found for the specific model, fallback and check for application serializer
5398
- serializer = _serializerCache.application || owner.lookup('serializer:application');
5399
- if (serializer !== undefined) {
5400
- _serializerCache[normalizedModelName] = serializer;
5401
- _serializerCache.application = serializer;
5402
- return serializer;
5548
+ if (shouldFetch) {
5549
+ return response;
5550
+ } else if (shouldBackgroundFetch) {
5551
+ store.notifications._flush();
5403
5552
  }
5404
- return null;
5405
- }
5406
-
5407
- // @ts-expect-error
5408
- destroy() {
5409
- if (this.isDestroyed) {
5410
- // @ember/test-helpers will call destroy multiple times
5411
- return;
5553
+ }, error => {
5554
+ store.requestManager._pending.delete(context.id);
5555
+ if (context.request.signal?.aborted) {
5556
+ throw error;
5412
5557
  }
5413
- this.isDestroying = true;
5414
- // enqueue destruction of any adapters/serializers we have created
5415
- for (let adapterName in this._adapterCache) {
5416
- let adapter = this._adapterCache[adapterName];
5417
- if (typeof adapter.destroy === 'function') {
5418
- adapter.destroy();
5558
+ store.requestManager._pending.delete(context.id);
5559
+ store._enableAsyncFlush = true;
5560
+ let response;
5561
+ store._join(() => {
5562
+ if (isMutation(context.request)) {
5563
+ // TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
5564
+ // currently we let the response remain undefiend.
5565
+ const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
5566
+ const record = context.request.data?.record || context.request.records?.[0];
5567
+ store.cache.commitWasRejected(record, errors);
5568
+ // re-throw the original error to preserve `errors` property.
5569
+ throw error;
5570
+ } else {
5571
+ response = store.cache.put(error);
5572
+ response = maybeUpdateUiObjects(store, context.request, {
5573
+ shouldHydrate,
5574
+ shouldFetch,
5575
+ shouldBackgroundFetch,
5576
+ identifier
5577
+ }, response, false);
5419
5578
  }
5579
+ });
5580
+ store._enableAsyncFlush = null;
5581
+ if (identifier && store.lifetimes?.didRequest) {
5582
+ store.lifetimes.didRequest(context.request, error.response, identifier, store);
5420
5583
  }
5421
- for (let serializerName in this._serializerCache) {
5422
- let serializer = this._serializerCache[serializerName];
5423
- if (typeof serializer.destroy === 'function') {
5424
- serializer.destroy();
5425
- }
5584
+ if (!shouldBackgroundFetch) {
5585
+ const newError = cloneError(error);
5586
+ newError.content = response;
5587
+ throw newError;
5588
+ } else {
5589
+ store.notifications._flush();
5426
5590
  }
5427
- this._graph?.destroy();
5428
- this._graph = undefined;
5429
- this.notifications.destroy();
5430
- this.recordArrayManager.destroy();
5431
- this.identifierCache.destroy();
5432
- this.unloadAll();
5433
- this.isDestroyed = true;
5434
- }
5435
- static create(args) {
5436
- return new this(args);
5591
+ });
5592
+ if (!isMut) {
5593
+ return promise;
5437
5594
  }
5595
+ assert(`Expected a mutation`, isMutation(context.request));
5596
+
5597
+ // for mutations we need to enqueue the promise with the requestStateService
5598
+ // TODO should we enque a request per record in records?
5599
+ const record = context.request.data?.record || context.request.records?.[0];
5600
+ return store._requestCache._enqueue(promise, {
5601
+ data: [{
5602
+ op: 'saveRecord',
5603
+ recordIdentifier: record,
5604
+ options: undefined
5605
+ }]
5606
+ });
5438
5607
  }
5439
- let assertDestroyingStore;
5440
- let assertDestroyedStoreOnly;
5441
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5442
- // eslint-disable-next-line @typescript-eslint/no-shadow
5443
- assertDestroyingStore = function assertDestroyingStore(store, method) {
5444
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5445
- };
5446
- // eslint-disable-next-line @typescript-eslint/no-shadow
5447
- assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5448
- assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5449
- };
5450
- }
5451
- function isMaybeIdentifier(maybeIdentifier) {
5452
- return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5608
+ function cloneError(error) {
5609
+ const cloned = new Error(error.message);
5610
+ cloned.stack = error.stack;
5611
+ cloned.error = error.error;
5612
+ return cloned;
5453
5613
  }
5454
- function normalizeProperties(store, identifier, properties) {
5455
- // assert here
5456
- if (properties !== undefined) {
5457
- if ('id' in properties) {
5458
- assert(`expected id to be a string or null`, properties.id !== undefined);
5614
+
5615
+ /**
5616
+ * A CacheHandler that adds support for using an EmberData Cache with a RequestManager.
5617
+ *
5618
+ * This handler will only run when a request has supplied a `store` instance. Requests
5619
+ * issued by the store via `store.request()` will automatically have the `store` instance
5620
+ * attached to the request.
5621
+ *
5622
+ * ```ts
5623
+ * requestManager.request({
5624
+ * store: store,
5625
+ * url: '/api/posts',
5626
+ * method: 'GET'
5627
+ * });
5628
+ * ```
5629
+ *
5630
+ * When this handler elects to handle a request, it will return the raw `StructuredDocument`
5631
+ * unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
5632
+ * return a `Document` instance that will automatically update the UI when the cache is updated
5633
+ * in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
5634
+ *
5635
+ * When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
5636
+ * means that if desired you can issue requests that utilize the cache without needing to also
5637
+ * utilize Record instances if desired.
5638
+ *
5639
+ * Said differently, you could elect to issue all requests via a RequestManager, without ever using
5640
+ * the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
5641
+ * necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
5642
+ * approach of EmberData allows for this flexibility.
5643
+ *
5644
+ * ```ts
5645
+ * import { EnableHydration } from '@warp-drive/core-types/request';
5646
+ *
5647
+ * requestManager.request({
5648
+ * store: store,
5649
+ * url: '/api/posts',
5650
+ * method: 'GET',
5651
+ * [EnableHydration]: true
5652
+ * });
5653
+ *
5654
+ * @typedoc
5655
+ */
5656
+ const CacheHandler = {
5657
+ request(context, next) {
5658
+ // if we have no cache or no cache-key skip cache handling
5659
+ if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
5660
+ return next(context.request);
5459
5661
  }
5460
- assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5461
5662
  const {
5462
- type
5463
- } = identifier;
5663
+ store
5664
+ } = context.request;
5665
+ const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
5666
+ const peeked = identifier ? store.cache.peekRequest(identifier) : null;
5464
5667
 
5465
- // convert relationship Records to RecordDatas before passing to RecordData
5466
- let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5467
- type
5468
- });
5469
- if (defs !== null) {
5470
- let keys = Object.keys(properties);
5471
- let relationshipValue;
5472
- for (let i = 0; i < keys.length; i++) {
5473
- let prop = keys[i];
5474
- let def = defs[prop];
5475
- if (def !== undefined) {
5476
- if (def.kind === 'hasMany') {
5477
- if (macroCondition(getOwnConfig().env.DEBUG)) {
5478
- assertRecordsPassedToHasMany(properties[prop]);
5479
- }
5480
- relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5481
- } else {
5482
- relationshipValue = extractIdentifierFromRecord(properties[prop]);
5483
- }
5484
- properties[prop] = relationshipValue;
5485
- }
5486
- }
5668
+ // determine if we should skip cache
5669
+ if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
5670
+ return fetchContentAndHydrate(next, context, identifier, true, false);
5671
+ }
5672
+
5673
+ // if we have not skipped cache, determine if we should update behind the scenes
5674
+ if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
5675
+ const promise = fetchContentAndHydrate(next, context, identifier, false, true);
5676
+ store.requestManager._pending.set(context.id, promise);
5677
+ }
5678
+ const shouldHydrate = context.request[EnableHydration] || false;
5679
+ if ('error' in peeked) {
5680
+ const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5681
+ shouldHydrate,
5682
+ identifier
5683
+ }, peeked.content, true) : peeked.content;
5684
+ const newError = cloneError(peeked);
5685
+ newError.content = content;
5686
+ throw newError;
5487
5687
  }
5688
+ const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5689
+ shouldHydrate,
5690
+ identifier
5691
+ }, peeked.content, true) : peeked.content;
5692
+ return result;
5693
+ }
5694
+ };
5695
+ function copyDocumentProperties(target, source) {
5696
+ if ('links' in source) {
5697
+ target.links = source.links;
5698
+ }
5699
+ if ('meta' in source) {
5700
+ target.meta = source.meta;
5701
+ }
5702
+ if ('errors' in source) {
5703
+ target.errors = source.errors;
5488
5704
  }
5489
- return properties;
5490
- }
5491
- function assertRecordsPassedToHasMany(records) {
5492
- assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5493
- assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5494
- return records.every(record => {
5495
- try {
5496
- recordIdentifierFor(record);
5497
- return true;
5498
- } catch {
5499
- return false;
5500
- }
5501
- });
5502
- }());
5503
- }
5504
- function extractIdentifiersFromRecords(records) {
5505
- return records.map(record => extractIdentifierFromRecord(record));
5506
5705
  }
5507
- function extractIdentifierFromRecord(recordOrPromiseRecord) {
5508
- if (!recordOrPromiseRecord) {
5509
- return null;
5706
+ function isCacheAffecting(document) {
5707
+ if (!isMutation(document.request)) {
5708
+ return true;
5510
5709
  }
5511
- const extract = recordIdentifierFor;
5512
- return extract(recordOrPromiseRecord);
5710
+ // a mutation combined with a 204 has no cache impact when no known records were involved
5711
+ // a createRecord with a 201 with an empty response and no known records should similarly
5712
+ // have no cache impact
5713
+
5714
+ if (document.request.op === 'createRecord' && document.response?.status === 201) {
5715
+ return document.content ? Object.keys(document.content).length > 0 : false;
5716
+ }
5717
+ return document.response?.status !== 204;
5513
5718
  }
5514
- 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 };
5719
+ 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 };