@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
|
@@ -1,28 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { assert, deprecate } from '@ember/debug';
|
|
2
|
+
|
|
3
|
+
import { DEPRECATE_V1CACHE_STORE_APIS } from '@ember-data/private-build-infra/deprecations';
|
|
4
|
+
import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
3
5
|
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
6
|
+
import type { AttributesSchema, RelationshipsSchema } from '@ember-data/types/q/record-data-schemas';
|
|
4
7
|
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from '@ember-data/types/q/
|
|
9
|
-
import type { RecordDataStoreWrapper as StoreWrapper } from '@ember-data/types/q/record-data-store-wrapper';
|
|
8
|
+
LegacyRecordDataStoreWrapper,
|
|
9
|
+
V2RecordDataStoreWrapper as StoreWrapper,
|
|
10
|
+
} from '@ember-data/types/q/record-data-store-wrapper';
|
|
11
|
+
import { SchemaDefinitionService } from '@ember-data/types/q/schema-definition-service';
|
|
10
12
|
|
|
11
|
-
import
|
|
13
|
+
import { IdentifierCache, isStableIdentifier } from '../caches/identifier-cache';
|
|
12
14
|
import type Store from '../store-service';
|
|
15
|
+
import coerceId from '../utils/coerce-id';
|
|
13
16
|
import constructResource from '../utils/construct-resource';
|
|
17
|
+
import normalizeModelName from '../utils/normalize-model-name';
|
|
18
|
+
import { NotificationType } from './record-notification-manager';
|
|
14
19
|
|
|
15
20
|
/**
|
|
16
21
|
@module @ember-data/store
|
|
17
22
|
*/
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
return typeof (meta as RelationshipDefinition)._inverseKey === 'function';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
24
|
+
class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
24
25
|
declare _willNotify: boolean;
|
|
25
|
-
declare _pendingNotifies: Map<StableRecordIdentifier,
|
|
26
|
+
declare _pendingNotifies: Map<StableRecordIdentifier, Set<string>>;
|
|
26
27
|
declare _store: Store;
|
|
27
28
|
|
|
28
29
|
constructor(_store: Store) {
|
|
@@ -35,32 +36,27 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
35
36
|
return this._store.identifierCache;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
_scheduleNotification(identifier: StableRecordIdentifier, key: string
|
|
39
|
+
_scheduleNotification(identifier: StableRecordIdentifier, key: string) {
|
|
39
40
|
let pending = this._pendingNotifies.get(identifier);
|
|
40
41
|
|
|
41
42
|
if (!pending) {
|
|
42
|
-
pending = new
|
|
43
|
+
pending = new Set();
|
|
43
44
|
this._pendingNotifies.set(identifier, pending);
|
|
44
45
|
}
|
|
45
|
-
pending.
|
|
46
|
+
pending.add(key);
|
|
46
47
|
|
|
47
48
|
if (this._willNotify === true) {
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
this._willNotify = true;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
notifyErrorsChange(type: string, id: string | null, lid: string | null): void {
|
|
60
|
-
const resource = constructResource(type, id, lid);
|
|
61
|
-
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
62
|
-
|
|
63
|
-
this._store._notificationManager.notify(identifier, 'errors');
|
|
53
|
+
// it's possible a RecordData adhoc notifies us,
|
|
54
|
+
// in which case we sync flush
|
|
55
|
+
if (this._store._cbs) {
|
|
56
|
+
this._store._schedule('notify', () => this._flushNotifications());
|
|
57
|
+
} else {
|
|
58
|
+
this._flushNotifications();
|
|
59
|
+
}
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
_flushNotifications(): void {
|
|
@@ -72,61 +68,93 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
72
68
|
this._pendingNotifies = new Map();
|
|
73
69
|
this._willNotify = false;
|
|
74
70
|
|
|
75
|
-
pending.forEach((
|
|
76
|
-
|
|
71
|
+
pending.forEach((set, identifier) => {
|
|
72
|
+
set.forEach((key) => {
|
|
77
73
|
this._store._notificationManager.notify(identifier, 'relationships', key);
|
|
78
74
|
});
|
|
79
75
|
});
|
|
80
76
|
}
|
|
81
77
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
relationshipsDefinitionFor(type: string): RelationshipsSchema {
|
|
87
|
-
return this._store.getSchemaDefinitionService().relationshipsDefinitionFor({ type });
|
|
88
|
-
}
|
|
78
|
+
notifyChange(identifier: StableRecordIdentifier, namespace: NotificationType, key?: string): void {
|
|
79
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
89
80
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return null;
|
|
81
|
+
// TODO do we still get value from this?
|
|
82
|
+
if (namespace === 'relationships' && key) {
|
|
83
|
+
this._scheduleNotification(identifier, key);
|
|
84
|
+
return;
|
|
95
85
|
}
|
|
96
86
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
} else {
|
|
102
|
-
return null;
|
|
87
|
+
this._store._notificationManager.notify(identifier, namespace, key);
|
|
88
|
+
|
|
89
|
+
if (namespace === 'state') {
|
|
90
|
+
this._store.recordArrayManager.identifierChanged(identifier);
|
|
103
91
|
}
|
|
104
92
|
}
|
|
105
93
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
|
|
94
|
+
notifyErrorsChange(type: string, id: string, lid: string | null): void;
|
|
95
|
+
notifyErrorsChange(type: string, id: string | null, lid: string): void;
|
|
96
|
+
notifyErrorsChange(type: string, id: string | null, lid: string | null): void {
|
|
97
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
98
|
+
deprecate(`StoreWrapper.notifyErrorsChange has been deprecated in favor of StoreWrapper.notifyChange`, false, {
|
|
99
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
100
|
+
for: 'ember-data',
|
|
101
|
+
until: '5.0',
|
|
102
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
103
|
+
});
|
|
111
104
|
}
|
|
105
|
+
const resource = constructResource(type, id, lid);
|
|
106
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
112
107
|
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
this._store._notificationManager.notify(identifier, 'errors');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
attributesDefinitionFor(type: string): AttributesSchema {
|
|
112
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
113
|
+
deprecate(
|
|
114
|
+
`StoreWrapper.attributesDefinitionFor has been deprecated in favor of StoreWrapper.getSchemaDefinitionService().attributesDefinitionFor`,
|
|
115
|
+
false,
|
|
116
|
+
{
|
|
117
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
118
|
+
for: 'ember-data',
|
|
119
|
+
until: '5.0',
|
|
120
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
121
|
+
}
|
|
122
|
+
);
|
|
115
123
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
return this._store.getSchemaDefinitionService().attributesDefinitionFor({ type });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
relationshipsDefinitionFor(type: string): RelationshipsSchema {
|
|
128
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
129
|
+
deprecate(
|
|
130
|
+
`StoreWrapper.relationshipsDefinitionFor has been deprecated in favor of StoreWrapper.getSchemaDefinitionService().relationshipsDefinitionFor`,
|
|
131
|
+
false,
|
|
132
|
+
{
|
|
133
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
134
|
+
for: 'ember-data',
|
|
135
|
+
until: '5.0',
|
|
136
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
137
|
+
}
|
|
138
|
+
);
|
|
124
139
|
}
|
|
140
|
+
return this._store.getSchemaDefinitionService().relationshipsDefinitionFor({ type });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getSchemaDefinitionService(): SchemaDefinitionService {
|
|
144
|
+
return this._store.getSchemaDefinitionService();
|
|
125
145
|
}
|
|
126
146
|
|
|
127
147
|
notifyPropertyChange(type: string, id: string | null, lid: string, key?: string): void;
|
|
128
148
|
notifyPropertyChange(type: string, id: string, lid: string | null | undefined, key?: string): void;
|
|
129
149
|
notifyPropertyChange(type: string, id: string | null, lid: string | null | undefined, key?: string): void {
|
|
150
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
151
|
+
deprecate(`StoreWrapper.notifyPropertyChange has been deprecated in favor of StoreWrapper.notifyChange`, false, {
|
|
152
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
153
|
+
for: 'ember-data',
|
|
154
|
+
until: '5.0',
|
|
155
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
130
158
|
const resource = constructResource(type, id, lid);
|
|
131
159
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
132
160
|
|
|
@@ -136,65 +164,122 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
136
164
|
notifyHasManyChange(type: string, id: string | null, lid: string, key: string): void;
|
|
137
165
|
notifyHasManyChange(type: string, id: string, lid: string | null | undefined, key: string): void;
|
|
138
166
|
notifyHasManyChange(type: string, id: string | null, lid: string | null | undefined, key: string): void {
|
|
167
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
168
|
+
deprecate(`StoreWrapper.notifyHasManyChange has been deprecated in favor of StoreWrapper.notifyChange`, false, {
|
|
169
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
170
|
+
for: 'ember-data',
|
|
171
|
+
until: '5.0',
|
|
172
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
139
175
|
const resource = constructResource(type, id, lid);
|
|
140
176
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
141
|
-
this._scheduleNotification(identifier, key
|
|
177
|
+
this._scheduleNotification(identifier, key);
|
|
142
178
|
}
|
|
143
179
|
|
|
144
180
|
notifyBelongsToChange(type: string, id: string | null, lid: string, key: string): void;
|
|
145
181
|
notifyBelongsToChange(type: string, id: string, lid: string | null | undefined, key: string): void;
|
|
146
182
|
notifyBelongsToChange(type: string, id: string | null, lid: string | null | undefined, key: string): void {
|
|
183
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
184
|
+
deprecate(`StoreWrapper.notifyBelongsToChange has been deprecated in favor of StoreWrapper.notifyChange`, false, {
|
|
185
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
186
|
+
for: 'ember-data',
|
|
187
|
+
until: '5.0',
|
|
188
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
189
|
+
});
|
|
190
|
+
}
|
|
147
191
|
const resource = constructResource(type, id, lid);
|
|
148
192
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
149
193
|
|
|
150
|
-
this._scheduleNotification(identifier, key
|
|
194
|
+
this._scheduleNotification(identifier, key);
|
|
151
195
|
}
|
|
152
196
|
|
|
153
197
|
notifyStateChange(type: string, id: string, lid: string | null, key?: string): void;
|
|
154
198
|
notifyStateChange(type: string, id: string | null, lid: string, key?: string): void;
|
|
155
199
|
notifyStateChange(type: string, id: string | null, lid: string | null, key?: string): void {
|
|
200
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
201
|
+
deprecate(`StoreWrapper.notifyStateChange has been deprecated in favor of StoreWrapper.notifyChange`, false, {
|
|
202
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
203
|
+
for: 'ember-data',
|
|
204
|
+
until: '5.0',
|
|
205
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
156
208
|
const resource = constructResource(type, id, lid);
|
|
157
209
|
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
158
210
|
|
|
159
211
|
this._store._notificationManager.notify(identifier, 'state');
|
|
160
|
-
|
|
161
|
-
if (!key || key === 'isDeletionCommitted') {
|
|
162
|
-
this._store.recordArrayManager.recordDidChange(identifier);
|
|
163
|
-
}
|
|
212
|
+
this._store.recordArrayManager.identifierChanged(identifier);
|
|
164
213
|
}
|
|
165
214
|
|
|
166
215
|
recordDataFor(type: string, id: string, lid?: string | null): RecordData;
|
|
167
216
|
recordDataFor(type: string, id: string | null, lid: string): RecordData;
|
|
168
217
|
recordDataFor(type: string): RecordData;
|
|
169
|
-
recordDataFor(type:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
218
|
+
recordDataFor(type: StableRecordIdentifier): RecordData;
|
|
219
|
+
recordDataFor(type: string | StableRecordIdentifier, id?: string | null, lid?: string | null): RecordData {
|
|
220
|
+
let identifier: StableRecordIdentifier;
|
|
221
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
222
|
+
if (!isStableIdentifier(type)) {
|
|
223
|
+
// we also deprecate create capability. This behavior was problematic because
|
|
224
|
+
// there's no outside association between this RecordData and an Identifier.
|
|
225
|
+
// It's likely a mistake when we hit this codepath, but we said in an early
|
|
226
|
+
// RFC we'd allow this.
|
|
227
|
+
// With V2 we are enforcing someone to use the record-data and identifier-cache APIs to
|
|
228
|
+
// create a new identifier and then call clientDidCreate on the RecordData
|
|
229
|
+
// instead.
|
|
230
|
+
identifier =
|
|
231
|
+
!id && !lid
|
|
232
|
+
? this.identifierCache.createIdentifierForNewRecord({ type: type })
|
|
233
|
+
: this.identifierCache.getOrCreateRecordIdentifier(constructResource(type, id, lid));
|
|
234
|
+
} else {
|
|
235
|
+
identifier = type;
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
assert(`Expected a stable identifier`, isStableIdentifier(type));
|
|
239
|
+
identifier = type;
|
|
240
|
+
}
|
|
180
241
|
|
|
181
242
|
const recordData = this._store._instanceCache.getRecordData(identifier);
|
|
182
243
|
|
|
183
244
|
if (!id && !lid) {
|
|
184
|
-
recordData.clientDidCreate();
|
|
185
|
-
this._store.recordArrayManager.
|
|
245
|
+
recordData.clientDidCreate(identifier);
|
|
246
|
+
this._store.recordArrayManager.identifierAdded(identifier);
|
|
186
247
|
}
|
|
187
248
|
|
|
188
249
|
return recordData;
|
|
189
250
|
}
|
|
190
251
|
|
|
191
|
-
setRecordId(type: string, id: string, lid
|
|
192
|
-
|
|
252
|
+
setRecordId(type: string | StableRecordIdentifier, id: string, lid?: string) {
|
|
253
|
+
let identifier: StableRecordIdentifier | undefined;
|
|
254
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
255
|
+
if (!isStableIdentifier(type)) {
|
|
256
|
+
const modelName = normalizeModelName(type);
|
|
257
|
+
const resource = constructResource(modelName, null, coerceId(lid));
|
|
258
|
+
identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
259
|
+
} else {
|
|
260
|
+
identifier = type;
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
assert(`Expected a stable identifier`, isStableIdentifier(type));
|
|
264
|
+
identifier = type;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
assert(`Unable to find an identifier to update the ID for for ${lid}`, identifier);
|
|
268
|
+
|
|
269
|
+
this._store._instanceCache.setRecordId(identifier, id);
|
|
193
270
|
}
|
|
194
271
|
|
|
195
272
|
isRecordInUse(type: string, id: string | null, lid: string): boolean;
|
|
196
273
|
isRecordInUse(type: string, id: string, lid?: string | null): boolean;
|
|
197
274
|
isRecordInUse(type: string, id: string | null, lid?: string | null): boolean {
|
|
275
|
+
if (DEPRECATE_V1CACHE_STORE_APIS) {
|
|
276
|
+
deprecate(`StoreWrapper.isRecordInUSe has been deprecated in favor of StoreWrapper.hasRecord`, false, {
|
|
277
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
278
|
+
for: 'ember-data',
|
|
279
|
+
until: '5.0',
|
|
280
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
281
|
+
});
|
|
282
|
+
}
|
|
198
283
|
const resource = constructResource(type, id, lid);
|
|
199
284
|
const identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
200
285
|
|
|
@@ -203,15 +288,134 @@ export default class RecordDataStoreWrapper implements StoreWrapper {
|
|
|
203
288
|
return record ? !(record.isDestroyed || record.isDestroying) : false;
|
|
204
289
|
}
|
|
205
290
|
|
|
291
|
+
hasRecord(identifier: StableRecordIdentifier): boolean {
|
|
292
|
+
return Boolean(this._store._instanceCache.peek({ identifier, bucket: 'record' }));
|
|
293
|
+
}
|
|
294
|
+
|
|
206
295
|
disconnectRecord(type: string, id: string | null, lid: string): void;
|
|
207
296
|
disconnectRecord(type: string, id: string, lid?: string | null): void;
|
|
208
|
-
disconnectRecord(type:
|
|
209
|
-
|
|
210
|
-
|
|
297
|
+
disconnectRecord(type: StableRecordIdentifier): void;
|
|
298
|
+
disconnectRecord(type: string | StableRecordIdentifier, id?: string | null, lid?: string | null): void {
|
|
299
|
+
let identifier: StableRecordIdentifier;
|
|
300
|
+
if (DEPRECATE_V1CACHE_STORE_APIS && typeof type === 'string') {
|
|
301
|
+
deprecate(
|
|
302
|
+
`StoreWrapper.disconnectRecord(<type>) has been deprecated in favor of StoreWrapper.disconnectRecord(<identifier>)`,
|
|
303
|
+
false,
|
|
304
|
+
{
|
|
305
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
306
|
+
for: 'ember-data',
|
|
307
|
+
until: '5.0',
|
|
308
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
let resource = constructResource(type, id, lid) as RecordIdentifier;
|
|
312
|
+
identifier = this.identifierCache.peekRecordIdentifier(resource)!;
|
|
313
|
+
} else {
|
|
314
|
+
identifier = type as StableRecordIdentifier;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
318
|
+
|
|
319
|
+
this._store._instanceCache.disconnect(identifier);
|
|
320
|
+
this._pendingNotifies.delete(identifier);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
class V2RecordDataStoreWrapper implements StoreWrapper {
|
|
325
|
+
declare _willNotify: boolean;
|
|
326
|
+
declare _pendingNotifies: Map<StableRecordIdentifier, Set<string>>;
|
|
327
|
+
declare _store: Store;
|
|
211
328
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
329
|
+
constructor(_store: Store) {
|
|
330
|
+
this._store = _store;
|
|
331
|
+
this._willNotify = false;
|
|
332
|
+
this._pendingNotifies = new Map();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
get identifierCache(): IdentifierCache {
|
|
336
|
+
return this._store.identifierCache;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
_scheduleNotification(identifier: StableRecordIdentifier, key: string) {
|
|
340
|
+
let pending = this._pendingNotifies.get(identifier);
|
|
341
|
+
|
|
342
|
+
if (!pending) {
|
|
343
|
+
pending = new Set();
|
|
344
|
+
this._pendingNotifies.set(identifier, pending);
|
|
345
|
+
}
|
|
346
|
+
pending.add(key);
|
|
347
|
+
|
|
348
|
+
if (this._willNotify === true) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this._willNotify = true;
|
|
353
|
+
// it's possible a RecordData adhoc notifies us,
|
|
354
|
+
// in which case we sync flush
|
|
355
|
+
if (this._store._cbs) {
|
|
356
|
+
this._store._schedule('notify', () => this._flushNotifications());
|
|
357
|
+
} else {
|
|
358
|
+
this._flushNotifications();
|
|
215
359
|
}
|
|
216
360
|
}
|
|
361
|
+
|
|
362
|
+
_flushNotifications(): void {
|
|
363
|
+
if (this._willNotify === false) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
let pending = this._pendingNotifies;
|
|
368
|
+
this._pendingNotifies = new Map();
|
|
369
|
+
this._willNotify = false;
|
|
370
|
+
|
|
371
|
+
pending.forEach((set, identifier) => {
|
|
372
|
+
set.forEach((key) => {
|
|
373
|
+
this._store._notificationManager.notify(identifier, 'relationships', key);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
notifyChange(identifier: StableRecordIdentifier, namespace: NotificationType, key?: string): void {
|
|
379
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
380
|
+
|
|
381
|
+
// TODO do we still get value from this?
|
|
382
|
+
if (namespace === 'relationships' && key) {
|
|
383
|
+
this._scheduleNotification(identifier, key);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
this._store._notificationManager.notify(identifier, namespace, key);
|
|
388
|
+
|
|
389
|
+
if (namespace === 'state') {
|
|
390
|
+
this._store.recordArrayManager.identifierChanged(identifier);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
getSchemaDefinitionService(): SchemaDefinitionService {
|
|
395
|
+
return this._store.getSchemaDefinitionService();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
recordDataFor(identifier: StableRecordIdentifier): RecordData {
|
|
399
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
400
|
+
|
|
401
|
+
return this._store._instanceCache.getRecordData(identifier);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
setRecordId(identifier: StableRecordIdentifier, id: string) {
|
|
405
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
406
|
+
this._store._instanceCache.setRecordId(identifier, id);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
hasRecord(identifier: StableRecordIdentifier): boolean {
|
|
410
|
+
return Boolean(this._store._instanceCache.peek({ identifier, bucket: 'record' }));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
disconnectRecord(identifier: StableRecordIdentifier): void {
|
|
414
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
415
|
+
this._store._instanceCache.disconnect(identifier);
|
|
416
|
+
this._pendingNotifies.delete(identifier);
|
|
417
|
+
}
|
|
217
418
|
}
|
|
419
|
+
export type RecordDataStoreWrapper = LegacyWrapper | V2RecordDataStoreWrapper;
|
|
420
|
+
|
|
421
|
+
export const RecordDataStoreWrapper = DEPRECATE_V1CACHE_STORE_APIS ? LegacyWrapper : V2RecordDataStoreWrapper;
|
|
@@ -6,67 +6,74 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
|
6
6
|
|
|
7
7
|
import { isStableIdentifier } from '../caches/identifier-cache';
|
|
8
8
|
import type Store from '../store-service';
|
|
9
|
-
import WeakCache from '../utils/weak-cache';
|
|
10
9
|
|
|
11
10
|
type UnsubscribeToken = object;
|
|
11
|
+
let tokenId = 0;
|
|
12
12
|
|
|
13
|
-
const Cache = new
|
|
14
|
-
|
|
15
|
-
);
|
|
16
|
-
Cache._generator = () => new Map();
|
|
17
|
-
const Tokens = new WeakCache<UnsubscribeToken, StableRecordIdentifier>(DEBUG ? 'identifier' : '');
|
|
13
|
+
const Cache = new Map<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>();
|
|
14
|
+
const Tokens = new Map<UnsubscribeToken, StableRecordIdentifier>();
|
|
18
15
|
|
|
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.
|
|
16
|
+
export type NotificationType = 'attributes' | 'relationships' | 'identity' | 'errors' | 'meta' | 'state';
|
|
28
17
|
|
|
29
18
|
export interface NotificationCallback {
|
|
30
|
-
(
|
|
31
|
-
|
|
32
|
-
notificationType: 'attributes' | 'relationships' | 'property',
|
|
33
|
-
key?: string
|
|
34
|
-
): void;
|
|
35
|
-
(identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): void;
|
|
19
|
+
(identifier: StableRecordIdentifier, notificationType: 'attributes' | 'relationships', key?: string): void;
|
|
20
|
+
(identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'state'): void;
|
|
36
21
|
(identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
|
|
37
22
|
}
|
|
38
23
|
|
|
24
|
+
// TODO this isn't importable anyway, remove and use a map on the manager?
|
|
39
25
|
export function unsubscribe(token: UnsubscribeToken) {
|
|
40
26
|
let identifier = Tokens.get(token);
|
|
41
|
-
if (!identifier) {
|
|
42
|
-
|
|
27
|
+
if (LOG_NOTIFICATIONS && !identifier) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log('Passed unknown unsubscribe token to unsubscribe', identifier);
|
|
30
|
+
}
|
|
31
|
+
if (identifier) {
|
|
32
|
+
Tokens.delete(token);
|
|
33
|
+
const map = Cache.get(identifier);
|
|
34
|
+
map?.delete(token);
|
|
43
35
|
}
|
|
44
|
-
Tokens.delete(token);
|
|
45
|
-
const map = Cache.get(identifier);
|
|
46
|
-
map?.delete(token);
|
|
47
36
|
}
|
|
48
37
|
/*
|
|
49
38
|
Currently only support a single callback per identifier
|
|
50
39
|
*/
|
|
51
40
|
export default class NotificationManager {
|
|
52
|
-
|
|
41
|
+
declare store: Store;
|
|
42
|
+
declare isDestroyed: boolean;
|
|
43
|
+
constructor(store: Store) {
|
|
44
|
+
this.store = store;
|
|
45
|
+
this.isDestroyed = false;
|
|
46
|
+
}
|
|
53
47
|
|
|
54
48
|
subscribe(identifier: StableRecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
|
|
55
49
|
assert(`Expected to receive a stable Identifier to subscribe to`, isStableIdentifier(identifier));
|
|
56
|
-
let map = Cache.
|
|
57
|
-
|
|
50
|
+
let map = Cache.get(identifier);
|
|
51
|
+
|
|
52
|
+
if (!map) {
|
|
53
|
+
map = new Map();
|
|
54
|
+
Cache.set(identifier, map);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let unsubToken = DEBUG ? { _tokenRef: tokenId++ } : {};
|
|
58
58
|
map.set(unsubToken, callback);
|
|
59
59
|
Tokens.set(unsubToken, identifier);
|
|
60
60
|
return unsubToken;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
unsubscribe(token: UnsubscribeToken) {
|
|
64
|
-
|
|
64
|
+
if (!this.isDestroyed) {
|
|
65
|
+
unsubscribe(token);
|
|
66
|
+
}
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
notify(identifier: StableRecordIdentifier, value: '
|
|
69
|
+
// deactivated type signature overloads because pass-through was failing to match any. Bring back if possible.
|
|
70
|
+
// notify(identifier: StableRecordIdentifier, value: 'attributes' | 'relationships', key?: string): boolean;
|
|
71
|
+
// notify(identifier: StableRecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'state'): boolean;
|
|
69
72
|
notify(identifier: StableRecordIdentifier, value: NotificationType, key?: string): boolean {
|
|
73
|
+
assert(
|
|
74
|
+
`Notify does not accept a key argument for the namespace '${value}'. Received key '${key}'.`,
|
|
75
|
+
!key || value === 'attributes' || value === 'relationships'
|
|
76
|
+
);
|
|
70
77
|
if (!isStableIdentifier(identifier)) {
|
|
71
78
|
if (LOG_NOTIFICATIONS) {
|
|
72
79
|
// eslint-disable-next-line no-console
|
|
@@ -93,4 +100,10 @@ export default class NotificationManager {
|
|
|
93
100
|
});
|
|
94
101
|
return true;
|
|
95
102
|
}
|
|
103
|
+
|
|
104
|
+
destroy() {
|
|
105
|
+
this.isDestroyed = true;
|
|
106
|
+
Tokens.clear();
|
|
107
|
+
Cache.clear();
|
|
108
|
+
}
|
|
96
109
|
}
|