@nadohq/mobile-client 0.36.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
@@ -1,4 +1,4 @@
1
- import { mapMobileFeedPage, mapMobileNotificationPreferences, mapMobileNotificationPreferencesToServer, mapMobilePublicProfile, mapMobileRegisteredWallet } from "./dataMappers.js";
1
+ import { mapMobileFeedPage, mapMobileFollowListPage, mapMobileFollowMutationResult, mapMobileNotificationPreferences, mapMobileNotificationPreferencesToServer, mapMobileProfilesIncludeToServer, mapMobilePublicProfile, mapMobileRegisteredWallet } from "./dataMappers.js";
2
2
  import { buildSignedMobileRequest } from "./signing/buildSignedMobileRequest.js";
3
3
  import "./signing/index.js";
4
4
  import { MobileServerFailureError } from "./types/MobileServerFailureError.js";
@@ -7,8 +7,8 @@ import { WalletNotProvidedError, getNadoClientTypeHeaders, subaccountToHex } fro
7
7
  import axios from "axios";
8
8
  //#region src/MobileClient.ts
9
9
  /**
10
- * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, privacy
11
- * settings, and push notification device/preference management.
10
+ * Client for the Nado mobile service API: usernames, public profile lookups, the global trade feed, the
11
+ * follower/following graph, privacy settings, and push notification device/preference management.
12
12
  */
13
13
  var MobileClient = class {
14
14
  constructor(opts) {
@@ -42,19 +42,27 @@ var MobileClient = class {
42
42
  };
43
43
  }
44
44
  /**
45
- * Looks up a subaccount's public profile. Every non-isolated subaccount resolves, with `username` and
46
- * `displayName` `null` until a username is claimed. Private Mode does not hide the profile itself, only the
47
- * account's activity.
45
+ * Looks up public profiles, optionally with follower totals and a follow summary. Returns one profile per
46
+ * requested subaccount, in the requested order, so callers can correlate positionally. Every non-isolated
47
+ * subaccount resolves, with `username` and `displayName` `null` until a username is claimed. Private Mode
48
+ * does not hide the profile itself, only the account's activity.
48
49
  *
49
- * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the subaccount is in the
50
- * engine-created isolated namespace, which cannot own a profile.
50
+ * This is the only profile route, so use it for a single subaccount too. It is unsigned, which means
51
+ * `include.followSummary.viewAs` is an unauthenticated claim: the follow *lists* remain signed precisely
52
+ * because they prove the viewer.
53
+ *
54
+ * @throws {MobileServerFailureError} With error code `INVALID_PROFILES_REQUEST` if `subaccounts` is empty,
55
+ * holds duplicates, or exceeds `MOBILE_PROFILES_MAX_BATCH_SIZE` (25), or `PROFILE_NOT_FOUND` if any
56
+ * requested subaccount — or the follow summary's `viewAs` — is in the engine-created isolated namespace.
57
+ * One isolated member fails the whole batch rather than being omitted.
51
58
  */
52
- async getPublicProfile(params) {
59
+ async getProfiles(params) {
53
60
  const body = {
54
- type: "profile",
55
- subaccount: subaccountToHex(params)
61
+ type: "profiles",
62
+ subaccounts: params.subaccounts.map(subaccountToHex),
63
+ include: params.include ? mapMobileProfilesIncludeToServer(params.include) : void 0
56
64
  };
57
- return mapMobilePublicProfile((await this.publicQuery(body)).profile);
65
+ return (await this.publicQuery(body)).profiles.map(mapMobilePublicProfile);
58
66
  }
59
67
  /**
60
68
  * Fetches a page of the global trade feed: public perpetual trades, newest first, optionally filtered by a
@@ -147,6 +155,40 @@ var MobileClient = class {
147
155
  return this.publicExecute(body);
148
156
  }
149
157
  /**
158
+ * Fetches a page of the accounts that follow the given Profile.
159
+ *
160
+ * Familiar accounts (those the signing Viewer follows) come first, unfamiliar second, and within each group
161
+ * the newest listed relationship first with a bytes32 tie-break. The grouping is evaluated per request
162
+ * against the live graph, so following an account from inside the list moves it between groups on the next
163
+ * page — deduplicate by subaccount and do not re-sort locally.
164
+ *
165
+ * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the viewed Profile is isolated,
166
+ * `INVALID_FOLLOW_CURSOR` if the cursor is malformed or was issued for a different Viewer, Profile, or list
167
+ * direction (discard it and restart from the first page), or `INVALID_FOLLOW_LIMIT` if `limit` is outside
168
+ * 1–50.
169
+ */
170
+ async getFollowers(params) {
171
+ const signedRequest = await this.getSignedRequest("followers", params, {
172
+ subaccount: subaccountToHex(params.target),
173
+ cursor: params.cursor ?? null,
174
+ limit: params.limit ?? null
175
+ });
176
+ return mapMobileFollowListPage(await this.signedQuery(signedRequest));
177
+ }
178
+ /**
179
+ * Fetches a page of the accounts the given Profile follows. Ordering, cursor rules, and errors match
180
+ * {@link MobileClient.getFollowers}. When the Viewer reads their own Following list every row is familiar,
181
+ * so every `isFollowing` is `true`.
182
+ */
183
+ async getFollowing(params) {
184
+ const signedRequest = await this.getSignedRequest("following", params, {
185
+ subaccount: subaccountToHex(params.target),
186
+ cursor: params.cursor ?? null,
187
+ limit: params.limit ?? null
188
+ });
189
+ return mapMobileFollowListPage(await this.signedQuery(signedRequest));
190
+ }
191
+ /**
150
192
  * Sets a subaccount's username, derived from the given display name. Upserts: the same call claims a first
151
193
  * username and renames an existing one, so callers never need to read the current identity first.
152
194
  *
@@ -184,6 +226,28 @@ var MobileClient = class {
184
226
  });
185
227
  return this.execute(signedRequest);
186
228
  }
229
+ /**
230
+ * Sets whether the signing subaccount follows a Profile. Both directions are idempotent: following again
231
+ * keeps the original relationship creation time, so it does not move the account in recency order, and
232
+ * unfollowing a relationship that does not exist succeeds with `isFollowing: false`. Unfollowing deletes
233
+ * the relationship rather than deactivating it, so a later follow gets a new creation time and moves the
234
+ * account to the front of recency order.
235
+ *
236
+ * Only `isFollowing: true` is checked against the canonical Query DB subaccount index, so unfollowing still
237
+ * works while that index lags.
238
+ *
239
+ * @throws {MobileServerFailureError} With error code `INVALID_FOLLOW_TARGET` if the target is the sender or
240
+ * either party is isolated, `FOLLOWER_NOT_ELIGIBLE` if the sender is not yet in the canonical Query DB
241
+ * subaccount index, or `FOLLOWING_NOT_FOUND` if the target is not. Both index errors are transient for an
242
+ * account that is being recorded, so they are retryable after catch-up — unlike `INVALID_FOLLOW_TARGET`.
243
+ */
244
+ async setFollow(params) {
245
+ const signedRequest = await this.getSignedRequest("set_follow", params, {
246
+ subaccount: subaccountToHex(params.target),
247
+ is_following: params.isFollowing
248
+ });
249
+ return mapMobileFollowMutationResult(await this.execute(signedRequest));
250
+ }
187
251
  async getSignedRequest(type, params, innerParams) {
188
252
  const walletClient = this.opts.linkedSignerWalletClient ?? this.opts.walletClient;
189
253
  if (walletClient == null) throw new WalletNotProvidedError();
@@ -215,6 +279,12 @@ var MobileClient = class {
215
279
  this.checkServerStatus(response);
216
280
  return response.data;
217
281
  }
282
+ async signedQuery(body) {
283
+ const response = await this.axiosInstance.post(`${this.opts.url}/mobile/query`, body);
284
+ this.checkResponseStatus(response);
285
+ this.checkServerStatus(response);
286
+ return response.data;
287
+ }
218
288
  /**
219
289
  * Validates the HTTP status before interpreting the body. Unlike the engine, the mobile backend returns
220
290
  * 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.js","names":[],"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 mapMobileNotificationPreferences,\n mapMobileNotificationPreferencesToServer,\n mapMobilePublicProfile,\n mapMobileRegisteredWallet,\n} from './dataMappers';\nimport {\n buildSignedMobileRequest,\n MobileSignedInner,\n MobileSignedInnerParams,\n MobileSignedRequest,\n} from './signing';\nimport {\n GetMobileFeedParams,\n GetMobileNotificationPreferencesParams,\n GetMobilePublicProfileParams,\n GetMobileRegisteredWalletParams,\n GetMobileUsernameAvailabilityParams,\n MobileFeedPage,\n MobileNotificationPreferences,\n MobilePublicProfile,\n MobileRegisteredWallet,\n MobileRegisterExpoTokenParams,\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 MobileServerPublicExecuteRequest,\n} from './types/serverExecuteTypes';\nimport {\n MobileServerPublicQueryRequest,\n MobileServerPublicQuerySuccessResponse,\n} from './types/serverQueryTypes';\nimport {\n MobileServerPublicQueryRequestByType,\n MobileServerPublicQueryRequestType,\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, privacy\n * 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 a subaccount's public profile. Every non-isolated subaccount resolves, with `username` and\n * `displayName` `null` until a username is claimed. Private Mode does not hide the profile itself, only the\n * account's activity.\n *\n * @throws {MobileServerFailureError} With error code `PROFILE_NOT_FOUND` if the subaccount is in the\n * engine-created isolated namespace, which cannot own a profile.\n */\n async getPublicProfile(\n params: GetMobilePublicProfileParams,\n ): Promise<MobilePublicProfile> {\n const body: MobileServerPublicQueryRequest<'profile'> = {\n type: 'profile',\n subaccount: subaccountToHex(params),\n };\n const data = await this.publicQuery(body);\n return mapMobilePublicProfile(data.profile);\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 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 Base Fns\n */\n\n protected async getSignedRequest<T extends MobileSignedInner['type']>(\n type: T,\n params: MobileSignedRequestParams,\n innerParams: MobileSignedInnerParams<T>,\n ): Promise<MobileSignedRequest> {\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 = { type, ...innerParams } as unknown as MobileSignedInner;\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 protected async execute(\n body: MobileSignedRequest,\n ): Promise<MobileServerSuccessResponse> {\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 MobileServerSuccessResponse;\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
+ {"version":3,"file":"MobileClient.js","names":[],"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,5 +1,5 @@
1
- import { o as MobileServerRequestType } from "./serverRequestTypes-GAtVYIda.cjs";
2
- import { t as MobileServerFailureResponse } from "./serverBaseTypes-C8KjE9Fm.cjs";
1
+ import { s as MobileServerRequestType } from "./serverRequestTypes-q4xTJHk0.cjs";
2
+ import { t as MobileServerFailureResponse } from "./serverBaseTypes-BybrVE6L.cjs";
3
3
  import { BaseServerFailureError } from "@nadohq/shared";
4
4
 
5
5
  //#region src/types/MobileServerFailureError.d.ts
@@ -20,4 +20,4 @@ declare class MobileServerFailureError extends BaseServerFailureError {
20
20
  }
21
21
  //#endregion
22
22
  export { MobileServerFailureError as t };
23
- //# sourceMappingURL=MobileServerFailureError-CPFjwUBx.d.cts.map
23
+ //# sourceMappingURL=MobileServerFailureError-CVK3aLOi.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MobileServerFailureError-CPFjwUBx.d.cts","names":[],"sources":["../src/types/src/types/MobileServerFailureError.ts"],"mappings":";;;;;;;AASA;;;cAAa,wBAAA,SAAiC,sBAAA;EAAA,SAC3B,YAAA,EAAc,2BAAA;EAAA,SACd,WAAA,EAAa,uBAAA;EAFc;;;;EAAA,SAQnC,UAAA;cAEG,YAAA,EAAc,2BAAA,EAA6B,UAAA;AAAA"}
1
+ {"version":3,"file":"MobileServerFailureError-CVK3aLOi.d.cts","names":[],"sources":["../src/types/src/types/MobileServerFailureError.ts"],"mappings":";;;;;;;AASA;;;cAAa,wBAAA,SAAiC,sBAAA;EAAA,SAC3B,YAAA,EAAc,2BAAA;EAAA,SACd,WAAA,EAAa,uBAAA;EAFc;;;;EAAA,SAQnC,UAAA;cAEG,YAAA,EAAc,2BAAA,EAA6B,UAAA;AAAA"}
@@ -1,5 +1,5 @@
1
- import { o as MobileServerRequestType } from "./serverRequestTypes-516zXnQ6.js";
2
- import { t as MobileServerFailureResponse } from "./serverBaseTypes-B_KHFDab.js";
1
+ import { s as MobileServerRequestType } from "./serverRequestTypes-BDXIPjtw.js";
2
+ import { t as MobileServerFailureResponse } from "./serverBaseTypes-DPB1vEvv.js";
3
3
  import { BaseServerFailureError } from "@nadohq/shared";
4
4
 
5
5
  //#region src/types/MobileServerFailureError.d.ts
@@ -20,4 +20,4 @@ declare class MobileServerFailureError extends BaseServerFailureError {
20
20
  }
21
21
  //#endregion
22
22
  export { MobileServerFailureError as t };
23
- //# sourceMappingURL=MobileServerFailureError-Terv2Wzy.d.ts.map
23
+ //# sourceMappingURL=MobileServerFailureError-D-76n3pS.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MobileServerFailureError-Terv2Wzy.d.ts","names":[],"sources":["../src/types/src/types/MobileServerFailureError.ts"],"mappings":";;;;;;;AASA;;;cAAa,wBAAA,SAAiC,sBAAA;EAAA,SAC3B,YAAA,EAAc,2BAAA;EAAA,SACd,WAAA,EAAa,uBAAA;EAFc;;;;EAAA,SAQnC,UAAA;cAEG,YAAA,EAAc,2BAAA,EAA6B,UAAA;AAAA"}
1
+ {"version":3,"file":"MobileServerFailureError-D-76n3pS.d.ts","names":[],"sources":["../src/types/src/types/MobileServerFailureError.ts"],"mappings":";;;;;;;AASA;;;cAAa,wBAAA,SAAiC,sBAAA;EAAA,SAC3B,YAAA,EAAc,2BAAA;EAAA,SACd,WAAA,EAAa,uBAAA;EAFc;;;;EAAA,SAQnC,UAAA;cAEG,YAAA,EAAc,2BAAA,EAA6B,UAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- import { i as MobileSignedRequest, n as MobileSignedInner, t as BuildSignedMobileRequestParams } from "./types-D5kFJzEX.js";
1
+ import { a as MobileSignedRequest, n as MobileSignedInner, t as BuildSignedMobileRequestParams } from "./types-B8lDIhBP.js";
2
2
 
3
3
  //#region src/signing/buildSignedMobileRequest.d.ts
4
4
  /**
@@ -11,4 +11,4 @@ declare function buildSignedMobileRequest<T extends MobileSignedInner>(params: B
11
11
  }): Promise<MobileSignedRequest<T>>;
12
12
  //#endregion
13
13
  export { buildSignedMobileRequest as t };
14
- //# sourceMappingURL=buildSignedMobileRequest-BScnCGPF.d.ts.map
14
+ //# sourceMappingURL=buildSignedMobileRequest-42meR4yV.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildSignedMobileRequest-BScnCGPF.d.ts","names":[],"sources":["../src/signing/src/signing/buildSignedMobileRequest.ts"],"mappings":";;;;;AAgBA;;;iBAAsB,wBAAA,WAAmC,iBAAA,CAAA,CACvD,MAAA,EAAQ,8BAAA;EAAmC,KAAA,EAAO,CAAA;AAAA,IACjD,OAAA,CAAQ,mBAAA,CAAoB,CAAA"}
1
+ {"version":3,"file":"buildSignedMobileRequest-42meR4yV.d.ts","names":[],"sources":["../src/signing/src/signing/buildSignedMobileRequest.ts"],"mappings":";;;;;AAgBA;;;iBAAsB,wBAAA,WAAmC,iBAAA,CAAA,CACvD,MAAA,EAAQ,8BAAA;EAAmC,KAAA,EAAO,CAAA;AAAA,IACjD,OAAA,CAAQ,mBAAA,CAAoB,CAAA"}
@@ -1,4 +1,4 @@
1
- import { i as MobileSignedRequest, n as MobileSignedInner, t as BuildSignedMobileRequestParams } from "./types-47IPsDyA.cjs";
1
+ import { a as MobileSignedRequest, n as MobileSignedInner, t as BuildSignedMobileRequestParams } from "./types-CClB00EN.cjs";
2
2
 
3
3
  //#region src/signing/buildSignedMobileRequest.d.ts
4
4
  /**
@@ -11,4 +11,4 @@ declare function buildSignedMobileRequest<T extends MobileSignedInner>(params: B
11
11
  }): Promise<MobileSignedRequest<T>>;
12
12
  //#endregion
13
13
  export { buildSignedMobileRequest as t };
14
- //# sourceMappingURL=buildSignedMobileRequest-CXfnhc-4.d.cts.map
14
+ //# sourceMappingURL=buildSignedMobileRequest-BDxji7Vy.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildSignedMobileRequest-CXfnhc-4.d.cts","names":[],"sources":["../src/signing/src/signing/buildSignedMobileRequest.ts"],"mappings":";;;;;AAgBA;;;iBAAsB,wBAAA,WAAmC,iBAAA,CAAA,CACvD,MAAA,EAAQ,8BAAA;EAAmC,KAAA,EAAO,CAAA;AAAA,IACjD,OAAA,CAAQ,mBAAA,CAAoB,CAAA"}
1
+ {"version":3,"file":"buildSignedMobileRequest-BDxji7Vy.d.cts","names":[],"sources":["../src/signing/src/signing/buildSignedMobileRequest.ts"],"mappings":";;;;;AAgBA;;;iBAAsB,wBAAA,WAAmC,iBAAA,CAAA,CACvD,MAAA,EAAQ,8BAAA;EAAmC,KAAA,EAAO,CAAA;AAAA,IACjD,OAAA,CAAQ,mBAAA,CAAoB,CAAA"}
@@ -1,4 +1,4 @@
1
- import { n as MobileSignedInner } from "./types-D5kFJzEX.js";
1
+ import { n as MobileSignedInner } from "./types-B8lDIhBP.js";
2
2
 
3
3
  //#region src/signing/canonicalize.d.ts
4
4
  /**
@@ -11,4 +11,4 @@ import { n as MobileSignedInner } from "./types-D5kFJzEX.js";
11
11
  declare function canonicalizeMobileInner(inner: MobileSignedInner): MobileSignedInner;
12
12
  //#endregion
13
13
  export { canonicalizeMobileInner as t };
14
- //# sourceMappingURL=canonicalize-CQ-pMYIt.d.ts.map
14
+ //# sourceMappingURL=canonicalize-BebHhHcF.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonicalize-BebHhHcF.d.ts","names":[],"sources":["../src/signing/src/signing/canonicalize.ts"],"mappings":";;;;;AASA;;;;;iBAAgB,uBAAA,CACd,KAAA,EAAO,iBAAA,GACN,iBAAiB"}
@@ -1,4 +1,4 @@
1
- import { n as MobileSignedInner } from "./types-47IPsDyA.cjs";
1
+ import { n as MobileSignedInner } from "./types-CClB00EN.cjs";
2
2
 
3
3
  //#region src/signing/canonicalize.d.ts
4
4
  /**
@@ -11,4 +11,4 @@ import { n as MobileSignedInner } from "./types-47IPsDyA.cjs";
11
11
  declare function canonicalizeMobileInner(inner: MobileSignedInner): MobileSignedInner;
12
12
  //#endregion
13
13
  export { canonicalizeMobileInner as t };
14
- //# sourceMappingURL=canonicalize-hzhboB-C.d.cts.map
14
+ //# sourceMappingURL=canonicalize-CIDJLLgg.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonicalize-CIDJLLgg.d.cts","names":[],"sources":["../src/signing/src/signing/canonicalize.ts"],"mappings":";;;;;AASA;;;;;iBAAgB,uBAAA,CACd,KAAA,EAAO,iBAAA,GACN,iBAAiB"}
@@ -1,11 +1,15 @@
1
- import { a as MobileFeedTradePosition, c as MobileNotificationCategory, i as MobileFeedMarginMode, l as MobileNotificationPlatform } from "./serverModelTypes-CwgibK-l.cjs";
1
+ import { a as MobileFeedTradePosition, c as MobileNotificationCategory, i as MobileFeedMarginMode, l as MobileNotificationPlatform } from "./serverModelTypes-DV-eARP8.js";
2
2
  import { SignatureParams, Subaccount } from "@nadohq/shared";
3
3
  import { Hex } from "viem";
4
4
 
5
5
  //#region src/types/clientTypes.d.ts
6
6
  /**
7
7
  * A subaccount's public profile, as returned by an unsigned profile lookup. Private Mode hides the account's
8
- * activity, not its profile, so `privateMode` is exposed here too.
8
+ * activity, not its profile, so `privateMode` is exposed here too. A subaccount with no identity row still
9
+ * yields a profile, with `null` names and `privateMode: false`.
10
+ *
11
+ * The last three fields are `undefined` unless the matching {@link MobileProfilesInclude} asked for them —
12
+ * absent means "not requested" rather than "none".
9
13
  */
10
14
  interface MobilePublicProfile {
11
15
  subaccount: Hex;
@@ -14,6 +18,75 @@ interface MobilePublicProfile {
14
18
  /** User-facing name as claimed, or `null` if no username has been claimed. */
15
19
  displayName: string | null;
16
20
  privateMode: boolean;
21
+ /**
22
+ * Exact count at query time rather than a cached counter, including unnamed and private accounts — Private
23
+ * Mode hides activity, not relationships. Requires the `followCounts` include.
24
+ */
25
+ followerCount?: number;
26
+ followingCount?: number;
27
+ /** Requires the `followSummary` include, and is relative to that include's `viewAs`. */
28
+ followSummary?: MobileFollowSummary;
29
+ }
30
+ /**
31
+ * The identity fields the backend attaches to a subaccount wherever it surfaces one alongside other data.
32
+ * Resolved at read time, so a rename shows up on the next read without any change to the surrounding record.
33
+ */
34
+ interface MobileIdentitySummary {
35
+ subaccount: Hex;
36
+ /** `null` when the account has not claimed a username; render a local fallback label from `subaccount`. */
37
+ username: string | null;
38
+ /** `null` when the account has not claimed a username; render a local fallback label from `subaccount`. */
39
+ displayName: string | null;
40
+ /** Reserved for a future avatar source; `null` until one is implemented. */
41
+ avatarUrl: string | null;
42
+ }
43
+ /**
44
+ * A viewer's relationship with one Profile, plus the Followed By preview: accounts the viewer follows that
45
+ * also follow that Profile. Every preview entry is therefore already familiar to the viewer, which is why it
46
+ * carries no `isFollowing` of its own.
47
+ */
48
+ interface MobileFollowSummary {
49
+ /** The direct `viewer -> viewed Profile` relationship. */
50
+ isFollowing: boolean;
51
+ /** Exact size of the two-edge intersection, independent of how many previews came back. */
52
+ followedByCount: number;
53
+ /**
54
+ * Newest `preview -> viewed Profile` relationship first, with a bytes32 tie-break. Capped at
55
+ * `MOBILE_FOLLOWED_BY_PREVIEW_LIMIT` (2) by the backend and not client-configurable.
56
+ */
57
+ followedBy: MobileIdentitySummary[];
58
+ }
59
+ /**
60
+ * One row of a Followers or Following page.
61
+ */
62
+ interface MobileFollowListAccount {
63
+ identity: MobileIdentitySummary;
64
+ /**
65
+ * Whether the *Viewer* follows this account — not the listed relationship that put it in the page. The
66
+ * Viewer can appear in another Profile's list, and does not follow themself, so their own row is `false`.
67
+ */
68
+ isFollowing: boolean;
69
+ /** This account's own follower count. */
70
+ followerCount: number;
71
+ }
72
+ /**
73
+ * A page of Followers or Following. Pagination is live rather than snapshot: a Follow or Unfollow between
74
+ * pages can move an account, so an account can appear twice or not at all across one session. Deduplicate by
75
+ * {@link MobileIdentitySummary.subaccount} and restart from `cursor: undefined` on refresh.
76
+ */
77
+ interface MobileFollowListPage {
78
+ accounts: MobileFollowListAccount[];
79
+ /** `null` means the list is complete. */
80
+ nextCursor: string | null;
81
+ }
82
+ /**
83
+ * Post-commit state of a follow or unfollow. The mutation and its count share one database transaction, so
84
+ * these values are authoritative — apply them rather than re-reading.
85
+ */
86
+ interface MobileFollowMutationResult {
87
+ isFollowing: boolean;
88
+ /** The *target's* follower count after the mutation, not the sender's. */
89
+ followerCount: number;
17
90
  }
18
91
  /**
19
92
  * Result of a username availability check.
@@ -36,16 +109,9 @@ interface MobileFeedMargin {
36
109
  * (a rename can change an already-fetched trade on the next read). Amounts are display-oriented human-unit
37
110
  * numbers — do not use them for accounting, order construction, or exact threshold decisions.
38
111
  */
39
- interface MobileFeedTrade {
112
+ interface MobileFeedTrade extends MobileIdentitySummary {
40
113
  /** Order digest this trade is keyed by; the stable id for reconciling live pagination. */
41
114
  orderDigest: Hex;
42
- subaccount: Hex;
43
- /** `null` when the trader has not claimed a username; render a local fallback label from `subaccount`. */
44
- username: string | null;
45
- /** `null` when the trader has not claimed a username; render a local fallback label from `subaccount`. */
46
- displayName: string | null;
47
- /** Reserved for a future avatar source; `null` until one is implemented. */
48
- avatarUrl: string | null;
49
115
  productId: number;
50
116
  /** Executed quantity in human units. */
51
117
  quantity: number;
@@ -142,9 +208,32 @@ interface GetMobileUsernameAvailabilityParams {
142
208
  displayName: string;
143
209
  }
144
210
  /**
145
- * Params for {@link MobileClient.getPublicProfile}.
211
+ * Opt-in extras for a profiles lookup. Each one adds fields that are otherwise absent from every returned
212
+ * profile, and costs extra rate-limit weight per profile, so ask only for what the UI shows.
146
213
  */
147
- type GetMobilePublicProfileParams = Subaccount;
214
+ interface MobileProfilesInclude {
215
+ /** Populates `followerCount` and `followingCount`. */
216
+ followCounts?: boolean;
217
+ /**
218
+ * Populates `followSummary`, relative to `viewAs`. Unlike the signed follow list reads, this viewer
219
+ * identity is an unauthenticated claim — the route takes no signature — so do not treat the result as
220
+ * proof of who is asking.
221
+ */
222
+ followSummary?: {
223
+ viewAs: Subaccount;
224
+ };
225
+ }
226
+ /**
227
+ * Params for {@link MobileClient.getProfiles}.
228
+ */
229
+ interface GetMobileProfilesParams {
230
+ /**
231
+ * 1–`MOBILE_PROFILES_MAX_BATCH_SIZE` (25) distinct subaccounts. Duplicates and an empty list are rejected
232
+ * rather than deduplicated, and results come back in this exact order.
233
+ */
234
+ subaccounts: Subaccount[];
235
+ include?: MobileProfilesInclude;
236
+ }
148
237
  /**
149
238
  * Params for {@link MobileClient.getFeed}.
150
239
  */
@@ -175,6 +264,45 @@ interface MobileSetUsernameParams extends MobileSignedRequestParams {
175
264
  interface MobileSetPrivateModeParams extends MobileSignedRequestParams {
176
265
  privateMode: boolean;
177
266
  }
267
+ /**
268
+ * Common params for the signed follow requests. The subaccount carried by {@link MobileSignedRequestParams}
269
+ * is the signing Viewer (the Follower, for mutations); `target` is the other party.
270
+ */
271
+ interface MobileFollowRequestParams extends MobileSignedRequestParams {
272
+ /** The Profile being followed, unfollowed, or read. Must not be the signing subaccount. */
273
+ target: Subaccount;
274
+ }
275
+ /**
276
+ * Params for {@link MobileClient.setFollow}.
277
+ */
278
+ interface MobileSetFollowParams extends MobileFollowRequestParams {
279
+ /** `true` follows the target, `false` unfollows it. Both directions are idempotent. */
280
+ isFollowing: boolean;
281
+ }
282
+ /**
283
+ * Common params for a Followers or Following page.
284
+ */
285
+ interface GetMobileFollowListParams extends MobileFollowRequestParams {
286
+ /**
287
+ * Opaque cursor from the previous page's {@link MobileFollowListPage.nextCursor}. Omit for the first page.
288
+ * A cursor is bound to its Viewer, Profile, and list direction — reusing it elsewhere fails with
289
+ * `INVALID_FOLLOW_CURSOR`.
290
+ */
291
+ cursor?: string;
292
+ /**
293
+ * Page size, 1–`MOBILE_FOLLOW_LIST_MAX_PAGE_SIZE` (50); the backend defaults to
294
+ * `MOBILE_FOLLOW_LIST_DEFAULT_PAGE_SIZE` (25) when omitted.
295
+ */
296
+ limit?: number;
297
+ }
298
+ /**
299
+ * Params for {@link MobileClient.getFollowers}.
300
+ */
301
+ type GetMobileFollowersParams = GetMobileFollowListParams;
302
+ /**
303
+ * Params for {@link MobileClient.getFollowing}.
304
+ */
305
+ type GetMobileFollowingParams = GetMobileFollowListParams;
178
306
  /**
179
307
  * Params for {@link MobileClient.registerExpoToken}.
180
308
  */
@@ -212,5 +340,5 @@ type GetMobileNotificationPreferencesParams = MobileWithExpoTokenParams;
212
340
  */
213
341
  type GetMobileRegisteredWalletParams = MobileWithExpoTokenParams;
214
342
  //#endregion
215
- export { MobileSignedRequestParams as _, GetMobileUsernameAvailabilityParams as a, MobileUsernameAvailability as b, MobileFeedTrade as c, MobileNotificationPreferences as d, MobilePublicProfile as f, MobileSetUsernameParams as g, MobileSetPrivateModeParams as h, GetMobileRegisteredWalletParams as i, MobileNotificationCategoryPreference as l, MobileRegisteredWallet as m, GetMobileNotificationPreferencesParams as n, MobileFeedMargin as o, MobileRegisterExpoTokenParams as p, GetMobilePublicProfileParams as r, MobileFeedPage as s, GetMobileFeedParams as t, MobileNotificationPreferenceScope as u, MobileUnregisterExpoTokenParams as v, MobileWithExpoTokenParams as x, MobileUpdateNotificationPreferencesParams as y };
216
- //# sourceMappingURL=clientTypes-BwakovWk.d.cts.map
343
+ export { MobileUpdateNotificationPreferencesParams as A, MobileRegisterExpoTokenParams as C, MobileSetUsernameParams as D, MobileSetPrivateModeParams as E, MobileWithExpoTokenParams as M, MobileSignedRequestParams as O, MobilePublicProfile as S, MobileSetFollowParams as T, MobileIdentitySummary as _, GetMobileNotificationPreferencesParams as a, MobileNotificationPreferences as b, GetMobileUsernameAvailabilityParams as c, MobileFeedTrade as d, MobileFollowListAccount as f, MobileFollowSummary as g, MobileFollowRequestParams as h, GetMobileFollowingParams as i, MobileUsernameAvailability as j, MobileUnregisterExpoTokenParams as k, MobileFeedMargin as l, MobileFollowMutationResult as m, GetMobileFollowListParams as n, GetMobileProfilesParams as o, MobileFollowListPage as p, GetMobileFollowersParams as r, GetMobileRegisteredWalletParams as s, GetMobileFeedParams as t, MobileFeedPage as u, MobileNotificationCategoryPreference as v, MobileRegisteredWallet as w, MobileProfilesInclude as x, MobileNotificationPreferenceScope as y };
344
+ //# sourceMappingURL=clientTypes-2I3x4TTt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clientTypes-2I3x4TTt.d.ts","names":[],"sources":["../src/types/src/types/clientTypes.ts"],"mappings":"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"}