@ember-data/store 4.12.0-beta.6 → 4.12.0-beta.8
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 +47 -40
- package/addon/-private.js +1 -1
- package/addon/{index-35424a6b.js → index-0d52354f.js} +267 -217
- package/addon/index-0d52354f.js.map +1 -0
- package/addon/index.js +1 -1
- package/addon-main.js +1 -1
- package/ember-data-logo-dark.svg +12 -0
- package/ember-data-logo-light.svg +12 -0
- package/package.json +15 -9
- package/addon/index-35424a6b.js.map +0 -1
|
@@ -35,6 +35,111 @@ import ObjectProxy from '@ember/object/proxy';
|
|
|
35
35
|
function normalizeModelName$1(modelName) {
|
|
36
36
|
return dasherize(modelName);
|
|
37
37
|
}
|
|
38
|
+
function getHydratedContent(store, request, document) {
|
|
39
|
+
if (Array.isArray(document.data)) {
|
|
40
|
+
const {
|
|
41
|
+
lid
|
|
42
|
+
} = document;
|
|
43
|
+
const {
|
|
44
|
+
recordArrayManager
|
|
45
|
+
} = store;
|
|
46
|
+
if (!lid) {
|
|
47
|
+
return recordArrayManager.createArray({
|
|
48
|
+
identifiers: document.data,
|
|
49
|
+
doc: document,
|
|
50
|
+
query: request
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
let managed = recordArrayManager._keyedArrays.get(lid);
|
|
54
|
+
if (!managed) {
|
|
55
|
+
managed = recordArrayManager.createArray({
|
|
56
|
+
identifiers: document.data,
|
|
57
|
+
doc: document
|
|
58
|
+
});
|
|
59
|
+
recordArrayManager._keyedArrays.set(lid, managed);
|
|
60
|
+
} else {
|
|
61
|
+
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
62
|
+
}
|
|
63
|
+
return managed;
|
|
64
|
+
} else {
|
|
65
|
+
return Object.assign({}, document, {
|
|
66
|
+
data: document.data ? store.peekRecord(document.data) : null
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
71
|
+
const {
|
|
72
|
+
cacheOptions
|
|
73
|
+
} = request;
|
|
74
|
+
return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier) : false);
|
|
75
|
+
}
|
|
76
|
+
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
77
|
+
const {
|
|
78
|
+
cacheOptions
|
|
79
|
+
} = request;
|
|
80
|
+
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier) : false));
|
|
81
|
+
}
|
|
82
|
+
function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetch) {
|
|
83
|
+
const {
|
|
84
|
+
store
|
|
85
|
+
} = context.request;
|
|
86
|
+
const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
|
|
87
|
+
return next(context.request).then(document => {
|
|
88
|
+
store._enableAsyncFlush = true;
|
|
89
|
+
let response;
|
|
90
|
+
store._join(() => {
|
|
91
|
+
response = store.cache.put(document);
|
|
92
|
+
if (shouldFetch && shouldHydrate) {
|
|
93
|
+
response = getHydratedContent(store, context.request, response);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
store._enableAsyncFlush = null;
|
|
97
|
+
if (shouldFetch) {
|
|
98
|
+
return response;
|
|
99
|
+
}
|
|
100
|
+
}, error => {
|
|
101
|
+
store._enableAsyncFlush = true;
|
|
102
|
+
store._join(() => {
|
|
103
|
+
store.cache.put(error);
|
|
104
|
+
});
|
|
105
|
+
store._enableAsyncFlush = null;
|
|
106
|
+
|
|
107
|
+
// TODO @runspired this is probably not the right thing to throw so make sure we add a test
|
|
108
|
+
if (!shouldBackgroundFetch) {
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const SkipCache = Symbol.for('ember-data:skip-cache');
|
|
114
|
+
const EnableHydration = Symbol.for('ember-data:enable-hydration');
|
|
115
|
+
const CacheHandler = {
|
|
116
|
+
request(context, next) {
|
|
117
|
+
// if we have no cache or no cache-key skip cache handling
|
|
118
|
+
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
119
|
+
return next(context.request);
|
|
120
|
+
}
|
|
121
|
+
const {
|
|
122
|
+
store
|
|
123
|
+
} = context.request;
|
|
124
|
+
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
125
|
+
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
126
|
+
|
|
127
|
+
// determine if we should skip cache
|
|
128
|
+
if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
|
|
129
|
+
return fetchContentAndHydrate(next, context, true, false);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// if we have not skipped cache, determine if we should update behind the scenes
|
|
133
|
+
if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
|
|
134
|
+
void fetchContentAndHydrate(next, context, false, true);
|
|
135
|
+
}
|
|
136
|
+
if ('error' in peeked) {
|
|
137
|
+
throw peeked.error;
|
|
138
|
+
}
|
|
139
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
140
|
+
return Promise.resolve(shouldHydrate ? getHydratedContent(store, context.request, peeked.content) : peeked.content);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
38
143
|
|
|
39
144
|
/*
|
|
40
145
|
* Returns the Cache instance associated with a given
|
|
@@ -145,9 +250,13 @@ function installPolyfill() {
|
|
|
145
250
|
@module @ember-data/store
|
|
146
251
|
*/
|
|
147
252
|
const IDENTIFIERS = new Set();
|
|
253
|
+
const DOCUMENTS = new Set();
|
|
148
254
|
function isStableIdentifier(identifier) {
|
|
149
255
|
return IDENTIFIERS.has(identifier);
|
|
150
256
|
}
|
|
257
|
+
function isDocumentIdentifier(identifier) {
|
|
258
|
+
return DOCUMENTS.has(identifier);
|
|
259
|
+
}
|
|
151
260
|
const isFastBoot = typeof FastBoot !== 'undefined';
|
|
152
261
|
const _crypto = isFastBoot ? FastBoot.require('crypto') : window.crypto;
|
|
153
262
|
if (macroCondition(getOwnConfig().polyfillUUID)) {
|
|
@@ -180,20 +289,31 @@ function setIdentifierResetMethod(method) {
|
|
|
180
289
|
configuredResetMethod = method;
|
|
181
290
|
}
|
|
182
291
|
function defaultGenerationMethod(data, bucket) {
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (data.id !== undefined) {
|
|
187
|
-
let {
|
|
188
|
-
type,
|
|
189
|
-
id
|
|
190
|
-
} = data;
|
|
191
|
-
// TODO: add test for id not a string
|
|
192
|
-
if (isNonEmptyString(coerceId(id))) {
|
|
193
|
-
return `@lid:${normalizeModelName$1(type)}-${id}`;
|
|
292
|
+
if (bucket === 'record') {
|
|
293
|
+
if (isNonEmptyString(data.lid)) {
|
|
294
|
+
return data.lid;
|
|
194
295
|
}
|
|
296
|
+
if (data.id !== undefined) {
|
|
297
|
+
let {
|
|
298
|
+
type,
|
|
299
|
+
id
|
|
300
|
+
} = data;
|
|
301
|
+
// TODO: add test for id not a string
|
|
302
|
+
if (isNonEmptyString(coerceId(id))) {
|
|
303
|
+
return `@lid:${normalizeModelName$1(type)}-${id}`;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return uuidv4();
|
|
307
|
+
} else if (bucket === 'document') {
|
|
308
|
+
if (!data.url) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
if (!data.method || data.method.toUpperCase() === 'GET') {
|
|
312
|
+
return data.url;
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
195
315
|
}
|
|
196
|
-
|
|
316
|
+
assert(`Unknown bucket ${bucket}`, false);
|
|
197
317
|
}
|
|
198
318
|
function defaultEmptyCallback(...args) {}
|
|
199
319
|
let DEBUG_MAP;
|
|
@@ -218,7 +338,8 @@ class IdentifierCache {
|
|
|
218
338
|
constructor() {
|
|
219
339
|
this._cache = {
|
|
220
340
|
lids: new Map(),
|
|
221
|
-
types: Object.create(null)
|
|
341
|
+
types: Object.create(null),
|
|
342
|
+
documents: new Map()
|
|
222
343
|
};
|
|
223
344
|
// we cache the user configuredGenerationMethod at init because it must
|
|
224
345
|
// be configured prior and is not allowed to be changed
|
|
@@ -392,6 +513,36 @@ class IdentifierCache {
|
|
|
392
513
|
return this._getRecordIdentifier(resource, false);
|
|
393
514
|
}
|
|
394
515
|
|
|
516
|
+
/**
|
|
517
|
+
Returns the DocumentIdentifier for the given Request, creates one if it does not yet exist.
|
|
518
|
+
Returns `null` if the request does not have a `cacheKey` or `url`.
|
|
519
|
+
@method getOrCreateDocumentIdentifier
|
|
520
|
+
@param request
|
|
521
|
+
@returns {StableDocumentIdentifier | null}
|
|
522
|
+
@public
|
|
523
|
+
*/
|
|
524
|
+
getOrCreateDocumentIdentifier(request) {
|
|
525
|
+
let cacheKey = request.cacheOptions?.key;
|
|
526
|
+
if (!cacheKey) {
|
|
527
|
+
cacheKey = this._generate(request, 'document');
|
|
528
|
+
}
|
|
529
|
+
if (!cacheKey) {
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
let identifier = this._cache.documents.get(cacheKey);
|
|
533
|
+
if (identifier === undefined) {
|
|
534
|
+
identifier = {
|
|
535
|
+
lid: cacheKey
|
|
536
|
+
};
|
|
537
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
538
|
+
Object.freeze(identifier);
|
|
539
|
+
}
|
|
540
|
+
DOCUMENTS.add(identifier);
|
|
541
|
+
this._cache.documents.set(cacheKey, identifier);
|
|
542
|
+
}
|
|
543
|
+
return identifier;
|
|
544
|
+
}
|
|
545
|
+
|
|
395
546
|
/**
|
|
396
547
|
Returns the Identifier for the given Resource, creates one if it does not yet exist.
|
|
397
548
|
Specifically this means that we:
|
|
@@ -559,6 +710,9 @@ class IdentifierCache {
|
|
|
559
710
|
}
|
|
560
711
|
}
|
|
561
712
|
destroy() {
|
|
713
|
+
this._cache.documents.forEach(identifier => {
|
|
714
|
+
DOCUMENTS.delete(identifier);
|
|
715
|
+
});
|
|
562
716
|
this._reset();
|
|
563
717
|
}
|
|
564
718
|
}
|
|
@@ -727,9 +881,9 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
|
|
|
727
881
|
return desc;
|
|
728
882
|
}
|
|
729
883
|
let tokenId = 0;
|
|
730
|
-
const CacheOperations
|
|
884
|
+
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
731
885
|
function isCacheOperationValue(value) {
|
|
732
|
-
return CacheOperations
|
|
886
|
+
return CacheOperations.has(value);
|
|
733
887
|
}
|
|
734
888
|
function runLoopIsFlushing() {
|
|
735
889
|
//@ts-expect-error
|
|
@@ -792,8 +946,9 @@ class NotificationManager {
|
|
|
792
946
|
* @param {NotificationCallback} callback
|
|
793
947
|
* @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
|
|
794
948
|
*/
|
|
949
|
+
|
|
795
950
|
subscribe(identifier, callback) {
|
|
796
|
-
assert(`Expected to receive a stable Identifier to subscribe to`, identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier));
|
|
951
|
+
assert(`Expected to receive a stable Identifier to subscribe to`, identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
|
|
797
952
|
let map = Cache.get(identifier);
|
|
798
953
|
if (!map) {
|
|
799
954
|
map = new Map();
|
|
@@ -833,7 +988,7 @@ class NotificationManager {
|
|
|
833
988
|
|
|
834
989
|
notify(identifier, value, key) {
|
|
835
990
|
assert(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key || ''}'.`, !key || value === 'attributes' || value === 'relationships');
|
|
836
|
-
if (!isStableIdentifier(identifier)) {
|
|
991
|
+
if (!isStableIdentifier(identifier) && !isDocumentIdentifier(identifier)) {
|
|
837
992
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
838
993
|
// eslint-disable-next-line no-console
|
|
839
994
|
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 +997,7 @@ class NotificationManager {
|
|
|
842
997
|
}
|
|
843
998
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
844
999
|
// eslint-disable-next-line no-console
|
|
845
|
-
console.log(`Buffering Notify: ${String(identifier)}\t${value}\t${key || ''}`);
|
|
1000
|
+
console.log(`Buffering Notify: ${String(identifier.lid)}\t${value}\t${key || ''}`);
|
|
846
1001
|
}
|
|
847
1002
|
const hasSubscribers = Boolean(Cache.get(identifier)?.size);
|
|
848
1003
|
if (isCacheOperationValue(value) || hasSubscribers) {
|
|
@@ -894,7 +1049,7 @@ class NotificationManager {
|
|
|
894
1049
|
|
|
895
1050
|
// TODO for documents this will need to switch based on Identifier kind
|
|
896
1051
|
if (isCacheOperationValue(value)) {
|
|
897
|
-
let callbackMap = Cache.get('resource');
|
|
1052
|
+
let callbackMap = Cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
|
|
898
1053
|
if (callbackMap) {
|
|
899
1054
|
callbackMap.forEach(cb => {
|
|
900
1055
|
cb(identifier, value);
|
|
@@ -2273,7 +2428,7 @@ class LegacyWrapper {
|
|
|
2273
2428
|
});
|
|
2274
2429
|
}
|
|
2275
2430
|
notifyChange(identifier, namespace, key) {
|
|
2276
|
-
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
2431
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
|
|
2277
2432
|
|
|
2278
2433
|
// TODO do we still get value from this?
|
|
2279
2434
|
if (namespace === 'relationships' && key) {
|
|
@@ -2540,7 +2695,7 @@ class V2CacheStoreWrapper {
|
|
|
2540
2695
|
});
|
|
2541
2696
|
}
|
|
2542
2697
|
notifyChange(identifier, namespace, key) {
|
|
2543
|
-
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
2698
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier) || isDocumentIdentifier(identifier));
|
|
2544
2699
|
|
|
2545
2700
|
// TODO do we still get value from this?
|
|
2546
2701
|
if (namespace === 'relationships' && key) {
|
|
@@ -4712,75 +4867,18 @@ class Store {
|
|
|
4712
4867
|
*/
|
|
4713
4868
|
|
|
4714
4869
|
/**
|
|
4715
|
-
* Provides access to the
|
|
4716
|
-
* for this
|
|
4717
|
-
*
|
|
4718
|
-
* The IdentifierCache can be used to generate or
|
|
4719
|
-
* retrieve a stable unique identifier for any resource.
|
|
4720
|
-
*
|
|
4721
|
-
* @property {IdentifierCache} identifierCache
|
|
4722
|
-
* @public
|
|
4723
|
-
*/
|
|
4724
|
-
|
|
4725
|
-
/**
|
|
4726
|
-
* Provides access to the requestManager instance associated
|
|
4727
|
-
* with this Store instance.
|
|
4728
|
-
*
|
|
4729
|
-
* When using `ember-data` this property is automatically
|
|
4730
|
-
* set to an instance of `RequestManager`. When not using `ember-data`
|
|
4731
|
-
* you must configure this property yourself, either by declaring
|
|
4732
|
-
* it as a service or by initializing it.
|
|
4733
|
-
*
|
|
4734
|
-
* ```ts
|
|
4735
|
-
* import Store, { CacheHandler } from '@ember-data/store';
|
|
4736
|
-
* import RequestManager from '@ember-data/request';
|
|
4737
|
-
* import Fetch from '@ember/data/request/fetch';
|
|
4738
|
-
*
|
|
4739
|
-
* class extends Store {
|
|
4740
|
-
* constructor() {
|
|
4741
|
-
* super(...arguments);
|
|
4742
|
-
* this.requestManager = new RequestManager();
|
|
4743
|
-
* this.requestManager.use([Fetch]);
|
|
4744
|
-
* this.requestManager.useCache(CacheHandler);
|
|
4745
|
-
* }
|
|
4746
|
-
* }
|
|
4747
|
-
* ```
|
|
4748
|
-
*
|
|
4749
|
-
* @public
|
|
4750
|
-
* @property {RequestManager} requestManager
|
|
4751
|
-
*/
|
|
4752
|
-
|
|
4753
|
-
/**
|
|
4754
|
-
* A Property which an App may set to provide a Lifetimes Service
|
|
4755
|
-
* to control when a cached request becomes stale.
|
|
4756
|
-
*
|
|
4757
|
-
* Note, when defined, these methods will only be invoked if `key` `url` and `method`
|
|
4758
|
-
* are all present.
|
|
4759
|
-
*
|
|
4760
|
-
* `isSoftExpired` will only be invoked if `isHardExpired` returns `false`.
|
|
4761
|
-
*
|
|
4762
|
-
* ```ts
|
|
4763
|
-
* store.lifetimes = {
|
|
4764
|
-
* // make the request and ignore the current cache state
|
|
4765
|
-
* isHardExpired(key: string, url: string, method?: HTTPMethod): boolean {
|
|
4766
|
-
* return false;
|
|
4767
|
-
* }
|
|
4870
|
+
* Provides access to the SchemaService instance
|
|
4871
|
+
* for this Store instance.
|
|
4768
4872
|
*
|
|
4769
|
-
*
|
|
4770
|
-
*
|
|
4771
|
-
* return false;
|
|
4772
|
-
* }
|
|
4773
|
-
* }
|
|
4774
|
-
* ```
|
|
4873
|
+
* The SchemaService can be used to query for
|
|
4874
|
+
* information about the schema of a resource.
|
|
4775
4875
|
*
|
|
4876
|
+
* @property {SchemaService} schema
|
|
4776
4877
|
* @public
|
|
4777
|
-
* @property {LivetimesService|undefined} lifetimes
|
|
4778
4878
|
*/
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
// DEBUG-only properties
|
|
4783
|
-
|
|
4879
|
+
get schema() {
|
|
4880
|
+
return this.getSchemaDefinitionService();
|
|
4881
|
+
}
|
|
4784
4882
|
/**
|
|
4785
4883
|
@method init
|
|
4786
4884
|
@private
|
|
@@ -4889,7 +4987,8 @@ class Store {
|
|
|
4889
4987
|
// because constructor doesn't allow for this to run after
|
|
4890
4988
|
// the user has had the chance to set the prop.
|
|
4891
4989
|
let opts = {
|
|
4892
|
-
store: this
|
|
4990
|
+
store: this,
|
|
4991
|
+
[EnableHydration]: true
|
|
4893
4992
|
};
|
|
4894
4993
|
if (macroCondition(getOwnConfig().env.TESTING)) {
|
|
4895
4994
|
if (this.DISABLE_WAITER) {
|
|
@@ -4992,25 +5091,27 @@ class Store {
|
|
|
4992
5091
|
*/
|
|
4993
5092
|
getSchemaDefinitionService() {
|
|
4994
5093
|
if (macroCondition(getOwnConfig().packages.HAS_MODEL_PACKAGE)) {
|
|
4995
|
-
if (!this.
|
|
5094
|
+
if (!this._schema) {
|
|
4996
5095
|
// it is potentially a mistake for the RFC to have not enabled chaining these services, though highlander rule is nice.
|
|
4997
5096
|
// what ember-m3 did via private API to allow both worlds to interop would be much much harder using this.
|
|
4998
|
-
this.
|
|
5097
|
+
this._schema = new DSModelSchemaDefinitionService(this);
|
|
4999
5098
|
}
|
|
5000
5099
|
}
|
|
5001
|
-
assert(`You must registerSchemaDefinitionService with the store to use custom model classes`, this.
|
|
5002
|
-
return this.
|
|
5100
|
+
assert(`You must registerSchemaDefinitionService with the store to use custom model classes`, this._schema);
|
|
5101
|
+
return this._schema;
|
|
5003
5102
|
}
|
|
5004
5103
|
|
|
5005
5104
|
/**
|
|
5006
|
-
*
|
|
5105
|
+
* DEPRECATED - Use `registerSchema` instead.
|
|
5106
|
+
*
|
|
5107
|
+
* Allows an app to register a custom SchemaService
|
|
5007
5108
|
* for use when information about a resource's schema needs
|
|
5008
5109
|
* to be queried.
|
|
5009
5110
|
*
|
|
5010
5111
|
* This method can only be called more than once, but only one schema
|
|
5011
5112
|
* definition service may exist. Therefore if you wish to chain services
|
|
5012
5113
|
* you must lookup the existing service and close over it with the new
|
|
5013
|
-
* service by
|
|
5114
|
+
* service by accessing `store.schema` prior to registration.
|
|
5014
5115
|
*
|
|
5015
5116
|
* For Example:
|
|
5016
5117
|
*
|
|
@@ -5043,18 +5144,73 @@ class Store {
|
|
|
5043
5144
|
* constructor(...args) {
|
|
5044
5145
|
* super(...args);
|
|
5045
5146
|
*
|
|
5046
|
-
* const schema = this.
|
|
5147
|
+
* const schema = this.schema;
|
|
5047
5148
|
* this.registerSchemaDefinitionService(new SchemaDelegator(schema));
|
|
5048
5149
|
* }
|
|
5049
5150
|
* }
|
|
5050
5151
|
* ```
|
|
5051
5152
|
*
|
|
5052
5153
|
* @method registerSchemaDefinitionService
|
|
5053
|
-
* @param {
|
|
5154
|
+
* @param {SchemaService} schema
|
|
5155
|
+
* @deprecated
|
|
5054
5156
|
* @public
|
|
5055
5157
|
*/
|
|
5056
5158
|
registerSchemaDefinitionService(schema) {
|
|
5057
|
-
this.
|
|
5159
|
+
this._schema = schema;
|
|
5160
|
+
}
|
|
5161
|
+
/**
|
|
5162
|
+
* Allows an app to register a custom SchemaService
|
|
5163
|
+
* for use when information about a resource's schema needs
|
|
5164
|
+
* to be queried.
|
|
5165
|
+
*
|
|
5166
|
+
* This method can only be called more than once, but only one schema
|
|
5167
|
+
* definition service may exist. Therefore if you wish to chain services
|
|
5168
|
+
* you must lookup the existing service and close over it with the new
|
|
5169
|
+
* service by accessing `store.schema` prior to registration.
|
|
5170
|
+
*
|
|
5171
|
+
* For Example:
|
|
5172
|
+
*
|
|
5173
|
+
* ```ts
|
|
5174
|
+
* import Store from '@ember-data/store';
|
|
5175
|
+
*
|
|
5176
|
+
* class SchemaDelegator {
|
|
5177
|
+
* constructor(schema) {
|
|
5178
|
+
* this._schema = schema;
|
|
5179
|
+
* }
|
|
5180
|
+
*
|
|
5181
|
+
* doesTypeExist(type: string): boolean {
|
|
5182
|
+
* if (AbstractSchemas.has(type)) {
|
|
5183
|
+
* return true;
|
|
5184
|
+
* }
|
|
5185
|
+
* return this._schema.doesTypeExist(type);
|
|
5186
|
+
* }
|
|
5187
|
+
*
|
|
5188
|
+
* attributesDefinitionFor(identifier: RecordIdentifier | { type: string }): AttributesSchema {
|
|
5189
|
+
* return this._schema.attributesDefinitionFor(identifier);
|
|
5190
|
+
* }
|
|
5191
|
+
*
|
|
5192
|
+
* relationshipsDefinitionFor(identifier: RecordIdentifier | { type: string }): RelationshipsSchema {
|
|
5193
|
+
* const schema = AbstractSchemas.get(identifier.type);
|
|
5194
|
+
* return schema || this._schema.relationshipsDefinitionFor(identifier);
|
|
5195
|
+
* }
|
|
5196
|
+
* }
|
|
5197
|
+
*
|
|
5198
|
+
* export default class extends Store {
|
|
5199
|
+
* constructor(...args) {
|
|
5200
|
+
* super(...args);
|
|
5201
|
+
*
|
|
5202
|
+
* const schema = this.schema;
|
|
5203
|
+
* this.registerSchema(new SchemaDelegator(schema));
|
|
5204
|
+
* }
|
|
5205
|
+
* }
|
|
5206
|
+
* ```
|
|
5207
|
+
*
|
|
5208
|
+
* @method registerSchema
|
|
5209
|
+
* @param {SchemaService} schema
|
|
5210
|
+
* @public
|
|
5211
|
+
*/
|
|
5212
|
+
registerSchema(schema) {
|
|
5213
|
+
this._schema = schema;
|
|
5058
5214
|
}
|
|
5059
5215
|
|
|
5060
5216
|
/**
|
|
@@ -5585,6 +5741,9 @@ class Store {
|
|
|
5585
5741
|
data: {
|
|
5586
5742
|
record: identifier,
|
|
5587
5743
|
options
|
|
5744
|
+
},
|
|
5745
|
+
cacheOptions: {
|
|
5746
|
+
[SkipCache]: true
|
|
5588
5747
|
}
|
|
5589
5748
|
});
|
|
5590
5749
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -5799,6 +5958,9 @@ class Store {
|
|
|
5799
5958
|
type: normalizeModelName$1(modelName),
|
|
5800
5959
|
query,
|
|
5801
5960
|
options: options || {}
|
|
5961
|
+
},
|
|
5962
|
+
cacheOptions: {
|
|
5963
|
+
[SkipCache]: true
|
|
5802
5964
|
}
|
|
5803
5965
|
});
|
|
5804
5966
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -5901,6 +6063,9 @@ class Store {
|
|
|
5901
6063
|
type: normalizeModelName$1(modelName),
|
|
5902
6064
|
query,
|
|
5903
6065
|
options: options || {}
|
|
6066
|
+
},
|
|
6067
|
+
cacheOptions: {
|
|
6068
|
+
[SkipCache]: true
|
|
5904
6069
|
}
|
|
5905
6070
|
});
|
|
5906
6071
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -6071,6 +6236,9 @@ class Store {
|
|
|
6071
6236
|
data: {
|
|
6072
6237
|
type: normalizeModelName$1(modelName),
|
|
6073
6238
|
options: options || {}
|
|
6239
|
+
},
|
|
6240
|
+
cacheOptions: {
|
|
6241
|
+
[SkipCache]: true
|
|
6074
6242
|
}
|
|
6075
6243
|
});
|
|
6076
6244
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_PROMISE_PROXIES)) {
|
|
@@ -6459,6 +6627,9 @@ class Store {
|
|
|
6459
6627
|
data: {
|
|
6460
6628
|
options,
|
|
6461
6629
|
record: identifier
|
|
6630
|
+
},
|
|
6631
|
+
cacheOptions: {
|
|
6632
|
+
[SkipCache]: true
|
|
6462
6633
|
}
|
|
6463
6634
|
}).then(document => document.content);
|
|
6464
6635
|
}
|
|
@@ -6798,127 +6969,6 @@ function secretInit(record, cache, identifier, store) {
|
|
|
6798
6969
|
StoreMap.set(record, store);
|
|
6799
6970
|
setCacheFor(record, cache);
|
|
6800
6971
|
}
|
|
6801
|
-
const CacheOperations = new Set(['findRecord', 'findAll', 'query', 'queryRecord', 'findBelongsTo', 'findHasMany', 'updateRecord', 'createRecord', 'deleteRecord']);
|
|
6802
|
-
function getHydratedContent(store, request, document) {
|
|
6803
|
-
if (!request.op || !CacheOperations.has(request.op)) {
|
|
6804
|
-
return document;
|
|
6805
|
-
}
|
|
6806
|
-
if (Array.isArray(document.data)) {
|
|
6807
|
-
const {
|
|
6808
|
-
lid
|
|
6809
|
-
} = document;
|
|
6810
|
-
const {
|
|
6811
|
-
recordArrayManager
|
|
6812
|
-
} = store;
|
|
6813
|
-
if (!lid) {
|
|
6814
|
-
return recordArrayManager.createArray({
|
|
6815
|
-
identifiers: document.data,
|
|
6816
|
-
doc: document,
|
|
6817
|
-
query: request
|
|
6818
|
-
});
|
|
6819
|
-
}
|
|
6820
|
-
let managed = recordArrayManager._keyedArrays.get(lid);
|
|
6821
|
-
if (!managed) {
|
|
6822
|
-
managed = recordArrayManager.createArray({
|
|
6823
|
-
identifiers: document.data,
|
|
6824
|
-
doc: document
|
|
6825
|
-
});
|
|
6826
|
-
recordArrayManager._keyedArrays.set(lid, managed);
|
|
6827
|
-
} else {
|
|
6828
|
-
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
6829
|
-
}
|
|
6830
|
-
return managed;
|
|
6831
|
-
} else {
|
|
6832
|
-
switch (request.op) {
|
|
6833
|
-
case 'findBelongsTo':
|
|
6834
|
-
case 'queryRecord':
|
|
6835
|
-
case 'findRecord':
|
|
6836
|
-
return document.data ? store.peekRecord(document.data) : null;
|
|
6837
|
-
default:
|
|
6838
|
-
return document.data;
|
|
6839
|
-
}
|
|
6840
|
-
}
|
|
6841
|
-
}
|
|
6842
|
-
function calcShouldFetch(store, request, hasCachedValue, lid) {
|
|
6843
|
-
const {
|
|
6844
|
-
cacheOptions,
|
|
6845
|
-
url,
|
|
6846
|
-
method
|
|
6847
|
-
} = request;
|
|
6848
|
-
return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && lid && url && method ? store.lifetimes.isHardExpired(lid, url, method) : false);
|
|
6849
|
-
}
|
|
6850
|
-
function calcShouldBackgroundFetch(store, request, willFetch, lid) {
|
|
6851
|
-
const {
|
|
6852
|
-
cacheOptions,
|
|
6853
|
-
url,
|
|
6854
|
-
method
|
|
6855
|
-
} = request;
|
|
6856
|
-
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && lid && url && method ? store.lifetimes.isSoftExpired(lid, url, method) : false));
|
|
6857
|
-
}
|
|
6858
|
-
function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetch) {
|
|
6859
|
-
const {
|
|
6860
|
-
store
|
|
6861
|
-
} = context.request;
|
|
6862
|
-
return next(context.request).then(document => {
|
|
6863
|
-
store._enableAsyncFlush = true;
|
|
6864
|
-
let response;
|
|
6865
|
-
store._join(() => {
|
|
6866
|
-
response = store.cache.put(document);
|
|
6867
|
-
if (shouldFetch) {
|
|
6868
|
-
response = getHydratedContent(store, context.request, response);
|
|
6869
|
-
}
|
|
6870
|
-
});
|
|
6871
|
-
store._enableAsyncFlush = null;
|
|
6872
|
-
if (shouldFetch) {
|
|
6873
|
-
return response;
|
|
6874
|
-
}
|
|
6875
|
-
}, error => {
|
|
6876
|
-
store._enableAsyncFlush = true;
|
|
6877
|
-
store._join(() => {
|
|
6878
|
-
store.cache.put(error);
|
|
6879
|
-
});
|
|
6880
|
-
store._enableAsyncFlush = null;
|
|
6881
|
-
|
|
6882
|
-
// TODO @runspired this is probably not the right thing to throw so make sure we add a test
|
|
6883
|
-
if (!shouldBackgroundFetch) {
|
|
6884
|
-
throw error;
|
|
6885
|
-
}
|
|
6886
|
-
});
|
|
6887
|
-
}
|
|
6888
|
-
const CacheHandler = {
|
|
6889
|
-
request(context, next) {
|
|
6890
|
-
// if we are a legacy request or did not originate from the store, skip cache handling
|
|
6891
|
-
if (!context.request.store || context.request.op && CacheOperations.has(context.request.op) && !context.request.url) {
|
|
6892
|
-
return next(context.request);
|
|
6893
|
-
}
|
|
6894
|
-
const {
|
|
6895
|
-
store
|
|
6896
|
-
} = context.request;
|
|
6897
|
-
const {
|
|
6898
|
-
cacheOptions,
|
|
6899
|
-
url,
|
|
6900
|
-
method
|
|
6901
|
-
} = context.request;
|
|
6902
|
-
const lid = cacheOptions?.key || method === 'GET' && url ? url : null;
|
|
6903
|
-
const peeked = lid ? store.cache.peekRequest({
|
|
6904
|
-
lid
|
|
6905
|
-
}) : null;
|
|
6906
|
-
|
|
6907
|
-
// determine if we should skip cache
|
|
6908
|
-
if (calcShouldFetch(store, context.request, !!peeked, lid)) {
|
|
6909
|
-
return fetchContentAndHydrate(next, context, true, false);
|
|
6910
|
-
}
|
|
6911
|
-
|
|
6912
|
-
// if we have not skipped cache, determine if we should update behind the scenes
|
|
6913
|
-
if (calcShouldBackgroundFetch(store, context.request, false, lid)) {
|
|
6914
|
-
void fetchContentAndHydrate(next, context, false, true);
|
|
6915
|
-
}
|
|
6916
|
-
if ('error' in peeked) {
|
|
6917
|
-
throw peeked.error;
|
|
6918
|
-
}
|
|
6919
|
-
return Promise.resolve(getHydratedContent(store, context.request, peeked.content));
|
|
6920
|
-
}
|
|
6921
|
-
};
|
|
6922
6972
|
function normalizeModelName(modelName) {
|
|
6923
6973
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_HELPERS)) {
|
|
6924
6974
|
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, {
|