@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
@@ -1,318 +0,0 @@
1
- /**
2
- @module @ember-data/store
3
- */
4
- import type NativeArray from '@ember/array/-private/native-array';
5
- import ArrayProxy from '@ember/array/proxy';
6
- import { assert, deprecate } from '@ember/debug';
7
- import { get, set } from '@ember/object';
8
- import { tracked } from '@glimmer/tracking';
9
-
10
- import { Promise } from 'rsvp';
11
-
12
- import type { RecordArrayManager, Snapshot } from 'ember-data/-private';
13
-
14
- import { DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS } from '@ember-data/private-build-infra/deprecations';
15
- import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
16
- import type { RecordInstance } from '@ember-data/types/q/record-instance';
17
- import type { FindOptions } from '@ember-data/types/q/store';
18
-
19
- import type Store from '../core-store';
20
- import type { PromiseArray } from '../promise-proxies';
21
- import { promiseArray } from '../promise-proxies';
22
- import SnapshotRecordArray from '../snapshot-record-array';
23
-
24
- function recordForIdentifier(store: Store, identifier: StableRecordIdentifier): RecordInstance {
25
- return store._instanceCache.getRecord(identifier);
26
- }
27
-
28
- export interface RecordArrayCreateArgs {
29
- modelName: string;
30
- store: Store;
31
- manager: RecordArrayManager;
32
- content: NativeArray<StableRecordIdentifier>;
33
- isLoaded: boolean;
34
- }
35
-
36
- /**
37
- A record array is an array that contains records of a certain modelName. The record
38
- array materializes records as needed when they are retrieved for the first
39
- time. You should not create record arrays yourself. Instead, an instance of
40
- `RecordArray` or its subclasses will be returned by your application's store
41
- in response to queries.
42
-
43
- This class should not be imported and instantiated by consuming applications.
44
-
45
- @class RecordArray
46
- @public
47
- @extends Ember.ArrayProxy
48
- */
49
- export default class RecordArray extends ArrayProxy<StableRecordIdentifier, RecordInstance> {
50
- /**
51
- The array of client ids backing the record array. When a
52
- record is requested from the record array, the record
53
- for the client id at the same index is materialized, if
54
- necessary, by the store.
55
-
56
- @property content
57
- @private
58
- @type Ember.Array
59
- */
60
- declare content: NativeArray<StableRecordIdentifier>;
61
- declare _getDeprecatedEventedInfo: () => string;
62
- declare modelName: string;
63
- /**
64
- The flag to signal a `RecordArray` is finished loading data.
65
-
66
- Example
67
-
68
- ```javascript
69
- let people = store.peekAll('person');
70
- people.get('isLoaded'); // true
71
- ```
72
-
73
- @property isLoaded
74
- @public
75
- @type Boolean
76
- */
77
- declare isLoaded: boolean;
78
- /**
79
- The store that created this record array.
80
-
81
- @property store
82
- @private
83
- @type Store
84
- */
85
- declare store: Store;
86
- declare _updatingPromise: PromiseArray<RecordInstance, RecordArray> | null;
87
- declare manager: RecordArrayManager;
88
-
89
- /**
90
- The flag to signal a `RecordArray` is currently loading data.
91
- Example
92
- ```javascript
93
- let people = store.peekAll('person');
94
- people.get('isUpdating'); // false
95
- people.update();
96
- people.get('isUpdating'); // true
97
- ```
98
- @property isUpdating
99
- @public
100
- @type Boolean
101
- */
102
- @tracked isUpdating: boolean = false;
103
-
104
- init(props?: RecordArrayCreateArgs) {
105
- assert(`Cannot initialize RecordArray with isUpdating`, !props || !('isUpdating' in props));
106
- assert(`Cannot initialize RecordArray with isUpdating`, !props || !('_updatingPromise' in props));
107
- super.init();
108
-
109
- // TODO can we get rid of this?
110
- this.set('content', this.content || null);
111
- this._updatingPromise = null;
112
- }
113
-
114
- replace() {
115
- throw new Error(
116
- `The result of a server query (for all ${this.modelName} types) is immutable. To modify contents, use toArray()`
117
- );
118
- }
119
-
120
- /**
121
- The modelClass represented by this record array.
122
-
123
- @property type
124
- @public
125
- @deprecated
126
- @type {subclass of Model}
127
- */
128
-
129
- /**
130
- Retrieves an object from the content by index.
131
-
132
- @method objectAtContent
133
- @private
134
- @param {Number} index
135
- @return {Model} record
136
- */
137
- objectAtContent(index: number): RecordInstance | undefined {
138
- let identifier = get(this, 'content').objectAt(index);
139
- return identifier ? recordForIdentifier(this.store, identifier) : undefined;
140
- }
141
-
142
- /**
143
- Used to get the latest version of all of the records in this array
144
- from the adapter.
145
-
146
- Example
147
-
148
- ```javascript
149
- let people = store.peekAll('person');
150
- people.get('isUpdating'); // false
151
-
152
- people.update().then(function() {
153
- people.get('isUpdating'); // false
154
- });
155
-
156
- people.get('isUpdating'); // true
157
- ```
158
-
159
- @method update
160
- @public
161
- */
162
- update(): PromiseArray<RecordInstance, RecordArray> {
163
- if (this.isUpdating) {
164
- return this._updatingPromise!;
165
- }
166
-
167
- this.isUpdating = true;
168
-
169
- let updatingPromise = this._update();
170
- updatingPromise.finally(() => {
171
- this._updatingPromise = null;
172
- if (this.isDestroying || this.isDestroyed) {
173
- return;
174
- }
175
- this.isUpdating = false;
176
- });
177
-
178
- this._updatingPromise = updatingPromise;
179
-
180
- return updatingPromise;
181
- }
182
-
183
- /*
184
- Update this RecordArray and return a promise which resolves once the update
185
- is finished.
186
- */
187
- _update(): PromiseArray<RecordInstance, RecordArray> {
188
- return this.store.findAll(this.modelName, { reload: true });
189
- }
190
-
191
- /**
192
- Saves all of the records in the `RecordArray`.
193
-
194
- Example
195
-
196
- ```javascript
197
- let messages = store.peekAll('message');
198
- messages.forEach(function(message) {
199
- message.set('hasBeenSeen', true);
200
- });
201
- messages.save();
202
- ```
203
-
204
- @method save
205
- @public
206
- @return {PromiseArray} promise
207
- */
208
- save(): PromiseArray<RecordInstance, RecordArray> {
209
- let promiseLabel = `DS: RecordArray#save ${this.modelName}`;
210
- let promise = Promise.all(this.invoke('save'), promiseLabel).then(
211
- () => this,
212
- null,
213
- 'DS: RecordArray#save return RecordArray'
214
- );
215
-
216
- return promiseArray<RecordInstance, RecordArray>(promise);
217
- }
218
-
219
- /**
220
- @method _unregisterFromManager
221
- @internal
222
- */
223
- _unregisterFromManager() {
224
- this.manager.unregisterRecordArray(this);
225
- }
226
-
227
- willDestroy() {
228
- this._unregisterFromManager();
229
- this._dissociateFromOwnRecords();
230
- // TODO: we should not do work during destroy:
231
- // * when objects are destroyed, they should simply be left to do
232
- // * if logic errors do to this, that logic needs to be more careful during
233
- // teardown (ember provides isDestroying/isDestroyed) for this reason
234
- // * the exception being: if an dominator has a reference to this object,
235
- // and must be informed to release e.g. e.g. removing itself from th
236
- // recordArrayMananger
237
- set(this, 'content', null as unknown as NativeArray<StableRecordIdentifier>);
238
- set(this, 'length', 0);
239
- super.willDestroy();
240
- }
241
-
242
- /**
243
- @method _createSnapshot
244
- @private
245
- */
246
- _createSnapshot(options: FindOptions) {
247
- // this is private for users, but public for ember-data internals
248
- // meta will only be present for an AdapterPopulatedRecordArray
249
- return new SnapshotRecordArray(this, null, options);
250
- }
251
-
252
- /**
253
- @method _dissociateFromOwnRecords
254
- @internal
255
- */
256
- _dissociateFromOwnRecords() {
257
- this.content.forEach((identifier) => {
258
- let recordArrays = this.manager.getRecordArraysForIdentifier(identifier);
259
-
260
- if (recordArrays) {
261
- recordArrays.delete(this);
262
- }
263
- });
264
- }
265
-
266
- /**
267
- Adds identifiers to the `RecordArray` without duplicates
268
-
269
- @method _pushIdentifiers
270
- @internal
271
- @param {StableRecordIdentifier[]} identifiers
272
- */
273
- _pushIdentifiers(identifiers: StableRecordIdentifier[]): void {
274
- this.content.pushObjects(identifiers);
275
- }
276
-
277
- /**
278
- Removes identifiers from the `RecordArray`.
279
-
280
- @method _removeIdentifiers
281
- @internal
282
- @param {StableRecordIdentifier[]} identifiers
283
- */
284
- _removeIdentifiers(identifiers: StableRecordIdentifier[]): void {
285
- this.content.removeObjects(identifiers);
286
- }
287
-
288
- /**
289
- @method _takeSnapshot
290
- @internal
291
- */
292
- _takeSnapshot(): Snapshot[] {
293
- return this.content.map((identifier) => this.store._instanceCache.createSnapshot(identifier));
294
- }
295
- }
296
-
297
- if (DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS) {
298
- Object.defineProperty(RecordArray.prototype, 'type', {
299
- get() {
300
- deprecate(
301
- `Using RecordArray.type to access the ModelClass for a record is deprecated. Use store.modelFor(<modelName>) instead.`,
302
- false,
303
- {
304
- id: 'ember-data:deprecate-snapshot-model-class-access',
305
- until: '5.0',
306
- for: 'ember-data',
307
- since: { available: '4.5.0', enabled: '4.5.0' },
308
- }
309
- );
310
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
311
- if (!this.modelName) {
312
- return null;
313
- }
314
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
315
- return this.store.modelFor(this.modelName);
316
- },
317
- });
318
- }
@@ -1,243 +0,0 @@
1
- import { importSync } from '@embroider/macros';
2
-
3
- import type { RelationshipDefinition } from '@ember-data/model/-private/relationship-meta';
4
- import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
5
- import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
6
- import type { RecordData } from '@ember-data/types/q/record-data';
7
- import type {
8
- AttributesSchema,
9
- RelationshipSchema,
10
- RelationshipsSchema,
11
- } from '@ember-data/types/q/record-data-schemas';
12
- import type { RecordDataStoreWrapper as StoreWrapper } from '@ember-data/types/q/record-data-store-wrapper';
13
-
14
- import type Store from './core-store';
15
- import type { IdentifierCache } from './identifier-cache';
16
- import { internalModelFactoryFor } from './internal-model-factory';
17
- import constructResource from './utils/construct-resource';
18
-
19
- /**
20
- @module @ember-data/store
21
- */
22
-
23
- function metaIsRelationshipDefinition(meta: RelationshipSchema): meta is RelationshipDefinition {
24
- return typeof (meta as RelationshipDefinition)._inverseKey === 'function';
25
- }
26
-
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
- export default class RecordDataStoreWrapper implements StoreWrapper {
39
- declare _willNotify: boolean;
40
- declare _pendingNotifies: Map<StableRecordIdentifier, Map<string, string>>;
41
- declare _store: Store;
42
-
43
- constructor(_store: Store) {
44
- this._store = _store;
45
- this._willNotify = false;
46
- this._pendingNotifies = new Map();
47
- }
48
-
49
- get identifierCache(): IdentifierCache {
50
- return this._store.identifierCache;
51
- }
52
-
53
- _scheduleNotification(identifier: StableRecordIdentifier, key: string, kind: 'belongsTo' | 'hasMany') {
54
- let pending = this._pendingNotifies.get(identifier);
55
-
56
- if (!pending) {
57
- pending = new Map();
58
- this._pendingNotifies.set(identifier, pending);
59
- }
60
- pending.set(key, kind);
61
-
62
- if (this._willNotify === true) {
63
- return;
64
- }
65
-
66
- this._willNotify = true;
67
- let backburner: any = this._store._backburner;
68
-
69
- backburner.schedule('notify', this, this._flushNotifications);
70
- }
71
-
72
- notifyErrorsChange(type: string, id: string, lid: string | null): void;
73
- notifyErrorsChange(type: string, id: string | null, lid: string): void;
74
- notifyErrorsChange(type: string, id: string | null, lid: string | null): void {
75
- const resource = constructResource(type, id, lid);
76
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
77
-
78
- let internalModel = internalModelFactoryFor(this._store).peek(identifier);
79
-
80
- if (internalModel) {
81
- internalModel.notifyErrorsChange();
82
- }
83
- }
84
-
85
- _flushNotifications(): void {
86
- if (this._willNotify === false) {
87
- return;
88
- }
89
-
90
- let pending = this._pendingNotifies;
91
- this._pendingNotifies = new Map();
92
- this._willNotify = false;
93
- const factory = internalModelFactoryFor(this._store);
94
-
95
- pending.forEach((map, identifier) => {
96
- const internalModel = factory.peek(identifier);
97
- if (internalModel) {
98
- map.forEach((kind, key) => {
99
- if (kind === 'belongsTo') {
100
- internalModel.notifyBelongsToChange(key);
101
- } else {
102
- internalModel.notifyHasManyChange(key);
103
- }
104
- });
105
- }
106
- });
107
- }
108
-
109
- attributesDefinitionFor(type: string): AttributesSchema {
110
- return this._store.getSchemaDefinitionService().attributesDefinitionFor({ type });
111
- }
112
-
113
- relationshipsDefinitionFor(type: string): RelationshipsSchema {
114
- return this._store.getSchemaDefinitionService().relationshipsDefinitionFor({ type });
115
- }
116
-
117
- inverseForRelationship(type: string, key: string): string | null {
118
- const modelClass = this._store.modelFor(type);
119
- const definition = this.relationshipsDefinitionFor(type)[key];
120
- if (!definition) {
121
- return null;
122
- }
123
-
124
- if (metaIsRelationshipDefinition(definition)) {
125
- return definition._inverseKey(this._store, modelClass);
126
- } else if (definition.options && definition.options.inverse !== undefined) {
127
- return definition.options.inverse;
128
- } else {
129
- return null;
130
- }
131
- }
132
-
133
- inverseIsAsyncForRelationship(type: string, key: string): boolean {
134
- const modelClass = this._store.modelFor(type);
135
- const definition = this.relationshipsDefinitionFor(type)[key];
136
- if (!definition) {
137
- return false;
138
- }
139
-
140
- if (definition.options && definition.options.inverse === null) {
141
- return false;
142
- }
143
- if ((definition as unknown as { inverseIsAsync?: boolean }).inverseIsAsync !== undefined) {
144
- // TODO do we need to amend the RFC for this prop?
145
- // else we should add it to the TS interface and document.
146
- return !!(definition as unknown as { inverseIsAsync: boolean }).inverseIsAsync;
147
- } else if (metaIsRelationshipDefinition(definition)) {
148
- return definition._inverseIsAsync(this._store, modelClass);
149
- } else {
150
- return false;
151
- }
152
- }
153
-
154
- notifyPropertyChange(type: string, id: string | null, lid: string, key?: string): void;
155
- notifyPropertyChange(type: string, id: string, lid: string | null | undefined, key?: string): void;
156
- notifyPropertyChange(type: string, id: string | null, lid: string | null | undefined, key?: string): void {
157
- const resource = constructResource(type, id, lid);
158
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
159
- let internalModel = internalModelFactoryFor(this._store).peek(identifier);
160
-
161
- if (internalModel) {
162
- internalModel.notifyAttributes(key ? [key] : []);
163
- }
164
- }
165
-
166
- notifyHasManyChange(type: string, id: string | null, lid: string, key: string): void;
167
- notifyHasManyChange(type: string, id: string, lid: string | null | undefined, key: string): void;
168
- notifyHasManyChange(type: string, id: string | null, lid: string | null | undefined, key: string): void {
169
- const resource = constructResource(type, id, lid);
170
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
171
- this._scheduleNotification(identifier, key, 'hasMany');
172
- }
173
-
174
- notifyBelongsToChange(type: string, id: string | null, lid: string, key: string): void;
175
- notifyBelongsToChange(type: string, id: string, lid: string | null | undefined, key: string): void;
176
- notifyBelongsToChange(type: string, id: string | null, lid: string | null | undefined, key: string): void {
177
- const resource = constructResource(type, id, lid);
178
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
179
-
180
- this._scheduleNotification(identifier, key, 'belongsTo');
181
- }
182
-
183
- notifyStateChange(type: string, id: string, lid: string | null, key?: string): void;
184
- notifyStateChange(type: string, id: string | null, lid: string, key?: string): void;
185
- notifyStateChange(type: string, id: string | null, lid: string | null, key?: string): void {
186
- const resource = constructResource(type, id, lid);
187
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
188
- let internalModel = internalModelFactoryFor(this._store).peek(identifier);
189
-
190
- if (internalModel) {
191
- internalModel.notifyStateChange(key);
192
- }
193
- }
194
-
195
- recordDataFor(type: string, id: string, lid?: string | null): RecordData;
196
- recordDataFor(type: string, id: string | null, lid: string): RecordData;
197
- recordDataFor(type: string): RecordData;
198
- recordDataFor(type: string, id?: string | null, lid?: string | null): RecordData {
199
- let identifier: StableRecordIdentifier | { type: string };
200
- let isCreate: boolean = false;
201
- if (!id && !lid) {
202
- isCreate = true;
203
- identifier = { type };
204
- } else {
205
- const resource = constructResource(type, id, lid);
206
- identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
207
- }
208
-
209
- return this._store._instanceCache.recordDataFor(identifier, isCreate);
210
- }
211
-
212
- setRecordId(type: string, id: string, lid: string) {
213
- this._store._instanceCache.setRecordId(type, id, lid);
214
- }
215
-
216
- isRecordInUse(type: string, id: string | null, lid: string): boolean;
217
- isRecordInUse(type: string, id: string, lid?: string | null): boolean;
218
- isRecordInUse(type: string, id: string | null, lid?: string | null): boolean {
219
- const resource = constructResource(type, id, lid);
220
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
221
-
222
- const record = this._store._instanceCache.peek({ identifier, bucket: 'record' });
223
-
224
- return record ? !(record.isDestroyed || record.isDestroying) : false;
225
- }
226
-
227
- disconnectRecord(type: string, id: string | null, lid: string): void;
228
- disconnectRecord(type: string, id: string, lid?: string | null): void;
229
- disconnectRecord(type: string, id: string | null, lid?: string | null): void {
230
- const resource = constructResource(type, id, lid);
231
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
232
- if (HAS_RECORD_DATA_PACKAGE) {
233
- let graph = peekGraph(this);
234
- if (graph) {
235
- graph.remove(identifier);
236
- }
237
- }
238
- let internalModel = internalModelFactoryFor(this._store).peek(identifier);
239
- if (internalModel) {
240
- internalModel.destroyFromRecordData();
241
- }
242
- }
243
- }
@@ -1,73 +0,0 @@
1
- import { DEBUG } from '@glimmer/env';
2
-
3
- import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier';
4
-
5
- import type Store from './core-store';
6
- import WeakCache from './weak-cache';
7
-
8
- type UnsubscribeToken = Object;
9
-
10
- const Cache = new WeakCache<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>(
11
- DEBUG ? 'subscribers' : ''
12
- );
13
- Cache._generator = () => new Map();
14
- const Tokens = new WeakCache<UnsubscribeToken, StableRecordIdentifier>(DEBUG ? 'identifier' : '');
15
-
16
- export type NotificationType =
17
- | 'attributes'
18
- | 'relationships'
19
- | 'identity'
20
- | 'errors'
21
- | 'meta'
22
- | 'unload'
23
- | 'state'
24
- | 'property'; // 'property' is an internal EmberData only transition period concept.
25
-
26
- export interface NotificationCallback {
27
- (identifier: RecordIdentifier, notificationType: 'attributes' | 'relationships' | 'property', key?: string): void;
28
- (identifier: RecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): void;
29
- (identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
30
- }
31
-
32
- export function unsubscribe(token: UnsubscribeToken) {
33
- let identifier = Tokens.get(token);
34
- if (!identifier) {
35
- throw new Error('Passed unknown unsubscribe token to unsubscribe');
36
- }
37
- Tokens.delete(token);
38
- const map = Cache.get(identifier);
39
- map?.delete(token);
40
- }
41
- /*
42
- Currently only support a single callback per identifier
43
- */
44
- export default class NotificationManager {
45
- constructor(private store: Store) {}
46
-
47
- subscribe(identifier: RecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
48
- let stableIdentifier = this.store.identifierCache.getOrCreateRecordIdentifier(identifier);
49
- let map = Cache.lookup(stableIdentifier);
50
- let unsubToken = {};
51
- map.set(unsubToken, callback);
52
- Tokens.set(unsubToken, stableIdentifier);
53
- return unsubToken;
54
- }
55
-
56
- unsubscribe(token: UnsubscribeToken) {
57
- unsubscribe(token);
58
- }
59
-
60
- notify(identifier: RecordIdentifier, value: 'attributes' | 'relationships' | 'property', key?: string): boolean;
61
- notify(identifier: RecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): boolean;
62
- notify(identifier: RecordIdentifier, value: NotificationType, key?: string): boolean {
63
- let stableIdentifier = this.store.identifierCache.getOrCreateRecordIdentifier(identifier);
64
- let callbackMap = Cache.get(stableIdentifier);
65
- if (!callbackMap || !callbackMap.size) {
66
- return false;
67
- }
68
- callbackMap.forEach((cb) => {
69
- cb(stableIdentifier, value, key);
70
- });
71
- return true;
72
- }
73
- }