@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
package/src/core/http.ts CHANGED
@@ -1,111 +1,112 @@
1
1
  import { SDK_VERSION } from '../version.js'
2
2
  import { apiErrorFrom, MonitoringConnectionError, MonitoringError, MonitoringTimeoutError } from './errors.js'
3
3
 
4
- /** Настройки одного запроса — последний необязательный аргумент любого метода */
4
+ /** Per-request options, accepted as the last argument of every method. */
5
5
  export interface RequestOptions {
6
- /** Отмена запроса; повторы тоже прекращаются */
6
+ /** Cancels the request, including pending retries. */
7
7
  signal?: AbortSignal
8
- /** Таймаут этого запроса, мс — вместо {@link ClientOptions.timeoutMs} */
8
+ /** Timeout for this request in milliseconds. Overrides {@link ClientOptions.timeoutMs}. */
9
9
  timeoutMs?: number
10
- /** Сколько раз повторить этот запрос — вместо {@link ClientOptions.maxRetries} */
10
+ /** Maximum number of retries for this request. Overrides {@link ClientOptions.maxRetries}. */
11
11
  maxRetries?: number
12
12
  }
13
13
 
14
- /** Настройки клиента */
14
+ /** Options of {@link MonitoringClient}. */
15
15
  export interface ClientOptions {
16
+ /** Your API key. Sent in the `x-api-key` header. */
17
+ apiKey: string
16
18
  /**
17
- * API-ключ: ключ клиента `ce_…` (адреса, история и транзакции своего клиента) или личный ключ `ce_user_…`
18
- * (всё, что вы можете в панели, с вашими правами). Уходит в заголовке `x-api-key`.
19
+ * Your client ID. Optional: when omitted, it is resolved from the API key on first use and cached.
19
20
  */
20
- apiKey?: string
21
- /** Токен сессии из {@link MonitoringClient.signIn} — вместо `apiKey`. Уходит в `Authorization: Bearer` */
22
- sessionToken?: string
23
- /** Адрес API. По умолчанию `https://monitoring.v3.finance` */
21
+ clientId?: string
22
+ /** API base URL. Defaults to `https://monitoring.v3.finance`. */
24
23
  baseUrl?: string
25
- /** Таймаут запроса, мс. По умолчанию 30 000 */
24
+ /** Request timeout in milliseconds. Defaults to `30000`. */
26
25
  timeoutMs?: number
27
26
  /**
28
- * Сколько раз повторять запрос после временной ошибки. По умолчанию 2.
27
+ * Maximum number of retries for transient errors. Defaults to `2`.
29
28
  *
30
- * Повторяются ответы 429 и 503 (запрос не был выполнен), а сбои сети и таймауты — только у запросов,
31
- * которые безопасно выполнить дважды (чтение, снятие и добавление адресов, отзыв ключа). Пауза — из
32
- * `Retry-After`, иначе экспоненциальная с разбросом.
29
+ * `429` and `503` responses are retried for every request, honoring `Retry-After`. Network errors and timeouts are
30
+ * retried only for requests that are safe to repeat. Backoff is exponential with jitter.
33
31
  */
34
32
  maxRetries?: number
35
- /** Своя реализация `fetch` — для тестов, прокси, логирования. По умолчанию глобальный `fetch` */
33
+ /** Custom `fetch` implementation, e.g. for proxies or instrumentation. Defaults to the global `fetch`. */
36
34
  fetch?: typeof fetch
37
- /** Дополнительные заголовки каждого запроса */
35
+ /** Additional headers sent with every request. */
38
36
  headers?: Record<string, string>
39
37
  }
40
38
 
41
39
  type Query = Record<string, string | number | boolean | null | undefined>
42
40
 
43
- /** Описание одного запроса к API */
41
+ /** @internal A request to the API. */
44
42
  export interface Call {
45
43
  method: 'GET' | 'POST' | 'PATCH' | 'DELETE'
46
44
  path: string
47
45
  query?: Query
48
46
  body?: unknown
49
- /** Можно ли повторить после сбоя сети: повтор не изменит результат */
47
+ /** The request can be repeated after a network failure without changing the outcome. */
50
48
  idempotent: boolean
51
- /** Статусы, которые возвращаются как данные, а не ошибка (тестовый вебхук: 502 — «не доставлен») */
49
+ /** Statuses returned as data rather than thrown. */
52
50
  acceptStatuses?: number[]
53
- /** false — запрос без учётных данных (вход) */
54
- auth?: boolean
55
51
  options?: RequestOptions | undefined
56
52
  }
57
53
 
58
54
  const DEFAULTS = { timeoutMs: 30_000, maxRetries: 2 }
59
55
  const MAX_RETRY_DELAY_MS = 60_000
60
56
 
61
- /** Транспорт SDK: адрес, авторизация, таймауты, повторы, разбор ошибок. Ресурсы ходят в API только через него */
57
+ /** @internal Transport: base URL, authentication, timeouts, retries and error mapping. */
62
58
  export class HttpClient {
63
59
  readonly baseUrl: string
64
- readonly #apiKey: string | undefined
65
- readonly #sessionToken: string | undefined
60
+ readonly #apiKey: string
66
61
  readonly #timeoutMs: number
67
62
  readonly #maxRetries: number
68
63
  readonly #fetch: typeof fetch
69
64
  readonly #headers: Record<string, string>
70
65
 
71
66
  constructor(options: ClientOptions, defaultBaseUrl: string) {
72
- if (options.apiKey && options.sessionToken) throw new MonitoringError('pass either apiKey or sessionToken, not both')
67
+ if (!options?.apiKey) throw new MonitoringError('apiKey is required')
73
68
  this.baseUrl = (options.baseUrl ?? defaultBaseUrl).replace(/\/+$/, '')
74
69
  this.#apiKey = options.apiKey
75
- this.#sessionToken = options.sessionToken
76
70
  this.#timeoutMs = options.timeoutMs ?? DEFAULTS.timeoutMs
77
71
  this.#maxRetries = options.maxRetries ?? DEFAULTS.maxRetries
78
72
  const f = options.fetch ?? globalThis.fetch
79
- if (typeof f !== 'function') throw new MonitoringError('fetch is not available: use Node.js 20+ or pass options.fetch')
73
+ if (typeof f !== 'function') throw new MonitoringError('fetch is not available: use Node.js 20 or later, or pass options.fetch')
80
74
  this.#fetch = f
81
75
  this.#headers = { ...options.headers }
82
76
  }
83
77
 
84
- /** Выполняет запрос и возвращает разобранный JSON ответа */
78
+ /** Sends a request and returns the parsed JSON response. */
85
79
  async request<T>(call: Call): Promise<T> {
86
80
  const url = this.#url(call.path, call.query)
87
- const headers = this.#headersFor(call)
81
+ const headers: Record<string, string> = {
82
+ accept: 'application/json',
83
+ 'user-agent': `@v3finance/monitoring-sdk/${SDK_VERSION}`,
84
+ ...this.#headers,
85
+ 'x-api-key': this.#apiKey,
86
+ }
87
+ if (call.body !== undefined) headers['content-type'] = 'application/json'
88
88
  const body = call.body === undefined ? undefined : JSON.stringify(call.body)
89
89
  const maxRetries = call.options?.maxRetries ?? this.#maxRetries
90
90
  const timeoutMs = call.options?.timeoutMs ?? this.#timeoutMs
91
+ const signal = call.options?.signal
91
92
 
92
93
  for (let attempt = 0; ; attempt++) {
93
94
  let res: { status: number; headers: Headers; data: unknown }
94
95
  try {
95
- res = await this.#send(url, { method: call.method, headers, body }, timeoutMs, call.options?.signal)
96
- } catch (e) {
97
- if (call.options?.signal?.aborted) throw call.options.signal.reason
96
+ res = await this.#send(url, { method: call.method, headers, body }, timeoutMs, signal)
97
+ } catch (error) {
98
+ if (signal?.aborted) throw signal.reason
98
99
  if (call.idempotent && attempt < maxRetries) {
99
- await sleep(backoff(attempt), call.options?.signal)
100
+ await sleep(backoff(attempt), signal)
100
101
  continue
101
102
  }
102
- throw e
103
+ throw error
103
104
  }
104
105
  if ((res.status >= 200 && res.status < 300) || call.acceptStatuses?.includes(res.status)) return res.data as T
105
106
  const error = apiErrorFrom(res.status, res.data, res.headers.get('cf-ray') ?? undefined)
106
- // 429 и 503: запрос не выполнен — повтор безопасен для любого метода
107
+ // 429 and 503 mean the request was not processed, so retrying is safe for any method
107
108
  if (error.retryable && attempt < maxRetries) {
108
- await sleep(retryAfter(res.headers) ?? backoff(attempt), call.options?.signal)
109
+ await sleep(retryAfter(res.headers) ?? backoff(attempt), signal)
109
110
  continue
110
111
  }
111
112
  throw error
@@ -118,18 +119,7 @@ export class HttpClient {
118
119
  return url.toString()
119
120
  }
120
121
 
121
- #headersFor(call: Call): Record<string, string> {
122
- const headers: Record<string, string> = { accept: 'application/json', 'user-agent': `@v3finance/monitoring-sdk/${SDK_VERSION}`, ...this.#headers }
123
- if (call.body !== undefined) headers['content-type'] = 'application/json'
124
- if (call.auth !== false) {
125
- if (this.#apiKey) headers['x-api-key'] = this.#apiKey
126
- else if (this.#sessionToken) headers.authorization = `Bearer ${this.#sessionToken}`
127
- else throw new MonitoringError('credentials required: pass apiKey or sessionToken to MonitoringClient')
128
- }
129
- return headers
130
- }
131
-
132
- /** Один запрос с таймаутом на всё — и соединение, и чтение тела */
122
+ /** One attempt, with a single timeout covering both the connection and the response body. */
133
123
  async #send(url: string, init: RequestInit, timeoutMs: number, signal: AbortSignal | undefined) {
134
124
  const controller = new AbortController()
135
125
  const timer = setTimeout(() => controller.abort(new MonitoringTimeoutError(timeoutMs)), timeoutMs)
@@ -140,11 +130,11 @@ export class HttpClient {
140
130
  const res = await this.#fetch(url, { ...init, signal: controller.signal })
141
131
  const text = await res.text()
142
132
  return { status: res.status, headers: res.headers, data: text ? parseJson(text) : undefined }
143
- } catch (e) {
133
+ } catch (error) {
144
134
  const reason: unknown = controller.signal.reason
145
135
  if (reason instanceof MonitoringTimeoutError) throw reason
146
136
  if (signal?.aborted) throw signal.reason
147
- throw new MonitoringConnectionError(`request to ${new URL(url).origin} failed: ${(e as Error).message}`, { cause: e })
137
+ throw new MonitoringConnectionError(`request to ${new URL(url).origin} failed: ${(error as Error).message}`, { cause: error })
148
138
  } finally {
149
139
  clearTimeout(timer)
150
140
  signal?.removeEventListener('abort', onAbort)
@@ -160,12 +150,12 @@ function parseJson(text: string): unknown {
160
150
  }
161
151
  }
162
152
 
163
- /** Экспоненциальная пауза с разбросом: ~0,5 с, 1 с, 2 с… не больше 8 с */
153
+ /** Exponential backoff with jitter: ~0.5 s, 1 s, 2 s… capped at 8 s. */
164
154
  function backoff(attempt: number): number {
165
155
  return Math.min(8_000, 500 * 2 ** attempt) * (0.5 + Math.random() / 2)
166
156
  }
167
157
 
168
- /** Пауза из заголовка Retry-After: секунды или дата */
158
+ /** Delay from a `Retry-After` header: seconds or an HTTP date. */
169
159
  function retryAfter(headers: Headers): number | undefined {
170
160
  const raw = headers.get('retry-after')
171
161
  if (!raw) return undefined
@@ -189,5 +179,5 @@ function sleep(ms: number, signal: AbortSignal | undefined): Promise<void> {
189
179
  })
190
180
  }
191
181
 
192
- /** Сегмент пути: id клиента, хеш и т. п. */
182
+ /** @internal Encodes a path segment. */
193
183
  export const seg = (value: string | number) => encodeURIComponent(String(value))
@@ -1,17 +1,16 @@
1
1
  /**
2
- * Страница списка. `next` — курсор следующей страницы; `null` — страниц больше нет.
2
+ * A page of results. `next` is the cursor of the next page; `null` when there are no more pages.
3
3
  *
4
- * @typeParam T - элемент списка
5
- * @typeParam Cursor - тип курсора: строка (клиенты) или число (адреса, история)
4
+ * @typeParam T - Item type.
6
5
  */
7
- export interface Page<T, Cursor = string | number> {
6
+ export interface Page<T> {
8
7
  items: T[]
9
- next: Cursor | null
8
+ next: number | null
10
9
  }
11
10
 
12
- /** Обходит все страницы по порядку, запрашивая следующую, только когда закончилась текущая */
13
- export async function* paginate<T, Cursor>(fetchPage: (cursor: Cursor | undefined) => Promise<Page<T, Cursor>>): AsyncGenerator<T, void, undefined> {
14
- let cursor: Cursor | undefined
11
+ /** @internal Yields every item, fetching the next page only when the current one is exhausted. */
12
+ export async function* paginate<T>(fetchPage: (cursor: number | undefined) => Promise<Page<T>>): AsyncGenerator<T, void, undefined> {
13
+ let cursor: number | undefined
15
14
  for (;;) {
16
15
  const page = await fetchPage(cursor)
17
16
  yield* page.items
@@ -0,0 +1,8 @@
1
+ import type { HttpClient } from './http.js'
2
+
3
+ /** @internal What resources share: the transport and the client ID of the API key. */
4
+ export interface Scope {
5
+ http: HttpClient
6
+ /** Path segment of the client: resolved from the API key once, then cached. */
7
+ clientPath: () => Promise<string>
8
+ }