@metamask/social-controllers 3.6.0 → 3.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.
- package/CHANGELOG.md +18 -1
- package/dist/SocialRealtimeService.d.ts +1 -1
- package/dist/SocialRealtimeService.d.ts.map +1 -1
- package/dist/SocialRealtimeService.js +13 -6
- package/dist/SocialRealtimeService.js.map +1 -1
- package/dist/SocialService-method-action-types.d.ts +50 -2
- package/dist/SocialService-method-action-types.d.ts.map +1 -1
- package/dist/SocialService-method-action-types.js.map +1 -1
- package/dist/SocialService.d.ts +41 -2
- package/dist/SocialService.d.ts.map +1 -1
- package/dist/SocialService.js +137 -33
- package/dist/SocialService.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/social-constants.d.ts +4 -0
- package/dist/social-constants.d.ts.map +1 -1
- package/dist/social-constants.js +4 -0
- package/dist/social-constants.js.map +1 -1
- package/dist/social-types.d.ts +27 -6
- package/dist/social-types.d.ts.map +1 -1
- package/dist/social-types.js.map +1 -1
- package/package.json +4 -4
package/dist/SocialService.js
CHANGED
|
@@ -33,10 +33,13 @@ const PositionStruct = structType({
|
|
|
33
33
|
chain: string(),
|
|
34
34
|
positionAmount: number(),
|
|
35
35
|
isOpen: optional(boolean()),
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
// Incomplete Clicker metrics serialize these as `null` (`undefined + n`
|
|
37
|
+
// becomes `NaN`, then JSON `null`). Rejecting the field used to fail the
|
|
38
|
+
// entire feed page, so they are nullable here.
|
|
39
|
+
boughtUsd: nullable(number()),
|
|
40
|
+
soldUsd: nullable(number()),
|
|
41
|
+
realizedPnl: nullable(number()),
|
|
42
|
+
costBasis: nullable(number()),
|
|
40
43
|
trades: array(TradeStruct),
|
|
41
44
|
lastTradeAt: number(),
|
|
42
45
|
tokenImageUrl: optional(nullable(string())),
|
|
@@ -182,6 +185,34 @@ const FeedResponseStruct = structType({
|
|
|
182
185
|
items: array(FeedItemStruct),
|
|
183
186
|
pagination: FeedPaginationStruct,
|
|
184
187
|
});
|
|
188
|
+
/**
|
|
189
|
+
* One malformed feed item must not blank the page. When the envelope is
|
|
190
|
+
* well-shaped, keep items that match {@link FeedItemStruct} and drop the rest.
|
|
191
|
+
* Still throw when `items` or `pagination` themselves are unusable.
|
|
192
|
+
*
|
|
193
|
+
* @param feedData - Parsed JSON body from a feed endpoint.
|
|
194
|
+
* @param invalidMessage - Error message to throw when the envelope is unusable.
|
|
195
|
+
* @returns The validated feed response, possibly with invalid items dropped.
|
|
196
|
+
*/
|
|
197
|
+
const parseFeedResponse = (feedData, invalidMessage) => {
|
|
198
|
+
if (is(feedData, FeedResponseStruct)) {
|
|
199
|
+
return feedData;
|
|
200
|
+
}
|
|
201
|
+
if (typeof feedData !== 'object' ||
|
|
202
|
+
feedData === null ||
|
|
203
|
+
!('items' in feedData) ||
|
|
204
|
+
!('pagination' in feedData)) {
|
|
205
|
+
throw new Error(invalidMessage);
|
|
206
|
+
}
|
|
207
|
+
const { items, pagination } = feedData;
|
|
208
|
+
if (!Array.isArray(items) || !is(pagination, FeedPaginationStruct)) {
|
|
209
|
+
throw new Error(invalidMessage);
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
items: items.filter((item) => is(item, FeedItemStruct)),
|
|
213
|
+
pagination,
|
|
214
|
+
};
|
|
215
|
+
};
|
|
185
216
|
const FollowersResponseStruct = structType({
|
|
186
217
|
followers: array(ProfileSummaryStruct),
|
|
187
218
|
count: number(),
|
|
@@ -227,7 +258,10 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
227
258
|
'fetchOpenPositions',
|
|
228
259
|
'fetchClosedPositions',
|
|
229
260
|
'fetchFollowers',
|
|
261
|
+
'fetchTraderFollowing',
|
|
230
262
|
'fetchFollowing',
|
|
263
|
+
'fetchMyFollowing',
|
|
264
|
+
'fetchMyFollowers',
|
|
231
265
|
'fetchPositionById',
|
|
232
266
|
'fetchFeed',
|
|
233
267
|
'fetchTraderFeed',
|
|
@@ -314,7 +348,7 @@ export class SocialService extends BaseDataService {
|
|
|
314
348
|
headers: authHeaders,
|
|
315
349
|
});
|
|
316
350
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_LEADERBOARD_FAILED);
|
|
317
|
-
const leaderboardData = await response.json();
|
|
351
|
+
const leaderboardData = (await response.json());
|
|
318
352
|
if (!is(leaderboardData, LeaderboardResponseStruct)) {
|
|
319
353
|
throw new Error(SocialServiceErrorMessage.FETCH_LEADERBOARD_INVALID_RESPONSE);
|
|
320
354
|
}
|
|
@@ -341,7 +375,7 @@ export class SocialService extends BaseDataService {
|
|
|
341
375
|
const authHeaders = await this.#getAuthHeaders();
|
|
342
376
|
const response = await fetch(url, { headers: authHeaders });
|
|
343
377
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TRADER_PROFILE_FAILED);
|
|
344
|
-
const traderProfileData = await response.json();
|
|
378
|
+
const traderProfileData = (await response.json());
|
|
345
379
|
if (!is(traderProfileData, TraderProfileResponseStruct)) {
|
|
346
380
|
throw new Error(SocialServiceErrorMessage.FETCH_TRADER_PROFILE_INVALID_RESPONSE);
|
|
347
381
|
}
|
|
@@ -385,22 +419,31 @@ export class SocialService extends BaseDataService {
|
|
|
385
419
|
/**
|
|
386
420
|
* Fetches a trader's MetaMask followers.
|
|
387
421
|
*
|
|
388
|
-
* Calls `GET ${baseUrl}/traders/${addressOrId}/followers`.
|
|
422
|
+
* Calls `GET ${baseUrl}/traders/${addressOrId}/followers`. Pass
|
|
423
|
+
* `mutuals: true` to append `?mutuals=true` (profiles that follow both this
|
|
424
|
+
* trader and the authenticated user; `count` is the mutual total). Requires a
|
|
425
|
+
* bearer token; the trader is identified by `addressOrId`, not the JWT
|
|
426
|
+
* subject (see {@link fetchMyFollowers} for the current user's inbound list).
|
|
389
427
|
*
|
|
390
428
|
* @param options - Options bag.
|
|
391
429
|
* @param options.addressOrId - Wallet address or Clicker profile ID.
|
|
430
|
+
* @param options.mutuals - When true, request mutual followers only.
|
|
392
431
|
* @returns The followers response.
|
|
393
432
|
*/
|
|
394
433
|
async fetchFollowers(options) {
|
|
395
|
-
const { addressOrId } = options;
|
|
434
|
+
const { addressOrId, mutuals } = options;
|
|
435
|
+
const mutualsOnly = mutuals === true;
|
|
396
436
|
const followersResponse = await this.fetchQuery({
|
|
397
|
-
queryKey: [`${this.name}:fetchFollowers`, addressOrId],
|
|
437
|
+
queryKey: [`${this.name}:fetchFollowers`, addressOrId, mutualsOnly],
|
|
398
438
|
queryFn: async () => {
|
|
399
|
-
const url = `${this.#v1Url}/traders/${encodeURIComponent(addressOrId)}/followers
|
|
439
|
+
const url = new URL(`${this.#v1Url}/traders/${encodeURIComponent(addressOrId)}/followers`);
|
|
440
|
+
if (mutualsOnly) {
|
|
441
|
+
url.searchParams.append('mutuals', 'true');
|
|
442
|
+
}
|
|
400
443
|
const authHeaders = await this.#getAuthHeaders();
|
|
401
|
-
const response = await fetch(url, { headers: authHeaders });
|
|
444
|
+
const response = await fetch(url.toString(), { headers: authHeaders });
|
|
402
445
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_FOLLOWERS_FAILED);
|
|
403
|
-
const followersData = await response.json();
|
|
446
|
+
const followersData = (await response.json());
|
|
404
447
|
if (!is(followersData, FollowersResponseStruct)) {
|
|
405
448
|
throw new Error(SocialServiceErrorMessage.FETCH_FOLLOWERS_INVALID_RESPONSE);
|
|
406
449
|
}
|
|
@@ -409,6 +452,35 @@ export class SocialService extends BaseDataService {
|
|
|
409
452
|
});
|
|
410
453
|
return followersResponse;
|
|
411
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Fetches the list of traders a profile follows.
|
|
457
|
+
*
|
|
458
|
+
* Calls `GET ${baseUrl}/traders/${addressOrId}/following`. Requires a bearer
|
|
459
|
+
* token; the trader is identified by `addressOrId`, not the JWT subject (see
|
|
460
|
+
* {@link fetchFollowing} for the current user's outbound list).
|
|
461
|
+
*
|
|
462
|
+
* @param options - Options bag.
|
|
463
|
+
* @param options.addressOrId - Wallet address or Clicker profile ID.
|
|
464
|
+
* @returns The following response.
|
|
465
|
+
*/
|
|
466
|
+
async fetchTraderFollowing(options) {
|
|
467
|
+
const { addressOrId } = options;
|
|
468
|
+
const followingResponse = await this.fetchQuery({
|
|
469
|
+
queryKey: [`${this.name}:fetchTraderFollowing`, addressOrId],
|
|
470
|
+
queryFn: async () => {
|
|
471
|
+
const url = `${this.#v1Url}/traders/${encodeURIComponent(addressOrId)}/following`;
|
|
472
|
+
const authHeaders = await this.#getAuthHeaders();
|
|
473
|
+
const response = await fetch(url, { headers: authHeaders });
|
|
474
|
+
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TRADER_FOLLOWING_FAILED);
|
|
475
|
+
const followingData = (await response.json());
|
|
476
|
+
if (!is(followingData, FollowingResponseStruct)) {
|
|
477
|
+
throw new Error(SocialServiceErrorMessage.FETCH_TRADER_FOLLOWING_INVALID_RESPONSE);
|
|
478
|
+
}
|
|
479
|
+
return followingData;
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
return followingResponse;
|
|
483
|
+
}
|
|
412
484
|
/**
|
|
413
485
|
* Fetches a single position by its unique ID.
|
|
414
486
|
*
|
|
@@ -427,7 +499,7 @@ export class SocialService extends BaseDataService {
|
|
|
427
499
|
const authHeaders = await this.#getAuthHeaders();
|
|
428
500
|
const response = await fetch(url, { headers: authHeaders });
|
|
429
501
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_POSITION_BY_ID_FAILED);
|
|
430
|
-
const positionData = await response.json();
|
|
502
|
+
const positionData = (await response.json());
|
|
431
503
|
if (!is(positionData, PositionStruct)) {
|
|
432
504
|
throw new Error(SocialServiceErrorMessage.FETCH_POSITION_BY_ID_INVALID_RESPONSE);
|
|
433
505
|
}
|
|
@@ -484,11 +556,8 @@ export class SocialService extends BaseDataService {
|
|
|
484
556
|
headers: authHeaders,
|
|
485
557
|
});
|
|
486
558
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_FEED_FAILED);
|
|
487
|
-
const feedData = await response.json();
|
|
488
|
-
|
|
489
|
-
throw new Error(SocialServiceErrorMessage.FETCH_FEED_INVALID_RESPONSE);
|
|
490
|
-
}
|
|
491
|
-
return feedData;
|
|
559
|
+
const feedData = (await response.json());
|
|
560
|
+
return parseFeedResponse(feedData, SocialServiceErrorMessage.FETCH_FEED_INVALID_RESPONSE);
|
|
492
561
|
},
|
|
493
562
|
});
|
|
494
563
|
return feedResponse;
|
|
@@ -541,11 +610,8 @@ export class SocialService extends BaseDataService {
|
|
|
541
610
|
headers: authHeaders,
|
|
542
611
|
});
|
|
543
612
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TRADER_FEED_FAILED);
|
|
544
|
-
const feedData = await response.json();
|
|
545
|
-
|
|
546
|
-
throw new Error(SocialServiceErrorMessage.FETCH_TRADER_FEED_INVALID_RESPONSE);
|
|
547
|
-
}
|
|
548
|
-
return feedData;
|
|
613
|
+
const feedData = (await response.json());
|
|
614
|
+
return parseFeedResponse(feedData, SocialServiceErrorMessage.FETCH_TRADER_FEED_INVALID_RESPONSE);
|
|
549
615
|
},
|
|
550
616
|
});
|
|
551
617
|
return traderFeedResponse;
|
|
@@ -600,10 +666,7 @@ export class SocialService extends BaseDataService {
|
|
|
600
666
|
});
|
|
601
667
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_TOKEN_FEED_FAILED);
|
|
602
668
|
const feedData = (await response.json());
|
|
603
|
-
|
|
604
|
-
throw new Error(SocialServiceErrorMessage.FETCH_TOKEN_FEED_INVALID_RESPONSE);
|
|
605
|
-
}
|
|
606
|
-
return feedData;
|
|
669
|
+
return parseFeedResponse(feedData, SocialServiceErrorMessage.FETCH_TOKEN_FEED_INVALID_RESPONSE);
|
|
607
670
|
},
|
|
608
671
|
});
|
|
609
672
|
return tokenFeedResponse;
|
|
@@ -633,7 +696,7 @@ export class SocialService extends BaseDataService {
|
|
|
633
696
|
body: JSON.stringify({ emotion }),
|
|
634
697
|
});
|
|
635
698
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.REACT_TO_COMMENT_FAILED);
|
|
636
|
-
const metrics = await response.json();
|
|
699
|
+
const metrics = (await response.json());
|
|
637
700
|
if (!is(metrics, CommentEngagementStruct)) {
|
|
638
701
|
throw new Error(SocialServiceErrorMessage.REACT_TO_COMMENT_INVALID_RESPONSE);
|
|
639
702
|
}
|
|
@@ -664,7 +727,7 @@ export class SocialService extends BaseDataService {
|
|
|
664
727
|
headers: authHeaders,
|
|
665
728
|
});
|
|
666
729
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.REMOVE_COMMENT_REACTION_FAILED);
|
|
667
|
-
const metrics = await response.json();
|
|
730
|
+
const metrics = (await response.json());
|
|
668
731
|
if (!is(metrics, CommentEngagementStruct)) {
|
|
669
732
|
throw new Error(SocialServiceErrorMessage.REMOVE_COMMENT_REACTION_INVALID_RESPONSE);
|
|
670
733
|
}
|
|
@@ -729,6 +792,10 @@ export class SocialService extends BaseDataService {
|
|
|
729
792
|
* Calls `GET ${baseUrl}/users/me/following`. The caller is identified
|
|
730
793
|
* server-side from the JWT sub claim carried in the Authorization header.
|
|
731
794
|
*
|
|
795
|
+
* Prefer {@link fetchMyFollowing} in new code (pairs with
|
|
796
|
+
* {@link fetchMyFollowers}). This method is unchanged and remains the
|
|
797
|
+
* `/users/me/following` read.
|
|
798
|
+
*
|
|
732
799
|
* @returns The following response.
|
|
733
800
|
*/
|
|
734
801
|
async fetchFollowing() {
|
|
@@ -740,7 +807,7 @@ export class SocialService extends BaseDataService {
|
|
|
740
807
|
const authHeaders = await this.#getAuthHeaders();
|
|
741
808
|
const response = await fetch(url, { headers: authHeaders });
|
|
742
809
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_FOLLOWING_FAILED);
|
|
743
|
-
const followingData = await response.json();
|
|
810
|
+
const followingData = (await response.json());
|
|
744
811
|
if (!is(followingData, FollowingResponseStruct)) {
|
|
745
812
|
throw new Error(SocialServiceErrorMessage.FETCH_FOLLOWING_INVALID_RESPONSE);
|
|
746
813
|
}
|
|
@@ -749,6 +816,43 @@ export class SocialService extends BaseDataService {
|
|
|
749
816
|
});
|
|
750
817
|
return followingResponse;
|
|
751
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* Fetches the list of traders the current user is following.
|
|
821
|
+
*
|
|
822
|
+
* Alias of {@link fetchFollowing}. Calls `GET ${baseUrl}/users/me/following`.
|
|
823
|
+
* Preferred name in new code so `/users/me` reads are both `fetchMy*`.
|
|
824
|
+
*
|
|
825
|
+
* @returns The following response.
|
|
826
|
+
*/
|
|
827
|
+
async fetchMyFollowing() {
|
|
828
|
+
return this.fetchFollowing();
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Fetches the list of traders following the current user.
|
|
832
|
+
*
|
|
833
|
+
* Calls `GET ${baseUrl}/users/me/followers`. The caller is identified
|
|
834
|
+
* server-side from the JWT sub claim carried in the Authorization header.
|
|
835
|
+
*
|
|
836
|
+
* @returns The followers response.
|
|
837
|
+
*/
|
|
838
|
+
async fetchMyFollowers() {
|
|
839
|
+
const followersResponse = await this.fetchQuery({
|
|
840
|
+
queryKey: [`${this.name}:fetchMyFollowers`],
|
|
841
|
+
staleTime: 0,
|
|
842
|
+
queryFn: async () => {
|
|
843
|
+
const url = `${this.#v1Url}/users/me/followers`;
|
|
844
|
+
const authHeaders = await this.#getAuthHeaders();
|
|
845
|
+
const response = await fetch(url, { headers: authHeaders });
|
|
846
|
+
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FETCH_MY_FOLLOWERS_FAILED);
|
|
847
|
+
const followersData = (await response.json());
|
|
848
|
+
if (!is(followersData, FollowersResponseStruct)) {
|
|
849
|
+
throw new Error(SocialServiceErrorMessage.FETCH_MY_FOLLOWERS_INVALID_RESPONSE);
|
|
850
|
+
}
|
|
851
|
+
return followersData;
|
|
852
|
+
},
|
|
853
|
+
});
|
|
854
|
+
return followersResponse;
|
|
855
|
+
}
|
|
752
856
|
/**
|
|
753
857
|
* Follows one or more traders on behalf of the current user.
|
|
754
858
|
*
|
|
@@ -773,7 +877,7 @@ export class SocialService extends BaseDataService {
|
|
|
773
877
|
body: JSON.stringify({ targets }),
|
|
774
878
|
});
|
|
775
879
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.FOLLOW_FAILED);
|
|
776
|
-
const followData = await response.json();
|
|
880
|
+
const followData = (await response.json());
|
|
777
881
|
if (!is(followData, FollowResponseStruct)) {
|
|
778
882
|
throw new Error(SocialServiceErrorMessage.FOLLOW_INVALID_RESPONSE);
|
|
779
883
|
}
|
|
@@ -810,7 +914,7 @@ export class SocialService extends BaseDataService {
|
|
|
810
914
|
headers: authHeaders,
|
|
811
915
|
});
|
|
812
916
|
_a.#throwIfNotOk(response, SocialServiceErrorMessage.UNFOLLOW_FAILED);
|
|
813
|
-
const unfollowData = await response.json();
|
|
917
|
+
const unfollowData = (await response.json());
|
|
814
918
|
if (!is(unfollowData, UnfollowResponseStruct)) {
|
|
815
919
|
throw new Error(SocialServiceErrorMessage.UNFOLLOW_INVALID_RESPONSE);
|
|
816
920
|
}
|
|
@@ -1035,7 +1139,7 @@ export class SocialService extends BaseDataService {
|
|
|
1035
1139
|
headers: authHeaders,
|
|
1036
1140
|
});
|
|
1037
1141
|
_a.#throwIfNotOk(response, failedMessage);
|
|
1038
|
-
const positionsData = await response.json();
|
|
1142
|
+
const positionsData = (await response.json());
|
|
1039
1143
|
if (!is(positionsData, PositionsResponseStruct)) {
|
|
1040
1144
|
throw new Error(invalidMessage);
|
|
1041
1145
|
}
|