@ember-data/store 5.4.0-alpha.5 → 5.4.0-alpha.50
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-aa7f91c0.js → cache-handler-BGVZPfAA.js} +1247 -1042
- package/addon/cache-handler-BGVZPfAA.js.map +1 -0
- package/addon/index.js +1 -1
- package/addon/index.js.map +1 -1
- package/addon-main.js +1 -0
- package/package.json +64 -43
- package/unstable-preview-types/-private/cache-handler.d.ts +141 -0
- package/unstable-preview-types/-private/cache-handler.d.ts.map +1 -0
- package/unstable-preview-types/-private/caches/cache-utils.d.ts +11 -0
- package/unstable-preview-types/-private/caches/cache-utils.d.ts.map +1 -0
- package/unstable-preview-types/-private/caches/identifier-cache.d.ts +182 -0
- package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -0
- package/unstable-preview-types/-private/caches/instance-cache.d.ts +63 -0
- package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -0
- package/unstable-preview-types/-private/caches/resource-utils.d.ts +12 -0
- package/unstable-preview-types/-private/caches/resource-utils.d.ts.map +1 -0
- package/unstable-preview-types/-private/document.d.ts +146 -0
- package/unstable-preview-types/-private/document.d.ts.map +1 -0
- package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts +179 -0
- package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts.map +1 -0
- package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts +19 -0
- package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts.map +1 -0
- package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts +29 -0
- package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts.map +1 -0
- package/unstable-preview-types/-private/managers/cache-manager.d.ts +442 -0
- package/unstable-preview-types/-private/managers/cache-manager.d.ts.map +1 -0
- package/unstable-preview-types/-private/managers/notification-manager.d.ts +98 -0
- package/unstable-preview-types/-private/managers/notification-manager.d.ts.map +1 -0
- package/unstable-preview-types/-private/managers/record-array-manager.d.ts +97 -0
- package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -0
- package/unstable-preview-types/-private/network/request-cache.d.ts +109 -0
- package/unstable-preview-types/-private/network/request-cache.d.ts.map +1 -0
- package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +133 -0
- package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -0
- package/unstable-preview-types/-private/record-arrays/native-proxy-type-fix.d.ts +118 -0
- package/unstable-preview-types/-private/record-arrays/native-proxy-type-fix.d.ts.map +1 -0
- package/unstable-preview-types/-private/store-service.d.ts +1554 -0
- package/unstable-preview-types/-private/store-service.d.ts.map +1 -0
- package/unstable-preview-types/-private/utils/coerce-id.d.ts +10 -0
- package/unstable-preview-types/-private/utils/coerce-id.d.ts.map +1 -0
- package/unstable-preview-types/-private/utils/construct-resource.d.ts +10 -0
- package/unstable-preview-types/-private/utils/construct-resource.d.ts.map +1 -0
- package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts +7 -0
- package/unstable-preview-types/-private/utils/identifier-debug-consts.d.ts.map +1 -0
- package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts +4 -0
- package/unstable-preview-types/-private/utils/is-non-empty-string.d.ts.map +1 -0
- package/unstable-preview-types/-private/utils/normalize-model-name.d.ts +4 -0
- package/unstable-preview-types/-private/utils/normalize-model-name.d.ts.map +1 -0
- package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts +4 -0
- package/unstable-preview-types/-private/utils/uuid-polyfill.d.ts.map +1 -0
- package/unstable-preview-types/-private.d.ts +20 -0
- package/unstable-preview-types/-private.d.ts.map +1 -0
- package/unstable-preview-types/-types/overview.d.ts +21 -0
- package/unstable-preview-types/-types/overview.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts +107 -0
- package/unstable-preview-types/-types/q/cache-store-wrapper.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/cache.d.ts +47 -0
- package/unstable-preview-types/-types/q/cache.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/ds-model.d.ts +15 -0
- package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/identifier.d.ts +171 -0
- package/unstable-preview-types/-types/q/identifier.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/promise-proxies.d.ts +4 -0
- package/unstable-preview-types/-types/q/promise-proxies.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/record-data-json-api.d.ts +36 -0
- package/unstable-preview-types/-types/q/record-data-json-api.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/record-instance.d.ts +29 -0
- package/unstable-preview-types/-types/q/record-instance.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/schema-service.d.ts +214 -0
- package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -0
- package/unstable-preview-types/-types/q/store.d.ts +17 -0
- package/unstable-preview-types/-types/q/store.d.ts.map +1 -0
- package/unstable-preview-types/index.d.ts +220 -0
- package/unstable-preview-types/index.d.ts.map +1 -0
- package/addon/store-service-aa7f91c0.js.map +0 -1
|
@@ -1,365 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getOwner } from '@ember/application';
|
|
3
|
-
import { assert, deprecate, warn } from '@ember/debug';
|
|
1
|
+
import { deprecate, assert, warn } from '@ember/debug';
|
|
4
2
|
import EmberObject from '@ember/object';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { SkipCache, EnableHydration } from '@warp-drive/core-types/request';
|
|
4
|
+
import { macroCondition, getOwnConfig } from '@embroider/macros';
|
|
5
|
+
import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '@warp-drive/core-types/identifier';
|
|
7
6
|
import { dasherize } from '@ember/string';
|
|
8
|
-
import { addToTransaction, subscribe, addTransactionCB } from '@ember-data/tracking/-private';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { dirtyTag } from '@glimmer/validator';
|
|
12
|
-
import Ember from 'ember';
|
|
13
|
-
function _initializerDefineProperty(target, property, descriptor, context) {
|
|
14
|
-
if (!descriptor) return;
|
|
15
|
-
Object.defineProperty(target, property, {
|
|
16
|
-
enumerable: descriptor.enumerable,
|
|
17
|
-
configurable: descriptor.configurable,
|
|
18
|
-
writable: descriptor.writable,
|
|
19
|
-
value: descriptor.initializer ? descriptor.initializer.call(context) : void 0
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function _classPrivateFieldBase(receiver, privateKey) {
|
|
23
|
-
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
24
|
-
throw new TypeError("attempted to use private field on non-instance");
|
|
25
|
-
}
|
|
26
|
-
return receiver;
|
|
27
|
-
}
|
|
28
|
-
var id = 0;
|
|
29
|
-
function _classPrivateFieldKey(name) {
|
|
30
|
-
return "__private_" + id++ + "_" + name;
|
|
31
|
-
}
|
|
32
|
-
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
|
33
|
-
var desc = {};
|
|
34
|
-
Object.keys(descriptor).forEach(function (key) {
|
|
35
|
-
desc[key] = descriptor[key];
|
|
36
|
-
});
|
|
37
|
-
desc.enumerable = !!desc.enumerable;
|
|
38
|
-
desc.configurable = !!desc.configurable;
|
|
39
|
-
if ('value' in desc || desc.initializer) {
|
|
40
|
-
desc.writable = true;
|
|
41
|
-
}
|
|
42
|
-
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
|
|
43
|
-
return decorator(target, property, desc) || desc;
|
|
44
|
-
}, desc);
|
|
45
|
-
if (context && desc.initializer !== void 0) {
|
|
46
|
-
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
|
|
47
|
-
desc.initializer = undefined;
|
|
48
|
-
}
|
|
49
|
-
if (desc.initializer === void 0) {
|
|
50
|
-
Object.defineProperty(target, property, desc);
|
|
51
|
-
desc = null;
|
|
52
|
-
}
|
|
53
|
-
return desc;
|
|
54
|
-
}
|
|
55
|
-
var _class$2, _descriptor$2, _descriptor2$1, _descriptor3, _descriptor4, _store, _request;
|
|
56
|
-
function urlFromLink(link) {
|
|
57
|
-
if (typeof link === 'string') return link;
|
|
58
|
-
return link.href;
|
|
59
|
-
}
|
|
60
|
-
let Document = (_class$2 = (_store = /*#__PURE__*/_classPrivateFieldKey("store"), _request = /*#__PURE__*/_classPrivateFieldKey("request"), class Document {
|
|
61
|
-
constructor(store, identifier) {
|
|
62
|
-
Object.defineProperty(this, _request, {
|
|
63
|
-
value: _request2
|
|
64
|
-
});
|
|
65
|
-
_initializerDefineProperty(this, "links", _descriptor$2, this);
|
|
66
|
-
_initializerDefineProperty(this, "data", _descriptor2$1, this);
|
|
67
|
-
_initializerDefineProperty(this, "errors", _descriptor3, this);
|
|
68
|
-
_initializerDefineProperty(this, "meta", _descriptor4, this);
|
|
69
|
-
Object.defineProperty(this, _store, {
|
|
70
|
-
writable: true,
|
|
71
|
-
value: void 0
|
|
72
|
-
});
|
|
73
|
-
_classPrivateFieldBase(this, _store)[_store] = store;
|
|
74
|
-
this.identifier = identifier;
|
|
75
|
-
}
|
|
76
|
-
fetch(options = {}) {
|
|
77
|
-
assert(`No self link`, this.links?.self);
|
|
78
|
-
options.cacheOptions = options.cacheOptions || {};
|
|
79
|
-
options.cacheOptions.key = this.identifier?.lid;
|
|
80
|
-
return _classPrivateFieldBase(this, _request)[_request]('self', options);
|
|
81
|
-
}
|
|
82
|
-
next(options) {
|
|
83
|
-
return _classPrivateFieldBase(this, _request)[_request]('next', options);
|
|
84
|
-
}
|
|
85
|
-
prev(options) {
|
|
86
|
-
return _classPrivateFieldBase(this, _request)[_request]('prev', options);
|
|
87
|
-
}
|
|
88
|
-
first(options) {
|
|
89
|
-
return _classPrivateFieldBase(this, _request)[_request]('first', options);
|
|
90
|
-
}
|
|
91
|
-
last(options) {
|
|
92
|
-
return _classPrivateFieldBase(this, _request)[_request]('last', options);
|
|
93
|
-
}
|
|
94
|
-
toJSON() {
|
|
95
|
-
const data = {};
|
|
96
|
-
data.identifier = this.identifier;
|
|
97
|
-
if (this.data !== undefined) {
|
|
98
|
-
data.data = this.data;
|
|
99
|
-
}
|
|
100
|
-
if (this.links !== undefined) {
|
|
101
|
-
data.links = this.links;
|
|
102
|
-
}
|
|
103
|
-
if (this.errors !== undefined) {
|
|
104
|
-
data.errors = this.errors;
|
|
105
|
-
}
|
|
106
|
-
if (this.meta !== undefined) {
|
|
107
|
-
data.meta = this.meta;
|
|
108
|
-
}
|
|
109
|
-
return data;
|
|
110
|
-
}
|
|
111
|
-
}), (_descriptor$2 = _applyDecoratedDescriptor(_class$2.prototype, "links", [tracked], {
|
|
112
|
-
configurable: true,
|
|
113
|
-
enumerable: true,
|
|
114
|
-
writable: true,
|
|
115
|
-
initializer: null
|
|
116
|
-
}), _descriptor2$1 = _applyDecoratedDescriptor(_class$2.prototype, "data", [tracked], {
|
|
117
|
-
configurable: true,
|
|
118
|
-
enumerable: true,
|
|
119
|
-
writable: true,
|
|
120
|
-
initializer: null
|
|
121
|
-
}), _descriptor3 = _applyDecoratedDescriptor(_class$2.prototype, "errors", [tracked], {
|
|
122
|
-
configurable: true,
|
|
123
|
-
enumerable: true,
|
|
124
|
-
writable: true,
|
|
125
|
-
initializer: null
|
|
126
|
-
}), _descriptor4 = _applyDecoratedDescriptor(_class$2.prototype, "meta", [tracked], {
|
|
127
|
-
configurable: true,
|
|
128
|
-
enumerable: true,
|
|
129
|
-
writable: true,
|
|
130
|
-
initializer: null
|
|
131
|
-
})), _class$2);
|
|
132
|
-
async function _request2(link, options = {}) {
|
|
133
|
-
const href = this.links?.[link];
|
|
134
|
-
if (!href) {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
|
|
138
|
-
url: urlFromLink(href)
|
|
139
|
-
}));
|
|
140
|
-
return response.content;
|
|
141
|
-
}
|
|
142
|
-
function isErrorDocument(document) {
|
|
143
|
-
return 'errors' in document;
|
|
144
|
-
}
|
|
145
|
-
function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
146
|
-
const {
|
|
147
|
-
identifier
|
|
148
|
-
} = options;
|
|
149
|
-
if (isErrorDocument(document)) {
|
|
150
|
-
if (!identifier && !options.shouldHydrate) {
|
|
151
|
-
return document;
|
|
152
|
-
}
|
|
153
|
-
let doc;
|
|
154
|
-
if (identifier) {
|
|
155
|
-
doc = store._documentCache.get(identifier);
|
|
156
|
-
}
|
|
157
|
-
if (!doc) {
|
|
158
|
-
doc = new Document(store, identifier);
|
|
159
|
-
copyDocumentProperties(doc, document);
|
|
160
|
-
if (identifier) {
|
|
161
|
-
store._documentCache.set(identifier, doc);
|
|
162
|
-
}
|
|
163
|
-
} else if (!isFromCache) {
|
|
164
|
-
doc.data = undefined;
|
|
165
|
-
copyDocumentProperties(doc, document);
|
|
166
|
-
}
|
|
167
|
-
return options.shouldHydrate ? doc : document;
|
|
168
|
-
}
|
|
169
|
-
if (Array.isArray(document.data)) {
|
|
170
|
-
const {
|
|
171
|
-
recordArrayManager
|
|
172
|
-
} = store;
|
|
173
|
-
if (!identifier) {
|
|
174
|
-
if (!options.shouldHydrate) {
|
|
175
|
-
return document;
|
|
176
|
-
}
|
|
177
|
-
const data = recordArrayManager.createArray({
|
|
178
|
-
type: request.url,
|
|
179
|
-
identifiers: document.data,
|
|
180
|
-
doc: document,
|
|
181
|
-
query: request
|
|
182
|
-
});
|
|
183
|
-
const doc = new Document(store, null);
|
|
184
|
-
doc.data = data;
|
|
185
|
-
doc.meta = document.meta;
|
|
186
|
-
doc.links = document.links;
|
|
187
|
-
return doc;
|
|
188
|
-
}
|
|
189
|
-
let managed = recordArrayManager._keyedArrays.get(identifier.lid);
|
|
190
|
-
if (!managed) {
|
|
191
|
-
managed = recordArrayManager.createArray({
|
|
192
|
-
type: identifier.lid,
|
|
193
|
-
identifiers: document.data,
|
|
194
|
-
doc: document
|
|
195
|
-
});
|
|
196
|
-
recordArrayManager._keyedArrays.set(identifier.lid, managed);
|
|
197
|
-
const doc = new Document(store, identifier);
|
|
198
|
-
doc.data = managed;
|
|
199
|
-
doc.meta = document.meta;
|
|
200
|
-
doc.links = document.links;
|
|
201
|
-
store._documentCache.set(identifier, doc);
|
|
202
|
-
return options.shouldHydrate ? doc : document;
|
|
203
|
-
} else {
|
|
204
|
-
const doc = store._documentCache.get(identifier);
|
|
205
|
-
if (!isFromCache) {
|
|
206
|
-
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
207
|
-
doc.data = managed;
|
|
208
|
-
doc.meta = document.meta;
|
|
209
|
-
doc.links = document.links;
|
|
210
|
-
}
|
|
211
|
-
return options.shouldHydrate ? doc : document;
|
|
212
|
-
}
|
|
213
|
-
} else {
|
|
214
|
-
if (!identifier && !options.shouldHydrate) {
|
|
215
|
-
return document;
|
|
216
|
-
}
|
|
217
|
-
const data = document.data ? store.peekRecord(document.data) : null;
|
|
218
|
-
let doc;
|
|
219
|
-
if (identifier) {
|
|
220
|
-
doc = store._documentCache.get(identifier);
|
|
221
|
-
}
|
|
222
|
-
if (!doc) {
|
|
223
|
-
doc = new Document(store, identifier);
|
|
224
|
-
doc.data = data;
|
|
225
|
-
copyDocumentProperties(doc, document);
|
|
226
|
-
if (identifier) {
|
|
227
|
-
store._documentCache.set(identifier, doc);
|
|
228
|
-
}
|
|
229
|
-
} else if (!isFromCache) {
|
|
230
|
-
doc.data = data;
|
|
231
|
-
copyDocumentProperties(doc, document);
|
|
232
|
-
}
|
|
233
|
-
return options.shouldHydrate ? doc : document;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
237
|
-
const {
|
|
238
|
-
cacheOptions
|
|
239
|
-
} = request;
|
|
240
|
-
return cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier) : false);
|
|
241
|
-
}
|
|
242
|
-
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
243
|
-
const {
|
|
244
|
-
cacheOptions
|
|
245
|
-
} = request;
|
|
246
|
-
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier) : false));
|
|
247
|
-
}
|
|
248
|
-
function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
|
|
249
|
-
const {
|
|
250
|
-
store
|
|
251
|
-
} = context.request;
|
|
252
|
-
const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
|
|
253
|
-
return next(context.request).then(document => {
|
|
254
|
-
store.requestManager._pending.delete(context.id);
|
|
255
|
-
store._enableAsyncFlush = true;
|
|
256
|
-
let response;
|
|
257
|
-
store._join(() => {
|
|
258
|
-
response = store.cache.put(document);
|
|
259
|
-
response = maybeUpdateUiObjects(store, context.request, {
|
|
260
|
-
shouldHydrate,
|
|
261
|
-
shouldFetch,
|
|
262
|
-
shouldBackgroundFetch,
|
|
263
|
-
identifier
|
|
264
|
-
}, response, false);
|
|
265
|
-
});
|
|
266
|
-
store._enableAsyncFlush = null;
|
|
267
|
-
if (shouldFetch) {
|
|
268
|
-
return response;
|
|
269
|
-
} else if (shouldBackgroundFetch) {
|
|
270
|
-
store.notifications._flush();
|
|
271
|
-
}
|
|
272
|
-
}, error => {
|
|
273
|
-
store.requestManager._pending.delete(context.id);
|
|
274
|
-
if (context.request.signal?.aborted) {
|
|
275
|
-
throw error;
|
|
276
|
-
}
|
|
277
|
-
store.requestManager._pending.delete(context.id);
|
|
278
|
-
store._enableAsyncFlush = true;
|
|
279
|
-
let response;
|
|
280
|
-
store._join(() => {
|
|
281
|
-
response = store.cache.put(error);
|
|
282
|
-
response = maybeUpdateUiObjects(store, context.request, {
|
|
283
|
-
shouldHydrate,
|
|
284
|
-
shouldFetch,
|
|
285
|
-
shouldBackgroundFetch,
|
|
286
|
-
identifier
|
|
287
|
-
}, response, false);
|
|
288
|
-
});
|
|
289
|
-
store._enableAsyncFlush = null;
|
|
290
|
-
if (!shouldBackgroundFetch) {
|
|
291
|
-
const newError = cloneError(error);
|
|
292
|
-
newError.content = response;
|
|
293
|
-
throw newError;
|
|
294
|
-
} else {
|
|
295
|
-
store.notifications._flush();
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
function cloneError(error) {
|
|
300
|
-
const cloned = new Error(error.message);
|
|
301
|
-
cloned.stack = error.stack;
|
|
302
|
-
cloned.error = error.error;
|
|
303
|
-
return cloned;
|
|
304
|
-
}
|
|
305
|
-
const SkipCache = Symbol.for('ember-data:skip-cache');
|
|
306
|
-
const EnableHydration = Symbol.for('ember-data:enable-hydration');
|
|
307
|
-
const CacheHandler = {
|
|
308
|
-
request(context, next) {
|
|
309
|
-
// if we have no cache or no cache-key skip cache handling
|
|
310
|
-
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
311
|
-
return next(context.request);
|
|
312
|
-
}
|
|
313
|
-
const {
|
|
314
|
-
store
|
|
315
|
-
} = context.request;
|
|
316
|
-
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
317
|
-
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
318
|
-
|
|
319
|
-
// determine if we should skip cache
|
|
320
|
-
if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
|
|
321
|
-
return fetchContentAndHydrate(next, context, identifier, true, false);
|
|
322
|
-
}
|
|
7
|
+
import { defineSignal, addToTransaction, createSignal, subscribe, createArrayTags, addTransactionCB } from '@ember-data/tracking/-private';
|
|
8
|
+
import { _backburner } from '@ember/runloop';
|
|
9
|
+
import { compat } from '@ember-data/tracking';
|
|
323
10
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
store.requestManager._pending.set(context.id, promise);
|
|
328
|
-
}
|
|
329
|
-
const shouldHydrate = context.request[EnableHydration] || false;
|
|
330
|
-
if ('error' in peeked) {
|
|
331
|
-
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
332
|
-
shouldHydrate,
|
|
333
|
-
identifier
|
|
334
|
-
}, peeked.content, true) : peeked.content;
|
|
335
|
-
const newError = cloneError(peeked);
|
|
336
|
-
newError.content = content;
|
|
337
|
-
throw newError;
|
|
338
|
-
}
|
|
339
|
-
return Promise.resolve(shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
340
|
-
shouldHydrate,
|
|
341
|
-
identifier
|
|
342
|
-
}, peeked.content, true) : peeked.content);
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
function copyDocumentProperties(target, source) {
|
|
346
|
-
if ('links' in source) {
|
|
347
|
-
target.links = source.links;
|
|
348
|
-
}
|
|
349
|
-
if ('meta' in source) {
|
|
350
|
-
target.meta = source.meta;
|
|
351
|
-
}
|
|
352
|
-
if ('errors' in source) {
|
|
353
|
-
target.errors = source.errors;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
11
|
+
/**
|
|
12
|
+
@module @ember-data/store
|
|
13
|
+
*/
|
|
356
14
|
|
|
357
|
-
// Used by the store to normalize IDs entering the store. Despite the fact
|
|
358
|
-
// that developers may provide IDs as numbers (e.g., `store.findRecord('person', 1)`),
|
|
359
|
-
// it is important that internally we use strings, since IDs may be serialized
|
|
360
|
-
// and lose type information. For example, Ember's router may put a record's
|
|
361
|
-
// ID into the URL, and if we later try to deserialize that URL and find the
|
|
362
|
-
// corresponding record, we will not know if it is a string or a number.
|
|
363
15
|
function coerceId(id) {
|
|
364
16
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_ID)) {
|
|
365
17
|
let normalized;
|
|
@@ -392,14 +44,6 @@ function ensureStringId(id) {
|
|
|
392
44
|
assert(`Expected id to be a string or number, received ${String(id)}`, normalized !== null);
|
|
393
45
|
return normalized;
|
|
394
46
|
}
|
|
395
|
-
|
|
396
|
-
// provided for additional debuggability
|
|
397
|
-
const DEBUG_CLIENT_ORIGINATED = Symbol('record-originated-on-client');
|
|
398
|
-
const DEBUG_IDENTIFIER_BUCKET = Symbol('identifier-bucket');
|
|
399
|
-
const DEBUG_STALE_CACHE_OWNER = Symbol('warpDriveStaleCache');
|
|
400
|
-
|
|
401
|
-
// also present in production
|
|
402
|
-
const CACHE_OWNER = Symbol('warpDriveCache');
|
|
403
47
|
function normalizeModelName(type) {
|
|
404
48
|
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_TYPES)) {
|
|
405
49
|
const result = dasherize(type);
|
|
@@ -428,7 +72,7 @@ function installPolyfill() {
|
|
|
428
72
|
// we might be able to optimize this by requesting more bytes than we need at a time
|
|
429
73
|
const rng = function () {
|
|
430
74
|
// WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
|
|
431
|
-
|
|
75
|
+
const rnds8 = new Uint8Array(16);
|
|
432
76
|
if (!CRYPTO.getRandomValues && !isFastBoot) {
|
|
433
77
|
throw new Error(`Unable to generate bytes for UUID`);
|
|
434
78
|
}
|
|
@@ -444,12 +88,12 @@ function installPolyfill() {
|
|
|
444
88
|
byteToHex[i] = (i + 0x100).toString(16).substr(1);
|
|
445
89
|
}
|
|
446
90
|
const bytesToUuid = function (buf) {
|
|
447
|
-
|
|
91
|
+
const bth = byteToHex;
|
|
448
92
|
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
|
449
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('');
|
|
450
94
|
};
|
|
451
95
|
CRYPTO.randomUUID = function uuidv4() {
|
|
452
|
-
|
|
96
|
+
const rnds = rng();
|
|
453
97
|
|
|
454
98
|
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
|
|
455
99
|
rnds[6] = rnds[6] & 0x0f | 0x40;
|
|
@@ -503,6 +147,7 @@ function freeze(obj) {
|
|
|
503
147
|
|
|
504
148
|
// type IdentifierTypeLookup = { all: Set<StableRecordIdentifier>; id: Map<string, StableRecordIdentifier> };
|
|
505
149
|
// type IdentifiersByType = Map<string, IdentifierTypeLookup>;
|
|
150
|
+
|
|
506
151
|
let configuredForgetMethod;
|
|
507
152
|
let configuredGenerationMethod;
|
|
508
153
|
let configuredResetMethod;
|
|
@@ -610,7 +255,8 @@ class IdentifierCache {
|
|
|
610
255
|
this._cache = {
|
|
611
256
|
resources: new Map(),
|
|
612
257
|
resourcesByType: Object.create(null),
|
|
613
|
-
documents: new Map()
|
|
258
|
+
documents: new Map(),
|
|
259
|
+
polymorphicLidBackMap: new Map()
|
|
614
260
|
};
|
|
615
261
|
}
|
|
616
262
|
|
|
@@ -664,7 +310,7 @@ class IdentifierCache {
|
|
|
664
310
|
console.log(`Identifiers: ${lid ? 'no ' : ''}lid ${lid ? lid + ' ' : ''}determined for resource`, resource);
|
|
665
311
|
}
|
|
666
312
|
let identifier = /*#__NOINLINE__*/getIdentifierFromLid(this._cache, lid, resource);
|
|
667
|
-
if (identifier !==
|
|
313
|
+
if (identifier !== null) {
|
|
668
314
|
if (macroCondition(getOwnConfig().debug.LOG_IDENTIFIERS)) {
|
|
669
315
|
// eslint-disable-next-line no-console
|
|
670
316
|
console.groupEnd();
|
|
@@ -706,7 +352,7 @@ class IdentifierCache {
|
|
|
706
352
|
*
|
|
707
353
|
* @method peekRecordIdentifier
|
|
708
354
|
* @param resource
|
|
709
|
-
* @
|
|
355
|
+
* @return {StableRecordIdentifier | undefined}
|
|
710
356
|
* @private
|
|
711
357
|
*/
|
|
712
358
|
peekRecordIdentifier(resource) {
|
|
@@ -718,7 +364,7 @@ class IdentifierCache {
|
|
|
718
364
|
Returns `null` if the request does not have a `cacheKey` or `url`.
|
|
719
365
|
@method getOrCreateDocumentIdentifier
|
|
720
366
|
@param request
|
|
721
|
-
@
|
|
367
|
+
@return {StableDocumentIdentifier | null}
|
|
722
368
|
@public
|
|
723
369
|
*/
|
|
724
370
|
getOrCreateDocumentIdentifier(request) {
|
|
@@ -752,7 +398,7 @@ class IdentifierCache {
|
|
|
752
398
|
- this referential stability of the object itself is guaranteed
|
|
753
399
|
@method getOrCreateRecordIdentifier
|
|
754
400
|
@param resource
|
|
755
|
-
@
|
|
401
|
+
@return {StableRecordIdentifier}
|
|
756
402
|
@public
|
|
757
403
|
*/
|
|
758
404
|
getOrCreateRecordIdentifier(resource) {
|
|
@@ -767,12 +413,12 @@ class IdentifierCache {
|
|
|
767
413
|
with the signature `generateMethod({ type }, 'record')`.
|
|
768
414
|
@method createIdentifierForNewRecord
|
|
769
415
|
@param data
|
|
770
|
-
@
|
|
416
|
+
@return {StableRecordIdentifier}
|
|
771
417
|
@public
|
|
772
418
|
*/
|
|
773
419
|
createIdentifierForNewRecord(data) {
|
|
774
|
-
|
|
775
|
-
|
|
420
|
+
const newLid = this._generate(data, 'record');
|
|
421
|
+
const identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
|
|
776
422
|
id: data.id || null,
|
|
777
423
|
type: data.type,
|
|
778
424
|
lid: newLid,
|
|
@@ -810,7 +456,7 @@ class IdentifierCache {
|
|
|
810
456
|
@method updateRecordIdentifier
|
|
811
457
|
@param identifierObject
|
|
812
458
|
@param data
|
|
813
|
-
@
|
|
459
|
+
@return {StableRecordIdentifier}
|
|
814
460
|
@public
|
|
815
461
|
*/
|
|
816
462
|
updateRecordIdentifier(identifierObject, data) {
|
|
@@ -830,7 +476,7 @@ class IdentifierCache {
|
|
|
830
476
|
}
|
|
831
477
|
}
|
|
832
478
|
if (existingIdentifier) {
|
|
833
|
-
|
|
479
|
+
const generatedIdentifier = identifier;
|
|
834
480
|
identifier = this._mergeRecordIdentifiers(keyInfo, generatedIdentifier, existingIdentifier, data);
|
|
835
481
|
|
|
836
482
|
// make sure that the `lid` on the data we are processing matches the lid we kept
|
|
@@ -842,7 +488,7 @@ class IdentifierCache {
|
|
|
842
488
|
console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
|
|
843
489
|
}
|
|
844
490
|
}
|
|
845
|
-
|
|
491
|
+
const id = identifier.id;
|
|
846
492
|
/*#__NOINLINE__*/
|
|
847
493
|
performRecordIdentifierUpdate(identifier, keyInfo, data, this._update);
|
|
848
494
|
const newId = identifier.id;
|
|
@@ -876,16 +522,32 @@ class IdentifierCache {
|
|
|
876
522
|
const kept = this._merge(identifier, existingIdentifier, data);
|
|
877
523
|
const abandoned = kept === identifier ? existingIdentifier : identifier;
|
|
878
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
|
+
|
|
879
532
|
// cleanup the identifier we no longer need
|
|
880
533
|
this.forgetRecordIdentifier(abandoned);
|
|
881
534
|
|
|
882
|
-
// ensure a secondary cache entry for
|
|
883
|
-
|
|
535
|
+
// ensure a secondary cache entry for the original lid for the abandoned identifier
|
|
536
|
+
this._cache.resources.set(abandoned.lid, kept);
|
|
884
537
|
|
|
885
|
-
//
|
|
886
|
-
//
|
|
887
|
-
|
|
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);
|
|
888
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);
|
|
889
551
|
return kept;
|
|
890
552
|
}
|
|
891
553
|
|
|
@@ -908,6 +570,13 @@ class IdentifierCache {
|
|
|
908
570
|
}
|
|
909
571
|
this._cache.resources.delete(identifier.lid);
|
|
910
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
|
+
}
|
|
911
580
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
912
581
|
identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
|
|
913
582
|
}
|
|
@@ -953,17 +622,22 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
953
622
|
},
|
|
954
623
|
set [DEBUG_STALE_CACHE_OWNER](value) {
|
|
955
624
|
recordIdentifier[DEBUG_STALE_CACHE_OWNER] = value;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
Object.defineProperty(wrapper, 'toString', {
|
|
628
|
+
enumerable: false,
|
|
629
|
+
value: () => {
|
|
959
630
|
const {
|
|
960
631
|
type,
|
|
961
632
|
id,
|
|
962
633
|
lid
|
|
963
634
|
} = recordIdentifier;
|
|
964
635
|
return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
|
|
965
|
-
}
|
|
966
|
-
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
Object.defineProperty(wrapper, 'toJSON', {
|
|
639
|
+
enumerable: false,
|
|
640
|
+
value: () => {
|
|
967
641
|
const {
|
|
968
642
|
type,
|
|
969
643
|
id,
|
|
@@ -975,7 +649,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
975
649
|
lid
|
|
976
650
|
};
|
|
977
651
|
}
|
|
978
|
-
};
|
|
652
|
+
});
|
|
979
653
|
wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
|
|
980
654
|
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
|
|
981
655
|
IDENTIFIERS.add(wrapper);
|
|
@@ -993,7 +667,7 @@ function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
|
|
|
993
667
|
} = keyInfo;
|
|
994
668
|
|
|
995
669
|
// get the mutable instance behind our proxy wrapper
|
|
996
|
-
|
|
670
|
+
const wrapper = identifier;
|
|
997
671
|
identifier = DEBUG_MAP.get(wrapper);
|
|
998
672
|
if (hasLid(data)) {
|
|
999
673
|
const lid = data.lid;
|
|
@@ -1048,7 +722,7 @@ function detectMerge(cache, keyInfo, identifier, data) {
|
|
|
1048
722
|
// we trigger a merge of the identifiers
|
|
1049
723
|
// though probably we should just throw an error here
|
|
1050
724
|
if (id !== null && id === newId && newType === type && hasLid(data) && data.lid !== lid) {
|
|
1051
|
-
return cache
|
|
725
|
+
return getIdentifierFromLid(cache, data.lid, data) || false;
|
|
1052
726
|
|
|
1053
727
|
// If the lids are the same, and ids are the same, but types are different we should trigger a merge of the identifiers
|
|
1054
728
|
} else if (id !== null && id === newId && newType && newType !== type && hasLid(data) && data.lid === lid) {
|
|
@@ -1065,7 +739,7 @@ function getIdentifierFromLid(cache, lid, resource) {
|
|
|
1065
739
|
// eslint-disable-next-line no-console
|
|
1066
740
|
console.log(`Identifiers: cache ${identifier ? 'HIT' : 'MISS'} - Non-Stable ${lid}`, resource);
|
|
1067
741
|
}
|
|
1068
|
-
return identifier;
|
|
742
|
+
return identifier || null;
|
|
1069
743
|
}
|
|
1070
744
|
function addResourceToCache(cache, identifier) {
|
|
1071
745
|
cache.resources.set(identifier.lid, identifier);
|
|
@@ -1082,28 +756,27 @@ function addResourceToCache(cache, identifier) {
|
|
|
1082
756
|
typeSet.id.set(identifier.id, identifier);
|
|
1083
757
|
}
|
|
1084
758
|
}
|
|
1085
|
-
var _class$1, _descriptor$1;
|
|
1086
759
|
|
|
1087
760
|
/**
|
|
1088
761
|
@module @ember-data/store
|
|
1089
762
|
*/
|
|
763
|
+
|
|
1090
764
|
/**
|
|
1091
765
|
@module @ember-data/store
|
|
1092
766
|
*/
|
|
767
|
+
|
|
1093
768
|
/**
|
|
1094
769
|
A `RecordReference` is a low-level API that allows users and
|
|
1095
770
|
addon authors to perform meta-operations on a record.
|
|
1096
771
|
|
|
1097
772
|
@class RecordReference
|
|
1098
773
|
@public
|
|
1099
|
-
@extends Reference
|
|
1100
774
|
*/
|
|
1101
|
-
|
|
775
|
+
class RecordReference {
|
|
1102
776
|
constructor(store, identifier) {
|
|
1103
777
|
// unsubscribe token given to us by the notification manager
|
|
1104
778
|
this.___token = void 0;
|
|
1105
779
|
this.___identifier = void 0;
|
|
1106
|
-
_initializerDefineProperty(this, "_ref", _descriptor$1, this);
|
|
1107
780
|
this.store = store;
|
|
1108
781
|
this.___identifier = identifier;
|
|
1109
782
|
this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
|
|
@@ -1271,14 +944,8 @@ let RecordReference = (_class$1 = class RecordReference {
|
|
|
1271
944
|
}
|
|
1272
945
|
assert(`Unable to fetch record of type ${this.type} without an id`);
|
|
1273
946
|
}
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
enumerable: true,
|
|
1277
|
-
writable: true,
|
|
1278
|
-
initializer: function () {
|
|
1279
|
-
return 0;
|
|
1280
|
-
}
|
|
1281
|
-
}), _class$1);
|
|
947
|
+
}
|
|
948
|
+
defineSignal(RecordReference.prototype, '_ref');
|
|
1282
949
|
|
|
1283
950
|
/**
|
|
1284
951
|
@module @ember-data/store
|
|
@@ -1309,6 +976,8 @@ class CacheCapabilitiesManager {
|
|
|
1309
976
|
if (this._store._cbs) {
|
|
1310
977
|
this._store._schedule('notify', () => this._flushNotifications());
|
|
1311
978
|
} else {
|
|
979
|
+
// TODO @runspired determine if relationship mutations should schedule
|
|
980
|
+
// into join/run vs immediate flush
|
|
1312
981
|
this._flushNotifications();
|
|
1313
982
|
}
|
|
1314
983
|
}
|
|
@@ -1316,7 +985,7 @@ class CacheCapabilitiesManager {
|
|
|
1316
985
|
if (this._willNotify === false) {
|
|
1317
986
|
return;
|
|
1318
987
|
}
|
|
1319
|
-
|
|
988
|
+
const pending = this._pendingNotifies;
|
|
1320
989
|
this._pendingNotifies = new Map();
|
|
1321
990
|
this._willNotify = false;
|
|
1322
991
|
pending.forEach((set, identifier) => {
|
|
@@ -1340,6 +1009,9 @@ class CacheCapabilitiesManager {
|
|
|
1340
1009
|
getSchemaDefinitionService() {
|
|
1341
1010
|
return this._store.getSchemaDefinitionService();
|
|
1342
1011
|
}
|
|
1012
|
+
get schema() {
|
|
1013
|
+
return this._store.schema;
|
|
1014
|
+
}
|
|
1343
1015
|
setRecordId(identifier, id) {
|
|
1344
1016
|
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
1345
1017
|
this._store._instanceCache.setRecordId(identifier, id);
|
|
@@ -1373,6 +1045,9 @@ function peekCache(instance) {
|
|
|
1373
1045
|
}
|
|
1374
1046
|
return null;
|
|
1375
1047
|
}
|
|
1048
|
+
function isDestroyable(record) {
|
|
1049
|
+
return Boolean(record && typeof record === 'object' && typeof record.destroy === 'function');
|
|
1050
|
+
}
|
|
1376
1051
|
|
|
1377
1052
|
/**
|
|
1378
1053
|
@module @ember-data/store
|
|
@@ -1400,8 +1075,9 @@ function peekRecordIdentifier(record) {
|
|
|
1400
1075
|
@static
|
|
1401
1076
|
@for @ember-data/store
|
|
1402
1077
|
@param {Object} record a record instance previously obstained from the store.
|
|
1403
|
-
@
|
|
1078
|
+
@return {StableRecordIdentifier}
|
|
1404
1079
|
*/
|
|
1080
|
+
|
|
1405
1081
|
function recordIdentifierFor(record) {
|
|
1406
1082
|
assert(`${String(record)} is not a record instantiated by @ember-data/store`, RecordCache.has(record));
|
|
1407
1083
|
return RecordCache.get(record);
|
|
@@ -1447,11 +1123,11 @@ class InstanceCache {
|
|
|
1447
1123
|
// @ts-expect-error TODO this needs to be fixed
|
|
1448
1124
|
'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
|
|
1449
1125
|
}
|
|
1450
|
-
|
|
1126
|
+
const staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
|
|
1451
1127
|
|
|
1452
1128
|
// check for duplicate entities
|
|
1453
|
-
|
|
1454
|
-
|
|
1129
|
+
const keptHasRecord = this.__instances.record.has(keptIdentifier);
|
|
1130
|
+
const staleHasRecord = this.__instances.record.has(staleIdentifier);
|
|
1455
1131
|
|
|
1456
1132
|
// we cannot merge entities when both have records
|
|
1457
1133
|
// (this may not be strictly true, we could probably swap the cache data the record points at)
|
|
@@ -1505,7 +1181,7 @@ class InstanceCache {
|
|
|
1505
1181
|
return record;
|
|
1506
1182
|
}
|
|
1507
1183
|
getReference(identifier) {
|
|
1508
|
-
|
|
1184
|
+
const cache = this.__instances.reference;
|
|
1509
1185
|
let reference = cache.get(identifier);
|
|
1510
1186
|
if (!reference) {
|
|
1511
1187
|
reference = new RecordReference(this.store, identifier);
|
|
@@ -1536,7 +1212,7 @@ class InstanceCache {
|
|
|
1536
1212
|
}
|
|
1537
1213
|
disconnect(identifier) {
|
|
1538
1214
|
const record = this.__instances.record.get(identifier);
|
|
1539
|
-
assert('Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying);
|
|
1215
|
+
assert('Cannot destroy record while it is still materialized', !isDestroyable(record) || record.isDestroyed || record.isDestroying);
|
|
1540
1216
|
this.store._graph?.remove(identifier);
|
|
1541
1217
|
this.store.identifierCache.forgetRecordIdentifier(identifier);
|
|
1542
1218
|
removeRecordDataFor(identifier);
|
|
@@ -1604,7 +1280,7 @@ class InstanceCache {
|
|
|
1604
1280
|
});
|
|
1605
1281
|
} else {
|
|
1606
1282
|
const typeCache = cache.resourcesByType;
|
|
1607
|
-
|
|
1283
|
+
const identifiers = typeCache[type]?.lid;
|
|
1608
1284
|
if (identifiers) {
|
|
1609
1285
|
identifiers.forEach(identifier => {
|
|
1610
1286
|
// if (rds.has(identifier)) {
|
|
@@ -1622,7 +1298,7 @@ class InstanceCache {
|
|
|
1622
1298
|
type,
|
|
1623
1299
|
lid
|
|
1624
1300
|
} = identifier;
|
|
1625
|
-
|
|
1301
|
+
const oldId = identifier.id;
|
|
1626
1302
|
|
|
1627
1303
|
// ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
|
|
1628
1304
|
assert(`'${type}' was saved to the server, but the response does not have an id and your record does not either.`, !(id === null && oldId === null));
|
|
@@ -1641,7 +1317,7 @@ class InstanceCache {
|
|
|
1641
1317
|
// eslint-disable-next-line no-console
|
|
1642
1318
|
console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
|
|
1643
1319
|
}
|
|
1644
|
-
|
|
1320
|
+
const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
|
|
1645
1321
|
type,
|
|
1646
1322
|
id
|
|
1647
1323
|
});
|
|
@@ -1680,13 +1356,13 @@ function resourceIsFullyDeleted(instanceCache, identifier) {
|
|
|
1680
1356
|
*/
|
|
1681
1357
|
|
|
1682
1358
|
function preloadData(store, identifier, preload) {
|
|
1683
|
-
|
|
1359
|
+
const jsonPayload = {};
|
|
1684
1360
|
//TODO(Igor) consider the polymorphic case
|
|
1685
1361
|
const schemas = store.getSchemaDefinitionService();
|
|
1686
1362
|
const relationships = schemas.relationshipsDefinitionFor(identifier);
|
|
1687
1363
|
Object.keys(preload).forEach(key => {
|
|
1688
|
-
|
|
1689
|
-
|
|
1364
|
+
const preloadValue = preload[key];
|
|
1365
|
+
const relationshipMeta = relationships[key];
|
|
1690
1366
|
if (relationshipMeta) {
|
|
1691
1367
|
if (!jsonPayload.relationships) {
|
|
1692
1368
|
jsonPayload.relationships = {};
|
|
@@ -1755,7 +1431,7 @@ function getShimClass(store, modelName) {
|
|
|
1755
1431
|
}
|
|
1756
1432
|
function mapFromHash(hash) {
|
|
1757
1433
|
const map = new Map();
|
|
1758
|
-
for (
|
|
1434
|
+
for (const i in hash) {
|
|
1759
1435
|
if (Object.prototype.hasOwnProperty.call(hash, i)) {
|
|
1760
1436
|
map.set(i, hash[i]);
|
|
1761
1437
|
}
|
|
@@ -1770,31 +1446,31 @@ class ShimModelClass {
|
|
|
1770
1446
|
this.modelName = modelName;
|
|
1771
1447
|
}
|
|
1772
1448
|
get fields() {
|
|
1773
|
-
|
|
1449
|
+
const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1774
1450
|
type: this.modelName
|
|
1775
1451
|
});
|
|
1776
|
-
|
|
1452
|
+
const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1777
1453
|
type: this.modelName
|
|
1778
1454
|
});
|
|
1779
|
-
|
|
1455
|
+
const fields = new Map();
|
|
1780
1456
|
Object.keys(attrs).forEach(key => fields.set(key, 'attribute'));
|
|
1781
1457
|
Object.keys(relationships).forEach(key => fields.set(key, relationships[key].kind));
|
|
1782
1458
|
return fields;
|
|
1783
1459
|
}
|
|
1784
1460
|
get attributes() {
|
|
1785
|
-
|
|
1461
|
+
const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1786
1462
|
type: this.modelName
|
|
1787
1463
|
});
|
|
1788
1464
|
return mapFromHash(attrs);
|
|
1789
1465
|
}
|
|
1790
1466
|
get relationshipsByName() {
|
|
1791
|
-
|
|
1467
|
+
const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1792
1468
|
type: this.modelName
|
|
1793
1469
|
});
|
|
1794
1470
|
return mapFromHash(relationships);
|
|
1795
1471
|
}
|
|
1796
1472
|
eachAttribute(callback, binding) {
|
|
1797
|
-
|
|
1473
|
+
const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1798
1474
|
type: this.modelName
|
|
1799
1475
|
});
|
|
1800
1476
|
Object.keys(attrDefs).forEach(key => {
|
|
@@ -1802,7 +1478,7 @@ class ShimModelClass {
|
|
|
1802
1478
|
});
|
|
1803
1479
|
}
|
|
1804
1480
|
eachRelationship(callback, binding) {
|
|
1805
|
-
|
|
1481
|
+
const relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1806
1482
|
type: this.modelName
|
|
1807
1483
|
});
|
|
1808
1484
|
Object.keys(relationshipDefs).forEach(key => {
|
|
@@ -1820,6 +1496,16 @@ class ShimModelClass {
|
|
|
1820
1496
|
});
|
|
1821
1497
|
}
|
|
1822
1498
|
}
|
|
1499
|
+
function _classPrivateFieldBase(receiver, privateKey) {
|
|
1500
|
+
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
1501
|
+
throw new TypeError("attempted to use private field on non-instance");
|
|
1502
|
+
}
|
|
1503
|
+
return receiver;
|
|
1504
|
+
}
|
|
1505
|
+
var id = 0;
|
|
1506
|
+
function _classPrivateFieldKey(name) {
|
|
1507
|
+
return "__private_" + id++ + "_" + name;
|
|
1508
|
+
}
|
|
1823
1509
|
var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
|
|
1824
1510
|
/**
|
|
1825
1511
|
* The CacheManager wraps a Cache enforcing that only
|
|
@@ -1846,16 +1532,6 @@ class CacheManager {
|
|
|
1846
1532
|
writable: true,
|
|
1847
1533
|
value: void 0
|
|
1848
1534
|
});
|
|
1849
|
-
/**
|
|
1850
|
-
* Query the cache for whether a given resource has been deleted and that deletion
|
|
1851
|
-
* has also been persisted.
|
|
1852
|
-
*
|
|
1853
|
-
* @method isDeletionCommitted
|
|
1854
|
-
* @public
|
|
1855
|
-
* @param identifier
|
|
1856
|
-
* @returns {boolean}
|
|
1857
|
-
*/
|
|
1858
|
-
this.isDel = void 0;
|
|
1859
1535
|
_classPrivateFieldBase(this, _cache)[_cache] = cache;
|
|
1860
1536
|
}
|
|
1861
1537
|
|
|
@@ -1869,7 +1545,7 @@ class CacheManager {
|
|
|
1869
1545
|
* semantics, `put` has `replace` semantics similar to
|
|
1870
1546
|
* the `http` method `PUT`
|
|
1871
1547
|
*
|
|
1872
|
-
* the individually
|
|
1548
|
+
* the individually cacheable
|
|
1873
1549
|
* e resource data it may contain
|
|
1874
1550
|
* should upsert, but the document data surrounding it should
|
|
1875
1551
|
* fully replace any existing information
|
|
@@ -1882,7 +1558,7 @@ class CacheManager {
|
|
|
1882
1558
|
*
|
|
1883
1559
|
* @method put
|
|
1884
1560
|
* @param {StructuredDocument} doc
|
|
1885
|
-
* @
|
|
1561
|
+
* @return {ResourceDocument}
|
|
1886
1562
|
* @public
|
|
1887
1563
|
*/
|
|
1888
1564
|
put(doc) {
|
|
@@ -1898,7 +1574,7 @@ class CacheManager {
|
|
|
1898
1574
|
* @method patch
|
|
1899
1575
|
* @public
|
|
1900
1576
|
* @param op the operation to perform
|
|
1901
|
-
* @
|
|
1577
|
+
* @return {void}
|
|
1902
1578
|
*/
|
|
1903
1579
|
patch(op) {
|
|
1904
1580
|
_classPrivateFieldBase(this, _cache)[_cache].patch(op);
|
|
@@ -1933,7 +1609,7 @@ class CacheManager {
|
|
|
1933
1609
|
* An implementation might want to do this because
|
|
1934
1610
|
* de-referencing records which read from their own
|
|
1935
1611
|
* blob is generally safer because the record does
|
|
1936
|
-
* not require
|
|
1612
|
+
* not require retaining connections to the Store
|
|
1937
1613
|
* and Cache to present data on a per-field basis.
|
|
1938
1614
|
*
|
|
1939
1615
|
* This generally takes the place of `getAttr` as
|
|
@@ -1946,7 +1622,7 @@ class CacheManager {
|
|
|
1946
1622
|
* @method peek
|
|
1947
1623
|
* @public
|
|
1948
1624
|
* @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
|
|
1949
|
-
* @
|
|
1625
|
+
* @return {ResourceDocument | ResourceBlob | null} the known resource data
|
|
1950
1626
|
*/
|
|
1951
1627
|
|
|
1952
1628
|
peek(identifier) {
|
|
@@ -1959,7 +1635,7 @@ class CacheManager {
|
|
|
1959
1635
|
*
|
|
1960
1636
|
* @method peekRequest
|
|
1961
1637
|
* @param {StableDocumentIdentifier}
|
|
1962
|
-
* @
|
|
1638
|
+
* @return {StableDocumentIdentifier | null}
|
|
1963
1639
|
* @public
|
|
1964
1640
|
*/
|
|
1965
1641
|
peekRequest(identifier) {
|
|
@@ -1974,7 +1650,7 @@ class CacheManager {
|
|
|
1974
1650
|
* @param identifier
|
|
1975
1651
|
* @param data
|
|
1976
1652
|
* @param hasRecord
|
|
1977
|
-
* @
|
|
1653
|
+
* @return {void | string[]} if `hasRecord` is true then calculated key changes should be returned
|
|
1978
1654
|
*/
|
|
1979
1655
|
upsert(identifier, data, hasRecord) {
|
|
1980
1656
|
return _classPrivateFieldBase(this, _cache)[_cache].upsert(identifier, data, hasRecord);
|
|
@@ -1992,7 +1668,7 @@ class CacheManager {
|
|
|
1992
1668
|
*
|
|
1993
1669
|
* @method fork
|
|
1994
1670
|
* @public
|
|
1995
|
-
* @
|
|
1671
|
+
* @return Promise<Cache>
|
|
1996
1672
|
*/
|
|
1997
1673
|
fork() {
|
|
1998
1674
|
return _classPrivateFieldBase(this, _cache)[_cache].fork();
|
|
@@ -2008,7 +1684,7 @@ class CacheManager {
|
|
|
2008
1684
|
* @method merge
|
|
2009
1685
|
* @param {Cache} cache
|
|
2010
1686
|
* @public
|
|
2011
|
-
* @
|
|
1687
|
+
* @return Promise<void>
|
|
2012
1688
|
*/
|
|
2013
1689
|
merge(cache) {
|
|
2014
1690
|
return _classPrivateFieldBase(this, _cache)[_cache].merge(cache);
|
|
@@ -2060,7 +1736,7 @@ class CacheManager {
|
|
|
2060
1736
|
* via `cache.hydrate`.
|
|
2061
1737
|
*
|
|
2062
1738
|
* @method dump
|
|
2063
|
-
* @
|
|
1739
|
+
* @return {Promise<ReadableStream>}
|
|
2064
1740
|
* @public
|
|
2065
1741
|
*/
|
|
2066
1742
|
dump() {
|
|
@@ -2081,7 +1757,7 @@ class CacheManager {
|
|
|
2081
1757
|
*
|
|
2082
1758
|
* @method hydrate
|
|
2083
1759
|
* @param {ReadableStream} stream
|
|
2084
|
-
* @
|
|
1760
|
+
* @return {Promise<void>}
|
|
2085
1761
|
* @public
|
|
2086
1762
|
*/
|
|
2087
1763
|
hydrate(stream) {
|
|
@@ -2095,7 +1771,7 @@ class CacheManager {
|
|
|
2095
1771
|
// ================
|
|
2096
1772
|
|
|
2097
1773
|
/**
|
|
2098
|
-
* [
|
|
1774
|
+
* [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client
|
|
2099
1775
|
*
|
|
2100
1776
|
* It returns properties from options that should be set on the record during the create
|
|
2101
1777
|
* process. This return value behavior is deprecated.
|
|
@@ -2169,7 +1845,7 @@ class CacheManager {
|
|
|
2169
1845
|
* @public
|
|
2170
1846
|
* @param identifier
|
|
2171
1847
|
* @param propertyName
|
|
2172
|
-
* @
|
|
1848
|
+
* @return {unknown}
|
|
2173
1849
|
*/
|
|
2174
1850
|
getAttr(identifier, propertyName) {
|
|
2175
1851
|
return _classPrivateFieldBase(this, _cache)[_cache].getAttr(identifier, propertyName);
|
|
@@ -2194,7 +1870,7 @@ class CacheManager {
|
|
|
2194
1870
|
* @method changedAttrs
|
|
2195
1871
|
* @public
|
|
2196
1872
|
* @param identifier
|
|
2197
|
-
* @
|
|
1873
|
+
* @return
|
|
2198
1874
|
*/
|
|
2199
1875
|
changedAttrs(identifier) {
|
|
2200
1876
|
return _classPrivateFieldBase(this, _cache)[_cache].changedAttrs(identifier);
|
|
@@ -2206,7 +1882,7 @@ class CacheManager {
|
|
|
2206
1882
|
* @method hasChangedAttrs
|
|
2207
1883
|
* @public
|
|
2208
1884
|
* @param identifier
|
|
2209
|
-
* @
|
|
1885
|
+
* @return {boolean}
|
|
2210
1886
|
*/
|
|
2211
1887
|
hasChangedAttrs(identifier) {
|
|
2212
1888
|
return _classPrivateFieldBase(this, _cache)[_cache].hasChangedAttrs(identifier);
|
|
@@ -2218,7 +1894,7 @@ class CacheManager {
|
|
|
2218
1894
|
* @method rollbackAttrs
|
|
2219
1895
|
* @public
|
|
2220
1896
|
* @param identifier
|
|
2221
|
-
* @
|
|
1897
|
+
* @return the names of attributes that were restored
|
|
2222
1898
|
*/
|
|
2223
1899
|
rollbackAttrs(identifier) {
|
|
2224
1900
|
return _classPrivateFieldBase(this, _cache)[_cache].rollbackAttrs(identifier);
|
|
@@ -2227,6 +1903,65 @@ class CacheManager {
|
|
|
2227
1903
|
// Relationships
|
|
2228
1904
|
// =============
|
|
2229
1905
|
|
|
1906
|
+
/**
|
|
1907
|
+
* Query the cache for the changes to relationships of a resource.
|
|
1908
|
+
*
|
|
1909
|
+
* Returns a map of relationship names to RelationshipDiff objects.
|
|
1910
|
+
*
|
|
1911
|
+
* ```ts
|
|
1912
|
+
* type RelationshipDiff =
|
|
1913
|
+
| {
|
|
1914
|
+
kind: 'collection';
|
|
1915
|
+
remoteState: StableRecordIdentifier[];
|
|
1916
|
+
additions: Set<StableRecordIdentifier>;
|
|
1917
|
+
removals: Set<StableRecordIdentifier>;
|
|
1918
|
+
localState: StableRecordIdentifier[];
|
|
1919
|
+
reordered: boolean;
|
|
1920
|
+
}
|
|
1921
|
+
| {
|
|
1922
|
+
kind: 'resource';
|
|
1923
|
+
remoteState: StableRecordIdentifier | null;
|
|
1924
|
+
localState: StableRecordIdentifier | null;
|
|
1925
|
+
};
|
|
1926
|
+
```
|
|
1927
|
+
*
|
|
1928
|
+
* @method changedRelationships
|
|
1929
|
+
* @public
|
|
1930
|
+
* @param {StableRecordIdentifier} identifier
|
|
1931
|
+
* @return {Map<string, RelationshipDiff>}
|
|
1932
|
+
*/
|
|
1933
|
+
changedRelationships(identifier) {
|
|
1934
|
+
return _classPrivateFieldBase(this, _cache)[_cache].changedRelationships(identifier);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
/**
|
|
1938
|
+
* Query the cache for whether any mutated attributes exist
|
|
1939
|
+
*
|
|
1940
|
+
* @method hasChangedRelationships
|
|
1941
|
+
* @public
|
|
1942
|
+
* @param {StableRecordIdentifier} identifier
|
|
1943
|
+
* @return {boolean}
|
|
1944
|
+
*/
|
|
1945
|
+
hasChangedRelationships(identifier) {
|
|
1946
|
+
return _classPrivateFieldBase(this, _cache)[_cache].hasChangedRelationships(identifier);
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
/**
|
|
1950
|
+
* Tell the cache to discard any uncommitted mutations to relationships.
|
|
1951
|
+
*
|
|
1952
|
+
* This will also discard the change on any appropriate inverses.
|
|
1953
|
+
*
|
|
1954
|
+
* This method is a candidate to become a mutation
|
|
1955
|
+
*
|
|
1956
|
+
* @method rollbackRelationships
|
|
1957
|
+
* @public
|
|
1958
|
+
* @param {StableRecordIdentifier} identifier
|
|
1959
|
+
* @return {string[]} the names of relationships that were restored
|
|
1960
|
+
*/
|
|
1961
|
+
rollbackRelationships(identifier) {
|
|
1962
|
+
return _classPrivateFieldBase(this, _cache)[_cache].rollbackRelationships(identifier);
|
|
1963
|
+
}
|
|
1964
|
+
|
|
2230
1965
|
/**
|
|
2231
1966
|
* Query the cache for the current state of a relationship property
|
|
2232
1967
|
*
|
|
@@ -2234,7 +1969,7 @@ class CacheManager {
|
|
|
2234
1969
|
* @public
|
|
2235
1970
|
* @param identifier
|
|
2236
1971
|
* @param propertyName
|
|
2237
|
-
* @
|
|
1972
|
+
* @return resource relationship object
|
|
2238
1973
|
*/
|
|
2239
1974
|
getRelationship(identifier, propertyName) {
|
|
2240
1975
|
return _classPrivateFieldBase(this, _cache)[_cache].getRelationship(identifier, propertyName);
|
|
@@ -2262,7 +1997,7 @@ class CacheManager {
|
|
|
2262
1997
|
* @method getErrors
|
|
2263
1998
|
* @public
|
|
2264
1999
|
* @param identifier
|
|
2265
|
-
* @
|
|
2000
|
+
* @return
|
|
2266
2001
|
*/
|
|
2267
2002
|
getErrors(identifier) {
|
|
2268
2003
|
return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier);
|
|
@@ -2274,7 +2009,7 @@ class CacheManager {
|
|
|
2274
2009
|
* @method isEmpty
|
|
2275
2010
|
* @public
|
|
2276
2011
|
* @param identifier
|
|
2277
|
-
* @
|
|
2012
|
+
* @return {boolean}
|
|
2278
2013
|
*/
|
|
2279
2014
|
isEmpty(identifier) {
|
|
2280
2015
|
return _classPrivateFieldBase(this, _cache)[_cache].isEmpty(identifier);
|
|
@@ -2287,7 +2022,7 @@ class CacheManager {
|
|
|
2287
2022
|
* @method isNew
|
|
2288
2023
|
* @public
|
|
2289
2024
|
* @param identifier
|
|
2290
|
-
* @
|
|
2025
|
+
* @return {boolean}
|
|
2291
2026
|
*/
|
|
2292
2027
|
isNew(identifier) {
|
|
2293
2028
|
return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier);
|
|
@@ -2300,15 +2035,29 @@ class CacheManager {
|
|
|
2300
2035
|
* @method isDeleted
|
|
2301
2036
|
* @public
|
|
2302
2037
|
* @param identifier
|
|
2303
|
-
* @
|
|
2038
|
+
* @return {boolean}
|
|
2304
2039
|
*/
|
|
2305
2040
|
isDeleted(identifier) {
|
|
2306
2041
|
return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier);
|
|
2307
2042
|
}
|
|
2043
|
+
|
|
2044
|
+
/**
|
|
2045
|
+
* Query the cache for whether a given resource has been deleted and that deletion
|
|
2046
|
+
* has also been persisted.
|
|
2047
|
+
*
|
|
2048
|
+
* @method isDeletionCommitted
|
|
2049
|
+
* @public
|
|
2050
|
+
* @param identifier
|
|
2051
|
+
* @return {boolean}
|
|
2052
|
+
*/
|
|
2308
2053
|
isDeletionCommitted(identifier) {
|
|
2309
2054
|
return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier);
|
|
2310
2055
|
}
|
|
2311
2056
|
}
|
|
2057
|
+
|
|
2058
|
+
/**
|
|
2059
|
+
* @module @ember-data/store
|
|
2060
|
+
*/
|
|
2312
2061
|
let tokenId = 0;
|
|
2313
2062
|
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
2314
2063
|
function isCacheOperationValue(value) {
|
|
@@ -2319,7 +2068,7 @@ function runLoopIsFlushing() {
|
|
|
2319
2068
|
return !!_backburner.currentInstance && _backburner._autorun !== true;
|
|
2320
2069
|
}
|
|
2321
2070
|
function _unsubscribe(tokens, token, cache) {
|
|
2322
|
-
|
|
2071
|
+
const identifier = tokens.get(token);
|
|
2323
2072
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
2324
2073
|
if (!identifier) {
|
|
2325
2074
|
// eslint-disable-next-line no-console
|
|
@@ -2378,7 +2127,7 @@ class NotificationManager {
|
|
|
2378
2127
|
* @public
|
|
2379
2128
|
* @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
|
|
2380
2129
|
* @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
|
|
2381
|
-
* @
|
|
2130
|
+
* @return {UnsubscribeToken} an opaque token to be used with unsubscribe
|
|
2382
2131
|
*/
|
|
2383
2132
|
|
|
2384
2133
|
subscribe(identifier, callback) {
|
|
@@ -2388,7 +2137,7 @@ class NotificationManager {
|
|
|
2388
2137
|
map = new Map();
|
|
2389
2138
|
this._cache.set(identifier, map);
|
|
2390
2139
|
}
|
|
2391
|
-
|
|
2140
|
+
const unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
|
|
2392
2141
|
_tokenRef: tokenId++
|
|
2393
2142
|
} : {};
|
|
2394
2143
|
map.set(unsubToken, callback);
|
|
@@ -2441,7 +2190,7 @@ class NotificationManager {
|
|
|
2441
2190
|
this._buffered.set(identifier, buffer);
|
|
2442
2191
|
}
|
|
2443
2192
|
buffer.push([value, key]);
|
|
2444
|
-
|
|
2193
|
+
this._scheduleNotify();
|
|
2445
2194
|
}
|
|
2446
2195
|
return hasSubscribers;
|
|
2447
2196
|
}
|
|
@@ -2483,14 +2232,14 @@ class NotificationManager {
|
|
|
2483
2232
|
|
|
2484
2233
|
// TODO for documents this will need to switch based on Identifier kind
|
|
2485
2234
|
if (isCacheOperationValue(value)) {
|
|
2486
|
-
|
|
2235
|
+
const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
|
|
2487
2236
|
if (callbackMap) {
|
|
2488
2237
|
callbackMap.forEach(cb => {
|
|
2489
2238
|
cb(identifier, value);
|
|
2490
2239
|
});
|
|
2491
2240
|
}
|
|
2492
2241
|
}
|
|
2493
|
-
|
|
2242
|
+
const callbackMap = this._cache.get(identifier);
|
|
2494
2243
|
if (!callbackMap || !callbackMap.size) {
|
|
2495
2244
|
return false;
|
|
2496
2245
|
}
|
|
@@ -2506,7 +2255,40 @@ class NotificationManager {
|
|
|
2506
2255
|
this._cache.clear();
|
|
2507
2256
|
}
|
|
2508
2257
|
}
|
|
2509
|
-
|
|
2258
|
+
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
|
2259
|
+
var desc = {};
|
|
2260
|
+
Object.keys(descriptor).forEach(function (key) {
|
|
2261
|
+
desc[key] = descriptor[key];
|
|
2262
|
+
});
|
|
2263
|
+
desc.enumerable = !!desc.enumerable;
|
|
2264
|
+
desc.configurable = !!desc.configurable;
|
|
2265
|
+
if ('value' in desc || desc.initializer) {
|
|
2266
|
+
desc.writable = true;
|
|
2267
|
+
}
|
|
2268
|
+
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
|
|
2269
|
+
return decorator(target, property, desc) || desc;
|
|
2270
|
+
}, desc);
|
|
2271
|
+
if (context && desc.initializer !== void 0) {
|
|
2272
|
+
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
|
|
2273
|
+
desc.initializer = undefined;
|
|
2274
|
+
}
|
|
2275
|
+
if (desc.initializer === void 0) {
|
|
2276
|
+
Object.defineProperty(target, property, desc);
|
|
2277
|
+
desc = null;
|
|
2278
|
+
}
|
|
2279
|
+
return desc;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2283
|
+
/*
|
|
2284
|
+
We redefine Proxy because the native Proxy type treats the `target` and
|
|
2285
|
+
`receiver` as the same type incorrectly.
|
|
2286
|
+
|
|
2287
|
+
We ported this from Typescript's own Proxy types on 3/10/2024.
|
|
2288
|
+
*/
|
|
2289
|
+
|
|
2290
|
+
const NativeProxy = Proxy;
|
|
2291
|
+
var _class;
|
|
2510
2292
|
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']);
|
|
2511
2293
|
const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
|
|
2512
2294
|
const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
|
|
@@ -2516,19 +2298,16 @@ function isArrayGetter(prop) {
|
|
|
2516
2298
|
function isArraySetter(prop) {
|
|
2517
2299
|
return ARRAY_SETTER_METHODS.has(prop);
|
|
2518
2300
|
}
|
|
2519
|
-
|
|
2301
|
+
function isSelfProp(self, prop) {
|
|
2302
|
+
return prop in self;
|
|
2303
|
+
}
|
|
2304
|
+
const ARRAY_SIGNAL = Symbol('#signal');
|
|
2520
2305
|
const SOURCE = Symbol('#source');
|
|
2521
2306
|
const MUTATE = Symbol('#update');
|
|
2522
2307
|
const NOTIFY = Symbol('#notify');
|
|
2523
2308
|
const IS_COLLECTION = Symbol.for('Collection');
|
|
2524
2309
|
function notifyArray(arr) {
|
|
2525
|
-
addToTransaction(arr[
|
|
2526
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
2527
|
-
// eslint-disable-next-line
|
|
2528
|
-
dirtyTag(tagForProperty(arr, 'length'));
|
|
2529
|
-
// eslint-disable-next-line
|
|
2530
|
-
dirtyTag(tagForProperty(arr, '[]'));
|
|
2531
|
-
}
|
|
2310
|
+
addToTransaction(arr[ARRAY_SIGNAL]);
|
|
2532
2311
|
}
|
|
2533
2312
|
function convertToInt(prop) {
|
|
2534
2313
|
if (typeof prop === 'symbol') return null;
|
|
@@ -2536,29 +2315,6 @@ function convertToInt(prop) {
|
|
|
2536
2315
|
if (isNaN(num)) return null;
|
|
2537
2316
|
return num % 1 === 0 ? num : null;
|
|
2538
2317
|
}
|
|
2539
|
-
let Tag = (_class = class Tag {
|
|
2540
|
-
/*
|
|
2541
|
-
* whether this was part of a transaction when last mutated
|
|
2542
|
-
*/
|
|
2543
|
-
|
|
2544
|
-
constructor() {
|
|
2545
|
-
_initializerDefineProperty(this, "ref", _descriptor, this);
|
|
2546
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
2547
|
-
const [arr, prop] = arguments;
|
|
2548
|
-
this._debug_base = arr.constructor.name + ':' + String(arr.modelName);
|
|
2549
|
-
this._debug_prop = prop;
|
|
2550
|
-
}
|
|
2551
|
-
this.shouldReset = false;
|
|
2552
|
-
this.t = false;
|
|
2553
|
-
}
|
|
2554
|
-
}, _descriptor = _applyDecoratedDescriptor(_class.prototype, "ref", [tracked], {
|
|
2555
|
-
configurable: true,
|
|
2556
|
-
enumerable: true,
|
|
2557
|
-
writable: true,
|
|
2558
|
-
initializer: function () {
|
|
2559
|
-
return null;
|
|
2560
|
-
}
|
|
2561
|
-
}), _class);
|
|
2562
2318
|
function safeForEach(instance, arr, store, callback, target) {
|
|
2563
2319
|
if (target === undefined) {
|
|
2564
2320
|
target = null;
|
|
@@ -2591,7 +2347,7 @@ function safeForEach(instance, arr, store, callback, target) {
|
|
|
2591
2347
|
@class RecordArray
|
|
2592
2348
|
@public
|
|
2593
2349
|
*/
|
|
2594
|
-
let IdentifierArray = (
|
|
2350
|
+
let IdentifierArray = (_class = class IdentifierArray {
|
|
2595
2351
|
[NOTIFY]() {
|
|
2596
2352
|
notifyArray(this);
|
|
2597
2353
|
}
|
|
@@ -2619,14 +2375,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2619
2375
|
set length(value) {
|
|
2620
2376
|
this[SOURCE].length = value;
|
|
2621
2377
|
}
|
|
2622
|
-
|
|
2623
|
-
// here to support computed chains
|
|
2624
|
-
// and {{#each}}
|
|
2625
|
-
get '[]'() {
|
|
2626
|
-
if (macroCondition(getOwnConfig().deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
2627
|
-
return this;
|
|
2628
|
-
}
|
|
2629
|
-
}
|
|
2630
2378
|
constructor(options) {
|
|
2631
2379
|
/**
|
|
2632
2380
|
The flag to signal a `RecordArray` is currently loading data.
|
|
@@ -2641,7 +2389,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2641
2389
|
@public
|
|
2642
2390
|
@type Boolean
|
|
2643
2391
|
*/
|
|
2644
|
-
_initializerDefineProperty(this, "isUpdating", _descriptor2, this);
|
|
2645
2392
|
this.isLoaded = true;
|
|
2646
2393
|
this.isDestroying = false;
|
|
2647
2394
|
this.isDestroyed = false;
|
|
@@ -2649,16 +2396,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2649
2396
|
this[IS_COLLECTION] = true;
|
|
2650
2397
|
this[SOURCE] = void 0;
|
|
2651
2398
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2652
|
-
|
|
2399
|
+
const self = this;
|
|
2653
2400
|
this.modelName = options.type;
|
|
2654
2401
|
this.store = options.store;
|
|
2655
2402
|
this._manager = options.manager;
|
|
2656
2403
|
this[SOURCE] = options.identifiers;
|
|
2657
|
-
|
|
2658
|
-
this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
|
|
2404
|
+
this[ARRAY_SIGNAL] = createSignal(this, 'length');
|
|
2659
2405
|
const store = options.store;
|
|
2660
2406
|
const boundFns = new Map();
|
|
2661
|
-
const
|
|
2407
|
+
const _SIGNAL = this[ARRAY_SIGNAL];
|
|
2662
2408
|
const PrivateState = {
|
|
2663
2409
|
links: options.links || null,
|
|
2664
2410
|
meta: options.meta || null
|
|
@@ -2669,42 +2415,42 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2669
2415
|
// we track all mutations within the call
|
|
2670
2416
|
// and forward them as one
|
|
2671
2417
|
|
|
2672
|
-
const proxy = new
|
|
2418
|
+
const proxy = new NativeProxy(this[SOURCE], {
|
|
2673
2419
|
get(target, prop, receiver) {
|
|
2674
|
-
|
|
2675
|
-
if (
|
|
2420
|
+
const index = convertToInt(prop);
|
|
2421
|
+
if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
|
|
2676
2422
|
options.manager._syncArray(receiver);
|
|
2677
|
-
|
|
2678
|
-
|
|
2423
|
+
_SIGNAL.t = false;
|
|
2424
|
+
_SIGNAL.shouldReset = false;
|
|
2679
2425
|
}
|
|
2680
2426
|
if (index !== null) {
|
|
2681
2427
|
const identifier = target[index];
|
|
2682
2428
|
if (!transaction) {
|
|
2683
|
-
subscribe(
|
|
2429
|
+
subscribe(_SIGNAL);
|
|
2684
2430
|
}
|
|
2685
2431
|
return identifier && store._instanceCache.getRecord(identifier);
|
|
2686
2432
|
}
|
|
2687
|
-
if (prop === 'meta') return subscribe(
|
|
2688
|
-
if (prop === 'links') return subscribe(
|
|
2689
|
-
if (prop === '[]') return subscribe(
|
|
2433
|
+
if (prop === 'meta') return subscribe(_SIGNAL), PrivateState.meta;
|
|
2434
|
+
if (prop === 'links') return subscribe(_SIGNAL), PrivateState.links;
|
|
2435
|
+
if (prop === '[]') return subscribe(_SIGNAL), receiver;
|
|
2690
2436
|
if (isArrayGetter(prop)) {
|
|
2691
2437
|
let fn = boundFns.get(prop);
|
|
2692
2438
|
if (fn === undefined) {
|
|
2693
2439
|
if (prop === 'forEach') {
|
|
2694
2440
|
fn = function () {
|
|
2695
|
-
subscribe(
|
|
2441
|
+
subscribe(_SIGNAL);
|
|
2696
2442
|
transaction = true;
|
|
2697
|
-
|
|
2443
|
+
const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
|
|
2698
2444
|
transaction = false;
|
|
2699
2445
|
return result;
|
|
2700
2446
|
};
|
|
2701
2447
|
} else {
|
|
2702
2448
|
fn = function () {
|
|
2703
|
-
subscribe(
|
|
2449
|
+
subscribe(_SIGNAL);
|
|
2704
2450
|
// array functions must run through Reflect to work properly
|
|
2705
2451
|
// binding via other means will not work.
|
|
2706
2452
|
transaction = true;
|
|
2707
|
-
|
|
2453
|
+
const result = Reflect.apply(target[prop], receiver, arguments);
|
|
2708
2454
|
transaction = false;
|
|
2709
2455
|
return result;
|
|
2710
2456
|
};
|
|
@@ -2726,10 +2472,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2726
2472
|
const args = Array.prototype.slice.call(arguments);
|
|
2727
2473
|
assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
|
|
2728
2474
|
transaction = true;
|
|
2729
|
-
|
|
2730
|
-
self[MUTATE](prop, args, result);
|
|
2731
|
-
addToTransaction(_TAG);
|
|
2732
|
-
// TODO handle cache updates
|
|
2475
|
+
const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
|
|
2733
2476
|
transaction = false;
|
|
2734
2477
|
return result;
|
|
2735
2478
|
};
|
|
@@ -2737,16 +2480,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2737
2480
|
}
|
|
2738
2481
|
return fn;
|
|
2739
2482
|
}
|
|
2740
|
-
if (prop
|
|
2741
|
-
if (prop === NOTIFY || prop ===
|
|
2483
|
+
if (isSelfProp(self, prop)) {
|
|
2484
|
+
if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
|
|
2742
2485
|
return self[prop];
|
|
2743
2486
|
}
|
|
2744
2487
|
let fn = boundFns.get(prop);
|
|
2745
2488
|
if (fn) return fn;
|
|
2746
|
-
|
|
2489
|
+
const outcome = self[prop];
|
|
2747
2490
|
if (typeof outcome === 'function') {
|
|
2748
2491
|
fn = function () {
|
|
2749
|
-
subscribe(
|
|
2492
|
+
subscribe(_SIGNAL);
|
|
2750
2493
|
// array functions must run through Reflect to work properly
|
|
2751
2494
|
// binding via other means will not work.
|
|
2752
2495
|
return Reflect.apply(outcome, receiver, arguments);
|
|
@@ -2754,17 +2497,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2754
2497
|
boundFns.set(prop, fn);
|
|
2755
2498
|
return fn;
|
|
2756
2499
|
}
|
|
2757
|
-
return subscribe(
|
|
2500
|
+
return subscribe(_SIGNAL), outcome;
|
|
2758
2501
|
}
|
|
2759
2502
|
return target[prop];
|
|
2760
2503
|
},
|
|
2761
|
-
|
|
2504
|
+
// FIXME: Should this get a generic like get above?
|
|
2505
|
+
set(target, prop, value, receiver) {
|
|
2762
2506
|
if (prop === 'length') {
|
|
2763
2507
|
if (!transaction && value === 0) {
|
|
2764
2508
|
transaction = true;
|
|
2765
|
-
|
|
2766
|
-
Reflect.set(target, prop, value);
|
|
2767
|
-
self[MUTATE]('length 0', []);
|
|
2509
|
+
self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
|
|
2768
2510
|
transaction = false;
|
|
2769
2511
|
return true;
|
|
2770
2512
|
} else if (transaction) {
|
|
@@ -2781,9 +2523,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2781
2523
|
PrivateState.meta = value || null;
|
|
2782
2524
|
return true;
|
|
2783
2525
|
}
|
|
2784
|
-
|
|
2526
|
+
const index = convertToInt(prop);
|
|
2527
|
+
|
|
2528
|
+
// we do not allow "holey" arrays and so if the index is
|
|
2529
|
+
// greater than length then we will disallow setting it.
|
|
2530
|
+
// however, there is a special case for "unshift" with more than
|
|
2531
|
+
// one item being inserted since current items will be moved to the
|
|
2532
|
+
// new indices first.
|
|
2533
|
+
// we "loosely" detect this by just checking whether we are in
|
|
2534
|
+
// a transaction.
|
|
2785
2535
|
if (index === null || index > target.length) {
|
|
2786
|
-
if (
|
|
2536
|
+
if (index !== null && transaction) {
|
|
2537
|
+
const identifier = recordIdentifierFor(value);
|
|
2538
|
+
assert(`Cannot set index ${index} past the end of the array.`, isStableIdentifier(identifier));
|
|
2539
|
+
target[index] = identifier;
|
|
2540
|
+
return true;
|
|
2541
|
+
} else if (isSelfProp(self, prop)) {
|
|
2542
|
+
// @ts-expect-error not all properties are indeces and we can't safely cast
|
|
2787
2543
|
self[prop] = value;
|
|
2788
2544
|
return true;
|
|
2789
2545
|
}
|
|
@@ -2793,12 +2549,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2793
2549
|
assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
|
|
2794
2550
|
return false;
|
|
2795
2551
|
}
|
|
2796
|
-
|
|
2797
|
-
|
|
2552
|
+
const original = target[index];
|
|
2553
|
+
const newIdentifier = extractIdentifierFromRecord$1(value);
|
|
2798
2554
|
target[index] = newIdentifier;
|
|
2555
|
+
assert(`Expected a record`, isStableIdentifier(newIdentifier));
|
|
2556
|
+
// We generate "transactions" whenever a setter method on the array
|
|
2557
|
+
// is called and might bulk update multiple array cells. Fundamentally,
|
|
2558
|
+
// all array operations decompose into individual cell replacements.
|
|
2559
|
+
// e.g. a push is really a "replace cell at next index with new value"
|
|
2560
|
+
// or a splice is "shift all values left/right by X and set out of new
|
|
2561
|
+
// bounds cells to undefined"
|
|
2562
|
+
//
|
|
2563
|
+
// so, if we are in a transaction, then this is not a user generated change
|
|
2564
|
+
// but one generated by a setter method. In this case we want to only apply
|
|
2565
|
+
// the change to the target array and not call the MUTATE method.
|
|
2566
|
+
// If there is no transaction though, then this means the user themselves has
|
|
2567
|
+
// directly changed the value of a specific index and we need to thus generate
|
|
2568
|
+
// a mutation for that change.
|
|
2569
|
+
// e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
|
|
2570
|
+
// a transaction.
|
|
2571
|
+
// while "arr[arr.length] = newVal;" is handled by this replace cell code path.
|
|
2799
2572
|
if (!transaction) {
|
|
2800
|
-
self[MUTATE]('replace cell', [index, original, newIdentifier]);
|
|
2801
|
-
|
|
2573
|
+
self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
|
|
2574
|
+
} else {
|
|
2575
|
+
target[index] = newIdentifier;
|
|
2802
2576
|
}
|
|
2803
2577
|
return true;
|
|
2804
2578
|
},
|
|
@@ -2813,12 +2587,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2813
2587
|
return IdentifierArray.prototype;
|
|
2814
2588
|
}
|
|
2815
2589
|
});
|
|
2816
|
-
|
|
2817
|
-
const meta = Ember.meta(this);
|
|
2818
|
-
meta.hasMixin = mixin => {
|
|
2819
|
-
assert(`Do not call A() on EmberData RecordArrays`);
|
|
2820
|
-
};
|
|
2821
|
-
}
|
|
2590
|
+
createArrayTags(proxy, _SIGNAL);
|
|
2822
2591
|
this[NOTIFY] = this[NOTIFY].bind(proxy);
|
|
2823
2592
|
return proxy;
|
|
2824
2593
|
}
|
|
@@ -2843,7 +2612,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2843
2612
|
return this._updatingPromise;
|
|
2844
2613
|
}
|
|
2845
2614
|
this.isUpdating = true;
|
|
2846
|
-
|
|
2615
|
+
const updatingPromise = this._update();
|
|
2847
2616
|
void updatingPromise.finally(() => {
|
|
2848
2617
|
this._updatingPromise = null;
|
|
2849
2618
|
if (this.isDestroying || this.isDestroyed) {
|
|
@@ -2861,6 +2630,10 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2861
2630
|
*/
|
|
2862
2631
|
_update() {
|
|
2863
2632
|
assert(`_update cannot be used with this array`, this.modelName);
|
|
2633
|
+
// @ts-expect-error typescript is unable to handle the complexity of
|
|
2634
|
+
// T = unknown, modelName = string
|
|
2635
|
+
// T extends TypedRecordInstance, modelName = TypeFromInstance<T>
|
|
2636
|
+
// both being valid options to pass through here.
|
|
2864
2637
|
return this.store.findAll(this.modelName, {
|
|
2865
2638
|
reload: true
|
|
2866
2639
|
});
|
|
@@ -2882,17 +2655,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2882
2655
|
@return {Promise<IdentifierArray>} promise
|
|
2883
2656
|
*/
|
|
2884
2657
|
save() {
|
|
2885
|
-
|
|
2658
|
+
const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
|
|
2886
2659
|
return promise;
|
|
2887
2660
|
}
|
|
2888
|
-
},
|
|
2889
|
-
|
|
2661
|
+
}, _applyDecoratedDescriptor(_class.prototype, "length", [compat], Object.getOwnPropertyDescriptor(_class.prototype, "length"), _class.prototype), _class); // this will error if someone tries to call
|
|
2662
|
+
// A(identifierArray) since it is not configurable
|
|
2663
|
+
// which is preferable to the `meta` override we used
|
|
2664
|
+
// before which required importing all of Ember
|
|
2665
|
+
const desc = {
|
|
2890
2666
|
enumerable: true,
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
return
|
|
2667
|
+
configurable: false,
|
|
2668
|
+
get: function () {
|
|
2669
|
+
return this;
|
|
2894
2670
|
}
|
|
2895
|
-
}
|
|
2671
|
+
};
|
|
2672
|
+
compat(desc);
|
|
2673
|
+
Object.defineProperty(IdentifierArray.prototype, '[]', desc);
|
|
2674
|
+
defineSignal(IdentifierArray.prototype, 'isUpdating', false);
|
|
2896
2675
|
class Collection extends IdentifierArray {
|
|
2897
2676
|
constructor(options) {
|
|
2898
2677
|
super(options);
|
|
@@ -2909,6 +2688,10 @@ class Collection extends IdentifierArray {
|
|
|
2909
2688
|
// TODO save options from initial request?
|
|
2910
2689
|
assert(`update cannot be used with this array`, this.modelName);
|
|
2911
2690
|
assert(`update cannot be used with no query`, query);
|
|
2691
|
+
// @ts-expect-error typescript is unable to handle the complexity of
|
|
2692
|
+
// T = unknown, modelName = string
|
|
2693
|
+
// T extends TypedRecordInstance, modelName = TypeFromInstance<T>
|
|
2694
|
+
// both being valid options to pass through here.
|
|
2912
2695
|
const promise = store.query(this.modelName, query, {
|
|
2913
2696
|
_recordArray: this
|
|
2914
2697
|
});
|
|
@@ -2924,7 +2707,8 @@ class Collection extends IdentifierArray {
|
|
|
2924
2707
|
Collection.prototype.query = null;
|
|
2925
2708
|
|
|
2926
2709
|
// Ensure instanceof works correctly
|
|
2927
|
-
//Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2710
|
+
// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2711
|
+
|
|
2928
2712
|
function assertRecordPassedToHasMany(record) {
|
|
2929
2713
|
assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
|
|
2930
2714
|
try {
|
|
@@ -2946,7 +2730,6 @@ function extractIdentifierFromRecord$1(record) {
|
|
|
2946
2730
|
/**
|
|
2947
2731
|
@module @ember-data/store
|
|
2948
2732
|
*/
|
|
2949
|
-
|
|
2950
2733
|
const FAKE_ARR = {};
|
|
2951
2734
|
const SLICE_BATCH_SIZE = 1200;
|
|
2952
2735
|
/**
|
|
@@ -2989,7 +2772,7 @@ const SLICE_BATCH_SIZE = 1200;
|
|
|
2989
2772
|
*/
|
|
2990
2773
|
function fastPush(target, source) {
|
|
2991
2774
|
let startLength = 0;
|
|
2992
|
-
|
|
2775
|
+
const newLength = source.length;
|
|
2993
2776
|
while (newLength - startLength > SLICE_BATCH_SIZE) {
|
|
2994
2777
|
// eslint-disable-next-line prefer-spread
|
|
2995
2778
|
target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
|
|
@@ -3046,8 +2829,8 @@ class RecordArrayManager {
|
|
|
3046
2829
|
*/
|
|
3047
2830
|
liveArrayFor(type) {
|
|
3048
2831
|
let array = this._live.get(type);
|
|
3049
|
-
|
|
3050
|
-
|
|
2832
|
+
const identifiers = [];
|
|
2833
|
+
const staged = this._staged.get(type);
|
|
3051
2834
|
if (staged) {
|
|
3052
2835
|
staged.forEach((value, key) => {
|
|
3053
2836
|
if (value === 'add') {
|
|
@@ -3070,7 +2853,7 @@ class RecordArrayManager {
|
|
|
3070
2853
|
return array;
|
|
3071
2854
|
}
|
|
3072
2855
|
createArray(config) {
|
|
3073
|
-
|
|
2856
|
+
const options = {
|
|
3074
2857
|
type: config.type,
|
|
3075
2858
|
links: config.doc?.links || null,
|
|
3076
2859
|
meta: config.doc?.meta || null,
|
|
@@ -3081,7 +2864,7 @@ class RecordArrayManager {
|
|
|
3081
2864
|
store: this.store,
|
|
3082
2865
|
manager: this
|
|
3083
2866
|
};
|
|
3084
|
-
|
|
2867
|
+
const array = new Collection(options);
|
|
3085
2868
|
this._managed.add(array);
|
|
3086
2869
|
this._set.set(array, new Set(options.identifiers || []));
|
|
3087
2870
|
if (config.identifiers) {
|
|
@@ -3093,7 +2876,7 @@ class RecordArrayManager {
|
|
|
3093
2876
|
if (array === FAKE_ARR) {
|
|
3094
2877
|
return;
|
|
3095
2878
|
}
|
|
3096
|
-
|
|
2879
|
+
const tag = array[ARRAY_SIGNAL];
|
|
3097
2880
|
if (!tag.shouldReset) {
|
|
3098
2881
|
tag.shouldReset = true;
|
|
3099
2882
|
addTransactionCB(array[NOTIFY]);
|
|
@@ -3105,11 +2888,11 @@ class RecordArrayManager {
|
|
|
3105
2888
|
if (this.isDestroying || this.isDestroyed) {
|
|
3106
2889
|
return;
|
|
3107
2890
|
}
|
|
3108
|
-
|
|
2891
|
+
const liveArray = this._live.get(identifier.type);
|
|
3109
2892
|
const allPending = this._pending;
|
|
3110
|
-
|
|
2893
|
+
const pending = new Map();
|
|
3111
2894
|
if (includeManaged) {
|
|
3112
|
-
|
|
2895
|
+
const managed = this._identifiers.get(identifier);
|
|
3113
2896
|
if (managed) {
|
|
3114
2897
|
managed.forEach(arr => {
|
|
3115
2898
|
let changes = allPending.get(arr);
|
|
@@ -3164,10 +2947,10 @@ class RecordArrayManager {
|
|
|
3164
2947
|
associate(this._identifiers, array, identifiers);
|
|
3165
2948
|
}
|
|
3166
2949
|
identifierAdded(identifier) {
|
|
3167
|
-
|
|
2950
|
+
const changeSets = this._getPendingFor(identifier, false);
|
|
3168
2951
|
if (changeSets) {
|
|
3169
2952
|
changeSets.forEach((changes, array) => {
|
|
3170
|
-
|
|
2953
|
+
const existing = changes.get(identifier);
|
|
3171
2954
|
if (existing === 'del') {
|
|
3172
2955
|
changes.delete(identifier);
|
|
3173
2956
|
} else {
|
|
@@ -3178,10 +2961,10 @@ class RecordArrayManager {
|
|
|
3178
2961
|
}
|
|
3179
2962
|
}
|
|
3180
2963
|
identifierRemoved(identifier) {
|
|
3181
|
-
|
|
2964
|
+
const changeSets = this._getPendingFor(identifier, true, true);
|
|
3182
2965
|
if (changeSets) {
|
|
3183
2966
|
changeSets.forEach((changes, array) => {
|
|
3184
|
-
|
|
2967
|
+
const existing = changes.get(identifier);
|
|
3185
2968
|
if (existing === 'add') {
|
|
3186
2969
|
changes.delete(identifier);
|
|
3187
2970
|
} else {
|
|
@@ -3192,7 +2975,7 @@ class RecordArrayManager {
|
|
|
3192
2975
|
}
|
|
3193
2976
|
}
|
|
3194
2977
|
identifierChanged(identifier) {
|
|
3195
|
-
|
|
2978
|
+
const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
|
|
3196
2979
|
|
|
3197
2980
|
// if the change matches the most recent direct added/removed
|
|
3198
2981
|
// state, then we can ignore it
|
|
@@ -3219,13 +3002,12 @@ class RecordArrayManager {
|
|
|
3219
3002
|
this.clear(false);
|
|
3220
3003
|
this._live.clear();
|
|
3221
3004
|
this.isDestroyed = true;
|
|
3222
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
3223
3005
|
this.store.notifications.unsubscribe(this._subscription);
|
|
3224
3006
|
}
|
|
3225
3007
|
}
|
|
3226
3008
|
function associate(ArraysCache, array, identifiers) {
|
|
3227
3009
|
for (let i = 0; i < identifiers.length; i++) {
|
|
3228
|
-
|
|
3010
|
+
const identifier = identifiers[i];
|
|
3229
3011
|
let cache = ArraysCache.get(identifier);
|
|
3230
3012
|
if (!cache) {
|
|
3231
3013
|
cache = new Set();
|
|
@@ -3240,13 +3022,13 @@ function disassociate(ArraysCache, array, identifiers) {
|
|
|
3240
3022
|
}
|
|
3241
3023
|
}
|
|
3242
3024
|
function disassociateIdentifier(ArraysCache, array, identifier) {
|
|
3243
|
-
|
|
3025
|
+
const cache = ArraysCache.get(identifier);
|
|
3244
3026
|
if (cache) {
|
|
3245
3027
|
cache.delete(array);
|
|
3246
3028
|
}
|
|
3247
3029
|
}
|
|
3248
3030
|
function sync(array, changes, arraySet) {
|
|
3249
|
-
|
|
3031
|
+
const state = array[SOURCE];
|
|
3250
3032
|
const adds = [];
|
|
3251
3033
|
const removes = [];
|
|
3252
3034
|
changes.forEach((value, key) => {
|
|
@@ -3260,13 +3042,13 @@ function sync(array, changes, arraySet) {
|
|
|
3260
3042
|
} else {
|
|
3261
3043
|
if (arraySet.has(key)) {
|
|
3262
3044
|
removes.push(key);
|
|
3045
|
+
arraySet.delete(key);
|
|
3263
3046
|
}
|
|
3264
3047
|
}
|
|
3265
3048
|
});
|
|
3266
3049
|
if (removes.length) {
|
|
3267
3050
|
if (removes.length === state.length) {
|
|
3268
3051
|
state.length = 0;
|
|
3269
|
-
arraySet.clear();
|
|
3270
3052
|
// changing the reference breaks the Proxy
|
|
3271
3053
|
// state = array[SOURCE] = [];
|
|
3272
3054
|
} else {
|
|
@@ -3293,6 +3075,9 @@ function sync(array, changes, arraySet) {
|
|
|
3293
3075
|
}
|
|
3294
3076
|
}
|
|
3295
3077
|
|
|
3078
|
+
/**
|
|
3079
|
+
* @module @ember-data/store
|
|
3080
|
+
*/
|
|
3296
3081
|
const Touching = Symbol('touching');
|
|
3297
3082
|
const RequestPromise = Symbol('promise');
|
|
3298
3083
|
const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
|
|
@@ -3320,14 +3105,14 @@ class RequestStateService {
|
|
|
3320
3105
|
this._done.delete(identifier);
|
|
3321
3106
|
}
|
|
3322
3107
|
_enqueue(promise, queryRequest) {
|
|
3323
|
-
|
|
3108
|
+
const query = queryRequest.data[0];
|
|
3324
3109
|
if (hasRecordIdentifier(query)) {
|
|
3325
3110
|
const identifier = query.recordIdentifier;
|
|
3326
|
-
|
|
3111
|
+
const type = query.op === 'saveRecord' ? 'mutation' : 'query';
|
|
3327
3112
|
if (!this._pending.has(identifier)) {
|
|
3328
3113
|
this._pending.set(identifier, []);
|
|
3329
3114
|
}
|
|
3330
|
-
|
|
3115
|
+
const request = {
|
|
3331
3116
|
state: 'pending',
|
|
3332
3117
|
request: queryRequest,
|
|
3333
3118
|
type
|
|
@@ -3338,7 +3123,7 @@ class RequestStateService {
|
|
|
3338
3123
|
this._triggerSubscriptions(request);
|
|
3339
3124
|
return promise.then(result => {
|
|
3340
3125
|
this._dequeue(identifier, request);
|
|
3341
|
-
|
|
3126
|
+
const finalizedRequest = {
|
|
3342
3127
|
state: 'fulfilled',
|
|
3343
3128
|
request: queryRequest,
|
|
3344
3129
|
type,
|
|
@@ -3352,7 +3137,7 @@ class RequestStateService {
|
|
|
3352
3137
|
return result;
|
|
3353
3138
|
}, error => {
|
|
3354
3139
|
this._dequeue(identifier, request);
|
|
3355
|
-
|
|
3140
|
+
const finalizedRequest = {
|
|
3356
3141
|
state: 'rejected',
|
|
3357
3142
|
request: queryRequest,
|
|
3358
3143
|
type,
|
|
@@ -3401,7 +3186,7 @@ class RequestStateService {
|
|
|
3401
3186
|
_addDone(request) {
|
|
3402
3187
|
request[Touching].forEach(identifier => {
|
|
3403
3188
|
// TODO add support for multiple
|
|
3404
|
-
|
|
3189
|
+
const requestDataOp = request.request.data[0].op;
|
|
3405
3190
|
let requests = this._done.get(identifier);
|
|
3406
3191
|
if (requests) {
|
|
3407
3192
|
requests = requests.filter(req => {
|
|
@@ -3465,7 +3250,7 @@ class RequestStateService {
|
|
|
3465
3250
|
* @method getPendingRequestsForRecord
|
|
3466
3251
|
* @public
|
|
3467
3252
|
* @param {StableRecordIdentifier} identifier
|
|
3468
|
-
* @
|
|
3253
|
+
* @return {RequestState[]} an array of request states for any pending requests for the given identifier
|
|
3469
3254
|
*/
|
|
3470
3255
|
getPendingRequestsForRecord(identifier) {
|
|
3471
3256
|
return this._pending.get(identifier) || EMPTY_ARR;
|
|
@@ -3477,10 +3262,10 @@ class RequestStateService {
|
|
|
3477
3262
|
* @method getLastRequestForRecord
|
|
3478
3263
|
* @public
|
|
3479
3264
|
* @param {StableRecordIdentifier} identifier
|
|
3480
|
-
* @
|
|
3265
|
+
* @return {RequestState | null} the state of the most recent request for the given identifier
|
|
3481
3266
|
*/
|
|
3482
3267
|
getLastRequestForRecord(identifier) {
|
|
3483
|
-
|
|
3268
|
+
const requests = this._done.get(identifier);
|
|
3484
3269
|
if (requests) {
|
|
3485
3270
|
return requests[requests.length - 1];
|
|
3486
3271
|
}
|
|
@@ -3492,7 +3277,7 @@ function isNonEmptyString(str) {
|
|
|
3492
3277
|
}
|
|
3493
3278
|
function constructResource(type, id, lid) {
|
|
3494
3279
|
if (typeof type === 'object' && type !== null) {
|
|
3495
|
-
|
|
3280
|
+
const resource = type;
|
|
3496
3281
|
if (isStableIdentifier(resource)) {
|
|
3497
3282
|
return resource;
|
|
3498
3283
|
}
|
|
@@ -3527,6 +3312,29 @@ function constructResource(type, id, lid) {
|
|
|
3527
3312
|
}
|
|
3528
3313
|
}
|
|
3529
3314
|
|
|
3315
|
+
/**
|
|
3316
|
+
@module @ember-data/store
|
|
3317
|
+
*/
|
|
3318
|
+
// this import location is deprecated but breaks in 4.8 and older
|
|
3319
|
+
|
|
3320
|
+
/**
|
|
3321
|
+
* Currently only records that extend object can be created via
|
|
3322
|
+
* store.createRecord. This is a limitation of the current API,
|
|
3323
|
+
* but can be worked around by creating a new identifier, running
|
|
3324
|
+
* the cache.clientDidCreate method, and then peeking the record
|
|
3325
|
+
* for the identifier.
|
|
3326
|
+
*
|
|
3327
|
+
* To assign primary key to a record during creation, only `id` will
|
|
3328
|
+
* work correctly for `store.createRecord`, other primary key may be
|
|
3329
|
+
* handled by updating the record after creation or using the flow
|
|
3330
|
+
* described above.
|
|
3331
|
+
*
|
|
3332
|
+
* TODO: These are limitations we want to (and can) address. If you
|
|
3333
|
+
* have need of lifting these limitations, please open an issue.
|
|
3334
|
+
*
|
|
3335
|
+
* @typedoc
|
|
3336
|
+
*/
|
|
3337
|
+
|
|
3530
3338
|
/**
|
|
3531
3339
|
* A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
|
|
3532
3340
|
* and sources of data (such as your API or a local persistence layer)
|
|
@@ -3545,7 +3353,10 @@ function constructResource(type, id, lid) {
|
|
|
3545
3353
|
|
|
3546
3354
|
@class Store
|
|
3547
3355
|
@public
|
|
3548
|
-
*/
|
|
3356
|
+
*/
|
|
3357
|
+
|
|
3358
|
+
// @ts-expect-error
|
|
3359
|
+
|
|
3549
3360
|
class Store extends EmberObject {
|
|
3550
3361
|
/**
|
|
3551
3362
|
* Provides access to the NotificationManager associated
|
|
@@ -3677,8 +3488,6 @@ class Store extends EmberObject {
|
|
|
3677
3488
|
// private
|
|
3678
3489
|
this._requestCache = new RequestStateService(this);
|
|
3679
3490
|
this._instanceCache = new InstanceCache(this);
|
|
3680
|
-
this._adapterCache = Object.create(null);
|
|
3681
|
-
this._serializerCache = Object.create(null);
|
|
3682
3491
|
this._documentCache = new Map();
|
|
3683
3492
|
this.isDestroying = false;
|
|
3684
3493
|
this.isDestroyed = false;
|
|
@@ -3736,7 +3545,7 @@ class Store extends EmberObject {
|
|
|
3736
3545
|
* that have been initiated for a given identifier.
|
|
3737
3546
|
*
|
|
3738
3547
|
* @method getRequestStateService
|
|
3739
|
-
* @
|
|
3548
|
+
* @return {RequestStateService}
|
|
3740
3549
|
* @public
|
|
3741
3550
|
*/
|
|
3742
3551
|
getRequestStateService() {
|
|
@@ -3761,10 +3570,11 @@ class Store extends EmberObject {
|
|
|
3761
3570
|
* inserting the response into the cache and handing
|
|
3762
3571
|
* back a Future which resolves to a ResponseDocument
|
|
3763
3572
|
*
|
|
3764
|
-
*
|
|
3573
|
+
* ## Cache Keys
|
|
3765
3574
|
*
|
|
3766
|
-
* Only GET requests
|
|
3767
|
-
*
|
|
3575
|
+
* Only GET requests with a url or requests with an explicit
|
|
3576
|
+
* cache key (`cacheOptions.key`) will have the request result
|
|
3577
|
+
* and document cached.
|
|
3768
3578
|
*
|
|
3769
3579
|
* The cache key used is `requestConfig.cacheOptions.key`
|
|
3770
3580
|
* if present, falling back to `requestconfig.url`.
|
|
@@ -3775,16 +3585,44 @@ class Store extends EmberObject {
|
|
|
3775
3585
|
* via the `POST` method `requestConfig.cacheOptions.key`
|
|
3776
3586
|
* MUST be supplied for the document to be cached.
|
|
3777
3587
|
*
|
|
3588
|
+
* ## Requesting Without a Cache Key
|
|
3589
|
+
*
|
|
3590
|
+
* Resource data within the request is always updated in the cache,
|
|
3591
|
+
* regardless of whether a cache key is present for the request.
|
|
3592
|
+
*
|
|
3593
|
+
* ## Fulfilling From Cache
|
|
3594
|
+
*
|
|
3595
|
+
* When a cache-key is determined, the request may fulfill
|
|
3596
|
+
* from cache provided the cache is not stale.
|
|
3597
|
+
*
|
|
3598
|
+
* Cache staleness is determined by the configured LifetimesService
|
|
3599
|
+
* with priority given to the `cacheOptions.reload` and
|
|
3600
|
+
* `cacheOptions.backgroundReload` on the request if present.
|
|
3601
|
+
*
|
|
3602
|
+
* If the cache data has soft expired or the request asks for a background
|
|
3603
|
+
* reload, the request will fulfill from cache if possible and
|
|
3604
|
+
* make a non-blocking request in the background to update the cache.
|
|
3605
|
+
*
|
|
3606
|
+
* If the cache data has hard expired or the request asks for a reload,
|
|
3607
|
+
* the request will not fulfill from cache and will make a blocking
|
|
3608
|
+
* request to update the cache.
|
|
3609
|
+
*
|
|
3610
|
+
* ## The Response
|
|
3611
|
+
*
|
|
3612
|
+
* The primary difference between `requestManager.request` and `store.request`
|
|
3613
|
+
* is that `store.request` will attempt to hydrate the response content into
|
|
3614
|
+
* a response Document containing RecordInstances.
|
|
3615
|
+
*
|
|
3778
3616
|
* @method request
|
|
3779
3617
|
* @param {StoreRequestInput} requestConfig
|
|
3780
|
-
* @
|
|
3618
|
+
* @return {Future}
|
|
3781
3619
|
* @public
|
|
3782
3620
|
*/
|
|
3783
3621
|
request(requestConfig) {
|
|
3784
3622
|
// we lazily set the cache handler when we issue the first request
|
|
3785
3623
|
// because constructor doesn't allow for this to run after
|
|
3786
3624
|
// the user has had the chance to set the prop.
|
|
3787
|
-
|
|
3625
|
+
const opts = {
|
|
3788
3626
|
store: this,
|
|
3789
3627
|
[EnableHydration]: true
|
|
3790
3628
|
};
|
|
@@ -3836,7 +3674,7 @@ class Store extends EmberObject {
|
|
|
3836
3674
|
* @param createRecordArgs
|
|
3837
3675
|
* @param recordDataFor deprecated use this.cache
|
|
3838
3676
|
* @param notificationManager deprecated use this.notifications
|
|
3839
|
-
* @
|
|
3677
|
+
* @return A record instance
|
|
3840
3678
|
* @public
|
|
3841
3679
|
*/
|
|
3842
3680
|
|
|
@@ -3979,7 +3817,7 @@ class Store extends EmberObject {
|
|
|
3979
3817
|
}
|
|
3980
3818
|
|
|
3981
3819
|
/**
|
|
3982
|
-
Returns the schema for a particular
|
|
3820
|
+
Returns the schema for a particular resource type (modelName).
|
|
3983
3821
|
When used with Model from @ember-data/model the return is the model class,
|
|
3984
3822
|
but this is not guaranteed.
|
|
3985
3823
|
If looking to query attribute or relationship information it is
|
|
@@ -3993,7 +3831,7 @@ class Store extends EmberObject {
|
|
|
3993
3831
|
for example.
|
|
3994
3832
|
@method modelFor
|
|
3995
3833
|
@public
|
|
3996
|
-
@param {
|
|
3834
|
+
@param {string} type
|
|
3997
3835
|
@return {ModelSchema}
|
|
3998
3836
|
*/
|
|
3999
3837
|
// TODO @deprecate in favor of schema APIs, requires adapter/serializer overhaul or replacement
|
|
@@ -4026,17 +3864,18 @@ class Store extends EmberObject {
|
|
|
4026
3864
|
```
|
|
4027
3865
|
@method createRecord
|
|
4028
3866
|
@public
|
|
4029
|
-
@param {String}
|
|
3867
|
+
@param {String} type the name of the resource
|
|
4030
3868
|
@param {Object} inputProperties a hash of properties to set on the
|
|
4031
3869
|
newly created record.
|
|
4032
3870
|
@return {Model} record
|
|
4033
3871
|
*/
|
|
4034
|
-
|
|
3872
|
+
|
|
3873
|
+
createRecord(type, inputProperties) {
|
|
4035
3874
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4036
3875
|
assertDestroyingStore(this, 'createRecord');
|
|
4037
3876
|
}
|
|
4038
|
-
assert(`You need to pass a model name to the store's createRecord method`,
|
|
4039
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${
|
|
3877
|
+
assert(`You need to pass a model name to the store's createRecord method`, type);
|
|
3878
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4040
3879
|
|
|
4041
3880
|
// This is wrapped in a `run.join` so that in test environments users do not need to manually wrap
|
|
4042
3881
|
// calls to `createRecord`. The run loop usage here is because we batch the joining and updating
|
|
@@ -4044,43 +3883,40 @@ class Store extends EmberObject {
|
|
|
4044
3883
|
//
|
|
4045
3884
|
// to remove this, we would need to move to a new `async` API.
|
|
4046
3885
|
let record;
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
};
|
|
4053
|
-
|
|
4054
|
-
// If the passed properties do not include a primary key,
|
|
4055
|
-
// give the adapter an opportunity to generate one. Typically,
|
|
4056
|
-
// client-side ID generators will use something like uuid.js
|
|
4057
|
-
// to avoid conflicts.
|
|
4058
|
-
|
|
4059
|
-
if (properties.id === null || properties.id === undefined) {
|
|
4060
|
-
let adapter = this.adapterFor(modelName);
|
|
4061
|
-
if (adapter && adapter.generateIdForRecord) {
|
|
4062
|
-
properties.id = adapter.generateIdForRecord(this, modelName, properties);
|
|
4063
|
-
} else {
|
|
4064
|
-
properties.id = null;
|
|
4065
|
-
}
|
|
4066
|
-
}
|
|
3886
|
+
this._join(() => {
|
|
3887
|
+
const normalizedModelName = normalizeModelName(type);
|
|
3888
|
+
const properties = {
|
|
3889
|
+
...inputProperties
|
|
3890
|
+
};
|
|
4067
3891
|
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
3892
|
+
// If the passed properties do not include a primary key,
|
|
3893
|
+
// give the adapter an opportunity to generate one. Typically,
|
|
3894
|
+
// client-side ID generators will use something like uuid.js
|
|
3895
|
+
// to avoid conflicts.
|
|
3896
|
+
let id = null;
|
|
3897
|
+
if (properties.id === null || properties.id === undefined) {
|
|
3898
|
+
const adapter = this.adapterFor?.(normalizedModelName, true);
|
|
3899
|
+
if (adapter && adapter.generateIdForRecord) {
|
|
3900
|
+
id = properties.id = coerceId(adapter.generateIdForRecord(this, normalizedModelName, properties));
|
|
3901
|
+
} else {
|
|
3902
|
+
id = properties.id = null;
|
|
4077
3903
|
}
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
3904
|
+
} else {
|
|
3905
|
+
id = properties.id = coerceId(properties.id);
|
|
3906
|
+
}
|
|
3907
|
+
const resource = {
|
|
3908
|
+
type: normalizedModelName,
|
|
3909
|
+
id
|
|
3910
|
+
};
|
|
3911
|
+
if (resource.id) {
|
|
3912
|
+
const identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
3913
|
+
assert(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`, !identifier);
|
|
3914
|
+
}
|
|
3915
|
+
const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
|
|
3916
|
+
const cache = this.cache;
|
|
3917
|
+
const createOptions = normalizeProperties(this, identifier, properties);
|
|
3918
|
+
const resultProps = cache.clientDidCreate(identifier, createOptions);
|
|
3919
|
+
record = this._instanceCache.getRecord(identifier, resultProps);
|
|
4084
3920
|
});
|
|
4085
3921
|
return record;
|
|
4086
3922
|
}
|
|
@@ -4096,7 +3932,7 @@ class Store extends EmberObject {
|
|
|
4096
3932
|
```
|
|
4097
3933
|
@method deleteRecord
|
|
4098
3934
|
@public
|
|
4099
|
-
@param {
|
|
3935
|
+
@param {unknown} record
|
|
4100
3936
|
*/
|
|
4101
3937
|
deleteRecord(record) {
|
|
4102
3938
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
@@ -4108,9 +3944,7 @@ class Store extends EmberObject {
|
|
|
4108
3944
|
this._join(() => {
|
|
4109
3945
|
cache.setIsDeleted(identifier, true);
|
|
4110
3946
|
if (cache.isNew(identifier)) {
|
|
4111
|
-
|
|
4112
|
-
this._instanceCache.unloadRecord(identifier);
|
|
4113
|
-
});
|
|
3947
|
+
this._instanceCache.unloadRecord(identifier);
|
|
4114
3948
|
}
|
|
4115
3949
|
});
|
|
4116
3950
|
}
|
|
@@ -4192,8 +4026,7 @@ class Store extends EmberObject {
|
|
|
4192
4026
|
In your adapter you can then access this id without triggering a network request via the
|
|
4193
4027
|
snapshot:
|
|
4194
4028
|
```app/adapters/application.js
|
|
4195
|
-
|
|
4196
|
-
export default class Adapter extends EmberObject {
|
|
4029
|
+
export default class Adapter {
|
|
4197
4030
|
findRecord(store, schema, id, snapshot) {
|
|
4198
4031
|
let type = schema.modelName;
|
|
4199
4032
|
if (type === 'comment')
|
|
@@ -4202,6 +4035,9 @@ class Store extends EmberObject {
|
|
|
4202
4035
|
.then(response => response.json())
|
|
4203
4036
|
}
|
|
4204
4037
|
}
|
|
4038
|
+
static create() {
|
|
4039
|
+
return new this();
|
|
4040
|
+
}
|
|
4205
4041
|
}
|
|
4206
4042
|
```
|
|
4207
4043
|
This could also be achieved by supplying the post id to the adapter via the adapterOptions
|
|
@@ -4215,9 +4051,8 @@ class Store extends EmberObject {
|
|
|
4215
4051
|
}
|
|
4216
4052
|
```
|
|
4217
4053
|
```app/adapters/application.js
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
findRecord(store, schema, id, snapshot) {
|
|
4054
|
+
export default class Adapter {
|
|
4055
|
+
findRecord(store, schema, id, snapshot) {
|
|
4221
4056
|
let type = schema.modelName;
|
|
4222
4057
|
if (type === 'comment')
|
|
4223
4058
|
let postId = snapshot.adapterOptions.post;
|
|
@@ -4225,6 +4060,9 @@ class Store extends EmberObject {
|
|
|
4225
4060
|
.then(response => response.json())
|
|
4226
4061
|
}
|
|
4227
4062
|
}
|
|
4063
|
+
static create() {
|
|
4064
|
+
return new this();
|
|
4065
|
+
}
|
|
4228
4066
|
}
|
|
4229
4067
|
```
|
|
4230
4068
|
If you have access to the post model you can also pass the model itself to preload:
|
|
@@ -4351,9 +4189,8 @@ class Store extends EmberObject {
|
|
|
4351
4189
|
}
|
|
4352
4190
|
```
|
|
4353
4191
|
```app/adapters/application.js
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
findRecord(store, schema, id, snapshot) {
|
|
4192
|
+
export default class Adapter {
|
|
4193
|
+
findRecord(store, schema, id, snapshot) {
|
|
4357
4194
|
let type = schema.modelName;
|
|
4358
4195
|
if (type === 'post')
|
|
4359
4196
|
let includes = snapshot.adapterOptions.include;
|
|
@@ -4361,6 +4198,9 @@ class Store extends EmberObject {
|
|
|
4361
4198
|
.then(response => response.json())
|
|
4362
4199
|
}
|
|
4363
4200
|
}
|
|
4201
|
+
static create() {
|
|
4202
|
+
return new this();
|
|
4203
|
+
}
|
|
4364
4204
|
}
|
|
4365
4205
|
```
|
|
4366
4206
|
In this case, the post's comments would then be available in your template as
|
|
@@ -4414,7 +4254,7 @@ class Store extends EmberObject {
|
|
|
4414
4254
|
@since 1.13.0
|
|
4415
4255
|
@method findRecord
|
|
4416
4256
|
@public
|
|
4417
|
-
@param {String|object}
|
|
4257
|
+
@param {String|object} type - either a string representing the name of the resource or a ResourceIdentifier object containing both the type (a string) and the id (a string) for the record or an lid (a string) of an existing record
|
|
4418
4258
|
@param {(String|Integer|Object)} id - optional object with options for the request only if the first param is a ResourceIdentifier, else the string id of the record to be retrieved
|
|
4419
4259
|
@param {Object} [options] - if the first param is a string this will be the optional options for the request. See examples for available options.
|
|
4420
4260
|
@return {Promise} promise
|
|
@@ -4504,7 +4344,7 @@ class Store extends EmberObject {
|
|
|
4504
4344
|
resourceIdentifier = constructResource(type, normalizedId);
|
|
4505
4345
|
}
|
|
4506
4346
|
assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
|
|
4507
|
-
|
|
4347
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
4508
4348
|
return this._instanceCache.getReference(identifier);
|
|
4509
4349
|
}
|
|
4510
4350
|
|
|
@@ -4599,24 +4439,25 @@ class Store extends EmberObject {
|
|
|
4599
4439
|
@since 1.13.0
|
|
4600
4440
|
@method query
|
|
4601
4441
|
@public
|
|
4602
|
-
@param {String}
|
|
4603
|
-
@param {
|
|
4442
|
+
@param {String} type the name of the resource
|
|
4443
|
+
@param {object} query a query to be used by the adapter
|
|
4604
4444
|
@param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.query
|
|
4605
4445
|
@return {Promise} promise
|
|
4606
4446
|
*/
|
|
4607
|
-
|
|
4447
|
+
|
|
4448
|
+
query(type, query, options = {}) {
|
|
4608
4449
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4609
4450
|
assertDestroyingStore(this, 'query');
|
|
4610
4451
|
}
|
|
4611
|
-
assert(`You need to pass a model name to the store's query method`,
|
|
4452
|
+
assert(`You need to pass a model name to the store's query method`, type);
|
|
4612
4453
|
assert(`You need to pass a query hash to the store's query method`, query);
|
|
4613
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${
|
|
4454
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4614
4455
|
const promise = this.request({
|
|
4615
4456
|
op: 'query',
|
|
4616
4457
|
data: {
|
|
4617
|
-
type: normalizeModelName(
|
|
4458
|
+
type: normalizeModelName(type),
|
|
4618
4459
|
query,
|
|
4619
|
-
options: options
|
|
4460
|
+
options: options
|
|
4620
4461
|
},
|
|
4621
4462
|
cacheOptions: {
|
|
4622
4463
|
[SkipCache]: true
|
|
@@ -4874,20 +4715,21 @@ class Store extends EmberObject {
|
|
|
4874
4715
|
@since 1.13.0
|
|
4875
4716
|
@method findAll
|
|
4876
4717
|
@public
|
|
4877
|
-
@param {
|
|
4878
|
-
@param {
|
|
4718
|
+
@param {string} type the name of the resource
|
|
4719
|
+
@param {object} options
|
|
4879
4720
|
@return {Promise} promise
|
|
4880
4721
|
*/
|
|
4881
|
-
|
|
4722
|
+
|
|
4723
|
+
findAll(type, options = {}) {
|
|
4882
4724
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4883
4725
|
assertDestroyingStore(this, 'findAll');
|
|
4884
4726
|
}
|
|
4885
|
-
assert(`You need to pass a model name to the store's findAll method`,
|
|
4886
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${
|
|
4727
|
+
assert(`You need to pass a model name to the store's findAll method`, type);
|
|
4728
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4887
4729
|
const promise = this.request({
|
|
4888
4730
|
op: 'findAll',
|
|
4889
4731
|
data: {
|
|
4890
|
-
type: normalizeModelName(
|
|
4732
|
+
type: normalizeModelName(type),
|
|
4891
4733
|
options: options || {}
|
|
4892
4734
|
},
|
|
4893
4735
|
cacheOptions: {
|
|
@@ -4914,17 +4756,17 @@ class Store extends EmberObject {
|
|
|
4914
4756
|
@since 1.13.0
|
|
4915
4757
|
@method peekAll
|
|
4916
4758
|
@public
|
|
4917
|
-
@param {
|
|
4759
|
+
@param {string} type the name of the resource
|
|
4918
4760
|
@return {RecordArray}
|
|
4919
4761
|
*/
|
|
4920
|
-
|
|
4762
|
+
|
|
4763
|
+
peekAll(type) {
|
|
4921
4764
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4922
4765
|
assertDestroyingStore(this, 'peekAll');
|
|
4923
4766
|
}
|
|
4924
|
-
assert(`You need to pass a model name to the store's peekAll method`,
|
|
4925
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${
|
|
4926
|
-
|
|
4927
|
-
return this.recordArrayManager.liveArrayFor(type);
|
|
4767
|
+
assert(`You need to pass a model name to the store's peekAll method`, type);
|
|
4768
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4769
|
+
return this.recordArrayManager.liveArrayFor(normalizeModelName(type));
|
|
4928
4770
|
}
|
|
4929
4771
|
|
|
4930
4772
|
/**
|
|
@@ -4936,16 +4778,17 @@ class Store extends EmberObject {
|
|
|
4936
4778
|
store.unloadAll('post');
|
|
4937
4779
|
```
|
|
4938
4780
|
@method unloadAll
|
|
4781
|
+
@param {string} type the name of the resource
|
|
4939
4782
|
@public
|
|
4940
|
-
@param {String} modelName
|
|
4941
4783
|
*/
|
|
4942
|
-
|
|
4784
|
+
|
|
4785
|
+
unloadAll(type) {
|
|
4943
4786
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4944
4787
|
assertDestroyedStoreOnly(this, 'unloadAll');
|
|
4945
4788
|
}
|
|
4946
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(
|
|
4789
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`, !type || typeof type === 'string');
|
|
4947
4790
|
this._join(() => {
|
|
4948
|
-
if (
|
|
4791
|
+
if (type === undefined) {
|
|
4949
4792
|
// destroy the graph before unloadAll
|
|
4950
4793
|
// since then we avoid churning relationships
|
|
4951
4794
|
// during unload
|
|
@@ -4953,8 +4796,7 @@ class Store extends EmberObject {
|
|
|
4953
4796
|
this.recordArrayManager.clear();
|
|
4954
4797
|
this._instanceCache.clear();
|
|
4955
4798
|
} else {
|
|
4956
|
-
|
|
4957
|
-
this._instanceCache.clear(normalizedModelName);
|
|
4799
|
+
this._instanceCache.clear(normalizeModelName(type));
|
|
4958
4800
|
}
|
|
4959
4801
|
});
|
|
4960
4802
|
}
|
|
@@ -5094,421 +4936,784 @@ class Store extends EmberObject {
|
|
|
5094
4936
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5095
4937
|
assertDestroyingStore(this, 'push');
|
|
5096
4938
|
}
|
|
5097
|
-
|
|
4939
|
+
const pushed = this._push(data, false);
|
|
5098
4940
|
if (Array.isArray(pushed)) {
|
|
5099
|
-
|
|
5100
|
-
return records;
|
|
4941
|
+
return pushed.map(identifier => this._instanceCache.getRecord(identifier));
|
|
5101
4942
|
}
|
|
5102
4943
|
if (pushed === null) {
|
|
5103
4944
|
return null;
|
|
5104
4945
|
}
|
|
5105
4946
|
return this._instanceCache.getRecord(pushed);
|
|
5106
4947
|
}
|
|
5107
|
-
|
|
4948
|
+
|
|
4949
|
+
/**
|
|
4950
|
+
Push some data in the form of a json-api document into the store,
|
|
4951
|
+
without creating materialized records.
|
|
4952
|
+
@method _push
|
|
4953
|
+
@private
|
|
4954
|
+
@param {Object} jsonApiDoc
|
|
4955
|
+
@return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
|
|
4956
|
+
*/
|
|
4957
|
+
_push(jsonApiDoc, asyncFlush) {
|
|
4958
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4959
|
+
assertDestroyingStore(this, '_push');
|
|
4960
|
+
}
|
|
4961
|
+
if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
|
|
4962
|
+
try {
|
|
4963
|
+
const data = JSON.parse(JSON.stringify(jsonApiDoc));
|
|
4964
|
+
// eslint-disable-next-line no-console
|
|
4965
|
+
console.log('EmberData | Payload - push', data);
|
|
4966
|
+
} catch (e) {
|
|
4967
|
+
// eslint-disable-next-line no-console
|
|
4968
|
+
console.log('EmberData | Payload - push', jsonApiDoc);
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
if (asyncFlush) {
|
|
4972
|
+
this._enableAsyncFlush = true;
|
|
4973
|
+
}
|
|
4974
|
+
let ret;
|
|
4975
|
+
this._join(() => {
|
|
4976
|
+
ret = this.cache.put({
|
|
4977
|
+
content: jsonApiDoc
|
|
4978
|
+
});
|
|
4979
|
+
});
|
|
4980
|
+
this._enableAsyncFlush = null;
|
|
4981
|
+
return 'data' in ret ? ret.data : null;
|
|
4982
|
+
}
|
|
4983
|
+
|
|
4984
|
+
/**
|
|
4985
|
+
* Trigger a save for a Record.
|
|
4986
|
+
*
|
|
4987
|
+
* Returns a promise resolving with the same record when the save is complete.
|
|
4988
|
+
*
|
|
4989
|
+
* @method saveRecord
|
|
4990
|
+
* @public
|
|
4991
|
+
* @param {unknown} record
|
|
4992
|
+
* @param options
|
|
4993
|
+
* @return {Promise<record>}
|
|
4994
|
+
*/
|
|
4995
|
+
saveRecord(record, options = {}) {
|
|
4996
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4997
|
+
assertDestroyingStore(this, 'saveRecord');
|
|
4998
|
+
}
|
|
4999
|
+
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
|
|
5000
|
+
const identifier = recordIdentifierFor(record);
|
|
5001
|
+
const cache = this.cache;
|
|
5002
|
+
if (!identifier) {
|
|
5003
|
+
// this commonly means we're disconnected
|
|
5004
|
+
// but just in case we reject here to prevent bad things.
|
|
5005
|
+
return Promise.reject(new Error(`Record Is Disconnected`));
|
|
5006
|
+
}
|
|
5007
|
+
// TODO we used to check if the record was destroyed here
|
|
5008
|
+
assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
|
|
5009
|
+
if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
|
|
5010
|
+
return Promise.resolve(record);
|
|
5011
|
+
}
|
|
5012
|
+
if (!options) {
|
|
5013
|
+
options = {};
|
|
5014
|
+
}
|
|
5015
|
+
let operation = 'updateRecord';
|
|
5016
|
+
if (cache.isNew(identifier)) {
|
|
5017
|
+
operation = 'createRecord';
|
|
5018
|
+
} else if (cache.isDeleted(identifier)) {
|
|
5019
|
+
operation = 'deleteRecord';
|
|
5020
|
+
}
|
|
5021
|
+
const request = {
|
|
5022
|
+
op: operation,
|
|
5023
|
+
data: {
|
|
5024
|
+
options,
|
|
5025
|
+
record: identifier
|
|
5026
|
+
},
|
|
5027
|
+
records: [identifier],
|
|
5028
|
+
cacheOptions: {
|
|
5029
|
+
[SkipCache]: true
|
|
5030
|
+
}
|
|
5031
|
+
};
|
|
5032
|
+
|
|
5033
|
+
// we lie here on the type because legacy doesn't have enough context
|
|
5034
|
+
cache.willCommit(identifier, {
|
|
5035
|
+
request
|
|
5036
|
+
});
|
|
5037
|
+
return this.request(request).then(document => document.content);
|
|
5038
|
+
}
|
|
5039
|
+
|
|
5040
|
+
/**
|
|
5041
|
+
* Instantiation hook allowing applications or addons to configure the store
|
|
5042
|
+
* to utilize a custom Cache implementation.
|
|
5043
|
+
*
|
|
5044
|
+
* This hook should not be called directly by consuming applications or libraries.
|
|
5045
|
+
* Use `Store.cache` to access the Cache instance.
|
|
5046
|
+
*
|
|
5047
|
+
* @method createCache (hook)
|
|
5048
|
+
* @public
|
|
5049
|
+
* @param storeWrapper
|
|
5050
|
+
* @return {Cache}
|
|
5051
|
+
*/
|
|
5052
|
+
|
|
5053
|
+
/**
|
|
5054
|
+
* Returns the cache instance associated to this Store, instantiates the Cache
|
|
5055
|
+
* if necessary via `Store.createCache`
|
|
5056
|
+
*
|
|
5057
|
+
* @property {Cache} cache
|
|
5058
|
+
* @public
|
|
5059
|
+
*/
|
|
5060
|
+
get cache() {
|
|
5061
|
+
let {
|
|
5062
|
+
cache
|
|
5063
|
+
} = this._instanceCache;
|
|
5064
|
+
if (!cache) {
|
|
5065
|
+
cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
|
|
5066
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5067
|
+
cache = new CacheManager(cache);
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
return cache;
|
|
5071
|
+
}
|
|
5072
|
+
|
|
5073
|
+
// @ts-expect-error
|
|
5074
|
+
destroy() {
|
|
5075
|
+
if (this.isDestroyed) {
|
|
5076
|
+
// @ember/test-helpers will call destroy multiple times
|
|
5077
|
+
return;
|
|
5078
|
+
}
|
|
5079
|
+
this.isDestroying = true;
|
|
5080
|
+
this._graph?.destroy();
|
|
5081
|
+
this._graph = undefined;
|
|
5082
|
+
this.notifications.destroy();
|
|
5083
|
+
this.recordArrayManager.destroy();
|
|
5084
|
+
this.identifierCache.destroy();
|
|
5085
|
+
this.unloadAll();
|
|
5086
|
+
this.isDestroyed = true;
|
|
5087
|
+
}
|
|
5088
|
+
static create(args) {
|
|
5089
|
+
return new this(args);
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
let assertDestroyingStore;
|
|
5093
|
+
let assertDestroyedStoreOnly;
|
|
5094
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5095
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5096
|
+
assertDestroyingStore = function assertDestroyingStore(store, method) {
|
|
5097
|
+
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
|
|
5098
|
+
};
|
|
5099
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5100
|
+
assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
|
|
5101
|
+
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
|
|
5102
|
+
};
|
|
5103
|
+
}
|
|
5104
|
+
function isMaybeIdentifier(maybeIdentifier) {
|
|
5105
|
+
return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
|
|
5106
|
+
}
|
|
5107
|
+
function normalizeProperties(store, identifier, properties) {
|
|
5108
|
+
// assert here
|
|
5109
|
+
if (properties !== undefined) {
|
|
5110
|
+
if ('id' in properties) {
|
|
5111
|
+
assert(`expected id to be a string or null`, properties.id !== undefined);
|
|
5112
|
+
}
|
|
5113
|
+
assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
|
|
5114
|
+
const {
|
|
5115
|
+
type
|
|
5116
|
+
} = identifier;
|
|
5117
|
+
|
|
5118
|
+
// convert relationship Records to RecordDatas before passing to RecordData
|
|
5119
|
+
const defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
5120
|
+
type
|
|
5121
|
+
});
|
|
5122
|
+
if (defs !== null) {
|
|
5123
|
+
const keys = Object.keys(properties);
|
|
5124
|
+
let relationshipValue;
|
|
5125
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5126
|
+
const prop = keys[i];
|
|
5127
|
+
const def = defs[prop];
|
|
5128
|
+
if (def !== undefined) {
|
|
5129
|
+
if (def.kind === 'hasMany') {
|
|
5130
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5131
|
+
assertRecordsPassedToHasMany(properties[prop]);
|
|
5132
|
+
}
|
|
5133
|
+
relationshipValue = extractIdentifiersFromRecords(properties[prop]);
|
|
5134
|
+
} else {
|
|
5135
|
+
relationshipValue = extractIdentifierFromRecord(properties[prop]);
|
|
5136
|
+
}
|
|
5137
|
+
properties[prop] = relationshipValue;
|
|
5138
|
+
}
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
return properties;
|
|
5143
|
+
}
|
|
5144
|
+
function assertRecordsPassedToHasMany(records) {
|
|
5145
|
+
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
5146
|
+
assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
|
|
5147
|
+
return records.every(record => {
|
|
5148
|
+
try {
|
|
5149
|
+
recordIdentifierFor(record);
|
|
5150
|
+
return true;
|
|
5151
|
+
} catch {
|
|
5152
|
+
return false;
|
|
5153
|
+
}
|
|
5154
|
+
});
|
|
5155
|
+
}());
|
|
5156
|
+
}
|
|
5157
|
+
function extractIdentifiersFromRecords(records) {
|
|
5158
|
+
return records.map(record => extractIdentifierFromRecord(record));
|
|
5159
|
+
}
|
|
5160
|
+
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
5161
|
+
if (!recordOrPromiseRecord) {
|
|
5162
|
+
return null;
|
|
5163
|
+
}
|
|
5164
|
+
const extract = recordIdentifierFor;
|
|
5165
|
+
return extract(recordOrPromiseRecord);
|
|
5166
|
+
}
|
|
5167
|
+
function urlFromLink(link) {
|
|
5168
|
+
if (typeof link === 'string') return link;
|
|
5169
|
+
return link.href;
|
|
5170
|
+
}
|
|
5171
|
+
|
|
5172
|
+
/**
|
|
5173
|
+
* A Document is a class that wraps the response content from a request to the API
|
|
5174
|
+
* returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
|
|
5175
|
+
* record instances.
|
|
5176
|
+
*
|
|
5177
|
+
* It is not directly instantiated by the user, and its properties should not
|
|
5178
|
+
* be directly modified. Whether individual properties are mutable or not is
|
|
5179
|
+
* determined by the record instance itself.
|
|
5180
|
+
*
|
|
5181
|
+
* @public
|
|
5182
|
+
* @class Document
|
|
5183
|
+
*/
|
|
5184
|
+
var _store = /*#__PURE__*/_classPrivateFieldKey("store");
|
|
5185
|
+
var _request = /*#__PURE__*/_classPrivateFieldKey("request");
|
|
5186
|
+
class Document {
|
|
5187
|
+
constructor(store, identifier) {
|
|
5188
|
+
Object.defineProperty(this, _request, {
|
|
5189
|
+
value: _request2
|
|
5190
|
+
});
|
|
5191
|
+
/**
|
|
5192
|
+
* The links object for this document, if any
|
|
5193
|
+
*
|
|
5194
|
+
* e.g.
|
|
5195
|
+
*
|
|
5196
|
+
* ```
|
|
5197
|
+
* {
|
|
5198
|
+
* self: '/articles?page[number]=3',
|
|
5199
|
+
* }
|
|
5200
|
+
* ```
|
|
5201
|
+
*
|
|
5202
|
+
* @property links
|
|
5203
|
+
* @type {object|undefined} - a links object
|
|
5204
|
+
* @public
|
|
5205
|
+
*/
|
|
5206
|
+
/**
|
|
5207
|
+
* The primary data for this document, if any.
|
|
5208
|
+
*
|
|
5209
|
+
* If this document has no primary data (e.g. because it is an error document)
|
|
5210
|
+
* this property will be `undefined`.
|
|
5211
|
+
*
|
|
5212
|
+
* For collections this will be an array of record instances,
|
|
5213
|
+
* for single resource requests it will be a single record instance or null.
|
|
5214
|
+
*
|
|
5215
|
+
* @property data
|
|
5216
|
+
* @public
|
|
5217
|
+
* @type {object|Array<object>|null|undefined} - a data object
|
|
5218
|
+
*/
|
|
5219
|
+
/**
|
|
5220
|
+
* The errors returned by the API for this request, if any
|
|
5221
|
+
*
|
|
5222
|
+
* @property errors
|
|
5223
|
+
* @public
|
|
5224
|
+
* @type {object|undefined} - an errors object
|
|
5225
|
+
*/
|
|
5226
|
+
/**
|
|
5227
|
+
* The meta object for this document, if any
|
|
5228
|
+
*
|
|
5229
|
+
* @property meta
|
|
5230
|
+
* @public
|
|
5231
|
+
* @type {object|undefined} - a meta object
|
|
5232
|
+
*/
|
|
5233
|
+
/**
|
|
5234
|
+
* The identifier associated with this document, if any
|
|
5235
|
+
*
|
|
5236
|
+
* @property identifier
|
|
5237
|
+
* @public
|
|
5238
|
+
* @type {StableDocumentIdentifier|null}
|
|
5239
|
+
*/
|
|
5240
|
+
Object.defineProperty(this, _store, {
|
|
5241
|
+
writable: true,
|
|
5242
|
+
value: void 0
|
|
5243
|
+
});
|
|
5244
|
+
_classPrivateFieldBase(this, _store)[_store] = store;
|
|
5245
|
+
this.identifier = identifier;
|
|
5246
|
+
}
|
|
5108
5247
|
/**
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
// eslint-disable-next-line no-console
|
|
5124
|
-
console.log('EmberData | Payload - push', data);
|
|
5125
|
-
} catch (e) {
|
|
5126
|
-
// eslint-disable-next-line no-console
|
|
5127
|
-
console.log('EmberData | Payload - push', jsonApiDoc);
|
|
5128
|
-
}
|
|
5129
|
-
}
|
|
5130
|
-
if (asyncFlush) {
|
|
5131
|
-
this._enableAsyncFlush = true;
|
|
5132
|
-
}
|
|
5133
|
-
let ret;
|
|
5134
|
-
this._join(() => {
|
|
5135
|
-
ret = this.cache.put({
|
|
5136
|
-
content: jsonApiDoc
|
|
5137
|
-
});
|
|
5138
|
-
});
|
|
5139
|
-
this._enableAsyncFlush = null;
|
|
5140
|
-
return 'data' in ret ? ret.data : null;
|
|
5248
|
+
* Fetches the related link for this document, returning a promise that resolves
|
|
5249
|
+
* with the document when the request completes. If no related link is present,
|
|
5250
|
+
* will fallback to the self link if present
|
|
5251
|
+
*
|
|
5252
|
+
* @method fetch
|
|
5253
|
+
* @public
|
|
5254
|
+
* @param {object} options
|
|
5255
|
+
* @return Promise<Document>
|
|
5256
|
+
*/
|
|
5257
|
+
fetch(options = {}) {
|
|
5258
|
+
assert(`No self or related link`, this.links?.related || this.links?.self);
|
|
5259
|
+
options.cacheOptions = options.cacheOptions || {};
|
|
5260
|
+
options.cacheOptions.key = this.identifier?.lid;
|
|
5261
|
+
return _classPrivateFieldBase(this, _request)[_request](this.links.related ? 'related' : 'self', options);
|
|
5141
5262
|
}
|
|
5142
5263
|
|
|
5143
5264
|
/**
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
let pushData = {
|
|
5156
|
-
posts: [
|
|
5157
|
-
{ id: 1, postTitle: "Great post", commentIds: [2] }
|
|
5158
|
-
],
|
|
5159
|
-
comments: [
|
|
5160
|
-
{ id: 2, commentBody: "Insightful comment" }
|
|
5161
|
-
]
|
|
5162
|
-
}
|
|
5163
|
-
store.pushPayload(pushData);
|
|
5164
|
-
```
|
|
5165
|
-
By default, the data will be deserialized using a default
|
|
5166
|
-
serializer (the application serializer if it exists).
|
|
5167
|
-
Alternatively, `pushPayload` will accept a model type which
|
|
5168
|
-
will determine which serializer will process the payload.
|
|
5169
|
-
```app/serializers/application.js
|
|
5170
|
-
import RESTSerializer from '@ember-data/serializer/rest';
|
|
5171
|
-
export default class ApplicationSerializer extends RESTSerializer;
|
|
5172
|
-
```
|
|
5173
|
-
```app/serializers/post.js
|
|
5174
|
-
import JSONSerializer from '@ember-data/serializer/json';
|
|
5175
|
-
export default JSONSerializer;
|
|
5176
|
-
```
|
|
5177
|
-
```js
|
|
5178
|
-
store.pushPayload(pushData); // Will use the application serializer
|
|
5179
|
-
store.pushPayload('post', pushData); // Will use the post serializer
|
|
5180
|
-
```
|
|
5181
|
-
@method pushPayload
|
|
5182
|
-
@public
|
|
5183
|
-
@param {String} modelName Optionally, a model type used to determine which serializer will be used
|
|
5184
|
-
@param {Object} inputPayload
|
|
5185
|
-
*/
|
|
5186
|
-
// TODO @runspired @deprecate pushPayload in favor of looking up the serializer
|
|
5187
|
-
pushPayload(modelName, inputPayload) {
|
|
5188
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5189
|
-
assertDestroyingStore(this, 'pushPayload');
|
|
5190
|
-
}
|
|
5191
|
-
const payload = inputPayload || modelName;
|
|
5192
|
-
const normalizedModelName = inputPayload ? normalizeModelName(modelName) : 'application';
|
|
5193
|
-
const serializer = this.serializerFor(normalizedModelName);
|
|
5194
|
-
assert(`You cannot use 'store.pushPayload(<type>, <payload>)' unless the serializer for '${normalizedModelName}' defines 'pushPayload'`, serializer && typeof serializer.pushPayload === 'function');
|
|
5195
|
-
serializer.pushPayload(this, payload);
|
|
5265
|
+
* Fetches the next 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
|
+
* next link.
|
|
5268
|
+
*
|
|
5269
|
+
* @method next
|
|
5270
|
+
* @public
|
|
5271
|
+
* @param {object} options
|
|
5272
|
+
* @return Promise<Document | null>
|
|
5273
|
+
*/
|
|
5274
|
+
next(options = {}) {
|
|
5275
|
+
return _classPrivateFieldBase(this, _request)[_request]('next', options);
|
|
5196
5276
|
}
|
|
5197
5277
|
|
|
5198
5278
|
/**
|
|
5199
|
-
*
|
|
5279
|
+
* Fetches the prev link for this document, returning a promise that resolves
|
|
5280
|
+
* with the new document when the request completes, or null if there is no
|
|
5281
|
+
* prev link.
|
|
5200
5282
|
*
|
|
5201
|
-
* @method
|
|
5283
|
+
* @method prev
|
|
5202
5284
|
* @public
|
|
5203
|
-
* @param {
|
|
5204
|
-
* @
|
|
5205
|
-
* @returns {Promise<RecordInstance>}
|
|
5285
|
+
* @param {object} options
|
|
5286
|
+
* @return Promise<Document | null>
|
|
5206
5287
|
*/
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
assertDestroyingStore(this, 'saveRecord');
|
|
5210
|
-
}
|
|
5211
|
-
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
|
|
5212
|
-
let identifier = recordIdentifierFor(record);
|
|
5213
|
-
const cache = this.cache;
|
|
5214
|
-
if (!identifier) {
|
|
5215
|
-
// this commonly means we're disconnected
|
|
5216
|
-
// but just in case we reject here to prevent bad things.
|
|
5217
|
-
return Promise.reject(`Record Is Disconnected`);
|
|
5218
|
-
}
|
|
5219
|
-
// TODO we used to check if the record was destroyed here
|
|
5220
|
-
assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
|
|
5221
|
-
if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
|
|
5222
|
-
return Promise.resolve(record);
|
|
5223
|
-
}
|
|
5224
|
-
if (!options) {
|
|
5225
|
-
options = {};
|
|
5226
|
-
}
|
|
5227
|
-
let operation = 'updateRecord';
|
|
5228
|
-
if (cache.isNew(identifier)) {
|
|
5229
|
-
operation = 'createRecord';
|
|
5230
|
-
} else if (cache.isDeleted(identifier)) {
|
|
5231
|
-
operation = 'deleteRecord';
|
|
5232
|
-
}
|
|
5233
|
-
const request = {
|
|
5234
|
-
op: operation,
|
|
5235
|
-
data: {
|
|
5236
|
-
options,
|
|
5237
|
-
record: identifier
|
|
5238
|
-
},
|
|
5239
|
-
cacheOptions: {
|
|
5240
|
-
[SkipCache]: true
|
|
5241
|
-
}
|
|
5242
|
-
};
|
|
5243
|
-
|
|
5244
|
-
// we lie here on the type because legacy doesn't have enough context
|
|
5245
|
-
cache.willCommit(identifier, {
|
|
5246
|
-
request
|
|
5247
|
-
});
|
|
5248
|
-
return this.request(request).then(document => document.content);
|
|
5288
|
+
prev(options = {}) {
|
|
5289
|
+
return _classPrivateFieldBase(this, _request)[_request]('prev', options);
|
|
5249
5290
|
}
|
|
5250
5291
|
|
|
5251
5292
|
/**
|
|
5252
|
-
*
|
|
5253
|
-
*
|
|
5293
|
+
* Fetches the first link for this document, returning a promise that resolves
|
|
5294
|
+
* with the new document when the request completes, or null if there is no
|
|
5295
|
+
* first link.
|
|
5254
5296
|
*
|
|
5255
|
-
*
|
|
5256
|
-
*
|
|
5297
|
+
* @method first
|
|
5298
|
+
* @public
|
|
5299
|
+
* @param {object} options
|
|
5300
|
+
* @return Promise<Document | null>
|
|
5301
|
+
*/
|
|
5302
|
+
first(options = {}) {
|
|
5303
|
+
return _classPrivateFieldBase(this, _request)[_request]('first', options);
|
|
5304
|
+
}
|
|
5305
|
+
|
|
5306
|
+
/**
|
|
5307
|
+
* Fetches the last link for this document, returning a promise that resolves
|
|
5308
|
+
* with the new document when the request completes, or null if there is no
|
|
5309
|
+
* last link.
|
|
5257
5310
|
*
|
|
5258
|
-
* @method
|
|
5311
|
+
* @method last
|
|
5259
5312
|
* @public
|
|
5260
|
-
* @param
|
|
5261
|
-
* @
|
|
5313
|
+
* @param {object} options
|
|
5314
|
+
* @return Promise<Document | null>
|
|
5262
5315
|
*/
|
|
5316
|
+
last(options = {}) {
|
|
5317
|
+
return _classPrivateFieldBase(this, _request)[_request]('last', options);
|
|
5318
|
+
}
|
|
5263
5319
|
|
|
5264
5320
|
/**
|
|
5265
|
-
*
|
|
5266
|
-
* if necessary via `Store.createCache`
|
|
5321
|
+
* Implemented for `JSON.stringify` support.
|
|
5267
5322
|
*
|
|
5268
|
-
*
|
|
5323
|
+
* Returns the JSON representation of the document wrapper.
|
|
5324
|
+
*
|
|
5325
|
+
* This is a shallow serialization, it does not deeply serialize
|
|
5326
|
+
* the document's contents, leaving that to the individual record
|
|
5327
|
+
* instances to determine how to do, if at all.
|
|
5328
|
+
*
|
|
5329
|
+
* @method toJSON
|
|
5269
5330
|
* @public
|
|
5331
|
+
* @return
|
|
5270
5332
|
*/
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5333
|
+
toJSON() {
|
|
5334
|
+
const data = {};
|
|
5335
|
+
data.identifier = this.identifier;
|
|
5336
|
+
if (this.data !== undefined) {
|
|
5337
|
+
data.data = this.data;
|
|
5338
|
+
}
|
|
5339
|
+
if (this.links !== undefined) {
|
|
5340
|
+
data.links = this.links;
|
|
5341
|
+
}
|
|
5342
|
+
if (this.errors !== undefined) {
|
|
5343
|
+
data.errors = this.errors;
|
|
5344
|
+
}
|
|
5345
|
+
if (this.meta !== undefined) {
|
|
5346
|
+
data.meta = this.meta;
|
|
5347
|
+
}
|
|
5348
|
+
return data;
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
async function _request2(link, options) {
|
|
5352
|
+
const href = this.links?.[link];
|
|
5353
|
+
if (!href) {
|
|
5354
|
+
return null;
|
|
5355
|
+
}
|
|
5356
|
+
options.method = options.method || 'GET';
|
|
5357
|
+
const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
|
|
5358
|
+
url: urlFromLink(href)
|
|
5359
|
+
}));
|
|
5360
|
+
return response.content;
|
|
5361
|
+
}
|
|
5362
|
+
defineSignal(Document.prototype, 'data');
|
|
5363
|
+
defineSignal(Document.prototype, 'links');
|
|
5364
|
+
defineSignal(Document.prototype, 'errors');
|
|
5365
|
+
defineSignal(Document.prototype, 'meta');
|
|
5366
|
+
|
|
5367
|
+
/**
|
|
5368
|
+
* @module @ember-data/store
|
|
5369
|
+
*/
|
|
5370
|
+
|
|
5371
|
+
/**
|
|
5372
|
+
* A service which an application may provide to the store via
|
|
5373
|
+
* the store's `lifetimes` property to configure the behavior
|
|
5374
|
+
* of the CacheHandler.
|
|
5375
|
+
*
|
|
5376
|
+
* The default behavior for request lifetimes is to never expire
|
|
5377
|
+
* unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
|
|
5378
|
+
*
|
|
5379
|
+
* Implementing this service allows you to programatically define
|
|
5380
|
+
* when a request should be considered expired.
|
|
5381
|
+
*
|
|
5382
|
+
* @class <Interface> LifetimesService
|
|
5383
|
+
* @public
|
|
5384
|
+
*/
|
|
5385
|
+
|
|
5386
|
+
const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
|
|
5387
|
+
function isErrorDocument(document) {
|
|
5388
|
+
return 'errors' in document;
|
|
5389
|
+
}
|
|
5390
|
+
function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
5391
|
+
const {
|
|
5392
|
+
identifier
|
|
5393
|
+
} = options;
|
|
5394
|
+
if (!document) {
|
|
5395
|
+
assert(`The CacheHandler expected response content but none was found`, !options.shouldHydrate);
|
|
5396
|
+
return document;
|
|
5397
|
+
}
|
|
5398
|
+
if (isErrorDocument(document)) {
|
|
5399
|
+
if (!identifier && !options.shouldHydrate) {
|
|
5400
|
+
return document;
|
|
5401
|
+
}
|
|
5402
|
+
let doc;
|
|
5403
|
+
if (identifier) {
|
|
5404
|
+
doc = store._documentCache.get(identifier);
|
|
5405
|
+
}
|
|
5406
|
+
if (!doc) {
|
|
5407
|
+
doc = new Document(store, identifier);
|
|
5408
|
+
copyDocumentProperties(doc, document);
|
|
5409
|
+
if (identifier) {
|
|
5410
|
+
store._documentCache.set(identifier, doc);
|
|
5411
|
+
}
|
|
5412
|
+
} else if (!isFromCache) {
|
|
5413
|
+
doc.data = undefined;
|
|
5414
|
+
copyDocumentProperties(doc, document);
|
|
5415
|
+
}
|
|
5416
|
+
return options.shouldHydrate ? doc : document;
|
|
5417
|
+
}
|
|
5418
|
+
if (Array.isArray(document.data)) {
|
|
5419
|
+
const {
|
|
5420
|
+
recordArrayManager
|
|
5421
|
+
} = store;
|
|
5422
|
+
if (!identifier) {
|
|
5423
|
+
if (!options.shouldHydrate) {
|
|
5424
|
+
return document;
|
|
5425
|
+
}
|
|
5426
|
+
const data = recordArrayManager.createArray({
|
|
5427
|
+
type: request.url,
|
|
5428
|
+
identifiers: document.data,
|
|
5429
|
+
doc: document,
|
|
5430
|
+
query: request
|
|
5431
|
+
});
|
|
5432
|
+
const doc = new Document(store, null);
|
|
5433
|
+
doc.data = data;
|
|
5434
|
+
doc.meta = document.meta;
|
|
5435
|
+
doc.links = document.links;
|
|
5436
|
+
return doc;
|
|
5437
|
+
}
|
|
5438
|
+
let managed = recordArrayManager._keyedArrays.get(identifier.lid);
|
|
5439
|
+
if (!managed) {
|
|
5440
|
+
managed = recordArrayManager.createArray({
|
|
5441
|
+
type: identifier.lid,
|
|
5442
|
+
identifiers: document.data,
|
|
5443
|
+
doc: document
|
|
5444
|
+
});
|
|
5445
|
+
recordArrayManager._keyedArrays.set(identifier.lid, managed);
|
|
5446
|
+
const doc = new Document(store, identifier);
|
|
5447
|
+
doc.data = managed;
|
|
5448
|
+
doc.meta = document.meta;
|
|
5449
|
+
doc.links = document.links;
|
|
5450
|
+
store._documentCache.set(identifier, doc);
|
|
5451
|
+
return options.shouldHydrate ? doc : document;
|
|
5452
|
+
} else {
|
|
5453
|
+
const doc = store._documentCache.get(identifier);
|
|
5454
|
+
if (!isFromCache) {
|
|
5455
|
+
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
5456
|
+
doc.data = managed;
|
|
5457
|
+
doc.meta = document.meta;
|
|
5458
|
+
doc.links = document.links;
|
|
5279
5459
|
}
|
|
5460
|
+
return options.shouldHydrate ? doc : document;
|
|
5280
5461
|
}
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
/**
|
|
5285
|
-
`normalize` converts a json payload into the normalized form that
|
|
5286
|
-
[push](../methods/push?anchor=push) expects.
|
|
5287
|
-
Example
|
|
5288
|
-
```js
|
|
5289
|
-
socket.on('message', function(message) {
|
|
5290
|
-
let modelName = message.model;
|
|
5291
|
-
let data = message.data;
|
|
5292
|
-
store.push(store.normalize(modelName, data));
|
|
5293
|
-
});
|
|
5294
|
-
```
|
|
5295
|
-
@method normalize
|
|
5296
|
-
@public
|
|
5297
|
-
@param {String} modelName The name of the model type for this payload
|
|
5298
|
-
@param {Object} payload
|
|
5299
|
-
@return {Object} The normalized payload
|
|
5300
|
-
*/
|
|
5301
|
-
// TODO @runspired @deprecate users should call normalize on the associated serializer directly
|
|
5302
|
-
normalize(modelName, payload) {
|
|
5303
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5304
|
-
assertDestroyingStore(this, 'normalize');
|
|
5305
|
-
}
|
|
5306
|
-
assert(`You need to pass a model name to the store's normalize method`, modelName);
|
|
5307
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${typeof modelName}`, typeof modelName === 'string');
|
|
5308
|
-
const normalizedModelName = normalizeModelName(modelName);
|
|
5309
|
-
const serializer = this.serializerFor(normalizedModelName);
|
|
5310
|
-
const schema = this.modelFor(normalizedModelName);
|
|
5311
|
-
assert(`You must define a normalize method in your serializer in order to call store.normalize`, typeof serializer?.normalize === 'function');
|
|
5312
|
-
return serializer.normalize(schema, payload);
|
|
5313
|
-
}
|
|
5314
|
-
|
|
5315
|
-
/**
|
|
5316
|
-
Returns an instance of the adapter for a given type. For
|
|
5317
|
-
example, `adapterFor('person')` will return an instance of
|
|
5318
|
-
the adapter located at `app/adapters/person.js`
|
|
5319
|
-
If no `person` adapter is found, this method will look
|
|
5320
|
-
for an `application` adapter (the default adapter for
|
|
5321
|
-
your entire application).
|
|
5322
|
-
@method adapterFor
|
|
5323
|
-
@public
|
|
5324
|
-
@param {String} modelName
|
|
5325
|
-
@return Adapter
|
|
5326
|
-
*/
|
|
5327
|
-
adapterFor(modelName) {
|
|
5328
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5329
|
-
assertDestroyingStore(this, 'adapterFor');
|
|
5330
|
-
}
|
|
5331
|
-
assert(`You need to pass a model name to the store's adapterFor method`, modelName);
|
|
5332
|
-
assert(`Passing classes to store.adapterFor has been removed. Please pass a dasherized string instead of ${modelName}`, typeof modelName === 'string');
|
|
5333
|
-
let normalizedModelName = normalizeModelName(modelName);
|
|
5334
|
-
let {
|
|
5335
|
-
_adapterCache
|
|
5336
|
-
} = this;
|
|
5337
|
-
let adapter = _adapterCache[normalizedModelName];
|
|
5338
|
-
if (adapter) {
|
|
5339
|
-
return adapter;
|
|
5462
|
+
} else {
|
|
5463
|
+
if (!identifier && !options.shouldHydrate) {
|
|
5464
|
+
return document;
|
|
5340
5465
|
}
|
|
5341
|
-
const
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
if (adapter !== undefined) {
|
|
5346
|
-
_adapterCache[normalizedModelName] = adapter;
|
|
5347
|
-
return adapter;
|
|
5466
|
+
const data = document.data ? store.peekRecord(document.data) : null;
|
|
5467
|
+
let doc;
|
|
5468
|
+
if (identifier) {
|
|
5469
|
+
doc = store._documentCache.get(identifier);
|
|
5348
5470
|
}
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5471
|
+
if (!doc) {
|
|
5472
|
+
doc = new Document(store, identifier);
|
|
5473
|
+
doc.data = data;
|
|
5474
|
+
copyDocumentProperties(doc, document);
|
|
5475
|
+
if (identifier) {
|
|
5476
|
+
store._documentCache.set(identifier, doc);
|
|
5477
|
+
}
|
|
5478
|
+
} else if (!isFromCache) {
|
|
5479
|
+
doc.data = data;
|
|
5480
|
+
copyDocumentProperties(doc, document);
|
|
5356
5481
|
}
|
|
5357
|
-
|
|
5482
|
+
return options.shouldHydrate ? doc : document;
|
|
5358
5483
|
}
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5484
|
+
}
|
|
5485
|
+
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
5486
|
+
const {
|
|
5487
|
+
cacheOptions
|
|
5488
|
+
} = request;
|
|
5489
|
+
return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
|
|
5490
|
+
}
|
|
5491
|
+
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
5492
|
+
const {
|
|
5493
|
+
cacheOptions
|
|
5494
|
+
} = request;
|
|
5495
|
+
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false));
|
|
5496
|
+
}
|
|
5497
|
+
function isMutation(request) {
|
|
5498
|
+
return Boolean(request.op && MUTATION_OPS.has(request.op));
|
|
5499
|
+
}
|
|
5500
|
+
function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
|
|
5501
|
+
const {
|
|
5502
|
+
store
|
|
5503
|
+
} = context.request;
|
|
5504
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
5505
|
+
let isMut = false;
|
|
5506
|
+
if (isMutation(context.request)) {
|
|
5507
|
+
isMut = true;
|
|
5508
|
+
// TODO should we handle multiple records in request.records by iteratively calling willCommit for each
|
|
5509
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5510
|
+
assert(`Expected to receive a list of records included in the ${context.request.op} request`, record || !shouldHydrate);
|
|
5511
|
+
if (record) {
|
|
5512
|
+
store.cache.willCommit(record, context);
|
|
5387
5513
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5514
|
+
}
|
|
5515
|
+
if (store.lifetimes?.willRequest) {
|
|
5516
|
+
store.lifetimes.willRequest(context.request, identifier, store);
|
|
5517
|
+
}
|
|
5518
|
+
const promise = next(context.request).then(document => {
|
|
5519
|
+
store.requestManager._pending.delete(context.id);
|
|
5520
|
+
store._enableAsyncFlush = true;
|
|
5521
|
+
let response;
|
|
5522
|
+
store._join(() => {
|
|
5523
|
+
if (isMutation(context.request)) {
|
|
5524
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5525
|
+
if (record) {
|
|
5526
|
+
response = store.cache.didCommit(record, document);
|
|
5527
|
+
|
|
5528
|
+
// a mutation combined with a 204 has no cache impact when no known records were involved
|
|
5529
|
+
// a createRecord with a 201 with an empty response and no known records should similarly
|
|
5530
|
+
// have no cache impact
|
|
5531
|
+
} else if (isCacheAffecting(document)) {
|
|
5532
|
+
response = store.cache.put(document);
|
|
5533
|
+
}
|
|
5534
|
+
} else {
|
|
5535
|
+
response = store.cache.put(document);
|
|
5536
|
+
}
|
|
5537
|
+
response = maybeUpdateUiObjects(store, context.request, {
|
|
5538
|
+
shouldHydrate,
|
|
5539
|
+
shouldFetch,
|
|
5540
|
+
shouldBackgroundFetch,
|
|
5541
|
+
identifier
|
|
5542
|
+
}, response, false);
|
|
5543
|
+
});
|
|
5544
|
+
store._enableAsyncFlush = null;
|
|
5545
|
+
if (store.lifetimes?.didRequest) {
|
|
5546
|
+
store.lifetimes.didRequest(context.request, document.response, identifier, store);
|
|
5395
5547
|
}
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
5401
|
-
_serializerCache.application = serializer;
|
|
5402
|
-
return serializer;
|
|
5548
|
+
if (shouldFetch) {
|
|
5549
|
+
return response;
|
|
5550
|
+
} else if (shouldBackgroundFetch) {
|
|
5551
|
+
store.notifications._flush();
|
|
5403
5552
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
destroy() {
|
|
5409
|
-
if (this.isDestroyed) {
|
|
5410
|
-
// @ember/test-helpers will call destroy multiple times
|
|
5411
|
-
return;
|
|
5553
|
+
}, error => {
|
|
5554
|
+
store.requestManager._pending.delete(context.id);
|
|
5555
|
+
if (context.request.signal?.aborted) {
|
|
5556
|
+
throw error;
|
|
5412
5557
|
}
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
if (
|
|
5418
|
-
|
|
5558
|
+
store.requestManager._pending.delete(context.id);
|
|
5559
|
+
store._enableAsyncFlush = true;
|
|
5560
|
+
let response;
|
|
5561
|
+
store._join(() => {
|
|
5562
|
+
if (isMutation(context.request)) {
|
|
5563
|
+
// TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
|
|
5564
|
+
// currently we let the response remain undefiend.
|
|
5565
|
+
const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
|
|
5566
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5567
|
+
store.cache.commitWasRejected(record, errors);
|
|
5568
|
+
// re-throw the original error to preserve `errors` property.
|
|
5569
|
+
throw error;
|
|
5570
|
+
} else {
|
|
5571
|
+
response = store.cache.put(error);
|
|
5572
|
+
response = maybeUpdateUiObjects(store, context.request, {
|
|
5573
|
+
shouldHydrate,
|
|
5574
|
+
shouldFetch,
|
|
5575
|
+
shouldBackgroundFetch,
|
|
5576
|
+
identifier
|
|
5577
|
+
}, response, false);
|
|
5419
5578
|
}
|
|
5579
|
+
});
|
|
5580
|
+
store._enableAsyncFlush = null;
|
|
5581
|
+
if (identifier && store.lifetimes?.didRequest) {
|
|
5582
|
+
store.lifetimes.didRequest(context.request, error.response, identifier, store);
|
|
5420
5583
|
}
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5584
|
+
if (!shouldBackgroundFetch) {
|
|
5585
|
+
const newError = cloneError(error);
|
|
5586
|
+
newError.content = response;
|
|
5587
|
+
throw newError;
|
|
5588
|
+
} else {
|
|
5589
|
+
store.notifications._flush();
|
|
5426
5590
|
}
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
this.recordArrayManager.destroy();
|
|
5431
|
-
this.identifierCache.destroy();
|
|
5432
|
-
this.unloadAll();
|
|
5433
|
-
this.isDestroyed = true;
|
|
5434
|
-
}
|
|
5435
|
-
static create(args) {
|
|
5436
|
-
return new this(args);
|
|
5591
|
+
});
|
|
5592
|
+
if (!isMut) {
|
|
5593
|
+
return promise;
|
|
5437
5594
|
}
|
|
5595
|
+
assert(`Expected a mutation`, isMutation(context.request));
|
|
5596
|
+
|
|
5597
|
+
// for mutations we need to enqueue the promise with the requestStateService
|
|
5598
|
+
// TODO should we enque a request per record in records?
|
|
5599
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5600
|
+
return store._requestCache._enqueue(promise, {
|
|
5601
|
+
data: [{
|
|
5602
|
+
op: 'saveRecord',
|
|
5603
|
+
recordIdentifier: record,
|
|
5604
|
+
options: undefined
|
|
5605
|
+
}]
|
|
5606
|
+
});
|
|
5438
5607
|
}
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
|
|
5445
|
-
};
|
|
5446
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5447
|
-
assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
|
|
5448
|
-
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
|
|
5449
|
-
};
|
|
5450
|
-
}
|
|
5451
|
-
function isMaybeIdentifier(maybeIdentifier) {
|
|
5452
|
-
return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
|
|
5608
|
+
function cloneError(error) {
|
|
5609
|
+
const cloned = new Error(error.message);
|
|
5610
|
+
cloned.stack = error.stack;
|
|
5611
|
+
cloned.error = error.error;
|
|
5612
|
+
return cloned;
|
|
5453
5613
|
}
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5614
|
+
|
|
5615
|
+
/**
|
|
5616
|
+
* A CacheHandler that adds support for using an EmberData Cache with a RequestManager.
|
|
5617
|
+
*
|
|
5618
|
+
* This handler will only run when a request has supplied a `store` instance. Requests
|
|
5619
|
+
* issued by the store via `store.request()` will automatically have the `store` instance
|
|
5620
|
+
* attached to the request.
|
|
5621
|
+
*
|
|
5622
|
+
* ```ts
|
|
5623
|
+
* requestManager.request({
|
|
5624
|
+
* store: store,
|
|
5625
|
+
* url: '/api/posts',
|
|
5626
|
+
* method: 'GET'
|
|
5627
|
+
* });
|
|
5628
|
+
* ```
|
|
5629
|
+
*
|
|
5630
|
+
* When this handler elects to handle a request, it will return the raw `StructuredDocument`
|
|
5631
|
+
* unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
|
|
5632
|
+
* return a `Document` instance that will automatically update the UI when the cache is updated
|
|
5633
|
+
* in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
|
|
5634
|
+
*
|
|
5635
|
+
* When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
|
|
5636
|
+
* means that if desired you can issue requests that utilize the cache without needing to also
|
|
5637
|
+
* utilize Record instances if desired.
|
|
5638
|
+
*
|
|
5639
|
+
* Said differently, you could elect to issue all requests via a RequestManager, without ever using
|
|
5640
|
+
* the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
|
|
5641
|
+
* necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
|
|
5642
|
+
* approach of EmberData allows for this flexibility.
|
|
5643
|
+
*
|
|
5644
|
+
* ```ts
|
|
5645
|
+
* import { EnableHydration } from '@warp-drive/core-types/request';
|
|
5646
|
+
*
|
|
5647
|
+
* requestManager.request({
|
|
5648
|
+
* store: store,
|
|
5649
|
+
* url: '/api/posts',
|
|
5650
|
+
* method: 'GET',
|
|
5651
|
+
* [EnableHydration]: true
|
|
5652
|
+
* });
|
|
5653
|
+
*
|
|
5654
|
+
* @typedoc
|
|
5655
|
+
*/
|
|
5656
|
+
const CacheHandler = {
|
|
5657
|
+
request(context, next) {
|
|
5658
|
+
// if we have no cache or no cache-key skip cache handling
|
|
5659
|
+
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
5660
|
+
return next(context.request);
|
|
5459
5661
|
}
|
|
5460
|
-
assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
|
|
5461
5662
|
const {
|
|
5462
|
-
|
|
5463
|
-
} =
|
|
5663
|
+
store
|
|
5664
|
+
} = context.request;
|
|
5665
|
+
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
5666
|
+
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
5464
5667
|
|
|
5465
|
-
//
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
}
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
properties[prop] = relationshipValue;
|
|
5485
|
-
}
|
|
5486
|
-
}
|
|
5668
|
+
// determine if we should skip cache
|
|
5669
|
+
if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
|
|
5670
|
+
return fetchContentAndHydrate(next, context, identifier, true, false);
|
|
5671
|
+
}
|
|
5672
|
+
|
|
5673
|
+
// if we have not skipped cache, determine if we should update behind the scenes
|
|
5674
|
+
if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
|
|
5675
|
+
const promise = fetchContentAndHydrate(next, context, identifier, false, true);
|
|
5676
|
+
store.requestManager._pending.set(context.id, promise);
|
|
5677
|
+
}
|
|
5678
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
5679
|
+
if ('error' in peeked) {
|
|
5680
|
+
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
5681
|
+
shouldHydrate,
|
|
5682
|
+
identifier
|
|
5683
|
+
}, peeked.content, true) : peeked.content;
|
|
5684
|
+
const newError = cloneError(peeked);
|
|
5685
|
+
newError.content = content;
|
|
5686
|
+
throw newError;
|
|
5487
5687
|
}
|
|
5688
|
+
const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
5689
|
+
shouldHydrate,
|
|
5690
|
+
identifier
|
|
5691
|
+
}, peeked.content, true) : peeked.content;
|
|
5692
|
+
return result;
|
|
5693
|
+
}
|
|
5694
|
+
};
|
|
5695
|
+
function copyDocumentProperties(target, source) {
|
|
5696
|
+
if ('links' in source) {
|
|
5697
|
+
target.links = source.links;
|
|
5698
|
+
}
|
|
5699
|
+
if ('meta' in source) {
|
|
5700
|
+
target.meta = source.meta;
|
|
5701
|
+
}
|
|
5702
|
+
if ('errors' in source) {
|
|
5703
|
+
target.errors = source.errors;
|
|
5488
5704
|
}
|
|
5489
|
-
return properties;
|
|
5490
|
-
}
|
|
5491
|
-
function assertRecordsPassedToHasMany(records) {
|
|
5492
|
-
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
5493
|
-
assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
|
|
5494
|
-
return records.every(record => {
|
|
5495
|
-
try {
|
|
5496
|
-
recordIdentifierFor(record);
|
|
5497
|
-
return true;
|
|
5498
|
-
} catch {
|
|
5499
|
-
return false;
|
|
5500
|
-
}
|
|
5501
|
-
});
|
|
5502
|
-
}());
|
|
5503
|
-
}
|
|
5504
|
-
function extractIdentifiersFromRecords(records) {
|
|
5505
|
-
return records.map(record => extractIdentifierFromRecord(record));
|
|
5506
5705
|
}
|
|
5507
|
-
function
|
|
5508
|
-
if (!
|
|
5509
|
-
return
|
|
5706
|
+
function isCacheAffecting(document) {
|
|
5707
|
+
if (!isMutation(document.request)) {
|
|
5708
|
+
return true;
|
|
5510
5709
|
}
|
|
5511
|
-
|
|
5512
|
-
|
|
5710
|
+
// a mutation combined with a 204 has no cache impact when no known records were involved
|
|
5711
|
+
// a createRecord with a 201 with an empty response and no known records should similarly
|
|
5712
|
+
// have no cache impact
|
|
5713
|
+
|
|
5714
|
+
if (document.request.op === 'createRecord' && document.response?.status === 201) {
|
|
5715
|
+
return document.content ? Object.keys(document.content).length > 0 : false;
|
|
5716
|
+
}
|
|
5717
|
+
return document.response?.status !== 204;
|
|
5513
5718
|
}
|
|
5514
|
-
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,
|
|
5719
|
+
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 };
|