node-chargepoint 0.0.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.
- package/LICENSE +21 -0
- package/README.md +460 -0
- package/dist/cli.cjs +814 -0
- package/dist/index.cjs +712 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +438 -0
- package/dist/index.d.ts +438 -0
- package/dist/index.js +700 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Marc Billow
|
|
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
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# node-chargepoint
|
|
2
|
+
|
|
3
|
+
A simple, async Node.js/TypeScript wrapper around the ChargePoint EV Charging Network API.
|
|
4
|
+
|
|
5
|
+
> Based on [python-chargepoint](https://github.com/mbillow/python-chargepoint) by Marc Billow (MIT).
|
|
6
|
+
|
|
7
|
+
## Disclaimer
|
|
8
|
+
|
|
9
|
+
This project is not affiliated with or endorsed by ChargePoint in any way. Use at your own risk.
|
|
10
|
+
ChargePoint is a registered trademark of ChargePoint, Inc.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add node-chargepoint
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Requires **Node.js ≥ 24**.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Library Usage
|
|
25
|
+
|
|
26
|
+
All client methods are `async` and return Promises.
|
|
27
|
+
|
|
28
|
+
### Authentication
|
|
29
|
+
|
|
30
|
+
Three authentication methods are supported. The client is created via the async factory `ChargePoint.create()`.
|
|
31
|
+
|
|
32
|
+
**Password:**
|
|
33
|
+
```typescript
|
|
34
|
+
import { ChargePoint } from 'node-chargepoint';
|
|
35
|
+
|
|
36
|
+
const client = await ChargePoint.create('user@example.com');
|
|
37
|
+
await client.loginWithPassword('password');
|
|
38
|
+
// ...
|
|
39
|
+
await client.logout();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Long-lived session token** (recommended for automation):
|
|
43
|
+
```typescript
|
|
44
|
+
const client = await ChargePoint.create('user@example.com', {
|
|
45
|
+
coulombToken: '<coulomb_sess cookie value>',
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**SSO JWT:**
|
|
50
|
+
```typescript
|
|
51
|
+
const client = await ChargePoint.create('user@example.com');
|
|
52
|
+
await client.loginWithSsoSession('<sso jwt>');
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### Obtaining Tokens Manually
|
|
58
|
+
|
|
59
|
+
Password-based login may be blocked by bot-protection (Datadome). When that happens,
|
|
60
|
+
you can capture a token directly from your browser and pass it to the client.
|
|
61
|
+
|
|
62
|
+
1. Open [https://driver.chargepoint.com](https://driver.chargepoint.com) in your browser and log in normally.
|
|
63
|
+
2. Open Developer Tools and navigate to **Application > Cookies > https://driver.chargepoint.com**.
|
|
64
|
+
3. Copy the value of one of the following cookies:
|
|
65
|
+
|
|
66
|
+
| Cookie | Use as |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `coulomb_sess` | `coulombToken:` option (recommended — long-lived) |
|
|
69
|
+
| `auth-session` | `loginWithSsoSession()` (shorter-lived JWT) |
|
|
70
|
+
|
|
71
|
+
> **Note:** The `coulomb_sess` value contains `#` and `?` characters. When setting it as a
|
|
72
|
+
> shell environment variable, always wrap the value in **double quotes** to prevent the shell
|
|
73
|
+
> from interpreting `#` as a comment:
|
|
74
|
+
>
|
|
75
|
+
> ```bash
|
|
76
|
+
> export CP_TOKEN="Ab3dEf...token...#D???????#RNA-US"
|
|
77
|
+
> ```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### Account
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const account = await client.getAccount();
|
|
85
|
+
console.log(account.user.fullName); // "Jane Smith"
|
|
86
|
+
console.log(account.accountBalance.amount); // 12.34
|
|
87
|
+
|
|
88
|
+
const vehicles = await client.getVehicles();
|
|
89
|
+
for (const ev of vehicles) {
|
|
90
|
+
console.log(`${ev.year} ${ev.make} ${ev.model}`); // "2023 Polestar 2"
|
|
91
|
+
console.log(` AC: ${ev.chargingSpeed} kW DC: ${ev.dcChargingSpeed} kW`);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### Home Charger
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const chargerIds = await client.getHomeChargers();
|
|
101
|
+
// [12345678]
|
|
102
|
+
|
|
103
|
+
const chargerId = chargerIds[0];
|
|
104
|
+
|
|
105
|
+
const status = await client.getHomeChargerStatus(chargerId);
|
|
106
|
+
// {
|
|
107
|
+
// chargerId: 12345678,
|
|
108
|
+
// brand: 'CP',
|
|
109
|
+
// model: 'HOME FLEX',
|
|
110
|
+
// chargingStatus: 'AVAILABLE',
|
|
111
|
+
// isPluggedIn: true,
|
|
112
|
+
// isConnected: true,
|
|
113
|
+
// amperageLimit: 28,
|
|
114
|
+
// possibleAmperageLimits: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
const tech = await client.getHomeChargerTechnicalInfo(chargerId);
|
|
118
|
+
// {
|
|
119
|
+
// modelNumber: 'CPH50-NEMA6-50-L23',
|
|
120
|
+
// serialNumber: '...',
|
|
121
|
+
// softwareVersion: '1.2.3.4',
|
|
122
|
+
// lastConnectedAt: '2024-06-01T08:30:00Z'
|
|
123
|
+
// }
|
|
124
|
+
|
|
125
|
+
const config = await client.getHomeChargerConfig(chargerId);
|
|
126
|
+
// {
|
|
127
|
+
// stationNickname: 'Home Flex',
|
|
128
|
+
// ledBrightness: { level: 5, supportedLevels: [0,1,2,3,4,5] },
|
|
129
|
+
// utility: { name: 'Austin Energy', ... }
|
|
130
|
+
// }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Amperage limit
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Print valid amperage values
|
|
137
|
+
console.log(status.possibleAmperageLimits);
|
|
138
|
+
// [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
|
|
139
|
+
|
|
140
|
+
await client.setAmperageLimit(chargerId, 24);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### LED brightness
|
|
144
|
+
|
|
145
|
+
Levels map to: `0`=off, `1`=20%, `2`=40%, `3`=60%, `4`=80%, `5`=100%.
|
|
146
|
+
Available levels are returned by `getHomeChargerConfig()`.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
await client.setLedBrightness(chargerId, 3); // 60%
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Restart
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
await client.restartHomeCharger(chargerId);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### Charging schedule
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const schedule = await client.getHomeChargerSchedule(chargerId);
|
|
162
|
+
console.log(schedule.scheduleEnabled); // false
|
|
163
|
+
console.log(schedule.defaultSchedule.weekdays.startTime); // "23:00"
|
|
164
|
+
console.log(schedule.defaultSchedule.weekdays.endTime); // "07:00"
|
|
165
|
+
console.log(schedule.defaultSchedule.weekends.startTime); // "19:00"
|
|
166
|
+
console.log(schedule.defaultSchedule.weekends.endTime); // "15:00"
|
|
167
|
+
|
|
168
|
+
// Enable a schedule
|
|
169
|
+
const updated = await client.setHomeChargerSchedule(
|
|
170
|
+
chargerId,
|
|
171
|
+
'23:00', '07:00', // weekday start, weekday end
|
|
172
|
+
'19:00', '15:00', // weekend start, weekend end
|
|
173
|
+
);
|
|
174
|
+
console.log(updated.scheduleEnabled); // true
|
|
175
|
+
|
|
176
|
+
// Disable the schedule
|
|
177
|
+
await client.disableHomeChargerSchedule(chargerId);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Charging Status and Sessions
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
const status = await client.getUserChargingStatus();
|
|
186
|
+
if (status) {
|
|
187
|
+
console.log(status.state); // "CHARGING"
|
|
188
|
+
console.log(status.sessionId); // 1234567890
|
|
189
|
+
|
|
190
|
+
const session = await client.getChargingSession(status.sessionId);
|
|
191
|
+
console.log(session.chargingState); // "CHARGING"
|
|
192
|
+
console.log(session.energyKwh); // 6.42
|
|
193
|
+
console.log(session.milesAdded); // 22.3
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### Starting and stopping a session
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// Stop the current session
|
|
201
|
+
const session = await client.getChargingSession(status.sessionId);
|
|
202
|
+
await session.stop();
|
|
203
|
+
|
|
204
|
+
// Start a new session on any device
|
|
205
|
+
const newSession = await client.startChargingSession(chargerId);
|
|
206
|
+
console.log(newSession.sessionId);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Station Info
|
|
212
|
+
|
|
213
|
+
Fetch detailed information about any station by device ID — ports, pricing, connector types, and real-time status.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const info = await client.getStation(13055991);
|
|
217
|
+
console.log(info.name.join(' / ')); // "DOMAIN TOWER 2 / LVL 2_STATION 2"
|
|
218
|
+
console.log(info.address.address1); // "10025 Alterra Pkwy"
|
|
219
|
+
console.log(info.stationStatusV2); // "available"
|
|
220
|
+
console.log(info.portsInfo.totalCount); // 2
|
|
221
|
+
|
|
222
|
+
for (const port of info.portsInfo.ports) {
|
|
223
|
+
console.log(`Port ${port.outletNumber}: ${port.statusV2} (Level ${port.level})`);
|
|
224
|
+
for (const c of port.connectorList) {
|
|
225
|
+
console.log(` ${c.displayPlugType}: ${c.statusV2}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (info.stationPrice) {
|
|
230
|
+
for (const tou of info.stationPrice.touFees) {
|
|
231
|
+
console.log(`Rate: ${tou.price} ${info.stationPrice.currencyCode}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### Nearby Stations
|
|
239
|
+
|
|
240
|
+
Fetch all charging stations visible within a geographic bounding box.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import type { MapFilter, ZoomBounds } from 'node-chargepoint';
|
|
244
|
+
|
|
245
|
+
const bounds: ZoomBounds = {
|
|
246
|
+
swLat: 30.37, swLon: -97.66,
|
|
247
|
+
neLat: 30.40, neLon: -97.64,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// No filter — return all stations
|
|
251
|
+
const stations = await client.getNearbyStations(bounds);
|
|
252
|
+
|
|
253
|
+
// Optional: filter by connector type or status
|
|
254
|
+
const filter: MapFilter = {
|
|
255
|
+
connectorL2: true,
|
|
256
|
+
connectorCombo: true,
|
|
257
|
+
statusAvailable: true,
|
|
258
|
+
};
|
|
259
|
+
const filtered = await client.getNearbyStations(bounds, filter);
|
|
260
|
+
|
|
261
|
+
for (const s of filtered) {
|
|
262
|
+
console.log(`${s.name1} — ${s.stationStatusV2}`);
|
|
263
|
+
if (s.isHome && s.chargingInfo) {
|
|
264
|
+
console.log(` Charging: ${s.chargingStatus}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**`MapFilter` fields** (all `boolean`, all optional):
|
|
270
|
+
|
|
271
|
+
| Field | Description |
|
|
272
|
+
|---|---|
|
|
273
|
+
| `connectorL2` | Level 2 AC |
|
|
274
|
+
| `connectorCombo` | CCS combo (DC) |
|
|
275
|
+
| `connectorChademo` | CHAdeMO (DC) |
|
|
276
|
+
| `connectorTesla` | Tesla proprietary |
|
|
277
|
+
| `connectorL1` | Level 1 AC |
|
|
278
|
+
| `connectorL2Tesla` | Tesla Level 2 |
|
|
279
|
+
| `connectorL2Nema1450` | NEMA 14-50 |
|
|
280
|
+
| `dcFastCharging` | Any DC fast charger |
|
|
281
|
+
| `statusAvailable` | Only available stations |
|
|
282
|
+
| `priceFree` | Only free stations |
|
|
283
|
+
| `vanAccessible` | Van-accessible spaces |
|
|
284
|
+
| `disabledParking` | Disability-accessible parking |
|
|
285
|
+
| `networkChargepoint` | ChargePoint network |
|
|
286
|
+
| `networkEvgo` | EVgo network |
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## CLI
|
|
291
|
+
|
|
292
|
+
After installation, a `chargepoint` command is available.
|
|
293
|
+
|
|
294
|
+
### Authentication
|
|
295
|
+
|
|
296
|
+
Credentials are read from environment variables:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
export CP_USERNAME="user@example.com"
|
|
300
|
+
export CP_TOKEN="<coulomb_sess cookie value>"
|
|
301
|
+
# or use --password / CP_PASSWORD for password-based login
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Commands
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Show account info
|
|
308
|
+
chargepoint -u user@example.com -t $CP_TOKEN account
|
|
309
|
+
|
|
310
|
+
# List registered vehicles
|
|
311
|
+
chargepoint -u user@example.com -t $CP_TOKEN vehicles
|
|
312
|
+
|
|
313
|
+
# Show current charging session status
|
|
314
|
+
chargepoint -u user@example.com -t $CP_TOKEN status
|
|
315
|
+
|
|
316
|
+
# List home charger IDs
|
|
317
|
+
chargepoint -u user@example.com -t $CP_TOKEN chargers
|
|
318
|
+
|
|
319
|
+
# Start a charging session on a device
|
|
320
|
+
chargepoint -u user@example.com -t $CP_TOKEN start <deviceId>
|
|
321
|
+
|
|
322
|
+
# Show details for a charging station
|
|
323
|
+
chargepoint -u user@example.com -t $CP_TOKEN station <deviceId>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Global options:
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
-u, --username <username> ChargePoint username (or set CP_USERNAME)
|
|
330
|
+
-t, --token <token> Coulomb session token (or set CP_TOKEN)
|
|
331
|
+
-p, --password <password> Password login fallback (or set CP_PASSWORD)
|
|
332
|
+
-V, --version Print version
|
|
333
|
+
-h, --help Display help
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Error Handling
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import {
|
|
342
|
+
ChargePoint,
|
|
343
|
+
LoginError,
|
|
344
|
+
InvalidSession,
|
|
345
|
+
DatadomeCaptcha,
|
|
346
|
+
CommunicationError,
|
|
347
|
+
APIError,
|
|
348
|
+
} from 'node-chargepoint';
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
await client.loginWithPassword('bad-password');
|
|
352
|
+
} catch (err) {
|
|
353
|
+
if (err instanceof LoginError) {
|
|
354
|
+
console.error('Wrong credentials');
|
|
355
|
+
} else if (err instanceof InvalidSession) {
|
|
356
|
+
console.error('Session expired — re-authenticate');
|
|
357
|
+
} else if (err instanceof DatadomeCaptcha) {
|
|
358
|
+
console.error('Bot protection triggered:', err.captchaUrl);
|
|
359
|
+
} else if (err instanceof CommunicationError) {
|
|
360
|
+
console.error(`API error ${err.statusCode}:`, err.message);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**Error hierarchy:**
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
APIError
|
|
369
|
+
├── CommunicationError (non-2xx response)
|
|
370
|
+
│ ├── LoginError (authentication failed)
|
|
371
|
+
│ └── InvalidSession (session expired — HTTP 401)
|
|
372
|
+
└── DatadomeCaptcha (Datadome bot protection — HTTP 403)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Development
|
|
378
|
+
|
|
379
|
+
### Setup
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
git clone https://github.com/musicbender/node-chargepoint.git
|
|
383
|
+
cd node-chargepoint
|
|
384
|
+
pnpm install
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Type checking
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
pnpm typecheck
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Tests
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
pnpm test
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Tests use [Vitest](https://vitest.dev/) and [MSW](https://mswjs.io/) to intercept `fetch` calls. No real network traffic is made during tests.
|
|
400
|
+
|
|
401
|
+
### E2E Tests
|
|
402
|
+
|
|
403
|
+
An optional E2E suite runs against the live ChargePoint API using a real home charger. It requires valid credentials and is not run as part of `pnpm test`.
|
|
404
|
+
|
|
405
|
+
**Setup:**
|
|
406
|
+
|
|
407
|
+
Create a `.env.e2e` file in the project root (it is gitignored):
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
CP_USERNAME=your-chargepoint-email@example.com
|
|
411
|
+
CP_PASSWORD=your-chargepoint-password
|
|
412
|
+
|
|
413
|
+
# After first run, paste the printed token here to avoid re-logging in:
|
|
414
|
+
# CP_TOKEN=
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
> **Tip:** `coulomb_sess` tokens contain special characters (`#`, `?`). Always wrap the value in
|
|
418
|
+
> double quotes when exporting to your shell:
|
|
419
|
+
> ```bash
|
|
420
|
+
> export CP_TOKEN="abc...#D???"
|
|
421
|
+
> ```
|
|
422
|
+
|
|
423
|
+
**Run (read-only — safe with charger in any state):**
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
pnpm test:e2e
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
On first run the session token is printed. Paste it into `CP_TOKEN` in `.env.e2e` to skip
|
|
430
|
+
password re-authentication on subsequent runs.
|
|
431
|
+
|
|
432
|
+
**Run with mutation tests** (schedule, amperage limit, LED brightness — each test restores
|
|
433
|
+
original values):
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
E2E_MUTATIONS=true pnpm test:e2e
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The following operations are intentionally excluded from the automated suite due to their
|
|
440
|
+
disruptive nature: `restartHomeCharger()`, `startChargingSession()`, `stopChargingSession()`.
|
|
441
|
+
Use the CLI to invoke these manually.
|
|
442
|
+
|
|
443
|
+
### Build
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
pnpm build
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Produces `dist/index.js` (ESM), `dist/index.cjs` (CommonJS), and `dist/index.d.ts` (type declarations) via [tsup](https://tsup.egoist.dev/).
|
|
450
|
+
|
|
451
|
+
### Checks
|
|
452
|
+
|
|
453
|
+
| Command | Purpose |
|
|
454
|
+
|---|---|
|
|
455
|
+
| `pnpm typecheck` | TypeScript strict type checking |
|
|
456
|
+
| `pnpm test` | Run all tests |
|
|
457
|
+
| `pnpm build` | Build CJS + ESM + `.d.ts` |
|
|
458
|
+
| `pnpm prepublishOnly` | Build + typecheck (runs automatically before `pnpm publish`) |
|
|
459
|
+
|
|
460
|
+
---
|