@ember-data/store 5.4.0-beta.1 → 5.4.0-beta.3
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-f6cb9898.js → cache-handler-XLbbNJdo.js} +1003 -1010
- 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 +65 -42
- package/addon/store-service-f6cb9898.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,21 +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
|
-
// @ts-expect-error tagForProperty is mistyped to Tag instead of DirtyableTag
|
|
2629
|
-
// eslint-disable-next-line
|
|
2630
|
-
dirtyTag(tagForProperty(arr, 'length'));
|
|
2631
|
-
// @ts-expect-error tagForProperty is mistyped to Tag instead of DirtyableTag
|
|
2632
|
-
// eslint-disable-next-line
|
|
2633
|
-
dirtyTag(tagForProperty(arr, '[]'));
|
|
2634
|
-
}
|
|
2299
|
+
addToTransaction(arr[ARRAY_SIGNAL]);
|
|
2635
2300
|
}
|
|
2636
2301
|
function convertToInt(prop) {
|
|
2637
2302
|
if (typeof prop === 'symbol') return null;
|
|
@@ -2639,29 +2304,6 @@ function convertToInt(prop) {
|
|
|
2639
2304
|
if (isNaN(num)) return null;
|
|
2640
2305
|
return num % 1 === 0 ? num : null;
|
|
2641
2306
|
}
|
|
2642
|
-
let Tag = (_class = class Tag {
|
|
2643
|
-
/*
|
|
2644
|
-
* whether this was part of a transaction when last mutated
|
|
2645
|
-
*/
|
|
2646
|
-
|
|
2647
|
-
constructor() {
|
|
2648
|
-
_initializerDefineProperty(this, "ref", _descriptor, this);
|
|
2649
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
2650
|
-
const [arr, prop] = arguments;
|
|
2651
|
-
this._debug_base = arr.constructor.name + ':' + String(arr.modelName);
|
|
2652
|
-
this._debug_prop = prop;
|
|
2653
|
-
}
|
|
2654
|
-
this.shouldReset = false;
|
|
2655
|
-
this.t = false;
|
|
2656
|
-
}
|
|
2657
|
-
}, _descriptor = _applyDecoratedDescriptor(_class.prototype, "ref", [tracked], {
|
|
2658
|
-
configurable: true,
|
|
2659
|
-
enumerable: true,
|
|
2660
|
-
writable: true,
|
|
2661
|
-
initializer: function () {
|
|
2662
|
-
return null;
|
|
2663
|
-
}
|
|
2664
|
-
}), _class);
|
|
2665
2307
|
function safeForEach(instance, arr, store, callback, target) {
|
|
2666
2308
|
if (target === undefined) {
|
|
2667
2309
|
target = null;
|
|
@@ -2694,7 +2336,7 @@ function safeForEach(instance, arr, store, callback, target) {
|
|
|
2694
2336
|
@class RecordArray
|
|
2695
2337
|
@public
|
|
2696
2338
|
*/
|
|
2697
|
-
let IdentifierArray = (
|
|
2339
|
+
let IdentifierArray = (_class = class IdentifierArray {
|
|
2698
2340
|
[NOTIFY]() {
|
|
2699
2341
|
notifyArray(this);
|
|
2700
2342
|
}
|
|
@@ -2722,14 +2364,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2722
2364
|
set length(value) {
|
|
2723
2365
|
this[SOURCE].length = value;
|
|
2724
2366
|
}
|
|
2725
|
-
|
|
2726
|
-
// here to support computed chains
|
|
2727
|
-
// and {{#each}}
|
|
2728
|
-
get '[]'() {
|
|
2729
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
2730
|
-
return this;
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
2367
|
constructor(options) {
|
|
2734
2368
|
/**
|
|
2735
2369
|
The flag to signal a `RecordArray` is currently loading data.
|
|
@@ -2744,7 +2378,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2744
2378
|
@public
|
|
2745
2379
|
@type Boolean
|
|
2746
2380
|
*/
|
|
2747
|
-
_initializerDefineProperty(this, "isUpdating", _descriptor2, this);
|
|
2748
2381
|
this.isLoaded = true;
|
|
2749
2382
|
this.isDestroying = false;
|
|
2750
2383
|
this.isDestroyed = false;
|
|
@@ -2752,16 +2385,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2752
2385
|
this[IS_COLLECTION] = true;
|
|
2753
2386
|
this[SOURCE] = void 0;
|
|
2754
2387
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2755
|
-
|
|
2388
|
+
const self = this;
|
|
2756
2389
|
this.modelName = options.type;
|
|
2757
2390
|
this.store = options.store;
|
|
2758
2391
|
this._manager = options.manager;
|
|
2759
2392
|
this[SOURCE] = options.identifiers;
|
|
2760
|
-
|
|
2761
|
-
this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
|
|
2393
|
+
this[ARRAY_SIGNAL] = createSignal(this, 'length');
|
|
2762
2394
|
const store = options.store;
|
|
2763
2395
|
const boundFns = new Map();
|
|
2764
|
-
const
|
|
2396
|
+
const _SIGNAL = this[ARRAY_SIGNAL];
|
|
2765
2397
|
const PrivateState = {
|
|
2766
2398
|
links: options.links || null,
|
|
2767
2399
|
meta: options.meta || null
|
|
@@ -2774,40 +2406,40 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2774
2406
|
|
|
2775
2407
|
const proxy = new Proxy(this[SOURCE], {
|
|
2776
2408
|
get(target, prop, receiver) {
|
|
2777
|
-
|
|
2778
|
-
if (
|
|
2409
|
+
const index = convertToInt(prop);
|
|
2410
|
+
if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
|
|
2779
2411
|
options.manager._syncArray(receiver);
|
|
2780
|
-
|
|
2781
|
-
|
|
2412
|
+
_SIGNAL.t = false;
|
|
2413
|
+
_SIGNAL.shouldReset = false;
|
|
2782
2414
|
}
|
|
2783
2415
|
if (index !== null) {
|
|
2784
2416
|
const identifier = target[index];
|
|
2785
2417
|
if (!transaction) {
|
|
2786
|
-
subscribe(
|
|
2418
|
+
subscribe(_SIGNAL);
|
|
2787
2419
|
}
|
|
2788
2420
|
return identifier && store._instanceCache.getRecord(identifier);
|
|
2789
2421
|
}
|
|
2790
|
-
if (prop === 'meta') return subscribe(
|
|
2791
|
-
if (prop === 'links') return subscribe(
|
|
2792
|
-
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;
|
|
2793
2425
|
if (isArrayGetter(prop)) {
|
|
2794
2426
|
let fn = boundFns.get(prop);
|
|
2795
2427
|
if (fn === undefined) {
|
|
2796
2428
|
if (prop === 'forEach') {
|
|
2797
2429
|
fn = function () {
|
|
2798
|
-
subscribe(
|
|
2430
|
+
subscribe(_SIGNAL);
|
|
2799
2431
|
transaction = true;
|
|
2800
|
-
|
|
2432
|
+
const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
|
|
2801
2433
|
transaction = false;
|
|
2802
2434
|
return result;
|
|
2803
2435
|
};
|
|
2804
2436
|
} else {
|
|
2805
2437
|
fn = function () {
|
|
2806
|
-
subscribe(
|
|
2438
|
+
subscribe(_SIGNAL);
|
|
2807
2439
|
// array functions must run through Reflect to work properly
|
|
2808
2440
|
// binding via other means will not work.
|
|
2809
2441
|
transaction = true;
|
|
2810
|
-
|
|
2442
|
+
const result = Reflect.apply(target[prop], receiver, arguments);
|
|
2811
2443
|
transaction = false;
|
|
2812
2444
|
return result;
|
|
2813
2445
|
};
|
|
@@ -2829,10 +2461,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2829
2461
|
const args = Array.prototype.slice.call(arguments);
|
|
2830
2462
|
assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
|
|
2831
2463
|
transaction = true;
|
|
2832
|
-
|
|
2833
|
-
self[MUTATE](prop, args, result);
|
|
2834
|
-
addToTransaction(_TAG);
|
|
2835
|
-
// TODO handle cache updates
|
|
2464
|
+
const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
|
|
2836
2465
|
transaction = false;
|
|
2837
2466
|
return result;
|
|
2838
2467
|
};
|
|
@@ -2840,16 +2469,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2840
2469
|
}
|
|
2841
2470
|
return fn;
|
|
2842
2471
|
}
|
|
2843
|
-
if (prop
|
|
2844
|
-
if (prop === NOTIFY || prop ===
|
|
2472
|
+
if (isSelfProp(self, prop)) {
|
|
2473
|
+
if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
|
|
2845
2474
|
return self[prop];
|
|
2846
2475
|
}
|
|
2847
2476
|
let fn = boundFns.get(prop);
|
|
2848
2477
|
if (fn) return fn;
|
|
2849
|
-
|
|
2478
|
+
const outcome = self[prop];
|
|
2850
2479
|
if (typeof outcome === 'function') {
|
|
2851
2480
|
fn = function () {
|
|
2852
|
-
subscribe(
|
|
2481
|
+
subscribe(_SIGNAL);
|
|
2853
2482
|
// array functions must run through Reflect to work properly
|
|
2854
2483
|
// binding via other means will not work.
|
|
2855
2484
|
return Reflect.apply(outcome, receiver, arguments);
|
|
@@ -2857,17 +2486,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2857
2486
|
boundFns.set(prop, fn);
|
|
2858
2487
|
return fn;
|
|
2859
2488
|
}
|
|
2860
|
-
return subscribe(
|
|
2489
|
+
return subscribe(_SIGNAL), outcome;
|
|
2861
2490
|
}
|
|
2862
2491
|
return target[prop];
|
|
2863
2492
|
},
|
|
2864
|
-
|
|
2493
|
+
// FIXME: Should this get a generic like get above?
|
|
2494
|
+
set(target, prop, value, receiver) {
|
|
2865
2495
|
if (prop === 'length') {
|
|
2866
2496
|
if (!transaction && value === 0) {
|
|
2867
2497
|
transaction = true;
|
|
2868
|
-
|
|
2869
|
-
Reflect.set(target, prop, value);
|
|
2870
|
-
self[MUTATE]('length 0', []);
|
|
2498
|
+
self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
|
|
2871
2499
|
transaction = false;
|
|
2872
2500
|
return true;
|
|
2873
2501
|
} else if (transaction) {
|
|
@@ -2884,9 +2512,22 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2884
2512
|
PrivateState.meta = value || null;
|
|
2885
2513
|
return true;
|
|
2886
2514
|
}
|
|
2887
|
-
|
|
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.
|
|
2888
2524
|
if (index === null || index > target.length) {
|
|
2889
|
-
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)) {
|
|
2890
2531
|
self[prop] = value;
|
|
2891
2532
|
return true;
|
|
2892
2533
|
}
|
|
@@ -2896,12 +2537,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2896
2537
|
assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
|
|
2897
2538
|
return false;
|
|
2898
2539
|
}
|
|
2899
|
-
|
|
2900
|
-
|
|
2540
|
+
const original = target[index];
|
|
2541
|
+
const newIdentifier = extractIdentifierFromRecord$1(value);
|
|
2901
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.
|
|
2902
2560
|
if (!transaction) {
|
|
2903
|
-
self[MUTATE]('replace cell', [index, original, newIdentifier]);
|
|
2904
|
-
|
|
2561
|
+
self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
|
|
2562
|
+
} else {
|
|
2563
|
+
target[index] = newIdentifier;
|
|
2905
2564
|
}
|
|
2906
2565
|
return true;
|
|
2907
2566
|
},
|
|
@@ -2916,12 +2575,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2916
2575
|
return IdentifierArray.prototype;
|
|
2917
2576
|
}
|
|
2918
2577
|
});
|
|
2919
|
-
|
|
2920
|
-
const meta = Ember.meta(this);
|
|
2921
|
-
meta.addMixin = mixin => {
|
|
2922
|
-
assert(`Do not call A() on EmberData RecordArrays`);
|
|
2923
|
-
};
|
|
2924
|
-
}
|
|
2578
|
+
createArrayTags(proxy, _SIGNAL);
|
|
2925
2579
|
this[NOTIFY] = this[NOTIFY].bind(proxy);
|
|
2926
2580
|
return proxy;
|
|
2927
2581
|
}
|
|
@@ -2946,7 +2600,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2946
2600
|
return this._updatingPromise;
|
|
2947
2601
|
}
|
|
2948
2602
|
this.isUpdating = true;
|
|
2949
|
-
|
|
2603
|
+
const updatingPromise = this._update();
|
|
2950
2604
|
void updatingPromise.finally(() => {
|
|
2951
2605
|
this._updatingPromise = null;
|
|
2952
2606
|
if (this.isDestroying || this.isDestroyed) {
|
|
@@ -2985,17 +2639,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2985
2639
|
@return {Promise<IdentifierArray>} promise
|
|
2986
2640
|
*/
|
|
2987
2641
|
save() {
|
|
2988
|
-
|
|
2642
|
+
const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
|
|
2989
2643
|
return promise;
|
|
2990
2644
|
}
|
|
2991
|
-
},
|
|
2992
|
-
|
|
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 = {
|
|
2993
2650
|
enumerable: true,
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
return
|
|
2651
|
+
configurable: false,
|
|
2652
|
+
get: function () {
|
|
2653
|
+
return this;
|
|
2997
2654
|
}
|
|
2998
|
-
}
|
|
2655
|
+
};
|
|
2656
|
+
compat(desc);
|
|
2657
|
+
Object.defineProperty(IdentifierArray.prototype, '[]', desc);
|
|
2658
|
+
defineSignal(IdentifierArray.prototype, 'isUpdating', false);
|
|
2999
2659
|
class Collection extends IdentifierArray {
|
|
3000
2660
|
constructor(options) {
|
|
3001
2661
|
super(options);
|
|
@@ -3027,7 +2687,8 @@ class Collection extends IdentifierArray {
|
|
|
3027
2687
|
Collection.prototype.query = null;
|
|
3028
2688
|
|
|
3029
2689
|
// Ensure instanceof works correctly
|
|
3030
|
-
//Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2690
|
+
// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2691
|
+
|
|
3031
2692
|
function assertRecordPassedToHasMany(record) {
|
|
3032
2693
|
assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
|
|
3033
2694
|
try {
|
|
@@ -3049,7 +2710,6 @@ function extractIdentifierFromRecord$1(record) {
|
|
|
3049
2710
|
/**
|
|
3050
2711
|
@module @ember-data/store
|
|
3051
2712
|
*/
|
|
3052
|
-
|
|
3053
2713
|
const FAKE_ARR = {};
|
|
3054
2714
|
const SLICE_BATCH_SIZE = 1200;
|
|
3055
2715
|
/**
|
|
@@ -3092,7 +2752,7 @@ const SLICE_BATCH_SIZE = 1200;
|
|
|
3092
2752
|
*/
|
|
3093
2753
|
function fastPush(target, source) {
|
|
3094
2754
|
let startLength = 0;
|
|
3095
|
-
|
|
2755
|
+
const newLength = source.length;
|
|
3096
2756
|
while (newLength - startLength > SLICE_BATCH_SIZE) {
|
|
3097
2757
|
// eslint-disable-next-line prefer-spread
|
|
3098
2758
|
target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
|
|
@@ -3149,8 +2809,8 @@ class RecordArrayManager {
|
|
|
3149
2809
|
*/
|
|
3150
2810
|
liveArrayFor(type) {
|
|
3151
2811
|
let array = this._live.get(type);
|
|
3152
|
-
|
|
3153
|
-
|
|
2812
|
+
const identifiers = [];
|
|
2813
|
+
const staged = this._staged.get(type);
|
|
3154
2814
|
if (staged) {
|
|
3155
2815
|
staged.forEach((value, key) => {
|
|
3156
2816
|
if (value === 'add') {
|
|
@@ -3173,7 +2833,7 @@ class RecordArrayManager {
|
|
|
3173
2833
|
return array;
|
|
3174
2834
|
}
|
|
3175
2835
|
createArray(config) {
|
|
3176
|
-
|
|
2836
|
+
const options = {
|
|
3177
2837
|
type: config.type,
|
|
3178
2838
|
links: config.doc?.links || null,
|
|
3179
2839
|
meta: config.doc?.meta || null,
|
|
@@ -3184,7 +2844,7 @@ class RecordArrayManager {
|
|
|
3184
2844
|
store: this.store,
|
|
3185
2845
|
manager: this
|
|
3186
2846
|
};
|
|
3187
|
-
|
|
2847
|
+
const array = new Collection(options);
|
|
3188
2848
|
this._managed.add(array);
|
|
3189
2849
|
this._set.set(array, new Set(options.identifiers || []));
|
|
3190
2850
|
if (config.identifiers) {
|
|
@@ -3196,7 +2856,7 @@ class RecordArrayManager {
|
|
|
3196
2856
|
if (array === FAKE_ARR) {
|
|
3197
2857
|
return;
|
|
3198
2858
|
}
|
|
3199
|
-
|
|
2859
|
+
const tag = array[ARRAY_SIGNAL];
|
|
3200
2860
|
if (!tag.shouldReset) {
|
|
3201
2861
|
tag.shouldReset = true;
|
|
3202
2862
|
addTransactionCB(array[NOTIFY]);
|
|
@@ -3208,11 +2868,11 @@ class RecordArrayManager {
|
|
|
3208
2868
|
if (this.isDestroying || this.isDestroyed) {
|
|
3209
2869
|
return;
|
|
3210
2870
|
}
|
|
3211
|
-
|
|
2871
|
+
const liveArray = this._live.get(identifier.type);
|
|
3212
2872
|
const allPending = this._pending;
|
|
3213
|
-
|
|
2873
|
+
const pending = new Map();
|
|
3214
2874
|
if (includeManaged) {
|
|
3215
|
-
|
|
2875
|
+
const managed = this._identifiers.get(identifier);
|
|
3216
2876
|
if (managed) {
|
|
3217
2877
|
managed.forEach(arr => {
|
|
3218
2878
|
let changes = allPending.get(arr);
|
|
@@ -3267,10 +2927,10 @@ class RecordArrayManager {
|
|
|
3267
2927
|
associate(this._identifiers, array, identifiers);
|
|
3268
2928
|
}
|
|
3269
2929
|
identifierAdded(identifier) {
|
|
3270
|
-
|
|
2930
|
+
const changeSets = this._getPendingFor(identifier, false);
|
|
3271
2931
|
if (changeSets) {
|
|
3272
2932
|
changeSets.forEach((changes, array) => {
|
|
3273
|
-
|
|
2933
|
+
const existing = changes.get(identifier);
|
|
3274
2934
|
if (existing === 'del') {
|
|
3275
2935
|
changes.delete(identifier);
|
|
3276
2936
|
} else {
|
|
@@ -3281,10 +2941,10 @@ class RecordArrayManager {
|
|
|
3281
2941
|
}
|
|
3282
2942
|
}
|
|
3283
2943
|
identifierRemoved(identifier) {
|
|
3284
|
-
|
|
2944
|
+
const changeSets = this._getPendingFor(identifier, true, true);
|
|
3285
2945
|
if (changeSets) {
|
|
3286
2946
|
changeSets.forEach((changes, array) => {
|
|
3287
|
-
|
|
2947
|
+
const existing = changes.get(identifier);
|
|
3288
2948
|
if (existing === 'add') {
|
|
3289
2949
|
changes.delete(identifier);
|
|
3290
2950
|
} else {
|
|
@@ -3295,7 +2955,7 @@ class RecordArrayManager {
|
|
|
3295
2955
|
}
|
|
3296
2956
|
}
|
|
3297
2957
|
identifierChanged(identifier) {
|
|
3298
|
-
|
|
2958
|
+
const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
|
|
3299
2959
|
|
|
3300
2960
|
// if the change matches the most recent direct added/removed
|
|
3301
2961
|
// state, then we can ignore it
|
|
@@ -3322,13 +2982,12 @@ class RecordArrayManager {
|
|
|
3322
2982
|
this.clear(false);
|
|
3323
2983
|
this._live.clear();
|
|
3324
2984
|
this.isDestroyed = true;
|
|
3325
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
3326
2985
|
this.store.notifications.unsubscribe(this._subscription);
|
|
3327
2986
|
}
|
|
3328
2987
|
}
|
|
3329
2988
|
function associate(ArraysCache, array, identifiers) {
|
|
3330
2989
|
for (let i = 0; i < identifiers.length; i++) {
|
|
3331
|
-
|
|
2990
|
+
const identifier = identifiers[i];
|
|
3332
2991
|
let cache = ArraysCache.get(identifier);
|
|
3333
2992
|
if (!cache) {
|
|
3334
2993
|
cache = new Set();
|
|
@@ -3343,13 +3002,13 @@ function disassociate(ArraysCache, array, identifiers) {
|
|
|
3343
3002
|
}
|
|
3344
3003
|
}
|
|
3345
3004
|
function disassociateIdentifier(ArraysCache, array, identifier) {
|
|
3346
|
-
|
|
3005
|
+
const cache = ArraysCache.get(identifier);
|
|
3347
3006
|
if (cache) {
|
|
3348
3007
|
cache.delete(array);
|
|
3349
3008
|
}
|
|
3350
3009
|
}
|
|
3351
3010
|
function sync(array, changes, arraySet) {
|
|
3352
|
-
|
|
3011
|
+
const state = array[SOURCE];
|
|
3353
3012
|
const adds = [];
|
|
3354
3013
|
const removes = [];
|
|
3355
3014
|
changes.forEach((value, key) => {
|
|
@@ -3363,13 +3022,13 @@ function sync(array, changes, arraySet) {
|
|
|
3363
3022
|
} else {
|
|
3364
3023
|
if (arraySet.has(key)) {
|
|
3365
3024
|
removes.push(key);
|
|
3025
|
+
arraySet.delete(key);
|
|
3366
3026
|
}
|
|
3367
3027
|
}
|
|
3368
3028
|
});
|
|
3369
3029
|
if (removes.length) {
|
|
3370
3030
|
if (removes.length === state.length) {
|
|
3371
3031
|
state.length = 0;
|
|
3372
|
-
arraySet.clear();
|
|
3373
3032
|
// changing the reference breaks the Proxy
|
|
3374
3033
|
// state = array[SOURCE] = [];
|
|
3375
3034
|
} else {
|
|
@@ -3396,6 +3055,9 @@ function sync(array, changes, arraySet) {
|
|
|
3396
3055
|
}
|
|
3397
3056
|
}
|
|
3398
3057
|
|
|
3058
|
+
/**
|
|
3059
|
+
* @module @ember-data/store
|
|
3060
|
+
*/
|
|
3399
3061
|
const Touching = Symbol('touching');
|
|
3400
3062
|
const RequestPromise = Symbol('promise');
|
|
3401
3063
|
const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
|
|
@@ -3423,14 +3085,14 @@ class RequestStateService {
|
|
|
3423
3085
|
this._done.delete(identifier);
|
|
3424
3086
|
}
|
|
3425
3087
|
_enqueue(promise, queryRequest) {
|
|
3426
|
-
|
|
3088
|
+
const query = queryRequest.data[0];
|
|
3427
3089
|
if (hasRecordIdentifier(query)) {
|
|
3428
3090
|
const identifier = query.recordIdentifier;
|
|
3429
|
-
|
|
3091
|
+
const type = query.op === 'saveRecord' ? 'mutation' : 'query';
|
|
3430
3092
|
if (!this._pending.has(identifier)) {
|
|
3431
3093
|
this._pending.set(identifier, []);
|
|
3432
3094
|
}
|
|
3433
|
-
|
|
3095
|
+
const request = {
|
|
3434
3096
|
state: 'pending',
|
|
3435
3097
|
request: queryRequest,
|
|
3436
3098
|
type
|
|
@@ -3441,7 +3103,7 @@ class RequestStateService {
|
|
|
3441
3103
|
this._triggerSubscriptions(request);
|
|
3442
3104
|
return promise.then(result => {
|
|
3443
3105
|
this._dequeue(identifier, request);
|
|
3444
|
-
|
|
3106
|
+
const finalizedRequest = {
|
|
3445
3107
|
state: 'fulfilled',
|
|
3446
3108
|
request: queryRequest,
|
|
3447
3109
|
type,
|
|
@@ -3455,7 +3117,7 @@ class RequestStateService {
|
|
|
3455
3117
|
return result;
|
|
3456
3118
|
}, error => {
|
|
3457
3119
|
this._dequeue(identifier, request);
|
|
3458
|
-
|
|
3120
|
+
const finalizedRequest = {
|
|
3459
3121
|
state: 'rejected',
|
|
3460
3122
|
request: queryRequest,
|
|
3461
3123
|
type,
|
|
@@ -3504,7 +3166,7 @@ class RequestStateService {
|
|
|
3504
3166
|
_addDone(request) {
|
|
3505
3167
|
request[Touching].forEach(identifier => {
|
|
3506
3168
|
// TODO add support for multiple
|
|
3507
|
-
|
|
3169
|
+
const requestDataOp = request.request.data[0].op;
|
|
3508
3170
|
let requests = this._done.get(identifier);
|
|
3509
3171
|
if (requests) {
|
|
3510
3172
|
requests = requests.filter(req => {
|
|
@@ -3568,7 +3230,7 @@ class RequestStateService {
|
|
|
3568
3230
|
* @method getPendingRequestsForRecord
|
|
3569
3231
|
* @public
|
|
3570
3232
|
* @param {StableRecordIdentifier} identifier
|
|
3571
|
-
* @
|
|
3233
|
+
* @return {RequestState[]} an array of request states for any pending requests for the given identifier
|
|
3572
3234
|
*/
|
|
3573
3235
|
getPendingRequestsForRecord(identifier) {
|
|
3574
3236
|
return this._pending.get(identifier) || EMPTY_ARR;
|
|
@@ -3580,10 +3242,10 @@ class RequestStateService {
|
|
|
3580
3242
|
* @method getLastRequestForRecord
|
|
3581
3243
|
* @public
|
|
3582
3244
|
* @param {StableRecordIdentifier} identifier
|
|
3583
|
-
* @
|
|
3245
|
+
* @return {RequestState | null} the state of the most recent request for the given identifier
|
|
3584
3246
|
*/
|
|
3585
3247
|
getLastRequestForRecord(identifier) {
|
|
3586
|
-
|
|
3248
|
+
const requests = this._done.get(identifier);
|
|
3587
3249
|
if (requests) {
|
|
3588
3250
|
return requests[requests.length - 1];
|
|
3589
3251
|
}
|
|
@@ -3595,7 +3257,7 @@ function isNonEmptyString(str) {
|
|
|
3595
3257
|
}
|
|
3596
3258
|
function constructResource(type, id, lid) {
|
|
3597
3259
|
if (typeof type === 'object' && type !== null) {
|
|
3598
|
-
|
|
3260
|
+
const resource = type;
|
|
3599
3261
|
if (isStableIdentifier(resource)) {
|
|
3600
3262
|
return resource;
|
|
3601
3263
|
}
|
|
@@ -3630,6 +3292,11 @@ function constructResource(type, id, lid) {
|
|
|
3630
3292
|
}
|
|
3631
3293
|
}
|
|
3632
3294
|
|
|
3295
|
+
/**
|
|
3296
|
+
@module @ember-data/store
|
|
3297
|
+
*/
|
|
3298
|
+
// this import location is deprecated but breaks in 4.8 and older
|
|
3299
|
+
|
|
3633
3300
|
/**
|
|
3634
3301
|
* A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
|
|
3635
3302
|
* and sources of data (such as your API or a local persistence layer)
|
|
@@ -3648,7 +3315,10 @@ function constructResource(type, id, lid) {
|
|
|
3648
3315
|
|
|
3649
3316
|
@class Store
|
|
3650
3317
|
@public
|
|
3651
|
-
*/
|
|
3318
|
+
*/
|
|
3319
|
+
|
|
3320
|
+
// @ts-expect-error
|
|
3321
|
+
|
|
3652
3322
|
class Store extends EmberObject {
|
|
3653
3323
|
/**
|
|
3654
3324
|
* Provides access to the NotificationManager associated
|
|
@@ -3780,8 +3450,6 @@ class Store extends EmberObject {
|
|
|
3780
3450
|
// private
|
|
3781
3451
|
this._requestCache = new RequestStateService(this);
|
|
3782
3452
|
this._instanceCache = new InstanceCache(this);
|
|
3783
|
-
this._adapterCache = Object.create(null);
|
|
3784
|
-
this._serializerCache = Object.create(null);
|
|
3785
3453
|
this._documentCache = new Map();
|
|
3786
3454
|
this.isDestroying = false;
|
|
3787
3455
|
this.isDestroyed = false;
|
|
@@ -3839,7 +3507,7 @@ class Store extends EmberObject {
|
|
|
3839
3507
|
* that have been initiated for a given identifier.
|
|
3840
3508
|
*
|
|
3841
3509
|
* @method getRequestStateService
|
|
3842
|
-
* @
|
|
3510
|
+
* @return {RequestStateService}
|
|
3843
3511
|
* @public
|
|
3844
3512
|
*/
|
|
3845
3513
|
getRequestStateService() {
|
|
@@ -3864,10 +3532,11 @@ class Store extends EmberObject {
|
|
|
3864
3532
|
* inserting the response into the cache and handing
|
|
3865
3533
|
* back a Future which resolves to a ResponseDocument
|
|
3866
3534
|
*
|
|
3867
|
-
*
|
|
3535
|
+
* ## Cache Keys
|
|
3868
3536
|
*
|
|
3869
|
-
* Only GET requests
|
|
3870
|
-
*
|
|
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.
|
|
3871
3540
|
*
|
|
3872
3541
|
* The cache key used is `requestConfig.cacheOptions.key`
|
|
3873
3542
|
* if present, falling back to `requestconfig.url`.
|
|
@@ -3878,16 +3547,44 @@ class Store extends EmberObject {
|
|
|
3878
3547
|
* via the `POST` method `requestConfig.cacheOptions.key`
|
|
3879
3548
|
* MUST be supplied for the document to be cached.
|
|
3880
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
|
+
*
|
|
3881
3578
|
* @method request
|
|
3882
3579
|
* @param {StoreRequestInput} requestConfig
|
|
3883
|
-
* @
|
|
3580
|
+
* @return {Future}
|
|
3884
3581
|
* @public
|
|
3885
3582
|
*/
|
|
3886
3583
|
request(requestConfig) {
|
|
3887
3584
|
// we lazily set the cache handler when we issue the first request
|
|
3888
3585
|
// because constructor doesn't allow for this to run after
|
|
3889
3586
|
// the user has had the chance to set the prop.
|
|
3890
|
-
|
|
3587
|
+
const opts = {
|
|
3891
3588
|
store: this,
|
|
3892
3589
|
[EnableHydration]: true
|
|
3893
3590
|
};
|
|
@@ -3939,7 +3636,7 @@ class Store extends EmberObject {
|
|
|
3939
3636
|
* @param createRecordArgs
|
|
3940
3637
|
* @param recordDataFor deprecated use this.cache
|
|
3941
3638
|
* @param notificationManager deprecated use this.notifications
|
|
3942
|
-
* @
|
|
3639
|
+
* @return A record instance
|
|
3943
3640
|
* @public
|
|
3944
3641
|
*/
|
|
3945
3642
|
|
|
@@ -4148,8 +3845,8 @@ class Store extends EmberObject {
|
|
|
4148
3845
|
// to remove this, we would need to move to a new `async` API.
|
|
4149
3846
|
let record;
|
|
4150
3847
|
this._join(() => {
|
|
4151
|
-
|
|
4152
|
-
|
|
3848
|
+
const normalizedModelName = normalizeModelName(modelName);
|
|
3849
|
+
const properties = {
|
|
4153
3850
|
...inputProperties
|
|
4154
3851
|
};
|
|
4155
3852
|
|
|
@@ -4159,7 +3856,7 @@ class Store extends EmberObject {
|
|
|
4159
3856
|
// to avoid conflicts.
|
|
4160
3857
|
|
|
4161
3858
|
if (properties.id === null || properties.id === undefined) {
|
|
4162
|
-
|
|
3859
|
+
const adapter = this.adapterFor?.(modelName, true);
|
|
4163
3860
|
if (adapter && adapter.generateIdForRecord) {
|
|
4164
3861
|
properties.id = adapter.generateIdForRecord(this, modelName, properties);
|
|
4165
3862
|
} else {
|
|
@@ -4291,8 +3988,7 @@ class Store extends EmberObject {
|
|
|
4291
3988
|
In your adapter you can then access this id without triggering a network request via the
|
|
4292
3989
|
snapshot:
|
|
4293
3990
|
```app/adapters/application.js
|
|
4294
|
-
|
|
4295
|
-
export default class Adapter extends EmberObject {
|
|
3991
|
+
export default class Adapter {
|
|
4296
3992
|
findRecord(store, schema, id, snapshot) {
|
|
4297
3993
|
let type = schema.modelName;
|
|
4298
3994
|
if (type === 'comment')
|
|
@@ -4301,6 +3997,9 @@ class Store extends EmberObject {
|
|
|
4301
3997
|
.then(response => response.json())
|
|
4302
3998
|
}
|
|
4303
3999
|
}
|
|
4000
|
+
static create() {
|
|
4001
|
+
return new this();
|
|
4002
|
+
}
|
|
4304
4003
|
}
|
|
4305
4004
|
```
|
|
4306
4005
|
This could also be achieved by supplying the post id to the adapter via the adapterOptions
|
|
@@ -4314,9 +4013,8 @@ class Store extends EmberObject {
|
|
|
4314
4013
|
}
|
|
4315
4014
|
```
|
|
4316
4015
|
```app/adapters/application.js
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
findRecord(store, schema, id, snapshot) {
|
|
4016
|
+
export default class Adapter {
|
|
4017
|
+
findRecord(store, schema, id, snapshot) {
|
|
4320
4018
|
let type = schema.modelName;
|
|
4321
4019
|
if (type === 'comment')
|
|
4322
4020
|
let postId = snapshot.adapterOptions.post;
|
|
@@ -4324,6 +4022,9 @@ class Store extends EmberObject {
|
|
|
4324
4022
|
.then(response => response.json())
|
|
4325
4023
|
}
|
|
4326
4024
|
}
|
|
4025
|
+
static create() {
|
|
4026
|
+
return new this();
|
|
4027
|
+
}
|
|
4327
4028
|
}
|
|
4328
4029
|
```
|
|
4329
4030
|
If you have access to the post model you can also pass the model itself to preload:
|
|
@@ -4450,9 +4151,8 @@ class Store extends EmberObject {
|
|
|
4450
4151
|
}
|
|
4451
4152
|
```
|
|
4452
4153
|
```app/adapters/application.js
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
findRecord(store, schema, id, snapshot) {
|
|
4154
|
+
export default class Adapter {
|
|
4155
|
+
findRecord(store, schema, id, snapshot) {
|
|
4456
4156
|
let type = schema.modelName;
|
|
4457
4157
|
if (type === 'post')
|
|
4458
4158
|
let includes = snapshot.adapterOptions.include;
|
|
@@ -4460,6 +4160,9 @@ class Store extends EmberObject {
|
|
|
4460
4160
|
.then(response => response.json())
|
|
4461
4161
|
}
|
|
4462
4162
|
}
|
|
4163
|
+
static create() {
|
|
4164
|
+
return new this();
|
|
4165
|
+
}
|
|
4463
4166
|
}
|
|
4464
4167
|
```
|
|
4465
4168
|
In this case, the post's comments would then be available in your template as
|
|
@@ -4603,7 +4306,7 @@ class Store extends EmberObject {
|
|
|
4603
4306
|
resourceIdentifier = constructResource(type, normalizedId);
|
|
4604
4307
|
}
|
|
4605
4308
|
assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
|
|
4606
|
-
|
|
4309
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
4607
4310
|
return this._instanceCache.getReference(identifier);
|
|
4608
4311
|
}
|
|
4609
4312
|
|
|
@@ -5022,7 +4725,7 @@ class Store extends EmberObject {
|
|
|
5022
4725
|
}
|
|
5023
4726
|
assert(`You need to pass a model name to the store's peekAll method`, modelName);
|
|
5024
4727
|
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
|
|
5025
|
-
|
|
4728
|
+
const type = normalizeModelName(modelName);
|
|
5026
4729
|
return this.recordArrayManager.liveArrayFor(type);
|
|
5027
4730
|
}
|
|
5028
4731
|
|
|
@@ -5052,7 +4755,7 @@ class Store extends EmberObject {
|
|
|
5052
4755
|
this.recordArrayManager.clear();
|
|
5053
4756
|
this._instanceCache.clear();
|
|
5054
4757
|
} else {
|
|
5055
|
-
|
|
4758
|
+
const normalizedModelName = normalizeModelName(modelName);
|
|
5056
4759
|
this._instanceCache.clear(normalizedModelName);
|
|
5057
4760
|
}
|
|
5058
4761
|
});
|
|
@@ -5193,422 +4896,712 @@ class Store extends EmberObject {
|
|
|
5193
4896
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5194
4897
|
assertDestroyingStore(this, 'push');
|
|
5195
4898
|
}
|
|
5196
|
-
|
|
4899
|
+
const pushed = this._push(data, false);
|
|
5197
4900
|
if (Array.isArray(pushed)) {
|
|
5198
|
-
|
|
5199
|
-
return records;
|
|
4901
|
+
return pushed.map(identifier => this._instanceCache.getRecord(identifier));
|
|
5200
4902
|
}
|
|
5201
4903
|
if (pushed === null) {
|
|
5202
4904
|
return null;
|
|
5203
4905
|
}
|
|
5204
4906
|
return this._instanceCache.getRecord(pushed);
|
|
5205
4907
|
}
|
|
5206
|
-
|
|
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
|
+
}
|
|
5207
5205
|
/**
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
// eslint-disable-next-line no-console
|
|
5223
|
-
console.log('EmberData | Payload - push', data);
|
|
5224
|
-
} catch (e) {
|
|
5225
|
-
// eslint-disable-next-line no-console
|
|
5226
|
-
console.log('EmberData | Payload - push', jsonApiDoc);
|
|
5227
|
-
}
|
|
5228
|
-
}
|
|
5229
|
-
if (asyncFlush) {
|
|
5230
|
-
this._enableAsyncFlush = true;
|
|
5231
|
-
}
|
|
5232
|
-
let ret;
|
|
5233
|
-
this._join(() => {
|
|
5234
|
-
ret = this.cache.put({
|
|
5235
|
-
content: jsonApiDoc
|
|
5236
|
-
});
|
|
5237
|
-
});
|
|
5238
|
-
this._enableAsyncFlush = null;
|
|
5239
|
-
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);
|
|
5240
5220
|
}
|
|
5241
5221
|
|
|
5242
5222
|
/**
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
let pushData = {
|
|
5255
|
-
posts: [
|
|
5256
|
-
{ id: 1, postTitle: "Great post", commentIds: [2] }
|
|
5257
|
-
],
|
|
5258
|
-
comments: [
|
|
5259
|
-
{ id: 2, commentBody: "Insightful comment" }
|
|
5260
|
-
]
|
|
5261
|
-
}
|
|
5262
|
-
store.pushPayload(pushData);
|
|
5263
|
-
```
|
|
5264
|
-
By default, the data will be deserialized using a default
|
|
5265
|
-
serializer (the application serializer if it exists).
|
|
5266
|
-
Alternatively, `pushPayload` will accept a model type which
|
|
5267
|
-
will determine which serializer will process the payload.
|
|
5268
|
-
```app/serializers/application.js
|
|
5269
|
-
import RESTSerializer from '@ember-data/serializer/rest';
|
|
5270
|
-
export default class ApplicationSerializer extends RESTSerializer;
|
|
5271
|
-
```
|
|
5272
|
-
```app/serializers/post.js
|
|
5273
|
-
import JSONSerializer from '@ember-data/serializer/json';
|
|
5274
|
-
export default JSONSerializer;
|
|
5275
|
-
```
|
|
5276
|
-
```js
|
|
5277
|
-
store.pushPayload(pushData); // Will use the application serializer
|
|
5278
|
-
store.pushPayload('post', pushData); // Will use the post serializer
|
|
5279
|
-
```
|
|
5280
|
-
@method pushPayload
|
|
5281
|
-
@public
|
|
5282
|
-
@param {String} modelName Optionally, a model type used to determine which serializer will be used
|
|
5283
|
-
@param {Object} inputPayload
|
|
5284
|
-
*/
|
|
5285
|
-
// TODO @runspired @deprecate pushPayload in favor of looking up the serializer
|
|
5286
|
-
pushPayload(modelName, inputPayload) {
|
|
5287
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5288
|
-
assertDestroyingStore(this, 'pushPayload');
|
|
5289
|
-
}
|
|
5290
|
-
const payload = inputPayload || modelName;
|
|
5291
|
-
const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
|
|
5292
|
-
const serializer = this.serializerFor(normalizedModelName);
|
|
5293
|
-
assert(`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`, serializer && typeof serializer.pushPayload === 'function');
|
|
5294
|
-
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);
|
|
5295
5234
|
}
|
|
5296
5235
|
|
|
5297
5236
|
/**
|
|
5298
|
-
*
|
|
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.
|
|
5299
5240
|
*
|
|
5300
|
-
* @method
|
|
5241
|
+
* @method prev
|
|
5301
5242
|
* @public
|
|
5302
|
-
* @param {
|
|
5303
|
-
* @
|
|
5304
|
-
* @returns {Promise<RecordInstance>}
|
|
5243
|
+
* @param {object} options
|
|
5244
|
+
* @return Promise<Document | null>
|
|
5305
5245
|
*/
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
assertDestroyingStore(this, 'saveRecord');
|
|
5309
|
-
}
|
|
5310
|
-
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
|
|
5311
|
-
let identifier = recordIdentifierFor(record);
|
|
5312
|
-
const cache = this.cache;
|
|
5313
|
-
if (!identifier) {
|
|
5314
|
-
// this commonly means we're disconnected
|
|
5315
|
-
// but just in case we reject here to prevent bad things.
|
|
5316
|
-
return Promise.reject(`Record Is Disconnected`);
|
|
5317
|
-
}
|
|
5318
|
-
// TODO we used to check if the record was destroyed here
|
|
5319
|
-
assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
|
|
5320
|
-
if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
|
|
5321
|
-
return Promise.resolve(record);
|
|
5322
|
-
}
|
|
5323
|
-
if (!options) {
|
|
5324
|
-
options = {};
|
|
5325
|
-
}
|
|
5326
|
-
let operation = 'updateRecord';
|
|
5327
|
-
if (cache.isNew(identifier)) {
|
|
5328
|
-
operation = 'createRecord';
|
|
5329
|
-
} else if (cache.isDeleted(identifier)) {
|
|
5330
|
-
operation = 'deleteRecord';
|
|
5331
|
-
}
|
|
5332
|
-
const request = {
|
|
5333
|
-
op: operation,
|
|
5334
|
-
data: {
|
|
5335
|
-
options,
|
|
5336
|
-
record: identifier
|
|
5337
|
-
},
|
|
5338
|
-
cacheOptions: {
|
|
5339
|
-
[SkipCache]: true
|
|
5340
|
-
}
|
|
5341
|
-
};
|
|
5342
|
-
|
|
5343
|
-
// we lie here on the type because legacy doesn't have enough context
|
|
5344
|
-
cache.willCommit(identifier, {
|
|
5345
|
-
request
|
|
5346
|
-
});
|
|
5347
|
-
return this.request(request).then(document => document.content);
|
|
5246
|
+
prev(options = {}) {
|
|
5247
|
+
return _classPrivateFieldBase(this, _request)[_request]('prev', options);
|
|
5348
5248
|
}
|
|
5349
5249
|
|
|
5350
5250
|
/**
|
|
5351
|
-
*
|
|
5352
|
-
*
|
|
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.
|
|
5353
5254
|
*
|
|
5354
|
-
*
|
|
5355
|
-
*
|
|
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.
|
|
5356
5268
|
*
|
|
5357
|
-
* @method
|
|
5269
|
+
* @method last
|
|
5358
5270
|
* @public
|
|
5359
|
-
* @param
|
|
5360
|
-
* @
|
|
5271
|
+
* @param {object} options
|
|
5272
|
+
* @return Promise<Document | null>
|
|
5361
5273
|
*/
|
|
5274
|
+
last(options = {}) {
|
|
5275
|
+
return _classPrivateFieldBase(this, _request)[_request]('last', options);
|
|
5276
|
+
}
|
|
5362
5277
|
|
|
5363
5278
|
/**
|
|
5364
|
-
*
|
|
5365
|
-
* if necessary via `Store.createCache`
|
|
5279
|
+
* Implemented for `JSON.stringify` support.
|
|
5366
5280
|
*
|
|
5367
|
-
*
|
|
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
|
|
5368
5288
|
* @public
|
|
5289
|
+
* @return
|
|
5369
5290
|
*/
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
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);
|
|
5378
5431
|
}
|
|
5432
|
+
} else if (!isFromCache) {
|
|
5433
|
+
doc.data = data;
|
|
5434
|
+
copyDocumentProperties(doc, document);
|
|
5379
5435
|
}
|
|
5380
|
-
return
|
|
5436
|
+
return options.shouldHydrate ? doc : document;
|
|
5381
5437
|
}
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
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);
|
|
5392
5487
|
});
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
@param {String} modelName The name of the model type for this payload
|
|
5397
|
-
@param {Object} payload
|
|
5398
|
-
@return {Object} The normalized payload
|
|
5399
|
-
*/
|
|
5400
|
-
// TODO @runspired @deprecate users should call normalize on the associated serializer directly
|
|
5401
|
-
normalize(modelName, payload) {
|
|
5402
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5403
|
-
assertDestroyingStore(this, 'normalize');
|
|
5488
|
+
store._enableAsyncFlush = null;
|
|
5489
|
+
if (store.lifetimes?.didRequest) {
|
|
5490
|
+
store.lifetimes.didRequest(context.request, document.response, identifier, store);
|
|
5404
5491
|
}
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
const schema = this.modelFor(normalizedModelName);
|
|
5410
|
-
assert(`You must define a normalize method in your serializer in order to call store.normalize`, typeof serializer?.normalize === 'function');
|
|
5411
|
-
return serializer.normalize(schema, payload);
|
|
5412
|
-
}
|
|
5413
|
-
|
|
5414
|
-
/**
|
|
5415
|
-
Returns an instance of the adapter for a given type. For
|
|
5416
|
-
example, `adapterFor('person')` will return an instance of
|
|
5417
|
-
the adapter located at `app/adapters/person.js`
|
|
5418
|
-
If no `person` adapter is found, this method will look
|
|
5419
|
-
for an `application` adapter (the default adapter for
|
|
5420
|
-
your entire application).
|
|
5421
|
-
@method adapterFor
|
|
5422
|
-
@public
|
|
5423
|
-
@param {String} modelName
|
|
5424
|
-
@return Adapter
|
|
5425
|
-
*/
|
|
5426
|
-
|
|
5427
|
-
adapterFor(modelName, _allowMissing) {
|
|
5428
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5429
|
-
assertDestroyingStore(this, 'adapterFor');
|
|
5492
|
+
if (shouldFetch) {
|
|
5493
|
+
return response;
|
|
5494
|
+
} else if (shouldBackgroundFetch) {
|
|
5495
|
+
store.notifications._flush();
|
|
5430
5496
|
}
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
_adapterCache
|
|
5436
|
-
} = this;
|
|
5437
|
-
let adapter = _adapterCache[normalizedModelName];
|
|
5438
|
-
if (adapter) {
|
|
5439
|
-
return adapter;
|
|
5497
|
+
}, error => {
|
|
5498
|
+
store.requestManager._pending.delete(context.id);
|
|
5499
|
+
if (context.request.signal?.aborted) {
|
|
5500
|
+
throw error;
|
|
5440
5501
|
}
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
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);
|
|
5448
5527
|
}
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
return adapter;
|
|
5528
|
+
if (!shouldBackgroundFetch) {
|
|
5529
|
+
const newError = cloneError(error);
|
|
5530
|
+
newError.content = response;
|
|
5531
|
+
throw newError;
|
|
5532
|
+
} else {
|
|
5533
|
+
store.notifications._flush();
|
|
5456
5534
|
}
|
|
5457
|
-
|
|
5535
|
+
});
|
|
5536
|
+
if (!isMut) {
|
|
5537
|
+
return promise;
|
|
5458
5538
|
}
|
|
5539
|
+
assert(`Expected a mutation`, isMutation(context.request));
|
|
5459
5540
|
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
_serializerCache
|
|
5483
|
-
} = this;
|
|
5484
|
-
let serializer = _serializerCache[normalizedModelName];
|
|
5485
|
-
if (serializer) {
|
|
5486
|
-
return serializer;
|
|
5487
|
-
}
|
|
5488
|
-
|
|
5489
|
-
// by name
|
|
5490
|
-
const owner = getOwner(this);
|
|
5491
|
-
serializer = owner.lookup(`serializer:${normalizedModelName}`);
|
|
5492
|
-
if (serializer !== undefined) {
|
|
5493
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
5494
|
-
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);
|
|
5495
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;
|
|
5496
5569
|
|
|
5497
|
-
//
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
5501
|
-
_serializerCache.application = serializer;
|
|
5502
|
-
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);
|
|
5503
5573
|
}
|
|
5504
|
-
return null;
|
|
5505
|
-
}
|
|
5506
5574
|
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
return;
|
|
5512
|
-
}
|
|
5513
|
-
this.isDestroying = true;
|
|
5514
|
-
// enqueue destruction of any adapters/serializers we have created
|
|
5515
|
-
for (let adapterName in this._adapterCache) {
|
|
5516
|
-
let adapter = this._adapterCache[adapterName];
|
|
5517
|
-
if (typeof adapter.destroy === 'function') {
|
|
5518
|
-
adapter.destroy();
|
|
5519
|
-
}
|
|
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);
|
|
5520
5579
|
}
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
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;
|
|
5526
5589
|
}
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
this.identifierCache.destroy();
|
|
5532
|
-
this.unloadAll();
|
|
5533
|
-
this.isDestroyed = true;
|
|
5590
|
+
return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
5591
|
+
shouldHydrate,
|
|
5592
|
+
identifier
|
|
5593
|
+
}, peeked.content, true) : peeked.content);
|
|
5534
5594
|
}
|
|
5535
|
-
|
|
5536
|
-
|
|
5595
|
+
};
|
|
5596
|
+
function copyDocumentProperties(target, source) {
|
|
5597
|
+
if ('links' in source) {
|
|
5598
|
+
target.links = source.links;
|
|
5537
5599
|
}
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
let assertDestroyedStoreOnly;
|
|
5541
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5542
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5543
|
-
assertDestroyingStore = function assertDestroyingStore(store, method) {
|
|
5544
|
-
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
|
|
5545
|
-
};
|
|
5546
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5547
|
-
assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
|
|
5548
|
-
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
|
|
5549
|
-
};
|
|
5550
|
-
}
|
|
5551
|
-
function isMaybeIdentifier(maybeIdentifier) {
|
|
5552
|
-
return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
|
|
5553
|
-
}
|
|
5554
|
-
function normalizeProperties(store, identifier, properties) {
|
|
5555
|
-
// assert here
|
|
5556
|
-
if (properties !== undefined) {
|
|
5557
|
-
if ('id' in properties) {
|
|
5558
|
-
assert(`expected id to be a string or null`, properties.id !== undefined);
|
|
5559
|
-
}
|
|
5560
|
-
assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
|
|
5561
|
-
const {
|
|
5562
|
-
type
|
|
5563
|
-
} = identifier;
|
|
5564
|
-
|
|
5565
|
-
// convert relationship Records to RecordDatas before passing to RecordData
|
|
5566
|
-
let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
5567
|
-
type
|
|
5568
|
-
});
|
|
5569
|
-
if (defs !== null) {
|
|
5570
|
-
let keys = Object.keys(properties);
|
|
5571
|
-
let relationshipValue;
|
|
5572
|
-
for (let i = 0; i < keys.length; i++) {
|
|
5573
|
-
let prop = keys[i];
|
|
5574
|
-
let def = defs[prop];
|
|
5575
|
-
if (def !== undefined) {
|
|
5576
|
-
if (def.kind === 'hasMany') {
|
|
5577
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5578
|
-
assertRecordsPassedToHasMany(properties[prop]);
|
|
5579
|
-
}
|
|
5580
|
-
relationshipValue = extractIdentifiersFromRecords(properties[prop]);
|
|
5581
|
-
} else {
|
|
5582
|
-
relationshipValue = extractIdentifierFromRecord(properties[prop]);
|
|
5583
|
-
}
|
|
5584
|
-
properties[prop] = relationshipValue;
|
|
5585
|
-
}
|
|
5586
|
-
}
|
|
5587
|
-
}
|
|
5600
|
+
if ('meta' in source) {
|
|
5601
|
+
target.meta = source.meta;
|
|
5588
5602
|
}
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
function assertRecordsPassedToHasMany(records) {
|
|
5592
|
-
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
5593
|
-
assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
|
|
5594
|
-
return records.every(record => {
|
|
5595
|
-
try {
|
|
5596
|
-
recordIdentifierFor(record);
|
|
5597
|
-
return true;
|
|
5598
|
-
} catch {
|
|
5599
|
-
return false;
|
|
5600
|
-
}
|
|
5601
|
-
});
|
|
5602
|
-
}());
|
|
5603
|
-
}
|
|
5604
|
-
function extractIdentifiersFromRecords(records) {
|
|
5605
|
-
return records.map(record => extractIdentifierFromRecord(record));
|
|
5606
|
-
}
|
|
5607
|
-
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
5608
|
-
if (!recordOrPromiseRecord) {
|
|
5609
|
-
return null;
|
|
5603
|
+
if ('errors' in source) {
|
|
5604
|
+
target.errors = source.errors;
|
|
5610
5605
|
}
|
|
5611
|
-
const extract = recordIdentifierFor;
|
|
5612
|
-
return extract(recordOrPromiseRecord);
|
|
5613
5606
|
}
|
|
5614
|
-
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 };
|