@ember-data/store 4.8.0-alpha.2 → 4.8.0-alpha.3
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 +1 -1
- package/addon/-private/{identifier-cache.ts → caches/identifier-cache.ts} +83 -8
- package/addon/-private/caches/instance-cache.ts +759 -0
- package/addon/-private/{record-data-for.ts → caches/record-data-for.ts} +2 -2
- package/addon/-private/index.ts +12 -15
- package/addon/-private/{model → legacy-model-support}/record-reference.ts +3 -3
- package/addon/-private/{schema-definition-service.ts → legacy-model-support/schema-definition-service.ts} +4 -5
- package/addon/-private/{model → legacy-model-support}/shim-model-class.ts +2 -2
- package/addon/-private/{record-array-manager.ts → managers/record-array-manager.ts} +16 -44
- package/addon/-private/{record-data-store-wrapper.ts → managers/record-data-store-wrapper.ts} +34 -56
- package/addon/-private/managers/record-notification-manager.ts +96 -0
- package/addon/-private/{fetch-manager.ts → network/fetch-manager.ts} +18 -16
- package/addon/-private/{finders.js → network/finders.js} +4 -12
- package/addon/-private/{request-cache.ts → network/request-cache.ts} +0 -0
- package/addon/-private/{snapshot-record-array.ts → network/snapshot-record-array.ts} +3 -3
- package/addon/-private/{snapshot.ts → network/snapshot.ts} +24 -31
- package/addon/-private/{promise-proxies.ts → proxies/promise-proxies.ts} +4 -4
- package/addon/-private/{promise-proxy-base.js → proxies/promise-proxy-base.js} +0 -0
- package/addon/-private/record-arrays/adapter-populated-record-array.ts +9 -11
- package/addon/-private/record-arrays/record-array.ts +16 -14
- package/addon/-private/{core-store.ts → store-service.ts} +186 -127
- package/addon/-private/{coerce-id.ts → utils/coerce-id.ts} +1 -1
- package/addon/-private/{common.js → utils/common.js} +1 -2
- package/addon/-private/utils/construct-resource.ts +2 -2
- package/addon/-private/{identifer-debug-consts.ts → utils/identifer-debug-consts.ts} +0 -0
- package/addon/-private/{normalize-model-name.ts → utils/normalize-model-name.ts} +0 -0
- package/addon/-private/utils/promise-record.ts +3 -3
- package/addon/-private/{serializer-response.ts → utils/serializer-response.ts} +2 -2
- package/addon/-private/{weak-cache.ts → utils/weak-cache.ts} +0 -0
- package/package.json +5 -5
- package/addon/-private/identity-map.ts +0 -54
- package/addon/-private/instance-cache.ts +0 -387
- package/addon/-private/internal-model-factory.ts +0 -359
- package/addon/-private/internal-model-map.ts +0 -121
- package/addon/-private/model/internal-model.ts +0 -600
- package/addon/-private/record-notification-manager.ts +0 -73
|
@@ -5,7 +5,7 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
|
5
5
|
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
6
6
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
7
7
|
|
|
8
|
-
import WeakCache from '
|
|
8
|
+
import WeakCache from '../utils/weak-cache';
|
|
9
9
|
|
|
10
10
|
/*
|
|
11
11
|
* Returns the RecordData instance associated with a given
|
|
@@ -24,7 +24,7 @@ export function setRecordDataFor(identifier: StableRecordIdentifier | RecordInst
|
|
|
24
24
|
RecordDataForIdentifierCache.set(identifier, recordData);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function removeRecordDataFor(identifier: StableRecordIdentifier): void {
|
|
27
|
+
export function removeRecordDataFor(identifier: StableRecordIdentifier | RecordInstance): void {
|
|
28
28
|
RecordDataForIdentifierCache.delete(identifier);
|
|
29
29
|
}
|
|
30
30
|
|
package/addon/-private/index.ts
CHANGED
|
@@ -6,19 +6,19 @@ import { assert, deprecate } from '@ember/debug';
|
|
|
6
6
|
|
|
7
7
|
import { DEPRECATE_HELPERS } from '@ember-data/private-build-infra/deprecations';
|
|
8
8
|
|
|
9
|
-
import _normalize from './normalize-model-name';
|
|
9
|
+
import _normalize from './utils/normalize-model-name';
|
|
10
10
|
|
|
11
|
-
export { default as Store, storeFor } from './
|
|
11
|
+
export { default as Store, storeFor } from './store-service';
|
|
12
12
|
|
|
13
|
-
export { recordIdentifierFor } from './
|
|
13
|
+
export { recordIdentifierFor } from './caches/instance-cache';
|
|
14
14
|
|
|
15
|
-
export { default as Snapshot } from './snapshot';
|
|
15
|
+
export { default as Snapshot } from './network/snapshot';
|
|
16
16
|
export {
|
|
17
17
|
setIdentifierGenerationMethod,
|
|
18
18
|
setIdentifierUpdateMethod,
|
|
19
19
|
setIdentifierForgetMethod,
|
|
20
20
|
setIdentifierResetMethod,
|
|
21
|
-
} from './identifier-cache';
|
|
21
|
+
} from './caches/identifier-cache';
|
|
22
22
|
|
|
23
23
|
export function normalizeModelName(modelName: string) {
|
|
24
24
|
if (DEPRECATE_HELPERS) {
|
|
@@ -37,23 +37,20 @@ export function normalizeModelName(modelName: string) {
|
|
|
37
37
|
assert(`normalizeModelName support has been removed`);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export { default as coerceId } from './coerce-id';
|
|
40
|
+
export { default as coerceId } from './utils/coerce-id';
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
export { default as InternalModel } from './model/internal-model';
|
|
44
|
-
|
|
45
|
-
export { PromiseArray, PromiseObject, deprecatedPromiseObject } from './promise-proxies';
|
|
42
|
+
export { PromiseArray, PromiseObject, deprecatedPromiseObject } from './proxies/promise-proxies';
|
|
46
43
|
|
|
47
44
|
export { default as RecordArray } from './record-arrays/record-array';
|
|
48
45
|
export { default as AdapterPopulatedRecordArray } from './record-arrays/adapter-populated-record-array';
|
|
49
46
|
|
|
50
|
-
export { default as RecordArrayManager } from './record-array-manager';
|
|
47
|
+
export { default as RecordArrayManager } from './managers/record-array-manager';
|
|
51
48
|
|
|
52
49
|
// // Used by tests
|
|
53
|
-
export { default as SnapshotRecordArray } from './snapshot-record-array';
|
|
50
|
+
export { default as SnapshotRecordArray } from './network/snapshot-record-array';
|
|
54
51
|
|
|
55
52
|
// New
|
|
56
|
-
export { default as recordDataFor, removeRecordDataFor } from './record-data-for';
|
|
57
|
-
export { default as RecordDataStoreWrapper } from './record-data-store-wrapper';
|
|
53
|
+
export { default as recordDataFor, removeRecordDataFor } from './caches/record-data-for';
|
|
54
|
+
export { default as RecordDataStoreWrapper } from './managers/record-data-store-wrapper';
|
|
58
55
|
|
|
59
|
-
export { default as WeakCache } from './weak-cache';
|
|
56
|
+
export { default as WeakCache } from './utils/weak-cache';
|
|
@@ -10,9 +10,9 @@ import type { SingleResourceDocument } from '@ember-data/types/q/ember-data-json
|
|
|
10
10
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
11
11
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
12
12
|
|
|
13
|
-
import type
|
|
14
|
-
import
|
|
15
|
-
import
|
|
13
|
+
import type { NotificationType } from '../managers/record-notification-manager';
|
|
14
|
+
import { unsubscribe } from '../managers/record-notification-manager';
|
|
15
|
+
import type Store from '../store-service';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
@module @ember-data/store
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getOwner } from '@ember/application';
|
|
2
2
|
import { deprecate } from '@ember/debug';
|
|
3
|
-
import { get } from '@ember/object';
|
|
4
3
|
|
|
5
4
|
import { importSync } from '@embroider/macros';
|
|
6
5
|
|
|
@@ -10,8 +9,8 @@ import { DEPRECATE_STRING_ARG_SCHEMAS } from '@ember-data/private-build-infra/de
|
|
|
10
9
|
import type { RecordIdentifier } from '@ember-data/types/q/identifier';
|
|
11
10
|
import type { AttributesSchema, RelationshipsSchema } from '@ember-data/types/q/record-data-schemas';
|
|
12
11
|
|
|
13
|
-
import type Store from '
|
|
14
|
-
import normalizeModelName from '
|
|
12
|
+
import type Store from '../store-service';
|
|
13
|
+
import normalizeModelName from '../utils/normalize-model-name';
|
|
15
14
|
|
|
16
15
|
type ModelForMixin = (store: Store, normalizedModelName: string) => Model | null;
|
|
17
16
|
|
|
@@ -55,7 +54,7 @@ export class DSModelSchemaDefinitionService {
|
|
|
55
54
|
|
|
56
55
|
if (attributes === undefined) {
|
|
57
56
|
let modelClass = this.store.modelFor(modelName);
|
|
58
|
-
let attributeMap =
|
|
57
|
+
let attributeMap = modelClass.attributes;
|
|
59
58
|
|
|
60
59
|
attributes = Object.create(null);
|
|
61
60
|
attributeMap.forEach((meta, name) => (attributes[name] = meta));
|
|
@@ -88,7 +87,7 @@ export class DSModelSchemaDefinitionService {
|
|
|
88
87
|
|
|
89
88
|
if (relationships === undefined) {
|
|
90
89
|
let modelClass = this.store.modelFor(modelName);
|
|
91
|
-
relationships =
|
|
90
|
+
relationships = modelClass.relationshipsObject || null;
|
|
92
91
|
this._relationshipsDefCache[modelName] = relationships;
|
|
93
92
|
}
|
|
94
93
|
|
|
@@ -4,8 +4,8 @@ import type { ModelSchema } from '@ember-data/types/q/ds-model';
|
|
|
4
4
|
import type { AttributeSchema, RelationshipSchema } from '@ember-data/types/q/record-data-schemas';
|
|
5
5
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
6
6
|
|
|
7
|
-
import type Store from '../
|
|
8
|
-
import WeakCache from '../weak-cache';
|
|
7
|
+
import type Store from '../store-service';
|
|
8
|
+
import WeakCache from '../utils/weak-cache';
|
|
9
9
|
|
|
10
10
|
const AvailableShims = new WeakCache<Store, Dict<ShimModelClass>>(DEBUG ? 'schema-shims' : '');
|
|
11
11
|
AvailableShims._generator = () => {
|
|
@@ -4,20 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
import { A } from '@ember/array';
|
|
6
6
|
import { assert } from '@ember/debug';
|
|
7
|
-
import { set } from '@ember/object';
|
|
8
7
|
import { _backburner as emberBackburner } from '@ember/runloop';
|
|
9
8
|
import { DEBUG } from '@glimmer/env';
|
|
10
9
|
|
|
11
|
-
// import isStableIdentifier from '../identifiers/is-stable-identifier';
|
|
12
10
|
import type { CollectionResourceDocument, Meta } from '@ember-data/types/q/ember-data-json-api';
|
|
13
11
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
14
12
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
15
13
|
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import WeakCache from './weak-cache';
|
|
14
|
+
import AdapterPopulatedRecordArray from '../record-arrays/adapter-populated-record-array';
|
|
15
|
+
import RecordArray from '../record-arrays/record-array';
|
|
16
|
+
import type Store from '../store-service';
|
|
17
|
+
import WeakCache from '../utils/weak-cache';
|
|
21
18
|
|
|
22
19
|
const RecordArraysCache = new WeakCache<StableRecordIdentifier, Set<RecordArray>>(DEBUG ? 'record-arrays' : '');
|
|
23
20
|
RecordArraysCache._generator = () => new Set();
|
|
@@ -27,27 +24,6 @@ export function recordArraysForIdentifier(identifier: StableRecordIdentifier): S
|
|
|
27
24
|
|
|
28
25
|
const pendingForIdentifier: Set<StableRecordIdentifier> = new Set([]);
|
|
29
26
|
|
|
30
|
-
function getIdentifier(identifier: StableRecordIdentifier): StableRecordIdentifier {
|
|
31
|
-
// during dematerialization we will get an identifier that
|
|
32
|
-
// has already been removed from the identifiers cache
|
|
33
|
-
// so it will not behave as if stable. This is a bug we should fix.
|
|
34
|
-
// if (!isStableIdentifier(identifierOrInternalModel)) {
|
|
35
|
-
// console.log({ unstable: i });
|
|
36
|
-
// }
|
|
37
|
-
|
|
38
|
-
return identifier;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function shouldIncludeInRecordArrays(store: Store, identifier: StableRecordIdentifier): boolean {
|
|
42
|
-
const cache = internalModelFactoryFor(store);
|
|
43
|
-
const internalModel = cache.peek(identifier);
|
|
44
|
-
|
|
45
|
-
if (internalModel === null) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
return !internalModel.isHiddenFromRecordArrays();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
27
|
/**
|
|
52
28
|
@class RecordArrayManager
|
|
53
29
|
@internal
|
|
@@ -84,6 +60,7 @@ class RecordArrayManager {
|
|
|
84
60
|
return;
|
|
85
61
|
}
|
|
86
62
|
let identifiersToRemove: StableRecordIdentifier[] = [];
|
|
63
|
+
let cache = this.store._instanceCache;
|
|
87
64
|
|
|
88
65
|
for (let j = 0; j < identifiers.length; j++) {
|
|
89
66
|
let i = identifiers[j];
|
|
@@ -91,8 +68,7 @@ class RecordArrayManager {
|
|
|
91
68
|
// recordArrayManager
|
|
92
69
|
pendingForIdentifier.delete(i);
|
|
93
70
|
// build up a set of models to ensure we have purged correctly;
|
|
94
|
-
|
|
95
|
-
if (!isIncluded) {
|
|
71
|
+
if (!cache.recordIsLoaded(i, true)) {
|
|
96
72
|
identifiersToRemove.push(i);
|
|
97
73
|
}
|
|
98
74
|
}
|
|
@@ -130,8 +106,8 @@ class RecordArrayManager {
|
|
|
130
106
|
return;
|
|
131
107
|
}
|
|
132
108
|
let hasNoPotentialDeletions = pending.length === 0;
|
|
133
|
-
let
|
|
134
|
-
let hasNoInsertionsOrRemovals =
|
|
109
|
+
let listSize = this.store._instanceCache.peekList[modelName]?.size;
|
|
110
|
+
let hasNoInsertionsOrRemovals = listSize === array.length;
|
|
135
111
|
|
|
136
112
|
/*
|
|
137
113
|
Ideally the recordArrayManager has knowledge of the changes to be applied to
|
|
@@ -165,7 +141,7 @@ class RecordArrayManager {
|
|
|
165
141
|
_didUpdateAll(modelName: string): void {
|
|
166
142
|
let recordArray = this._liveRecordArrays[modelName];
|
|
167
143
|
if (recordArray) {
|
|
168
|
-
|
|
144
|
+
recordArray.isUpdating = false;
|
|
169
145
|
// TODO potentially we should sync here, currently
|
|
170
146
|
// this occurs as a side-effect of individual records updating
|
|
171
147
|
// this._syncLiveRecordArray(recordArray, modelName);
|
|
@@ -204,13 +180,14 @@ class RecordArrayManager {
|
|
|
204
180
|
}
|
|
205
181
|
|
|
206
182
|
_visibleIdentifiersByType(modelName: string) {
|
|
207
|
-
|
|
183
|
+
const cache = this.store._instanceCache;
|
|
184
|
+
const list = cache.peekList[modelName];
|
|
185
|
+
let all = list ? [...list.values()] : [];
|
|
208
186
|
let visible: StableRecordIdentifier[] = [];
|
|
209
187
|
for (let i = 0; i < all.length; i++) {
|
|
210
188
|
let identifier = all[i];
|
|
211
|
-
let shouldInclude = shouldIncludeInRecordArrays(this.store, identifier);
|
|
212
189
|
|
|
213
|
-
if (
|
|
190
|
+
if (cache.recordIsLoaded(identifier, true)) {
|
|
214
191
|
visible.push(identifier);
|
|
215
192
|
}
|
|
216
193
|
}
|
|
@@ -335,7 +312,6 @@ class RecordArrayManager {
|
|
|
335
312
|
_associateWithRecordArray(identifiers: StableRecordIdentifier[], array: RecordArray): void {
|
|
336
313
|
for (let i = 0, l = identifiers.length; i < l; i++) {
|
|
337
314
|
let identifier = identifiers[i];
|
|
338
|
-
identifier = getIdentifier(identifier);
|
|
339
315
|
let recordArrays = this.getRecordArraysForIdentifier(identifier);
|
|
340
316
|
recordArrays.add(array);
|
|
341
317
|
}
|
|
@@ -350,7 +326,6 @@ class RecordArrayManager {
|
|
|
350
326
|
return;
|
|
351
327
|
}
|
|
352
328
|
let modelName = identifier.type;
|
|
353
|
-
identifier = getIdentifier(identifier);
|
|
354
329
|
|
|
355
330
|
if (pendingForIdentifier.has(identifier)) {
|
|
356
331
|
return;
|
|
@@ -397,20 +372,18 @@ function removeFromArray(array: RecordArray[], item: RecordArray): boolean {
|
|
|
397
372
|
function updateLiveRecordArray(store: Store, recordArray: RecordArray, identifiers: StableRecordIdentifier[]): void {
|
|
398
373
|
let identifiersToAdd: StableRecordIdentifier[] = [];
|
|
399
374
|
let identifiersToRemove: StableRecordIdentifier[] = [];
|
|
375
|
+
const cache = store._instanceCache;
|
|
400
376
|
|
|
401
377
|
for (let i = 0; i < identifiers.length; i++) {
|
|
402
378
|
let identifier = identifiers[i];
|
|
403
|
-
let shouldInclude = shouldIncludeInRecordArrays(store, identifier);
|
|
404
379
|
let recordArrays = recordArraysForIdentifier(identifier);
|
|
405
380
|
|
|
406
|
-
if (
|
|
381
|
+
if (cache.recordIsLoaded(identifier, true)) {
|
|
407
382
|
if (!recordArrays.has(recordArray)) {
|
|
408
383
|
identifiersToAdd.push(identifier);
|
|
409
384
|
recordArrays.add(recordArray);
|
|
410
385
|
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (!shouldInclude) {
|
|
386
|
+
} else {
|
|
414
387
|
identifiersToRemove.push(identifier);
|
|
415
388
|
recordArrays.delete(recordArray);
|
|
416
389
|
}
|
|
@@ -431,7 +404,6 @@ function removeFromAdapterPopulatedRecordArrays(store: Store, identifiers: Stabl
|
|
|
431
404
|
}
|
|
432
405
|
|
|
433
406
|
function removeFromAll(store: Store, identifier: StableRecordIdentifier): void {
|
|
434
|
-
identifier = getIdentifier(identifier);
|
|
435
407
|
const recordArrays = recordArraysForIdentifier(identifier);
|
|
436
408
|
|
|
437
409
|
recordArrays.forEach(function (recordArray) {
|
package/addon/-private/{record-data-store-wrapper.ts → managers/record-data-store-wrapper.ts}
RENAMED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { importSync } from '@embroider/macros';
|
|
2
|
-
|
|
3
1
|
import type { RelationshipDefinition } from '@ember-data/model/-private/relationship-meta';
|
|
4
|
-
import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
|
|
5
2
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
6
3
|
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
7
4
|
import type {
|
|
@@ -11,10 +8,9 @@ import type {
|
|
|
11
8
|
} from '@ember-data/types/q/record-data-schemas';
|
|
12
9
|
import type { RecordDataStoreWrapper as StoreWrapper } from '@ember-data/types/q/record-data-store-wrapper';
|
|
13
10
|
|
|
14
|
-
import type
|
|
15
|
-
import type
|
|
16
|
-
import
|
|
17
|
-
import constructResource from './utils/construct-resource';
|
|
11
|
+
import type { IdentifierCache } from '../caches/identifier-cache';
|
|
12
|
+
import type Store from '../store-service';
|
|
13
|
+
import constructResource from '../utils/construct-resource';
|
|
18
14
|
|
|
19
15
|
/**
|
|
20
16
|
@module @ember-data/store
|
|
@@ -24,17 +20,6 @@ function metaIsRelationshipDefinition(meta: RelationshipSchema): meta is Relatio
|
|
|
24
20
|
return typeof (meta as RelationshipDefinition)._inverseKey === 'function';
|
|
25
21
|
}
|
|
26
22
|
|
|
27
|
-
let peekGraph;
|
|
28
|
-
if (HAS_RECORD_DATA_PACKAGE) {
|
|
29
|
-
let _peekGraph;
|
|
30
|
-
peekGraph = (wrapper) => {
|
|
31
|
-
_peekGraph =
|
|
32
|
-
_peekGraph ||
|
|
33
|
-
(importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')).peekGraph;
|
|
34
|
-
return _peekGraph(wrapper);
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
23
|
export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
39
24
|
declare _willNotify: boolean;
|
|
40
25
|
declare _pendingNotifies: Map<StableRecordIdentifier, Map<string, string>>;
|
|
@@ -86,19 +71,11 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
86
71
|
let pending = this._pendingNotifies;
|
|
87
72
|
this._pendingNotifies = new Map();
|
|
88
73
|
this._willNotify = false;
|
|
89
|
-
const factory = internalModelFactoryFor(this._store);
|
|
90
74
|
|
|
91
75
|
pending.forEach((map, identifier) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (kind === 'belongsTo') {
|
|
96
|
-
internalModel.notifyBelongsToChange(key);
|
|
97
|
-
} else {
|
|
98
|
-
internalModel.notifyHasManyChange(key);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
76
|
+
map.forEach((kind, key) => {
|
|
77
|
+
this._store._notificationManager.notify(identifier, 'relationships', key);
|
|
78
|
+
});
|
|
102
79
|
});
|
|
103
80
|
}
|
|
104
81
|
|
|
@@ -152,11 +129,8 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
152
129
|
notifyPropertyChange(type: string, id: string | null, lid: string | null | undefined, key?: string): void {
|
|
153
130
|
const resource = constructResource(type, id, lid);
|
|
154
131
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
155
|
-
let internalModel = internalModelFactoryFor(this._store).peek(identifier);
|
|
156
132
|
|
|
157
|
-
|
|
158
|
-
internalModel.notifyAttributes(key ? [key] : []);
|
|
159
|
-
}
|
|
133
|
+
this._store._notificationManager.notify(identifier, 'attributes', key);
|
|
160
134
|
}
|
|
161
135
|
|
|
162
136
|
notifyHasManyChange(type: string, id: string | null, lid: string, key: string): void;
|
|
@@ -181,10 +155,11 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
181
155
|
notifyStateChange(type: string, id: string | null, lid: string | null, key?: string): void {
|
|
182
156
|
const resource = constructResource(type, id, lid);
|
|
183
157
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
184
|
-
let internalModel = internalModelFactoryFor(this._store).peek(identifier);
|
|
185
158
|
|
|
186
|
-
|
|
187
|
-
|
|
159
|
+
this._store._notificationManager.notify(identifier, 'state');
|
|
160
|
+
|
|
161
|
+
if (!key || key === 'isDeletionCommitted') {
|
|
162
|
+
this._store.recordArrayManager.recordDidChange(identifier);
|
|
188
163
|
}
|
|
189
164
|
}
|
|
190
165
|
|
|
@@ -192,17 +167,25 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
192
167
|
recordDataFor(type: string, id: string | null, lid: string): RecordData;
|
|
193
168
|
recordDataFor(type: string): RecordData;
|
|
194
169
|
recordDataFor(type: string, id?: string | null, lid?: string | null): RecordData {
|
|
195
|
-
|
|
196
|
-
|
|
170
|
+
// TODO @deprecate create capability. This is problematic because there's
|
|
171
|
+
// no outside association between this RecordData and an Identifier. It's
|
|
172
|
+
// likely a mistake but we said in an RFC we'd allow this. We should RFC
|
|
173
|
+
// enforcing someone to use the record-data and identifier-cache APIs to
|
|
174
|
+
// create a new identifier and then call clientDidCreate on the RecordData
|
|
175
|
+
// instead.
|
|
176
|
+
const identifier =
|
|
177
|
+
!id && !lid
|
|
178
|
+
? this.identifierCache.createIdentifierForNewRecord({ type: type })
|
|
179
|
+
: this.identifierCache.getOrCreateRecordIdentifier(constructResource(type, id, lid));
|
|
180
|
+
|
|
181
|
+
const recordData = this._store._instanceCache.getRecordData(identifier);
|
|
182
|
+
|
|
197
183
|
if (!id && !lid) {
|
|
198
|
-
|
|
199
|
-
identifier
|
|
200
|
-
} else {
|
|
201
|
-
const resource = constructResource(type, id, lid);
|
|
202
|
-
identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
184
|
+
recordData.clientDidCreate();
|
|
185
|
+
this._store.recordArrayManager.recordDidChange(identifier);
|
|
203
186
|
}
|
|
204
187
|
|
|
205
|
-
return
|
|
188
|
+
return recordData;
|
|
206
189
|
}
|
|
207
190
|
|
|
208
191
|
setRecordId(type: string, id: string, lid: string) {
|
|
@@ -213,9 +196,9 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
213
196
|
isRecordInUse(type: string, id: string, lid?: string | null): boolean;
|
|
214
197
|
isRecordInUse(type: string, id: string | null, lid?: string | null): boolean {
|
|
215
198
|
const resource = constructResource(type, id, lid);
|
|
216
|
-
const identifier = this.identifierCache.
|
|
199
|
+
const identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
217
200
|
|
|
218
|
-
const record = this._store._instanceCache.peek({ identifier, bucket: 'record' });
|
|
201
|
+
const record = identifier && this._store._instanceCache.peek({ identifier, bucket: 'record' });
|
|
219
202
|
|
|
220
203
|
return record ? !(record.isDestroyed || record.isDestroying) : false;
|
|
221
204
|
}
|
|
@@ -224,16 +207,11 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
224
207
|
disconnectRecord(type: string, id: string, lid?: string | null): void;
|
|
225
208
|
disconnectRecord(type: string, id: string | null, lid?: string | null): void {
|
|
226
209
|
const resource = constructResource(type, id, lid);
|
|
227
|
-
const identifier = this.identifierCache.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
let internalModel = internalModelFactoryFor(this._store).peek(identifier);
|
|
235
|
-
if (internalModel) {
|
|
236
|
-
internalModel.destroyFromRecordData();
|
|
210
|
+
const identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
211
|
+
|
|
212
|
+
if (identifier) {
|
|
213
|
+
this._store._instanceCache.disconnect(identifier);
|
|
214
|
+
this._pendingNotifies.delete(identifier);
|
|
237
215
|
}
|
|
238
216
|
}
|
|
239
217
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { assert } from '@ember/debug';
|
|
2
|
+
import { DEBUG } from '@glimmer/env';
|
|
3
|
+
|
|
4
|
+
import { LOG_NOTIFICATIONS } from '@ember-data/private-build-infra/debugging';
|
|
5
|
+
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
6
|
+
|
|
7
|
+
import { isStableIdentifier } from '../caches/identifier-cache';
|
|
8
|
+
import type Store from '../store-service';
|
|
9
|
+
import WeakCache from '../utils/weak-cache';
|
|
10
|
+
|
|
11
|
+
type UnsubscribeToken = object;
|
|
12
|
+
|
|
13
|
+
const Cache = new WeakCache<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>(
|
|
14
|
+
DEBUG ? 'subscribers' : ''
|
|
15
|
+
);
|
|
16
|
+
Cache._generator = () => new Map();
|
|
17
|
+
const Tokens = new WeakCache<UnsubscribeToken, StableRecordIdentifier>(DEBUG ? 'identifier' : '');
|
|
18
|
+
|
|
19
|
+
export type NotificationType =
|
|
20
|
+
| 'attributes'
|
|
21
|
+
| 'relationships'
|
|
22
|
+
| 'identity'
|
|
23
|
+
| 'errors'
|
|
24
|
+
| 'meta'
|
|
25
|
+
| 'unload'
|
|
26
|
+
| 'state'
|
|
27
|
+
| 'property'; // 'property' is an internal EmberData only transition period concept.
|
|
28
|
+
|
|
29
|
+
export interface NotificationCallback {
|
|
30
|
+
(
|
|
31
|
+
identifier: StableRecordIdentifier,
|
|
32
|
+
notificationType: 'attributes' | 'relationships' | 'property',
|
|
33
|
+
key?: string
|
|
34
|
+
): void;
|
|
35
|
+
(identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): void;
|
|
36
|
+
(identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function unsubscribe(token: UnsubscribeToken) {
|
|
40
|
+
let identifier = Tokens.get(token);
|
|
41
|
+
if (!identifier) {
|
|
42
|
+
throw new Error('Passed unknown unsubscribe token to unsubscribe');
|
|
43
|
+
}
|
|
44
|
+
Tokens.delete(token);
|
|
45
|
+
const map = Cache.get(identifier);
|
|
46
|
+
map?.delete(token);
|
|
47
|
+
}
|
|
48
|
+
/*
|
|
49
|
+
Currently only support a single callback per identifier
|
|
50
|
+
*/
|
|
51
|
+
export default class NotificationManager {
|
|
52
|
+
constructor(private store: Store) {}
|
|
53
|
+
|
|
54
|
+
subscribe(identifier: StableRecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
|
|
55
|
+
assert(`Expected to receive a stable Identifier to subscribe to`, isStableIdentifier(identifier));
|
|
56
|
+
let map = Cache.lookup(identifier);
|
|
57
|
+
let unsubToken = {};
|
|
58
|
+
map.set(unsubToken, callback);
|
|
59
|
+
Tokens.set(unsubToken, identifier);
|
|
60
|
+
return unsubToken;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
unsubscribe(token: UnsubscribeToken) {
|
|
64
|
+
unsubscribe(token);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
notify(identifier: StableRecordIdentifier, value: 'attributes' | 'relationships' | 'property', key?: string): boolean;
|
|
68
|
+
notify(identifier: StableRecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): boolean;
|
|
69
|
+
notify(identifier: StableRecordIdentifier, value: NotificationType, key?: string): boolean {
|
|
70
|
+
if (!isStableIdentifier(identifier)) {
|
|
71
|
+
if (LOG_NOTIFICATIONS) {
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.log(
|
|
74
|
+
`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key}' with, but ${String(
|
|
75
|
+
identifier
|
|
76
|
+
)} is not in the cache`,
|
|
77
|
+
identifier
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (LOG_NOTIFICATIONS) {
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.log(`Notifying: ${String(identifier)}\t${value}\t${key}`);
|
|
86
|
+
}
|
|
87
|
+
let callbackMap = Cache.get(identifier);
|
|
88
|
+
if (!callbackMap || !callbackMap.size) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
callbackMap.forEach((cb) => {
|
|
92
|
+
cb(identifier, value, key);
|
|
93
|
+
});
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -19,14 +19,14 @@ import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-ser
|
|
|
19
19
|
import type { FindOptions } from '@ember-data/types/q/store';
|
|
20
20
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
21
21
|
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
22
|
+
import ShimModelClass from '../legacy-model-support/shim-model-class';
|
|
23
|
+
import type Store from '../store-service';
|
|
24
|
+
import coerceId from '../utils/coerce-id';
|
|
25
|
+
import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from '../utils/common';
|
|
26
|
+
import { normalizeResponseHelper } from '../utils/serializer-response';
|
|
27
|
+
import WeakCache from '../utils/weak-cache';
|
|
26
28
|
import RequestCache from './request-cache';
|
|
27
|
-
import { normalizeResponseHelper } from './serializer-response';
|
|
28
29
|
import Snapshot from './snapshot';
|
|
29
|
-
import WeakCache from './weak-cache';
|
|
30
30
|
|
|
31
31
|
function payloadIsNotBlank(adapterPayload): boolean {
|
|
32
32
|
if (Array.isArray(adapterPayload)) {
|
|
@@ -84,6 +84,10 @@ export default class FetchManager {
|
|
|
84
84
|
this.isDestroyed = false;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
clearEntries(identifier: StableRecordIdentifier) {
|
|
88
|
+
delete this.requestCache._done[identifier.lid];
|
|
89
|
+
}
|
|
90
|
+
|
|
87
91
|
/**
|
|
88
92
|
This method is called by `record.save`, and gets passed a
|
|
89
93
|
resolver for the promise that `record.save` returns.
|
|
@@ -126,10 +130,10 @@ export default class FetchManager {
|
|
|
126
130
|
let adapter = this._store.adapterFor(identifier.type);
|
|
127
131
|
let operation = options[SaveOp];
|
|
128
132
|
|
|
129
|
-
let internalModel = snapshot._internalModel;
|
|
130
133
|
let modelName = snapshot.modelName;
|
|
131
134
|
let store = this._store;
|
|
132
135
|
let modelClass = store.modelFor(modelName);
|
|
136
|
+
const record = store._instanceCache.getRecord(identifier);
|
|
133
137
|
|
|
134
138
|
assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter);
|
|
135
139
|
assert(
|
|
@@ -139,16 +143,16 @@ export default class FetchManager {
|
|
|
139
143
|
|
|
140
144
|
let promise = resolve().then(() => adapter[operation](store, modelClass, snapshot));
|
|
141
145
|
let serializer: SerializerWithParseErrors | null = store.serializerFor(modelName);
|
|
142
|
-
let label = `DS: Extract and notify about ${operation} completion of ${
|
|
146
|
+
let label = `DS: Extract and notify about ${operation} completion of ${identifier}`;
|
|
143
147
|
|
|
144
148
|
assert(
|
|
145
149
|
`Your adapter's '${operation}' method must return a value, but it returned 'undefined'`,
|
|
146
150
|
promise !== undefined
|
|
147
151
|
);
|
|
148
152
|
|
|
149
|
-
promise = _guard(guardDestroyedStore(promise, store, label), _bind(_objectIsAlive,
|
|
153
|
+
promise = _guard(guardDestroyedStore(promise, store, label), _bind(_objectIsAlive, record)).then(
|
|
150
154
|
(adapterPayload) => {
|
|
151
|
-
if (!_objectIsAlive(
|
|
155
|
+
if (!_objectIsAlive(record)) {
|
|
152
156
|
if (DEPRECATE_RSVP_PROMISE) {
|
|
153
157
|
deprecate(
|
|
154
158
|
`A Promise while saving ${modelName} did not resolve by the time your model was destroyed. This will error in a future release.`,
|
|
@@ -239,11 +243,8 @@ export default class FetchManager {
|
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
let resolverPromise = resolver.promise;
|
|
242
|
-
|
|
243
|
-
// TODO replace with some form of record state cache
|
|
244
246
|
const store = this._store;
|
|
245
|
-
const
|
|
246
|
-
const isLoading = !internalModel.isLoaded; // we don't use isLoading directly because we are the request
|
|
247
|
+
const isLoading = !store._instanceCache.recordIsLoaded(identifier); // we don't use isLoading directly because we are the request
|
|
247
248
|
|
|
248
249
|
const promise = resolverPromise.then(
|
|
249
250
|
(payload) => {
|
|
@@ -262,8 +263,9 @@ export default class FetchManager {
|
|
|
262
263
|
return identifier;
|
|
263
264
|
},
|
|
264
265
|
(error) => {
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
const recordData = store._instanceCache.peek({ identifier, bucket: 'recordData' });
|
|
267
|
+
if (!recordData || recordData.isEmpty?.() || isLoading) {
|
|
268
|
+
store._instanceCache.unloadRecord(identifier);
|
|
267
269
|
}
|
|
268
270
|
throw error;
|
|
269
271
|
}
|