@v3finance/monitoring-sdk 0.1.0 → 0.2.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 (154) hide show
  1. package/CHANGELOG.md +21 -11
  2. package/README.md +223 -148
  3. package/dist/cjs/client.d.ts +23 -42
  4. package/dist/cjs/client.d.ts.map +1 -1
  5. package/dist/cjs/client.js +44 -53
  6. package/dist/cjs/client.js.map +1 -1
  7. package/dist/cjs/core/errors.d.ts +18 -18
  8. package/dist/cjs/core/errors.d.ts.map +1 -1
  9. package/dist/cjs/core/errors.js +17 -17
  10. package/dist/cjs/core/errors.js.map +1 -1
  11. package/dist/cjs/core/http.d.ts +22 -26
  12. package/dist/cjs/core/http.d.ts.map +1 -1
  13. package/dist/cjs/core/http.js +28 -36
  14. package/dist/cjs/core/http.js.map +1 -1
  15. package/dist/cjs/core/pagination.d.ts +6 -7
  16. package/dist/cjs/core/pagination.d.ts.map +1 -1
  17. package/dist/cjs/core/pagination.js +1 -1
  18. package/dist/cjs/core/pagination.js.map +1 -1
  19. package/dist/cjs/core/scope.d.ts +8 -0
  20. package/dist/cjs/core/scope.d.ts.map +1 -0
  21. package/dist/cjs/core/scope.js +3 -0
  22. package/dist/cjs/core/scope.js.map +1 -0
  23. package/dist/cjs/generated/types.d.ts +129 -117
  24. package/dist/cjs/generated/types.d.ts.map +1 -1
  25. package/dist/cjs/generated/types.js +1 -1
  26. package/dist/cjs/generated/types.js.map +1 -1
  27. package/dist/cjs/index.d.ts +6 -6
  28. package/dist/cjs/index.d.ts.map +1 -1
  29. package/dist/cjs/index.js +13 -16
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/cjs/resources/account.d.ts +12 -51
  32. package/dist/cjs/resources/account.d.ts.map +1 -1
  33. package/dist/cjs/resources/account.js +17 -61
  34. package/dist/cjs/resources/account.js.map +1 -1
  35. package/dist/cjs/resources/addresses.d.ts +49 -65
  36. package/dist/cjs/resources/addresses.d.ts.map +1 -1
  37. package/dist/cjs/resources/addresses.js +60 -69
  38. package/dist/cjs/resources/addresses.js.map +1 -1
  39. package/dist/cjs/resources/catalog.d.ts +17 -10
  40. package/dist/cjs/resources/catalog.d.ts.map +1 -1
  41. package/dist/cjs/resources/catalog.js +23 -17
  42. package/dist/cjs/resources/catalog.js.map +1 -1
  43. package/dist/cjs/resources/deliveries.d.ts +34 -23
  44. package/dist/cjs/resources/deliveries.d.ts.map +1 -1
  45. package/dist/cjs/resources/deliveries.js +27 -17
  46. package/dist/cjs/resources/deliveries.js.map +1 -1
  47. package/dist/cjs/resources/transactions.d.ts +14 -14
  48. package/dist/cjs/resources/transactions.d.ts.map +1 -1
  49. package/dist/cjs/resources/transactions.js +15 -16
  50. package/dist/cjs/resources/transactions.js.map +1 -1
  51. package/dist/cjs/resources/webhooks.d.ts +22 -0
  52. package/dist/cjs/resources/webhooks.d.ts.map +1 -0
  53. package/dist/cjs/resources/webhooks.js +35 -0
  54. package/dist/cjs/resources/webhooks.js.map +1 -0
  55. package/dist/cjs/units.d.ts +11 -11
  56. package/dist/cjs/units.js +14 -14
  57. package/dist/cjs/units.js.map +1 -1
  58. package/dist/cjs/version.d.ts +3 -3
  59. package/dist/cjs/version.d.ts.map +1 -1
  60. package/dist/cjs/version.js +3 -3
  61. package/dist/cjs/version.js.map +1 -1
  62. package/dist/cjs/webhooks.d.ts +33 -28
  63. package/dist/cjs/webhooks.d.ts.map +1 -1
  64. package/dist/cjs/webhooks.js +33 -28
  65. package/dist/cjs/webhooks.js.map +1 -1
  66. package/dist/esm/client.d.ts +23 -42
  67. package/dist/esm/client.d.ts.map +1 -1
  68. package/dist/esm/client.js +46 -55
  69. package/dist/esm/client.js.map +1 -1
  70. package/dist/esm/core/errors.d.ts +18 -18
  71. package/dist/esm/core/errors.d.ts.map +1 -1
  72. package/dist/esm/core/errors.js +17 -17
  73. package/dist/esm/core/errors.js.map +1 -1
  74. package/dist/esm/core/http.d.ts +22 -26
  75. package/dist/esm/core/http.d.ts.map +1 -1
  76. package/dist/esm/core/http.js +28 -36
  77. package/dist/esm/core/http.js.map +1 -1
  78. package/dist/esm/core/pagination.d.ts +6 -7
  79. package/dist/esm/core/pagination.d.ts.map +1 -1
  80. package/dist/esm/core/pagination.js +1 -1
  81. package/dist/esm/core/pagination.js.map +1 -1
  82. package/dist/esm/core/scope.d.ts +8 -0
  83. package/dist/esm/core/scope.d.ts.map +1 -0
  84. package/dist/esm/core/scope.js +2 -0
  85. package/dist/esm/core/scope.js.map +1 -0
  86. package/dist/esm/generated/types.d.ts +129 -117
  87. package/dist/esm/generated/types.d.ts.map +1 -1
  88. package/dist/esm/generated/types.js +1 -1
  89. package/dist/esm/generated/types.js.map +1 -1
  90. package/dist/esm/index.d.ts +6 -6
  91. package/dist/esm/index.d.ts.map +1 -1
  92. package/dist/esm/index.js +6 -6
  93. package/dist/esm/index.js.map +1 -1
  94. package/dist/esm/resources/account.d.ts +12 -51
  95. package/dist/esm/resources/account.d.ts.map +1 -1
  96. package/dist/esm/resources/account.js +15 -57
  97. package/dist/esm/resources/account.js.map +1 -1
  98. package/dist/esm/resources/addresses.d.ts +49 -65
  99. package/dist/esm/resources/addresses.d.ts.map +1 -1
  100. package/dist/esm/resources/addresses.js +60 -69
  101. package/dist/esm/resources/addresses.js.map +1 -1
  102. package/dist/esm/resources/catalog.d.ts +17 -10
  103. package/dist/esm/resources/catalog.d.ts.map +1 -1
  104. package/dist/esm/resources/catalog.js +23 -17
  105. package/dist/esm/resources/catalog.js.map +1 -1
  106. package/dist/esm/resources/deliveries.d.ts +34 -23
  107. package/dist/esm/resources/deliveries.d.ts.map +1 -1
  108. package/dist/esm/resources/deliveries.js +27 -17
  109. package/dist/esm/resources/deliveries.js.map +1 -1
  110. package/dist/esm/resources/transactions.d.ts +14 -14
  111. package/dist/esm/resources/transactions.d.ts.map +1 -1
  112. package/dist/esm/resources/transactions.js +15 -16
  113. package/dist/esm/resources/transactions.js.map +1 -1
  114. package/dist/esm/resources/webhooks.d.ts +22 -0
  115. package/dist/esm/resources/webhooks.d.ts.map +1 -0
  116. package/dist/esm/resources/webhooks.js +31 -0
  117. package/dist/esm/resources/webhooks.js.map +1 -0
  118. package/dist/esm/units.d.ts +11 -11
  119. package/dist/esm/units.js +14 -14
  120. package/dist/esm/units.js.map +1 -1
  121. package/dist/esm/version.d.ts +3 -3
  122. package/dist/esm/version.d.ts.map +1 -1
  123. package/dist/esm/version.js +3 -3
  124. package/dist/esm/version.js.map +1 -1
  125. package/dist/esm/webhooks.d.ts +33 -28
  126. package/dist/esm/webhooks.d.ts.map +1 -1
  127. package/dist/esm/webhooks.js +33 -28
  128. package/dist/esm/webhooks.js.map +1 -1
  129. package/package.json +8 -5
  130. package/src/client.ts +50 -61
  131. package/src/core/errors.ts +18 -18
  132. package/src/core/http.ts +45 -55
  133. package/src/core/pagination.ts +7 -8
  134. package/src/core/scope.ts +8 -0
  135. package/src/generated/types.ts +139 -125
  136. package/src/index.ts +7 -24
  137. package/src/resources/account.ts +18 -72
  138. package/src/resources/addresses.ts +84 -109
  139. package/src/resources/catalog.ts +28 -19
  140. package/src/resources/deliveries.ts +43 -30
  141. package/src/resources/transactions.ts +18 -17
  142. package/src/resources/webhooks.ts +36 -0
  143. package/src/units.ts +14 -14
  144. package/src/version.ts +3 -3
  145. package/src/webhooks.ts +37 -32
  146. package/dist/cjs/resources/clients.d.ts +0 -84
  147. package/dist/cjs/resources/clients.d.ts.map +0 -1
  148. package/dist/cjs/resources/clients.js +0 -102
  149. package/dist/cjs/resources/clients.js.map +0 -1
  150. package/dist/esm/resources/clients.d.ts +0 -84
  151. package/dist/esm/resources/clients.d.ts.map +0 -1
  152. package/dist/esm/resources/clients.js +0 -97
  153. package/dist/esm/resources/clients.js.map +0 -1
  154. package/src/resources/clients.ts +0 -135
@@ -1,126 +1,105 @@
1
1
  import { MonitoringApiError } from '../core/errors.js'
2
- import { seg, type HttpClient, type RequestOptions } from '../core/http.js'
3
- import { paginate, type Page } from '../core/pagination.js'
4
- import type { Address, AddressInput, AssetSelection, Direction, UpdateAddressesRequest } from '../generated/types.js'
2
+ import type { RequestOptions } from '../core/http.js'
3
+ import { paginate } from '../core/pagination.js'
4
+ import type { Scope } from '../core/scope.js'
5
+ import type {
6
+ AddAddressesResult,
7
+ Address,
8
+ AddressInput,
9
+ AddressPage,
10
+ AddressRef,
11
+ AddressUpdate,
12
+ AssetSelection,
13
+ Direction,
14
+ RemoveAddressesResult,
15
+ UpdateAddressesResult,
16
+ } from '../generated/types.js'
5
17
 
6
- /** Столько адресов API принимает за один запрос; больше SDK разбивает на пачки сам */
18
+ /** Maximum number of addresses the API accepts per request. Larger lists are split into batches automatically. */
7
19
  export const ADDRESS_BATCH_SIZE = 1000
8
20
 
9
21
  /**
10
- * Подписка по умолчанию для всех адресов запроса — действует на элементы, где своей нет.
11
- * Без неё адрес получает все активы сети в обе стороны.
22
+ * Default subscription for every address in a request that does not set its own. Without it, an address is notified
23
+ * about all assets of its network in both directions.
12
24
  */
13
- export interface AddressDefaults {
14
- /** Символы (`USDT`) или адреса контрактов; `null` — все активы сети, включая добавленные позже */
25
+ export interface SubscriptionDefaults {
26
+ /** Asset symbols (`USDT`) or contract addresses. `null` — all assets of the network, including ones added later. */
15
27
  assets?: AssetSelection
16
- /** `in` — только входящие, `out` — только исходящие, `both` — любые */
28
+ /** `in` — incoming only, `out` — outgoing only, `both` — either. */
17
29
  direction?: Direction
18
30
  }
19
31
 
20
- /** Адрес в сети — для снятия с мониторинга */
21
- export interface AddressRef {
22
- /** Сеть: slug (`tron`, `ethereum`) или CAIP-2 (`eip155:1`) */
23
- network: string
24
- address: string
25
- }
26
-
27
- /** Новая подписка одного адреса; что не передано — не меняется */
28
- export type AddressSubscriptionUpdate = UpdateAddressesRequest['addresses'][number]
29
-
30
- export interface AddAddressesResult {
31
- /** Добавлено новых адресов */
32
- added: number
33
- /** Уже отслеживались — их подписка не менялась */
34
- alreadyWatched: number
35
- }
36
-
37
- export interface UpdateAddressesResult {
38
- /** Найдено и изменено */
39
- updated: number
40
- /** Не на мониторинге у этого клиента */
41
- notWatched: number
42
- }
43
-
44
- export interface RemoveAddressesResult {
45
- /** Снято с мониторинга */
46
- removed: number
47
- /** Не отслеживались */
48
- notWatched: number
49
- }
50
-
32
+ /** Parameters of {@link AddressesResource.list}. */
51
33
  export interface ListAddressesParams {
52
- /** Только эта сеть */
34
+ /** Only addresses on this network. */
53
35
  network?: string
54
- /** Размер страницы, до 5000; по умолчанию 500 */
36
+ /** Page size, up to 5000. Defaults to 500. */
55
37
  limit?: number
56
- /** Курсор: `next` прошлой страницы */
38
+ /** Cursor: `next` of the previous page. */
57
39
  after?: number
58
40
  }
59
41
 
60
- /**
61
- * Адреса клиента на мониторинге и их подписка: какие активы и в каком направлении сообщать.
62
- *
63
- * Новый адрес начинает отслеживаться через несколько секунд после добавления.
64
- */
42
+ /** Addresses you monitor and their subscriptions: which assets and which direction you are notified about. */
65
43
  export class AddressesResource {
66
- readonly #http: HttpClient
67
- constructor(http: HttpClient) {
68
- this.#http = http
44
+ readonly #scope: Scope
45
+
46
+ /** @internal */
47
+ constructor(scope: Scope) {
48
+ this.#scope = scope
69
49
  }
70
50
 
71
51
  /**
72
- * Страница адресов с подпиской каждого.
52
+ * Lists monitored addresses, one page at a time.
73
53
  *
74
54
  * @example
75
55
  * ```ts
76
- * const page = await monitoring.addresses.list(clientId, { network: 'tron' })
56
+ * const { items, next } = await monitoring.addresses.list({ network: 'tron' })
77
57
  * ```
78
58
  */
79
- list(clientId: string, params: ListAddressesParams = {}, options?: RequestOptions): Promise<Page<Address, number>> {
80
- return this.#http.request({ method: 'GET', path: `/v1/clients/${seg(clientId)}/addresses`, query: { ...params }, idempotent: true, options })
59
+ async list(params: ListAddressesParams = {}, options?: RequestOptions): Promise<AddressPage> {
60
+ return this.#scope.http.request({ method: 'GET', path: `${await this.#scope.clientPath()}/addresses`, query: { ...params }, idempotent: true, options })
81
61
  }
82
62
 
83
63
  /**
84
- * Все адреса клиента — страница за страницей.
64
+ * Iterates over all monitored addresses, fetching pages as needed.
85
65
  *
86
66
  * @example
87
67
  * ```ts
88
- * for await (const a of monitoring.addresses.iterate(clientId)) console.log(a.network, a.address, a.direction)
68
+ * for await (const address of monitoring.addresses.iterate({ network: 'tron' })) {
69
+ * console.log(address.address, address.assets ?? 'all assets', address.direction)
70
+ * }
89
71
  * ```
90
72
  */
91
- iterate(clientId: string, params: Omit<ListAddressesParams, 'after'> = {}, options?: RequestOptions): AsyncGenerator<Address, void, undefined> {
92
- return paginate<Address, number>((after) => this.list(clientId, { ...params, after }, options))
73
+ iterate(params: Omit<ListAddressesParams, 'after'> = {}, options?: RequestOptions): AsyncGenerator<Address, void, undefined> {
74
+ return paginate<Address>((after) => this.list({ ...params, after }, options))
93
75
  }
94
76
 
95
77
  /**
96
- * Ставит адреса на мониторинг. Уже отслеживаемые не меняются (подписку меняет {@link update}).
78
+ * Starts monitoring addresses. New addresses are picked up within seconds. Addresses that are already monitored are
79
+ * left unchanged — use {@link AddressesResource.update} to change their subscription.
97
80
  *
98
- * Каждая пачка до {@link ADDRESS_BATCH_SIZE} адресов проверяется целиком: одна ошибка — пачка не записана.
99
- * Больше 1000 адресов SDK отправит несколькими пачками по порядку; если пачка не прошла, предыдущие уже
100
- * записаны, а номера в `error.details` указывают на элементы исходного массива. Повтор безопасен.
81
+ * Each batch of up to {@link ADDRESS_BATCH_SIZE} addresses is validated as a whole: if any item is invalid, the
82
+ * batch is not applied. Larger lists are sent in batches, in order; if a batch fails, the previous batches have
83
+ * already been applied, and the indexes in `error.details` refer to your original list. Retrying is safe.
101
84
  *
102
- * @param defaults - подписка для элементов без своей; по умолчанию — все активы, обе стороны
103
- * @throws {@link MonitoringApiError} 400 — адрес не той сети, неверный регистр TRON, неизвестный актив…
85
+ * @param addresses - Addresses to monitor. An EVM address is monitored per network: add it once per network.
86
+ * @param defaults - Subscription for addresses that do not set their own.
87
+ * @throws {@link MonitoringApiError} `400` with `details` when an address is invalid for its network or an asset
88
+ * is unknown.
104
89
  *
105
90
  * @example
106
91
  * ```ts
107
- * // депозитные адреса: только входящие USDT
108
- * await monitoring.addresses.add(clientId, [{ network: 'tron', address: 'T…', label: 'user-42' }], {
92
+ * // deposit addresses: incoming USDT only
93
+ * await monitoring.addresses.add([{ network: 'tron', address: 'TA8Hg…', label: 'user-42' }], {
109
94
  * assets: ['USDT'],
110
95
  * direction: 'in',
111
96
  * })
112
97
  * ```
113
98
  */
114
- async add(clientId: string, addresses: AddressInput[], defaults: AddressDefaults = {}, options?: RequestOptions): Promise<AddAddressesResult> {
99
+ async add(addresses: AddressInput[], defaults: SubscriptionDefaults = {}, options?: RequestOptions): Promise<AddAddressesResult> {
115
100
  const total: AddAddressesResult = { added: 0, alreadyWatched: 0 }
116
- await this.#inBatches(addresses, async (batch) => {
117
- const r = await this.#http.request<AddAddressesResult>({
118
- method: 'POST',
119
- path: `/v1/clients/${seg(clientId)}/addresses`,
120
- body: { ...defaults, addresses: batch },
121
- idempotent: true,
122
- options,
123
- })
101
+ await this.#batches(addresses, async (batch, path) => {
102
+ const r = await this.#scope.http.request<AddAddressesResult>({ method: 'POST', path: `${path}/addresses`, body: { ...defaults, addresses: batch }, idempotent: true, options })
124
103
  total.added += r.added
125
104
  total.alreadyWatched += r.alreadyWatched
126
105
  })
@@ -128,61 +107,57 @@ export class AddressesResource {
128
107
  }
129
108
 
130
109
  /**
131
- * Меняет подписку отслеживаемых адресов: меняется только переданное. Без `assets` активы остаются прежними,
132
- * `assets: null` — снова все активы сети.
110
+ * Changes the subscription of monitored addresses. Only the fields you pass are changed: omit `assets` to keep the
111
+ * current assets, or pass `null` to subscribe to all assets of the network.
133
112
  *
134
113
  * @example
135
114
  * ```ts
136
- * await monitoring.addresses.update(clientId, [{ network: 'tron', address: 'T…' }], { direction: 'both' })
115
+ * await monitoring.addresses.update([{ network: 'tron', address: 'TA8Hg…' }], { direction: 'both' })
137
116
  * ```
138
117
  */
139
- async update(clientId: string, addresses: AddressSubscriptionUpdate[], defaults: AddressDefaults = {}, options?: RequestOptions): Promise<UpdateAddressesResult> {
118
+ async update(addresses: AddressUpdate[], defaults: SubscriptionDefaults = {}, options?: RequestOptions): Promise<UpdateAddressesResult> {
140
119
  const total: UpdateAddressesResult = { updated: 0, notWatched: 0 }
141
- await this.#inBatches(addresses, async (batch) => {
142
- const r = await this.#http.request<UpdateAddressesResult>({
143
- method: 'POST',
144
- path: `/v1/clients/${seg(clientId)}/addresses/update`,
145
- body: { ...defaults, addresses: batch },
146
- idempotent: true,
147
- options,
148
- })
120
+ await this.#batches(addresses, async (batch, path) => {
121
+ const r = await this.#scope.http.request<UpdateAddressesResult>({ method: 'POST', path: `${path}/addresses/update`, body: { ...defaults, addresses: batch }, idempotent: true, options })
149
122
  total.updated += r.updated
150
123
  total.notWatched += r.notWatched
151
124
  })
152
125
  return total
153
126
  }
154
127
 
155
- /** Снимает адреса с мониторинга. Больше 1000 — несколькими пачками */
156
- async remove(clientId: string, addresses: AddressRef[], options?: RequestOptions): Promise<RemoveAddressesResult> {
128
+ /**
129
+ * Stops monitoring addresses.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * await monitoring.addresses.remove([{ network: 'tron', address: 'TA8Hg…' }])
134
+ * ```
135
+ */
136
+ async remove(addresses: AddressRef[], options?: RequestOptions): Promise<RemoveAddressesResult> {
157
137
  const total: RemoveAddressesResult = { removed: 0, notWatched: 0 }
158
- await this.#inBatches(addresses, async (batch) => {
159
- const r = await this.#http.request<RemoveAddressesResult>({
160
- method: 'POST',
161
- path: `/v1/clients/${seg(clientId)}/addresses/remove`,
162
- body: { addresses: batch },
163
- idempotent: true,
164
- options,
165
- })
138
+ await this.#batches(addresses, async (batch, path) => {
139
+ const r = await this.#scope.http.request<RemoveAddressesResult>({ method: 'POST', path: `${path}/addresses/remove`, body: { addresses: batch }, idempotent: true, options })
166
140
  total.removed += r.removed
167
141
  total.notWatched += r.notWatched
168
142
  })
169
143
  return total
170
144
  }
171
145
 
172
- /** Пачками по порядку; номера ошибок по элементам — в нумерации исходного массива */
173
- async #inBatches<T>(items: T[], send: (batch: T[]) => Promise<void>): Promise<void> {
146
+ /** Sends items in batches, in order; maps per-item error indexes back to the original list. */
147
+ async #batches<T>(items: T[], send: (batch: T[], clientPath: string) => Promise<void>): Promise<void> {
148
+ const path = await this.#scope.clientPath()
174
149
  for (let offset = 0; offset < items.length; offset += ADDRESS_BATCH_SIZE) {
175
150
  try {
176
- await send(items.slice(offset, offset + ADDRESS_BATCH_SIZE))
177
- } catch (e) {
178
- if (!(e instanceof MonitoringApiError) || offset === 0) throw e
151
+ await send(items.slice(offset, offset + ADDRESS_BATCH_SIZE), path)
152
+ } catch (error) {
153
+ if (!(error instanceof MonitoringApiError) || offset === 0) throw error
179
154
  throw new MonitoringApiError({
180
- status: e.status,
181
- code: e.code,
182
- message: `${e.message.replace(/ \(HTTP \d+, [\w-]+\)$/, '')}; addresses before #${offset} were already applied`,
183
- details: e.details.map((d) => ({ ...d, index: d.index + offset })),
184
- requestId: e.requestId,
185
- body: e.body,
155
+ status: error.status,
156
+ code: error.code,
157
+ message: `${error.message.replace(/ \(HTTP \d+, [\w-]+\)$/, '')}; addresses before index ${offset} were already applied`,
158
+ details: error.details.map((d) => ({ ...d, index: d.index + offset })),
159
+ requestId: error.requestId,
160
+ body: error.body,
186
161
  })
187
162
  }
188
163
  }
@@ -1,47 +1,56 @@
1
- import { seg, type HttpClient, type RequestOptions } from '../core/http.js'
2
- import type { Asset, Network } from '../generated/types.js'
1
+ import { seg, type RequestOptions } from '../core/http.js'
2
+ import type { Scope } from '../core/scope.js'
3
+ import type { Asset, AssetList, Network, NetworkList } from '../generated/types.js'
3
4
 
4
- /** Сети: TRON и EVM-сети с идентификаторами CAIP-2 */
5
+ /** Supported networks. */
5
6
  export class NetworksResource {
6
- readonly #http: HttpClient
7
- constructor(http: HttpClient) {
8
- this.#http = http
7
+ readonly #scope: Scope
8
+
9
+ /** @internal */
10
+ constructor(scope: Scope) {
11
+ this.#scope = scope
9
12
  }
10
13
 
11
- /** Все сети. `enabled: false` — сеть описана, но не читается: вебхуков по ней нет */
14
+ /** Lists all networks. Transfers are monitored on networks with `enabled: true`. */
12
15
  async list(options?: RequestOptions): Promise<Network[]> {
13
- const r = await this.#http.request<{ items: Network[] }>({ method: 'GET', path: '/v1/networks', idempotent: true, options })
16
+ const r = await this.#scope.http.request<NetworkList>({ method: 'GET', path: '/v1/networks', idempotent: true, options })
14
17
  return r.items
15
18
  }
16
19
 
17
- /** Сеть по slug (`tron`) или CAIP-2 (`eip155:1`) */
20
+ /**
21
+ * Retrieves a network.
22
+ *
23
+ * @param network - Network slug (`tron`, `ethereum`) or CAIP-2 ID (`eip155:1`).
24
+ */
18
25
  get(network: string, options?: RequestOptions): Promise<Network> {
19
- return this.#http.request({ method: 'GET', path: `/v1/networks/${seg(network)}`, idempotent: true, options })
26
+ return this.#scope.http.request({ method: 'GET', path: `/v1/networks/${seg(network)}`, idempotent: true, options })
20
27
  }
21
28
  }
22
29
 
23
- /** Активы: нативные монеты и токены. Вебхуки приходят о переводах включённых активов не меньше порога `minAmount` */
30
+ /** Native coins and tokens. You are notified about transfers of enabled assets of at least `minAmount`. */
24
31
  export class AssetsResource {
25
- readonly #http: HttpClient
26
- constructor(http: HttpClient) {
27
- this.#http = http
32
+ readonly #scope: Scope
33
+
34
+ /** @internal */
35
+ constructor(scope: Scope) {
36
+ this.#scope = scope
28
37
  }
29
38
 
30
39
  /**
31
- * Активы — все или одной сети.
40
+ * Lists assets, optionally of one network.
32
41
  *
33
42
  * @example
34
43
  * ```ts
35
- * const usdt = (await monitoring.assets.list({ network: 'tron' })).find((a) => a.symbol === 'USDT')
36
- * formatUnits(webhook.tx.value, usdt!.decimals)
44
+ * const usdt = (await monitoring.assets.list({ network: 'tron' })).find((asset) => asset.symbol === 'USDT')
37
45
  * ```
38
46
  */
39
47
  async list(params: { network?: string } = {}, options?: RequestOptions): Promise<Asset[]> {
40
- const r = await this.#http.request<{ items: Asset[] }>({ method: 'GET', path: '/v1/assets', query: params, idempotent: true, options })
48
+ const r = await this.#scope.http.request<AssetList>({ method: 'GET', path: '/v1/assets', query: params, idempotent: true, options })
41
49
  return r.items
42
50
  }
43
51
 
52
+ /** Retrieves an asset by ID. */
44
53
  get(assetId: number, options?: RequestOptions): Promise<Asset> {
45
- return this.#http.request({ method: 'GET', path: `/v1/assets/${seg(assetId)}`, idempotent: true, options })
54
+ return this.#scope.http.request({ method: 'GET', path: `/v1/assets/${seg(assetId)}`, idempotent: true, options })
46
55
  }
47
56
  }
@@ -1,69 +1,82 @@
1
- import { seg, type HttpClient, type RequestOptions } from '../core/http.js'
2
- import { paginate, type Page } from '../core/pagination.js'
3
- import type { Delivery, DeliveryDetail, DeliveryStatus } from '../generated/types.js'
1
+ import { seg, type RequestOptions } from '../core/http.js'
2
+ import { paginate } from '../core/pagination.js'
3
+ import type { Scope } from '../core/scope.js'
4
+ import type { Delivery, DeliveryDetail, DeliveryPage, DeliveryStatus } from '../generated/types.js'
4
5
 
5
- /** Фильтры истории; все необязательны и сочетаются */
6
+ /** Filters for webhook deliveries. All are optional and can be combined. */
6
7
  export interface DeliveryFilter {
7
- /** `delivered` — вы ответили 2xx, `retrying` — будет повтор, `failed` — повторы исчерпаны */
8
+ /** `delivered`, `retrying` or `failed`. */
8
9
  status?: DeliveryStatus
9
- /** Сеть: slug или CAIP-2 */
10
+ /** Network slug or CAIP-2 ID. */
10
11
  network?: string
11
- /** Символ актива */
12
+ /** Asset symbol. */
12
13
  asset?: string
13
- /** Хеш транзакции */
14
+ /** Transaction hash. */
14
15
  tx?: string
15
- /** Отправитель или получатель */
16
+ /** Sender or recipient address. */
16
17
  address?: string
17
- /** Первая попытка не раньше (дата или время) */
18
+ /** First attempt at or after this time. */
18
19
  since?: Date | string
19
- /** Первая попытка раньше (не включая) */
20
+ /** First attempt before this time. */
20
21
  until?: Date | string
21
22
  }
22
23
 
24
+ /** Parameters of {@link DeliveriesResource.list}. */
23
25
  export interface ListDeliveriesParams extends DeliveryFilter {
24
- /** Размер страницы, до 500; по умолчанию 100 */
26
+ /** Page size, up to 500. Defaults to 100. */
25
27
  limit?: number
26
- /** Курсор: `next` прошлой страницы */
28
+ /** Cursor: `next` of the previous page. */
27
29
  before?: number
28
30
  }
29
31
 
30
- const iso = (v: Date | string | undefined) => (v instanceof Date ? v.toISOString() : v)
32
+ const iso = (value: Date | string | undefined) => (value instanceof Date ? value.toISOString() : value)
31
33
 
32
34
  /**
33
- * История вебхуков клиента: что, когда и куда отправлено, сколько было попыток и что вы ответили.
34
- * Повторы одного вебхука — одна запись. Хранится 90 дней.
35
+ * Every webhook sent to you: status, attempts and your endpoint's response. Retries of the same webhook are one
36
+ * delivery with an attempt count. Deliveries are kept for 90 days.
35
37
  */
36
38
  export class DeliveriesResource {
37
- readonly #http: HttpClient
38
- constructor(http: HttpClient) {
39
- this.#http = http
39
+ readonly #scope: Scope
40
+
41
+ /** @internal */
42
+ constructor(scope: Scope) {
43
+ this.#scope = scope
40
44
  }
41
45
 
42
46
  /**
43
- * Страница истории, новые сверху.
47
+ * Lists deliveries, newest first, one page at a time.
44
48
  *
45
49
  * @example
46
50
  * ```ts
47
- * const { items } = await monitoring.deliveries.list(clientId, { status: 'failed', since: new Date(Date.now() - 86_400_000) })
51
+ * const { items } = await monitoring.deliveries.list({ status: 'failed', since: new Date(Date.now() - 86_400_000) })
48
52
  * ```
49
53
  */
50
- list(clientId: string, params: ListDeliveriesParams = {}, options?: RequestOptions): Promise<Page<Delivery, number>> {
51
- return this.#http.request({
54
+ async list(params: ListDeliveriesParams = {}, options?: RequestOptions): Promise<DeliveryPage> {
55
+ return this.#scope.http.request({
52
56
  method: 'GET',
53
- path: `/v1/clients/${seg(clientId)}/deliveries`,
57
+ path: `${await this.#scope.clientPath()}/deliveries`,
54
58
  query: { ...params, since: iso(params.since), until: iso(params.until) },
55
59
  idempotent: true,
56
60
  options,
57
61
  })
58
62
  }
59
63
 
60
- /** Вся история по фильтру — от новых к старым, страница за страницей */
61
- iterate(clientId: string, filter: DeliveryFilter & { limit?: number } = {}, options?: RequestOptions): AsyncGenerator<Delivery, void, undefined> {
62
- return paginate<Delivery, number>((before) => this.list(clientId, { ...filter, before }, options))
64
+ /**
65
+ * Iterates over all deliveries matching the filter, newest first.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * for await (const delivery of monitoring.deliveries.iterate({ status: 'failed' })) {
70
+ * console.log(delivery.txId, delivery.attempts, delivery.lastHttpStatus ?? delivery.lastError)
71
+ * }
72
+ * ```
73
+ */
74
+ iterate(filter: DeliveryFilter & { limit?: number } = {}, options?: RequestOptions): AsyncGenerator<Delivery, void, undefined> {
75
+ return paginate<Delivery>((before) => this.list({ ...filter, before }, options))
63
76
  }
64
77
 
65
- /** Одна запись с URL последней попытки и точным телом запроса */
66
- get(clientId: string, deliveryId: number, options?: RequestOptions): Promise<DeliveryDetail> {
67
- return this.#http.request({ method: 'GET', path: `/v1/clients/${seg(clientId)}/deliveries/${seg(deliveryId)}`, idempotent: true, options })
78
+ /** Retrieves a delivery with the URL of the last attempt and the exact request body. */
79
+ async get(deliveryId: number, options?: RequestOptions): Promise<DeliveryDetail> {
80
+ return this.#scope.http.request({ method: 'GET', path: `${await this.#scope.clientPath()}/deliveries/${seg(deliveryId)}`, idempotent: true, options })
68
81
  }
69
82
  }
@@ -1,33 +1,34 @@
1
- import { seg, type HttpClient, type RequestOptions } from '../core/http.js'
1
+ import { seg, type RequestOptions } from '../core/http.js'
2
+ import type { Scope } from '../core/scope.js'
2
3
  import type { Transaction } from '../generated/types.js'
3
4
 
4
- /** Любая транзакция сети по хешу — не только те, о которых пришёл вебхук */
5
+ /** Any transaction on a supported network, by hash. */
5
6
  export class TransactionsResource {
6
- readonly #http: HttpClient
7
- constructor(http: HttpClient) {
8
- this.#http = http
7
+ readonly #scope: Scope
8
+
9
+ /** @internal */
10
+ constructor(scope: Scope) {
11
+ this.#scope = scope
9
12
  }
10
13
 
11
14
  /**
12
- * Статус, блок, подтверждения, финальность, комиссия и все переводы внутри транзакции; для ваших адресов —
13
- * направление и вебхуки, которые мы по ней отправили.
14
- *
15
- * `final: true` — транзакцию уже не отменить: в TRON блок солидифицирован (около минуты), в EVM —
16
- * финализирован (Ethereum — около 13 минут). Крупные суммы зачисляйте после `final`.
15
+ * Retrieves a transaction — not only the ones you were notified about: status, confirmations, finality, fee and
16
+ * every transfer inside it, plus your monitored addresses and the webhooks sent to you for it.
17
17
  *
18
- * Не больше 60 запросов в минуту на ключ; при превышении SDK подождёт и повторит.
18
+ * `final: true` means the transaction can no longer be reverted: on TRON the block is solidified (about 1 minute),
19
+ * on EVM networks it is finalized (about 13 minutes on Ethereum). Credit large amounts once the transaction is final.
19
20
  *
20
- * @param network - slug (`tron`, `ethereum`) или CAIP-2
21
- * @param hash - EVM — `0x` + 64 hex; TRON — 64 hex (можно с `0x`)
22
- * @throws {@link MonitoringApiError} 404 — такой транзакции в сети нет
21
+ * @param network - Network slug (`tron`, `ethereum`) or CAIP-2 ID.
22
+ * @param hash - EVM: `0x` followed by 64 hex characters. TRON: 64 hex characters.
23
+ * @throws {@link MonitoringApiError} `404` when the transaction does not exist on the network.
23
24
  *
24
25
  * @example
25
26
  * ```ts
26
- * const tx = await monitoring.transactions.get('tron', webhook.tx.id)
27
- * if (tx.status === 'success' && tx.final) credit(webhook)
27
+ * const tx = await monitoring.transactions.get('tron', event.tx.id)
28
+ * if (tx.status === 'success' && tx.final) await credit(event.tx)
28
29
  * ```
29
30
  */
30
31
  get(network: string, hash: string, options?: RequestOptions): Promise<Transaction> {
31
- return this.#http.request({ method: 'GET', path: `/v1/networks/${seg(network)}/transactions/${seg(hash)}`, idempotent: true, options })
32
+ return this.#scope.http.request({ method: 'GET', path: `/v1/networks/${seg(network)}/transactions/${seg(hash)}`, idempotent: true, options })
32
33
  }
33
34
  }
@@ -0,0 +1,36 @@
1
+ import type { RequestOptions } from '../core/http.js'
2
+ import type { Scope } from '../core/scope.js'
3
+ import type { TestWebhookRequest, TestWebhookResult } from '../generated/types.js'
4
+
5
+ /** Test your webhook endpoint. To receive webhooks, see {@link parseWebhook}. */
6
+ export class WebhooksResource {
7
+ readonly #scope: Scope
8
+
9
+ /** @internal */
10
+ constructor(scope: Scope) {
11
+ this.#scope = scope
12
+ }
13
+
14
+ /**
15
+ * Sends a signed test webhook to your webhook URL and returns the outcome, the signature and the exact body sent.
16
+ * Test webhooks have `"test": true` in the body and the `x-webhook-test: 1` header — do not credit them.
17
+ * Limited to 10 per minute.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const { delivery } = await monitoring.webhooks.sendTest({ asset: 'USDT' })
22
+ * if (!delivery.ok) console.error('Endpoint responded with', delivery.status ?? delivery.error)
23
+ * ```
24
+ */
25
+ async sendTest(params: TestWebhookRequest = {}, options?: RequestOptions): Promise<TestWebhookResult> {
26
+ return this.#scope.http.request({
27
+ method: 'POST',
28
+ path: `${await this.#scope.clientPath()}/webhooks/test`,
29
+ body: params,
30
+ idempotent: false,
31
+ // 502 means "your endpoint did not accept it": an outcome, not a failed request
32
+ acceptStatuses: [502],
33
+ options,
34
+ })
35
+ }
36
+ }
package/src/units.ts CHANGED
@@ -1,26 +1,26 @@
1
1
  /**
2
- * Суммы в API и вебхуках — строки в минимальных единицах (sun, wei; у USDT 6 знаков после запятой): uint256 не
3
- * помещается в `number`. Эти функции переводят их в привычный вид и обратно без потери точности.
4
- * Знаки после запятой актива — `decimals` из `monitoring.assets.list()`.
2
+ * Amounts in the API and in webhooks are decimal strings in the asset's smallest unit (sun, wei; USDT has 6 decimals),
3
+ * because uint256 values do not fit in a `number`. These helpers convert them without losing precision. The decimals
4
+ * of every asset are returned by `monitoring.assets.list()`.
5
5
  */
6
6
 
7
7
  const INTEGER = /^-?\d+$/
8
8
  const DECIMAL = /^(\d+)(?:\.(\d+))?$/
9
9
 
10
10
  /**
11
- * Минимальные единицы → строка в монетах.
11
+ * Converts an amount in the smallest unit to a decimal string.
12
12
  *
13
13
  * @example
14
14
  * ```ts
15
- * formatUnits('100000000', 6) // '100' — 100 USDT
16
- * formatUnits('1500000', 6) // '1.5'
17
- * formatUnits('10090000000000000', 18) // '0.01009' — ETH
15
+ * formatUnits('100000000', 6) // '100' — 100 USDT
16
+ * formatUnits('1500000', 6) // '1.5'
17
+ * formatUnits('10090000000000000', 18) // '0.01009' — ETH
18
18
  * ```
19
19
  */
20
20
  export function formatUnits(value: string | bigint, decimals: number): string {
21
21
  assertDecimals(decimals)
22
22
  const raw = typeof value === 'bigint' ? value.toString() : value.trim()
23
- if (!INTEGER.test(raw)) throw new RangeError(`formatUnits: integer in minimal units expected, got ${JSON.stringify(value)}`)
23
+ if (!INTEGER.test(raw)) throw new RangeError(`formatUnits: expected an integer amount in the smallest unit, got ${JSON.stringify(value)}`)
24
24
  const negative = raw.startsWith('-')
25
25
  const digits = (negative ? raw.slice(1) : raw).replace(/^0+(?=\d)/, '')
26
26
  if (decimals === 0) return (negative && digits !== '0' ? '-' : '') + digits
@@ -32,25 +32,25 @@ export function formatUnits(value: string | bigint, decimals: number): string {
32
32
  }
33
33
 
34
34
  /**
35
- * Строка в монетах → минимальные единицы.
35
+ * Converts a decimal string to an amount in the smallest unit.
36
36
  *
37
- * @throws RangeError — больше знаков после запятой, чем у актива, или не число
37
+ * @throws RangeError when the amount is not a non-negative decimal or has more decimal places than the asset.
38
38
  *
39
39
  * @example
40
40
  * ```ts
41
- * parseUnits('1.5', 6) // '1500000'
42
- * parseUnits('100', 6) // '100000000'
41
+ * parseUnits('1.5', 6) // '1500000'
42
+ * parseUnits('100', 6) // '100000000'
43
43
  * ```
44
44
  */
45
45
  export function parseUnits(amount: string, decimals: number): string {
46
46
  assertDecimals(decimals)
47
47
  const m = DECIMAL.exec(amount.trim())
48
- if (!m) throw new RangeError(`parseUnits: non-negative decimal expected, got ${JSON.stringify(amount)}`)
48
+ if (!m) throw new RangeError(`parseUnits: expected a non-negative decimal, got ${JSON.stringify(amount)}`)
49
49
  const fraction = m[2] ?? ''
50
50
  if (fraction.length > decimals) throw new RangeError(`parseUnits: ${amount} has more than ${decimals} decimal places`)
51
51
  return BigInt(m[1]! + fraction.padEnd(decimals, '0')).toString()
52
52
  }
53
53
 
54
54
  function assertDecimals(decimals: number): void {
55
- if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) throw new RangeError(`decimals: integer 0..255 expected, got ${decimals}`)
55
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) throw new RangeError(`decimals: expected an integer from 0 to 255, got ${decimals}`)
56
56
  }