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