@ember-data/store 5.4.0-beta.0 → 5.4.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/addon/-private.js +1 -1
- package/addon/{store-service-aee9dd76.js → cache-handler-XLbbNJdo.js} +1003 -1008
- package/addon/cache-handler-XLbbNJdo.js.map +1 -0
- package/addon/index.js +1 -1
- package/addon/index.js.map +1 -1
- package/package.json +66 -41
- package/addon/store-service-aee9dd76.js.map +0 -1
|
@@ -1,402 +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 {
|
|
3
|
+
import { SkipCache, EnableHydration } from '@warp-drive/core-types/request';
|
|
4
|
+
import { macroCondition, getOwnConfig } from '@embroider/macros';
|
|
5
|
+
import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '@warp-drive/core-types/identifier';
|
|
6
6
|
import { dasherize } from '@ember/string';
|
|
7
|
+
import { defineSignal, addToTransaction, createSignal, subscribe, createArrayTags, addTransactionCB } from '@ember-data/tracking/-private';
|
|
7
8
|
import { _backburner } from '@ember/runloop';
|
|
8
|
-
import {
|
|
9
|
-
import { tagForProperty } from '@ember/-internals/metal';
|
|
10
|
-
import { dependentKeyCompat } from '@ember/object/compat';
|
|
11
|
-
import { dirtyTag } from '@glimmer/validator';
|
|
12
|
-
import Ember from 'ember';
|
|
13
|
-
function _initializerDefineProperty(target, property, descriptor, context) {
|
|
14
|
-
if (!descriptor) return;
|
|
15
|
-
Object.defineProperty(target, property, {
|
|
16
|
-
enumerable: descriptor.enumerable,
|
|
17
|
-
configurable: descriptor.configurable,
|
|
18
|
-
writable: descriptor.writable,
|
|
19
|
-
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function _classPrivateFieldBase(receiver, privateKey) {
|
|
23
|
-
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
24
|
-
throw new TypeError("attempted to use private field on non-instance");
|
|
25
|
-
}
|
|
26
|
-
return receiver;
|
|
27
|
-
}
|
|
28
|
-
var id = 0;
|
|
29
|
-
function _classPrivateFieldKey(name) {
|
|
30
|
-
return "__private_" + id++ + "_" + name;
|
|
31
|
-
}
|
|
32
|
-
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
|
33
|
-
var desc = {};
|
|
34
|
-
Object.keys(descriptor).forEach(function (key) {
|
|
35
|
-
desc[key] = descriptor[key];
|
|
36
|
-
});
|
|
37
|
-
desc.enumerable = !!desc.enumerable;
|
|
38
|
-
desc.configurable = !!desc.configurable;
|
|
39
|
-
if ('value' in desc || desc.initializer) {
|
|
40
|
-
desc.writable = true;
|
|
41
|
-
}
|
|
42
|
-
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
|
|
43
|
-
return decorator(target, property, desc) || desc;
|
|
44
|
-
}, desc);
|
|
45
|
-
if (context && desc.initializer !== void 0) {
|
|
46
|
-
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
|
|
47
|
-
desc.initializer = undefined;
|
|
48
|
-
}
|
|
49
|
-
if (desc.initializer === void 0) {
|
|
50
|
-
Object.defineProperty(target, property, desc);
|
|
51
|
-
desc = null;
|
|
52
|
-
}
|
|
53
|
-
return desc;
|
|
54
|
-
}
|
|
55
|
-
var _class$2, _descriptor$2, _descriptor2$1, _descriptor3, _descriptor4, _store, _request;
|
|
56
|
-
function urlFromLink(link) {
|
|
57
|
-
if (typeof link === 'string') return link;
|
|
58
|
-
return link.href;
|
|
59
|
-
}
|
|
60
|
-
let Document = (_class$2 = (_store = /*#__PURE__*/_classPrivateFieldKey("store"), _request = /*#__PURE__*/_classPrivateFieldKey("request"), class Document {
|
|
61
|
-
constructor(store, identifier) {
|
|
62
|
-
Object.defineProperty(this, _request, {
|
|
63
|
-
value: _request2
|
|
64
|
-
});
|
|
65
|
-
_initializerDefineProperty(this, "links", _descriptor$2, this);
|
|
66
|
-
_initializerDefineProperty(this, "data", _descriptor2$1, this);
|
|
67
|
-
_initializerDefineProperty(this, "errors", _descriptor3, this);
|
|
68
|
-
_initializerDefineProperty(this, "meta", _descriptor4, this);
|
|
69
|
-
Object.defineProperty(this, _store, {
|
|
70
|
-
writable: true,
|
|
71
|
-
value: void 0
|
|
72
|
-
});
|
|
73
|
-
_classPrivateFieldBase(this, _store)[_store] = store;
|
|
74
|
-
this.identifier = identifier;
|
|
75
|
-
}
|
|
76
|
-
fetch(options = {}) {
|
|
77
|
-
assert(`No self link`, this.links?.self);
|
|
78
|
-
options.cacheOptions = options.cacheOptions || {};
|
|
79
|
-
options.cacheOptions.key = this.identifier?.lid;
|
|
80
|
-
return _classPrivateFieldBase(this, _request)[_request]('self', options);
|
|
81
|
-
}
|
|
82
|
-
next(options) {
|
|
83
|
-
return _classPrivateFieldBase(this, _request)[_request]('next', options);
|
|
84
|
-
}
|
|
85
|
-
prev(options) {
|
|
86
|
-
return _classPrivateFieldBase(this, _request)[_request]('prev', options);
|
|
87
|
-
}
|
|
88
|
-
first(options) {
|
|
89
|
-
return _classPrivateFieldBase(this, _request)[_request]('first', options);
|
|
90
|
-
}
|
|
91
|
-
last(options) {
|
|
92
|
-
return _classPrivateFieldBase(this, _request)[_request]('last', options);
|
|
93
|
-
}
|
|
94
|
-
toJSON() {
|
|
95
|
-
const data = {};
|
|
96
|
-
data.identifier = this.identifier;
|
|
97
|
-
if (this.data !== undefined) {
|
|
98
|
-
data.data = this.data;
|
|
99
|
-
}
|
|
100
|
-
if (this.links !== undefined) {
|
|
101
|
-
data.links = this.links;
|
|
102
|
-
}
|
|
103
|
-
if (this.errors !== undefined) {
|
|
104
|
-
data.errors = this.errors;
|
|
105
|
-
}
|
|
106
|
-
if (this.meta !== undefined) {
|
|
107
|
-
data.meta = this.meta;
|
|
108
|
-
}
|
|
109
|
-
return data;
|
|
110
|
-
}
|
|
111
|
-
}), (_descriptor$2 = _applyDecoratedDescriptor(_class$2.prototype, "links", [tracked], {
|
|
112
|
-
configurable: true,
|
|
113
|
-
enumerable: true,
|
|
114
|
-
writable: true,
|
|
115
|
-
initializer: null
|
|
116
|
-
}), _descriptor2$1 = _applyDecoratedDescriptor(_class$2.prototype, "data", [tracked], {
|
|
117
|
-
configurable: true,
|
|
118
|
-
enumerable: true,
|
|
119
|
-
writable: true,
|
|
120
|
-
initializer: null
|
|
121
|
-
}), _descriptor3 = _applyDecoratedDescriptor(_class$2.prototype, "errors", [tracked], {
|
|
122
|
-
configurable: true,
|
|
123
|
-
enumerable: true,
|
|
124
|
-
writable: true,
|
|
125
|
-
initializer: null
|
|
126
|
-
}), _descriptor4 = _applyDecoratedDescriptor(_class$2.prototype, "meta", [tracked], {
|
|
127
|
-
configurable: true,
|
|
128
|
-
enumerable: true,
|
|
129
|
-
writable: true,
|
|
130
|
-
initializer: null
|
|
131
|
-
})), _class$2);
|
|
132
|
-
async function _request2(link, options = {}) {
|
|
133
|
-
const href = this.links?.[link];
|
|
134
|
-
if (!href) {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
|
|
138
|
-
url: urlFromLink(href)
|
|
139
|
-
}));
|
|
140
|
-
return response.content;
|
|
141
|
-
}
|
|
142
|
-
function isErrorDocument(document) {
|
|
143
|
-
return 'errors' in document;
|
|
144
|
-
}
|
|
145
|
-
function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
146
|
-
const {
|
|
147
|
-
identifier
|
|
148
|
-
} = options;
|
|
149
|
-
if (isErrorDocument(document)) {
|
|
150
|
-
if (!identifier && !options.shouldHydrate) {
|
|
151
|
-
return document;
|
|
152
|
-
}
|
|
153
|
-
let doc;
|
|
154
|
-
if (identifier) {
|
|
155
|
-
doc = store._documentCache.get(identifier);
|
|
156
|
-
}
|
|
157
|
-
if (!doc) {
|
|
158
|
-
doc = new Document(store, identifier);
|
|
159
|
-
copyDocumentProperties(doc, document);
|
|
160
|
-
if (identifier) {
|
|
161
|
-
store._documentCache.set(identifier, doc);
|
|
162
|
-
}
|
|
163
|
-
} else if (!isFromCache) {
|
|
164
|
-
doc.data = undefined;
|
|
165
|
-
copyDocumentProperties(doc, document);
|
|
166
|
-
}
|
|
167
|
-
return options.shouldHydrate ? doc : document;
|
|
168
|
-
}
|
|
169
|
-
if (Array.isArray(document.data)) {
|
|
170
|
-
const {
|
|
171
|
-
recordArrayManager
|
|
172
|
-
} = store;
|
|
173
|
-
if (!identifier) {
|
|
174
|
-
if (!options.shouldHydrate) {
|
|
175
|
-
return document;
|
|
176
|
-
}
|
|
177
|
-
const data = recordArrayManager.createArray({
|
|
178
|
-
type: request.url,
|
|
179
|
-
identifiers: document.data,
|
|
180
|
-
doc: document,
|
|
181
|
-
query: request
|
|
182
|
-
});
|
|
183
|
-
const doc = new Document(store, null);
|
|
184
|
-
doc.data = data;
|
|
185
|
-
doc.meta = document.meta;
|
|
186
|
-
doc.links = document.links;
|
|
187
|
-
return doc;
|
|
188
|
-
}
|
|
189
|
-
let managed = recordArrayManager._keyedArrays.get(identifier.lid);
|
|
190
|
-
if (!managed) {
|
|
191
|
-
managed = recordArrayManager.createArray({
|
|
192
|
-
type: identifier.lid,
|
|
193
|
-
identifiers: document.data,
|
|
194
|
-
doc: document
|
|
195
|
-
});
|
|
196
|
-
recordArrayManager._keyedArrays.set(identifier.lid, managed);
|
|
197
|
-
const doc = new Document(store, identifier);
|
|
198
|
-
doc.data = managed;
|
|
199
|
-
doc.meta = document.meta;
|
|
200
|
-
doc.links = document.links;
|
|
201
|
-
store._documentCache.set(identifier, doc);
|
|
202
|
-
return options.shouldHydrate ? doc : document;
|
|
203
|
-
} else {
|
|
204
|
-
const doc = store._documentCache.get(identifier);
|
|
205
|
-
if (!isFromCache) {
|
|
206
|
-
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
207
|
-
doc.data = managed;
|
|
208
|
-
doc.meta = document.meta;
|
|
209
|
-
doc.links = document.links;
|
|
210
|
-
}
|
|
211
|
-
return options.shouldHydrate ? doc : document;
|
|
212
|
-
}
|
|
213
|
-
} else {
|
|
214
|
-
if (!identifier && !options.shouldHydrate) {
|
|
215
|
-
return document;
|
|
216
|
-
}
|
|
217
|
-
const data = document.data ? store.peekRecord(document.data) : null;
|
|
218
|
-
let doc;
|
|
219
|
-
if (identifier) {
|
|
220
|
-
doc = store._documentCache.get(identifier);
|
|
221
|
-
}
|
|
222
|
-
if (!doc) {
|
|
223
|
-
doc = new Document(store, identifier);
|
|
224
|
-
doc.data = data;
|
|
225
|
-
copyDocumentProperties(doc, document);
|
|
226
|
-
if (identifier) {
|
|
227
|
-
store._documentCache.set(identifier, doc);
|
|
228
|
-
}
|
|
229
|
-
} else if (!isFromCache) {
|
|
230
|
-
doc.data = data;
|
|
231
|
-
copyDocumentProperties(doc, document);
|
|
232
|
-
}
|
|
233
|
-
return options.shouldHydrate ? doc : document;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
|
|
237
|
-
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
238
|
-
const {
|
|
239
|
-
cacheOptions
|
|
240
|
-
} = request;
|
|
241
|
-
return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier) : false);
|
|
242
|
-
}
|
|
243
|
-
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
244
|
-
const {
|
|
245
|
-
cacheOptions
|
|
246
|
-
} = request;
|
|
247
|
-
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier) : false));
|
|
248
|
-
}
|
|
249
|
-
function isMutation(request) {
|
|
250
|
-
return Boolean(request.op && MUTATION_OPS.has(request.op));
|
|
251
|
-
}
|
|
252
|
-
function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
|
|
253
|
-
const {
|
|
254
|
-
store
|
|
255
|
-
} = context.request;
|
|
256
|
-
const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
|
|
257
|
-
let isMut = false;
|
|
258
|
-
if (isMutation(context.request)) {
|
|
259
|
-
isMut = true;
|
|
260
|
-
const record = context.request.data?.record;
|
|
261
|
-
assert(`Expected to receive a list of records included in the ${context.request.op} request`, record);
|
|
262
|
-
store.cache.willCommit(record, context);
|
|
263
|
-
}
|
|
264
|
-
const promise = next(context.request).then(document => {
|
|
265
|
-
store.requestManager._pending.delete(context.id);
|
|
266
|
-
store._enableAsyncFlush = true;
|
|
267
|
-
let response;
|
|
268
|
-
store._join(() => {
|
|
269
|
-
if (isMutation(context.request)) {
|
|
270
|
-
response = store.cache.didCommit(context.request.data.record, document);
|
|
271
|
-
} else {
|
|
272
|
-
response = store.cache.put(document);
|
|
273
|
-
}
|
|
274
|
-
response = maybeUpdateUiObjects(store, context.request, {
|
|
275
|
-
shouldHydrate,
|
|
276
|
-
shouldFetch,
|
|
277
|
-
shouldBackgroundFetch,
|
|
278
|
-
identifier
|
|
279
|
-
}, response, false);
|
|
280
|
-
});
|
|
281
|
-
store._enableAsyncFlush = null;
|
|
282
|
-
if (shouldFetch) {
|
|
283
|
-
return response;
|
|
284
|
-
} else if (shouldBackgroundFetch) {
|
|
285
|
-
store.notifications._flush();
|
|
286
|
-
}
|
|
287
|
-
}, error => {
|
|
288
|
-
store.requestManager._pending.delete(context.id);
|
|
289
|
-
if (context.request.signal?.aborted) {
|
|
290
|
-
throw error;
|
|
291
|
-
}
|
|
292
|
-
store.requestManager._pending.delete(context.id);
|
|
293
|
-
store._enableAsyncFlush = true;
|
|
294
|
-
let response;
|
|
295
|
-
store._join(() => {
|
|
296
|
-
if (isMutation(context.request)) {
|
|
297
|
-
// TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
|
|
298
|
-
// currently we let the response remain undefiend.
|
|
299
|
-
const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
|
|
300
|
-
store.cache.commitWasRejected(context.request.data.record, errors);
|
|
301
|
-
// re-throw the original error to preserve `errors` property.
|
|
302
|
-
throw error;
|
|
303
|
-
} else {
|
|
304
|
-
response = store.cache.put(error);
|
|
305
|
-
response = maybeUpdateUiObjects(store, context.request, {
|
|
306
|
-
shouldHydrate,
|
|
307
|
-
shouldFetch,
|
|
308
|
-
shouldBackgroundFetch,
|
|
309
|
-
identifier
|
|
310
|
-
}, response, false);
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
store._enableAsyncFlush = null;
|
|
314
|
-
if (!shouldBackgroundFetch) {
|
|
315
|
-
const newError = cloneError(error);
|
|
316
|
-
newError.content = response;
|
|
317
|
-
throw newError;
|
|
318
|
-
} else {
|
|
319
|
-
store.notifications._flush();
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
if (!isMut) {
|
|
323
|
-
return promise;
|
|
324
|
-
}
|
|
325
|
-
assert(`Expected a mutation`, isMutation(context.request));
|
|
326
|
-
|
|
327
|
-
// for mutations we need to enqueue the promise with the requestStateService
|
|
328
|
-
return store._requestCache._enqueue(promise, {
|
|
329
|
-
data: [{
|
|
330
|
-
op: 'saveRecord',
|
|
331
|
-
recordIdentifier: context.request.data.record,
|
|
332
|
-
options: undefined
|
|
333
|
-
}]
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
function cloneError(error) {
|
|
337
|
-
const cloned = new Error(error.message);
|
|
338
|
-
cloned.stack = error.stack;
|
|
339
|
-
cloned.error = error.error;
|
|
340
|
-
return cloned;
|
|
341
|
-
}
|
|
342
|
-
const SkipCache = Symbol.for('ember-data:skip-cache');
|
|
343
|
-
const EnableHydration = Symbol.for('ember-data:enable-hydration');
|
|
344
|
-
const CacheHandler = {
|
|
345
|
-
request(context, next) {
|
|
346
|
-
// if we have no cache or no cache-key skip cache handling
|
|
347
|
-
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
348
|
-
return next(context.request);
|
|
349
|
-
}
|
|
350
|
-
const {
|
|
351
|
-
store
|
|
352
|
-
} = context.request;
|
|
353
|
-
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
354
|
-
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
9
|
+
import { compat } from '@ember-data/tracking';
|
|
355
10
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// if we have not skipped cache, determine if we should update behind the scenes
|
|
362
|
-
if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
|
|
363
|
-
let promise = fetchContentAndHydrate(next, context, identifier, false, true);
|
|
364
|
-
store.requestManager._pending.set(context.id, promise);
|
|
365
|
-
}
|
|
366
|
-
const shouldHydrate = context.request[EnableHydration] || false;
|
|
367
|
-
if ('error' in peeked) {
|
|
368
|
-
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
369
|
-
shouldHydrate,
|
|
370
|
-
identifier
|
|
371
|
-
}, peeked.content, true) : peeked.content;
|
|
372
|
-
const newError = cloneError(peeked);
|
|
373
|
-
newError.content = content;
|
|
374
|
-
throw newError;
|
|
375
|
-
}
|
|
376
|
-
return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
377
|
-
shouldHydrate,
|
|
378
|
-
identifier
|
|
379
|
-
}, peeked.content, true) : peeked.content);
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
function copyDocumentProperties(target, source) {
|
|
383
|
-
if ('links' in source) {
|
|
384
|
-
target.links = source.links;
|
|
385
|
-
}
|
|
386
|
-
if ('meta' in source) {
|
|
387
|
-
target.meta = source.meta;
|
|
388
|
-
}
|
|
389
|
-
if ('errors' in source) {
|
|
390
|
-
target.errors = source.errors;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
11
|
+
/**
|
|
12
|
+
@module @ember-data/store
|
|
13
|
+
*/
|
|
393
14
|
|
|
394
|
-
// Used by the store to normalize IDs entering the store. Despite the fact
|
|
395
|
-
// that developers may provide IDs as numbers (e.g., `store.findRecord('person', 1)`),
|
|
396
|
-
// it is important that internally we use strings, since IDs may be serialized
|
|
397
|
-
// and lose type information. For example, Ember's router may put a record's
|
|
398
|
-
// ID into the URL, and if we later try to deserialize that URL and find the
|
|
399
|
-
// corresponding record, we will not know if it is a string or a number.
|
|
400
15
|
function coerceId(id) {
|
|
401
16
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_ID)) {
|
|
402
17
|
let normalized;
|
|
@@ -429,14 +44,6 @@ function ensureStringId(id) {
|
|
|
429
44
|
assert(`Expected id to be a string or number, received ${String(id)}`, normalized !== null);
|
|
430
45
|
return normalized;
|
|
431
46
|
}
|
|
432
|
-
|
|
433
|
-
// provided for additional debuggability
|
|
434
|
-
const DEBUG_CLIENT_ORIGINATED = Symbol('record-originated-on-client');
|
|
435
|
-
const DEBUG_IDENTIFIER_BUCKET = Symbol('identifier-bucket');
|
|
436
|
-
const DEBUG_STALE_CACHE_OWNER = Symbol('warpDriveStaleCache');
|
|
437
|
-
|
|
438
|
-
// also present in production
|
|
439
|
-
const CACHE_OWNER = Symbol('warpDriveCache');
|
|
440
47
|
function normalizeModelName(type) {
|
|
441
48
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_TYPES)) {
|
|
442
49
|
const result = dasherize(type);
|
|
@@ -465,7 +72,7 @@ function installPolyfill() {
|
|
|
465
72
|
// we might be able to optimize this by requesting more bytes than we need at a time
|
|
466
73
|
const rng = function () {
|
|
467
74
|
// WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
|
|
468
|
-
|
|
75
|
+
const rnds8 = new Uint8Array(16);
|
|
469
76
|
if (!CRYPTO.getRandomValues && !isFastBoot) {
|
|
470
77
|
throw new Error(`Unable to generate bytes for UUID`);
|
|
471
78
|
}
|
|
@@ -481,12 +88,12 @@ function installPolyfill() {
|
|
|
481
88
|
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
|
482
89
|
}
|
|
483
90
|
const bytesToUuid = function (buf) {
|
|
484
|
-
|
|
91
|
+
const bth = byteToHex;
|
|
485
92
|
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
|
486
93
|
return [bth[buf[0]], bth[buf[1]], bth[buf[2]], bth[buf[3]], '-', bth[buf[4]], bth[buf[5]], '-', bth[buf[6]], bth[buf[7]], '-', bth[buf[8]], bth[buf[9]], '-', bth[buf[10]], bth[buf[11]], bth[buf[12]], bth[buf[13]], bth[buf[14]], bth[buf[15]]].join('');
|
|
487
94
|
};
|
|
488
95
|
CRYPTO.randomUUID = function uuidv4() {
|
|
489
|
-
|
|
96
|
+
const rnds = rng();
|
|
490
97
|
|
|
491
98
|
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
|
|
492
99
|
rnds[6] = rnds[6] & 0x0f | 0x40;
|
|
@@ -540,6 +147,7 @@ function freeze(obj) {
|
|
|
540
147
|
|
|
541
148
|
// type IdentifierTypeLookup = { all: Set<StableRecordIdentifier>; id: Map<string, StableRecordIdentifier> };
|
|
542
149
|
// type IdentifiersByType = Map<string, IdentifierTypeLookup>;
|
|
150
|
+
|
|
543
151
|
let configuredForgetMethod;
|
|
544
152
|
let configuredGenerationMethod;
|
|
545
153
|
let configuredResetMethod;
|
|
@@ -647,7 +255,8 @@ class IdentifierCache {
|
|
|
647
255
|
this._cache = {
|
|
648
256
|
resources: new Map(),
|
|
649
257
|
resourcesByType: Object.create(null),
|
|
650
|
-
documents: new Map()
|
|
258
|
+
documents: new Map(),
|
|
259
|
+
polymorphicLidBackMap: new Map()
|
|
651
260
|
};
|
|
652
261
|
}
|
|
653
262
|
|
|
@@ -743,7 +352,7 @@ class IdentifierCache {
|
|
|
743
352
|
*
|
|
744
353
|
* @method peekRecordIdentifier
|
|
745
354
|
* @param resource
|
|
746
|
-
* @
|
|
355
|
+
* @return {StableRecordIdentifier | undefined}
|
|
747
356
|
* @private
|
|
748
357
|
*/
|
|
749
358
|
peekRecordIdentifier(resource) {
|
|
@@ -755,7 +364,7 @@ class IdentifierCache {
|
|
|
755
364
|
Returns `null` if the request does not have a `cacheKey` or `url`.
|
|
756
365
|
@method getOrCreateDocumentIdentifier
|
|
757
366
|
@param request
|
|
758
|
-
@
|
|
367
|
+
@return {StableDocumentIdentifier | null}
|
|
759
368
|
@public
|
|
760
369
|
*/
|
|
761
370
|
getOrCreateDocumentIdentifier(request) {
|
|
@@ -789,7 +398,7 @@ class IdentifierCache {
|
|
|
789
398
|
- this referential stability of the object itself is guaranteed
|
|
790
399
|
@method getOrCreateRecordIdentifier
|
|
791
400
|
@param resource
|
|
792
|
-
@
|
|
401
|
+
@return {StableRecordIdentifier}
|
|
793
402
|
@public
|
|
794
403
|
*/
|
|
795
404
|
getOrCreateRecordIdentifier(resource) {
|
|
@@ -804,12 +413,12 @@ class IdentifierCache {
|
|
|
804
413
|
with the signature `generateMethod({ type }, 'record')`.
|
|
805
414
|
@method createIdentifierForNewRecord
|
|
806
415
|
@param data
|
|
807
|
-
@
|
|
416
|
+
@return {StableRecordIdentifier}
|
|
808
417
|
@public
|
|
809
418
|
*/
|
|
810
419
|
createIdentifierForNewRecord(data) {
|
|
811
|
-
|
|
812
|
-
|
|
420
|
+
const newLid = this._generate(data, 'record');
|
|
421
|
+
const identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
|
|
813
422
|
id: data.id || null,
|
|
814
423
|
type: data.type,
|
|
815
424
|
lid: newLid,
|
|
@@ -847,7 +456,7 @@ class IdentifierCache {
|
|
|
847
456
|
@method updateRecordIdentifier
|
|
848
457
|
@param identifierObject
|
|
849
458
|
@param data
|
|
850
|
-
@
|
|
459
|
+
@return {StableRecordIdentifier}
|
|
851
460
|
@public
|
|
852
461
|
*/
|
|
853
462
|
updateRecordIdentifier(identifierObject, data) {
|
|
@@ -867,7 +476,7 @@ class IdentifierCache {
|
|
|
867
476
|
}
|
|
868
477
|
}
|
|
869
478
|
if (existingIdentifier) {
|
|
870
|
-
|
|
479
|
+
const generatedIdentifier = identifier;
|
|
871
480
|
identifier = this._mergeRecordIdentifiers(keyInfo, generatedIdentifier, existingIdentifier, data);
|
|
872
481
|
|
|
873
482
|
// make sure that the `lid` on the data we are processing matches the lid we kept
|
|
@@ -879,7 +488,7 @@ class IdentifierCache {
|
|
|
879
488
|
console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
|
|
880
489
|
}
|
|
881
490
|
}
|
|
882
|
-
|
|
491
|
+
const id = identifier.id;
|
|
883
492
|
/*#__NOINLINE__*/
|
|
884
493
|
performRecordIdentifierUpdate(identifier, keyInfo, data, this._update);
|
|
885
494
|
const newId = identifier.id;
|
|
@@ -913,16 +522,32 @@ class IdentifierCache {
|
|
|
913
522
|
const kept = this._merge(identifier, existingIdentifier, data);
|
|
914
523
|
const abandoned = kept === identifier ? existingIdentifier : identifier;
|
|
915
524
|
|
|
525
|
+
// get any backreferences before forgetting this identifier, as it will be removed from the cache
|
|
526
|
+
// and we will no longer be able to find them
|
|
527
|
+
const abandonedBackReferences = this._cache.polymorphicLidBackMap.get(abandoned.lid);
|
|
528
|
+
// delete the backreferences for the abandoned identifier so that forgetRecordIdentifier
|
|
529
|
+
// does not try to remove them.
|
|
530
|
+
if (abandonedBackReferences) this._cache.polymorphicLidBackMap.delete(abandoned.lid);
|
|
531
|
+
|
|
916
532
|
// cleanup the identifier we no longer need
|
|
917
533
|
this.forgetRecordIdentifier(abandoned);
|
|
918
534
|
|
|
919
|
-
// ensure a secondary cache entry for
|
|
920
|
-
|
|
535
|
+
// ensure a secondary cache entry for the original lid for the abandoned identifier
|
|
536
|
+
this._cache.resources.set(abandoned.lid, kept);
|
|
921
537
|
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
|
|
538
|
+
// backReferences let us know which other identifiers are pointing at this identifier
|
|
539
|
+
// so we can delete them later if we forget this identifier
|
|
540
|
+
const keptBackReferences = this._cache.polymorphicLidBackMap.get(kept.lid) ?? [];
|
|
541
|
+
keptBackReferences.push(abandoned.lid);
|
|
925
542
|
|
|
543
|
+
// update the backreferences from the abandoned identifier to be for the kept identifier
|
|
544
|
+
if (abandonedBackReferences) {
|
|
545
|
+
abandonedBackReferences.forEach(lid => {
|
|
546
|
+
keptBackReferences.push(lid);
|
|
547
|
+
this._cache.resources.set(lid, kept);
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
this._cache.polymorphicLidBackMap.set(kept.lid, keptBackReferences);
|
|
926
551
|
return kept;
|
|
927
552
|
}
|
|
928
553
|
|
|
@@ -945,6 +570,13 @@ class IdentifierCache {
|
|
|
945
570
|
}
|
|
946
571
|
this._cache.resources.delete(identifier.lid);
|
|
947
572
|
typeSet.lid.delete(identifier.lid);
|
|
573
|
+
const backReferences = this._cache.polymorphicLidBackMap.get(identifier.lid);
|
|
574
|
+
if (backReferences) {
|
|
575
|
+
backReferences.forEach(lid => {
|
|
576
|
+
this._cache.resources.delete(lid);
|
|
577
|
+
});
|
|
578
|
+
this._cache.polymorphicLidBackMap.delete(identifier.lid);
|
|
579
|
+
}
|
|
948
580
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
949
581
|
identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
|
|
950
582
|
}
|
|
@@ -1035,7 +667,7 @@ function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
|
|
|
1035
667
|
} = keyInfo;
|
|
1036
668
|
|
|
1037
669
|
// get the mutable instance behind our proxy wrapper
|
|
1038
|
-
|
|
670
|
+
const wrapper = identifier;
|
|
1039
671
|
identifier = DEBUG_MAP.get(wrapper);
|
|
1040
672
|
if (hasLid(data)) {
|
|
1041
673
|
const lid = data.lid;
|
|
@@ -1124,28 +756,27 @@ function addResourceToCache(cache, identifier) {
|
|
|
1124
756
|
typeSet.id.set(identifier.id, identifier);
|
|
1125
757
|
}
|
|
1126
758
|
}
|
|
1127
|
-
var _class$1, _descriptor$1;
|
|
1128
759
|
|
|
1129
760
|
/**
|
|
1130
761
|
@module @ember-data/store
|
|
1131
762
|
*/
|
|
763
|
+
|
|
1132
764
|
/**
|
|
1133
765
|
@module @ember-data/store
|
|
1134
766
|
*/
|
|
767
|
+
|
|
1135
768
|
/**
|
|
1136
769
|
A `RecordReference` is a low-level API that allows users and
|
|
1137
770
|
addon authors to perform meta-operations on a record.
|
|
1138
771
|
|
|
1139
772
|
@class RecordReference
|
|
1140
773
|
@public
|
|
1141
|
-
@extends Reference
|
|
1142
774
|
*/
|
|
1143
|
-
|
|
775
|
+
class RecordReference {
|
|
1144
776
|
constructor(store, identifier) {
|
|
1145
777
|
// unsubscribe token given to us by the notification manager
|
|
1146
778
|
this.___token = void 0;
|
|
1147
779
|
this.___identifier = void 0;
|
|
1148
|
-
_initializerDefineProperty(this, "_ref", _descriptor$1, this);
|
|
1149
780
|
this.store = store;
|
|
1150
781
|
this.___identifier = identifier;
|
|
1151
782
|
this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
|
|
@@ -1313,14 +944,8 @@ let RecordReference = (_class$1 = class RecordReference {
|
|
|
1313
944
|
}
|
|
1314
945
|
assert(`Unable to fetch record of type ${this.type} without an id`);
|
|
1315
946
|
}
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
enumerable: true,
|
|
1319
|
-
writable: true,
|
|
1320
|
-
initializer: function () {
|
|
1321
|
-
return 0;
|
|
1322
|
-
}
|
|
1323
|
-
}), _class$1);
|
|
947
|
+
}
|
|
948
|
+
defineSignal(RecordReference.prototype, '_ref');
|
|
1324
949
|
|
|
1325
950
|
/**
|
|
1326
951
|
@module @ember-data/store
|
|
@@ -1351,6 +976,8 @@ class CacheCapabilitiesManager {
|
|
|
1351
976
|
if (this._store._cbs) {
|
|
1352
977
|
this._store._schedule('notify', () => this._flushNotifications());
|
|
1353
978
|
} else {
|
|
979
|
+
// TODO @runspired determine if relationship mutations should schedule
|
|
980
|
+
// into join/run vs immediate flush
|
|
1354
981
|
this._flushNotifications();
|
|
1355
982
|
}
|
|
1356
983
|
}
|
|
@@ -1358,7 +985,7 @@ class CacheCapabilitiesManager {
|
|
|
1358
985
|
if (this._willNotify === false) {
|
|
1359
986
|
return;
|
|
1360
987
|
}
|
|
1361
|
-
|
|
988
|
+
const pending = this._pendingNotifies;
|
|
1362
989
|
this._pendingNotifies = new Map();
|
|
1363
990
|
this._willNotify = false;
|
|
1364
991
|
pending.forEach((set, identifier) => {
|
|
@@ -1382,6 +1009,9 @@ class CacheCapabilitiesManager {
|
|
|
1382
1009
|
getSchemaDefinitionService() {
|
|
1383
1010
|
return this._store.getSchemaDefinitionService();
|
|
1384
1011
|
}
|
|
1012
|
+
get schema() {
|
|
1013
|
+
return this._store.schema;
|
|
1014
|
+
}
|
|
1385
1015
|
setRecordId(identifier, id) {
|
|
1386
1016
|
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
1387
1017
|
this._store._instanceCache.setRecordId(identifier, id);
|
|
@@ -1415,6 +1045,9 @@ function peekCache(instance) {
|
|
|
1415
1045
|
}
|
|
1416
1046
|
return null;
|
|
1417
1047
|
}
|
|
1048
|
+
function isDestroyable(record) {
|
|
1049
|
+
return Boolean(record && typeof record === 'object' && typeof record.destroy === 'function');
|
|
1050
|
+
}
|
|
1418
1051
|
|
|
1419
1052
|
/**
|
|
1420
1053
|
@module @ember-data/store
|
|
@@ -1442,7 +1075,7 @@ function peekRecordIdentifier(record) {
|
|
|
1442
1075
|
@static
|
|
1443
1076
|
@for @ember-data/store
|
|
1444
1077
|
@param {Object} record a record instance previously obstained from the store.
|
|
1445
|
-
@
|
|
1078
|
+
@return {StableRecordIdentifier}
|
|
1446
1079
|
*/
|
|
1447
1080
|
function recordIdentifierFor(record) {
|
|
1448
1081
|
assert(`${String(record)} is not a record instantiated by @ember-data/store`, RecordCache.has(record));
|
|
@@ -1489,11 +1122,11 @@ class InstanceCache {
|
|
|
1489
1122
|
// @ts-expect-error TODO this needs to be fixed
|
|
1490
1123
|
'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
|
|
1491
1124
|
}
|
|
1492
|
-
|
|
1125
|
+
const staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
|
|
1493
1126
|
|
|
1494
1127
|
// check for duplicate entities
|
|
1495
|
-
|
|
1496
|
-
|
|
1128
|
+
const keptHasRecord = this.__instances.record.has(keptIdentifier);
|
|
1129
|
+
const staleHasRecord = this.__instances.record.has(staleIdentifier);
|
|
1497
1130
|
|
|
1498
1131
|
// we cannot merge entities when both have records
|
|
1499
1132
|
// (this may not be strictly true, we could probably swap the cache data the record points at)
|
|
@@ -1547,7 +1180,7 @@ class InstanceCache {
|
|
|
1547
1180
|
return record;
|
|
1548
1181
|
}
|
|
1549
1182
|
getReference(identifier) {
|
|
1550
|
-
|
|
1183
|
+
const cache = this.__instances.reference;
|
|
1551
1184
|
let reference = cache.get(identifier);
|
|
1552
1185
|
if (!reference) {
|
|
1553
1186
|
reference = new RecordReference(this.store, identifier);
|
|
@@ -1578,7 +1211,7 @@ class InstanceCache {
|
|
|
1578
1211
|
}
|
|
1579
1212
|
disconnect(identifier) {
|
|
1580
1213
|
const record = this.__instances.record.get(identifier);
|
|
1581
|
-
assert('Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying);
|
|
1214
|
+
assert('Cannot destroy record while it is still materialized', !isDestroyable(record) || record.isDestroyed || record.isDestroying);
|
|
1582
1215
|
this.store._graph?.remove(identifier);
|
|
1583
1216
|
this.store.identifierCache.forgetRecordIdentifier(identifier);
|
|
1584
1217
|
removeRecordDataFor(identifier);
|
|
@@ -1646,7 +1279,7 @@ class InstanceCache {
|
|
|
1646
1279
|
});
|
|
1647
1280
|
} else {
|
|
1648
1281
|
const typeCache = cache.resourcesByType;
|
|
1649
|
-
|
|
1282
|
+
const identifiers = typeCache[type]?.lid;
|
|
1650
1283
|
if (identifiers) {
|
|
1651
1284
|
identifiers.forEach(identifier => {
|
|
1652
1285
|
// if (rds.has(identifier)) {
|
|
@@ -1664,7 +1297,7 @@ class InstanceCache {
|
|
|
1664
1297
|
type,
|
|
1665
1298
|
lid
|
|
1666
1299
|
} = identifier;
|
|
1667
|
-
|
|
1300
|
+
const oldId = identifier.id;
|
|
1668
1301
|
|
|
1669
1302
|
// ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
|
|
1670
1303
|
assert(`'${type}' was saved to the server, but the response does not have an id and your record does not either.`, !(id === null && oldId === null));
|
|
@@ -1683,7 +1316,7 @@ class InstanceCache {
|
|
|
1683
1316
|
// eslint-disable-next-line no-console
|
|
1684
1317
|
console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
|
|
1685
1318
|
}
|
|
1686
|
-
|
|
1319
|
+
const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
|
|
1687
1320
|
type,
|
|
1688
1321
|
id
|
|
1689
1322
|
});
|
|
@@ -1722,13 +1355,13 @@ function resourceIsFullyDeleted(instanceCache, identifier) {
|
|
|
1722
1355
|
*/
|
|
1723
1356
|
|
|
1724
1357
|
function preloadData(store, identifier, preload) {
|
|
1725
|
-
|
|
1358
|
+
const jsonPayload = {};
|
|
1726
1359
|
//TODO(Igor) consider the polymorphic case
|
|
1727
1360
|
const schemas = store.getSchemaDefinitionService();
|
|
1728
1361
|
const relationships = schemas.relationshipsDefinitionFor(identifier);
|
|
1729
1362
|
Object.keys(preload).forEach(key => {
|
|
1730
|
-
|
|
1731
|
-
|
|
1363
|
+
const preloadValue = preload[key];
|
|
1364
|
+
const relationshipMeta = relationships[key];
|
|
1732
1365
|
if (relationshipMeta) {
|
|
1733
1366
|
if (!jsonPayload.relationships) {
|
|
1734
1367
|
jsonPayload.relationships = {};
|
|
@@ -1797,7 +1430,7 @@ function getShimClass(store, modelName) {
|
|
|
1797
1430
|
}
|
|
1798
1431
|
function mapFromHash(hash) {
|
|
1799
1432
|
const map = new Map();
|
|
1800
|
-
for (
|
|
1433
|
+
for (const i in hash) {
|
|
1801
1434
|
if (Object.prototype.hasOwnProperty.call(hash, i)) {
|
|
1802
1435
|
map.set(i, hash[i]);
|
|
1803
1436
|
}
|
|
@@ -1812,31 +1445,31 @@ class ShimModelClass {
|
|
|
1812
1445
|
this.modelName = modelName;
|
|
1813
1446
|
}
|
|
1814
1447
|
get fields() {
|
|
1815
|
-
|
|
1448
|
+
const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1816
1449
|
type: this.modelName
|
|
1817
1450
|
});
|
|
1818
|
-
|
|
1451
|
+
const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1819
1452
|
type: this.modelName
|
|
1820
1453
|
});
|
|
1821
|
-
|
|
1454
|
+
const fields = new Map();
|
|
1822
1455
|
Object.keys(attrs).forEach(key => fields.set(key, 'attribute'));
|
|
1823
1456
|
Object.keys(relationships).forEach(key => fields.set(key, relationships[key].kind));
|
|
1824
1457
|
return fields;
|
|
1825
1458
|
}
|
|
1826
1459
|
get attributes() {
|
|
1827
|
-
|
|
1460
|
+
const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1828
1461
|
type: this.modelName
|
|
1829
1462
|
});
|
|
1830
1463
|
return mapFromHash(attrs);
|
|
1831
1464
|
}
|
|
1832
1465
|
get relationshipsByName() {
|
|
1833
|
-
|
|
1466
|
+
const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1834
1467
|
type: this.modelName
|
|
1835
1468
|
});
|
|
1836
1469
|
return mapFromHash(relationships);
|
|
1837
1470
|
}
|
|
1838
1471
|
eachAttribute(callback, binding) {
|
|
1839
|
-
|
|
1472
|
+
const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1840
1473
|
type: this.modelName
|
|
1841
1474
|
});
|
|
1842
1475
|
Object.keys(attrDefs).forEach(key => {
|
|
@@ -1844,7 +1477,7 @@ class ShimModelClass {
|
|
|
1844
1477
|
});
|
|
1845
1478
|
}
|
|
1846
1479
|
eachRelationship(callback, binding) {
|
|
1847
|
-
|
|
1480
|
+
const relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1848
1481
|
type: this.modelName
|
|
1849
1482
|
});
|
|
1850
1483
|
Object.keys(relationshipDefs).forEach(key => {
|
|
@@ -1862,6 +1495,16 @@ class ShimModelClass {
|
|
|
1862
1495
|
});
|
|
1863
1496
|
}
|
|
1864
1497
|
}
|
|
1498
|
+
function _classPrivateFieldBase(receiver, privateKey) {
|
|
1499
|
+
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
1500
|
+
throw new TypeError("attempted to use private field on non-instance");
|
|
1501
|
+
}
|
|
1502
|
+
return receiver;
|
|
1503
|
+
}
|
|
1504
|
+
var id = 0;
|
|
1505
|
+
function _classPrivateFieldKey(name) {
|
|
1506
|
+
return "__private_" + id++ + "_" + name;
|
|
1507
|
+
}
|
|
1865
1508
|
var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
|
|
1866
1509
|
/**
|
|
1867
1510
|
* The CacheManager wraps a Cache enforcing that only
|
|
@@ -1888,16 +1531,6 @@ class CacheManager {
|
|
|
1888
1531
|
writable: true,
|
|
1889
1532
|
value: void 0
|
|
1890
1533
|
});
|
|
1891
|
-
/**
|
|
1892
|
-
* Query the cache for whether a given resource has been deleted and that deletion
|
|
1893
|
-
* has also been persisted.
|
|
1894
|
-
*
|
|
1895
|
-
* @method isDeletionCommitted
|
|
1896
|
-
* @public
|
|
1897
|
-
* @param identifier
|
|
1898
|
-
* @returns {boolean}
|
|
1899
|
-
*/
|
|
1900
|
-
this.isDel = void 0;
|
|
1901
1534
|
_classPrivateFieldBase(this, _cache)[_cache] = cache;
|
|
1902
1535
|
}
|
|
1903
1536
|
|
|
@@ -1911,7 +1544,7 @@ class CacheManager {
|
|
|
1911
1544
|
* semantics, `put` has `replace` semantics similar to
|
|
1912
1545
|
* the `http` method `PUT`
|
|
1913
1546
|
*
|
|
1914
|
-
* the individually
|
|
1547
|
+
* the individually cacheable
|
|
1915
1548
|
* e resource data it may contain
|
|
1916
1549
|
* should upsert, but the document data surrounding it should
|
|
1917
1550
|
* fully replace any existing information
|
|
@@ -1924,7 +1557,7 @@ class CacheManager {
|
|
|
1924
1557
|
*
|
|
1925
1558
|
* @method put
|
|
1926
1559
|
* @param {StructuredDocument} doc
|
|
1927
|
-
* @
|
|
1560
|
+
* @return {ResourceDocument}
|
|
1928
1561
|
* @public
|
|
1929
1562
|
*/
|
|
1930
1563
|
put(doc) {
|
|
@@ -1940,7 +1573,7 @@ class CacheManager {
|
|
|
1940
1573
|
* @method patch
|
|
1941
1574
|
* @public
|
|
1942
1575
|
* @param op the operation to perform
|
|
1943
|
-
* @
|
|
1576
|
+
* @return {void}
|
|
1944
1577
|
*/
|
|
1945
1578
|
patch(op) {
|
|
1946
1579
|
_classPrivateFieldBase(this, _cache)[_cache].patch(op);
|
|
@@ -1975,7 +1608,7 @@ class CacheManager {
|
|
|
1975
1608
|
* An implementation might want to do this because
|
|
1976
1609
|
* de-referencing records which read from their own
|
|
1977
1610
|
* blob is generally safer because the record does
|
|
1978
|
-
* not require
|
|
1611
|
+
* not require retaining connections to the Store
|
|
1979
1612
|
* and Cache to present data on a per-field basis.
|
|
1980
1613
|
*
|
|
1981
1614
|
* This generally takes the place of `getAttr` as
|
|
@@ -1988,7 +1621,7 @@ class CacheManager {
|
|
|
1988
1621
|
* @method peek
|
|
1989
1622
|
* @public
|
|
1990
1623
|
* @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
|
|
1991
|
-
* @
|
|
1624
|
+
* @return {ResourceDocument | ResourceBlob | null} the known resource data
|
|
1992
1625
|
*/
|
|
1993
1626
|
|
|
1994
1627
|
peek(identifier) {
|
|
@@ -2001,7 +1634,7 @@ class CacheManager {
|
|
|
2001
1634
|
*
|
|
2002
1635
|
* @method peekRequest
|
|
2003
1636
|
* @param {StableDocumentIdentifier}
|
|
2004
|
-
* @
|
|
1637
|
+
* @return {StableDocumentIdentifier | null}
|
|
2005
1638
|
* @public
|
|
2006
1639
|
*/
|
|
2007
1640
|
peekRequest(identifier) {
|
|
@@ -2016,7 +1649,7 @@ class CacheManager {
|
|
|
2016
1649
|
* @param identifier
|
|
2017
1650
|
* @param data
|
|
2018
1651
|
* @param hasRecord
|
|
2019
|
-
* @
|
|
1652
|
+
* @return {void | string[]} if `hasRecord` is true then calculated key changes should be returned
|
|
2020
1653
|
*/
|
|
2021
1654
|
upsert(identifier, data, hasRecord) {
|
|
2022
1655
|
return _classPrivateFieldBase(this, _cache)[_cache].upsert(identifier, data, hasRecord);
|
|
@@ -2034,7 +1667,7 @@ class CacheManager {
|
|
|
2034
1667
|
*
|
|
2035
1668
|
* @method fork
|
|
2036
1669
|
* @public
|
|
2037
|
-
* @
|
|
1670
|
+
* @return Promise<Cache>
|
|
2038
1671
|
*/
|
|
2039
1672
|
fork() {
|
|
2040
1673
|
return _classPrivateFieldBase(this, _cache)[_cache].fork();
|
|
@@ -2050,7 +1683,7 @@ class CacheManager {
|
|
|
2050
1683
|
* @method merge
|
|
2051
1684
|
* @param {Cache} cache
|
|
2052
1685
|
* @public
|
|
2053
|
-
* @
|
|
1686
|
+
* @return Promise<void>
|
|
2054
1687
|
*/
|
|
2055
1688
|
merge(cache) {
|
|
2056
1689
|
return _classPrivateFieldBase(this, _cache)[_cache].merge(cache);
|
|
@@ -2102,7 +1735,7 @@ class CacheManager {
|
|
|
2102
1735
|
* via `cache.hydrate`.
|
|
2103
1736
|
*
|
|
2104
1737
|
* @method dump
|
|
2105
|
-
* @
|
|
1738
|
+
* @return {Promise<ReadableStream>}
|
|
2106
1739
|
* @public
|
|
2107
1740
|
*/
|
|
2108
1741
|
dump() {
|
|
@@ -2123,7 +1756,7 @@ class CacheManager {
|
|
|
2123
1756
|
*
|
|
2124
1757
|
* @method hydrate
|
|
2125
1758
|
* @param {ReadableStream} stream
|
|
2126
|
-
* @
|
|
1759
|
+
* @return {Promise<void>}
|
|
2127
1760
|
* @public
|
|
2128
1761
|
*/
|
|
2129
1762
|
hydrate(stream) {
|
|
@@ -2137,7 +1770,7 @@ class CacheManager {
|
|
|
2137
1770
|
// ================
|
|
2138
1771
|
|
|
2139
1772
|
/**
|
|
2140
|
-
* [
|
|
1773
|
+
* [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client
|
|
2141
1774
|
*
|
|
2142
1775
|
* It returns properties from options that should be set on the record during the create
|
|
2143
1776
|
* process. This return value behavior is deprecated.
|
|
@@ -2211,7 +1844,7 @@ class CacheManager {
|
|
|
2211
1844
|
* @public
|
|
2212
1845
|
* @param identifier
|
|
2213
1846
|
* @param propertyName
|
|
2214
|
-
* @
|
|
1847
|
+
* @return {unknown}
|
|
2215
1848
|
*/
|
|
2216
1849
|
getAttr(identifier, propertyName) {
|
|
2217
1850
|
return _classPrivateFieldBase(this, _cache)[_cache].getAttr(identifier, propertyName);
|
|
@@ -2236,7 +1869,7 @@ class CacheManager {
|
|
|
2236
1869
|
* @method changedAttrs
|
|
2237
1870
|
* @public
|
|
2238
1871
|
* @param identifier
|
|
2239
|
-
* @
|
|
1872
|
+
* @return
|
|
2240
1873
|
*/
|
|
2241
1874
|
changedAttrs(identifier) {
|
|
2242
1875
|
return _classPrivateFieldBase(this, _cache)[_cache].changedAttrs(identifier);
|
|
@@ -2248,7 +1881,7 @@ class CacheManager {
|
|
|
2248
1881
|
* @method hasChangedAttrs
|
|
2249
1882
|
* @public
|
|
2250
1883
|
* @param identifier
|
|
2251
|
-
* @
|
|
1884
|
+
* @return {boolean}
|
|
2252
1885
|
*/
|
|
2253
1886
|
hasChangedAttrs(identifier) {
|
|
2254
1887
|
return _classPrivateFieldBase(this, _cache)[_cache].hasChangedAttrs(identifier);
|
|
@@ -2260,7 +1893,7 @@ class CacheManager {
|
|
|
2260
1893
|
* @method rollbackAttrs
|
|
2261
1894
|
* @public
|
|
2262
1895
|
* @param identifier
|
|
2263
|
-
* @
|
|
1896
|
+
* @return the names of attributes that were restored
|
|
2264
1897
|
*/
|
|
2265
1898
|
rollbackAttrs(identifier) {
|
|
2266
1899
|
return _classPrivateFieldBase(this, _cache)[_cache].rollbackAttrs(identifier);
|
|
@@ -2294,7 +1927,7 @@ class CacheManager {
|
|
|
2294
1927
|
* @method changedRelationships
|
|
2295
1928
|
* @public
|
|
2296
1929
|
* @param {StableRecordIdentifier} identifier
|
|
2297
|
-
* @
|
|
1930
|
+
* @return {Map<string, RelationshipDiff>}
|
|
2298
1931
|
*/
|
|
2299
1932
|
changedRelationships(identifier) {
|
|
2300
1933
|
return _classPrivateFieldBase(this, _cache)[_cache].changedRelationships(identifier);
|
|
@@ -2306,7 +1939,7 @@ class CacheManager {
|
|
|
2306
1939
|
* @method hasChangedRelationships
|
|
2307
1940
|
* @public
|
|
2308
1941
|
* @param {StableRecordIdentifier} identifier
|
|
2309
|
-
* @
|
|
1942
|
+
* @return {boolean}
|
|
2310
1943
|
*/
|
|
2311
1944
|
hasChangedRelationships(identifier) {
|
|
2312
1945
|
return _classPrivateFieldBase(this, _cache)[_cache].hasChangedRelationships(identifier);
|
|
@@ -2322,7 +1955,7 @@ class CacheManager {
|
|
|
2322
1955
|
* @method rollbackRelationships
|
|
2323
1956
|
* @public
|
|
2324
1957
|
* @param {StableRecordIdentifier} identifier
|
|
2325
|
-
* @
|
|
1958
|
+
* @return {string[]} the names of relationships that were restored
|
|
2326
1959
|
*/
|
|
2327
1960
|
rollbackRelationships(identifier) {
|
|
2328
1961
|
return _classPrivateFieldBase(this, _cache)[_cache].rollbackRelationships(identifier);
|
|
@@ -2335,7 +1968,7 @@ class CacheManager {
|
|
|
2335
1968
|
* @public
|
|
2336
1969
|
* @param identifier
|
|
2337
1970
|
* @param propertyName
|
|
2338
|
-
* @
|
|
1971
|
+
* @return resource relationship object
|
|
2339
1972
|
*/
|
|
2340
1973
|
getRelationship(identifier, propertyName) {
|
|
2341
1974
|
return _classPrivateFieldBase(this, _cache)[_cache].getRelationship(identifier, propertyName);
|
|
@@ -2363,7 +1996,7 @@ class CacheManager {
|
|
|
2363
1996
|
* @method getErrors
|
|
2364
1997
|
* @public
|
|
2365
1998
|
* @param identifier
|
|
2366
|
-
* @
|
|
1999
|
+
* @return
|
|
2367
2000
|
*/
|
|
2368
2001
|
getErrors(identifier) {
|
|
2369
2002
|
return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier);
|
|
@@ -2375,7 +2008,7 @@ class CacheManager {
|
|
|
2375
2008
|
* @method isEmpty
|
|
2376
2009
|
* @public
|
|
2377
2010
|
* @param identifier
|
|
2378
|
-
* @
|
|
2011
|
+
* @return {boolean}
|
|
2379
2012
|
*/
|
|
2380
2013
|
isEmpty(identifier) {
|
|
2381
2014
|
return _classPrivateFieldBase(this, _cache)[_cache].isEmpty(identifier);
|
|
@@ -2388,7 +2021,7 @@ class CacheManager {
|
|
|
2388
2021
|
* @method isNew
|
|
2389
2022
|
* @public
|
|
2390
2023
|
* @param identifier
|
|
2391
|
-
* @
|
|
2024
|
+
* @return {boolean}
|
|
2392
2025
|
*/
|
|
2393
2026
|
isNew(identifier) {
|
|
2394
2027
|
return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier);
|
|
@@ -2401,15 +2034,29 @@ class CacheManager {
|
|
|
2401
2034
|
* @method isDeleted
|
|
2402
2035
|
* @public
|
|
2403
2036
|
* @param identifier
|
|
2404
|
-
* @
|
|
2037
|
+
* @return {boolean}
|
|
2405
2038
|
*/
|
|
2406
2039
|
isDeleted(identifier) {
|
|
2407
2040
|
return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier);
|
|
2408
2041
|
}
|
|
2042
|
+
|
|
2043
|
+
/**
|
|
2044
|
+
* Query the cache for whether a given resource has been deleted and that deletion
|
|
2045
|
+
* has also been persisted.
|
|
2046
|
+
*
|
|
2047
|
+
* @method isDeletionCommitted
|
|
2048
|
+
* @public
|
|
2049
|
+
* @param identifier
|
|
2050
|
+
* @return {boolean}
|
|
2051
|
+
*/
|
|
2409
2052
|
isDeletionCommitted(identifier) {
|
|
2410
2053
|
return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier);
|
|
2411
2054
|
}
|
|
2412
2055
|
}
|
|
2056
|
+
|
|
2057
|
+
/**
|
|
2058
|
+
* @module @ember-data/store
|
|
2059
|
+
*/
|
|
2413
2060
|
let tokenId = 0;
|
|
2414
2061
|
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
2415
2062
|
function isCacheOperationValue(value) {
|
|
@@ -2420,7 +2067,7 @@ function runLoopIsFlushing() {
|
|
|
2420
2067
|
return !!_backburner.currentInstance && _backburner._autorun !== true;
|
|
2421
2068
|
}
|
|
2422
2069
|
function _unsubscribe(tokens, token, cache) {
|
|
2423
|
-
|
|
2070
|
+
const identifier = tokens.get(token);
|
|
2424
2071
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
2425
2072
|
if (!identifier) {
|
|
2426
2073
|
// eslint-disable-next-line no-console
|
|
@@ -2479,7 +2126,7 @@ class NotificationManager {
|
|
|
2479
2126
|
* @public
|
|
2480
2127
|
* @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
|
|
2481
2128
|
* @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
|
|
2482
|
-
* @
|
|
2129
|
+
* @return {UnsubscribeToken} an opaque token to be used with unsubscribe
|
|
2483
2130
|
*/
|
|
2484
2131
|
|
|
2485
2132
|
subscribe(identifier, callback) {
|
|
@@ -2489,7 +2136,7 @@ class NotificationManager {
|
|
|
2489
2136
|
map = new Map();
|
|
2490
2137
|
this._cache.set(identifier, map);
|
|
2491
2138
|
}
|
|
2492
|
-
|
|
2139
|
+
const unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
|
|
2493
2140
|
_tokenRef: tokenId++
|
|
2494
2141
|
} : {};
|
|
2495
2142
|
map.set(unsubToken, callback);
|
|
@@ -2542,7 +2189,7 @@ class NotificationManager {
|
|
|
2542
2189
|
this._buffered.set(identifier, buffer);
|
|
2543
2190
|
}
|
|
2544
2191
|
buffer.push([value, key]);
|
|
2545
|
-
|
|
2192
|
+
this._scheduleNotify();
|
|
2546
2193
|
}
|
|
2547
2194
|
return hasSubscribers;
|
|
2548
2195
|
}
|
|
@@ -2584,14 +2231,14 @@ class NotificationManager {
|
|
|
2584
2231
|
|
|
2585
2232
|
// TODO for documents this will need to switch based on Identifier kind
|
|
2586
2233
|
if (isCacheOperationValue(value)) {
|
|
2587
|
-
|
|
2234
|
+
const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
|
|
2588
2235
|
if (callbackMap) {
|
|
2589
2236
|
callbackMap.forEach(cb => {
|
|
2590
2237
|
cb(identifier, value);
|
|
2591
2238
|
});
|
|
2592
2239
|
}
|
|
2593
2240
|
}
|
|
2594
|
-
|
|
2241
|
+
const callbackMap = this._cache.get(identifier);
|
|
2595
2242
|
if (!callbackMap || !callbackMap.size) {
|
|
2596
2243
|
return false;
|
|
2597
2244
|
}
|
|
@@ -2607,7 +2254,30 @@ class NotificationManager {
|
|
|
2607
2254
|
this._cache.clear();
|
|
2608
2255
|
}
|
|
2609
2256
|
}
|
|
2610
|
-
|
|
2257
|
+
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
|
2258
|
+
var desc = {};
|
|
2259
|
+
Object.keys(descriptor).forEach(function (key) {
|
|
2260
|
+
desc[key] = descriptor[key];
|
|
2261
|
+
});
|
|
2262
|
+
desc.enumerable = !!desc.enumerable;
|
|
2263
|
+
desc.configurable = !!desc.configurable;
|
|
2264
|
+
if ('value' in desc || desc.initializer) {
|
|
2265
|
+
desc.writable = true;
|
|
2266
|
+
}
|
|
2267
|
+
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
|
|
2268
|
+
return decorator(target, property, desc) || desc;
|
|
2269
|
+
}, desc);
|
|
2270
|
+
if (context && desc.initializer !== void 0) {
|
|
2271
|
+
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
|
|
2272
|
+
desc.initializer = undefined;
|
|
2273
|
+
}
|
|
2274
|
+
if (desc.initializer === void 0) {
|
|
2275
|
+
Object.defineProperty(target, property, desc);
|
|
2276
|
+
desc = null;
|
|
2277
|
+
}
|
|
2278
|
+
return desc;
|
|
2279
|
+
}
|
|
2280
|
+
var _class;
|
|
2611
2281
|
const ARRAY_GETTER_METHODS = new Set([Symbol.iterator, 'concat', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'map', 'reduce', 'reduceRight', 'slice', 'some', 'values']);
|
|
2612
2282
|
const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
|
|
2613
2283
|
const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
|
|
@@ -2617,19 +2287,16 @@ function isArrayGetter(prop) {
|
|
|
2617
2287
|
function isArraySetter(prop) {
|
|
2618
2288
|
return ARRAY_SETTER_METHODS.has(prop);
|
|
2619
2289
|
}
|
|
2620
|
-
|
|
2290
|
+
function isSelfProp(self, prop) {
|
|
2291
|
+
return prop in self;
|
|
2292
|
+
}
|
|
2293
|
+
const ARRAY_SIGNAL = Symbol('#signal');
|
|
2621
2294
|
const SOURCE = Symbol('#source');
|
|
2622
2295
|
const MUTATE = Symbol('#update');
|
|
2623
2296
|
const NOTIFY = Symbol('#notify');
|
|
2624
2297
|
const IS_COLLECTION = Symbol.for('Collection');
|
|
2625
2298
|
function notifyArray(arr) {
|
|
2626
|
-
addToTransaction(arr[
|
|
2627
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
2628
|
-
// eslint-disable-next-line
|
|
2629
|
-
dirtyTag(tagForProperty(arr, 'length'));
|
|
2630
|
-
// eslint-disable-next-line
|
|
2631
|
-
dirtyTag(tagForProperty(arr, '[]'));
|
|
2632
|
-
}
|
|
2299
|
+
addToTransaction(arr[ARRAY_SIGNAL]);
|
|
2633
2300
|
}
|
|
2634
2301
|
function convertToInt(prop) {
|
|
2635
2302
|
if (typeof prop === 'symbol') return null;
|
|
@@ -2637,29 +2304,6 @@ function convertToInt(prop) {
|
|
|
2637
2304
|
if (isNaN(num)) return null;
|
|
2638
2305
|
return num % 1 === 0 ? num : null;
|
|
2639
2306
|
}
|
|
2640
|
-
let Tag = (_class = class Tag {
|
|
2641
|
-
/*
|
|
2642
|
-
* whether this was part of a transaction when last mutated
|
|
2643
|
-
*/
|
|
2644
|
-
|
|
2645
|
-
constructor() {
|
|
2646
|
-
_initializerDefineProperty(this, "ref", _descriptor, this);
|
|
2647
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
2648
|
-
const [arr, prop] = arguments;
|
|
2649
|
-
this._debug_base = arr.constructor.name + ':' + String(arr.modelName);
|
|
2650
|
-
this._debug_prop = prop;
|
|
2651
|
-
}
|
|
2652
|
-
this.shouldReset = false;
|
|
2653
|
-
this.t = false;
|
|
2654
|
-
}
|
|
2655
|
-
}, _descriptor = _applyDecoratedDescriptor(_class.prototype, "ref", [tracked], {
|
|
2656
|
-
configurable: true,
|
|
2657
|
-
enumerable: true,
|
|
2658
|
-
writable: true,
|
|
2659
|
-
initializer: function () {
|
|
2660
|
-
return null;
|
|
2661
|
-
}
|
|
2662
|
-
}), _class);
|
|
2663
2307
|
function safeForEach(instance, arr, store, callback, target) {
|
|
2664
2308
|
if (target === undefined) {
|
|
2665
2309
|
target = null;
|
|
@@ -2692,7 +2336,7 @@ function safeForEach(instance, arr, store, callback, target) {
|
|
|
2692
2336
|
@class RecordArray
|
|
2693
2337
|
@public
|
|
2694
2338
|
*/
|
|
2695
|
-
let IdentifierArray = (
|
|
2339
|
+
let IdentifierArray = (_class = class IdentifierArray {
|
|
2696
2340
|
[NOTIFY]() {
|
|
2697
2341
|
notifyArray(this);
|
|
2698
2342
|
}
|
|
@@ -2720,14 +2364,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2720
2364
|
set length(value) {
|
|
2721
2365
|
this[SOURCE].length = value;
|
|
2722
2366
|
}
|
|
2723
|
-
|
|
2724
|
-
// here to support computed chains
|
|
2725
|
-
// and {{#each}}
|
|
2726
|
-
get '[]'() {
|
|
2727
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
2728
|
-
return this;
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2731
2367
|
constructor(options) {
|
|
2732
2368
|
/**
|
|
2733
2369
|
The flag to signal a `RecordArray` is currently loading data.
|
|
@@ -2742,7 +2378,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2742
2378
|
@public
|
|
2743
2379
|
@type Boolean
|
|
2744
2380
|
*/
|
|
2745
|
-
_initializerDefineProperty(this, "isUpdating", _descriptor2, this);
|
|
2746
2381
|
this.isLoaded = true;
|
|
2747
2382
|
this.isDestroying = false;
|
|
2748
2383
|
this.isDestroyed = false;
|
|
@@ -2750,16 +2385,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2750
2385
|
this[IS_COLLECTION] = true;
|
|
2751
2386
|
this[SOURCE] = void 0;
|
|
2752
2387
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2753
|
-
|
|
2388
|
+
const self = this;
|
|
2754
2389
|
this.modelName = options.type;
|
|
2755
2390
|
this.store = options.store;
|
|
2756
2391
|
this._manager = options.manager;
|
|
2757
2392
|
this[SOURCE] = options.identifiers;
|
|
2758
|
-
|
|
2759
|
-
this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
|
|
2393
|
+
this[ARRAY_SIGNAL] = createSignal(this, 'length');
|
|
2760
2394
|
const store = options.store;
|
|
2761
2395
|
const boundFns = new Map();
|
|
2762
|
-
const
|
|
2396
|
+
const _SIGNAL = this[ARRAY_SIGNAL];
|
|
2763
2397
|
const PrivateState = {
|
|
2764
2398
|
links: options.links || null,
|
|
2765
2399
|
meta: options.meta || null
|
|
@@ -2772,40 +2406,40 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2772
2406
|
|
|
2773
2407
|
const proxy = new Proxy(this[SOURCE], {
|
|
2774
2408
|
get(target, prop, receiver) {
|
|
2775
|
-
|
|
2776
|
-
if (
|
|
2409
|
+
const index = convertToInt(prop);
|
|
2410
|
+
if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
|
|
2777
2411
|
options.manager._syncArray(receiver);
|
|
2778
|
-
|
|
2779
|
-
|
|
2412
|
+
_SIGNAL.t = false;
|
|
2413
|
+
_SIGNAL.shouldReset = false;
|
|
2780
2414
|
}
|
|
2781
2415
|
if (index !== null) {
|
|
2782
2416
|
const identifier = target[index];
|
|
2783
2417
|
if (!transaction) {
|
|
2784
|
-
subscribe(
|
|
2418
|
+
subscribe(_SIGNAL);
|
|
2785
2419
|
}
|
|
2786
2420
|
return identifier && store._instanceCache.getRecord(identifier);
|
|
2787
2421
|
}
|
|
2788
|
-
if (prop === 'meta') return subscribe(
|
|
2789
|
-
if (prop === 'links') return subscribe(
|
|
2790
|
-
if (prop === '[]') return subscribe(
|
|
2422
|
+
if (prop === 'meta') return subscribe(_SIGNAL), PrivateState.meta;
|
|
2423
|
+
if (prop === 'links') return subscribe(_SIGNAL), PrivateState.links;
|
|
2424
|
+
if (prop === '[]') return subscribe(_SIGNAL), receiver;
|
|
2791
2425
|
if (isArrayGetter(prop)) {
|
|
2792
2426
|
let fn = boundFns.get(prop);
|
|
2793
2427
|
if (fn === undefined) {
|
|
2794
2428
|
if (prop === 'forEach') {
|
|
2795
2429
|
fn = function () {
|
|
2796
|
-
subscribe(
|
|
2430
|
+
subscribe(_SIGNAL);
|
|
2797
2431
|
transaction = true;
|
|
2798
|
-
|
|
2432
|
+
const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
|
|
2799
2433
|
transaction = false;
|
|
2800
2434
|
return result;
|
|
2801
2435
|
};
|
|
2802
2436
|
} else {
|
|
2803
2437
|
fn = function () {
|
|
2804
|
-
subscribe(
|
|
2438
|
+
subscribe(_SIGNAL);
|
|
2805
2439
|
// array functions must run through Reflect to work properly
|
|
2806
2440
|
// binding via other means will not work.
|
|
2807
2441
|
transaction = true;
|
|
2808
|
-
|
|
2442
|
+
const result = Reflect.apply(target[prop], receiver, arguments);
|
|
2809
2443
|
transaction = false;
|
|
2810
2444
|
return result;
|
|
2811
2445
|
};
|
|
@@ -2827,10 +2461,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2827
2461
|
const args = Array.prototype.slice.call(arguments);
|
|
2828
2462
|
assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
|
|
2829
2463
|
transaction = true;
|
|
2830
|
-
|
|
2831
|
-
self[MUTATE](prop, args, result);
|
|
2832
|
-
addToTransaction(_TAG);
|
|
2833
|
-
// TODO handle cache updates
|
|
2464
|
+
const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
|
|
2834
2465
|
transaction = false;
|
|
2835
2466
|
return result;
|
|
2836
2467
|
};
|
|
@@ -2838,16 +2469,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2838
2469
|
}
|
|
2839
2470
|
return fn;
|
|
2840
2471
|
}
|
|
2841
|
-
if (prop
|
|
2842
|
-
if (prop === NOTIFY || prop ===
|
|
2472
|
+
if (isSelfProp(self, prop)) {
|
|
2473
|
+
if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
|
|
2843
2474
|
return self[prop];
|
|
2844
2475
|
}
|
|
2845
2476
|
let fn = boundFns.get(prop);
|
|
2846
2477
|
if (fn) return fn;
|
|
2847
|
-
|
|
2478
|
+
const outcome = self[prop];
|
|
2848
2479
|
if (typeof outcome === 'function') {
|
|
2849
2480
|
fn = function () {
|
|
2850
|
-
subscribe(
|
|
2481
|
+
subscribe(_SIGNAL);
|
|
2851
2482
|
// array functions must run through Reflect to work properly
|
|
2852
2483
|
// binding via other means will not work.
|
|
2853
2484
|
return Reflect.apply(outcome, receiver, arguments);
|
|
@@ -2855,17 +2486,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2855
2486
|
boundFns.set(prop, fn);
|
|
2856
2487
|
return fn;
|
|
2857
2488
|
}
|
|
2858
|
-
return subscribe(
|
|
2489
|
+
return subscribe(_SIGNAL), outcome;
|
|
2859
2490
|
}
|
|
2860
2491
|
return target[prop];
|
|
2861
2492
|
},
|
|
2862
|
-
|
|
2493
|
+
// FIXME: Should this get a generic like get above?
|
|
2494
|
+
set(target, prop, value, receiver) {
|
|
2863
2495
|
if (prop === 'length') {
|
|
2864
2496
|
if (!transaction && value === 0) {
|
|
2865
2497
|
transaction = true;
|
|
2866
|
-
|
|
2867
|
-
Reflect.set(target, prop, value);
|
|
2868
|
-
self[MUTATE]('length 0', []);
|
|
2498
|
+
self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
|
|
2869
2499
|
transaction = false;
|
|
2870
2500
|
return true;
|
|
2871
2501
|
} else if (transaction) {
|
|
@@ -2882,9 +2512,22 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2882
2512
|
PrivateState.meta = value || null;
|
|
2883
2513
|
return true;
|
|
2884
2514
|
}
|
|
2885
|
-
|
|
2515
|
+
const index = convertToInt(prop);
|
|
2516
|
+
|
|
2517
|
+
// we do not allow "holey" arrays and so if the index is
|
|
2518
|
+
// greater than length then we will disallow setting it.
|
|
2519
|
+
// however, there is a special case for "unshift" with more than
|
|
2520
|
+
// one item being inserted since current items will be moved to the
|
|
2521
|
+
// new indices first.
|
|
2522
|
+
// we "loosely" detect this by just checking whether we are in
|
|
2523
|
+
// a transaction.
|
|
2886
2524
|
if (index === null || index > target.length) {
|
|
2887
|
-
if (
|
|
2525
|
+
if (index !== null && transaction) {
|
|
2526
|
+
const identifier = recordIdentifierFor(value);
|
|
2527
|
+
assert(`Cannot set index ${index} past the end of the array.`, isStableIdentifier(identifier));
|
|
2528
|
+
target[index] = identifier;
|
|
2529
|
+
return true;
|
|
2530
|
+
} else if (isSelfProp(self, prop)) {
|
|
2888
2531
|
self[prop] = value;
|
|
2889
2532
|
return true;
|
|
2890
2533
|
}
|
|
@@ -2894,12 +2537,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2894
2537
|
assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
|
|
2895
2538
|
return false;
|
|
2896
2539
|
}
|
|
2897
|
-
|
|
2898
|
-
|
|
2540
|
+
const original = target[index];
|
|
2541
|
+
const newIdentifier = extractIdentifierFromRecord$1(value);
|
|
2899
2542
|
target[index] = newIdentifier;
|
|
2543
|
+
assert(`Expected a record`, isStableIdentifier(newIdentifier));
|
|
2544
|
+
// We generate "transactions" whenever a setter method on the array
|
|
2545
|
+
// is called and might bulk update multiple array cells. Fundamentally,
|
|
2546
|
+
// all array operations decompose into individual cell replacements.
|
|
2547
|
+
// e.g. a push is really a "replace cell at next index with new value"
|
|
2548
|
+
// or a splice is "shift all values left/right by X and set out of new
|
|
2549
|
+
// bounds cells to undefined"
|
|
2550
|
+
//
|
|
2551
|
+
// so, if we are in a transaction, then this is not a user generated change
|
|
2552
|
+
// but one generated by a setter method. In this case we want to only apply
|
|
2553
|
+
// the change to the target array and not call the MUTATE method.
|
|
2554
|
+
// If there is no transaction though, then this means the user themselves has
|
|
2555
|
+
// directly changed the value of a specific index and we need to thus generate
|
|
2556
|
+
// a mutation for that change.
|
|
2557
|
+
// e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
|
|
2558
|
+
// a transaction.
|
|
2559
|
+
// while "arr[arr.length] = newVal;" is handled by this replace cell code path.
|
|
2900
2560
|
if (!transaction) {
|
|
2901
|
-
self[MUTATE]('replace cell', [index, original, newIdentifier]);
|
|
2902
|
-
|
|
2561
|
+
self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
|
|
2562
|
+
} else {
|
|
2563
|
+
target[index] = newIdentifier;
|
|
2903
2564
|
}
|
|
2904
2565
|
return true;
|
|
2905
2566
|
},
|
|
@@ -2914,12 +2575,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2914
2575
|
return IdentifierArray.prototype;
|
|
2915
2576
|
}
|
|
2916
2577
|
});
|
|
2917
|
-
|
|
2918
|
-
const meta = Ember.meta(this);
|
|
2919
|
-
meta.addMixin = mixin => {
|
|
2920
|
-
assert(`Do not call A() on EmberData RecordArrays`);
|
|
2921
|
-
};
|
|
2922
|
-
}
|
|
2578
|
+
createArrayTags(proxy, _SIGNAL);
|
|
2923
2579
|
this[NOTIFY] = this[NOTIFY].bind(proxy);
|
|
2924
2580
|
return proxy;
|
|
2925
2581
|
}
|
|
@@ -2944,7 +2600,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2944
2600
|
return this._updatingPromise;
|
|
2945
2601
|
}
|
|
2946
2602
|
this.isUpdating = true;
|
|
2947
|
-
|
|
2603
|
+
const updatingPromise = this._update();
|
|
2948
2604
|
void updatingPromise.finally(() => {
|
|
2949
2605
|
this._updatingPromise = null;
|
|
2950
2606
|
if (this.isDestroying || this.isDestroyed) {
|
|
@@ -2983,17 +2639,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2983
2639
|
@return {Promise<IdentifierArray>} promise
|
|
2984
2640
|
*/
|
|
2985
2641
|
save() {
|
|
2986
|
-
|
|
2642
|
+
const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
|
|
2987
2643
|
return promise;
|
|
2988
2644
|
}
|
|
2989
|
-
},
|
|
2990
|
-
|
|
2645
|
+
}, _applyDecoratedDescriptor(_class.prototype, "length", [compat], Object.getOwnPropertyDescriptor(_class.prototype, "length"), _class.prototype), _class); // this will error if someone tries to call
|
|
2646
|
+
// A(identifierArray) since it is not configurable
|
|
2647
|
+
// which is preferable to the `meta` override we used
|
|
2648
|
+
// before which required importing all of Ember
|
|
2649
|
+
const desc = {
|
|
2991
2650
|
enumerable: true,
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
return
|
|
2651
|
+
configurable: false,
|
|
2652
|
+
get: function () {
|
|
2653
|
+
return this;
|
|
2995
2654
|
}
|
|
2996
|
-
}
|
|
2655
|
+
};
|
|
2656
|
+
compat(desc);
|
|
2657
|
+
Object.defineProperty(IdentifierArray.prototype, '[]', desc);
|
|
2658
|
+
defineSignal(IdentifierArray.prototype, 'isUpdating', false);
|
|
2997
2659
|
class Collection extends IdentifierArray {
|
|
2998
2660
|
constructor(options) {
|
|
2999
2661
|
super(options);
|
|
@@ -3025,7 +2687,8 @@ class Collection extends IdentifierArray {
|
|
|
3025
2687
|
Collection.prototype.query = null;
|
|
3026
2688
|
|
|
3027
2689
|
// Ensure instanceof works correctly
|
|
3028
|
-
//Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2690
|
+
// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2691
|
+
|
|
3029
2692
|
function assertRecordPassedToHasMany(record) {
|
|
3030
2693
|
assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
|
|
3031
2694
|
try {
|
|
@@ -3047,7 +2710,6 @@ function extractIdentifierFromRecord$1(record) {
|
|
|
3047
2710
|
/**
|
|
3048
2711
|
@module @ember-data/store
|
|
3049
2712
|
*/
|
|
3050
|
-
|
|
3051
2713
|
const FAKE_ARR = {};
|
|
3052
2714
|
const SLICE_BATCH_SIZE = 1200;
|
|
3053
2715
|
/**
|
|
@@ -3090,7 +2752,7 @@ const SLICE_BATCH_SIZE = 1200;
|
|
|
3090
2752
|
*/
|
|
3091
2753
|
function fastPush(target, source) {
|
|
3092
2754
|
let startLength = 0;
|
|
3093
|
-
|
|
2755
|
+
const newLength = source.length;
|
|
3094
2756
|
while (newLength - startLength > SLICE_BATCH_SIZE) {
|
|
3095
2757
|
// eslint-disable-next-line prefer-spread
|
|
3096
2758
|
target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
|
|
@@ -3147,8 +2809,8 @@ class RecordArrayManager {
|
|
|
3147
2809
|
*/
|
|
3148
2810
|
liveArrayFor(type) {
|
|
3149
2811
|
let array = this._live.get(type);
|
|
3150
|
-
|
|
3151
|
-
|
|
2812
|
+
const identifiers = [];
|
|
2813
|
+
const staged = this._staged.get(type);
|
|
3152
2814
|
if (staged) {
|
|
3153
2815
|
staged.forEach((value, key) => {
|
|
3154
2816
|
if (value === 'add') {
|
|
@@ -3171,7 +2833,7 @@ class RecordArrayManager {
|
|
|
3171
2833
|
return array;
|
|
3172
2834
|
}
|
|
3173
2835
|
createArray(config) {
|
|
3174
|
-
|
|
2836
|
+
const options = {
|
|
3175
2837
|
type: config.type,
|
|
3176
2838
|
links: config.doc?.links || null,
|
|
3177
2839
|
meta: config.doc?.meta || null,
|
|
@@ -3182,7 +2844,7 @@ class RecordArrayManager {
|
|
|
3182
2844
|
store: this.store,
|
|
3183
2845
|
manager: this
|
|
3184
2846
|
};
|
|
3185
|
-
|
|
2847
|
+
const array = new Collection(options);
|
|
3186
2848
|
this._managed.add(array);
|
|
3187
2849
|
this._set.set(array, new Set(options.identifiers || []));
|
|
3188
2850
|
if (config.identifiers) {
|
|
@@ -3194,7 +2856,7 @@ class RecordArrayManager {
|
|
|
3194
2856
|
if (array === FAKE_ARR) {
|
|
3195
2857
|
return;
|
|
3196
2858
|
}
|
|
3197
|
-
|
|
2859
|
+
const tag = array[ARRAY_SIGNAL];
|
|
3198
2860
|
if (!tag.shouldReset) {
|
|
3199
2861
|
tag.shouldReset = true;
|
|
3200
2862
|
addTransactionCB(array[NOTIFY]);
|
|
@@ -3206,11 +2868,11 @@ class RecordArrayManager {
|
|
|
3206
2868
|
if (this.isDestroying || this.isDestroyed) {
|
|
3207
2869
|
return;
|
|
3208
2870
|
}
|
|
3209
|
-
|
|
2871
|
+
const liveArray = this._live.get(identifier.type);
|
|
3210
2872
|
const allPending = this._pending;
|
|
3211
|
-
|
|
2873
|
+
const pending = new Map();
|
|
3212
2874
|
if (includeManaged) {
|
|
3213
|
-
|
|
2875
|
+
const managed = this._identifiers.get(identifier);
|
|
3214
2876
|
if (managed) {
|
|
3215
2877
|
managed.forEach(arr => {
|
|
3216
2878
|
let changes = allPending.get(arr);
|
|
@@ -3265,10 +2927,10 @@ class RecordArrayManager {
|
|
|
3265
2927
|
associate(this._identifiers, array, identifiers);
|
|
3266
2928
|
}
|
|
3267
2929
|
identifierAdded(identifier) {
|
|
3268
|
-
|
|
2930
|
+
const changeSets = this._getPendingFor(identifier, false);
|
|
3269
2931
|
if (changeSets) {
|
|
3270
2932
|
changeSets.forEach((changes, array) => {
|
|
3271
|
-
|
|
2933
|
+
const existing = changes.get(identifier);
|
|
3272
2934
|
if (existing === 'del') {
|
|
3273
2935
|
changes.delete(identifier);
|
|
3274
2936
|
} else {
|
|
@@ -3279,10 +2941,10 @@ class RecordArrayManager {
|
|
|
3279
2941
|
}
|
|
3280
2942
|
}
|
|
3281
2943
|
identifierRemoved(identifier) {
|
|
3282
|
-
|
|
2944
|
+
const changeSets = this._getPendingFor(identifier, true, true);
|
|
3283
2945
|
if (changeSets) {
|
|
3284
2946
|
changeSets.forEach((changes, array) => {
|
|
3285
|
-
|
|
2947
|
+
const existing = changes.get(identifier);
|
|
3286
2948
|
if (existing === 'add') {
|
|
3287
2949
|
changes.delete(identifier);
|
|
3288
2950
|
} else {
|
|
@@ -3293,7 +2955,7 @@ class RecordArrayManager {
|
|
|
3293
2955
|
}
|
|
3294
2956
|
}
|
|
3295
2957
|
identifierChanged(identifier) {
|
|
3296
|
-
|
|
2958
|
+
const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
|
|
3297
2959
|
|
|
3298
2960
|
// if the change matches the most recent direct added/removed
|
|
3299
2961
|
// state, then we can ignore it
|
|
@@ -3320,13 +2982,12 @@ class RecordArrayManager {
|
|
|
3320
2982
|
this.clear(false);
|
|
3321
2983
|
this._live.clear();
|
|
3322
2984
|
this.isDestroyed = true;
|
|
3323
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
3324
2985
|
this.store.notifications.unsubscribe(this._subscription);
|
|
3325
2986
|
}
|
|
3326
2987
|
}
|
|
3327
2988
|
function associate(ArraysCache, array, identifiers) {
|
|
3328
2989
|
for (let i = 0; i < identifiers.length; i++) {
|
|
3329
|
-
|
|
2990
|
+
const identifier = identifiers[i];
|
|
3330
2991
|
let cache = ArraysCache.get(identifier);
|
|
3331
2992
|
if (!cache) {
|
|
3332
2993
|
cache = new Set();
|
|
@@ -3341,13 +3002,13 @@ function disassociate(ArraysCache, array, identifiers) {
|
|
|
3341
3002
|
}
|
|
3342
3003
|
}
|
|
3343
3004
|
function disassociateIdentifier(ArraysCache, array, identifier) {
|
|
3344
|
-
|
|
3005
|
+
const cache = ArraysCache.get(identifier);
|
|
3345
3006
|
if (cache) {
|
|
3346
3007
|
cache.delete(array);
|
|
3347
3008
|
}
|
|
3348
3009
|
}
|
|
3349
3010
|
function sync(array, changes, arraySet) {
|
|
3350
|
-
|
|
3011
|
+
const state = array[SOURCE];
|
|
3351
3012
|
const adds = [];
|
|
3352
3013
|
const removes = [];
|
|
3353
3014
|
changes.forEach((value, key) => {
|
|
@@ -3361,13 +3022,13 @@ function sync(array, changes, arraySet) {
|
|
|
3361
3022
|
} else {
|
|
3362
3023
|
if (arraySet.has(key)) {
|
|
3363
3024
|
removes.push(key);
|
|
3025
|
+
arraySet.delete(key);
|
|
3364
3026
|
}
|
|
3365
3027
|
}
|
|
3366
3028
|
});
|
|
3367
3029
|
if (removes.length) {
|
|
3368
3030
|
if (removes.length === state.length) {
|
|
3369
3031
|
state.length = 0;
|
|
3370
|
-
arraySet.clear();
|
|
3371
3032
|
// changing the reference breaks the Proxy
|
|
3372
3033
|
// state = array[SOURCE] = [];
|
|
3373
3034
|
} else {
|
|
@@ -3394,6 +3055,9 @@ function sync(array, changes, arraySet) {
|
|
|
3394
3055
|
}
|
|
3395
3056
|
}
|
|
3396
3057
|
|
|
3058
|
+
/**
|
|
3059
|
+
* @module @ember-data/store
|
|
3060
|
+
*/
|
|
3397
3061
|
const Touching = Symbol('touching');
|
|
3398
3062
|
const RequestPromise = Symbol('promise');
|
|
3399
3063
|
const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
|
|
@@ -3421,14 +3085,14 @@ class RequestStateService {
|
|
|
3421
3085
|
this._done.delete(identifier);
|
|
3422
3086
|
}
|
|
3423
3087
|
_enqueue(promise, queryRequest) {
|
|
3424
|
-
|
|
3088
|
+
const query = queryRequest.data[0];
|
|
3425
3089
|
if (hasRecordIdentifier(query)) {
|
|
3426
3090
|
const identifier = query.recordIdentifier;
|
|
3427
|
-
|
|
3091
|
+
const type = query.op === 'saveRecord' ? 'mutation' : 'query';
|
|
3428
3092
|
if (!this._pending.has(identifier)) {
|
|
3429
3093
|
this._pending.set(identifier, []);
|
|
3430
3094
|
}
|
|
3431
|
-
|
|
3095
|
+
const request = {
|
|
3432
3096
|
state: 'pending',
|
|
3433
3097
|
request: queryRequest,
|
|
3434
3098
|
type
|
|
@@ -3439,7 +3103,7 @@ class RequestStateService {
|
|
|
3439
3103
|
this._triggerSubscriptions(request);
|
|
3440
3104
|
return promise.then(result => {
|
|
3441
3105
|
this._dequeue(identifier, request);
|
|
3442
|
-
|
|
3106
|
+
const finalizedRequest = {
|
|
3443
3107
|
state: 'fulfilled',
|
|
3444
3108
|
request: queryRequest,
|
|
3445
3109
|
type,
|
|
@@ -3453,7 +3117,7 @@ class RequestStateService {
|
|
|
3453
3117
|
return result;
|
|
3454
3118
|
}, error => {
|
|
3455
3119
|
this._dequeue(identifier, request);
|
|
3456
|
-
|
|
3120
|
+
const finalizedRequest = {
|
|
3457
3121
|
state: 'rejected',
|
|
3458
3122
|
request: queryRequest,
|
|
3459
3123
|
type,
|
|
@@ -3502,7 +3166,7 @@ class RequestStateService {
|
|
|
3502
3166
|
_addDone(request) {
|
|
3503
3167
|
request[Touching].forEach(identifier => {
|
|
3504
3168
|
// TODO add support for multiple
|
|
3505
|
-
|
|
3169
|
+
const requestDataOp = request.request.data[0].op;
|
|
3506
3170
|
let requests = this._done.get(identifier);
|
|
3507
3171
|
if (requests) {
|
|
3508
3172
|
requests = requests.filter(req => {
|
|
@@ -3566,7 +3230,7 @@ class RequestStateService {
|
|
|
3566
3230
|
* @method getPendingRequestsForRecord
|
|
3567
3231
|
* @public
|
|
3568
3232
|
* @param {StableRecordIdentifier} identifier
|
|
3569
|
-
* @
|
|
3233
|
+
* @return {RequestState[]} an array of request states for any pending requests for the given identifier
|
|
3570
3234
|
*/
|
|
3571
3235
|
getPendingRequestsForRecord(identifier) {
|
|
3572
3236
|
return this._pending.get(identifier) || EMPTY_ARR;
|
|
@@ -3578,10 +3242,10 @@ class RequestStateService {
|
|
|
3578
3242
|
* @method getLastRequestForRecord
|
|
3579
3243
|
* @public
|
|
3580
3244
|
* @param {StableRecordIdentifier} identifier
|
|
3581
|
-
* @
|
|
3245
|
+
* @return {RequestState | null} the state of the most recent request for the given identifier
|
|
3582
3246
|
*/
|
|
3583
3247
|
getLastRequestForRecord(identifier) {
|
|
3584
|
-
|
|
3248
|
+
const requests = this._done.get(identifier);
|
|
3585
3249
|
if (requests) {
|
|
3586
3250
|
return requests[requests.length - 1];
|
|
3587
3251
|
}
|
|
@@ -3593,7 +3257,7 @@ function isNonEmptyString(str) {
|
|
|
3593
3257
|
}
|
|
3594
3258
|
function constructResource(type, id, lid) {
|
|
3595
3259
|
if (typeof type === 'object' && type !== null) {
|
|
3596
|
-
|
|
3260
|
+
const resource = type;
|
|
3597
3261
|
if (isStableIdentifier(resource)) {
|
|
3598
3262
|
return resource;
|
|
3599
3263
|
}
|
|
@@ -3628,6 +3292,11 @@ function constructResource(type, id, lid) {
|
|
|
3628
3292
|
}
|
|
3629
3293
|
}
|
|
3630
3294
|
|
|
3295
|
+
/**
|
|
3296
|
+
@module @ember-data/store
|
|
3297
|
+
*/
|
|
3298
|
+
// this import location is deprecated but breaks in 4.8 and older
|
|
3299
|
+
|
|
3631
3300
|
/**
|
|
3632
3301
|
* A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
|
|
3633
3302
|
* and sources of data (such as your API or a local persistence layer)
|
|
@@ -3646,7 +3315,10 @@ function constructResource(type, id, lid) {
|
|
|
3646
3315
|
|
|
3647
3316
|
@class Store
|
|
3648
3317
|
@public
|
|
3649
|
-
*/
|
|
3318
|
+
*/
|
|
3319
|
+
|
|
3320
|
+
// @ts-expect-error
|
|
3321
|
+
|
|
3650
3322
|
class Store extends EmberObject {
|
|
3651
3323
|
/**
|
|
3652
3324
|
* Provides access to the NotificationManager associated
|
|
@@ -3778,8 +3450,6 @@ class Store extends EmberObject {
|
|
|
3778
3450
|
// private
|
|
3779
3451
|
this._requestCache = new RequestStateService(this);
|
|
3780
3452
|
this._instanceCache = new InstanceCache(this);
|
|
3781
|
-
this._adapterCache = Object.create(null);
|
|
3782
|
-
this._serializerCache = Object.create(null);
|
|
3783
3453
|
this._documentCache = new Map();
|
|
3784
3454
|
this.isDestroying = false;
|
|
3785
3455
|
this.isDestroyed = false;
|
|
@@ -3837,7 +3507,7 @@ class Store extends EmberObject {
|
|
|
3837
3507
|
* that have been initiated for a given identifier.
|
|
3838
3508
|
*
|
|
3839
3509
|
* @method getRequestStateService
|
|
3840
|
-
* @
|
|
3510
|
+
* @return {RequestStateService}
|
|
3841
3511
|
* @public
|
|
3842
3512
|
*/
|
|
3843
3513
|
getRequestStateService() {
|
|
@@ -3862,10 +3532,11 @@ class Store extends EmberObject {
|
|
|
3862
3532
|
* inserting the response into the cache and handing
|
|
3863
3533
|
* back a Future which resolves to a ResponseDocument
|
|
3864
3534
|
*
|
|
3865
|
-
*
|
|
3535
|
+
* ## Cache Keys
|
|
3866
3536
|
*
|
|
3867
|
-
* Only GET requests
|
|
3868
|
-
*
|
|
3537
|
+
* Only GET requests with a url or requests with an explicit
|
|
3538
|
+
* cache key (`cacheOptions.key`) will have the request result
|
|
3539
|
+
* and document cached.
|
|
3869
3540
|
*
|
|
3870
3541
|
* The cache key used is `requestConfig.cacheOptions.key`
|
|
3871
3542
|
* if present, falling back to `requestconfig.url`.
|
|
@@ -3876,16 +3547,44 @@ class Store extends EmberObject {
|
|
|
3876
3547
|
* via the `POST` method `requestConfig.cacheOptions.key`
|
|
3877
3548
|
* MUST be supplied for the document to be cached.
|
|
3878
3549
|
*
|
|
3550
|
+
* ## Requesting Without a Cache Key
|
|
3551
|
+
*
|
|
3552
|
+
* Resource data within the request is always updated in the cache,
|
|
3553
|
+
* regardless of whether a cache key is present for the request.
|
|
3554
|
+
*
|
|
3555
|
+
* ## Fulfilling From Cache
|
|
3556
|
+
*
|
|
3557
|
+
* When a cache-key is determined, the request may fulfill
|
|
3558
|
+
* from cache provided the cache is not stale.
|
|
3559
|
+
*
|
|
3560
|
+
* Cache staleness is determined by the configured LifetimesService
|
|
3561
|
+
* with priority given to the `cacheOptions.reload` and
|
|
3562
|
+
* `cacheOptions.backgroundReload` on the request if present.
|
|
3563
|
+
*
|
|
3564
|
+
* If the cache data has soft expired or the request asks for a background
|
|
3565
|
+
* reload, the request will fulfill from cache if possible and
|
|
3566
|
+
* make a non-blocking request in the background to update the cache.
|
|
3567
|
+
*
|
|
3568
|
+
* If the cache data has hard expired or the request asks for a reload,
|
|
3569
|
+
* the request will not fulfill from cache and will make a blocking
|
|
3570
|
+
* request to update the cache.
|
|
3571
|
+
*
|
|
3572
|
+
* ## The Response
|
|
3573
|
+
*
|
|
3574
|
+
* The primary difference between `requestManager.request` and `store.request`
|
|
3575
|
+
* is that `store.request` will attempt to hydrate the response content into
|
|
3576
|
+
* a response Document containing RecordInstances.
|
|
3577
|
+
*
|
|
3879
3578
|
* @method request
|
|
3880
3579
|
* @param {StoreRequestInput} requestConfig
|
|
3881
|
-
* @
|
|
3580
|
+
* @return {Future}
|
|
3882
3581
|
* @public
|
|
3883
3582
|
*/
|
|
3884
3583
|
request(requestConfig) {
|
|
3885
3584
|
// we lazily set the cache handler when we issue the first request
|
|
3886
3585
|
// because constructor doesn't allow for this to run after
|
|
3887
3586
|
// the user has had the chance to set the prop.
|
|
3888
|
-
|
|
3587
|
+
const opts = {
|
|
3889
3588
|
store: this,
|
|
3890
3589
|
[EnableHydration]: true
|
|
3891
3590
|
};
|
|
@@ -3937,7 +3636,7 @@ class Store extends EmberObject {
|
|
|
3937
3636
|
* @param createRecordArgs
|
|
3938
3637
|
* @param recordDataFor deprecated use this.cache
|
|
3939
3638
|
* @param notificationManager deprecated use this.notifications
|
|
3940
|
-
* @
|
|
3639
|
+
* @return A record instance
|
|
3941
3640
|
* @public
|
|
3942
3641
|
*/
|
|
3943
3642
|
|
|
@@ -4146,8 +3845,8 @@ class Store extends EmberObject {
|
|
|
4146
3845
|
// to remove this, we would need to move to a new `async` API.
|
|
4147
3846
|
let record;
|
|
4148
3847
|
this._join(() => {
|
|
4149
|
-
|
|
4150
|
-
|
|
3848
|
+
const normalizedModelName = normalizeModelName(modelName);
|
|
3849
|
+
const properties = {
|
|
4151
3850
|
...inputProperties
|
|
4152
3851
|
};
|
|
4153
3852
|
|
|
@@ -4157,7 +3856,7 @@ class Store extends EmberObject {
|
|
|
4157
3856
|
// to avoid conflicts.
|
|
4158
3857
|
|
|
4159
3858
|
if (properties.id === null || properties.id === undefined) {
|
|
4160
|
-
|
|
3859
|
+
const adapter = this.adapterFor?.(modelName, true);
|
|
4161
3860
|
if (adapter && adapter.generateIdForRecord) {
|
|
4162
3861
|
properties.id = adapter.generateIdForRecord(this, modelName, properties);
|
|
4163
3862
|
} else {
|
|
@@ -4289,8 +3988,7 @@ class Store extends EmberObject {
|
|
|
4289
3988
|
In your adapter you can then access this id without triggering a network request via the
|
|
4290
3989
|
snapshot:
|
|
4291
3990
|
```app/adapters/application.js
|
|
4292
|
-
|
|
4293
|
-
export default class Adapter extends EmberObject {
|
|
3991
|
+
export default class Adapter {
|
|
4294
3992
|
findRecord(store, schema, id, snapshot) {
|
|
4295
3993
|
let type = schema.modelName;
|
|
4296
3994
|
if (type === 'comment')
|
|
@@ -4299,6 +3997,9 @@ class Store extends EmberObject {
|
|
|
4299
3997
|
.then(response => response.json())
|
|
4300
3998
|
}
|
|
4301
3999
|
}
|
|
4000
|
+
static create() {
|
|
4001
|
+
return new this();
|
|
4002
|
+
}
|
|
4302
4003
|
}
|
|
4303
4004
|
```
|
|
4304
4005
|
This could also be achieved by supplying the post id to the adapter via the adapterOptions
|
|
@@ -4312,9 +4013,8 @@ class Store extends EmberObject {
|
|
|
4312
4013
|
}
|
|
4313
4014
|
```
|
|
4314
4015
|
```app/adapters/application.js
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
findRecord(store, schema, id, snapshot) {
|
|
4016
|
+
export default class Adapter {
|
|
4017
|
+
findRecord(store, schema, id, snapshot) {
|
|
4318
4018
|
let type = schema.modelName;
|
|
4319
4019
|
if (type === 'comment')
|
|
4320
4020
|
let postId = snapshot.adapterOptions.post;
|
|
@@ -4322,6 +4022,9 @@ class Store extends EmberObject {
|
|
|
4322
4022
|
.then(response => response.json())
|
|
4323
4023
|
}
|
|
4324
4024
|
}
|
|
4025
|
+
static create() {
|
|
4026
|
+
return new this();
|
|
4027
|
+
}
|
|
4325
4028
|
}
|
|
4326
4029
|
```
|
|
4327
4030
|
If you have access to the post model you can also pass the model itself to preload:
|
|
@@ -4448,9 +4151,8 @@ class Store extends EmberObject {
|
|
|
4448
4151
|
}
|
|
4449
4152
|
```
|
|
4450
4153
|
```app/adapters/application.js
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
findRecord(store, schema, id, snapshot) {
|
|
4154
|
+
export default class Adapter {
|
|
4155
|
+
findRecord(store, schema, id, snapshot) {
|
|
4454
4156
|
let type = schema.modelName;
|
|
4455
4157
|
if (type === 'post')
|
|
4456
4158
|
let includes = snapshot.adapterOptions.include;
|
|
@@ -4458,6 +4160,9 @@ class Store extends EmberObject {
|
|
|
4458
4160
|
.then(response => response.json())
|
|
4459
4161
|
}
|
|
4460
4162
|
}
|
|
4163
|
+
static create() {
|
|
4164
|
+
return new this();
|
|
4165
|
+
}
|
|
4461
4166
|
}
|
|
4462
4167
|
```
|
|
4463
4168
|
In this case, the post's comments would then be available in your template as
|
|
@@ -4601,7 +4306,7 @@ class Store extends EmberObject {
|
|
|
4601
4306
|
resourceIdentifier = constructResource(type, normalizedId);
|
|
4602
4307
|
}
|
|
4603
4308
|
assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
|
|
4604
|
-
|
|
4309
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
4605
4310
|
return this._instanceCache.getReference(identifier);
|
|
4606
4311
|
}
|
|
4607
4312
|
|
|
@@ -5020,7 +4725,7 @@ class Store extends EmberObject {
|
|
|
5020
4725
|
}
|
|
5021
4726
|
assert(`You need to pass a model name to the store's peekAll method`, modelName);
|
|
5022
4727
|
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
|
|
5023
|
-
|
|
4728
|
+
const type = normalizeModelName(modelName);
|
|
5024
4729
|
return this.recordArrayManager.liveArrayFor(type);
|
|
5025
4730
|
}
|
|
5026
4731
|
|
|
@@ -5050,7 +4755,7 @@ class Store extends EmberObject {
|
|
|
5050
4755
|
this.recordArrayManager.clear();
|
|
5051
4756
|
this._instanceCache.clear();
|
|
5052
4757
|
} else {
|
|
5053
|
-
|
|
4758
|
+
const normalizedModelName = normalizeModelName(modelName);
|
|
5054
4759
|
this._instanceCache.clear(normalizedModelName);
|
|
5055
4760
|
}
|
|
5056
4761
|
});
|
|
@@ -5191,422 +4896,712 @@ class Store extends EmberObject {
|
|
|
5191
4896
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5192
4897
|
assertDestroyingStore(this, 'push');
|
|
5193
4898
|
}
|
|
5194
|
-
|
|
4899
|
+
const pushed = this._push(data, false);
|
|
5195
4900
|
if (Array.isArray(pushed)) {
|
|
5196
|
-
|
|
5197
|
-
return records;
|
|
4901
|
+
return pushed.map(identifier => this._instanceCache.getRecord(identifier));
|
|
5198
4902
|
}
|
|
5199
4903
|
if (pushed === null) {
|
|
5200
4904
|
return null;
|
|
5201
4905
|
}
|
|
5202
4906
|
return this._instanceCache.getRecord(pushed);
|
|
5203
4907
|
}
|
|
5204
|
-
|
|
4908
|
+
|
|
4909
|
+
/**
|
|
4910
|
+
Push some data in the form of a json-api document into the store,
|
|
4911
|
+
without creating materialized records.
|
|
4912
|
+
@method _push
|
|
4913
|
+
@private
|
|
4914
|
+
@param {Object} jsonApiDoc
|
|
4915
|
+
@return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
|
|
4916
|
+
*/
|
|
4917
|
+
_push(jsonApiDoc, asyncFlush) {
|
|
4918
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4919
|
+
assertDestroyingStore(this, '_push');
|
|
4920
|
+
}
|
|
4921
|
+
if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
|
|
4922
|
+
try {
|
|
4923
|
+
const data = JSON.parse(JSON.stringify(jsonApiDoc));
|
|
4924
|
+
// eslint-disable-next-line no-console
|
|
4925
|
+
console.log('EmberData | Payload - push', data);
|
|
4926
|
+
} catch (e) {
|
|
4927
|
+
// eslint-disable-next-line no-console
|
|
4928
|
+
console.log('EmberData | Payload - push', jsonApiDoc);
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
if (asyncFlush) {
|
|
4932
|
+
this._enableAsyncFlush = true;
|
|
4933
|
+
}
|
|
4934
|
+
let ret;
|
|
4935
|
+
this._join(() => {
|
|
4936
|
+
ret = this.cache.put({
|
|
4937
|
+
content: jsonApiDoc
|
|
4938
|
+
});
|
|
4939
|
+
});
|
|
4940
|
+
this._enableAsyncFlush = null;
|
|
4941
|
+
return 'data' in ret ? ret.data : null;
|
|
4942
|
+
}
|
|
4943
|
+
|
|
4944
|
+
/**
|
|
4945
|
+
* Trigger a save for a Record.
|
|
4946
|
+
*
|
|
4947
|
+
* @method saveRecord
|
|
4948
|
+
* @public
|
|
4949
|
+
* @param {RecordInstance} record
|
|
4950
|
+
* @param options
|
|
4951
|
+
* @return {Promise<RecordInstance>}
|
|
4952
|
+
*/
|
|
4953
|
+
saveRecord(record, options = {}) {
|
|
4954
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4955
|
+
assertDestroyingStore(this, 'saveRecord');
|
|
4956
|
+
}
|
|
4957
|
+
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
|
|
4958
|
+
const identifier = recordIdentifierFor(record);
|
|
4959
|
+
const cache = this.cache;
|
|
4960
|
+
if (!identifier) {
|
|
4961
|
+
// this commonly means we're disconnected
|
|
4962
|
+
// but just in case we reject here to prevent bad things.
|
|
4963
|
+
return Promise.reject(new Error(`Record Is Disconnected`));
|
|
4964
|
+
}
|
|
4965
|
+
// TODO we used to check if the record was destroyed here
|
|
4966
|
+
assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
|
|
4967
|
+
if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
|
|
4968
|
+
return Promise.resolve(record);
|
|
4969
|
+
}
|
|
4970
|
+
if (!options) {
|
|
4971
|
+
options = {};
|
|
4972
|
+
}
|
|
4973
|
+
let operation = 'updateRecord';
|
|
4974
|
+
if (cache.isNew(identifier)) {
|
|
4975
|
+
operation = 'createRecord';
|
|
4976
|
+
} else if (cache.isDeleted(identifier)) {
|
|
4977
|
+
operation = 'deleteRecord';
|
|
4978
|
+
}
|
|
4979
|
+
const request = {
|
|
4980
|
+
op: operation,
|
|
4981
|
+
data: {
|
|
4982
|
+
options,
|
|
4983
|
+
record: identifier
|
|
4984
|
+
},
|
|
4985
|
+
records: [identifier],
|
|
4986
|
+
cacheOptions: {
|
|
4987
|
+
[SkipCache]: true
|
|
4988
|
+
}
|
|
4989
|
+
};
|
|
4990
|
+
|
|
4991
|
+
// we lie here on the type because legacy doesn't have enough context
|
|
4992
|
+
cache.willCommit(identifier, {
|
|
4993
|
+
request
|
|
4994
|
+
});
|
|
4995
|
+
return this.request(request).then(document => document.content);
|
|
4996
|
+
}
|
|
4997
|
+
|
|
4998
|
+
/**
|
|
4999
|
+
* Instantiation hook allowing applications or addons to configure the store
|
|
5000
|
+
* to utilize a custom Cache implementation.
|
|
5001
|
+
*
|
|
5002
|
+
* This hook should not be called directly by consuming applications or libraries.
|
|
5003
|
+
* Use `Store.cache` to access the Cache instance.
|
|
5004
|
+
*
|
|
5005
|
+
* @method createCache (hook)
|
|
5006
|
+
* @public
|
|
5007
|
+
* @param storeWrapper
|
|
5008
|
+
* @return {Cache}
|
|
5009
|
+
*/
|
|
5010
|
+
|
|
5011
|
+
/**
|
|
5012
|
+
* Returns the cache instance associated to this Store, instantiates the Cache
|
|
5013
|
+
* if necessary via `Store.createCache`
|
|
5014
|
+
*
|
|
5015
|
+
* @property {Cache} cache
|
|
5016
|
+
* @public
|
|
5017
|
+
*/
|
|
5018
|
+
get cache() {
|
|
5019
|
+
let {
|
|
5020
|
+
cache
|
|
5021
|
+
} = this._instanceCache;
|
|
5022
|
+
if (!cache) {
|
|
5023
|
+
cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
|
|
5024
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5025
|
+
cache = new CacheManager(cache);
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
return cache;
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
// @ts-expect-error
|
|
5032
|
+
destroy() {
|
|
5033
|
+
if (this.isDestroyed) {
|
|
5034
|
+
// @ember/test-helpers will call destroy multiple times
|
|
5035
|
+
return;
|
|
5036
|
+
}
|
|
5037
|
+
this.isDestroying = true;
|
|
5038
|
+
this._graph?.destroy();
|
|
5039
|
+
this._graph = undefined;
|
|
5040
|
+
this.notifications.destroy();
|
|
5041
|
+
this.recordArrayManager.destroy();
|
|
5042
|
+
this.identifierCache.destroy();
|
|
5043
|
+
this.unloadAll();
|
|
5044
|
+
this.isDestroyed = true;
|
|
5045
|
+
}
|
|
5046
|
+
static create(args) {
|
|
5047
|
+
return new this(args);
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
let assertDestroyingStore;
|
|
5051
|
+
let assertDestroyedStoreOnly;
|
|
5052
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5053
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5054
|
+
assertDestroyingStore = function assertDestroyingStore(store, method) {
|
|
5055
|
+
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
|
|
5056
|
+
};
|
|
5057
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5058
|
+
assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
|
|
5059
|
+
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
|
|
5060
|
+
};
|
|
5061
|
+
}
|
|
5062
|
+
function isMaybeIdentifier(maybeIdentifier) {
|
|
5063
|
+
return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
|
|
5064
|
+
}
|
|
5065
|
+
function normalizeProperties(store, identifier, properties) {
|
|
5066
|
+
// assert here
|
|
5067
|
+
if (properties !== undefined) {
|
|
5068
|
+
if ('id' in properties) {
|
|
5069
|
+
assert(`expected id to be a string or null`, properties.id !== undefined);
|
|
5070
|
+
}
|
|
5071
|
+
assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
|
|
5072
|
+
const {
|
|
5073
|
+
type
|
|
5074
|
+
} = identifier;
|
|
5075
|
+
|
|
5076
|
+
// convert relationship Records to RecordDatas before passing to RecordData
|
|
5077
|
+
const defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
5078
|
+
type
|
|
5079
|
+
});
|
|
5080
|
+
if (defs !== null) {
|
|
5081
|
+
const keys = Object.keys(properties);
|
|
5082
|
+
let relationshipValue;
|
|
5083
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5084
|
+
const prop = keys[i];
|
|
5085
|
+
const def = defs[prop];
|
|
5086
|
+
if (def !== undefined) {
|
|
5087
|
+
if (def.kind === 'hasMany') {
|
|
5088
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5089
|
+
assertRecordsPassedToHasMany(properties[prop]);
|
|
5090
|
+
}
|
|
5091
|
+
relationshipValue = extractIdentifiersFromRecords(properties[prop]);
|
|
5092
|
+
} else {
|
|
5093
|
+
relationshipValue = extractIdentifierFromRecord(properties[prop]);
|
|
5094
|
+
}
|
|
5095
|
+
properties[prop] = relationshipValue;
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
return properties;
|
|
5101
|
+
}
|
|
5102
|
+
function assertRecordsPassedToHasMany(records) {
|
|
5103
|
+
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
5104
|
+
assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
|
|
5105
|
+
return records.every(record => {
|
|
5106
|
+
try {
|
|
5107
|
+
recordIdentifierFor(record);
|
|
5108
|
+
return true;
|
|
5109
|
+
} catch {
|
|
5110
|
+
return false;
|
|
5111
|
+
}
|
|
5112
|
+
});
|
|
5113
|
+
}());
|
|
5114
|
+
}
|
|
5115
|
+
function extractIdentifiersFromRecords(records) {
|
|
5116
|
+
return records.map(record => extractIdentifierFromRecord(record));
|
|
5117
|
+
}
|
|
5118
|
+
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
5119
|
+
if (!recordOrPromiseRecord) {
|
|
5120
|
+
return null;
|
|
5121
|
+
}
|
|
5122
|
+
const extract = recordIdentifierFor;
|
|
5123
|
+
return extract(recordOrPromiseRecord);
|
|
5124
|
+
}
|
|
5125
|
+
function urlFromLink(link) {
|
|
5126
|
+
if (typeof link === 'string') return link;
|
|
5127
|
+
return link.href;
|
|
5128
|
+
}
|
|
5129
|
+
|
|
5130
|
+
/**
|
|
5131
|
+
* A Document is a class that wraps the response content from a request to the API
|
|
5132
|
+
* returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
|
|
5133
|
+
* record instances.
|
|
5134
|
+
*
|
|
5135
|
+
* It is not directly instantiated by the user, and its properties should not
|
|
5136
|
+
* be directly modified. Whether individual properties are mutable or not is
|
|
5137
|
+
* determined by the record instance itself.
|
|
5138
|
+
*
|
|
5139
|
+
* @public
|
|
5140
|
+
* @class Document
|
|
5141
|
+
*/
|
|
5142
|
+
var _store = /*#__PURE__*/_classPrivateFieldKey("store");
|
|
5143
|
+
var _request = /*#__PURE__*/_classPrivateFieldKey("request");
|
|
5144
|
+
class Document {
|
|
5145
|
+
constructor(store, identifier) {
|
|
5146
|
+
Object.defineProperty(this, _request, {
|
|
5147
|
+
value: _request2
|
|
5148
|
+
});
|
|
5149
|
+
/**
|
|
5150
|
+
* The links object for this document, if any
|
|
5151
|
+
*
|
|
5152
|
+
* e.g.
|
|
5153
|
+
*
|
|
5154
|
+
* ```
|
|
5155
|
+
* {
|
|
5156
|
+
* self: '/articles?page[number]=3',
|
|
5157
|
+
* }
|
|
5158
|
+
* ```
|
|
5159
|
+
*
|
|
5160
|
+
* @property links
|
|
5161
|
+
* @type {object|undefined} - a links object
|
|
5162
|
+
* @public
|
|
5163
|
+
*/
|
|
5164
|
+
/**
|
|
5165
|
+
* The primary data for this document, if any.
|
|
5166
|
+
*
|
|
5167
|
+
* If this document has no primary data (e.g. because it is an error document)
|
|
5168
|
+
* this property will be `undefined`.
|
|
5169
|
+
*
|
|
5170
|
+
* For collections this will be an array of record instances,
|
|
5171
|
+
* for single resource requests it will be a single record instance or null.
|
|
5172
|
+
*
|
|
5173
|
+
* @property data
|
|
5174
|
+
* @public
|
|
5175
|
+
* @type {object|Array<object>|null|undefined} - a data object
|
|
5176
|
+
*/
|
|
5177
|
+
/**
|
|
5178
|
+
* The errors returned by the API for this request, if any
|
|
5179
|
+
*
|
|
5180
|
+
* @property errors
|
|
5181
|
+
* @public
|
|
5182
|
+
* @type {object|undefined} - an errors object
|
|
5183
|
+
*/
|
|
5184
|
+
/**
|
|
5185
|
+
* The meta object for this document, if any
|
|
5186
|
+
*
|
|
5187
|
+
* @property meta
|
|
5188
|
+
* @public
|
|
5189
|
+
* @type {object|undefined} - a meta object
|
|
5190
|
+
*/
|
|
5191
|
+
/**
|
|
5192
|
+
* The identifier associated with this document, if any
|
|
5193
|
+
*
|
|
5194
|
+
* @property identifier
|
|
5195
|
+
* @public
|
|
5196
|
+
* @type {StableDocumentIdentifier|null}
|
|
5197
|
+
*/
|
|
5198
|
+
Object.defineProperty(this, _store, {
|
|
5199
|
+
writable: true,
|
|
5200
|
+
value: void 0
|
|
5201
|
+
});
|
|
5202
|
+
_classPrivateFieldBase(this, _store)[_store] = store;
|
|
5203
|
+
this.identifier = identifier;
|
|
5204
|
+
}
|
|
5205
5205
|
/**
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
// eslint-disable-next-line no-console
|
|
5221
|
-
console.log('EmberData | Payload - push', data);
|
|
5222
|
-
} catch (e) {
|
|
5223
|
-
// eslint-disable-next-line no-console
|
|
5224
|
-
console.log('EmberData | Payload - push', jsonApiDoc);
|
|
5225
|
-
}
|
|
5226
|
-
}
|
|
5227
|
-
if (asyncFlush) {
|
|
5228
|
-
this._enableAsyncFlush = true;
|
|
5229
|
-
}
|
|
5230
|
-
let ret;
|
|
5231
|
-
this._join(() => {
|
|
5232
|
-
ret = this.cache.put({
|
|
5233
|
-
content: jsonApiDoc
|
|
5234
|
-
});
|
|
5235
|
-
});
|
|
5236
|
-
this._enableAsyncFlush = null;
|
|
5237
|
-
return 'data' in ret ? ret.data : null;
|
|
5206
|
+
* Fetches the related link for this document, returning a promise that resolves
|
|
5207
|
+
* with the document when the request completes. If no related link is present,
|
|
5208
|
+
* will fallback to the self link if present
|
|
5209
|
+
*
|
|
5210
|
+
* @method fetch
|
|
5211
|
+
* @public
|
|
5212
|
+
* @param {object} options
|
|
5213
|
+
* @return Promise<Document>
|
|
5214
|
+
*/
|
|
5215
|
+
fetch(options = {}) {
|
|
5216
|
+
assert(`No self or related link`, this.links?.related || this.links?.self);
|
|
5217
|
+
options.cacheOptions = options.cacheOptions || {};
|
|
5218
|
+
options.cacheOptions.key = this.identifier?.lid;
|
|
5219
|
+
return _classPrivateFieldBase(this, _request)[_request](this.links.related ? 'related' : 'self', options);
|
|
5238
5220
|
}
|
|
5239
5221
|
|
|
5240
5222
|
/**
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
let pushData = {
|
|
5253
|
-
posts: [
|
|
5254
|
-
{ id: 1, postTitle: "Great post", commentIds: [2] }
|
|
5255
|
-
],
|
|
5256
|
-
comments: [
|
|
5257
|
-
{ id: 2, commentBody: "Insightful comment" }
|
|
5258
|
-
]
|
|
5259
|
-
}
|
|
5260
|
-
store.pushPayload(pushData);
|
|
5261
|
-
```
|
|
5262
|
-
By default, the data will be deserialized using a default
|
|
5263
|
-
serializer (the application serializer if it exists).
|
|
5264
|
-
Alternatively, `pushPayload` will accept a model type which
|
|
5265
|
-
will determine which serializer will process the payload.
|
|
5266
|
-
```app/serializers/application.js
|
|
5267
|
-
import RESTSerializer from '@ember-data/serializer/rest';
|
|
5268
|
-
export default class ApplicationSerializer extends RESTSerializer;
|
|
5269
|
-
```
|
|
5270
|
-
```app/serializers/post.js
|
|
5271
|
-
import JSONSerializer from '@ember-data/serializer/json';
|
|
5272
|
-
export default JSONSerializer;
|
|
5273
|
-
```
|
|
5274
|
-
```js
|
|
5275
|
-
store.pushPayload(pushData); // Will use the application serializer
|
|
5276
|
-
store.pushPayload('post', pushData); // Will use the post serializer
|
|
5277
|
-
```
|
|
5278
|
-
@method pushPayload
|
|
5279
|
-
@public
|
|
5280
|
-
@param {String} modelName Optionally, a model type used to determine which serializer will be used
|
|
5281
|
-
@param {Object} inputPayload
|
|
5282
|
-
*/
|
|
5283
|
-
// TODO @runspired @deprecate pushPayload in favor of looking up the serializer
|
|
5284
|
-
pushPayload(modelName, inputPayload) {
|
|
5285
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5286
|
-
assertDestroyingStore(this, 'pushPayload');
|
|
5287
|
-
}
|
|
5288
|
-
const payload = inputPayload || modelName;
|
|
5289
|
-
const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
|
|
5290
|
-
const serializer = this.serializerFor(normalizedModelName);
|
|
5291
|
-
assert(`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`, serializer && typeof serializer.pushPayload === 'function');
|
|
5292
|
-
serializer.pushPayload(this, payload);
|
|
5223
|
+
* Fetches the next link for this document, returning a promise that resolves
|
|
5224
|
+
* with the new document when the request completes, or null if there is no
|
|
5225
|
+
* next link.
|
|
5226
|
+
*
|
|
5227
|
+
* @method next
|
|
5228
|
+
* @public
|
|
5229
|
+
* @param {object} options
|
|
5230
|
+
* @return Promise<Document | null>
|
|
5231
|
+
*/
|
|
5232
|
+
next(options = {}) {
|
|
5233
|
+
return _classPrivateFieldBase(this, _request)[_request]('next', options);
|
|
5293
5234
|
}
|
|
5294
5235
|
|
|
5295
5236
|
/**
|
|
5296
|
-
*
|
|
5237
|
+
* Fetches the prev link for this document, returning a promise that resolves
|
|
5238
|
+
* with the new document when the request completes, or null if there is no
|
|
5239
|
+
* prev link.
|
|
5297
5240
|
*
|
|
5298
|
-
* @method
|
|
5241
|
+
* @method prev
|
|
5299
5242
|
* @public
|
|
5300
|
-
* @param {
|
|
5301
|
-
* @
|
|
5302
|
-
* @returns {Promise<RecordInstance>}
|
|
5243
|
+
* @param {object} options
|
|
5244
|
+
* @return Promise<Document | null>
|
|
5303
5245
|
*/
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
assertDestroyingStore(this, 'saveRecord');
|
|
5307
|
-
}
|
|
5308
|
-
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
|
|
5309
|
-
let identifier = recordIdentifierFor(record);
|
|
5310
|
-
const cache = this.cache;
|
|
5311
|
-
if (!identifier) {
|
|
5312
|
-
// this commonly means we're disconnected
|
|
5313
|
-
// but just in case we reject here to prevent bad things.
|
|
5314
|
-
return Promise.reject(`Record Is Disconnected`);
|
|
5315
|
-
}
|
|
5316
|
-
// TODO we used to check if the record was destroyed here
|
|
5317
|
-
assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
|
|
5318
|
-
if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
|
|
5319
|
-
return Promise.resolve(record);
|
|
5320
|
-
}
|
|
5321
|
-
if (!options) {
|
|
5322
|
-
options = {};
|
|
5323
|
-
}
|
|
5324
|
-
let operation = 'updateRecord';
|
|
5325
|
-
if (cache.isNew(identifier)) {
|
|
5326
|
-
operation = 'createRecord';
|
|
5327
|
-
} else if (cache.isDeleted(identifier)) {
|
|
5328
|
-
operation = 'deleteRecord';
|
|
5329
|
-
}
|
|
5330
|
-
const request = {
|
|
5331
|
-
op: operation,
|
|
5332
|
-
data: {
|
|
5333
|
-
options,
|
|
5334
|
-
record: identifier
|
|
5335
|
-
},
|
|
5336
|
-
cacheOptions: {
|
|
5337
|
-
[SkipCache]: true
|
|
5338
|
-
}
|
|
5339
|
-
};
|
|
5340
|
-
|
|
5341
|
-
// we lie here on the type because legacy doesn't have enough context
|
|
5342
|
-
cache.willCommit(identifier, {
|
|
5343
|
-
request
|
|
5344
|
-
});
|
|
5345
|
-
return this.request(request).then(document => document.content);
|
|
5246
|
+
prev(options = {}) {
|
|
5247
|
+
return _classPrivateFieldBase(this, _request)[_request]('prev', options);
|
|
5346
5248
|
}
|
|
5347
5249
|
|
|
5348
5250
|
/**
|
|
5349
|
-
*
|
|
5350
|
-
*
|
|
5251
|
+
* Fetches the first link for this document, returning a promise that resolves
|
|
5252
|
+
* with the new document when the request completes, or null if there is no
|
|
5253
|
+
* first link.
|
|
5351
5254
|
*
|
|
5352
|
-
*
|
|
5353
|
-
*
|
|
5255
|
+
* @method first
|
|
5256
|
+
* @public
|
|
5257
|
+
* @param {object} options
|
|
5258
|
+
* @return Promise<Document | null>
|
|
5259
|
+
*/
|
|
5260
|
+
first(options = {}) {
|
|
5261
|
+
return _classPrivateFieldBase(this, _request)[_request]('first', options);
|
|
5262
|
+
}
|
|
5263
|
+
|
|
5264
|
+
/**
|
|
5265
|
+
* Fetches the last link for this document, returning a promise that resolves
|
|
5266
|
+
* with the new document when the request completes, or null if there is no
|
|
5267
|
+
* last link.
|
|
5354
5268
|
*
|
|
5355
|
-
* @method
|
|
5269
|
+
* @method last
|
|
5356
5270
|
* @public
|
|
5357
|
-
* @param
|
|
5358
|
-
* @
|
|
5271
|
+
* @param {object} options
|
|
5272
|
+
* @return Promise<Document | null>
|
|
5359
5273
|
*/
|
|
5274
|
+
last(options = {}) {
|
|
5275
|
+
return _classPrivateFieldBase(this, _request)[_request]('last', options);
|
|
5276
|
+
}
|
|
5360
5277
|
|
|
5361
5278
|
/**
|
|
5362
|
-
*
|
|
5363
|
-
* if necessary via `Store.createCache`
|
|
5279
|
+
* Implemented for `JSON.stringify` support.
|
|
5364
5280
|
*
|
|
5365
|
-
*
|
|
5281
|
+
* Returns the JSON representation of the document wrapper.
|
|
5282
|
+
*
|
|
5283
|
+
* This is a shallow serialization, it does not deeply serialize
|
|
5284
|
+
* the document's contents, leaving that to the individual record
|
|
5285
|
+
* instances to determine how to do, if at all.
|
|
5286
|
+
*
|
|
5287
|
+
* @method toJSON
|
|
5366
5288
|
* @public
|
|
5289
|
+
* @return
|
|
5367
5290
|
*/
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5291
|
+
toJSON() {
|
|
5292
|
+
const data = {};
|
|
5293
|
+
data.identifier = this.identifier;
|
|
5294
|
+
if (this.data !== undefined) {
|
|
5295
|
+
data.data = this.data;
|
|
5296
|
+
}
|
|
5297
|
+
if (this.links !== undefined) {
|
|
5298
|
+
data.links = this.links;
|
|
5299
|
+
}
|
|
5300
|
+
if (this.errors !== undefined) {
|
|
5301
|
+
data.errors = this.errors;
|
|
5302
|
+
}
|
|
5303
|
+
if (this.meta !== undefined) {
|
|
5304
|
+
data.meta = this.meta;
|
|
5305
|
+
}
|
|
5306
|
+
return data;
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
async function _request2(link, options) {
|
|
5310
|
+
const href = this.links?.[link];
|
|
5311
|
+
if (!href) {
|
|
5312
|
+
return null;
|
|
5313
|
+
}
|
|
5314
|
+
options.method = options.method || 'GET';
|
|
5315
|
+
const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
|
|
5316
|
+
url: urlFromLink(href)
|
|
5317
|
+
}));
|
|
5318
|
+
return response.content;
|
|
5319
|
+
}
|
|
5320
|
+
defineSignal(Document.prototype, 'data');
|
|
5321
|
+
defineSignal(Document.prototype, 'links');
|
|
5322
|
+
defineSignal(Document.prototype, 'errors');
|
|
5323
|
+
defineSignal(Document.prototype, 'meta');
|
|
5324
|
+
|
|
5325
|
+
/**
|
|
5326
|
+
* @module @ember-data/store
|
|
5327
|
+
*/
|
|
5328
|
+
|
|
5329
|
+
/**
|
|
5330
|
+
* A service which an application may provide to the store via
|
|
5331
|
+
* the store's `lifetimes` property to configure the behavior
|
|
5332
|
+
* of the CacheHandler.
|
|
5333
|
+
*
|
|
5334
|
+
* The default behavior for request lifetimes is to never expire
|
|
5335
|
+
* unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
|
|
5336
|
+
*
|
|
5337
|
+
* Implementing this service allows you to programatically define
|
|
5338
|
+
* when a request should be considered expired.
|
|
5339
|
+
*
|
|
5340
|
+
* @class <Interface> LifetimesService
|
|
5341
|
+
* @public
|
|
5342
|
+
*/
|
|
5343
|
+
|
|
5344
|
+
const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
|
|
5345
|
+
function isErrorDocument(document) {
|
|
5346
|
+
return 'errors' in document;
|
|
5347
|
+
}
|
|
5348
|
+
function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
5349
|
+
const {
|
|
5350
|
+
identifier
|
|
5351
|
+
} = options;
|
|
5352
|
+
if (isErrorDocument(document)) {
|
|
5353
|
+
if (!identifier && !options.shouldHydrate) {
|
|
5354
|
+
return document;
|
|
5355
|
+
}
|
|
5356
|
+
let doc;
|
|
5357
|
+
if (identifier) {
|
|
5358
|
+
doc = store._documentCache.get(identifier);
|
|
5359
|
+
}
|
|
5360
|
+
if (!doc) {
|
|
5361
|
+
doc = new Document(store, identifier);
|
|
5362
|
+
copyDocumentProperties(doc, document);
|
|
5363
|
+
if (identifier) {
|
|
5364
|
+
store._documentCache.set(identifier, doc);
|
|
5365
|
+
}
|
|
5366
|
+
} else if (!isFromCache) {
|
|
5367
|
+
doc.data = undefined;
|
|
5368
|
+
copyDocumentProperties(doc, document);
|
|
5369
|
+
}
|
|
5370
|
+
return options.shouldHydrate ? doc : document;
|
|
5371
|
+
}
|
|
5372
|
+
if (Array.isArray(document.data)) {
|
|
5373
|
+
const {
|
|
5374
|
+
recordArrayManager
|
|
5375
|
+
} = store;
|
|
5376
|
+
if (!identifier) {
|
|
5377
|
+
if (!options.shouldHydrate) {
|
|
5378
|
+
return document;
|
|
5379
|
+
}
|
|
5380
|
+
const data = recordArrayManager.createArray({
|
|
5381
|
+
type: request.url,
|
|
5382
|
+
identifiers: document.data,
|
|
5383
|
+
doc: document,
|
|
5384
|
+
query: request
|
|
5385
|
+
});
|
|
5386
|
+
const doc = new Document(store, null);
|
|
5387
|
+
doc.data = data;
|
|
5388
|
+
doc.meta = document.meta;
|
|
5389
|
+
doc.links = document.links;
|
|
5390
|
+
return doc;
|
|
5391
|
+
}
|
|
5392
|
+
let managed = recordArrayManager._keyedArrays.get(identifier.lid);
|
|
5393
|
+
if (!managed) {
|
|
5394
|
+
managed = recordArrayManager.createArray({
|
|
5395
|
+
type: identifier.lid,
|
|
5396
|
+
identifiers: document.data,
|
|
5397
|
+
doc: document
|
|
5398
|
+
});
|
|
5399
|
+
recordArrayManager._keyedArrays.set(identifier.lid, managed);
|
|
5400
|
+
const doc = new Document(store, identifier);
|
|
5401
|
+
doc.data = managed;
|
|
5402
|
+
doc.meta = document.meta;
|
|
5403
|
+
doc.links = document.links;
|
|
5404
|
+
store._documentCache.set(identifier, doc);
|
|
5405
|
+
return options.shouldHydrate ? doc : document;
|
|
5406
|
+
} else {
|
|
5407
|
+
const doc = store._documentCache.get(identifier);
|
|
5408
|
+
if (!isFromCache) {
|
|
5409
|
+
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
5410
|
+
doc.data = managed;
|
|
5411
|
+
doc.meta = document.meta;
|
|
5412
|
+
doc.links = document.links;
|
|
5413
|
+
}
|
|
5414
|
+
return options.shouldHydrate ? doc : document;
|
|
5415
|
+
}
|
|
5416
|
+
} else {
|
|
5417
|
+
if (!identifier && !options.shouldHydrate) {
|
|
5418
|
+
return document;
|
|
5419
|
+
}
|
|
5420
|
+
const data = document.data ? store.peekRecord(document.data) : null;
|
|
5421
|
+
let doc;
|
|
5422
|
+
if (identifier) {
|
|
5423
|
+
doc = store._documentCache.get(identifier);
|
|
5424
|
+
}
|
|
5425
|
+
if (!doc) {
|
|
5426
|
+
doc = new Document(store, identifier);
|
|
5427
|
+
doc.data = data;
|
|
5428
|
+
copyDocumentProperties(doc, document);
|
|
5429
|
+
if (identifier) {
|
|
5430
|
+
store._documentCache.set(identifier, doc);
|
|
5376
5431
|
}
|
|
5432
|
+
} else if (!isFromCache) {
|
|
5433
|
+
doc.data = data;
|
|
5434
|
+
copyDocumentProperties(doc, document);
|
|
5377
5435
|
}
|
|
5378
|
-
return
|
|
5436
|
+
return options.shouldHydrate ? doc : document;
|
|
5379
5437
|
}
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5438
|
+
}
|
|
5439
|
+
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
5440
|
+
const {
|
|
5441
|
+
cacheOptions
|
|
5442
|
+
} = request;
|
|
5443
|
+
return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
|
|
5444
|
+
}
|
|
5445
|
+
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
5446
|
+
const {
|
|
5447
|
+
cacheOptions
|
|
5448
|
+
} = request;
|
|
5449
|
+
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false));
|
|
5450
|
+
}
|
|
5451
|
+
function isMutation(request) {
|
|
5452
|
+
return Boolean(request.op && MUTATION_OPS.has(request.op));
|
|
5453
|
+
}
|
|
5454
|
+
function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
|
|
5455
|
+
const {
|
|
5456
|
+
store
|
|
5457
|
+
} = context.request;
|
|
5458
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
5459
|
+
let isMut = false;
|
|
5460
|
+
if (isMutation(context.request)) {
|
|
5461
|
+
isMut = true;
|
|
5462
|
+
// TODO should we handle multiple records in request.records by iteratively calling willCommit for each
|
|
5463
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5464
|
+
assert(`Expected to receive a list of records included in the ${context.request.op} request`, record);
|
|
5465
|
+
store.cache.willCommit(record, context);
|
|
5466
|
+
}
|
|
5467
|
+
if (store.lifetimes?.willRequest) {
|
|
5468
|
+
store.lifetimes.willRequest(context.request, identifier, store);
|
|
5469
|
+
}
|
|
5470
|
+
const promise = next(context.request).then(document => {
|
|
5471
|
+
store.requestManager._pending.delete(context.id);
|
|
5472
|
+
store._enableAsyncFlush = true;
|
|
5473
|
+
let response;
|
|
5474
|
+
store._join(() => {
|
|
5475
|
+
if (isMutation(context.request)) {
|
|
5476
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5477
|
+
response = store.cache.didCommit(record, document);
|
|
5478
|
+
} else {
|
|
5479
|
+
response = store.cache.put(document);
|
|
5480
|
+
}
|
|
5481
|
+
response = maybeUpdateUiObjects(store, context.request, {
|
|
5482
|
+
shouldHydrate,
|
|
5483
|
+
shouldFetch,
|
|
5484
|
+
shouldBackgroundFetch,
|
|
5485
|
+
identifier
|
|
5486
|
+
}, response, false);
|
|
5390
5487
|
});
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
@param {String} modelName The name of the model type for this payload
|
|
5395
|
-
@param {Object} payload
|
|
5396
|
-
@return {Object} The normalized payload
|
|
5397
|
-
*/
|
|
5398
|
-
// TODO @runspired @deprecate users should call normalize on the associated serializer directly
|
|
5399
|
-
normalize(modelName, payload) {
|
|
5400
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5401
|
-
assertDestroyingStore(this, 'normalize');
|
|
5488
|
+
store._enableAsyncFlush = null;
|
|
5489
|
+
if (store.lifetimes?.didRequest) {
|
|
5490
|
+
store.lifetimes.didRequest(context.request, document.response, identifier, store);
|
|
5402
5491
|
}
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
const schema = this.modelFor(normalizedModelName);
|
|
5408
|
-
assert(`You must define a normalize method in your serializer in order to call store.normalize`, typeof serializer?.normalize === 'function');
|
|
5409
|
-
return serializer.normalize(schema, payload);
|
|
5410
|
-
}
|
|
5411
|
-
|
|
5412
|
-
/**
|
|
5413
|
-
Returns an instance of the adapter for a given type. For
|
|
5414
|
-
example, `adapterFor('person')` will return an instance of
|
|
5415
|
-
the adapter located at `app/adapters/person.js`
|
|
5416
|
-
If no `person` adapter is found, this method will look
|
|
5417
|
-
for an `application` adapter (the default adapter for
|
|
5418
|
-
your entire application).
|
|
5419
|
-
@method adapterFor
|
|
5420
|
-
@public
|
|
5421
|
-
@param {String} modelName
|
|
5422
|
-
@return Adapter
|
|
5423
|
-
*/
|
|
5424
|
-
|
|
5425
|
-
adapterFor(modelName, _allowMissing) {
|
|
5426
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5427
|
-
assertDestroyingStore(this, 'adapterFor');
|
|
5492
|
+
if (shouldFetch) {
|
|
5493
|
+
return response;
|
|
5494
|
+
} else if (shouldBackgroundFetch) {
|
|
5495
|
+
store.notifications._flush();
|
|
5428
5496
|
}
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
_adapterCache
|
|
5434
|
-
} = this;
|
|
5435
|
-
let adapter = _adapterCache[normalizedModelName];
|
|
5436
|
-
if (adapter) {
|
|
5437
|
-
return adapter;
|
|
5497
|
+
}, error => {
|
|
5498
|
+
store.requestManager._pending.delete(context.id);
|
|
5499
|
+
if (context.request.signal?.aborted) {
|
|
5500
|
+
throw error;
|
|
5438
5501
|
}
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5502
|
+
store.requestManager._pending.delete(context.id);
|
|
5503
|
+
store._enableAsyncFlush = true;
|
|
5504
|
+
let response;
|
|
5505
|
+
store._join(() => {
|
|
5506
|
+
if (isMutation(context.request)) {
|
|
5507
|
+
// TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
|
|
5508
|
+
// currently we let the response remain undefiend.
|
|
5509
|
+
const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
|
|
5510
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5511
|
+
store.cache.commitWasRejected(record, errors);
|
|
5512
|
+
// re-throw the original error to preserve `errors` property.
|
|
5513
|
+
throw error;
|
|
5514
|
+
} else {
|
|
5515
|
+
response = store.cache.put(error);
|
|
5516
|
+
response = maybeUpdateUiObjects(store, context.request, {
|
|
5517
|
+
shouldHydrate,
|
|
5518
|
+
shouldFetch,
|
|
5519
|
+
shouldBackgroundFetch,
|
|
5520
|
+
identifier
|
|
5521
|
+
}, response, false);
|
|
5522
|
+
}
|
|
5523
|
+
});
|
|
5524
|
+
store._enableAsyncFlush = null;
|
|
5525
|
+
if (identifier && store.lifetimes?.didRequest) {
|
|
5526
|
+
store.lifetimes.didRequest(context.request, error.response, identifier, store);
|
|
5446
5527
|
}
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
return adapter;
|
|
5528
|
+
if (!shouldBackgroundFetch) {
|
|
5529
|
+
const newError = cloneError(error);
|
|
5530
|
+
newError.content = response;
|
|
5531
|
+
throw newError;
|
|
5532
|
+
} else {
|
|
5533
|
+
store.notifications._flush();
|
|
5454
5534
|
}
|
|
5455
|
-
|
|
5535
|
+
});
|
|
5536
|
+
if (!isMut) {
|
|
5537
|
+
return promise;
|
|
5456
5538
|
}
|
|
5539
|
+
assert(`Expected a mutation`, isMutation(context.request));
|
|
5457
5540
|
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
_serializerCache
|
|
5481
|
-
} = this;
|
|
5482
|
-
let serializer = _serializerCache[normalizedModelName];
|
|
5483
|
-
if (serializer) {
|
|
5484
|
-
return serializer;
|
|
5485
|
-
}
|
|
5486
|
-
|
|
5487
|
-
// by name
|
|
5488
|
-
const owner = getOwner(this);
|
|
5489
|
-
serializer = owner.lookup(`serializer:${normalizedModelName}`);
|
|
5490
|
-
if (serializer !== undefined) {
|
|
5491
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
5492
|
-
return serializer;
|
|
5541
|
+
// for mutations we need to enqueue the promise with the requestStateService
|
|
5542
|
+
// TODO should we enque a request per record in records?
|
|
5543
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5544
|
+
return store._requestCache._enqueue(promise, {
|
|
5545
|
+
data: [{
|
|
5546
|
+
op: 'saveRecord',
|
|
5547
|
+
recordIdentifier: record,
|
|
5548
|
+
options: undefined
|
|
5549
|
+
}]
|
|
5550
|
+
});
|
|
5551
|
+
}
|
|
5552
|
+
function cloneError(error) {
|
|
5553
|
+
const cloned = new Error(error.message);
|
|
5554
|
+
cloned.stack = error.stack;
|
|
5555
|
+
cloned.error = error.error;
|
|
5556
|
+
return cloned;
|
|
5557
|
+
}
|
|
5558
|
+
const CacheHandler = {
|
|
5559
|
+
request(context, next) {
|
|
5560
|
+
// if we have no cache or no cache-key skip cache handling
|
|
5561
|
+
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
5562
|
+
return next(context.request);
|
|
5493
5563
|
}
|
|
5564
|
+
const {
|
|
5565
|
+
store
|
|
5566
|
+
} = context.request;
|
|
5567
|
+
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
5568
|
+
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
5494
5569
|
|
|
5495
|
-
//
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
5499
|
-
_serializerCache.application = serializer;
|
|
5500
|
-
return serializer;
|
|
5570
|
+
// determine if we should skip cache
|
|
5571
|
+
if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
|
|
5572
|
+
return fetchContentAndHydrate(next, context, identifier, true, false);
|
|
5501
5573
|
}
|
|
5502
|
-
return null;
|
|
5503
|
-
}
|
|
5504
5574
|
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
return;
|
|
5510
|
-
}
|
|
5511
|
-
this.isDestroying = true;
|
|
5512
|
-
// enqueue destruction of any adapters/serializers we have created
|
|
5513
|
-
for (let adapterName in this._adapterCache) {
|
|
5514
|
-
let adapter = this._adapterCache[adapterName];
|
|
5515
|
-
if (typeof adapter.destroy === 'function') {
|
|
5516
|
-
adapter.destroy();
|
|
5517
|
-
}
|
|
5575
|
+
// if we have not skipped cache, determine if we should update behind the scenes
|
|
5576
|
+
if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
|
|
5577
|
+
const promise = fetchContentAndHydrate(next, context, identifier, false, true);
|
|
5578
|
+
store.requestManager._pending.set(context.id, promise);
|
|
5518
5579
|
}
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5580
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
5581
|
+
if ('error' in peeked) {
|
|
5582
|
+
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
5583
|
+
shouldHydrate,
|
|
5584
|
+
identifier
|
|
5585
|
+
}, peeked.content, true) : peeked.content;
|
|
5586
|
+
const newError = cloneError(peeked);
|
|
5587
|
+
newError.content = content;
|
|
5588
|
+
throw newError;
|
|
5524
5589
|
}
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
this.identifierCache.destroy();
|
|
5530
|
-
this.unloadAll();
|
|
5531
|
-
this.isDestroyed = true;
|
|
5590
|
+
return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
5591
|
+
shouldHydrate,
|
|
5592
|
+
identifier
|
|
5593
|
+
}, peeked.content, true) : peeked.content);
|
|
5532
5594
|
}
|
|
5533
|
-
|
|
5534
|
-
|
|
5595
|
+
};
|
|
5596
|
+
function copyDocumentProperties(target, source) {
|
|
5597
|
+
if ('links' in source) {
|
|
5598
|
+
target.links = source.links;
|
|
5535
5599
|
}
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
let assertDestroyedStoreOnly;
|
|
5539
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5540
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5541
|
-
assertDestroyingStore = function assertDestroyingStore(store, method) {
|
|
5542
|
-
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
|
|
5543
|
-
};
|
|
5544
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5545
|
-
assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
|
|
5546
|
-
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
|
|
5547
|
-
};
|
|
5548
|
-
}
|
|
5549
|
-
function isMaybeIdentifier(maybeIdentifier) {
|
|
5550
|
-
return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
|
|
5551
|
-
}
|
|
5552
|
-
function normalizeProperties(store, identifier, properties) {
|
|
5553
|
-
// assert here
|
|
5554
|
-
if (properties !== undefined) {
|
|
5555
|
-
if ('id' in properties) {
|
|
5556
|
-
assert(`expected id to be a string or null`, properties.id !== undefined);
|
|
5557
|
-
}
|
|
5558
|
-
assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
|
|
5559
|
-
const {
|
|
5560
|
-
type
|
|
5561
|
-
} = identifier;
|
|
5562
|
-
|
|
5563
|
-
// convert relationship Records to RecordDatas before passing to RecordData
|
|
5564
|
-
let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
5565
|
-
type
|
|
5566
|
-
});
|
|
5567
|
-
if (defs !== null) {
|
|
5568
|
-
let keys = Object.keys(properties);
|
|
5569
|
-
let relationshipValue;
|
|
5570
|
-
for (let i = 0; i < keys.length; i++) {
|
|
5571
|
-
let prop = keys[i];
|
|
5572
|
-
let def = defs[prop];
|
|
5573
|
-
if (def !== undefined) {
|
|
5574
|
-
if (def.kind === 'hasMany') {
|
|
5575
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5576
|
-
assertRecordsPassedToHasMany(properties[prop]);
|
|
5577
|
-
}
|
|
5578
|
-
relationshipValue = extractIdentifiersFromRecords(properties[prop]);
|
|
5579
|
-
} else {
|
|
5580
|
-
relationshipValue = extractIdentifierFromRecord(properties[prop]);
|
|
5581
|
-
}
|
|
5582
|
-
properties[prop] = relationshipValue;
|
|
5583
|
-
}
|
|
5584
|
-
}
|
|
5585
|
-
}
|
|
5600
|
+
if ('meta' in source) {
|
|
5601
|
+
target.meta = source.meta;
|
|
5586
5602
|
}
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
function assertRecordsPassedToHasMany(records) {
|
|
5590
|
-
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
5591
|
-
assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
|
|
5592
|
-
return records.every(record => {
|
|
5593
|
-
try {
|
|
5594
|
-
recordIdentifierFor(record);
|
|
5595
|
-
return true;
|
|
5596
|
-
} catch {
|
|
5597
|
-
return false;
|
|
5598
|
-
}
|
|
5599
|
-
});
|
|
5600
|
-
}());
|
|
5601
|
-
}
|
|
5602
|
-
function extractIdentifiersFromRecords(records) {
|
|
5603
|
-
return records.map(record => extractIdentifierFromRecord(record));
|
|
5604
|
-
}
|
|
5605
|
-
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
5606
|
-
if (!recordOrPromiseRecord) {
|
|
5607
|
-
return null;
|
|
5603
|
+
if ('errors' in source) {
|
|
5604
|
+
target.errors = source.errors;
|
|
5608
5605
|
}
|
|
5609
|
-
const extract = recordIdentifierFor;
|
|
5610
|
-
return extract(recordOrPromiseRecord);
|
|
5611
5606
|
}
|
|
5612
|
-
export { CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, coerceId as e, Collection as f, SOURCE as g,
|
|
5607
|
+
export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, coerceId as e, Collection as f, SOURCE as g, fastPush as h, isStableIdentifier as i, removeRecordDataFor as j, setRecordIdentifier as k, StoreMap as l, setCacheFor as m, notifyArray as n, normalizeModelName as o, peekCache as p, recordIdentifierFor as r, storeFor as s };
|