@kkkermit/trading212.js 0.0.0-stage → 0.1.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 (4) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +290 -2
  4. package/package.json +134 -5
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @kkkermit/trading212.js
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - dcfed11: First release: a typed client for every Trading 212 Public API endpoint, with a live-trading switch, safe errors with hints, rate limiting, pagination, a fake Trading 212 for tests and a `doctor` command.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kkkermit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,291 @@
1
- # Temporary Holding Version
1
+ <h1><img src="docs/public/logo.svg" alt="trading212.js" height="64"></h1>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![CI](https://github.com/Kkkermit/Trading212.js/actions/workflows/ci.yml/badge.svg)](https://github.com/Kkkermit/Trading212.js/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/@kkkermit/trading212.js)](https://www.npmjs.com/package/@kkkermit/trading212.js)
5
+ [![Licence: MIT](https://img.shields.io/badge/licence-MIT-blue)](LICENSE)
6
+ ![Node 22+](https://img.shields.io/badge/node-%3E%3D22-brightgreen)
7
+
8
+ A TypeScript client for the [Trading 212 Public API](https://docs.trading212.com/api) that is safe around real
9
+ money, tells you how to fix what went wrong, and comes with a fake Trading 212 to test your code against.
10
+
11
+ ```ts
12
+ import { Trading212 } from "@kkkermit/trading212.js";
13
+
14
+ const t212 = new Trading212({ environment: "demo", apiKey, apiSecret });
15
+
16
+ const { totalValue, holdings } = await t212.portfolio.snapshot();
17
+ const apple = await t212.instruments.resolve("Apple");
18
+ const order = await t212.orders.buy({ ticker: apple.ticker, quantity: 0.5 });
19
+ await t212.orders.waitFor(order); // FILLED
20
+ ```
21
+
22
+ > [!WARNING]
23
+ > **Unofficial.** Not affiliated with or endorsed by Trading 212. The API is in beta and can change. Live trading
24
+ > uses real money: test on demo first, and read [Safety](#safety-around-real-money).
25
+
26
+ ## Why this one
27
+
28
+ - **A live client cannot trade by accident.** It only reads until you pass `trading: "enabled"`, and so does any
29
+ client pointed at a custom `baseUrl`, because a proxy could forward to live. Orders are never
30
+ retried, a request that may or may not have executed throws `OrderOutcomeUnknownError` rather than a plain
31
+ failure, and `{ dryRun: true }` shows the exact request without sending it.
32
+ - **Errors tell you what to do.** Every error is a named class with a `hint` in plain English. A 403 names the
33
+ permission your key lacks. A 401 explains that demo keys do not work on live. An order that is too small says
34
+ so, and one selling more than you hold points you at the shares locked in pies.
35
+ - **Your key stays secret.** Credentials are redacted from every log line, error, hook event and test recording,
36
+ and a test suite proves it by echoing them back from a hostile server. Redirects are refused, pagination links
37
+ are pinned to Trading 212's host, and signed download links never receive your key.
38
+ - **Rate limits are handled.** Every endpoint has its own budget, generated from the official spec, corrected from
39
+ the response headers, and shared by every client using the same key in your process. Calls are paced the way
40
+ Trading 212 recommends, so you should not see a 429.
41
+ - **Common tasks take one call.** Turn "Apple" into `AAPL_US_EQ`, sell a whole position without the
42
+ 4-decimal-place error, wait for an order to fill, export a year of history as CSV in one call, check whether a
43
+ market is open, and get your portfolio's weights and profit in one call.
44
+ - **Test without the network.** `createFakeTrading212()` gives you the real client talking to an in-memory Trading
45
+ 212 that fills orders, tracks cash and positions, paginates history, and fails on demand.
46
+ - **`npx @kkkermit/trading212.js doctor`** checks your key, environment and every permission in one go.
47
+ - **Small and modern.** Zero runtime dependencies, about 14 kB minified and compressed, ESM and CommonJS, full types.
48
+ Works on Node 22+, Bun and Deno.
49
+
50
+ ### How it compares
51
+
52
+ There are several community clients for this API on npm, all young. When this library was written (November
53
+ 2026), none of the ones reviewed shipped a fake server for tests, a command-line check, or errors that name the
54
+ missing permission and say how to fix it. Several have runtime dependencies. Compare for yourself:
55
+ `trading212-api`, `t212-sdk`, `@mingchuno/trading212-client` and `@azaini/t212-sdk` are the main ones.
56
+
57
+ ## Install
58
+
59
+ ```sh
60
+ npm install @kkkermit/trading212.js
61
+ ```
62
+
63
+ Node 22 or later, Bun or Deno. Server-side only: Trading 212 refuses requests from web pages, and a key in a page is
64
+ public, so the client refuses to start in a browser.
65
+
66
+ New to it? [Getting started](https://kkkermit.github.io/Trading212.js/getting-started/) goes from install to a working script step by step,
67
+ starting with a try-out that needs no key. The [examples](examples) are complete programs.
68
+
69
+ ## Get a key
70
+
71
+ In the Trading 212 app, go to **Settings > API (Beta)** and create a key. You will see the secret **once**, so save
72
+ it straight into your secret manager.
73
+
74
+ - **A key works in one environment only.** Make a demo key while the app is in practice mode, and a separate one
75
+ for live.
76
+ - **Tick only the permissions you need.** A dashboard needs no `orders:execute`.
77
+ - **Restrict the key to your server's IP address** if it runs somewhere fixed.
78
+ - Only **Invest** and **Stocks ISA** accounts can use the API.
79
+
80
+ Then check it:
81
+
82
+ ```sh
83
+ T212_ENV=demo T212_API_KEY=... T212_API_SECRET=... npx @kkkermit/trading212.js doctor
84
+ ```
85
+
86
+ The output looks like this:
87
+
88
+ ```
89
+ ✓ settings: environment demo, key 37 characters, secret 43 characters
90
+ ✓ sign in: account 12345678 (GBP) on demo
91
+ ✓ portfolio: allowed
92
+ ✗ history:dividends: not on this key (create one with "history:dividends" ticked)
93
+ · exports: live only
94
+ · orders:execute: not checked: it would need a real order
95
+ ✓ trading: orders can be placed on demo
96
+ ```
97
+
98
+ ## Quick start
99
+
100
+ ```ts
101
+ import { Trading212 } from "@kkkermit/trading212.js";
102
+
103
+ // Reads T212_ENV, T212_API_KEY and T212_API_SECRET. There is no default environment, on purpose.
104
+ const t212 = Trading212.fromEnv();
105
+
106
+ const me = await t212.verify(); // { environment, accountId, currency, trading }
107
+ const positions = await t212.positions.list();
108
+
109
+ for await (const dividend of t212.history.dividends.all()) {
110
+ console.log(dividend.paidOn, dividend.ticker, dividend.amount);
111
+ }
112
+ ```
113
+
114
+ ## What it covers
115
+
116
+ Every one of the 22 operations in Trading 212's Public API, on both of its environments. A test fails the build if
117
+ the official spec gains an operation this library does not implement.
118
+
119
+ | Environment | Host | What it is |
120
+ | ----------- | ----------------------------- | -------------------------------------------------------- |
121
+ | `demo` | `https://demo.trading212.com` | Paper trading with practice money. Trading is on. |
122
+ | `live` | `https://live.trading212.com` | Your real account ("prod"). No trading until you opt in. |
123
+
124
+ There is no third environment: "production" and "real" mean `live`, and "paper", "practice" and "sandbox" mean
125
+ `demo`. The client asks you to name one of the two and never guesses.
126
+
127
+ | Method | Endpoint | Permission | Limit |
128
+ | -------------------------------------------------- | ---------------------------------- | ---------------------- | -------- |
129
+ | `account.summary()` | `GET /equity/account/summary` | `account` | 1 / 5 s |
130
+ | `positions.list()`, `positions.get()` | `GET /equity/positions` | `portfolio` | 1 / 1 s |
131
+ | `instruments.list()`, `search()`, `resolve()` | `GET /equity/metadata/instruments` | `metadata` | 1 / 50 s |
132
+ | `exchanges.list()`, `exchanges.status()` | `GET /equity/metadata/exchanges` | `metadata` | 1 / 30 s |
133
+ | `orders.list()` | `GET /equity/orders` | `orders:read` | 1 / 5 s |
134
+ | `orders.get()`, `orders.waitFor()`¹ | `GET /equity/orders/{id}` | `orders:read` | 1 / 1 s |
135
+ | `orders.cancel()` | `DELETE /equity/orders/{id}` | `orders:execute` | 50 / min |
136
+ | `orders.market()`, `buy()`, `sell()`, `sellAll()`² | `POST /equity/orders/market` | `orders:execute` | 50 / min |
137
+ | `orders.limit()` | `POST /equity/orders/limit` | `orders:execute` | 1 / 2 s |
138
+ | `orders.stop()` | `POST /equity/orders/stop` | `orders:execute` | 1 / 2 s |
139
+ | `orders.stopLimit()` | `POST /equity/orders/stop_limit` | `orders:execute` | 1 / 2 s |
140
+ | `history.orders.list()`, `.all()` | `GET /equity/history/orders` | `history:orders` | 20 / min |
141
+ | `history.dividends.list()`, `.all()` | `GET /equity/history/dividends` | `history:dividends` | 20 / min |
142
+ | `history.transactions.list()`, `.all()` | `GET /equity/history/transactions` | `history:transactions` | 20 / min |
143
+ | `reports.list()`, `reports.waitFor()` | `GET /equity/history/exports` | not stated in the spec | 1 / min |
144
+ | `reports.request()`, `reports.export()` | `POST /equity/history/exports` | not stated in the spec | 1 / 30 s |
145
+ | `pies.list()` | `GET /equity/pies` | `pies:read` | 1 / 30 s |
146
+ | `pies.get()` | `GET /equity/pies/{id}` | `pies:read` | 1 / 5 s |
147
+ | `pies.create()` | `POST /equity/pies` | `pies:write` | 1 / 5 s |
148
+ | `pies.update()` | `POST /equity/pies/{id}` | `pies:write` | 1 / 5 s |
149
+ | `pies.duplicate()` | `POST /equity/pies/{id}/duplicate` | `pies:write` | 1 / 5 s |
150
+ | `pies.delete()` | `DELETE /equity/pies/{id}` | `pies:write` | 1 / 5 s |
151
+
152
+ ¹ Also needs `history:orders`, to find the order once it has left the pending list. ² `sellAll()` also needs
153
+ `portfolio`, to read the position.
154
+
155
+ Paths are under `/api/v0`. `portfolio.snapshot()` combines the account summary and positions. CSV exports are
156
+ live only: demo refuses them, as the contract tests confirm. Trading 212 has deprecated the pie
157
+ endpoints, which still work. The exported `ENDPOINTS` table holds the same facts at run time. See the
158
+ [documentation](#documentation) for each part.
159
+
160
+ ## Safety around real money
161
+
162
+ ```ts
163
+ // A live client reads, but refuses every order before anything is sent.
164
+ const reader = new Trading212({ environment: "live", apiKey, apiSecret });
165
+ await reader.orders.buy({ ticker: "AAPL_US_EQ", quantity: 1 }); // throws TradingDisabledError
166
+
167
+ // Preview exactly what would be sent, on any client.
168
+ const preview = await reader.orders.buy({ ticker: "AAPL_US_EQ", quantity: 1 }, { dryRun: true });
169
+ // { dryRun: true, url: "https://live.trading212.com/api/v0/equity/orders/market", body: {...}, wouldSend: false }
170
+
171
+ // Trading on live is a deliberate choice.
172
+ const trader = new Trading212({ environment: "live", apiKey, apiSecret, trading: "enabled" });
173
+ ```
174
+
175
+ - **Orders are never retried.** Reads are, with backoff.
176
+ - **If an order may have executed** (a timeout, a dropped connection, a 5xx), you get `OrderOutcomeUnknownError`.
177
+ Check `orders.list()` and `history.orders.list()` before trying again. If the connection never opened (DNS failure,
178
+ connection refused), you get a `NetworkError` with `sent: false` instead, and it is safe to retry.
179
+ - **`buy()` and `sell()` take a positive quantity** and set the sign themselves, so a stray minus sign cannot turn a
180
+ sale into a purchase. The low-level `market()`, `limit()`, `stop()` and `stopLimit()` follow the API's
181
+ convention, where a negative quantity sells.
182
+ - **Orders are checked before sending:** a ticker without spaces, at most 4 decimal places, a positive price,
183
+ `timeValidity` stated, and `extendedHours` on market orders only. Every problem is reported at once.
184
+
185
+ ## Errors
186
+
187
+ Every error extends `Trading212Error`, with `code`, `hint`, `status`, `request`, `rateLimit`, the redacted `body`
188
+ and Trading 212's own `apiCode`. `instanceof` works even if two copies of the package are loaded.
189
+
190
+ | Error | When |
191
+ | -------------------------- | ------------------------------------------------------------- |
192
+ | `ConfigurationError` | Bad options or environment variables. Nothing was sent |
193
+ | `AuthenticationError` | 401: wrong key or secret, or a key from the other environment |
194
+ | `MissingScopeError` | 403: the key lacks `error.scope`, such as `orders:execute` |
195
+ | `NotAvailableInDemoError` | 403: live-only features, such as CSV exports |
196
+ | `PermissionError` | Any other 403, such as an IP restriction |
197
+ | `ValidationError` | Invalid request, before sending (`error.issues`) or a 400 |
198
+ | `OrderRejectedError` | A refused order, with `error.reason` |
199
+ | `NotFoundError` | 404 |
200
+ | `InstrumentNotFoundError` | `resolve()` found none or several, with `error.suggestions` |
201
+ | `RateLimitError` | 429, with `retryAfterMs` and `resetAt` |
202
+ | `TimeoutError` | 408, or no answer within `timeoutMs` |
203
+ | `OrderOutcomeUnknownError` | An order may or may not have executed |
204
+ | `TradingDisabledError` | An order on live without `trading: "enabled"` |
205
+ | `NetworkError` | The connection failed; `sent` says whether it left |
206
+ | `ServerError` | 5xx |
207
+
208
+ ```ts
209
+ import { isTrading212Error, MissingScopeError } from "@kkkermit/trading212.js";
210
+
211
+ try {
212
+ await t212.orders.sellAll("AAPL_US_EQ");
213
+ } catch (error) {
214
+ if (error instanceof MissingScopeError) console.error(`Missing ${error.scope}: ${error.hint}`);
215
+ else if (isTrading212Error(error))
216
+ logger.error(error.message, error.toLog()); // safe to log as-is
217
+ else throw error;
218
+ }
219
+ ```
220
+
221
+ ## Logging and hooks
222
+
223
+ The client is silent by default. Give it a logger and it writes one line per event: every order at `info`, every
224
+ read at `debug`, and retries, long rate-limit waits and orders with an unknown outcome at `warn`. Every field is
225
+ redacted first.
226
+
227
+ ```ts
228
+ import { consoleLogger, fieldsFirst } from "@kkkermit/trading212.js";
229
+
230
+ new Trading212({ ...options, logger: consoleLogger({ level: "info" }) });
231
+ // [trading212] INFO POST /api/v0/equity/orders/market returned 200 in 212 ms endpoint=placeMarketOrder status=200 …
232
+ new Trading212({ ...options, logger: fieldsFirst(pino()) }); // pino and bunyan take (fields, message)
233
+ new Trading212({
234
+ ...options,
235
+ hooks: { onResponse: ({ endpoint, status, durationMs }) => metrics.observe(endpoint, durationMs) },
236
+ });
237
+ ```
238
+
239
+ ## Testing your code
240
+
241
+ ```ts
242
+ import { createFakeTrading212 } from "@kkkermit/trading212.js/testing";
243
+
244
+ const { client, server } = createFakeTrading212({
245
+ state: { cash: 1000, positions: [{ ticker: "AAPL_US_EQ", quantity: 2 }] },
246
+ });
247
+
248
+ await client.orders.sell({ ticker: "AAPL_US_EQ", quantity: 1 });
249
+ expect(server.position("AAPL_US_EQ")?.quantity).toBe(1);
250
+
251
+ server.marketOpen = false; // the next order is refused with market-closed
252
+ server.failNext("getAccountSummary", { status: 503 }); // or { network: "ECONNRESET" }
253
+ ```
254
+
255
+ `scriptedFetch()` is there too, for testing at the HTTP level.
256
+
257
+ ## Documentation
258
+
259
+ The full documentation is at **[kkkermit.github.io/Trading212.js](https://kkkermit.github.io/Trading212.js/)**. Its source is the MDX in
260
+ [`docs/src/content/docs`](docs/src/content/docs).
261
+
262
+ - [Getting started](https://kkkermit.github.io/Trading212.js/getting-started/): install, keys, the doctor and a first script
263
+ - [Key concepts](https://kkkermit.github.io/Trading212.js/concepts/) and [your first program](https://kkkermit.github.io/Trading212.js/tutorial/), for beginners
264
+ - [Recipes](https://kkkermit.github.io/Trading212.js/recipes/): complete programs for common tasks, in TypeScript and JavaScript
265
+ - [API coverage](https://kkkermit.github.io/Trading212.js/reference/api-coverage/): every endpoint, its method, permission and limit
266
+ - [Orders](https://kkkermit.github.io/Trading212.js/guides/orders/): order types, quantities, `sellAll`, `waitFor`, dry runs
267
+ - [Instruments and market hours](https://kkkermit.github.io/Trading212.js/guides/instruments-and-markets/), [history](https://kkkermit.github.io/Trading212.js/guides/history/),
268
+ [CSV reports](https://kkkermit.github.io/Trading212.js/guides/reports/) and the [portfolio](https://kkkermit.github.io/Trading212.js/guides/account-and-portfolio/)
269
+ - [Errors](https://kkkermit.github.io/Trading212.js/guides/errors/): every error, what causes it and how to fix it
270
+ - [Rate limits](https://kkkermit.github.io/Trading212.js/guides/rate-limits/), [logging and hooks](https://kkkermit.github.io/Trading212.js/guides/logging/) and
271
+ [configuration](https://kkkermit.github.io/Trading212.js/reference/configuration/)
272
+ - [Testing your code](https://kkkermit.github.io/Trading212.js/guides/testing/): the fake server and scripted fetch
273
+ - [Safety and security](https://kkkermit.github.io/Trading212.js/guides/safety/)
274
+ - The [reference](https://kkkermit.github.io/Trading212.js/reference/client/): every method, option, helper, error and type
275
+ - [FAQ and troubleshooting](https://kkkermit.github.io/Trading212.js/help/faq/)
276
+
277
+ ## Status
278
+
279
+ Version 0.x while the API is in beta. Types and endpoint metadata are generated from Trading 212's OpenAPI spec, and
280
+ a weekly job checks for changes. The spec does not document error bodies, so the ones demo really sends are recorded in
281
+ `fixtures/errors.json` and replayed by the tests; the few refusals not seen there yet are handled defensively. The
282
+ contract tests in `tests/contract` check the rest against demo when run with a demo key.
283
+
284
+ ## Contributing and security
285
+
286
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [AGENTS.md](AGENTS.md), and follow the [code of conduct](CODE_OF_CONDUCT.md).
287
+ Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
288
+
289
+ ## Licence
290
+
291
+ [MIT](LICENSE). "Trading 212" is a trademark of its owner, used here only to say what this library talks to.
package/package.json CHANGED
@@ -1,6 +1,135 @@
1
1
  {
2
- "name": "@kkkermit/trading212.js",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@kkkermit/trading212.js",
3
+ "version": "0.1.0",
4
+ "description": "Unofficial, zero-dependency TypeScript client for the Trading 212 Public API: typed, rate-limit aware, safe around live money, with errors that tell you how to fix them.",
5
+ "keywords": [
6
+ "trading212",
7
+ "trading 212",
8
+ "t212",
9
+ "stocks",
10
+ "isa",
11
+ "investing",
12
+ "trading",
13
+ "api",
14
+ "sdk",
15
+ "client",
16
+ "typescript"
17
+ ],
18
+ "license": "MIT",
19
+ "author": "kkkermit",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Kkkermit/Trading212.js.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/Kkkermit/Trading212.js/issues"
26
+ },
27
+ "homepage": "https://github.com/Kkkermit/Trading212.js#readme",
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "engines": {
31
+ "node": ">=22"
32
+ },
33
+ "main": "./dist/index.cjs",
34
+ "module": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "import": {
39
+ "types": "./dist/index.d.ts",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/index.d.cts",
44
+ "default": "./dist/index.cjs"
45
+ }
46
+ },
47
+ "./testing": {
48
+ "import": {
49
+ "types": "./dist/testing.d.ts",
50
+ "default": "./dist/testing.js"
51
+ },
52
+ "require": {
53
+ "types": "./dist/testing.d.cts",
54
+ "default": "./dist/testing.cjs"
55
+ }
56
+ },
57
+ "./csv": {
58
+ "import": {
59
+ "types": "./dist/csv.d.ts",
60
+ "default": "./dist/csv.js"
61
+ },
62
+ "require": {
63
+ "types": "./dist/csv.d.cts",
64
+ "default": "./dist/csv.cjs"
65
+ }
66
+ },
67
+ "./package.json": "./package.json"
68
+ },
69
+ "bin": {
70
+ "trading212": "./dist/cli.js"
71
+ },
72
+ "files": [
73
+ "dist",
74
+ "README.md",
75
+ "LICENSE",
76
+ "CHANGELOG.md"
77
+ ],
78
+ "publishConfig": {
79
+ "access": "public",
80
+ "provenance": true
81
+ },
82
+ "scripts": {
83
+ "build": "tsup",
84
+ "check": "npm run typecheck && npm run lint && npm run format:check && npm run test && npm run build && npm run check:package && npm run size",
85
+ "check:package": "publint --strict && attw --pack . --profile node16",
86
+ "typecheck": "tsc --noEmit && tsc --noEmit -p examples && tsc --noEmit -p docs/src/examples",
87
+ "lint": "eslint .",
88
+ "lint:fix": "eslint . --fix",
89
+ "format": "prettier --write .",
90
+ "format:check": "prettier --check .",
91
+ "test": "vitest run --coverage",
92
+ "test:watch": "vitest",
93
+ "test:demo": "vitest run --config vitest.contract.config.ts",
94
+ "size": "size-limit",
95
+ "generate": "tsx scripts/generateApi.ts",
96
+ "generate:check": "tsx scripts/generateApi.ts --check",
97
+ "spec:fetch": "tsx scripts/fetchSpec.ts",
98
+ "fixtures:capture": "tsx scripts/captureErrors.ts",
99
+ "secrets": "secretlint \"**/*\"",
100
+ "changeset": "changeset",
101
+ "release": "npm run check && changeset publish",
102
+ "prepare": "husky",
103
+ "smoke": "node scripts/smoke.mjs && node scripts/smoke.cjs",
104
+ "example": "tsx --tsconfig examples/tsconfig.json",
105
+ "docs:dev": "node scripts/docs.mjs dev",
106
+ "docs:build": "node scripts/docs.mjs build"
107
+ },
108
+ "devDependencies": {
109
+ "@arethetypeswrong/cli": "^0.18.5",
110
+ "@changesets/cli": "^3.0.3",
111
+ "@commitlint/cli": "^21.2.4",
112
+ "@eslint/js": "^10.0.1",
113
+ "@secretlint/secretlint-rule-preset-recommend": "^13.1.0",
114
+ "@size-limit/preset-small-lib": "^14.3.0",
115
+ "@types/node": "^22.20.5",
116
+ "@vitest/coverage-v8": "^5.1.0",
117
+ "eslint": "^10.14.0",
118
+ "eslint-config-prettier": "^10.1.8",
119
+ "husky": "^9.1.7",
120
+ "lint-staged": "^17.6.0",
121
+ "prettier": "^3.9.10",
122
+ "publint": "^0.3.25",
123
+ "secretlint": "^13.1.0",
124
+ "size-limit": "^14.3.0",
125
+ "tsup": "^9.0.0",
126
+ "tsx": "^4.23.15",
127
+ "typescript": "~6.0.3",
128
+ "typescript-eslint": "^8.72.1",
129
+ "vitest": "^5.1.0",
130
+ "yaml": "^2.9.1"
131
+ },
132
+ "overrides": {
133
+ "minimatch": "^11.0.5"
134
+ }
135
+ }