@ember-data/store 5.4.0-alpha.6 → 5.4.0-alpha.61
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 +16 -4
- package/addon/-private.js +1 -1
- package/addon/{store-service-7ffc4d68.js → cache-handler-Cm55UqNi.js} +1245 -1033
- package/addon/cache-handler-Cm55UqNi.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 +65 -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 +177 -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 +1557 -0
- package/unstable-preview-types/-private/store-service.d.ts.map +1 -0
- package/unstable-preview-types/-private/store-service.type-test.d.ts +4 -0
- package/unstable-preview-types/-private/store-service.type-test.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 +21 -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 +195 -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 +38 -0
- package/unstable-preview-types/-types/q/store.d.ts.map +1 -0
- package/unstable-preview-types/index.d.ts +223 -0
- package/unstable-preview-types/index.d.ts.map +1 -0
- package/addon/store-service-7ffc4d68.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,10 +147,12 @@ 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;
|
|
509
154
|
let configuredUpdateMethod;
|
|
155
|
+
let configuredKeyInfoMethod;
|
|
510
156
|
function setIdentifierGenerationMethod(method) {
|
|
511
157
|
configuredGenerationMethod = method;
|
|
512
158
|
}
|
|
@@ -519,6 +165,9 @@ function setIdentifierForgetMethod(method) {
|
|
|
519
165
|
function setIdentifierResetMethod(method) {
|
|
520
166
|
configuredResetMethod = method;
|
|
521
167
|
}
|
|
168
|
+
function setKeyInfoForResource(method) {
|
|
169
|
+
configuredKeyInfoMethod = method;
|
|
170
|
+
}
|
|
522
171
|
|
|
523
172
|
// Map<type, Map<id, lid>>
|
|
524
173
|
|
|
@@ -604,13 +253,14 @@ class IdentifierCache {
|
|
|
604
253
|
this._forget = configuredForgetMethod || defaultEmptyCallback;
|
|
605
254
|
this._reset = configuredResetMethod || defaultEmptyCallback;
|
|
606
255
|
this._merge = defaultMergeMethod;
|
|
607
|
-
this._keyInfoForResource = defaultKeyInfoMethod;
|
|
256
|
+
this._keyInfoForResource = configuredKeyInfoMethod || defaultKeyInfoMethod;
|
|
608
257
|
this._isDefaultConfig = !configuredGenerationMethod;
|
|
609
258
|
this._id = IDENTIFIER_CACHE_ID++;
|
|
610
259
|
this._cache = {
|
|
611
260
|
resources: new Map(),
|
|
612
261
|
resourcesByType: Object.create(null),
|
|
613
|
-
documents: new Map()
|
|
262
|
+
documents: new Map(),
|
|
263
|
+
polymorphicLidBackMap: new Map()
|
|
614
264
|
};
|
|
615
265
|
}
|
|
616
266
|
|
|
@@ -706,7 +356,7 @@ class IdentifierCache {
|
|
|
706
356
|
*
|
|
707
357
|
* @method peekRecordIdentifier
|
|
708
358
|
* @param resource
|
|
709
|
-
* @
|
|
359
|
+
* @return {StableRecordIdentifier | undefined}
|
|
710
360
|
* @private
|
|
711
361
|
*/
|
|
712
362
|
peekRecordIdentifier(resource) {
|
|
@@ -718,7 +368,7 @@ class IdentifierCache {
|
|
|
718
368
|
Returns `null` if the request does not have a `cacheKey` or `url`.
|
|
719
369
|
@method getOrCreateDocumentIdentifier
|
|
720
370
|
@param request
|
|
721
|
-
@
|
|
371
|
+
@return {StableDocumentIdentifier | null}
|
|
722
372
|
@public
|
|
723
373
|
*/
|
|
724
374
|
getOrCreateDocumentIdentifier(request) {
|
|
@@ -752,7 +402,7 @@ class IdentifierCache {
|
|
|
752
402
|
- this referential stability of the object itself is guaranteed
|
|
753
403
|
@method getOrCreateRecordIdentifier
|
|
754
404
|
@param resource
|
|
755
|
-
@
|
|
405
|
+
@return {StableRecordIdentifier}
|
|
756
406
|
@public
|
|
757
407
|
*/
|
|
758
408
|
getOrCreateRecordIdentifier(resource) {
|
|
@@ -767,12 +417,12 @@ class IdentifierCache {
|
|
|
767
417
|
with the signature `generateMethod({ type }, 'record')`.
|
|
768
418
|
@method createIdentifierForNewRecord
|
|
769
419
|
@param data
|
|
770
|
-
@
|
|
420
|
+
@return {StableRecordIdentifier}
|
|
771
421
|
@public
|
|
772
422
|
*/
|
|
773
423
|
createIdentifierForNewRecord(data) {
|
|
774
|
-
|
|
775
|
-
|
|
424
|
+
const newLid = this._generate(data, 'record');
|
|
425
|
+
const identifier = /*#__NOINLINE__*/makeStableRecordIdentifier({
|
|
776
426
|
id: data.id || null,
|
|
777
427
|
type: data.type,
|
|
778
428
|
lid: newLid,
|
|
@@ -810,7 +460,7 @@ class IdentifierCache {
|
|
|
810
460
|
@method updateRecordIdentifier
|
|
811
461
|
@param identifierObject
|
|
812
462
|
@param data
|
|
813
|
-
@
|
|
463
|
+
@return {StableRecordIdentifier}
|
|
814
464
|
@public
|
|
815
465
|
*/
|
|
816
466
|
updateRecordIdentifier(identifierObject, data) {
|
|
@@ -830,7 +480,7 @@ class IdentifierCache {
|
|
|
830
480
|
}
|
|
831
481
|
}
|
|
832
482
|
if (existingIdentifier) {
|
|
833
|
-
|
|
483
|
+
const generatedIdentifier = identifier;
|
|
834
484
|
identifier = this._mergeRecordIdentifiers(keyInfo, generatedIdentifier, existingIdentifier, data);
|
|
835
485
|
|
|
836
486
|
// make sure that the `lid` on the data we are processing matches the lid we kept
|
|
@@ -842,7 +492,7 @@ class IdentifierCache {
|
|
|
842
492
|
console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
|
|
843
493
|
}
|
|
844
494
|
}
|
|
845
|
-
|
|
495
|
+
const id = identifier.id;
|
|
846
496
|
/*#__NOINLINE__*/
|
|
847
497
|
performRecordIdentifierUpdate(identifier, keyInfo, data, this._update);
|
|
848
498
|
const newId = identifier.id;
|
|
@@ -876,16 +526,32 @@ class IdentifierCache {
|
|
|
876
526
|
const kept = this._merge(identifier, existingIdentifier, data);
|
|
877
527
|
const abandoned = kept === identifier ? existingIdentifier : identifier;
|
|
878
528
|
|
|
529
|
+
// get any backreferences before forgetting this identifier, as it will be removed from the cache
|
|
530
|
+
// and we will no longer be able to find them
|
|
531
|
+
const abandonedBackReferences = this._cache.polymorphicLidBackMap.get(abandoned.lid);
|
|
532
|
+
// delete the backreferences for the abandoned identifier so that forgetRecordIdentifier
|
|
533
|
+
// does not try to remove them.
|
|
534
|
+
if (abandonedBackReferences) this._cache.polymorphicLidBackMap.delete(abandoned.lid);
|
|
535
|
+
|
|
879
536
|
// cleanup the identifier we no longer need
|
|
880
537
|
this.forgetRecordIdentifier(abandoned);
|
|
881
538
|
|
|
882
|
-
// ensure a secondary cache entry for
|
|
883
|
-
|
|
539
|
+
// ensure a secondary cache entry for the original lid for the abandoned identifier
|
|
540
|
+
this._cache.resources.set(abandoned.lid, kept);
|
|
884
541
|
|
|
885
|
-
//
|
|
886
|
-
//
|
|
887
|
-
|
|
542
|
+
// backReferences let us know which other identifiers are pointing at this identifier
|
|
543
|
+
// so we can delete them later if we forget this identifier
|
|
544
|
+
const keptBackReferences = this._cache.polymorphicLidBackMap.get(kept.lid) ?? [];
|
|
545
|
+
keptBackReferences.push(abandoned.lid);
|
|
888
546
|
|
|
547
|
+
// update the backreferences from the abandoned identifier to be for the kept identifier
|
|
548
|
+
if (abandonedBackReferences) {
|
|
549
|
+
abandonedBackReferences.forEach(lid => {
|
|
550
|
+
keptBackReferences.push(lid);
|
|
551
|
+
this._cache.resources.set(lid, kept);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
this._cache.polymorphicLidBackMap.set(kept.lid, keptBackReferences);
|
|
889
555
|
return kept;
|
|
890
556
|
}
|
|
891
557
|
|
|
@@ -908,6 +574,13 @@ class IdentifierCache {
|
|
|
908
574
|
}
|
|
909
575
|
this._cache.resources.delete(identifier.lid);
|
|
910
576
|
typeSet.lid.delete(identifier.lid);
|
|
577
|
+
const backReferences = this._cache.polymorphicLidBackMap.get(identifier.lid);
|
|
578
|
+
if (backReferences) {
|
|
579
|
+
backReferences.forEach(lid => {
|
|
580
|
+
this._cache.resources.delete(lid);
|
|
581
|
+
});
|
|
582
|
+
this._cache.polymorphicLidBackMap.delete(identifier.lid);
|
|
583
|
+
}
|
|
911
584
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
912
585
|
identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
|
|
913
586
|
}
|
|
@@ -953,17 +626,22 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
953
626
|
},
|
|
954
627
|
set [DEBUG_STALE_CACHE_OWNER](value) {
|
|
955
628
|
recordIdentifier[DEBUG_STALE_CACHE_OWNER] = value;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
Object.defineProperty(wrapper, 'toString', {
|
|
632
|
+
enumerable: false,
|
|
633
|
+
value: () => {
|
|
959
634
|
const {
|
|
960
635
|
type,
|
|
961
636
|
id,
|
|
962
637
|
lid
|
|
963
638
|
} = recordIdentifier;
|
|
964
639
|
return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
|
|
965
|
-
}
|
|
966
|
-
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
Object.defineProperty(wrapper, 'toJSON', {
|
|
643
|
+
enumerable: false,
|
|
644
|
+
value: () => {
|
|
967
645
|
const {
|
|
968
646
|
type,
|
|
969
647
|
id,
|
|
@@ -975,7 +653,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
|
|
|
975
653
|
lid
|
|
976
654
|
};
|
|
977
655
|
}
|
|
978
|
-
};
|
|
656
|
+
});
|
|
979
657
|
wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
|
|
980
658
|
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
|
|
981
659
|
IDENTIFIERS.add(wrapper);
|
|
@@ -993,7 +671,7 @@ function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
|
|
|
993
671
|
} = keyInfo;
|
|
994
672
|
|
|
995
673
|
// get the mutable instance behind our proxy wrapper
|
|
996
|
-
|
|
674
|
+
const wrapper = identifier;
|
|
997
675
|
identifier = DEBUG_MAP.get(wrapper);
|
|
998
676
|
if (hasLid(data)) {
|
|
999
677
|
const lid = data.lid;
|
|
@@ -1082,28 +760,27 @@ function addResourceToCache(cache, identifier) {
|
|
|
1082
760
|
typeSet.id.set(identifier.id, identifier);
|
|
1083
761
|
}
|
|
1084
762
|
}
|
|
1085
|
-
var _class$1, _descriptor$1;
|
|
1086
763
|
|
|
1087
764
|
/**
|
|
1088
765
|
@module @ember-data/store
|
|
1089
766
|
*/
|
|
767
|
+
|
|
1090
768
|
/**
|
|
1091
769
|
@module @ember-data/store
|
|
1092
770
|
*/
|
|
771
|
+
|
|
1093
772
|
/**
|
|
1094
773
|
A `RecordReference` is a low-level API that allows users and
|
|
1095
774
|
addon authors to perform meta-operations on a record.
|
|
1096
775
|
|
|
1097
776
|
@class RecordReference
|
|
1098
777
|
@public
|
|
1099
|
-
@extends Reference
|
|
1100
778
|
*/
|
|
1101
|
-
|
|
779
|
+
class RecordReference {
|
|
1102
780
|
constructor(store, identifier) {
|
|
1103
781
|
// unsubscribe token given to us by the notification manager
|
|
1104
782
|
this.___token = void 0;
|
|
1105
783
|
this.___identifier = void 0;
|
|
1106
|
-
_initializerDefineProperty(this, "_ref", _descriptor$1, this);
|
|
1107
784
|
this.store = store;
|
|
1108
785
|
this.___identifier = identifier;
|
|
1109
786
|
this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
|
|
@@ -1271,14 +948,8 @@ let RecordReference = (_class$1 = class RecordReference {
|
|
|
1271
948
|
}
|
|
1272
949
|
assert(`Unable to fetch record of type ${this.type} without an id`);
|
|
1273
950
|
}
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
enumerable: true,
|
|
1277
|
-
writable: true,
|
|
1278
|
-
initializer: function () {
|
|
1279
|
-
return 0;
|
|
1280
|
-
}
|
|
1281
|
-
}), _class$1);
|
|
951
|
+
}
|
|
952
|
+
defineSignal(RecordReference.prototype, '_ref');
|
|
1282
953
|
|
|
1283
954
|
/**
|
|
1284
955
|
@module @ember-data/store
|
|
@@ -1309,6 +980,8 @@ class CacheCapabilitiesManager {
|
|
|
1309
980
|
if (this._store._cbs) {
|
|
1310
981
|
this._store._schedule('notify', () => this._flushNotifications());
|
|
1311
982
|
} else {
|
|
983
|
+
// TODO @runspired determine if relationship mutations should schedule
|
|
984
|
+
// into join/run vs immediate flush
|
|
1312
985
|
this._flushNotifications();
|
|
1313
986
|
}
|
|
1314
987
|
}
|
|
@@ -1316,7 +989,7 @@ class CacheCapabilitiesManager {
|
|
|
1316
989
|
if (this._willNotify === false) {
|
|
1317
990
|
return;
|
|
1318
991
|
}
|
|
1319
|
-
|
|
992
|
+
const pending = this._pendingNotifies;
|
|
1320
993
|
this._pendingNotifies = new Map();
|
|
1321
994
|
this._willNotify = false;
|
|
1322
995
|
pending.forEach((set, identifier) => {
|
|
@@ -1340,6 +1013,9 @@ class CacheCapabilitiesManager {
|
|
|
1340
1013
|
getSchemaDefinitionService() {
|
|
1341
1014
|
return this._store.getSchemaDefinitionService();
|
|
1342
1015
|
}
|
|
1016
|
+
get schema() {
|
|
1017
|
+
return this._store.schema;
|
|
1018
|
+
}
|
|
1343
1019
|
setRecordId(identifier, id) {
|
|
1344
1020
|
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
1345
1021
|
this._store._instanceCache.setRecordId(identifier, id);
|
|
@@ -1373,6 +1049,9 @@ function peekCache(instance) {
|
|
|
1373
1049
|
}
|
|
1374
1050
|
return null;
|
|
1375
1051
|
}
|
|
1052
|
+
function isDestroyable(record) {
|
|
1053
|
+
return Boolean(record && typeof record === 'object' && typeof record.destroy === 'function');
|
|
1054
|
+
}
|
|
1376
1055
|
|
|
1377
1056
|
/**
|
|
1378
1057
|
@module @ember-data/store
|
|
@@ -1400,8 +1079,9 @@ function peekRecordIdentifier(record) {
|
|
|
1400
1079
|
@static
|
|
1401
1080
|
@for @ember-data/store
|
|
1402
1081
|
@param {Object} record a record instance previously obstained from the store.
|
|
1403
|
-
@
|
|
1082
|
+
@return {StableRecordIdentifier}
|
|
1404
1083
|
*/
|
|
1084
|
+
|
|
1405
1085
|
function recordIdentifierFor(record) {
|
|
1406
1086
|
assert(`${String(record)} is not a record instantiated by @ember-data/store`, RecordCache.has(record));
|
|
1407
1087
|
return RecordCache.get(record);
|
|
@@ -1447,11 +1127,11 @@ class InstanceCache {
|
|
|
1447
1127
|
// @ts-expect-error TODO this needs to be fixed
|
|
1448
1128
|
'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
|
|
1449
1129
|
}
|
|
1450
|
-
|
|
1130
|
+
const staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
|
|
1451
1131
|
|
|
1452
1132
|
// check for duplicate entities
|
|
1453
|
-
|
|
1454
|
-
|
|
1133
|
+
const keptHasRecord = this.__instances.record.has(keptIdentifier);
|
|
1134
|
+
const staleHasRecord = this.__instances.record.has(staleIdentifier);
|
|
1455
1135
|
|
|
1456
1136
|
// we cannot merge entities when both have records
|
|
1457
1137
|
// (this may not be strictly true, we could probably swap the cache data the record points at)
|
|
@@ -1505,7 +1185,7 @@ class InstanceCache {
|
|
|
1505
1185
|
return record;
|
|
1506
1186
|
}
|
|
1507
1187
|
getReference(identifier) {
|
|
1508
|
-
|
|
1188
|
+
const cache = this.__instances.reference;
|
|
1509
1189
|
let reference = cache.get(identifier);
|
|
1510
1190
|
if (!reference) {
|
|
1511
1191
|
reference = new RecordReference(this.store, identifier);
|
|
@@ -1536,7 +1216,7 @@ class InstanceCache {
|
|
|
1536
1216
|
}
|
|
1537
1217
|
disconnect(identifier) {
|
|
1538
1218
|
const record = this.__instances.record.get(identifier);
|
|
1539
|
-
assert('Cannot destroy record while it is still materialized', !record || record.isDestroyed || record.isDestroying);
|
|
1219
|
+
assert('Cannot destroy record while it is still materialized', !isDestroyable(record) || record.isDestroyed || record.isDestroying);
|
|
1540
1220
|
this.store._graph?.remove(identifier);
|
|
1541
1221
|
this.store.identifierCache.forgetRecordIdentifier(identifier);
|
|
1542
1222
|
removeRecordDataFor(identifier);
|
|
@@ -1604,7 +1284,7 @@ class InstanceCache {
|
|
|
1604
1284
|
});
|
|
1605
1285
|
} else {
|
|
1606
1286
|
const typeCache = cache.resourcesByType;
|
|
1607
|
-
|
|
1287
|
+
const identifiers = typeCache[type]?.lid;
|
|
1608
1288
|
if (identifiers) {
|
|
1609
1289
|
identifiers.forEach(identifier => {
|
|
1610
1290
|
// if (rds.has(identifier)) {
|
|
@@ -1622,7 +1302,7 @@ class InstanceCache {
|
|
|
1622
1302
|
type,
|
|
1623
1303
|
lid
|
|
1624
1304
|
} = identifier;
|
|
1625
|
-
|
|
1305
|
+
const oldId = identifier.id;
|
|
1626
1306
|
|
|
1627
1307
|
// ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
|
|
1628
1308
|
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 +1321,7 @@ class InstanceCache {
|
|
|
1641
1321
|
// eslint-disable-next-line no-console
|
|
1642
1322
|
console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
|
|
1643
1323
|
}
|
|
1644
|
-
|
|
1324
|
+
const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
|
|
1645
1325
|
type,
|
|
1646
1326
|
id
|
|
1647
1327
|
});
|
|
@@ -1680,13 +1360,13 @@ function resourceIsFullyDeleted(instanceCache, identifier) {
|
|
|
1680
1360
|
*/
|
|
1681
1361
|
|
|
1682
1362
|
function preloadData(store, identifier, preload) {
|
|
1683
|
-
|
|
1363
|
+
const jsonPayload = {};
|
|
1684
1364
|
//TODO(Igor) consider the polymorphic case
|
|
1685
1365
|
const schemas = store.getSchemaDefinitionService();
|
|
1686
1366
|
const relationships = schemas.relationshipsDefinitionFor(identifier);
|
|
1687
1367
|
Object.keys(preload).forEach(key => {
|
|
1688
|
-
|
|
1689
|
-
|
|
1368
|
+
const preloadValue = preload[key];
|
|
1369
|
+
const relationshipMeta = relationships[key];
|
|
1690
1370
|
if (relationshipMeta) {
|
|
1691
1371
|
if (!jsonPayload.relationships) {
|
|
1692
1372
|
jsonPayload.relationships = {};
|
|
@@ -1755,7 +1435,7 @@ function getShimClass(store, modelName) {
|
|
|
1755
1435
|
}
|
|
1756
1436
|
function mapFromHash(hash) {
|
|
1757
1437
|
const map = new Map();
|
|
1758
|
-
for (
|
|
1438
|
+
for (const i in hash) {
|
|
1759
1439
|
if (Object.prototype.hasOwnProperty.call(hash, i)) {
|
|
1760
1440
|
map.set(i, hash[i]);
|
|
1761
1441
|
}
|
|
@@ -1770,31 +1450,31 @@ class ShimModelClass {
|
|
|
1770
1450
|
this.modelName = modelName;
|
|
1771
1451
|
}
|
|
1772
1452
|
get fields() {
|
|
1773
|
-
|
|
1453
|
+
const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1774
1454
|
type: this.modelName
|
|
1775
1455
|
});
|
|
1776
|
-
|
|
1456
|
+
const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1777
1457
|
type: this.modelName
|
|
1778
1458
|
});
|
|
1779
|
-
|
|
1459
|
+
const fields = new Map();
|
|
1780
1460
|
Object.keys(attrs).forEach(key => fields.set(key, 'attribute'));
|
|
1781
1461
|
Object.keys(relationships).forEach(key => fields.set(key, relationships[key].kind));
|
|
1782
1462
|
return fields;
|
|
1783
1463
|
}
|
|
1784
1464
|
get attributes() {
|
|
1785
|
-
|
|
1465
|
+
const attrs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1786
1466
|
type: this.modelName
|
|
1787
1467
|
});
|
|
1788
1468
|
return mapFromHash(attrs);
|
|
1789
1469
|
}
|
|
1790
1470
|
get relationshipsByName() {
|
|
1791
|
-
|
|
1471
|
+
const relationships = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1792
1472
|
type: this.modelName
|
|
1793
1473
|
});
|
|
1794
1474
|
return mapFromHash(relationships);
|
|
1795
1475
|
}
|
|
1796
1476
|
eachAttribute(callback, binding) {
|
|
1797
|
-
|
|
1477
|
+
const attrDefs = this.__store.getSchemaDefinitionService().attributesDefinitionFor({
|
|
1798
1478
|
type: this.modelName
|
|
1799
1479
|
});
|
|
1800
1480
|
Object.keys(attrDefs).forEach(key => {
|
|
@@ -1802,7 +1482,7 @@ class ShimModelClass {
|
|
|
1802
1482
|
});
|
|
1803
1483
|
}
|
|
1804
1484
|
eachRelationship(callback, binding) {
|
|
1805
|
-
|
|
1485
|
+
const relationshipDefs = this.__store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
1806
1486
|
type: this.modelName
|
|
1807
1487
|
});
|
|
1808
1488
|
Object.keys(relationshipDefs).forEach(key => {
|
|
@@ -1820,6 +1500,16 @@ class ShimModelClass {
|
|
|
1820
1500
|
});
|
|
1821
1501
|
}
|
|
1822
1502
|
}
|
|
1503
|
+
function _classPrivateFieldBase(receiver, privateKey) {
|
|
1504
|
+
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
|
|
1505
|
+
throw new TypeError("attempted to use private field on non-instance");
|
|
1506
|
+
}
|
|
1507
|
+
return receiver;
|
|
1508
|
+
}
|
|
1509
|
+
var id = 0;
|
|
1510
|
+
function _classPrivateFieldKey(name) {
|
|
1511
|
+
return "__private_" + id++ + "_" + name;
|
|
1512
|
+
}
|
|
1823
1513
|
var _cache = /*#__PURE__*/_classPrivateFieldKey("cache");
|
|
1824
1514
|
/**
|
|
1825
1515
|
* The CacheManager wraps a Cache enforcing that only
|
|
@@ -1846,16 +1536,6 @@ class CacheManager {
|
|
|
1846
1536
|
writable: true,
|
|
1847
1537
|
value: void 0
|
|
1848
1538
|
});
|
|
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
1539
|
_classPrivateFieldBase(this, _cache)[_cache] = cache;
|
|
1860
1540
|
}
|
|
1861
1541
|
|
|
@@ -1869,7 +1549,7 @@ class CacheManager {
|
|
|
1869
1549
|
* semantics, `put` has `replace` semantics similar to
|
|
1870
1550
|
* the `http` method `PUT`
|
|
1871
1551
|
*
|
|
1872
|
-
* the individually
|
|
1552
|
+
* the individually cacheable
|
|
1873
1553
|
* e resource data it may contain
|
|
1874
1554
|
* should upsert, but the document data surrounding it should
|
|
1875
1555
|
* fully replace any existing information
|
|
@@ -1882,7 +1562,7 @@ class CacheManager {
|
|
|
1882
1562
|
*
|
|
1883
1563
|
* @method put
|
|
1884
1564
|
* @param {StructuredDocument} doc
|
|
1885
|
-
* @
|
|
1565
|
+
* @return {ResourceDocument}
|
|
1886
1566
|
* @public
|
|
1887
1567
|
*/
|
|
1888
1568
|
put(doc) {
|
|
@@ -1898,7 +1578,7 @@ class CacheManager {
|
|
|
1898
1578
|
* @method patch
|
|
1899
1579
|
* @public
|
|
1900
1580
|
* @param op the operation to perform
|
|
1901
|
-
* @
|
|
1581
|
+
* @return {void}
|
|
1902
1582
|
*/
|
|
1903
1583
|
patch(op) {
|
|
1904
1584
|
_classPrivateFieldBase(this, _cache)[_cache].patch(op);
|
|
@@ -1933,7 +1613,7 @@ class CacheManager {
|
|
|
1933
1613
|
* An implementation might want to do this because
|
|
1934
1614
|
* de-referencing records which read from their own
|
|
1935
1615
|
* blob is generally safer because the record does
|
|
1936
|
-
* not require
|
|
1616
|
+
* not require retaining connections to the Store
|
|
1937
1617
|
* and Cache to present data on a per-field basis.
|
|
1938
1618
|
*
|
|
1939
1619
|
* This generally takes the place of `getAttr` as
|
|
@@ -1946,7 +1626,7 @@ class CacheManager {
|
|
|
1946
1626
|
* @method peek
|
|
1947
1627
|
* @public
|
|
1948
1628
|
* @param {StableRecordIdentifier | StableDocumentIdentifier} identifier
|
|
1949
|
-
* @
|
|
1629
|
+
* @return {ResourceDocument | ResourceBlob | null} the known resource data
|
|
1950
1630
|
*/
|
|
1951
1631
|
|
|
1952
1632
|
peek(identifier) {
|
|
@@ -1959,7 +1639,7 @@ class CacheManager {
|
|
|
1959
1639
|
*
|
|
1960
1640
|
* @method peekRequest
|
|
1961
1641
|
* @param {StableDocumentIdentifier}
|
|
1962
|
-
* @
|
|
1642
|
+
* @return {StableDocumentIdentifier | null}
|
|
1963
1643
|
* @public
|
|
1964
1644
|
*/
|
|
1965
1645
|
peekRequest(identifier) {
|
|
@@ -1974,7 +1654,7 @@ class CacheManager {
|
|
|
1974
1654
|
* @param identifier
|
|
1975
1655
|
* @param data
|
|
1976
1656
|
* @param hasRecord
|
|
1977
|
-
* @
|
|
1657
|
+
* @return {void | string[]} if `hasRecord` is true then calculated key changes should be returned
|
|
1978
1658
|
*/
|
|
1979
1659
|
upsert(identifier, data, hasRecord) {
|
|
1980
1660
|
return _classPrivateFieldBase(this, _cache)[_cache].upsert(identifier, data, hasRecord);
|
|
@@ -1992,7 +1672,7 @@ class CacheManager {
|
|
|
1992
1672
|
*
|
|
1993
1673
|
* @method fork
|
|
1994
1674
|
* @public
|
|
1995
|
-
* @
|
|
1675
|
+
* @return Promise<Cache>
|
|
1996
1676
|
*/
|
|
1997
1677
|
fork() {
|
|
1998
1678
|
return _classPrivateFieldBase(this, _cache)[_cache].fork();
|
|
@@ -2008,7 +1688,7 @@ class CacheManager {
|
|
|
2008
1688
|
* @method merge
|
|
2009
1689
|
* @param {Cache} cache
|
|
2010
1690
|
* @public
|
|
2011
|
-
* @
|
|
1691
|
+
* @return Promise<void>
|
|
2012
1692
|
*/
|
|
2013
1693
|
merge(cache) {
|
|
2014
1694
|
return _classPrivateFieldBase(this, _cache)[_cache].merge(cache);
|
|
@@ -2060,7 +1740,7 @@ class CacheManager {
|
|
|
2060
1740
|
* via `cache.hydrate`.
|
|
2061
1741
|
*
|
|
2062
1742
|
* @method dump
|
|
2063
|
-
* @
|
|
1743
|
+
* @return {Promise<ReadableStream>}
|
|
2064
1744
|
* @public
|
|
2065
1745
|
*/
|
|
2066
1746
|
dump() {
|
|
@@ -2081,7 +1761,7 @@ class CacheManager {
|
|
|
2081
1761
|
*
|
|
2082
1762
|
* @method hydrate
|
|
2083
1763
|
* @param {ReadableStream} stream
|
|
2084
|
-
* @
|
|
1764
|
+
* @return {Promise<void>}
|
|
2085
1765
|
* @public
|
|
2086
1766
|
*/
|
|
2087
1767
|
hydrate(stream) {
|
|
@@ -2095,7 +1775,7 @@ class CacheManager {
|
|
|
2095
1775
|
// ================
|
|
2096
1776
|
|
|
2097
1777
|
/**
|
|
2098
|
-
* [
|
|
1778
|
+
* [LIFECYCLE] Signal to the cache that a new record has been instantiated on the client
|
|
2099
1779
|
*
|
|
2100
1780
|
* It returns properties from options that should be set on the record during the create
|
|
2101
1781
|
* process. This return value behavior is deprecated.
|
|
@@ -2169,7 +1849,7 @@ class CacheManager {
|
|
|
2169
1849
|
* @public
|
|
2170
1850
|
* @param identifier
|
|
2171
1851
|
* @param propertyName
|
|
2172
|
-
* @
|
|
1852
|
+
* @return {unknown}
|
|
2173
1853
|
*/
|
|
2174
1854
|
getAttr(identifier, propertyName) {
|
|
2175
1855
|
return _classPrivateFieldBase(this, _cache)[_cache].getAttr(identifier, propertyName);
|
|
@@ -2194,7 +1874,7 @@ class CacheManager {
|
|
|
2194
1874
|
* @method changedAttrs
|
|
2195
1875
|
* @public
|
|
2196
1876
|
* @param identifier
|
|
2197
|
-
* @
|
|
1877
|
+
* @return
|
|
2198
1878
|
*/
|
|
2199
1879
|
changedAttrs(identifier) {
|
|
2200
1880
|
return _classPrivateFieldBase(this, _cache)[_cache].changedAttrs(identifier);
|
|
@@ -2206,7 +1886,7 @@ class CacheManager {
|
|
|
2206
1886
|
* @method hasChangedAttrs
|
|
2207
1887
|
* @public
|
|
2208
1888
|
* @param identifier
|
|
2209
|
-
* @
|
|
1889
|
+
* @return {boolean}
|
|
2210
1890
|
*/
|
|
2211
1891
|
hasChangedAttrs(identifier) {
|
|
2212
1892
|
return _classPrivateFieldBase(this, _cache)[_cache].hasChangedAttrs(identifier);
|
|
@@ -2218,7 +1898,7 @@ class CacheManager {
|
|
|
2218
1898
|
* @method rollbackAttrs
|
|
2219
1899
|
* @public
|
|
2220
1900
|
* @param identifier
|
|
2221
|
-
* @
|
|
1901
|
+
* @return the names of attributes that were restored
|
|
2222
1902
|
*/
|
|
2223
1903
|
rollbackAttrs(identifier) {
|
|
2224
1904
|
return _classPrivateFieldBase(this, _cache)[_cache].rollbackAttrs(identifier);
|
|
@@ -2227,6 +1907,65 @@ class CacheManager {
|
|
|
2227
1907
|
// Relationships
|
|
2228
1908
|
// =============
|
|
2229
1909
|
|
|
1910
|
+
/**
|
|
1911
|
+
* Query the cache for the changes to relationships of a resource.
|
|
1912
|
+
*
|
|
1913
|
+
* Returns a map of relationship names to RelationshipDiff objects.
|
|
1914
|
+
*
|
|
1915
|
+
* ```ts
|
|
1916
|
+
* type RelationshipDiff =
|
|
1917
|
+
| {
|
|
1918
|
+
kind: 'collection';
|
|
1919
|
+
remoteState: StableRecordIdentifier[];
|
|
1920
|
+
additions: Set<StableRecordIdentifier>;
|
|
1921
|
+
removals: Set<StableRecordIdentifier>;
|
|
1922
|
+
localState: StableRecordIdentifier[];
|
|
1923
|
+
reordered: boolean;
|
|
1924
|
+
}
|
|
1925
|
+
| {
|
|
1926
|
+
kind: 'resource';
|
|
1927
|
+
remoteState: StableRecordIdentifier | null;
|
|
1928
|
+
localState: StableRecordIdentifier | null;
|
|
1929
|
+
};
|
|
1930
|
+
```
|
|
1931
|
+
*
|
|
1932
|
+
* @method changedRelationships
|
|
1933
|
+
* @public
|
|
1934
|
+
* @param {StableRecordIdentifier} identifier
|
|
1935
|
+
* @return {Map<string, RelationshipDiff>}
|
|
1936
|
+
*/
|
|
1937
|
+
changedRelationships(identifier) {
|
|
1938
|
+
return _classPrivateFieldBase(this, _cache)[_cache].changedRelationships(identifier);
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
/**
|
|
1942
|
+
* Query the cache for whether any mutated attributes exist
|
|
1943
|
+
*
|
|
1944
|
+
* @method hasChangedRelationships
|
|
1945
|
+
* @public
|
|
1946
|
+
* @param {StableRecordIdentifier} identifier
|
|
1947
|
+
* @return {boolean}
|
|
1948
|
+
*/
|
|
1949
|
+
hasChangedRelationships(identifier) {
|
|
1950
|
+
return _classPrivateFieldBase(this, _cache)[_cache].hasChangedRelationships(identifier);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
/**
|
|
1954
|
+
* Tell the cache to discard any uncommitted mutations to relationships.
|
|
1955
|
+
*
|
|
1956
|
+
* This will also discard the change on any appropriate inverses.
|
|
1957
|
+
*
|
|
1958
|
+
* This method is a candidate to become a mutation
|
|
1959
|
+
*
|
|
1960
|
+
* @method rollbackRelationships
|
|
1961
|
+
* @public
|
|
1962
|
+
* @param {StableRecordIdentifier} identifier
|
|
1963
|
+
* @return {string[]} the names of relationships that were restored
|
|
1964
|
+
*/
|
|
1965
|
+
rollbackRelationships(identifier) {
|
|
1966
|
+
return _classPrivateFieldBase(this, _cache)[_cache].rollbackRelationships(identifier);
|
|
1967
|
+
}
|
|
1968
|
+
|
|
2230
1969
|
/**
|
|
2231
1970
|
* Query the cache for the current state of a relationship property
|
|
2232
1971
|
*
|
|
@@ -2234,7 +1973,7 @@ class CacheManager {
|
|
|
2234
1973
|
* @public
|
|
2235
1974
|
* @param identifier
|
|
2236
1975
|
* @param propertyName
|
|
2237
|
-
* @
|
|
1976
|
+
* @return resource relationship object
|
|
2238
1977
|
*/
|
|
2239
1978
|
getRelationship(identifier, propertyName) {
|
|
2240
1979
|
return _classPrivateFieldBase(this, _cache)[_cache].getRelationship(identifier, propertyName);
|
|
@@ -2262,7 +2001,7 @@ class CacheManager {
|
|
|
2262
2001
|
* @method getErrors
|
|
2263
2002
|
* @public
|
|
2264
2003
|
* @param identifier
|
|
2265
|
-
* @
|
|
2004
|
+
* @return
|
|
2266
2005
|
*/
|
|
2267
2006
|
getErrors(identifier) {
|
|
2268
2007
|
return _classPrivateFieldBase(this, _cache)[_cache].getErrors(identifier);
|
|
@@ -2274,7 +2013,7 @@ class CacheManager {
|
|
|
2274
2013
|
* @method isEmpty
|
|
2275
2014
|
* @public
|
|
2276
2015
|
* @param identifier
|
|
2277
|
-
* @
|
|
2016
|
+
* @return {boolean}
|
|
2278
2017
|
*/
|
|
2279
2018
|
isEmpty(identifier) {
|
|
2280
2019
|
return _classPrivateFieldBase(this, _cache)[_cache].isEmpty(identifier);
|
|
@@ -2287,7 +2026,7 @@ class CacheManager {
|
|
|
2287
2026
|
* @method isNew
|
|
2288
2027
|
* @public
|
|
2289
2028
|
* @param identifier
|
|
2290
|
-
* @
|
|
2029
|
+
* @return {boolean}
|
|
2291
2030
|
*/
|
|
2292
2031
|
isNew(identifier) {
|
|
2293
2032
|
return _classPrivateFieldBase(this, _cache)[_cache].isNew(identifier);
|
|
@@ -2300,15 +2039,29 @@ class CacheManager {
|
|
|
2300
2039
|
* @method isDeleted
|
|
2301
2040
|
* @public
|
|
2302
2041
|
* @param identifier
|
|
2303
|
-
* @
|
|
2042
|
+
* @return {boolean}
|
|
2304
2043
|
*/
|
|
2305
2044
|
isDeleted(identifier) {
|
|
2306
2045
|
return _classPrivateFieldBase(this, _cache)[_cache].isDeleted(identifier);
|
|
2307
2046
|
}
|
|
2047
|
+
|
|
2048
|
+
/**
|
|
2049
|
+
* Query the cache for whether a given resource has been deleted and that deletion
|
|
2050
|
+
* has also been persisted.
|
|
2051
|
+
*
|
|
2052
|
+
* @method isDeletionCommitted
|
|
2053
|
+
* @public
|
|
2054
|
+
* @param identifier
|
|
2055
|
+
* @return {boolean}
|
|
2056
|
+
*/
|
|
2308
2057
|
isDeletionCommitted(identifier) {
|
|
2309
2058
|
return _classPrivateFieldBase(this, _cache)[_cache].isDeletionCommitted(identifier);
|
|
2310
2059
|
}
|
|
2311
2060
|
}
|
|
2061
|
+
|
|
2062
|
+
/**
|
|
2063
|
+
* @module @ember-data/store
|
|
2064
|
+
*/
|
|
2312
2065
|
let tokenId = 0;
|
|
2313
2066
|
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
2314
2067
|
function isCacheOperationValue(value) {
|
|
@@ -2319,7 +2072,7 @@ function runLoopIsFlushing() {
|
|
|
2319
2072
|
return !!_backburner.currentInstance && _backburner._autorun !== true;
|
|
2320
2073
|
}
|
|
2321
2074
|
function _unsubscribe(tokens, token, cache) {
|
|
2322
|
-
|
|
2075
|
+
const identifier = tokens.get(token);
|
|
2323
2076
|
if (macroCondition(getOwnConfig().debug.LOG_NOTIFICATIONS)) {
|
|
2324
2077
|
if (!identifier) {
|
|
2325
2078
|
// eslint-disable-next-line no-console
|
|
@@ -2378,7 +2131,7 @@ class NotificationManager {
|
|
|
2378
2131
|
* @public
|
|
2379
2132
|
* @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
|
|
2380
2133
|
* @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
|
|
2381
|
-
* @
|
|
2134
|
+
* @return {UnsubscribeToken} an opaque token to be used with unsubscribe
|
|
2382
2135
|
*/
|
|
2383
2136
|
|
|
2384
2137
|
subscribe(identifier, callback) {
|
|
@@ -2388,7 +2141,7 @@ class NotificationManager {
|
|
|
2388
2141
|
map = new Map();
|
|
2389
2142
|
this._cache.set(identifier, map);
|
|
2390
2143
|
}
|
|
2391
|
-
|
|
2144
|
+
const unsubToken = macroCondition(getOwnConfig().env.DEBUG) ? {
|
|
2392
2145
|
_tokenRef: tokenId++
|
|
2393
2146
|
} : {};
|
|
2394
2147
|
map.set(unsubToken, callback);
|
|
@@ -2441,7 +2194,7 @@ class NotificationManager {
|
|
|
2441
2194
|
this._buffered.set(identifier, buffer);
|
|
2442
2195
|
}
|
|
2443
2196
|
buffer.push([value, key]);
|
|
2444
|
-
|
|
2197
|
+
this._scheduleNotify();
|
|
2445
2198
|
}
|
|
2446
2199
|
return hasSubscribers;
|
|
2447
2200
|
}
|
|
@@ -2483,14 +2236,14 @@ class NotificationManager {
|
|
|
2483
2236
|
|
|
2484
2237
|
// TODO for documents this will need to switch based on Identifier kind
|
|
2485
2238
|
if (isCacheOperationValue(value)) {
|
|
2486
|
-
|
|
2239
|
+
const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
|
|
2487
2240
|
if (callbackMap) {
|
|
2488
2241
|
callbackMap.forEach(cb => {
|
|
2489
2242
|
cb(identifier, value);
|
|
2490
2243
|
});
|
|
2491
2244
|
}
|
|
2492
2245
|
}
|
|
2493
|
-
|
|
2246
|
+
const callbackMap = this._cache.get(identifier);
|
|
2494
2247
|
if (!callbackMap || !callbackMap.size) {
|
|
2495
2248
|
return false;
|
|
2496
2249
|
}
|
|
@@ -2500,13 +2253,46 @@ class NotificationManager {
|
|
|
2500
2253
|
});
|
|
2501
2254
|
return true;
|
|
2502
2255
|
}
|
|
2503
|
-
destroy() {
|
|
2504
|
-
this.isDestroyed = true;
|
|
2505
|
-
this._tokens.clear();
|
|
2506
|
-
this._cache.clear();
|
|
2256
|
+
destroy() {
|
|
2257
|
+
this.isDestroyed = true;
|
|
2258
|
+
this._tokens.clear();
|
|
2259
|
+
this._cache.clear();
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
|
|
2263
|
+
var desc = {};
|
|
2264
|
+
Object.keys(descriptor).forEach(function (key) {
|
|
2265
|
+
desc[key] = descriptor[key];
|
|
2266
|
+
});
|
|
2267
|
+
desc.enumerable = !!desc.enumerable;
|
|
2268
|
+
desc.configurable = !!desc.configurable;
|
|
2269
|
+
if ('value' in desc || desc.initializer) {
|
|
2270
|
+
desc.writable = true;
|
|
2271
|
+
}
|
|
2272
|
+
desc = decorators.slice().reverse().reduce(function (desc, decorator) {
|
|
2273
|
+
return decorator(target, property, desc) || desc;
|
|
2274
|
+
}, desc);
|
|
2275
|
+
if (context && desc.initializer !== void 0) {
|
|
2276
|
+
desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
|
|
2277
|
+
desc.initializer = undefined;
|
|
2278
|
+
}
|
|
2279
|
+
if (desc.initializer === void 0) {
|
|
2280
|
+
Object.defineProperty(target, property, desc);
|
|
2281
|
+
desc = null;
|
|
2507
2282
|
}
|
|
2283
|
+
return desc;
|
|
2508
2284
|
}
|
|
2509
|
-
|
|
2285
|
+
|
|
2286
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2287
|
+
/*
|
|
2288
|
+
We redefine Proxy because the native Proxy type treats the `target` and
|
|
2289
|
+
`receiver` as the same type incorrectly.
|
|
2290
|
+
|
|
2291
|
+
We ported this from Typescript's own Proxy types on 3/10/2024.
|
|
2292
|
+
*/
|
|
2293
|
+
|
|
2294
|
+
const NativeProxy = Proxy;
|
|
2295
|
+
var _class;
|
|
2510
2296
|
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
2297
|
const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
|
|
2512
2298
|
const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
|
|
@@ -2516,19 +2302,16 @@ function isArrayGetter(prop) {
|
|
|
2516
2302
|
function isArraySetter(prop) {
|
|
2517
2303
|
return ARRAY_SETTER_METHODS.has(prop);
|
|
2518
2304
|
}
|
|
2519
|
-
|
|
2305
|
+
function isSelfProp(self, prop) {
|
|
2306
|
+
return prop in self;
|
|
2307
|
+
}
|
|
2308
|
+
const ARRAY_SIGNAL = Symbol('#signal');
|
|
2520
2309
|
const SOURCE = Symbol('#source');
|
|
2521
2310
|
const MUTATE = Symbol('#update');
|
|
2522
2311
|
const NOTIFY = Symbol('#notify');
|
|
2523
2312
|
const IS_COLLECTION = Symbol.for('Collection');
|
|
2524
2313
|
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
|
-
}
|
|
2314
|
+
addToTransaction(arr[ARRAY_SIGNAL]);
|
|
2532
2315
|
}
|
|
2533
2316
|
function convertToInt(prop) {
|
|
2534
2317
|
if (typeof prop === 'symbol') return null;
|
|
@@ -2536,29 +2319,6 @@ function convertToInt(prop) {
|
|
|
2536
2319
|
if (isNaN(num)) return null;
|
|
2537
2320
|
return num % 1 === 0 ? num : null;
|
|
2538
2321
|
}
|
|
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
2322
|
function safeForEach(instance, arr, store, callback, target) {
|
|
2563
2323
|
if (target === undefined) {
|
|
2564
2324
|
target = null;
|
|
@@ -2591,7 +2351,7 @@ function safeForEach(instance, arr, store, callback, target) {
|
|
|
2591
2351
|
@class RecordArray
|
|
2592
2352
|
@public
|
|
2593
2353
|
*/
|
|
2594
|
-
let IdentifierArray = (
|
|
2354
|
+
let IdentifierArray = (_class = class IdentifierArray {
|
|
2595
2355
|
[NOTIFY]() {
|
|
2596
2356
|
notifyArray(this);
|
|
2597
2357
|
}
|
|
@@ -2619,14 +2379,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2619
2379
|
set length(value) {
|
|
2620
2380
|
this[SOURCE].length = value;
|
|
2621
2381
|
}
|
|
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
2382
|
constructor(options) {
|
|
2631
2383
|
/**
|
|
2632
2384
|
The flag to signal a `RecordArray` is currently loading data.
|
|
@@ -2641,7 +2393,6 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2641
2393
|
@public
|
|
2642
2394
|
@type Boolean
|
|
2643
2395
|
*/
|
|
2644
|
-
_initializerDefineProperty(this, "isUpdating", _descriptor2, this);
|
|
2645
2396
|
this.isLoaded = true;
|
|
2646
2397
|
this.isDestroying = false;
|
|
2647
2398
|
this.isDestroyed = false;
|
|
@@ -2649,16 +2400,15 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2649
2400
|
this[IS_COLLECTION] = true;
|
|
2650
2401
|
this[SOURCE] = void 0;
|
|
2651
2402
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
2652
|
-
|
|
2403
|
+
const self = this;
|
|
2653
2404
|
this.modelName = options.type;
|
|
2654
2405
|
this.store = options.store;
|
|
2655
2406
|
this._manager = options.manager;
|
|
2656
2407
|
this[SOURCE] = options.identifiers;
|
|
2657
|
-
|
|
2658
|
-
this[IDENTIFIER_ARRAY_TAG] = macroCondition(getOwnConfig().env.DEBUG) ? new Tag(this, 'length') : new Tag();
|
|
2408
|
+
this[ARRAY_SIGNAL] = createSignal(this, 'length');
|
|
2659
2409
|
const store = options.store;
|
|
2660
2410
|
const boundFns = new Map();
|
|
2661
|
-
const
|
|
2411
|
+
const _SIGNAL = this[ARRAY_SIGNAL];
|
|
2662
2412
|
const PrivateState = {
|
|
2663
2413
|
links: options.links || null,
|
|
2664
2414
|
meta: options.meta || null
|
|
@@ -2669,42 +2419,42 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2669
2419
|
// we track all mutations within the call
|
|
2670
2420
|
// and forward them as one
|
|
2671
2421
|
|
|
2672
|
-
const proxy = new
|
|
2422
|
+
const proxy = new NativeProxy(this[SOURCE], {
|
|
2673
2423
|
get(target, prop, receiver) {
|
|
2674
|
-
|
|
2675
|
-
if (
|
|
2424
|
+
const index = convertToInt(prop);
|
|
2425
|
+
if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
|
|
2676
2426
|
options.manager._syncArray(receiver);
|
|
2677
|
-
|
|
2678
|
-
|
|
2427
|
+
_SIGNAL.t = false;
|
|
2428
|
+
_SIGNAL.shouldReset = false;
|
|
2679
2429
|
}
|
|
2680
2430
|
if (index !== null) {
|
|
2681
2431
|
const identifier = target[index];
|
|
2682
2432
|
if (!transaction) {
|
|
2683
|
-
subscribe(
|
|
2433
|
+
subscribe(_SIGNAL);
|
|
2684
2434
|
}
|
|
2685
2435
|
return identifier && store._instanceCache.getRecord(identifier);
|
|
2686
2436
|
}
|
|
2687
|
-
if (prop === 'meta') return subscribe(
|
|
2688
|
-
if (prop === 'links') return subscribe(
|
|
2689
|
-
if (prop === '[]') return subscribe(
|
|
2437
|
+
if (prop === 'meta') return subscribe(_SIGNAL), PrivateState.meta;
|
|
2438
|
+
if (prop === 'links') return subscribe(_SIGNAL), PrivateState.links;
|
|
2439
|
+
if (prop === '[]') return subscribe(_SIGNAL), receiver;
|
|
2690
2440
|
if (isArrayGetter(prop)) {
|
|
2691
2441
|
let fn = boundFns.get(prop);
|
|
2692
2442
|
if (fn === undefined) {
|
|
2693
2443
|
if (prop === 'forEach') {
|
|
2694
2444
|
fn = function () {
|
|
2695
|
-
subscribe(
|
|
2445
|
+
subscribe(_SIGNAL);
|
|
2696
2446
|
transaction = true;
|
|
2697
|
-
|
|
2447
|
+
const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
|
|
2698
2448
|
transaction = false;
|
|
2699
2449
|
return result;
|
|
2700
2450
|
};
|
|
2701
2451
|
} else {
|
|
2702
2452
|
fn = function () {
|
|
2703
|
-
subscribe(
|
|
2453
|
+
subscribe(_SIGNAL);
|
|
2704
2454
|
// array functions must run through Reflect to work properly
|
|
2705
2455
|
// binding via other means will not work.
|
|
2706
2456
|
transaction = true;
|
|
2707
|
-
|
|
2457
|
+
const result = Reflect.apply(target[prop], receiver, arguments);
|
|
2708
2458
|
transaction = false;
|
|
2709
2459
|
return result;
|
|
2710
2460
|
};
|
|
@@ -2726,10 +2476,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2726
2476
|
const args = Array.prototype.slice.call(arguments);
|
|
2727
2477
|
assert(`Cannot start a new array transaction while a previous transaction is underway`, !transaction);
|
|
2728
2478
|
transaction = true;
|
|
2729
|
-
|
|
2730
|
-
self[MUTATE](prop, args, result);
|
|
2731
|
-
addToTransaction(_TAG);
|
|
2732
|
-
// TODO handle cache updates
|
|
2479
|
+
const result = self[MUTATE](target, receiver, prop, args, _SIGNAL);
|
|
2733
2480
|
transaction = false;
|
|
2734
2481
|
return result;
|
|
2735
2482
|
};
|
|
@@ -2737,16 +2484,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2737
2484
|
}
|
|
2738
2485
|
return fn;
|
|
2739
2486
|
}
|
|
2740
|
-
if (prop
|
|
2741
|
-
if (prop === NOTIFY || prop ===
|
|
2487
|
+
if (isSelfProp(self, prop)) {
|
|
2488
|
+
if (prop === NOTIFY || prop === ARRAY_SIGNAL || prop === SOURCE) {
|
|
2742
2489
|
return self[prop];
|
|
2743
2490
|
}
|
|
2744
2491
|
let fn = boundFns.get(prop);
|
|
2745
2492
|
if (fn) return fn;
|
|
2746
|
-
|
|
2493
|
+
const outcome = self[prop];
|
|
2747
2494
|
if (typeof outcome === 'function') {
|
|
2748
2495
|
fn = function () {
|
|
2749
|
-
subscribe(
|
|
2496
|
+
subscribe(_SIGNAL);
|
|
2750
2497
|
// array functions must run through Reflect to work properly
|
|
2751
2498
|
// binding via other means will not work.
|
|
2752
2499
|
return Reflect.apply(outcome, receiver, arguments);
|
|
@@ -2754,17 +2501,16 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2754
2501
|
boundFns.set(prop, fn);
|
|
2755
2502
|
return fn;
|
|
2756
2503
|
}
|
|
2757
|
-
return subscribe(
|
|
2504
|
+
return subscribe(_SIGNAL), outcome;
|
|
2758
2505
|
}
|
|
2759
2506
|
return target[prop];
|
|
2760
2507
|
},
|
|
2761
|
-
|
|
2508
|
+
// FIXME: Should this get a generic like get above?
|
|
2509
|
+
set(target, prop, value, receiver) {
|
|
2762
2510
|
if (prop === 'length') {
|
|
2763
2511
|
if (!transaction && value === 0) {
|
|
2764
2512
|
transaction = true;
|
|
2765
|
-
|
|
2766
|
-
Reflect.set(target, prop, value);
|
|
2767
|
-
self[MUTATE]('length 0', []);
|
|
2513
|
+
self[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
|
|
2768
2514
|
transaction = false;
|
|
2769
2515
|
return true;
|
|
2770
2516
|
} else if (transaction) {
|
|
@@ -2781,9 +2527,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2781
2527
|
PrivateState.meta = value || null;
|
|
2782
2528
|
return true;
|
|
2783
2529
|
}
|
|
2784
|
-
|
|
2530
|
+
const index = convertToInt(prop);
|
|
2531
|
+
|
|
2532
|
+
// we do not allow "holey" arrays and so if the index is
|
|
2533
|
+
// greater than length then we will disallow setting it.
|
|
2534
|
+
// however, there is a special case for "unshift" with more than
|
|
2535
|
+
// one item being inserted since current items will be moved to the
|
|
2536
|
+
// new indices first.
|
|
2537
|
+
// we "loosely" detect this by just checking whether we are in
|
|
2538
|
+
// a transaction.
|
|
2785
2539
|
if (index === null || index > target.length) {
|
|
2786
|
-
if (
|
|
2540
|
+
if (index !== null && transaction) {
|
|
2541
|
+
const identifier = recordIdentifierFor(value);
|
|
2542
|
+
assert(`Cannot set index ${index} past the end of the array.`, isStableIdentifier(identifier));
|
|
2543
|
+
target[index] = identifier;
|
|
2544
|
+
return true;
|
|
2545
|
+
} else if (isSelfProp(self, prop)) {
|
|
2546
|
+
// @ts-expect-error not all properties are indeces and we can't safely cast
|
|
2787
2547
|
self[prop] = value;
|
|
2788
2548
|
return true;
|
|
2789
2549
|
}
|
|
@@ -2793,12 +2553,30 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2793
2553
|
assert(`Mutating ${String(prop)} on this RecordArray is not allowed.`, options.allowMutation);
|
|
2794
2554
|
return false;
|
|
2795
2555
|
}
|
|
2796
|
-
|
|
2797
|
-
|
|
2556
|
+
const original = target[index];
|
|
2557
|
+
const newIdentifier = extractIdentifierFromRecord$1(value);
|
|
2798
2558
|
target[index] = newIdentifier;
|
|
2559
|
+
assert(`Expected a record`, isStableIdentifier(newIdentifier));
|
|
2560
|
+
// We generate "transactions" whenever a setter method on the array
|
|
2561
|
+
// is called and might bulk update multiple array cells. Fundamentally,
|
|
2562
|
+
// all array operations decompose into individual cell replacements.
|
|
2563
|
+
// e.g. a push is really a "replace cell at next index with new value"
|
|
2564
|
+
// or a splice is "shift all values left/right by X and set out of new
|
|
2565
|
+
// bounds cells to undefined"
|
|
2566
|
+
//
|
|
2567
|
+
// so, if we are in a transaction, then this is not a user generated change
|
|
2568
|
+
// but one generated by a setter method. In this case we want to only apply
|
|
2569
|
+
// the change to the target array and not call the MUTATE method.
|
|
2570
|
+
// If there is no transaction though, then this means the user themselves has
|
|
2571
|
+
// directly changed the value of a specific index and we need to thus generate
|
|
2572
|
+
// a mutation for that change.
|
|
2573
|
+
// e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
|
|
2574
|
+
// a transaction.
|
|
2575
|
+
// while "arr[arr.length] = newVal;" is handled by this replace cell code path.
|
|
2799
2576
|
if (!transaction) {
|
|
2800
|
-
self[MUTATE]('replace cell', [index, original, newIdentifier]);
|
|
2801
|
-
|
|
2577
|
+
self[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
|
|
2578
|
+
} else {
|
|
2579
|
+
target[index] = newIdentifier;
|
|
2802
2580
|
}
|
|
2803
2581
|
return true;
|
|
2804
2582
|
},
|
|
@@ -2813,12 +2591,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2813
2591
|
return IdentifierArray.prototype;
|
|
2814
2592
|
}
|
|
2815
2593
|
});
|
|
2816
|
-
|
|
2817
|
-
const meta = Ember.meta(this);
|
|
2818
|
-
meta.hasMixin = mixin => {
|
|
2819
|
-
assert(`Do not call A() on EmberData RecordArrays`);
|
|
2820
|
-
};
|
|
2821
|
-
}
|
|
2594
|
+
createArrayTags(proxy, _SIGNAL);
|
|
2822
2595
|
this[NOTIFY] = this[NOTIFY].bind(proxy);
|
|
2823
2596
|
return proxy;
|
|
2824
2597
|
}
|
|
@@ -2843,7 +2616,7 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2843
2616
|
return this._updatingPromise;
|
|
2844
2617
|
}
|
|
2845
2618
|
this.isUpdating = true;
|
|
2846
|
-
|
|
2619
|
+
const updatingPromise = this._update();
|
|
2847
2620
|
void updatingPromise.finally(() => {
|
|
2848
2621
|
this._updatingPromise = null;
|
|
2849
2622
|
if (this.isDestroying || this.isDestroyed) {
|
|
@@ -2861,6 +2634,10 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2861
2634
|
*/
|
|
2862
2635
|
_update() {
|
|
2863
2636
|
assert(`_update cannot be used with this array`, this.modelName);
|
|
2637
|
+
// @ts-expect-error typescript is unable to handle the complexity of
|
|
2638
|
+
// T = unknown, modelName = string
|
|
2639
|
+
// T extends TypedRecordInstance, modelName = TypeFromInstance<T>
|
|
2640
|
+
// both being valid options to pass through here.
|
|
2864
2641
|
return this.store.findAll(this.modelName, {
|
|
2865
2642
|
reload: true
|
|
2866
2643
|
});
|
|
@@ -2882,17 +2659,23 @@ let IdentifierArray = (_class3 = class IdentifierArray {
|
|
|
2882
2659
|
@return {Promise<IdentifierArray>} promise
|
|
2883
2660
|
*/
|
|
2884
2661
|
save() {
|
|
2885
|
-
|
|
2662
|
+
const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
|
|
2886
2663
|
return promise;
|
|
2887
2664
|
}
|
|
2888
|
-
},
|
|
2889
|
-
|
|
2665
|
+
}, _applyDecoratedDescriptor(_class.prototype, "length", [compat], Object.getOwnPropertyDescriptor(_class.prototype, "length"), _class.prototype), _class); // this will error if someone tries to call
|
|
2666
|
+
// A(identifierArray) since it is not configurable
|
|
2667
|
+
// which is preferable to the `meta` override we used
|
|
2668
|
+
// before which required importing all of Ember
|
|
2669
|
+
const desc = {
|
|
2890
2670
|
enumerable: true,
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
return
|
|
2671
|
+
configurable: false,
|
|
2672
|
+
get: function () {
|
|
2673
|
+
return this;
|
|
2894
2674
|
}
|
|
2895
|
-
}
|
|
2675
|
+
};
|
|
2676
|
+
compat(desc);
|
|
2677
|
+
Object.defineProperty(IdentifierArray.prototype, '[]', desc);
|
|
2678
|
+
defineSignal(IdentifierArray.prototype, 'isUpdating', false);
|
|
2896
2679
|
class Collection extends IdentifierArray {
|
|
2897
2680
|
constructor(options) {
|
|
2898
2681
|
super(options);
|
|
@@ -2909,6 +2692,10 @@ class Collection extends IdentifierArray {
|
|
|
2909
2692
|
// TODO save options from initial request?
|
|
2910
2693
|
assert(`update cannot be used with this array`, this.modelName);
|
|
2911
2694
|
assert(`update cannot be used with no query`, query);
|
|
2695
|
+
// @ts-expect-error typescript is unable to handle the complexity of
|
|
2696
|
+
// T = unknown, modelName = string
|
|
2697
|
+
// T extends TypedRecordInstance, modelName = TypeFromInstance<T>
|
|
2698
|
+
// both being valid options to pass through here.
|
|
2912
2699
|
const promise = store.query(this.modelName, query, {
|
|
2913
2700
|
_recordArray: this
|
|
2914
2701
|
});
|
|
@@ -2924,7 +2711,8 @@ class Collection extends IdentifierArray {
|
|
|
2924
2711
|
Collection.prototype.query = null;
|
|
2925
2712
|
|
|
2926
2713
|
// Ensure instanceof works correctly
|
|
2927
|
-
//Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2714
|
+
// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2715
|
+
|
|
2928
2716
|
function assertRecordPassedToHasMany(record) {
|
|
2929
2717
|
assert(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`, function () {
|
|
2930
2718
|
try {
|
|
@@ -2946,7 +2734,6 @@ function extractIdentifierFromRecord$1(record) {
|
|
|
2946
2734
|
/**
|
|
2947
2735
|
@module @ember-data/store
|
|
2948
2736
|
*/
|
|
2949
|
-
|
|
2950
2737
|
const FAKE_ARR = {};
|
|
2951
2738
|
const SLICE_BATCH_SIZE = 1200;
|
|
2952
2739
|
/**
|
|
@@ -2989,7 +2776,7 @@ const SLICE_BATCH_SIZE = 1200;
|
|
|
2989
2776
|
*/
|
|
2990
2777
|
function fastPush(target, source) {
|
|
2991
2778
|
let startLength = 0;
|
|
2992
|
-
|
|
2779
|
+
const newLength = source.length;
|
|
2993
2780
|
while (newLength - startLength > SLICE_BATCH_SIZE) {
|
|
2994
2781
|
// eslint-disable-next-line prefer-spread
|
|
2995
2782
|
target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
|
|
@@ -3046,8 +2833,8 @@ class RecordArrayManager {
|
|
|
3046
2833
|
*/
|
|
3047
2834
|
liveArrayFor(type) {
|
|
3048
2835
|
let array = this._live.get(type);
|
|
3049
|
-
|
|
3050
|
-
|
|
2836
|
+
const identifiers = [];
|
|
2837
|
+
const staged = this._staged.get(type);
|
|
3051
2838
|
if (staged) {
|
|
3052
2839
|
staged.forEach((value, key) => {
|
|
3053
2840
|
if (value === 'add') {
|
|
@@ -3070,7 +2857,7 @@ class RecordArrayManager {
|
|
|
3070
2857
|
return array;
|
|
3071
2858
|
}
|
|
3072
2859
|
createArray(config) {
|
|
3073
|
-
|
|
2860
|
+
const options = {
|
|
3074
2861
|
type: config.type,
|
|
3075
2862
|
links: config.doc?.links || null,
|
|
3076
2863
|
meta: config.doc?.meta || null,
|
|
@@ -3081,7 +2868,7 @@ class RecordArrayManager {
|
|
|
3081
2868
|
store: this.store,
|
|
3082
2869
|
manager: this
|
|
3083
2870
|
};
|
|
3084
|
-
|
|
2871
|
+
const array = new Collection(options);
|
|
3085
2872
|
this._managed.add(array);
|
|
3086
2873
|
this._set.set(array, new Set(options.identifiers || []));
|
|
3087
2874
|
if (config.identifiers) {
|
|
@@ -3093,7 +2880,7 @@ class RecordArrayManager {
|
|
|
3093
2880
|
if (array === FAKE_ARR) {
|
|
3094
2881
|
return;
|
|
3095
2882
|
}
|
|
3096
|
-
|
|
2883
|
+
const tag = array[ARRAY_SIGNAL];
|
|
3097
2884
|
if (!tag.shouldReset) {
|
|
3098
2885
|
tag.shouldReset = true;
|
|
3099
2886
|
addTransactionCB(array[NOTIFY]);
|
|
@@ -3105,11 +2892,11 @@ class RecordArrayManager {
|
|
|
3105
2892
|
if (this.isDestroying || this.isDestroyed) {
|
|
3106
2893
|
return;
|
|
3107
2894
|
}
|
|
3108
|
-
|
|
2895
|
+
const liveArray = this._live.get(identifier.type);
|
|
3109
2896
|
const allPending = this._pending;
|
|
3110
|
-
|
|
2897
|
+
const pending = new Map();
|
|
3111
2898
|
if (includeManaged) {
|
|
3112
|
-
|
|
2899
|
+
const managed = this._identifiers.get(identifier);
|
|
3113
2900
|
if (managed) {
|
|
3114
2901
|
managed.forEach(arr => {
|
|
3115
2902
|
let changes = allPending.get(arr);
|
|
@@ -3164,10 +2951,10 @@ class RecordArrayManager {
|
|
|
3164
2951
|
associate(this._identifiers, array, identifiers);
|
|
3165
2952
|
}
|
|
3166
2953
|
identifierAdded(identifier) {
|
|
3167
|
-
|
|
2954
|
+
const changeSets = this._getPendingFor(identifier, false);
|
|
3168
2955
|
if (changeSets) {
|
|
3169
2956
|
changeSets.forEach((changes, array) => {
|
|
3170
|
-
|
|
2957
|
+
const existing = changes.get(identifier);
|
|
3171
2958
|
if (existing === 'del') {
|
|
3172
2959
|
changes.delete(identifier);
|
|
3173
2960
|
} else {
|
|
@@ -3178,10 +2965,10 @@ class RecordArrayManager {
|
|
|
3178
2965
|
}
|
|
3179
2966
|
}
|
|
3180
2967
|
identifierRemoved(identifier) {
|
|
3181
|
-
|
|
2968
|
+
const changeSets = this._getPendingFor(identifier, true, true);
|
|
3182
2969
|
if (changeSets) {
|
|
3183
2970
|
changeSets.forEach((changes, array) => {
|
|
3184
|
-
|
|
2971
|
+
const existing = changes.get(identifier);
|
|
3185
2972
|
if (existing === 'add') {
|
|
3186
2973
|
changes.delete(identifier);
|
|
3187
2974
|
} else {
|
|
@@ -3192,7 +2979,7 @@ class RecordArrayManager {
|
|
|
3192
2979
|
}
|
|
3193
2980
|
}
|
|
3194
2981
|
identifierChanged(identifier) {
|
|
3195
|
-
|
|
2982
|
+
const newState = this.store._instanceCache.recordIsLoaded(identifier, true);
|
|
3196
2983
|
|
|
3197
2984
|
// if the change matches the most recent direct added/removed
|
|
3198
2985
|
// state, then we can ignore it
|
|
@@ -3219,13 +3006,12 @@ class RecordArrayManager {
|
|
|
3219
3006
|
this.clear(false);
|
|
3220
3007
|
this._live.clear();
|
|
3221
3008
|
this.isDestroyed = true;
|
|
3222
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
3223
3009
|
this.store.notifications.unsubscribe(this._subscription);
|
|
3224
3010
|
}
|
|
3225
3011
|
}
|
|
3226
3012
|
function associate(ArraysCache, array, identifiers) {
|
|
3227
3013
|
for (let i = 0; i < identifiers.length; i++) {
|
|
3228
|
-
|
|
3014
|
+
const identifier = identifiers[i];
|
|
3229
3015
|
let cache = ArraysCache.get(identifier);
|
|
3230
3016
|
if (!cache) {
|
|
3231
3017
|
cache = new Set();
|
|
@@ -3240,13 +3026,13 @@ function disassociate(ArraysCache, array, identifiers) {
|
|
|
3240
3026
|
}
|
|
3241
3027
|
}
|
|
3242
3028
|
function disassociateIdentifier(ArraysCache, array, identifier) {
|
|
3243
|
-
|
|
3029
|
+
const cache = ArraysCache.get(identifier);
|
|
3244
3030
|
if (cache) {
|
|
3245
3031
|
cache.delete(array);
|
|
3246
3032
|
}
|
|
3247
3033
|
}
|
|
3248
3034
|
function sync(array, changes, arraySet) {
|
|
3249
|
-
|
|
3035
|
+
const state = array[SOURCE];
|
|
3250
3036
|
const adds = [];
|
|
3251
3037
|
const removes = [];
|
|
3252
3038
|
changes.forEach((value, key) => {
|
|
@@ -3260,13 +3046,13 @@ function sync(array, changes, arraySet) {
|
|
|
3260
3046
|
} else {
|
|
3261
3047
|
if (arraySet.has(key)) {
|
|
3262
3048
|
removes.push(key);
|
|
3049
|
+
arraySet.delete(key);
|
|
3263
3050
|
}
|
|
3264
3051
|
}
|
|
3265
3052
|
});
|
|
3266
3053
|
if (removes.length) {
|
|
3267
3054
|
if (removes.length === state.length) {
|
|
3268
3055
|
state.length = 0;
|
|
3269
|
-
arraySet.clear();
|
|
3270
3056
|
// changing the reference breaks the Proxy
|
|
3271
3057
|
// state = array[SOURCE] = [];
|
|
3272
3058
|
} else {
|
|
@@ -3293,6 +3079,9 @@ function sync(array, changes, arraySet) {
|
|
|
3293
3079
|
}
|
|
3294
3080
|
}
|
|
3295
3081
|
|
|
3082
|
+
/**
|
|
3083
|
+
* @module @ember-data/store
|
|
3084
|
+
*/
|
|
3296
3085
|
const Touching = Symbol('touching');
|
|
3297
3086
|
const RequestPromise = Symbol('promise');
|
|
3298
3087
|
const EMPTY_ARR = macroCondition(getOwnConfig().env.DEBUG) ? Object.freeze([]) : [];
|
|
@@ -3320,14 +3109,14 @@ class RequestStateService {
|
|
|
3320
3109
|
this._done.delete(identifier);
|
|
3321
3110
|
}
|
|
3322
3111
|
_enqueue(promise, queryRequest) {
|
|
3323
|
-
|
|
3112
|
+
const query = queryRequest.data[0];
|
|
3324
3113
|
if (hasRecordIdentifier(query)) {
|
|
3325
3114
|
const identifier = query.recordIdentifier;
|
|
3326
|
-
|
|
3115
|
+
const type = query.op === 'saveRecord' ? 'mutation' : 'query';
|
|
3327
3116
|
if (!this._pending.has(identifier)) {
|
|
3328
3117
|
this._pending.set(identifier, []);
|
|
3329
3118
|
}
|
|
3330
|
-
|
|
3119
|
+
const request = {
|
|
3331
3120
|
state: 'pending',
|
|
3332
3121
|
request: queryRequest,
|
|
3333
3122
|
type
|
|
@@ -3338,7 +3127,7 @@ class RequestStateService {
|
|
|
3338
3127
|
this._triggerSubscriptions(request);
|
|
3339
3128
|
return promise.then(result => {
|
|
3340
3129
|
this._dequeue(identifier, request);
|
|
3341
|
-
|
|
3130
|
+
const finalizedRequest = {
|
|
3342
3131
|
state: 'fulfilled',
|
|
3343
3132
|
request: queryRequest,
|
|
3344
3133
|
type,
|
|
@@ -3352,7 +3141,7 @@ class RequestStateService {
|
|
|
3352
3141
|
return result;
|
|
3353
3142
|
}, error => {
|
|
3354
3143
|
this._dequeue(identifier, request);
|
|
3355
|
-
|
|
3144
|
+
const finalizedRequest = {
|
|
3356
3145
|
state: 'rejected',
|
|
3357
3146
|
request: queryRequest,
|
|
3358
3147
|
type,
|
|
@@ -3401,7 +3190,7 @@ class RequestStateService {
|
|
|
3401
3190
|
_addDone(request) {
|
|
3402
3191
|
request[Touching].forEach(identifier => {
|
|
3403
3192
|
// TODO add support for multiple
|
|
3404
|
-
|
|
3193
|
+
const requestDataOp = request.request.data[0].op;
|
|
3405
3194
|
let requests = this._done.get(identifier);
|
|
3406
3195
|
if (requests) {
|
|
3407
3196
|
requests = requests.filter(req => {
|
|
@@ -3465,7 +3254,7 @@ class RequestStateService {
|
|
|
3465
3254
|
* @method getPendingRequestsForRecord
|
|
3466
3255
|
* @public
|
|
3467
3256
|
* @param {StableRecordIdentifier} identifier
|
|
3468
|
-
* @
|
|
3257
|
+
* @return {RequestState[]} an array of request states for any pending requests for the given identifier
|
|
3469
3258
|
*/
|
|
3470
3259
|
getPendingRequestsForRecord(identifier) {
|
|
3471
3260
|
return this._pending.get(identifier) || EMPTY_ARR;
|
|
@@ -3477,10 +3266,10 @@ class RequestStateService {
|
|
|
3477
3266
|
* @method getLastRequestForRecord
|
|
3478
3267
|
* @public
|
|
3479
3268
|
* @param {StableRecordIdentifier} identifier
|
|
3480
|
-
* @
|
|
3269
|
+
* @return {RequestState | null} the state of the most recent request for the given identifier
|
|
3481
3270
|
*/
|
|
3482
3271
|
getLastRequestForRecord(identifier) {
|
|
3483
|
-
|
|
3272
|
+
const requests = this._done.get(identifier);
|
|
3484
3273
|
if (requests) {
|
|
3485
3274
|
return requests[requests.length - 1];
|
|
3486
3275
|
}
|
|
@@ -3492,7 +3281,7 @@ function isNonEmptyString(str) {
|
|
|
3492
3281
|
}
|
|
3493
3282
|
function constructResource(type, id, lid) {
|
|
3494
3283
|
if (typeof type === 'object' && type !== null) {
|
|
3495
|
-
|
|
3284
|
+
const resource = type;
|
|
3496
3285
|
if (isStableIdentifier(resource)) {
|
|
3497
3286
|
return resource;
|
|
3498
3287
|
}
|
|
@@ -3527,6 +3316,29 @@ function constructResource(type, id, lid) {
|
|
|
3527
3316
|
}
|
|
3528
3317
|
}
|
|
3529
3318
|
|
|
3319
|
+
/**
|
|
3320
|
+
@module @ember-data/store
|
|
3321
|
+
*/
|
|
3322
|
+
// this import location is deprecated but breaks in 4.8 and older
|
|
3323
|
+
|
|
3324
|
+
/**
|
|
3325
|
+
* Currently only records that extend object can be created via
|
|
3326
|
+
* store.createRecord. This is a limitation of the current API,
|
|
3327
|
+
* but can be worked around by creating a new identifier, running
|
|
3328
|
+
* the cache.clientDidCreate method, and then peeking the record
|
|
3329
|
+
* for the identifier.
|
|
3330
|
+
*
|
|
3331
|
+
* To assign primary key to a record during creation, only `id` will
|
|
3332
|
+
* work correctly for `store.createRecord`, other primary key may be
|
|
3333
|
+
* handled by updating the record after creation or using the flow
|
|
3334
|
+
* described above.
|
|
3335
|
+
*
|
|
3336
|
+
* TODO: These are limitations we want to (and can) address. If you
|
|
3337
|
+
* have need of lifting these limitations, please open an issue.
|
|
3338
|
+
*
|
|
3339
|
+
* @typedoc
|
|
3340
|
+
*/
|
|
3341
|
+
|
|
3530
3342
|
/**
|
|
3531
3343
|
* A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
|
|
3532
3344
|
* and sources of data (such as your API or a local persistence layer)
|
|
@@ -3545,7 +3357,10 @@ function constructResource(type, id, lid) {
|
|
|
3545
3357
|
|
|
3546
3358
|
@class Store
|
|
3547
3359
|
@public
|
|
3548
|
-
*/
|
|
3360
|
+
*/
|
|
3361
|
+
|
|
3362
|
+
// @ts-expect-error
|
|
3363
|
+
|
|
3549
3364
|
class Store extends EmberObject {
|
|
3550
3365
|
/**
|
|
3551
3366
|
* Provides access to the NotificationManager associated
|
|
@@ -3677,8 +3492,6 @@ class Store extends EmberObject {
|
|
|
3677
3492
|
// private
|
|
3678
3493
|
this._requestCache = new RequestStateService(this);
|
|
3679
3494
|
this._instanceCache = new InstanceCache(this);
|
|
3680
|
-
this._adapterCache = Object.create(null);
|
|
3681
|
-
this._serializerCache = Object.create(null);
|
|
3682
3495
|
this._documentCache = new Map();
|
|
3683
3496
|
this.isDestroying = false;
|
|
3684
3497
|
this.isDestroyed = false;
|
|
@@ -3736,7 +3549,7 @@ class Store extends EmberObject {
|
|
|
3736
3549
|
* that have been initiated for a given identifier.
|
|
3737
3550
|
*
|
|
3738
3551
|
* @method getRequestStateService
|
|
3739
|
-
* @
|
|
3552
|
+
* @return {RequestStateService}
|
|
3740
3553
|
* @public
|
|
3741
3554
|
*/
|
|
3742
3555
|
getRequestStateService() {
|
|
@@ -3761,13 +3574,14 @@ class Store extends EmberObject {
|
|
|
3761
3574
|
* inserting the response into the cache and handing
|
|
3762
3575
|
* back a Future which resolves to a ResponseDocument
|
|
3763
3576
|
*
|
|
3764
|
-
*
|
|
3577
|
+
* ## Cache Keys
|
|
3765
3578
|
*
|
|
3766
|
-
* Only GET requests
|
|
3767
|
-
*
|
|
3579
|
+
* Only GET requests with a url or requests with an explicit
|
|
3580
|
+
* cache key (`cacheOptions.key`) will have the request result
|
|
3581
|
+
* and document cached.
|
|
3768
3582
|
*
|
|
3769
3583
|
* The cache key used is `requestConfig.cacheOptions.key`
|
|
3770
|
-
* if present, falling back to `
|
|
3584
|
+
* if present, falling back to `requestConfig.url`.
|
|
3771
3585
|
*
|
|
3772
3586
|
* Params are not serialized as part of the cache-key, so
|
|
3773
3587
|
* either ensure they are already in the url or utilize
|
|
@@ -3775,16 +3589,44 @@ class Store extends EmberObject {
|
|
|
3775
3589
|
* via the `POST` method `requestConfig.cacheOptions.key`
|
|
3776
3590
|
* MUST be supplied for the document to be cached.
|
|
3777
3591
|
*
|
|
3592
|
+
* ## Requesting Without a Cache Key
|
|
3593
|
+
*
|
|
3594
|
+
* Resource data within the request is always updated in the cache,
|
|
3595
|
+
* regardless of whether a cache key is present for the request.
|
|
3596
|
+
*
|
|
3597
|
+
* ## Fulfilling From Cache
|
|
3598
|
+
*
|
|
3599
|
+
* When a cache-key is determined, the request may fulfill
|
|
3600
|
+
* from cache provided the cache is not stale.
|
|
3601
|
+
*
|
|
3602
|
+
* Cache staleness is determined by the configured LifetimesService
|
|
3603
|
+
* with priority given to the `cacheOptions.reload` and
|
|
3604
|
+
* `cacheOptions.backgroundReload` on the request if present.
|
|
3605
|
+
*
|
|
3606
|
+
* If the cache data has soft expired or the request asks for a background
|
|
3607
|
+
* reload, the request will fulfill from cache if possible and
|
|
3608
|
+
* make a non-blocking request in the background to update the cache.
|
|
3609
|
+
*
|
|
3610
|
+
* If the cache data has hard expired or the request asks for a reload,
|
|
3611
|
+
* the request will not fulfill from cache and will make a blocking
|
|
3612
|
+
* request to update the cache.
|
|
3613
|
+
*
|
|
3614
|
+
* ## The Response
|
|
3615
|
+
*
|
|
3616
|
+
* The primary difference between `requestManager.request` and `store.request`
|
|
3617
|
+
* is that `store.request` will attempt to hydrate the response content into
|
|
3618
|
+
* a response Document containing RecordInstances.
|
|
3619
|
+
*
|
|
3778
3620
|
* @method request
|
|
3779
3621
|
* @param {StoreRequestInput} requestConfig
|
|
3780
|
-
* @
|
|
3622
|
+
* @return {Future}
|
|
3781
3623
|
* @public
|
|
3782
3624
|
*/
|
|
3783
3625
|
request(requestConfig) {
|
|
3784
3626
|
// we lazily set the cache handler when we issue the first request
|
|
3785
3627
|
// because constructor doesn't allow for this to run after
|
|
3786
3628
|
// the user has had the chance to set the prop.
|
|
3787
|
-
|
|
3629
|
+
const opts = {
|
|
3788
3630
|
store: this,
|
|
3789
3631
|
[EnableHydration]: true
|
|
3790
3632
|
};
|
|
@@ -3828,7 +3670,7 @@ class Store extends EmberObject {
|
|
|
3828
3670
|
* a resource.
|
|
3829
3671
|
*
|
|
3830
3672
|
* This hook can be used to select or instantiate any desired
|
|
3831
|
-
* mechanism of
|
|
3673
|
+
* mechanism of presenting cache data to the ui for access
|
|
3832
3674
|
* mutation, and interaction.
|
|
3833
3675
|
*
|
|
3834
3676
|
* @method instantiateRecord (hook)
|
|
@@ -3836,7 +3678,7 @@ class Store extends EmberObject {
|
|
|
3836
3678
|
* @param createRecordArgs
|
|
3837
3679
|
* @param recordDataFor deprecated use this.cache
|
|
3838
3680
|
* @param notificationManager deprecated use this.notifications
|
|
3839
|
-
* @
|
|
3681
|
+
* @return A record instance
|
|
3840
3682
|
* @public
|
|
3841
3683
|
*/
|
|
3842
3684
|
|
|
@@ -3979,7 +3821,7 @@ class Store extends EmberObject {
|
|
|
3979
3821
|
}
|
|
3980
3822
|
|
|
3981
3823
|
/**
|
|
3982
|
-
Returns the schema for a particular
|
|
3824
|
+
Returns the schema for a particular resource type (modelName).
|
|
3983
3825
|
When used with Model from @ember-data/model the return is the model class,
|
|
3984
3826
|
but this is not guaranteed.
|
|
3985
3827
|
If looking to query attribute or relationship information it is
|
|
@@ -3993,7 +3835,7 @@ class Store extends EmberObject {
|
|
|
3993
3835
|
for example.
|
|
3994
3836
|
@method modelFor
|
|
3995
3837
|
@public
|
|
3996
|
-
@param {
|
|
3838
|
+
@param {string} type
|
|
3997
3839
|
@return {ModelSchema}
|
|
3998
3840
|
*/
|
|
3999
3841
|
// TODO @deprecate in favor of schema APIs, requires adapter/serializer overhaul or replacement
|
|
@@ -4018,7 +3860,7 @@ class Store extends EmberObject {
|
|
|
4018
3860
|
```
|
|
4019
3861
|
To create a new instance of a `Post` that has a relationship with a `User` record:
|
|
4020
3862
|
```js
|
|
4021
|
-
let user = this.store.peekRecord('user', 1);
|
|
3863
|
+
let user = this.store.peekRecord('user', '1');
|
|
4022
3864
|
store.createRecord('post', {
|
|
4023
3865
|
title: 'Ember is awesome!',
|
|
4024
3866
|
user: user
|
|
@@ -4026,17 +3868,18 @@ class Store extends EmberObject {
|
|
|
4026
3868
|
```
|
|
4027
3869
|
@method createRecord
|
|
4028
3870
|
@public
|
|
4029
|
-
@param {String}
|
|
3871
|
+
@param {String} type the name of the resource
|
|
4030
3872
|
@param {Object} inputProperties a hash of properties to set on the
|
|
4031
3873
|
newly created record.
|
|
4032
3874
|
@return {Model} record
|
|
4033
3875
|
*/
|
|
4034
|
-
|
|
3876
|
+
|
|
3877
|
+
createRecord(type, inputProperties) {
|
|
4035
3878
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4036
3879
|
assertDestroyingStore(this, 'createRecord');
|
|
4037
3880
|
}
|
|
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 ${
|
|
3881
|
+
assert(`You need to pass a model name to the store's createRecord method`, type);
|
|
3882
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4040
3883
|
|
|
4041
3884
|
// This is wrapped in a `run.join` so that in test environments users do not need to manually wrap
|
|
4042
3885
|
// calls to `createRecord`. The run loop usage here is because we batch the joining and updating
|
|
@@ -4044,43 +3887,40 @@ class Store extends EmberObject {
|
|
|
4044
3887
|
//
|
|
4045
3888
|
// to remove this, we would need to move to a new `async` API.
|
|
4046
3889
|
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
|
-
}
|
|
3890
|
+
this._join(() => {
|
|
3891
|
+
const normalizedModelName = normalizeModelName(type);
|
|
3892
|
+
const properties = {
|
|
3893
|
+
...inputProperties
|
|
3894
|
+
};
|
|
4067
3895
|
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
3896
|
+
// If the passed properties do not include a primary key,
|
|
3897
|
+
// give the adapter an opportunity to generate one. Typically,
|
|
3898
|
+
// client-side ID generators will use something like uuid.js
|
|
3899
|
+
// to avoid conflicts.
|
|
3900
|
+
let id = null;
|
|
3901
|
+
if (properties.id === null || properties.id === undefined) {
|
|
3902
|
+
const adapter = this.adapterFor?.(normalizedModelName, true);
|
|
3903
|
+
if (adapter && adapter.generateIdForRecord) {
|
|
3904
|
+
id = properties.id = coerceId(adapter.generateIdForRecord(this, normalizedModelName, properties));
|
|
3905
|
+
} else {
|
|
3906
|
+
id = properties.id = null;
|
|
4077
3907
|
}
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
3908
|
+
} else {
|
|
3909
|
+
id = properties.id = coerceId(properties.id);
|
|
3910
|
+
}
|
|
3911
|
+
const resource = {
|
|
3912
|
+
type: normalizedModelName,
|
|
3913
|
+
id
|
|
3914
|
+
};
|
|
3915
|
+
if (resource.id) {
|
|
3916
|
+
const identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
3917
|
+
assert(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`, !identifier);
|
|
3918
|
+
}
|
|
3919
|
+
const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
|
|
3920
|
+
const cache = this.cache;
|
|
3921
|
+
const createOptions = normalizeProperties(this, identifier, properties);
|
|
3922
|
+
const resultProps = cache.clientDidCreate(identifier, createOptions);
|
|
3923
|
+
record = this._instanceCache.getRecord(identifier, resultProps);
|
|
4084
3924
|
});
|
|
4085
3925
|
return record;
|
|
4086
3926
|
}
|
|
@@ -4096,7 +3936,7 @@ class Store extends EmberObject {
|
|
|
4096
3936
|
```
|
|
4097
3937
|
@method deleteRecord
|
|
4098
3938
|
@public
|
|
4099
|
-
@param {
|
|
3939
|
+
@param {unknown} record
|
|
4100
3940
|
*/
|
|
4101
3941
|
deleteRecord(record) {
|
|
4102
3942
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
@@ -4108,9 +3948,7 @@ class Store extends EmberObject {
|
|
|
4108
3948
|
this._join(() => {
|
|
4109
3949
|
cache.setIsDeleted(identifier, true);
|
|
4110
3950
|
if (cache.isNew(identifier)) {
|
|
4111
|
-
|
|
4112
|
-
this._instanceCache.unloadRecord(identifier);
|
|
4113
|
-
});
|
|
3951
|
+
this._instanceCache.unloadRecord(identifier);
|
|
4114
3952
|
}
|
|
4115
3953
|
});
|
|
4116
3954
|
}
|
|
@@ -4120,7 +3958,7 @@ class Store extends EmberObject {
|
|
|
4120
3958
|
This will cause the record to be destroyed and freed up for garbage collection.
|
|
4121
3959
|
Example
|
|
4122
3960
|
```javascript
|
|
4123
|
-
store.findRecord('post', 1).then(function(post) {
|
|
3961
|
+
store.findRecord('post', '1').then(function(post) {
|
|
4124
3962
|
store.unloadRecord(post);
|
|
4125
3963
|
});
|
|
4126
3964
|
```
|
|
@@ -4192,8 +4030,7 @@ class Store extends EmberObject {
|
|
|
4192
4030
|
In your adapter you can then access this id without triggering a network request via the
|
|
4193
4031
|
snapshot:
|
|
4194
4032
|
```app/adapters/application.js
|
|
4195
|
-
|
|
4196
|
-
export default class Adapter extends EmberObject {
|
|
4033
|
+
export default class Adapter {
|
|
4197
4034
|
findRecord(store, schema, id, snapshot) {
|
|
4198
4035
|
let type = schema.modelName;
|
|
4199
4036
|
if (type === 'comment')
|
|
@@ -4202,6 +4039,9 @@ class Store extends EmberObject {
|
|
|
4202
4039
|
.then(response => response.json())
|
|
4203
4040
|
}
|
|
4204
4041
|
}
|
|
4042
|
+
static create() {
|
|
4043
|
+
return new this();
|
|
4044
|
+
}
|
|
4205
4045
|
}
|
|
4206
4046
|
```
|
|
4207
4047
|
This could also be achieved by supplying the post id to the adapter via the adapterOptions
|
|
@@ -4215,9 +4055,8 @@ class Store extends EmberObject {
|
|
|
4215
4055
|
}
|
|
4216
4056
|
```
|
|
4217
4057
|
```app/adapters/application.js
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
findRecord(store, schema, id, snapshot) {
|
|
4058
|
+
export default class Adapter {
|
|
4059
|
+
findRecord(store, schema, id, snapshot) {
|
|
4221
4060
|
let type = schema.modelName;
|
|
4222
4061
|
if (type === 'comment')
|
|
4223
4062
|
let postId = snapshot.adapterOptions.post;
|
|
@@ -4225,12 +4064,15 @@ class Store extends EmberObject {
|
|
|
4225
4064
|
.then(response => response.json())
|
|
4226
4065
|
}
|
|
4227
4066
|
}
|
|
4067
|
+
static create() {
|
|
4068
|
+
return new this();
|
|
4069
|
+
}
|
|
4228
4070
|
}
|
|
4229
4071
|
```
|
|
4230
4072
|
If you have access to the post model you can also pass the model itself to preload:
|
|
4231
4073
|
```javascript
|
|
4232
|
-
let post = await store.findRecord('post', 1);
|
|
4233
|
-
let comment = await store.findRecord('comment', 2, { post: myPostModel });
|
|
4074
|
+
let post = await store.findRecord('post', '1');
|
|
4075
|
+
let comment = await store.findRecord('comment', '2', { post: myPostModel });
|
|
4234
4076
|
```
|
|
4235
4077
|
### Reloading
|
|
4236
4078
|
The reload behavior is configured either via the passed `options` hash or
|
|
@@ -4254,7 +4096,7 @@ class Store extends EmberObject {
|
|
|
4254
4096
|
// revision: 2
|
|
4255
4097
|
// }
|
|
4256
4098
|
// ]
|
|
4257
|
-
store.findRecord('post', 1, { reload: true }).then(function(post) {
|
|
4099
|
+
store.findRecord('post', '1', { reload: true }).then(function(post) {
|
|
4258
4100
|
post.revision; // 2
|
|
4259
4101
|
});
|
|
4260
4102
|
```
|
|
@@ -4283,7 +4125,7 @@ class Store extends EmberObject {
|
|
|
4283
4125
|
revision: 1
|
|
4284
4126
|
}
|
|
4285
4127
|
});
|
|
4286
|
-
let blogPost = store.findRecord('post', 1).then(function(post) {
|
|
4128
|
+
let blogPost = store.findRecord('post', '1').then(function(post) {
|
|
4287
4129
|
post.revision; // 1
|
|
4288
4130
|
});
|
|
4289
4131
|
// later, once adapter#findRecord resolved with
|
|
@@ -4351,9 +4193,8 @@ class Store extends EmberObject {
|
|
|
4351
4193
|
}
|
|
4352
4194
|
```
|
|
4353
4195
|
```app/adapters/application.js
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
findRecord(store, schema, id, snapshot) {
|
|
4196
|
+
export default class Adapter {
|
|
4197
|
+
findRecord(store, schema, id, snapshot) {
|
|
4357
4198
|
let type = schema.modelName;
|
|
4358
4199
|
if (type === 'post')
|
|
4359
4200
|
let includes = snapshot.adapterOptions.include;
|
|
@@ -4361,6 +4202,9 @@ class Store extends EmberObject {
|
|
|
4361
4202
|
.then(response => response.json())
|
|
4362
4203
|
}
|
|
4363
4204
|
}
|
|
4205
|
+
static create() {
|
|
4206
|
+
return new this();
|
|
4207
|
+
}
|
|
4364
4208
|
}
|
|
4365
4209
|
```
|
|
4366
4210
|
In this case, the post's comments would then be available in your template as
|
|
@@ -4414,7 +4258,7 @@ class Store extends EmberObject {
|
|
|
4414
4258
|
@since 1.13.0
|
|
4415
4259
|
@method findRecord
|
|
4416
4260
|
@public
|
|
4417
|
-
@param {String|object}
|
|
4261
|
+
@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
4262
|
@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
4263
|
@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
4264
|
@return {Promise} promise
|
|
@@ -4465,7 +4309,7 @@ class Store extends EmberObject {
|
|
|
4465
4309
|
Get the reference for the specified record.
|
|
4466
4310
|
Example
|
|
4467
4311
|
```javascript
|
|
4468
|
-
let userRef = store.getReference('user', 1);
|
|
4312
|
+
let userRef = store.getReference('user', '1');
|
|
4469
4313
|
// check if the user is loaded
|
|
4470
4314
|
let isLoaded = userRef.value() !== null;
|
|
4471
4315
|
// get the record of the reference (null if not yet available)
|
|
@@ -4504,7 +4348,7 @@ class Store extends EmberObject {
|
|
|
4504
4348
|
resourceIdentifier = constructResource(type, normalizedId);
|
|
4505
4349
|
}
|
|
4506
4350
|
assert('getReference expected to receive either a resource identifier or type and id as arguments', isMaybeIdentifier(resourceIdentifier));
|
|
4507
|
-
|
|
4351
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
4508
4352
|
return this._instanceCache.getReference(identifier);
|
|
4509
4353
|
}
|
|
4510
4354
|
|
|
@@ -4517,8 +4361,8 @@ class Store extends EmberObject {
|
|
|
4517
4361
|
_Note: This is a synchronous method and does not return a promise._
|
|
4518
4362
|
**Example 1**
|
|
4519
4363
|
```js
|
|
4520
|
-
let post = store.peekRecord('post', 1);
|
|
4521
|
-
post.id; // 1
|
|
4364
|
+
let post = store.peekRecord('post', '1');
|
|
4365
|
+
post.id; // '1'
|
|
4522
4366
|
```
|
|
4523
4367
|
`peekRecord` can be called with a single identifier argument instead of the combination
|
|
4524
4368
|
of `type` (modelName) and `id` as separate arguments. You may recognize this combo as
|
|
@@ -4526,14 +4370,14 @@ class Store extends EmberObject {
|
|
|
4526
4370
|
**Example 2**
|
|
4527
4371
|
```js
|
|
4528
4372
|
let post = store.peekRecord({ type: 'post', id });
|
|
4529
|
-
post.id; // 1
|
|
4373
|
+
post.id; // '1'
|
|
4530
4374
|
```
|
|
4531
4375
|
If you have previously received an lid from an Identifier for this record, you can lookup the record again using
|
|
4532
4376
|
just the lid.
|
|
4533
4377
|
**Example 3**
|
|
4534
4378
|
```js
|
|
4535
4379
|
let post = store.peekRecord({ lid });
|
|
4536
|
-
post.id; // 1
|
|
4380
|
+
post.id; // '1'
|
|
4537
4381
|
```
|
|
4538
4382
|
@since 1.13.0
|
|
4539
4383
|
@method peekRecord
|
|
@@ -4586,7 +4430,7 @@ class Store extends EmberObject {
|
|
|
4586
4430
|
---
|
|
4587
4431
|
If you do something like this:
|
|
4588
4432
|
```javascript
|
|
4589
|
-
store.query('person', { ids: [1, 2, 3] });
|
|
4433
|
+
store.query('person', { ids: ['1', '2', '3'] });
|
|
4590
4434
|
```
|
|
4591
4435
|
The request made to the server will look something like this:
|
|
4592
4436
|
```
|
|
@@ -4599,24 +4443,25 @@ class Store extends EmberObject {
|
|
|
4599
4443
|
@since 1.13.0
|
|
4600
4444
|
@method query
|
|
4601
4445
|
@public
|
|
4602
|
-
@param {String}
|
|
4603
|
-
@param {
|
|
4446
|
+
@param {String} type the name of the resource
|
|
4447
|
+
@param {object} query a query to be used by the adapter
|
|
4604
4448
|
@param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.query
|
|
4605
4449
|
@return {Promise} promise
|
|
4606
4450
|
*/
|
|
4607
|
-
|
|
4451
|
+
|
|
4452
|
+
query(type, query, options = {}) {
|
|
4608
4453
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4609
4454
|
assertDestroyingStore(this, 'query');
|
|
4610
4455
|
}
|
|
4611
|
-
assert(`You need to pass a model name to the store's query method`,
|
|
4456
|
+
assert(`You need to pass a model name to the store's query method`, type);
|
|
4612
4457
|
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 ${
|
|
4458
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4614
4459
|
const promise = this.request({
|
|
4615
4460
|
op: 'query',
|
|
4616
4461
|
data: {
|
|
4617
|
-
type: normalizeModelName(
|
|
4462
|
+
type: normalizeModelName(type),
|
|
4618
4463
|
query,
|
|
4619
|
-
options: options
|
|
4464
|
+
options: options
|
|
4620
4465
|
},
|
|
4621
4466
|
cacheOptions: {
|
|
4622
4467
|
[SkipCache]: true
|
|
@@ -4701,22 +4546,23 @@ class Store extends EmberObject {
|
|
|
4701
4546
|
@since 1.13.0
|
|
4702
4547
|
@method queryRecord
|
|
4703
4548
|
@public
|
|
4704
|
-
@param {
|
|
4705
|
-
@param {
|
|
4706
|
-
@param {
|
|
4549
|
+
@param {string} type
|
|
4550
|
+
@param {object} query an opaque query to be used by the adapter
|
|
4551
|
+
@param {object} options optional, may include `adapterOptions` hash which will be passed to adapter.queryRecord
|
|
4707
4552
|
@return {Promise} promise which resolves with the found record or `null`
|
|
4708
4553
|
*/
|
|
4709
|
-
|
|
4554
|
+
|
|
4555
|
+
queryRecord(type, query, options) {
|
|
4710
4556
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4711
4557
|
assertDestroyingStore(this, 'queryRecord');
|
|
4712
4558
|
}
|
|
4713
|
-
assert(`You need to pass a model name to the store's queryRecord method`,
|
|
4559
|
+
assert(`You need to pass a model name to the store's queryRecord method`, type);
|
|
4714
4560
|
assert(`You need to pass a query hash to the store's queryRecord method`, query);
|
|
4715
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${
|
|
4561
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4716
4562
|
const promise = this.request({
|
|
4717
4563
|
op: 'queryRecord',
|
|
4718
4564
|
data: {
|
|
4719
|
-
type: normalizeModelName(
|
|
4565
|
+
type: normalizeModelName(type),
|
|
4720
4566
|
query,
|
|
4721
4567
|
options: options || {}
|
|
4722
4568
|
},
|
|
@@ -4874,20 +4720,21 @@ class Store extends EmberObject {
|
|
|
4874
4720
|
@since 1.13.0
|
|
4875
4721
|
@method findAll
|
|
4876
4722
|
@public
|
|
4877
|
-
@param {
|
|
4878
|
-
@param {
|
|
4723
|
+
@param {string} type the name of the resource
|
|
4724
|
+
@param {object} options
|
|
4879
4725
|
@return {Promise} promise
|
|
4880
4726
|
*/
|
|
4881
|
-
|
|
4727
|
+
|
|
4728
|
+
findAll(type, options = {}) {
|
|
4882
4729
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4883
4730
|
assertDestroyingStore(this, 'findAll');
|
|
4884
4731
|
}
|
|
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 ${
|
|
4732
|
+
assert(`You need to pass a model name to the store's findAll method`, type);
|
|
4733
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4887
4734
|
const promise = this.request({
|
|
4888
4735
|
op: 'findAll',
|
|
4889
4736
|
data: {
|
|
4890
|
-
type: normalizeModelName(
|
|
4737
|
+
type: normalizeModelName(type),
|
|
4891
4738
|
options: options || {}
|
|
4892
4739
|
},
|
|
4893
4740
|
cacheOptions: {
|
|
@@ -4914,17 +4761,17 @@ class Store extends EmberObject {
|
|
|
4914
4761
|
@since 1.13.0
|
|
4915
4762
|
@method peekAll
|
|
4916
4763
|
@public
|
|
4917
|
-
@param {
|
|
4764
|
+
@param {string} type the name of the resource
|
|
4918
4765
|
@return {RecordArray}
|
|
4919
4766
|
*/
|
|
4920
|
-
|
|
4767
|
+
|
|
4768
|
+
peekAll(type) {
|
|
4921
4769
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4922
4770
|
assertDestroyingStore(this, 'peekAll');
|
|
4923
4771
|
}
|
|
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);
|
|
4772
|
+
assert(`You need to pass a model name to the store's peekAll method`, type);
|
|
4773
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`, typeof type === 'string');
|
|
4774
|
+
return this.recordArrayManager.liveArrayFor(normalizeModelName(type));
|
|
4928
4775
|
}
|
|
4929
4776
|
|
|
4930
4777
|
/**
|
|
@@ -4936,16 +4783,17 @@ class Store extends EmberObject {
|
|
|
4936
4783
|
store.unloadAll('post');
|
|
4937
4784
|
```
|
|
4938
4785
|
@method unloadAll
|
|
4786
|
+
@param {string} type the name of the resource
|
|
4939
4787
|
@public
|
|
4940
|
-
@param {String} modelName
|
|
4941
4788
|
*/
|
|
4942
|
-
|
|
4789
|
+
|
|
4790
|
+
unloadAll(type) {
|
|
4943
4791
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
4944
4792
|
assertDestroyedStoreOnly(this, 'unloadAll');
|
|
4945
4793
|
}
|
|
4946
|
-
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(
|
|
4794
|
+
assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`, !type || typeof type === 'string');
|
|
4947
4795
|
this._join(() => {
|
|
4948
|
-
if (
|
|
4796
|
+
if (type === undefined) {
|
|
4949
4797
|
// destroy the graph before unloadAll
|
|
4950
4798
|
// since then we avoid churning relationships
|
|
4951
4799
|
// during unload
|
|
@@ -4953,8 +4801,7 @@ class Store extends EmberObject {
|
|
|
4953
4801
|
this.recordArrayManager.clear();
|
|
4954
4802
|
this._instanceCache.clear();
|
|
4955
4803
|
} else {
|
|
4956
|
-
|
|
4957
|
-
this._instanceCache.clear(normalizedModelName);
|
|
4804
|
+
this._instanceCache.clear(normalizeModelName(type));
|
|
4958
4805
|
}
|
|
4959
4806
|
});
|
|
4960
4807
|
}
|
|
@@ -5094,10 +4941,9 @@ class Store extends EmberObject {
|
|
|
5094
4941
|
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5095
4942
|
assertDestroyingStore(this, 'push');
|
|
5096
4943
|
}
|
|
5097
|
-
|
|
4944
|
+
const pushed = this._push(data, false);
|
|
5098
4945
|
if (Array.isArray(pushed)) {
|
|
5099
|
-
|
|
5100
|
-
return records;
|
|
4946
|
+
return pushed.map(identifier => this._instanceCache.getRecord(identifier));
|
|
5101
4947
|
}
|
|
5102
4948
|
if (pushed === null) {
|
|
5103
4949
|
return null;
|
|
@@ -5119,7 +4965,7 @@ class Store extends EmberObject {
|
|
|
5119
4965
|
}
|
|
5120
4966
|
if (macroCondition(getOwnConfig().debug.LOG_PAYLOADS)) {
|
|
5121
4967
|
try {
|
|
5122
|
-
|
|
4968
|
+
const data = JSON.parse(JSON.stringify(jsonApiDoc));
|
|
5123
4969
|
// eslint-disable-next-line no-console
|
|
5124
4970
|
console.log('EmberData | Payload - push', data);
|
|
5125
4971
|
} catch (e) {
|
|
@@ -5136,379 +4982,745 @@ class Store extends EmberObject {
|
|
|
5136
4982
|
content: jsonApiDoc
|
|
5137
4983
|
});
|
|
5138
4984
|
});
|
|
5139
|
-
this._enableAsyncFlush = null;
|
|
5140
|
-
return 'data' in ret ? ret.data : null;
|
|
4985
|
+
this._enableAsyncFlush = null;
|
|
4986
|
+
return 'data' in ret ? ret.data : null;
|
|
4987
|
+
}
|
|
4988
|
+
|
|
4989
|
+
/**
|
|
4990
|
+
* Trigger a save for a Record.
|
|
4991
|
+
*
|
|
4992
|
+
* Returns a promise resolving with the same record when the save is complete.
|
|
4993
|
+
*
|
|
4994
|
+
* @method saveRecord
|
|
4995
|
+
* @public
|
|
4996
|
+
* @param {unknown} record
|
|
4997
|
+
* @param options
|
|
4998
|
+
* @return {Promise<record>}
|
|
4999
|
+
*/
|
|
5000
|
+
saveRecord(record, options = {}) {
|
|
5001
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5002
|
+
assertDestroyingStore(this, 'saveRecord');
|
|
5003
|
+
}
|
|
5004
|
+
assert(`Unable to initiate save for a record in a disconnected state`, storeFor(record));
|
|
5005
|
+
const identifier = recordIdentifierFor(record);
|
|
5006
|
+
const cache = this.cache;
|
|
5007
|
+
if (!identifier) {
|
|
5008
|
+
// this commonly means we're disconnected
|
|
5009
|
+
// but just in case we reject here to prevent bad things.
|
|
5010
|
+
return Promise.reject(new Error(`Record Is Disconnected`));
|
|
5011
|
+
}
|
|
5012
|
+
assert(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`, this._instanceCache.recordIsLoaded(identifier));
|
|
5013
|
+
if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
|
|
5014
|
+
return Promise.resolve(record);
|
|
5015
|
+
}
|
|
5016
|
+
if (!options) {
|
|
5017
|
+
options = {};
|
|
5018
|
+
}
|
|
5019
|
+
let operation = 'updateRecord';
|
|
5020
|
+
if (cache.isNew(identifier)) {
|
|
5021
|
+
operation = 'createRecord';
|
|
5022
|
+
} else if (cache.isDeleted(identifier)) {
|
|
5023
|
+
operation = 'deleteRecord';
|
|
5024
|
+
}
|
|
5025
|
+
const request = {
|
|
5026
|
+
op: operation,
|
|
5027
|
+
data: {
|
|
5028
|
+
options,
|
|
5029
|
+
record: identifier
|
|
5030
|
+
},
|
|
5031
|
+
records: [identifier],
|
|
5032
|
+
cacheOptions: {
|
|
5033
|
+
[SkipCache]: true
|
|
5034
|
+
}
|
|
5035
|
+
};
|
|
5036
|
+
return this.request(request).then(document => document.content);
|
|
5037
|
+
}
|
|
5038
|
+
|
|
5039
|
+
/**
|
|
5040
|
+
* Instantiation hook allowing applications or addons to configure the store
|
|
5041
|
+
* to utilize a custom Cache implementation.
|
|
5042
|
+
*
|
|
5043
|
+
* This hook should not be called directly by consuming applications or libraries.
|
|
5044
|
+
* Use `Store.cache` to access the Cache instance.
|
|
5045
|
+
*
|
|
5046
|
+
* @method createCache (hook)
|
|
5047
|
+
* @public
|
|
5048
|
+
* @param storeWrapper
|
|
5049
|
+
* @return {Cache}
|
|
5050
|
+
*/
|
|
5051
|
+
|
|
5052
|
+
/**
|
|
5053
|
+
* Returns the cache instance associated to this Store, instantiates the Cache
|
|
5054
|
+
* if necessary via `Store.createCache`
|
|
5055
|
+
*
|
|
5056
|
+
* @property {Cache} cache
|
|
5057
|
+
* @public
|
|
5058
|
+
*/
|
|
5059
|
+
get cache() {
|
|
5060
|
+
let {
|
|
5061
|
+
cache
|
|
5062
|
+
} = this._instanceCache;
|
|
5063
|
+
if (!cache) {
|
|
5064
|
+
cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
|
|
5065
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5066
|
+
cache = new CacheManager(cache);
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
return cache;
|
|
5070
|
+
}
|
|
5071
|
+
|
|
5072
|
+
// @ts-expect-error
|
|
5073
|
+
destroy() {
|
|
5074
|
+
if (this.isDestroyed) {
|
|
5075
|
+
// @ember/test-helpers will call destroy multiple times
|
|
5076
|
+
return;
|
|
5077
|
+
}
|
|
5078
|
+
this.isDestroying = true;
|
|
5079
|
+
this._graph?.destroy();
|
|
5080
|
+
this._graph = undefined;
|
|
5081
|
+
this.notifications.destroy();
|
|
5082
|
+
this.recordArrayManager.destroy();
|
|
5083
|
+
this.identifierCache.destroy();
|
|
5084
|
+
this.unloadAll();
|
|
5085
|
+
this.isDestroyed = true;
|
|
5086
|
+
}
|
|
5087
|
+
static create(args) {
|
|
5088
|
+
return new this(args);
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
let assertDestroyingStore;
|
|
5092
|
+
let assertDestroyedStoreOnly;
|
|
5093
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5094
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5095
|
+
assertDestroyingStore = function assertDestroyingStore(store, method) {
|
|
5096
|
+
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !(store.isDestroying || store.isDestroyed));
|
|
5097
|
+
};
|
|
5098
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5099
|
+
assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
|
|
5100
|
+
assert(`Attempted to call store.${method}(), but the store instance has already been destroyed.`, !store.isDestroyed);
|
|
5101
|
+
};
|
|
5102
|
+
}
|
|
5103
|
+
function isMaybeIdentifier(maybeIdentifier) {
|
|
5104
|
+
return Boolean(maybeIdentifier !== null && typeof maybeIdentifier === 'object' && ('id' in maybeIdentifier && 'type' in maybeIdentifier && maybeIdentifier.id && maybeIdentifier.type || maybeIdentifier.lid));
|
|
5105
|
+
}
|
|
5106
|
+
function normalizeProperties(store, identifier, properties) {
|
|
5107
|
+
// assert here
|
|
5108
|
+
if (properties !== undefined) {
|
|
5109
|
+
if ('id' in properties) {
|
|
5110
|
+
assert(`expected id to be a string or null`, properties.id !== undefined);
|
|
5111
|
+
}
|
|
5112
|
+
assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
|
|
5113
|
+
const {
|
|
5114
|
+
type
|
|
5115
|
+
} = identifier;
|
|
5116
|
+
|
|
5117
|
+
// convert relationship Records to RecordDatas before passing to RecordData
|
|
5118
|
+
const defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({
|
|
5119
|
+
type
|
|
5120
|
+
});
|
|
5121
|
+
if (defs !== null) {
|
|
5122
|
+
const keys = Object.keys(properties);
|
|
5123
|
+
let relationshipValue;
|
|
5124
|
+
for (let i = 0; i < keys.length; i++) {
|
|
5125
|
+
const prop = keys[i];
|
|
5126
|
+
const def = defs[prop];
|
|
5127
|
+
if (def !== undefined) {
|
|
5128
|
+
if (def.kind === 'hasMany') {
|
|
5129
|
+
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5130
|
+
assertRecordsPassedToHasMany(properties[prop]);
|
|
5131
|
+
}
|
|
5132
|
+
relationshipValue = extractIdentifiersFromRecords(properties[prop]);
|
|
5133
|
+
} else {
|
|
5134
|
+
relationshipValue = extractIdentifierFromRecord(properties[prop]);
|
|
5135
|
+
}
|
|
5136
|
+
properties[prop] = relationshipValue;
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
return properties;
|
|
5142
|
+
}
|
|
5143
|
+
function assertRecordsPassedToHasMany(records) {
|
|
5144
|
+
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
5145
|
+
assert(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`, function () {
|
|
5146
|
+
return records.every(record => {
|
|
5147
|
+
try {
|
|
5148
|
+
recordIdentifierFor(record);
|
|
5149
|
+
return true;
|
|
5150
|
+
} catch {
|
|
5151
|
+
return false;
|
|
5152
|
+
}
|
|
5153
|
+
});
|
|
5154
|
+
}());
|
|
5155
|
+
}
|
|
5156
|
+
function extractIdentifiersFromRecords(records) {
|
|
5157
|
+
return records.map(record => extractIdentifierFromRecord(record));
|
|
5158
|
+
}
|
|
5159
|
+
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
5160
|
+
if (!recordOrPromiseRecord) {
|
|
5161
|
+
return null;
|
|
5141
5162
|
}
|
|
5163
|
+
const extract = recordIdentifierFor;
|
|
5164
|
+
return extract(recordOrPromiseRecord);
|
|
5165
|
+
}
|
|
5166
|
+
function urlFromLink(link) {
|
|
5167
|
+
if (typeof link === 'string') return link;
|
|
5168
|
+
return link.href;
|
|
5169
|
+
}
|
|
5142
5170
|
|
|
5171
|
+
/**
|
|
5172
|
+
* A Document is a class that wraps the response content from a request to the API
|
|
5173
|
+
* returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
|
|
5174
|
+
* record instances.
|
|
5175
|
+
*
|
|
5176
|
+
* It is not directly instantiated by the user, and its properties should not
|
|
5177
|
+
* be directly modified. Whether individual properties are mutable or not is
|
|
5178
|
+
* determined by the record instance itself.
|
|
5179
|
+
*
|
|
5180
|
+
* @public
|
|
5181
|
+
* @class Document
|
|
5182
|
+
*/
|
|
5183
|
+
var _store = /*#__PURE__*/_classPrivateFieldKey("store");
|
|
5184
|
+
var _request = /*#__PURE__*/_classPrivateFieldKey("request");
|
|
5185
|
+
class Document {
|
|
5186
|
+
constructor(store, identifier) {
|
|
5187
|
+
Object.defineProperty(this, _request, {
|
|
5188
|
+
value: _request2
|
|
5189
|
+
});
|
|
5190
|
+
/**
|
|
5191
|
+
* The links object for this document, if any
|
|
5192
|
+
*
|
|
5193
|
+
* e.g.
|
|
5194
|
+
*
|
|
5195
|
+
* ```
|
|
5196
|
+
* {
|
|
5197
|
+
* self: '/articles?page[number]=3',
|
|
5198
|
+
* }
|
|
5199
|
+
* ```
|
|
5200
|
+
*
|
|
5201
|
+
* @property links
|
|
5202
|
+
* @type {object|undefined} - a links object
|
|
5203
|
+
* @public
|
|
5204
|
+
*/
|
|
5205
|
+
/**
|
|
5206
|
+
* The primary data for this document, if any.
|
|
5207
|
+
*
|
|
5208
|
+
* If this document has no primary data (e.g. because it is an error document)
|
|
5209
|
+
* this property will be `undefined`.
|
|
5210
|
+
*
|
|
5211
|
+
* For collections this will be an array of record instances,
|
|
5212
|
+
* for single resource requests it will be a single record instance or null.
|
|
5213
|
+
*
|
|
5214
|
+
* @property data
|
|
5215
|
+
* @public
|
|
5216
|
+
* @type {object|Array<object>|null|undefined} - a data object
|
|
5217
|
+
*/
|
|
5218
|
+
/**
|
|
5219
|
+
* The errors returned by the API for this request, if any
|
|
5220
|
+
*
|
|
5221
|
+
* @property errors
|
|
5222
|
+
* @public
|
|
5223
|
+
* @type {object|undefined} - an errors object
|
|
5224
|
+
*/
|
|
5225
|
+
/**
|
|
5226
|
+
* The meta object for this document, if any
|
|
5227
|
+
*
|
|
5228
|
+
* @property meta
|
|
5229
|
+
* @public
|
|
5230
|
+
* @type {object|undefined} - a meta object
|
|
5231
|
+
*/
|
|
5232
|
+
/**
|
|
5233
|
+
* The identifier associated with this document, if any
|
|
5234
|
+
*
|
|
5235
|
+
* @property identifier
|
|
5236
|
+
* @public
|
|
5237
|
+
* @type {StableDocumentIdentifier|null}
|
|
5238
|
+
*/
|
|
5239
|
+
Object.defineProperty(this, _store, {
|
|
5240
|
+
writable: true,
|
|
5241
|
+
value: void 0
|
|
5242
|
+
});
|
|
5243
|
+
_classPrivateFieldBase(this, _store)[_store] = store;
|
|
5244
|
+
this.identifier = identifier;
|
|
5245
|
+
}
|
|
5143
5246
|
/**
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
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);
|
|
5247
|
+
* Fetches the related link for this document, returning a promise that resolves
|
|
5248
|
+
* with the document when the request completes. If no related link is present,
|
|
5249
|
+
* will fallback to the self link if present
|
|
5250
|
+
*
|
|
5251
|
+
* @method fetch
|
|
5252
|
+
* @public
|
|
5253
|
+
* @param {object} options
|
|
5254
|
+
* @return Promise<Document>
|
|
5255
|
+
*/
|
|
5256
|
+
fetch(options = {}) {
|
|
5257
|
+
assert(`No self or related link`, this.links?.related || this.links?.self);
|
|
5258
|
+
options.cacheOptions = options.cacheOptions || {};
|
|
5259
|
+
options.cacheOptions.key = this.identifier?.lid;
|
|
5260
|
+
return _classPrivateFieldBase(this, _request)[_request](this.links.related ? 'related' : 'self', options);
|
|
5196
5261
|
}
|
|
5197
5262
|
|
|
5198
5263
|
/**
|
|
5199
|
-
*
|
|
5264
|
+
* Fetches the next link for this document, returning a promise that resolves
|
|
5265
|
+
* with the new document when the request completes, or null if there is no
|
|
5266
|
+
* next link.
|
|
5200
5267
|
*
|
|
5201
|
-
* @method
|
|
5268
|
+
* @method next
|
|
5202
5269
|
* @public
|
|
5203
|
-
* @param {
|
|
5204
|
-
* @
|
|
5205
|
-
* @returns {Promise<RecordInstance>}
|
|
5270
|
+
* @param {object} options
|
|
5271
|
+
* @return Promise<Document | null>
|
|
5206
5272
|
*/
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
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
|
-
};
|
|
5273
|
+
next(options = {}) {
|
|
5274
|
+
return _classPrivateFieldBase(this, _request)[_request]('next', options);
|
|
5275
|
+
}
|
|
5243
5276
|
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5277
|
+
/**
|
|
5278
|
+
* Fetches the prev link for this document, returning a promise that resolves
|
|
5279
|
+
* with the new document when the request completes, or null if there is no
|
|
5280
|
+
* prev link.
|
|
5281
|
+
*
|
|
5282
|
+
* @method prev
|
|
5283
|
+
* @public
|
|
5284
|
+
* @param {object} options
|
|
5285
|
+
* @return Promise<Document | null>
|
|
5286
|
+
*/
|
|
5287
|
+
prev(options = {}) {
|
|
5288
|
+
return _classPrivateFieldBase(this, _request)[_request]('prev', options);
|
|
5249
5289
|
}
|
|
5250
5290
|
|
|
5251
5291
|
/**
|
|
5252
|
-
*
|
|
5253
|
-
*
|
|
5292
|
+
* Fetches the first link for this document, returning a promise that resolves
|
|
5293
|
+
* with the new document when the request completes, or null if there is no
|
|
5294
|
+
* first link.
|
|
5254
5295
|
*
|
|
5255
|
-
*
|
|
5256
|
-
*
|
|
5296
|
+
* @method first
|
|
5297
|
+
* @public
|
|
5298
|
+
* @param {object} options
|
|
5299
|
+
* @return Promise<Document | null>
|
|
5300
|
+
*/
|
|
5301
|
+
first(options = {}) {
|
|
5302
|
+
return _classPrivateFieldBase(this, _request)[_request]('first', options);
|
|
5303
|
+
}
|
|
5304
|
+
|
|
5305
|
+
/**
|
|
5306
|
+
* Fetches the last link for this document, returning a promise that resolves
|
|
5307
|
+
* with the new document when the request completes, or null if there is no
|
|
5308
|
+
* last link.
|
|
5257
5309
|
*
|
|
5258
|
-
* @method
|
|
5310
|
+
* @method last
|
|
5259
5311
|
* @public
|
|
5260
|
-
* @param
|
|
5261
|
-
* @
|
|
5312
|
+
* @param {object} options
|
|
5313
|
+
* @return Promise<Document | null>
|
|
5262
5314
|
*/
|
|
5315
|
+
last(options = {}) {
|
|
5316
|
+
return _classPrivateFieldBase(this, _request)[_request]('last', options);
|
|
5317
|
+
}
|
|
5263
5318
|
|
|
5264
5319
|
/**
|
|
5265
|
-
*
|
|
5266
|
-
* if necessary via `Store.createCache`
|
|
5320
|
+
* Implemented for `JSON.stringify` support.
|
|
5267
5321
|
*
|
|
5268
|
-
*
|
|
5322
|
+
* Returns the JSON representation of the document wrapper.
|
|
5323
|
+
*
|
|
5324
|
+
* This is a shallow serialization, it does not deeply serialize
|
|
5325
|
+
* the document's contents, leaving that to the individual record
|
|
5326
|
+
* instances to determine how to do, if at all.
|
|
5327
|
+
*
|
|
5328
|
+
* @method toJSON
|
|
5269
5329
|
* @public
|
|
5330
|
+
* @return
|
|
5270
5331
|
*/
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5332
|
+
toJSON() {
|
|
5333
|
+
const data = {};
|
|
5334
|
+
data.identifier = this.identifier;
|
|
5335
|
+
if (this.data !== undefined) {
|
|
5336
|
+
data.data = this.data;
|
|
5337
|
+
}
|
|
5338
|
+
if (this.links !== undefined) {
|
|
5339
|
+
data.links = this.links;
|
|
5340
|
+
}
|
|
5341
|
+
if (this.errors !== undefined) {
|
|
5342
|
+
data.errors = this.errors;
|
|
5343
|
+
}
|
|
5344
|
+
if (this.meta !== undefined) {
|
|
5345
|
+
data.meta = this.meta;
|
|
5346
|
+
}
|
|
5347
|
+
return data;
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
async function _request2(link, options) {
|
|
5351
|
+
const href = this.links?.[link];
|
|
5352
|
+
if (!href) {
|
|
5353
|
+
return null;
|
|
5354
|
+
}
|
|
5355
|
+
options.method = options.method || 'GET';
|
|
5356
|
+
const response = await _classPrivateFieldBase(this, _store)[_store].request(Object.assign(options, {
|
|
5357
|
+
url: urlFromLink(href)
|
|
5358
|
+
}));
|
|
5359
|
+
return response.content;
|
|
5360
|
+
}
|
|
5361
|
+
defineSignal(Document.prototype, 'data');
|
|
5362
|
+
defineSignal(Document.prototype, 'links');
|
|
5363
|
+
defineSignal(Document.prototype, 'errors');
|
|
5364
|
+
defineSignal(Document.prototype, 'meta');
|
|
5365
|
+
|
|
5366
|
+
/**
|
|
5367
|
+
* @module @ember-data/store
|
|
5368
|
+
*/
|
|
5369
|
+
|
|
5370
|
+
/**
|
|
5371
|
+
* A service which an application may provide to the store via
|
|
5372
|
+
* the store's `lifetimes` property to configure the behavior
|
|
5373
|
+
* of the CacheHandler.
|
|
5374
|
+
*
|
|
5375
|
+
* The default behavior for request lifetimes is to never expire
|
|
5376
|
+
* unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
|
|
5377
|
+
*
|
|
5378
|
+
* Implementing this service allows you to programatically define
|
|
5379
|
+
* when a request should be considered expired.
|
|
5380
|
+
*
|
|
5381
|
+
* @class <Interface> LifetimesService
|
|
5382
|
+
* @public
|
|
5383
|
+
*/
|
|
5384
|
+
|
|
5385
|
+
const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
|
|
5386
|
+
function isErrorDocument(document) {
|
|
5387
|
+
return 'errors' in document;
|
|
5388
|
+
}
|
|
5389
|
+
function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
5390
|
+
const {
|
|
5391
|
+
identifier
|
|
5392
|
+
} = options;
|
|
5393
|
+
if (!document) {
|
|
5394
|
+
assert(`The CacheHandler expected response content but none was found`, !options.shouldHydrate);
|
|
5395
|
+
return document;
|
|
5396
|
+
}
|
|
5397
|
+
if (isErrorDocument(document)) {
|
|
5398
|
+
if (!identifier && !options.shouldHydrate) {
|
|
5399
|
+
return document;
|
|
5400
|
+
}
|
|
5401
|
+
let doc;
|
|
5402
|
+
if (identifier) {
|
|
5403
|
+
doc = store._documentCache.get(identifier);
|
|
5404
|
+
}
|
|
5405
|
+
if (!doc) {
|
|
5406
|
+
doc = new Document(store, identifier);
|
|
5407
|
+
copyDocumentProperties(doc, document);
|
|
5408
|
+
if (identifier) {
|
|
5409
|
+
store._documentCache.set(identifier, doc);
|
|
5410
|
+
}
|
|
5411
|
+
} else if (!isFromCache) {
|
|
5412
|
+
doc.data = undefined;
|
|
5413
|
+
copyDocumentProperties(doc, document);
|
|
5414
|
+
}
|
|
5415
|
+
return options.shouldHydrate ? doc : document;
|
|
5416
|
+
}
|
|
5417
|
+
if (Array.isArray(document.data)) {
|
|
5418
|
+
const {
|
|
5419
|
+
recordArrayManager
|
|
5420
|
+
} = store;
|
|
5421
|
+
if (!identifier) {
|
|
5422
|
+
if (!options.shouldHydrate) {
|
|
5423
|
+
return document;
|
|
5424
|
+
}
|
|
5425
|
+
const data = recordArrayManager.createArray({
|
|
5426
|
+
type: request.url,
|
|
5427
|
+
identifiers: document.data,
|
|
5428
|
+
doc: document,
|
|
5429
|
+
query: request
|
|
5430
|
+
});
|
|
5431
|
+
const doc = new Document(store, null);
|
|
5432
|
+
doc.data = data;
|
|
5433
|
+
doc.meta = document.meta;
|
|
5434
|
+
doc.links = document.links;
|
|
5435
|
+
return doc;
|
|
5436
|
+
}
|
|
5437
|
+
let managed = recordArrayManager._keyedArrays.get(identifier.lid);
|
|
5438
|
+
if (!managed) {
|
|
5439
|
+
managed = recordArrayManager.createArray({
|
|
5440
|
+
type: identifier.lid,
|
|
5441
|
+
identifiers: document.data,
|
|
5442
|
+
doc: document
|
|
5443
|
+
});
|
|
5444
|
+
recordArrayManager._keyedArrays.set(identifier.lid, managed);
|
|
5445
|
+
const doc = new Document(store, identifier);
|
|
5446
|
+
doc.data = managed;
|
|
5447
|
+
doc.meta = document.meta;
|
|
5448
|
+
doc.links = document.links;
|
|
5449
|
+
store._documentCache.set(identifier, doc);
|
|
5450
|
+
return options.shouldHydrate ? doc : document;
|
|
5451
|
+
} else {
|
|
5452
|
+
const doc = store._documentCache.get(identifier);
|
|
5453
|
+
if (!isFromCache) {
|
|
5454
|
+
recordArrayManager.populateManagedArray(managed, document.data, document);
|
|
5455
|
+
doc.data = managed;
|
|
5456
|
+
doc.meta = document.meta;
|
|
5457
|
+
doc.links = document.links;
|
|
5279
5458
|
}
|
|
5459
|
+
return options.shouldHydrate ? doc : document;
|
|
5280
5460
|
}
|
|
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;
|
|
5461
|
+
} else {
|
|
5462
|
+
if (!identifier && !options.shouldHydrate) {
|
|
5463
|
+
return document;
|
|
5340
5464
|
}
|
|
5341
|
-
const
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
if (adapter !== undefined) {
|
|
5346
|
-
_adapterCache[normalizedModelName] = adapter;
|
|
5347
|
-
return adapter;
|
|
5465
|
+
const data = document.data ? store.peekRecord(document.data) : null;
|
|
5466
|
+
let doc;
|
|
5467
|
+
if (identifier) {
|
|
5468
|
+
doc = store._documentCache.get(identifier);
|
|
5348
5469
|
}
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5470
|
+
if (!doc) {
|
|
5471
|
+
doc = new Document(store, identifier);
|
|
5472
|
+
doc.data = data;
|
|
5473
|
+
copyDocumentProperties(doc, document);
|
|
5474
|
+
if (identifier) {
|
|
5475
|
+
store._documentCache.set(identifier, doc);
|
|
5476
|
+
}
|
|
5477
|
+
} else if (!isFromCache) {
|
|
5478
|
+
doc.data = data;
|
|
5479
|
+
copyDocumentProperties(doc, document);
|
|
5356
5480
|
}
|
|
5357
|
-
|
|
5481
|
+
return options.shouldHydrate ? doc : document;
|
|
5358
5482
|
}
|
|
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
|
-
|
|
5483
|
+
}
|
|
5484
|
+
function calcShouldFetch(store, request, hasCachedValue, identifier) {
|
|
5485
|
+
const {
|
|
5486
|
+
cacheOptions
|
|
5487
|
+
} = request;
|
|
5488
|
+
return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
|
|
5489
|
+
}
|
|
5490
|
+
function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
|
|
5491
|
+
const {
|
|
5492
|
+
cacheOptions
|
|
5493
|
+
} = request;
|
|
5494
|
+
return !willFetch && (cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false));
|
|
5495
|
+
}
|
|
5496
|
+
function isMutation(request) {
|
|
5497
|
+
return Boolean(request.op && MUTATION_OPS.has(request.op));
|
|
5498
|
+
}
|
|
5499
|
+
function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
|
|
5500
|
+
const {
|
|
5501
|
+
store
|
|
5502
|
+
} = context.request;
|
|
5503
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
5504
|
+
let isMut = false;
|
|
5505
|
+
if (isMutation(context.request)) {
|
|
5506
|
+
isMut = true;
|
|
5507
|
+
// TODO should we handle multiple records in request.records by iteratively calling willCommit for each
|
|
5508
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5509
|
+
assert(`Expected to receive a list of records included in the ${context.request.op} request`, record || !shouldHydrate);
|
|
5510
|
+
if (record) {
|
|
5511
|
+
store.cache.willCommit(record, context);
|
|
5387
5512
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5513
|
+
}
|
|
5514
|
+
if (store.lifetimes?.willRequest) {
|
|
5515
|
+
store.lifetimes.willRequest(context.request, identifier, store);
|
|
5516
|
+
}
|
|
5517
|
+
const promise = next(context.request).then(document => {
|
|
5518
|
+
store.requestManager._pending.delete(context.id);
|
|
5519
|
+
store._enableAsyncFlush = true;
|
|
5520
|
+
let response;
|
|
5521
|
+
store._join(() => {
|
|
5522
|
+
if (isMutation(context.request)) {
|
|
5523
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5524
|
+
if (record) {
|
|
5525
|
+
response = store.cache.didCommit(record, document);
|
|
5526
|
+
|
|
5527
|
+
// a mutation combined with a 204 has no cache impact when no known records were involved
|
|
5528
|
+
// a createRecord with a 201 with an empty response and no known records should similarly
|
|
5529
|
+
// have no cache impact
|
|
5530
|
+
} else if (isCacheAffecting(document)) {
|
|
5531
|
+
response = store.cache.put(document);
|
|
5532
|
+
}
|
|
5533
|
+
} else {
|
|
5534
|
+
response = store.cache.put(document);
|
|
5535
|
+
}
|
|
5536
|
+
response = maybeUpdateUiObjects(store, context.request, {
|
|
5537
|
+
shouldHydrate,
|
|
5538
|
+
shouldFetch,
|
|
5539
|
+
shouldBackgroundFetch,
|
|
5540
|
+
identifier
|
|
5541
|
+
}, response, false);
|
|
5542
|
+
});
|
|
5543
|
+
store._enableAsyncFlush = null;
|
|
5544
|
+
if (store.lifetimes?.didRequest) {
|
|
5545
|
+
store.lifetimes.didRequest(context.request, document.response, identifier, store);
|
|
5395
5546
|
}
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
_serializerCache[normalizedModelName] = serializer;
|
|
5401
|
-
_serializerCache.application = serializer;
|
|
5402
|
-
return serializer;
|
|
5547
|
+
if (shouldFetch) {
|
|
5548
|
+
return response;
|
|
5549
|
+
} else if (shouldBackgroundFetch) {
|
|
5550
|
+
store.notifications._flush();
|
|
5403
5551
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
destroy() {
|
|
5409
|
-
if (this.isDestroyed) {
|
|
5410
|
-
// @ember/test-helpers will call destroy multiple times
|
|
5411
|
-
return;
|
|
5552
|
+
}, error => {
|
|
5553
|
+
store.requestManager._pending.delete(context.id);
|
|
5554
|
+
if (context.request.signal?.aborted) {
|
|
5555
|
+
throw error;
|
|
5412
5556
|
}
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
if (
|
|
5418
|
-
|
|
5557
|
+
store.requestManager._pending.delete(context.id);
|
|
5558
|
+
store._enableAsyncFlush = true;
|
|
5559
|
+
let response;
|
|
5560
|
+
store._join(() => {
|
|
5561
|
+
if (isMutation(context.request)) {
|
|
5562
|
+
// TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
|
|
5563
|
+
// currently we let the response remain undefiend.
|
|
5564
|
+
const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
|
|
5565
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5566
|
+
store.cache.commitWasRejected(record, errors);
|
|
5567
|
+
// re-throw the original error to preserve `errors` property.
|
|
5568
|
+
throw error;
|
|
5569
|
+
} else {
|
|
5570
|
+
response = store.cache.put(error);
|
|
5571
|
+
response = maybeUpdateUiObjects(store, context.request, {
|
|
5572
|
+
shouldHydrate,
|
|
5573
|
+
shouldFetch,
|
|
5574
|
+
shouldBackgroundFetch,
|
|
5575
|
+
identifier
|
|
5576
|
+
}, response, false);
|
|
5419
5577
|
}
|
|
5578
|
+
});
|
|
5579
|
+
store._enableAsyncFlush = null;
|
|
5580
|
+
if (identifier && store.lifetimes?.didRequest) {
|
|
5581
|
+
store.lifetimes.didRequest(context.request, error.response, identifier, store);
|
|
5420
5582
|
}
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5583
|
+
if (!shouldBackgroundFetch) {
|
|
5584
|
+
const newError = cloneError(error);
|
|
5585
|
+
newError.content = response;
|
|
5586
|
+
throw newError;
|
|
5587
|
+
} else {
|
|
5588
|
+
store.notifications._flush();
|
|
5426
5589
|
}
|
|
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);
|
|
5590
|
+
});
|
|
5591
|
+
if (!isMut) {
|
|
5592
|
+
return promise;
|
|
5437
5593
|
}
|
|
5594
|
+
assert(`Expected a mutation`, isMutation(context.request));
|
|
5595
|
+
|
|
5596
|
+
// for mutations we need to enqueue the promise with the requestStateService
|
|
5597
|
+
// TODO should we enque a request per record in records?
|
|
5598
|
+
const record = context.request.data?.record || context.request.records?.[0];
|
|
5599
|
+
return store._requestCache._enqueue(promise, {
|
|
5600
|
+
data: [{
|
|
5601
|
+
op: 'saveRecord',
|
|
5602
|
+
recordIdentifier: record,
|
|
5603
|
+
options: undefined
|
|
5604
|
+
}]
|
|
5605
|
+
});
|
|
5438
5606
|
}
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
if (macroCondition(getOwnConfig().env.DEBUG)) {
|
|
5442
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
5443
|
-
assertDestroyingStore = function assertDestroyingStore(store, method) {
|
|
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
|
-
};
|
|
5607
|
+
function isAggregateError(error) {
|
|
5608
|
+
return error instanceof AggregateError || error.name === 'AggregateError' && Array.isArray(error.errors);
|
|
5450
5609
|
}
|
|
5451
|
-
|
|
5452
|
-
|
|
5610
|
+
// TODO @runspired, consider if we should deep freeze errors (potentially only in debug) vs cloning them
|
|
5611
|
+
function cloneError(error) {
|
|
5612
|
+
const isAggregate = isAggregateError(error);
|
|
5613
|
+
const cloned = isAggregate ? new AggregateError(structuredClone(error.errors), error.message) : new Error(error.message);
|
|
5614
|
+
cloned.stack = error.stack;
|
|
5615
|
+
cloned.error = error.error;
|
|
5616
|
+
|
|
5617
|
+
// copy over enumerable properties
|
|
5618
|
+
Object.assign(cloned, error);
|
|
5619
|
+
return cloned;
|
|
5453
5620
|
}
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5621
|
+
|
|
5622
|
+
/**
|
|
5623
|
+
* A CacheHandler that adds support for using an EmberData Cache with a RequestManager.
|
|
5624
|
+
*
|
|
5625
|
+
* This handler will only run when a request has supplied a `store` instance. Requests
|
|
5626
|
+
* issued by the store via `store.request()` will automatically have the `store` instance
|
|
5627
|
+
* attached to the request.
|
|
5628
|
+
*
|
|
5629
|
+
* ```ts
|
|
5630
|
+
* requestManager.request({
|
|
5631
|
+
* store: store,
|
|
5632
|
+
* url: '/api/posts',
|
|
5633
|
+
* method: 'GET'
|
|
5634
|
+
* });
|
|
5635
|
+
* ```
|
|
5636
|
+
*
|
|
5637
|
+
* When this handler elects to handle a request, it will return the raw `StructuredDocument`
|
|
5638
|
+
* unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
|
|
5639
|
+
* return a `Document` instance that will automatically update the UI when the cache is updated
|
|
5640
|
+
* in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
|
|
5641
|
+
*
|
|
5642
|
+
* When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
|
|
5643
|
+
* means that if desired you can issue requests that utilize the cache without needing to also
|
|
5644
|
+
* utilize Record instances if desired.
|
|
5645
|
+
*
|
|
5646
|
+
* Said differently, you could elect to issue all requests via a RequestManager, without ever using
|
|
5647
|
+
* the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
|
|
5648
|
+
* necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
|
|
5649
|
+
* approach of EmberData allows for this flexibility.
|
|
5650
|
+
*
|
|
5651
|
+
* ```ts
|
|
5652
|
+
* import { EnableHydration } from '@warp-drive/core-types/request';
|
|
5653
|
+
*
|
|
5654
|
+
* requestManager.request({
|
|
5655
|
+
* store: store,
|
|
5656
|
+
* url: '/api/posts',
|
|
5657
|
+
* method: 'GET',
|
|
5658
|
+
* [EnableHydration]: true
|
|
5659
|
+
* });
|
|
5660
|
+
*
|
|
5661
|
+
* @typedoc
|
|
5662
|
+
*/
|
|
5663
|
+
const CacheHandler = {
|
|
5664
|
+
request(context, next) {
|
|
5665
|
+
// if we have no cache or no cache-key skip cache handling
|
|
5666
|
+
if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
|
|
5667
|
+
return next(context.request);
|
|
5459
5668
|
}
|
|
5460
|
-
assert(`You passed '${typeof properties}' as properties for record creation instead of an object.`, typeof properties === 'object' && properties !== null);
|
|
5461
5669
|
const {
|
|
5462
|
-
|
|
5463
|
-
} =
|
|
5670
|
+
store
|
|
5671
|
+
} = context.request;
|
|
5672
|
+
const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
|
|
5673
|
+
const peeked = identifier ? store.cache.peekRequest(identifier) : null;
|
|
5464
5674
|
|
|
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
|
-
}
|
|
5675
|
+
// determine if we should skip cache
|
|
5676
|
+
if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
|
|
5677
|
+
return fetchContentAndHydrate(next, context, identifier, true, false);
|
|
5678
|
+
}
|
|
5679
|
+
|
|
5680
|
+
// if we have not skipped cache, determine if we should update behind the scenes
|
|
5681
|
+
if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
|
|
5682
|
+
const promise = fetchContentAndHydrate(next, context, identifier, false, true);
|
|
5683
|
+
store.requestManager._pending.set(context.id, promise);
|
|
5684
|
+
}
|
|
5685
|
+
const shouldHydrate = context.request[EnableHydration] || false;
|
|
5686
|
+
if ('error' in peeked) {
|
|
5687
|
+
const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
5688
|
+
shouldHydrate,
|
|
5689
|
+
identifier
|
|
5690
|
+
}, peeked.content, true) : peeked.content;
|
|
5691
|
+
const newError = cloneError(peeked);
|
|
5692
|
+
newError.content = content;
|
|
5693
|
+
throw newError;
|
|
5487
5694
|
}
|
|
5695
|
+
const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
5696
|
+
shouldHydrate,
|
|
5697
|
+
identifier
|
|
5698
|
+
}, peeked.content, true) : peeked.content;
|
|
5699
|
+
return result;
|
|
5700
|
+
}
|
|
5701
|
+
};
|
|
5702
|
+
function copyDocumentProperties(target, source) {
|
|
5703
|
+
if ('links' in source) {
|
|
5704
|
+
target.links = source.links;
|
|
5705
|
+
}
|
|
5706
|
+
if ('meta' in source) {
|
|
5707
|
+
target.meta = source.meta;
|
|
5708
|
+
}
|
|
5709
|
+
if ('errors' in source) {
|
|
5710
|
+
target.errors = source.errors;
|
|
5488
5711
|
}
|
|
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
5712
|
}
|
|
5507
|
-
function
|
|
5508
|
-
if (!
|
|
5509
|
-
return
|
|
5713
|
+
function isCacheAffecting(document) {
|
|
5714
|
+
if (!isMutation(document.request)) {
|
|
5715
|
+
return true;
|
|
5510
5716
|
}
|
|
5511
|
-
|
|
5512
|
-
|
|
5717
|
+
// a mutation combined with a 204 has no cache impact when no known records were involved
|
|
5718
|
+
// a createRecord with a 201 with an empty response and no known records should similarly
|
|
5719
|
+
// have no cache impact
|
|
5720
|
+
|
|
5721
|
+
if (document.request.op === 'createRecord' && document.response?.status === 201) {
|
|
5722
|
+
return document.content ? Object.keys(document.content).length > 0 : false;
|
|
5723
|
+
}
|
|
5724
|
+
return document.response?.status !== 204;
|
|
5513
5725
|
}
|
|
5514
|
-
export { CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _,
|
|
5726
|
+
export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, coerceId as a, Collection as b, constructResource as c, SOURCE as d, ensureStringId as e, fastPush as f, removeRecordDataFor as g, setRecordIdentifier as h, isStableIdentifier as i, StoreMap as j, setCacheFor as k, normalizeModelName as l, setIdentifierGenerationMethod as m, notifyArray as n, setIdentifierUpdateMethod as o, peekCache as p, setIdentifierForgetMethod as q, recordIdentifierFor as r, storeFor as s, setIdentifierResetMethod as t, setKeyInfoForResource as u };
|