@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/docs/errors.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Error Handling in Shipstack
|
|
2
|
+
|
|
3
|
+
Shipstack provides a unified, predictable error-handling system across USPS, FedEx, and UPS.
|
|
4
|
+
All Shipstack-specific errors extend from `ShipstackError`.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Shipstack errors fall into two main categories:
|
|
11
|
+
|
|
12
|
+
1. **ShipstackError** for normalized library failures
|
|
13
|
+
2. **ThrottlingError** when a carrier enforces rate limits
|
|
14
|
+
|
|
15
|
+
Carrier-specific details are preserved on the error's `cause` property when available.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Base Error: `ShipstackError`
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
class ShipstackError extends Error {
|
|
23
|
+
carrier: "usps" | "fedex" | "ups";
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Fields
|
|
29
|
+
|
|
30
|
+
| Field | Description |
|
|
31
|
+
|-------|-------------|
|
|
32
|
+
| `message` | Human-readable error message |
|
|
33
|
+
| `carrier` | The carrier associated with the failure |
|
|
34
|
+
| `cause` | Raw upstream error, response, or context when available |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Example: Catching Errors
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
try {
|
|
42
|
+
await client.getRates(request);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (err instanceof ShipstackError) {
|
|
45
|
+
console.error("Carrier:", err.carrier);
|
|
46
|
+
console.error("Message:", err.message);
|
|
47
|
+
console.error("Cause:", err.cause);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## `ThrottlingError`
|
|
55
|
+
|
|
56
|
+
UPS and FedEx may enforce rate limits. Shipstack throws a `ThrottlingError` when this happens.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
class ThrottlingError extends ShipstackError {
|
|
60
|
+
retryAfter?: number;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### When It Occurs
|
|
65
|
+
|
|
66
|
+
- Too many UPS tracking requests in a short period
|
|
67
|
+
- FedEx rate-limit enforcement
|
|
68
|
+
- Carrier-side retry-after signals
|
|
69
|
+
|
|
70
|
+
### Example
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
try {
|
|
74
|
+
await trackShipment(numbers, "ups", config);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (err instanceof ThrottlingError) {
|
|
77
|
+
console.log("Carrier is rate limiting. Retry after:", err.retryAfter);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Error Behavior by Workflow
|
|
85
|
+
|
|
86
|
+
### Rates
|
|
87
|
+
|
|
88
|
+
- Missing or invalid ZIP codes
|
|
89
|
+
- Unsupported service combinations
|
|
90
|
+
- Carrier downtime
|
|
91
|
+
|
|
92
|
+
### Tracking
|
|
93
|
+
|
|
94
|
+
- Invalid tracking numbers
|
|
95
|
+
- Carrier throttling
|
|
96
|
+
- Upstream authentication or network failures
|
|
97
|
+
|
|
98
|
+
### Address Validation
|
|
99
|
+
|
|
100
|
+
- Missing required fields
|
|
101
|
+
- Invalid postal codes
|
|
102
|
+
- Ambiguous addresses
|
|
103
|
+
|
|
104
|
+
### Shipments
|
|
105
|
+
|
|
106
|
+
#### Staged Shipments
|
|
107
|
+
Errors occur only if request data is incomplete or invalid.
|
|
108
|
+
|
|
109
|
+
#### Actual Shipments
|
|
110
|
+
Errors may occur due to authentication failures, invalid shipment payloads, carrier downtime, or billing issues.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Best Practices
|
|
115
|
+
|
|
116
|
+
### Always Catch `ShipstackError`
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
try {
|
|
120
|
+
await createShipment(request, config);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
if (err instanceof ShipstackError) {
|
|
123
|
+
// handle gracefully
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Log `carrier` and `cause`
|
|
129
|
+
|
|
130
|
+
These are the most useful fields for debugging carrier-specific failures.
|
|
131
|
+
|
|
132
|
+
### Do Not Expose Raw Upstream Errors to End Users
|
|
133
|
+
Prefer logging `cause` internally and returning a safe application-level message to the frontend.
|
|
134
|
+
|
|
135
|
+
Use `message` for user‑facing output.
|
|
136
|
+
Use `cause` for internal logging.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Summary
|
|
141
|
+
|
|
142
|
+
Shipstack’s error system provides:
|
|
143
|
+
|
|
144
|
+
- A unified error interface
|
|
145
|
+
- Normalized carrier errors
|
|
146
|
+
- Specialized throttling detection
|
|
147
|
+
- Consistent behavior across all workflows
|
|
148
|
+
- Strong typing for safe error handling
|
|
149
|
+
|
|
150
|
+
---
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Actual Shipment Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to use Shipstack's advanced shipment workflow.
|
|
5
|
+
* This calls the carrier API and PURCHASES a real shipping label.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT:
|
|
8
|
+
* - Only run this in a secure backend environment.
|
|
9
|
+
* - This will generate a real tracking number and label.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createShipment } from "@teralabs/shipstack";
|
|
13
|
+
import { config } from "../config.example";
|
|
14
|
+
|
|
15
|
+
async function exampleActualShipment() {
|
|
16
|
+
const shipment = await createShipment(
|
|
17
|
+
{
|
|
18
|
+
carrier: "ups",
|
|
19
|
+
serviceCode: "UPS_GROUND",
|
|
20
|
+
|
|
21
|
+
fromAddress: {
|
|
22
|
+
name: "Sender Name",
|
|
23
|
+
streetLines: ["123 Warehouse Rd"],
|
|
24
|
+
city: "Los Angeles",
|
|
25
|
+
stateOrProvinceCode: "CA",
|
|
26
|
+
postalCode: "90001",
|
|
27
|
+
countryCode: "US"
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
toAddress: {
|
|
31
|
+
name: "Customer Name",
|
|
32
|
+
streetLines: ["55 W 46th St"],
|
|
33
|
+
city: "New York",
|
|
34
|
+
stateOrProvinceCode: "NY",
|
|
35
|
+
postalCode: "10036",
|
|
36
|
+
countryCode: "US"
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
package: {
|
|
40
|
+
weightOz: 48,
|
|
41
|
+
lengthInches: 14,
|
|
42
|
+
widthInches: 10,
|
|
43
|
+
heightInches: 6
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
config
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
console.log("Carrier:", shipment.carrier);
|
|
50
|
+
console.log("Tracking Number:", shipment.trackingNumber);
|
|
51
|
+
console.log("Service Code:", shipment.serviceCode);
|
|
52
|
+
console.log("Charges:", shipment.charges);
|
|
53
|
+
|
|
54
|
+
// Label is returned as base64
|
|
55
|
+
console.log("Label Format:", shipment.label.format);
|
|
56
|
+
console.log("Label Base64 (first 100 chars):", shipment.label.base64.slice(0, 100));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
exampleActualShipment();
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic Shipstack Examples
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates:
|
|
5
|
+
* - Fetching rates
|
|
6
|
+
* - Ranking rates across carriers
|
|
7
|
+
* - Tracking shipments
|
|
8
|
+
* - Validating addresses
|
|
9
|
+
* - Using ShippingClient, ShippingManager, and the functional API
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
ShippingClient,
|
|
14
|
+
ShippingManager,
|
|
15
|
+
getRates,
|
|
16
|
+
trackShipment,
|
|
17
|
+
validateAddress
|
|
18
|
+
} from "@teralabs/shipstack";
|
|
19
|
+
|
|
20
|
+
import { config } from "../config.example";
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------
|
|
23
|
+
// Stateful API (ShippingClient + ShippingManager)
|
|
24
|
+
// ---------------------------------------------
|
|
25
|
+
|
|
26
|
+
const client = new ShippingClient(config);
|
|
27
|
+
const manager = new ShippingManager(config);
|
|
28
|
+
|
|
29
|
+
async function exampleStateful() {
|
|
30
|
+
// 1. Get Rates
|
|
31
|
+
const rates = await client.getRates({
|
|
32
|
+
carrier: "usps",
|
|
33
|
+
originZip: "90210",
|
|
34
|
+
destZip: "10001",
|
|
35
|
+
weightOz: 16,
|
|
36
|
+
lengthInches: 10,
|
|
37
|
+
widthInches: 5,
|
|
38
|
+
heightInches: 5
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
console.log("USPS Rates:", rates);
|
|
42
|
+
|
|
43
|
+
// 2. Rank Rates Across Carriers
|
|
44
|
+
const rankedRates = await manager.getRankedRates(
|
|
45
|
+
{
|
|
46
|
+
originZip: "90210",
|
|
47
|
+
destZip: "10001",
|
|
48
|
+
weightOz: 16,
|
|
49
|
+
lengthInches: 10,
|
|
50
|
+
widthInches: 5,
|
|
51
|
+
heightInches: 5
|
|
52
|
+
},
|
|
53
|
+
["usps", "fedex", "ups"]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
console.log("Ranked Rates:", rankedRates);
|
|
57
|
+
|
|
58
|
+
// 3. Track a Shipment
|
|
59
|
+
const tracking = await client.track(
|
|
60
|
+
["9400100000000000000000"],
|
|
61
|
+
"usps"
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
console.log("Tracking:", tracking);
|
|
65
|
+
|
|
66
|
+
// 4. Validate an Address
|
|
67
|
+
const address = await client.validateAddress({
|
|
68
|
+
carrier: "fedex",
|
|
69
|
+
address: {
|
|
70
|
+
streetLines: ["123 Main St"],
|
|
71
|
+
city: "New York",
|
|
72
|
+
stateOrProvinceCode: "NY",
|
|
73
|
+
postalCode: "10001",
|
|
74
|
+
countryCode: "US"
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log("Address Validation:", address);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------
|
|
82
|
+
// Functional API
|
|
83
|
+
// ---------------------------------------------
|
|
84
|
+
|
|
85
|
+
async function exampleFunctional() {
|
|
86
|
+
// 1. Get Rates
|
|
87
|
+
const rates = await getRates(
|
|
88
|
+
{
|
|
89
|
+
carrier: "ups",
|
|
90
|
+
originZip: "94103",
|
|
91
|
+
destZip: "10001",
|
|
92
|
+
weightOz: 32,
|
|
93
|
+
lengthInches: 12,
|
|
94
|
+
widthInches: 8,
|
|
95
|
+
heightInches: 6
|
|
96
|
+
},
|
|
97
|
+
config
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
console.log("UPS Rates:", rates);
|
|
101
|
+
|
|
102
|
+
// 2. Track a Shipment
|
|
103
|
+
const tracking = await trackShipment(
|
|
104
|
+
["1Z9999999999999999"],
|
|
105
|
+
"ups",
|
|
106
|
+
config
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
console.log("Tracking:", tracking);
|
|
110
|
+
|
|
111
|
+
// 3. Validate an Address
|
|
112
|
+
const address = await validateAddress(
|
|
113
|
+
{
|
|
114
|
+
carrier: "usps",
|
|
115
|
+
address: {
|
|
116
|
+
streetLines: ["1600 Amphitheatre Pkwy"],
|
|
117
|
+
city: "Mountain View",
|
|
118
|
+
stateOrProvinceCode: "CA",
|
|
119
|
+
postalCode: "94043",
|
|
120
|
+
countryCode: "US"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
config
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
console.log("Address Validation:", address);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Run examples
|
|
130
|
+
exampleStateful();
|
|
131
|
+
exampleFunctional();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getRates, type RateRequest } from "@teralabs/shipstack";
|
|
2
|
+
|
|
3
|
+
import { config } from "../config.example";
|
|
4
|
+
|
|
5
|
+
export async function POST(request: Request) {
|
|
6
|
+
const body = (await request.json()) as Partial<RateRequest>;
|
|
7
|
+
|
|
8
|
+
const rateRequest: RateRequest = {
|
|
9
|
+
carrier: body.carrier ?? "usps",
|
|
10
|
+
originZip: body.originZip ?? "90210",
|
|
11
|
+
destZip: body.destZip ?? "10001",
|
|
12
|
+
weightOz: body.weightOz ?? 16,
|
|
13
|
+
lengthInches: body.lengthInches ?? 10,
|
|
14
|
+
widthInches: body.widthInches ?? 5,
|
|
15
|
+
heightInches: body.heightInches ?? 5,
|
|
16
|
+
destCountryCode: body.destCountryCode
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const rates = await getRates(rateRequest, config);
|
|
20
|
+
|
|
21
|
+
return Response.json({ rates });
|
|
22
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Staged Shipment Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates Shipstack's safe shipment builder. It generates a carrier-specific payload without purchasing a label.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { buildShipment } from "@teralabs/shipstack";
|
|
7
|
+
import { config } from "../config.example";
|
|
8
|
+
|
|
9
|
+
async function exampleStagedShipment() {
|
|
10
|
+
const staged = await buildShipment(
|
|
11
|
+
{
|
|
12
|
+
carrier: "fedex",
|
|
13
|
+
serviceCode: "FEDEX_GROUND",
|
|
14
|
+
fromAddress: {
|
|
15
|
+
name: "Sender Name",
|
|
16
|
+
streetLines: ["123 Warehouse Rd"],
|
|
17
|
+
city: "Los Angeles",
|
|
18
|
+
stateOrProvinceCode: "CA",
|
|
19
|
+
postalCode: "90001",
|
|
20
|
+
countryCode: "US"
|
|
21
|
+
},
|
|
22
|
+
toAddress: {
|
|
23
|
+
name: "Customer Name",
|
|
24
|
+
streetLines: ["55 W 46th St"],
|
|
25
|
+
city: "New York",
|
|
26
|
+
stateOrProvinceCode: "NY",
|
|
27
|
+
postalCode: "10036",
|
|
28
|
+
countryCode: "US"
|
|
29
|
+
},
|
|
30
|
+
package: {
|
|
31
|
+
weightOz: 32,
|
|
32
|
+
lengthInches: 12,
|
|
33
|
+
widthInches: 8,
|
|
34
|
+
heightInches: 4
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
config
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log("Carrier:", staged.carrier);
|
|
41
|
+
console.log("Service Code:", staged.serviceCode);
|
|
42
|
+
console.log("Payload:", staged.payload);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exampleStagedShipment();
|
|
46
|
+
```
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Shipstack Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to Shipstack. This documentation will help you get started, integrate, and extend Shipstack in any JavaScript or TypeScript environment.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
- [What is Shipstack?](#what-is-shipstack)
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [Usage Examples](#usage-examples)
|
|
14
|
+
- [Advanced Workflows](#advanced-workflows)
|
|
15
|
+
- [Error Handling](#error-handling)
|
|
16
|
+
- [API Reference](#api-reference)
|
|
17
|
+
- [Testing](#testing)
|
|
18
|
+
- [Contributing](#contributing)
|
|
19
|
+
- [Changelog](#changelog)
|
|
20
|
+
- [License](#license)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## What is Shipstack?
|
|
25
|
+
|
|
26
|
+
Shipstack is a high‑performance, type‑safe, and framework‑agnostic shipping SDK for orchestrating logistics across USPS, FedEx, and UPS.
|
|
27
|
+
It provides a unified API for:
|
|
28
|
+
|
|
29
|
+
- Address validation
|
|
30
|
+
- Rate comparison
|
|
31
|
+
- Tracking
|
|
32
|
+
- Staged shipment generation
|
|
33
|
+
- Actual label creation (backend‑only)
|
|
34
|
+
|
|
35
|
+
Shipstack works in Node.js, Deno, Bun, Cloudflare Workers, and any modern JavaScript runtime.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- Unified orchestration for USPS, FedEx, and UPS
|
|
42
|
+
- Standardized data models for rates, tracking, and addresses
|
|
43
|
+
- Batch tracking with automatic carrier‑specific chunking
|
|
44
|
+
- Staged shipment system for safe, frontend‑compatible workflows
|
|
45
|
+
- Actual shipment creation for backend automation
|
|
46
|
+
- Auto‑generated OpenAPI clients for accuracy and compliance
|
|
47
|
+
- Fully framework‑agnostic and open source
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install @teralabs/shipstack
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { ShippingClient, ShippingManager } from "@teralabs/shipstack";
|
|
63
|
+
|
|
64
|
+
const config = {
|
|
65
|
+
environment: "sandbox",
|
|
66
|
+
usps: {
|
|
67
|
+
enabled: true,
|
|
68
|
+
clientId: "YOUR_USPS_CLIENT_ID",
|
|
69
|
+
clientSecret: "YOUR_USPS_CLIENT_SECRET",
|
|
70
|
+
baseUrl: "https://sandbox.api.usps.com"
|
|
71
|
+
},
|
|
72
|
+
fedex: {
|
|
73
|
+
enabled: true,
|
|
74
|
+
clientId: "YOUR_FEDEX_CLIENT_ID",
|
|
75
|
+
clientSecret: "YOUR_FEDEX_CLIENT_SECRET",
|
|
76
|
+
accountNumber: "YOUR_FEDEX_ACCOUNT_NUMBER"
|
|
77
|
+
},
|
|
78
|
+
ups: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
clientId: "YOUR_UPS_CLIENT_ID",
|
|
81
|
+
clientSecret: "YOUR_UPS_CLIENT_SECRET",
|
|
82
|
+
accountNumber: "YOUR_UPS_ACCOUNT_NUMBER"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const client = new ShippingClient(config);
|
|
87
|
+
const manager = new ShippingManager(config);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
Shipstack does not rely on environment variables.
|
|
95
|
+
All configuration is passed as plain objects.
|
|
96
|
+
|
|
97
|
+
See `docs/config.example.ts` for a complete template.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Usage Examples
|
|
102
|
+
|
|
103
|
+
### Get Rates
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const rates = await client.getRates({
|
|
107
|
+
carrier: "usps",
|
|
108
|
+
originZip: "90210",
|
|
109
|
+
destZip: "10001",
|
|
110
|
+
weightOz: 16,
|
|
111
|
+
lengthInches: 10,
|
|
112
|
+
widthInches: 5,
|
|
113
|
+
heightInches: 5
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Rank Rates Across Carriers
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const rankedRates = await manager.getRankedRates(
|
|
121
|
+
{
|
|
122
|
+
originZip: "90210",
|
|
123
|
+
destZip: "10001",
|
|
124
|
+
weightOz: 16,
|
|
125
|
+
lengthInches: 10,
|
|
126
|
+
widthInches: 5,
|
|
127
|
+
heightInches: 5
|
|
128
|
+
},
|
|
129
|
+
["usps", "fedex", "ups"]
|
|
130
|
+
);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Track Shipments
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const tracking = await client.track(["9400100000000000000000"], "usps");
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Validate Address
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const result = await client.validateAddress({
|
|
143
|
+
carrier: "fedex",
|
|
144
|
+
address: {
|
|
145
|
+
streetLines: ["123 Main St"],
|
|
146
|
+
city: "New York",
|
|
147
|
+
stateOrProvinceCode: "NY",
|
|
148
|
+
postalCode: "10001",
|
|
149
|
+
countryCode: "US"
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Direct Carrier Access
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { createUspsRatesClient } from "@teralabs/shipstack";
|
|
158
|
+
|
|
159
|
+
const usps = createUspsRatesClient(config.usps);
|
|
160
|
+
const raw = await usps.getRates({ ... });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Advanced Workflows
|
|
166
|
+
|
|
167
|
+
### Functional API
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { getRates, validateAddress, trackShipment } from "@teralabs/shipstack";
|
|
171
|
+
|
|
172
|
+
const rates = await getRates(
|
|
173
|
+
{
|
|
174
|
+
carrier: "usps",
|
|
175
|
+
originZip: "90210",
|
|
176
|
+
destZip: "10001",
|
|
177
|
+
weightOz: 16,
|
|
178
|
+
lengthInches: 10,
|
|
179
|
+
widthInches: 5,
|
|
180
|
+
heightInches: 5
|
|
181
|
+
},
|
|
182
|
+
config
|
|
183
|
+
);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### ShippingManager
|
|
187
|
+
|
|
188
|
+
`ShippingManager` is the class-only helper for ranked multi-carrier checkout workflows.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const ranked = await manager.getRankedRates(
|
|
192
|
+
{
|
|
193
|
+
originZip: "90210",
|
|
194
|
+
destZip: "10001",
|
|
195
|
+
weightOz: 16,
|
|
196
|
+
lengthInches: 10,
|
|
197
|
+
widthInches: 5,
|
|
198
|
+
heightInches: 5
|
|
199
|
+
},
|
|
200
|
+
["usps", "fedex", "ups"]
|
|
201
|
+
);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Staged Shipments (Safe Mode)
|
|
205
|
+
|
|
206
|
+
Generates a carrier‑specific payload without purchasing a label.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { buildShipment } from "@teralabs/shipstack";
|
|
210
|
+
|
|
211
|
+
const staged = await buildShipment({ /* ShipmentRequest */ }, config);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Actual Shipments (Backend Only)
|
|
215
|
+
|
|
216
|
+
Purchases a real label.
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { createShipment } from "@teralabs/shipstack";
|
|
220
|
+
|
|
221
|
+
const shipment = await createShipment({ /* ShipmentRequest */ }, config);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Error Handling
|
|
227
|
+
|
|
228
|
+
All errors are standardized as `ShipstackError`.
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { ShipstackError } from "@teralabs/shipstack";
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
await client.getRates({
|
|
235
|
+
carrier: "usps",
|
|
236
|
+
originZip: "90210",
|
|
237
|
+
destZip: "10001",
|
|
238
|
+
weightOz: 16,
|
|
239
|
+
lengthInches: 10,
|
|
240
|
+
widthInches: 5,
|
|
241
|
+
heightInches: 5
|
|
242
|
+
});
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (error instanceof ShipstackError) {
|
|
245
|
+
// handle
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## API Reference
|
|
253
|
+
|
|
254
|
+
See `API.md` for detailed type and method documentation.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Testing
|
|
259
|
+
|
|
260
|
+
Shipstack uses Vitest for unit and integration tests.
|
|
261
|
+
|
|
262
|
+
Run tests:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
npm run test
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Contributing
|
|
271
|
+
|
|
272
|
+
See `CONTRIBUTING.md` for guidelines.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Changelog
|
|
277
|
+
|
|
278
|
+
See `CHANGELOG.md`.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
ISC License
|
|
285
|
+
|
|
286
|
+
---
|