@ember-data/store 4.8.0-alpha.6 → 4.8.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/LICENSE.md +3 -1
- package/addon/-private/caches/identifier-cache.ts +12 -9
- package/addon/-private/caches/instance-cache.ts +89 -90
- package/addon/-private/index.ts +1 -1
- package/addon/-private/legacy-model-support/schema-definition-service.ts +32 -24
- package/addon/-private/managers/record-array-manager.ts +8 -9
- package/addon/-private/managers/record-data-manager.ts +27 -4
- package/addon/-private/managers/record-data-store-wrapper.ts +25 -21
- package/addon/-private/managers/record-notification-manager.ts +5 -3
- package/addon/-private/network/fetch-manager.ts +2 -2
- package/addon/-private/network/snapshot.ts +2 -2
- package/addon/-private/proxies/promise-proxies.ts +39 -11
- package/addon/-private/record-arrays/identifier-array.ts +27 -22
- package/addon/-private/store-service.ts +105 -51
- package/addon/-private/utils/uuid-polyfill.ts +58 -56
- package/index.js +1 -1
- package/package.json +36 -47
- package/addon/-debug/index.js +0 -47
- package/config/environment.js +0 -5
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (
|
|
3
|
+
Copyright (C) 2017-2022 Ember.js contributors
|
|
4
|
+
Portions Copyright (C) 2011-2017 Tilde, Inc. and contributors.
|
|
5
|
+
Portions Copyright (C) 2011 LivingSocial Inc.
|
|
4
6
|
|
|
5
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
8
|
|
|
@@ -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 {
|
|
@@ -265,14 +266,16 @@ export class IdentifierCache {
|
|
|
265
266
|
// one level up
|
|
266
267
|
keyOptions.lid.set(identifier.lid, identifier);
|
|
267
268
|
|
|
268
|
-
if (LOG_IDENTIFIERS
|
|
269
|
-
|
|
270
|
-
console.log(`Identifiers: generated ${String(identifier)} for`, resource);
|
|
271
|
-
if (resource[DEBUG_IDENTIFIER_BUCKET]) {
|
|
269
|
+
if (LOG_IDENTIFIERS) {
|
|
270
|
+
if (shouldGenerate) {
|
|
272
271
|
// eslint-disable-next-line no-console
|
|
273
|
-
console.
|
|
274
|
-
|
|
275
|
-
|
|
272
|
+
console.log(`Identifiers: generated ${String(identifier)} for`, resource);
|
|
273
|
+
if (resource[DEBUG_IDENTIFIER_BUCKET]) {
|
|
274
|
+
// eslint-disable-next-line no-console
|
|
275
|
+
console.trace(
|
|
276
|
+
`[WARNING] Identifiers: generated a new identifier from a previously used identifier. This is likely a bug.`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
276
279
|
}
|
|
277
280
|
}
|
|
278
281
|
}
|
|
@@ -7,7 +7,8 @@ import { resolve } from 'rsvp';
|
|
|
7
7
|
import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
|
|
8
8
|
import { LOG_INSTANCE_CACHE } from '@ember-data/private-build-infra/debugging';
|
|
9
9
|
import { DEPRECATE_V1_RECORD_DATA, DEPRECATE_V1CACHE_STORE_APIS } from '@ember-data/private-build-infra/deprecations';
|
|
10
|
-
import type { Graph
|
|
10
|
+
import type { Graph } from '@ember-data/record-data/-private/graph/graph';
|
|
11
|
+
import type { peekGraph } from '@ember-data/record-data/-private/graph/index';
|
|
11
12
|
import type {
|
|
12
13
|
ExistingResourceIdentifierObject,
|
|
13
14
|
ExistingResourceObject,
|
|
@@ -83,8 +84,10 @@ export function recordIdentifierFor(record: RecordInstance): StableRecordIdentif
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
export function setRecordIdentifier(record: RecordInstance, identifier: StableRecordIdentifier): void {
|
|
86
|
-
if (DEBUG
|
|
87
|
-
|
|
87
|
+
if (DEBUG) {
|
|
88
|
+
if (RecordCache.has(record) && RecordCache.get(record) !== identifier) {
|
|
89
|
+
throw new Error(`${String(record)} was already assigned an identifier`);
|
|
90
|
+
}
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
/*
|
|
@@ -128,44 +131,6 @@ export class InstanceCache {
|
|
|
128
131
|
reference: new WeakMap<StableRecordIdentifier, RecordReference>(),
|
|
129
132
|
};
|
|
130
133
|
|
|
131
|
-
recordIsLoaded(identifier: StableRecordIdentifier, filterDeleted: boolean = false) {
|
|
132
|
-
const recordData = this.__instances.recordData.get(identifier);
|
|
133
|
-
if (!recordData) {
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
const isNew = recordData.isNew(identifier);
|
|
137
|
-
const isEmpty = recordData.isEmpty(identifier);
|
|
138
|
-
|
|
139
|
-
// if we are new we must consider ourselves loaded
|
|
140
|
-
if (isNew) {
|
|
141
|
-
return !recordData.isDeleted(identifier);
|
|
142
|
-
}
|
|
143
|
-
// even if we have a past request, if we are now empty we are not loaded
|
|
144
|
-
// typically this is true after an unloadRecord call
|
|
145
|
-
|
|
146
|
-
// if we are not empty, not new && we have a fulfilled request then we are loaded
|
|
147
|
-
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
148
|
-
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
149
|
-
// in that it does not account for unloading.
|
|
150
|
-
return filterDeleted && recordData.isDeletionCommitted(identifier) ? false : !isEmpty;
|
|
151
|
-
|
|
152
|
-
/*
|
|
153
|
-
const req = this.store.getRequestStateService();
|
|
154
|
-
const fulfilled = req.getLastRequestForRecord(identifier);
|
|
155
|
-
const isLocallyLoaded = !isEmpty;
|
|
156
|
-
const isLoading =
|
|
157
|
-
!isLocallyLoaded &&
|
|
158
|
-
fulfilled === null &&
|
|
159
|
-
req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query');
|
|
160
|
-
|
|
161
|
-
if (isEmpty || (filterDeleted && recordData.isDeletionCommitted(identifier)) || isLoading) {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return true;
|
|
166
|
-
*/
|
|
167
|
-
}
|
|
168
|
-
|
|
169
134
|
constructor(store: Store) {
|
|
170
135
|
this.store = store;
|
|
171
136
|
|
|
@@ -178,25 +143,24 @@ export class InstanceCache {
|
|
|
178
143
|
|
|
179
144
|
store.identifierCache.__configureMerge(
|
|
180
145
|
(identifier: StableRecordIdentifier, matchedIdentifier: StableRecordIdentifier, resourceData) => {
|
|
181
|
-
let
|
|
146
|
+
let keptIdentifier = identifier;
|
|
182
147
|
if (identifier.id !== matchedIdentifier.id) {
|
|
183
|
-
|
|
184
|
-
'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier;
|
|
148
|
+
keptIdentifier = 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier;
|
|
185
149
|
} else if (identifier.type !== matchedIdentifier.type) {
|
|
186
|
-
|
|
150
|
+
keptIdentifier =
|
|
187
151
|
'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
|
|
188
152
|
}
|
|
189
|
-
let
|
|
153
|
+
let staleIdentifier = identifier === keptIdentifier ? matchedIdentifier : identifier;
|
|
190
154
|
|
|
191
155
|
// check for duplicate entities
|
|
192
|
-
let
|
|
193
|
-
let
|
|
194
|
-
let
|
|
195
|
-
let
|
|
156
|
+
let keptHasRecord = this.__instances.record.has(keptIdentifier);
|
|
157
|
+
let staleHasRecord = this.__instances.record.has(staleIdentifier);
|
|
158
|
+
let keptRecordData = this.__instances.recordData.get(keptIdentifier) || null;
|
|
159
|
+
let staleRecordData = this.__instances.recordData.get(staleIdentifier) || null;
|
|
196
160
|
|
|
197
161
|
// we cannot merge entities when both have records
|
|
198
162
|
// (this may not be strictly true, we could probably swap the recordData the record points at)
|
|
199
|
-
if (
|
|
163
|
+
if (keptHasRecord && staleHasRecord) {
|
|
200
164
|
// TODO we probably don't need to throw these errors anymore
|
|
201
165
|
// we can probably just "swap" what data source the abandoned
|
|
202
166
|
// record points at so long as
|
|
@@ -210,7 +174,7 @@ export class InstanceCache {
|
|
|
210
174
|
}:${String(matchedIdentifier.id)} (${matchedIdentifier.lid})'`
|
|
211
175
|
);
|
|
212
176
|
}
|
|
213
|
-
|
|
177
|
+
|
|
214
178
|
assert(
|
|
215
179
|
`Failed to update the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${
|
|
216
180
|
identifier.lid
|
|
@@ -220,33 +184,26 @@ export class InstanceCache {
|
|
|
220
184
|
);
|
|
221
185
|
}
|
|
222
186
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
187
|
+
let recordData = keptRecordData || staleRecordData;
|
|
188
|
+
|
|
189
|
+
if (recordData) {
|
|
190
|
+
recordData.sync({
|
|
191
|
+
op: 'mergeIdentifiers',
|
|
192
|
+
record: staleIdentifier,
|
|
193
|
+
value: keptIdentifier,
|
|
194
|
+
});
|
|
195
|
+
} else if (HAS_RECORD_DATA_PACKAGE) {
|
|
196
|
+
// TODO notify cache always, this requires it always being a singleton
|
|
197
|
+
// and not ever specific to one record-data
|
|
198
|
+
this.store.__private_singleton_recordData?.sync({
|
|
199
|
+
op: 'mergeIdentifiers',
|
|
200
|
+
record: staleIdentifier,
|
|
201
|
+
value: keptIdentifier,
|
|
202
|
+
});
|
|
226
203
|
}
|
|
227
204
|
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
return intendedIdentifier;
|
|
231
|
-
|
|
232
|
-
// only the other has a RecordData
|
|
233
|
-
// OR only the other has a Record
|
|
234
|
-
} else if (
|
|
235
|
-
(imRecordData === null && otherRecordData !== null) ||
|
|
236
|
-
(imRecordData && !imHasRecord && otherRecordData && otherHasRecord)
|
|
237
|
-
) {
|
|
238
|
-
if (imRecordData) {
|
|
239
|
-
// TODO check if we are retained in any async relationships
|
|
240
|
-
// TODO probably need to release other things
|
|
241
|
-
// im.destroy();
|
|
242
|
-
}
|
|
243
|
-
imRecordData = otherRecordData!;
|
|
244
|
-
// TODO do we need to notify the id change?
|
|
245
|
-
// TODO swap recordIdentifierFor result?
|
|
246
|
-
|
|
247
|
-
// just use im
|
|
248
|
-
} else {
|
|
249
|
-
// otherIm.destroy();
|
|
205
|
+
if (staleRecordData === null) {
|
|
206
|
+
return keptIdentifier;
|
|
250
207
|
}
|
|
251
208
|
|
|
252
209
|
/*
|
|
@@ -259,7 +216,8 @@ export class InstanceCache {
|
|
|
259
216
|
}
|
|
260
217
|
*/
|
|
261
218
|
|
|
262
|
-
|
|
219
|
+
this.unloadRecord(staleIdentifier);
|
|
220
|
+
return keptIdentifier;
|
|
263
221
|
}
|
|
264
222
|
);
|
|
265
223
|
}
|
|
@@ -304,8 +262,8 @@ export class InstanceCache {
|
|
|
304
262
|
getRecordData(identifier: StableRecordIdentifier): RecordData {
|
|
305
263
|
let recordData = this.__instances.recordData.get(identifier);
|
|
306
264
|
|
|
307
|
-
if (
|
|
308
|
-
if (
|
|
265
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
266
|
+
if (!recordData && this.store.createRecordDataFor.length > 2) {
|
|
309
267
|
deprecate(
|
|
310
268
|
`Store.createRecordDataFor(<type>, <id>, <lid>, <storeWrapper>) has been deprecated in favor of Store.createRecordDataFor(<identifier>, <storeWrapper>)`,
|
|
311
269
|
false,
|
|
@@ -313,7 +271,7 @@ export class InstanceCache {
|
|
|
313
271
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
314
272
|
for: 'ember-data',
|
|
315
273
|
until: '5.0',
|
|
316
|
-
since: { enabled: '4.
|
|
274
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
317
275
|
}
|
|
318
276
|
);
|
|
319
277
|
let recordDataInstance = this.store.createRecordDataFor(
|
|
@@ -329,19 +287,22 @@ export class InstanceCache {
|
|
|
329
287
|
recordData = this.__cacheManager =
|
|
330
288
|
this.__cacheManager || new NonSingletonRecordDataManager(this.store, recordDataInstance, identifier);
|
|
331
289
|
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!recordData) {
|
|
294
|
+
let recordDataInstance = this.store.createRecordDataFor(identifier, this._storeWrapper);
|
|
295
|
+
if (DEPRECATE_V1_RECORD_DATA) {
|
|
296
|
+
recordData = new NonSingletonRecordDataManager(this.store, recordDataInstance, identifier);
|
|
332
297
|
} else {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
recordData
|
|
298
|
+
if (DEBUG) {
|
|
299
|
+
recordData = this.__cacheManager = this.__cacheManager || new SingletonRecordDataManager();
|
|
300
|
+
(recordData as SingletonRecordDataManager)._addRecordData(identifier, recordDataInstance as RecordData);
|
|
336
301
|
} else {
|
|
337
|
-
|
|
338
|
-
recordData = this.__cacheManager = this.__cacheManager || new SingletonRecordDataManager();
|
|
339
|
-
(recordData as SingletonRecordDataManager)._addRecordData(identifier, recordDataInstance as RecordData);
|
|
340
|
-
} else {
|
|
341
|
-
recordData = recordDataInstance as RecordData;
|
|
342
|
-
}
|
|
302
|
+
recordData = recordDataInstance as RecordData;
|
|
343
303
|
}
|
|
344
304
|
}
|
|
305
|
+
|
|
345
306
|
setRecordDataFor(identifier, recordData);
|
|
346
307
|
|
|
347
308
|
this.__instances.recordData.set(identifier, recordData);
|
|
@@ -365,6 +326,44 @@ export class InstanceCache {
|
|
|
365
326
|
return reference;
|
|
366
327
|
}
|
|
367
328
|
|
|
329
|
+
recordIsLoaded(identifier: StableRecordIdentifier, filterDeleted: boolean = false) {
|
|
330
|
+
const recordData = this.__instances.recordData.get(identifier);
|
|
331
|
+
if (!recordData) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
const isNew = recordData.isNew(identifier);
|
|
335
|
+
const isEmpty = recordData.isEmpty(identifier);
|
|
336
|
+
|
|
337
|
+
// if we are new we must consider ourselves loaded
|
|
338
|
+
if (isNew) {
|
|
339
|
+
return !recordData.isDeleted(identifier);
|
|
340
|
+
}
|
|
341
|
+
// even if we have a past request, if we are now empty we are not loaded
|
|
342
|
+
// typically this is true after an unloadRecord call
|
|
343
|
+
|
|
344
|
+
// if we are not empty, not new && we have a fulfilled request then we are loaded
|
|
345
|
+
// we should consider allowing for something to be loaded that is simply "not empty".
|
|
346
|
+
// which is how RecordState currently handles this case; however, RecordState is buggy
|
|
347
|
+
// in that it does not account for unloading.
|
|
348
|
+
return filterDeleted && recordData.isDeletionCommitted(identifier) ? false : !isEmpty;
|
|
349
|
+
|
|
350
|
+
/*
|
|
351
|
+
const req = this.store.getRequestStateService();
|
|
352
|
+
const fulfilled = req.getLastRequestForRecord(identifier);
|
|
353
|
+
const isLocallyLoaded = !isEmpty;
|
|
354
|
+
const isLoading =
|
|
355
|
+
!isLocallyLoaded &&
|
|
356
|
+
fulfilled === null &&
|
|
357
|
+
req.getPendingRequestsForRecord(identifier).some((req) => req.type === 'query');
|
|
358
|
+
|
|
359
|
+
if (isEmpty || (filterDeleted && recordData.isDeletionCommitted(identifier)) || isLoading) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return true;
|
|
364
|
+
*/
|
|
365
|
+
}
|
|
366
|
+
|
|
368
367
|
createSnapshot(identifier: StableRecordIdentifier, options: FindOptions = {}): Snapshot {
|
|
369
368
|
return new Snapshot(options, identifier, this.store);
|
|
370
369
|
}
|
package/addon/-private/index.ts
CHANGED
|
@@ -30,7 +30,7 @@ export function normalizeModelName(modelName: string) {
|
|
|
30
30
|
id: 'ember-data:deprecate-normalize-modelname-helper',
|
|
31
31
|
for: 'ember-data',
|
|
32
32
|
until: '5.0',
|
|
33
|
-
since: { available: '4.
|
|
33
|
+
since: { available: '4.7', enabled: '4.7' },
|
|
34
34
|
}
|
|
35
35
|
);
|
|
36
36
|
return _normalize(modelName);
|
|
@@ -39,18 +39,22 @@ export class DSModelSchemaDefinitionService {
|
|
|
39
39
|
// Following the existing RD implementation
|
|
40
40
|
attributesDefinitionFor(identifier: RecordIdentifier | { type: string }): AttributesSchema {
|
|
41
41
|
let modelName, attributes;
|
|
42
|
-
if (DEPRECATE_STRING_ARG_SCHEMAS
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
if (DEPRECATE_STRING_ARG_SCHEMAS) {
|
|
43
|
+
if (typeof identifier === 'string') {
|
|
44
|
+
deprecate(
|
|
45
|
+
`attributesDefinitionFor expects either a record identifier or an argument of shape { type: string }, received a string.`,
|
|
46
|
+
false,
|
|
47
|
+
{
|
|
48
|
+
id: 'ember-data:deprecate-string-arg-schemas',
|
|
49
|
+
for: 'ember-data',
|
|
50
|
+
until: '5.0',
|
|
51
|
+
since: { enabled: '4.5', available: '4.5' },
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
modelName = identifier;
|
|
55
|
+
} else {
|
|
56
|
+
modelName = identifier.type;
|
|
57
|
+
}
|
|
54
58
|
} else {
|
|
55
59
|
modelName = identifier.type;
|
|
56
60
|
}
|
|
@@ -72,18 +76,22 @@ export class DSModelSchemaDefinitionService {
|
|
|
72
76
|
// Following the existing RD implementation
|
|
73
77
|
relationshipsDefinitionFor(identifier: RecordIdentifier | { type: string }): RelationshipsSchema {
|
|
74
78
|
let modelName, relationships;
|
|
75
|
-
if (DEPRECATE_STRING_ARG_SCHEMAS
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
if (DEPRECATE_STRING_ARG_SCHEMAS) {
|
|
80
|
+
if (typeof identifier === 'string') {
|
|
81
|
+
deprecate(
|
|
82
|
+
`relationshipsDefinitionFor expects either a record identifier or an argument of shape { type: string }, received a string.`,
|
|
83
|
+
false,
|
|
84
|
+
{
|
|
85
|
+
id: 'ember-data:deprecate-string-arg-schemas',
|
|
86
|
+
for: 'ember-data',
|
|
87
|
+
until: '5.0',
|
|
88
|
+
since: { enabled: '4.5', available: '4.5' },
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
modelName = identifier;
|
|
92
|
+
} else {
|
|
93
|
+
modelName = identifier.type;
|
|
94
|
+
}
|
|
87
95
|
} else {
|
|
88
96
|
modelName = identifier.type;
|
|
89
97
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
@module @ember-data/store
|
|
3
3
|
*/
|
|
4
|
+
import { addToTransaction } from '@ember-data/tracking/-private';
|
|
4
5
|
import type { CollectionResourceDocument } from '@ember-data/types/q/ember-data-json-api';
|
|
5
6
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
6
7
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
@@ -60,7 +61,7 @@ export function fastPush<T>(target: T[], source: T[]) {
|
|
|
60
61
|
let newLength = source.length;
|
|
61
62
|
while (newLength - startLength > SLICE_BATCH_SIZE) {
|
|
62
63
|
// eslint-disable-next-line prefer-spread
|
|
63
|
-
target.push.apply(target, source.slice(startLength, SLICE_BATCH_SIZE));
|
|
64
|
+
target.push.apply(target, source.slice(startLength, startLength + SLICE_BATCH_SIZE));
|
|
64
65
|
startLength += SLICE_BATCH_SIZE;
|
|
65
66
|
}
|
|
66
67
|
// eslint-disable-next-line prefer-spread
|
|
@@ -167,14 +168,16 @@ class RecordArrayManager {
|
|
|
167
168
|
return array;
|
|
168
169
|
}
|
|
169
170
|
|
|
170
|
-
dirtyArray(array: IdentifierArray): void {
|
|
171
|
+
dirtyArray(array: IdentifierArray, delta: number): void {
|
|
171
172
|
if (array === FAKE_ARR) {
|
|
172
173
|
return;
|
|
173
174
|
}
|
|
174
175
|
let tag = array[IDENTIFIER_ARRAY_TAG];
|
|
175
176
|
if (!tag.shouldReset) {
|
|
176
177
|
tag.shouldReset = true;
|
|
177
|
-
tag
|
|
178
|
+
addToTransaction(tag);
|
|
179
|
+
} else if (delta > 0 && tag.t) {
|
|
180
|
+
addToTransaction(tag);
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
183
|
|
|
@@ -257,9 +260,7 @@ class RecordArrayManager {
|
|
|
257
260
|
} else {
|
|
258
261
|
changes.set(identifier, 'add');
|
|
259
262
|
|
|
260
|
-
|
|
261
|
-
this.dirtyArray(array);
|
|
262
|
-
}
|
|
263
|
+
this.dirtyArray(array, changes.size);
|
|
263
264
|
}
|
|
264
265
|
});
|
|
265
266
|
}
|
|
@@ -275,9 +276,7 @@ class RecordArrayManager {
|
|
|
275
276
|
} else {
|
|
276
277
|
changes.set(identifier, 'del');
|
|
277
278
|
|
|
278
|
-
|
|
279
|
-
this.dirtyArray(array);
|
|
280
|
-
}
|
|
279
|
+
this.dirtyArray(array, changes.size);
|
|
281
280
|
}
|
|
282
281
|
});
|
|
283
282
|
}
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
SingleResourceRelationship,
|
|
7
7
|
} from '@ember-data/types/q/ember-data-json-api';
|
|
8
8
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
9
|
-
import type { ChangedAttributesHash, RecordData, RecordDataV1 } from '@ember-data/types/q/record-data';
|
|
9
|
+
import type { ChangedAttributesHash, MergeOperation, RecordData, RecordDataV1 } from '@ember-data/types/q/record-data';
|
|
10
10
|
import type { JsonApiResource, JsonApiValidationError } from '@ember-data/types/q/record-data-json-api';
|
|
11
11
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
12
12
|
|
|
@@ -76,7 +76,7 @@ export class NonSingletonRecordDataManager implements RecordData {
|
|
|
76
76
|
{
|
|
77
77
|
id: 'ember-data:deprecate-v1-cache',
|
|
78
78
|
until: '5.0',
|
|
79
|
-
since: { available: '4.
|
|
79
|
+
since: { available: '4.7', enabled: '4.7' },
|
|
80
80
|
for: 'ember-data',
|
|
81
81
|
}
|
|
82
82
|
);
|
|
@@ -128,6 +128,25 @@ export class NonSingletonRecordDataManager implements RecordData {
|
|
|
128
128
|
return recordData.pushData(identifier, data, hasRecord);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Perform an operation on the cache to update the remote state.
|
|
133
|
+
*
|
|
134
|
+
* Note: currently the only valid operation is a MergeOperation
|
|
135
|
+
* which occurs when a collision of identifiers is detected.
|
|
136
|
+
*
|
|
137
|
+
* @method sync
|
|
138
|
+
* @public
|
|
139
|
+
* @param op the operation to perform
|
|
140
|
+
* @returns {void}
|
|
141
|
+
*/
|
|
142
|
+
sync(op: MergeOperation): void {
|
|
143
|
+
const recordData = this.#recordData;
|
|
144
|
+
if (this.#isDeprecated(recordData)) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
recordData.sync(op);
|
|
148
|
+
}
|
|
149
|
+
|
|
131
150
|
/**
|
|
132
151
|
* Update resource data with a local mutation. Currently supports operations
|
|
133
152
|
* on relationships only.
|
|
@@ -395,7 +414,7 @@ export class NonSingletonRecordDataManager implements RecordData {
|
|
|
395
414
|
* @method hasChangedAttributes
|
|
396
415
|
* @public
|
|
397
416
|
* @deprecated
|
|
398
|
-
* @returns
|
|
417
|
+
* @returns {boolean}
|
|
399
418
|
*/
|
|
400
419
|
hasChangedAttributes(): boolean {
|
|
401
420
|
const recordData = this.#recordData;
|
|
@@ -410,7 +429,7 @@ export class NonSingletonRecordDataManager implements RecordData {
|
|
|
410
429
|
* @method hasChangedAttrs
|
|
411
430
|
* @public
|
|
412
431
|
* @param identifier
|
|
413
|
-
* @returns
|
|
432
|
+
* @returns {boolean}
|
|
414
433
|
*/
|
|
415
434
|
hasChangedAttrs(identifier: StableRecordIdentifier): boolean {
|
|
416
435
|
const recordData = this.#recordData;
|
|
@@ -740,6 +759,10 @@ export class SingletonRecordDataManager implements RecordData {
|
|
|
740
759
|
return this.#recordData(identifier).pushData(identifier, data, hasRecord);
|
|
741
760
|
}
|
|
742
761
|
|
|
762
|
+
sync(op: MergeOperation): void {
|
|
763
|
+
this.#recordData(op.record).sync(op);
|
|
764
|
+
}
|
|
765
|
+
|
|
743
766
|
clientDidCreate(identifier: StableRecordIdentifier, options?: Dict<unknown>): Dict<unknown> {
|
|
744
767
|
return this.#recordData(identifier).clientDidCreate(identifier, options);
|
|
745
768
|
}
|
|
@@ -99,7 +99,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
99
99
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
100
100
|
for: 'ember-data',
|
|
101
101
|
until: '5.0',
|
|
102
|
-
since: { enabled: '4.
|
|
102
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
const resource = constructResource(type, id, lid);
|
|
@@ -117,7 +117,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
117
117
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
118
118
|
for: 'ember-data',
|
|
119
119
|
until: '5.0',
|
|
120
|
-
since: { enabled: '4.
|
|
120
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
121
121
|
}
|
|
122
122
|
);
|
|
123
123
|
}
|
|
@@ -133,7 +133,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
133
133
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
134
134
|
for: 'ember-data',
|
|
135
135
|
until: '5.0',
|
|
136
|
-
since: { enabled: '4.
|
|
136
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
137
137
|
}
|
|
138
138
|
);
|
|
139
139
|
}
|
|
@@ -152,7 +152,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
152
152
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
153
153
|
for: 'ember-data',
|
|
154
154
|
until: '5.0',
|
|
155
|
-
since: { enabled: '4.
|
|
155
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
156
156
|
});
|
|
157
157
|
}
|
|
158
158
|
const resource = constructResource(type, id, lid);
|
|
@@ -169,7 +169,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
169
169
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
170
170
|
for: 'ember-data',
|
|
171
171
|
until: '5.0',
|
|
172
|
-
since: { enabled: '4.
|
|
172
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
173
173
|
});
|
|
174
174
|
}
|
|
175
175
|
const resource = constructResource(type, id, lid);
|
|
@@ -185,7 +185,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
185
185
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
186
186
|
for: 'ember-data',
|
|
187
187
|
until: '5.0',
|
|
188
|
-
since: { enabled: '4.
|
|
188
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
191
|
const resource = constructResource(type, id, lid);
|
|
@@ -202,7 +202,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
202
202
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
203
203
|
for: 'ember-data',
|
|
204
204
|
until: '5.0',
|
|
205
|
-
since: { enabled: '4.
|
|
205
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
208
|
const resource = constructResource(type, id, lid);
|
|
@@ -277,7 +277,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
277
277
|
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
278
278
|
for: 'ember-data',
|
|
279
279
|
until: '5.0',
|
|
280
|
-
since: { enabled: '4.
|
|
280
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
281
281
|
});
|
|
282
282
|
}
|
|
283
283
|
const resource = constructResource(type, id, lid);
|
|
@@ -297,19 +297,23 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
297
297
|
disconnectRecord(type: StableRecordIdentifier): void;
|
|
298
298
|
disconnectRecord(type: string | StableRecordIdentifier, id?: string | null, lid?: string | null): void {
|
|
299
299
|
let identifier: StableRecordIdentifier;
|
|
300
|
-
if (DEPRECATE_V1CACHE_STORE_APIS
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
300
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
301
|
+
if (typeof type === 'string') {
|
|
302
|
+
deprecate(
|
|
303
|
+
`StoreWrapper.disconnectRecord(<type>) has been deprecated in favor of StoreWrapper.disconnectRecord(<identifier>)`,
|
|
304
|
+
false,
|
|
305
|
+
{
|
|
306
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
307
|
+
for: 'ember-data',
|
|
308
|
+
until: '5.0',
|
|
309
|
+
since: { enabled: '4.7', available: '4.7' },
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
let resource = constructResource(type, id, lid) as RecordIdentifier;
|
|
313
|
+
identifier = this.identifierCache.peekRecordIdentifier(resource)!;
|
|
314
|
+
} else {
|
|
315
|
+
identifier = type as StableRecordIdentifier;
|
|
316
|
+
}
|
|
313
317
|
} else {
|
|
314
318
|
identifier = type as StableRecordIdentifier;
|
|
315
319
|
}
|
|
@@ -24,9 +24,11 @@ export interface NotificationCallback {
|
|
|
24
24
|
// TODO this isn't importable anyway, remove and use a map on the manager?
|
|
25
25
|
export function unsubscribe(token: UnsubscribeToken) {
|
|
26
26
|
let identifier = Tokens.get(token);
|
|
27
|
-
if (LOG_NOTIFICATIONS
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if (LOG_NOTIFICATIONS) {
|
|
28
|
+
if (!identifier) {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.log('Passed unknown unsubscribe token to unsubscribe', identifier);
|
|
31
|
+
}
|
|
30
32
|
}
|
|
31
33
|
if (identifier) {
|
|
32
34
|
Tokens.delete(token);
|