@nadohq/mobile-client 0.35.0 → 0.37.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.
Files changed (189) hide show
  1. package/README.md +19 -6
  2. package/dist/{MobileClient-D5TNTnLx.cjs → MobileClient-C9S_o_eC.cjs} +82 -12
  3. package/dist/MobileClient-C9S_o_eC.cjs.map +1 -0
  4. package/dist/MobileClient.cjs +1 -1
  5. package/dist/MobileClient.d.cts +64 -16
  6. package/dist/MobileClient.d.cts.map +1 -1
  7. package/dist/MobileClient.d.ts +64 -16
  8. package/dist/MobileClient.d.ts.map +1 -1
  9. package/dist/MobileClient.js +82 -12
  10. package/dist/MobileClient.js.map +1 -1
  11. package/dist/{MobileServerFailureError-CPFjwUBx.d.cts → MobileServerFailureError-CVK3aLOi.d.cts} +3 -3
  12. package/dist/{MobileServerFailureError-CPFjwUBx.d.cts.map → MobileServerFailureError-CVK3aLOi.d.cts.map} +1 -1
  13. package/dist/{MobileServerFailureError-Terv2Wzy.d.ts → MobileServerFailureError-D-76n3pS.d.ts} +3 -3
  14. package/dist/{MobileServerFailureError-Terv2Wzy.d.ts.map → MobileServerFailureError-D-76n3pS.d.ts.map} +1 -1
  15. package/dist/{buildSignedMobileRequest-BScnCGPF.d.ts → buildSignedMobileRequest-42meR4yV.d.ts} +2 -2
  16. package/dist/{buildSignedMobileRequest-BScnCGPF.d.ts.map → buildSignedMobileRequest-42meR4yV.d.ts.map} +1 -1
  17. package/dist/{buildSignedMobileRequest-CXfnhc-4.d.cts → buildSignedMobileRequest-BDxji7Vy.d.cts} +2 -2
  18. package/dist/{buildSignedMobileRequest-CXfnhc-4.d.cts.map → buildSignedMobileRequest-BDxji7Vy.d.cts.map} +1 -1
  19. package/dist/{canonicalize-CQ-pMYIt.d.ts → canonicalize-BebHhHcF.d.ts} +2 -2
  20. package/dist/canonicalize-BebHhHcF.d.ts.map +1 -0
  21. package/dist/{canonicalize-hzhboB-C.d.cts → canonicalize-CIDJLLgg.d.cts} +2 -2
  22. package/dist/canonicalize-CIDJLLgg.d.cts.map +1 -0
  23. package/dist/{clientTypes-BwakovWk.d.cts → clientTypes-2I3x4TTt.d.ts} +142 -14
  24. package/dist/clientTypes-2I3x4TTt.d.ts.map +1 -0
  25. package/dist/{clientTypes-D-o1_2Yc.d.ts → clientTypes-tLld8E6J.d.cts} +142 -14
  26. package/dist/clientTypes-tLld8E6J.d.cts.map +1 -0
  27. package/dist/dataMappers.cjs +65 -6
  28. package/dist/dataMappers.cjs.map +1 -1
  29. package/dist/dataMappers.d.cts +21 -5
  30. package/dist/dataMappers.d.cts.map +1 -1
  31. package/dist/dataMappers.d.ts +21 -5
  32. package/dist/dataMappers.d.ts.map +1 -1
  33. package/dist/dataMappers.js +62 -7
  34. package/dist/dataMappers.js.map +1 -1
  35. package/dist/{eip712MethodByType-3aiJkrHj.d.cts → eip712MethodByType-2-vyN5SU.d.cts} +2 -2
  36. package/dist/eip712MethodByType-2-vyN5SU.d.cts.map +1 -0
  37. package/dist/{eip712MethodByType-zNvZukU7.d.ts → eip712MethodByType-CAVpvZoS.d.ts} +2 -2
  38. package/dist/eip712MethodByType-CAVpvZoS.d.ts.map +1 -0
  39. package/dist/follow-Bh3UmXMc.d.cts +23 -0
  40. package/dist/follow-Bh3UmXMc.d.cts.map +1 -0
  41. package/dist/follow-DjkQm1vR.d.ts +23 -0
  42. package/dist/follow-DjkQm1vR.d.ts.map +1 -0
  43. package/dist/index.cjs +6 -1
  44. package/dist/index.d.cts +17 -16
  45. package/dist/index.d.ts +17 -16
  46. package/dist/index.js +2 -1
  47. package/dist/{mobileErrorCodes-CuQ5lyBV.d.ts → mobileErrorCodes-B3eR6t4b.d.ts} +8 -2
  48. package/dist/mobileErrorCodes-B3eR6t4b.d.ts.map +1 -0
  49. package/dist/{mobileErrorCodes-D_SyBm0k.d.cts → mobileErrorCodes-DkzBW4v3.d.cts} +8 -2
  50. package/dist/mobileErrorCodes-DkzBW4v3.d.cts.map +1 -0
  51. package/dist/{patterns-B5gi7OfV.d.ts → patterns-C-aUAnKl.d.ts} +1 -1
  52. package/dist/patterns-C-aUAnKl.d.ts.map +1 -0
  53. package/dist/{patterns-Db6fgz8i.d.cts → patterns-DS8P5LH0.d.cts} +1 -1
  54. package/dist/patterns-DS8P5LH0.d.cts.map +1 -0
  55. package/dist/{payloadHash-DVkEt-Rg.d.cts → payloadHash-BVPYKdk4.d.cts} +2 -2
  56. package/dist/payloadHash-BVPYKdk4.d.cts.map +1 -0
  57. package/dist/{payloadHash-BxL26uwq.d.ts → payloadHash-DMqE5aMo.d.ts} +2 -2
  58. package/dist/payloadHash-DMqE5aMo.d.ts.map +1 -0
  59. package/dist/{serverBaseTypes-C8KjE9Fm.d.cts → serverBaseTypes-BybrVE6L.d.cts} +2 -2
  60. package/dist/{serverBaseTypes-C8KjE9Fm.d.cts.map → serverBaseTypes-BybrVE6L.d.cts.map} +1 -1
  61. package/dist/{serverBaseTypes-B_KHFDab.d.ts → serverBaseTypes-DPB1vEvv.d.ts} +2 -2
  62. package/dist/{serverBaseTypes-B_KHFDab.d.ts.map → serverBaseTypes-DPB1vEvv.d.ts.map} +1 -1
  63. package/dist/serverExecuteTypes-DoR0InCd.d.cts +50 -0
  64. package/dist/serverExecuteTypes-DoR0InCd.d.cts.map +1 -0
  65. package/dist/serverExecuteTypes-ih4Xbr3J.d.ts +50 -0
  66. package/dist/serverExecuteTypes-ih4Xbr3J.d.ts.map +1 -0
  67. package/dist/{serverModelTypes-_cX99seo.d.ts → serverModelTypes-DV-eARP8.d.ts} +54 -10
  68. package/dist/serverModelTypes-DV-eARP8.d.ts.map +1 -0
  69. package/dist/{serverModelTypes-CwgibK-l.d.cts → serverModelTypes-KSm0CAcF.d.cts} +54 -10
  70. package/dist/serverModelTypes-KSm0CAcF.d.cts.map +1 -0
  71. package/dist/{serverQueryTypes-NIrPm2Sl.d.ts → serverQueryTypes-B9OOpiJB.d.cts} +32 -9
  72. package/dist/serverQueryTypes-B9OOpiJB.d.cts.map +1 -0
  73. package/dist/{serverQueryTypes-DwE1iJ9f.d.cts → serverQueryTypes-LygPrZn7.d.ts} +32 -9
  74. package/dist/serverQueryTypes-LygPrZn7.d.ts.map +1 -0
  75. package/dist/serverRequestTypes-BDXIPjtw.d.ts +101 -0
  76. package/dist/serverRequestTypes-BDXIPjtw.d.ts.map +1 -0
  77. package/dist/serverRequestTypes-q4xTJHk0.d.cts +101 -0
  78. package/dist/serverRequestTypes-q4xTJHk0.d.cts.map +1 -0
  79. package/dist/{serverResponseGuards-Cep5s7j5.d.ts → serverResponseGuards-BcdkKV30.d.ts} +2 -2
  80. package/dist/{serverResponseGuards-Cep5s7j5.d.ts.map → serverResponseGuards-BcdkKV30.d.ts.map} +1 -1
  81. package/dist/{serverResponseGuards-Dxt9_aGh.d.cts → serverResponseGuards-k6xK1bZ1.d.cts} +2 -2
  82. package/dist/{serverResponseGuards-Dxt9_aGh.d.cts.map → serverResponseGuards-k6xK1bZ1.d.cts.map} +1 -1
  83. package/dist/signing/buildSignedMobileRequest.cjs +1 -1
  84. package/dist/signing/buildSignedMobileRequest.d.cts +1 -1
  85. package/dist/signing/buildSignedMobileRequest.d.ts +1 -1
  86. package/dist/signing/canonicalize.cjs +17 -0
  87. package/dist/signing/canonicalize.cjs.map +1 -1
  88. package/dist/signing/canonicalize.d.cts +1 -1
  89. package/dist/signing/canonicalize.d.ts +1 -1
  90. package/dist/signing/canonicalize.js +17 -0
  91. package/dist/signing/canonicalize.js.map +1 -1
  92. package/dist/signing/eip712MethodByType.cjs +4 -1
  93. package/dist/signing/eip712MethodByType.cjs.map +1 -1
  94. package/dist/signing/eip712MethodByType.d.cts +1 -1
  95. package/dist/signing/eip712MethodByType.d.ts +1 -1
  96. package/dist/signing/eip712MethodByType.js +4 -1
  97. package/dist/signing/eip712MethodByType.js.map +1 -1
  98. package/dist/signing/index.d.cts +6 -6
  99. package/dist/signing/index.d.ts +6 -6
  100. package/dist/signing/payloadHash.cjs +1 -1
  101. package/dist/signing/payloadHash.d.cts +1 -1
  102. package/dist/signing/payloadHash.d.ts +1 -1
  103. package/dist/signing/types.d.cts +2 -2
  104. package/dist/signing/types.d.ts +2 -2
  105. package/dist/types/MobileServerFailureError.cjs +1 -1
  106. package/dist/types/MobileServerFailureError.d.cts +1 -1
  107. package/dist/types/MobileServerFailureError.d.ts +1 -1
  108. package/dist/types/clientTypes.d.cts +2 -2
  109. package/dist/types/clientTypes.d.ts +2 -2
  110. package/dist/types/index.d.cts +9 -9
  111. package/dist/types/index.d.ts +9 -9
  112. package/dist/types/mobileErrorCodes.cjs +13 -1
  113. package/dist/types/mobileErrorCodes.cjs.map +1 -1
  114. package/dist/types/mobileErrorCodes.d.cts +1 -1
  115. package/dist/types/mobileErrorCodes.d.ts +1 -1
  116. package/dist/types/mobileErrorCodes.js +12 -0
  117. package/dist/types/mobileErrorCodes.js.map +1 -1
  118. package/dist/types/serverBaseTypes.d.cts +1 -1
  119. package/dist/types/serverBaseTypes.d.ts +1 -1
  120. package/dist/types/serverExecuteTypes.d.cts +2 -2
  121. package/dist/types/serverExecuteTypes.d.ts +2 -2
  122. package/dist/types/serverModelTypes.cjs.map +1 -1
  123. package/dist/types/serverModelTypes.d.cts +2 -2
  124. package/dist/types/serverModelTypes.d.ts +2 -2
  125. package/dist/types/serverModelTypes.js.map +1 -1
  126. package/dist/types/serverQueryTypes.d.cts +2 -2
  127. package/dist/types/serverQueryTypes.d.ts +2 -2
  128. package/dist/types/serverRequestTypes.d.cts +2 -2
  129. package/dist/types/serverRequestTypes.d.ts +2 -2
  130. package/dist/{types-47IPsDyA.d.cts → types-B8lDIhBP.d.ts} +30 -7
  131. package/dist/types-B8lDIhBP.d.ts.map +1 -0
  132. package/dist/{types-D5kFJzEX.d.ts → types-CClB00EN.d.cts} +30 -7
  133. package/dist/types-CClB00EN.d.cts.map +1 -0
  134. package/dist/utils/follow.cjs +28 -0
  135. package/dist/utils/follow.cjs.map +1 -0
  136. package/dist/utils/follow.d.cts +2 -0
  137. package/dist/utils/follow.d.ts +2 -0
  138. package/dist/utils/follow.js +24 -0
  139. package/dist/utils/follow.js.map +1 -0
  140. package/dist/utils/index.cjs +5 -0
  141. package/dist/utils/index.d.cts +4 -3
  142. package/dist/utils/index.d.ts +4 -3
  143. package/dist/utils/index.js +2 -1
  144. package/dist/utils/patterns.d.cts +1 -1
  145. package/dist/utils/patterns.d.ts +1 -1
  146. package/dist/utils/serverResponseGuards.d.cts +1 -1
  147. package/dist/utils/serverResponseGuards.d.ts +1 -1
  148. package/package.json +3 -3
  149. package/src/MobileClient.ts +138 -21
  150. package/src/dataMappers.ts +89 -5
  151. package/src/signing/canonicalize.ts +20 -0
  152. package/src/signing/eip712MethodByType.ts +3 -0
  153. package/src/signing/signing.test.ts +58 -4
  154. package/src/signing/types.ts +34 -1
  155. package/src/types/clientTypes.ts +148 -11
  156. package/src/types/mobileErrorCodes.ts +12 -0
  157. package/src/types/serverExecuteTypes.ts +40 -2
  158. package/src/types/serverModelTypes.ts +55 -8
  159. package/src/types/serverQueryTypes.ts +35 -4
  160. package/src/types/serverRequestTypes.ts +41 -6
  161. package/src/utils/follow.ts +22 -0
  162. package/src/utils/index.ts +1 -0
  163. package/dist/MobileClient-D5TNTnLx.cjs.map +0 -1
  164. package/dist/canonicalize-CQ-pMYIt.d.ts.map +0 -1
  165. package/dist/canonicalize-hzhboB-C.d.cts.map +0 -1
  166. package/dist/clientTypes-BwakovWk.d.cts.map +0 -1
  167. package/dist/clientTypes-D-o1_2Yc.d.ts.map +0 -1
  168. package/dist/eip712MethodByType-3aiJkrHj.d.cts.map +0 -1
  169. package/dist/eip712MethodByType-zNvZukU7.d.ts.map +0 -1
  170. package/dist/mobileErrorCodes-CuQ5lyBV.d.ts.map +0 -1
  171. package/dist/mobileErrorCodes-D_SyBm0k.d.cts.map +0 -1
  172. package/dist/patterns-B5gi7OfV.d.ts.map +0 -1
  173. package/dist/patterns-Db6fgz8i.d.cts.map +0 -1
  174. package/dist/payloadHash-BxL26uwq.d.ts.map +0 -1
  175. package/dist/payloadHash-DVkEt-Rg.d.cts.map +0 -1
  176. package/dist/serverExecuteTypes-Cb5v7y0A.d.cts +0 -19
  177. package/dist/serverExecuteTypes-Cb5v7y0A.d.cts.map +0 -1
  178. package/dist/serverExecuteTypes-GFjZj_DC.d.ts +0 -19
  179. package/dist/serverExecuteTypes-GFjZj_DC.d.ts.map +0 -1
  180. package/dist/serverModelTypes-CwgibK-l.d.cts.map +0 -1
  181. package/dist/serverModelTypes-_cX99seo.d.ts.map +0 -1
  182. package/dist/serverQueryTypes-DwE1iJ9f.d.cts.map +0 -1
  183. package/dist/serverQueryTypes-NIrPm2Sl.d.ts.map +0 -1
  184. package/dist/serverRequestTypes-516zXnQ6.d.ts +0 -72
  185. package/dist/serverRequestTypes-516zXnQ6.d.ts.map +0 -1
  186. package/dist/serverRequestTypes-GAtVYIda.d.cts +0 -72
  187. package/dist/serverRequestTypes-GAtVYIda.d.cts.map +0 -1
  188. package/dist/types-47IPsDyA.d.cts.map +0 -1
  189. package/dist/types-D5kFJzEX.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # `@nadohq/mobile-client`
2
2
 
3
3
  HTTP client for the Nado mobile service API. Manages usernames, public profile lookups, the global trade feed,
4
- privacy settings, and push notification devices/preferences, using EIP-712 + msgpack signed authentication.
4
+ the follower/following graph, privacy settings, and push notification devices/preferences, using EIP-712 +
5
+ msgpack signed authentication.
5
6
 
6
7
  [Full SDK Documentation](https://nadohq.github.io/nado-typescript-sdk/index.html)
7
8
 
@@ -26,9 +27,9 @@ const mobile = new MobileClient({
26
27
 
27
28
  // Unsigned public lookups
28
29
  const availability = await mobile.getUsernameAvailability({ displayName: 'Alice.One' });
29
- const profile = await mobile.getPublicProfile({
30
- subaccountOwner: '0x...',
31
- subaccountName: 'default',
30
+ const [profile] = await mobile.getProfiles({
31
+ subaccounts: [{ subaccountOwner: '0x...', subaccountName: 'default' }],
32
+ include: { followCounts: true },
32
33
  });
33
34
 
34
35
  // Signed operations
@@ -45,16 +46,28 @@ await mobile.setUsername({
45
46
 
46
47
  ### Public Queries
47
48
 
48
- `getUsernameAvailability`, `getPublicProfile`, `getFeed`, `getNotificationPreferences`, `getRegisteredWallet`.
49
+ `getUsernameAvailability`, `getProfiles`, `getFeed`, `getNotificationPreferences`, `getRegisteredWallet`.
50
+
51
+ `getProfiles` is the only profile route: it batches up to 25 subaccounts, returns them in the requested order,
52
+ and carries follower totals (`include.followCounts`) and a follow summary (`include.followSummary.viewAs`) as
53
+ opt-in includes, so use it for a single subaccount too. Because it is unsigned, that `viewAs` is an
54
+ unauthenticated claim. The backend still serves the older singular `profile` request for compatibility, but it
55
+ returns base fields only and costs more weight than the batched equivalent, so the SDK does not expose it.
49
56
 
50
57
  ### Public Executes
51
58
 
52
59
  `unregisterExpoToken`, `updateNotificationPreferences`. These are authenticated by possession of an active Expo
53
60
  push token rather than a wallet signature, so they still work at logout when a signature may be unobtainable.
54
61
 
62
+ ### Signed Queries
63
+
64
+ `getFollowers`, `getFollowing`. Both follow list reads are signed because the rows carry an `isFollowing`
65
+ relative to the signing Viewer, which has to be proven rather than claimed — including on each paginated
66
+ request.
67
+
55
68
  ### Signed Executes
56
69
 
57
- `setUsername`, `setPrivateMode`, `registerExpoToken`.
70
+ `setUsername`, `setPrivateMode`, `registerExpoToken`, `setFollow`.
58
71
 
59
72
  ### Linked Signers
60
73
 
@@ -29,8 +29,8 @@ let axios = require("axios");
29
29
  axios = __toESM(axios, 1);
30
30
  //#region src/MobileClient.ts
31
31
  /**
32
- * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, privacy
33
- * settings, and push notification device/preference management.
32
+ * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, the
33
+ * follower/following graph, privacy settings, and push notification device/preference management.
34
34
  */
35
35
  var MobileClient = class {
36
36
  constructor(opts) {
@@ -64,19 +64,27 @@ var MobileClient = class {
64
64
  };
65
65
  }
66
66
  /**
67
- * Looks up a subaccount's public profile. Every non-isolated subaccount resolves, with `username` and
68
- * `displayName` `null` until a username is claimed. Private Mode does not hide the profile itself, only the
69
- * account's activity.
67
+ * Looks up public profiles, optionally with follower totals and a follow summary. Returns one profile per
68
+ * requested subaccount, in the requested order, so callers can correlate positionally. Every non-isolated
69
+ * subaccount resolves, with `username` and `displayName` `null` until a username is claimed. Private Mode
70
+ * does not hide the profile itself, only the account's activity.
70
71
  *
71
- * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the subaccount is in the
72
- * engine-created isolated namespace, which cannot own a profile.
72
+ * This is the only profile route, so use it for a single subaccount too. It is unsigned, which means
73
+ * `include.followSummary.viewAs` is an unauthenticated claim: the follow *lists* remain signed precisely
74
+ * because they prove the viewer.
75
+ *
76
+ * @throws {MobileServerFailureError} With error code `INVALID_PROFILES_REQUEST` if `subaccounts` is empty,
77
+ * holds duplicates, or exceeds `MOBILE_PROFILES_MAX_BATCH_SIZE` (25), or `PROFILE_NOT_FOUND` if any
78
+ * requested subaccount — or the follow summary's `viewAs` — is in the engine-created isolated namespace.
79
+ * One isolated member fails the whole batch rather than being omitted.
73
80
  */
74
- async getPublicProfile(params) {
81
+ async getProfiles(params) {
75
82
  const body = {
76
- type: "profile",
77
- subaccount: (0, _nadohq_shared.subaccountToHex)(params)
83
+ type: "profiles",
84
+ subaccounts: params.subaccounts.map(_nadohq_shared.subaccountToHex),
85
+ include: params.include ? require_dataMappers.mapMobileProfilesIncludeToServer(params.include) : void 0
78
86
  };
79
- return require_dataMappers.mapMobilePublicProfile((await this.publicQuery(body)).profile);
87
+ return (await this.publicQuery(body)).profiles.map(require_dataMappers.mapMobilePublicProfile);
80
88
  }
81
89
  /**
82
90
  * Fetches a page of the global trade feed: public perpetual trades, newest first, optionally filtered by a
@@ -169,6 +177,40 @@ var MobileClient = class {
169
177
  return this.publicExecute(body);
170
178
  }
171
179
  /**
180
+ * Fetches a page of the accounts that follow the given Profile.
181
+ *
182
+ * Familiar accounts (those the signing Viewer follows) come first, unfamiliar second, and within each group
183
+ * the newest listed relationship first with a bytes32 tie-break. The grouping is evaluated per request
184
+ * against the live graph, so following an account from inside the list moves it between groups on the next
185
+ * page — deduplicate by subaccount and do not re-sort locally.
186
+ *
187
+ * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the viewed Profile is isolated,
188
+ * `INVALID_FOLLOW_CURSOR` if the cursor is malformed or was issued for a different Viewer, Profile, or list
189
+ * direction (discard it and restart from the first page), or `INVALID_FOLLOW_LIMIT` if `limit` is outside
190
+ * 1–50.
191
+ */
192
+ async getFollowers(params) {
193
+ const signedRequest = await this.getSignedRequest("followers", params, {
194
+ subaccount: (0, _nadohq_shared.subaccountToHex)(params.target),
195
+ cursor: params.cursor ?? null,
196
+ limit: params.limit ?? null
197
+ });
198
+ return require_dataMappers.mapMobileFollowListPage(await this.signedQuery(signedRequest));
199
+ }
200
+ /**
201
+ * Fetches a page of the accounts the given Profile follows. Ordering, cursor rules, and errors match
202
+ * {@link MobileClient.getFollowers}. When the Viewer reads their own Following list every row is familiar,
203
+ * so every `isFollowing` is `true`.
204
+ */
205
+ async getFollowing(params) {
206
+ const signedRequest = await this.getSignedRequest("following", params, {
207
+ subaccount: (0, _nadohq_shared.subaccountToHex)(params.target),
208
+ cursor: params.cursor ?? null,
209
+ limit: params.limit ?? null
210
+ });
211
+ return require_dataMappers.mapMobileFollowListPage(await this.signedQuery(signedRequest));
212
+ }
213
+ /**
172
214
  * Sets a subaccount's username, derived from the given display name. Upserts: the same call claims a first
173
215
  * username and renames an existing one, so callers never need to read the current identity first.
174
216
  *
@@ -206,6 +248,28 @@ var MobileClient = class {
206
248
  });
207
249
  return this.execute(signedRequest);
208
250
  }
251
+ /**
252
+ * Sets whether the signing subaccount follows a Profile. Both directions are idempotent: following again
253
+ * keeps the original relationship creation time, so it does not move the account in recency order, and
254
+ * unfollowing a relationship that does not exist succeeds with `isFollowing: false`. Unfollowing deletes
255
+ * the relationship rather than deactivating it, so a later follow gets a new creation time and moves the
256
+ * account to the front of recency order.
257
+ *
258
+ * Only `isFollowing: true` is checked against the canonical Query DB subaccount index, so unfollowing still
259
+ * works while that index lags.
260
+ *
261
+ * @throws {MobileServerFailureError} With error code `INVALID_FOLLOW_TARGET` if the target is the sender or
262
+ * either party is isolated, `FOLLOWER_NOT_ELIGIBLE` if the sender is not yet in the canonical Query DB
263
+ * subaccount index, or `FOLLOWING_NOT_FOUND` if the target is not. Both index errors are transient for an
264
+ * account that is being recorded, so they are retryable after catch-up — unlike `INVALID_FOLLOW_TARGET`.
265
+ */
266
+ async setFollow(params) {
267
+ const signedRequest = await this.getSignedRequest("set_follow", params, {
268
+ subaccount: (0, _nadohq_shared.subaccountToHex)(params.target),
269
+ is_following: params.isFollowing
270
+ });
271
+ return require_dataMappers.mapMobileFollowMutationResult(await this.execute(signedRequest));
272
+ }
209
273
  async getSignedRequest(type, params, innerParams) {
210
274
  const walletClient = this.opts.linkedSignerWalletClient ?? this.opts.walletClient;
211
275
  if (walletClient == null) throw new _nadohq_shared.WalletNotProvidedError();
@@ -237,6 +301,12 @@ var MobileClient = class {
237
301
  this.checkServerStatus(response);
238
302
  return response.data;
239
303
  }
304
+ async signedQuery(body) {
305
+ const response = await this.axiosInstance.post(`${this.opts.url}/mobile/query`, body);
306
+ this.checkResponseStatus(response);
307
+ this.checkServerStatus(response);
308
+ return response.data;
309
+ }
240
310
  /**
241
311
  * Validates the HTTP status before interpreting the body. Unlike the engine, the mobile backend returns
242
312
  * failure envelopes with non-2xx statuses (e.g. 404 for `PROFILE_NOT_FOUND`), so a non-2xx response with a
@@ -267,4 +337,4 @@ Object.defineProperty(exports, "__toESM", {
267
337
  }
268
338
  });
269
339
 
270
- //# sourceMappingURL=MobileClient-D5TNTnLx.cjs.map
340
+ //# sourceMappingURL=MobileClient-C9S_o_eC.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobileClient-C9S_o_eC.cjs","names":["subaccountToHex","mapMobileProfilesIncludeToServer","mapMobilePublicProfile","mapMobileFeedPage","mapMobileNotificationPreferences","mapMobileRegisteredWallet","mapMobileNotificationPreferencesToServer","mapMobileFollowListPage","mapMobileFollowMutationResult","WalletNotProvidedError","buildSignedMobileRequest","isMobileServerFailureResponse","MobileServerFailureError","isMobileServerSuccessResponse"],"sources":["../src/MobileClient.ts"],"sourcesContent":["import {\n getNadoClientTypeHeaders,\n subaccountToHex,\n WalletClientWithAccount,\n WalletNotProvidedError,\n} from '@nadohq/shared';\nimport axios, { AxiosInstance, AxiosResponse } from 'axios';\nimport {\n mapMobileFeedPage,\n mapMobileFollowListPage,\n mapMobileFollowMutationResult,\n mapMobileNotificationPreferences,\n mapMobileNotificationPreferencesToServer,\n mapMobileProfilesIncludeToServer,\n mapMobilePublicProfile,\n mapMobileRegisteredWallet,\n} from './dataMappers';\nimport {\n buildSignedMobileRequest,\n MobileSignedInner,\n MobileSignedInnerByType,\n MobileSignedInnerParams,\n MobileSignedRequest,\n} from './signing';\nimport {\n GetMobileFeedParams,\n GetMobileFollowersParams,\n GetMobileFollowingParams,\n GetMobileNotificationPreferencesParams,\n GetMobileProfilesParams,\n GetMobileRegisteredWalletParams,\n GetMobileUsernameAvailabilityParams,\n MobileFeedPage,\n MobileFollowListPage,\n MobileFollowMutationResult,\n MobileNotificationPreferences,\n MobilePublicProfile,\n MobileRegisteredWallet,\n MobileRegisterExpoTokenParams,\n MobileSetFollowParams,\n MobileSetPrivateModeParams,\n MobileSetUsernameParams,\n MobileSignedRequestParams,\n MobileUnregisterExpoTokenParams,\n MobileUpdateNotificationPreferencesParams,\n MobileUsernameAvailability,\n} from './types/clientTypes';\nimport { MobileServerFailureError } from './types/MobileServerFailureError';\nimport { MobileServerSuccessResponse } from './types/serverBaseTypes';\nimport {\n MobileServerExecuteResult,\n MobileServerExecuteSuccessResponse,\n MobileServerPublicExecuteRequest,\n} from './types/serverExecuteTypes';\nimport {\n MobileServerPublicQueryRequest,\n MobileServerPublicQuerySuccessResponse,\n MobileServerSignedQuerySuccessResponse,\n} from './types/serverQueryTypes';\nimport {\n MobileServerExecuteRequestType,\n MobileServerPublicQueryRequestByType,\n MobileServerPublicQueryRequestType,\n MobileServerSignedQueryRequestType,\n} from './types/serverRequestTypes';\nimport {\n isMobileServerFailureResponse,\n isMobileServerSuccessResponse,\n} from './utils/serverResponseGuards';\n\n/**\n * Options for constructing a {@link MobileClient}.\n */\nexport interface MobileClientOpts {\n /**\n * Base URL of the mobile service API, e.g. {@link MOBILE_CLIENT_ENDPOINTS}.\n */\n url: string;\n /**\n * Wallet client used to sign requests.\n */\n walletClient?: WalletClientWithAccount;\n /**\n * If provided, this signer is used instead of `walletClient` for all signed requests.\n */\n linkedSignerWalletClient?: WalletClientWithAccount;\n /**\n * If provided, identifies the calling client, sent as a header with every request.\n */\n clientType?: string;\n}\n\n/**\n * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, the\n * follower/following graph, privacy settings, and push notification device/preference management.\n */\nexport class MobileClient {\n readonly opts: MobileClientOpts;\n readonly axiosInstance: AxiosInstance;\n\n constructor(opts: MobileClientOpts) {\n this.opts = opts;\n this.axiosInstance = axios.create({\n withCredentials: true,\n // We have custom logic to validate response status and create an appropriate error\n validateStatus: () => true,\n headers: getNadoClientTypeHeaders(opts.clientType),\n });\n }\n\n /**\n * Sets the linked signer for requests\n *\n * @param linkedSignerWalletClient The linkedSigner to use for all signatures. Set to null to revert to the chain signer\n */\n public setLinkedSigner(\n linkedSignerWalletClient: WalletClientWithAccount | null,\n ) {\n this.opts.linkedSignerWalletClient = linkedSignerWalletClient ?? undefined;\n }\n\n /*\n Public queries\n */\n\n /**\n * Checks whether a username derived from the given display name is available to claim.\n */\n async getUsernameAvailability(\n params: GetMobileUsernameAvailabilityParams,\n ): Promise<MobileUsernameAvailability> {\n const body: MobileServerPublicQueryRequest<'username_availability'> = {\n type: 'username_availability',\n display_name: params.displayName,\n };\n const data = await this.publicQuery(body);\n return { username: data.username, available: data.available };\n }\n\n /**\n * Looks up public profiles, optionally with follower totals and a follow summary. Returns one profile per\n * requested subaccount, in the requested order, so callers can correlate positionally. Every non-isolated\n * subaccount resolves, with `username` and `displayName` `null` until a username is claimed. Private Mode\n * does not hide the profile itself, only the account's activity.\n *\n * This is the only profile route, so use it for a single subaccount too. It is unsigned, which means\n * `include.followSummary.viewAs` is an unauthenticated claim: the follow *lists* remain signed precisely\n * because they prove the viewer.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_PROFILES_REQUEST` if `subaccounts` is empty,\n * holds duplicates, or exceeds `MOBILE_PROFILES_MAX_BATCH_SIZE` (25), or `PROFILE_NOT_FOUND` if any\n * requested subaccount — or the follow summary's `viewAs` — is in the engine-created isolated namespace.\n * One isolated member fails the whole batch rather than being omitted.\n */\n async getProfiles(\n params: GetMobileProfilesParams,\n ): Promise<MobilePublicProfile[]> {\n const body: MobileServerPublicQueryRequest<'profiles'> = {\n type: 'profiles',\n subaccounts: params.subaccounts.map(subaccountToHex),\n include: params.include\n ? mapMobileProfilesIncludeToServer(params.include)\n : undefined,\n };\n const data = await this.publicQuery(body);\n return data.profiles.map(mapMobilePublicProfile);\n }\n\n /**\n * Fetches a page of the global trade feed: public perpetual trades, newest first, optionally filtered by a\n * whole-dollar minimum notional (omitted means unfiltered). Trades by subaccounts that have not claimed a\n * username are included with `null` name fields; only private accounts are filtered out. The feed is\n * best-effort rather than authoritative history, and pagination is live (not snapshot), so deduplicate pages\n * by {@link MobileFeedTrade.orderDigest}.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_FEED_FILTER` if `minimumNotional` or\n * `limit` is outside its allowed domain (fix the request; do not retry unchanged), or\n * `INVALID_FEED_CURSOR` if the cursor is malformed or was issued for a different `minimumNotional`\n * (discard the cursor and restart from the first page).\n */\n async getFeed(params: GetMobileFeedParams = {}): Promise<MobileFeedPage> {\n const body: MobileServerPublicQueryRequest<'feed'> = {\n type: 'feed',\n minimum_notional: params.minimumNotional,\n limit: params.limit,\n cursor: params.cursor,\n };\n const data = await this.publicQuery(body);\n return mapMobileFeedPage(data);\n }\n\n /**\n * Fetches the push notification preferences of the wallet that owns the given Expo push token. Unsigned —\n * the token identifies the wallet on its own. Falls back to the backend's defaults if the wallet has never\n * updated its preferences.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_EXPO_TOKEN` if the token is malformed, or is\n * not currently registered to a wallet.\n */\n async getNotificationPreferences(\n params: GetMobileNotificationPreferencesParams,\n ): Promise<MobileNotificationPreferences> {\n const body: MobileServerPublicQueryRequest<'notification_preferences'> = {\n type: 'notification_preferences',\n expo_token: params.expoToken,\n };\n const data = await this.publicQuery(body);\n return mapMobileNotificationPreferences(data.preferences);\n }\n\n /**\n * Resolves the wallet that an Expo push token is currently registered to, along with the redacted metadata\n * of the device that registered it. Unsigned — the token identifies the wallet on its own. Use it to check\n * whether a locally held token is still active and which wallet currently owns it, since registering the\n * same token under another wallet transfers ownership.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_EXPO_TOKEN` if the token is malformed, or is\n * not currently registered to a wallet — including a token that was unregistered. The backend does not\n * distinguish those cases.\n */\n async getRegisteredWallet(\n params: GetMobileRegisteredWalletParams,\n ): Promise<MobileRegisteredWallet> {\n const body: MobileServerPublicQueryRequest<'registered_wallet'> = {\n type: 'registered_wallet',\n expo_token: params.expoToken,\n };\n const data = await this.publicQuery(body);\n return mapMobileRegisteredWallet(data);\n }\n\n /*\n Public executes\n */\n\n /**\n * Unregisters an Expo push token, stopping delivery to that device. Unsigned, so it still works at logout\n * when a wallet signature may no longer be obtainable. Idempotent: unregistering an inactive or unknown\n * well-formed token succeeds.\n *\n * A registration that commits after this call re-activates the token, so stop or await any in-flight\n * {@link MobileClient.registerExpoToken} before unregistering.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_EXPO_TOKEN` if the token is malformed.\n */\n async unregisterExpoToken(\n params: MobileUnregisterExpoTokenParams,\n ): Promise<MobileServerSuccessResponse> {\n const body: MobileServerPublicExecuteRequest<'unregister_expo_token'> = {\n type: 'unregister_expo_token',\n expo_token: params.expoToken,\n };\n return this.publicExecute(body);\n }\n\n /**\n * Replaces the push notification preferences of the wallet that owns the given Expo push token. Possession\n * of an active token is the credential rather than a signature, so anyone holding the token can overwrite\n * that wallet's preferences. The backend requires `schemaVersion` of 1, exactly one entry per known\n * category, and empty `scopes` on every entry.\n *\n * Writes are last-write-wins on commit order rather than ordered by a client nonce, so callers must\n * serialize their own preference writes.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_PREFERENCES` if those rules are violated, or\n * `INVALID_EXPO_TOKEN` if the token is malformed or is not currently registered to a wallet.\n */\n async updateNotificationPreferences(\n params: MobileUpdateNotificationPreferencesParams,\n ): Promise<MobileServerSuccessResponse> {\n const body: MobileServerPublicExecuteRequest<'update_preferences'> = {\n type: 'update_preferences',\n expo_token: params.expoToken,\n preferences: mapMobileNotificationPreferencesToServer(params.preferences),\n };\n return this.publicExecute(body);\n }\n\n /*\n Signed queries\n */\n\n /**\n * Fetches a page of the accounts that follow the given Profile.\n *\n * Familiar accounts (those the signing Viewer follows) come first, unfamiliar second, and within each group\n * the newest listed relationship first with a bytes32 tie-break. The grouping is evaluated per request\n * against the live graph, so following an account from inside the list moves it between groups on the next\n * page — deduplicate by subaccount and do not re-sort locally.\n *\n * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the viewed Profile is isolated,\n * `INVALID_FOLLOW_CURSOR` if the cursor is malformed or was issued for a different Viewer, Profile, or list\n * direction (discard it and restart from the first page), or `INVALID_FOLLOW_LIMIT` if `limit` is outside\n * 1–50.\n */\n async getFollowers(\n params: GetMobileFollowersParams,\n ): Promise<MobileFollowListPage> {\n const signedRequest = await this.getSignedRequest('followers', params, {\n subaccount: subaccountToHex(params.target),\n cursor: params.cursor ?? null,\n limit: params.limit ?? null,\n });\n const data = await this.signedQuery(signedRequest);\n return mapMobileFollowListPage(data);\n }\n\n /**\n * Fetches a page of the accounts the given Profile follows. Ordering, cursor rules, and errors match\n * {@link MobileClient.getFollowers}. When the Viewer reads their own Following list every row is familiar,\n * so every `isFollowing` is `true`.\n */\n async getFollowing(\n params: GetMobileFollowingParams,\n ): Promise<MobileFollowListPage> {\n const signedRequest = await this.getSignedRequest('following', params, {\n subaccount: subaccountToHex(params.target),\n cursor: params.cursor ?? null,\n limit: params.limit ?? null,\n });\n const data = await this.signedQuery(signedRequest);\n return mapMobileFollowListPage(data);\n }\n\n /*\n Signed executes\n */\n\n /**\n * Sets a subaccount's username, derived from the given display name. Upserts: the same call claims a first\n * username and renames an existing one, so callers never need to read the current identity first.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_DISPLAY_NAME` if the display name violates\n * `MOBILE_DISPLAY_NAME_PATTERN`, `USERNAME_UNAVAILABLE` if the derived username is reserved or already\n * taken by another subaccount, `INVALID_IDENTITY_TARGET` if the sender is in the engine-created isolated\n * namespace, or `STALE_IDENTITY_UPDATE` if another identity write for this subaccount committed with a\n * later nonce (re-read the identity before retrying).\n */\n async setUsername(\n params: MobileSetUsernameParams,\n ): Promise<MobileServerSuccessResponse> {\n const signedRequest = await this.getSignedRequest('set_username', params, {\n display_name: params.displayName,\n });\n return this.execute(signedRequest);\n }\n\n /**\n * Sets whether a subaccount's profile is private.\n */\n async setPrivateMode(\n params: MobileSetPrivateModeParams,\n ): Promise<MobileServerSuccessResponse> {\n const signedRequest = await this.getSignedRequest(\n 'set_private_mode',\n params,\n { private_mode: params.privateMode },\n );\n return this.execute(signedRequest);\n }\n\n /**\n * Registers an Expo push token for the wallet, transferring active ownership of the token if another\n * wallet had registered it. Idempotent per `(wallet, token)`.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_EXPO_TOKEN` if the token is not a valid Expo\n * push token, or `INVALID_DEVICE_METADATA` if `locale` (max 35 chars) or `appVersion` (max 64 chars) is\n * too long.\n */\n async registerExpoToken(\n params: MobileRegisterExpoTokenParams,\n ): Promise<MobileServerSuccessResponse> {\n const signedRequest = await this.getSignedRequest(\n 'register_expo_token',\n params,\n {\n expo_token: params.expoToken,\n platform: params.platform,\n locale: params.locale ?? null,\n app_version: params.appVersion ?? null,\n },\n );\n return this.execute(signedRequest);\n }\n\n /**\n * Sets whether the signing subaccount follows a Profile. Both directions are idempotent: following again\n * keeps the original relationship creation time, so it does not move the account in recency order, and\n * unfollowing a relationship that does not exist succeeds with `isFollowing: false`. Unfollowing deletes\n * the relationship rather than deactivating it, so a later follow gets a new creation time and moves the\n * account to the front of recency order.\n *\n * Only `isFollowing: true` is checked against the canonical Query DB subaccount index, so unfollowing still\n * works while that index lags.\n *\n * @throws {MobileServerFailureError} With error code `INVALID_FOLLOW_TARGET` if the target is the sender or\n * either party is isolated, `FOLLOWER_NOT_ELIGIBLE` if the sender is not yet in the canonical Query DB\n * subaccount index, or `FOLLOWING_NOT_FOUND` if the target is not. Both index errors are transient for an\n * account that is being recorded, so they are retryable after catch-up — unlike `INVALID_FOLLOW_TARGET`.\n */\n async setFollow(\n params: MobileSetFollowParams,\n ): Promise<MobileFollowMutationResult> {\n const signedRequest = await this.getSignedRequest('set_follow', params, {\n subaccount: subaccountToHex(params.target),\n is_following: params.isFollowing,\n });\n const data = await this.execute(signedRequest);\n return mapMobileFollowMutationResult(data);\n }\n\n /*\n Base Fns\n */\n\n // Returns the request narrowed to `type` so `execute` and `signedQuery` can infer their response from the\n // request they are given, instead of being told the type a second time at the call site.\n protected async getSignedRequest<T extends MobileSignedInner['type']>(\n type: T,\n params: MobileSignedRequestParams,\n innerParams: MobileSignedInnerParams<T>,\n ): Promise<MobileSignedRequest<MobileSignedInnerByType<T>>> {\n // Use the linked signer if provided, otherwise use the default signer provided to the client\n const walletClient =\n this.opts.linkedSignerWalletClient ?? this.opts.walletClient;\n\n if (walletClient == null) {\n throw new WalletNotProvidedError();\n }\n\n const inner = {\n type,\n ...innerParams,\n } as unknown as MobileSignedInnerByType<T>;\n return buildSignedMobileRequest({ ...params, walletClient, inner });\n }\n\n // Spelled out as an intersection rather than `MobileServerPublicQueryRequest<T>`: the latter is an indexed\n // access on a mapped type, which is not an inference site, so `T` would always widen to the full union.\n protected async publicQuery<T extends MobileServerPublicQueryRequestType>(\n body: { type: T } & MobileServerPublicQueryRequestByType[T],\n ): Promise<MobileServerPublicQuerySuccessResponse<T>> {\n const response = await this.axiosInstance.post<unknown>(\n `${this.opts.url}/mobile/public_query`,\n body,\n );\n\n this.checkResponseStatus(response);\n this.checkServerStatus(response);\n\n // checkServerStatus throws on failure responses so the cast to the success response is acceptable here\n return response.data as MobileServerPublicQuerySuccessResponse<T>;\n }\n\n protected async publicExecute(\n body: MobileServerPublicExecuteRequest,\n ): Promise<MobileServerSuccessResponse> {\n const response = await this.axiosInstance.post<MobileServerExecuteResult>(\n `${this.opts.url}/mobile/public_execute`,\n body,\n );\n\n this.checkResponseStatus(response);\n this.checkServerStatus(response);\n\n // checkServerStatus catches the failure result and throws the error, so the cast to the success response is acceptable here\n return response.data as MobileServerSuccessResponse;\n }\n\n // `T` is inferred from the body's `type` rather than declared by the caller, for the same reason\n // `publicQuery` spells its body out as an intersection: an indexed access is not an inference site.\n protected async execute<T extends MobileServerExecuteRequestType>(\n body: MobileSignedRequest & { type: T },\n ): Promise<MobileServerExecuteSuccessResponse<T>> {\n const response = await this.axiosInstance.post<MobileServerExecuteResult>(\n `${this.opts.url}/mobile/execute`,\n body,\n );\n\n this.checkResponseStatus(response);\n this.checkServerStatus(response);\n\n // checkServerStatus catches the failure result and throws the error, so the cast to the success response is acceptable here\n return response.data as MobileServerExecuteSuccessResponse<T>;\n }\n\n protected async signedQuery<T extends MobileServerSignedQueryRequestType>(\n body: MobileSignedRequest & { type: T },\n ): Promise<MobileServerSignedQuerySuccessResponse<T>> {\n const response = await this.axiosInstance.post<unknown>(\n `${this.opts.url}/mobile/query`,\n body,\n );\n\n this.checkResponseStatus(response);\n this.checkServerStatus(response);\n\n // checkServerStatus throws on failure responses so the cast to the success response is acceptable here\n return response.data as MobileServerSignedQuerySuccessResponse<T>;\n }\n\n /**\n * Validates the HTTP status before interpreting the body. Unlike the engine, the mobile backend returns\n * failure envelopes with non-2xx statuses (e.g. 404 for `PROFILE_NOT_FOUND`), so a non-2xx response with a\n * failure envelope still throws the typed error; anything else is a transport-level error.\n */\n private checkResponseStatus(response: AxiosResponse<unknown>) {\n if (response.status >= 200 && response.status < 300) {\n return;\n }\n if (isMobileServerFailureResponse(response.data)) {\n throw new MobileServerFailureError(response.data, response.status);\n }\n throw new Error(\n `Unexpected response from mobile service: ${response.status} ${response.statusText}. Data: ${JSON.stringify(response.data)}`,\n );\n }\n\n private checkServerStatus(response: AxiosResponse<unknown>) {\n const { data } = response;\n\n if (isMobileServerFailureResponse(data)) {\n throw new MobileServerFailureError(data, response.status);\n }\n if (!isMobileServerSuccessResponse(data)) {\n throw new Error(\n `Unexpected response from mobile service: missing success envelope. Data: ${JSON.stringify(data)}`,\n );\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_MobileClient = require("./MobileClient-D5TNTnLx.cjs");
2
+ const require_MobileClient = require("./MobileClient-C9S_o_eC.cjs");
3
3
  require("./signing/index.cjs");
4
4
  exports.MobileClient = require_MobileClient.MobileClient;
@@ -1,9 +1,9 @@
1
- import { _ as MobileSignedRequestParams, a as GetMobileUsernameAvailabilityParams, b as MobileUsernameAvailability, d as MobileNotificationPreferences, f as MobilePublicProfile, g as MobileSetUsernameParams, h as MobileSetPrivateModeParams, i as GetMobileRegisteredWalletParams, m as MobileRegisteredWallet, n as GetMobileNotificationPreferencesParams, p as MobileRegisterExpoTokenParams, r as GetMobilePublicProfileParams, s as MobileFeedPage, t as GetMobileFeedParams, v as MobileUnregisterExpoTokenParams, y as MobileUpdateNotificationPreferencesParams } from "./clientTypes-BwakovWk.cjs";
2
- import { i as MobileSignedRequest, n as MobileSignedInner, r as MobileSignedInnerParams } from "./types-47IPsDyA.cjs";
3
- import { a as MobileServerPublicQueryRequestType, i as MobileServerPublicQueryRequestByType } from "./serverRequestTypes-GAtVYIda.cjs";
4
- import { n as MobileServerSuccessResponse } from "./serverBaseTypes-C8KjE9Fm.cjs";
5
- import { n as MobileServerPublicExecuteRequest } from "./serverExecuteTypes-Cb5v7y0A.cjs";
6
- import { o as MobileServerPublicQuerySuccessResponse } from "./serverQueryTypes-DwE1iJ9f.cjs";
1
+ import { A as MobileUpdateNotificationPreferencesParams, C as MobileRegisterExpoTokenParams, D as MobileSetUsernameParams, E as MobileSetPrivateModeParams, O as MobileSignedRequestParams, S as MobilePublicProfile, T as MobileSetFollowParams, a as GetMobileNotificationPreferencesParams, b as MobileNotificationPreferences, c as GetMobileUsernameAvailabilityParams, i as GetMobileFollowingParams, j as MobileUsernameAvailability, k as MobileUnregisterExpoTokenParams, m as MobileFollowMutationResult, o as GetMobileProfilesParams, p as MobileFollowListPage, r as GetMobileFollowersParams, s as GetMobileRegisteredWalletParams, t as GetMobileFeedParams, u as MobileFeedPage, w as MobileRegisteredWallet } from "./clientTypes-tLld8E6J.cjs";
2
+ import { a as MobileSignedRequest, i as MobileSignedInnerParams, n as MobileSignedInner, r as MobileSignedInnerByType } from "./types-CClB00EN.cjs";
3
+ import { a as MobileServerPublicQueryRequestByType, c as MobileServerSignedQueryRequestType, o as MobileServerPublicQueryRequestType, t as MobileServerExecuteRequestType } from "./serverRequestTypes-q4xTJHk0.cjs";
4
+ import { n as MobileServerSuccessResponse } from "./serverBaseTypes-BybrVE6L.cjs";
5
+ import { i as MobileServerExecuteSuccessResponse, o as MobileServerPublicExecuteRequest } from "./serverExecuteTypes-DoR0InCd.cjs";
6
+ import { s as MobileServerPublicQuerySuccessResponse, u as MobileServerSignedQuerySuccessResponse } from "./serverQueryTypes-B9OOpiJB.cjs";
7
7
  import { WalletClientWithAccount } from "@nadohq/shared";
8
8
  import { AxiosInstance } from "axios";
9
9
 
@@ -30,8 +30,8 @@ interface MobileClientOpts {
30
30
  clientType?: string;
31
31
  }
32
32
  /**
33
- * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, privacy
34
- * settings, and push notification device/preference management.
33
+ * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, the
34
+ * follower/following graph, privacy settings, and push notification device/preference management.
35
35
  */
36
36
  declare class MobileClient {
37
37
  readonly opts: MobileClientOpts;
@@ -48,14 +48,21 @@ declare class MobileClient {
48
48
  */
49
49
  getUsernameAvailability(params: GetMobileUsernameAvailabilityParams): Promise<MobileUsernameAvailability>;
50
50
  /**
51
- * Looks up a subaccount's public profile. Every non-isolated subaccount resolves, with `username` and
52
- * `displayName` `null` until a username is claimed. Private Mode does not hide the profile itself, only the
53
- * account's activity.
51
+ * Looks up public profiles, optionally with follower totals and a follow summary. Returns one profile per
52
+ * requested subaccount, in the requested order, so callers can correlate positionally. Every non-isolated
53
+ * subaccount resolves, with `username` and `displayName` `null` until a username is claimed. Private Mode
54
+ * does not hide the profile itself, only the account's activity.
54
55
  *
55
- * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the subaccount is in the
56
- * engine-created isolated namespace, which cannot own a profile.
56
+ * This is the only profile route, so use it for a single subaccount too. It is unsigned, which means
57
+ * `include.followSummary.viewAs` is an unauthenticated claim: the follow *lists* remain signed precisely
58
+ * because they prove the viewer.
59
+ *
60
+ * @throws {MobileServerFailureError} With error code `INVALID_PROFILES_REQUEST` if `subaccounts` is empty,
61
+ * holds duplicates, or exceeds `MOBILE_PROFILES_MAX_BATCH_SIZE` (25), or `PROFILE_NOT_FOUND` if any
62
+ * requested subaccount — or the follow summary's `viewAs` — is in the engine-created isolated namespace.
63
+ * One isolated member fails the whole batch rather than being omitted.
57
64
  */
58
- getPublicProfile(params: GetMobilePublicProfileParams): Promise<MobilePublicProfile>;
65
+ getProfiles(params: GetMobileProfilesParams): Promise<MobilePublicProfile[]>;
59
66
  /**
60
67
  * Fetches a page of the global trade feed: public perpetual trades, newest first, optionally filtered by a
61
68
  * whole-dollar minimum notional (omitted means unfiltered). Trades by subaccounts that have not claimed a
@@ -113,6 +120,26 @@ declare class MobileClient {
113
120
  * `INVALID_EXPO_TOKEN` if the token is malformed or is not currently registered to a wallet.
114
121
  */
115
122
  updateNotificationPreferences(params: MobileUpdateNotificationPreferencesParams): Promise<MobileServerSuccessResponse>;
123
+ /**
124
+ * Fetches a page of the accounts that follow the given Profile.
125
+ *
126
+ * Familiar accounts (those the signing Viewer follows) come first, unfamiliar second, and within each group
127
+ * the newest listed relationship first with a bytes32 tie-break. The grouping is evaluated per request
128
+ * against the live graph, so following an account from inside the list moves it between groups on the next
129
+ * page — deduplicate by subaccount and do not re-sort locally.
130
+ *
131
+ * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the viewed Profile is isolated,
132
+ * `INVALID_FOLLOW_CURSOR` if the cursor is malformed or was issued for a different Viewer, Profile, or list
133
+ * direction (discard it and restart from the first page), or `INVALID_FOLLOW_LIMIT` if `limit` is outside
134
+ * 1–50.
135
+ */
136
+ getFollowers(params: GetMobileFollowersParams): Promise<MobileFollowListPage>;
137
+ /**
138
+ * Fetches a page of the accounts the given Profile follows. Ordering, cursor rules, and errors match
139
+ * {@link MobileClient.getFollowers}. When the Viewer reads their own Following list every row is familiar,
140
+ * so every `isFollowing` is `true`.
141
+ */
142
+ getFollowing(params: GetMobileFollowingParams): Promise<MobileFollowListPage>;
116
143
  /**
117
144
  * Sets a subaccount's username, derived from the given display name. Upserts: the same call claims a first
118
145
  * username and renames an existing one, so callers never need to read the current identity first.
@@ -137,12 +164,33 @@ declare class MobileClient {
137
164
  * too long.
138
165
  */
139
166
  registerExpoToken(params: MobileRegisterExpoTokenParams): Promise<MobileServerSuccessResponse>;
140
- protected getSignedRequest<T extends MobileSignedInner['type']>(type: T, params: MobileSignedRequestParams, innerParams: MobileSignedInnerParams<T>): Promise<MobileSignedRequest>;
167
+ /**
168
+ * Sets whether the signing subaccount follows a Profile. Both directions are idempotent: following again
169
+ * keeps the original relationship creation time, so it does not move the account in recency order, and
170
+ * unfollowing a relationship that does not exist succeeds with `isFollowing: false`. Unfollowing deletes
171
+ * the relationship rather than deactivating it, so a later follow gets a new creation time and moves the
172
+ * account to the front of recency order.
173
+ *
174
+ * Only `isFollowing: true` is checked against the canonical Query DB subaccount index, so unfollowing still
175
+ * works while that index lags.
176
+ *
177
+ * @throws {MobileServerFailureError} With error code `INVALID_FOLLOW_TARGET` if the target is the sender or
178
+ * either party is isolated, `FOLLOWER_NOT_ELIGIBLE` if the sender is not yet in the canonical Query DB
179
+ * subaccount index, or `FOLLOWING_NOT_FOUND` if the target is not. Both index errors are transient for an
180
+ * account that is being recorded, so they are retryable after catch-up — unlike `INVALID_FOLLOW_TARGET`.
181
+ */
182
+ setFollow(params: MobileSetFollowParams): Promise<MobileFollowMutationResult>;
183
+ protected getSignedRequest<T extends MobileSignedInner['type']>(type: T, params: MobileSignedRequestParams, innerParams: MobileSignedInnerParams<T>): Promise<MobileSignedRequest<MobileSignedInnerByType<T>>>;
141
184
  protected publicQuery<T extends MobileServerPublicQueryRequestType>(body: {
142
185
  type: T;
143
186
  } & MobileServerPublicQueryRequestByType[T]): Promise<MobileServerPublicQuerySuccessResponse<T>>;
144
187
  protected publicExecute(body: MobileServerPublicExecuteRequest): Promise<MobileServerSuccessResponse>;
145
- protected execute(body: MobileSignedRequest): Promise<MobileServerSuccessResponse>;
188
+ protected execute<T extends MobileServerExecuteRequestType>(body: MobileSignedRequest & {
189
+ type: T;
190
+ }): Promise<MobileServerExecuteSuccessResponse<T>>;
191
+ protected signedQuery<T extends MobileServerSignedQueryRequestType>(body: MobileSignedRequest & {
192
+ type: T;
193
+ }): Promise<MobileServerSignedQuerySuccessResponse<T>>;
146
194
  /**
147
195
  * Validates the HTTP status before interpreting the body. Unlike the engine, the mobile backend returns
148
196
  * failure envelopes with non-2xx statuses (e.g. 404 for `PROFILE_NOT_FOUND`), so a non-2xx response with a
@@ -1 +1 @@
1
- {"version":3,"file":"MobileClient.d.cts","names":[],"sources":["../src/src/MobileClient.ts"],"mappings":";;;;;;;;;;;;;UA4DiB,gBAAA;EAAgB;;;EAI/B,GAAA;EAIA;;;EAAA,YAAA,GAAe,uBAAA;EAQf;;AAAU;EAJV,wBAAA,GAA2B,uBAAuB;EAW3B;;;EAPvB,UAAA;AAAA;;;;;cAOW,YAAA;EAAA,SACF,IAAA,EAAM,gBAAA;EAAA,SACN,aAAA,EAAe,aAAA;cAEZ,IAAA,EAAM,gBAAA;EAsEuC;;;;;EAvDlD,eAAA,CACL,wBAAA,EAA0B,uBAAA;EAgGjB;;;EApFL,uBAAA,CACJ,MAAA,EAAQ,mCAAA,GACP,OAAA,CAAQ,0BAAA;EA2GR;;;;;;;;EA1FG,gBAAA,CACJ,MAAA,EAAQ,4BAAA,GACP,OAAA,CAAQ,mBAAA;EAmJR;;;;;;;;;;;;EA9HG,OAAA,CAAQ,MAAA,GAAQ,mBAAA,GAA2B,OAAA,CAAQ,cAAA;EAuLnC;;;;;;;;EApKhB,0BAAA,CACJ,MAAA,EAAQ,sCAAA,GACP,OAAA,CAAQ,6BAAA;EAiMA;;;;;;;;;;EA9KL,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,sBAAA;EAjGJ;;;;;;;;;;EAwHD,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,2BAAA;EAxFR;;;;;;;;;;;;EA4GG,6BAAA,CACJ,MAAA,EAAQ,yCAAA,GACP,OAAA,CAAQ,2BAAA;EAhDD;;;;;;;;;;EAuEJ,WAAA,CACJ,MAAA,EAAQ,uBAAA,GACP,OAAA,CAAQ,2BAAA;EA1BT;;;EAoCI,cAAA,CACJ,MAAA,EAAQ,0BAAA,GACP,OAAA,CAAQ,2BAAA;EAbD;;;;;;;;EA8BJ,iBAAA,CACJ,MAAA,EAAQ,6BAAA,GACP,OAAA,CAAQ,2BAAA;EAAA,UAkBK,gBAAA,WAA2B,iBAAA,SAAA,CACzC,IAAA,EAAM,CAAA,EACN,MAAA,EAAQ,yBAAA,EACR,WAAA,EAAa,uBAAA,CAAwB,CAAA,IACpC,OAAA,CAAQ,mBAAA;EAAA,UAeK,WAAA,WAAsB,kCAAA,CAAA,CACpC,IAAA;IAAQ,IAAA,EAAM,CAAA;EAAA,IAAM,oCAAA,CAAqC,CAAA,IACxD,OAAA,CAAQ,sCAAA,CAAuC,CAAA;EAAA,UAalC,aAAA,CACd,IAAA,EAAM,gCAAA,GACL,OAAA,CAAQ,2BAAA;EAAA,UAaK,OAAA,CACd,IAAA,EAAM,mBAAA,GACL,OAAA,CAAQ,2BAAA;EAnDsB;;;;;EAAA,QAqEzB,mBAAA;EAAA,QAYA,iBAAA;AAAA"}
1
+ {"version":3,"file":"MobileClient.d.cts","names":[],"sources":["../src/src/MobileClient.ts"],"mappings":";;;;;;;;;;;;;UAyEiB,gBAAA;EAAgB;;;EAI/B,GAAA;EAIA;;;EAAA,YAAA,GAAe,uBAAA;EAQf;;AAAU;EAJV,wBAAA,GAA2B,uBAAuB;EAW3B;;;EAPvB,UAAA;AAAA;;;;;cAOW,YAAA;EAAA,SACF,IAAA,EAAM,gBAAA;EAAA,SACN,aAAA,EAAe,aAAA;cAEZ,IAAA,EAAM,gBAAA;EAgFuC;;;;;EAjElD,eAAA,CACL,wBAAA,EAA0B,uBAAA;EA0GjB;;;EA9FL,uBAAA,CACJ,MAAA,EAAQ,mCAAA,GACP,OAAA,CAAQ,0BAAA;EAqHR;;;;;;;;;;;;;;;EA7FG,WAAA,CACJ,MAAA,EAAQ,uBAAA,GACP,OAAA,CAAQ,mBAAA;EAsND;;;;;;;;;;;;EA9LJ,OAAA,CAAQ,MAAA,GAAQ,mBAAA,GAA2B,OAAA,CAAQ,cAAA;EAiP9C;;;;;;;;EA9NL,0BAAA,CACJ,MAAA,EAAQ,sCAAA,GACP,OAAA,CAAQ,6BAAA;EA8PH;;;;;;;;;;EA3OF,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,sBAAA;EAyQ2B;;;;;;;;;;EAlPhC,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,2BAAA;EAnJO;;;;;;;;;;;;EAuKZ,6BAAA,CACJ,MAAA,EAAQ,yCAAA,GACP,OAAA,CAAQ,2BAAA;EAjHR;;;;;;;;;;;;;EA2IG,YAAA,CACJ,MAAA,EAAQ,wBAAA,GACP,OAAA,CAAQ,oBAAA;EA5ET;;;;;EA2FI,YAAA,CACJ,MAAA,EAAQ,wBAAA,GACP,OAAA,CAAQ,oBAAA;EAnER;;;;;;;;;;EA2FG,WAAA,CACJ,MAAA,EAAQ,uBAAA,GACP,OAAA,CAAQ,2BAAA;EA3CA;;;EAqDL,cAAA,CACJ,MAAA,EAAQ,0BAAA,GACP,OAAA,CAAQ,2BAAA;EAtCR;;;;;;;;EAuDG,iBAAA,CACJ,MAAA,EAAQ,6BAAA,GACP,OAAA,CAAQ,2BAAA;EApBT;;;;;;;;;;;;;;;EAiDI,SAAA,CACJ,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,0BAAA;EAAA,UAeK,gBAAA,WAA2B,iBAAA,SAAA,CACzC,IAAA,EAAM,CAAA,EACN,MAAA,EAAQ,yBAAA,EACR,WAAA,EAAa,uBAAA,CAAwB,CAAA,IACpC,OAAA,CAAQ,mBAAA,CAAoB,uBAAA,CAAwB,CAAA;EAAA,UAkBvC,WAAA,WAAsB,kCAAA,CAAA,CACpC,IAAA;IAAQ,IAAA,EAAM,CAAA;EAAA,IAAM,oCAAA,CAAqC,CAAA,IACxD,OAAA,CAAQ,sCAAA,CAAuC,CAAA;EAAA,UAalC,aAAA,CACd,IAAA,EAAM,gCAAA,GACL,OAAA,CAAQ,2BAAA;EAAA,UAeK,OAAA,WAAkB,8BAAA,CAAA,CAChC,IAAA,EAAM,mBAAA;IAAwB,IAAA,EAAM,CAAA;EAAA,IACnC,OAAA,CAAQ,kCAAA,CAAmC,CAAA;EAAA,UAa9B,WAAA,WAAsB,kCAAA,CAAA,CACpC,IAAA,EAAM,mBAAA;IAAwB,IAAA,EAAM,CAAA;EAAA,IACnC,OAAA,CAAQ,sCAAA,CAAuC,CAAA;EAjDlC;;;;;EAAA,QAmER,mBAAA;EAAA,QAYA,iBAAA;AAAA"}
@@ -1,9 +1,9 @@
1
- import { _ as MobileSignedRequestParams, a as GetMobileUsernameAvailabilityParams, b as MobileUsernameAvailability, d as MobileNotificationPreferences, f as MobilePublicProfile, g as MobileSetUsernameParams, h as MobileSetPrivateModeParams, i as GetMobileRegisteredWalletParams, m as MobileRegisteredWallet, n as GetMobileNotificationPreferencesParams, p as MobileRegisterExpoTokenParams, r as GetMobilePublicProfileParams, s as MobileFeedPage, t as GetMobileFeedParams, v as MobileUnregisterExpoTokenParams, y as MobileUpdateNotificationPreferencesParams } from "./clientTypes-D-o1_2Yc.js";
2
- import { i as MobileSignedRequest, n as MobileSignedInner, r as MobileSignedInnerParams } from "./types-D5kFJzEX.js";
3
- import { a as MobileServerPublicQueryRequestType, i as MobileServerPublicQueryRequestByType } from "./serverRequestTypes-516zXnQ6.js";
4
- import { n as MobileServerSuccessResponse } from "./serverBaseTypes-B_KHFDab.js";
5
- import { n as MobileServerPublicExecuteRequest } from "./serverExecuteTypes-GFjZj_DC.js";
6
- import { o as MobileServerPublicQuerySuccessResponse } from "./serverQueryTypes-NIrPm2Sl.js";
1
+ import { A as MobileUpdateNotificationPreferencesParams, C as MobileRegisterExpoTokenParams, D as MobileSetUsernameParams, E as MobileSetPrivateModeParams, O as MobileSignedRequestParams, S as MobilePublicProfile, T as MobileSetFollowParams, a as GetMobileNotificationPreferencesParams, b as MobileNotificationPreferences, c as GetMobileUsernameAvailabilityParams, i as GetMobileFollowingParams, j as MobileUsernameAvailability, k as MobileUnregisterExpoTokenParams, m as MobileFollowMutationResult, o as GetMobileProfilesParams, p as MobileFollowListPage, r as GetMobileFollowersParams, s as GetMobileRegisteredWalletParams, t as GetMobileFeedParams, u as MobileFeedPage, w as MobileRegisteredWallet } from "./clientTypes-2I3x4TTt.js";
2
+ import { a as MobileSignedRequest, i as MobileSignedInnerParams, n as MobileSignedInner, r as MobileSignedInnerByType } from "./types-B8lDIhBP.js";
3
+ import { a as MobileServerPublicQueryRequestByType, c as MobileServerSignedQueryRequestType, o as MobileServerPublicQueryRequestType, t as MobileServerExecuteRequestType } from "./serverRequestTypes-BDXIPjtw.js";
4
+ import { n as MobileServerSuccessResponse } from "./serverBaseTypes-DPB1vEvv.js";
5
+ import { i as MobileServerExecuteSuccessResponse, o as MobileServerPublicExecuteRequest } from "./serverExecuteTypes-ih4Xbr3J.js";
6
+ import { s as MobileServerPublicQuerySuccessResponse, u as MobileServerSignedQuerySuccessResponse } from "./serverQueryTypes-LygPrZn7.js";
7
7
  import { WalletClientWithAccount } from "@nadohq/shared";
8
8
  import { AxiosInstance } from "axios";
9
9
 
@@ -30,8 +30,8 @@ interface MobileClientOpts {
30
30
  clientType?: string;
31
31
  }
32
32
  /**
33
- * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, privacy
34
- * settings, and push notification device/preference management.
33
+ * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, the
34
+ * follower/following graph, privacy settings, and push notification device/preference management.
35
35
  */
36
36
  declare class MobileClient {
37
37
  readonly opts: MobileClientOpts;
@@ -48,14 +48,21 @@ declare class MobileClient {
48
48
  */
49
49
  getUsernameAvailability(params: GetMobileUsernameAvailabilityParams): Promise<MobileUsernameAvailability>;
50
50
  /**
51
- * Looks up a subaccount's public profile. Every non-isolated subaccount resolves, with `username` and
52
- * `displayName` `null` until a username is claimed. Private Mode does not hide the profile itself, only the
53
- * account's activity.
51
+ * Looks up public profiles, optionally with follower totals and a follow summary. Returns one profile per
52
+ * requested subaccount, in the requested order, so callers can correlate positionally. Every non-isolated
53
+ * subaccount resolves, with `username` and `displayName` `null` until a username is claimed. Private Mode
54
+ * does not hide the profile itself, only the account's activity.
54
55
  *
55
- * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the subaccount is in the
56
- * engine-created isolated namespace, which cannot own a profile.
56
+ * This is the only profile route, so use it for a single subaccount too. It is unsigned, which means
57
+ * `include.followSummary.viewAs` is an unauthenticated claim: the follow *lists* remain signed precisely
58
+ * because they prove the viewer.
59
+ *
60
+ * @throws {MobileServerFailureError} With error code `INVALID_PROFILES_REQUEST` if `subaccounts` is empty,
61
+ * holds duplicates, or exceeds `MOBILE_PROFILES_MAX_BATCH_SIZE` (25), or `PROFILE_NOT_FOUND` if any
62
+ * requested subaccount — or the follow summary's `viewAs` — is in the engine-created isolated namespace.
63
+ * One isolated member fails the whole batch rather than being omitted.
57
64
  */
58
- getPublicProfile(params: GetMobilePublicProfileParams): Promise<MobilePublicProfile>;
65
+ getProfiles(params: GetMobileProfilesParams): Promise<MobilePublicProfile[]>;
59
66
  /**
60
67
  * Fetches a page of the global trade feed: public perpetual trades, newest first, optionally filtered by a
61
68
  * whole-dollar minimum notional (omitted means unfiltered). Trades by subaccounts that have not claimed a
@@ -113,6 +120,26 @@ declare class MobileClient {
113
120
  * `INVALID_EXPO_TOKEN` if the token is malformed or is not currently registered to a wallet.
114
121
  */
115
122
  updateNotificationPreferences(params: MobileUpdateNotificationPreferencesParams): Promise<MobileServerSuccessResponse>;
123
+ /**
124
+ * Fetches a page of the accounts that follow the given Profile.
125
+ *
126
+ * Familiar accounts (those the signing Viewer follows) come first, unfamiliar second, and within each group
127
+ * the newest listed relationship first with a bytes32 tie-break. The grouping is evaluated per request
128
+ * against the live graph, so following an account from inside the list moves it between groups on the next
129
+ * page — deduplicate by subaccount and do not re-sort locally.
130
+ *
131
+ * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the viewed Profile is isolated,
132
+ * `INVALID_FOLLOW_CURSOR` if the cursor is malformed or was issued for a different Viewer, Profile, or list
133
+ * direction (discard it and restart from the first page), or `INVALID_FOLLOW_LIMIT` if `limit` is outside
134
+ * 1–50.
135
+ */
136
+ getFollowers(params: GetMobileFollowersParams): Promise<MobileFollowListPage>;
137
+ /**
138
+ * Fetches a page of the accounts the given Profile follows. Ordering, cursor rules, and errors match
139
+ * {@link MobileClient.getFollowers}. When the Viewer reads their own Following list every row is familiar,
140
+ * so every `isFollowing` is `true`.
141
+ */
142
+ getFollowing(params: GetMobileFollowingParams): Promise<MobileFollowListPage>;
116
143
  /**
117
144
  * Sets a subaccount's username, derived from the given display name. Upserts: the same call claims a first
118
145
  * username and renames an existing one, so callers never need to read the current identity first.
@@ -137,12 +164,33 @@ declare class MobileClient {
137
164
  * too long.
138
165
  */
139
166
  registerExpoToken(params: MobileRegisterExpoTokenParams): Promise<MobileServerSuccessResponse>;
140
- protected getSignedRequest<T extends MobileSignedInner['type']>(type: T, params: MobileSignedRequestParams, innerParams: MobileSignedInnerParams<T>): Promise<MobileSignedRequest>;
167
+ /**
168
+ * Sets whether the signing subaccount follows a Profile. Both directions are idempotent: following again
169
+ * keeps the original relationship creation time, so it does not move the account in recency order, and
170
+ * unfollowing a relationship that does not exist succeeds with `isFollowing: false`. Unfollowing deletes
171
+ * the relationship rather than deactivating it, so a later follow gets a new creation time and moves the
172
+ * account to the front of recency order.
173
+ *
174
+ * Only `isFollowing: true` is checked against the canonical Query DB subaccount index, so unfollowing still
175
+ * works while that index lags.
176
+ *
177
+ * @throws {MobileServerFailureError} With error code `INVALID_FOLLOW_TARGET` if the target is the sender or
178
+ * either party is isolated, `FOLLOWER_NOT_ELIGIBLE` if the sender is not yet in the canonical Query DB
179
+ * subaccount index, or `FOLLOWING_NOT_FOUND` if the target is not. Both index errors are transient for an
180
+ * account that is being recorded, so they are retryable after catch-up — unlike `INVALID_FOLLOW_TARGET`.
181
+ */
182
+ setFollow(params: MobileSetFollowParams): Promise<MobileFollowMutationResult>;
183
+ protected getSignedRequest<T extends MobileSignedInner['type']>(type: T, params: MobileSignedRequestParams, innerParams: MobileSignedInnerParams<T>): Promise<MobileSignedRequest<MobileSignedInnerByType<T>>>;
141
184
  protected publicQuery<T extends MobileServerPublicQueryRequestType>(body: {
142
185
  type: T;
143
186
  } & MobileServerPublicQueryRequestByType[T]): Promise<MobileServerPublicQuerySuccessResponse<T>>;
144
187
  protected publicExecute(body: MobileServerPublicExecuteRequest): Promise<MobileServerSuccessResponse>;
145
- protected execute(body: MobileSignedRequest): Promise<MobileServerSuccessResponse>;
188
+ protected execute<T extends MobileServerExecuteRequestType>(body: MobileSignedRequest & {
189
+ type: T;
190
+ }): Promise<MobileServerExecuteSuccessResponse<T>>;
191
+ protected signedQuery<T extends MobileServerSignedQueryRequestType>(body: MobileSignedRequest & {
192
+ type: T;
193
+ }): Promise<MobileServerSignedQuerySuccessResponse<T>>;
146
194
  /**
147
195
  * Validates the HTTP status before interpreting the body. Unlike the engine, the mobile backend returns
148
196
  * failure envelopes with non-2xx statuses (e.g. 404 for `PROFILE_NOT_FOUND`), so a non-2xx response with a
@@ -1 +1 @@
1
- {"version":3,"file":"MobileClient.d.ts","names":[],"sources":["../src/src/MobileClient.ts"],"mappings":";;;;;;;;;;;;;UA4DiB,gBAAA;EAAgB;;;EAI/B,GAAA;EAIA;;;EAAA,YAAA,GAAe,uBAAA;EAQf;;AAAU;EAJV,wBAAA,GAA2B,uBAAuB;EAW3B;;;EAPvB,UAAA;AAAA;;;;;cAOW,YAAA;EAAA,SACF,IAAA,EAAM,gBAAA;EAAA,SACN,aAAA,EAAe,aAAA;cAEZ,IAAA,EAAM,gBAAA;EAsEuC;;;;;EAvDlD,eAAA,CACL,wBAAA,EAA0B,uBAAA;EAgGjB;;;EApFL,uBAAA,CACJ,MAAA,EAAQ,mCAAA,GACP,OAAA,CAAQ,0BAAA;EA2GR;;;;;;;;EA1FG,gBAAA,CACJ,MAAA,EAAQ,4BAAA,GACP,OAAA,CAAQ,mBAAA;EAmJR;;;;;;;;;;;;EA9HG,OAAA,CAAQ,MAAA,GAAQ,mBAAA,GAA2B,OAAA,CAAQ,cAAA;EAuLnC;;;;;;;;EApKhB,0BAAA,CACJ,MAAA,EAAQ,sCAAA,GACP,OAAA,CAAQ,6BAAA;EAiMA;;;;;;;;;;EA9KL,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,sBAAA;EAjGJ;;;;;;;;;;EAwHD,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,2BAAA;EAxFR;;;;;;;;;;;;EA4GG,6BAAA,CACJ,MAAA,EAAQ,yCAAA,GACP,OAAA,CAAQ,2BAAA;EAhDD;;;;;;;;;;EAuEJ,WAAA,CACJ,MAAA,EAAQ,uBAAA,GACP,OAAA,CAAQ,2BAAA;EA1BT;;;EAoCI,cAAA,CACJ,MAAA,EAAQ,0BAAA,GACP,OAAA,CAAQ,2BAAA;EAbD;;;;;;;;EA8BJ,iBAAA,CACJ,MAAA,EAAQ,6BAAA,GACP,OAAA,CAAQ,2BAAA;EAAA,UAkBK,gBAAA,WAA2B,iBAAA,SAAA,CACzC,IAAA,EAAM,CAAA,EACN,MAAA,EAAQ,yBAAA,EACR,WAAA,EAAa,uBAAA,CAAwB,CAAA,IACpC,OAAA,CAAQ,mBAAA;EAAA,UAeK,WAAA,WAAsB,kCAAA,CAAA,CACpC,IAAA;IAAQ,IAAA,EAAM,CAAA;EAAA,IAAM,oCAAA,CAAqC,CAAA,IACxD,OAAA,CAAQ,sCAAA,CAAuC,CAAA;EAAA,UAalC,aAAA,CACd,IAAA,EAAM,gCAAA,GACL,OAAA,CAAQ,2BAAA;EAAA,UAaK,OAAA,CACd,IAAA,EAAM,mBAAA,GACL,OAAA,CAAQ,2BAAA;EAnDsB;;;;;EAAA,QAqEzB,mBAAA;EAAA,QAYA,iBAAA;AAAA"}
1
+ {"version":3,"file":"MobileClient.d.ts","names":[],"sources":["../src/src/MobileClient.ts"],"mappings":";;;;;;;;;;;;;UAyEiB,gBAAA;EAAgB;;;EAI/B,GAAA;EAIA;;;EAAA,YAAA,GAAe,uBAAA;EAQf;;AAAU;EAJV,wBAAA,GAA2B,uBAAuB;EAW3B;;;EAPvB,UAAA;AAAA;;;;;cAOW,YAAA;EAAA,SACF,IAAA,EAAM,gBAAA;EAAA,SACN,aAAA,EAAe,aAAA;cAEZ,IAAA,EAAM,gBAAA;EAgFuC;;;;;EAjElD,eAAA,CACL,wBAAA,EAA0B,uBAAA;EA0GjB;;;EA9FL,uBAAA,CACJ,MAAA,EAAQ,mCAAA,GACP,OAAA,CAAQ,0BAAA;EAqHR;;;;;;;;;;;;;;;EA7FG,WAAA,CACJ,MAAA,EAAQ,uBAAA,GACP,OAAA,CAAQ,mBAAA;EAsND;;;;;;;;;;;;EA9LJ,OAAA,CAAQ,MAAA,GAAQ,mBAAA,GAA2B,OAAA,CAAQ,cAAA;EAiP9C;;;;;;;;EA9NL,0BAAA,CACJ,MAAA,EAAQ,sCAAA,GACP,OAAA,CAAQ,6BAAA;EA8PH;;;;;;;;;;EA3OF,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,sBAAA;EAyQ2B;;;;;;;;;;EAlPhC,mBAAA,CACJ,MAAA,EAAQ,+BAAA,GACP,OAAA,CAAQ,2BAAA;EAnJO;;;;;;;;;;;;EAuKZ,6BAAA,CACJ,MAAA,EAAQ,yCAAA,GACP,OAAA,CAAQ,2BAAA;EAjHR;;;;;;;;;;;;;EA2IG,YAAA,CACJ,MAAA,EAAQ,wBAAA,GACP,OAAA,CAAQ,oBAAA;EA5ET;;;;;EA2FI,YAAA,CACJ,MAAA,EAAQ,wBAAA,GACP,OAAA,CAAQ,oBAAA;EAnER;;;;;;;;;;EA2FG,WAAA,CACJ,MAAA,EAAQ,uBAAA,GACP,OAAA,CAAQ,2BAAA;EA3CA;;;EAqDL,cAAA,CACJ,MAAA,EAAQ,0BAAA,GACP,OAAA,CAAQ,2BAAA;EAtCR;;;;;;;;EAuDG,iBAAA,CACJ,MAAA,EAAQ,6BAAA,GACP,OAAA,CAAQ,2BAAA;EApBT;;;;;;;;;;;;;;;EAiDI,SAAA,CACJ,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,0BAAA;EAAA,UAeK,gBAAA,WAA2B,iBAAA,SAAA,CACzC,IAAA,EAAM,CAAA,EACN,MAAA,EAAQ,yBAAA,EACR,WAAA,EAAa,uBAAA,CAAwB,CAAA,IACpC,OAAA,CAAQ,mBAAA,CAAoB,uBAAA,CAAwB,CAAA;EAAA,UAkBvC,WAAA,WAAsB,kCAAA,CAAA,CACpC,IAAA;IAAQ,IAAA,EAAM,CAAA;EAAA,IAAM,oCAAA,CAAqC,CAAA,IACxD,OAAA,CAAQ,sCAAA,CAAuC,CAAA;EAAA,UAalC,aAAA,CACd,IAAA,EAAM,gCAAA,GACL,OAAA,CAAQ,2BAAA;EAAA,UAeK,OAAA,WAAkB,8BAAA,CAAA,CAChC,IAAA,EAAM,mBAAA;IAAwB,IAAA,EAAM,CAAA;EAAA,IACnC,OAAA,CAAQ,kCAAA,CAAmC,CAAA;EAAA,UAa9B,WAAA,WAAsB,kCAAA,CAAA,CACpC,IAAA,EAAM,mBAAA;IAAwB,IAAA,EAAM,CAAA;EAAA,IACnC,OAAA,CAAQ,sCAAA,CAAuC,CAAA;EAjDlC;;;;;EAAA,QAmER,mBAAA;EAAA,QAYA,iBAAA;AAAA"}