@ember-data/store 3.28.10 → 3.28.11

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.
@@ -2500,6 +2500,7 @@ abstract class CoreStore extends Service {
2500
2500
  const factory = internalModelFactoryFor(this);
2501
2501
 
2502
2502
  if (modelName === undefined) {
2503
+ this._notificationManager.destroy();
2503
2504
  factory.clear();
2504
2505
  } else {
2505
2506
  let normalizedModelName = normalizeModelName(modelName);
@@ -1,13 +1,16 @@
1
- import { identifierCacheFor } from '../identifiers/cache';
1
+ import { assert } from '@ember/debug';
2
+ import { DEBUG } from '@glimmer/env';
3
+
4
+ import isStableIdentifier from '../identifiers/is-stable-identifier';
2
5
 
3
6
  type CoreStore = import('./core-store').default;
4
- type RecordIdentifier = import('../ts-interfaces/identifier').RecordIdentifier;
5
7
  type StableRecordIdentifier = import('../ts-interfaces/identifier').StableRecordIdentifier;
6
8
 
7
- type UnsubscribeToken = Object;
9
+ type UnsubscribeToken = object;
10
+ let tokenId = 0;
8
11
 
9
- const Cache = new WeakMap<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>();
10
- const Tokens = new WeakMap<UnsubscribeToken, StableRecordIdentifier>();
12
+ const Cache = new Map<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>();
13
+ const Tokens = new Map<UnsubscribeToken, StableRecordIdentifier>();
11
14
 
12
15
  export type NotificationType =
13
16
  | 'attributes'
@@ -20,50 +23,73 @@ export type NotificationType =
20
23
  | 'property'; // 'property' is an internal EmberData only transition period concept.
21
24
 
22
25
  export interface NotificationCallback {
23
- (identifier: RecordIdentifier, notificationType: 'attributes' | 'relationships' | 'property', key?: string): void;
24
- (identifier: RecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): void;
26
+ (identifier: StableRecordIdentifier, notificationType: 'attributes' | 'relationships', key?: string): void;
27
+ (identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'state'): void;
25
28
  (identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
26
29
  }
27
30
 
31
+ // TODO this isn't importable anyway, remove and use a map on the manager?
28
32
  export function unsubscribe(token: UnsubscribeToken) {
29
33
  let identifier = Tokens.get(token);
30
- if (!identifier) {
31
- throw new Error('Passed unknown unsubscribe token to unsubscribe');
34
+
35
+ if (identifier) {
36
+ Tokens.delete(token);
37
+ const map = Cache.get(identifier);
38
+ map?.delete(token);
32
39
  }
33
- Tokens.delete(token);
34
- const map = Cache.get(identifier);
35
- map?.delete(token);
36
40
  }
37
41
  /*
38
42
  Currently only support a single callback per identifier
39
43
  */
40
44
  export default class NotificationManager {
41
- constructor(private store: CoreStore) {}
45
+ declare store: CoreStore;
46
+ constructor(store: CoreStore) {
47
+ this.store = store;
48
+ }
42
49
 
43
- subscribe(identifier: RecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
44
- let stableIdentifier = identifierCacheFor(this.store).getOrCreateRecordIdentifier(identifier);
45
- let map = Cache.get(stableIdentifier);
46
- if (map === undefined) {
50
+ subscribe(identifier: StableRecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
51
+ assert(`Expected to receive a stable Identifier to subscribe to`, isStableIdentifier(identifier));
52
+ let map = Cache.get(identifier);
53
+
54
+ if (!map) {
47
55
  map = new Map();
48
- Cache.set(stableIdentifier, map);
56
+ Cache.set(identifier, map);
49
57
  }
50
- let unsubToken = {};
58
+
59
+ let unsubToken = DEBUG ? { _tokenRef: tokenId++ } : {};
51
60
  map.set(unsubToken, callback);
52
- Tokens.set(unsubToken, stableIdentifier);
61
+ Tokens.set(unsubToken, identifier);
53
62
  return unsubToken;
54
63
  }
55
64
 
56
- notify(identifier: RecordIdentifier, value: 'attributes' | 'relationships' | 'property', key?: string): boolean;
57
- notify(identifier: RecordIdentifier, value: 'errors' | 'meta' | 'identity' | 'unload' | 'state'): boolean;
58
- notify(identifier: RecordIdentifier, value: NotificationType, key?: string): boolean {
59
- let stableIdentifier = identifierCacheFor(this.store).getOrCreateRecordIdentifier(identifier);
60
- let callbackMap = Cache.get(stableIdentifier);
65
+ unsubscribe(token: UnsubscribeToken) {
66
+ unsubscribe(token);
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
+ return false;
79
+ }
80
+
81
+ let callbackMap = Cache.get(identifier);
61
82
  if (!callbackMap || !callbackMap.size) {
62
83
  return false;
63
84
  }
64
85
  callbackMap.forEach((cb) => {
65
- cb(stableIdentifier, value, key);
86
+ cb(identifier, value, key);
66
87
  });
67
88
  return true;
68
89
  }
90
+
91
+ destroy() {
92
+ Tokens.clear();
93
+ Cache.clear();
94
+ }
69
95
  }
@@ -40,8 +40,8 @@ export default class BelongsToReference extends Reference {
40
40
  declare parentIdentifier: StableRecordIdentifier;
41
41
 
42
42
  // unsubscribe tokens given to us by the notification manager
43
- #token!: Object;
44
- #relatedToken: Object | null = null;
43
+ #token!: object;
44
+ #relatedToken: object | null = null;
45
45
 
46
46
  @tracked _ref = 0;
47
47
 
@@ -76,8 +76,10 @@ export default class BelongsToReference extends Reference {
76
76
  destroy() {
77
77
  if (CUSTOM_MODEL_CLASS) {
78
78
  unsubscribe(this.#token);
79
+ this.#token = null as unknown as object;
79
80
  if (this.#relatedToken) {
80
81
  unsubscribe(this.#relatedToken);
82
+ this.#relatedToken = null;
81
83
  }
82
84
  }
83
85
  }
@@ -88,6 +90,7 @@ export default class BelongsToReference extends Reference {
88
90
  this._ref; // consume the tracked prop
89
91
  if (this.#relatedToken) {
90
92
  unsubscribe(this.#relatedToken);
93
+ this.#relatedToken = null;
91
94
  }
92
95
 
93
96
  let resource = this._resource();
@@ -76,6 +76,7 @@ export default class HasManyReference extends Reference {
76
76
  destroy() {
77
77
  if (CUSTOM_MODEL_CLASS) {
78
78
  unsubscribe(this.#token);
79
+ this.#token = null as unknown as object;
79
80
  this.#relatedTokenMap.forEach((token) => {
80
81
  unsubscribe(token);
81
82
  });
@@ -90,22 +91,30 @@ export default class HasManyReference extends Reference {
90
91
 
91
92
  let resource = this._resource();
92
93
 
93
- this.#relatedTokenMap.forEach((token) => {
94
- unsubscribe(token);
95
- });
96
- this.#relatedTokenMap.clear();
94
+ let map = this.#relatedTokenMap;
95
+ this.#relatedTokenMap = new Map();
97
96
 
98
97
  if (resource && resource.data) {
99
98
  return resource.data.map((resourceIdentifier) => {
100
99
  const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
101
- const token = this.store._notificationManager.subscribe(
102
- identifier,
103
- (_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
104
- if (bucket === 'identity' || ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')) {
105
- this._ref++;
100
+
101
+ let token = map.get(identifier);
102
+
103
+ if (token) {
104
+ map.delete(identifier);
105
+ } else {
106
+ token = this.store._notificationManager.subscribe(
107
+ identifier,
108
+ (_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
109
+ if (
110
+ bucket === 'identity' ||
111
+ ((bucket === 'attributes' || bucket === 'property') && notifiedKey === 'id')
112
+ ) {
113
+ this._ref++;
114
+ }
106
115
  }
107
- }
108
- );
116
+ );
117
+ }
109
118
 
110
119
  this.#relatedTokenMap.set(identifier, token);
111
120
 
@@ -113,6 +122,11 @@ export default class HasManyReference extends Reference {
113
122
  });
114
123
  }
115
124
 
125
+ map.forEach((token) => {
126
+ this.store._notificationManager.unsubscribe(token);
127
+ });
128
+ map.clear();
129
+
116
130
  return [];
117
131
  }
118
132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ember-data/store",
3
- "version": "3.28.10",
3
+ "version": "3.28.11",
4
4
  "description": "The default blueprint for ember-cli addons.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -17,8 +17,8 @@
17
17
  "start": "ember serve"
18
18
  },
19
19
  "dependencies": {
20
- "@ember-data/canary-features": "3.28.10",
21
- "@ember-data/private-build-infra": "3.28.10",
20
+ "@ember-data/canary-features": "3.28.11",
21
+ "@ember-data/private-build-infra": "3.28.11",
22
22
  "@ember/string": "^3.0.0",
23
23
  "@glimmer/tracking": "^1.0.4",
24
24
  "ember-cached-decorator-polyfill": "^0.1.4",
@@ -27,7 +27,7 @@
27
27
  "ember-cli-typescript": "^4.1.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@ember-data/unpublished-test-infra": "3.28.10",
30
+ "@ember-data/unpublished-test-infra": "3.28.11",
31
31
  "@ember/optional-features": "^2.0.0",
32
32
  "@ember/test-helpers": "^2.2.5",
33
33
  "@types/ember": "^3.16.5",