@metamask/social-controllers 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -1
- package/dist/SocialRealtimeService.d.ts +34 -0
- package/dist/SocialRealtimeService.d.ts.map +1 -0
- package/dist/SocialRealtimeService.js +126 -0
- package/dist/SocialRealtimeService.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.5.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add `SocialRealtimeService` for subscribing to Social realtime feed events through the shared backend WebSocket connection ([#10561](https://github.com/MetaMask/core/pull/10561))
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Bump `@metamask/base-data-service` from `^2.0.0` to `^2.1.0` ([#10502](https://github.com/MetaMask/core/pull/10502))
|
|
19
|
+
- Bump `@metamask/core-backend` from `^11.0.0` to `^12.0.0` ([#10648](https://github.com/MetaMask/core/pull/10648))
|
|
20
|
+
|
|
10
21
|
## [3.4.0]
|
|
11
22
|
|
|
12
23
|
### Added
|
|
@@ -225,7 +236,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
225
236
|
- `unfollowTrader` — unfollows traders and removes addresses from state
|
|
226
237
|
- `updateFollowing` — fetches following list and replaces addresses in state
|
|
227
238
|
|
|
228
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.
|
|
239
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.5.0...HEAD
|
|
240
|
+
[3.5.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.4.0...@metamask/social-controllers@3.5.0
|
|
229
241
|
[3.4.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.3.0...@metamask/social-controllers@3.4.0
|
|
230
242
|
[3.3.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.2.0...@metamask/social-controllers@3.3.0
|
|
231
243
|
[3.2.0]: https://github.com/MetaMask/core/compare/@metamask/social-controllers@3.1.0...@metamask/social-controllers@3.2.0
|
|
@@ -0,0 +1,34 @@
|
|
|
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.all';
|
|
4
|
+
export declare const SOCIAL_FEED_CHANNEL_TYPE: 'social.v1';
|
|
5
|
+
export type SocialFeedEvent = {
|
|
6
|
+
version: 1;
|
|
7
|
+
kind: 'feed-item';
|
|
8
|
+
eventId: string;
|
|
9
|
+
feedItemId: string;
|
|
10
|
+
revision: number;
|
|
11
|
+
occurredAt: string;
|
|
12
|
+
data: FeedItem;
|
|
13
|
+
};
|
|
14
|
+
export type SocialFeedEventListener = (event: SocialFeedEvent) => void;
|
|
15
|
+
export type SocialFeedReconnectListener = () => void;
|
|
16
|
+
export type SocialRealtimeServiceOptions = {
|
|
17
|
+
messenger: BackendWebSocketServiceMessenger;
|
|
18
|
+
isEnabled?: () => boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Social realtime subscription service built on the shared backend WebSocket.
|
|
22
|
+
*
|
|
23
|
+
* The POC subscribes to the global feed channel. Additional Social channels
|
|
24
|
+
* can be added here without creating another WebSocket connection.
|
|
25
|
+
*/
|
|
26
|
+
export declare class SocialRealtimeService {
|
|
27
|
+
#private;
|
|
28
|
+
readonly name: 'SocialRealtimeService';
|
|
29
|
+
constructor({ messenger, isEnabled, }: SocialRealtimeServiceOptions);
|
|
30
|
+
addListener(listener: SocialFeedEventListener): () => void;
|
|
31
|
+
addReconnectListener(listener: SocialFeedReconnectListener): () => void;
|
|
32
|
+
setActive(active: boolean): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=SocialRealtimeService.d.ts.map
|
|
@@ -0,0 +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,oBAA6B,CAAC;AACjE,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;AAuBF;;;;;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;CAyEF"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { WebSocketState } from '@metamask/core-backend';
|
|
2
|
+
export const SOCIAL_FEED_CHANNEL = 'social.v1.feed.all';
|
|
3
|
+
export const SOCIAL_FEED_CHANNEL_TYPE = 'social.v1';
|
|
4
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
5
|
+
const parseSocialFeedEvent = (data) => {
|
|
6
|
+
if (data.version !== 1 ||
|
|
7
|
+
data.kind !== 'feed-item' ||
|
|
8
|
+
typeof data.eventId !== 'string' ||
|
|
9
|
+
typeof data.feedItemId !== 'string' ||
|
|
10
|
+
typeof data.revision !== 'number' ||
|
|
11
|
+
typeof data.occurredAt !== 'string' ||
|
|
12
|
+
!isRecord(data.data)) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return data;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Social realtime subscription service built on the shared backend WebSocket.
|
|
19
|
+
*
|
|
20
|
+
* The POC subscribes to the global feed channel. Additional Social channels
|
|
21
|
+
* can be added here without creating another WebSocket connection.
|
|
22
|
+
*/
|
|
23
|
+
export class SocialRealtimeService {
|
|
24
|
+
name = 'SocialRealtimeService';
|
|
25
|
+
#messenger;
|
|
26
|
+
#listeners = new Set();
|
|
27
|
+
#reconnectListeners = new Set();
|
|
28
|
+
#isEnabled;
|
|
29
|
+
#active = false;
|
|
30
|
+
#subscription;
|
|
31
|
+
#subscriptionPromise;
|
|
32
|
+
#wasDisconnected = false;
|
|
33
|
+
constructor({ messenger, isEnabled = () => true, }) {
|
|
34
|
+
this.#messenger = messenger;
|
|
35
|
+
this.#isEnabled = isEnabled;
|
|
36
|
+
this.#messenger.subscribe('BackendWebSocketService:connectionStateChanged', ({ state }) => {
|
|
37
|
+
if (state === WebSocketState.DISCONNECTED) {
|
|
38
|
+
this.#subscription = undefined;
|
|
39
|
+
this.#wasDisconnected = true;
|
|
40
|
+
}
|
|
41
|
+
if (state === WebSocketState.CONNECTED && this.#active) {
|
|
42
|
+
const isReconnect = this.#wasDisconnected;
|
|
43
|
+
this.#wasDisconnected = false;
|
|
44
|
+
// Subscription setup handles its own errors and must not block reconnect handling.
|
|
45
|
+
// eslint-disable-next-line no-void
|
|
46
|
+
void this.#subscribeToFeed();
|
|
47
|
+
if (isReconnect) {
|
|
48
|
+
this.#reconnectListeners.forEach((listener) => listener());
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
addListener(listener) {
|
|
54
|
+
this.#listeners.add(listener);
|
|
55
|
+
return () => this.#listeners.delete(listener);
|
|
56
|
+
}
|
|
57
|
+
addReconnectListener(listener) {
|
|
58
|
+
this.#reconnectListeners.add(listener);
|
|
59
|
+
return () => this.#reconnectListeners.delete(listener);
|
|
60
|
+
}
|
|
61
|
+
async setActive(active) {
|
|
62
|
+
this.#active = active && this.#isEnabled();
|
|
63
|
+
if (!this.#active) {
|
|
64
|
+
await this.#unsubscribeFromFeed();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
await this.#subscribeToFeed();
|
|
68
|
+
}
|
|
69
|
+
async #subscribeToFeed() {
|
|
70
|
+
if (!this.#active || this.#subscription) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
while (this.#subscriptionPromise) {
|
|
74
|
+
await this.#subscriptionPromise;
|
|
75
|
+
if (!this.#active || this.#subscription) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const subscriptionPromise = Promise.resolve().then(async () => {
|
|
80
|
+
try {
|
|
81
|
+
await this.#messenger.call('BackendWebSocketService:connect');
|
|
82
|
+
// Each service needs its own callback and subscription lifecycle.
|
|
83
|
+
const subscription = await this.#messenger.call('BackendWebSocketService:subscribe', {
|
|
84
|
+
channels: [SOCIAL_FEED_CHANNEL],
|
|
85
|
+
channelType: SOCIAL_FEED_CHANNEL_TYPE,
|
|
86
|
+
callback: (notification) => {
|
|
87
|
+
this.#handleNotification(notification);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
if (!this.#active) {
|
|
91
|
+
await subscription.unsubscribe();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.#subscription = subscription;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
this.#subscription = undefined;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
this.#subscriptionPromise = subscriptionPromise;
|
|
101
|
+
try {
|
|
102
|
+
await subscriptionPromise;
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
this.#subscriptionPromise = undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async #unsubscribeFromFeed() {
|
|
109
|
+
const subscription = this.#subscription;
|
|
110
|
+
this.#subscription = undefined;
|
|
111
|
+
if (subscription) {
|
|
112
|
+
await subscription.unsubscribe();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
#handleNotification(notification) {
|
|
116
|
+
if (notification.channel !== SOCIAL_FEED_CHANNEL) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const event = parseSocialFeedEvent(notification.data);
|
|
120
|
+
if (!event) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
this.#listeners.forEach((listener) => listener(event));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=SocialRealtimeService.js.map
|
|
@@ -0,0 +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.all' 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 = (\n data: Record<string, unknown>,\n): SocialFeedEvent | undefined => {\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\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 const event = parseSocialFeedEvent(notification.data);\n if (!event) {\n return;\n }\n\n this.#listeners.forEach((listener) => listener(event));\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type { SocialServiceActions, SocialServiceEvents, SocialServiceMessenger,
|
|
|
5
5
|
export { SocialService } from './SocialService.js';
|
|
6
6
|
export type { SocialServiceFetchClosedPositionsAction, SocialServiceFetchFeedAction, SocialServiceFetchFollowersAction, SocialServiceFetchTokenFeedAction, SocialServiceFetchFollowingAction, SocialServiceFetchLeaderboardAction, SocialServiceFetchOpenPositionsAction, SocialServiceFetchPositionByIdAction, SocialServiceFetchTraderFeedAction, SocialServiceFetchTraderProfileAction, SocialServiceFollowAction, SocialServiceOptInToLeaderboardAction, SocialServiceOptOutOfLeaderboardAction, SocialServiceReactToCommentAction, SocialServiceCreateSwapCommentAction, SocialServiceRefreshNotificationPreferencesCacheAction, SocialServiceRemoveCommentReactionAction, SocialServiceUnfollowAction, } from './SocialService-method-action-types.js';
|
|
7
7
|
export { TRADER_RANKING_TAGS, TradeStruct } from './social-types.js';
|
|
8
|
+
export { SOCIAL_FEED_CHANNEL, SOCIAL_FEED_CHANNEL_TYPE, SocialRealtimeService, } from './SocialRealtimeService.js';
|
|
9
|
+
export type { SocialFeedEvent, SocialFeedEventListener, SocialFeedReconnectListener, SocialRealtimeServiceOptions, } from './SocialRealtimeService.js';
|
|
8
10
|
export type { AuthorComment, CommentEngagement, CommentReaction, CommentReactionProfile, CopytradedAllTime, CreateSwapCommentOptions, FeedActorSummary, FeedItem, FeedPagination, FeedResponse, FetchFeedOptions, FetchFollowersOptions, FetchTokenFeedOptions, FetchLeaderboardOptions, FetchPositionByIdOptions, FetchPositionsOptions, FetchTraderFeedOptions, FetchTraderProfileOptions, FollowersResponse, FollowingResponse, FollowOptions, FollowResponse, LeaderboardEntry, LeaderboardResponse, Pagination, PerChainBreakdown, Position, PositionsResponse, ProfileSummary, ReactToCommentOptions, RemoveCommentReactionOptions, SocialControllerState, SocialHandles, SwapCommentAuthor, SwapCommentResponse, TokenFeedChain, TokenFeedStatus, Trade, TradeAction, TradeInFlight, TraderProfile, TraderProfileResponse, TraderRankingTag, TraderStats, UnfollowOptions, UnfollowResponse, } from './social-types.js';
|
|
9
11
|
export { serviceName as socialServiceName, SocialServiceErrorMessage, } from './social-constants.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EAC9B,yBAAyB,EACzB,uBAAuB,EACvB,gCAAgC,GACjC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,+BAA+B,GAChC,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,kCAAkC,EAClC,wCAAwC,EACxC,yCAAyC,EACzC,oCAAoC,EACpC,qCAAqC,EACrC,uCAAuC,GACxC,MAAM,2CAA2C,CAAC;AAEnD,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,YAAY,EACV,uCAAuC,EACvC,4BAA4B,EAC5B,iCAAiC,EACjC,iCAAiC,EACjC,iCAAiC,EACjC,mCAAmC,EACnC,qCAAqC,EACrC,oCAAoC,EACpC,kCAAkC,EAClC,qCAAqC,EACrC,yBAAyB,EACzB,qCAAqC,EACrC,sCAAsC,EACtC,iCAAiC,EACjC,oCAAoC,EACpC,sDAAsD,EACtD,wCAAwC,EACxC,2BAA2B,GAC5B,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,4BAA4B,EAC5B,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,KAAK,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,WAAW,IAAI,iBAAiB,EAChC,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EAC9B,yBAAyB,EACzB,uBAAuB,EACvB,gCAAgC,GACjC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,gBAAgB,EAChB,+BAA+B,GAChC,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,kCAAkC,EAClC,wCAAwC,EACxC,yCAAyC,EACzC,oCAAoC,EACpC,qCAAqC,EACrC,uCAAuC,GACxC,MAAM,2CAA2C,CAAC;AAEnD,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,YAAY,EACV,uCAAuC,EACvC,4BAA4B,EAC5B,iCAAiC,EACjC,iCAAiC,EACjC,iCAAiC,EACjC,mCAAmC,EACnC,qCAAqC,EACrC,oCAAoC,EACpC,kCAAkC,EAClC,qCAAqC,EACrC,yBAAyB,EACzB,qCAAqC,EACrC,sCAAsC,EACtC,iCAAiC,EACjC,oCAAoC,EACpC,sDAAsD,EACtD,wCAAwC,EACxC,2BAA2B,GAC5B,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,eAAe,EACf,uBAAuB,EACvB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,4BAA4B,EAC5B,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,KAAK,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,WAAW,IAAI,iBAAiB,EAChC,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { SocialController, getDefaultSocialControllerState, } from './SocialController.js';
|
|
2
2
|
export { SocialService } from './SocialService.js';
|
|
3
3
|
export { TRADER_RANKING_TAGS, TradeStruct } from './social-types.js';
|
|
4
|
+
export { SOCIAL_FEED_CHANNEL, SOCIAL_FEED_CHANNEL_TYPE, SocialRealtimeService, } from './SocialRealtimeService.js';
|
|
4
5
|
export { serviceName as socialServiceName, SocialServiceErrorMessage, } from './social-constants.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,gBAAgB,EAChB,+BAA+B,GAChC,MAAM,uBAAuB,CAAC;AAgB/B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAuBnD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,gBAAgB,EAChB,+BAA+B,GAChC,MAAM,uBAAuB,CAAC;AAgB/B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAuBnD,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;AAwDpC,OAAO,EACL,WAAW,IAAI,iBAAiB,EAChC,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC","sourcesContent":["export type {\n SocialControllerActions,\n SocialControllerEvents,\n SocialControllerGetStateAction,\n SocialControllerMessenger,\n SocialControllerOptions,\n SocialControllerStateChangeEvent,\n} from './SocialController.js';\nexport {\n SocialController,\n getDefaultSocialControllerState,\n} from './SocialController.js';\n\nexport type {\n SocialControllerFollowTraderAction,\n SocialControllerOptInToLeaderboardAction,\n SocialControllerOptOutOfLeaderboardAction,\n SocialControllerUnfollowTraderAction,\n SocialControllerUpdateFollowingAction,\n SocialControllerUpdateLeaderboardAction,\n} from './SocialController-method-action-types.js';\n\nexport type {\n SocialServiceActions,\n SocialServiceEvents,\n SocialServiceMessenger,\n} from './SocialService.js';\nexport { SocialService } from './SocialService.js';\n\nexport type {\n SocialServiceFetchClosedPositionsAction,\n SocialServiceFetchFeedAction,\n SocialServiceFetchFollowersAction,\n SocialServiceFetchTokenFeedAction,\n SocialServiceFetchFollowingAction,\n SocialServiceFetchLeaderboardAction,\n SocialServiceFetchOpenPositionsAction,\n SocialServiceFetchPositionByIdAction,\n SocialServiceFetchTraderFeedAction,\n SocialServiceFetchTraderProfileAction,\n SocialServiceFollowAction,\n SocialServiceOptInToLeaderboardAction,\n SocialServiceOptOutOfLeaderboardAction,\n SocialServiceReactToCommentAction,\n SocialServiceCreateSwapCommentAction,\n SocialServiceRefreshNotificationPreferencesCacheAction,\n SocialServiceRemoveCommentReactionAction,\n SocialServiceUnfollowAction,\n} from './SocialService-method-action-types.js';\n\nexport { TRADER_RANKING_TAGS, TradeStruct } from './social-types.js';\nexport {\n SOCIAL_FEED_CHANNEL,\n SOCIAL_FEED_CHANNEL_TYPE,\n SocialRealtimeService,\n} from './SocialRealtimeService.js';\nexport type {\n SocialFeedEvent,\n SocialFeedEventListener,\n SocialFeedReconnectListener,\n SocialRealtimeServiceOptions,\n} from './SocialRealtimeService.js';\nexport type {\n AuthorComment,\n CommentEngagement,\n CommentReaction,\n CommentReactionProfile,\n CopytradedAllTime,\n CreateSwapCommentOptions,\n FeedActorSummary,\n FeedItem,\n FeedPagination,\n FeedResponse,\n FetchFeedOptions,\n FetchFollowersOptions,\n FetchTokenFeedOptions,\n FetchLeaderboardOptions,\n FetchPositionByIdOptions,\n FetchPositionsOptions,\n FetchTraderFeedOptions,\n FetchTraderProfileOptions,\n FollowersResponse,\n FollowingResponse,\n FollowOptions,\n FollowResponse,\n LeaderboardEntry,\n LeaderboardResponse,\n Pagination,\n PerChainBreakdown,\n Position,\n PositionsResponse,\n ProfileSummary,\n ReactToCommentOptions,\n RemoveCommentReactionOptions,\n SocialControllerState,\n SocialHandles,\n SwapCommentAuthor,\n SwapCommentResponse,\n TokenFeedChain,\n TokenFeedStatus,\n Trade,\n TradeAction,\n TradeInFlight,\n TraderProfile,\n TraderProfileResponse,\n TraderRankingTag,\n TraderStats,\n UnfollowOptions,\n UnfollowResponse,\n} from './social-types.js';\n\nexport {\n serviceName as socialServiceName,\n SocialServiceErrorMessage,\n} from './social-constants.js';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask/social-controllers",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "A collection of social related controllers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"build:only-clean": "rimraf './dist' './tsconfig.build.tsbuildinfo'",
|
|
40
40
|
"changelog:update": "../../scripts/update-changelog.sh @metamask/social-controllers",
|
|
41
41
|
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/social-controllers",
|
|
42
|
-
"lint:tsconfigs": "
|
|
43
|
-
"lint:tsconfigs:fix": "
|
|
44
|
-
"messenger-action-types:check": "
|
|
45
|
-
"messenger-action-types:generate": "
|
|
42
|
+
"lint:tsconfigs": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../scripts/lint-tsconfigs/lint-tsconfigs.ts",
|
|
43
|
+
"lint:tsconfigs:fix": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../scripts/lint-tsconfigs/lint-tsconfigs.ts --fix",
|
|
44
|
+
"messenger-action-types:check": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --check",
|
|
45
|
+
"messenger-action-types:generate": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --generate",
|
|
46
46
|
"since-latest-release": "../../scripts/since-latest-release.sh",
|
|
47
47
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
|
|
48
48
|
"test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
|
|
@@ -51,8 +51,9 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@metamask/base-controller": "^10.0.0",
|
|
54
|
-
"@metamask/base-data-service": "^2.
|
|
54
|
+
"@metamask/base-data-service": "^2.1.0",
|
|
55
55
|
"@metamask/controller-utils": "^13.0.0",
|
|
56
|
+
"@metamask/core-backend": "^12.0.0",
|
|
56
57
|
"@metamask/messenger": "^3.0.0",
|
|
57
58
|
"@metamask/profile-sync-controller": "^33.0.0",
|
|
58
59
|
"@metamask/superstruct": "^3.4.1"
|
|
@@ -62,10 +63,9 @@
|
|
|
62
63
|
"@types/jest": "^30.0.0",
|
|
63
64
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
64
65
|
"deepmerge": "^4.3.1",
|
|
65
|
-
"jest": "^30.
|
|
66
|
+
"jest": "^30.5.2",
|
|
66
67
|
"rimraf": "^6.1.3",
|
|
67
|
-
"ts-jest": "^29.4.
|
|
68
|
-
"tsx": "^4.23.15",
|
|
68
|
+
"ts-jest": "^29.4.14",
|
|
69
69
|
"typedoc": "^0.25.13",
|
|
70
70
|
"typedoc-plugin-missing-exports": "^2.0.0",
|
|
71
71
|
"typescript": "npm:@typescript/typescript6@^6.0.2"
|