@v3finance/core-sdk 0.1.0 → 0.2.1

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 (217) hide show
  1. package/CHANGELOG.md +441 -7
  2. package/README.md +219 -73
  3. package/dist/cjs/client.d.ts +67 -22
  4. package/dist/cjs/client.d.ts.map +1 -1
  5. package/dist/cjs/client.js +38 -2
  6. package/dist/cjs/client.js.map +1 -1
  7. package/dist/cjs/core/errors.d.ts +30 -10
  8. package/dist/cjs/core/errors.d.ts.map +1 -1
  9. package/dist/cjs/core/errors.js +47 -15
  10. package/dist/cjs/core/errors.js.map +1 -1
  11. package/dist/cjs/core/gated.d.ts +30 -6
  12. package/dist/cjs/core/gated.d.ts.map +1 -1
  13. package/dist/cjs/core/gated.js +21 -6
  14. package/dist/cjs/core/gated.js.map +1 -1
  15. package/dist/cjs/core/http.d.ts +12 -8
  16. package/dist/cjs/core/http.d.ts.map +1 -1
  17. package/dist/cjs/core/http.js +147 -43
  18. package/dist/cjs/core/http.js.map +1 -1
  19. package/dist/cjs/core/pagination.d.ts +28 -1
  20. package/dist/cjs/core/pagination.d.ts.map +1 -1
  21. package/dist/cjs/core/pagination.js +36 -1
  22. package/dist/cjs/core/pagination.js.map +1 -1
  23. package/dist/cjs/generated/operations.js +1 -1
  24. package/dist/cjs/generated/types.d.ts +965 -179
  25. package/dist/cjs/generated/types.d.ts.map +1 -1
  26. package/dist/cjs/index.d.ts +32 -8
  27. package/dist/cjs/index.d.ts.map +1 -1
  28. package/dist/cjs/index.js +12 -0
  29. package/dist/cjs/index.js.map +1 -1
  30. package/dist/cjs/resources/addresses.d.ts +23 -8
  31. package/dist/cjs/resources/addresses.d.ts.map +1 -1
  32. package/dist/cjs/resources/addresses.js +8 -1
  33. package/dist/cjs/resources/addresses.js.map +1 -1
  34. package/dist/cjs/resources/admin.d.ts +150 -25
  35. package/dist/cjs/resources/admin.d.ts.map +1 -1
  36. package/dist/cjs/resources/admin.js +94 -23
  37. package/dist/cjs/resources/admin.js.map +1 -1
  38. package/dist/cjs/resources/approvals.d.ts +46 -5
  39. package/dist/cjs/resources/approvals.d.ts.map +1 -1
  40. package/dist/cjs/resources/approvals.js +27 -4
  41. package/dist/cjs/resources/approvals.js.map +1 -1
  42. package/dist/cjs/resources/balances.d.ts +152 -16
  43. package/dist/cjs/resources/balances.d.ts.map +1 -1
  44. package/dist/cjs/resources/balances.js +9 -5
  45. package/dist/cjs/resources/balances.js.map +1 -1
  46. package/dist/cjs/resources/categories.d.ts +93 -21
  47. package/dist/cjs/resources/categories.d.ts.map +1 -1
  48. package/dist/cjs/resources/categories.js +65 -18
  49. package/dist/cjs/resources/categories.js.map +1 -1
  50. package/dist/cjs/resources/counterparties.d.ts +26 -3
  51. package/dist/cjs/resources/counterparties.d.ts.map +1 -1
  52. package/dist/cjs/resources/counterparties.js +17 -0
  53. package/dist/cjs/resources/counterparties.js.map +1 -1
  54. package/dist/cjs/resources/folders.d.ts +51 -10
  55. package/dist/cjs/resources/folders.d.ts.map +1 -1
  56. package/dist/cjs/resources/folders.js +36 -5
  57. package/dist/cjs/resources/folders.js.map +1 -1
  58. package/dist/cjs/resources/history.d.ts +20 -2
  59. package/dist/cjs/resources/history.d.ts.map +1 -1
  60. package/dist/cjs/resources/history.js.map +1 -1
  61. package/dist/cjs/resources/me.d.ts +27 -4
  62. package/dist/cjs/resources/me.d.ts.map +1 -1
  63. package/dist/cjs/resources/me.js +22 -1
  64. package/dist/cjs/resources/me.js.map +1 -1
  65. package/dist/cjs/resources/members.d.ts +19 -4
  66. package/dist/cjs/resources/members.d.ts.map +1 -1
  67. package/dist/cjs/resources/members.js +15 -6
  68. package/dist/cjs/resources/members.js.map +1 -1
  69. package/dist/cjs/resources/operations.d.ts +64 -8
  70. package/dist/cjs/resources/operations.d.ts.map +1 -1
  71. package/dist/cjs/resources/operations.js +26 -2
  72. package/dist/cjs/resources/operations.js.map +1 -1
  73. package/dist/cjs/resources/registries.d.ts +87 -9
  74. package/dist/cjs/resources/registries.d.ts.map +1 -1
  75. package/dist/cjs/resources/registries.js +54 -11
  76. package/dist/cjs/resources/registries.js.map +1 -1
  77. package/dist/cjs/resources/reports.d.ts +300 -41
  78. package/dist/cjs/resources/reports.d.ts.map +1 -1
  79. package/dist/cjs/resources/reports.js +135 -7
  80. package/dist/cjs/resources/reports.js.map +1 -1
  81. package/dist/cjs/resources/sharing.d.ts +61 -10
  82. package/dist/cjs/resources/sharing.d.ts.map +1 -1
  83. package/dist/cjs/resources/sharing.js +51 -4
  84. package/dist/cjs/resources/sharing.js.map +1 -1
  85. package/dist/cjs/resources/transfers.d.ts +44 -6
  86. package/dist/cjs/resources/transfers.d.ts.map +1 -1
  87. package/dist/cjs/resources/transfers.js +41 -3
  88. package/dist/cjs/resources/transfers.js.map +1 -1
  89. package/dist/cjs/resources/types.d.ts +16 -0
  90. package/dist/cjs/resources/types.d.ts.map +1 -1
  91. package/dist/cjs/units.d.ts.map +1 -1
  92. package/dist/cjs/units.js +18 -2
  93. package/dist/cjs/units.js.map +1 -1
  94. package/dist/cjs/version.d.ts +1 -1
  95. package/dist/cjs/version.js +1 -1
  96. package/dist/esm/client.d.ts +67 -22
  97. package/dist/esm/client.d.ts.map +1 -1
  98. package/dist/esm/client.js +38 -2
  99. package/dist/esm/client.js.map +1 -1
  100. package/dist/esm/core/errors.d.ts +30 -10
  101. package/dist/esm/core/errors.d.ts.map +1 -1
  102. package/dist/esm/core/errors.js +47 -15
  103. package/dist/esm/core/errors.js.map +1 -1
  104. package/dist/esm/core/gated.d.ts +30 -6
  105. package/dist/esm/core/gated.d.ts.map +1 -1
  106. package/dist/esm/core/gated.js +21 -6
  107. package/dist/esm/core/gated.js.map +1 -1
  108. package/dist/esm/core/http.d.ts +12 -8
  109. package/dist/esm/core/http.d.ts.map +1 -1
  110. package/dist/esm/core/http.js +148 -44
  111. package/dist/esm/core/http.js.map +1 -1
  112. package/dist/esm/core/pagination.d.ts +28 -1
  113. package/dist/esm/core/pagination.d.ts.map +1 -1
  114. package/dist/esm/core/pagination.js +36 -1
  115. package/dist/esm/core/pagination.js.map +1 -1
  116. package/dist/esm/generated/operations.js +1 -1
  117. package/dist/esm/generated/types.d.ts +965 -179
  118. package/dist/esm/generated/types.d.ts.map +1 -1
  119. package/dist/esm/index.d.ts +32 -8
  120. package/dist/esm/index.d.ts.map +1 -1
  121. package/dist/esm/index.js +17 -5
  122. package/dist/esm/index.js.map +1 -1
  123. package/dist/esm/resources/addresses.d.ts +23 -8
  124. package/dist/esm/resources/addresses.d.ts.map +1 -1
  125. package/dist/esm/resources/addresses.js +8 -1
  126. package/dist/esm/resources/addresses.js.map +1 -1
  127. package/dist/esm/resources/admin.d.ts +150 -25
  128. package/dist/esm/resources/admin.d.ts.map +1 -1
  129. package/dist/esm/resources/admin.js +94 -23
  130. package/dist/esm/resources/admin.js.map +1 -1
  131. package/dist/esm/resources/approvals.d.ts +46 -5
  132. package/dist/esm/resources/approvals.d.ts.map +1 -1
  133. package/dist/esm/resources/approvals.js +27 -4
  134. package/dist/esm/resources/approvals.js.map +1 -1
  135. package/dist/esm/resources/balances.d.ts +152 -16
  136. package/dist/esm/resources/balances.d.ts.map +1 -1
  137. package/dist/esm/resources/balances.js +9 -5
  138. package/dist/esm/resources/balances.js.map +1 -1
  139. package/dist/esm/resources/categories.d.ts +93 -21
  140. package/dist/esm/resources/categories.d.ts.map +1 -1
  141. package/dist/esm/resources/categories.js +65 -18
  142. package/dist/esm/resources/categories.js.map +1 -1
  143. package/dist/esm/resources/counterparties.d.ts +26 -3
  144. package/dist/esm/resources/counterparties.d.ts.map +1 -1
  145. package/dist/esm/resources/counterparties.js +17 -0
  146. package/dist/esm/resources/counterparties.js.map +1 -1
  147. package/dist/esm/resources/folders.d.ts +51 -10
  148. package/dist/esm/resources/folders.d.ts.map +1 -1
  149. package/dist/esm/resources/folders.js +36 -5
  150. package/dist/esm/resources/folders.js.map +1 -1
  151. package/dist/esm/resources/history.d.ts +20 -2
  152. package/dist/esm/resources/history.d.ts.map +1 -1
  153. package/dist/esm/resources/history.js.map +1 -1
  154. package/dist/esm/resources/me.d.ts +27 -4
  155. package/dist/esm/resources/me.d.ts.map +1 -1
  156. package/dist/esm/resources/me.js +22 -1
  157. package/dist/esm/resources/me.js.map +1 -1
  158. package/dist/esm/resources/members.d.ts +19 -4
  159. package/dist/esm/resources/members.d.ts.map +1 -1
  160. package/dist/esm/resources/members.js +15 -6
  161. package/dist/esm/resources/members.js.map +1 -1
  162. package/dist/esm/resources/operations.d.ts +64 -8
  163. package/dist/esm/resources/operations.d.ts.map +1 -1
  164. package/dist/esm/resources/operations.js +26 -2
  165. package/dist/esm/resources/operations.js.map +1 -1
  166. package/dist/esm/resources/registries.d.ts +87 -9
  167. package/dist/esm/resources/registries.d.ts.map +1 -1
  168. package/dist/esm/resources/registries.js +54 -11
  169. package/dist/esm/resources/registries.js.map +1 -1
  170. package/dist/esm/resources/reports.d.ts +300 -41
  171. package/dist/esm/resources/reports.d.ts.map +1 -1
  172. package/dist/esm/resources/reports.js +135 -7
  173. package/dist/esm/resources/reports.js.map +1 -1
  174. package/dist/esm/resources/sharing.d.ts +61 -10
  175. package/dist/esm/resources/sharing.d.ts.map +1 -1
  176. package/dist/esm/resources/sharing.js +51 -4
  177. package/dist/esm/resources/sharing.js.map +1 -1
  178. package/dist/esm/resources/transfers.d.ts +44 -6
  179. package/dist/esm/resources/transfers.d.ts.map +1 -1
  180. package/dist/esm/resources/transfers.js +41 -3
  181. package/dist/esm/resources/transfers.js.map +1 -1
  182. package/dist/esm/resources/types.d.ts +16 -0
  183. package/dist/esm/resources/types.d.ts.map +1 -1
  184. package/dist/esm/units.d.ts.map +1 -1
  185. package/dist/esm/units.js +18 -2
  186. package/dist/esm/units.js.map +1 -1
  187. package/dist/esm/version.d.ts +1 -1
  188. package/dist/esm/version.js +1 -1
  189. package/docs/concepts.md +193 -0
  190. package/docs/errors.md +491 -0
  191. package/package.json +7 -6
  192. package/src/client.ts +89 -25
  193. package/src/core/errors.ts +49 -15
  194. package/src/core/gated.ts +31 -7
  195. package/src/core/http.ts +173 -49
  196. package/src/core/pagination.ts +38 -1
  197. package/src/generated/operations.ts +1 -1
  198. package/src/generated/types.ts +1001 -180
  199. package/src/index.ts +64 -7
  200. package/src/resources/addresses.ts +24 -8
  201. package/src/resources/admin.ts +186 -33
  202. package/src/resources/approvals.ts +55 -9
  203. package/src/resources/balances.ts +186 -14
  204. package/src/resources/categories.ts +125 -30
  205. package/src/resources/counterparties.ts +23 -6
  206. package/src/resources/folders.ts +74 -18
  207. package/src/resources/history.ts +27 -2
  208. package/src/resources/me.ts +30 -5
  209. package/src/resources/members.ts +26 -7
  210. package/src/resources/operations.ts +73 -8
  211. package/src/resources/registries.ts +90 -11
  212. package/src/resources/reports.ts +348 -67
  213. package/src/resources/sharing.ts +71 -18
  214. package/src/resources/transfers.ts +51 -7
  215. package/src/resources/types.ts +16 -0
  216. package/src/units.ts +19 -2
  217. package/src/version.ts +1 -1
package/README.md CHANGED
@@ -5,25 +5,43 @@
5
5
  [![types](https://img.shields.io/npm/types/@v3finance/core-sdk.svg)](https://www.npmjs.com/package/@v3finance/core-sdk)
6
6
  [![license](https://img.shields.io/npm/l/@v3finance/core-sdk.svg)](./LICENSE)
7
7
 
8
- The official TypeScript SDK for the V3 Finance Core API.
9
- Custodial addresses, transfers under policy control, approvals and shared access.
10
-
11
- - **Vault scoping** that carries no state: `core.vault(code)` is an expression, so two vaults work side by side.
12
- - **Policy-aware results**: a transfer either executes, waits for signatures or replays — told apart by status, not guesswork.
13
- - **Money that cannot silently lose precision**: smallest-unit strings and BigInt arithmetic, with no `toNumber`.
14
- - **Pluggable authentication**: browser cookie, native cookie jar or bearer token — you supply headers, the SDK hands events back.
15
- - **Complete**: every endpoint of the API, grouped into resources, with the administrative surface marked.
16
- - **Fully typed**, zero dependencies, ESM and CommonJS. Runs on Node.js, Bun, Deno, Cloudflare Workers and React Native.
17
-
18
- The whole API is covered: addresses and transfers, approvals, counterparties, shared access and
19
- folders, operation labelling, reports, and the administrative surface. A test asserts that every
20
- endpoint is reachable through some resource, so an endpoint added to the API shows up as a failure
21
- rather than as a gap nobody noticed.
22
-
23
- Two endpoints are deliberately absent: `/welcome` is a browser landing page the API redirects to,
24
- and `/webhooks/wallet` is the inbound channel for the wallet service.
25
-
26
- ## Install
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
- ## Three things to know before the first call
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').addresses.list()
98
+ core.vault('beta').transfers.send(…)
53
99
  ```
54
100
 
55
- There is deliberately no "current vault". The code is visible on every call site, an administrator can
56
- work with several vaults at once, and switching the active vault at runtime cannot leave a stale scope
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 and address creation run through the policy engine. The result is `Gated`, and the three
62
- branches **must** be distinguished: the response body is identical, only the status differs.
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,91 @@ const result = await core.vault(code).transfers.send({
71
118
  })
72
119
 
73
120
  switch (result.outcome) {
74
- case 'applied':
75
- return toast('Transfer sent')
76
- case 'pending':
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(describeStatus(result.data.transaction.status))
80
124
  }
81
125
  ```
82
126
 
83
- Reporting "sent" on `pending` tells a person their money has gone when it is frozen.
127
+ Reporting "sent" on `pending` tells a person their money has gone when it is frozen. `replayed` is
128
+ not "sent" either: it means a transfer with this key already existed, and its `status` says whether
129
+ it went out (`confirmed` or `submitted`), is waiting for signatures (`awaiting_approval`), is still
130
+ executing (`pending`) or did not go out (`failed`, `rejected`, `cancelled`).
84
131
 
85
- **Always pass `idempotencyKey`** and keep it until you have an answer: after a timeout it is the only
86
- way to retry without sending money twice.
132
+ **Always pass `idempotencyKey`** on a transfer and keep it until you have an answer: after a
133
+ timeout it is the only way to retry without sending money twice. The key is read from the request
134
+ **body** — the `idempotency-key` header is not sent at all. It used to be, and in a browser it broke the
135
+ request outright: the API's CORS preflight allows only `Content-Type` and `Authorization`.
87
136
 
88
137
  ### 3. Amounts are strings, never numbers
89
138
 
90
- Balances arrive in smallest units. `float64` loses precision above 2⁵³, and production balances go higher.
91
-
92
139
  ```ts
93
140
  import { amount, parseDecimal, format, add } from '@v3finance/core-sdk'
94
141
 
95
142
  const balance = amount(address.balance ?? '0', 6, 'usdt')
96
- format(balance, { withAsset: true }) // "1,500.00 USDT"
143
+ format(balance, { withAsset: true }) // "1,500.00 USDT"
97
144
  add(balance, parseDecimal('2.5', 6))
98
145
  ```
99
146
 
100
- There is no `toNumber`, on purpose — it would be a convenient door into the bug this layer prevents.
147
+ There is no `toNumber`, on purpose — it would be a convenient door into the precision loss this
148
+ layer exists to prevent. `float64` breaks above 2⁵³ and real balances go higher.
101
149
 
102
150
  **`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 in an interface.
151
+ arrived. "No account" and "empty account" deserve different wording.
152
+
153
+ ## Resources
154
+
155
+ Scoped to a vault — `core.vault(code).…`:
156
+
157
+ | Resource | What it covers |
158
+ | --- | --- |
159
+ | `addresses` | list, detail, create (gated), edit metadata |
160
+ | `transfers` | send (gated), detail, vote, cancel, reconcile (admin) |
161
+ | `history` | the unified feed of operations; direction is a filter, not an endpoint |
162
+ | `balances` | balances, a point in time, the raw ledger |
163
+ | `approvals` | requests, detail, vote, cancel, composite chains |
164
+ | `counterparties`, `counterpartyGroups` | the directory and its groups — every mutation is gated |
165
+ | `sharing` | sharing an address, participants, shared by me and with me |
166
+ | `folders` | address folders, their grants and participants |
167
+ | `members` | the directory, resolve by username, user groups |
168
+ | `categories` | the labelling schema and the values on operations |
169
+ | `operations` | metadata, links, status history, deposits, approval relations |
170
+ | `reports` | reports, templates, snapshots, exports |
171
+
172
+ At the root — `core.…`:
173
+
174
+ | Resource | What it covers |
175
+ | --- | --- |
176
+ | `me` | profile, email binding, push tokens, allowed segments |
177
+ | `vaults` | list, detail, create, derivation-path build and parse |
178
+ | `registries` | networks, tokens, rates, policy preflight, AML |
179
+ | `auth` | request a magic link |
180
+ | `admin` | policies, policy groups, users, invitations, visibility grants, seats |
181
+ | `health()` | liveness, no authentication |
104
182
 
105
- ## Errors
183
+ Fourteen of the API's 171 operations are deliberately absent. Two are not a client's business:
184
+ `/welcome` is a browser landing page the API redirects to, and `/webhooks/wallet` is the inbound
185
+ channel for the wallet service. The other twelve are the `/address-groups/*` paths — the older
186
+ prefix of the endpoints `folders` already serves under `/address-folders`. One entity, two
187
+ prefixes; see [docs/concepts.md](./docs/concepts.md#5-shared-access-roles-out-flags-in).
106
188
 
107
- Every failure is a `CoreError` and always carries a `code` — sent by the server or derived from the
108
- HTTP status. Branch on `code`, display `message`.
189
+ ## Amounts
190
+
191
+ ```ts
192
+ amount(raw, decimals, asset?) // from smallest units
193
+ parseDecimal('1.5', 6) // from human input; extra digits are refused, not rounded
194
+ toDecimalString(a) // '1.5'
195
+ format(a, { withAsset: true }) // '1.50 USDT', grouped by locale
196
+ add(a, b) · sub(a, b) · compare(a, b) · isZero(a) · isNegative(a)
197
+ ```
198
+
199
+ Adding different assets throws, as does adding different precision: the result would be a
200
+ meaningless number rather than a missing one.
201
+
202
+ ## Error handling
203
+
204
+ Every failure is a `CoreError` and always carries a `code` — sent by the server, or derived from
205
+ the HTTP status. Branch on `code`, display `message`.
109
206
 
110
207
  ```ts
111
208
  import { hasCode, isCoreError } from '@v3finance/core-sdk'
@@ -121,27 +218,16 @@ try {
121
218
  ```
122
219
 
123
220
  `kind` separates `http` (the server answered), `network` (the request never left), `timeout`,
124
- `aborted` and `parse`.
221
+ `aborted` and `parse`. The full code reference is in [docs/errors.md](./docs/errors.md).
125
222
 
126
- ## Authentication
127
-
128
- The SDK does not store credentials: you provide headers, it reports two events back.
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.
223
+ Batch endpoints — sharing an address with several subjects — need one extra habit: **read
224
+ `results[]` whenever the call succeeds**. Each subject is handled independently and a partial
225
+ failure still answers 201; 400 comes only when every subject was rejected.
141
226
 
142
227
  ## Pagination
143
228
 
144
- Lists return `{ items, nextCursor }`. The cursor is opaque — do not parse it.
229
+ Lists return `{ items, nextCursor }`. The cursor is opaque — do not parse it, and treat
230
+ `nextCursor === null` as the only end-of-list signal.
145
231
 
146
232
  ```ts
147
233
  import { paginate } from '@v3finance/core-sdk'
@@ -151,7 +237,10 @@ for await (const address of paginate((p) => core.vault(code).addresses.list(p)))
151
237
  }
152
238
  ```
153
239
 
154
- ## Admin operations
240
+ Not every list is paginated: report templates and snapshots return what they have, and snapshots
241
+ cut off at a limit rather than continuing with a cursor.
242
+
243
+ ## Administrative operations
155
244
 
156
245
  The package covers the whole API, administrative endpoints included. Access level is available
157
246
  programmatically:
@@ -159,31 +248,88 @@ programmatically:
159
248
  ```ts
160
249
  import { OPERATIONS, isAdminOperation } from '@v3finance/core-sdk'
161
250
 
162
- isAdminOperation('POST /api/v1/policies') // true
251
+ isAdminOperation('POST /api/v1/policies') // true
252
+ ```
253
+
254
+ A method existing here grants nothing: the server decides from your session, and a
255
+ non-administrator receives 403.
256
+
257
+ ## TanStack Query
258
+
259
+ Cache keys live in a separate entry point, so the package itself stays dependency-free and the
260
+ keys match between applications — otherwise invalidation in one does not match the other, and the
261
+ mismatch surfaces as "the data did not refresh".
262
+
263
+ ```ts
264
+ import { queryKeys } from '@v3finance/core-sdk/query-keys'
265
+
266
+ useQuery({
267
+ queryKey: queryKeys.addresses(code, filters),
268
+ queryFn: () => core.vault(code).addresses.list(filters),
269
+ })
270
+ ```
271
+
272
+ ## TypeScript
273
+
274
+ Every **named** model of the API is exported — all 262 schemas of its OpenAPI document, generated
275
+ rather than retyped, so a shape the resources do not surface yet is still reachable by name. Where
276
+ the package presents a name of its own (`Address`, `Transaction`, `PersonCard`) it is an alias of
277
+ the generated schema, not a parallel definition. The deliberate exceptions narrow a generated
278
+ `string` to the literal union it really is — `TransactionStatus` — and say so where they are
279
+ declared.
280
+
281
+ What has no name is what the document declares inline, which is most success bodies: those shapes
282
+ are written out by the resource method that returns them. If you need one as a standalone type,
283
+ take it from the method — `Awaited<ReturnType<…>>` — rather than redeclaring it.
284
+
285
+ Where a report requires a parameter, the compiler requires it too, and the answer is typed as
286
+ THAT report — the ten have little in common beyond the `report` header:
287
+
288
+ ```ts
289
+ const movement = await core.vault(code).reports.run('asset_movement', { from, to, tokenId })
290
+ movement.openingBalance.total // AssetMovementReport — tokenId was required
291
+
292
+ const position = await core.vault(code).reports.run('balance_position', {})
293
+ position.totalsByAsset // BalancePositionReport — needs no period, has no `summary`
163
294
  ```
164
295
 
165
- A method existing in the package grants nothing: the server decides, based on your session.
296
+ Where the type is known only at runtime — the answer of `runTemplate()` — you get `AnyReport`, the
297
+ union of the ten. Amounts in reports are decimal strings (`"1.5"`), not smallest units, with the
298
+ one exception named on `reports.run`: read them with `parseDecimal`, not `amount`.
299
+
300
+ ## Requirements
301
+
302
+ Node.js 20 or newer, or any runtime with `fetch`. Developing the package itself needs Node 24,
303
+ because its build scripts are TypeScript executed directly.
166
304
 
167
305
  ## Staying in sync with the API
168
306
 
169
- The repository holds a snapshot of the OpenAPI document (`openapi/spec.json`). Every model is
170
- generated from it into `src/generated/types.ts` — one exported type per schema, descriptions
171
- included — by a small purpose-built generator rather than `openapi-typescript`, which is built on
172
- the TypeScript 5 compiler API that TypeScript 7 no longer ships.
307
+ The repository holds a snapshot of the OpenAPI document. Every model is generated from it into
308
+ `src/generated/types.ts` by a small purpose-built generator — `openapi-typescript` is built on the
309
+ TypeScript 5 compiler API, which TypeScript 7 no longer ships.
173
310
 
174
311
  ```bash
175
312
  npm run spec:update # refresh the snapshot from the live API
176
313
  npm run generate # regenerate types and the operation catalogue
177
314
  ```
178
315
 
179
- CI fails when the generated files drift from the snapshot, and when the API gains an operation that
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
316
+ A test keeps the generated files in step with the snapshot, so drift shows up in a pull request
183
317
  rather than in a consumer at runtime.
184
318
 
185
- Development requires Node.js 24 (the build scripts are TypeScript run directly). The published
186
- package supports Node.js 20 and above.
319
+ ## Further reading
320
+
321
+ - [docs/concepts.md](./docs/concepts.md) — the domain behind the API: the two tiers of
322
+ authorisation, how approvals work, why money is a string, how sharing is modelled. Worth reading
323
+ before building anything non-trivial.
324
+ - [docs/errors.md](./docs/errors.md) — every error code, what causes it and what to do about it.
325
+ - [AGENTS.md](./AGENTS.md) — for changing this package: what is generated, what is written by
326
+ hand, and the rules that are not style.
327
+ - [CHANGELOG.md](./CHANGELOG.md) — including the known limitations of the current release.
328
+
329
+ ## Versioning
330
+
331
+ [Semantic versioning](https://semver.org). The package is below 1.0 while the shape settles;
332
+ breaking changes are listed in the changelog.
187
333
 
188
334
  ## License
189
335
 
@@ -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
- export interface VaultSummary {
70
- id: string;
71
- code: string;
72
- name: string;
73
- [extra: string]: unknown;
74
- }
75
- export interface Profile {
76
- id: string;
77
- name: string | null;
78
- username: string | null;
79
- email: string | null;
80
- /** The account came from an invitation link and has no real email yet. */
81
- needsEmail: boolean;
82
- [extra: string]: unknown;
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
- /** Administrators only: a plain member sees their own vault through `me.vault()`. */
92
- list: () => Promise<VaultSummary[]>;
93
- get: (code: string) => Promise<VaultSummary>;
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
- buildDerivationPath: (code: string, body: unknown) => Promise<unknown>;
104
- parseDerivationPath: (code: string, body: unknown) => Promise<unknown>;
143
+ parseDerivationPath: (code: string, body: ParsePathBody) => Promise<ParsedPathResponse>;
105
144
  };
106
- /** Liveness probe. Needs no authentication. */
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;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -51,8 +51,21 @@ class CoreClient {
51
51
  };
52
52
  }
53
53
  vaults = {
54
- /** Administrators only: a plain member sees their own vault through `me.vault()`. */
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
- /** Liveness probe. Needs no authentication. */
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
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. Batch sharing
9
- * endpoints answer 400 with `{results:[…]}`, which is not a failure of
10
- * the whole call but "some subjects were rejected", and has to be read
11
- * item by item.
12
- * 2. Validation failures arrive as a plain string — `{error: "path: msg"}`
13
- * — with no code and no details.
14
- * 3. Inside a batch result `message` equals the code (`{code:"X",
15
- * message:"X"}`), so it must never be shown to a person as-is.
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, mirroring the mapping the API uses, so that both sides call
58
- * the same thing by the same name.
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
  /**