@ember-data/store 5.4.0-alpha.6 → 5.4.0-alpha.61

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 (79) hide show
  1. package/README.md +16 -4
  2. package/addon/-private.js +1 -1
  3. package/addon/{store-service-7ffc4d68.js → cache-handler-Cm55UqNi.js} +1245 -1033
  4. package/addon/cache-handler-Cm55UqNi.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 +65 -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 +177 -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 +1557 -0
  40. package/unstable-preview-types/-private/store-service.d.ts.map +1 -0
  41. package/unstable-preview-types/-private/store-service.type-test.d.ts +4 -0
  42. package/unstable-preview-types/-private/store-service.type-test.d.ts.map +1 -0
  43. package/unstable-preview-types/-private/utils/coerce-id.d.ts +10 -0
  44. package/unstable-preview-types/-private/utils/coerce-id.d.ts.map +1 -0
  45. package/unstable-preview-types/-private/utils/construct-resource.d.ts +10 -0
  46. package/unstable-preview-types/-private/utils/construct-resource.d.ts.map +1 -0
  47. package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts +7 -0
  48. package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts.map +1 -0
  49. package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts +4 -0
  50. package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts.map +1 -0
  51. package/unstable-preview-types/-private/utils/normalize-model-name.d.ts +4 -0
  52. package/unstable-preview-types/-private/utils/normalize-model-name.d.ts.map +1 -0
  53. package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts +4 -0
  54. package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts.map +1 -0
  55. package/unstable-preview-types/-private.d.ts +21 -0
  56. package/unstable-preview-types/-private.d.ts.map +1 -0
  57. package/unstable-preview-types/-types/overview.d.ts +21 -0
  58. package/unstable-preview-types/-types/overview.d.ts.map +1 -0
  59. package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts +107 -0
  60. package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts.map +1 -0
  61. package/unstable-preview-types/-types/q/cache.d.ts +47 -0
  62. package/unstable-preview-types/-types/q/cache.d.ts.map +1 -0
  63. package/unstable-preview-types/-types/q/ds-model.d.ts +15 -0
  64. package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -0
  65. package/unstable-preview-types/-types/q/identifier.d.ts +195 -0
  66. package/unstable-preview-types/-types/q/identifier.d.ts.map +1 -0
  67. package/unstable-preview-types/-types/q/promise-proxies.d.ts +4 -0
  68. package/unstable-preview-types/-types/q/promise-proxies.d.ts.map +1 -0
  69. package/unstable-preview-types/-types/q/record-data-json-api.d.ts +36 -0
  70. package/unstable-preview-types/-types/q/record-data-json-api.d.ts.map +1 -0
  71. package/unstable-preview-types/-types/q/record-instance.d.ts +29 -0
  72. package/unstable-preview-types/-types/q/record-instance.d.ts.map +1 -0
  73. package/unstable-preview-types/-types/q/schema-service.d.ts +214 -0
  74. package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -0
  75. package/unstable-preview-types/-types/q/store.d.ts +38 -0
  76. package/unstable-preview-types/-types/q/store.d.ts.map +1 -0
  77. package/unstable-preview-types/index.d.ts +223 -0
  78. package/unstable-preview-types/index.d.ts.map +1 -0
  79. package/addon/store-service-7ffc4d68.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,10 +147,12 @@ 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;
509
154
  let configuredUpdateMethod;
155
+ let configuredKeyInfoMethod;
510
156
  function setIdentifierGenerationMethod(method) {
511
157
  configuredGenerationMethod = method;
512
158
  }
@@ -519,6 +165,9 @@ function setIdentifierForgetMethod(method) {
519
165
  function setIdentifierResetMethod(method) {
520
166
  configuredResetMethod = method;
521
167
  }
168
+ function setKeyInfoForResource(method) {
169
+ configuredKeyInfoMethod = method;
170
+ }
522
171
 
523
172
  // Map<type, Map<id, lid>>
524
173
 
@@ -604,13 +253,14 @@ class IdentifierCache {
604
253
  this._forget = configuredForgetMethod || defaultEmptyCallback;
605
254
  this._reset = configuredResetMethod || defaultEmptyCallback;
606
255
  this._merge = defaultMergeMethod;
607
- this._keyInfoForResource = defaultKeyInfoMethod;
256
+ this._keyInfoForResource = configuredKeyInfoMethod || defaultKeyInfoMethod;
608
257
  this._isDefaultConfig = !configuredGenerationMethod;
609
258
  this._id = IDENTIFIER_CACHE_ID++;
610
259
  this._cache = {
611
260
  resources: new Map(),
612
261
  resourcesByType: Object.create(null),
613
- documents: new Map()
262
+ documents: new Map(),
263
+ polymorphicLidBackMap: new Map()
614
264
  };
615
265
  }
616
266
 
@@ -706,7 +356,7 @@ class IdentifierCache {
706
356
  *
707
357
  * @method peekRecordIdentifier
708
358
  * @param resource
709
- * @returns {StableRecordIdentifier | undefined}
359
+ * @return {StableRecordIdentifier | undefined}
710
360
  * @private
711
361
  */
712
362
  peekRecordIdentifier(resource) {
@@ -718,7 +368,7 @@ class IdentifierCache {
718
368
  Returns `null` if the request does not have a `cacheKey` or `url`.
719
369
  @method getOrCreateDocumentIdentifier
720
370
  @param request
721
- @returns {StableDocumentIdentifier | null}
371
+ @return {StableDocumentIdentifier | null}
722
372
  @public
723
373
  */
724
374
  getOrCreateDocumentIdentifier(request) {
@@ -752,7 +402,7 @@ class IdentifierCache {
752
402
  - this referential stability of the object itself is guaranteed
753
403
  @method getOrCreateRecordIdentifier
754
404
  @param resource
755
- @returns {StableRecordIdentifier}
405
+ @return {StableRecordIdentifier}
756
406
  @public
757
407
  */
758
408
  getOrCreateRecordIdentifier(resource) {
@@ -767,12 +417,12 @@ class IdentifierCache {
767
417
  with the signature `generateMethod({ type }, 'record')`.
768
418
  @method createIdentifierForNewRecord
769
419
  @param data
770
- @returns {StableRecordIdentifier}
420
+ @return {StableRecordIdentifier}
771
421
  @public
772
422
  */
773
423
  createIdentifierForNewRecord(data) {
774
- let newLid = this._generate(data, 'record');
775
- let identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
424
+ const newLid = this._generate(data, 'record');
425
+ const identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
776
426
  id: data.id || null,
777
427
  type: data.type,
778
428
  lid: newLid,
@@ -810,7 +460,7 @@ class IdentifierCache {
810
460
  @method updateRecordIdentifier
811
461
  @param identifierObject
812
462
  @param data
813
- @returns {StableRecordIdentifier}
463
+ @return {StableRecordIdentifier}
814
464
  @public
815
465
  */
816
466
  updateRecordIdentifier(identifierObject, data) {
@@ -830,7 +480,7 @@ class IdentifierCache {
830
480
  }
831
481
  }
832
482
  if (existingIdentifier) {
833
- let generatedIdentifier = identifier;
483
+ const generatedIdentifier = identifier;
834
484
  identifier = this._mergeRecordIdentifiers(keyInfo, generatedIdentifier, existingIdentifier, data);
835
485
 
836
486
  // make sure that the `lid` on the data we are processing matches the lid we kept
@@ -842,7 +492,7 @@ class IdentifierCache {
842
492
  console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
843
493
  }
844
494
  }
845
- let id = identifier.id;
495
+ const id = identifier.id;
846
496
  /*#__NOINLINE__*/
847
497
  performRecordIdentifierUpdate(identifier, keyInfo, data, this._update);
848
498
  const newId = identifier.id;
@@ -876,16 +526,32 @@ class IdentifierCache {
876
526
  const kept = this._merge(identifier, existingIdentifier, data);
877
527
  const abandoned = kept === identifier ? existingIdentifier : identifier;
878
528
 
529
+ // get any backreferences before forgetting this identifier, as it will be removed from the cache
530
+ // and we will no longer be able to find them
531
+ const abandonedBackReferences = this._cache.polymorphicLidBackMap.get(abandoned.lid);
532
+ // delete the backreferences for the abandoned identifier so that forgetRecordIdentifier
533
+ // does not try to remove them.
534
+ if (abandonedBackReferences) this._cache.polymorphicLidBackMap.delete(abandoned.lid);
535
+
879
536
  // cleanup the identifier we no longer need
880
537
  this.forgetRecordIdentifier(abandoned);
881
538
 
882
- // ensure a secondary cache entry for this id for the identifier we do keep
883
- // keyOptions.id.set(newId, kept);
539
+ // ensure a secondary cache entry for the original lid for the abandoned identifier
540
+ this._cache.resources.set(abandoned.lid, kept);
884
541
 
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);
542
+ // backReferences let us know which other identifiers are pointing at this identifier
543
+ // so we can delete them later if we forget this identifier
544
+ const keptBackReferences = this._cache.polymorphicLidBackMap.get(kept.lid) ?? [];
545
+ keptBackReferences.push(abandoned.lid);
888
546
 
547
+ // update the backreferences from the abandoned identifier to be for the kept identifier
548
+ if (abandonedBackReferences) {
549
+ abandonedBackReferences.forEach(lid => {
550
+ keptBackReferences.push(lid);
551
+ this._cache.resources.set(lid, kept);
552
+ });
553
+ }
554
+ this._cache.polymorphicLidBackMap.set(kept.lid, keptBackReferences);
889
555
  return kept;
890
556
  }
891
557
 
@@ -908,6 +574,13 @@ class IdentifierCache {
908
574
  }
909
575
  this._cache.resources.delete(identifier.lid);
910
576
  typeSet.lid.delete(identifier.lid);
577
+ const backReferences = this._cache.polymorphicLidBackMap.get(identifier.lid);
578
+ if (backReferences) {
579
+ backReferences.forEach(lid => {
580
+ this._cache.resources.delete(lid);
581
+ });
582
+ this._cache.polymorphicLidBackMap.delete(identifier.lid);
583
+ }
911
584
  if (macroCondition(getOwnConfig().env.DEBUG)) {
912
585
  identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
913
586
  }
@@ -953,17 +626,22 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
953
626
  },
954
627
  set [DEBUG_STALE_CACHE_OWNER](value) {
955
628
  recordIdentifier[DEBUG_STALE_CACHE_OWNER] = value;
956
- },
957
- // @ts-expect-error debug only
958
- toString() {
629
+ }
630
+ };
631
+ Object.defineProperty(wrapper, 'toString', {
632
+ enumerable: false,
633
+ value: () => {
959
634
  const {
960
635
  type,
961
636
  id,
962
637
  lid
963
638
  } = recordIdentifier;
964
639
  return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
965
- },
966
- toJSON() {
640
+ }
641
+ });
642
+ Object.defineProperty(wrapper, 'toJSON', {
643
+ enumerable: false,
644
+ value: () => {
967
645
  const {
968
646
  type,
969
647
  id,
@@ -975,7 +653,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
975
653
  lid
976
654
  };
977
655
  }
978
- };
656
+ });
979
657
  wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
980
658
  wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
981
659
  IDENTIFIERS.add(wrapper);
@@ -993,7 +671,7 @@ function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
993
671
  } = keyInfo;
994
672
 
995
673
  // get the mutable instance behind our proxy wrapper
996
- let wrapper = identifier;
674
+ const wrapper = identifier;
997
675
  identifier = DEBUG_MAP.get(wrapper);
998
676
  if (hasLid(data)) {
999
677
  const lid = data.lid;
@@ -1082,28 +760,27 @@ function addResourceToCache(cache, identifier) {
1082
760
  typeSet.id.set(identifier.id, identifier);
1083
761
  }
1084
762
  }
1085
- var _class$1, _descriptor$1;
1086
763
 
1087
764
  /**
1088
765
  @module @ember-data/store
1089
766
  */
767
+
1090
768
  /**
1091
769
  @module @ember-data/store
1092
770
  */
771
+
1093
772
  /**
1094
773
  A `RecordReference` is a low-level API that allows users and
1095
774
  addon authors to perform meta-operations on a record.
1096
775
 
1097
776
  @class RecordReference
1098
777
  @public
1099
- @extends Reference
1100
778
  */
1101
- let RecordReference = (_class$1 = class RecordReference {
779
+ class RecordReference {
1102
780
  constructor(store, identifier) {
1103
781
  // unsubscribe token given to us by the notification manager
1104
782
  this.___token = void 0;
1105
783
  this.___identifier = void 0;
1106
- _initializerDefineProperty(this, "_ref", _descriptor$1, this);
1107
784
  this.store = store;
1108
785
  this.___identifier = identifier;
1109
786
  this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
@@ -1271,14 +948,8 @@ let RecordReference = (_class$1 = class RecordReference {
1271
948
  }
1272
949
  assert(`Unable to fetch record of type ${this.type} without an id`);
1273
950
  }
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);
951
+ }
952
+ defineSignal(RecordReference.prototype, '_ref');
1282
953
 
1283
954
  /**
1284
955
  @module @ember-data/store
@@ -1309,6 +980,8 @@ class CacheCapabilitiesManager {
1309
980
  if (this._store._cbs) {
1310
981
  this._store._schedule('notify', () => this._flushNotifications());
1311
982
  } else {
983
+ // TODO @runspired determine if relationship mutations should schedule
984
+ // into join/run vs immediate flush
1312
985
  this._flushNotifications();
1313
986
  }
1314
987
  }
@@ -1316,7 +989,7 @@ class CacheCapabilitiesManager {
1316
989
  if (this._willNotify === false) {
1317
990
  return;
1318
991
  }
1319
- let pending = this._pendingNotifies;
992
+ const pending = this._pendingNotifies;
1320
993
  this._pendingNotifies = new Map();
1321
994
  this._willNotify = false;
1322
995
  pending.forEach((set, identifier) => {
@@ -1340,6 +1013,9 @@ class CacheCapabilitiesManager {
1340
1013
  getSchemaDefinitionService() {
1341
1014
  return this._store.getSchemaDefinitionService();
1342
1015
  }
1016
+ get schema() {
1017
+ return this._store.schema;
1018
+ }
1343
1019
  setRecordId(identifier, id) {
1344
1020
  assert(`Expected a stable identifier`, isStableIdentifier(identifier));
1345
1021
  this._store._instanceCache.setRecordId(identifier, id);
@@ -1373,6 +1049,9 @@ function peekCache(instance) {
1373
1049
  }
1374
1050
  return null;
1375
1051
  }
1052
+ function isDestroyable(record) {
1053
+ return Boolean(record && typeof record === 'object' && typeof record.destroy === 'function');
1054
+ }
1376
1055
 
1377
1056
  /**
1378
1057
  @module @ember-data/store
@@ -1400,8 +1079,9 @@ function peekRecordIdentifier(record) {
1400
1079
  @static
1401
1080
  @for @ember-data/store
1402
1081
  @param {Object} record a record instance previously obstained from the store.
1403
- @returns {StableRecordIdentifier}
1082
+ @return {StableRecordIdentifier}
1404
1083
  */
1084
+
1405
1085
  function recordIdentifierFor(record) {
1406
1086
  assert(`${String(record)} is not a record instantiated by @ember-data/store`, RecordCache.has(record));
1407
1087
  return RecordCache.get(record);
@@ -1447,11 +1127,11 @@ class InstanceCache {
1447
1127
  // @ts-expect-error TODO this needs to be fixed
1448
1128
  'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
1449
1129
  }
1450
- let staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1130
+ const staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
1451
1131
 
1452
1132
  // check for duplicate entities
1453
- let keptHasRecord = this.__instances.record.has(keptIdentifier);
1454
- let staleHasRecord = this.__instances.record.has(staleIdentifier);
1133
+ const keptHasRecord = this.__instances.record.has(keptIdentifier);
1134
+ const staleHasRecord = this.__instances.record.has(staleIdentifier);
1455
1135
 
1456
1136
  // we cannot merge entities when both have records
1457
1137
  // (this may not be strictly true, we could probably swap the cache data the record points at)
@@ -1505,7 +1185,7 @@ class InstanceCache {
1505
1185
  return record;
1506
1186
  }
1507
1187
  getReference(identifier) {
1508
- let cache = this.__instances.reference;
1188
+ const cache = this.__instances.reference;
1509
1189
  let reference = cache.get(identifier);
1510
1190
  if (!reference) {
1511
1191
  reference = new RecordReference(this.store, identifier);
@@ -1536,7 +1216,7 @@ class InstanceCache {
1536
1216
  }
1537
1217
  disconnect(identifier) {
1538
1218
  const record = this.__instances.record.get(identifier);
1539
- assert('Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying);
1219
+ assert('Cannot destroy record while it is still materialized', !isDestroyable(record) || record.isDestroyed || record.isDestroying);
1540
1220
  this.store._graph?.remove(identifier);
1541
1221
  this.store.identifierCache.forgetRecordIdentifier(identifier);
1542
1222
  removeRecordDataFor(identifier);
@@ -1604,7 +1284,7 @@ class InstanceCache {
1604
1284
  });
1605
1285
  } else {
1606
1286
  const typeCache = cache.resourcesByType;
1607
- let identifiers = typeCache[type]?.lid;
1287
+ const identifiers = typeCache[type]?.lid;
1608
1288
  if (identifiers) {
1609
1289
  identifiers.forEach(identifier => {
1610
1290
  // if (rds.has(identifier)) {
@@ -1622,7 +1302,7 @@ class InstanceCache {
1622
1302
  type,
1623
1303
  lid
1624
1304
  } = identifier;
1625
- let oldId = identifier.id;
1305
+ const oldId = identifier.id;
1626
1306
 
1627
1307
  // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
1628
1308
  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 +1321,7 @@ class InstanceCache {
1641
1321
  // eslint-disable-next-line no-console
1642
1322
  console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
1643
1323
  }
1644
- let existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1324
+ const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1645
1325
  type,
1646
1326
  id
1647
1327
  });
@@ -1680,13 +1360,13 @@ function resourceIsFullyDeleted(instanceCache, identifier) {
1680
1360
  */
1681
1361
 
1682
1362
  function preloadData(store, identifier, preload) {
1683
- let jsonPayload = {};
1363
+ const jsonPayload = {};
1684
1364
  //TODO(Igor) consider the polymorphic case
1685
1365
  const schemas = store.getSchemaDefinitionService();
1686
1366
  const relationships = schemas.relationshipsDefinitionFor(identifier);
1687
1367
  Object.keys(preload).forEach(key => {
1688
- let preloadValue = preload[key];
1689
- let relationshipMeta = relationships[key];
1368
+ const preloadValue = preload[key];
1369
+ const relationshipMeta = relationships[key];
1690
1370
  if (relationshipMeta) {
1691
1371
  if (!jsonPayload.relationships) {
1692
1372
  jsonPayload.relationships = {};
@@ -1755,7 +1435,7 @@ function getShimClass(store, modelName) {
1755
1435
  }
1756
1436
  function mapFromHash(hash) {
1757
1437
  const map = new Map();
1758
- for (let i in hash) {
1438
+ for (const i in hash) {
1759
1439
  if (Object.prototype.hasOwnProperty.call(hash, i)) {
1760
1440
  map.set(i, hash[i]);
1761
1441
  }
@@ -1770,31 +1450,31 @@ class ShimModelClass {
1770
1450
  this.modelName = modelName;
1771
1451
  }
1772
1452
  get fields() {
1773
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1453
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1774
1454
  type: this.modelName
1775
1455
  });
1776
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1456
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1777
1457
  type: this.modelName
1778
1458
  });
1779
- let fields = new Map();
1459
+ const fields = new Map();
1780
1460
  Object.keys(attrs).forEach(key => fields.set(key, 'attribute'));
1781
1461
  Object.keys(relationships).forEach(key => fields.set(key, relationships[key].kind));
1782
1462
  return fields;
1783
1463
  }
1784
1464
  get attributes() {
1785
- let attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1465
+ const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1786
1466
  type: this.modelName
1787
1467
  });
1788
1468
  return mapFromHash(attrs);
1789
1469
  }
1790
1470
  get relationshipsByName() {
1791
- let relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1471
+ const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1792
1472
  type: this.modelName
1793
1473
  });
1794
1474
  return mapFromHash(relationships);
1795
1475
  }
1796
1476
  eachAttribute(callback, binding) {
1797
- let attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1477
+ const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
1798
1478
  type: this.modelName
1799
1479
  });
1800
1480
  Object.keys(attrDefs).forEach(key => {
@@ -1802,7 +1482,7 @@ class ShimModelClass {
1802
1482
  });
1803
1483
  }
1804
1484
  eachRelationship(callback, binding) {
1805
- let relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1485
+ const relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
1806
1486
  type: this.modelName
1807
1487
  });
1808
1488
  Object.keys(relationshipDefs).forEach(key => {
@@ -1820,6 +1500,16 @@ class ShimModelClass {
1820
1500
  });
1821
1501
  }
1822
1502
  }
1503
+ function _classPrivateFieldBase(receiver, privateKey) {
1504
+ if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
1505
+ throw new TypeError("attempted to use private field on non-instance");
1506
+ }
1507
+ return receiver;
1508
+ }
1509
+ var id = 0;
1510
+ function _classPrivateFieldKey(name) {
1511
+ return "__private_" + id++ + "_" + name;
1512
+ }
1823
1513
  var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
1824
1514
  /**
1825
1515
  * The CacheManager wraps a Cache enforcing that only
@@ -1846,16 +1536,6 @@ class CacheManager {
1846
1536
  writable: true,
1847
1537
  value: void 0
1848
1538
  });
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
1539
  _classPrivateFieldBase(this, _cache)[_cache] = cache;
1860
1540
  }
1861
1541
 
@@ -1869,7 +1549,7 @@ class CacheManager {
1869
1549
  * semantics, `put` has `replace` semantics similar to
1870
1550
  * the `http` method `PUT`
1871
1551
  *
1872
- * the individually cacheabl
1552
+ * the individually cacheable
1873
1553
  * e resource data it may contain
1874
1554
  * should upsert, but the document data surrounding it should
1875
1555
  * fully replace any existing information
@@ -1882,7 +1562,7 @@ class CacheManager {
1882
1562
  *
1883
1563
  * @method put
1884
1564
  * @param {StructuredDocument} doc
1885
- * @returns {ResourceDocument}
1565
+ * @return {ResourceDocument}
1886
1566
  * @public
1887
1567
  */
1888
1568
  put(doc) {
@@ -1898,7 +1578,7 @@ class CacheManager {
1898
1578
  * @method patch
1899
1579
  * @public
1900
1580
  * @param op the operation to perform
1901
- * @returns {void}
1581
+ * @return {void}
1902
1582
  */
1903
1583
  patch(op) {
1904
1584
  _classPrivateFieldBase(this, _cache)[_cache].patch(op);
@@ -1933,7 +1613,7 @@ class CacheManager {
1933
1613
  * An implementation might want to do this because
1934
1614
  * de-referencing records which read from their own
1935
1615
  * blob is generally safer because the record does
1936
- * not require retainining connections to the Store
1616
+ * not require retaining connections to the Store
1937
1617
  * and Cache to present data on a per-field basis.
1938
1618
  *
1939
1619
  * This generally takes the place of `getAttr` as
@@ -1946,7 +1626,7 @@ class CacheManager {
1946
1626
  * @method peek
1947
1627
  * @public
1948
1628
  * @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
1949
- * @returns {ResourceDocument | ResourceBlob | null} the known resource data
1629
+ * @return {ResourceDocument | ResourceBlob | null} the known resource data
1950
1630
  */
1951
1631
 
1952
1632
  peek(identifier) {
@@ -1959,7 +1639,7 @@ class CacheManager {
1959
1639
  *
1960
1640
  * @method peekRequest
1961
1641
  * @param {StableDocumentIdentifier}
1962
- * @returns {StableDocumentIdentifier | null}
1642
+ * @return {StableDocumentIdentifier | null}
1963
1643
  * @public
1964
1644
  */
1965
1645
  peekRequest(identifier) {
@@ -1974,7 +1654,7 @@ class CacheManager {
1974
1654
  * @param identifier
1975
1655
  * @param data
1976
1656
  * @param hasRecord
1977
- * @returns {void | string[]} if `hasRecord` is true then calculated key changes should be returned
1657
+ * @return {void | string[]} if `hasRecord` is true then calculated key changes should be returned
1978
1658
  */
1979
1659
  upsert(identifier, data, hasRecord) {
1980
1660
  return _classPrivateFieldBase(this, _cache)[_cache].upsert(identifier, data, hasRecord);
@@ -1992,7 +1672,7 @@ class CacheManager {
1992
1672
  *
1993
1673
  * @method fork
1994
1674
  * @public
1995
- * @returns Promise<Cache>
1675
+ * @return Promise<Cache>
1996
1676
  */
1997
1677
  fork() {
1998
1678
  return _classPrivateFieldBase(this, _cache)[_cache].fork();
@@ -2008,7 +1688,7 @@ class CacheManager {
2008
1688
  * @method merge
2009
1689
  * @param {Cache} cache
2010
1690
  * @public
2011
- * @returns Promise<void>
1691
+ * @return Promise<void>
2012
1692
  */
2013
1693
  merge(cache) {
2014
1694
  return _classPrivateFieldBase(this, _cache)[_cache].merge(cache);
@@ -2060,7 +1740,7 @@ class CacheManager {
2060
1740
  * via `cache.hydrate`.
2061
1741
  *
2062
1742
  * @method dump
2063
- * @returns {Promise<ReadableStream>}
1743
+ * @return {Promise<ReadableStream>}
2064
1744
  * @public
2065
1745
  */
2066
1746
  dump() {
@@ -2081,7 +1761,7 @@ class CacheManager {
2081
1761
  *
2082
1762
  * @method hydrate
2083
1763
  * @param {ReadableStream} stream
2084
- * @returns {Promise<void>}
1764
+ * @return {Promise<void>}
2085
1765
  * @public
2086
1766
  */
2087
1767
  hydrate(stream) {
@@ -2095,7 +1775,7 @@ class CacheManager {
2095
1775
  // ================
2096
1776
 
2097
1777
  /**
2098
- * [LIFECYLCE] Signal to the cache that a new record has been instantiated on the client
1778
+ * [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client
2099
1779
  *
2100
1780
  * It returns properties from options that should be set on the record during the create
2101
1781
  * process. This return value behavior is deprecated.
@@ -2169,7 +1849,7 @@ class CacheManager {
2169
1849
  * @public
2170
1850
  * @param identifier
2171
1851
  * @param propertyName
2172
- * @returns {unknown}
1852
+ * @return {unknown}
2173
1853
  */
2174
1854
  getAttr(identifier, propertyName) {
2175
1855
  return _classPrivateFieldBase(this, _cache)[_cache].getAttr(identifier, propertyName);
@@ -2194,7 +1874,7 @@ class CacheManager {
2194
1874
  * @method changedAttrs
2195
1875
  * @public
2196
1876
  * @param identifier
2197
- * @returns
1877
+ * @return
2198
1878
  */
2199
1879
  changedAttrs(identifier) {
2200
1880
  return _classPrivateFieldBase(this, _cache)[_cache].changedAttrs(identifier);
@@ -2206,7 +1886,7 @@ class CacheManager {
2206
1886
  * @method hasChangedAttrs
2207
1887
  * @public
2208
1888
  * @param identifier
2209
- * @returns {boolean}
1889
+ * @return {boolean}
2210
1890
  */
2211
1891
  hasChangedAttrs(identifier) {
2212
1892
  return _classPrivateFieldBase(this, _cache)[_cache].hasChangedAttrs(identifier);
@@ -2218,7 +1898,7 @@ class CacheManager {
2218
1898
  * @method rollbackAttrs
2219
1899
  * @public
2220
1900
  * @param identifier
2221
- * @returns the names of attributes that were restored
1901
+ * @return the names of attributes that were restored
2222
1902
  */
2223
1903
  rollbackAttrs(identifier) {
2224
1904
  return _classPrivateFieldBase(this, _cache)[_cache].rollbackAttrs(identifier);
@@ -2227,6 +1907,65 @@ class CacheManager {
2227
1907
  // Relationships
2228
1908
  // =============
2229
1909
 
1910
+ /**
1911
+ * Query the cache for the changes to relationships of a resource.
1912
+ *
1913
+ * Returns a map of relationship names to RelationshipDiff objects.
1914
+ *
1915
+ * ```ts
1916
+ * type RelationshipDiff =
1917
+ | {
1918
+ kind: 'collection';
1919
+ remoteState: StableRecordIdentifier[];
1920
+ additions: Set<StableRecordIdentifier>;
1921
+ removals: Set<StableRecordIdentifier>;
1922
+ localState: StableRecordIdentifier[];
1923
+ reordered: boolean;
1924
+ }
1925
+ | {
1926
+ kind: 'resource';
1927
+ remoteState: StableRecordIdentifier | null;
1928
+ localState: StableRecordIdentifier | null;
1929
+ };
1930
+ ```
1931
+ *
1932
+ * @method changedRelationships
1933
+ * @public
1934
+ * @param {StableRecordIdentifier} identifier
1935
+ * @return {Map<string, RelationshipDiff>}
1936
+ */
1937
+ changedRelationships(identifier) {
1938
+ return _classPrivateFieldBase(this, _cache)[_cache].changedRelationships(identifier);
1939
+ }
1940
+
1941
+ /**
1942
+ * Query the cache for whether any mutated attributes exist
1943
+ *
1944
+ * @method hasChangedRelationships
1945
+ * @public
1946
+ * @param {StableRecordIdentifier} identifier
1947
+ * @return {boolean}
1948
+ */
1949
+ hasChangedRelationships(identifier) {
1950
+ return _classPrivateFieldBase(this, _cache)[_cache].hasChangedRelationships(identifier);
1951
+ }
1952
+
1953
+ /**
1954
+ * Tell the cache to discard any uncommitted mutations to relationships.
1955
+ *
1956
+ * This will also discard the change on any appropriate inverses.
1957
+ *
1958
+ * This method is a candidate to become a mutation
1959
+ *
1960
+ * @method rollbackRelationships
1961
+ * @public
1962
+ * @param {StableRecordIdentifier} identifier
1963
+ * @return {string[]} the names of relationships that were restored
1964
+ */
1965
+ rollbackRelationships(identifier) {
1966
+ return _classPrivateFieldBase(this, _cache)[_cache].rollbackRelationships(identifier);
1967
+ }
1968
+
2230
1969
  /**
2231
1970
  * Query the cache for the current state of a relationship property
2232
1971
  *
@@ -2234,7 +1973,7 @@ class CacheManager {
2234
1973
  * @public
2235
1974
  * @param identifier
2236
1975
  * @param propertyName
2237
- * @returns resource relationship object
1976
+ * @return resource relationship object
2238
1977
  */
2239
1978
  getRelationship(identifier, propertyName) {
2240
1979
  return _classPrivateFieldBase(this, _cache)[_cache].getRelationship(identifier, propertyName);
@@ -2262,7 +2001,7 @@ class CacheManager {
2262
2001
  * @method getErrors
2263
2002
  * @public
2264
2003
  * @param identifier
2265
- * @returns
2004
+ * @return
2266
2005
  */
2267
2006
  getErrors(identifier) {
2268
2007
  return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier);
@@ -2274,7 +2013,7 @@ class CacheManager {
2274
2013
  * @method isEmpty
2275
2014
  * @public
2276
2015
  * @param identifier
2277
- * @returns {boolean}
2016
+ * @return {boolean}
2278
2017
  */
2279
2018
  isEmpty(identifier) {
2280
2019
  return _classPrivateFieldBase(this, _cache)[_cache].isEmpty(identifier);
@@ -2287,7 +2026,7 @@ class CacheManager {
2287
2026
  * @method isNew
2288
2027
  * @public
2289
2028
  * @param identifier
2290
- * @returns {boolean}
2029
+ * @return {boolean}
2291
2030
  */
2292
2031
  isNew(identifier) {
2293
2032
  return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier);
@@ -2300,15 +2039,29 @@ class CacheManager {
2300
2039
  * @method isDeleted
2301
2040
  * @public
2302
2041
  * @param identifier
2303
- * @returns {boolean}
2042
+ * @return {boolean}
2304
2043
  */
2305
2044
  isDeleted(identifier) {
2306
2045
  return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier);
2307
2046
  }
2047
+
2048
+ /**
2049
+ * Query the cache for whether a given resource has been deleted and that deletion
2050
+ * has also been persisted.
2051
+ *
2052
+ * @method isDeletionCommitted
2053
+ * @public
2054
+ * @param identifier
2055
+ * @return {boolean}
2056
+ */
2308
2057
  isDeletionCommitted(identifier) {
2309
2058
  return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier);
2310
2059
  }
2311
2060
  }
2061
+
2062
+ /**
2063
+ * @module @ember-data/store
2064
+ */
2312
2065
  let tokenId = 0;
2313
2066
  const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
2314
2067
  function isCacheOperationValue(value) {
@@ -2319,7 +2072,7 @@ function runLoopIsFlushing() {
2319
2072
  return !!_backburner.currentInstance && _backburner._autorun !== true;
2320
2073
  }
2321
2074
  function _unsubscribe(tokens, token, cache) {
2322
- let identifier = tokens.get(token);
2075
+ const identifier = tokens.get(token);
2323
2076
  if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
2324
2077
  if (!identifier) {
2325
2078
  // eslint-disable-next-line no-console
@@ -2378,7 +2131,7 @@ class NotificationManager {
2378
2131
  * @public
2379
2132
  * @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
2380
2133
  * @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
2381
- * @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
2134
+ * @return {UnsubscribeToken} an opaque token to be used with unsubscribe
2382
2135
  */
2383
2136
 
2384
2137
  subscribe(identifier, callback) {
@@ -2388,7 +2141,7 @@ class NotificationManager {
2388
2141
  map = new Map();
2389
2142
  this._cache.set(identifier, map);
2390
2143
  }
2391
- let unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2144
+ const unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
2392
2145
  _tokenRef: tokenId++
2393
2146
  } : {};
2394
2147
  map.set(unsubToken, callback);
@@ -2441,7 +2194,7 @@ class NotificationManager {
2441
2194
  this._buffered.set(identifier, buffer);
2442
2195
  }
2443
2196
  buffer.push([value, key]);
2444
- void this._scheduleNotify();
2197
+ this._scheduleNotify();
2445
2198
  }
2446
2199
  return hasSubscribers;
2447
2200
  }
@@ -2483,14 +2236,14 @@ class NotificationManager {
2483
2236
 
2484
2237
  // TODO for documents this will need to switch based on Identifier kind
2485
2238
  if (isCacheOperationValue(value)) {
2486
- let callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2239
+ const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
2487
2240
  if (callbackMap) {
2488
2241
  callbackMap.forEach(cb => {
2489
2242
  cb(identifier, value);
2490
2243
  });
2491
2244
  }
2492
2245
  }
2493
- let callbackMap = this._cache.get(identifier);
2246
+ const callbackMap = this._cache.get(identifier);
2494
2247
  if (!callbackMap || !callbackMap.size) {
2495
2248
  return false;
2496
2249
  }
@@ -2500,13 +2253,46 @@ class NotificationManager {
2500
2253
  });
2501
2254
  return true;
2502
2255
  }
2503
- destroy() {
2504
- this.isDestroyed = true;
2505
- this._tokens.clear();
2506
- this._cache.clear();
2256
+ destroy() {
2257
+ this.isDestroyed = true;
2258
+ this._tokens.clear();
2259
+ this._cache.clear();
2260
+ }
2261
+ }
2262
+ function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
2263
+ var desc = {};
2264
+ Object.keys(descriptor).forEach(function (key) {
2265
+ desc[key] = descriptor[key];
2266
+ });
2267
+ desc.enumerable = !!desc.enumerable;
2268
+ desc.configurable = !!desc.configurable;
2269
+ if ('value' in desc || desc.initializer) {
2270
+ desc.writable = true;
2271
+ }
2272
+ desc = decorators.slice().reverse().reduce(function (desc, decorator) {
2273
+ return decorator(target, property, desc) || desc;
2274
+ }, desc);
2275
+ if (context && desc.initializer !== void 0) {
2276
+ desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
2277
+ desc.initializer = undefined;
2278
+ }
2279
+ if (desc.initializer === void 0) {
2280
+ Object.defineProperty(target, property, desc);
2281
+ desc = null;
2507
2282
  }
2283
+ return desc;
2508
2284
  }
2509
- var _class, _descriptor, _class3, _descriptor2;
2285
+
2286
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2287
+ /*
2288
+ We redefine Proxy because the native Proxy type treats the `target` and
2289
+ `receiver` as the same type incorrectly.
2290
+
2291
+ We ported this from Typescript's own Proxy types on 3/10/2024.
2292
+ */
2293
+
2294
+ const NativeProxy = Proxy;
2295
+ var _class;
2510
2296
  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
2297
  const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
2512
2298
  const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
@@ -2516,19 +2302,16 @@ function isArrayGetter(prop) {
2516
2302
  function isArraySetter(prop) {
2517
2303
  return ARRAY_SETTER_METHODS.has(prop);
2518
2304
  }
2519
- const IDENTIFIER_ARRAY_TAG = Symbol('#tag');
2305
+ function isSelfProp(self, prop) {
2306
+ return prop in self;
2307
+ }
2308
+ const ARRAY_SIGNAL = Symbol('#signal');
2520
2309
  const SOURCE = Symbol('#source');
2521
2310
  const MUTATE = Symbol('#update');
2522
2311
  const NOTIFY = Symbol('#notify');
2523
2312
  const IS_COLLECTION = Symbol.for('Collection');
2524
2313
  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
- }
2314
+ addToTransaction(arr[ARRAY_SIGNAL]);
2532
2315
  }
2533
2316
  function convertToInt(prop) {
2534
2317
  if (typeof prop === 'symbol') return null;
@@ -2536,29 +2319,6 @@ function convertToInt(prop) {
2536
2319
  if (isNaN(num)) return null;
2537
2320
  return num % 1 === 0 ? num : null;
2538
2321
  }
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
2322
  function safeForEach(instance, arr, store, callback, target) {
2563
2323
  if (target === undefined) {
2564
2324
  target = null;
@@ -2591,7 +2351,7 @@ function safeForEach(instance, arr, store, callback, target) {
2591
2351
  @class RecordArray
2592
2352
  @public
2593
2353
  */
2594
- let IdentifierArray = (_class3 = class IdentifierArray {
2354
+ let IdentifierArray = (_class = class IdentifierArray {
2595
2355
  [NOTIFY]() {
2596
2356
  notifyArray(this);
2597
2357
  }
@@ -2619,14 +2379,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2619
2379
  set length(value) {
2620
2380
  this[SOURCE].length = value;
2621
2381
  }
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
2382
  constructor(options) {
2631
2383
  /**
2632
2384
  The flag to signal a `RecordArray` is currently loading data.
@@ -2641,7 +2393,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2641
2393
  @public
2642
2394
  @type Boolean
2643
2395
  */
2644
- _initializerDefineProperty(this, "isUpdating", _descriptor2, this);
2645
2396
  this.isLoaded = true;
2646
2397
  this.isDestroying = false;
2647
2398
  this.isDestroyed = false;
@@ -2649,16 +2400,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2649
2400
  this[IS_COLLECTION] = true;
2650
2401
  this[SOURCE] = void 0;
2651
2402
  // eslint-disable-next-line @typescript-eslint/no-this-alias
2652
- let self = this;
2403
+ const self = this;
2653
2404
  this.modelName = options.type;
2654
2405
  this.store = options.store;
2655
2406
  this._manager = options.manager;
2656
2407
  this[SOURCE] = options.identifiers;
2657
- // @ts-expect-error
2658
- this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
2408
+ this[ARRAY_SIGNAL] = createSignal(this, 'length');
2659
2409
  const store = options.store;
2660
2410
  const boundFns = new Map();
2661
- const _TAG = this[IDENTIFIER_ARRAY_TAG];
2411
+ const _SIGNAL = this[ARRAY_SIGNAL];
2662
2412
  const PrivateState = {
2663
2413
  links: options.links || null,
2664
2414
  meta: options.meta || null
@@ -2669,42 +2419,42 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2669
2419
  // we track all mutations within the call
2670
2420
  // and forward them as one
2671
2421
 
2672
- const proxy = new Proxy(this[SOURCE], {
2422
+ const proxy = new NativeProxy(this[SOURCE], {
2673
2423
  get(target, prop, receiver) {
2674
- let index = convertToInt(prop);
2675
- if (_TAG.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2424
+ const index = convertToInt(prop);
2425
+ if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
2676
2426
  options.manager._syncArray(receiver);
2677
- _TAG.t = false;
2678
- _TAG.shouldReset = false;
2427
+ _SIGNAL.t = false;
2428
+ _SIGNAL.shouldReset = false;
2679
2429
  }
2680
2430
  if (index !== null) {
2681
2431
  const identifier = target[index];
2682
2432
  if (!transaction) {
2683
- subscribe(_TAG);
2433
+ subscribe(_SIGNAL);
2684
2434
  }
2685
2435
  return identifier && store._instanceCache.getRecord(identifier);
2686
2436
  }
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;
2437
+ if (prop === 'meta') return subscribe(_SIGNAL), PrivateState.meta;
2438
+ if (prop === 'links') return subscribe(_SIGNAL), PrivateState.links;
2439
+ if (prop === '[]') return subscribe(_SIGNAL), receiver;
2690
2440
  if (isArrayGetter(prop)) {
2691
2441
  let fn = boundFns.get(prop);
2692
2442
  if (fn === undefined) {
2693
2443
  if (prop === 'forEach') {
2694
2444
  fn = function () {
2695
- subscribe(_TAG);
2445
+ subscribe(_SIGNAL);
2696
2446
  transaction = true;
2697
- let result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2447
+ const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
2698
2448
  transaction = false;
2699
2449
  return result;
2700
2450
  };
2701
2451
  } else {
2702
2452
  fn = function () {
2703
- subscribe(_TAG);
2453
+ subscribe(_SIGNAL);
2704
2454
  // array functions must run through Reflect to work properly
2705
2455
  // binding via other means will not work.
2706
2456
  transaction = true;
2707
- let result = Reflect.apply(target[prop], receiver, arguments);
2457
+ const result = Reflect.apply(target[prop], receiver, arguments);
2708
2458
  transaction = false;
2709
2459
  return result;
2710
2460
  };
@@ -2726,10 +2476,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2726
2476
  const args = Array.prototype.slice.call(arguments);
2727
2477
  assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
2728
2478
  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
2479
+ const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
2733
2480
  transaction = false;
2734
2481
  return result;
2735
2482
  };
@@ -2737,16 +2484,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2737
2484
  }
2738
2485
  return fn;
2739
2486
  }
2740
- if (prop in self) {
2741
- if (prop === NOTIFY || prop === IDENTIFIER_ARRAY_TAG || prop === SOURCE) {
2487
+ if (isSelfProp(self, prop)) {
2488
+ if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
2742
2489
  return self[prop];
2743
2490
  }
2744
2491
  let fn = boundFns.get(prop);
2745
2492
  if (fn) return fn;
2746
- let outcome = self[prop];
2493
+ const outcome = self[prop];
2747
2494
  if (typeof outcome === 'function') {
2748
2495
  fn = function () {
2749
- subscribe(_TAG);
2496
+ subscribe(_SIGNAL);
2750
2497
  // array functions must run through Reflect to work properly
2751
2498
  // binding via other means will not work.
2752
2499
  return Reflect.apply(outcome, receiver, arguments);
@@ -2754,17 +2501,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2754
2501
  boundFns.set(prop, fn);
2755
2502
  return fn;
2756
2503
  }
2757
- return subscribe(_TAG), outcome;
2504
+ return subscribe(_SIGNAL), outcome;
2758
2505
  }
2759
2506
  return target[prop];
2760
2507
  },
2761
- set(target, prop, value) {
2508
+ // FIXME: Should this get a generic like get above?
2509
+ set(target, prop, value, receiver) {
2762
2510
  if (prop === 'length') {
2763
2511
  if (!transaction && value === 0) {
2764
2512
  transaction = true;
2765
- addToTransaction(_TAG);
2766
- Reflect.set(target, prop, value);
2767
- self[MUTATE]('length 0', []);
2513
+ self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
2768
2514
  transaction = false;
2769
2515
  return true;
2770
2516
  } else if (transaction) {
@@ -2781,9 +2527,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2781
2527
  PrivateState.meta = value || null;
2782
2528
  return true;
2783
2529
  }
2784
- let index = convertToInt(prop);
2530
+ const index = convertToInt(prop);
2531
+
2532
+ // we do not allow "holey" arrays and so if the index is
2533
+ // greater than length then we will disallow setting it.
2534
+ // however, there is a special case for "unshift" with more than
2535
+ // one item being inserted since current items will be moved to the
2536
+ // new indices first.
2537
+ // we "loosely" detect this by just checking whether we are in
2538
+ // a transaction.
2785
2539
  if (index === null || index > target.length) {
2786
- if (prop in self) {
2540
+ if (index !== null && transaction) {
2541
+ const identifier = recordIdentifierFor(value);
2542
+ assert(`Cannot set index ${index} past the end of the array.`, isStableIdentifier(identifier));
2543
+ target[index] = identifier;
2544
+ return true;
2545
+ } else if (isSelfProp(self, prop)) {
2546
+ // @ts-expect-error not all properties are indeces and we can't safely cast
2787
2547
  self[prop] = value;
2788
2548
  return true;
2789
2549
  }
@@ -2793,12 +2553,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2793
2553
  assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
2794
2554
  return false;
2795
2555
  }
2796
- let original = target[index];
2797
- let newIdentifier = extractIdentifierFromRecord$1(value);
2556
+ const original = target[index];
2557
+ const newIdentifier = extractIdentifierFromRecord$1(value);
2798
2558
  target[index] = newIdentifier;
2559
+ assert(`Expected a record`, isStableIdentifier(newIdentifier));
2560
+ // We generate "transactions" whenever a setter method on the array
2561
+ // is called and might bulk update multiple array cells. Fundamentally,
2562
+ // all array operations decompose into individual cell replacements.
2563
+ // e.g. a push is really a "replace cell at next index with new value"
2564
+ // or a splice is "shift all values left/right by X and set out of new
2565
+ // bounds cells to undefined"
2566
+ //
2567
+ // so, if we are in a transaction, then this is not a user generated change
2568
+ // but one generated by a setter method. In this case we want to only apply
2569
+ // the change to the target array and not call the MUTATE method.
2570
+ // If there is no transaction though, then this means the user themselves has
2571
+ // directly changed the value of a specific index and we need to thus generate
2572
+ // a mutation for that change.
2573
+ // e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
2574
+ // a transaction.
2575
+ // while "arr[arr.length] = newVal;" is handled by this replace cell code path.
2799
2576
  if (!transaction) {
2800
- self[MUTATE]('replace cell', [index, original, newIdentifier]);
2801
- addToTransaction(_TAG);
2577
+ self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
2578
+ } else {
2579
+ target[index] = newIdentifier;
2802
2580
  }
2803
2581
  return true;
2804
2582
  },
@@ -2813,12 +2591,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2813
2591
  return IdentifierArray.prototype;
2814
2592
  }
2815
2593
  });
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
- }
2594
+ createArrayTags(proxy, _SIGNAL);
2822
2595
  this[NOTIFY] = this[NOTIFY].bind(proxy);
2823
2596
  return proxy;
2824
2597
  }
@@ -2843,7 +2616,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2843
2616
  return this._updatingPromise;
2844
2617
  }
2845
2618
  this.isUpdating = true;
2846
- let updatingPromise = this._update();
2619
+ const updatingPromise = this._update();
2847
2620
  void updatingPromise.finally(() => {
2848
2621
  this._updatingPromise = null;
2849
2622
  if (this.isDestroying || this.isDestroyed) {
@@ -2861,6 +2634,10 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2861
2634
  */
2862
2635
  _update() {
2863
2636
  assert(`_update cannot be used with this array`, this.modelName);
2637
+ // @ts-expect-error typescript is unable to handle the complexity of
2638
+ // T = unknown, modelName = string
2639
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
2640
+ // both being valid options to pass through here.
2864
2641
  return this.store.findAll(this.modelName, {
2865
2642
  reload: true
2866
2643
  });
@@ -2882,17 +2659,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
2882
2659
  @return {Promise<IdentifierArray>} promise
2883
2660
  */
2884
2661
  save() {
2885
- let promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2662
+ const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
2886
2663
  return promise;
2887
2664
  }
2888
- }, (_descriptor2 = _applyDecoratedDescriptor(_class3.prototype, "isUpdating", [tracked], {
2889
- configurable: true,
2665
+ }, _applyDecoratedDescriptor(_class.prototype, "length", [compat], Object.getOwnPropertyDescriptor(_class.prototype, "length"), _class.prototype), _class); // this will error if someone tries to call
2666
+ // A(identifierArray) since it is not configurable
2667
+ // which is preferable to the `meta` override we used
2668
+ // before which required importing all of Ember
2669
+ const desc = {
2890
2670
  enumerable: true,
2891
- writable: true,
2892
- initializer: function () {
2893
- return false;
2671
+ configurable: false,
2672
+ get: function () {
2673
+ return this;
2894
2674
  }
2895
- }), _applyDecoratedDescriptor(_class3.prototype, "length", [dependentKeyCompat], Object.getOwnPropertyDescriptor(_class3.prototype, "length"), _class3.prototype)), _class3);
2675
+ };
2676
+ compat(desc);
2677
+ Object.defineProperty(IdentifierArray.prototype, '[]', desc);
2678
+ defineSignal(IdentifierArray.prototype, 'isUpdating', false);
2896
2679
  class Collection extends IdentifierArray {
2897
2680
  constructor(options) {
2898
2681
  super(options);
@@ -2909,6 +2692,10 @@ class Collection extends IdentifierArray {
2909
2692
  // TODO save options from initial request?
2910
2693
  assert(`update cannot be used with this array`, this.modelName);
2911
2694
  assert(`update cannot be used with no query`, query);
2695
+ // @ts-expect-error typescript is unable to handle the complexity of
2696
+ // T = unknown, modelName = string
2697
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
2698
+ // both being valid options to pass through here.
2912
2699
  const promise = store.query(this.modelName, query, {
2913
2700
  _recordArray: this
2914
2701
  });
@@ -2924,7 +2711,8 @@ class Collection extends IdentifierArray {
2924
2711
  Collection.prototype.query = null;
2925
2712
 
2926
2713
  // Ensure instanceof works correctly
2927
- //Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2714
+ // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2715
+
2928
2716
  function assertRecordPassedToHasMany(record) {
2929
2717
  assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
2930
2718
  try {
@@ -2946,7 +2734,6 @@ function extractIdentifierFromRecord$1(record) {
2946
2734
  /**
2947
2735
  @module @ember-data/store
2948
2736
  */
2949
-
2950
2737
  const FAKE_ARR = {};
2951
2738
  const SLICE_BATCH_SIZE = 1200;
2952
2739
  /**
@@ -2989,7 +2776,7 @@ const SLICE_BATCH_SIZE = 1200;
2989
2776
  */
2990
2777
  function fastPush(target, source) {
2991
2778
  let startLength = 0;
2992
- let newLength = source.length;
2779
+ const newLength = source.length;
2993
2780
  while (newLength - startLength > SLICE_BATCH_SIZE) {
2994
2781
  // eslint-disable-next-line prefer-spread
2995
2782
  target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
@@ -3046,8 +2833,8 @@ class RecordArrayManager {
3046
2833
  */
3047
2834
  liveArrayFor(type) {
3048
2835
  let array = this._live.get(type);
3049
- let identifiers = [];
3050
- let staged = this._staged.get(type);
2836
+ const identifiers = [];
2837
+ const staged = this._staged.get(type);
3051
2838
  if (staged) {
3052
2839
  staged.forEach((value, key) => {
3053
2840
  if (value === 'add') {
@@ -3070,7 +2857,7 @@ class RecordArrayManager {
3070
2857
  return array;
3071
2858
  }
3072
2859
  createArray(config) {
3073
- let options = {
2860
+ const options = {
3074
2861
  type: config.type,
3075
2862
  links: config.doc?.links || null,
3076
2863
  meta: config.doc?.meta || null,
@@ -3081,7 +2868,7 @@ class RecordArrayManager {
3081
2868
  store: this.store,
3082
2869
  manager: this
3083
2870
  };
3084
- let array = new Collection(options);
2871
+ const array = new Collection(options);
3085
2872
  this._managed.add(array);
3086
2873
  this._set.set(array, new Set(options.identifiers || []));
3087
2874
  if (config.identifiers) {
@@ -3093,7 +2880,7 @@ class RecordArrayManager {
3093
2880
  if (array === FAKE_ARR) {
3094
2881
  return;
3095
2882
  }
3096
- let tag = array[IDENTIFIER_ARRAY_TAG];
2883
+ const tag = array[ARRAY_SIGNAL];
3097
2884
  if (!tag.shouldReset) {
3098
2885
  tag.shouldReset = true;
3099
2886
  addTransactionCB(array[NOTIFY]);
@@ -3105,11 +2892,11 @@ class RecordArrayManager {
3105
2892
  if (this.isDestroying || this.isDestroyed) {
3106
2893
  return;
3107
2894
  }
3108
- let liveArray = this._live.get(identifier.type);
2895
+ const liveArray = this._live.get(identifier.type);
3109
2896
  const allPending = this._pending;
3110
- let pending = new Map();
2897
+ const pending = new Map();
3111
2898
  if (includeManaged) {
3112
- let managed = this._identifiers.get(identifier);
2899
+ const managed = this._identifiers.get(identifier);
3113
2900
  if (managed) {
3114
2901
  managed.forEach(arr => {
3115
2902
  let changes = allPending.get(arr);
@@ -3164,10 +2951,10 @@ class RecordArrayManager {
3164
2951
  associate(this._identifiers, array, identifiers);
3165
2952
  }
3166
2953
  identifierAdded(identifier) {
3167
- let changeSets = this._getPendingFor(identifier, false);
2954
+ const changeSets = this._getPendingFor(identifier, false);
3168
2955
  if (changeSets) {
3169
2956
  changeSets.forEach((changes, array) => {
3170
- let existing = changes.get(identifier);
2957
+ const existing = changes.get(identifier);
3171
2958
  if (existing === 'del') {
3172
2959
  changes.delete(identifier);
3173
2960
  } else {
@@ -3178,10 +2965,10 @@ class RecordArrayManager {
3178
2965
  }
3179
2966
  }
3180
2967
  identifierRemoved(identifier) {
3181
- let changeSets = this._getPendingFor(identifier, true, true);
2968
+ const changeSets = this._getPendingFor(identifier, true, true);
3182
2969
  if (changeSets) {
3183
2970
  changeSets.forEach((changes, array) => {
3184
- let existing = changes.get(identifier);
2971
+ const existing = changes.get(identifier);
3185
2972
  if (existing === 'add') {
3186
2973
  changes.delete(identifier);
3187
2974
  } else {
@@ -3192,7 +2979,7 @@ class RecordArrayManager {
3192
2979
  }
3193
2980
  }
3194
2981
  identifierChanged(identifier) {
3195
- let newState = this.store._instanceCache.recordIsLoaded(identifier, true);
2982
+ const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
3196
2983
 
3197
2984
  // if the change matches the most recent direct added/removed
3198
2985
  // state, then we can ignore it
@@ -3219,13 +3006,12 @@ class RecordArrayManager {
3219
3006
  this.clear(false);
3220
3007
  this._live.clear();
3221
3008
  this.isDestroyed = true;
3222
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
3223
3009
  this.store.notifications.unsubscribe(this._subscription);
3224
3010
  }
3225
3011
  }
3226
3012
  function associate(ArraysCache, array, identifiers) {
3227
3013
  for (let i = 0; i < identifiers.length; i++) {
3228
- let identifier = identifiers[i];
3014
+ const identifier = identifiers[i];
3229
3015
  let cache = ArraysCache.get(identifier);
3230
3016
  if (!cache) {
3231
3017
  cache = new Set();
@@ -3240,13 +3026,13 @@ function disassociate(ArraysCache, array, identifiers) {
3240
3026
  }
3241
3027
  }
3242
3028
  function disassociateIdentifier(ArraysCache, array, identifier) {
3243
- let cache = ArraysCache.get(identifier);
3029
+ const cache = ArraysCache.get(identifier);
3244
3030
  if (cache) {
3245
3031
  cache.delete(array);
3246
3032
  }
3247
3033
  }
3248
3034
  function sync(array, changes, arraySet) {
3249
- let state = array[SOURCE];
3035
+ const state = array[SOURCE];
3250
3036
  const adds = [];
3251
3037
  const removes = [];
3252
3038
  changes.forEach((value, key) => {
@@ -3260,13 +3046,13 @@ function sync(array, changes, arraySet) {
3260
3046
  } else {
3261
3047
  if (arraySet.has(key)) {
3262
3048
  removes.push(key);
3049
+ arraySet.delete(key);
3263
3050
  }
3264
3051
  }
3265
3052
  });
3266
3053
  if (removes.length) {
3267
3054
  if (removes.length === state.length) {
3268
3055
  state.length = 0;
3269
- arraySet.clear();
3270
3056
  // changing the reference breaks the Proxy
3271
3057
  // state = array[SOURCE] = [];
3272
3058
  } else {
@@ -3293,6 +3079,9 @@ function sync(array, changes, arraySet) {
3293
3079
  }
3294
3080
  }
3295
3081
 
3082
+ /**
3083
+ * @module @ember-data/store
3084
+ */
3296
3085
  const Touching = Symbol('touching');
3297
3086
  const RequestPromise = Symbol('promise');
3298
3087
  const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
@@ -3320,14 +3109,14 @@ class RequestStateService {
3320
3109
  this._done.delete(identifier);
3321
3110
  }
3322
3111
  _enqueue(promise, queryRequest) {
3323
- let query = queryRequest.data[0];
3112
+ const query = queryRequest.data[0];
3324
3113
  if (hasRecordIdentifier(query)) {
3325
3114
  const identifier = query.recordIdentifier;
3326
- let type = query.op === 'saveRecord' ? 'mutation' : 'query';
3115
+ const type = query.op === 'saveRecord' ? 'mutation' : 'query';
3327
3116
  if (!this._pending.has(identifier)) {
3328
3117
  this._pending.set(identifier, []);
3329
3118
  }
3330
- let request = {
3119
+ const request = {
3331
3120
  state: 'pending',
3332
3121
  request: queryRequest,
3333
3122
  type
@@ -3338,7 +3127,7 @@ class RequestStateService {
3338
3127
  this._triggerSubscriptions(request);
3339
3128
  return promise.then(result => {
3340
3129
  this._dequeue(identifier, request);
3341
- let finalizedRequest = {
3130
+ const finalizedRequest = {
3342
3131
  state: 'fulfilled',
3343
3132
  request: queryRequest,
3344
3133
  type,
@@ -3352,7 +3141,7 @@ class RequestStateService {
3352
3141
  return result;
3353
3142
  }, error => {
3354
3143
  this._dequeue(identifier, request);
3355
- let finalizedRequest = {
3144
+ const finalizedRequest = {
3356
3145
  state: 'rejected',
3357
3146
  request: queryRequest,
3358
3147
  type,
@@ -3401,7 +3190,7 @@ class RequestStateService {
3401
3190
  _addDone(request) {
3402
3191
  request[Touching].forEach(identifier => {
3403
3192
  // TODO add support for multiple
3404
- let requestDataOp = request.request.data[0].op;
3193
+ const requestDataOp = request.request.data[0].op;
3405
3194
  let requests = this._done.get(identifier);
3406
3195
  if (requests) {
3407
3196
  requests = requests.filter(req => {
@@ -3465,7 +3254,7 @@ class RequestStateService {
3465
3254
  * @method getPendingRequestsForRecord
3466
3255
  * @public
3467
3256
  * @param {StableRecordIdentifier} identifier
3468
- * @returns {RequestState[]} an array of request states for any pending requests for the given identifier
3257
+ * @return {RequestState[]} an array of request states for any pending requests for the given identifier
3469
3258
  */
3470
3259
  getPendingRequestsForRecord(identifier) {
3471
3260
  return this._pending.get(identifier) || EMPTY_ARR;
@@ -3477,10 +3266,10 @@ class RequestStateService {
3477
3266
  * @method getLastRequestForRecord
3478
3267
  * @public
3479
3268
  * @param {StableRecordIdentifier} identifier
3480
- * @returns {RequestState | null} the state of the most recent request for the given identifier
3269
+ * @return {RequestState | null} the state of the most recent request for the given identifier
3481
3270
  */
3482
3271
  getLastRequestForRecord(identifier) {
3483
- let requests = this._done.get(identifier);
3272
+ const requests = this._done.get(identifier);
3484
3273
  if (requests) {
3485
3274
  return requests[requests.length - 1];
3486
3275
  }
@@ -3492,7 +3281,7 @@ function isNonEmptyString(str) {
3492
3281
  }
3493
3282
  function constructResource(type, id, lid) {
3494
3283
  if (typeof type === 'object' && type !== null) {
3495
- let resource = type;
3284
+ const resource = type;
3496
3285
  if (isStableIdentifier(resource)) {
3497
3286
  return resource;
3498
3287
  }
@@ -3527,6 +3316,29 @@ function constructResource(type, id, lid) {
3527
3316
  }
3528
3317
  }
3529
3318
 
3319
+ /**
3320
+ @module @ember-data/store
3321
+ */
3322
+ // this import location is deprecated but breaks in 4.8 and older
3323
+
3324
+ /**
3325
+ * Currently only records that extend object can be created via
3326
+ * store.createRecord. This is a limitation of the current API,
3327
+ * but can be worked around by creating a new identifier, running
3328
+ * the cache.clientDidCreate method, and then peeking the record
3329
+ * for the identifier.
3330
+ *
3331
+ * To assign primary key to a record during creation, only `id` will
3332
+ * work correctly for `store.createRecord`, other primary key may be
3333
+ * handled by updating the record after creation or using the flow
3334
+ * described above.
3335
+ *
3336
+ * TODO: These are limitations we want to (and can) address. If you
3337
+ * have need of lifting these limitations, please open an issue.
3338
+ *
3339
+ * @typedoc
3340
+ */
3341
+
3530
3342
  /**
3531
3343
  * A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
3532
3344
  * and sources of data (such as your API or a local persistence layer)
@@ -3545,7 +3357,10 @@ function constructResource(type, id, lid) {
3545
3357
 
3546
3358
  @class Store
3547
3359
  @public
3548
- */ // @ts-expect-error
3360
+ */
3361
+
3362
+ // @ts-expect-error
3363
+
3549
3364
  class Store extends EmberObject {
3550
3365
  /**
3551
3366
  * Provides access to the NotificationManager associated
@@ -3677,8 +3492,6 @@ class Store extends EmberObject {
3677
3492
  // private
3678
3493
  this._requestCache = new RequestStateService(this);
3679
3494
  this._instanceCache = new InstanceCache(this);
3680
- this._adapterCache = Object.create(null);
3681
- this._serializerCache = Object.create(null);
3682
3495
  this._documentCache = new Map();
3683
3496
  this.isDestroying = false;
3684
3497
  this.isDestroyed = false;
@@ -3736,7 +3549,7 @@ class Store extends EmberObject {
3736
3549
  * that have been initiated for a given identifier.
3737
3550
  *
3738
3551
  * @method getRequestStateService
3739
- * @returns {RequestStateService}
3552
+ * @return {RequestStateService}
3740
3553
  * @public
3741
3554
  */
3742
3555
  getRequestStateService() {
@@ -3761,13 +3574,14 @@ class Store extends EmberObject {
3761
3574
  * inserting the response into the cache and handing
3762
3575
  * back a Future which resolves to a ResponseDocument
3763
3576
  *
3764
- * Resource data is always updated in the cache.
3577
+ * ## Cache Keys
3765
3578
  *
3766
- * Only GET requests have the request result and document
3767
- * cached by default when a cache key is present.
3579
+ * Only GET requests with a url or requests with an explicit
3580
+ * cache key (`cacheOptions.key`) will have the request result
3581
+ * and document cached.
3768
3582
  *
3769
3583
  * The cache key used is `requestConfig.cacheOptions.key`
3770
- * if present, falling back to `requestconfig.url`.
3584
+ * if present, falling back to `requestConfig.url`.
3771
3585
  *
3772
3586
  * Params are not serialized as part of the cache-key, so
3773
3587
  * either ensure they are already in the url or utilize
@@ -3775,16 +3589,44 @@ class Store extends EmberObject {
3775
3589
  * via the `POST` method `requestConfig.cacheOptions.key`
3776
3590
  * MUST be supplied for the document to be cached.
3777
3591
  *
3592
+ * ## Requesting Without a Cache Key
3593
+ *
3594
+ * Resource data within the request is always updated in the cache,
3595
+ * regardless of whether a cache key is present for the request.
3596
+ *
3597
+ * ## Fulfilling From Cache
3598
+ *
3599
+ * When a cache-key is determined, the request may fulfill
3600
+ * from cache provided the cache is not stale.
3601
+ *
3602
+ * Cache staleness is determined by the configured LifetimesService
3603
+ * with priority given to the `cacheOptions.reload` and
3604
+ * `cacheOptions.backgroundReload` on the request if present.
3605
+ *
3606
+ * If the cache data has soft expired or the request asks for a background
3607
+ * reload, the request will fulfill from cache if possible and
3608
+ * make a non-blocking request in the background to update the cache.
3609
+ *
3610
+ * If the cache data has hard expired or the request asks for a reload,
3611
+ * the request will not fulfill from cache and will make a blocking
3612
+ * request to update the cache.
3613
+ *
3614
+ * ## The Response
3615
+ *
3616
+ * The primary difference between `requestManager.request` and `store.request`
3617
+ * is that `store.request` will attempt to hydrate the response content into
3618
+ * a response Document containing RecordInstances.
3619
+ *
3778
3620
  * @method request
3779
3621
  * @param {StoreRequestInput} requestConfig
3780
- * @returns {Future}
3622
+ * @return {Future}
3781
3623
  * @public
3782
3624
  */
3783
3625
  request(requestConfig) {
3784
3626
  // we lazily set the cache handler when we issue the first request
3785
3627
  // because constructor doesn't allow for this to run after
3786
3628
  // the user has had the chance to set the prop.
3787
- let opts = {
3629
+ const opts = {
3788
3630
  store: this,
3789
3631
  [EnableHydration]: true
3790
3632
  };
@@ -3828,7 +3670,7 @@ class Store extends EmberObject {
3828
3670
  * a resource.
3829
3671
  *
3830
3672
  * This hook can be used to select or instantiate any desired
3831
- * mechanism of presentating cache data to the ui for access
3673
+ * mechanism of presenting cache data to the ui for access
3832
3674
  * mutation, and interaction.
3833
3675
  *
3834
3676
  * @method instantiateRecord (hook)
@@ -3836,7 +3678,7 @@ class Store extends EmberObject {
3836
3678
  * @param createRecordArgs
3837
3679
  * @param recordDataFor deprecated use this.cache
3838
3680
  * @param notificationManager deprecated use this.notifications
3839
- * @returns A record instance
3681
+ * @return A record instance
3840
3682
  * @public
3841
3683
  */
3842
3684
 
@@ -3979,7 +3821,7 @@ class Store extends EmberObject {
3979
3821
  }
3980
3822
 
3981
3823
  /**
3982
- Returns the schema for a particular `modelName`.
3824
+ Returns the schema for a particular resource type (modelName).
3983
3825
  When used with Model from @ember-data/model the return is the model class,
3984
3826
  but this is not guaranteed.
3985
3827
  If looking to query attribute or relationship information it is
@@ -3993,7 +3835,7 @@ class Store extends EmberObject {
3993
3835
  for example.
3994
3836
  @method modelFor
3995
3837
  @public
3996
- @param {String} type
3838
+ @param {string} type
3997
3839
  @return {ModelSchema}
3998
3840
  */
3999
3841
  // TODO @deprecate in favor of schema APIs, requires adapter/serializer overhaul or replacement
@@ -4018,7 +3860,7 @@ class Store extends EmberObject {
4018
3860
  ```
4019
3861
  To create a new instance of a `Post` that has a relationship with a `User` record:
4020
3862
  ```js
4021
- let user = this.store.peekRecord('user', 1);
3863
+ let user = this.store.peekRecord('user', '1');
4022
3864
  store.createRecord('post', {
4023
3865
  title: 'Ember is awesome!',
4024
3866
  user: user
@@ -4026,17 +3868,18 @@ class Store extends EmberObject {
4026
3868
  ```
4027
3869
  @method createRecord
4028
3870
  @public
4029
- @param {String} modelName
3871
+ @param {String} type the name of the resource
4030
3872
  @param {Object} inputProperties a hash of properties to set on the
4031
3873
  newly created record.
4032
3874
  @return {Model} record
4033
3875
  */
4034
- createRecord(modelName, inputProperties) {
3876
+
3877
+ createRecord(type, inputProperties) {
4035
3878
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4036
3879
  assertDestroyingStore(this, 'createRecord');
4037
3880
  }
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');
3881
+ assert(`You need to pass a model name to the store's createRecord method`, type);
3882
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4040
3883
 
4041
3884
  // This is wrapped in a `run.join` so that in test environments users do not need to manually wrap
4042
3885
  // calls to `createRecord`. The run loop usage here is because we batch the joining and updating
@@ -4044,43 +3887,40 @@ class Store extends EmberObject {
4044
3887
  //
4045
3888
  // to remove this, we would need to move to a new `async` API.
4046
3889
  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
- }
3890
+ this._join(() => {
3891
+ const normalizedModelName = normalizeModelName(type);
3892
+ const properties = {
3893
+ ...inputProperties
3894
+ };
4067
3895
 
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);
3896
+ // If the passed properties do not include a primary key,
3897
+ // give the adapter an opportunity to generate one. Typically,
3898
+ // client-side ID generators will use something like uuid.js
3899
+ // to avoid conflicts.
3900
+ let id = null;
3901
+ if (properties.id === null || properties.id === undefined) {
3902
+ const adapter = this.adapterFor?.(normalizedModelName, true);
3903
+ if (adapter && adapter.generateIdForRecord) {
3904
+ id = properties.id = coerceId(adapter.generateIdForRecord(this, normalizedModelName, properties));
3905
+ } else {
3906
+ id = properties.id = null;
4077
3907
  }
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
- });
3908
+ } else {
3909
+ id = properties.id = coerceId(properties.id);
3910
+ }
3911
+ const resource = {
3912
+ type: normalizedModelName,
3913
+ id
3914
+ };
3915
+ if (resource.id) {
3916
+ const identifier = this.identifierCache.peekRecordIdentifier(resource);
3917
+ assert(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`, !identifier);
3918
+ }
3919
+ const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
3920
+ const cache = this.cache;
3921
+ const createOptions = normalizeProperties(this, identifier, properties);
3922
+ const resultProps = cache.clientDidCreate(identifier, createOptions);
3923
+ record = this._instanceCache.getRecord(identifier, resultProps);
4084
3924
  });
4085
3925
  return record;
4086
3926
  }
@@ -4096,7 +3936,7 @@ class Store extends EmberObject {
4096
3936
  ```
4097
3937
  @method deleteRecord
4098
3938
  @public
4099
- @param {Model} record
3939
+ @param {unknown} record
4100
3940
  */
4101
3941
  deleteRecord(record) {
4102
3942
  if (macroCondition(getOwnConfig().env.DEBUG)) {
@@ -4108,9 +3948,7 @@ class Store extends EmberObject {
4108
3948
  this._join(() => {
4109
3949
  cache.setIsDeleted(identifier, true);
4110
3950
  if (cache.isNew(identifier)) {
4111
- _backburner.join(() => {
4112
- this._instanceCache.unloadRecord(identifier);
4113
- });
3951
+ this._instanceCache.unloadRecord(identifier);
4114
3952
  }
4115
3953
  });
4116
3954
  }
@@ -4120,7 +3958,7 @@ class Store extends EmberObject {
4120
3958
  This will cause the record to be destroyed and freed up for garbage collection.
4121
3959
  Example
4122
3960
  ```javascript
4123
- store.findRecord('post', 1).then(function(post) {
3961
+ store.findRecord('post', '1').then(function(post) {
4124
3962
  store.unloadRecord(post);
4125
3963
  });
4126
3964
  ```
@@ -4192,8 +4030,7 @@ class Store extends EmberObject {
4192
4030
  In your adapter you can then access this id without triggering a network request via the
4193
4031
  snapshot:
4194
4032
  ```app/adapters/application.js
4195
- import EmberObject from '@ember/object';
4196
- export default class Adapter extends EmberObject {
4033
+ export default class Adapter {
4197
4034
  findRecord(store, schema, id, snapshot) {
4198
4035
  let type = schema.modelName;
4199
4036
  if (type === 'comment')
@@ -4202,6 +4039,9 @@ class Store extends EmberObject {
4202
4039
  .then(response => response.json())
4203
4040
  }
4204
4041
  }
4042
+ static create() {
4043
+ return new this();
4044
+ }
4205
4045
  }
4206
4046
  ```
4207
4047
  This could also be achieved by supplying the post id to the adapter via the adapterOptions
@@ -4215,9 +4055,8 @@ class Store extends EmberObject {
4215
4055
  }
4216
4056
  ```
4217
4057
  ```app/adapters/application.js
4218
- import EmberObject from '@ember/object';
4219
- export default class Adapter extends EmberObject {
4220
- findRecord(store, schema, id, snapshot) {
4058
+ export default class Adapter {
4059
+ findRecord(store, schema, id, snapshot) {
4221
4060
  let type = schema.modelName;
4222
4061
  if (type === 'comment')
4223
4062
  let postId = snapshot.adapterOptions.post;
@@ -4225,12 +4064,15 @@ class Store extends EmberObject {
4225
4064
  .then(response => response.json())
4226
4065
  }
4227
4066
  }
4067
+ static create() {
4068
+ return new this();
4069
+ }
4228
4070
  }
4229
4071
  ```
4230
4072
  If you have access to the post model you can also pass the model itself to preload:
4231
4073
  ```javascript
4232
- let post = await store.findRecord('post', 1);
4233
- let comment = await store.findRecord('comment', 2, { post: myPostModel });
4074
+ let post = await store.findRecord('post', '1');
4075
+ let comment = await store.findRecord('comment', '2', { post: myPostModel });
4234
4076
  ```
4235
4077
  ### Reloading
4236
4078
  The reload behavior is configured either via the passed `options` hash or
@@ -4254,7 +4096,7 @@ class Store extends EmberObject {
4254
4096
  // revision: 2
4255
4097
  // }
4256
4098
  // ]
4257
- store.findRecord('post', 1, { reload: true }).then(function(post) {
4099
+ store.findRecord('post', '1', { reload: true }).then(function(post) {
4258
4100
  post.revision; // 2
4259
4101
  });
4260
4102
  ```
@@ -4283,7 +4125,7 @@ class Store extends EmberObject {
4283
4125
  revision: 1
4284
4126
  }
4285
4127
  });
4286
- let blogPost = store.findRecord('post', 1).then(function(post) {
4128
+ let blogPost = store.findRecord('post', '1').then(function(post) {
4287
4129
  post.revision; // 1
4288
4130
  });
4289
4131
  // later, once adapter#findRecord resolved with
@@ -4351,9 +4193,8 @@ class Store extends EmberObject {
4351
4193
  }
4352
4194
  ```
4353
4195
  ```app/adapters/application.js
4354
- import EmberObject from '@ember/object';
4355
- export default class Adapter extends EmberObject {
4356
- findRecord(store, schema, id, snapshot) {
4196
+ export default class Adapter {
4197
+ findRecord(store, schema, id, snapshot) {
4357
4198
  let type = schema.modelName;
4358
4199
  if (type === 'post')
4359
4200
  let includes = snapshot.adapterOptions.include;
@@ -4361,6 +4202,9 @@ class Store extends EmberObject {
4361
4202
  .then(response => response.json())
4362
4203
  }
4363
4204
  }
4205
+ static create() {
4206
+ return new this();
4207
+ }
4364
4208
  }
4365
4209
  ```
4366
4210
  In this case, the post's comments would then be available in your template as
@@ -4414,7 +4258,7 @@ class Store extends EmberObject {
4414
4258
  @since 1.13.0
4415
4259
  @method findRecord
4416
4260
  @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
4261
+ @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
4262
  @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
4263
  @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
4264
  @return {Promise} promise
@@ -4465,7 +4309,7 @@ class Store extends EmberObject {
4465
4309
  Get the reference for the specified record.
4466
4310
  Example
4467
4311
  ```javascript
4468
- let userRef = store.getReference('user', 1);
4312
+ let userRef = store.getReference('user', '1');
4469
4313
  // check if the user is loaded
4470
4314
  let isLoaded = userRef.value() !== null;
4471
4315
  // get the record of the reference (null if not yet available)
@@ -4504,7 +4348,7 @@ class Store extends EmberObject {
4504
4348
  resourceIdentifier = constructResource(type, normalizedId);
4505
4349
  }
4506
4350
  assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
4507
- let identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4351
+ const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
4508
4352
  return this._instanceCache.getReference(identifier);
4509
4353
  }
4510
4354
 
@@ -4517,8 +4361,8 @@ class Store extends EmberObject {
4517
4361
  _Note: This is a synchronous method and does not return a promise._
4518
4362
  **Example 1**
4519
4363
  ```js
4520
- let post = store.peekRecord('post', 1);
4521
- post.id; // 1
4364
+ let post = store.peekRecord('post', '1');
4365
+ post.id; // '1'
4522
4366
  ```
4523
4367
  `peekRecord` can be called with a single identifier argument instead of the combination
4524
4368
  of `type` (modelName) and `id` as separate arguments. You may recognize this combo as
@@ -4526,14 +4370,14 @@ class Store extends EmberObject {
4526
4370
  **Example 2**
4527
4371
  ```js
4528
4372
  let post = store.peekRecord({ type: 'post', id });
4529
- post.id; // 1
4373
+ post.id; // '1'
4530
4374
  ```
4531
4375
  If you have previously received an lid from an Identifier for this record, you can lookup the record again using
4532
4376
  just the lid.
4533
4377
  **Example 3**
4534
4378
  ```js
4535
4379
  let post = store.peekRecord({ lid });
4536
- post.id; // 1
4380
+ post.id; // '1'
4537
4381
  ```
4538
4382
  @since 1.13.0
4539
4383
  @method peekRecord
@@ -4586,7 +4430,7 @@ class Store extends EmberObject {
4586
4430
  ---
4587
4431
  If you do something like this:
4588
4432
  ```javascript
4589
- store.query('person', { ids: [1, 2, 3] });
4433
+ store.query('person', { ids: ['1', '2', '3'] });
4590
4434
  ```
4591
4435
  The request made to the server will look something like this:
4592
4436
  ```
@@ -4599,24 +4443,25 @@ class Store extends EmberObject {
4599
4443
  @since 1.13.0
4600
4444
  @method query
4601
4445
  @public
4602
- @param {String} modelName
4603
- @param {any} query an opaque query to be used by the adapter
4446
+ @param {String} type the name of the resource
4447
+ @param {object} query a query to be used by the adapter
4604
4448
  @param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.query
4605
4449
  @return {Promise} promise
4606
4450
  */
4607
- query(modelName, query, options) {
4451
+
4452
+ query(type, query, options = {}) {
4608
4453
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4609
4454
  assertDestroyingStore(this, 'query');
4610
4455
  }
4611
- assert(`You need to pass a model name to the store's query method`, modelName);
4456
+ assert(`You need to pass a model name to the store's query method`, type);
4612
4457
  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');
4458
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4614
4459
  const promise = this.request({
4615
4460
  op: 'query',
4616
4461
  data: {
4617
- type: normalizeModelName(modelName),
4462
+ type: normalizeModelName(type),
4618
4463
  query,
4619
- options: options || {}
4464
+ options: options
4620
4465
  },
4621
4466
  cacheOptions: {
4622
4467
  [SkipCache]: true
@@ -4701,22 +4546,23 @@ class Store extends EmberObject {
4701
4546
  @since 1.13.0
4702
4547
  @method queryRecord
4703
4548
  @public
4704
- @param {String} modelName
4705
- @param {any} query an opaque query to be used by the adapter
4706
- @param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.queryRecord
4549
+ @param {string} type
4550
+ @param {object} query an opaque query to be used by the adapter
4551
+ @param {object} options optional, may include `adapterOptions` hash which will be passed to adapter.queryRecord
4707
4552
  @return {Promise} promise which resolves with the found record or `null`
4708
4553
  */
4709
- queryRecord(modelName, query, options) {
4554
+
4555
+ queryRecord(type, query, options) {
4710
4556
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4711
4557
  assertDestroyingStore(this, 'queryRecord');
4712
4558
  }
4713
- assert(`You need to pass a model name to the store's queryRecord method`, modelName);
4559
+ assert(`You need to pass a model name to the store's queryRecord method`, type);
4714
4560
  assert(`You need to pass a query hash to the store's queryRecord method`, query);
4715
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
4561
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4716
4562
  const promise = this.request({
4717
4563
  op: 'queryRecord',
4718
4564
  data: {
4719
- type: normalizeModelName(modelName),
4565
+ type: normalizeModelName(type),
4720
4566
  query,
4721
4567
  options: options || {}
4722
4568
  },
@@ -4874,20 +4720,21 @@ class Store extends EmberObject {
4874
4720
  @since 1.13.0
4875
4721
  @method findAll
4876
4722
  @public
4877
- @param {String} modelName
4878
- @param {Object} options
4723
+ @param {string} type the name of the resource
4724
+ @param {object} options
4879
4725
  @return {Promise} promise
4880
4726
  */
4881
- findAll(modelName, options = {}) {
4727
+
4728
+ findAll(type, options = {}) {
4882
4729
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4883
4730
  assertDestroyingStore(this, 'findAll');
4884
4731
  }
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');
4732
+ assert(`You need to pass a model name to the store's findAll method`, type);
4733
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4887
4734
  const promise = this.request({
4888
4735
  op: 'findAll',
4889
4736
  data: {
4890
- type: normalizeModelName(modelName),
4737
+ type: normalizeModelName(type),
4891
4738
  options: options || {}
4892
4739
  },
4893
4740
  cacheOptions: {
@@ -4914,17 +4761,17 @@ class Store extends EmberObject {
4914
4761
  @since 1.13.0
4915
4762
  @method peekAll
4916
4763
  @public
4917
- @param {String} modelName
4764
+ @param {string} type the name of the resource
4918
4765
  @return {RecordArray}
4919
4766
  */
4920
- peekAll(modelName) {
4767
+
4768
+ peekAll(type) {
4921
4769
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4922
4770
  assertDestroyingStore(this, 'peekAll');
4923
4771
  }
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);
4772
+ assert(`You need to pass a model name to the store's peekAll method`, type);
4773
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
4774
+ return this.recordArrayManager.liveArrayFor(normalizeModelName(type));
4928
4775
  }
4929
4776
 
4930
4777
  /**
@@ -4936,16 +4783,17 @@ class Store extends EmberObject {
4936
4783
  store.unloadAll('post');
4937
4784
  ```
4938
4785
  @method unloadAll
4786
+ @param {string} type the name of the resource
4939
4787
  @public
4940
- @param {String} modelName
4941
4788
  */
4942
- unloadAll(modelName) {
4789
+
4790
+ unloadAll(type) {
4943
4791
  if (macroCondition(getOwnConfig().env.DEBUG)) {
4944
4792
  assertDestroyedStoreOnly(this, 'unloadAll');
4945
4793
  }
4946
- assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(modelName)}`, !modelName || typeof modelName === 'string');
4794
+ assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`, !type || typeof type === 'string');
4947
4795
  this._join(() => {
4948
- if (modelName === undefined) {
4796
+ if (type === undefined) {
4949
4797
  // destroy the graph before unloadAll
4950
4798
  // since then we avoid churning relationships
4951
4799
  // during unload
@@ -4953,8 +4801,7 @@ class Store extends EmberObject {
4953
4801
  this.recordArrayManager.clear();
4954
4802
  this._instanceCache.clear();
4955
4803
  } else {
4956
- let normalizedModelName = normalizeModelName(modelName);
4957
- this._instanceCache.clear(normalizedModelName);
4804
+ this._instanceCache.clear(normalizeModelName(type));
4958
4805
  }
4959
4806
  });
4960
4807
  }
@@ -5094,10 +4941,9 @@ class Store extends EmberObject {
5094
4941
  if (macroCondition(getOwnConfig().env.DEBUG)) {
5095
4942
  assertDestroyingStore(this, 'push');
5096
4943
  }
5097
- let pushed = this._push(data, false);
4944
+ const pushed = this._push(data, false);
5098
4945
  if (Array.isArray(pushed)) {
5099
- let records = pushed.map(identifier => this._instanceCache.getRecord(identifier));
5100
- return records;
4946
+ return pushed.map(identifier => this._instanceCache.getRecord(identifier));
5101
4947
  }
5102
4948
  if (pushed === null) {
5103
4949
  return null;
@@ -5119,7 +4965,7 @@ class Store extends EmberObject {
5119
4965
  }
5120
4966
  if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
5121
4967
  try {
5122
- let data = JSON.parse(JSON.stringify(jsonApiDoc));
4968
+ const data = JSON.parse(JSON.stringify(jsonApiDoc));
5123
4969
  // eslint-disable-next-line no-console
5124
4970
  console.log('EmberData | Payload - push', data);
5125
4971
  } catch (e) {
@@ -5136,379 +4982,745 @@ class Store extends EmberObject {
5136
4982
  content: jsonApiDoc
5137
4983
  });
5138
4984
  });
5139
- this._enableAsyncFlush = null;
5140
- return 'data' in ret ? ret.data : null;
4985
+ this._enableAsyncFlush = null;
4986
+ return 'data' in ret ? ret.data : null;
4987
+ }
4988
+
4989
+ /**
4990
+ * Trigger a save for a Record.
4991
+ *
4992
+ * Returns a promise resolving with the same record when the save is complete.
4993
+ *
4994
+ * @method saveRecord
4995
+ * @public
4996
+ * @param {unknown} record
4997
+ * @param options
4998
+ * @return {Promise<record>}
4999
+ */
5000
+ saveRecord(record, options = {}) {
5001
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5002
+ assertDestroyingStore(this, 'saveRecord');
5003
+ }
5004
+ assert(`Unable to initiate save for a record in a disconnected state`, storeFor(record));
5005
+ const identifier = recordIdentifierFor(record);
5006
+ const cache = this.cache;
5007
+ if (!identifier) {
5008
+ // this commonly means we're disconnected
5009
+ // but just in case we reject here to prevent bad things.
5010
+ return Promise.reject(new Error(`Record Is Disconnected`));
5011
+ }
5012
+ assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
5013
+ if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5014
+ return Promise.resolve(record);
5015
+ }
5016
+ if (!options) {
5017
+ options = {};
5018
+ }
5019
+ let operation = 'updateRecord';
5020
+ if (cache.isNew(identifier)) {
5021
+ operation = 'createRecord';
5022
+ } else if (cache.isDeleted(identifier)) {
5023
+ operation = 'deleteRecord';
5024
+ }
5025
+ const request = {
5026
+ op: operation,
5027
+ data: {
5028
+ options,
5029
+ record: identifier
5030
+ },
5031
+ records: [identifier],
5032
+ cacheOptions: {
5033
+ [SkipCache]: true
5034
+ }
5035
+ };
5036
+ return this.request(request).then(document => document.content);
5037
+ }
5038
+
5039
+ /**
5040
+ * Instantiation hook allowing applications or addons to configure the store
5041
+ * to utilize a custom Cache implementation.
5042
+ *
5043
+ * This hook should not be called directly by consuming applications or libraries.
5044
+ * Use `Store.cache` to access the Cache instance.
5045
+ *
5046
+ * @method createCache (hook)
5047
+ * @public
5048
+ * @param storeWrapper
5049
+ * @return {Cache}
5050
+ */
5051
+
5052
+ /**
5053
+ * Returns the cache instance associated to this Store, instantiates the Cache
5054
+ * if necessary via `Store.createCache`
5055
+ *
5056
+ * @property {Cache} cache
5057
+ * @public
5058
+ */
5059
+ get cache() {
5060
+ let {
5061
+ cache
5062
+ } = this._instanceCache;
5063
+ if (!cache) {
5064
+ cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5065
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5066
+ cache = new CacheManager(cache);
5067
+ }
5068
+ }
5069
+ return cache;
5070
+ }
5071
+
5072
+ // @ts-expect-error
5073
+ destroy() {
5074
+ if (this.isDestroyed) {
5075
+ // @ember/test-helpers will call destroy multiple times
5076
+ return;
5077
+ }
5078
+ this.isDestroying = true;
5079
+ this._graph?.destroy();
5080
+ this._graph = undefined;
5081
+ this.notifications.destroy();
5082
+ this.recordArrayManager.destroy();
5083
+ this.identifierCache.destroy();
5084
+ this.unloadAll();
5085
+ this.isDestroyed = true;
5086
+ }
5087
+ static create(args) {
5088
+ return new this(args);
5089
+ }
5090
+ }
5091
+ let assertDestroyingStore;
5092
+ let assertDestroyedStoreOnly;
5093
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5094
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5095
+ assertDestroyingStore = function assertDestroyingStore(store, method) {
5096
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
5097
+ };
5098
+ // eslint-disable-next-line @typescript-eslint/no-shadow
5099
+ assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5100
+ assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
5101
+ };
5102
+ }
5103
+ function isMaybeIdentifier(maybeIdentifier) {
5104
+ return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5105
+ }
5106
+ function normalizeProperties(store, identifier, properties) {
5107
+ // assert here
5108
+ if (properties !== undefined) {
5109
+ if ('id' in properties) {
5110
+ assert(`expected id to be a string or null`, properties.id !== undefined);
5111
+ }
5112
+ assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5113
+ const {
5114
+ type
5115
+ } = identifier;
5116
+
5117
+ // convert relationship Records to RecordDatas before passing to RecordData
5118
+ const defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
5119
+ type
5120
+ });
5121
+ if (defs !== null) {
5122
+ const keys = Object.keys(properties);
5123
+ let relationshipValue;
5124
+ for (let i = 0; i < keys.length; i++) {
5125
+ const prop = keys[i];
5126
+ const def = defs[prop];
5127
+ if (def !== undefined) {
5128
+ if (def.kind === 'hasMany') {
5129
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
5130
+ assertRecordsPassedToHasMany(properties[prop]);
5131
+ }
5132
+ relationshipValue = extractIdentifiersFromRecords(properties[prop]);
5133
+ } else {
5134
+ relationshipValue = extractIdentifierFromRecord(properties[prop]);
5135
+ }
5136
+ properties[prop] = relationshipValue;
5137
+ }
5138
+ }
5139
+ }
5140
+ }
5141
+ return properties;
5142
+ }
5143
+ function assertRecordsPassedToHasMany(records) {
5144
+ assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
5145
+ assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
5146
+ return records.every(record => {
5147
+ try {
5148
+ recordIdentifierFor(record);
5149
+ return true;
5150
+ } catch {
5151
+ return false;
5152
+ }
5153
+ });
5154
+ }());
5155
+ }
5156
+ function extractIdentifiersFromRecords(records) {
5157
+ return records.map(record => extractIdentifierFromRecord(record));
5158
+ }
5159
+ function extractIdentifierFromRecord(recordOrPromiseRecord) {
5160
+ if (!recordOrPromiseRecord) {
5161
+ return null;
5141
5162
  }
5163
+ const extract = recordIdentifierFor;
5164
+ return extract(recordOrPromiseRecord);
5165
+ }
5166
+ function urlFromLink(link) {
5167
+ if (typeof link === 'string') return link;
5168
+ return link.href;
5169
+ }
5142
5170
 
5171
+ /**
5172
+ * A Document is a class that wraps the response content from a request to the API
5173
+ * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
5174
+ * record instances.
5175
+ *
5176
+ * It is not directly instantiated by the user, and its properties should not
5177
+ * be directly modified. Whether individual properties are mutable or not is
5178
+ * determined by the record instance itself.
5179
+ *
5180
+ * @public
5181
+ * @class Document
5182
+ */
5183
+ var _store = /*#__PURE__*/_classPrivateFieldKey("store");
5184
+ var _request = /*#__PURE__*/_classPrivateFieldKey("request");
5185
+ class Document {
5186
+ constructor(store, identifier) {
5187
+ Object.defineProperty(this, _request, {
5188
+ value: _request2
5189
+ });
5190
+ /**
5191
+ * The links object for this document, if any
5192
+ *
5193
+ * e.g.
5194
+ *
5195
+ * ```
5196
+ * {
5197
+ * self: '/articles?page[number]=3',
5198
+ * }
5199
+ * ```
5200
+ *
5201
+ * @property links
5202
+ * @type {object|undefined} - a links object
5203
+ * @public
5204
+ */
5205
+ /**
5206
+ * The primary data for this document, if any.
5207
+ *
5208
+ * If this document has no primary data (e.g. because it is an error document)
5209
+ * this property will be `undefined`.
5210
+ *
5211
+ * For collections this will be an array of record instances,
5212
+ * for single resource requests it will be a single record instance or null.
5213
+ *
5214
+ * @property data
5215
+ * @public
5216
+ * @type {object|Array<object>|null|undefined} - a data object
5217
+ */
5218
+ /**
5219
+ * The errors returned by the API for this request, if any
5220
+ *
5221
+ * @property errors
5222
+ * @public
5223
+ * @type {object|undefined} - an errors object
5224
+ */
5225
+ /**
5226
+ * The meta object for this document, if any
5227
+ *
5228
+ * @property meta
5229
+ * @public
5230
+ * @type {object|undefined} - a meta object
5231
+ */
5232
+ /**
5233
+ * The identifier associated with this document, if any
5234
+ *
5235
+ * @property identifier
5236
+ * @public
5237
+ * @type {StableDocumentIdentifier|null}
5238
+ */
5239
+ Object.defineProperty(this, _store, {
5240
+ writable: true,
5241
+ value: void 0
5242
+ });
5243
+ _classPrivateFieldBase(this, _store)[_store] = store;
5244
+ this.identifier = identifier;
5245
+ }
5143
5246
  /**
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);
5247
+ * Fetches the related link for this document, returning a promise that resolves
5248
+ * with the document when the request completes. If no related link is present,
5249
+ * will fallback to the self link if present
5250
+ *
5251
+ * @method fetch
5252
+ * @public
5253
+ * @param {object} options
5254
+ * @return Promise<Document>
5255
+ */
5256
+ fetch(options = {}) {
5257
+ assert(`No self or related link`, this.links?.related || this.links?.self);
5258
+ options.cacheOptions = options.cacheOptions || {};
5259
+ options.cacheOptions.key = this.identifier?.lid;
5260
+ return _classPrivateFieldBase(this, _request)[_request](this.links.related ? 'related' : 'self', options);
5196
5261
  }
5197
5262
 
5198
5263
  /**
5199
- * Trigger a save for a Record.
5264
+ * Fetches the next link for this document, returning a promise that resolves
5265
+ * with the new document when the request completes, or null if there is no
5266
+ * next link.
5200
5267
  *
5201
- * @method saveRecord
5268
+ * @method next
5202
5269
  * @public
5203
- * @param {RecordInstance} record
5204
- * @param options
5205
- * @returns {Promise<RecordInstance>}
5270
+ * @param {object} options
5271
+ * @return Promise<Document | null>
5206
5272
  */
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
- };
5273
+ next(options = {}) {
5274
+ return _classPrivateFieldBase(this, _request)[_request]('next', options);
5275
+ }
5243
5276
 
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);
5277
+ /**
5278
+ * Fetches the prev link for this document, returning a promise that resolves
5279
+ * with the new document when the request completes, or null if there is no
5280
+ * prev link.
5281
+ *
5282
+ * @method prev
5283
+ * @public
5284
+ * @param {object} options
5285
+ * @return Promise<Document | null>
5286
+ */
5287
+ prev(options = {}) {
5288
+ return _classPrivateFieldBase(this, _request)[_request]('prev', options);
5249
5289
  }
5250
5290
 
5251
5291
  /**
5252
- * Instantiation hook allowing applications or addons to configure the store
5253
- * to utilize a custom Cache implementation.
5292
+ * Fetches the first link for this document, returning a promise that resolves
5293
+ * with the new document when the request completes, or null if there is no
5294
+ * first link.
5254
5295
  *
5255
- * This hook should not be called directly by consuming applications or libraries.
5256
- * Use `Store.cache` to access the Cache instance.
5296
+ * @method first
5297
+ * @public
5298
+ * @param {object} options
5299
+ * @return Promise<Document | null>
5300
+ */
5301
+ first(options = {}) {
5302
+ return _classPrivateFieldBase(this, _request)[_request]('first', options);
5303
+ }
5304
+
5305
+ /**
5306
+ * Fetches the last link for this document, returning a promise that resolves
5307
+ * with the new document when the request completes, or null if there is no
5308
+ * last link.
5257
5309
  *
5258
- * @method createCache (hook)
5310
+ * @method last
5259
5311
  * @public
5260
- * @param storeWrapper
5261
- * @returns {Cache}
5312
+ * @param {object} options
5313
+ * @return Promise<Document | null>
5262
5314
  */
5315
+ last(options = {}) {
5316
+ return _classPrivateFieldBase(this, _request)[_request]('last', options);
5317
+ }
5263
5318
 
5264
5319
  /**
5265
- * Returns the cache instance associated to this Store, instantiates the Cache
5266
- * if necessary via `Store.createCache`
5320
+ * Implemented for `JSON.stringify` support.
5267
5321
  *
5268
- * @property {Cache} cache
5322
+ * Returns the JSON representation of the document wrapper.
5323
+ *
5324
+ * This is a shallow serialization, it does not deeply serialize
5325
+ * the document's contents, leaving that to the individual record
5326
+ * instances to determine how to do, if at all.
5327
+ *
5328
+ * @method toJSON
5269
5329
  * @public
5330
+ * @return
5270
5331
  */
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);
5332
+ toJSON() {
5333
+ const data = {};
5334
+ data.identifier = this.identifier;
5335
+ if (this.data !== undefined) {
5336
+ data.data = this.data;
5337
+ }
5338
+ if (this.links !== undefined) {
5339
+ data.links = this.links;
5340
+ }
5341
+ if (this.errors !== undefined) {
5342
+ data.errors = this.errors;
5343
+ }
5344
+ if (this.meta !== undefined) {
5345
+ data.meta = this.meta;
5346
+ }
5347
+ return data;
5348
+ }
5349
+ }
5350
+ async function _request2(link, options) {
5351
+ const href = this.links?.[link];
5352
+ if (!href) {
5353
+ return null;
5354
+ }
5355
+ options.method = options.method || 'GET';
5356
+ const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
5357
+ url: urlFromLink(href)
5358
+ }));
5359
+ return response.content;
5360
+ }
5361
+ defineSignal(Document.prototype, 'data');
5362
+ defineSignal(Document.prototype, 'links');
5363
+ defineSignal(Document.prototype, 'errors');
5364
+ defineSignal(Document.prototype, 'meta');
5365
+
5366
+ /**
5367
+ * @module @ember-data/store
5368
+ */
5369
+
5370
+ /**
5371
+ * A service which an application may provide to the store via
5372
+ * the store's `lifetimes` property to configure the behavior
5373
+ * of the CacheHandler.
5374
+ *
5375
+ * The default behavior for request lifetimes is to never expire
5376
+ * unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
5377
+ *
5378
+ * Implementing this service allows you to programatically define
5379
+ * when a request should be considered expired.
5380
+ *
5381
+ * @class <Interface> LifetimesService
5382
+ * @public
5383
+ */
5384
+
5385
+ const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
5386
+ function isErrorDocument(document) {
5387
+ return 'errors' in document;
5388
+ }
5389
+ function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
5390
+ const {
5391
+ identifier
5392
+ } = options;
5393
+ if (!document) {
5394
+ assert(`The CacheHandler expected response content but none was found`, !options.shouldHydrate);
5395
+ return document;
5396
+ }
5397
+ if (isErrorDocument(document)) {
5398
+ if (!identifier && !options.shouldHydrate) {
5399
+ return document;
5400
+ }
5401
+ let doc;
5402
+ if (identifier) {
5403
+ doc = store._documentCache.get(identifier);
5404
+ }
5405
+ if (!doc) {
5406
+ doc = new Document(store, identifier);
5407
+ copyDocumentProperties(doc, document);
5408
+ if (identifier) {
5409
+ store._documentCache.set(identifier, doc);
5410
+ }
5411
+ } else if (!isFromCache) {
5412
+ doc.data = undefined;
5413
+ copyDocumentProperties(doc, document);
5414
+ }
5415
+ return options.shouldHydrate ? doc : document;
5416
+ }
5417
+ if (Array.isArray(document.data)) {
5418
+ const {
5419
+ recordArrayManager
5420
+ } = store;
5421
+ if (!identifier) {
5422
+ if (!options.shouldHydrate) {
5423
+ return document;
5424
+ }
5425
+ const data = recordArrayManager.createArray({
5426
+ type: request.url,
5427
+ identifiers: document.data,
5428
+ doc: document,
5429
+ query: request
5430
+ });
5431
+ const doc = new Document(store, null);
5432
+ doc.data = data;
5433
+ doc.meta = document.meta;
5434
+ doc.links = document.links;
5435
+ return doc;
5436
+ }
5437
+ let managed = recordArrayManager._keyedArrays.get(identifier.lid);
5438
+ if (!managed) {
5439
+ managed = recordArrayManager.createArray({
5440
+ type: identifier.lid,
5441
+ identifiers: document.data,
5442
+ doc: document
5443
+ });
5444
+ recordArrayManager._keyedArrays.set(identifier.lid, managed);
5445
+ const doc = new Document(store, identifier);
5446
+ doc.data = managed;
5447
+ doc.meta = document.meta;
5448
+ doc.links = document.links;
5449
+ store._documentCache.set(identifier, doc);
5450
+ return options.shouldHydrate ? doc : document;
5451
+ } else {
5452
+ const doc = store._documentCache.get(identifier);
5453
+ if (!isFromCache) {
5454
+ recordArrayManager.populateManagedArray(managed, document.data, document);
5455
+ doc.data = managed;
5456
+ doc.meta = document.meta;
5457
+ doc.links = document.links;
5279
5458
  }
5459
+ return options.shouldHydrate ? doc : document;
5280
5460
  }
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;
5461
+ } else {
5462
+ if (!identifier && !options.shouldHydrate) {
5463
+ return document;
5340
5464
  }
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;
5465
+ const data = document.data ? store.peekRecord(document.data) : null;
5466
+ let doc;
5467
+ if (identifier) {
5468
+ doc = store._documentCache.get(identifier);
5348
5469
  }
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;
5470
+ if (!doc) {
5471
+ doc = new Document(store, identifier);
5472
+ doc.data = data;
5473
+ copyDocumentProperties(doc, document);
5474
+ if (identifier) {
5475
+ store._documentCache.set(identifier, doc);
5476
+ }
5477
+ } else if (!isFromCache) {
5478
+ doc.data = data;
5479
+ copyDocumentProperties(doc, document);
5356
5480
  }
5357
- assert(`No adapter was found for '${modelName}' and no 'application' adapter was found as a fallback.`);
5481
+ return options.shouldHydrate ? doc : document;
5358
5482
  }
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;
5483
+ }
5484
+ function calcShouldFetch(store, request, hasCachedValue, identifier) {
5485
+ const {
5486
+ cacheOptions
5487
+ } = request;
5488
+ return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
5489
+ }
5490
+ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
5491
+ const {
5492
+ cacheOptions
5493
+ } = request;
5494
+ return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false));
5495
+ }
5496
+ function isMutation(request) {
5497
+ return Boolean(request.op && MUTATION_OPS.has(request.op));
5498
+ }
5499
+ function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
5500
+ const {
5501
+ store
5502
+ } = context.request;
5503
+ const shouldHydrate = context.request[EnableHydration] || false;
5504
+ let isMut = false;
5505
+ if (isMutation(context.request)) {
5506
+ isMut = true;
5507
+ // TODO should we handle multiple records in request.records by iteratively calling willCommit for each
5508
+ const record = context.request.data?.record || context.request.records?.[0];
5509
+ assert(`Expected to receive a list of records included in the ${context.request.op} request`, record || !shouldHydrate);
5510
+ if (record) {
5511
+ store.cache.willCommit(record, context);
5387
5512
  }
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;
5513
+ }
5514
+ if (store.lifetimes?.willRequest) {
5515
+ store.lifetimes.willRequest(context.request, identifier, store);
5516
+ }
5517
+ const promise = next(context.request).then(document => {
5518
+ store.requestManager._pending.delete(context.id);
5519
+ store._enableAsyncFlush = true;
5520
+ let response;
5521
+ store._join(() => {
5522
+ if (isMutation(context.request)) {
5523
+ const record = context.request.data?.record || context.request.records?.[0];
5524
+ if (record) {
5525
+ response = store.cache.didCommit(record, document);
5526
+
5527
+ // a mutation combined with a 204 has no cache impact when no known records were involved
5528
+ // a createRecord with a 201 with an empty response and no known records should similarly
5529
+ // have no cache impact
5530
+ } else if (isCacheAffecting(document)) {
5531
+ response = store.cache.put(document);
5532
+ }
5533
+ } else {
5534
+ response = store.cache.put(document);
5535
+ }
5536
+ response = maybeUpdateUiObjects(store, context.request, {
5537
+ shouldHydrate,
5538
+ shouldFetch,
5539
+ shouldBackgroundFetch,
5540
+ identifier
5541
+ }, response, false);
5542
+ });
5543
+ store._enableAsyncFlush = null;
5544
+ if (store.lifetimes?.didRequest) {
5545
+ store.lifetimes.didRequest(context.request, document.response, identifier, store);
5395
5546
  }
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;
5547
+ if (shouldFetch) {
5548
+ return response;
5549
+ } else if (shouldBackgroundFetch) {
5550
+ store.notifications._flush();
5403
5551
  }
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;
5552
+ }, error => {
5553
+ store.requestManager._pending.delete(context.id);
5554
+ if (context.request.signal?.aborted) {
5555
+ throw error;
5412
5556
  }
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();
5557
+ store.requestManager._pending.delete(context.id);
5558
+ store._enableAsyncFlush = true;
5559
+ let response;
5560
+ store._join(() => {
5561
+ if (isMutation(context.request)) {
5562
+ // TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
5563
+ // currently we let the response remain undefiend.
5564
+ const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
5565
+ const record = context.request.data?.record || context.request.records?.[0];
5566
+ store.cache.commitWasRejected(record, errors);
5567
+ // re-throw the original error to preserve `errors` property.
5568
+ throw error;
5569
+ } else {
5570
+ response = store.cache.put(error);
5571
+ response = maybeUpdateUiObjects(store, context.request, {
5572
+ shouldHydrate,
5573
+ shouldFetch,
5574
+ shouldBackgroundFetch,
5575
+ identifier
5576
+ }, response, false);
5419
5577
  }
5578
+ });
5579
+ store._enableAsyncFlush = null;
5580
+ if (identifier && store.lifetimes?.didRequest) {
5581
+ store.lifetimes.didRequest(context.request, error.response, identifier, store);
5420
5582
  }
5421
- for (let serializerName in this._serializerCache) {
5422
- let serializer = this._serializerCache[serializerName];
5423
- if (typeof serializer.destroy === 'function') {
5424
- serializer.destroy();
5425
- }
5583
+ if (!shouldBackgroundFetch) {
5584
+ const newError = cloneError(error);
5585
+ newError.content = response;
5586
+ throw newError;
5587
+ } else {
5588
+ store.notifications._flush();
5426
5589
  }
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);
5590
+ });
5591
+ if (!isMut) {
5592
+ return promise;
5437
5593
  }
5594
+ assert(`Expected a mutation`, isMutation(context.request));
5595
+
5596
+ // for mutations we need to enqueue the promise with the requestStateService
5597
+ // TODO should we enque a request per record in records?
5598
+ const record = context.request.data?.record || context.request.records?.[0];
5599
+ return store._requestCache._enqueue(promise, {
5600
+ data: [{
5601
+ op: 'saveRecord',
5602
+ recordIdentifier: record,
5603
+ options: undefined
5604
+ }]
5605
+ });
5438
5606
  }
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
- };
5607
+ function isAggregateError(error) {
5608
+ return error instanceof AggregateError || error.name === 'AggregateError' && Array.isArray(error.errors);
5450
5609
  }
5451
- function isMaybeIdentifier(maybeIdentifier) {
5452
- return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
5610
+ // TODO @runspired, consider if we should deep freeze errors (potentially only in debug) vs cloning them
5611
+ function cloneError(error) {
5612
+ const isAggregate = isAggregateError(error);
5613
+ const cloned = isAggregate ? new AggregateError(structuredClone(error.errors), error.message) : new Error(error.message);
5614
+ cloned.stack = error.stack;
5615
+ cloned.error = error.error;
5616
+
5617
+ // copy over enumerable properties
5618
+ Object.assign(cloned, error);
5619
+ return cloned;
5453
5620
  }
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);
5621
+
5622
+ /**
5623
+ * A CacheHandler that adds support for using an EmberData Cache with a RequestManager.
5624
+ *
5625
+ * This handler will only run when a request has supplied a `store` instance. Requests
5626
+ * issued by the store via `store.request()` will automatically have the `store` instance
5627
+ * attached to the request.
5628
+ *
5629
+ * ```ts
5630
+ * requestManager.request({
5631
+ * store: store,
5632
+ * url: '/api/posts',
5633
+ * method: 'GET'
5634
+ * });
5635
+ * ```
5636
+ *
5637
+ * When this handler elects to handle a request, it will return the raw `StructuredDocument`
5638
+ * unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
5639
+ * return a `Document` instance that will automatically update the UI when the cache is updated
5640
+ * in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
5641
+ *
5642
+ * When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
5643
+ * means that if desired you can issue requests that utilize the cache without needing to also
5644
+ * utilize Record instances if desired.
5645
+ *
5646
+ * Said differently, you could elect to issue all requests via a RequestManager, without ever using
5647
+ * the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
5648
+ * necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
5649
+ * approach of EmberData allows for this flexibility.
5650
+ *
5651
+ * ```ts
5652
+ * import { EnableHydration } from '@warp-drive/core-types/request';
5653
+ *
5654
+ * requestManager.request({
5655
+ * store: store,
5656
+ * url: '/api/posts',
5657
+ * method: 'GET',
5658
+ * [EnableHydration]: true
5659
+ * });
5660
+ *
5661
+ * @typedoc
5662
+ */
5663
+ const CacheHandler = {
5664
+ request(context, next) {
5665
+ // if we have no cache or no cache-key skip cache handling
5666
+ if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
5667
+ return next(context.request);
5459
5668
  }
5460
- assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
5461
5669
  const {
5462
- type
5463
- } = identifier;
5670
+ store
5671
+ } = context.request;
5672
+ const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
5673
+ const peeked = identifier ? store.cache.peekRequest(identifier) : null;
5464
5674
 
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
- }
5675
+ // determine if we should skip cache
5676
+ if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
5677
+ return fetchContentAndHydrate(next, context, identifier, true, false);
5678
+ }
5679
+
5680
+ // if we have not skipped cache, determine if we should update behind the scenes
5681
+ if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
5682
+ const promise = fetchContentAndHydrate(next, context, identifier, false, true);
5683
+ store.requestManager._pending.set(context.id, promise);
5684
+ }
5685
+ const shouldHydrate = context.request[EnableHydration] || false;
5686
+ if ('error' in peeked) {
5687
+ const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5688
+ shouldHydrate,
5689
+ identifier
5690
+ }, peeked.content, true) : peeked.content;
5691
+ const newError = cloneError(peeked);
5692
+ newError.content = content;
5693
+ throw newError;
5487
5694
  }
5695
+ const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5696
+ shouldHydrate,
5697
+ identifier
5698
+ }, peeked.content, true) : peeked.content;
5699
+ return result;
5700
+ }
5701
+ };
5702
+ function copyDocumentProperties(target, source) {
5703
+ if ('links' in source) {
5704
+ target.links = source.links;
5705
+ }
5706
+ if ('meta' in source) {
5707
+ target.meta = source.meta;
5708
+ }
5709
+ if ('errors' in source) {
5710
+ target.errors = source.errors;
5488
5711
  }
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
5712
  }
5507
- function extractIdentifierFromRecord(recordOrPromiseRecord) {
5508
- if (!recordOrPromiseRecord) {
5509
- return null;
5713
+ function isCacheAffecting(document) {
5714
+ if (!isMutation(document.request)) {
5715
+ return true;
5510
5716
  }
5511
- const extract = recordIdentifierFor;
5512
- return extract(recordOrPromiseRecord);
5717
+ // a mutation combined with a 204 has no cache impact when no known records were involved
5718
+ // a createRecord with a 201 with an empty response and no known records should similarly
5719
+ // have no cache impact
5720
+
5721
+ if (document.request.op === 'createRecord' && document.response?.status === 201) {
5722
+ return document.content ? Object.keys(document.content).length > 0 : false;
5723
+ }
5724
+ return document.response?.status !== 204;
5513
5725
  }
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 };
5726
+ export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, coerceId as a, Collection as b, constructResource as c, SOURCE as d, ensureStringId as e, fastPush as f, removeRecordDataFor as g, setRecordIdentifier as h, isStableIdentifier as i, StoreMap as j, setCacheFor as k, normalizeModelName as l, setIdentifierGenerationMethod as m, notifyArray as n, setIdentifierUpdateMethod as o, peekCache as p, setIdentifierForgetMethod as q, recordIdentifierFor as r, storeFor as s, setIdentifierResetMethod as t, setKeyInfoForResource as u };