@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.
Files changed (217) hide show
  1. package/CHANGELOG.md +397 -7
  2. package/README.md +215 -72
  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 +21 -3
  12. package/dist/cjs/core/gated.d.ts.map +1 -1
  13. package/dist/cjs/core/gated.js +12 -3
  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 +130 -40
  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 +948 -172
  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 +22 -8
  31. package/dist/cjs/resources/addresses.d.ts.map +1 -1
  32. package/dist/cjs/resources/addresses.js +7 -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 +26 -4
  86. package/dist/cjs/resources/transfers.d.ts.map +1 -1
  87. package/dist/cjs/resources/transfers.js +23 -1
  88. package/dist/cjs/resources/transfers.js.map +1 -1
  89. package/dist/cjs/resources/types.d.ts +20 -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 +21 -3
  105. package/dist/esm/core/gated.d.ts.map +1 -1
  106. package/dist/esm/core/gated.js +12 -3
  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 +131 -41
  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 +948 -172
  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 +22 -8
  124. package/dist/esm/resources/addresses.d.ts.map +1 -1
  125. package/dist/esm/resources/addresses.js +7 -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 +26 -4
  179. package/dist/esm/resources/transfers.d.ts.map +1 -1
  180. package/dist/esm/resources/transfers.js +23 -1
  181. package/dist/esm/resources/transfers.js.map +1 -1
  182. package/dist/esm/resources/types.d.ts +20 -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 +191 -0
  190. package/docs/errors.md +471 -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 +22 -4
  195. package/src/core/http.ts +156 -46
  196. package/src/core/pagination.ts +38 -1
  197. package/src/generated/operations.ts +1 -1
  198. package/src/generated/types.ts +983 -173
  199. package/src/index.ts +64 -7
  200. package/src/resources/addresses.ts +23 -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 +33 -5
  215. package/src/resources/types.ts +20 -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,88 @@ 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('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 timeout it is the only
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 }) // "1,500.00 USDT"
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 bug this layer prevents.
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 in an interface.
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
- ## Errors
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
- 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`.
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
- ## 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.
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
- ## Admin operations
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') // true
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
- A method existing in the package grants nothing: the server decides, based on your session.
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 (`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.
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
- 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
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
- Development requires Node.js 24 (the build scripts are TypeScript run directly). The published
186
- package supports Node.js 20 and above.
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
 
@@ -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
  /**