@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.
Files changed (45) hide show
  1. package/addon/-debug/index.js +35 -13
  2. package/addon/-private/{identifier-cache.ts → caches/identifier-cache.ts} +148 -73
  3. package/addon/-private/caches/instance-cache.ts +690 -0
  4. package/addon/-private/{record-data-for.ts → caches/record-data-for.ts} +2 -7
  5. package/addon/-private/index.ts +44 -24
  6. package/addon/-private/{model → legacy-model-support}/record-reference.ts +15 -13
  7. package/addon/-private/{schema-definition-service.ts → legacy-model-support/schema-definition-service.ts} +13 -9
  8. package/addon/-private/{model → legacy-model-support}/shim-model-class.ts +18 -11
  9. package/addon/-private/managers/record-array-manager.ts +377 -0
  10. package/addon/-private/managers/record-data-manager.ts +845 -0
  11. package/addon/-private/managers/record-data-store-wrapper.ts +421 -0
  12. package/addon/-private/managers/record-notification-manager.ts +109 -0
  13. package/addon/-private/network/fetch-manager.ts +567 -0
  14. package/addon/-private/{finders.js → network/finders.js} +14 -17
  15. package/addon/-private/{request-cache.ts → network/request-cache.ts} +21 -18
  16. package/addon/-private/{snapshot-record-array.ts → network/snapshot-record-array.ts} +14 -31
  17. package/addon/-private/{snapshot.ts → network/snapshot.ts} +40 -49
  18. package/addon/-private/{promise-proxies.ts → proxies/promise-proxies.ts} +76 -15
  19. package/addon/-private/{promise-proxy-base.js → proxies/promise-proxy-base.js} +0 -0
  20. package/addon/-private/record-arrays/identifier-array.ts +924 -0
  21. package/addon/-private/{core-store.ts → store-service.ts} +574 -215
  22. package/addon/-private/{coerce-id.ts → utils/coerce-id.ts} +1 -1
  23. package/addon/-private/{common.js → utils/common.js} +1 -2
  24. package/addon/-private/utils/construct-resource.ts +2 -2
  25. package/addon/-private/{identifer-debug-consts.ts → utils/identifer-debug-consts.ts} +0 -0
  26. package/addon/-private/utils/is-non-empty-string.ts +1 -1
  27. package/addon/-private/{normalize-model-name.ts → utils/normalize-model-name.ts} +1 -3
  28. package/addon/-private/utils/promise-record.ts +5 -6
  29. package/addon/-private/{serializer-response.ts → utils/serializer-response.ts} +2 -2
  30. package/addon/-private/utils/uuid-polyfill.ts +73 -0
  31. package/package.json +12 -8
  32. package/addon/-private/backburner.js +0 -25
  33. package/addon/-private/errors-utils.js +0 -146
  34. package/addon/-private/fetch-manager.ts +0 -597
  35. package/addon/-private/identity-map.ts +0 -54
  36. package/addon/-private/instance-cache.ts +0 -387
  37. package/addon/-private/internal-model-factory.ts +0 -359
  38. package/addon/-private/internal-model-map.ts +0 -121
  39. package/addon/-private/model/internal-model.ts +0 -602
  40. package/addon/-private/record-array-manager.ts +0 -444
  41. package/addon/-private/record-arrays/adapter-populated-record-array.ts +0 -130
  42. package/addon/-private/record-arrays/record-array.ts +0 -318
  43. package/addon/-private/record-data-store-wrapper.ts +0 -243
  44. package/addon/-private/record-notification-manager.ts +0 -73
  45. 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
+ }