@nadohq/mobile-client 0.57.0 → 0.59.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/README.md +6 -1
- package/dist/{MobileClient-DjNpN7Dw.cjs → MobileClient-aDxiM_Kf.cjs} +47 -1
- package/dist/MobileClient-aDxiM_Kf.cjs.map +1 -0
- package/dist/MobileClient.cjs +1 -1
- package/dist/MobileClient.d.cts +40 -6
- package/dist/MobileClient.d.cts.map +1 -1
- package/dist/MobileClient.d.ts +40 -6
- package/dist/MobileClient.d.ts.map +1 -1
- package/dist/MobileClient.js +47 -1
- package/dist/MobileClient.js.map +1 -1
- package/dist/{MobileServerFailureError-kbMi1Ntc.d.cts → MobileServerFailureError-Bpa_WA9f.d.cts} +3 -3
- package/dist/{MobileServerFailureError-kbMi1Ntc.d.cts.map → MobileServerFailureError-Bpa_WA9f.d.cts.map} +1 -1
- package/dist/{MobileServerFailureError-DvtkMAVX.d.ts → MobileServerFailureError-Dd3c6P_Z.d.ts} +3 -3
- package/dist/{MobileServerFailureError-DvtkMAVX.d.ts.map → MobileServerFailureError-Dd3c6P_Z.d.ts.map} +1 -1
- package/dist/{buildSignedMobileRequest-lSIJmG6D.d.ts → buildSignedMobileRequest-Cb1T74xV.d.ts} +2 -2
- package/dist/{buildSignedMobileRequest-lSIJmG6D.d.ts.map → buildSignedMobileRequest-Cb1T74xV.d.ts.map} +1 -1
- package/dist/{buildSignedMobileRequest-DBWN0z0u.d.cts → buildSignedMobileRequest-NHbMAPzd.d.cts} +2 -2
- package/dist/{buildSignedMobileRequest-DBWN0z0u.d.cts.map → buildSignedMobileRequest-NHbMAPzd.d.cts.map} +1 -1
- package/dist/{canonicalize-Cx3lCVpm.d.ts → canonicalize-BBYGh17w.d.ts} +2 -2
- package/dist/canonicalize-BBYGh17w.d.ts.map +1 -0
- package/dist/{canonicalize-DNgB8Jsm.d.cts → canonicalize-BF_SiNvO.d.cts} +2 -2
- package/dist/canonicalize-BF_SiNvO.d.cts.map +1 -0
- package/dist/{clientTypes-DnPE1zZO.d.cts → clientTypes-BWFKo9oE.d.cts} +63 -2
- package/dist/clientTypes-BWFKo9oE.d.cts.map +1 -0
- package/dist/{clientTypes-ws9CDHw4.d.ts → clientTypes-DCTiUV0H.d.ts} +63 -2
- package/dist/clientTypes-DCTiUV0H.d.ts.map +1 -0
- package/dist/dataMappers.cjs +34 -0
- package/dist/dataMappers.cjs.map +1 -1
- package/dist/dataMappers.d.cts +18 -5
- package/dist/dataMappers.d.cts.map +1 -1
- package/dist/dataMappers.d.ts +18 -5
- package/dist/dataMappers.d.ts.map +1 -1
- package/dist/dataMappers.js +32 -1
- package/dist/dataMappers.js.map +1 -1
- package/dist/{eip712MethodByType-T7dfjmC_.d.cts → eip712MethodByType-BSx11xG-.d.cts} +2 -2
- package/dist/eip712MethodByType-BSx11xG-.d.cts.map +1 -0
- package/dist/{eip712MethodByType-Bmu9PYx6.d.ts → eip712MethodByType-DkiAjCLs.d.ts} +2 -2
- package/dist/eip712MethodByType-DkiAjCLs.d.ts.map +1 -0
- package/dist/{follow-D6Mg8GHx.d.ts → follow-B3CZ04c0.d.cts} +7 -2
- package/dist/{follow-D6Mg8GHx.d.cts.map → follow-B3CZ04c0.d.cts.map} +1 -1
- package/dist/{follow-D6Mg8GHx.d.cts → follow-B3CZ04c0.d.ts} +7 -2
- package/dist/{follow-D6Mg8GHx.d.ts.map → follow-B3CZ04c0.d.ts.map} +1 -1
- package/dist/index.cjs +6 -1
- package/dist/index.d.cts +17 -16
- package/dist/index.d.ts +17 -16
- package/dist/index.js +4 -3
- package/dist/{mobileErrorCodes-B3kTLit_.d.ts → mobileErrorCodes-3LtreeZM.d.cts} +14 -3
- package/dist/mobileErrorCodes-3LtreeZM.d.cts.map +1 -0
- package/dist/{mobileErrorCodes-B3kTLit_.d.cts → mobileErrorCodes-3LtreeZM.d.ts} +14 -3
- package/dist/mobileErrorCodes-3LtreeZM.d.ts.map +1 -0
- package/dist/patterns-3UKIG5IS.d.cts +13 -0
- package/dist/patterns-3UKIG5IS.d.cts.map +1 -0
- package/dist/patterns-3UKIG5IS.d.ts +13 -0
- package/dist/patterns-3UKIG5IS.d.ts.map +1 -0
- package/dist/{payloadHash-Brc5ceFq.d.cts → payloadHash-BOWGjqdb.d.cts} +2 -2
- package/dist/payloadHash-BOWGjqdb.d.cts.map +1 -0
- package/dist/{payloadHash-cZgv7NSo.d.ts → payloadHash-Bzf4CtjV.d.ts} +2 -2
- package/dist/payloadHash-Bzf4CtjV.d.ts.map +1 -0
- package/dist/profileSearch-DCuB-IKD.d.cts +14 -0
- package/dist/profileSearch-DCuB-IKD.d.cts.map +1 -0
- package/dist/profileSearch-DCuB-IKD.d.ts +14 -0
- package/dist/profileSearch-DCuB-IKD.d.ts.map +1 -0
- package/dist/{serverBaseTypes-CWBdZomv.d.ts → serverBaseTypes-DWf3iGWy.d.ts} +2 -2
- package/dist/{serverBaseTypes-CWBdZomv.d.ts.map → serverBaseTypes-DWf3iGWy.d.ts.map} +1 -1
- package/dist/{serverBaseTypes-CcWT_ZBe.d.cts → serverBaseTypes-KREDe2M9.d.cts} +2 -2
- package/dist/{serverBaseTypes-CcWT_ZBe.d.cts.map → serverBaseTypes-KREDe2M9.d.cts.map} +1 -1
- package/dist/{serverExecuteTypes-CgjzwBaA.d.cts → serverExecuteTypes-DVRa6ifu.d.cts} +26 -6
- package/dist/serverExecuteTypes-DVRa6ifu.d.cts.map +1 -0
- package/dist/{serverExecuteTypes-jyr5aO5q.d.ts → serverExecuteTypes-DlYZCp1f.d.ts} +26 -6
- package/dist/serverExecuteTypes-DlYZCp1f.d.ts.map +1 -0
- package/dist/{serverQueryTypes-DPRP61yA.d.ts → serverQueryTypes-BFdwq0Z2.d.ts} +14 -4
- package/dist/serverQueryTypes-BFdwq0Z2.d.ts.map +1 -0
- package/dist/{serverQueryTypes-H_mI6fPl.d.cts → serverQueryTypes-DNdMpmDr.d.cts} +14 -4
- package/dist/serverQueryTypes-DNdMpmDr.d.cts.map +1 -0
- package/dist/{serverRequestTypes-BY0f5woc.d.ts → serverRequestTypes-BNA7FCY6.d.ts} +20 -3
- package/dist/serverRequestTypes-BNA7FCY6.d.ts.map +1 -0
- package/dist/{serverRequestTypes-DGGqQMK3.d.cts → serverRequestTypes-CxDRFKfv.d.cts} +20 -3
- package/dist/serverRequestTypes-CxDRFKfv.d.cts.map +1 -0
- package/dist/{serverResponseGuards-BGHTBbtz.d.ts → serverResponseGuards-BU1Xv_tG.d.ts} +2 -2
- package/dist/{serverResponseGuards-BGHTBbtz.d.ts.map → serverResponseGuards-BU1Xv_tG.d.ts.map} +1 -1
- package/dist/{serverResponseGuards-DGhrUL_c.d.cts → serverResponseGuards-Dkdok0Sm.d.cts} +2 -2
- package/dist/{serverResponseGuards-DGhrUL_c.d.cts.map → serverResponseGuards-Dkdok0Sm.d.cts.map} +1 -1
- package/dist/signing/buildSignedMobileRequest.d.cts +1 -1
- package/dist/signing/buildSignedMobileRequest.d.ts +1 -1
- package/dist/signing/canonicalize.cjs +5 -0
- package/dist/signing/canonicalize.cjs.map +1 -1
- package/dist/signing/canonicalize.d.cts +1 -1
- package/dist/signing/canonicalize.d.ts +1 -1
- package/dist/signing/canonicalize.js +5 -0
- package/dist/signing/canonicalize.js.map +1 -1
- package/dist/signing/eip712MethodByType.cjs +2 -1
- package/dist/signing/eip712MethodByType.cjs.map +1 -1
- package/dist/signing/eip712MethodByType.d.cts +1 -1
- package/dist/signing/eip712MethodByType.d.ts +1 -1
- package/dist/signing/eip712MethodByType.js +2 -1
- package/dist/signing/eip712MethodByType.js.map +1 -1
- package/dist/signing/index.d.cts +5 -5
- package/dist/signing/index.d.ts +5 -5
- package/dist/signing/payloadHash.d.cts +1 -1
- package/dist/signing/payloadHash.d.ts +1 -1
- package/dist/signing/types.d.cts +1 -1
- package/dist/signing/types.d.ts +1 -1
- package/dist/types/MobileServerFailureError.d.cts +1 -1
- package/dist/types/MobileServerFailureError.d.ts +1 -1
- package/dist/types/clientTypes.d.cts +2 -2
- package/dist/types/clientTypes.d.ts +2 -2
- package/dist/types/index.d.cts +8 -8
- package/dist/types/index.d.ts +8 -8
- package/dist/types/mobileErrorCodes.cjs +12 -0
- package/dist/types/mobileErrorCodes.cjs.map +1 -1
- package/dist/types/mobileErrorCodes.d.cts +1 -1
- package/dist/types/mobileErrorCodes.d.ts +1 -1
- package/dist/types/mobileErrorCodes.js +12 -0
- package/dist/types/mobileErrorCodes.js.map +1 -1
- package/dist/types/serverBaseTypes.d.cts +1 -1
- package/dist/types/serverBaseTypes.d.ts +1 -1
- package/dist/types/serverExecuteTypes.d.cts +2 -2
- package/dist/types/serverExecuteTypes.d.ts +2 -2
- package/dist/types/serverQueryTypes.d.cts +2 -2
- package/dist/types/serverQueryTypes.d.ts +2 -2
- package/dist/types/serverRequestTypes.d.cts +2 -2
- package/dist/types/serverRequestTypes.d.ts +2 -2
- package/dist/{types-l47aSWtA.d.cts → types-BCr2G5gS.d.cts} +6 -2
- package/dist/types-BCr2G5gS.d.cts.map +1 -0
- package/dist/{types-B021odcS.d.ts → types-C18k-9Vy.d.ts} +6 -2
- package/dist/types-C18k-9Vy.d.ts.map +1 -0
- package/dist/utils/follow.cjs +6 -0
- package/dist/utils/follow.cjs.map +1 -1
- package/dist/utils/follow.d.cts +2 -2
- package/dist/utils/follow.d.ts +2 -2
- package/dist/utils/follow.js +6 -1
- package/dist/utils/follow.js.map +1 -1
- package/dist/utils/index.cjs +5 -0
- package/dist/utils/index.d.cts +5 -4
- package/dist/utils/index.d.ts +5 -4
- package/dist/utils/index.js +4 -3
- package/dist/utils/patterns.cjs +6 -0
- package/dist/utils/patterns.cjs.map +1 -1
- package/dist/utils/patterns.d.cts +2 -2
- package/dist/utils/patterns.d.ts +2 -2
- package/dist/utils/patterns.js +6 -1
- package/dist/utils/patterns.js.map +1 -1
- package/dist/utils/profileSearch.cjs +17 -0
- package/dist/utils/profileSearch.cjs.map +1 -0
- package/dist/utils/profileSearch.d.cts +2 -0
- package/dist/utils/profileSearch.d.ts +2 -0
- package/dist/utils/profileSearch.js +15 -0
- package/dist/utils/profileSearch.js.map +1 -0
- package/dist/utils/serverResponseGuards.d.cts +1 -1
- package/dist/utils/serverResponseGuards.d.ts +1 -1
- package/package.json +3 -3
- package/src/MobileClient.ts +65 -0
- package/src/dataMappers.ts +48 -0
- package/src/signing/canonicalize.ts +6 -0
- package/src/signing/eip712MethodByType.ts +1 -0
- package/src/signing/signing.test.ts +34 -0
- package/src/signing/types.ts +7 -0
- package/src/types/clientTypes.ts +65 -0
- package/src/types/mobileErrorCodes.ts +12 -0
- package/src/types/serverExecuteTypes.ts +24 -2
- package/src/types/serverQueryTypes.ts +11 -0
- package/src/types/serverRequestTypes.ts +18 -0
- package/src/utils/follow.ts +6 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/patterns.ts +7 -0
- package/src/utils/profileSearch.ts +11 -0
- package/dist/MobileClient-DjNpN7Dw.cjs.map +0 -1
- package/dist/canonicalize-Cx3lCVpm.d.ts.map +0 -1
- package/dist/canonicalize-DNgB8Jsm.d.cts.map +0 -1
- package/dist/clientTypes-DnPE1zZO.d.cts.map +0 -1
- package/dist/clientTypes-ws9CDHw4.d.ts.map +0 -1
- package/dist/eip712MethodByType-Bmu9PYx6.d.ts.map +0 -1
- package/dist/eip712MethodByType-T7dfjmC_.d.cts.map +0 -1
- package/dist/mobileErrorCodes-B3kTLit_.d.cts.map +0 -1
- package/dist/mobileErrorCodes-B3kTLit_.d.ts.map +0 -1
- package/dist/patterns-BE0Audp5.d.cts +0 -8
- package/dist/patterns-BE0Audp5.d.cts.map +0 -1
- package/dist/patterns-BE0Audp5.d.ts +0 -8
- package/dist/patterns-BE0Audp5.d.ts.map +0 -1
- package/dist/payloadHash-Brc5ceFq.d.cts.map +0 -1
- package/dist/payloadHash-cZgv7NSo.d.ts.map +0 -1
- package/dist/serverExecuteTypes-CgjzwBaA.d.cts.map +0 -1
- package/dist/serverExecuteTypes-jyr5aO5q.d.ts.map +0 -1
- package/dist/serverQueryTypes-DPRP61yA.d.ts.map +0 -1
- package/dist/serverQueryTypes-H_mI6fPl.d.cts.map +0 -1
- package/dist/serverRequestTypes-BY0f5woc.d.ts.map +0 -1
- package/dist/serverRequestTypes-DGGqQMK3.d.cts.map +0 -1
- package/dist/types-B021odcS.d.ts.map +0 -1
- package/dist/types-l47aSWtA.d.cts.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/utils/profileSearch.ts
|
|
2
|
+
/**
|
|
3
|
+
* Maximum profile search page size without an `include`. Requests outside 1-50 fail with
|
|
4
|
+
* `INVALID_PROFILE_SEARCH_REQUEST`.
|
|
5
|
+
*/
|
|
6
|
+
const MOBILE_PROFILE_SEARCH_MAX_PAGE_SIZE = 50;
|
|
7
|
+
/**
|
|
8
|
+
* Maximum profile search page size when `include` asks for follow data, matching
|
|
9
|
+
* `MOBILE_PROFILES_MAX_BATCH_SIZE`. Requests outside 1-25 fail with `INVALID_PROFILE_SEARCH_REQUEST`.
|
|
10
|
+
*/
|
|
11
|
+
const MOBILE_PROFILE_SEARCH_MAX_ENRICHED_PAGE_SIZE = 25;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { MOBILE_PROFILE_SEARCH_MAX_ENRICHED_PAGE_SIZE, MOBILE_PROFILE_SEARCH_MAX_PAGE_SIZE };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=profileSearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profileSearch.js","names":[],"sources":["../../src/utils/profileSearch.ts"],"sourcesContent":["/**\n * Maximum profile search page size without an `include`. Requests outside 1-50 fail with\n * `INVALID_PROFILE_SEARCH_REQUEST`.\n */\nexport const MOBILE_PROFILE_SEARCH_MAX_PAGE_SIZE = 50;\n\n/**\n * Maximum profile search page size when `include` asks for follow data, matching\n * `MOBILE_PROFILES_MAX_BATCH_SIZE`. Requests outside 1-25 fail with `INVALID_PROFILE_SEARCH_REQUEST`.\n */\nexport const MOBILE_PROFILE_SEARCH_MAX_ENRICHED_PAGE_SIZE = 25;\n"],"mappings":";;;;;AAIA,MAAa,sCAAsC;;;;;AAMnD,MAAa,+CAA+C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as isMobileServerSuccessResponse, t as isMobileServerFailureResponse } from "../serverResponseGuards-
|
|
1
|
+
import { n as isMobileServerSuccessResponse, t as isMobileServerFailureResponse } from "../serverResponseGuards-Dkdok0Sm.cjs";
|
|
2
2
|
export { isMobileServerFailureResponse, isMobileServerSuccessResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as isMobileServerSuccessResponse, t as isMobileServerFailureResponse } from "../serverResponseGuards-
|
|
1
|
+
import { n as isMobileServerSuccessResponse, t as isMobileServerFailureResponse } from "../serverResponseGuards-BU1Xv_tG.js";
|
|
2
2
|
export { isMobileServerFailureResponse, isMobileServerSuccessResponse };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nadohq/mobile-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "HTTP client for the Nado mobile service API — username claims, profiles, privacy settings, and push notification devices & preferences",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"types": "./dist/index.d.ts",
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@msgpack/msgpack": "3.1.3",
|
|
45
|
-
"@nadohq/shared": "^0.
|
|
45
|
+
"@nadohq/shared": "^0.59.0",
|
|
46
46
|
"axios": "1.16.1"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"bignumber.js": "11.1.1",
|
|
55
55
|
"viem": "2.52.0"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "48af19e135f200eb240e406fdbdb26632970492b"
|
|
58
58
|
}
|
package/src/MobileClient.ts
CHANGED
|
@@ -10,11 +10,14 @@ import {
|
|
|
10
10
|
mapMobileAvatarSelectionToServer,
|
|
11
11
|
mapMobileAvatarUpload,
|
|
12
12
|
mapMobileFeedPage,
|
|
13
|
+
mapMobileFollowBatchResult,
|
|
13
14
|
mapMobileFollowListPage,
|
|
14
15
|
mapMobileFollowListParamsToServer,
|
|
15
16
|
mapMobileFollowMutationResult,
|
|
16
17
|
mapMobileNotificationPreferences,
|
|
17
18
|
mapMobileNotificationPreferencesToServer,
|
|
19
|
+
mapMobileProfileSearchPage,
|
|
20
|
+
mapMobileProfileSearchParamsToServer,
|
|
18
21
|
mapMobileProfilesIncludeToServer,
|
|
19
22
|
mapMobilePublicProfile,
|
|
20
23
|
mapMobileRegisteredWallet,
|
|
@@ -53,14 +56,17 @@ import {
|
|
|
53
56
|
MobileAvatarUpload,
|
|
54
57
|
MobileCreateAvatarUploadParams,
|
|
55
58
|
MobileFeedPage,
|
|
59
|
+
MobileFollowBatchResultItem,
|
|
56
60
|
MobileFollowListPage,
|
|
57
61
|
MobileFollowMutationResult,
|
|
58
62
|
MobileNotificationPreferences,
|
|
63
|
+
MobileProfileSearchPage,
|
|
59
64
|
MobilePublicProfile,
|
|
60
65
|
MobileRegisteredWallet,
|
|
61
66
|
MobileRegisterExpoTokenParams,
|
|
62
67
|
MobileSetAvatarParams,
|
|
63
68
|
MobileSetBioParams,
|
|
69
|
+
MobileSetFollowBatchParams,
|
|
64
70
|
MobileSetFollowParams,
|
|
65
71
|
MobileSetPrivateModeParams,
|
|
66
72
|
MobileSetUsernameParams,
|
|
@@ -73,6 +79,7 @@ import {
|
|
|
73
79
|
MobileUnregisterExpoTokenParams,
|
|
74
80
|
MobileUpdateNotificationPreferencesParams,
|
|
75
81
|
MobileUsernameAvailability,
|
|
82
|
+
SearchMobileProfilesParams,
|
|
76
83
|
} from './types/clientTypes';
|
|
77
84
|
import { MobileServerFailureError } from './types/MobileServerFailureError';
|
|
78
85
|
import { MobileServerSuccessResponse } from './types/serverBaseTypes';
|
|
@@ -206,6 +213,33 @@ export class MobileClient {
|
|
|
206
213
|
return data.profiles.map(mapMobilePublicProfile);
|
|
207
214
|
}
|
|
208
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Searches claimed profiles by the start of their username, in ascending username order with an exact
|
|
218
|
+
* match first. Only current claimed identities match: an unnamed profile never does, and Private Mode does
|
|
219
|
+
* not hide one. Takes the same `include` as {@link MobileClient.getProfiles}, so a page can carry follower
|
|
220
|
+
* totals and a follow summary without a second request; as there, `include.followSummary.viewAs` is an
|
|
221
|
+
* unauthenticated claim on an unsigned route.
|
|
222
|
+
*
|
|
223
|
+
* Pagination is live rather than snapshot, so deduplicate matches by subaccount across pages. A cursor is
|
|
224
|
+
* bound to the query alone, so `include` and `limit` may change between pages.
|
|
225
|
+
*
|
|
226
|
+
* @throws {MobileServerFailureError} With error code `INVALID_PROFILE_SEARCH_REQUEST` if `query` is outside
|
|
227
|
+
* the username alphabet or length or `limit` is out of range (1-50, or 1-25 with an include),
|
|
228
|
+
* `INVALID_PROFILE_SEARCH_CURSOR` if the cursor is malformed or was issued for a different query (discard
|
|
229
|
+
* it and restart from the first page), or `PROFILE_NOT_FOUND` if `include.followSummary.viewAs` is in the
|
|
230
|
+
* engine-created isolated namespace.
|
|
231
|
+
*/
|
|
232
|
+
async searchProfiles(
|
|
233
|
+
params: SearchMobileProfilesParams,
|
|
234
|
+
): Promise<MobileProfileSearchPage> {
|
|
235
|
+
const body: MobileServerPublicQueryRequest<'search_profiles'> = {
|
|
236
|
+
type: 'search_profiles',
|
|
237
|
+
...mapMobileProfileSearchParamsToServer(params),
|
|
238
|
+
};
|
|
239
|
+
const data = await this.publicQuery(body);
|
|
240
|
+
return mapMobileProfileSearchPage(data);
|
|
241
|
+
}
|
|
242
|
+
|
|
209
243
|
/**
|
|
210
244
|
* Fetches a page of the accounts that follow the given Profile.
|
|
211
245
|
*
|
|
@@ -675,6 +709,37 @@ export class MobileClient {
|
|
|
675
709
|
return mapMobileFollowMutationResult(data);
|
|
676
710
|
}
|
|
677
711
|
|
|
712
|
+
/**
|
|
713
|
+
* Sets whether the signing subaccount follows each of up to `MOBILE_FOLLOW_BATCH_MAX_SIZE` (25) Profiles in
|
|
714
|
+
* one signed request, which costs far less of the shared IP rate limit than one {@link MobileClient.setFollow}
|
|
715
|
+
* per target. The server validates the whole list before writing and commits every target in one database
|
|
716
|
+
* transaction, so the batch either fully applies or changes nothing. Each target has the same idempotent
|
|
717
|
+
* semantics as `setFollow`, so resending a batch is safe.
|
|
718
|
+
*
|
|
719
|
+
* Targets are lowercased before signing but not deduplicated, and two spellings of one subaccount are a
|
|
720
|
+
* duplicate. Longer lists must be split by the caller; each request commits on its own, so earlier chunks
|
|
721
|
+
* stay applied when a later one fails.
|
|
722
|
+
*
|
|
723
|
+
* @returns One result per target, in request order, carrying each target's post-commit follower count.
|
|
724
|
+
* @throws {MobileServerFailureError} With error code `INVALID_FOLLOW_BATCH` if `targets` is empty, holds
|
|
725
|
+
* duplicates, or exceeds the maximum, or `INVALID_FOLLOW_TARGET` if any target is the sender or either party
|
|
726
|
+
* is isolated. Either rejects the whole batch before any write, so do not retry it unchanged.
|
|
727
|
+
*/
|
|
728
|
+
async setFollowBatch(
|
|
729
|
+
params: MobileSetFollowBatchParams,
|
|
730
|
+
): Promise<MobileFollowBatchResultItem[]> {
|
|
731
|
+
const signedRequest = await this.getSignedRequest(
|
|
732
|
+
'set_follow_batch',
|
|
733
|
+
params,
|
|
734
|
+
{
|
|
735
|
+
subaccounts: params.targets.map(subaccountToHex),
|
|
736
|
+
is_following: params.isFollowing,
|
|
737
|
+
},
|
|
738
|
+
);
|
|
739
|
+
const data = await this.execute(signedRequest);
|
|
740
|
+
return mapMobileFollowBatchResult(data);
|
|
741
|
+
}
|
|
742
|
+
|
|
678
743
|
/*
|
|
679
744
|
Base Fns
|
|
680
745
|
*/
|
package/src/dataMappers.ts
CHANGED
|
@@ -6,12 +6,14 @@ import {
|
|
|
6
6
|
MobileAvatarUpload,
|
|
7
7
|
MobileFeedPage,
|
|
8
8
|
MobileFeedTrade,
|
|
9
|
+
MobileFollowBatchResultItem,
|
|
9
10
|
MobileFollowListPage,
|
|
10
11
|
MobileFollowMutationResult,
|
|
11
12
|
MobileFollowSummary,
|
|
12
13
|
MobileIdentitySummary,
|
|
13
14
|
MobileNotificationPreferenceScope,
|
|
14
15
|
MobileNotificationPreferences,
|
|
16
|
+
MobileProfileSearchPage,
|
|
15
17
|
MobileProfilesInclude,
|
|
16
18
|
MobilePublicProfile,
|
|
17
19
|
MobileRegisteredWallet,
|
|
@@ -36,9 +38,11 @@ import {
|
|
|
36
38
|
MobileSocialFeedTradeRowBase,
|
|
37
39
|
MobileSocialFeedTraderBadge,
|
|
38
40
|
MobileSocialFeedUpdates,
|
|
41
|
+
SearchMobileProfilesParams,
|
|
39
42
|
} from './types/clientTypes';
|
|
40
43
|
import {
|
|
41
44
|
MobileServerAvatarUploadResponse,
|
|
45
|
+
MobileServerFollowBatchResponse,
|
|
42
46
|
MobileServerFollowMutationResponse,
|
|
43
47
|
} from './types/serverExecuteTypes';
|
|
44
48
|
import {
|
|
@@ -69,6 +73,7 @@ import {
|
|
|
69
73
|
MobileServerFeedResponse,
|
|
70
74
|
MobileServerFollowListResponse,
|
|
71
75
|
MobileServerRegisteredWalletResponse,
|
|
76
|
+
MobileServerSearchProfilesResponse,
|
|
72
77
|
MobileServerSocialFeedItemResponse,
|
|
73
78
|
MobileServerSocialFeedRankedResponse,
|
|
74
79
|
MobileServerSocialFeedResponse,
|
|
@@ -78,6 +83,7 @@ import {
|
|
|
78
83
|
import {
|
|
79
84
|
MobileServerFollowListRequest,
|
|
80
85
|
MobileServerProfilesInclude,
|
|
86
|
+
MobileServerSearchProfilesRequest,
|
|
81
87
|
MobileServerSocialFeedListParams,
|
|
82
88
|
MobileServerSocialFeedReadParams,
|
|
83
89
|
} from './types/serverRequestTypes';
|
|
@@ -184,6 +190,19 @@ export function mapMobileFollowListPage(
|
|
|
184
190
|
};
|
|
185
191
|
}
|
|
186
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Maps a server-side profile search page (snake_case) to its client-side (camelCase) representation. Each
|
|
195
|
+
* match goes through {@link mapMobilePublicProfile}, so include-gated fields stay `undefined` when omitted.
|
|
196
|
+
*/
|
|
197
|
+
export function mapMobileProfileSearchPage(
|
|
198
|
+
server: MobileServerSearchProfilesResponse,
|
|
199
|
+
): MobileProfileSearchPage {
|
|
200
|
+
return {
|
|
201
|
+
profiles: server.profiles.map(mapMobilePublicProfile),
|
|
202
|
+
nextCursor: server.next_cursor,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
187
206
|
/**
|
|
188
207
|
* Maps a server-side follow or unfollow response to its client-side representation.
|
|
189
208
|
*/
|
|
@@ -196,6 +215,19 @@ export function mapMobileFollowMutationResult(
|
|
|
196
215
|
};
|
|
197
216
|
}
|
|
198
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Maps a server-side batch follow or unfollow response to its client-side representation.
|
|
220
|
+
*/
|
|
221
|
+
export function mapMobileFollowBatchResult(
|
|
222
|
+
server: MobileServerFollowBatchResponse,
|
|
223
|
+
): MobileFollowBatchResultItem[] {
|
|
224
|
+
return server.results.map((result) => ({
|
|
225
|
+
subaccount: result.subaccount,
|
|
226
|
+
isFollowing: result.is_following,
|
|
227
|
+
followerCount: result.follower_count,
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
|
|
199
231
|
/**
|
|
200
232
|
* Maps server-side notification preferences (snake_case) to their client-side (camelCase) representation.
|
|
201
233
|
*/
|
|
@@ -250,6 +282,22 @@ export function mapMobileFollowListParamsToServer(
|
|
|
250
282
|
};
|
|
251
283
|
}
|
|
252
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Maps the client-side params of a profile search page to the server-side wire shape.
|
|
287
|
+
*/
|
|
288
|
+
export function mapMobileProfileSearchParamsToServer(
|
|
289
|
+
params: SearchMobileProfilesParams,
|
|
290
|
+
): MobileServerSearchProfilesRequest {
|
|
291
|
+
return {
|
|
292
|
+
query: params.query,
|
|
293
|
+
include: params.include
|
|
294
|
+
? mapMobileProfilesIncludeToServer(params.include)
|
|
295
|
+
: undefined,
|
|
296
|
+
cursor: params.cursor,
|
|
297
|
+
limit: params.limit,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
253
301
|
/**
|
|
254
302
|
* Maps client-side notification preferences (camelCase) to the server-side (snake_case) wire shape.
|
|
255
303
|
*/
|
|
@@ -49,6 +49,12 @@ export function canonicalizeMobileInner(
|
|
|
49
49
|
subaccount: inner.subaccount,
|
|
50
50
|
is_following: inner.is_following,
|
|
51
51
|
};
|
|
52
|
+
case 'set_follow_batch':
|
|
53
|
+
return {
|
|
54
|
+
type: 'set_follow_batch',
|
|
55
|
+
subaccounts: [...inner.subaccounts],
|
|
56
|
+
is_following: inner.is_following,
|
|
57
|
+
};
|
|
52
58
|
default: {
|
|
53
59
|
throw new Error(
|
|
54
60
|
`canonicalizeMobileInner: unhandled inner payload type: ${JSON.stringify(inner)}`,
|
|
@@ -14,4 +14,5 @@ export const MOBILE_EIP712_METHOD_BY_TYPE: Record<
|
|
|
14
14
|
set_private_mode: 'mobile:execute_set_private_mode',
|
|
15
15
|
register_expo_token: 'mobile:execute_register_expo_token',
|
|
16
16
|
set_follow: 'mobile:execute_set_follow',
|
|
17
|
+
set_follow_batch: 'mobile:execute_set_follow_batch',
|
|
17
18
|
};
|
|
@@ -41,12 +41,20 @@ const PINNED_HASHES = {
|
|
|
41
41
|
'0x1ff8529b7c1a0111313eed98536e591555a3bcab51133776ce30c6ef32b559b3',
|
|
42
42
|
set_follow_false:
|
|
43
43
|
'0xec9e9e8f32ac4fcf3015f8b7f20c338623ff85db0488703c7fb688519a46e2e5',
|
|
44
|
+
set_follow_batch_true:
|
|
45
|
+
'0x0d86ef133ad5db8968911d102c48599691dcc40094ace985484c3f7e2cb3a87d',
|
|
46
|
+
set_follow_batch_false:
|
|
47
|
+
'0x02272ef5f6c7f9b315a892240af614f9cbbcb1edc69538db37ae1409549ff2fa',
|
|
44
48
|
} as const;
|
|
45
49
|
|
|
46
50
|
// Every follow fixture on the backend targets this subaccount.
|
|
47
51
|
const FIXTURE_TARGET_SUBACCOUNT =
|
|
48
52
|
'0x1111111111111111111111111111111111111111111111111111111111111111' as const;
|
|
49
53
|
|
|
54
|
+
// Batch follow fixtures target the subaccount above, then this one.
|
|
55
|
+
const FIXTURE_SECOND_TARGET_SUBACCOUNT =
|
|
56
|
+
'0x2222222222222222222222222222222222222222222222222222222222222222' as const;
|
|
57
|
+
|
|
50
58
|
describe('[mobile-client]: signing (offline)', () => {
|
|
51
59
|
describe('pinned payload hashes', () => {
|
|
52
60
|
it('set_bio', () => {
|
|
@@ -145,6 +153,32 @@ describe('[mobile-client]: signing (offline)', () => {
|
|
|
145
153
|
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
146
154
|
expect(hash).toBe(PINNED_HASHES.set_follow_false);
|
|
147
155
|
});
|
|
156
|
+
|
|
157
|
+
it('set_follow_batch (follow)', () => {
|
|
158
|
+
const inner: MobileSignedInner = {
|
|
159
|
+
type: 'set_follow_batch',
|
|
160
|
+
subaccounts: [
|
|
161
|
+
FIXTURE_TARGET_SUBACCOUNT,
|
|
162
|
+
FIXTURE_SECOND_TARGET_SUBACCOUNT,
|
|
163
|
+
],
|
|
164
|
+
is_following: true,
|
|
165
|
+
};
|
|
166
|
+
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
167
|
+
expect(hash).toBe(PINNED_HASHES.set_follow_batch_true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('set_follow_batch (unfollow)', () => {
|
|
171
|
+
const inner: MobileSignedInner = {
|
|
172
|
+
type: 'set_follow_batch',
|
|
173
|
+
subaccounts: [
|
|
174
|
+
FIXTURE_TARGET_SUBACCOUNT,
|
|
175
|
+
FIXTURE_SECOND_TARGET_SUBACCOUNT,
|
|
176
|
+
],
|
|
177
|
+
is_following: false,
|
|
178
|
+
};
|
|
179
|
+
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
180
|
+
expect(hash).toBe(PINNED_HASHES.set_follow_batch_false);
|
|
181
|
+
});
|
|
148
182
|
});
|
|
149
183
|
|
|
150
184
|
it('canonicalizes nested avatar fields before hashing', () => {
|
package/src/signing/types.ts
CHANGED
|
@@ -43,6 +43,13 @@ export type MobileSignedInner =
|
|
|
43
43
|
subaccount: Hex;
|
|
44
44
|
/** `true` follows the target, `false` unfollows it. Both directions are idempotent. */
|
|
45
45
|
is_following: boolean;
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
type: 'set_follow_batch';
|
|
49
|
+
/** Distinct targets in request order; the response keeps this order. */
|
|
50
|
+
subaccounts: Hex[];
|
|
51
|
+
/** `true` follows every target, `false` unfollows every target. Both directions are idempotent. */
|
|
52
|
+
is_following: boolean;
|
|
46
53
|
};
|
|
47
54
|
|
|
48
55
|
/**
|
package/src/types/clientTypes.ts
CHANGED
|
@@ -149,6 +149,18 @@ export interface MobileFollowListPage {
|
|
|
149
149
|
nextCursor: string | null;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* A page of profiles whose username starts with the searched prefix. Pagination is live rather than snapshot:
|
|
154
|
+
* a claim or rename between pages can surface a profile twice or skip it, so deduplicate by
|
|
155
|
+
* {@link MobilePublicProfile.subaccount} and restart from `cursor: undefined` on refresh.
|
|
156
|
+
*/
|
|
157
|
+
export interface MobileProfileSearchPage {
|
|
158
|
+
/** Matches in ascending bytewise username order, with an exact match ahead of longer ones. */
|
|
159
|
+
profiles: MobilePublicProfile[];
|
|
160
|
+
/** `null` means there are no further matches. */
|
|
161
|
+
nextCursor: string | null;
|
|
162
|
+
}
|
|
163
|
+
|
|
152
164
|
/**
|
|
153
165
|
* Post-commit state of a follow or unfollow. The mutation and its count share one database transaction, so
|
|
154
166
|
* these values are authoritative — apply them rather than re-reading.
|
|
@@ -159,6 +171,18 @@ export interface MobileFollowMutationResult {
|
|
|
159
171
|
followerCount: number;
|
|
160
172
|
}
|
|
161
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Post-commit state of one target in a batch follow or unfollow. The whole batch commits in one database
|
|
176
|
+
* transaction, so these values are authoritative — apply them rather than re-reading.
|
|
177
|
+
*/
|
|
178
|
+
export interface MobileFollowBatchResultItem {
|
|
179
|
+
/** The requested target, as lowercase hex. */
|
|
180
|
+
subaccount: Hex;
|
|
181
|
+
isFollowing: boolean;
|
|
182
|
+
/** The *target's* follower count after the batch, not the sender's. */
|
|
183
|
+
followerCount: number;
|
|
184
|
+
}
|
|
185
|
+
|
|
162
186
|
/**
|
|
163
187
|
* Result of a username availability check.
|
|
164
188
|
*/
|
|
@@ -312,6 +336,33 @@ export interface GetMobileProfilesParams {
|
|
|
312
336
|
include?: MobileProfilesInclude;
|
|
313
337
|
}
|
|
314
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Params for {@link MobileClient.searchProfiles}.
|
|
341
|
+
*/
|
|
342
|
+
export interface SearchMobileProfilesParams {
|
|
343
|
+
/**
|
|
344
|
+
* Username prefix to match, 1-20 ASCII characters matching `MOBILE_PROFILE_SEARCH_QUERY_PATTERN`: a letter
|
|
345
|
+
* or digit first, then letters, digits, `_` or `.`. Letters match case-insensitively; whitespace is not
|
|
346
|
+
* trimmed, so normalize the input before sending it.
|
|
347
|
+
*/
|
|
348
|
+
query: string;
|
|
349
|
+
/** Same opt-in extras as {@link GetMobileProfilesParams.include}, applied to every match on the page. */
|
|
350
|
+
include?: MobileProfilesInclude;
|
|
351
|
+
/**
|
|
352
|
+
* Opaque cursor from the previous page's {@link MobileProfileSearchPage.nextCursor}. Omit for the first
|
|
353
|
+
* page. A cursor is bound to its normalized query but not to `include` or `limit`, so an enriched page
|
|
354
|
+
* can continue an unenriched search; one issued for another query fails with
|
|
355
|
+
* `INVALID_PROFILE_SEARCH_CURSOR`.
|
|
356
|
+
*/
|
|
357
|
+
cursor?: string;
|
|
358
|
+
/**
|
|
359
|
+
* Page size, 1-`MOBILE_PROFILE_SEARCH_MAX_PAGE_SIZE` (50), or
|
|
360
|
+
* 1-`MOBILE_PROFILE_SEARCH_MAX_ENRICHED_PAGE_SIZE` (25) when `include` asks for follow data; the backend
|
|
361
|
+
* defaults to 25 when omitted.
|
|
362
|
+
*/
|
|
363
|
+
limit?: number;
|
|
364
|
+
}
|
|
365
|
+
|
|
315
366
|
/**
|
|
316
367
|
* Params for {@link MobileClient.getFeed}.
|
|
317
368
|
*/
|
|
@@ -380,6 +431,20 @@ export interface MobileSetFollowParams extends MobileSignedRequestParams {
|
|
|
380
431
|
isFollowing: boolean;
|
|
381
432
|
}
|
|
382
433
|
|
|
434
|
+
/**
|
|
435
|
+
* Params for {@link MobileClient.setFollowBatch}. The subaccount carried by {@link MobileSignedRequestParams}
|
|
436
|
+
* is the signing Follower; `targets` are the Profiles on the other end of each relationship.
|
|
437
|
+
*/
|
|
438
|
+
export interface MobileSetFollowBatchParams extends MobileSignedRequestParams {
|
|
439
|
+
/**
|
|
440
|
+
* 1 to `MOBILE_FOLLOW_BATCH_MAX_SIZE` (25) distinct Profiles, none of them the signing subaccount or an
|
|
441
|
+
* isolated subaccount. The results keep this order.
|
|
442
|
+
*/
|
|
443
|
+
targets: Subaccount[];
|
|
444
|
+
/** `true` follows every target, `false` unfollows every target. Both directions are idempotent. */
|
|
445
|
+
isFollowing: boolean;
|
|
446
|
+
}
|
|
447
|
+
|
|
383
448
|
/**
|
|
384
449
|
* Common params for a Followers or Following page.
|
|
385
450
|
*/
|
|
@@ -15,6 +15,16 @@ export const MOBILE_ERROR_CODES = {
|
|
|
15
15
|
PROFILE_NOT_FOUND: 6106,
|
|
16
16
|
/** Profiles lookup is empty, holds duplicates, or exceeds the batch maximum. Do not retry unchanged. */
|
|
17
17
|
INVALID_PROFILES_REQUEST: 6107,
|
|
18
|
+
/**
|
|
19
|
+
* Profile search query is outside the username alphabet or length, or its limit is out of range. Do not
|
|
20
|
+
* retry unchanged.
|
|
21
|
+
*/
|
|
22
|
+
INVALID_PROFILE_SEARCH_REQUEST: 6108,
|
|
23
|
+
/**
|
|
24
|
+
* Profile search cursor is malformed, oversized, or was issued for a different query. Restart from
|
|
25
|
+
* `cursor: undefined`.
|
|
26
|
+
*/
|
|
27
|
+
INVALID_PROFILE_SEARCH_CURSOR: 6109,
|
|
18
28
|
/** Follow mutation names the same source and target, or an isolated participant. Do not retry unchanged. */
|
|
19
29
|
INVALID_FOLLOW_TARGET: 6110,
|
|
20
30
|
/** Signed follower is not yet in the canonical Query DB subaccount index. Retry after index catch-up. */
|
|
@@ -29,6 +39,8 @@ export const MOBILE_ERROR_CODES = {
|
|
|
29
39
|
INVALID_BIO: 6115,
|
|
30
40
|
/** Invalid preset, upload size, staging key, or image. Correct the selection before retrying. */
|
|
31
41
|
INVALID_AVATAR: 6116,
|
|
42
|
+
/** Follow batch is empty, holds duplicates, or exceeds the batch maximum. Do not retry unchanged. */
|
|
43
|
+
INVALID_FOLLOW_BATCH: 6117,
|
|
32
44
|
INVALID_EXPO_TOKEN: 6200,
|
|
33
45
|
INVALID_DEVICE_METADATA: 6201,
|
|
34
46
|
INVALID_PREFERENCES: 6202,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Hex } from 'viem';
|
|
1
2
|
import {
|
|
2
3
|
MobileServerFailureResponse,
|
|
3
4
|
MobileServerSuccessResponse,
|
|
@@ -32,6 +33,26 @@ export interface MobileServerFollowMutationResponse {
|
|
|
32
33
|
follower_count: number;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* One target's post-commit state in a `set_follow_batch` response.
|
|
38
|
+
*/
|
|
39
|
+
export interface MobileServerFollowBatchResultItem {
|
|
40
|
+
/** The requested target, as lowercase hex. */
|
|
41
|
+
subaccount: Hex;
|
|
42
|
+
is_following: boolean;
|
|
43
|
+
/** The *target's* follower count after the batch, not the sender's. */
|
|
44
|
+
follower_count: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Payload the `set_follow_batch` execute returns alongside the success envelope: one result per target, in
|
|
49
|
+
* request order. Every write and count runs in one database transaction, so the batch either fully applies or
|
|
50
|
+
* changes nothing, and these values are authoritative post-commit state.
|
|
51
|
+
*/
|
|
52
|
+
export interface MobileServerFollowBatchResponse {
|
|
53
|
+
results: MobileServerFollowBatchResultItem[];
|
|
54
|
+
}
|
|
55
|
+
|
|
35
56
|
/**
|
|
36
57
|
* Payload of an execute route that returns nothing beyond the success discriminant. Spelled `Record<never,
|
|
37
58
|
* never>` rather than `Record<string, never>` because the latter's index signature would collapse `status` to
|
|
@@ -55,8 +76,8 @@ export interface MobileServerAvatarUploadResponse {
|
|
|
55
76
|
|
|
56
77
|
/**
|
|
57
78
|
* Success payloads for each signed `execute`, keyed by request `type`. Most execute routes carry no payload
|
|
58
|
-
* beyond the success discriminant and so map to an empty object; `set_follow`
|
|
59
|
-
* relationship state, and `create_avatar_upload` returns a temporary upload grant.
|
|
79
|
+
* beyond the success discriminant and so map to an empty object; `set_follow` and `set_follow_batch` return
|
|
80
|
+
* their post-commit relationship state, and `create_avatar_upload` returns a temporary upload grant.
|
|
60
81
|
*/
|
|
61
82
|
export interface MobileServerExecuteResponseByType {
|
|
62
83
|
/** Bio writes return only the success envelope. */
|
|
@@ -69,6 +90,7 @@ export interface MobileServerExecuteResponseByType {
|
|
|
69
90
|
set_private_mode: MobileServerEmptyExecuteResponse;
|
|
70
91
|
register_expo_token: MobileServerEmptyExecuteResponse;
|
|
71
92
|
set_follow: MobileServerFollowMutationResponse;
|
|
93
|
+
set_follow_batch: MobileServerFollowBatchResponse;
|
|
72
94
|
}
|
|
73
95
|
|
|
74
96
|
/**
|
|
@@ -55,6 +55,16 @@ export interface MobileServerProfilesResponse {
|
|
|
55
55
|
profiles: MobileServerProfile[];
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Payload of the `search_profiles` public query success response. Matches come in ascending bytewise
|
|
60
|
+
* username order, an exact match first, and carry the same include-gated fields as a `profiles` entry.
|
|
61
|
+
*/
|
|
62
|
+
export interface MobileServerSearchProfilesResponse {
|
|
63
|
+
profiles: MobileServerProfile[];
|
|
64
|
+
/** `null` means there are no further matches. */
|
|
65
|
+
next_cursor: string | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
/**
|
|
59
69
|
* Payload of the `followers` and `following` public query success responses. Both list directions share one
|
|
60
70
|
* shape; only the relationship that selected the rows differs.
|
|
@@ -177,6 +187,7 @@ export interface MobileServerPublicQueryResponseByType {
|
|
|
177
187
|
feed_updates: MobileServerSocialFeedUpdatesResponse;
|
|
178
188
|
username_availability: MobileServerUsernameAvailabilityResponse;
|
|
179
189
|
profiles: MobileServerProfilesResponse;
|
|
190
|
+
search_profiles: MobileServerSearchProfilesResponse;
|
|
180
191
|
followers: MobileServerFollowListResponse;
|
|
181
192
|
following: MobileServerFollowListResponse;
|
|
182
193
|
feed: MobileServerFeedResponse;
|
|
@@ -40,6 +40,23 @@ export interface MobileServerFollowListRequest {
|
|
|
40
40
|
limit?: number;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Params of the `search_profiles` public query.
|
|
45
|
+
*/
|
|
46
|
+
export interface MobileServerSearchProfilesRequest {
|
|
47
|
+
/**
|
|
48
|
+
* Username prefix, 1-20 ASCII bytes: a letter or digit first, then letters, digits, `_` or `.`. The
|
|
49
|
+
* backend lowercases letters and trims nothing.
|
|
50
|
+
*/
|
|
51
|
+
query: string;
|
|
52
|
+
/** Same opt-in extras as the `profiles` query, applied to every match on the page. */
|
|
53
|
+
include?: MobileServerProfilesInclude;
|
|
54
|
+
/** Opaque keyset cursor from a prior page's `next_cursor`. Omitted requests the first page. */
|
|
55
|
+
cursor?: string;
|
|
56
|
+
/** Page size, 1-50, or 1-25 when `include` asks for follow data; the backend defaults to 25 when omitted. */
|
|
57
|
+
limit?: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
/** Common filters and pagination for social feed lists. */
|
|
44
61
|
export interface MobileServerSocialFeedListParams {
|
|
45
62
|
/** OR-ed content filters; combined with other filters using AND. */
|
|
@@ -144,6 +161,7 @@ export interface MobileServerPublicQueryRequestByType {
|
|
|
144
161
|
subaccounts: Hex[];
|
|
145
162
|
include?: MobileServerProfilesInclude;
|
|
146
163
|
};
|
|
164
|
+
search_profiles: MobileServerSearchProfilesRequest;
|
|
147
165
|
followers: MobileServerFollowListRequest;
|
|
148
166
|
following: MobileServerFollowListRequest;
|
|
149
167
|
feed: {
|
package/src/utils/follow.ts
CHANGED
|
@@ -20,3 +20,9 @@ export const MOBILE_FOLLOWED_BY_PREVIEW_LIMIT = 2;
|
|
|
20
20
|
* this many fail with `INVALID_PROFILES_REQUEST`.
|
|
21
21
|
*/
|
|
22
22
|
export const MOBILE_PROFILES_MAX_BATCH_SIZE = 25;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Maximum number of targets a single follow batch accepts. An empty list, a duplicate, or more than this many
|
|
26
|
+
* fail with `INVALID_FOLLOW_BATCH`.
|
|
27
|
+
*/
|
|
28
|
+
export const MOBILE_FOLLOW_BATCH_MAX_SIZE = 25;
|
package/src/utils/index.ts
CHANGED
package/src/utils/patterns.ts
CHANGED
|
@@ -2,3 +2,10 @@
|
|
|
2
2
|
* Validates a display name: 3-20 ASCII characters from `[A-Za-z0-9_.]`, allowed in any position.
|
|
3
3
|
*/
|
|
4
4
|
export const MOBILE_DISPLAY_NAME_PATTERN = /^[A-Za-z0-9_.]{3,20}$/;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validates a profile search query: 1-20 ASCII characters, a letter or digit first, then letters, digits, `_`
|
|
8
|
+
* or `.`. The backend lowercases letters and trims nothing, so a prefix shorter than a full username matches.
|
|
9
|
+
*/
|
|
10
|
+
export const MOBILE_PROFILE_SEARCH_QUERY_PATTERN =
|
|
11
|
+
/^[A-Za-z0-9][A-Za-z0-9_.]{0,19}$/;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum profile search page size without an `include`. Requests outside 1-50 fail with
|
|
3
|
+
* `INVALID_PROFILE_SEARCH_REQUEST`.
|
|
4
|
+
*/
|
|
5
|
+
export const MOBILE_PROFILE_SEARCH_MAX_PAGE_SIZE = 50;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Maximum profile search page size when `include` asks for follow data, matching
|
|
9
|
+
* `MOBILE_PROFILES_MAX_BATCH_SIZE`. Requests outside 1-25 fail with `INVALID_PROFILE_SEARCH_REQUEST`.
|
|
10
|
+
*/
|
|
11
|
+
export const MOBILE_PROFILE_SEARCH_MAX_ENRICHED_PAGE_SIZE = 25;
|