@teralabs/shipstack 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +418 -0
- package/dist/index.d.mts +2741 -0
- package/dist/index.d.ts +2741 -0
- package/dist/index.js +9431 -0
- package/dist/index.mjs +9380 -0
- package/docs/API.md +176 -0
- package/docs/address-validation.md +114 -0
- package/docs/architecture.md +220 -0
- package/docs/carriers.md +180 -0
- package/docs/config.example.ts +32 -0
- package/docs/errors.md +150 -0
- package/docs/examples/actual-shipment.ts +59 -0
- package/docs/examples/basic.ts +131 -0
- package/docs/examples/nextjs-route.ts +22 -0
- package/docs/examples/staged-shipment.md +46 -0
- package/docs/index.md +286 -0
- package/docs/rates.md +145 -0
- package/docs/shipments.md +157 -0
- package/docs/tracking.md +115 -0
- package/package.json +98 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
# Shipstack
|
|
2
|
+
|
|
3
|
+
Shipstack is a high-performance, type-safe, framework-agnostic shipping SDK for USPS, FedEx, and UPS.
|
|
4
|
+
It provides unified rates, address validation, tracking, and shipment creation through a consistent, predictable API.
|
|
5
|
+
|
|
6
|
+
Shipstack is designed for:
|
|
7
|
+
|
|
8
|
+
- Storefronts (Nuxt, Next.js, SvelteKit, Remix)
|
|
9
|
+
- Serverless functions (Cloudflare, Vercel, Netlify)
|
|
10
|
+
- Node.js backends
|
|
11
|
+
- Edge runtimes
|
|
12
|
+
- Open-source projects that need reliable logistics tooling
|
|
13
|
+
|
|
14
|
+
Shipstack does not assume a backend, database, or dashboard.
|
|
15
|
+
You choose the workflow. Shipstack provides the building blocks.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
### Unified Orchestration
|
|
22
|
+
A single SDK surface for USPS, FedEx, and UPS.
|
|
23
|
+
|
|
24
|
+
### Multi-Carrier Rates
|
|
25
|
+
Fetch normalized rates from any carrier and rank them for checkout.
|
|
26
|
+
|
|
27
|
+
### Address Validation
|
|
28
|
+
Standardized address verification across all carriers.
|
|
29
|
+
|
|
30
|
+
### Tracking Aggregator
|
|
31
|
+
Batch tracking with automatic carrier-specific chunking and concurrency limits.
|
|
32
|
+
|
|
33
|
+
### Staged Shipments (Safe Mode)
|
|
34
|
+
Generate carrier-specific shipment payloads without purchasing a label.
|
|
35
|
+
|
|
36
|
+
### Actual Label Creation (Advanced Mode)
|
|
37
|
+
Backend-only workflow for purchasing real labels.
|
|
38
|
+
|
|
39
|
+
### Type-Safe
|
|
40
|
+
Strict TypeScript definitions for public request, response, and normalized models.
|
|
41
|
+
|
|
42
|
+
### Runtime Agnostic
|
|
43
|
+
Works in Node.js, Bun, Deno, Cloudflare Workers, and other modern JavaScript runtimes.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @teralabs/shipstack
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
### 1. Create your config and clients
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { ShippingClient, ShippingManager } from "@teralabs/shipstack";
|
|
61
|
+
|
|
62
|
+
const config = {
|
|
63
|
+
environment: "sandbox",
|
|
64
|
+
usps: {
|
|
65
|
+
enabled: true,
|
|
66
|
+
clientId: "YOUR_USPS_CLIENT_ID",
|
|
67
|
+
clientSecret: "YOUR_USPS_CLIENT_SECRET",
|
|
68
|
+
baseUrl: "https://sandbox.api.usps.com"
|
|
69
|
+
},
|
|
70
|
+
fedex: {
|
|
71
|
+
enabled: true,
|
|
72
|
+
clientId: "YOUR_FEDEX_CLIENT_ID",
|
|
73
|
+
clientSecret: "YOUR_FEDEX_CLIENT_SECRET",
|
|
74
|
+
accountNumber: "YOUR_FEDEX_ACCOUNT_NUMBER"
|
|
75
|
+
},
|
|
76
|
+
ups: {
|
|
77
|
+
enabled: true,
|
|
78
|
+
clientId: "YOUR_UPS_CLIENT_ID",
|
|
79
|
+
clientSecret: "YOUR_UPS_CLIENT_SECRET",
|
|
80
|
+
accountNumber: "YOUR_UPS_ACCOUNT_NUMBER"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const client = new ShippingClient(config);
|
|
85
|
+
const manager = new ShippingManager(config);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Use `ShippingClient` for direct per-carrier operations. Use `ShippingManager` for multi-carrier checkout ranking.
|
|
89
|
+
|
|
90
|
+
### 2. Compare carriers with `ShippingManager`
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const rankedRates = await manager.getRankedRates(
|
|
94
|
+
{
|
|
95
|
+
originZip: "90210",
|
|
96
|
+
destZip: "10001",
|
|
97
|
+
weightOz: 16,
|
|
98
|
+
lengthInches: 10,
|
|
99
|
+
widthInches: 5,
|
|
100
|
+
heightInches: 5
|
|
101
|
+
},
|
|
102
|
+
["usps", "fedex", "ups"]
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const cheapest = rankedRates.find(rate => rate.isCheapest);
|
|
106
|
+
const fastest = rankedRates.find(rate => rate.isFastest);
|
|
107
|
+
|
|
108
|
+
if (cheapest) {
|
|
109
|
+
console.log("Cheapest:", cheapest.serviceName, cheapest.cost.amount);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (fastest) {
|
|
113
|
+
console.log("Fastest:", fastest.serviceName, fastest.deliveryDays);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3. Track with `ShippingClient`
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const tracking = await client.track(
|
|
121
|
+
["9400100000000000000000", "9400100000000000000001"],
|
|
122
|
+
"usps"
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
tracking.forEach(pkg => {
|
|
126
|
+
console.log("Status of " + pkg.trackingNumber + ": " + pkg.status);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Best Value (Functional API)
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { getBestValueRate } from "@teralabs/shipstack";
|
|
134
|
+
|
|
135
|
+
const best = await getBestValueRate(
|
|
136
|
+
{
|
|
137
|
+
carrier: "usps",
|
|
138
|
+
originZip: "90210",
|
|
139
|
+
destZip: "10001",
|
|
140
|
+
weightOz: 16,
|
|
141
|
+
lengthInches: 10,
|
|
142
|
+
widthInches: 5,
|
|
143
|
+
heightInches: 5
|
|
144
|
+
},
|
|
145
|
+
config
|
|
146
|
+
);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Fastest Service (Functional API)
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { getFastestService } from "@teralabs/shipstack";
|
|
153
|
+
|
|
154
|
+
const fastest = await getFastestService(
|
|
155
|
+
{
|
|
156
|
+
carrier: "fedex",
|
|
157
|
+
originZip: "90210",
|
|
158
|
+
destZip: "10001",
|
|
159
|
+
weightOz: 16,
|
|
160
|
+
lengthInches: 10,
|
|
161
|
+
widthInches: 5,
|
|
162
|
+
heightInches: 5
|
|
163
|
+
},
|
|
164
|
+
config
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Validate an Address
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const result = await client.validateAddress({
|
|
174
|
+
carrier: "fedex",
|
|
175
|
+
address: {
|
|
176
|
+
streetLines: ["123 Main St"],
|
|
177
|
+
city: "New York",
|
|
178
|
+
stateOrProvinceCode: "NY",
|
|
179
|
+
postalCode: "10001",
|
|
180
|
+
countryCode: "US"
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Track Shipments
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { trackShipment } from "@teralabs/shipstack";
|
|
191
|
+
|
|
192
|
+
const tracking = await trackShipment(
|
|
193
|
+
["9400100000000000000000", "9400100000000000000001"],
|
|
194
|
+
"usps",
|
|
195
|
+
config
|
|
196
|
+
);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Staged Shipments (Safe Mode)
|
|
202
|
+
|
|
203
|
+
Generate a carrier-specific shipment payload without purchasing a label.
|
|
204
|
+
|
|
205
|
+
This is recommended for:
|
|
206
|
+
|
|
207
|
+
- Storefronts
|
|
208
|
+
- Email workflows
|
|
209
|
+
- Serverless environments
|
|
210
|
+
- Custom dashboards
|
|
211
|
+
- Manual label creation
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import { buildShipment } from "@teralabs/shipstack";
|
|
215
|
+
|
|
216
|
+
const staged = await buildShipment(
|
|
217
|
+
{
|
|
218
|
+
carrier: "fedex",
|
|
219
|
+
serviceCode: "FEDEX_GROUND",
|
|
220
|
+
fromAddress: {
|
|
221
|
+
name: "Sender Name",
|
|
222
|
+
streetLines: ["123 Warehouse Rd"],
|
|
223
|
+
city: "Los Angeles",
|
|
224
|
+
stateOrProvinceCode: "CA",
|
|
225
|
+
postalCode: "90001",
|
|
226
|
+
countryCode: "US"
|
|
227
|
+
},
|
|
228
|
+
toAddress: {
|
|
229
|
+
name: "Customer Name",
|
|
230
|
+
streetLines: ["55 W 46th St"],
|
|
231
|
+
city: "New York",
|
|
232
|
+
stateOrProvinceCode: "NY",
|
|
233
|
+
postalCode: "10036",
|
|
234
|
+
countryCode: "US"
|
|
235
|
+
},
|
|
236
|
+
package: {
|
|
237
|
+
weightOz: 32,
|
|
238
|
+
lengthInches: 12,
|
|
239
|
+
widthInches: 8,
|
|
240
|
+
heightInches: 4
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
config
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
console.log(staged.payload);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
This does not call the carrier API.
|
|
250
|
+
This does not purchase postage.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Actual Label Creation (Advanced Mode)
|
|
255
|
+
|
|
256
|
+
This method purchases a real label and must only be used in secure backend environments.
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import { createShipment } from "@teralabs/shipstack";
|
|
260
|
+
|
|
261
|
+
const shipment = await createShipment(
|
|
262
|
+
{
|
|
263
|
+
carrier: "ups",
|
|
264
|
+
serviceCode: "UPS_GROUND",
|
|
265
|
+
fromAddress: {
|
|
266
|
+
name: "Sender Name",
|
|
267
|
+
streetLines: ["123 Warehouse Rd"],
|
|
268
|
+
city: "Los Angeles",
|
|
269
|
+
stateOrProvinceCode: "CA",
|
|
270
|
+
postalCode: "90001",
|
|
271
|
+
countryCode: "US"
|
|
272
|
+
},
|
|
273
|
+
toAddress: {
|
|
274
|
+
name: "Customer Name",
|
|
275
|
+
streetLines: ["55 W 46th St"],
|
|
276
|
+
city: "New York",
|
|
277
|
+
stateOrProvinceCode: "NY",
|
|
278
|
+
postalCode: "10036",
|
|
279
|
+
countryCode: "US"
|
|
280
|
+
},
|
|
281
|
+
package: {
|
|
282
|
+
weightOz: 48,
|
|
283
|
+
lengthInches: 14,
|
|
284
|
+
widthInches: 10,
|
|
285
|
+
heightInches: 6
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
config
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
console.log(shipment.label.base64);
|
|
292
|
+
console.log(shipment.trackingNumber);
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Warning:
|
|
296
|
+
Do not call this from the frontend.
|
|
297
|
+
This charges the merchant's carrier account.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Safe vs Advanced Usage
|
|
302
|
+
|
|
303
|
+
| Feature | buildShipment() | createShipment() |
|
|
304
|
+
|--------|-----------------|------------------|
|
|
305
|
+
| Generates carrier payload | Yes | Yes |
|
|
306
|
+
| Purchases a label | No | Yes |
|
|
307
|
+
| Safe for frontend | Yes | No |
|
|
308
|
+
| Requires secure backend | No | Yes |
|
|
309
|
+
| Good for email workflows | Yes | Limited |
|
|
310
|
+
| Good for automation | Limited | Yes |
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Functional API (Stateless)
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import {
|
|
318
|
+
getRates,
|
|
319
|
+
getBestValueRate,
|
|
320
|
+
getFastestService,
|
|
321
|
+
predictCarrier,
|
|
322
|
+
validateAddress,
|
|
323
|
+
trackShipment
|
|
324
|
+
} from "@teralabs/shipstack";
|
|
325
|
+
|
|
326
|
+
const rates = await getRates(
|
|
327
|
+
{
|
|
328
|
+
carrier: "usps",
|
|
329
|
+
originZip: "90210",
|
|
330
|
+
destZip: "10001",
|
|
331
|
+
weightOz: 16,
|
|
332
|
+
lengthInches: 10,
|
|
333
|
+
widthInches: 5,
|
|
334
|
+
heightInches: 5
|
|
335
|
+
},
|
|
336
|
+
config
|
|
337
|
+
);
|
|
338
|
+
const carrier = predictCarrier("1Z9999999999999999");
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Direct Carrier Access
|
|
344
|
+
|
|
345
|
+
Only the low-level rate clients are exported directly from the package entrypoint.
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
import { createUspsRatesClient } from "@teralabs/shipstack";
|
|
349
|
+
|
|
350
|
+
const usps = createUspsRatesClient(config.usps);
|
|
351
|
+
const rawResponse = await usps.getRates({ /* USPS request payload */ });
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Error Handling
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { ShipstackError } from "@teralabs/shipstack";
|
|
360
|
+
|
|
361
|
+
const request = {
|
|
362
|
+
carrier: "usps",
|
|
363
|
+
originZip: "90210",
|
|
364
|
+
destZip: "10001",
|
|
365
|
+
weightOz: 16,
|
|
366
|
+
lengthInches: 10,
|
|
367
|
+
widthInches: 5,
|
|
368
|
+
heightInches: 5
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
await client.getRates(request);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
if (error instanceof ShipstackError) {
|
|
375
|
+
console.error(error.message);
|
|
376
|
+
console.error(error.carrier);
|
|
377
|
+
console.error(error.cause);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Development
|
|
385
|
+
|
|
386
|
+
**npm run generate:usps** (sync USPS v3 specs)
|
|
387
|
+
|
|
388
|
+
**npm run generate:fedex** (sync local FedEx specs)
|
|
389
|
+
|
|
390
|
+
**npm run generate:ups** (sync local UPS specs)
|
|
391
|
+
|
|
392
|
+
**npm run generate:all** (sync all carriers)
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
npm run generate:usps
|
|
396
|
+
npm run generate:fedex
|
|
397
|
+
npm run generate:ups
|
|
398
|
+
npm run generate:all
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Documentation
|
|
404
|
+
|
|
405
|
+
- docs/index.md
|
|
406
|
+
- docs/API.md
|
|
407
|
+
- docs/config.example.ts
|
|
408
|
+
- CHANGELOG.md
|
|
409
|
+
- CONTRIBUTING.md
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## License
|
|
414
|
+
|
|
415
|
+
ISC License
|
|
416
|
+
Copyright (c) 2026
|
|
417
|
+
|
|
418
|
+
---
|