@ember-data/store 4.6.1 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/-debug/index.js +35 -13
- package/addon/-private/{identifier-cache.ts → caches/identifier-cache.ts} +148 -73
- package/addon/-private/caches/instance-cache.ts +690 -0
- package/addon/-private/{record-data-for.ts → caches/record-data-for.ts} +2 -7
- package/addon/-private/index.ts +44 -24
- package/addon/-private/{model → legacy-model-support}/record-reference.ts +15 -13
- package/addon/-private/{schema-definition-service.ts → legacy-model-support/schema-definition-service.ts} +13 -9
- package/addon/-private/{model → legacy-model-support}/shim-model-class.ts +18 -11
- package/addon/-private/managers/record-array-manager.ts +377 -0
- package/addon/-private/managers/record-data-manager.ts +845 -0
- package/addon/-private/managers/record-data-store-wrapper.ts +421 -0
- package/addon/-private/managers/record-notification-manager.ts +109 -0
- package/addon/-private/network/fetch-manager.ts +567 -0
- package/addon/-private/{finders.js → network/finders.js} +14 -17
- package/addon/-private/{request-cache.ts → network/request-cache.ts} +21 -18
- package/addon/-private/{snapshot-record-array.ts → network/snapshot-record-array.ts} +14 -31
- package/addon/-private/{snapshot.ts → network/snapshot.ts} +40 -49
- package/addon/-private/{promise-proxies.ts → proxies/promise-proxies.ts} +76 -15
- package/addon/-private/{promise-proxy-base.js → proxies/promise-proxy-base.js} +0 -0
- package/addon/-private/record-arrays/identifier-array.ts +924 -0
- package/addon/-private/{core-store.ts → store-service.ts} +574 -215
- 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/utils/is-non-empty-string.ts +1 -1
- package/addon/-private/{normalize-model-name.ts → utils/normalize-model-name.ts} +1 -3
- package/addon/-private/utils/promise-record.ts +5 -6
- package/addon/-private/{serializer-response.ts → utils/serializer-response.ts} +2 -2
- package/addon/-private/utils/uuid-polyfill.ts +73 -0
- package/package.json +12 -8
- package/addon/-private/backburner.js +0 -25
- package/addon/-private/errors-utils.js +0 -146
- package/addon/-private/fetch-manager.ts +0 -597
- 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 -602
- package/addon/-private/record-array-manager.ts +0 -444
- package/addon/-private/record-arrays/adapter-populated-record-array.ts +0 -130
- package/addon/-private/record-arrays/record-array.ts +0 -318
- package/addon/-private/record-data-store-wrapper.ts +0 -243
- package/addon/-private/record-notification-manager.ts +0 -73
- package/addon/-private/weak-cache.ts +0 -125
|
@@ -0,0 +1,421 @@
|
|
|
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';
|
|
5
|
+
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
6
|
+
import type { AttributesSchema, RelationshipsSchema } from '@ember-data/types/q/record-data-schemas';
|
|
7
|
+
import type {
|
|
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';
|
|
12
|
+
|
|
13
|
+
import { IdentifierCache, isStableIdentifier } from '../caches/identifier-cache';
|
|
14
|
+
import type Store from '../store-service';
|
|
15
|
+
import coerceId from '../utils/coerce-id';
|
|
16
|
+
import constructResource from '../utils/construct-resource';
|
|
17
|
+
import normalizeModelName from '../utils/normalize-model-name';
|
|
18
|
+
import { NotificationType } from './record-notification-manager';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
@module @ember-data/store
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
25
|
+
declare _willNotify: boolean;
|
|
26
|
+
declare _pendingNotifies: Map<StableRecordIdentifier, Set<string>>;
|
|
27
|
+
declare _store: Store;
|
|
28
|
+
|
|
29
|
+
constructor(_store: Store) {
|
|
30
|
+
this._store = _store;
|
|
31
|
+
this._willNotify = false;
|
|
32
|
+
this._pendingNotifies = new Map();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get identifierCache(): IdentifierCache {
|
|
36
|
+
return this._store.identifierCache;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_scheduleNotification(identifier: StableRecordIdentifier, key: string) {
|
|
40
|
+
let pending = this._pendingNotifies.get(identifier);
|
|
41
|
+
|
|
42
|
+
if (!pending) {
|
|
43
|
+
pending = new Set();
|
|
44
|
+
this._pendingNotifies.set(identifier, pending);
|
|
45
|
+
}
|
|
46
|
+
pending.add(key);
|
|
47
|
+
|
|
48
|
+
if (this._willNotify === true) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this._willNotify = true;
|
|
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
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_flushNotifications(): void {
|
|
63
|
+
if (this._willNotify === false) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let pending = this._pendingNotifies;
|
|
68
|
+
this._pendingNotifies = new Map();
|
|
69
|
+
this._willNotify = false;
|
|
70
|
+
|
|
71
|
+
pending.forEach((set, identifier) => {
|
|
72
|
+
set.forEach((key) => {
|
|
73
|
+
this._store._notificationManager.notify(identifier, 'relationships', key);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
notifyChange(identifier: StableRecordIdentifier, namespace: NotificationType, key?: string): void {
|
|
79
|
+
assert(`Expected a stable identifier`, isStableIdentifier(identifier));
|
|
80
|
+
|
|
81
|
+
// TODO do we still get value from this?
|
|
82
|
+
if (namespace === 'relationships' && key) {
|
|
83
|
+
this._scheduleNotification(identifier, key);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this._store._notificationManager.notify(identifier, namespace, key);
|
|
88
|
+
|
|
89
|
+
if (namespace === 'state') {
|
|
90
|
+
this._store.recordArrayManager.identifierChanged(identifier);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
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
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const resource = constructResource(type, id, lid);
|
|
106
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
107
|
+
|
|
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
|
+
);
|
|
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
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return this._store.getSchemaDefinitionService().relationshipsDefinitionFor({ type });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getSchemaDefinitionService(): SchemaDefinitionService {
|
|
144
|
+
return this._store.getSchemaDefinitionService();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
notifyPropertyChange(type: string, id: string | null, lid: string, key?: string): void;
|
|
148
|
+
notifyPropertyChange(type: string, id: string, lid: string | null | undefined, key?: string): void;
|
|
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
|
+
}
|
|
158
|
+
const resource = constructResource(type, id, lid);
|
|
159
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
160
|
+
|
|
161
|
+
this._store._notificationManager.notify(identifier, 'attributes', key);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
notifyHasManyChange(type: string, id: string | null, lid: string, key: string): void;
|
|
165
|
+
notifyHasManyChange(type: string, id: string, lid: string | null | undefined, key: string): void;
|
|
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
|
+
}
|
|
175
|
+
const resource = constructResource(type, id, lid);
|
|
176
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
177
|
+
this._scheduleNotification(identifier, key);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
notifyBelongsToChange(type: string, id: string | null, lid: string, key: string): void;
|
|
181
|
+
notifyBelongsToChange(type: string, id: string, lid: string | null | undefined, key: string): void;
|
|
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
|
+
}
|
|
191
|
+
const resource = constructResource(type, id, lid);
|
|
192
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
193
|
+
|
|
194
|
+
this._scheduleNotification(identifier, key);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
notifyStateChange(type: string, id: string, lid: string | null, key?: string): void;
|
|
198
|
+
notifyStateChange(type: string, id: string | null, lid: string, key?: string): void;
|
|
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
|
+
}
|
|
208
|
+
const resource = constructResource(type, id, lid);
|
|
209
|
+
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
210
|
+
|
|
211
|
+
this._store._notificationManager.notify(identifier, 'state');
|
|
212
|
+
this._store.recordArrayManager.identifierChanged(identifier);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
recordDataFor(type: string, id: string, lid?: string | null): RecordData;
|
|
216
|
+
recordDataFor(type: string, id: string | null, lid: string): RecordData;
|
|
217
|
+
recordDataFor(type: string): RecordData;
|
|
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
|
+
}
|
|
241
|
+
|
|
242
|
+
const recordData = this._store._instanceCache.getRecordData(identifier);
|
|
243
|
+
|
|
244
|
+
if (!id && !lid) {
|
|
245
|
+
recordData.clientDidCreate(identifier);
|
|
246
|
+
this._store.recordArrayManager.identifierAdded(identifier);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return recordData;
|
|
250
|
+
}
|
|
251
|
+
|
|
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);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
isRecordInUse(type: string, id: string | null, lid: string): boolean;
|
|
273
|
+
isRecordInUse(type: string, id: string, lid?: string | null): boolean;
|
|
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
|
+
}
|
|
283
|
+
const resource = constructResource(type, id, lid);
|
|
284
|
+
const identifier = this.identifierCache.peekRecordIdentifier(resource);
|
|
285
|
+
|
|
286
|
+
const record = identifier && this._store._instanceCache.peek({ identifier, bucket: 'record' });
|
|
287
|
+
|
|
288
|
+
return record ? !(record.isDestroyed || record.isDestroying) : false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
hasRecord(identifier: StableRecordIdentifier): boolean {
|
|
292
|
+
return Boolean(this._store._instanceCache.peek({ identifier, bucket: 'record' }));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
disconnectRecord(type: string, id: string | null, lid: string): void;
|
|
296
|
+
disconnectRecord(type: string, id: string, lid?: string | null): void;
|
|
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;
|
|
328
|
+
|
|
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();
|
|
359
|
+
}
|
|
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
|
+
}
|
|
418
|
+
}
|
|
419
|
+
export type RecordDataStoreWrapper = LegacyWrapper | V2RecordDataStoreWrapper;
|
|
420
|
+
|
|
421
|
+
export const RecordDataStoreWrapper = DEPRECATE_V1CACHE_STORE_APIS ? LegacyWrapper : V2RecordDataStoreWrapper;
|
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
|
|
10
|
+
type UnsubscribeToken = object;
|
|
11
|
+
let tokenId = 0;
|
|
12
|
+
|
|
13
|
+
const Cache = new Map<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>();
|
|
14
|
+
const Tokens = new Map<UnsubscribeToken, StableRecordIdentifier>();
|
|
15
|
+
|
|
16
|
+
export type NotificationType = 'attributes' | 'relationships' | 'identity' | 'errors' | 'meta' | 'state';
|
|
17
|
+
|
|
18
|
+
export interface NotificationCallback {
|
|
19
|
+
(identifier: StableRecordIdentifier, notificationType: 'attributes' | 'relationships', key?: string): void;
|
|
20
|
+
(identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'state'): void;
|
|
21
|
+
(identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// TODO this isn't importable anyway, remove and use a map on the manager?
|
|
25
|
+
export function unsubscribe(token: UnsubscribeToken) {
|
|
26
|
+
let identifier = Tokens.get(token);
|
|
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);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/*
|
|
38
|
+
Currently only support a single callback per identifier
|
|
39
|
+
*/
|
|
40
|
+
export default class NotificationManager {
|
|
41
|
+
declare store: Store;
|
|
42
|
+
declare isDestroyed: boolean;
|
|
43
|
+
constructor(store: Store) {
|
|
44
|
+
this.store = store;
|
|
45
|
+
this.isDestroyed = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
subscribe(identifier: StableRecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
|
|
49
|
+
assert(`Expected to receive a stable Identifier to subscribe to`, isStableIdentifier(identifier));
|
|
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
|
+
map.set(unsubToken, callback);
|
|
59
|
+
Tokens.set(unsubToken, identifier);
|
|
60
|
+
return unsubToken;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
unsubscribe(token: UnsubscribeToken) {
|
|
64
|
+
if (!this.isDestroyed) {
|
|
65
|
+
unsubscribe(token);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
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;
|
|
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
|
+
);
|
|
77
|
+
if (!isStableIdentifier(identifier)) {
|
|
78
|
+
if (LOG_NOTIFICATIONS) {
|
|
79
|
+
// eslint-disable-next-line no-console
|
|
80
|
+
console.log(
|
|
81
|
+
`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key}' with, but ${String(
|
|
82
|
+
identifier
|
|
83
|
+
)} is not in the cache`,
|
|
84
|
+
identifier
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (LOG_NOTIFICATIONS) {
|
|
91
|
+
// eslint-disable-next-line no-console
|
|
92
|
+
console.log(`Notifying: ${String(identifier)}\t${value}\t${key}`);
|
|
93
|
+
}
|
|
94
|
+
let callbackMap = Cache.get(identifier);
|
|
95
|
+
if (!callbackMap || !callbackMap.size) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
callbackMap.forEach((cb) => {
|
|
99
|
+
cb(identifier, value, key);
|
|
100
|
+
});
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
destroy() {
|
|
105
|
+
this.isDestroyed = true;
|
|
106
|
+
Tokens.clear();
|
|
107
|
+
Cache.clear();
|
|
108
|
+
}
|
|
109
|
+
}
|