@ember-data/store 4.12.0-beta.7 → 4.12.0-beta.9
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/LICENSE.md +1 -1
- package/README.md +14 -12
- package/addon/-private.js +1 -1
- package/addon/{index-f7d3d5e5.js → index-8b77b852.js} +638 -209
- package/addon/index-8b77b852.js.map +1 -0
- package/addon/index.js +1 -1
- package/ember-data-logo-dark.svg +12 -0
- package/ember-data-logo-light.svg +12 -0
- package/package.json +22 -16
- package/addon/index-f7d3d5e5.js.map +0 -1
|
@@ -35,6 +35,347 @@ import ObjectProxy from '@ember/object/proxy';
|
|
|
35
35
|
function normalizeModelName$1(modelName) {
|
|
36
36
|
return dasherize(modelName);
|
|
37
37
|
}
|
|
38
|
+
function _initializerDefineProperty(target, property, descriptor, context) {
|
|
39
|
+
if (!descriptor) return;
|
|
40
|
+
Object.defineProperty(target, property, {
|
|
41
|
+
enumerable: descriptor.enumerable,
|
|
42
|
+
configurable: descriptor.configurable,
|
|
43
|
+
writable: descriptor.writable,
|
|
44
|
+
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function _classPrivateFieldBase(receiver, privateKey) {
|
|
48
|
+
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
49
|
+
throw new TypeError("attempted to use private field on non-instance");
|
|
50
|
+
}
|
|
51
|
+
return receiver;
|
|
52
|
+
}
|
|
53
|
+
var id = 0;
|
|
54
|
+
function _classPrivateFieldKey(name) {
|
|
55
|
+
return "__private_" + id++ + "_" + name;
|
|
56
|
+
}
|
|
57
|
+
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
|
58
|
+
var desc = {};
|
|
59
|
+
Object.keys(descriptor).forEach(function (key) {
|
|
60
|
+
desc[key] = descriptor[key];
|
|
61
|
+
});
|
|
62
|
+
desc.enumerable = !!desc.enumerable;
|
|
63
|
+
desc.configurable = !!desc.configurable;
|
|
64
|
+
if ('value' in desc || desc.initializer) {
|
|
65
|
+
desc.writable = true;
|
|
66
|
+
}
|
|
67
|
+
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
|
|
68
|
+
return decorator(target, property, desc) || desc;
|
|
69
|
+
}, desc);
|
|
70
|
+
if (context && desc.initializer !== void 0) {
|
|
71
|
+
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
|
|
72
|
+
desc.initializer = undefined;
|
|
73
|
+
}
|
|
74
|
+
if (desc.initializer === void 0) {
|
|
75
|
+
Object.defineProperty(target, property, desc);
|
|
76
|
+
desc = null;
|
|
77
|
+
}
|
|
78
|
+
return desc;
|
|
79
|
+
}
|
|
80
|
+
var _class$3, _descriptor$3, _descriptor2$1, _descriptor3, _descriptor4, _store$1, _request;
|
|
81
|
+
function urlFromLink(link) {
|
|
82
|
+
if (typeof link === 'string') return link;
|
|
83
|
+
return link.href;
|
|
84
|
+
}
|
|
85
|
+
let Document = (_class$3 = (_store$1 = /*#__PURE__*/_classPrivateFieldKey("store"), _request = /*#__PURE__*/_classPrivateFieldKey("request"), class Document {
|
|
86
|
+
constructor(store, identifier) {
|
|
87
|
+
Object.defineProperty(this, _request, {
|
|
88
|
+
value: _request2
|
|
89
|
+
});
|
|
90
|
+
_initializerDefineProperty(this, "links", _descriptor$3, this);
|
|
91
|
+
_initializerDefineProperty(this, "data", _descriptor2$1, this);
|
|
92
|
+
_initializerDefineProperty(this, "errors", _descriptor3, this);
|
|
93
|
+
_initializerDefineProperty(this, "meta", _descriptor4, this);
|
|
94
|
+
Object.defineProperty(this, _store$1, {
|
|
95
|
+
writable: true,
|
|
96
|
+
value: void 0
|
|
97
|
+
});
|
|
98
|
+
_classPrivateFieldBase(this, _store$1)[_store$1] = store;
|
|
99
|
+
this.identifier = identifier;
|
|
100
|
+
}
|
|
101
|
+
fetch(options = {}) {
|
|
102
|
+
assert(`No self link`, this.links?.self);
|
|
103
|
+
options.cacheOptions = options.cacheOptions || {};
|
|
104
|
+
options.cacheOptions.key = this.identifier?.lid;
|
|
105
|
+
return _classPrivateFieldBase(this, _request)[_request]('self', options);
|
|
106
|
+
}
|
|
107
|
+
next(options) {
|
|
108
|
+
return _classPrivateFieldBase(this, _request)[_request]('next', options);
|
|
109
|
+
}
|
|
110
|
+
prev(options) {
|
|
111
|
+
return _classPrivateFieldBase(this, _request)[_request]('prev', options);
|
|
112
|
+
}
|
|
113
|
+
first(options) {
|
|
114
|
+
return _classPrivateFieldBase(this, _request)[_request]('first', options);
|
|
115
|
+
}
|
|
116
|
+
last(options) {
|
|
117
|
+
return _classPrivateFieldBase(this, _request)[_request]('last', options);
|
|
118
|
+
}
|
|
119
|
+
toJSON() {
|
|
120
|
+
const data = {};
|
|
121
|
+
data.identifier = this.identifier;
|
|
122
|
+
if (this.data !== undefined) {
|
|
123
|
+
data.data = this.data;
|
|
124
|
+
}
|
|
125
|
+
if (this.links !== undefined) {
|
|
126
|
+
data.links = this.links;
|
|
127
|
+
}
|
|
128
|
+
if (this.errors !== undefined) {
|
|
129
|
+
data.errors = this.errors;
|
|
130
|
+
}
|
|
131
|
+
if (this.meta !== undefined) {
|
|
132
|
+
data.meta = this.meta;
|
|
133
|
+
}
|
|
134
|
+
return data;
|
|
135
|
+
}
|
|
136
|
+
}), (_descriptor$3 = _applyDecoratedDescriptor(_class$3.prototype, "links", [tracked], {
|
|
137
|
+
configurable: true,
|
|
138
|
+
enumerable: true,
|
|
139
|
+
writable: true,
|
|
140
|
+
initializer: null
|
|
141
|
+
}), _descriptor2$1 = _applyDecoratedDescriptor(_class$3.prototype, "data", [tracked], {
|
|
142
|
+
configurable: true,
|
|
143
|
+
enumerable: true,
|
|
144
|
+
writable: true,
|
|
145
|
+
initializer: null
|
|
146
|
+
}), _descriptor3 = _applyDecoratedDescriptor(_class$3.prototype, "errors", [tracked], {
|
|
147
|
+
configurable: true,
|
|
148
|
+
enumerable: true,
|
|
149
|
+
writable: true,
|
|
150
|
+
initializer: null
|
|
151
|
+
}), _descriptor4 = _applyDecoratedDescriptor(_class$3.prototype, "meta", [tracked], {
|
|
152
|
+
configurable: true,
|
|
153
|
+
enumerable: true,
|
|
154
|
+
writable: true,
|
|
155
|
+
initializer: null
|
|
156
|
+
})), _class$3);
|
|
157
|
+
async function _request2(link, options = {}) {
|
|
158
|
+
const href = this.links?.[link];
|
|
159
|
+
if (!href) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const response = await _classPrivateFieldBase(this, _store$1)[_store$1].request(Object.assign(options, {
|
|
163
|
+
url: urlFromLink(href)
|
|
164
|
+
}));
|
|
165
|
+
return response.content;
|
|
166
|
+
}
|
|
167
|
+
function isErrorDocument(document) {
|
|
168
|
+
return 'errors' in document;
|
|
169
|
+
}
|
|
170
|
+
function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
171
|
+
const {
|
|
172
|
+
identifier
|
|
173
|
+
} = options;
|
|
174
|
+
if (isErrorDocument(document)) {
|
|
175
|
+
if (!identifier && !options.shouldHydrate) {
|
|
176
|
+
return document;
|
|
177
|
+
}
|
|
178
|
+
let doc;
|
|
179
|
+
if (identifier) {
|
|
180
|
+
doc = store._documentCache.get(identifier);
|
|
181
|
+
}
|
|
182
|
+
if (!doc) {
|
|
183
|
+
doc = new Document(store, identifier);
|
|
184
|
+
copyDocumentProperties(doc, document);
|
|
185
|
+
if (identifier) {
|
|
186
|
+
store._documentCache.set(identifier, doc);
|
|
187
|
+
}
|
|
188
|
+
} else if (!isFromCache) {
|
|
189
|
+
doc.data = undefined;
|
|
190
|
+
copyDocumentProperties(doc, document);
|
|
191
|
+
}
|
|
192
|
+
return options.shouldHydrate ? doc : document;
|
|
193
|
+
}
|
|
194
|
+
if (Array.isArray(document.data)) {
|
|
195
|
+
const {
|
|
196
|
+
recordArrayManager
|
|
197
|
+
} = store;
|
|
198
|
+
if (!identifier) {
|
|
199
|
+
if (!options.shouldHydrate) {
|
|
200
|
+
return document;
|
|
201
|
+
}
|
|
202
|
+
const data = recordArrayManager.createArray({
|
|
203
|
+
identifiers: document.data,
|
|
204
|
+
doc: document,
|
|
205
|
+
query: request
|
|
206
|
+
});
|
|
207
|
+
const doc = new Document(store, null);
|
|
208
|
+
doc.data = data;
|
|
209
|
+
doc.meta = document.meta;
|
|
210
|
+
doc.links = document.links;
|
|
211
|
+
return doc;
|
|
212
|
+
}
|
|
213
|
+
let managed = recordArrayManager._keyedArrays.get(identifier.lid);
|
|
214
|
+
if (!managed) {
|
|
215
|
+
managed = recordArrayManager.createArray({
|
|
216
|
+
identifiers: document.data,
|
|
217
|
+
doc: document
|
|
218
|
+
});
|
|
219
|
+
recordArrayManager._keyedArrays.set(identifier.lid, managed);
|
|
220
|
+
const doc = new Document(store, identifier);
|
|
221
|
+
doc.data = managed;
|
|
222
|
+
doc.meta = document.meta;
|
|
223
|
+
doc.links = document.links;
|
|
224
|
+
store._documentCache.set(identifier, doc);
|
|
225
|
+
return options.shouldHydrate ? doc : document;
|
|
226
|
+
} else {
|
|
227
|
+
const doc = store._documentCache.get(identifier);
|
|
228
|
+
if (!isFromCache) {
|
|
229
|
+
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
230
|
+
doc.data = managed;
|
|
231
|
+
doc.meta = document.meta;
|
|
232
|
+
doc.links = document.links;
|
|
233
|
+
}
|
|
234
|
+
return options.shouldHydrate ? doc : document;
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
if (!identifier && !options.shouldHydrate) {
|
|
238
|
+
return document;
|
|
239
|
+
}
|
|
240
|
+
const data = document.data ? store.peekRecord(document.data) : null;
|
|
241
|
+
let doc;
|
|
242
|
+
if (identifier) {
|
|
243
|
+
doc = store._documentCache.get(identifier);
|
|
244
|
+
}
|
|
245
|
+
if (!doc) {
|
|
246
|
+
doc = new Document(store, identifier);
|
|
247
|
+
doc.data = data;
|
|
248
|
+
copyDocumentProperties(doc, document);
|
|
249
|
+
if (identifier) {
|
|
250
|
+
store._documentCache.set(identifier, doc);
|
|
251
|
+
}
|
|
252
|
+
} else if (!isFromCache) {
|
|
253
|
+
doc.data = data;
|
|
254
|
+
copyDocumentProperties(doc, document);
|
|
255
|
+
}
|
|
256
|
+
return options.shouldHydrate ? doc : document;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
260
|
+
const {
|
|
261
|
+
cacheOptions
|
|
262
|
+
} = request;
|
|
263
|
+
return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier) : false);
|
|
264
|
+
}
|
|
265
|
+
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
266
|
+
const {
|
|
267
|
+
cacheOptions
|
|
268
|
+
} = request;
|
|
269
|
+
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier) : false));
|
|
270
|
+
}
|
|
271
|
+
function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
|
|
272
|
+
const {
|
|
273
|
+
store
|
|
274
|
+
} = context.request;
|
|
275
|
+
const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
|
|
276
|
+
return next(context.request).then(document => {
|
|
277
|
+
store.requestManager._pending.delete(context.id);
|
|
278
|
+
store._enableAsyncFlush = true;
|
|
279
|
+
let response;
|
|
280
|
+
store._join(() => {
|
|
281
|
+
response = store.cache.put(document);
|
|
282
|
+
response = maybeUpdateUiObjects(store, context.request, {
|
|
283
|
+
shouldHydrate,
|
|
284
|
+
shouldFetch,
|
|
285
|
+
shouldBackgroundFetch,
|
|
286
|
+
identifier
|
|
287
|
+
}, response, false);
|
|
288
|
+
});
|
|
289
|
+
store._enableAsyncFlush = null;
|
|
290
|
+
if (shouldFetch) {
|
|
291
|
+
return response;
|
|
292
|
+
} else if (shouldBackgroundFetch) {
|
|
293
|
+
store.notifications._flush();
|
|
294
|
+
}
|
|
295
|
+
}, error => {
|
|
296
|
+
store.requestManager._pending.delete(context.id);
|
|
297
|
+
if (context.request.signal?.aborted) {
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
store.requestManager._pending.delete(context.id);
|
|
301
|
+
store._enableAsyncFlush = true;
|
|
302
|
+
let response;
|
|
303
|
+
store._join(() => {
|
|
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
|
+
store._enableAsyncFlush = null;
|
|
313
|
+
if (!shouldBackgroundFetch) {
|
|
314
|
+
const newError = cloneError(error);
|
|
315
|
+
newError.content = response;
|
|
316
|
+
throw newError;
|
|
317
|
+
} else {
|
|
318
|
+
store.notifications._flush();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function cloneError(error) {
|
|
323
|
+
const cloned = new Error(error.message);
|
|
324
|
+
cloned.stack = error.stack;
|
|
325
|
+
cloned.error = error.error;
|
|
326
|
+
return cloned;
|
|
327
|
+
}
|
|
328
|
+
const SkipCache = Symbol.for('ember-data:skip-cache');
|
|
329
|
+
const EnableHydration = Symbol.for('ember-data:enable-hydration');
|
|
330
|
+
const CacheHandler = {
|
|
331
|
+
request(context, next) {
|
|
332
|
+
// if we have no cache or no cache-key skip cache handling
|
|
333
|
+
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
334
|
+
return next(context.request);
|
|
335
|
+
}
|
|
336
|
+
const {
|
|
337
|
+
store
|
|
338
|
+
} = context.request;
|
|
339
|
+
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
340
|
+
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
341
|
+
|
|
342
|
+
// determine if we should skip cache
|
|
343
|
+
if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
|
|
344
|
+
return fetchContentAndHydrate(next, context, identifier, true, false);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// if we have not skipped cache, determine if we should update behind the scenes
|
|
348
|
+
if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
|
|
349
|
+
let promise = fetchContentAndHydrate(next, context, identifier, false, true);
|
|
350
|
+
store.requestManager._pending.set(context.id, promise);
|
|
351
|
+
}
|
|
352
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
353
|
+
if ('error' in peeked) {
|
|
354
|
+
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
355
|
+
shouldHydrate,
|
|
356
|
+
identifier
|
|
357
|
+
}, peeked.content, true) : peeked.content;
|
|
358
|
+
const newError = cloneError(peeked);
|
|
359
|
+
newError.content = content;
|
|
360
|
+
throw newError;
|
|
361
|
+
}
|
|
362
|
+
return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
363
|
+
shouldHydrate,
|
|
364
|
+
identifier
|
|
365
|
+
}, peeked.content, true) : peeked.content);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
function copyDocumentProperties(target, source) {
|
|
369
|
+
if ('links' in source) {
|
|
370
|
+
target.links = source.links;
|
|
371
|
+
}
|
|
372
|
+
if ('meta' in source) {
|
|
373
|
+
target.meta = source.meta;
|
|
374
|
+
}
|
|
375
|
+
if ('errors' in source) {
|
|
376
|
+
target.errors = source.errors;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
38
379
|
|
|
39
380
|
/*
|
|
40
381
|
* Returns the Cache instance associated with a given
|
|
@@ -145,9 +486,13 @@ function installPolyfill() {
|
|
|
145
486
|
@module @ember-data/store
|
|
146
487
|
*/
|
|
147
488
|
const IDENTIFIERS = new Set();
|
|
489
|
+
const DOCUMENTS = new Set();
|
|
148
490
|
function isStableIdentifier(identifier) {
|
|
149
491
|
return IDENTIFIERS.has(identifier);
|
|
150
492
|
}
|
|
493
|
+
function isDocumentIdentifier(identifier) {
|
|
494
|
+
return DOCUMENTS.has(identifier);
|
|
495
|
+
}
|
|
151
496
|
const isFastBoot = typeof FastBoot !== 'undefined';
|
|
152
497
|
const _crypto = isFastBoot ? FastBoot.require('crypto') : window.crypto;
|
|
153
498
|
if (macroCondition(getOwnConfig().polyfillUUID)) {
|
|
@@ -180,20 +525,31 @@ function setIdentifierResetMethod(method) {
|
|
|
180
525
|
configuredResetMethod = method;
|
|
181
526
|
}
|
|
182
527
|
function defaultGenerationMethod(data, bucket) {
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
528
|
+
if (bucket === 'record') {
|
|
529
|
+
if (isNonEmptyString(data.lid)) {
|
|
530
|
+
return data.lid;
|
|
531
|
+
}
|
|
532
|
+
if (data.id !== undefined) {
|
|
533
|
+
let {
|
|
534
|
+
type,
|
|
535
|
+
id
|
|
536
|
+
} = data;
|
|
537
|
+
// TODO: add test for id not a string
|
|
538
|
+
if (isNonEmptyString(coerceId(id))) {
|
|
539
|
+
return `@lid:${normalizeModelName$1(type)}-${id}`;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return uuidv4();
|
|
543
|
+
} else if (bucket === 'document') {
|
|
544
|
+
if (!data.url) {
|
|
545
|
+
return null;
|
|
194
546
|
}
|
|
547
|
+
if (!data.method || data.method.toUpperCase() === 'GET') {
|
|
548
|
+
return data.url;
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
195
551
|
}
|
|
196
|
-
|
|
552
|
+
assert(`Unknown bucket ${bucket}`, false);
|
|
197
553
|
}
|
|
198
554
|
function defaultEmptyCallback(...args) {}
|
|
199
555
|
let DEBUG_MAP;
|
|
@@ -218,7 +574,8 @@ class IdentifierCache {
|
|
|
218
574
|
constructor() {
|
|
219
575
|
this._cache = {
|
|
220
576
|
lids: new Map(),
|
|
221
|
-
types: Object.create(null)
|
|
577
|
+
types: Object.create(null),
|
|
578
|
+
documents: new Map()
|
|
222
579
|
};
|
|
223
580
|
// we cache the user configuredGenerationMethod at init because it must
|
|
224
581
|
// be configured prior and is not allowed to be changed
|
|
@@ -392,6 +749,36 @@ class IdentifierCache {
|
|
|
392
749
|
return this._getRecordIdentifier(resource, false);
|
|
393
750
|
}
|
|
394
751
|
|
|
752
|
+
/**
|
|
753
|
+
Returns the DocumentIdentifier for the given Request, creates one if it does not yet exist.
|
|
754
|
+
Returns `null` if the request does not have a `cacheKey` or `url`.
|
|
755
|
+
@method getOrCreateDocumentIdentifier
|
|
756
|
+
@param request
|
|
757
|
+
@returns {StableDocumentIdentifier | null}
|
|
758
|
+
@public
|
|
759
|
+
*/
|
|
760
|
+
getOrCreateDocumentIdentifier(request) {
|
|
761
|
+
let cacheKey = request.cacheOptions?.key;
|
|
762
|
+
if (!cacheKey) {
|
|
763
|
+
cacheKey = this._generate(request, 'document');
|
|
764
|
+
}
|
|
765
|
+
if (!cacheKey) {
|
|
766
|
+
return null;
|
|
767
|
+
}
|
|
768
|
+
let identifier = this._cache.documents.get(cacheKey);
|
|
769
|
+
if (identifier === undefined) {
|
|
770
|
+
identifier = {
|
|
771
|
+
lid: cacheKey
|
|
772
|
+
};
|
|
773
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
774
|
+
Object.freeze(identifier);
|
|
775
|
+
}
|
|
776
|
+
DOCUMENTS.add(identifier);
|
|
777
|
+
this._cache.documents.set(cacheKey, identifier);
|
|
778
|
+
}
|
|
779
|
+
return identifier;
|
|
780
|
+
}
|
|
781
|
+
|
|
395
782
|
/**
|
|
396
783
|
Returns the Identifier for the given Resource, creates one if it does not yet exist.
|
|
397
784
|
Specifically this means that we:
|
|
@@ -559,6 +946,9 @@ class IdentifierCache {
|
|
|
559
946
|
}
|
|
560
947
|
}
|
|
561
948
|
destroy() {
|
|
949
|
+
this._cache.documents.forEach(identifier => {
|
|
950
|
+
DOCUMENTS.delete(identifier);
|
|
951
|
+
});
|
|
562
952
|
this._reset();
|
|
563
953
|
}
|
|
564
954
|
}
|
|
@@ -694,38 +1084,6 @@ function detectMerge(typesCache, identifier, data, newId, lids) {
|
|
|
694
1084
|
}
|
|
695
1085
|
return false;
|
|
696
1086
|
}
|
|
697
|
-
function _initializerDefineProperty(target, property, descriptor, context) {
|
|
698
|
-
if (!descriptor) return;
|
|
699
|
-
Object.defineProperty(target, property, {
|
|
700
|
-
enumerable: descriptor.enumerable,
|
|
701
|
-
configurable: descriptor.configurable,
|
|
702
|
-
writable: descriptor.writable,
|
|
703
|
-
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
|
707
|
-
var desc = {};
|
|
708
|
-
Object.keys(descriptor).forEach(function (key) {
|
|
709
|
-
desc[key] = descriptor[key];
|
|
710
|
-
});
|
|
711
|
-
desc.enumerable = !!desc.enumerable;
|
|
712
|
-
desc.configurable = !!desc.configurable;
|
|
713
|
-
if ('value' in desc || desc.initializer) {
|
|
714
|
-
desc.writable = true;
|
|
715
|
-
}
|
|
716
|
-
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
|
|
717
|
-
return decorator(target, property, desc) || desc;
|
|
718
|
-
}, desc);
|
|
719
|
-
if (context && desc.initializer !== void 0) {
|
|
720
|
-
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
|
|
721
|
-
desc.initializer = undefined;
|
|
722
|
-
}
|
|
723
|
-
if (desc.initializer === void 0) {
|
|
724
|
-
Object.defineProperty(target, property, desc);
|
|
725
|
-
desc = null;
|
|
726
|
-
}
|
|
727
|
-
return desc;
|
|
728
|
-
}
|
|
729
1087
|
let tokenId = 0;
|
|
730
1088
|
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
731
1089
|
function isCacheOperationValue(value) {
|
|
@@ -753,16 +1111,12 @@ function unsubscribe(token) {
|
|
|
753
1111
|
}
|
|
754
1112
|
}
|
|
755
1113
|
|
|
756
|
-
/*
|
|
757
|
-
Currently only support a single callback per identifier
|
|
758
|
-
*/
|
|
759
|
-
|
|
760
1114
|
/**
|
|
761
1115
|
* The NotificationManager provides the ability to subscribe to
|
|
762
1116
|
* changes to Cache state.
|
|
763
1117
|
*
|
|
764
1118
|
* This Feature is what allows EmberData to create subscriptions that
|
|
765
|
-
* work with any framework or change
|
|
1119
|
+
* work with any framework or change-notification system.
|
|
766
1120
|
*
|
|
767
1121
|
* @class NotificationManager
|
|
768
1122
|
* @public
|
|
@@ -776,24 +1130,35 @@ class NotificationManager {
|
|
|
776
1130
|
}
|
|
777
1131
|
|
|
778
1132
|
/**
|
|
779
|
-
* Subscribe to changes for a given resource identifier
|
|
1133
|
+
* Subscribe to changes for a given resource identifier, resource addition/removal, or document addition/removal.
|
|
780
1134
|
*
|
|
781
1135
|
* ```ts
|
|
782
|
-
*
|
|
1136
|
+
* export type CacheOperation = 'added' | 'removed' | 'updated' | 'state';
|
|
1137
|
+
*
|
|
1138
|
+
* export interface NotificationCallback {
|
|
783
1139
|
* (identifier: StableRecordIdentifier, notificationType: 'attributes' | 'relationships', key?: string): void;
|
|
784
1140
|
* (identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'state'): void;
|
|
785
1141
|
* (identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
|
|
786
1142
|
* }
|
|
1143
|
+
* export interface ResourceOperationCallback {
|
|
1144
|
+
* // resource updates
|
|
1145
|
+
* (identifier: StableRecordIdentifier, notificationType: CacheOperation): void;
|
|
1146
|
+
* }
|
|
1147
|
+
* export interface DocumentOperationCallback {
|
|
1148
|
+
* // document updates
|
|
1149
|
+
* (identifier: StableDocumentIdentifier, notificationType: CacheOperation): void;
|
|
1150
|
+
* }
|
|
787
1151
|
* ```
|
|
788
1152
|
*
|
|
789
1153
|
* @method subscribe
|
|
790
1154
|
* @public
|
|
791
|
-
* @param {StableRecordIdentifier} identifier
|
|
792
|
-
* @param {NotificationCallback} callback
|
|
1155
|
+
* @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
|
|
1156
|
+
* @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
|
|
793
1157
|
* @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
|
|
794
1158
|
*/
|
|
1159
|
+
|
|
795
1160
|
subscribe(identifier, callback) {
|
|
796
|
-
assert(`Expected to receive a stable Identifier to subscribe to`, identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier));
|
|
1161
|
+
assert(`Expected to receive a stable Identifier to subscribe to`, identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
|
|
797
1162
|
let map = Cache.get(identifier);
|
|
798
1163
|
if (!map) {
|
|
799
1164
|
map = new Map();
|
|
@@ -833,7 +1198,7 @@ class NotificationManager {
|
|
|
833
1198
|
|
|
834
1199
|
notify(identifier, value, key) {
|
|
835
1200
|
assert(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key || ''}'.`, !key || value === 'attributes' || value === 'relationships');
|
|
836
|
-
if (!isStableIdentifier(identifier)) {
|
|
1201
|
+
if (!isStableIdentifier(identifier) && !isDocumentIdentifier(identifier)) {
|
|
837
1202
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
838
1203
|
// eslint-disable-next-line no-console
|
|
839
1204
|
console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key || ''}' with, but ${String(identifier)} is not in the cache`, identifier);
|
|
@@ -842,7 +1207,7 @@ class NotificationManager {
|
|
|
842
1207
|
}
|
|
843
1208
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
844
1209
|
// eslint-disable-next-line no-console
|
|
845
|
-
console.log(`Buffering Notify: ${String(identifier)}\t${value}\t${key || ''}`);
|
|
1210
|
+
console.log(`Buffering Notify: ${String(identifier.lid)}\t${value}\t${key || ''}`);
|
|
846
1211
|
}
|
|
847
1212
|
const hasSubscribers = Boolean(Cache.get(identifier)?.size);
|
|
848
1213
|
if (isCacheOperationValue(value) || hasSubscribers) {
|
|
@@ -894,7 +1259,7 @@ class NotificationManager {
|
|
|
894
1259
|
|
|
895
1260
|
// TODO for documents this will need to switch based on Identifier kind
|
|
896
1261
|
if (isCacheOperationValue(value)) {
|
|
897
|
-
let callbackMap = Cache.get('resource');
|
|
1262
|
+
let callbackMap = Cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
|
|
898
1263
|
if (callbackMap) {
|
|
899
1264
|
callbackMap.forEach(cb => {
|
|
900
1265
|
cb(identifier, value);
|
|
@@ -1111,16 +1476,6 @@ let RecordReference = (_class$2 = class RecordReference {
|
|
|
1111
1476
|
return 0;
|
|
1112
1477
|
}
|
|
1113
1478
|
}), _class$2);
|
|
1114
|
-
function _classPrivateFieldBase(receiver, privateKey) {
|
|
1115
|
-
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
1116
|
-
throw new TypeError("attempted to use private field on non-instance");
|
|
1117
|
-
}
|
|
1118
|
-
return receiver;
|
|
1119
|
-
}
|
|
1120
|
-
var id = 0;
|
|
1121
|
-
function _classPrivateFieldKey(name) {
|
|
1122
|
-
return "__private_" + id++ + "_" + name;
|
|
1123
|
-
}
|
|
1124
1479
|
function legacyCachePut(store, doc) {
|
|
1125
1480
|
const jsonApiDoc = doc.content;
|
|
1126
1481
|
let ret;
|
|
@@ -1242,7 +1597,6 @@ class NonSingletonCacheManager {
|
|
|
1242
1597
|
}
|
|
1243
1598
|
// Cache Management
|
|
1244
1599
|
// ================
|
|
1245
|
-
|
|
1246
1600
|
/**
|
|
1247
1601
|
* Cache the response to a request
|
|
1248
1602
|
*
|
|
@@ -2273,7 +2627,7 @@ class LegacyWrapper {
|
|
|
2273
2627
|
});
|
|
2274
2628
|
}
|
|
2275
2629
|
notifyChange(identifier, namespace, key) {
|
|
2276
|
-
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
2630
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
|
|
2277
2631
|
|
|
2278
2632
|
// TODO do we still get value from this?
|
|
2279
2633
|
if (namespace === 'relationships' && key) {
|
|
@@ -2420,7 +2774,7 @@ class LegacyWrapper {
|
|
|
2420
2774
|
assert(`Expected a stable identifier`, isStableIdentifier(type));
|
|
2421
2775
|
identifier = type;
|
|
2422
2776
|
}
|
|
2423
|
-
const cache = macroCondition(getOwnConfig().deprecations.
|
|
2777
|
+
const cache = macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._store._instanceCache.getResourceCache(identifier) : this._store.cache;
|
|
2424
2778
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1CACHE_STORE_APIS)) {
|
|
2425
2779
|
if (!id && !lid && typeof type === 'string') {
|
|
2426
2780
|
cache.clientDidCreate(identifier);
|
|
@@ -2540,7 +2894,7 @@ class V2CacheStoreWrapper {
|
|
|
2540
2894
|
});
|
|
2541
2895
|
}
|
|
2542
2896
|
notifyChange(identifier, namespace, key) {
|
|
2543
|
-
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
2897
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
|
|
2544
2898
|
|
|
2545
2899
|
// TODO do we still get value from this?
|
|
2546
2900
|
if (namespace === 'relationships' && key) {
|
|
@@ -2555,7 +2909,7 @@ class V2CacheStoreWrapper {
|
|
|
2555
2909
|
return this._store.getSchemaDefinitionService();
|
|
2556
2910
|
}
|
|
2557
2911
|
recordDataFor(identifier) {
|
|
2558
|
-
if (macroCondition(getOwnConfig().deprecations.
|
|
2912
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA)) {
|
|
2559
2913
|
deprecate(`StoreWrapper.recordDataFor is deprecated. With Singleton Cache, this method is no longer needed as the caller is its own cache reference.`, false, {
|
|
2560
2914
|
for: '@ember-data/store',
|
|
2561
2915
|
id: 'ember-data:deprecate-record-data-for',
|
|
@@ -2567,7 +2921,7 @@ class V2CacheStoreWrapper {
|
|
|
2567
2921
|
});
|
|
2568
2922
|
}
|
|
2569
2923
|
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
2570
|
-
return macroCondition(getOwnConfig().deprecations.
|
|
2924
|
+
return macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA) ? this._store._instanceCache.getResourceCache(identifier) : void 0;
|
|
2571
2925
|
}
|
|
2572
2926
|
setRecordId(identifier, id) {
|
|
2573
2927
|
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
@@ -2660,7 +3014,7 @@ class InstanceCache {
|
|
|
2660
3014
|
};
|
|
2661
3015
|
this.store = store;
|
|
2662
3016
|
this._storeWrapper = new CacheStoreWrapper(this.store);
|
|
2663
|
-
if (macroCondition(getOwnConfig().deprecations.
|
|
3017
|
+
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA)) {
|
|
2664
3018
|
this.__cacheFor = resource => {
|
|
2665
3019
|
// TODO enforce strict
|
|
2666
3020
|
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
@@ -2701,7 +3055,7 @@ class InstanceCache {
|
|
|
2701
3055
|
record: staleIdentifier,
|
|
2702
3056
|
value: keptIdentifier
|
|
2703
3057
|
});
|
|
2704
|
-
} else if (macroCondition(!getOwnConfig().deprecations.
|
|
3058
|
+
} else if (macroCondition(!getOwnConfig().deprecations.DEPRECATE_V1_RECORD_DATA)) {
|
|
2705
3059
|
this.store.cache.patch({
|
|
2706
3060
|
op: 'mergeIdentifiers',
|
|
2707
3061
|
record: staleIdentifier,
|
|
@@ -3343,7 +3697,6 @@ var _dec, _class$1, _descriptor$1;
|
|
|
3343
3697
|
@extends Ember.ArrayProxy
|
|
3344
3698
|
@uses Ember.PromiseProxyMixin
|
|
3345
3699
|
*/
|
|
3346
|
-
|
|
3347
3700
|
let PromiseArray = (_dec = reads('content.meta'), (_class$1 = class PromiseArray extends PromiseArrayProxy {
|
|
3348
3701
|
constructor(...args) {
|
|
3349
3702
|
super(...args);
|
|
@@ -3483,6 +3836,10 @@ function convertToInt(prop) {
|
|
|
3483
3836
|
return num % 1 === 0 ? num : null;
|
|
3484
3837
|
}
|
|
3485
3838
|
let Tag = (_class = class Tag {
|
|
3839
|
+
/*
|
|
3840
|
+
* whether this was part of a transaction when last mutated
|
|
3841
|
+
*/
|
|
3842
|
+
|
|
3486
3843
|
constructor() {
|
|
3487
3844
|
_initializerDefineProperty(this, "ref", _descriptor, this);
|
|
3488
3845
|
this.shouldReset = false;
|
|
@@ -3557,6 +3914,22 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
3557
3914
|
[NOTIFY]() {
|
|
3558
3915
|
notifyArray(this);
|
|
3559
3916
|
}
|
|
3917
|
+
|
|
3918
|
+
/**
|
|
3919
|
+
The modelClass represented by this record array.
|
|
3920
|
+
@property type
|
|
3921
|
+
@public
|
|
3922
|
+
@deprecated
|
|
3923
|
+
@type {subclass of Model}
|
|
3924
|
+
*/
|
|
3925
|
+
|
|
3926
|
+
/**
|
|
3927
|
+
The store that created this record array.
|
|
3928
|
+
@property store
|
|
3929
|
+
@private
|
|
3930
|
+
@type Store
|
|
3931
|
+
*/
|
|
3932
|
+
|
|
3560
3933
|
destroy() {
|
|
3561
3934
|
this.isDestroying = true;
|
|
3562
3935
|
// changing the reference breaks the Proxy
|
|
@@ -4555,12 +4928,23 @@ function sync(array, changes) {
|
|
|
4555
4928
|
}
|
|
4556
4929
|
}
|
|
4557
4930
|
|
|
4931
|
+
/**
|
|
4932
|
+
* @module @ember-data/store
|
|
4933
|
+
*/
|
|
4558
4934
|
const Touching = Symbol('touching');
|
|
4559
4935
|
const RequestPromise = Symbol('promise');
|
|
4560
4936
|
function hasRecordIdentifier(op) {
|
|
4561
4937
|
return 'recordIdentifier' in op;
|
|
4562
4938
|
}
|
|
4563
|
-
|
|
4939
|
+
|
|
4940
|
+
/**
|
|
4941
|
+
* The RequestStateService is used to track the state of requests
|
|
4942
|
+
* for fetching or updating known resource identifies that are inflight.
|
|
4943
|
+
*
|
|
4944
|
+
* @class RequestStateService
|
|
4945
|
+
* @public
|
|
4946
|
+
*/
|
|
4947
|
+
class RequestStateService {
|
|
4564
4948
|
constructor(store) {
|
|
4565
4949
|
this._pending = Object.create(null);
|
|
4566
4950
|
this._done = new Map();
|
|
@@ -4572,7 +4956,7 @@ class RequestCache {
|
|
|
4572
4956
|
_clearEntries(identifier) {
|
|
4573
4957
|
this._done.delete(identifier);
|
|
4574
4958
|
}
|
|
4575
|
-
|
|
4959
|
+
_enqueue(promise, queryRequest) {
|
|
4576
4960
|
let query = queryRequest.data[0];
|
|
4577
4961
|
if (hasRecordIdentifier(query)) {
|
|
4578
4962
|
let lid = query.recordIdentifier.lid;
|
|
@@ -4671,18 +5055,66 @@ class RequestCache {
|
|
|
4671
5055
|
this._done.set(identifier, requests);
|
|
4672
5056
|
});
|
|
4673
5057
|
}
|
|
5058
|
+
|
|
5059
|
+
/**
|
|
5060
|
+
* Subscribe to requests for a given resource identity.
|
|
5061
|
+
*
|
|
5062
|
+
* The callback will receive the current state of the request.
|
|
5063
|
+
*
|
|
5064
|
+
* ```ts
|
|
5065
|
+
* interface RequestState {
|
|
5066
|
+
* state: 'pending' | 'fulfilled' | 'rejected';
|
|
5067
|
+
* type: 'query' | 'mutation';
|
|
5068
|
+
* request: Request;
|
|
5069
|
+
* response?: { data: unknown };
|
|
5070
|
+
* }
|
|
5071
|
+
* ```
|
|
5072
|
+
*
|
|
5073
|
+
* Note: It should be considered dangerous to use this API for more than simple
|
|
5074
|
+
* state derivation or debugging. The `request` and `response` properties are poorly
|
|
5075
|
+
* spec'd and may change unexpectedly when shifting what Handlers are in use or how
|
|
5076
|
+
* requests are issued from the Store.
|
|
5077
|
+
*
|
|
5078
|
+
* We expect to revisit this API in the near future as we continue to refine the
|
|
5079
|
+
* RequestManager ergonomics, as a simpler but more powerful direct integration
|
|
5080
|
+
* with the RequestManager for these purposes is likely to be a better long-term
|
|
5081
|
+
* design.
|
|
5082
|
+
*
|
|
5083
|
+
* @method subscribeForRecord
|
|
5084
|
+
* @public
|
|
5085
|
+
* @param {StableRecordIdentifier} identifier
|
|
5086
|
+
* @param {(state: RequestState) => void} callback
|
|
5087
|
+
*/
|
|
4674
5088
|
subscribeForRecord(identifier, callback) {
|
|
4675
5089
|
if (!this._subscriptions[identifier.lid]) {
|
|
4676
5090
|
this._subscriptions[identifier.lid] = [];
|
|
4677
5091
|
}
|
|
4678
5092
|
this._subscriptions[identifier.lid].push(callback);
|
|
4679
5093
|
}
|
|
5094
|
+
|
|
5095
|
+
/**
|
|
5096
|
+
* Retrieve all active requests for a given resource identity.
|
|
5097
|
+
*
|
|
5098
|
+
* @method getPendingRequestsForRecord
|
|
5099
|
+
* @public
|
|
5100
|
+
* @param {StableRecordIdentifier} identifier
|
|
5101
|
+
* @returns {RequestState[]} an array of request states for any pending requests for the given identifier
|
|
5102
|
+
*/
|
|
4680
5103
|
getPendingRequestsForRecord(identifier) {
|
|
4681
5104
|
if (this._pending[identifier.lid]) {
|
|
4682
5105
|
return this._pending[identifier.lid];
|
|
4683
5106
|
}
|
|
4684
5107
|
return [];
|
|
4685
5108
|
}
|
|
5109
|
+
|
|
5110
|
+
/**
|
|
5111
|
+
* Retrieve the last completed request for a given resource identity.
|
|
5112
|
+
*
|
|
5113
|
+
* @method getLastRequestForRecord
|
|
5114
|
+
* @public
|
|
5115
|
+
* @param {StableRecordIdentifier} identifier
|
|
5116
|
+
* @returns {RequestState | null} the state of the most recent request for the given identifier
|
|
5117
|
+
*/
|
|
4686
5118
|
getLastRequestForRecord(identifier) {
|
|
4687
5119
|
let requests = this._done.get(identifier);
|
|
4688
5120
|
if (requests) {
|
|
@@ -4699,6 +5131,27 @@ class RequestCache {
|
|
|
4699
5131
|
// hello world
|
|
4700
5132
|
|
|
4701
5133
|
let _Cache;
|
|
5134
|
+
|
|
5135
|
+
/**
|
|
5136
|
+
* A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
|
|
5137
|
+
* and sources of data (such as your API or a local persistence layer)
|
|
5138
|
+
* accessed via a [RequestManager](https://github.com/emberjs/data/tree/main/packages/request).
|
|
5139
|
+
*
|
|
5140
|
+
* ```app/services/store.js
|
|
5141
|
+
* import Store from '@ember-data/store';
|
|
5142
|
+
*
|
|
5143
|
+
* export default class extends Store {}
|
|
5144
|
+
* ```
|
|
5145
|
+
*
|
|
5146
|
+
* Most Ember applications will only have a single `Store` configured as a Service
|
|
5147
|
+
* in this manner. However, setting up multiple stores is possible, including using
|
|
5148
|
+
* each as a unique service.
|
|
5149
|
+
*
|
|
5150
|
+
|
|
5151
|
+
@class Store
|
|
5152
|
+
@public
|
|
5153
|
+
*/
|
|
5154
|
+
|
|
4702
5155
|
class Store {
|
|
4703
5156
|
/**
|
|
4704
5157
|
* Provides access to the NotificationManager associated
|
|
@@ -4711,6 +5164,20 @@ class Store {
|
|
|
4711
5164
|
* @public
|
|
4712
5165
|
*/
|
|
4713
5166
|
|
|
5167
|
+
/**
|
|
5168
|
+
* Provides access to the SchemaService instance
|
|
5169
|
+
* for this Store instance.
|
|
5170
|
+
*
|
|
5171
|
+
* The SchemaService can be used to query for
|
|
5172
|
+
* information about the schema of a resource.
|
|
5173
|
+
*
|
|
5174
|
+
* @property {SchemaService} schema
|
|
5175
|
+
* @public
|
|
5176
|
+
*/
|
|
5177
|
+
get schema() {
|
|
5178
|
+
return this.getSchemaDefinitionService();
|
|
5179
|
+
}
|
|
5180
|
+
|
|
4714
5181
|
/**
|
|
4715
5182
|
* Provides access to the IdentifierCache instance
|
|
4716
5183
|
* for this store.
|
|
@@ -4754,20 +5221,23 @@ class Store {
|
|
|
4754
5221
|
* A Property which an App may set to provide a Lifetimes Service
|
|
4755
5222
|
* to control when a cached request becomes stale.
|
|
4756
5223
|
*
|
|
4757
|
-
* Note, when defined, these methods will only be invoked if
|
|
4758
|
-
*
|
|
5224
|
+
* Note, when defined, these methods will only be invoked if a
|
|
5225
|
+
* cache key exists for the request, either because the request
|
|
5226
|
+
* contains `cacheOptions.key` or because the [IdentifierCache](/ember-data/release/classes/IdentifierCache)
|
|
5227
|
+
* was able to generate a key for the request using the configured
|
|
5228
|
+
* [generation method](/ember-data/release/functions/@ember-data%2Fstore/setIdentifierGenerationMethod).
|
|
4759
5229
|
*
|
|
4760
5230
|
* `isSoftExpired` will only be invoked if `isHardExpired` returns `false`.
|
|
4761
5231
|
*
|
|
4762
5232
|
* ```ts
|
|
4763
5233
|
* store.lifetimes = {
|
|
4764
5234
|
* // make the request and ignore the current cache state
|
|
4765
|
-
* isHardExpired(
|
|
5235
|
+
* isHardExpired(identifier: StableDocumentIdentifier): boolean {
|
|
4766
5236
|
* return false;
|
|
4767
5237
|
* }
|
|
4768
5238
|
*
|
|
4769
5239
|
* // make the request in the background if true, return cache state
|
|
4770
|
-
* isSoftExpired(
|
|
5240
|
+
* isSoftExpired(identifier: StableDocumentIdentifier): boolean {
|
|
4771
5241
|
* return false;
|
|
4772
5242
|
* }
|
|
4773
5243
|
* }
|
|
@@ -4798,11 +5268,12 @@ class Store {
|
|
|
4798
5268
|
});
|
|
4799
5269
|
|
|
4800
5270
|
// private
|
|
4801
|
-
this._requestCache = new
|
|
5271
|
+
this._requestCache = new RequestStateService(this);
|
|
4802
5272
|
this._instanceCache = new InstanceCache(this);
|
|
4803
5273
|
this._adapterCache = Object.create(null);
|
|
4804
5274
|
this._serializerCache = Object.create(null);
|
|
4805
5275
|
this._modelFactoryCache = Object.create(null);
|
|
5276
|
+
this._documentCache = new Map();
|
|
4806
5277
|
}
|
|
4807
5278
|
_run(cb) {
|
|
4808
5279
|
assert(`EmberData should never encounter a nested run`, !this._cbs);
|
|
@@ -4854,6 +5325,7 @@ class Store {
|
|
|
4854
5325
|
lids.forEach(lid => {
|
|
4855
5326
|
all.push(...pending[lid].map(v => v[RequestPromise]));
|
|
4856
5327
|
});
|
|
5328
|
+
this.requestManager._pending.forEach(v => all.push(v));
|
|
4857
5329
|
const promise = Promise.allSettled(all);
|
|
4858
5330
|
promise.length = all.length;
|
|
4859
5331
|
return promise;
|
|
@@ -4888,10 +5360,9 @@ class Store {
|
|
|
4888
5360
|
// we lazily set the cache handler when we issue the first request
|
|
4889
5361
|
// because constructor doesn't allow for this to run after
|
|
4890
5362
|
// the user has had the chance to set the prop.
|
|
4891
|
-
const storeSymbol = Symbol.for('ember-data:enable-hydration');
|
|
4892
5363
|
let opts = {
|
|
4893
5364
|
store: this,
|
|
4894
|
-
[
|
|
5365
|
+
[EnableHydration]: true
|
|
4895
5366
|
};
|
|
4896
5367
|
if (macroCondition(getOwnConfig().env.TESTING)) {
|
|
4897
5368
|
if (this.DISABLE_WAITER) {
|
|
@@ -4994,25 +5465,27 @@ class Store {
|
|
|
4994
5465
|
*/
|
|
4995
5466
|
getSchemaDefinitionService() {
|
|
4996
5467
|
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
4997
|
-
if (!this.
|
|
5468
|
+
if (!this._schema) {
|
|
4998
5469
|
// it is potentially a mistake for the RFC to have not enabled chaining these services, though highlander rule is nice.
|
|
4999
5470
|
// what ember-m3 did via private API to allow both worlds to interop would be much much harder using this.
|
|
5000
|
-
this.
|
|
5471
|
+
this._schema = new DSModelSchemaDefinitionService(this);
|
|
5001
5472
|
}
|
|
5002
5473
|
}
|
|
5003
|
-
assert(`You must registerSchemaDefinitionService with the store to use custom model classes`, this.
|
|
5004
|
-
return this.
|
|
5474
|
+
assert(`You must registerSchemaDefinitionService with the store to use custom model classes`, this._schema);
|
|
5475
|
+
return this._schema;
|
|
5005
5476
|
}
|
|
5006
5477
|
|
|
5007
5478
|
/**
|
|
5008
|
-
*
|
|
5479
|
+
* DEPRECATED - Use `registerSchema` instead.
|
|
5480
|
+
*
|
|
5481
|
+
* Allows an app to register a custom SchemaService
|
|
5009
5482
|
* for use when information about a resource's schema needs
|
|
5010
5483
|
* to be queried.
|
|
5011
5484
|
*
|
|
5012
5485
|
* This method can only be called more than once, but only one schema
|
|
5013
5486
|
* definition service may exist. Therefore if you wish to chain services
|
|
5014
5487
|
* you must lookup the existing service and close over it with the new
|
|
5015
|
-
* service by
|
|
5488
|
+
* service by accessing `store.schema` prior to registration.
|
|
5016
5489
|
*
|
|
5017
5490
|
* For Example:
|
|
5018
5491
|
*
|
|
@@ -5045,18 +5518,73 @@ class Store {
|
|
|
5045
5518
|
* constructor(...args) {
|
|
5046
5519
|
* super(...args);
|
|
5047
5520
|
*
|
|
5048
|
-
* const schema = this.
|
|
5521
|
+
* const schema = this.schema;
|
|
5049
5522
|
* this.registerSchemaDefinitionService(new SchemaDelegator(schema));
|
|
5050
5523
|
* }
|
|
5051
5524
|
* }
|
|
5052
5525
|
* ```
|
|
5053
5526
|
*
|
|
5054
5527
|
* @method registerSchemaDefinitionService
|
|
5055
|
-
* @param {
|
|
5528
|
+
* @param {SchemaService} schema
|
|
5529
|
+
* @deprecated
|
|
5056
5530
|
* @public
|
|
5057
5531
|
*/
|
|
5058
5532
|
registerSchemaDefinitionService(schema) {
|
|
5059
|
-
this.
|
|
5533
|
+
this._schema = schema;
|
|
5534
|
+
}
|
|
5535
|
+
/**
|
|
5536
|
+
* Allows an app to register a custom SchemaService
|
|
5537
|
+
* for use when information about a resource's schema needs
|
|
5538
|
+
* to be queried.
|
|
5539
|
+
*
|
|
5540
|
+
* This method can only be called more than once, but only one schema
|
|
5541
|
+
* definition service may exist. Therefore if you wish to chain services
|
|
5542
|
+
* you must lookup the existing service and close over it with the new
|
|
5543
|
+
* service by accessing `store.schema` prior to registration.
|
|
5544
|
+
*
|
|
5545
|
+
* For Example:
|
|
5546
|
+
*
|
|
5547
|
+
* ```ts
|
|
5548
|
+
* import Store from '@ember-data/store';
|
|
5549
|
+
*
|
|
5550
|
+
* class SchemaDelegator {
|
|
5551
|
+
* constructor(schema) {
|
|
5552
|
+
* this._schema = schema;
|
|
5553
|
+
* }
|
|
5554
|
+
*
|
|
5555
|
+
* doesTypeExist(type: string): boolean {
|
|
5556
|
+
* if (AbstractSchemas.has(type)) {
|
|
5557
|
+
* return true;
|
|
5558
|
+
* }
|
|
5559
|
+
* return this._schema.doesTypeExist(type);
|
|
5560
|
+
* }
|
|
5561
|
+
*
|
|
5562
|
+
* attributesDefinitionFor(identifier: RecordIdentifier | { type: string }): AttributesSchema {
|
|
5563
|
+
* return this._schema.attributesDefinitionFor(identifier);
|
|
5564
|
+
* }
|
|
5565
|
+
*
|
|
5566
|
+
* relationshipsDefinitionFor(identifier: RecordIdentifier | { type: string }): RelationshipsSchema {
|
|
5567
|
+
* const schema = AbstractSchemas.get(identifier.type);
|
|
5568
|
+
* return schema || this._schema.relationshipsDefinitionFor(identifier);
|
|
5569
|
+
* }
|
|
5570
|
+
* }
|
|
5571
|
+
*
|
|
5572
|
+
* export default class extends Store {
|
|
5573
|
+
* constructor(...args) {
|
|
5574
|
+
* super(...args);
|
|
5575
|
+
*
|
|
5576
|
+
* const schema = this.schema;
|
|
5577
|
+
* this.registerSchema(new SchemaDelegator(schema));
|
|
5578
|
+
* }
|
|
5579
|
+
* }
|
|
5580
|
+
* ```
|
|
5581
|
+
*
|
|
5582
|
+
* @method registerSchema
|
|
5583
|
+
* @param {SchemaService} schema
|
|
5584
|
+
* @public
|
|
5585
|
+
*/
|
|
5586
|
+
registerSchema(schema) {
|
|
5587
|
+
this._schema = schema;
|
|
5060
5588
|
}
|
|
5061
5589
|
|
|
5062
5590
|
/**
|
|
@@ -5587,6 +6115,9 @@ class Store {
|
|
|
5587
6115
|
data: {
|
|
5588
6116
|
record: identifier,
|
|
5589
6117
|
options
|
|
6118
|
+
},
|
|
6119
|
+
cacheOptions: {
|
|
6120
|
+
[SkipCache]: true
|
|
5590
6121
|
}
|
|
5591
6122
|
});
|
|
5592
6123
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -5801,6 +6332,9 @@ class Store {
|
|
|
5801
6332
|
type: normalizeModelName$1(modelName),
|
|
5802
6333
|
query,
|
|
5803
6334
|
options: options || {}
|
|
6335
|
+
},
|
|
6336
|
+
cacheOptions: {
|
|
6337
|
+
[SkipCache]: true
|
|
5804
6338
|
}
|
|
5805
6339
|
});
|
|
5806
6340
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -5903,6 +6437,9 @@ class Store {
|
|
|
5903
6437
|
type: normalizeModelName$1(modelName),
|
|
5904
6438
|
query,
|
|
5905
6439
|
options: options || {}
|
|
6440
|
+
},
|
|
6441
|
+
cacheOptions: {
|
|
6442
|
+
[SkipCache]: true
|
|
5906
6443
|
}
|
|
5907
6444
|
});
|
|
5908
6445
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -6073,6 +6610,9 @@ class Store {
|
|
|
6073
6610
|
data: {
|
|
6074
6611
|
type: normalizeModelName$1(modelName),
|
|
6075
6612
|
options: options || {}
|
|
6613
|
+
},
|
|
6614
|
+
cacheOptions: {
|
|
6615
|
+
[SkipCache]: true
|
|
6076
6616
|
}
|
|
6077
6617
|
});
|
|
6078
6618
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -6461,6 +7001,9 @@ class Store {
|
|
|
6461
7001
|
data: {
|
|
6462
7002
|
options,
|
|
6463
7003
|
record: identifier
|
|
7004
|
+
},
|
|
7005
|
+
cacheOptions: {
|
|
7006
|
+
[SkipCache]: true
|
|
6464
7007
|
}
|
|
6465
7008
|
}).then(document => document.content);
|
|
6466
7009
|
}
|
|
@@ -6800,120 +7343,6 @@ function secretInit(record, cache, identifier, store) {
|
|
|
6800
7343
|
StoreMap.set(record, store);
|
|
6801
7344
|
setCacheFor(record, cache);
|
|
6802
7345
|
}
|
|
6803
|
-
function getHydratedContent(store, request, document) {
|
|
6804
|
-
if (Array.isArray(document.data)) {
|
|
6805
|
-
const {
|
|
6806
|
-
lid
|
|
6807
|
-
} = document;
|
|
6808
|
-
const {
|
|
6809
|
-
recordArrayManager
|
|
6810
|
-
} = store;
|
|
6811
|
-
if (!lid) {
|
|
6812
|
-
return recordArrayManager.createArray({
|
|
6813
|
-
identifiers: document.data,
|
|
6814
|
-
doc: document,
|
|
6815
|
-
query: request
|
|
6816
|
-
});
|
|
6817
|
-
}
|
|
6818
|
-
let managed = recordArrayManager._keyedArrays.get(lid);
|
|
6819
|
-
if (!managed) {
|
|
6820
|
-
managed = recordArrayManager.createArray({
|
|
6821
|
-
identifiers: document.data,
|
|
6822
|
-
doc: document
|
|
6823
|
-
});
|
|
6824
|
-
recordArrayManager._keyedArrays.set(lid, managed);
|
|
6825
|
-
} else {
|
|
6826
|
-
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
6827
|
-
}
|
|
6828
|
-
return managed;
|
|
6829
|
-
} else {
|
|
6830
|
-
return Object.assign({}, document, {
|
|
6831
|
-
data: document.data ? store.peekRecord(document.data) : null
|
|
6832
|
-
});
|
|
6833
|
-
}
|
|
6834
|
-
}
|
|
6835
|
-
function calcShouldFetch(store, request, hasCachedValue, lid) {
|
|
6836
|
-
const {
|
|
6837
|
-
cacheOptions,
|
|
6838
|
-
url,
|
|
6839
|
-
method
|
|
6840
|
-
} = request;
|
|
6841
|
-
return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && lid && url && method ? store.lifetimes.isHardExpired(lid, url, method) : false);
|
|
6842
|
-
}
|
|
6843
|
-
function calcShouldBackgroundFetch(store, request, willFetch, lid) {
|
|
6844
|
-
const {
|
|
6845
|
-
cacheOptions,
|
|
6846
|
-
url,
|
|
6847
|
-
method
|
|
6848
|
-
} = request;
|
|
6849
|
-
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && lid && url && method ? store.lifetimes.isSoftExpired(lid, url, method) : false));
|
|
6850
|
-
}
|
|
6851
|
-
function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetch) {
|
|
6852
|
-
const {
|
|
6853
|
-
store
|
|
6854
|
-
} = context.request;
|
|
6855
|
-
const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
|
|
6856
|
-
return next(context.request).then(document => {
|
|
6857
|
-
store._enableAsyncFlush = true;
|
|
6858
|
-
let response;
|
|
6859
|
-
store._join(() => {
|
|
6860
|
-
response = store.cache.put(document);
|
|
6861
|
-
if (shouldFetch && shouldHydrate) {
|
|
6862
|
-
response = getHydratedContent(store, context.request, response);
|
|
6863
|
-
}
|
|
6864
|
-
});
|
|
6865
|
-
store._enableAsyncFlush = null;
|
|
6866
|
-
if (shouldFetch) {
|
|
6867
|
-
return response;
|
|
6868
|
-
}
|
|
6869
|
-
}, error => {
|
|
6870
|
-
store._enableAsyncFlush = true;
|
|
6871
|
-
store._join(() => {
|
|
6872
|
-
store.cache.put(error);
|
|
6873
|
-
});
|
|
6874
|
-
store._enableAsyncFlush = null;
|
|
6875
|
-
|
|
6876
|
-
// TODO @runspired this is probably not the right thing to throw so make sure we add a test
|
|
6877
|
-
if (!shouldBackgroundFetch) {
|
|
6878
|
-
throw error;
|
|
6879
|
-
}
|
|
6880
|
-
});
|
|
6881
|
-
}
|
|
6882
|
-
const CacheHandler = {
|
|
6883
|
-
request(context, next) {
|
|
6884
|
-
// if we have no cache or no cache-key skip cache handling
|
|
6885
|
-
if (!context.request.store || !(context.request.cacheOptions?.key || context.request.url)) {
|
|
6886
|
-
return next(context.request);
|
|
6887
|
-
}
|
|
6888
|
-
const {
|
|
6889
|
-
store
|
|
6890
|
-
} = context.request;
|
|
6891
|
-
const {
|
|
6892
|
-
cacheOptions,
|
|
6893
|
-
url,
|
|
6894
|
-
method
|
|
6895
|
-
} = context.request;
|
|
6896
|
-
const lid = cacheOptions?.key || method === 'GET' && url ? url : null;
|
|
6897
|
-
const peeked = lid ? store.cache.peekRequest({
|
|
6898
|
-
lid
|
|
6899
|
-
}) : null;
|
|
6900
|
-
|
|
6901
|
-
// determine if we should skip cache
|
|
6902
|
-
if (calcShouldFetch(store, context.request, !!peeked, lid)) {
|
|
6903
|
-
return fetchContentAndHydrate(next, context, true, false);
|
|
6904
|
-
}
|
|
6905
|
-
|
|
6906
|
-
// if we have not skipped cache, determine if we should update behind the scenes
|
|
6907
|
-
if (calcShouldBackgroundFetch(store, context.request, false, lid)) {
|
|
6908
|
-
void fetchContentAndHydrate(next, context, false, true);
|
|
6909
|
-
}
|
|
6910
|
-
if ('error' in peeked) {
|
|
6911
|
-
throw peeked.error;
|
|
6912
|
-
}
|
|
6913
|
-
const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
|
|
6914
|
-
return Promise.resolve(shouldHydrate ? getHydratedContent(store, context.request, peeked.content) : peeked.content);
|
|
6915
|
-
}
|
|
6916
|
-
};
|
|
6917
7346
|
function normalizeModelName(modelName) {
|
|
6918
7347
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_HELPERS)) {
|
|
6919
7348
|
deprecate(`the helper function normalizeModelName is deprecated. You should use model names that are already normalized, or use string helpers of your own. This function is primarily an alias for dasherize from @ember/string.`, false, {
|