@ember-data/store 4.8.0-alpha.3 → 4.8.0-alpha.6
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/-private/caches/identifier-cache.ts +65 -66
- package/addon/-private/caches/instance-cache.ts +173 -236
- package/addon/-private/caches/record-data-for.ts +1 -6
- package/addon/-private/index.ts +13 -10
- package/addon/-private/legacy-model-support/record-reference.ts +12 -10
- package/addon/-private/legacy-model-support/schema-definition-service.ts +9 -4
- package/addon/-private/legacy-model-support/shim-model-class.ts +17 -10
- package/addon/-private/managers/record-array-manager.ts +282 -321
- package/addon/-private/managers/record-data-manager.ts +822 -0
- package/addon/-private/managers/record-data-store-wrapper.ts +295 -91
- package/addon/-private/managers/record-notification-manager.ts +45 -32
- package/addon/-private/network/fetch-manager.ts +292 -300
- package/addon/-private/network/finders.js +11 -6
- package/addon/-private/network/request-cache.ts +20 -17
- package/addon/-private/network/snapshot-record-array.ts +12 -29
- package/addon/-private/network/snapshot.ts +25 -27
- 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 +380 -114
- package/addon/-private/utils/is-non-empty-string.ts +1 -1
- package/addon/-private/utils/promise-record.ts +2 -3
- package/addon/-private/utils/uuid-polyfill.ts +71 -0
- package/package.json +11 -7
- package/addon/-private/backburner.js +0 -25
- package/addon/-private/record-arrays/adapter-populated-record-array.ts +0 -128
- package/addon/-private/record-arrays/record-array.ts +0 -320
- package/addon/-private/utils/weak-cache.ts +0 -125
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { assert, warn } from '@ember/debug';
|
|
5
5
|
import { DEBUG } from '@glimmer/env';
|
|
6
6
|
|
|
7
|
+
import { getOwnConfig, importSync, macroCondition } from '@embroider/macros';
|
|
8
|
+
|
|
7
9
|
import { LOG_IDENTIFIERS } from '@ember-data/private-build-infra/debugging';
|
|
8
10
|
import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
|
|
9
11
|
import type {
|
|
@@ -23,9 +25,8 @@ import coerceId from '../utils/coerce-id';
|
|
|
23
25
|
import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '../utils/identifer-debug-consts';
|
|
24
26
|
import isNonEmptyString from '../utils/is-non-empty-string';
|
|
25
27
|
import normalizeModelName from '../utils/normalize-model-name';
|
|
26
|
-
import WeakCache from '../utils/weak-cache';
|
|
27
28
|
|
|
28
|
-
const IDENTIFIERS = new
|
|
29
|
+
const IDENTIFIERS = new Set();
|
|
29
30
|
|
|
30
31
|
export function isStableIdentifier(identifier: Object): identifier is StableRecordIdentifier {
|
|
31
32
|
return IDENTIFIERS.has(identifier);
|
|
@@ -34,6 +35,10 @@ export function isStableIdentifier(identifier: Object): identifier is StableReco
|
|
|
34
35
|
const isFastBoot = typeof FastBoot !== 'undefined';
|
|
35
36
|
const _crypto: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
|
|
36
37
|
|
|
38
|
+
if (macroCondition(getOwnConfig<{ polyfillUUID: boolean }>().polyfillUUID)) {
|
|
39
|
+
importSync('./utils/uuid-polyfill');
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
function uuidv4(): string {
|
|
38
43
|
return _crypto.randomUUID();
|
|
39
44
|
}
|
|
@@ -48,10 +53,9 @@ function freeze<T>(obj: T): T {
|
|
|
48
53
|
interface KeyOptions {
|
|
49
54
|
lid: IdentifierMap;
|
|
50
55
|
id: IdentifierMap;
|
|
51
|
-
_allIdentifiers: StableRecordIdentifier[];
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
type IdentifierMap =
|
|
58
|
+
type IdentifierMap = Map<string, StableRecordIdentifier>;
|
|
55
59
|
type TypeMap = ConfidentDict<KeyOptions>;
|
|
56
60
|
export type MergeMethod = (
|
|
57
61
|
targetIdentifier: StableRecordIdentifier,
|
|
@@ -80,12 +84,15 @@ export function setIdentifierResetMethod(method: ResetMethod | null): void {
|
|
|
80
84
|
configuredResetMethod = method;
|
|
81
85
|
}
|
|
82
86
|
|
|
87
|
+
type WithLid = { lid: string };
|
|
88
|
+
type WithId = { id: string | null; type: string };
|
|
89
|
+
|
|
83
90
|
function defaultGenerationMethod(data: ResourceData | { type: string }, bucket: IdentifierBucket): string {
|
|
84
|
-
if (
|
|
85
|
-
return data.lid;
|
|
91
|
+
if (isNonEmptyString((data as WithLid).lid)) {
|
|
92
|
+
return (data as WithLid).lid;
|
|
86
93
|
}
|
|
87
|
-
if (
|
|
88
|
-
let { type, id } = data;
|
|
94
|
+
if ((data as WithId).id !== undefined) {
|
|
95
|
+
let { type, id } = data as WithId;
|
|
89
96
|
// TODO: add test for id not a string
|
|
90
97
|
if (isNonEmptyString(coerceId(id))) {
|
|
91
98
|
return `@lid:${normalizeModelName(type)}-${id}`;
|
|
@@ -98,7 +105,7 @@ function defaultEmptyCallback(...args: any[]): any {}
|
|
|
98
105
|
|
|
99
106
|
let DEBUG_MAP;
|
|
100
107
|
if (DEBUG) {
|
|
101
|
-
DEBUG_MAP = new
|
|
108
|
+
DEBUG_MAP = new WeakMap<StableRecordIdentifier, StableRecordIdentifier>();
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
/**
|
|
@@ -115,19 +122,16 @@ if (DEBUG) {
|
|
|
115
122
|
@public
|
|
116
123
|
*/
|
|
117
124
|
export class IdentifierCache {
|
|
118
|
-
// Typescript still leaks private properties in the final
|
|
119
|
-
// compiled class, so we may want to move these from _underscore
|
|
120
|
-
// to a WeakMap to avoid leaking
|
|
121
|
-
// currently we leak this for test purposes
|
|
122
125
|
_cache = {
|
|
123
|
-
lids:
|
|
126
|
+
lids: new Map<string, StableRecordIdentifier>(),
|
|
124
127
|
types: Object.create(null) as TypeMap,
|
|
125
128
|
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
declare _generate: GenerationMethod;
|
|
130
|
+
declare _update: UpdateMethod;
|
|
131
|
+
declare _forget: ForgetMethod;
|
|
132
|
+
declare _reset: ResetMethod;
|
|
133
|
+
declare _merge: MergeMethod;
|
|
134
|
+
declare _isDefaultConfig: boolean;
|
|
131
135
|
|
|
132
136
|
constructor() {
|
|
133
137
|
// we cache the user configuredGenerationMethod at init because it must
|
|
@@ -137,6 +141,7 @@ export class IdentifierCache {
|
|
|
137
141
|
this._forget = configuredForgetMethod || defaultEmptyCallback;
|
|
138
142
|
this._reset = configuredResetMethod || defaultEmptyCallback;
|
|
139
143
|
this._merge = defaultEmptyCallback;
|
|
144
|
+
this._isDefaultConfig = !configuredGenerationMethod;
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
/**
|
|
@@ -156,12 +161,9 @@ export class IdentifierCache {
|
|
|
156
161
|
* @method _getRecordIdentifier
|
|
157
162
|
* @private
|
|
158
163
|
*/
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
shouldGenerate: false
|
|
163
|
-
): StableRecordIdentifier | undefined;
|
|
164
|
-
private _getRecordIdentifier(
|
|
164
|
+
_getRecordIdentifier(resource: ResourceIdentifierObject, shouldGenerate: true): StableRecordIdentifier;
|
|
165
|
+
_getRecordIdentifier(resource: ResourceIdentifierObject, shouldGenerate: false): StableRecordIdentifier | undefined;
|
|
166
|
+
_getRecordIdentifier(
|
|
165
167
|
resource: ResourceIdentifierObject,
|
|
166
168
|
shouldGenerate: boolean = false
|
|
167
169
|
): StableRecordIdentifier | undefined {
|
|
@@ -169,7 +171,7 @@ export class IdentifierCache {
|
|
|
169
171
|
if (isStableIdentifier(resource)) {
|
|
170
172
|
if (DEBUG) {
|
|
171
173
|
// TODO should we instead just treat this case as a new generation skipping the short circuit?
|
|
172
|
-
if (!
|
|
174
|
+
if (!this._cache.lids.has(resource.lid) || this._cache.lids.get(resource.lid) !== resource) {
|
|
173
175
|
throw new Error(`The supplied identifier ${resource} does not belong to this store instance`);
|
|
174
176
|
}
|
|
175
177
|
}
|
|
@@ -181,7 +183,7 @@ export class IdentifierCache {
|
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
let lid = coerceId(resource.lid);
|
|
184
|
-
let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids
|
|
186
|
+
let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids.get(lid) : undefined;
|
|
185
187
|
|
|
186
188
|
if (identifier !== undefined) {
|
|
187
189
|
if (LOG_IDENTIFIERS) {
|
|
@@ -197,7 +199,7 @@ export class IdentifierCache {
|
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
if (shouldGenerate === false) {
|
|
200
|
-
if (!(
|
|
202
|
+
if (!(resource as ExistingResourceObject).type || !(resource as ExistingResourceObject).id) {
|
|
201
203
|
return;
|
|
202
204
|
}
|
|
203
205
|
}
|
|
@@ -212,13 +214,13 @@ export class IdentifierCache {
|
|
|
212
214
|
|
|
213
215
|
// go straight for the stable RecordIdentifier key'd to `lid`
|
|
214
216
|
if (lid !== null) {
|
|
215
|
-
identifier = keyOptions.lid
|
|
217
|
+
identifier = keyOptions.lid.get(lid);
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
// we may have not seen this resource before
|
|
219
221
|
// but just in case we check our own secondary lookup (`id`)
|
|
220
222
|
if (identifier === undefined && id !== null) {
|
|
221
|
-
identifier = keyOptions.id
|
|
223
|
+
identifier = keyOptions.id.get(id);
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
if (identifier === undefined) {
|
|
@@ -235,12 +237,12 @@ export class IdentifierCache {
|
|
|
235
237
|
// different than expected
|
|
236
238
|
if (lid !== null && newLid !== lid) {
|
|
237
239
|
throw new Error(`You should not change the <lid> of a RecordIdentifier`);
|
|
238
|
-
} else if (lid === null) {
|
|
240
|
+
} else if (lid === null && !this._isDefaultConfig) {
|
|
239
241
|
// allow configuration to tell us that we have
|
|
240
242
|
// seen this `lid` before. E.g. a secondary lookup
|
|
241
243
|
// connects this resource to a previously seen
|
|
242
244
|
// resource.
|
|
243
|
-
identifier = keyOptions.lid
|
|
245
|
+
identifier = keyOptions.lid.get(newLid);
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
if (shouldGenerate === true) {
|
|
@@ -252,19 +254,16 @@ export class IdentifierCache {
|
|
|
252
254
|
if (DEBUG) {
|
|
253
255
|
// realistically if you hit this it means you changed `type` :/
|
|
254
256
|
// TODO consider how to handle type change assertions more gracefully
|
|
255
|
-
if (
|
|
257
|
+
if (this._cache.lids.has(identifier.lid)) {
|
|
256
258
|
throw new Error(`You should not change the <type> of a RecordIdentifier`);
|
|
257
259
|
}
|
|
258
260
|
}
|
|
259
|
-
this._cache.lids
|
|
261
|
+
this._cache.lids.set(identifier.lid, identifier);
|
|
260
262
|
|
|
261
263
|
// populate our primary lookup table
|
|
262
264
|
// TODO consider having the `lid` cache be
|
|
263
265
|
// one level up
|
|
264
|
-
keyOptions.lid
|
|
265
|
-
// TODO exists temporarily to support `peekAll`
|
|
266
|
-
// but likely to move
|
|
267
|
-
keyOptions._allIdentifiers.push(identifier);
|
|
266
|
+
keyOptions.lid.set(identifier.lid, identifier);
|
|
268
267
|
|
|
269
268
|
if (LOG_IDENTIFIERS && shouldGenerate) {
|
|
270
269
|
// eslint-disable-next-line no-console
|
|
@@ -286,7 +285,7 @@ export class IdentifierCache {
|
|
|
286
285
|
// because they may not match and we prefer
|
|
287
286
|
// what we've set via resource data
|
|
288
287
|
if (identifier.id !== null) {
|
|
289
|
-
keyOptions.id
|
|
288
|
+
keyOptions.id.set(identifier.id, identifier);
|
|
290
289
|
|
|
291
290
|
// TODO allow filling out of `id` here
|
|
292
291
|
// for the `username` non-client created
|
|
@@ -360,17 +359,17 @@ export class IdentifierCache {
|
|
|
360
359
|
|
|
361
360
|
// populate our unique table
|
|
362
361
|
if (DEBUG) {
|
|
363
|
-
if (
|
|
362
|
+
if (this._cache.lids.has(identifier.lid)) {
|
|
364
363
|
throw new Error(`The lid generated for the new record is not unique as it matches an existing identifier`);
|
|
365
364
|
}
|
|
366
365
|
}
|
|
367
|
-
this._cache.lids
|
|
366
|
+
this._cache.lids.set(identifier.lid, identifier);
|
|
368
367
|
|
|
369
368
|
// populate the type+lid cache
|
|
370
|
-
keyOptions.lid
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
369
|
+
keyOptions.lid.set(newLid, identifier);
|
|
370
|
+
if (data.id) {
|
|
371
|
+
keyOptions.id.set(data.id, identifier);
|
|
372
|
+
}
|
|
374
373
|
|
|
375
374
|
if (LOG_IDENTIFIERS) {
|
|
376
375
|
// eslint-disable-next-line no-console
|
|
@@ -405,13 +404,17 @@ export class IdentifierCache {
|
|
|
405
404
|
updateRecordIdentifier(identifierObject: RecordIdentifier, data: ResourceData): StableRecordIdentifier {
|
|
406
405
|
let identifier = this.getOrCreateRecordIdentifier(identifierObject);
|
|
407
406
|
|
|
408
|
-
let newId =
|
|
407
|
+
let newId =
|
|
408
|
+
(data as ExistingResourceObject).id !== undefined ? coerceId((data as ExistingResourceObject).id) : null;
|
|
409
409
|
let existingIdentifier = detectMerge(this._cache.types, identifier, data, newId, this._cache.lids);
|
|
410
410
|
|
|
411
411
|
if (!existingIdentifier) {
|
|
412
412
|
// If the incoming type does not match the identifier type, we need to create an identifier for the incoming
|
|
413
413
|
// data so we can merge the incoming data with the existing identifier, see #7325 and #7363
|
|
414
|
-
if (
|
|
414
|
+
if (
|
|
415
|
+
(data as ExistingResourceObject).type &&
|
|
416
|
+
identifier.type !== normalizeModelName((data as ExistingResourceObject).type)
|
|
417
|
+
) {
|
|
415
418
|
let incomingDataResource = { ...data };
|
|
416
419
|
// Need to strip the lid from the incomingData in order force a new identifier creation
|
|
417
420
|
delete incomingDataResource.lid;
|
|
@@ -452,10 +455,10 @@ export class IdentifierCache {
|
|
|
452
455
|
);
|
|
453
456
|
}
|
|
454
457
|
let keyOptions = getTypeIndex(this._cache.types, identifier.type);
|
|
455
|
-
keyOptions.id
|
|
458
|
+
keyOptions.id.set(newId, identifier);
|
|
456
459
|
|
|
457
460
|
if (id !== null) {
|
|
458
|
-
|
|
461
|
+
keyOptions.id.delete(id);
|
|
459
462
|
}
|
|
460
463
|
} else if (LOG_IDENTIFIERS) {
|
|
461
464
|
// eslint-disable-next-line no-console
|
|
@@ -484,10 +487,10 @@ export class IdentifierCache {
|
|
|
484
487
|
this.forgetRecordIdentifier(abandoned);
|
|
485
488
|
|
|
486
489
|
// ensure a secondary cache entry for this id for the identifier we do keep
|
|
487
|
-
keyOptions.id
|
|
490
|
+
keyOptions.id.set(newId, kept);
|
|
488
491
|
// ensure a secondary cache entry for this id for the abandoned identifier's type we do keep
|
|
489
492
|
let baseKeyOptions = getTypeIndex(this._cache.types, existingIdentifier.type);
|
|
490
|
-
baseKeyOptions.id
|
|
493
|
+
baseKeyOptions.id.set(newId, kept);
|
|
491
494
|
|
|
492
495
|
// make sure that the `lid` on the data we are processing matches the lid we kept
|
|
493
496
|
data.lid = kept.lid;
|
|
@@ -511,13 +514,10 @@ export class IdentifierCache {
|
|
|
511
514
|
let identifier = this.getOrCreateRecordIdentifier(identifierObject);
|
|
512
515
|
let keyOptions = getTypeIndex(this._cache.types, identifier.type);
|
|
513
516
|
if (identifier.id !== null) {
|
|
514
|
-
|
|
517
|
+
keyOptions.id.delete(identifier.id);
|
|
515
518
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
let index = keyOptions._allIdentifiers.indexOf(identifier);
|
|
520
|
-
keyOptions._allIdentifiers.splice(index, 1);
|
|
519
|
+
this._cache.lids.delete(identifier.lid);
|
|
520
|
+
keyOptions.lid.delete(identifier.lid);
|
|
521
521
|
|
|
522
522
|
IDENTIFIERS.delete(identifierObject);
|
|
523
523
|
this._forget(identifier, 'record');
|
|
@@ -537,9 +537,8 @@ function getTypeIndex(typeMap: TypeMap, type: string): KeyOptions {
|
|
|
537
537
|
|
|
538
538
|
if (typeIndex === undefined) {
|
|
539
539
|
typeIndex = {
|
|
540
|
-
lid:
|
|
541
|
-
id:
|
|
542
|
-
_allIdentifiers: [],
|
|
540
|
+
lid: new Map(),
|
|
541
|
+
id: new Map(),
|
|
543
542
|
};
|
|
544
543
|
typeMap[type] = typeIndex;
|
|
545
544
|
}
|
|
@@ -643,8 +642,8 @@ function performRecordIdentifierUpdate(identifier: StableRecordIdentifier, data:
|
|
|
643
642
|
// for the multiple-cache-key scenario we "could"
|
|
644
643
|
// use a heuristic to guess the best id for display
|
|
645
644
|
// (usually when `data.id` is available and `data.attributes` is not)
|
|
646
|
-
if (
|
|
647
|
-
identifier.id = coerceId(data.id);
|
|
645
|
+
if ((data as ExistingResourceObject).id !== undefined) {
|
|
646
|
+
identifier.id = coerceId((data as ExistingResourceObject).id);
|
|
648
647
|
}
|
|
649
648
|
}
|
|
650
649
|
|
|
@@ -658,20 +657,20 @@ function detectMerge(
|
|
|
658
657
|
const { id, type, lid } = identifier;
|
|
659
658
|
if (id !== null && id !== newId && newId !== null) {
|
|
660
659
|
let keyOptions = getTypeIndex(typesCache, identifier.type);
|
|
661
|
-
let existingIdentifier = keyOptions.id
|
|
660
|
+
let existingIdentifier = keyOptions.id.get(newId);
|
|
662
661
|
|
|
663
662
|
return existingIdentifier !== undefined ? existingIdentifier : false;
|
|
664
663
|
} else {
|
|
665
|
-
let newType =
|
|
664
|
+
let newType = (data as ExistingResourceObject).type && normalizeModelName((data as ExistingResourceObject).type);
|
|
666
665
|
|
|
667
666
|
// If the ids and type are the same but lid is not the same, we should trigger a merge of the identifiers
|
|
668
667
|
if (id !== null && id === newId && newType === type && data.lid && data.lid !== lid) {
|
|
669
|
-
let existingIdentifier = lids
|
|
668
|
+
let existingIdentifier = lids.get(data.lid);
|
|
670
669
|
return existingIdentifier !== undefined ? existingIdentifier : false;
|
|
671
670
|
// If the lids are the same, and ids are the same, but types are different we should trigger a merge of the identifiers
|
|
672
671
|
} else if (id !== null && id === newId && newType && newType !== type && data.lid && data.lid === lid) {
|
|
673
672
|
let keyOptions = getTypeIndex(typesCache, newType);
|
|
674
|
-
let existingIdentifier = keyOptions.id
|
|
673
|
+
let existingIdentifier = keyOptions.id.get(id);
|
|
675
674
|
return existingIdentifier !== undefined ? existingIdentifier : false;
|
|
676
675
|
}
|
|
677
676
|
}
|