@ember-data/store 4.8.0-alpha.5 → 4.9.0-alpha.0
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/addon/-debug/index.js +34 -12
- package/addon/-private/caches/identifier-cache.ts +3 -2
- package/addon/-private/caches/instance-cache.ts +78 -85
- package/addon/-private/index.ts +10 -6
- package/addon/-private/legacy-model-support/shim-model-class.ts +1 -0
- package/addon/-private/managers/record-array-manager.ts +138 -59
- package/addon/-private/managers/record-data-manager.ts +143 -128
- package/addon/-private/managers/record-data-store-wrapper.ts +7 -1
- package/addon/-private/managers/record-notification-manager.ts +1 -0
- package/addon/-private/network/fetch-manager.ts +16 -1
- package/addon/-private/network/snapshot-record-array.ts +6 -6
- package/addon/-private/proxies/promise-proxies.ts +72 -11
- package/addon/-private/record-arrays/identifier-array.ts +924 -0
- package/addon/-private/store-service.ts +117 -60
- package/addon/-private/utils/promise-record.ts +2 -3
- package/addon/-private/utils/uuid-polyfill.ts +58 -56
- package/package.json +4 -4
- package/addon/-private/record-arrays/adapter-populated-record-array.ts +0 -99
- package/addon/-private/record-arrays/record-array.ts +0 -289
package/addon/-debug/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
2
|
import { DEBUG } from '@glimmer/env';
|
|
3
3
|
|
|
4
|
+
import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@ember-data/private-build-infra/deprecations';
|
|
5
|
+
|
|
4
6
|
/*
|
|
5
7
|
Assert that `addedRecord` has a valid type so it can be added to the
|
|
6
8
|
relationship of the `record`.
|
|
@@ -29,18 +31,38 @@ if (DEBUG) {
|
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
assertPolymorphicType = function assertPolymorphicType(parentIdentifier, parentDefinition, addedIdentifier, store) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
let asserted = false;
|
|
35
|
+
|
|
36
|
+
if (parentDefinition.inverseIsImplicit) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (parentDefinition.isPolymorphic) {
|
|
40
|
+
let meta = store.getSchemaDefinitionService().relationshipsDefinitionFor(addedIdentifier)[
|
|
41
|
+
parentDefinition.inverseKey
|
|
42
|
+
];
|
|
43
|
+
if (meta?.options?.as) {
|
|
44
|
+
asserted = true;
|
|
45
|
+
assert(
|
|
46
|
+
`The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
|
|
47
|
+
meta.options.as === parentDefinition.type
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM && !asserted) {
|
|
53
|
+
store = store._store ? store._store : store; // allow usage with storeWrapper
|
|
54
|
+
let addedModelName = addedIdentifier.type;
|
|
55
|
+
let parentModelName = parentIdentifier.type;
|
|
56
|
+
let key = parentDefinition.key;
|
|
57
|
+
let relationshipModelName = parentDefinition.type;
|
|
58
|
+
let relationshipClass = store.modelFor(relationshipModelName);
|
|
59
|
+
let addedClass = store.modelFor(addedModelName);
|
|
60
|
+
|
|
61
|
+
let assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
|
|
62
|
+
let isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
|
|
63
|
+
|
|
64
|
+
assert(assertionMessage, isPolymorphic);
|
|
65
|
+
}
|
|
44
66
|
};
|
|
45
67
|
}
|
|
46
68
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { assert, warn } from '@ember/debug';
|
|
5
5
|
import { DEBUG } from '@glimmer/env';
|
|
6
6
|
|
|
7
|
-
import { getOwnConfig,
|
|
7
|
+
import { getOwnConfig, macroCondition } from '@embroider/macros';
|
|
8
8
|
|
|
9
9
|
import { LOG_IDENTIFIERS } from '@ember-data/private-build-infra/debugging';
|
|
10
10
|
import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
|
|
@@ -25,6 +25,7 @@ import coerceId from '../utils/coerce-id';
|
|
|
25
25
|
import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '../utils/identifer-debug-consts';
|
|
26
26
|
import isNonEmptyString from '../utils/is-non-empty-string';
|
|
27
27
|
import normalizeModelName from '../utils/normalize-model-name';
|
|
28
|
+
import installPolyfill from '../utils/uuid-polyfill';
|
|
28
29
|
|
|
29
30
|
const IDENTIFIERS = new Set();
|
|
30
31
|
|
|
@@ -36,7 +37,7 @@ const isFastBoot = typeof FastBoot !== 'undefined';
|
|
|
36
37
|
const _crypto: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
|
|
37
38
|
|
|
38
39
|
if (macroCondition(getOwnConfig<{ polyfillUUID: boolean }>().polyfillUUID)) {
|
|
39
|
-
|
|
40
|
+
installPolyfill();
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
function uuidv4(): string {
|
|
@@ -4,11 +4,11 @@ import { DEBUG } from '@glimmer/env';
|
|
|
4
4
|
import { importSync } from '@embroider/macros';
|
|
5
5
|
import { resolve } from 'rsvp';
|
|
6
6
|
|
|
7
|
-
import { V2CACHE_SINGLETON_MANAGER } from '@ember-data/canary-features';
|
|
8
7
|
import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
|
|
9
8
|
import { LOG_INSTANCE_CACHE } from '@ember-data/private-build-infra/debugging';
|
|
10
|
-
import { DEPRECATE_V1CACHE_STORE_APIS } from '@ember-data/private-build-infra/deprecations';
|
|
11
|
-
import type { Graph
|
|
9
|
+
import { DEPRECATE_V1_RECORD_DATA, DEPRECATE_V1CACHE_STORE_APIS } from '@ember-data/private-build-infra/deprecations';
|
|
10
|
+
import type { Graph } from '@ember-data/record-data/-private/graph/graph';
|
|
11
|
+
import type { peekGraph } from '@ember-data/record-data/-private/graph/index';
|
|
12
12
|
import type {
|
|
13
13
|
ExistingResourceIdentifierObject,
|
|
14
14
|
ExistingResourceObject,
|
|
@@ -129,44 +129,6 @@ export class InstanceCache {
|
|
|
129
129
|
reference: new WeakMap<StableRecordIdentifier, RecordReference>(),
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
-
recordIsLoaded(identifier: StableRecordIdentifier, filterDeleted: boolean = false) {
|
|
133
|
-
const recordData = this.__instances.recordData.get(identifier);
|
|
134
|
-
if (!recordData) {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
const isNew = recordData.isNew(identifier);
|
|
138
|
-
const isEmpty = recordData.isEmpty(identifier);
|
|
139
|
-
|
|
140
|
-
// if we are new we must consider ourselves loaded
|
|
141
|
-
if (isNew) {
|
|
142
|
-
return !recordData.isDeleted(identifier);
|
|
143
|
-
}
|
|
144
|
-
// even if we have a past request, if we are now empty we are not loaded
|
|
145
|
-
// typically this is true after an unloadRecord call
|
|
146
|
-
|
|
147
|
-
// if we are not empty, not new && we have a fulfilled request then we are loaded
|
|
148
|
-
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
149
|
-
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
150
|
-
// in that it does not account for unloading.
|
|
151
|
-
return filterDeleted && recordData.isDeletionCommitted(identifier) ? false : !isEmpty;
|
|
152
|
-
|
|
153
|
-
/*
|
|
154
|
-
const req = this.store.getRequestStateService();
|
|
155
|
-
const fulfilled = req.getLastRequestForRecord(identifier);
|
|
156
|
-
const isLocallyLoaded = !isEmpty;
|
|
157
|
-
const isLoading =
|
|
158
|
-
!isLocallyLoaded &&
|
|
159
|
-
fulfilled === null &&
|
|
160
|
-
req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query');
|
|
161
|
-
|
|
162
|
-
if (isEmpty || (filterDeleted && recordData.isDeletionCommitted(identifier)) || isLoading) {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return true;
|
|
167
|
-
*/
|
|
168
|
-
}
|
|
169
|
-
|
|
170
132
|
constructor(store: Store) {
|
|
171
133
|
this.store = store;
|
|
172
134
|
|
|
@@ -179,25 +141,24 @@ export class InstanceCache {
|
|
|
179
141
|
|
|
180
142
|
store.identifierCache.__configureMerge(
|
|
181
143
|
(identifier: StableRecordIdentifier, matchedIdentifier: StableRecordIdentifier, resourceData) => {
|
|
182
|
-
let
|
|
144
|
+
let keptIdentifier = identifier;
|
|
183
145
|
if (identifier.id !== matchedIdentifier.id) {
|
|
184
|
-
|
|
185
|
-
'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier;
|
|
146
|
+
keptIdentifier = 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier;
|
|
186
147
|
} else if (identifier.type !== matchedIdentifier.type) {
|
|
187
|
-
|
|
148
|
+
keptIdentifier =
|
|
188
149
|
'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
|
|
189
150
|
}
|
|
190
|
-
let
|
|
151
|
+
let staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
|
|
191
152
|
|
|
192
153
|
// check for duplicate entities
|
|
193
|
-
let
|
|
194
|
-
let
|
|
195
|
-
let
|
|
196
|
-
let
|
|
154
|
+
let keptHasRecord = this.__instances.record.has(keptIdentifier);
|
|
155
|
+
let staleHasRecord = this.__instances.record.has(staleIdentifier);
|
|
156
|
+
let keptRecordData = this.__instances.recordData.get(keptIdentifier) || null;
|
|
157
|
+
let staleRecordData = this.__instances.recordData.get(staleIdentifier) || null;
|
|
197
158
|
|
|
198
159
|
// we cannot merge entities when both have records
|
|
199
160
|
// (this may not be strictly true, we could probably swap the recordData the record points at)
|
|
200
|
-
if (
|
|
161
|
+
if (keptHasRecord && staleHasRecord) {
|
|
201
162
|
// TODO we probably don't need to throw these errors anymore
|
|
202
163
|
// we can probably just "swap" what data source the abandoned
|
|
203
164
|
// record points at so long as
|
|
@@ -211,7 +172,7 @@ export class InstanceCache {
|
|
|
211
172
|
}:${String(matchedIdentifier.id)} (${matchedIdentifier.lid})'`
|
|
212
173
|
);
|
|
213
174
|
}
|
|
214
|
-
|
|
175
|
+
|
|
215
176
|
assert(
|
|
216
177
|
`Failed to update the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${
|
|
217
178
|
identifier.lid
|
|
@@ -221,33 +182,26 @@ export class InstanceCache {
|
|
|
221
182
|
);
|
|
222
183
|
}
|
|
223
184
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
185
|
+
let recordData = keptRecordData || staleRecordData;
|
|
186
|
+
|
|
187
|
+
if (recordData) {
|
|
188
|
+
recordData.sync({
|
|
189
|
+
op: 'mergeIdentifiers',
|
|
190
|
+
record: staleIdentifier,
|
|
191
|
+
value: keptIdentifier,
|
|
192
|
+
});
|
|
193
|
+
} else if (HAS_RECORD_DATA_PACKAGE) {
|
|
194
|
+
// TODO notify cache always, this requires it always being a singleton
|
|
195
|
+
// and not ever specific to one record-data
|
|
196
|
+
this.store.__private_singleton_recordData?.sync({
|
|
197
|
+
op: 'mergeIdentifiers',
|
|
198
|
+
record: staleIdentifier,
|
|
199
|
+
value: keptIdentifier,
|
|
200
|
+
});
|
|
227
201
|
}
|
|
228
202
|
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
return intendedIdentifier;
|
|
232
|
-
|
|
233
|
-
// only the other has a RecordData
|
|
234
|
-
// OR only the other has a Record
|
|
235
|
-
} else if (
|
|
236
|
-
(imRecordData === null && otherRecordData !== null) ||
|
|
237
|
-
(imRecordData && !imHasRecord && otherRecordData && otherHasRecord)
|
|
238
|
-
) {
|
|
239
|
-
if (imRecordData) {
|
|
240
|
-
// TODO check if we are retained in any async relationships
|
|
241
|
-
// TODO probably need to release other things
|
|
242
|
-
// im.destroy();
|
|
243
|
-
}
|
|
244
|
-
imRecordData = otherRecordData!;
|
|
245
|
-
// TODO do we need to notify the id change?
|
|
246
|
-
// TODO swap recordIdentifierFor result?
|
|
247
|
-
|
|
248
|
-
// just use im
|
|
249
|
-
} else {
|
|
250
|
-
// otherIm.destroy();
|
|
203
|
+
if (staleRecordData === null) {
|
|
204
|
+
return keptIdentifier;
|
|
251
205
|
}
|
|
252
206
|
|
|
253
207
|
/*
|
|
@@ -260,7 +214,8 @@ export class InstanceCache {
|
|
|
260
214
|
}
|
|
261
215
|
*/
|
|
262
216
|
|
|
263
|
-
|
|
217
|
+
this.unloadRecord(staleIdentifier);
|
|
218
|
+
return keptIdentifier;
|
|
264
219
|
}
|
|
265
220
|
);
|
|
266
221
|
}
|
|
@@ -324,23 +279,23 @@ export class InstanceCache {
|
|
|
324
279
|
identifier.lid,
|
|
325
280
|
this._storeWrapper
|
|
326
281
|
);
|
|
327
|
-
if (
|
|
282
|
+
if (DEPRECATE_V1_RECORD_DATA) {
|
|
283
|
+
recordData = new NonSingletonRecordDataManager(this.store, recordDataInstance, identifier);
|
|
284
|
+
} else {
|
|
328
285
|
recordData = this.__cacheManager =
|
|
329
286
|
this.__cacheManager || new NonSingletonRecordDataManager(this.store, recordDataInstance, identifier);
|
|
330
|
-
} else {
|
|
331
|
-
recordData = new NonSingletonRecordDataManager(this.store, recordDataInstance, identifier);
|
|
332
287
|
}
|
|
333
288
|
} else {
|
|
334
289
|
let recordDataInstance = this.store.createRecordDataFor(identifier, this._storeWrapper);
|
|
335
|
-
if (
|
|
290
|
+
if (DEPRECATE_V1_RECORD_DATA) {
|
|
291
|
+
recordData = new NonSingletonRecordDataManager(this.store, recordDataInstance, identifier);
|
|
292
|
+
} else {
|
|
336
293
|
if (DEBUG) {
|
|
337
|
-
recordData = this.__cacheManager = this.__cacheManager || new SingletonRecordDataManager(
|
|
294
|
+
recordData = this.__cacheManager = this.__cacheManager || new SingletonRecordDataManager();
|
|
338
295
|
(recordData as SingletonRecordDataManager)._addRecordData(identifier, recordDataInstance as RecordData);
|
|
339
296
|
} else {
|
|
340
297
|
recordData = recordDataInstance as RecordData;
|
|
341
298
|
}
|
|
342
|
-
} else {
|
|
343
|
-
recordData = new NonSingletonRecordDataManager(this.store, recordDataInstance, identifier);
|
|
344
299
|
}
|
|
345
300
|
}
|
|
346
301
|
setRecordDataFor(identifier, recordData);
|
|
@@ -366,6 +321,44 @@ export class InstanceCache {
|
|
|
366
321
|
return reference;
|
|
367
322
|
}
|
|
368
323
|
|
|
324
|
+
recordIsLoaded(identifier: StableRecordIdentifier, filterDeleted: boolean = false) {
|
|
325
|
+
const recordData = this.__instances.recordData.get(identifier);
|
|
326
|
+
if (!recordData) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
const isNew = recordData.isNew(identifier);
|
|
330
|
+
const isEmpty = recordData.isEmpty(identifier);
|
|
331
|
+
|
|
332
|
+
// if we are new we must consider ourselves loaded
|
|
333
|
+
if (isNew) {
|
|
334
|
+
return !recordData.isDeleted(identifier);
|
|
335
|
+
}
|
|
336
|
+
// even if we have a past request, if we are now empty we are not loaded
|
|
337
|
+
// typically this is true after an unloadRecord call
|
|
338
|
+
|
|
339
|
+
// if we are not empty, not new && we have a fulfilled request then we are loaded
|
|
340
|
+
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
341
|
+
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
342
|
+
// in that it does not account for unloading.
|
|
343
|
+
return filterDeleted && recordData.isDeletionCommitted(identifier) ? false : !isEmpty;
|
|
344
|
+
|
|
345
|
+
/*
|
|
346
|
+
const req = this.store.getRequestStateService();
|
|
347
|
+
const fulfilled = req.getLastRequestForRecord(identifier);
|
|
348
|
+
const isLocallyLoaded = !isEmpty;
|
|
349
|
+
const isLoading =
|
|
350
|
+
!isLocallyLoaded &&
|
|
351
|
+
fulfilled === null &&
|
|
352
|
+
req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query');
|
|
353
|
+
|
|
354
|
+
if (isEmpty || (filterDeleted && recordData.isDeletionCommitted(identifier)) || isLoading) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return true;
|
|
359
|
+
*/
|
|
360
|
+
}
|
|
361
|
+
|
|
369
362
|
createSnapshot(identifier: StableRecordIdentifier, options: FindOptions = {}): Snapshot {
|
|
370
363
|
return new Snapshot(options, identifier, this.store);
|
|
371
364
|
}
|
package/addon/-private/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export {
|
|
|
18
18
|
setIdentifierUpdateMethod,
|
|
19
19
|
setIdentifierForgetMethod,
|
|
20
20
|
setIdentifierResetMethod,
|
|
21
|
+
isStableIdentifier,
|
|
21
22
|
} from './caches/identifier-cache';
|
|
22
23
|
|
|
23
24
|
export function normalizeModelName(modelName: string) {
|
|
@@ -41,12 +42,15 @@ export function normalizeModelName(modelName: string) {
|
|
|
41
42
|
// to also eliminate
|
|
42
43
|
export { default as coerceId } from './utils/coerce-id';
|
|
43
44
|
|
|
44
|
-
export {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
export {
|
|
46
|
+
default as RecordArray,
|
|
47
|
+
default as IdentifierArray,
|
|
48
|
+
Collection as AdapterPopulatedRecordArray,
|
|
49
|
+
SOURCE,
|
|
50
|
+
MUTATE,
|
|
51
|
+
IDENTIFIER_ARRAY_TAG,
|
|
52
|
+
} from './record-arrays/identifier-array';
|
|
53
|
+
export { default as RecordArrayManager, fastPush } from './managers/record-array-manager';
|
|
50
54
|
|
|
51
55
|
// // Used by tests
|
|
52
56
|
export { default as SnapshotRecordArray } from './network/snapshot-record-array';
|
|
@@ -15,6 +15,7 @@ export function getShimClass(store: Store, modelName: string): ShimModelClass {
|
|
|
15
15
|
shims = Object.create(null) as Dict<ShimModelClass>;
|
|
16
16
|
AvailableShims.set(store, shims);
|
|
17
17
|
}
|
|
18
|
+
|
|
18
19
|
let shim = shims[modelName];
|
|
19
20
|
if (shim === undefined) {
|
|
20
21
|
shim = shims[modelName] = new ShimModelClass(store, modelName);
|