@v3finance/core-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.
- package/CHANGELOG.md +397 -7
- package/README.md +215 -72
- package/dist/cjs/client.d.ts +67 -22
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +38 -2
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/core/errors.d.ts +30 -10
- package/dist/cjs/core/errors.d.ts.map +1 -1
- package/dist/cjs/core/errors.js +47 -15
- package/dist/cjs/core/errors.js.map +1 -1
- package/dist/cjs/core/gated.d.ts +21 -3
- package/dist/cjs/core/gated.d.ts.map +1 -1
- package/dist/cjs/core/gated.js +12 -3
- package/dist/cjs/core/gated.js.map +1 -1
- package/dist/cjs/core/http.d.ts +12 -8
- package/dist/cjs/core/http.d.ts.map +1 -1
- package/dist/cjs/core/http.js +130 -40
- package/dist/cjs/core/http.js.map +1 -1
- package/dist/cjs/core/pagination.d.ts +28 -1
- package/dist/cjs/core/pagination.d.ts.map +1 -1
- package/dist/cjs/core/pagination.js +36 -1
- package/dist/cjs/core/pagination.js.map +1 -1
- package/dist/cjs/generated/operations.js +1 -1
- package/dist/cjs/generated/types.d.ts +948 -172
- package/dist/cjs/generated/types.d.ts.map +1 -1
- package/dist/cjs/index.d.ts +32 -8
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/resources/addresses.d.ts +22 -8
- package/dist/cjs/resources/addresses.d.ts.map +1 -1
- package/dist/cjs/resources/addresses.js +7 -1
- package/dist/cjs/resources/addresses.js.map +1 -1
- package/dist/cjs/resources/admin.d.ts +150 -25
- package/dist/cjs/resources/admin.d.ts.map +1 -1
- package/dist/cjs/resources/admin.js +94 -23
- package/dist/cjs/resources/admin.js.map +1 -1
- package/dist/cjs/resources/approvals.d.ts +46 -5
- package/dist/cjs/resources/approvals.d.ts.map +1 -1
- package/dist/cjs/resources/approvals.js +27 -4
- package/dist/cjs/resources/approvals.js.map +1 -1
- package/dist/cjs/resources/balances.d.ts +152 -16
- package/dist/cjs/resources/balances.d.ts.map +1 -1
- package/dist/cjs/resources/balances.js +9 -5
- package/dist/cjs/resources/balances.js.map +1 -1
- package/dist/cjs/resources/categories.d.ts +93 -21
- package/dist/cjs/resources/categories.d.ts.map +1 -1
- package/dist/cjs/resources/categories.js +65 -18
- package/dist/cjs/resources/categories.js.map +1 -1
- package/dist/cjs/resources/counterparties.d.ts +26 -3
- package/dist/cjs/resources/counterparties.d.ts.map +1 -1
- package/dist/cjs/resources/counterparties.js +17 -0
- package/dist/cjs/resources/counterparties.js.map +1 -1
- package/dist/cjs/resources/folders.d.ts +51 -10
- package/dist/cjs/resources/folders.d.ts.map +1 -1
- package/dist/cjs/resources/folders.js +36 -5
- package/dist/cjs/resources/folders.js.map +1 -1
- package/dist/cjs/resources/history.d.ts +20 -2
- package/dist/cjs/resources/history.d.ts.map +1 -1
- package/dist/cjs/resources/history.js.map +1 -1
- package/dist/cjs/resources/me.d.ts +27 -4
- package/dist/cjs/resources/me.d.ts.map +1 -1
- package/dist/cjs/resources/me.js +22 -1
- package/dist/cjs/resources/me.js.map +1 -1
- package/dist/cjs/resources/members.d.ts +19 -4
- package/dist/cjs/resources/members.d.ts.map +1 -1
- package/dist/cjs/resources/members.js +15 -6
- package/dist/cjs/resources/members.js.map +1 -1
- package/dist/cjs/resources/operations.d.ts +64 -8
- package/dist/cjs/resources/operations.d.ts.map +1 -1
- package/dist/cjs/resources/operations.js +26 -2
- package/dist/cjs/resources/operations.js.map +1 -1
- package/dist/cjs/resources/registries.d.ts +87 -9
- package/dist/cjs/resources/registries.d.ts.map +1 -1
- package/dist/cjs/resources/registries.js +54 -11
- package/dist/cjs/resources/registries.js.map +1 -1
- package/dist/cjs/resources/reports.d.ts +300 -41
- package/dist/cjs/resources/reports.d.ts.map +1 -1
- package/dist/cjs/resources/reports.js +135 -7
- package/dist/cjs/resources/reports.js.map +1 -1
- package/dist/cjs/resources/sharing.d.ts +61 -10
- package/dist/cjs/resources/sharing.d.ts.map +1 -1
- package/dist/cjs/resources/sharing.js +51 -4
- package/dist/cjs/resources/sharing.js.map +1 -1
- package/dist/cjs/resources/transfers.d.ts +26 -4
- package/dist/cjs/resources/transfers.d.ts.map +1 -1
- package/dist/cjs/resources/transfers.js +23 -1
- package/dist/cjs/resources/transfers.js.map +1 -1
- package/dist/cjs/resources/types.d.ts +20 -0
- package/dist/cjs/resources/types.d.ts.map +1 -1
- package/dist/cjs/units.d.ts.map +1 -1
- package/dist/cjs/units.js +18 -2
- package/dist/cjs/units.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +67 -22
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +38 -2
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/core/errors.d.ts +30 -10
- package/dist/esm/core/errors.d.ts.map +1 -1
- package/dist/esm/core/errors.js +47 -15
- package/dist/esm/core/errors.js.map +1 -1
- package/dist/esm/core/gated.d.ts +21 -3
- package/dist/esm/core/gated.d.ts.map +1 -1
- package/dist/esm/core/gated.js +12 -3
- package/dist/esm/core/gated.js.map +1 -1
- package/dist/esm/core/http.d.ts +12 -8
- package/dist/esm/core/http.d.ts.map +1 -1
- package/dist/esm/core/http.js +131 -41
- package/dist/esm/core/http.js.map +1 -1
- package/dist/esm/core/pagination.d.ts +28 -1
- package/dist/esm/core/pagination.d.ts.map +1 -1
- package/dist/esm/core/pagination.js +36 -1
- package/dist/esm/core/pagination.js.map +1 -1
- package/dist/esm/generated/operations.js +1 -1
- package/dist/esm/generated/types.d.ts +948 -172
- package/dist/esm/generated/types.d.ts.map +1 -1
- package/dist/esm/index.d.ts +32 -8
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +17 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/resources/addresses.d.ts +22 -8
- package/dist/esm/resources/addresses.d.ts.map +1 -1
- package/dist/esm/resources/addresses.js +7 -1
- package/dist/esm/resources/addresses.js.map +1 -1
- package/dist/esm/resources/admin.d.ts +150 -25
- package/dist/esm/resources/admin.d.ts.map +1 -1
- package/dist/esm/resources/admin.js +94 -23
- package/dist/esm/resources/admin.js.map +1 -1
- package/dist/esm/resources/approvals.d.ts +46 -5
- package/dist/esm/resources/approvals.d.ts.map +1 -1
- package/dist/esm/resources/approvals.js +27 -4
- package/dist/esm/resources/approvals.js.map +1 -1
- package/dist/esm/resources/balances.d.ts +152 -16
- package/dist/esm/resources/balances.d.ts.map +1 -1
- package/dist/esm/resources/balances.js +9 -5
- package/dist/esm/resources/balances.js.map +1 -1
- package/dist/esm/resources/categories.d.ts +93 -21
- package/dist/esm/resources/categories.d.ts.map +1 -1
- package/dist/esm/resources/categories.js +65 -18
- package/dist/esm/resources/categories.js.map +1 -1
- package/dist/esm/resources/counterparties.d.ts +26 -3
- package/dist/esm/resources/counterparties.d.ts.map +1 -1
- package/dist/esm/resources/counterparties.js +17 -0
- package/dist/esm/resources/counterparties.js.map +1 -1
- package/dist/esm/resources/folders.d.ts +51 -10
- package/dist/esm/resources/folders.d.ts.map +1 -1
- package/dist/esm/resources/folders.js +36 -5
- package/dist/esm/resources/folders.js.map +1 -1
- package/dist/esm/resources/history.d.ts +20 -2
- package/dist/esm/resources/history.d.ts.map +1 -1
- package/dist/esm/resources/history.js.map +1 -1
- package/dist/esm/resources/me.d.ts +27 -4
- package/dist/esm/resources/me.d.ts.map +1 -1
- package/dist/esm/resources/me.js +22 -1
- package/dist/esm/resources/me.js.map +1 -1
- package/dist/esm/resources/members.d.ts +19 -4
- package/dist/esm/resources/members.d.ts.map +1 -1
- package/dist/esm/resources/members.js +15 -6
- package/dist/esm/resources/members.js.map +1 -1
- package/dist/esm/resources/operations.d.ts +64 -8
- package/dist/esm/resources/operations.d.ts.map +1 -1
- package/dist/esm/resources/operations.js +26 -2
- package/dist/esm/resources/operations.js.map +1 -1
- package/dist/esm/resources/registries.d.ts +87 -9
- package/dist/esm/resources/registries.d.ts.map +1 -1
- package/dist/esm/resources/registries.js +54 -11
- package/dist/esm/resources/registries.js.map +1 -1
- package/dist/esm/resources/reports.d.ts +300 -41
- package/dist/esm/resources/reports.d.ts.map +1 -1
- package/dist/esm/resources/reports.js +135 -7
- package/dist/esm/resources/reports.js.map +1 -1
- package/dist/esm/resources/sharing.d.ts +61 -10
- package/dist/esm/resources/sharing.d.ts.map +1 -1
- package/dist/esm/resources/sharing.js +51 -4
- package/dist/esm/resources/sharing.js.map +1 -1
- package/dist/esm/resources/transfers.d.ts +26 -4
- package/dist/esm/resources/transfers.d.ts.map +1 -1
- package/dist/esm/resources/transfers.js +23 -1
- package/dist/esm/resources/transfers.js.map +1 -1
- package/dist/esm/resources/types.d.ts +20 -0
- package/dist/esm/resources/types.d.ts.map +1 -1
- package/dist/esm/units.d.ts.map +1 -1
- package/dist/esm/units.js +18 -2
- package/dist/esm/units.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/concepts.md +191 -0
- package/docs/errors.md +471 -0
- package/package.json +7 -6
- package/src/client.ts +89 -25
- package/src/core/errors.ts +49 -15
- package/src/core/gated.ts +22 -4
- package/src/core/http.ts +156 -46
- package/src/core/pagination.ts +38 -1
- package/src/generated/operations.ts +1 -1
- package/src/generated/types.ts +983 -173
- package/src/index.ts +64 -7
- package/src/resources/addresses.ts +23 -8
- package/src/resources/admin.ts +186 -33
- package/src/resources/approvals.ts +55 -9
- package/src/resources/balances.ts +186 -14
- package/src/resources/categories.ts +125 -30
- package/src/resources/counterparties.ts +23 -6
- package/src/resources/folders.ts +74 -18
- package/src/resources/history.ts +27 -2
- package/src/resources/me.ts +30 -5
- package/src/resources/members.ts +26 -7
- package/src/resources/operations.ts +73 -8
- package/src/resources/registries.ts +90 -11
- package/src/resources/reports.ts +348 -67
- package/src/resources/sharing.ts +71 -18
- package/src/resources/transfers.ts +33 -5
- package/src/resources/types.ts +20 -0
- package/src/units.ts +19 -2
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -5,25 +5,43 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@v3finance/core-sdk)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
|
-
The official TypeScript SDK for the V3 Finance Core API
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
8
|
+
The official TypeScript SDK for the V3 Finance Core API — custodial addresses, transfers under
|
|
9
|
+
policy control, approvals, shared access, operation labelling and reports.
|
|
10
|
+
|
|
11
|
+
- **Complete.** Every endpoint a client calls — 157 of the API's 171 operations, grouped into
|
|
12
|
+
resources, with the administrative surface marked. The fourteen left out are named under
|
|
13
|
+
[Resources](#resources). A test asserts that each of the rest is reachable, so an endpoint added
|
|
14
|
+
to the API shows up as a build failure rather than as a gap nobody noticed.
|
|
15
|
+
- **Honest about approvals.** An operation can be applied, queued for signatures or replayed, and
|
|
16
|
+
the response bodies are identical — the SDK tells them apart, so your interface does not report
|
|
17
|
+
"sent" when the funds are merely reserved.
|
|
18
|
+
- **Money that cannot silently lose precision.** Smallest-unit strings and BigInt arithmetic, with
|
|
19
|
+
no `toNumber`.
|
|
20
|
+
- **Pluggable authentication.** Browser cookie, native cookie jar or bearer token; the package
|
|
21
|
+
stores no credentials.
|
|
22
|
+
- **Fully typed, zero dependencies**, ESM and CommonJS. Runs on Node.js, Bun, Deno, Cloudflare
|
|
23
|
+
Workers and React Native.
|
|
24
|
+
|
|
25
|
+
## Contents
|
|
26
|
+
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [Quick start](#quick-start)
|
|
29
|
+
- [Configuration](#configuration)
|
|
30
|
+
- [Three things to know first](#three-things-to-know-first)
|
|
31
|
+
- [Resources](#resources)
|
|
32
|
+
- [Amounts](#amounts)
|
|
33
|
+
- [Error handling](#error-handling)
|
|
34
|
+
- [Pagination](#pagination)
|
|
35
|
+
- [Administrative operations](#administrative-operations)
|
|
36
|
+
- [TanStack Query](#tanstack-query)
|
|
37
|
+
- [TypeScript](#typescript)
|
|
38
|
+
- [Requirements](#requirements)
|
|
39
|
+
- [Staying in sync with the API](#staying-in-sync-with-the-api)
|
|
40
|
+
- [Further reading](#further-reading)
|
|
41
|
+
- [Versioning](#versioning)
|
|
42
|
+
- [License](#license)
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
27
45
|
|
|
28
46
|
```bash
|
|
29
47
|
npm install @v3finance/core-sdk
|
|
@@ -43,23 +61,52 @@ const core = new CoreClient({
|
|
|
43
61
|
const page = await core.vault('my-vault').addresses.list({ limit: 20 })
|
|
44
62
|
```
|
|
45
63
|
|
|
46
|
-
##
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
new CoreClient({
|
|
68
|
+
baseUrl, // defaults to the production API
|
|
69
|
+
getAuthHeaders, // called before every request
|
|
70
|
+
credentials, // 'include' in a browser served from another origin
|
|
71
|
+
onTokenRotated, // required in bearer mode — see below
|
|
72
|
+
onUnauthorized, // the session is gone: sign out, route to login
|
|
73
|
+
fetch, // your own, for tests or older runtimes
|
|
74
|
+
timeoutMs, // default 60s; money paths call out to other services
|
|
75
|
+
headers, // added to every request
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Sign-in is a magic link sent by email, and signing up is possible only on an administrator's
|
|
80
|
+
invitation. The session then travels in one of three ways:
|
|
81
|
+
|
|
82
|
+
| Runtime | How |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| Browser | `credentials: 'include'`, cookie set by the server |
|
|
85
|
+
| React Native | your own cookie jar, returned from `getAuthHeaders` |
|
|
86
|
+
| Anywhere | `Authorization: Bearer <session-token>` |
|
|
87
|
+
|
|
88
|
+
In bearer mode **`onTokenRotated` is required**: the server extends a session through the
|
|
89
|
+
`set-auth-token` header, and a client that does not store the new token is signed out at the next
|
|
90
|
+
rotation.
|
|
91
|
+
|
|
92
|
+
## Three things to know first
|
|
47
93
|
|
|
48
94
|
### 1. `vault(code)` is an expression, not state
|
|
49
95
|
|
|
50
96
|
```ts
|
|
51
97
|
core.vault('alpha').addresses.list()
|
|
52
|
-
core.vault('beta').
|
|
98
|
+
core.vault('beta').transfers.send(…)
|
|
53
99
|
```
|
|
54
100
|
|
|
55
|
-
There is deliberately no "current vault". The code is visible
|
|
56
|
-
work with several vaults at once, and switching the active vault at runtime cannot leave a
|
|
57
|
-
pointing at the wrong one. Build a scope where you use it; do not store it.
|
|
101
|
+
There is deliberately no "current vault". The code is visible at every call site, an administrator
|
|
102
|
+
can work with several vaults at once, and switching the active vault at runtime cannot leave a
|
|
103
|
+
stale scope pointing at the wrong one. Build a scope where you use it; do not store it.
|
|
58
104
|
|
|
59
105
|
### 2. An operation may not execute — it may go for approval
|
|
60
106
|
|
|
61
|
-
Transfers
|
|
62
|
-
branches **must** be distinguished: the response body is
|
|
107
|
+
Transfers, address creation and several other operations pass through the policy engine. The
|
|
108
|
+
result is `Gated`, and the three branches **must** be distinguished: the response body is the same
|
|
109
|
+
in all of them, only the status differs.
|
|
63
110
|
|
|
64
111
|
```ts
|
|
65
112
|
const result = await core.vault(code).transfers.send({
|
|
@@ -71,41 +118,88 @@ const result = await core.vault(code).transfers.send({
|
|
|
71
118
|
})
|
|
72
119
|
|
|
73
120
|
switch (result.outcome) {
|
|
74
|
-
case 'applied':
|
|
75
|
-
|
|
76
|
-
case '
|
|
77
|
-
return toast('Awaiting signatures — the funds are reserved')
|
|
78
|
-
case 'replayed':
|
|
79
|
-
return toast('This transfer was already sent')
|
|
121
|
+
case 'applied': return toast('Transfer sent')
|
|
122
|
+
case 'pending': return toast('Awaiting signatures — the funds are reserved')
|
|
123
|
+
case 'replayed': return toast('This transfer was already sent')
|
|
80
124
|
}
|
|
81
125
|
```
|
|
82
126
|
|
|
83
127
|
Reporting "sent" on `pending` tells a person their money has gone when it is frozen.
|
|
84
128
|
|
|
85
|
-
**Always pass `idempotencyKey`** and keep it until you have an answer: after a
|
|
86
|
-
way to retry without sending money twice.
|
|
129
|
+
**Always pass `idempotencyKey`** on a transfer and keep it until you have an answer: after a
|
|
130
|
+
timeout it is the only way to retry without sending money twice. The key is read from the request
|
|
131
|
+
**body** — the `idempotency-key` header is not sent at all. It used to be, and in a browser it broke the
|
|
132
|
+
request outright: the API's CORS preflight allows only `Content-Type` and `Authorization`.
|
|
87
133
|
|
|
88
134
|
### 3. Amounts are strings, never numbers
|
|
89
135
|
|
|
90
|
-
Balances arrive in smallest units. `float64` loses precision above 2⁵³, and production balances go higher.
|
|
91
|
-
|
|
92
136
|
```ts
|
|
93
137
|
import { amount, parseDecimal, format, add } from '@v3finance/core-sdk'
|
|
94
138
|
|
|
95
139
|
const balance = amount(address.balance ?? '0', 6, 'usdt')
|
|
96
|
-
format(balance, { withAsset: true })
|
|
140
|
+
format(balance, { withAsset: true }) // "1,500.00 USDT"
|
|
97
141
|
add(balance, parseDecimal('2.5', 6))
|
|
98
142
|
```
|
|
99
143
|
|
|
100
|
-
There is no `toNumber`, on purpose — it would be a convenient door into the
|
|
144
|
+
There is no `toNumber`, on purpose — it would be a convenient door into the precision loss this
|
|
145
|
+
layer exists to prevent. `float64` breaks above 2⁵³ and real balances go higher.
|
|
101
146
|
|
|
102
147
|
**`balance: null` is not zero.** It means the address has no ledger account yet: nothing has ever
|
|
103
|
-
arrived. "No account" and "empty account" deserve different wording
|
|
148
|
+
arrived. "No account" and "empty account" deserve different wording.
|
|
149
|
+
|
|
150
|
+
## Resources
|
|
151
|
+
|
|
152
|
+
Scoped to a vault — `core.vault(code).…`:
|
|
153
|
+
|
|
154
|
+
| Resource | What it covers |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `addresses` | list, detail, create (gated), edit metadata |
|
|
157
|
+
| `transfers` | send (gated), detail, vote, cancel, reconcile (admin) |
|
|
158
|
+
| `history` | the unified feed of operations; direction is a filter, not an endpoint |
|
|
159
|
+
| `balances` | balances, a point in time, the raw ledger |
|
|
160
|
+
| `approvals` | requests, detail, vote, cancel, composite chains |
|
|
161
|
+
| `counterparties`, `counterpartyGroups` | the directory and its groups — every mutation is gated |
|
|
162
|
+
| `sharing` | sharing an address, participants, shared by me and with me |
|
|
163
|
+
| `folders` | address folders, their grants and participants |
|
|
164
|
+
| `members` | the directory, resolve by username, user groups |
|
|
165
|
+
| `categories` | the labelling schema and the values on operations |
|
|
166
|
+
| `operations` | metadata, links, status history, deposits, approval relations |
|
|
167
|
+
| `reports` | reports, templates, snapshots, exports |
|
|
168
|
+
|
|
169
|
+
At the root — `core.…`:
|
|
170
|
+
|
|
171
|
+
| Resource | What it covers |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `me` | profile, email binding, push tokens, allowed segments |
|
|
174
|
+
| `vaults` | list, detail, create, derivation-path build and parse |
|
|
175
|
+
| `registries` | networks, tokens, rates, policy preflight, AML |
|
|
176
|
+
| `auth` | request a magic link |
|
|
177
|
+
| `admin` | policies, policy groups, users, invitations, visibility grants, seats |
|
|
178
|
+
| `health()` | liveness, no authentication |
|
|
104
179
|
|
|
105
|
-
|
|
180
|
+
Fourteen of the API's 171 operations are deliberately absent. Two are not a client's business:
|
|
181
|
+
`/welcome` is a browser landing page the API redirects to, and `/webhooks/wallet` is the inbound
|
|
182
|
+
channel for the wallet service. The other twelve are the `/address-groups/*` paths — the older
|
|
183
|
+
prefix of the endpoints `folders` already serves under `/address-folders`. One entity, two
|
|
184
|
+
prefixes; see [docs/concepts.md](./docs/concepts.md#5-shared-access-roles-out-flags-in).
|
|
106
185
|
|
|
107
|
-
|
|
108
|
-
|
|
186
|
+
## Amounts
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
amount(raw, decimals, asset?) // from smallest units
|
|
190
|
+
parseDecimal('1.5', 6) // from human input; extra digits are refused, not rounded
|
|
191
|
+
toDecimalString(a) // '1.5'
|
|
192
|
+
format(a, { withAsset: true }) // '1.50 USDT', grouped by locale
|
|
193
|
+
add(a, b) · sub(a, b) · compare(a, b) · isZero(a) · isNegative(a)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Adding different assets throws, as does adding different precision: the result would be a
|
|
197
|
+
meaningless number rather than a missing one.
|
|
198
|
+
|
|
199
|
+
## Error handling
|
|
200
|
+
|
|
201
|
+
Every failure is a `CoreError` and always carries a `code` — sent by the server, or derived from
|
|
202
|
+
the HTTP status. Branch on `code`, display `message`.
|
|
109
203
|
|
|
110
204
|
```ts
|
|
111
205
|
import { hasCode, isCoreError } from '@v3finance/core-sdk'
|
|
@@ -121,27 +215,16 @@ try {
|
|
|
121
215
|
```
|
|
122
216
|
|
|
123
217
|
`kind` separates `http` (the server answered), `network` (the request never left), `timeout`,
|
|
124
|
-
`aborted` and `parse`.
|
|
218
|
+
`aborted` and `parse`. The full code reference is in [docs/errors.md](./docs/errors.md).
|
|
125
219
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
| Runtime | How |
|
|
131
|
-
| --- | --- |
|
|
132
|
-
| Browser | `credentials: 'include'`, the cookie is set by the server |
|
|
133
|
-
| React Native | your own cookie jar, returned from `getAuthHeaders` |
|
|
134
|
-
| Anywhere | `Authorization: Bearer <session-token>` |
|
|
135
|
-
|
|
136
|
-
In bearer mode **`onTokenRotated` is required**: the server extends a session through the
|
|
137
|
-
`set-auth-token` header, and a client that does not store the new token is signed out at the next
|
|
138
|
-
rotation.
|
|
139
|
-
|
|
140
|
-
Sign-in is a magic link sent by email. Signing up is possible only on an administrator's invitation.
|
|
220
|
+
Batch endpoints — sharing an address with several subjects — need one extra habit: **read
|
|
221
|
+
`results[]` whenever the call succeeds**. Each subject is handled independently and a partial
|
|
222
|
+
failure still answers 201; 400 comes only when every subject was rejected.
|
|
141
223
|
|
|
142
224
|
## Pagination
|
|
143
225
|
|
|
144
|
-
Lists return `{ items, nextCursor }`. The cursor is opaque — do not parse it
|
|
226
|
+
Lists return `{ items, nextCursor }`. The cursor is opaque — do not parse it, and treat
|
|
227
|
+
`nextCursor === null` as the only end-of-list signal.
|
|
145
228
|
|
|
146
229
|
```ts
|
|
147
230
|
import { paginate } from '@v3finance/core-sdk'
|
|
@@ -151,7 +234,10 @@ for await (const address of paginate((p) => core.vault(code).addresses.list(p)))
|
|
|
151
234
|
}
|
|
152
235
|
```
|
|
153
236
|
|
|
154
|
-
|
|
237
|
+
Not every list is paginated: report templates and snapshots return what they have, and snapshots
|
|
238
|
+
cut off at a limit rather than continuing with a cursor.
|
|
239
|
+
|
|
240
|
+
## Administrative operations
|
|
155
241
|
|
|
156
242
|
The package covers the whole API, administrative endpoints included. Access level is available
|
|
157
243
|
programmatically:
|
|
@@ -159,31 +245,88 @@ programmatically:
|
|
|
159
245
|
```ts
|
|
160
246
|
import { OPERATIONS, isAdminOperation } from '@v3finance/core-sdk'
|
|
161
247
|
|
|
162
|
-
isAdminOperation('POST /api/v1/policies')
|
|
248
|
+
isAdminOperation('POST /api/v1/policies') // true
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
A method existing here grants nothing: the server decides from your session, and a
|
|
252
|
+
non-administrator receives 403.
|
|
253
|
+
|
|
254
|
+
## TanStack Query
|
|
255
|
+
|
|
256
|
+
Cache keys live in a separate entry point, so the package itself stays dependency-free and the
|
|
257
|
+
keys match between applications — otherwise invalidation in one does not match the other, and the
|
|
258
|
+
mismatch surfaces as "the data did not refresh".
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import { queryKeys } from '@v3finance/core-sdk/query-keys'
|
|
262
|
+
|
|
263
|
+
useQuery({
|
|
264
|
+
queryKey: queryKeys.addresses(code, filters),
|
|
265
|
+
queryFn: () => core.vault(code).addresses.list(filters),
|
|
266
|
+
})
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## TypeScript
|
|
270
|
+
|
|
271
|
+
Every **named** model of the API is exported — all 262 schemas of its OpenAPI document, generated
|
|
272
|
+
rather than retyped, so a shape the resources do not surface yet is still reachable by name. Where
|
|
273
|
+
the package presents a name of its own (`Address`, `Transaction`, `PersonCard`) it is an alias of
|
|
274
|
+
the generated schema, not a parallel definition. The deliberate exceptions narrow a generated
|
|
275
|
+
`string` to the literal union it really is — `TransactionStatus` — and say so where they are
|
|
276
|
+
declared.
|
|
277
|
+
|
|
278
|
+
What has no name is what the document declares inline, which is most success bodies: those shapes
|
|
279
|
+
are written out by the resource method that returns them. If you need one as a standalone type,
|
|
280
|
+
take it from the method — `Awaited<ReturnType<…>>` — rather than redeclaring it.
|
|
281
|
+
|
|
282
|
+
Where a report requires a parameter, the compiler requires it too, and the answer is typed as
|
|
283
|
+
THAT report — the ten have little in common beyond the `report` header:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
const movement = await core.vault(code).reports.run('asset_movement', { from, to, tokenId })
|
|
287
|
+
movement.openingBalance.total // AssetMovementReport — tokenId was required
|
|
288
|
+
|
|
289
|
+
const position = await core.vault(code).reports.run('balance_position', {})
|
|
290
|
+
position.totalsByAsset // BalancePositionReport — needs no period, has no `summary`
|
|
163
291
|
```
|
|
164
292
|
|
|
165
|
-
|
|
293
|
+
Where the type is known only at runtime — the answer of `runTemplate()` — you get `AnyReport`, the
|
|
294
|
+
union of the ten. Amounts in reports are decimal strings (`"1.5"`), not smallest units, with the
|
|
295
|
+
one exception named on `reports.run`: read them with `parseDecimal`, not `amount`.
|
|
296
|
+
|
|
297
|
+
## Requirements
|
|
298
|
+
|
|
299
|
+
Node.js 20 or newer, or any runtime with `fetch`. Developing the package itself needs Node 24,
|
|
300
|
+
because its build scripts are TypeScript executed directly.
|
|
166
301
|
|
|
167
302
|
## Staying in sync with the API
|
|
168
303
|
|
|
169
|
-
The repository holds a snapshot of the OpenAPI document
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
the TypeScript 5 compiler API that TypeScript 7 no longer ships.
|
|
304
|
+
The repository holds a snapshot of the OpenAPI document. Every model is generated from it into
|
|
305
|
+
`src/generated/types.ts` by a small purpose-built generator — `openapi-typescript` is built on the
|
|
306
|
+
TypeScript 5 compiler API, which TypeScript 7 no longer ships.
|
|
173
307
|
|
|
174
308
|
```bash
|
|
175
309
|
npm run spec:update # refresh the snapshot from the live API
|
|
176
310
|
npm run generate # regenerate types and the operation catalogue
|
|
177
311
|
```
|
|
178
312
|
|
|
179
|
-
|
|
180
|
-
has not been classified by access level.
|
|
181
|
-
|
|
182
|
-
A test keeps the generated files in step with the snapshot, so a drift shows up in a pull request
|
|
313
|
+
A test keeps the generated files in step with the snapshot, so drift shows up in a pull request
|
|
183
314
|
rather than in a consumer at runtime.
|
|
184
315
|
|
|
185
|
-
|
|
186
|
-
|
|
316
|
+
## Further reading
|
|
317
|
+
|
|
318
|
+
- [docs/concepts.md](./docs/concepts.md) — the domain behind the API: the two tiers of
|
|
319
|
+
authorisation, how approvals work, why money is a string, how sharing is modelled. Worth reading
|
|
320
|
+
before building anything non-trivial.
|
|
321
|
+
- [docs/errors.md](./docs/errors.md) — every error code, what causes it and what to do about it.
|
|
322
|
+
- [AGENTS.md](./AGENTS.md) — for changing this package: what is generated, what is written by
|
|
323
|
+
hand, and the rules that are not style.
|
|
324
|
+
- [CHANGELOG.md](./CHANGELOG.md) — including the known limitations of the current release.
|
|
325
|
+
|
|
326
|
+
## Versioning
|
|
327
|
+
|
|
328
|
+
[Semantic versioning](https://semver.org). The package is below 1.0 while the shape settles;
|
|
329
|
+
breaking changes are listed in the changelog.
|
|
187
330
|
|
|
188
331
|
## License
|
|
189
332
|
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpClient, type ClientOptions } from './core/http.js';
|
|
2
|
-
import type { CreateVaultBody, Health, VaultDetail } from './generated/types.js';
|
|
2
|
+
import type { BuildPathBody, BuildPathResponse, CreateVaultBody, Health, ParsePathBody, ParsedPathResponse, UserProfile, VaultDetail, VaultListItem } from './generated/types.js';
|
|
3
3
|
import { AddressesResource } from './resources/addresses.js';
|
|
4
4
|
import { ApprovalsResource } from './resources/approvals.js';
|
|
5
5
|
import { BalancesResource } from './resources/balances.js';
|
|
@@ -66,21 +66,30 @@ export interface VaultScope {
|
|
|
66
66
|
readonly reports: ReportsResource;
|
|
67
67
|
readonly operations: OperationsResource;
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
/**
|
|
70
|
+
* A vault as `vaults.list()` returns it.
|
|
71
|
+
*
|
|
72
|
+
* Listing and detail are DIFFERENT shapes, so this name can only stand for
|
|
73
|
+
* one of them. A list item carries `counts` (groups, invitations, policies,
|
|
74
|
+
* members) and a stripped-down `networks` (id, code, name, coinType); the
|
|
75
|
+
* detail response carries full `Network` records and the whole `routes` tree,
|
|
76
|
+
* and no counts at all. `vaults.get()` is therefore typed `VaultDetail`, not
|
|
77
|
+
* this.
|
|
78
|
+
*
|
|
79
|
+
* @deprecated Use `VaultListItem` directly. The name survives only because
|
|
80
|
+
* dropping an exported one breaks consumers: it used to be a hand-written
|
|
81
|
+
* interface that declared `id`, `code` and `name` and swept everything else
|
|
82
|
+
* into an index signature, which described neither shape — `counts` and
|
|
83
|
+
* `routes` alike arrived as `unknown`.
|
|
84
|
+
*/
|
|
85
|
+
export type VaultSummary = VaultListItem;
|
|
86
|
+
/**
|
|
87
|
+
* The caller's own profile — what `me.profile()` and `me.updateProfile()`
|
|
88
|
+
* both answer with.
|
|
89
|
+
*
|
|
90
|
+
* @deprecated Use `UserProfile` directly; the name is kept for consumers.
|
|
91
|
+
*/
|
|
92
|
+
export type Profile = UserProfile;
|
|
84
93
|
export declare class CoreClient {
|
|
85
94
|
/** Low-level access for endpoints the resources do not cover yet. */
|
|
86
95
|
readonly http: HttpClient;
|
|
@@ -88,9 +97,22 @@ export declare class CoreClient {
|
|
|
88
97
|
/** Scope of one vault. Build it at the call site; do not store it. */
|
|
89
98
|
vault(code: string): VaultScope;
|
|
90
99
|
readonly vaults: {
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Administrators only: a plain member sees their own vault through
|
|
102
|
+
* `me.vault()`. Not paginated and not filterable — the whole list comes
|
|
103
|
+
* back, newest first.
|
|
104
|
+
*/
|
|
105
|
+
list: () => Promise<VaultListItem[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Full configuration of one vault: every connected network and the whole
|
|
108
|
+
* `routes` tree, disabled variants included.
|
|
109
|
+
*
|
|
110
|
+
* Open to any MEMBER of the vault, unlike `list()`, which is
|
|
111
|
+
* administrator-only (an administrator passes the membership check too).
|
|
112
|
+
* The richer payload is also why the return type differs from a list
|
|
113
|
+
* item: see `VaultSummary`.
|
|
114
|
+
*/
|
|
115
|
+
get: (code: string) => Promise<VaultDetail>;
|
|
94
116
|
/** Administrators only. */
|
|
95
117
|
create: (input: CreateVaultBody) => Promise<VaultDetail>;
|
|
96
118
|
/**
|
|
@@ -99,11 +121,34 @@ export declare class CoreClient {
|
|
|
99
121
|
* Use these rather than building the string yourself: the path is written
|
|
100
122
|
* WITHOUT apostrophes (`m/44/195/…`, not `m/44'/…`), and the segment
|
|
101
123
|
* order comes from the vault's own configuration, not from a convention.
|
|
124
|
+
*
|
|
125
|
+
* Both are open to any MEMBER of the vault; they are not
|
|
126
|
+
* administrator-only.
|
|
127
|
+
*
|
|
128
|
+
* `values` is keyed by `Route.code` and the generated type says only
|
|
129
|
+
* `unknown` per entry, because the schema's `string | number` union does
|
|
130
|
+
* not survive generation. What the server accepts: for a static route a
|
|
131
|
+
* variant `code` or its numeric `pathId`, for a dynamic route an integer
|
|
132
|
+
* in `[0, 2^31-1]`. A missing route, an unknown variant or a disabled one
|
|
133
|
+
* is a 400, not a silently skipped segment.
|
|
134
|
+
*/
|
|
135
|
+
buildDerivationPath: (code: string, body: BuildPathBody) => Promise<BuildPathResponse>;
|
|
136
|
+
/**
|
|
137
|
+
* Breaks a path back into the network and one entry per route.
|
|
138
|
+
*
|
|
139
|
+
* `segments[].variant` is absent for a dynamic route — that is the normal
|
|
140
|
+
* case, not a lookup failure. For a static route it is present even when
|
|
141
|
+
* the variant is disabled, so that legacy addresses still parse.
|
|
102
142
|
*/
|
|
103
|
-
|
|
104
|
-
parseDerivationPath: (code: string, body: unknown) => Promise<unknown>;
|
|
143
|
+
parseDerivationPath: (code: string, body: ParsePathBody) => Promise<ParsedPathResponse>;
|
|
105
144
|
};
|
|
106
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Liveness probe. Needs no authentication.
|
|
147
|
+
*
|
|
148
|
+
* It answers "is the worker deployed at all?" and nothing more: the handler
|
|
149
|
+
* never touches the database, so a healthy answer does NOT mean D1 is
|
|
150
|
+
* reachable. Use a real read for that.
|
|
151
|
+
*/
|
|
107
152
|
health(): Promise<Health>;
|
|
108
153
|
/** The caller's own profile, devices and permissions. */
|
|
109
154
|
readonly me: MeResource;
|
package/dist/cjs/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAO,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACpE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/client.js
CHANGED
|
@@ -51,8 +51,21 @@ class CoreClient {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
vaults = {
|
|
54
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Administrators only: a plain member sees their own vault through
|
|
56
|
+
* `me.vault()`. Not paginated and not filterable — the whole list comes
|
|
57
|
+
* back, newest first.
|
|
58
|
+
*/
|
|
55
59
|
list: () => this.http.request('GET', '/api/v1/vaults'),
|
|
60
|
+
/**
|
|
61
|
+
* Full configuration of one vault: every connected network and the whole
|
|
62
|
+
* `routes` tree, disabled variants included.
|
|
63
|
+
*
|
|
64
|
+
* Open to any MEMBER of the vault, unlike `list()`, which is
|
|
65
|
+
* administrator-only (an administrator passes the membership check too).
|
|
66
|
+
* The richer payload is also why the return type differs from a list
|
|
67
|
+
* item: see `VaultSummary`.
|
|
68
|
+
*/
|
|
56
69
|
get: (code) => this.http.request('GET', `/api/v1/vaults/${(0, http_js_1.seg)(code)}`),
|
|
57
70
|
/** Administrators only. */
|
|
58
71
|
create: (input) => this.http.request('POST', '/api/v1/vaults', { body: input }),
|
|
@@ -62,11 +75,34 @@ class CoreClient {
|
|
|
62
75
|
* Use these rather than building the string yourself: the path is written
|
|
63
76
|
* WITHOUT apostrophes (`m/44/195/…`, not `m/44'/…`), and the segment
|
|
64
77
|
* order comes from the vault's own configuration, not from a convention.
|
|
78
|
+
*
|
|
79
|
+
* Both are open to any MEMBER of the vault; they are not
|
|
80
|
+
* administrator-only.
|
|
81
|
+
*
|
|
82
|
+
* `values` is keyed by `Route.code` and the generated type says only
|
|
83
|
+
* `unknown` per entry, because the schema's `string | number` union does
|
|
84
|
+
* not survive generation. What the server accepts: for a static route a
|
|
85
|
+
* variant `code` or its numeric `pathId`, for a dynamic route an integer
|
|
86
|
+
* in `[0, 2^31-1]`. A missing route, an unknown variant or a disabled one
|
|
87
|
+
* is a 400, not a silently skipped segment.
|
|
65
88
|
*/
|
|
66
89
|
buildDerivationPath: (code, body) => this.http.request('POST', `/api/v1/vaults/${(0, http_js_1.seg)(code)}/derivation-path/build`, { body }),
|
|
90
|
+
/**
|
|
91
|
+
* Breaks a path back into the network and one entry per route.
|
|
92
|
+
*
|
|
93
|
+
* `segments[].variant` is absent for a dynamic route — that is the normal
|
|
94
|
+
* case, not a lookup failure. For a static route it is present even when
|
|
95
|
+
* the variant is disabled, so that legacy addresses still parse.
|
|
96
|
+
*/
|
|
67
97
|
parseDerivationPath: (code, body) => this.http.request('POST', `/api/v1/vaults/${(0, http_js_1.seg)(code)}/derivation-path/parse`, { body }),
|
|
68
98
|
};
|
|
69
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Liveness probe. Needs no authentication.
|
|
101
|
+
*
|
|
102
|
+
* It answers "is the worker deployed at all?" and nothing more: the handler
|
|
103
|
+
* never touches the database, so a healthy answer does NOT mean D1 is
|
|
104
|
+
* reachable. Use a real read for that.
|
|
105
|
+
*/
|
|
70
106
|
health() {
|
|
71
107
|
return this.http.request('GET', '/');
|
|
72
108
|
}
|
package/dist/cjs/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":";;;AAAA,4CAAoE;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,14 +5,30 @@
|
|
|
5
5
|
* wrapper has three gaps the client has to close itself — otherwise a precise
|
|
6
6
|
* error turns into "something went wrong":
|
|
7
7
|
*
|
|
8
|
-
* 1. Bodies without an `error` key pass through untouched
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* item by item.
|
|
12
|
-
* 2.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* 1. Bodies without an `error` key pass through untouched — the wrapper
|
|
9
|
+
* keys off that one field. Batch sharing endpoints answer 400 with
|
|
10
|
+
* `{results:[…]}`, which is not a failure of the whole call but "some
|
|
11
|
+
* subjects were rejected", and has to be read item by item.
|
|
12
|
+
* 2. Non-JSON bodies have no wrapper at all: the layer leaves by
|
|
13
|
+
* content-type, and a thrown `HTTPException` carries its own response
|
|
14
|
+
* past it. So a 502 from the edge, a WAF 403 or a platform 429 arrive as
|
|
15
|
+
* text. `core/http.ts` reduces a short one to a flat `{error: "…"}`
|
|
16
|
+
* (dropping HTML pages) before it reaches this parser, which is why the
|
|
17
|
+
* flat string shape below is still read — and why the per-status
|
|
18
|
+
* fallback wording here is what a person ends up seeing.
|
|
19
|
+
* 3. `message` sometimes IS the code. Handlers answer `{error: "<CODE>",
|
|
20
|
+
* code: "<CODE>"}` and batch items carry `{code:"X", message:"X"}`; the
|
|
21
|
+
* wrapper copies the former into `message` faithfully. A code is not a
|
|
22
|
+
* sentence, so it must never be shown to a person as-is.
|
|
23
|
+
*
|
|
24
|
+
* What is NOT a gap any more: zod validation failures. The route hook does
|
|
25
|
+
* build a flat `{error: "amount: Required; …"}` (backend
|
|
26
|
+
* `openapi/common.ts`), but that is an intermediate form.
|
|
27
|
+
* `middleware/errorEnvelope` rewraps every JSON body carrying a string
|
|
28
|
+
* `error` into the canonical envelope, `code` derived from the status —
|
|
29
|
+
* `BAD_REQUEST` here, because the hook sets none of its own. The same layer
|
|
30
|
+
* answers an unmatched path and an uncaught throw. Reading `body.error` as a
|
|
31
|
+
* string against today's API therefore hands you an object.
|
|
16
32
|
*
|
|
17
33
|
* Because of that `code` is always present here: either the server sent one,
|
|
18
34
|
* or it is derived from the HTTP status. Branch on `code`, display `message`.
|
|
@@ -54,8 +70,12 @@ export declare function isCoreError(e: unknown): e is CoreError;
|
|
|
54
70
|
/** Convenience for `catch`: `if (hasCode(e, 'INSUFFICIENT_BALANCE'))`. */
|
|
55
71
|
export declare function hasCode(e: unknown, ...codes: readonly string[]): boolean;
|
|
56
72
|
/**
|
|
57
|
-
* Status to code,
|
|
58
|
-
*
|
|
73
|
+
* Status to code, used only when a body carried no code of its own: a proxy's
|
|
74
|
+
* HTML 502, a platform 429, an empty body.
|
|
75
|
+
*
|
|
76
|
+
* The names are the API's own names for the same statuses, so that one
|
|
77
|
+
* condition has one name whichever side produced it. 502 and 504 are the
|
|
78
|
+
* exception — the API has no envelope for them, they arrive from the edge.
|
|
59
79
|
*/
|
|
60
80
|
export declare function codeFromStatus(status: number): string;
|
|
61
81
|
/**
|