@nadohq/mobile-client 0.65.0 → 0.66.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 (207) hide show
  1. package/README.md +107 -8
  2. package/dist/{MobileClient-CfVZXTcz.cjs → MobileClient-M4oC4lrX.cjs} +258 -16
  3. package/dist/MobileClient-M4oC4lrX.cjs.map +1 -0
  4. package/dist/MobileClient.cjs +1 -1
  5. package/dist/MobileClient.d.cts +146 -16
  6. package/dist/MobileClient.d.cts.map +1 -1
  7. package/dist/MobileClient.d.ts +146 -16
  8. package/dist/MobileClient.d.ts.map +1 -1
  9. package/dist/MobileClient.js +258 -16
  10. package/dist/MobileClient.js.map +1 -1
  11. package/dist/{MobileServerFailureError-BZVwlsMg.d.ts → MobileServerFailureError-C9iqNsxI.d.ts} +2 -3
  12. package/dist/MobileServerFailureError-C9iqNsxI.d.ts.map +1 -0
  13. package/dist/{MobileServerFailureError-CbtK8kD4.d.cts → MobileServerFailureError-MtS2reel.d.cts} +2 -3
  14. package/dist/MobileServerFailureError-MtS2reel.d.cts.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-UeeWdYGU.d.ts → buildSignedMobileRequest-BR-KJhIF.d.ts} +2 -2
  20. package/dist/{buildSignedMobileRequest-UeeWdYGU.d.ts.map → buildSignedMobileRequest-BR-KJhIF.d.ts.map} +1 -1
  21. package/dist/{buildSignedMobileRequest-C1v0flCR.d.cts → buildSignedMobileRequest-DyKCK34Y.d.cts} +2 -2
  22. package/dist/{buildSignedMobileRequest-C1v0flCR.d.cts.map → buildSignedMobileRequest-DyKCK34Y.d.cts.map} +1 -1
  23. package/dist/{canonicalize-5eeZrkzQ.d.ts → canonicalize-Bv6UIAbR.d.ts} +2 -2
  24. package/dist/canonicalize-Bv6UIAbR.d.ts.map +1 -0
  25. package/dist/{canonicalize-CBaZprTo.d.cts → canonicalize-CWh3XZzj.d.cts} +2 -2
  26. package/dist/canonicalize-CWh3XZzj.d.cts.map +1 -0
  27. package/dist/{clientTypes-C9dJufxk.d.ts → clientTypes-BUn5_OzK.d.ts} +297 -20
  28. package/dist/clientTypes-BUn5_OzK.d.ts.map +1 -0
  29. package/dist/{clientTypes-_mEgzmNs.d.cts → clientTypes-DOpUcu80.d.cts} +297 -20
  30. package/dist/clientTypes-DOpUcu80.d.cts.map +1 -0
  31. package/dist/dataMappers.cjs +203 -5
  32. package/dist/dataMappers.cjs.map +1 -1
  33. package/dist/dataMappers.d.cts +70 -7
  34. package/dist/dataMappers.d.cts.map +1 -1
  35. package/dist/dataMappers.d.ts +70 -7
  36. package/dist/dataMappers.d.ts.map +1 -1
  37. package/dist/dataMappers.js +192 -6
  38. package/dist/dataMappers.js.map +1 -1
  39. package/dist/{eip712MethodByType-D8jLoxDu.d.cts → eip712MethodByType-CJ6BoayT.d.cts} +2 -2
  40. package/dist/eip712MethodByType-CJ6BoayT.d.cts.map +1 -0
  41. package/dist/{eip712MethodByType-DcUh8h-l.d.ts → eip712MethodByType-Dx4mvgni.d.ts} +2 -2
  42. package/dist/eip712MethodByType-Dx4mvgni.d.ts.map +1 -0
  43. package/dist/{feed-DTJlsg8W.d.ts → feed-DErOLaoY.d.cts} +4 -2
  44. package/dist/feed-DErOLaoY.d.cts.map +1 -0
  45. package/dist/{feed-DTJlsg8W.d.cts → feed-DErOLaoY.d.ts} +4 -2
  46. package/dist/feed-DErOLaoY.d.ts.map +1 -0
  47. package/dist/index.cjs +7 -2
  48. package/dist/index.d.cts +14 -16
  49. package/dist/index.d.ts +14 -16
  50. package/dist/index.js +4 -3
  51. package/dist/{mobileErrorCodes-CmjYEIYm.d.ts → mobileErrorCodes-vD6rbFuF.d.cts} +8 -3
  52. package/dist/mobileErrorCodes-vD6rbFuF.d.cts.map +1 -0
  53. package/dist/{mobileErrorCodes-CmjYEIYm.d.cts → mobileErrorCodes-vD6rbFuF.d.ts} +8 -3
  54. package/dist/mobileErrorCodes-vD6rbFuF.d.ts.map +1 -0
  55. package/dist/{payloadHash-DhHmgRqL.d.cts → payloadHash-CdkF2Ocr.d.cts} +2 -2
  56. package/dist/payloadHash-CdkF2Ocr.d.cts.map +1 -0
  57. package/dist/{payloadHash-E1_8fpLW.d.ts → payloadHash-HD29K77M.d.ts} +2 -2
  58. package/dist/payloadHash-HD29K77M.d.ts.map +1 -0
  59. package/dist/{serverExecuteTypes-B-OJok19.d.cts → serverExecuteTypes-5JyyIXRK.d.ts} +21 -5
  60. package/dist/serverExecuteTypes-5JyyIXRK.d.ts.map +1 -0
  61. package/dist/{serverExecuteTypes-BwWGspOg.d.ts → serverExecuteTypes-CHrQ9Rtq.d.cts} +21 -5
  62. package/dist/serverExecuteTypes-CHrQ9Rtq.d.cts.map +1 -0
  63. package/dist/{serverModelTypes-CiA-dy1R.d.ts → serverModelTypes-CGUZCi0y.d.cts} +163 -8
  64. package/dist/serverModelTypes-CGUZCi0y.d.cts.map +1 -0
  65. package/dist/{serverModelTypes-CiA-dy1R.d.cts → serverModelTypes-CGUZCi0y.d.ts} +163 -8
  66. package/dist/serverModelTypes-CGUZCi0y.d.ts.map +1 -0
  67. package/dist/{serverResponseGuards-CShiXpHT.d.ts → serverResponseGuards-Bvw8ltYJ.d.ts} +2 -2
  68. package/dist/{serverResponseGuards-CShiXpHT.d.ts.map → serverResponseGuards-Bvw8ltYJ.d.ts.map} +1 -1
  69. package/dist/{serverResponseGuards-BW69PS7O.d.cts → serverResponseGuards-i1njcYKN.d.cts} +2 -2
  70. package/dist/{serverResponseGuards-BW69PS7O.d.cts.map → serverResponseGuards-i1njcYKN.d.cts.map} +1 -1
  71. package/dist/signing/buildSignedMobileRequest.d.cts +1 -1
  72. package/dist/signing/buildSignedMobileRequest.d.ts +1 -1
  73. package/dist/signing/canonicalize.cjs +51 -2
  74. package/dist/signing/canonicalize.cjs.map +1 -1
  75. package/dist/signing/canonicalize.d.cts +1 -1
  76. package/dist/signing/canonicalize.d.ts +1 -1
  77. package/dist/signing/canonicalize.js +51 -2
  78. package/dist/signing/canonicalize.js.map +1 -1
  79. package/dist/signing/eip712MethodByType.cjs +7 -0
  80. package/dist/signing/eip712MethodByType.cjs.map +1 -1
  81. package/dist/signing/eip712MethodByType.d.cts +1 -1
  82. package/dist/signing/eip712MethodByType.d.ts +1 -1
  83. package/dist/signing/eip712MethodByType.js +7 -0
  84. package/dist/signing/eip712MethodByType.js.map +1 -1
  85. package/dist/signing/index.d.cts +5 -5
  86. package/dist/signing/index.d.ts +5 -5
  87. package/dist/signing/payloadHash.cjs +1 -1
  88. package/dist/signing/payloadHash.d.cts +1 -1
  89. package/dist/signing/payloadHash.d.ts +1 -1
  90. package/dist/signing/payloadHash.js +1 -1
  91. package/dist/signing/types.d.cts +1 -1
  92. package/dist/signing/types.d.ts +1 -1
  93. package/dist/types/MobileServerFailureError.d.cts +1 -1
  94. package/dist/types/MobileServerFailureError.d.ts +1 -1
  95. package/dist/types/MobileUnsupportedNotificationRouteError.cjs +19 -0
  96. package/dist/types/MobileUnsupportedNotificationRouteError.cjs.map +1 -0
  97. package/dist/types/MobileUnsupportedNotificationRouteError.d.cts +2 -0
  98. package/dist/types/MobileUnsupportedNotificationRouteError.d.ts +2 -0
  99. package/dist/types/MobileUnsupportedNotificationRouteError.js +18 -0
  100. package/dist/types/MobileUnsupportedNotificationRouteError.js.map +1 -0
  101. package/dist/types/clientTypes.d.cts +2 -2
  102. package/dist/types/clientTypes.d.ts +2 -2
  103. package/dist/types/index.cjs +4 -0
  104. package/dist/types/index.d.cts +8 -9
  105. package/dist/types/index.d.ts +8 -9
  106. package/dist/types/index.js +3 -2
  107. package/dist/types/mobileErrorCodes.cjs +11 -1
  108. package/dist/types/mobileErrorCodes.cjs.map +1 -1
  109. package/dist/types/mobileErrorCodes.d.cts +1 -1
  110. package/dist/types/mobileErrorCodes.d.ts +1 -1
  111. package/dist/types/mobileErrorCodes.js +11 -1
  112. package/dist/types/mobileErrorCodes.js.map +1 -1
  113. package/dist/types/serverBaseTypes.d.cts +1 -1
  114. package/dist/types/serverBaseTypes.d.ts +1 -1
  115. package/dist/types/serverExecuteTypes.d.cts +2 -2
  116. package/dist/types/serverExecuteTypes.d.ts +2 -2
  117. package/dist/types/serverModelTypes.cjs +15 -0
  118. package/dist/types/serverModelTypes.cjs.map +1 -1
  119. package/dist/types/serverModelTypes.d.cts +2 -2
  120. package/dist/types/serverModelTypes.d.ts +2 -2
  121. package/dist/types/serverModelTypes.js +14 -1
  122. package/dist/types/serverModelTypes.js.map +1 -1
  123. package/dist/types/serverQueryTypes.d.cts +2 -2
  124. package/dist/types/serverQueryTypes.d.ts +2 -2
  125. package/dist/types/serverRequestTypes.d.cts +2 -2
  126. package/dist/types/serverRequestTypes.d.ts +2 -2
  127. package/dist/types-C9YE4EIt.d.cts +794 -0
  128. package/dist/types-C9YE4EIt.d.cts.map +1 -0
  129. package/dist/types-Cwy1y_9x.d.ts +794 -0
  130. package/dist/types-Cwy1y_9x.d.ts.map +1 -0
  131. package/dist/utils/buildMobileFeedImageMultipart.cjs +31 -0
  132. package/dist/utils/buildMobileFeedImageMultipart.cjs.map +1 -0
  133. package/dist/utils/buildMobileFeedImageMultipart.d.cts +15 -0
  134. package/dist/utils/buildMobileFeedImageMultipart.d.cts.map +1 -0
  135. package/dist/utils/buildMobileFeedImageMultipart.d.ts +15 -0
  136. package/dist/utils/buildMobileFeedImageMultipart.d.ts.map +1 -0
  137. package/dist/utils/buildMobileFeedImageMultipart.js +30 -0
  138. package/dist/utils/buildMobileFeedImageMultipart.js.map +1 -0
  139. package/dist/utils/feed.cjs +3 -0
  140. package/dist/utils/feed.cjs.map +1 -1
  141. package/dist/utils/feed.d.cts +2 -2
  142. package/dist/utils/feed.d.ts +2 -2
  143. package/dist/utils/feed.js +3 -1
  144. package/dist/utils/feed.js.map +1 -1
  145. package/dist/utils/index.cjs +2 -1
  146. package/dist/utils/index.d.cts +3 -3
  147. package/dist/utils/index.d.ts +3 -3
  148. package/dist/utils/index.js +2 -2
  149. package/dist/utils/serverResponseGuards.d.cts +1 -1
  150. package/dist/utils/serverResponseGuards.d.ts +1 -1
  151. package/package.json +3 -3
  152. package/src/MobileClient.ts +388 -11
  153. package/src/dataMappers.test.ts +108 -2
  154. package/src/dataMappers.ts +253 -5
  155. package/src/feedImages.test.ts +309 -0
  156. package/src/notifications.test.ts +950 -0
  157. package/src/signing/canonicalize.ts +66 -0
  158. package/src/signing/eip712MethodByType.ts +10 -0
  159. package/src/signing/signing.test.ts +16 -1
  160. package/src/signing/types.ts +51 -9
  161. package/src/socialFeed.test.ts +38 -2
  162. package/src/types/MobileUnsupportedNotificationRouteError.ts +16 -0
  163. package/src/types/clientTypes.ts +372 -16
  164. package/src/types/index.ts +1 -0
  165. package/src/types/mobileErrorCodes.ts +10 -0
  166. package/src/types/serverExecuteTypes.ts +20 -0
  167. package/src/types/serverModelTypes.ts +205 -6
  168. package/src/types/serverQueryTypes.ts +74 -5
  169. package/src/types/serverRequestTypes.ts +101 -12
  170. package/src/utils/buildMobileFeedImageMultipart.test.ts +45 -0
  171. package/src/utils/buildMobileFeedImageMultipart.ts +36 -0
  172. package/src/utils/feed.ts +3 -0
  173. package/dist/MobileClient-CfVZXTcz.cjs.map +0 -1
  174. package/dist/MobileServerFailureError-BZVwlsMg.d.ts.map +0 -1
  175. package/dist/MobileServerFailureError-CbtK8kD4.d.cts.map +0 -1
  176. package/dist/canonicalize-5eeZrkzQ.d.ts.map +0 -1
  177. package/dist/canonicalize-CBaZprTo.d.cts.map +0 -1
  178. package/dist/clientTypes-C9dJufxk.d.ts.map +0 -1
  179. package/dist/clientTypes-_mEgzmNs.d.cts.map +0 -1
  180. package/dist/eip712MethodByType-D8jLoxDu.d.cts.map +0 -1
  181. package/dist/eip712MethodByType-DcUh8h-l.d.ts.map +0 -1
  182. package/dist/feed-DTJlsg8W.d.cts.map +0 -1
  183. package/dist/feed-DTJlsg8W.d.ts.map +0 -1
  184. package/dist/mobileErrorCodes-CmjYEIYm.d.cts.map +0 -1
  185. package/dist/mobileErrorCodes-CmjYEIYm.d.ts.map +0 -1
  186. package/dist/payloadHash-DhHmgRqL.d.cts.map +0 -1
  187. package/dist/payloadHash-E1_8fpLW.d.ts.map +0 -1
  188. package/dist/serverBaseTypes-B5J1_TyC.d.cts +0 -25
  189. package/dist/serverBaseTypes-B5J1_TyC.d.cts.map +0 -1
  190. package/dist/serverBaseTypes-DnLzsdm-.d.ts +0 -25
  191. package/dist/serverBaseTypes-DnLzsdm-.d.ts.map +0 -1
  192. package/dist/serverExecuteTypes-B-OJok19.d.cts.map +0 -1
  193. package/dist/serverExecuteTypes-BwWGspOg.d.ts.map +0 -1
  194. package/dist/serverModelTypes-CiA-dy1R.d.cts.map +0 -1
  195. package/dist/serverModelTypes-CiA-dy1R.d.ts.map +0 -1
  196. package/dist/serverQueryTypes-CJcjLB7n.d.ts +0 -283
  197. package/dist/serverQueryTypes-CJcjLB7n.d.ts.map +0 -1
  198. package/dist/serverQueryTypes-V1f9yE7L.d.cts +0 -283
  199. package/dist/serverQueryTypes-V1f9yE7L.d.cts.map +0 -1
  200. package/dist/serverRequestTypes-CRlkNSu5.d.cts +0 -270
  201. package/dist/serverRequestTypes-CRlkNSu5.d.cts.map +0 -1
  202. package/dist/serverRequestTypes-DZoOhV2o.d.ts +0 -270
  203. package/dist/serverRequestTypes-DZoOhV2o.d.ts.map +0 -1
  204. package/dist/types-BiRmH5Iv.d.cts +0 -108
  205. package/dist/types-BiRmH5Iv.d.cts.map +0 -1
  206. package/dist/types-DoJuq0Ij.d.ts +0 -108
  207. 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,
@@ -26,6 +35,7 @@ import {
26
35
  mapMobileSocialFeedCommentsPage,
27
36
  mapMobileSocialFeedDrawingMutationResult,
28
37
  mapMobileSocialFeedDrawingResult,
38
+ mapMobileSocialFeedImage,
29
39
  mapMobileSocialFeedItem,
30
40
  mapMobileSocialFeedItemResponse,
31
41
  mapMobileSocialFeedLikeResult,
@@ -46,6 +56,7 @@ import {
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,7 +75,11 @@ 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,
68
85
  GetMobilePositionHistoryParams,
@@ -77,19 +94,27 @@ import {
77
94
  MobileAvatarUpload,
78
95
  MobileCreateAvatarUploadParams,
79
96
  MobileCreateFeedCommentParams,
97
+ MobileCreateFeedImageUploadParams,
80
98
  MobileCreateFeedPostParams,
81
99
  MobileFeedChartDrawingParams,
100
+ MobileFeedImageRequest,
82
101
  MobileFeedPage,
83
102
  MobileFollowBatchResultItem,
84
103
  MobileFollowListPage,
85
104
  MobileFollowMutationResult,
105
+ MobileNotificationInboxV2,
86
106
  MobileNotificationPreferences,
107
+ MobileNotificationRegistrationV2,
108
+ MobileNotificationSettingsV2,
109
+ MobileNotificationSubscriptions,
87
110
  MobilePositionPresenceParams,
88
111
  MobileProfileSearchPage,
89
112
  MobilePublicProfile,
90
113
  MobileRecordFeedViewsParams,
114
+ MobileRecordProductEventsParams,
91
115
  MobileRegisteredWallet,
92
116
  MobileRegisterExpoTokenParams,
117
+ MobileRegisterExpoTokenV2Params,
93
118
  MobileSetAvatarParams,
94
119
  MobileSetBioParams,
95
120
  MobileSetFeedLikeParams,
@@ -103,6 +128,7 @@ import {
103
128
  MobileSocialFeedCommentsPage,
104
129
  MobileSocialFeedDrawingMutationResult,
105
130
  MobileSocialFeedDrawingResult,
131
+ MobileSocialFeedImage,
106
132
  MobileSocialFeedItem,
107
133
  MobileSocialFeedItemResponse,
108
134
  MobileSocialFeedLikeResult,
@@ -119,6 +145,8 @@ import {
119
145
  MobileSocialFeedWatchers,
120
146
  MobileUnregisterExpoTokenParams,
121
147
  MobileUpdateNotificationPreferencesParams,
148
+ MobileUpdateNotificationSettingsV2Params,
149
+ MobileUpdateNotificationSubscriptionsParams,
122
150
  MobileUsernameAvailability,
123
151
  ResolveMobileFeedActionParams,
124
152
  ResolveMobileFeedPositionParams,
@@ -135,6 +163,7 @@ import {
135
163
  import {
136
164
  MobileServerPublicQueryRequest,
137
165
  MobileServerPublicQuerySuccessResponse,
166
+ MobileServerSignedQuerySuccessResponse,
138
167
  } from './types/serverQueryTypes';
139
168
  import {
140
169
  MobileServerExecuteRequestType,
@@ -142,7 +171,10 @@ import {
142
171
  MobileServerPublicExecuteRequestType,
143
172
  MobileServerPublicQueryRequestByType,
144
173
  MobileServerPublicQueryRequestType,
174
+ MobileServerSignedQueryRequestType,
145
175
  } from './types/serverRequestTypes';
176
+ import { buildMobileFeedImageMultipart } from './utils/buildMobileFeedImageMultipart';
177
+ import { MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES } from './utils/feed';
146
178
  import {
147
179
  isMobileServerFailureResponse,
148
180
  isMobileServerSuccessResponse,
@@ -480,7 +512,9 @@ export class MobileClient {
480
512
  /**
481
513
  * Reads one item visible to viewAs, including that account's own hidden content.
482
514
  * Creates an item session directly when sessionId is omitted.
483
- * @param params - Item UUID, preview limit, and optional existing session/read identity.
515
+ * Pass {@link GetMobileFeedItemParams.viewEventId} when the user navigates into the detail so the
516
+ * subsequent {@link recordFeedViews} call can count that visit; omit it for prefetch.
517
+ * @param params - Item UUID, optional detail-visit event, preview limit, and optional existing session/read identity.
484
518
  * @returns The complete item and authorized reading session.
485
519
  * @throws {MobileServerFailureError} On inaccessible content or session mismatch/expiry.
486
520
  * @throws {MobileUnsupportedFeedItemError} If the service returns an unsupported item kind.
@@ -491,6 +525,7 @@ export class MobileClient {
491
525
  const body: MobileServerPublicQueryRequest<'feed_item'> = {
492
526
  type: 'feed_item',
493
527
  item_id: params.itemId,
528
+ view_event_id: params.viewEventId,
494
529
  comment_preview_limit: params.commentPreviewLimit,
495
530
  session_id: params.sessionId,
496
531
  ...mapMobileSocialFeedReadParamsToServer(params),
@@ -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,
@@ -649,6 +685,47 @@ export class MobileClient {
649
685
  return mapMobileSocialFeedDrawingResult(await this.publicQuery(body));
650
686
  }
651
687
 
688
+ /**
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 count as a detail visit or recorded view.
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 count as a detail visit or recorded view.
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
+
652
729
  /**
653
730
  * Reads a comment thread with an optional anchor and bidirectional pagination.
654
731
  * Independent reads create a public-only session; reused sessions retain their original scope.
@@ -743,6 +820,85 @@ export class MobileClient {
743
820
  return mapMobileSocialFeedWatchers(await this.publicQuery(body));
744
821
  }
745
822
 
823
+ /**
824
+ * Reads the token's latest registration generation and saved capabilities, including legacy v1 registrations.
825
+ * @param params - Active Expo token credential; no wallet signature is needed.
826
+ * @returns Saved generation as an opaque string and the complete capability declaration.
827
+ * @throws {MobileServerFailureError} If the token is invalid or inactive (`INVALID_EXPO_TOKEN`).
828
+ */
829
+ async getNotificationRegistrationV2(
830
+ params: GetMobileNotificationRegistrationV2Params,
831
+ ): Promise<MobileNotificationRegistrationV2> {
832
+ return mapMobileNotificationRegistrationV2(
833
+ await this.publicQuery({
834
+ type: 'notification_registration_v2',
835
+ expo_token: params.expoToken,
836
+ }),
837
+ );
838
+ }
839
+
840
+ /**
841
+ * Reads the wallet's full v2 settings document, including legacy preference mappings.
842
+ * @param params - Active Expo token credential. Before displaying settings, compare the wallet returned by
843
+ * {@link MobileClient.getRegisteredWallet} with the connected wallet (case-insensitively).
844
+ * @returns CamelCase settings with the current settings revision.
845
+ * @throws {MobileServerFailureError} If the token is invalid or inactive (`INVALID_EXPO_TOKEN`).
846
+ */
847
+ async getNotificationSettingsV2(
848
+ params: GetMobileNotificationSettingsV2Params,
849
+ ): Promise<MobileNotificationSettingsV2> {
850
+ return mapMobileNotificationSettingsV2(
851
+ await this.publicQuery({
852
+ type: 'notification_settings_v2',
853
+ expo_token: params.expoToken,
854
+ }),
855
+ );
856
+ }
857
+
858
+ /*
859
+ Signed queries
860
+ */
861
+
862
+ /**
863
+ * Reads wallet market subscriptions and the signing Identity's enabled trader bells.
864
+ * @param params - Wallet authentication context; no Expo token is needed.
865
+ * @returns Subscriptions, import state, and the independent subscription revision.
866
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
867
+ * @throws {MobileServerFailureError} If authentication or the query fails.
868
+ */
869
+ async getNotificationSubscriptions(
870
+ params: GetMobileNotificationSubscriptionsParams,
871
+ ): Promise<MobileNotificationSubscriptions> {
872
+ const request = await this.getSignedRequest(
873
+ 'notification_subscriptions',
874
+ params,
875
+ {},
876
+ );
877
+ return mapMobileNotificationSubscriptions(await this.query(request));
878
+ }
879
+
880
+ /**
881
+ * Reads private accepted notification intents, not delivery receipts or read states.
882
+ * @param params - Wallet authentication and optional cursor/page size (1-50, default 20).
883
+ * @returns A camelCase page; discard cached pages and cursors when switching wallets.
884
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
885
+ * @throws {MobileServerFailureError} If authentication or pagination validation fails.
886
+ * @throws {MobileUnsupportedNotificationRouteError} If a notification has an unsupported route name.
887
+ */
888
+ async getNotificationInboxV2(
889
+ params: GetMobileNotificationInboxV2Params,
890
+ ): Promise<MobileNotificationInboxV2> {
891
+ const request = await this.getSignedRequest(
892
+ 'notification_inbox_v2',
893
+ params,
894
+ {
895
+ cursor: params.cursor ?? null,
896
+ limit: params.limit ?? null,
897
+ },
898
+ );
899
+ return mapMobileNotificationInboxV2(await this.query(request));
900
+ }
901
+
746
902
  /*
747
903
  Public executes
748
904
  */
@@ -778,6 +934,7 @@ export class MobileClient {
778
934
  *
779
935
  * @throws {MobileServerFailureError} With error code `INVALID_PREFERENCES` if those rules are violated, or
780
936
  * `INVALID_EXPO_TOKEN` if the token is malformed or is not currently registered to a wallet.
937
+ * @deprecated Use {@link updateNotificationSettingsV2} instead.
781
938
  */
782
939
  async updateNotificationPreferences(
783
940
  params: MobileUpdateNotificationPreferencesParams,
@@ -791,13 +948,36 @@ export class MobileClient {
791
948
  }
792
949
 
793
950
  /**
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.
951
+ * Replaces all v2 settings fields using optimistic concurrency; legacy overlapping preferences are synced.
952
+ * @param params - Active Expo token, latest settings revision, and complete replacement settings.
953
+ * @returns The saved settings document with its incremented revision.
954
+ * @throws {MobileServerFailureError} On invalid token/settings or revision conflict (`NOTIFICATION_REVISION_CONFLICT`). Re-read and
955
+ * merge only the user's current change before retrying; never blindly replay a stale document.
956
+ */
957
+ async updateNotificationSettingsV2(
958
+ params: MobileUpdateNotificationSettingsV2Params,
959
+ ): Promise<MobileNotificationSettingsV2> {
960
+ return mapMobileNotificationSettingsV2(
961
+ await this.publicExecute({
962
+ type: 'update_notification_settings_v2',
963
+ expo_token: params.expoToken,
964
+ expected_revision: params.expectedRevision,
965
+ settings: mapMobileNotificationSettingsToServer(params.settings),
966
+ }),
967
+ );
968
+ }
969
+
970
+ /**
971
+ * Reports a detail visit without signing. Feed-list card impressions and previews do not count.
972
+ * After the primary detail target is shown in the foreground, send the same viewEventId previously
973
+ * registered through {@link getFeedItem}, together with that response's sessionId and viewerToken.
974
+ * Reuse the event ID on retries for the same visit; generate a new one only after leaving and re-entering.
975
+ * @param params - Original reading session and viewer token, the registered detail-visit event, and exactly
976
+ * one fully delivered, readable detail-target item ID.
977
+ * @returns Separate lists of newly accepted and already-counted item IDs for that event.
978
+ * @throws {MobileServerFailureError} On invalid parameters, an unregistered or rebound event
979
+ * (`SOCIAL_FEED_VIEW_EVENT_REBOUND`), session mismatch or expiry, or an ineligible item.
980
+ * Acquire a new session after expiry, re-register the same event via getFeedItem, then retry.
801
981
  */
802
982
  async recordFeedViews(
803
983
  params: MobileRecordFeedViewsParams,
@@ -805,8 +985,9 @@ export class MobileClient {
805
985
  const body: MobileServerPublicExecuteRequest<'record_feed_views'> = {
806
986
  type: 'record_feed_views',
807
987
  session_id: params.sessionId,
808
- item_ids: params.itemIds,
809
988
  viewer_token: params.viewerToken,
989
+ view_event_id: params.viewEventId,
990
+ item_ids: [params.itemId],
810
991
  };
811
992
  return mapMobileSocialFeedViewsResult(await this.publicExecute(body));
812
993
  }
@@ -942,11 +1123,13 @@ export class MobileClient {
942
1123
 
943
1124
  /**
944
1125
  * 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)`.
1126
+ * wallet had registered it. Each registration advances its generation and cancels old pending deliveries.
1127
+ * This legacy method replaces capabilities with v1; use registerExpoTokenV2 consistently after upgrading.
946
1128
  *
947
1129
  * @throws {MobileServerFailureError} With error code `INVALID_EXPO_TOKEN` if the token is not a valid Expo
948
1130
  * push token, or `INVALID_DEVICE_METADATA` if `locale` (max 35 chars) or `appVersion` (max 64 chars) is
949
1131
  * too long.
1132
+ * @deprecated Use {@link registerExpoTokenV2} instead.
950
1133
  */
951
1134
  async registerExpoToken(
952
1135
  params: MobileRegisterExpoTokenParams,
@@ -964,6 +1147,70 @@ export class MobileClient {
964
1147
  return this.execute(signedRequest);
965
1148
  }
966
1149
 
1150
+ /**
1151
+ * Registers a device with its actual v2 capabilities; do not use repeated registration as a heartbeat.
1152
+ * @param params - Wallet authentication, Expo token, metadata, and supported notification types/routes.
1153
+ * @returns Success only; query registration separately for its new generation.
1154
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1155
+ * @throws {MobileServerFailureError} On `INVALID_EXPO_TOKEN`, `INVALID_DEVICE_METADATA`,
1156
+ * `NOTIFICATION_DEVICE_LIMIT` when the wallet already has 32 active devices, or authentication errors.
1157
+ */
1158
+ async registerExpoTokenV2(
1159
+ params: MobileRegisterExpoTokenV2Params,
1160
+ ): Promise<MobileServerSuccessResponse> {
1161
+ const request = await this.getSignedRequest(
1162
+ 'register_expo_token_v2',
1163
+ params,
1164
+ {
1165
+ expo_token: params.expoToken,
1166
+ platform: params.platform,
1167
+ locale: params.locale ?? null,
1168
+ app_version: params.appVersion ?? null,
1169
+ capabilities: mapMobileNotificationCapabilitiesToServer(
1170
+ params.capabilities,
1171
+ ),
1172
+ },
1173
+ );
1174
+ return this.execute(request);
1175
+ }
1176
+
1177
+ /**
1178
+ * Applies exactly one bell, market, or one-time favorites-import operation.
1179
+ * @param params - Wallet authentication and subscription update with the latest subscription revision.
1180
+ * @returns Success only; re-query subscriptions for the new revision even after a repeated ON/OFF.
1181
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1182
+ * @throws {MobileServerFailureError} On `NOTIFICATION_REVISION_CONFLICT`, `INVALID_NOTIFICATION_REQUEST`, or
1183
+ * `INVALID_IDENTITY_TARGET`.
1184
+ */
1185
+ async updateNotificationSubscriptions(
1186
+ params: MobileUpdateNotificationSubscriptionsParams,
1187
+ ): Promise<MobileServerSuccessResponse> {
1188
+ const request = await this.getSignedRequest(
1189
+ 'update_notification_subscriptions',
1190
+ params,
1191
+ mapMobileNotificationSubscriptionsUpdateToServer(params.update),
1192
+ );
1193
+ return this.execute(request);
1194
+ }
1195
+
1196
+ /**
1197
+ * Atomically records 1-50 analytics events after the user has consented to analytics.
1198
+ * @param params - Wallet authentication and events with stable UUIDs and actual millisecond timestamps.
1199
+ * @returns Success only; reuse event IDs on retries. Notification clicks do not mark an inbox item as read.
1200
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1201
+ * @throws {MobileServerFailureError} If any event, timestamp, batch size, or notification ownership is invalid.
1202
+ */
1203
+ async recordProductEvents(
1204
+ params: MobileRecordProductEventsParams,
1205
+ ): Promise<MobileServerSuccessResponse> {
1206
+ const request = await this.getSignedRequest(
1207
+ 'record_product_events',
1208
+ params,
1209
+ { events: params.events.map(mapMobileProductEventToServer) },
1210
+ );
1211
+ return this.execute(request);
1212
+ }
1213
+
967
1214
  /**
968
1215
  * Sets whether the signing subaccount follows a Profile. Both directions are idempotent: following again
969
1216
  * keeps the original relationship creation time, so it does not move the account in recency order, and
@@ -1044,10 +1291,49 @@ export class MobileClient {
1044
1291
  text: params.text,
1045
1292
  mentions: params.mentions.map(mapMobileSocialFeedMentionToServer),
1046
1293
  drawing_data_json: params.drawingDataJson,
1294
+ image_ids: params.imageIds,
1047
1295
  });
1048
1296
  return mapMobileSocialFeedItem((await this.execute(request)).item);
1049
1297
  }
1050
1298
 
1299
+ /**
1300
+ * Uploads one static JPEG/PNG for a later {@link createFeedPost} via multipart execute.
1301
+ * The whole HTTP request must stay within 2 MiB including signature overhead; upload one file per call
1302
+ * and bind up to nine ready IDs at publication. There is no complete/PUT step — success means ready.
1303
+ * @param params - Author subaccount and signing options, plus the original file bytes.
1304
+ * @returns Ready image metadata, including original and thumbnail paths.
1305
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1306
+ * @throws {RangeError} If the complete multipart request exceeds {@link MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES}.
1307
+ * @throws {MobileServerFailureError} On authentication, format/size/hash errors (`INVALID_SOCIAL_FEED_IMAGE`),
1308
+ * or when publishing is disabled (`SERVICE_UNAVAILABLE`), or image storage is unavailable
1309
+ * (`SOCIAL_FEED_DATA_UNAVAILABLE`, 6320, HTTP 503).
1310
+ */
1311
+ async createFeedPostImageUpload(
1312
+ params: MobileCreateFeedImageUploadParams,
1313
+ ): Promise<MobileSocialFeedImage> {
1314
+ return this.createFeedImageUpload('create_feed_post_image_uploads', params);
1315
+ }
1316
+
1317
+ /**
1318
+ * Uploads one static JPEG/PNG for a later {@link createFeedComment} via multipart execute.
1319
+ * Same size and readiness rules as {@link createFeedPostImageUpload}; comment text remains required.
1320
+ * @param params - Author subaccount and signing options, plus the original file bytes.
1321
+ * @returns Ready image metadata, including original and thumbnail paths.
1322
+ * @throws {WalletNotProvidedError} If neither a linked signer nor a default wallet is configured.
1323
+ * @throws {RangeError} If the complete multipart request exceeds {@link MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES}.
1324
+ * @throws {MobileServerFailureError} On authentication, format/size/hash errors (`INVALID_SOCIAL_FEED_IMAGE`),
1325
+ * or when comments are disabled (`SERVICE_UNAVAILABLE`), or image storage is unavailable
1326
+ * (`SOCIAL_FEED_DATA_UNAVAILABLE`, 6320, HTTP 503).
1327
+ */
1328
+ async createFeedCommentImageUpload(
1329
+ params: MobileCreateFeedImageUploadParams,
1330
+ ): Promise<MobileSocialFeedImage> {
1331
+ return this.createFeedImageUpload(
1332
+ 'create_feed_comment_image_uploads',
1333
+ params,
1334
+ );
1335
+ }
1336
+
1051
1337
  /**
1052
1338
  * First attaches a drawing to an owned post or trade card while the author still holds its position.
1053
1339
  * The original JSON is signed unchanged; false and zero are valid drawing documents.
@@ -1119,6 +1405,7 @@ export class MobileClient {
1119
1405
  text: params.text,
1120
1406
  mentions: params.mentions.map(mapMobileSocialFeedMentionToServer),
1121
1407
  attached_position_id: params.attachedPositionId,
1408
+ image_ids: params.imageIds,
1122
1409
  });
1123
1410
  return mapMobileSocialFeedCommentResult(await this.execute(request));
1124
1411
  }
@@ -1188,6 +1475,27 @@ export class MobileClient {
1188
1475
  return response.data as MobileServerPublicQuerySuccessResponse<T>;
1189
1476
  }
1190
1477
 
1478
+ protected async publicGetBytes(
1479
+ url: string,
1480
+ headers: Record<string, string>,
1481
+ ): Promise<Uint8Array> {
1482
+ const response = await this.axiosInstance.get<ArrayBuffer>(url, {
1483
+ responseType: 'arraybuffer',
1484
+ headers,
1485
+ });
1486
+ if (response.status < 200 || response.status >= 300) {
1487
+ const responseText = bytesToString(new Uint8Array(response.data));
1488
+ let errorData: unknown;
1489
+ try {
1490
+ errorData = JSON.parse(responseText);
1491
+ } catch {
1492
+ errorData = responseText;
1493
+ }
1494
+ this.checkResponseStatus({ ...response, data: errorData });
1495
+ }
1496
+ return new Uint8Array(response.data);
1497
+ }
1498
+
1191
1499
  protected async publicExecute<T extends MobileServerPublicExecuteRequestType>(
1192
1500
  body: { type: T } & MobileServerPublicExecuteRequestByType[T],
1193
1501
  ): Promise<MobileServerPublicExecuteSuccessResponse<T>> {
@@ -1203,14 +1511,43 @@ export class MobileClient {
1203
1511
  return response.data as MobileServerPublicExecuteSuccessResponse<T>;
1204
1512
  }
1205
1513
 
1514
+ /**
1515
+ * Sends an authenticated read through the signed query route.
1516
+ * @param body - Canonical inner payload with its signature, sender, and nonce.
1517
+ * @returns The successful query envelope and its type-specific payload.
1518
+ * @throws {MobileServerFailureError} For a structured service failure, including non-2xx responses.
1519
+ */
1520
+ protected async query<T extends MobileServerSignedQueryRequestType>(
1521
+ body: MobileSignedRequest & { type: T },
1522
+ ): Promise<MobileServerSignedQuerySuccessResponse<T>> {
1523
+ const response = await this.axiosInstance.post<unknown>(
1524
+ `${this.opts.url}/query`,
1525
+ body,
1526
+ );
1527
+ this.checkResponseStatus(response);
1528
+ this.checkServerStatus(response);
1529
+ return response.data as MobileServerSignedQuerySuccessResponse<T>;
1530
+ }
1531
+
1206
1532
  // `T` is inferred from the body's `type` rather than declared by the caller, for the same reason
1207
1533
  // `publicQuery` spells its body out as an intersection: an indexed access is not an inference site.
1208
1534
  protected async execute<T extends MobileServerExecuteRequestType>(
1209
1535
  body: MobileSignedRequest & { type: T },
1536
+ fileBytes?: Uint8Array,
1210
1537
  ): Promise<MobileServerExecuteSuccessResponse<T>> {
1538
+ const multipart =
1539
+ fileBytes == null
1540
+ ? undefined
1541
+ : buildMobileFeedImageMultipart(JSON.stringify(body), fileBytes);
1542
+ if (multipart) {
1543
+ this.checkFeedImageRequestSize(multipart.body.byteLength);
1544
+ }
1211
1545
  const response = await this.axiosInstance.post<MobileServerExecuteResult>(
1212
1546
  `${this.opts.url}/execute`,
1213
- body,
1547
+ multipart?.body ?? body,
1548
+ multipart
1549
+ ? { headers: { 'Content-Type': multipart.contentType } }
1550
+ : undefined,
1214
1551
  );
1215
1552
 
1216
1553
  this.checkResponseStatus(response);
@@ -1220,6 +1557,46 @@ export class MobileClient {
1220
1557
  return response.data as MobileServerExecuteSuccessResponse<T>;
1221
1558
  }
1222
1559
 
1560
+ private async createFeedImageUpload(
1561
+ type:
1562
+ | 'create_feed_post_image_uploads'
1563
+ | 'create_feed_comment_image_uploads',
1564
+ params: MobileCreateFeedImageUploadParams,
1565
+ ): Promise<MobileSocialFeedImage> {
1566
+ const nonce = params.nonce ?? getMobileNonce();
1567
+ // Match the signed body's field lengths so oversized requests fail before prompting the wallet.
1568
+ const sizePayload = {
1569
+ type,
1570
+ content_hash: zeroHash,
1571
+ signature: `0x${'00'.repeat(65)}`,
1572
+ sender: subaccountToHex(params),
1573
+ nonce: nonce.toString(),
1574
+ };
1575
+ const overhead = buildMobileFeedImageMultipart(
1576
+ JSON.stringify(sizePayload),
1577
+ new Uint8Array(),
1578
+ ).body.byteLength;
1579
+ this.checkFeedImageRequestSize(overhead + params.fileBytes.byteLength);
1580
+ const contentHash = keccak256(params.fileBytes);
1581
+ const request = await this.getSignedRequest(
1582
+ type,
1583
+ { ...params, nonce },
1584
+ {
1585
+ content_hash: contentHash,
1586
+ },
1587
+ );
1588
+ const data = await this.execute(request, params.fileBytes);
1589
+ return mapMobileSocialFeedImage(data.image);
1590
+ }
1591
+
1592
+ private checkFeedImageRequestSize(byteLength: number): void {
1593
+ if (byteLength > MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES) {
1594
+ throw new RangeError(
1595
+ `Feed image upload request exceeds ${MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES} bytes (2 MiB).`,
1596
+ );
1597
+ }
1598
+ }
1599
+
1223
1600
  /**
1224
1601
  * Validates the HTTP status before interpreting the body. Unlike the engine, the mobile backend returns
1225
1602
  * failure envelopes with non-2xx statuses (e.g. 404 for `PROFILE_NOT_FOUND`), so a non-2xx response with a