@metamask-previews/social-controllers 3.8.0-preview-aeb52ce4f → 3.9.0-preview-a583980c5

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/CHANGELOG.md CHANGED
@@ -7,17 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.9.0]
11
+
10
12
  ### Added
11
13
 
12
14
  - Add optional `createdAt` to `TraderProfile`. `fetchTraderProfile` accepts an ISO-8601 timestamp, or `null`, on `GET /traders/:addressOrId/profile` and still accepts responses that omit it ([#10877](https://github.com/MetaMask/core/pull/10877))
13
15
  - Add the shared `all` feed scope to `FetchFeedOptions` and export `FeedScope`. Preserve optional `eventId`, `revision`, and `occurredAt` projection metadata on `FeedItem` ([#10865](https://github.com/MetaMask/core/pull/10865))
14
16
  - Add the `trending` feed scope to `FetchFeedOptions` and export `FeedScope` for policy-filtered trending feed reads ([#10895](https://github.com/MetaMask/core/pull/10895))
17
+ - Add explicit `all` and `trending` scope subscriptions to `SocialRealtimeService` and validate hydrated realtime feed events before forwarding them ([#10924](https://github.com/MetaMask/core/pull/10924))
15
18
 
16
19
  ### Changed
17
20
 
18
21
  - Bump `@metamask/core-backend` from `^12.1.0` to `^12.3.0` ([#10857](https://github.com/MetaMask/core/pull/10857), [#10876](https://github.com/MetaMask/core/pull/10876))
19
22
  - Bump `@metamask/base-controller` from `^10.0.0` to `^10.1.0` ([#10864](https://github.com/MetaMask/core/pull/10864))
20
- - Bump `@metamask/profile-sync-controller` from `^34.0.3` to `^34.1.0` ([#10905](https://github.com/MetaMask/core/pull/10905))
23
+ - Bump `@metamask/profile-sync-controller` from `^34.0.3` to `^35.0.0` ([#10905](https://github.com/MetaMask/core/pull/10905), [#10925](https://github.com/MetaMask/core/pull/10925))
21
24
 
22
25
  ## [3.8.0]
23
26
 
@@ -294,7 +297,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
294
297
  - `unfollowTrader` — unfollows traders and removes addresses from state
295
298
  - `updateFollowing` — fetches following list and replaces addresses in state
296
299
 
297
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.8.0...HEAD
300
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.9.0...HEAD
301
+ [3.9.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.8.0...@metamask/social-controllers@3.9.0
298
302
  [3.8.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.7.0...@metamask/social-controllers@3.8.0
299
303
  [3.7.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.6.0...@metamask/social-controllers@3.7.0
300
304
  [3.6.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.5.0...@metamask/social-controllers@3.6.0
@@ -1,6 +1,10 @@
1
1
  import type { BackendWebSocketServiceMessenger } from '@metamask/core-backend';
2
- import type { FeedItem } from './social-types.js';
3
- export declare const SOCIAL_FEED_CHANNEL: 'social.v1.feed.trending';
2
+ import type { SocialRealtimeFeedItem } from './structs.js';
3
+ export declare const SOCIAL_FEED_CHANNELS: {
4
+ readonly all: 'social.v1.feed.all';
5
+ readonly trending: 'social.v1.feed.trending';
6
+ };
7
+ export type SocialFeedRealtimeScope = keyof typeof SOCIAL_FEED_CHANNELS;
4
8
  export declare const SOCIAL_FEED_CHANNEL_TYPE: 'social.v1';
5
9
  export type SocialFeedEvent = {
6
10
  version: 1;
@@ -9,26 +13,28 @@ export type SocialFeedEvent = {
9
13
  feedItemId: string;
10
14
  revision: number;
11
15
  occurredAt: string;
12
- data: FeedItem;
16
+ data: SocialRealtimeFeedItem;
13
17
  };
14
- export type SocialFeedEventListener = (event: SocialFeedEvent) => void;
18
+ export type SocialFeedEventListener = (event: SocialFeedEvent, scope: SocialFeedRealtimeScope) => void;
15
19
  export type SocialFeedReconnectListener = () => void;
16
20
  export type SocialRealtimeServiceOptions = {
17
21
  messenger: BackendWebSocketServiceMessenger;
22
+ scopes: readonly SocialFeedRealtimeScope[];
18
23
  isEnabled?: () => boolean;
19
24
  };
20
25
  /**
21
26
  * Social realtime subscription service built on the shared backend WebSocket.
22
27
  *
23
- * The POC subscribes to the global feed channel. Additional Social channels
24
- * can be added here without creating another WebSocket connection.
28
+ * Clients explicitly select one or more global feed scopes. Additional Social
29
+ * channels can be added here without creating another WebSocket connection.
25
30
  */
26
31
  export declare class SocialRealtimeService {
27
32
  #private;
28
33
  readonly name: 'SocialRealtimeService';
29
- constructor({ messenger, isEnabled, }: SocialRealtimeServiceOptions);
34
+ constructor({ messenger, scopes, isEnabled, }: SocialRealtimeServiceOptions);
30
35
  addListener(listener: SocialFeedEventListener): () => void;
31
36
  addReconnectListener(listener: SocialFeedReconnectListener): () => void;
37
+ setScopes(scopes: readonly SocialFeedRealtimeScope[]): Promise<void>;
32
38
  setActive(active: boolean): Promise<void>;
33
39
  }
34
40
  //# sourceMappingURL=SocialRealtimeService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SocialRealtimeService.d.ts","sourceRoot":"","sources":["../src/SocialRealtimeService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gCAAgC,EAGjC,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,eAAO,MAAM,mBAAmB,EAAG,yBAAkC,CAAC;AACtE,eAAO,MAAM,wBAAwB,EAAG,WAAoB,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AACvE,MAAM,MAAM,2BAA2B,GAAG,MAAM,IAAI,CAAC;AAErD,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,gCAAgC,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;CAC3B,CAAC;AAiCF;;;;;GAKG;AACH,qBAAa,qBAAqB;;IAChC,QAAQ,CAAC,IAAI,EAAG,uBAAuB,CAAU;IAkBjD,YAAY,EACV,SAAS,EACT,SAA+B,GAChC,EAAE,4BAA4B,EAyB9B;IAED,WAAW,CAAC,QAAQ,EAAE,uBAAuB,GAAG,MAAM,IAAI,CAGzD;IAED,oBAAoB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,MAAM,IAAI,CAGtE;IAEK,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAS9C;CAsEF"}
1
+ {"version":3,"file":"SocialRealtimeService.d.ts","sourceRoot":"","sources":["../src/SocialRealtimeService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gCAAgC,EAGjC,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3D,eAAO,MAAM,oBAAoB;aAC/B,GAAG,EAAE,oBAAoB;aACzB,QAAQ,EAAE,yBAAyB;CAC3B,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,oBAAoB,CAAC;AAExE,eAAO,MAAM,wBAAwB,EAAG,WAAoB,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,sBAAsB,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,CACpC,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,uBAAuB,KAC3B,IAAI,CAAC;AACV,MAAM,MAAM,2BAA2B,GAAG,MAAM,IAAI,CAAC;AAErD,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,EAAE,gCAAgC,CAAC;IAC5C,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;CAC3B,CAAC;AAmDF;;;;;GAKG;AACH,qBAAa,qBAAqB;;IAChC,QAAQ,CAAC,IAAI,EAAG,uBAAuB,CAAU;IA4BjD,YAAY,EACV,SAAS,EACT,MAAM,EACN,SAA+B,GAChC,EAAE,4BAA4B,EA6B9B;IAED,WAAW,CAAC,QAAQ,EAAE,uBAAuB,GAAG,MAAM,IAAI,CAGzD;IAED,oBAAoB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,MAAM,IAAI,CAGtE;IAEK,SAAS,CAAC,MAAM,EAAE,SAAS,uBAAuB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBzE;IA4BK,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9C;CAuJF"}
@@ -1,21 +1,34 @@
1
1
  import { WebSocketState } from '@metamask/core-backend';
2
- export const SOCIAL_FEED_CHANNEL = 'social.v1.feed.trending';
2
+ import { parseSocialRealtimeFeedItem } from './structs.js';
3
+ export const SOCIAL_FEED_CHANNELS = {
4
+ all: 'social.v1.feed.all',
5
+ trending: 'social.v1.feed.trending',
6
+ };
3
7
  export const SOCIAL_FEED_CHANNEL_TYPE = 'social.v1';
4
8
  const isRecord = (value) => typeof value === 'object' && value !== null;
9
+ const normalizeScopes = (scopes) => {
10
+ const normalized = [...new Set(scopes)];
11
+ if (normalized.length === 0) {
12
+ throw new Error('At least one Social feed scope is required');
13
+ }
14
+ return normalized;
15
+ };
16
+ const scopeForChannel = (channel) => Object.entries(SOCIAL_FEED_CHANNELS).find(([, value]) => value === channel)?.[0] ?? undefined;
5
17
  const parseSocialFeedEvent = (data) => {
6
18
  if (!isRecord(data)) {
7
19
  return undefined;
8
20
  }
21
+ const feedItem = parseSocialRealtimeFeedItem(data.data);
9
22
  if (data.version !== 1 ||
10
23
  data.kind !== 'feed-item' ||
11
24
  typeof data.eventId !== 'string' ||
12
25
  typeof data.feedItemId !== 'string' ||
13
26
  typeof data.revision !== 'number' ||
14
27
  typeof data.occurredAt !== 'string' ||
15
- !isRecord(data.data)) {
28
+ feedItem === undefined) {
16
29
  return undefined;
17
30
  }
18
- return data;
31
+ return { ...data, data: feedItem };
19
32
  };
20
33
  const parseSocialFeedEvents = (data) => {
21
34
  const entries = Array.isArray(data) ? data : [data];
@@ -26,8 +39,8 @@ const parseSocialFeedEvents = (data) => {
26
39
  /**
27
40
  * Social realtime subscription service built on the shared backend WebSocket.
28
41
  *
29
- * The POC subscribes to the global feed channel. Additional Social channels
30
- * can be added here without creating another WebSocket connection.
42
+ * Clients explicitly select one or more global feed scopes. Additional Social
43
+ * channels can be added here without creating another WebSocket connection.
31
44
  */
32
45
  export class SocialRealtimeService {
33
46
  name = 'SocialRealtimeService';
@@ -35,27 +48,37 @@ export class SocialRealtimeService {
35
48
  #listeners = new Set();
36
49
  #reconnectListeners = new Set();
37
50
  #isEnabled;
51
+ #scopes;
52
+ #scopeUpdatePromise;
38
53
  #active = false;
39
54
  #subscription;
55
+ #subscriptionToken;
40
56
  #subscriptionPromise;
57
+ #pendingUnsubscriptions = new Set();
58
+ #connectionVersion = 0;
41
59
  #wasDisconnected = false;
42
- constructor({ messenger, isEnabled = () => true, }) {
60
+ constructor({ messenger, scopes, isEnabled = () => true, }) {
43
61
  this.#messenger = messenger;
62
+ this.#scopes = normalizeScopes(scopes);
44
63
  this.#isEnabled = isEnabled;
45
64
  this.#messenger.subscribe('BackendWebSocketService:connectionStateChanged', ({ state }) => {
46
65
  if (state === WebSocketState.DISCONNECTED) {
66
+ this.#connectionVersion += 1;
47
67
  this.#subscription = undefined;
68
+ this.#subscriptionToken = undefined;
48
69
  this.#wasDisconnected = true;
49
70
  }
50
71
  if (state === WebSocketState.CONNECTED && this.#active) {
51
72
  const isReconnect = this.#wasDisconnected;
52
73
  this.#wasDisconnected = false;
53
- // Subscription setup handles its own errors and must not block reconnect handling.
54
74
  // eslint-disable-next-line no-void
55
- void this.#subscribeToFeed();
56
- if (isReconnect) {
57
- this.#reconnectListeners.forEach((listener) => listener());
58
- }
75
+ void this.#subscribeToFeed()
76
+ .then(() => {
77
+ if (isReconnect) {
78
+ this.#reconnectListeners.forEach((listener) => listener());
79
+ }
80
+ })
81
+ .catch(() => undefined);
59
82
  }
60
83
  });
61
84
  }
@@ -67,12 +90,50 @@ export class SocialRealtimeService {
67
90
  this.#reconnectListeners.add(listener);
68
91
  return () => this.#reconnectListeners.delete(listener);
69
92
  }
93
+ async setScopes(scopes) {
94
+ const normalizedScopes = normalizeScopes(scopes);
95
+ const scopeUpdate = this.#scopeUpdatePromise
96
+ ? this.#scopeUpdatePromise
97
+ .catch(() => undefined)
98
+ .then(() => this.#applyScopes(normalizedScopes))
99
+ : this.#applyScopes(normalizedScopes);
100
+ this.#scopeUpdatePromise = scopeUpdate;
101
+ try {
102
+ await scopeUpdate;
103
+ }
104
+ finally {
105
+ if (this.#scopeUpdatePromise === scopeUpdate) {
106
+ this.#scopeUpdatePromise = Promise.resolve();
107
+ }
108
+ }
109
+ }
110
+ async #applyScopes(normalizedScopes) {
111
+ if (!this.#active) {
112
+ this.#scopes = normalizedScopes;
113
+ return;
114
+ }
115
+ const previousScopes = this.#scopes;
116
+ const connectionVersion = this.#connectionVersion;
117
+ this.#scopes = normalizedScopes;
118
+ try {
119
+ await this.#resubscribeToFeed();
120
+ }
121
+ catch (error) {
122
+ if (this.#active &&
123
+ this.#connectionVersion === connectionVersion &&
124
+ this.#subscription) {
125
+ this.#scopes = previousScopes;
126
+ }
127
+ throw error;
128
+ }
129
+ }
70
130
  async setActive(active) {
71
131
  this.#active = active && this.#isEnabled();
72
132
  if (!this.#active) {
73
133
  await this.#unsubscribeFromFeed();
74
134
  return;
75
135
  }
136
+ await this.#retryPendingUnsubscriptions();
76
137
  await this.#subscribeToFeed();
77
138
  }
78
139
  async #subscribeToFeed() {
@@ -86,25 +147,25 @@ export class SocialRealtimeService {
86
147
  }
87
148
  }
88
149
  const subscriptionPromise = Promise.resolve().then(async () => {
89
- try {
90
- await this.#messenger.call('BackendWebSocketService:connect');
91
- // Each service needs its own callback and subscription lifecycle.
92
- const subscription = await this.#messenger.call('BackendWebSocketService:subscribe', {
93
- channels: [SOCIAL_FEED_CHANNEL],
94
- channelType: SOCIAL_FEED_CHANNEL_TYPE,
95
- callback: (notification) => {
96
- this.#handleNotification(notification);
97
- },
98
- });
99
- if (!this.#active) {
100
- await subscription.unsubscribe();
101
- return;
102
- }
103
- this.#subscription = subscription;
104
- }
105
- catch {
106
- this.#subscription = undefined;
150
+ await this.#messenger.call('BackendWebSocketService:connect');
151
+ const subscriptionToken = Symbol('social-feed-subscription');
152
+ // Each service needs its own callback and subscription lifecycle.
153
+ const subscription = await this.#messenger.call('BackendWebSocketService:subscribe', {
154
+ channels: this.#scopes.map((scope) => SOCIAL_FEED_CHANNELS[scope]),
155
+ channelType: SOCIAL_FEED_CHANNEL_TYPE,
156
+ callback: (notification) => {
157
+ if (this.#subscriptionToken !== subscriptionToken) {
158
+ return;
159
+ }
160
+ this.#handleNotification(notification);
161
+ },
162
+ });
163
+ if (!this.#active) {
164
+ await subscription.unsubscribe();
165
+ return;
107
166
  }
167
+ this.#subscription = subscription;
168
+ this.#subscriptionToken = subscriptionToken;
108
169
  });
109
170
  this.#subscriptionPromise = subscriptionPromise;
110
171
  try {
@@ -114,19 +175,95 @@ export class SocialRealtimeService {
114
175
  this.#subscriptionPromise = undefined;
115
176
  }
116
177
  }
178
+ async #resubscribeToFeed() {
179
+ while (this.#subscriptionPromise) {
180
+ await this.#subscriptionPromise;
181
+ }
182
+ await this.#retryPendingUnsubscriptions();
183
+ const existingSubscription = this.#subscription;
184
+ const existingSubscriptionToken = this.#subscriptionToken;
185
+ const connectionVersion = this.#connectionVersion;
186
+ this.#subscription = undefined;
187
+ try {
188
+ await this.#subscribeToFeed();
189
+ }
190
+ catch (error) {
191
+ if (this.#active && this.#connectionVersion === connectionVersion) {
192
+ this.#subscription = existingSubscription;
193
+ this.#subscriptionToken = existingSubscriptionToken;
194
+ }
195
+ else {
196
+ this.#subscription = undefined;
197
+ this.#subscriptionToken = undefined;
198
+ if (existingSubscription) {
199
+ try {
200
+ await existingSubscription.unsubscribe();
201
+ }
202
+ catch {
203
+ this.#pendingUnsubscriptions.add(existingSubscription);
204
+ }
205
+ }
206
+ }
207
+ throw error;
208
+ }
209
+ const replacementSubscription = this.#subscription;
210
+ if (existingSubscription && existingSubscription !== this.#subscription) {
211
+ try {
212
+ await existingSubscription.unsubscribe();
213
+ }
214
+ catch (error) {
215
+ if (this.#active && this.#connectionVersion === connectionVersion) {
216
+ this.#subscription = existingSubscription;
217
+ this.#subscriptionToken = existingSubscriptionToken;
218
+ }
219
+ else {
220
+ this.#subscription = undefined;
221
+ this.#subscriptionToken = undefined;
222
+ this.#pendingUnsubscriptions.add(existingSubscription);
223
+ }
224
+ if (replacementSubscription) {
225
+ try {
226
+ await replacementSubscription.unsubscribe();
227
+ }
228
+ catch {
229
+ this.#pendingUnsubscriptions.add(replacementSubscription);
230
+ }
231
+ }
232
+ throw error;
233
+ }
234
+ }
235
+ }
117
236
  async #unsubscribeFromFeed() {
118
237
  const subscription = this.#subscription;
119
238
  this.#subscription = undefined;
120
- if (subscription) {
121
- await subscription.unsubscribe();
239
+ this.#subscriptionToken = undefined;
240
+ try {
241
+ if (subscription) {
242
+ await subscription.unsubscribe();
243
+ }
244
+ }
245
+ finally {
246
+ await this.#retryPendingUnsubscriptions();
247
+ }
248
+ }
249
+ async #retryPendingUnsubscriptions() {
250
+ for (const subscription of this.#pendingUnsubscriptions) {
251
+ try {
252
+ await subscription.unsubscribe();
253
+ this.#pendingUnsubscriptions.delete(subscription);
254
+ }
255
+ catch {
256
+ // Keep the subscription queued for a later cleanup attempt.
257
+ }
122
258
  }
123
259
  }
124
260
  #handleNotification(notification) {
125
- if (notification.channel !== SOCIAL_FEED_CHANNEL) {
261
+ const scope = scopeForChannel(notification.channel);
262
+ if (!scope || !this.#scopes.includes(scope)) {
126
263
  return;
127
264
  }
128
265
  parseSocialFeedEvents(notification.data).forEach((event) => {
129
- this.#listeners.forEach((listener) => listener(event));
266
+ this.#listeners.forEach((listener) => listener(event, scope));
130
267
  });
131
268
  }
132
269
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SocialRealtimeService.js","sourceRoot":"","sources":["../src/SocialRealtimeService.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebSocketState } from '@metamask/core-backend';\nimport type {\n BackendWebSocketServiceMessenger,\n ServerNotificationMessage,\n WebSocketSubscription,\n} from '@metamask/core-backend';\n\nimport type { FeedItem } from './social-types.js';\n\nexport const SOCIAL_FEED_CHANNEL = 'social.v1.feed.trending' as const;\nexport const SOCIAL_FEED_CHANNEL_TYPE = 'social.v1' as const;\n\nexport type SocialFeedEvent = {\n version: 1;\n kind: 'feed-item';\n eventId: string;\n feedItemId: string;\n revision: number;\n occurredAt: string;\n data: FeedItem;\n};\n\nexport type SocialFeedEventListener = (event: SocialFeedEvent) => void;\nexport type SocialFeedReconnectListener = () => void;\n\nexport type SocialRealtimeServiceOptions = {\n messenger: BackendWebSocketServiceMessenger;\n isEnabled?: () => boolean;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst parseSocialFeedEvent = (data: unknown): SocialFeedEvent | undefined => {\n if (!isRecord(data)) {\n return undefined;\n }\n\n if (\n data.version !== 1 ||\n data.kind !== 'feed-item' ||\n typeof data.eventId !== 'string' ||\n typeof data.feedItemId !== 'string' ||\n typeof data.revision !== 'number' ||\n typeof data.occurredAt !== 'string' ||\n !isRecord(data.data)\n ) {\n return undefined;\n }\n\n return data as unknown as SocialFeedEvent;\n};\n\nconst parseSocialFeedEvents = (data: unknown): SocialFeedEvent[] => {\n const entries = Array.isArray(data) ? data : [data];\n\n return entries\n .map((entry) => parseSocialFeedEvent(entry))\n .filter((event): event is SocialFeedEvent => event !== undefined);\n};\n\n/**\n * Social realtime subscription service built on the shared backend WebSocket.\n *\n * The POC subscribes to the global feed channel. Additional Social channels\n * can be added here without creating another WebSocket connection.\n */\nexport class SocialRealtimeService {\n readonly name = 'SocialRealtimeService' as const;\n\n readonly #messenger: BackendWebSocketServiceMessenger;\n\n readonly #listeners = new Set<SocialFeedEventListener>();\n\n readonly #reconnectListeners = new Set<SocialFeedReconnectListener>();\n\n readonly #isEnabled: () => boolean;\n\n #active = false;\n\n #subscription: WebSocketSubscription | undefined;\n\n #subscriptionPromise: Promise<void> | undefined;\n\n #wasDisconnected = false;\n\n constructor({\n messenger,\n isEnabled = (): boolean => true,\n }: SocialRealtimeServiceOptions) {\n this.#messenger = messenger;\n this.#isEnabled = isEnabled;\n\n this.#messenger.subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n ({ state }: { state: WebSocketState }): void => {\n if (state === WebSocketState.DISCONNECTED) {\n this.#subscription = undefined;\n this.#wasDisconnected = true;\n }\n\n if (state === WebSocketState.CONNECTED && this.#active) {\n const isReconnect = this.#wasDisconnected;\n this.#wasDisconnected = false;\n // Subscription setup handles its own errors and must not block reconnect handling.\n // eslint-disable-next-line no-void\n void this.#subscribeToFeed();\n\n if (isReconnect) {\n this.#reconnectListeners.forEach((listener) => listener());\n }\n }\n },\n );\n }\n\n addListener(listener: SocialFeedEventListener): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n addReconnectListener(listener: SocialFeedReconnectListener): () => void {\n this.#reconnectListeners.add(listener);\n return () => this.#reconnectListeners.delete(listener);\n }\n\n async setActive(active: boolean): Promise<void> {\n this.#active = active && this.#isEnabled();\n\n if (!this.#active) {\n await this.#unsubscribeFromFeed();\n return;\n }\n\n await this.#subscribeToFeed();\n }\n\n async #subscribeToFeed(): Promise<void> {\n if (!this.#active || this.#subscription) {\n return;\n }\n\n while (this.#subscriptionPromise) {\n await this.#subscriptionPromise;\n\n if (!this.#active || this.#subscription) {\n return;\n }\n }\n\n const subscriptionPromise = Promise.resolve().then(\n async (): Promise<void> => {\n try {\n await this.#messenger.call('BackendWebSocketService:connect');\n\n // Each service needs its own callback and subscription lifecycle.\n const subscription = await this.#messenger.call(\n 'BackendWebSocketService:subscribe',\n {\n channels: [SOCIAL_FEED_CHANNEL],\n channelType: SOCIAL_FEED_CHANNEL_TYPE,\n callback: (notification: ServerNotificationMessage) => {\n this.#handleNotification(notification);\n },\n },\n );\n\n if (!this.#active) {\n await subscription.unsubscribe();\n return;\n }\n\n this.#subscription = subscription;\n } catch {\n this.#subscription = undefined;\n }\n },\n );\n this.#subscriptionPromise = subscriptionPromise;\n\n try {\n await subscriptionPromise;\n } finally {\n this.#subscriptionPromise = undefined;\n }\n }\n\n async #unsubscribeFromFeed(): Promise<void> {\n const subscription = this.#subscription;\n this.#subscription = undefined;\n\n if (subscription) {\n await subscription.unsubscribe();\n }\n }\n\n #handleNotification(notification: ServerNotificationMessage): void {\n if (notification.channel !== SOCIAL_FEED_CHANNEL) {\n return;\n }\n\n parseSocialFeedEvents(notification.data).forEach((event) => {\n this.#listeners.forEach((listener) => listener(event));\n });\n }\n}\n"]}
1
+ {"version":3,"file":"SocialRealtimeService.js","sourceRoot":"","sources":["../src/SocialRealtimeService.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebSocketState } from '@metamask/core-backend';\nimport type {\n BackendWebSocketServiceMessenger,\n ServerNotificationMessage,\n WebSocketSubscription,\n} from '@metamask/core-backend';\n\nimport { parseSocialRealtimeFeedItem } from './structs.js';\nimport type { SocialRealtimeFeedItem } from './structs.js';\n\nexport const SOCIAL_FEED_CHANNELS = {\n all: 'social.v1.feed.all',\n trending: 'social.v1.feed.trending',\n} as const;\n\nexport type SocialFeedRealtimeScope = keyof typeof SOCIAL_FEED_CHANNELS;\n\nexport const SOCIAL_FEED_CHANNEL_TYPE = 'social.v1' as const;\n\nexport type SocialFeedEvent = {\n version: 1;\n kind: 'feed-item';\n eventId: string;\n feedItemId: string;\n revision: number;\n occurredAt: string;\n data: SocialRealtimeFeedItem;\n};\n\nexport type SocialFeedEventListener = (\n event: SocialFeedEvent,\n scope: SocialFeedRealtimeScope,\n) => void;\nexport type SocialFeedReconnectListener = () => void;\n\nexport type SocialRealtimeServiceOptions = {\n messenger: BackendWebSocketServiceMessenger;\n scopes: readonly SocialFeedRealtimeScope[];\n isEnabled?: () => boolean;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst normalizeScopes = (\n scopes: readonly SocialFeedRealtimeScope[],\n): SocialFeedRealtimeScope[] => {\n const normalized = [...new Set(scopes)];\n if (normalized.length === 0) {\n throw new Error('At least one Social feed scope is required');\n }\n return normalized;\n};\n\nconst scopeForChannel = (\n channel: string,\n): SocialFeedRealtimeScope | undefined =>\n (Object.entries(SOCIAL_FEED_CHANNELS).find(\n ([, value]) => value === channel,\n )?.[0] as SocialFeedRealtimeScope | undefined) ?? undefined;\n\nconst parseSocialFeedEvent = (data: unknown): SocialFeedEvent | undefined => {\n if (!isRecord(data)) {\n return undefined;\n }\n\n const feedItem = parseSocialRealtimeFeedItem(data.data);\n if (\n data.version !== 1 ||\n data.kind !== 'feed-item' ||\n typeof data.eventId !== 'string' ||\n typeof data.feedItemId !== 'string' ||\n typeof data.revision !== 'number' ||\n typeof data.occurredAt !== 'string' ||\n feedItem === undefined\n ) {\n return undefined;\n }\n\n return { ...data, data: feedItem } as unknown as SocialFeedEvent;\n};\n\nconst parseSocialFeedEvents = (data: unknown): SocialFeedEvent[] => {\n const entries = Array.isArray(data) ? data : [data];\n\n return entries\n .map((entry) => parseSocialFeedEvent(entry))\n .filter((event): event is SocialFeedEvent => event !== undefined);\n};\n\n/**\n * Social realtime subscription service built on the shared backend WebSocket.\n *\n * Clients explicitly select one or more global feed scopes. Additional Social\n * channels can be added here without creating another WebSocket connection.\n */\nexport class SocialRealtimeService {\n readonly name = 'SocialRealtimeService' as const;\n\n readonly #messenger: BackendWebSocketServiceMessenger;\n\n readonly #listeners = new Set<SocialFeedEventListener>();\n\n readonly #reconnectListeners = new Set<SocialFeedReconnectListener>();\n\n readonly #isEnabled: () => boolean;\n\n #scopes: SocialFeedRealtimeScope[];\n\n #scopeUpdatePromise: Promise<void> | undefined;\n\n #active = false;\n\n #subscription: WebSocketSubscription | undefined;\n\n #subscriptionToken: symbol | undefined;\n\n #subscriptionPromise: Promise<void> | undefined;\n\n readonly #pendingUnsubscriptions = new Set<WebSocketSubscription>();\n\n #connectionVersion = 0;\n\n #wasDisconnected = false;\n\n constructor({\n messenger,\n scopes,\n isEnabled = (): boolean => true,\n }: SocialRealtimeServiceOptions) {\n this.#messenger = messenger;\n this.#scopes = normalizeScopes(scopes);\n this.#isEnabled = isEnabled;\n\n this.#messenger.subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n ({ state }: { state: WebSocketState }): void => {\n if (state === WebSocketState.DISCONNECTED) {\n this.#connectionVersion += 1;\n this.#subscription = undefined;\n this.#subscriptionToken = undefined;\n this.#wasDisconnected = true;\n }\n\n if (state === WebSocketState.CONNECTED && this.#active) {\n const isReconnect = this.#wasDisconnected;\n this.#wasDisconnected = false;\n // eslint-disable-next-line no-void\n void this.#subscribeToFeed()\n .then(() => {\n if (isReconnect) {\n this.#reconnectListeners.forEach((listener) => listener());\n }\n })\n .catch(() => undefined);\n }\n },\n );\n }\n\n addListener(listener: SocialFeedEventListener): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n addReconnectListener(listener: SocialFeedReconnectListener): () => void {\n this.#reconnectListeners.add(listener);\n return () => this.#reconnectListeners.delete(listener);\n }\n\n async setScopes(scopes: readonly SocialFeedRealtimeScope[]): Promise<void> {\n const normalizedScopes = normalizeScopes(scopes);\n\n const scopeUpdate = this.#scopeUpdatePromise\n ? this.#scopeUpdatePromise\n .catch(() => undefined)\n .then(() => this.#applyScopes(normalizedScopes))\n : this.#applyScopes(normalizedScopes);\n this.#scopeUpdatePromise = scopeUpdate;\n\n try {\n await scopeUpdate;\n } finally {\n if (this.#scopeUpdatePromise === scopeUpdate) {\n this.#scopeUpdatePromise = Promise.resolve();\n }\n }\n }\n\n async #applyScopes(\n normalizedScopes: SocialFeedRealtimeScope[],\n ): Promise<void> {\n if (!this.#active) {\n this.#scopes = normalizedScopes;\n return;\n }\n\n const previousScopes = this.#scopes;\n const connectionVersion = this.#connectionVersion;\n this.#scopes = normalizedScopes;\n\n try {\n await this.#resubscribeToFeed();\n } catch (error) {\n if (\n this.#active &&\n this.#connectionVersion === connectionVersion &&\n this.#subscription\n ) {\n this.#scopes = previousScopes;\n }\n throw error;\n }\n }\n\n async setActive(active: boolean): Promise<void> {\n this.#active = active && this.#isEnabled();\n\n if (!this.#active) {\n await this.#unsubscribeFromFeed();\n return;\n }\n\n await this.#retryPendingUnsubscriptions();\n await this.#subscribeToFeed();\n }\n\n async #subscribeToFeed(): Promise<void> {\n if (!this.#active || this.#subscription) {\n return;\n }\n\n while (this.#subscriptionPromise) {\n await this.#subscriptionPromise;\n\n if (!this.#active || this.#subscription) {\n return;\n }\n }\n\n const subscriptionPromise = Promise.resolve().then(\n async (): Promise<void> => {\n await this.#messenger.call('BackendWebSocketService:connect');\n\n const subscriptionToken = Symbol('social-feed-subscription');\n\n // Each service needs its own callback and subscription lifecycle.\n const subscription = await this.#messenger.call(\n 'BackendWebSocketService:subscribe',\n {\n channels: this.#scopes.map((scope) => SOCIAL_FEED_CHANNELS[scope]),\n channelType: SOCIAL_FEED_CHANNEL_TYPE,\n callback: (notification: ServerNotificationMessage) => {\n if (this.#subscriptionToken !== subscriptionToken) {\n return;\n }\n this.#handleNotification(notification);\n },\n },\n );\n\n if (!this.#active) {\n await subscription.unsubscribe();\n return;\n }\n\n this.#subscription = subscription;\n this.#subscriptionToken = subscriptionToken;\n },\n );\n this.#subscriptionPromise = subscriptionPromise;\n\n try {\n await subscriptionPromise;\n } finally {\n this.#subscriptionPromise = undefined;\n }\n }\n\n async #resubscribeToFeed(): Promise<void> {\n while (this.#subscriptionPromise) {\n await this.#subscriptionPromise;\n }\n\n await this.#retryPendingUnsubscriptions();\n\n const existingSubscription = this.#subscription;\n const existingSubscriptionToken = this.#subscriptionToken;\n const connectionVersion = this.#connectionVersion;\n this.#subscription = undefined;\n\n try {\n await this.#subscribeToFeed();\n } catch (error) {\n if (this.#active && this.#connectionVersion === connectionVersion) {\n this.#subscription = existingSubscription;\n this.#subscriptionToken = existingSubscriptionToken;\n } else {\n this.#subscription = undefined;\n this.#subscriptionToken = undefined;\n\n if (existingSubscription) {\n try {\n await existingSubscription.unsubscribe();\n } catch {\n this.#pendingUnsubscriptions.add(existingSubscription);\n }\n }\n }\n throw error;\n }\n\n const replacementSubscription = this.#subscription as\n | WebSocketSubscription\n | undefined;\n if (existingSubscription && existingSubscription !== this.#subscription) {\n try {\n await existingSubscription.unsubscribe();\n } catch (error) {\n if (this.#active && this.#connectionVersion === connectionVersion) {\n this.#subscription = existingSubscription;\n this.#subscriptionToken = existingSubscriptionToken;\n } else {\n this.#subscription = undefined;\n this.#subscriptionToken = undefined;\n this.#pendingUnsubscriptions.add(existingSubscription);\n }\n\n if (replacementSubscription) {\n try {\n await replacementSubscription.unsubscribe();\n } catch {\n this.#pendingUnsubscriptions.add(replacementSubscription);\n }\n }\n\n throw error;\n }\n }\n }\n\n async #unsubscribeFromFeed(): Promise<void> {\n const subscription = this.#subscription;\n this.#subscription = undefined;\n this.#subscriptionToken = undefined;\n\n try {\n if (subscription) {\n await subscription.unsubscribe();\n }\n } finally {\n await this.#retryPendingUnsubscriptions();\n }\n }\n\n async #retryPendingUnsubscriptions(): Promise<void> {\n for (const subscription of this.#pendingUnsubscriptions) {\n try {\n await subscription.unsubscribe();\n this.#pendingUnsubscriptions.delete(subscription);\n } catch {\n // Keep the subscription queued for a later cleanup attempt.\n }\n }\n }\n\n #handleNotification(notification: ServerNotificationMessage): void {\n const scope = scopeForChannel(notification.channel);\n if (!scope || !this.#scopes.includes(scope)) {\n return;\n }\n\n parseSocialFeedEvents(notification.data).forEach((event) => {\n this.#listeners.forEach((listener) => listener(event, scope));\n });\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SocialService.d.ts","sourceRoot":"","sources":["../src/SocialService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SocialService.d.ts","sourceRoot":"","sources":["../src/SocialService.ts"],"names":[],"mappings":"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"}