@nadohq/mobile-client 0.65.0 → 0.67.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 (211) hide show
  1. package/README.md +202 -10
  2. package/dist/{MobileClient-CfVZXTcz.cjs → MobileClient-vuI5wlB_.cjs} +303 -21
  3. package/dist/MobileClient-vuI5wlB_.cjs.map +1 -0
  4. package/dist/MobileClient.cjs +1 -1
  5. package/dist/MobileClient.d.cts +175 -20
  6. package/dist/MobileClient.d.cts.map +1 -1
  7. package/dist/MobileClient.d.ts +175 -20
  8. package/dist/MobileClient.d.ts.map +1 -1
  9. package/dist/MobileClient.js +303 -21
  10. package/dist/MobileClient.js.map +1 -1
  11. package/dist/{MobileServerFailureError-BZVwlsMg.d.ts → MobileServerFailureError-BeCCNShj.d.cts} +2 -3
  12. package/dist/MobileServerFailureError-BeCCNShj.d.cts.map +1 -0
  13. package/dist/{MobileServerFailureError-CbtK8kD4.d.cts → MobileServerFailureError-hHv28weI.d.ts} +2 -3
  14. package/dist/MobileServerFailureError-hHv28weI.d.ts.map +1 -0
  15. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.cts +15 -0
  16. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.cts.map +1 -0
  17. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.ts +15 -0
  18. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.ts.map +1 -0
  19. package/dist/{buildSignedMobileRequest-C1v0flCR.d.cts → buildSignedMobileRequest-DcEMKU_4.d.cts} +2 -2
  20. package/dist/{buildSignedMobileRequest-C1v0flCR.d.cts.map → buildSignedMobileRequest-DcEMKU_4.d.cts.map} +1 -1
  21. package/dist/{buildSignedMobileRequest-UeeWdYGU.d.ts → buildSignedMobileRequest-DlTMvoXR.d.ts} +2 -2
  22. package/dist/{buildSignedMobileRequest-UeeWdYGU.d.ts.map → buildSignedMobileRequest-DlTMvoXR.d.ts.map} +1 -1
  23. package/dist/{canonicalize-5eeZrkzQ.d.ts → canonicalize-BOZlflI3.d.ts} +2 -2
  24. package/dist/canonicalize-BOZlflI3.d.ts.map +1 -0
  25. package/dist/{canonicalize-CBaZprTo.d.cts → canonicalize-BvCozzvJ.d.cts} +2 -2
  26. package/dist/canonicalize-BvCozzvJ.d.cts.map +1 -0
  27. package/dist/{clientTypes-_mEgzmNs.d.cts → clientTypes-KpHlCDGZ.d.cts} +341 -38
  28. package/dist/clientTypes-KpHlCDGZ.d.cts.map +1 -0
  29. package/dist/{clientTypes-C9dJufxk.d.ts → clientTypes-XRI41VdC.d.ts} +341 -38
  30. package/dist/clientTypes-XRI41VdC.d.ts.map +1 -0
  31. package/dist/dataMappers.cjs +257 -20
  32. package/dist/dataMappers.cjs.map +1 -1
  33. package/dist/dataMappers.d.cts +98 -15
  34. package/dist/dataMappers.d.cts.map +1 -1
  35. package/dist/dataMappers.d.ts +98 -15
  36. package/dist/dataMappers.d.ts.map +1 -1
  37. package/dist/dataMappers.js +242 -20
  38. package/dist/dataMappers.js.map +1 -1
  39. package/dist/{eip712MethodByType-D8jLoxDu.d.cts → eip712MethodByType-DyAmzH7b.d.cts} +2 -2
  40. package/dist/eip712MethodByType-DyAmzH7b.d.cts.map +1 -0
  41. package/dist/{eip712MethodByType-DcUh8h-l.d.ts → eip712MethodByType-qSSAF6i2.d.ts} +2 -2
  42. package/dist/eip712MethodByType-qSSAF6i2.d.ts.map +1 -0
  43. package/dist/feed-BrgkFqP3.d.cts +23 -0
  44. package/dist/feed-BrgkFqP3.d.cts.map +1 -0
  45. package/dist/feed-DcbhLoo1.d.ts +23 -0
  46. package/dist/feed-DcbhLoo1.d.ts.map +1 -0
  47. package/dist/index-BVVgDSdq.d.cts +1 -0
  48. package/dist/index-BVVgDSdq.d.ts +1 -0
  49. package/dist/index.cjs +8 -2
  50. package/dist/index.d.cts +14 -16
  51. package/dist/index.d.ts +14 -16
  52. package/dist/index.js +4 -3
  53. package/dist/{mobileErrorCodes-CmjYEIYm.d.ts → mobileErrorCodes-CdwX6QTO.d.cts} +8 -3
  54. package/dist/mobileErrorCodes-CdwX6QTO.d.cts.map +1 -0
  55. package/dist/{mobileErrorCodes-CmjYEIYm.d.cts → mobileErrorCodes-CdwX6QTO.d.ts} +8 -3
  56. package/dist/mobileErrorCodes-CdwX6QTO.d.ts.map +1 -0
  57. package/dist/{payloadHash-DhHmgRqL.d.cts → payloadHash-BgGSZock.d.cts} +2 -2
  58. package/dist/payloadHash-BgGSZock.d.cts.map +1 -0
  59. package/dist/{payloadHash-E1_8fpLW.d.ts → payloadHash-Bi6upCLQ.d.ts} +2 -2
  60. package/dist/payloadHash-Bi6upCLQ.d.ts.map +1 -0
  61. package/dist/{serverExecuteTypes-BwWGspOg.d.ts → serverExecuteTypes-DuCWe_H_.d.cts} +23 -14
  62. package/dist/serverExecuteTypes-DuCWe_H_.d.cts.map +1 -0
  63. package/dist/{serverExecuteTypes-B-OJok19.d.cts → serverExecuteTypes-usJ6HMJv.d.ts} +23 -14
  64. package/dist/serverExecuteTypes-usJ6HMJv.d.ts.map +1 -0
  65. package/dist/{serverModelTypes-CiA-dy1R.d.ts → serverModelTypes-CTHMNIQS.d.cts} +170 -13
  66. package/dist/serverModelTypes-CTHMNIQS.d.cts.map +1 -0
  67. package/dist/{serverModelTypes-CiA-dy1R.d.cts → serverModelTypes-CTHMNIQS.d.ts} +170 -13
  68. package/dist/serverModelTypes-CTHMNIQS.d.ts.map +1 -0
  69. package/dist/{serverResponseGuards-CShiXpHT.d.ts → serverResponseGuards-CzmldAad.d.cts} +2 -2
  70. package/dist/{serverResponseGuards-BW69PS7O.d.cts.map → serverResponseGuards-CzmldAad.d.cts.map} +1 -1
  71. package/dist/{serverResponseGuards-BW69PS7O.d.cts → serverResponseGuards-Dvkbjp2_.d.ts} +2 -2
  72. package/dist/{serverResponseGuards-CShiXpHT.d.ts.map → serverResponseGuards-Dvkbjp2_.d.ts.map} +1 -1
  73. package/dist/signing/buildSignedMobileRequest.d.cts +1 -1
  74. package/dist/signing/buildSignedMobileRequest.d.ts +1 -1
  75. package/dist/signing/canonicalize.cjs +53 -2
  76. package/dist/signing/canonicalize.cjs.map +1 -1
  77. package/dist/signing/canonicalize.d.cts +1 -1
  78. package/dist/signing/canonicalize.d.ts +1 -1
  79. package/dist/signing/canonicalize.js +53 -2
  80. package/dist/signing/canonicalize.js.map +1 -1
  81. package/dist/signing/eip712MethodByType.cjs +7 -0
  82. package/dist/signing/eip712MethodByType.cjs.map +1 -1
  83. package/dist/signing/eip712MethodByType.d.cts +1 -1
  84. package/dist/signing/eip712MethodByType.d.ts +1 -1
  85. package/dist/signing/eip712MethodByType.js +7 -0
  86. package/dist/signing/eip712MethodByType.js.map +1 -1
  87. package/dist/signing/index.d.cts +5 -5
  88. package/dist/signing/index.d.ts +5 -5
  89. package/dist/signing/payloadHash.cjs +1 -1
  90. package/dist/signing/payloadHash.d.cts +1 -1
  91. package/dist/signing/payloadHash.d.ts +1 -1
  92. package/dist/signing/payloadHash.js +1 -1
  93. package/dist/signing/types.d.cts +1 -1
  94. package/dist/signing/types.d.ts +1 -1
  95. package/dist/types/MobileServerFailureError.d.cts +1 -1
  96. package/dist/types/MobileServerFailureError.d.ts +1 -1
  97. package/dist/types/MobileUnsupportedNotificationRouteError.cjs +19 -0
  98. package/dist/types/MobileUnsupportedNotificationRouteError.cjs.map +1 -0
  99. package/dist/types/MobileUnsupportedNotificationRouteError.d.cts +2 -0
  100. package/dist/types/MobileUnsupportedNotificationRouteError.d.ts +2 -0
  101. package/dist/types/MobileUnsupportedNotificationRouteError.js +18 -0
  102. package/dist/types/MobileUnsupportedNotificationRouteError.js.map +1 -0
  103. package/dist/types/clientTypes.d.cts +2 -2
  104. package/dist/types/clientTypes.d.ts +2 -2
  105. package/dist/types/index.cjs +4 -0
  106. package/dist/types/index.d.cts +8 -9
  107. package/dist/types/index.d.ts +8 -9
  108. package/dist/types/index.js +3 -2
  109. package/dist/types/mobileErrorCodes.cjs +11 -1
  110. package/dist/types/mobileErrorCodes.cjs.map +1 -1
  111. package/dist/types/mobileErrorCodes.d.cts +1 -1
  112. package/dist/types/mobileErrorCodes.d.ts +1 -1
  113. package/dist/types/mobileErrorCodes.js +11 -1
  114. package/dist/types/mobileErrorCodes.js.map +1 -1
  115. package/dist/types/serverBaseTypes.d.cts +1 -1
  116. package/dist/types/serverBaseTypes.d.ts +1 -1
  117. package/dist/types/serverExecuteTypes.d.cts +2 -2
  118. package/dist/types/serverExecuteTypes.d.ts +2 -2
  119. package/dist/types/serverModelTypes.cjs +15 -0
  120. package/dist/types/serverModelTypes.cjs.map +1 -1
  121. package/dist/types/serverModelTypes.d.cts +2 -2
  122. package/dist/types/serverModelTypes.d.ts +2 -2
  123. package/dist/types/serverModelTypes.js +14 -1
  124. package/dist/types/serverModelTypes.js.map +1 -1
  125. package/dist/types/serverQueryTypes.d.cts +2 -2
  126. package/dist/types/serverQueryTypes.d.ts +2 -2
  127. package/dist/types/serverRequestTypes.d.cts +2 -2
  128. package/dist/types/serverRequestTypes.d.ts +2 -2
  129. package/dist/types-CByl_NyJ.d.ts +825 -0
  130. package/dist/types-CByl_NyJ.d.ts.map +1 -0
  131. package/dist/types-CR5iaKR4.d.cts +825 -0
  132. package/dist/types-CR5iaKR4.d.cts.map +1 -0
  133. package/dist/utils/buildMobileFeedImageMultipart.cjs +31 -0
  134. package/dist/utils/buildMobileFeedImageMultipart.cjs.map +1 -0
  135. package/dist/utils/buildMobileFeedImageMultipart.d.cts +15 -0
  136. package/dist/utils/buildMobileFeedImageMultipart.d.cts.map +1 -0
  137. package/dist/utils/buildMobileFeedImageMultipart.d.ts +15 -0
  138. package/dist/utils/buildMobileFeedImageMultipart.d.ts.map +1 -0
  139. package/dist/utils/buildMobileFeedImageMultipart.js +30 -0
  140. package/dist/utils/buildMobileFeedImageMultipart.js.map +1 -0
  141. package/dist/utils/feed.cjs +12 -0
  142. package/dist/utils/feed.cjs.map +1 -1
  143. package/dist/utils/feed.d.cts +2 -2
  144. package/dist/utils/feed.d.ts +2 -2
  145. package/dist/utils/feed.js +11 -1
  146. package/dist/utils/feed.js.map +1 -1
  147. package/dist/utils/index.cjs +3 -1
  148. package/dist/utils/index.d.cts +3 -3
  149. package/dist/utils/index.d.ts +3 -3
  150. package/dist/utils/index.js +2 -2
  151. package/dist/utils/serverResponseGuards.d.cts +1 -1
  152. package/dist/utils/serverResponseGuards.d.ts +1 -1
  153. package/package.json +3 -3
  154. package/src/MobileClient.ts +444 -19
  155. package/src/dataMappers.test.ts +511 -6
  156. package/src/dataMappers.ts +325 -24
  157. package/src/feedImages.test.ts +309 -0
  158. package/src/notifications.test.ts +950 -0
  159. package/src/signing/canonicalize.ts +72 -0
  160. package/src/signing/eip712MethodByType.ts +10 -0
  161. package/src/signing/signing.test.ts +67 -1
  162. package/src/signing/types.ts +55 -9
  163. package/src/socialFeed.test.ts +63 -2
  164. package/src/types/MobileUnsupportedNotificationRouteError.ts +16 -0
  165. package/src/types/clientTypes.ts +427 -36
  166. package/src/types/index.ts +1 -0
  167. package/src/types/mobileErrorCodes.ts +10 -0
  168. package/src/types/serverExecuteTypes.ts +22 -10
  169. package/src/types/serverModelTypes.ts +216 -11
  170. package/src/types/serverQueryTypes.ts +106 -9
  171. package/src/types/serverRequestTypes.ts +114 -17
  172. package/src/utils/buildMobileFeedImageMultipart.test.ts +45 -0
  173. package/src/utils/buildMobileFeedImageMultipart.ts +36 -0
  174. package/src/utils/feed.ts +16 -0
  175. package/dist/MobileClient-CfVZXTcz.cjs.map +0 -1
  176. package/dist/MobileServerFailureError-BZVwlsMg.d.ts.map +0 -1
  177. package/dist/MobileServerFailureError-CbtK8kD4.d.cts.map +0 -1
  178. package/dist/canonicalize-5eeZrkzQ.d.ts.map +0 -1
  179. package/dist/canonicalize-CBaZprTo.d.cts.map +0 -1
  180. package/dist/clientTypes-C9dJufxk.d.ts.map +0 -1
  181. package/dist/clientTypes-_mEgzmNs.d.cts.map +0 -1
  182. package/dist/eip712MethodByType-D8jLoxDu.d.cts.map +0 -1
  183. package/dist/eip712MethodByType-DcUh8h-l.d.ts.map +0 -1
  184. package/dist/feed-DTJlsg8W.d.cts +0 -14
  185. package/dist/feed-DTJlsg8W.d.cts.map +0 -1
  186. package/dist/feed-DTJlsg8W.d.ts +0 -14
  187. package/dist/feed-DTJlsg8W.d.ts.map +0 -1
  188. package/dist/mobileErrorCodes-CmjYEIYm.d.cts.map +0 -1
  189. package/dist/mobileErrorCodes-CmjYEIYm.d.ts.map +0 -1
  190. package/dist/payloadHash-DhHmgRqL.d.cts.map +0 -1
  191. package/dist/payloadHash-E1_8fpLW.d.ts.map +0 -1
  192. package/dist/serverBaseTypes-B5J1_TyC.d.cts +0 -25
  193. package/dist/serverBaseTypes-B5J1_TyC.d.cts.map +0 -1
  194. package/dist/serverBaseTypes-DnLzsdm-.d.ts +0 -25
  195. package/dist/serverBaseTypes-DnLzsdm-.d.ts.map +0 -1
  196. package/dist/serverExecuteTypes-B-OJok19.d.cts.map +0 -1
  197. package/dist/serverExecuteTypes-BwWGspOg.d.ts.map +0 -1
  198. package/dist/serverModelTypes-CiA-dy1R.d.cts.map +0 -1
  199. package/dist/serverModelTypes-CiA-dy1R.d.ts.map +0 -1
  200. package/dist/serverQueryTypes-CJcjLB7n.d.ts +0 -283
  201. package/dist/serverQueryTypes-CJcjLB7n.d.ts.map +0 -1
  202. package/dist/serverQueryTypes-V1f9yE7L.d.cts +0 -283
  203. package/dist/serverQueryTypes-V1f9yE7L.d.cts.map +0 -1
  204. package/dist/serverRequestTypes-CRlkNSu5.d.cts +0 -270
  205. package/dist/serverRequestTypes-CRlkNSu5.d.cts.map +0 -1
  206. package/dist/serverRequestTypes-DZoOhV2o.d.ts +0 -270
  207. package/dist/serverRequestTypes-DZoOhV2o.d.ts.map +0 -1
  208. package/dist/types-BiRmH5Iv.d.cts +0 -108
  209. package/dist/types-BiRmH5Iv.d.cts.map +0 -1
  210. package/dist/types-DoJuq0Ij.d.ts +0 -108
  211. package/dist/types-DoJuq0Ij.d.ts.map +0 -1
@@ -5,6 +5,7 @@ import {
5
5
  WalletNotProvidedError,
6
6
  } from '@nadohq/shared';
7
7
  import axios, { AxiosInstance, AxiosResponse } from 'axios';
8
+ import { bytesToString, keccak256, zeroHash } from 'viem';
8
9
  import {
9
10
  mapMobileAvatarPreset,
10
11
  mapMobileAvatarSelectionToServer,
@@ -14,8 +15,16 @@ import {
14
15
  mapMobileFollowListPage,
15
16
  mapMobileFollowListParamsToServer,
16
17
  mapMobileFollowMutationResult,
18
+ mapMobileNotificationCapabilitiesToServer,
19
+ mapMobileNotificationInboxV2,
17
20
  mapMobileNotificationPreferences,
18
21
  mapMobileNotificationPreferencesToServer,
22
+ mapMobileNotificationRegistrationV2,
23
+ mapMobileNotificationSettingsToServer,
24
+ mapMobileNotificationSettingsV2,
25
+ mapMobileNotificationSubscriptions,
26
+ mapMobileNotificationSubscriptionsUpdateToServer,
27
+ mapMobileProductEventToServer,
19
28
  mapMobileProfileSearchPage,
20
29
  mapMobileProfileSearchParamsToServer,
21
30
  mapMobileProfilesIncludeToServer,
@@ -36,16 +45,18 @@ import {
36
45
  mapMobileSocialFeedPositionHistory,
37
46
  mapMobileSocialFeedPositionResolutionResult,
38
47
  mapMobileSocialFeedPositionsPage,
48
+ mapMobileSocialFeedPositionThoughts,
39
49
  mapMobileSocialFeedPresence,
40
50
  mapMobileSocialFeedRankedPage,
41
51
  mapMobileSocialFeedReadParamsToServer,
42
52
  mapMobileSocialFeedTopTradersPage,
43
53
  mapMobileSocialFeedUpdates,
44
- mapMobileSocialFeedViewsResult,
54
+ mapMobileSocialFeedUploadedImage,
45
55
  mapMobileSocialFeedWatchers,
46
56
  } from './dataMappers';
47
57
  import {
48
58
  buildSignedMobileRequest,
59
+ getMobileNonce,
49
60
  MobileSignedInner,
50
61
  MobileSignedInnerByType,
51
62
  MobileSignedInnerParams,
@@ -54,6 +65,8 @@ import {
54
65
  import {
55
66
  GetMobileFeedChartDrawingParams,
56
67
  GetMobileFeedCommentsParams,
68
+ GetMobileFeedImageBytesParams,
69
+ GetMobileFeedImageRequestParams,
57
70
  GetMobileFeedItemParams,
58
71
  GetMobileFeedParams,
59
72
  GetMobileFeedUpdatesParams,
@@ -62,10 +75,16 @@ import {
62
75
  GetMobileForYouFeedParams,
63
76
  GetMobileFriendsFeedParams,
64
77
  GetMobileMarketFeedParams,
78
+ GetMobileNotificationInboxV2Params,
65
79
  GetMobileNotificationPreferencesParams,
80
+ GetMobileNotificationRegistrationV2Params,
81
+ GetMobileNotificationSettingsV2Params,
82
+ GetMobileNotificationSubscriptionsParams,
66
83
  GetMobilePositionChartParams,
67
84
  GetMobilePositionFeedParams,
85
+ GetMobilePositionHistoryAllParams,
68
86
  GetMobilePositionHistoryParams,
87
+ GetMobilePositionThoughtsParams,
69
88
  GetMobilePositionWatchersParams,
70
89
  GetMobileProfileFeedParams,
71
90
  GetMobileProfilesParams,
@@ -77,19 +96,27 @@ import {
77
96
  MobileAvatarUpload,
78
97
  MobileCreateAvatarUploadParams,
79
98
  MobileCreateFeedCommentParams,
99
+ MobileCreateFeedImageUploadParams,
80
100
  MobileCreateFeedPostParams,
81
101
  MobileFeedChartDrawingParams,
102
+ MobileFeedImageRequest,
82
103
  MobileFeedPage,
83
104
  MobileFollowBatchResultItem,
84
105
  MobileFollowListPage,
85
106
  MobileFollowMutationResult,
107
+ MobileNotificationInboxV2,
86
108
  MobileNotificationPreferences,
109
+ MobileNotificationRegistrationV2,
110
+ MobileNotificationSettingsV2,
111
+ MobileNotificationSubscriptions,
87
112
  MobilePositionPresenceParams,
88
113
  MobileProfileSearchPage,
89
114
  MobilePublicProfile,
90
115
  MobileRecordFeedViewsParams,
116
+ MobileRecordProductEventsParams,
91
117
  MobileRegisteredWallet,
92
118
  MobileRegisterExpoTokenParams,
119
+ MobileRegisterExpoTokenV2Params,
93
120
  MobileSetAvatarParams,
94
121
  MobileSetBioParams,
95
122
  MobileSetFeedLikeParams,
@@ -111,14 +138,17 @@ import {
111
138
  MobileSocialFeedPositionHistory,
112
139
  MobileSocialFeedPositionResolutionResult,
113
140
  MobileSocialFeedPositionsPage,
141
+ MobileSocialFeedPositionThoughtsPage,
114
142
  MobileSocialFeedPresence,
115
143
  MobileSocialFeedRankedPage,
116
144
  MobileSocialFeedTopTradersPage,
117
145
  MobileSocialFeedUpdates,
118
- MobileSocialFeedViewsResult,
146
+ MobileSocialFeedUploadedImage,
119
147
  MobileSocialFeedWatchers,
120
148
  MobileUnregisterExpoTokenParams,
121
149
  MobileUpdateNotificationPreferencesParams,
150
+ MobileUpdateNotificationSettingsV2Params,
151
+ MobileUpdateNotificationSubscriptionsParams,
122
152
  MobileUsernameAvailability,
123
153
  ResolveMobileFeedActionParams,
124
154
  ResolveMobileFeedPositionParams,
@@ -135,6 +165,7 @@ import {
135
165
  import {
136
166
  MobileServerPublicQueryRequest,
137
167
  MobileServerPublicQuerySuccessResponse,
168
+ MobileServerSignedQuerySuccessResponse,
138
169
  } from './types/serverQueryTypes';
139
170
  import {
140
171
  MobileServerExecuteRequestType,
@@ -142,7 +173,10 @@ import {
142
173
  MobileServerPublicExecuteRequestType,
143
174
  MobileServerPublicQueryRequestByType,
144
175
  MobileServerPublicQueryRequestType,
176
+ MobileServerSignedQueryRequestType,
145
177
  } from './types/serverRequestTypes';
178
+ import { buildMobileFeedImageMultipart } from './utils/buildMobileFeedImageMultipart';
179
+ import { MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES } from './utils/feed';
146
180
  import {
147
181
  isMobileServerFailureResponse,
148
182
  isMobileServerSuccessResponse,
@@ -480,6 +514,7 @@ export class MobileClient {
480
514
  /**
481
515
  * Reads one item visible to viewAs, including that account's own hidden content.
482
516
  * Creates an item session directly when sessionId is omitted.
517
+ * Reading does not increment views; use {@link recordFeedViews} independently when reporting views.
483
518
  * @param params - Item UUID, preview limit, and optional existing session/read identity.
484
519
  * @returns The complete item and authorized reading session.
485
520
  * @throws {MobileServerFailureError} On inaccessible content or session mismatch/expiry.
@@ -534,6 +569,7 @@ export class MobileClient {
534
569
  *
535
570
  * @throws {MobileServerFailureError} With error code `INVALID_EXPO_TOKEN` if the token is malformed, or is
536
571
  * not currently registered to a wallet.
572
+ * @deprecated Use {@link getNotificationSettingsV2} instead.
537
573
  */
538
574
  async getNotificationPreferences(
539
575
  params: GetMobileNotificationPreferencesParams,
@@ -650,11 +686,55 @@ export class MobileClient {
650
686
  }
651
687
 
652
688
  /**
653
- * Reads a comment thread with an optional anchor and bidirectional pagination.
689
+ * Resolves a backend feed image path against this client's mobile gateway URL.
690
+ * @param path - Backend path from image metadata, rooted at /v1/mobile or the mobile service root.
691
+ * @returns An absolute URL suitable for authenticated fetches.
692
+ */
693
+ resolveFeedImageUrl(path: string): string {
694
+ return `${this.opts.url}${path.replace(/^\/v1\/mobile(?=\/)/, '')}`;
695
+ }
696
+
697
+ /**
698
+ * Prepares a protected image request for native image components without downloading or encoding bytes.
699
+ * Does not increment view counts.
700
+ * @param params - Image path from metadata plus the original session credentials.
701
+ * @returns Absolute URL and headers; pass them as the image source's URI and headers.
702
+ */
703
+ getFeedImageRequest(
704
+ params: GetMobileFeedImageRequestParams,
705
+ ): MobileFeedImageRequest {
706
+ return {
707
+ url: this.resolveFeedImageUrl(params.path),
708
+ headers: {
709
+ 'x-feed-session-id': params.sessionId,
710
+ 'x-feed-viewer-token': params.viewerToken,
711
+ },
712
+ };
713
+ }
714
+
715
+ /**
716
+ * Fetches protected feed image bytes using the reading session that fully delivered the parent item.
717
+ * Does not increment view counts.
718
+ * @param params - Image path from metadata plus the original session credentials.
719
+ * @returns Raw image bytes.
720
+ * @throws {MobileServerFailureError} On session, permission, or availability errors.
721
+ */
722
+ async getFeedImageBytes(
723
+ params: GetMobileFeedImageBytesParams,
724
+ ): Promise<Uint8Array> {
725
+ const { url, headers } = this.getFeedImageRequest(params);
726
+ return this.publicGetBytes(url, headers);
727
+ }
728
+
729
+ /**
730
+ * Reads top-level comment groups with all readable descendants and bidirectional pagination.
731
+ * The limit counts groups, not descendants. Groups are newest first; their flat replies are oldest first,
732
+ * with depth then UUID breaking timestamp ties. An anchor at any depth selects its top-level group.
733
+ * Preview cursors open the first complete group page in the same snapshot; do not prepend preview items.
654
734
  * Independent reads create a public-only session; reused sessions retain their original scope.
655
- * @param params - Root item, optional anchor or cursor (mutually exclusive), page size, and read perspective.
735
+ * @param params - Root item, optional anchor or cursor (mutually exclusive), group limit, and read perspective.
656
736
  * Paging requires the original session and viewer token.
657
- * @returns Replies, total count, forward and backward cursors, the anchor, and the actual reading session.
737
+ * @returns Complete comment groups, whole-thread reply and top-level counts, cursors, anchor, and session.
658
738
  * @throws {MobileServerFailureError} On invalid parameters, session mismatch or expiry, inaccessible content,
659
739
  * or unavailable data. Restart pagination with a new session after expiry; do not reuse the old cursor.
660
740
  */
@@ -680,7 +760,8 @@ export class MobileClient {
680
760
  * the original position session and viewer token, not a feed session.
681
761
  * @returns Position metrics, ordered action/take history, summary, trading hints, cursor, and reading session.
682
762
  * @throws {MobileServerFailureError} On invalid parameters, session mismatch or expiry, inaccessible content,
683
- * or unavailable source data. Restart with a new position session after expiry.
763
+ * unavailable source data, or SOCIAL_FEED_HISTORY_TOO_LARGE when the initial directory exceeds capacity.
764
+ * Restart with a new position session after expiry.
684
765
  * @throws {MobileUnsupportedFeedItemError} If a timeline entry or take has an unsupported kind.
685
766
  */
686
767
  async getPositionHistory(
@@ -698,6 +779,53 @@ export class MobileClient {
698
779
  return mapMobileSocialFeedPositionHistory(await this.publicQuery(body));
699
780
  }
700
781
 
782
+ /**
783
+ * Reads the most recent 500 lifecycle actions, without authored text, drawings, or images.
784
+ * Creates a new position session on every call. A null nextCursor means no pagination, not complete
785
+ * history; use getPositionHistory with a fresh session for the full lifecycle. takeCount is always zero.
786
+ * @param params - Position and read perspective; an existing viewer token may be reused.
787
+ * @returns Position metrics, owner, recent actions, response-scope counts, trading hints, and a new session.
788
+ * @throws {MobileServerFailureError} On invalid parameters, inaccessible content, unavailable data,
789
+ * or SOCIAL_FEED_HISTORY_TOO_LARGE when the initial directory exceeds capacity.
790
+ * @throws {MobileUnsupportedFeedItemError} If a timeline entry or take has an unsupported kind.
791
+ */
792
+ async getPositionHistoryAll(
793
+ params: GetMobilePositionHistoryAllParams,
794
+ ): Promise<MobileSocialFeedPositionHistory> {
795
+ const body: MobileServerPublicQueryRequest<'position_history_all'> = {
796
+ type: 'position_history_all',
797
+ position_id: params.positionId,
798
+ ...mapMobileSocialFeedReadParamsToServer(params),
799
+ };
800
+ return mapMobileSocialFeedPositionHistory(await this.publicQuery(body));
801
+ }
802
+
803
+ /**
804
+ * Reads published position thoughts with both nonblank text and a saved drawing, without signing.
805
+ * Keeps descending drawing creation time / item UUID order; editing a drawing does not move the item.
806
+ * Continue until nextCursor is null, even after an empty page. There is no 500-item total cap.
807
+ * Keep positionId, limit, sessionId, and viewerToken unchanged when paging; omitted viewAs inherits
808
+ * the session. History/feed sessions cannot be substituted. Refresh to include newly eligible content.
809
+ * @param params - Position, page size, read perspective, and original thoughts-session credentials for paging.
810
+ * @returns Complete thoughts and drawings in server order, a continuation cursor, and the reading session.
811
+ * @throws {MobileServerFailureError} On invalid parameters, session mismatch/expiry, inaccessible content,
812
+ * unavailable source data, or SOCIAL_FEED_HISTORY_TOO_LARGE when the initial directory exceeds capacity.
813
+ * @throws {MobileUnsupportedFeedItemError} If an item is not a post or trade card.
814
+ */
815
+ async getPositionThoughts(
816
+ params: GetMobilePositionThoughtsParams,
817
+ ): Promise<MobileSocialFeedPositionThoughtsPage> {
818
+ const body: MobileServerPublicQueryRequest<'position_thoughts'> = {
819
+ type: 'position_thoughts',
820
+ position_id: params.positionId,
821
+ limit: params.limit,
822
+ session_id: params.sessionId,
823
+ cursor: params.cursor,
824
+ ...mapMobileSocialFeedReadParamsToServer(params),
825
+ };
826
+ return mapMobileSocialFeedPositionThoughts(await this.publicQuery(body));
827
+ }
828
+
701
829
  /**
702
830
  * Reads candles and action markers without creating a reading session.
703
831
  * Preserve the original range, interval, and read perspective when paging with nextBeforeMs.
@@ -743,6 +871,85 @@ export class MobileClient {
743
871
  return mapMobileSocialFeedWatchers(await this.publicQuery(body));
744
872
  }
745
873
 
874
+ /**
875
+ * Reads the token's latest registration generation and saved capabilities, including legacy v1 registrations.
876
+ * @param params - Active Expo token credential; no wallet signature is needed.
877
+ * @returns Saved generation as an opaque string and the complete capability declaration.
878
+ * @throws {MobileServerFailureError} If the token is invalid or inactive (`INVALID_EXPO_TOKEN`).
879
+ */
880
+ async getNotificationRegistrationV2(
881
+ params: GetMobileNotificationRegistrationV2Params,
882
+ ): Promise<MobileNotificationRegistrationV2> {
883
+ return mapMobileNotificationRegistrationV2(
884
+ await this.publicQuery({
885
+ type: 'notification_registration_v2',
886
+ expo_token: params.expoToken,
887
+ }),
888
+ );
889
+ }
890
+
891
+ /**
892
+ * Reads the wallet's full v2 settings document, including legacy preference mappings.
893
+ * @param params - Active Expo token credential. Before displaying settings, compare the wallet returned by
894
+ * {@link MobileClient.getRegisteredWallet} with the connected wallet (case-insensitively).
895
+ * @returns CamelCase settings with the current settings revision.
896
+ * @throws {MobileServerFailureError} If the token is invalid or inactive (`INVALID_EXPO_TOKEN`).
897
+ */
898
+ async getNotificationSettingsV2(
899
+ params: GetMobileNotificationSettingsV2Params,
900
+ ): Promise<MobileNotificationSettingsV2> {
901
+ return mapMobileNotificationSettingsV2(
902
+ await this.publicQuery({
903
+ type: 'notification_settings_v2',
904
+ expo_token: params.expoToken,
905
+ }),
906
+ );
907
+ }
908
+
909
+ /*
910
+ Signed queries
911
+ */
912
+
913
+ /**
914
+ * Reads wallet market subscriptions and the signing Identity's enabled trader bells.
915
+ * @param params - Wallet authentication context; no Expo token is needed.
916
+ * @returns Subscriptions, import state, and the independent subscription revision.
917
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
918
+ * @throws {MobileServerFailureError} If authentication or the query fails.
919
+ */
920
+ async getNotificationSubscriptions(
921
+ params: GetMobileNotificationSubscriptionsParams,
922
+ ): Promise<MobileNotificationSubscriptions> {
923
+ const request = await this.getSignedRequest(
924
+ 'notification_subscriptions',
925
+ params,
926
+ {},
927
+ );
928
+ return mapMobileNotificationSubscriptions(await this.query(request));
929
+ }
930
+
931
+ /**
932
+ * Reads private accepted notification intents, not delivery receipts or read states.
933
+ * @param params - Wallet authentication and optional cursor/page size (1-50, default 20).
934
+ * @returns A camelCase page; discard cached pages and cursors when switching wallets.
935
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
936
+ * @throws {MobileServerFailureError} If authentication or pagination validation fails.
937
+ * @throws {MobileUnsupportedNotificationRouteError} If a notification has an unsupported route name.
938
+ */
939
+ async getNotificationInboxV2(
940
+ params: GetMobileNotificationInboxV2Params,
941
+ ): Promise<MobileNotificationInboxV2> {
942
+ const request = await this.getSignedRequest(
943
+ 'notification_inbox_v2',
944
+ params,
945
+ {
946
+ cursor: params.cursor ?? null,
947
+ limit: params.limit ?? null,
948
+ },
949
+ );
950
+ return mapMobileNotificationInboxV2(await this.query(request));
951
+ }
952
+
746
953
  /*
747
954
  Public executes
748
955
  */
@@ -778,6 +985,7 @@ export class MobileClient {
778
985
  *
779
986
  * @throws {MobileServerFailureError} With error code `INVALID_PREFERENCES` if those rules are violated, or
780
987
  * `INVALID_EXPO_TOKEN` if the token is malformed or is not currently registered to a wallet.
988
+ * @deprecated Use {@link updateNotificationSettingsV2} instead.
781
989
  */
782
990
  async updateNotificationPreferences(
783
991
  params: MobileUpdateNotificationPreferencesParams,
@@ -791,24 +999,43 @@ export class MobileClient {
791
999
  }
792
1000
 
793
1001
  /**
794
- * Reports qualified exposures without signing. Ordinary reads and root previews do not count as exposures.
795
- * The caller must satisfy the session's viewTracking requirements before reporting an item.
796
- * @param params - Original reading session and viewer token, plus one to fifty fully delivered, readable,
797
- * interactive item IDs. The service deduplicates repeated exposures.
798
- * @returns Separate lists of newly accepted and already-counted item IDs.
799
- * @throws {MobileServerFailureError} On invalid parameters, session mismatch or expiry, or any ineligible item.
800
- * A rejected batch is not partially counted. Acquire a new session after expiry and re-evaluate exposure eligibility.
1002
+ * Replaces all v2 settings fields using optimistic concurrency; legacy overlapping preferences are synced.
1003
+ * @param params - Active Expo token, latest settings revision, and complete replacement settings.
1004
+ * @returns The saved settings document with its incremented revision.
1005
+ * @throws {MobileServerFailureError} On invalid token/settings or revision conflict (`NOTIFICATION_REVISION_CONFLICT`). Re-read and
1006
+ * merge only the user's current change before retrying; never blindly replay a stale document.
1007
+ */
1008
+ async updateNotificationSettingsV2(
1009
+ params: MobileUpdateNotificationSettingsV2Params,
1010
+ ): Promise<MobileNotificationSettingsV2> {
1011
+ return mapMobileNotificationSettingsV2(
1012
+ await this.publicExecute({
1013
+ type: 'update_notification_settings_v2',
1014
+ expo_token: params.expoToken,
1015
+ expected_revision: params.expectedRevision,
1016
+ settings: mapMobileNotificationSettingsToServer(params.settings),
1017
+ }),
1018
+ );
1019
+ }
1020
+
1021
+ /**
1022
+ * Reports views for posts, trade cards, comments, and replies without signing or a reading session.
1023
+ * Duplicate IDs count once within a request; another request increments them again. Only supplied IDs
1024
+ * count, not their parents. Missing, deleted, unpublished, excluded, or unsupported targets are skipped.
1025
+ * A successful response does not identify skipped items. Read interaction.viewCount for current counts.
1026
+ * This operation is not idempotent: retrying an uncertain result may count twice. No automatic retry is made.
1027
+ * @param params - itemIds: 1–50 canonical lowercase item UUIDs, including any same-request duplicates.
1028
+ * @returns Only the success status, even when every target was skipped.
1029
+ * @throws {MobileServerFailureError} On invalid parameters, disabled writes, rate limiting, or unavailable data.
801
1030
  */
802
1031
  async recordFeedViews(
803
1032
  params: MobileRecordFeedViewsParams,
804
- ): Promise<MobileSocialFeedViewsResult> {
1033
+ ): Promise<MobileServerSuccessResponse> {
805
1034
  const body: MobileServerPublicExecuteRequest<'record_feed_views'> = {
806
1035
  type: 'record_feed_views',
807
- session_id: params.sessionId,
808
1036
  item_ids: params.itemIds,
809
- viewer_token: params.viewerToken,
810
1037
  };
811
- return mapMobileSocialFeedViewsResult(await this.publicExecute(body));
1038
+ return this.publicExecute(body);
812
1039
  }
813
1040
 
814
1041
  /**
@@ -942,11 +1169,13 @@ export class MobileClient {
942
1169
 
943
1170
  /**
944
1171
  * Registers an Expo push token for the wallet, transferring active ownership of the token if another
945
- * wallet had registered it. Idempotent per `(wallet, token)`.
1172
+ * wallet had registered it. Each registration advances its generation and cancels old pending deliveries.
1173
+ * This legacy method replaces capabilities with v1; use registerExpoTokenV2 consistently after upgrading.
946
1174
  *
947
1175
  * @throws {MobileServerFailureError} With error code `INVALID_EXPO_TOKEN` if the token is not a valid Expo
948
1176
  * push token, or `INVALID_DEVICE_METADATA` if `locale` (max 35 chars) or `appVersion` (max 64 chars) is
949
1177
  * too long.
1178
+ * @deprecated Use {@link registerExpoTokenV2} instead.
950
1179
  */
951
1180
  async registerExpoToken(
952
1181
  params: MobileRegisterExpoTokenParams,
@@ -964,6 +1193,70 @@ export class MobileClient {
964
1193
  return this.execute(signedRequest);
965
1194
  }
966
1195
 
1196
+ /**
1197
+ * Registers a device with its actual v2 capabilities; do not use repeated registration as a heartbeat.
1198
+ * @param params - Wallet authentication, Expo token, metadata, and supported notification types/routes.
1199
+ * @returns Success only; query registration separately for its new generation.
1200
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1201
+ * @throws {MobileServerFailureError} On `INVALID_EXPO_TOKEN`, `INVALID_DEVICE_METADATA`,
1202
+ * `NOTIFICATION_DEVICE_LIMIT` when the wallet already has 32 active devices, or authentication errors.
1203
+ */
1204
+ async registerExpoTokenV2(
1205
+ params: MobileRegisterExpoTokenV2Params,
1206
+ ): Promise<MobileServerSuccessResponse> {
1207
+ const request = await this.getSignedRequest(
1208
+ 'register_expo_token_v2',
1209
+ params,
1210
+ {
1211
+ expo_token: params.expoToken,
1212
+ platform: params.platform,
1213
+ locale: params.locale ?? null,
1214
+ app_version: params.appVersion ?? null,
1215
+ capabilities: mapMobileNotificationCapabilitiesToServer(
1216
+ params.capabilities,
1217
+ ),
1218
+ },
1219
+ );
1220
+ return this.execute(request);
1221
+ }
1222
+
1223
+ /**
1224
+ * Applies exactly one bell, market, or one-time favorites-import operation.
1225
+ * @param params - Wallet authentication and subscription update with the latest subscription revision.
1226
+ * @returns Success only; re-query subscriptions for the new revision even after a repeated ON/OFF.
1227
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1228
+ * @throws {MobileServerFailureError} On `NOTIFICATION_REVISION_CONFLICT`, `INVALID_NOTIFICATION_REQUEST`, or
1229
+ * `INVALID_IDENTITY_TARGET`.
1230
+ */
1231
+ async updateNotificationSubscriptions(
1232
+ params: MobileUpdateNotificationSubscriptionsParams,
1233
+ ): Promise<MobileServerSuccessResponse> {
1234
+ const request = await this.getSignedRequest(
1235
+ 'update_notification_subscriptions',
1236
+ params,
1237
+ mapMobileNotificationSubscriptionsUpdateToServer(params.update),
1238
+ );
1239
+ return this.execute(request);
1240
+ }
1241
+
1242
+ /**
1243
+ * Atomically records 1-50 analytics events after the user has consented to analytics.
1244
+ * @param params - Wallet authentication and events with stable UUIDs and actual millisecond timestamps.
1245
+ * @returns Success only; reuse event IDs on retries. Notification clicks do not mark an inbox item as read.
1246
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1247
+ * @throws {MobileServerFailureError} If any event, timestamp, batch size, or notification ownership is invalid.
1248
+ */
1249
+ async recordProductEvents(
1250
+ params: MobileRecordProductEventsParams,
1251
+ ): Promise<MobileServerSuccessResponse> {
1252
+ const request = await this.getSignedRequest(
1253
+ 'record_product_events',
1254
+ params,
1255
+ { events: params.events.map(mapMobileProductEventToServer) },
1256
+ );
1257
+ return this.execute(request);
1258
+ }
1259
+
967
1260
  /**
968
1261
  * Sets whether the signing subaccount follows a Profile. Both directions are idempotent: following again
969
1262
  * keeps the original relationship creation time, so it does not move the account in recency order, and
@@ -1044,10 +1337,50 @@ export class MobileClient {
1044
1337
  text: params.text,
1045
1338
  mentions: params.mentions.map(mapMobileSocialFeedMentionToServer),
1046
1339
  drawing_data_json: params.drawingDataJson,
1340
+ image_ids: params.imageIds,
1341
+ image_urls: params.imageUrls,
1047
1342
  });
1048
1343
  return mapMobileSocialFeedItem((await this.execute(request)).item);
1049
1344
  }
1050
1345
 
1346
+ /**
1347
+ * Uploads one static JPEG/PNG for a later {@link createFeedPost} via multipart execute.
1348
+ * The whole HTTP request must stay within 2 MiB including signature overhead; upload one file per call
1349
+ * and bind up to nine ready IDs at publication. There is no complete/PUT step — success means ready.
1350
+ * @param params - Author subaccount and signing options, plus the original file bytes.
1351
+ * @returns Ready image metadata, including original and thumbnail paths.
1352
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1353
+ * @throws {RangeError} If the complete multipart request exceeds {@link MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES}.
1354
+ * @throws {MobileServerFailureError} On authentication, format/size/hash errors (`INVALID_SOCIAL_FEED_IMAGE`),
1355
+ * or when publishing is disabled (`SERVICE_UNAVAILABLE`), or image storage is unavailable
1356
+ * (`SOCIAL_FEED_DATA_UNAVAILABLE`, 6320, HTTP 503).
1357
+ */
1358
+ async createFeedPostImageUpload(
1359
+ params: MobileCreateFeedImageUploadParams,
1360
+ ): Promise<MobileSocialFeedUploadedImage> {
1361
+ return this.createFeedImageUpload('create_feed_post_image_uploads', params);
1362
+ }
1363
+
1364
+ /**
1365
+ * Uploads one static JPEG/PNG for a later {@link createFeedComment} via multipart execute.
1366
+ * Same size and readiness rules as {@link createFeedPostImageUpload}; comment text remains required.
1367
+ * @param params - Author subaccount and signing options, plus the original file bytes.
1368
+ * @returns Ready image metadata, including original and thumbnail paths.
1369
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1370
+ * @throws {RangeError} If the complete multipart request exceeds {@link MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES}.
1371
+ * @throws {MobileServerFailureError} On authentication, format/size/hash errors (`INVALID_SOCIAL_FEED_IMAGE`),
1372
+ * or when comments are disabled (`SERVICE_UNAVAILABLE`), or image storage is unavailable
1373
+ * (`SOCIAL_FEED_DATA_UNAVAILABLE`, 6320, HTTP 503).
1374
+ */
1375
+ async createFeedCommentImageUpload(
1376
+ params: MobileCreateFeedImageUploadParams,
1377
+ ): Promise<MobileSocialFeedUploadedImage> {
1378
+ return this.createFeedImageUpload(
1379
+ 'create_feed_comment_image_uploads',
1380
+ params,
1381
+ );
1382
+ }
1383
+
1051
1384
  /**
1052
1385
  * First attaches a drawing to an owned post or trade card while the author still holds its position.
1053
1386
  * The original JSON is signed unchanged; false and zero are valid drawing documents.
@@ -1119,6 +1452,8 @@ export class MobileClient {
1119
1452
  text: params.text,
1120
1453
  mentions: params.mentions.map(mapMobileSocialFeedMentionToServer),
1121
1454
  attached_position_id: params.attachedPositionId,
1455
+ image_ids: params.imageIds,
1456
+ image_urls: params.imageUrls,
1122
1457
  });
1123
1458
  return mapMobileSocialFeedCommentResult(await this.execute(request));
1124
1459
  }
@@ -1188,6 +1523,27 @@ export class MobileClient {
1188
1523
  return response.data as MobileServerPublicQuerySuccessResponse<T>;
1189
1524
  }
1190
1525
 
1526
+ protected async publicGetBytes(
1527
+ url: string,
1528
+ headers: Record<string, string>,
1529
+ ): Promise<Uint8Array> {
1530
+ const response = await this.axiosInstance.get<ArrayBuffer>(url, {
1531
+ responseType: 'arraybuffer',
1532
+ headers,
1533
+ });
1534
+ if (response.status < 200 || response.status >= 300) {
1535
+ const responseText = bytesToString(new Uint8Array(response.data));
1536
+ let errorData: unknown;
1537
+ try {
1538
+ errorData = JSON.parse(responseText);
1539
+ } catch {
1540
+ errorData = responseText;
1541
+ }
1542
+ this.checkResponseStatus({ ...response, data: errorData });
1543
+ }
1544
+ return new Uint8Array(response.data);
1545
+ }
1546
+
1191
1547
  protected async publicExecute<T extends MobileServerPublicExecuteRequestType>(
1192
1548
  body: { type: T } & MobileServerPublicExecuteRequestByType[T],
1193
1549
  ): Promise<MobileServerPublicExecuteSuccessResponse<T>> {
@@ -1203,14 +1559,43 @@ export class MobileClient {
1203
1559
  return response.data as MobileServerPublicExecuteSuccessResponse<T>;
1204
1560
  }
1205
1561
 
1562
+ /**
1563
+ * Sends an authenticated read through the signed query route.
1564
+ * @param body - Canonical inner payload with its signature, sender, and nonce.
1565
+ * @returns The successful query envelope and its type-specific payload.
1566
+ * @throws {MobileServerFailureError} For a structured service failure, including non-2xx responses.
1567
+ */
1568
+ protected async query<T extends MobileServerSignedQueryRequestType>(
1569
+ body: MobileSignedRequest & { type: T },
1570
+ ): Promise<MobileServerSignedQuerySuccessResponse<T>> {
1571
+ const response = await this.axiosInstance.post<unknown>(
1572
+ `${this.opts.url}/query`,
1573
+ body,
1574
+ );
1575
+ this.checkResponseStatus(response);
1576
+ this.checkServerStatus(response);
1577
+ return response.data as MobileServerSignedQuerySuccessResponse<T>;
1578
+ }
1579
+
1206
1580
  // `T` is inferred from the body's `type` rather than declared by the caller, for the same reason
1207
1581
  // `publicQuery` spells its body out as an intersection: an indexed access is not an inference site.
1208
1582
  protected async execute<T extends MobileServerExecuteRequestType>(
1209
1583
  body: MobileSignedRequest & { type: T },
1584
+ fileBytes?: Uint8Array,
1210
1585
  ): Promise<MobileServerExecuteSuccessResponse<T>> {
1586
+ const multipart =
1587
+ fileBytes == null
1588
+ ? undefined
1589
+ : buildMobileFeedImageMultipart(JSON.stringify(body), fileBytes);
1590
+ if (multipart) {
1591
+ this.checkFeedImageRequestSize(multipart.body.byteLength);
1592
+ }
1211
1593
  const response = await this.axiosInstance.post<MobileServerExecuteResult>(
1212
1594
  `${this.opts.url}/execute`,
1213
- body,
1595
+ multipart?.body ?? body,
1596
+ multipart
1597
+ ? { headers: { 'Content-Type': multipart.contentType } }
1598
+ : undefined,
1214
1599
  );
1215
1600
 
1216
1601
  this.checkResponseStatus(response);
@@ -1220,6 +1605,46 @@ export class MobileClient {
1220
1605
  return response.data as MobileServerExecuteSuccessResponse<T>;
1221
1606
  }
1222
1607
 
1608
+ private async createFeedImageUpload(
1609
+ type:
1610
+ | 'create_feed_post_image_uploads'
1611
+ | 'create_feed_comment_image_uploads',
1612
+ params: MobileCreateFeedImageUploadParams,
1613
+ ): Promise<MobileSocialFeedUploadedImage> {
1614
+ const nonce = params.nonce ?? getMobileNonce();
1615
+ // Match the signed body's field lengths so oversized requests fail before prompting the wallet.
1616
+ const sizePayload = {
1617
+ type,
1618
+ content_hash: zeroHash,
1619
+ signature: `0x${'00'.repeat(65)}`,
1620
+ sender: subaccountToHex(params),
1621
+ nonce: nonce.toString(),
1622
+ };
1623
+ const overhead = buildMobileFeedImageMultipart(
1624
+ JSON.stringify(sizePayload),
1625
+ new Uint8Array(),
1626
+ ).body.byteLength;
1627
+ this.checkFeedImageRequestSize(overhead + params.fileBytes.byteLength);
1628
+ const contentHash = keccak256(params.fileBytes);
1629
+ const request = await this.getSignedRequest(
1630
+ type,
1631
+ { ...params, nonce },
1632
+ {
1633
+ content_hash: contentHash,
1634
+ },
1635
+ );
1636
+ const data = await this.execute(request, params.fileBytes);
1637
+ return mapMobileSocialFeedUploadedImage(data.image);
1638
+ }
1639
+
1640
+ private checkFeedImageRequestSize(byteLength: number): void {
1641
+ if (byteLength > MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES) {
1642
+ throw new RangeError(
1643
+ `Feed image upload request exceeds ${MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES} bytes (2 MiB).`,
1644
+ );
1645
+ }
1646
+ }
1647
+
1223
1648
  /**
1224
1649
  * Validates the HTTP status before interpreting the body. Unlike the engine, the mobile backend returns
1225
1650
  * failure envelopes with non-2xx statuses (e.g. 404 for `PROFILE_NOT_FOUND`), so a non-2xx response with a