@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/docs/API.md ADDED
@@ -0,0 +1,176 @@
1
+ # Shipstack API Reference
2
+
3
+ This document covers the public Shipstack surface shipped through the npm entrypoint: `ShippingClient`, `ShippingManager`, the functional API, exported rate clients, and the core types.
4
+
5
+ ---
6
+
7
+ ## ShippingClient
8
+
9
+ The `ShippingClient` is the primary stateful API for direct per-carrier operations.
10
+
11
+ ### Constructor
12
+
13
+ ```ts
14
+ new ShippingClient(config: ShippingConfig)
15
+ ```
16
+
17
+ ### Methods
18
+
19
+ #### `getRates(request: RateRequest): Promise<NormalizedRate[]>`
20
+ Fetches normalized rates for the carrier specified on `request.carrier`.
21
+
22
+ #### `validateAddress(request: AddressValidationRequest): Promise<AddressValidationResult>`
23
+ Validates an address using the selected carrier.
24
+
25
+ #### `track(trackingNumbers: string | string[], carrier: Carrier): Promise<NormalizedTracking[]>`
26
+ Tracks one or more shipments with automatic batching and normalization.
27
+
28
+ #### `getBestValue(request: RateRequest): Promise<NormalizedRate | null>`
29
+ Returns the cheapest rate for the selected carrier.
30
+
31
+ #### `getFastest(request: RateRequest): Promise<NormalizedRate | null>`
32
+ Returns the fastest rate for the selected carrier.
33
+
34
+ #### `buildShipment(request: ShipmentRequest): Promise<StagedShipment>`
35
+ Builds a carrier-specific shipment payload without purchasing a label.
36
+
37
+ #### `createShipment(request: ShipmentRequest): Promise<NormalizedShipment>`
38
+ Creates an actual shipment and purchases a real label.
39
+
40
+ #### `ShippingClient.predict(trackingNumber: string): Carrier | "unknown"`
41
+ Static helper that predicts the carrier from a tracking number pattern.
42
+
43
+ ---
44
+
45
+ ## ShippingManager
46
+
47
+ `ShippingManager` is the advanced stateful helper for cross-carrier checkout workflows.
48
+
49
+ ### Constructor
50
+
51
+ ```ts
52
+ new ShippingManager(config: ShippingConfig)
53
+ ```
54
+
55
+ ### Methods
56
+
57
+ #### `getRankedRates(request: Omit<RateRequest, "carrier">, carriers: Carrier[]): Promise<NormalizedRate[]>`
58
+ Queries multiple carriers, merges the results, sorts by price, and marks `isCheapest` and `isFastest` when available.
59
+
60
+ #### `validateAddress(request: AddressValidationRequest): Promise<AddressValidationResult>`
61
+ Convenience wrapper around the address API using the manager's config.
62
+
63
+ #### `track(trackingNumbers: string | string[], carrier: Carrier): Promise<NormalizedTracking[]>`
64
+ Convenience wrapper around the tracking API using the manager's config.
65
+
66
+ ---
67
+
68
+ ## Functional API
69
+
70
+ The functional API exposes stateless helpers when you do not want to retain a client instance.
71
+
72
+ ```ts
73
+ getRates(request: RateRequest, config: ShippingConfig): Promise<NormalizedRate[]>
74
+ validateAddress(request: AddressValidationRequest, config: ShippingConfig): Promise<AddressValidationResult>
75
+ trackShipment(trackingNumbers: string | string[], carrier: Carrier, config: ShippingConfig): Promise<NormalizedTracking[]>
76
+ getBestValueRate(request: RateRequest, config: ShippingConfig): Promise<NormalizedRate | null>
77
+ getFastestService(request: RateRequest, config: ShippingConfig): Promise<NormalizedRate | null>
78
+ predictCarrier(trackingNumber: string): Carrier | "unknown"
79
+ buildShipment(request: ShipmentRequest, config: ShippingConfig): Promise<StagedShipment>
80
+ createShipment(request: ShipmentRequest, config: ShippingConfig): Promise<NormalizedShipment>
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Direct Carrier Access
86
+
87
+ Only the low-level rate clients are exported directly from the package entrypoint.
88
+
89
+ ```ts
90
+ createUspsRatesClient(config: UspsConfig)
91
+ createFedexRatesClient(config: FedexConfig)
92
+ createUpsRatesClient(config: UpsConfig)
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Core Types
98
+
99
+ ### `ShippingConfig`
100
+
101
+ ```ts
102
+ type ShippingConfig = {
103
+ environment?: "sandbox" | "production";
104
+ usps?: {
105
+ enabled: boolean;
106
+ clientId: string;
107
+ clientSecret: string;
108
+ apiKey?: string;
109
+ baseUrl?: string;
110
+ authUrl?: string;
111
+ labelsBaseUrl?: string;
112
+ };
113
+ fedex?: {
114
+ enabled: boolean;
115
+ clientId: string;
116
+ clientSecret: string;
117
+ accountNumber: string;
118
+ baseUrl?: string;
119
+ };
120
+ ups?: {
121
+ enabled: boolean;
122
+ clientId: string;
123
+ clientSecret: string;
124
+ accountNumber: string;
125
+ baseUrl?: string;
126
+ };
127
+ };
128
+ ```
129
+
130
+ ### `RateRequest`
131
+ Origin, destination, weight, and package dimensions for a single-carrier rate lookup.
132
+
133
+ ### `AddressValidationRequest`
134
+ Carrier plus a structured postal address using `streetLines`, `stateOrProvinceCode`, and `countryCode`.
135
+
136
+ ### `AddressValidationResult`
137
+ Top-level address validation response with `isValid`, optional `normalizedAddress`, and optional `messages`.
138
+
139
+ ### `NormalizedRate`
140
+ Unified rate object returned by all carriers.
141
+
142
+ ### `NormalizedTracking`
143
+ Normalized tracking status and event history.
144
+
145
+ ### `ShipmentRequest`
146
+ Carrier, service, from/to addresses, and package details required to build or purchase a shipment.
147
+
148
+ ### `StagedShipment`
149
+ Carrier-specific payload returned by `buildShipment`.
150
+
151
+ ### `NormalizedShipment`
152
+ Unified shipment result returned by `createShipment`.
153
+
154
+ ---
155
+
156
+ ## Error Types
157
+
158
+ ### `ShipstackError`
159
+ Base error type for Shipstack operations.
160
+
161
+ Properties include:
162
+
163
+ - `message`
164
+ - `carrier` (`"usps" | "fedex" | "ups"`)
165
+ - `cause`
166
+
167
+ ### `ThrottlingError`
168
+ Extends `ShipstackError` and adds `retryAfter?: number` when a carrier returns a throttling signal.
169
+
170
+ ---
171
+
172
+ ## Full Type Definitions
173
+
174
+ The published package exposes its full TypeScript surface through `dist/index.d.ts` and `dist/index.d.mts`.
175
+
176
+ ---
@@ -0,0 +1,114 @@
1
+ # Address Validation in Shipstack
2
+
3
+ Shipstack provides a unified interface for validating addresses across USPS, FedEx, and UPS.
4
+ All carrier responses are normalized into a consistent structure, regardless of the underlying API.
5
+
6
+ ---
7
+
8
+ ## Request Structure
9
+
10
+ ```ts
11
+ type AddressValidationRequest = {
12
+ carrier: "usps" | "fedex" | "ups";
13
+ address: {
14
+ streetLines: string[];
15
+ city: string;
16
+ stateOrProvinceCode: string;
17
+ postalCode: string;
18
+ countryCode: string;
19
+ };
20
+ };
21
+ ```
22
+
23
+ ### Notes
24
+
25
+ - `streetLines` supports multiple address lines.
26
+ - `countryCode` should be an ISO-3166-1 alpha-2 code such as `US`.
27
+ - USPS, FedEx, and UPS may each return different levels of correction detail, but Shipstack normalizes the top-level response shape.
28
+
29
+ ---
30
+
31
+ ## Example
32
+
33
+ ```ts
34
+ const result = await client.validateAddress({
35
+ carrier: "fedex",
36
+ address: {
37
+ streetLines: ["123 Main St"],
38
+ city: "New York",
39
+ stateOrProvinceCode: "NY",
40
+ postalCode: "10001",
41
+ countryCode: "US"
42
+ }
43
+ });
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Using the Functional API
49
+
50
+ ```ts
51
+ import { validateAddress } from "@teralabs/shipstack";
52
+
53
+ const result = await validateAddress(request, config);
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Address Validation Result
59
+
60
+ ```ts
61
+ type AddressValidationResult = {
62
+ isValid: boolean;
63
+ normalizedAddress?: {
64
+ street1: string;
65
+ street2?: string;
66
+ city: string;
67
+ state: string;
68
+ postalCode: string;
69
+ country: string;
70
+ isValid: boolean;
71
+ classification?: "RESIDENTIAL" | "COMMERCIAL" | "UNKNOWN";
72
+ isPoBox: boolean;
73
+ raw?: unknown;
74
+ };
75
+ messages?: string[];
76
+ raw?: unknown;
77
+ };
78
+ ```
79
+
80
+ ### Field Notes
81
+
82
+ - `isValid` indicates whether the carrier considers the address deliverable.
83
+ - `normalizedAddress` is included when the carrier returns a standardized address shape.
84
+ - `messages` may include warnings, suggestions, or correction notes.
85
+
86
+ ---
87
+
88
+ ## Carrier-Specific Behavior
89
+
90
+ ### USPS
91
+ - Often returns corrected addresses.
92
+ - May normalize abbreviations and formatting.
93
+
94
+ ### FedEx
95
+ - Provides detailed validation messages.
96
+ - May classify an address as residential or commercial.
97
+
98
+ ### UPS
99
+ - Uses stricter validation rules for some addresses.
100
+ - May require more complete postal information in edge cases.
101
+
102
+ ---
103
+
104
+ ## Summary
105
+
106
+ Shipstack's address validation system provides:
107
+
108
+ - A unified request format
109
+ - A normalized top-level result
110
+ - Optional corrected address data
111
+ - Carrier-specific messages when available
112
+ - Compatibility with both stateful and functional APIs
113
+
114
+ ---
@@ -0,0 +1,220 @@
1
+ # Shipstack Architecture
2
+
3
+ Shipstack is built around a layered, modular architecture designed for reliability, type‑safety, and framework‑agnostic operation.
4
+ This document explains how the system is structured internally and how each layer interacts with the others.
5
+
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ Shipstack consists of four major layers:
11
+
12
+ 1. **Generated Clients** (OpenAPI‑based, carrier‑specific)
13
+ 2. **API Layer** (request builders + converters)
14
+ 3. **Aggregators** (unified workflows)
15
+ 4. **Public API** (stateful + functional interfaces)
16
+
17
+ This structure ensures:
18
+
19
+ - Strong type‑safety
20
+ - Predictable behavior
21
+ - Easy extensibility
22
+ - Clear separation of concerns
23
+ - Zero framework assumptions
24
+
25
+ ---
26
+
27
+ ## 1. Generated Clients
28
+
29
+ Location:
30
+ ```
31
+ src/usps/generated/
32
+ src/fedex/generated/
33
+ src/ups/generated/
34
+ ```
35
+
36
+ These clients are auto‑generated from official OpenAPI specifications.
37
+
38
+ ### Responsibilities
39
+
40
+ - Define raw request/response types
41
+ - Handle low‑level HTTP calls
42
+ - Match carrier API behavior exactly
43
+ - Never contain business logic
44
+
45
+ ### Notes
46
+
47
+ - These files should never be edited manually.
48
+ - Regenerate using `npm run generate:*`.
49
+
50
+ ---
51
+
52
+ ## 2. API Layer
53
+
54
+ Location:
55
+ ```
56
+ src/usps/
57
+ src/fedex/
58
+ src/ups/
59
+ src/api/
60
+ ```
61
+
62
+ This layer wraps the generated clients and provides:
63
+
64
+ ### Request Builders
65
+ Convert normalized Shipstack requests into carrier‑specific formats.
66
+
67
+ Examples:
68
+ - `buildUspsRateRequest`
69
+ - `buildFedexShipRequest`
70
+ - `buildUpsTrackingRequest`
71
+
72
+ ### Converters
73
+ Normalize carrier responses into Shipstack’s unified types.
74
+
75
+ Examples:
76
+ - `convertUspsRateResponse`
77
+ - `convertFedexTrackingResponse`
78
+ - `convertUpsShipResponse`
79
+
80
+ ### Responsibilities
81
+
82
+ - Authentication
83
+ - Request shaping
84
+ - Response normalization
85
+ - Error mapping
86
+ - Carrier‑specific quirks
87
+
88
+ This layer ensures all carriers behave consistently from the outside.
89
+
90
+ ---
91
+
92
+ ## 3. Aggregators
93
+
94
+ Location:
95
+ ```
96
+ src/aggregator/
97
+ ```
98
+
99
+ Aggregators orchestrate multi‑step workflows and unify behavior across carriers.
100
+
101
+ ### Types of Aggregators
102
+
103
+ #### Address Aggregator
104
+ - Calls the correct carrier validation API
105
+ - Normalizes results
106
+ - Handles corrections and messages
107
+
108
+ #### Rates Aggregator
109
+ - Calls the correct carrier rate API
110
+ - Normalizes rates
111
+ - Supports ranking helpers
112
+
113
+ #### Tracking Aggregator
114
+ - Handles batching (USPS 35, FedEx 30)
115
+ - Handles UPS single‑inquiry limits
116
+ - Normalizes tracking events
117
+
118
+ #### Shipment Aggregator
119
+ - **Staged mode** → builds carrier payloads
120
+ - **Actual mode** → purchases labels
121
+ - Normalizes shipment results
122
+
123
+ ### Responsibilities
124
+
125
+ - Multi‑carrier orchestration
126
+ - Concurrency control
127
+ - Error handling
128
+ - Workflow consistency
129
+
130
+ Aggregators are the “brains” of Shipstack.
131
+
132
+ ---
133
+
134
+ ## 4. Public API
135
+
136
+ Shipstack exposes two public interfaces:
137
+
138
+ ### Stateful API
139
+
140
+ ```ts
141
+ const client = new ShippingClient(config);
142
+ const manager = new ShippingManager(config);
143
+
144
+ client.getRates(...)
145
+ client.track(...)
146
+ client.buildShipment(...)
147
+ client.createShipment(...)
148
+ manager.getRankedRates(...)
149
+ ```
150
+
151
+ ### Functional API
152
+
153
+ ```ts
154
+ getRates(request, config)
155
+ validateAddress(request, config)
156
+ trackShipment(numbers, carrier, config)
157
+ buildShipment(request, config)
158
+ createShipment(request, config)
159
+ ```
160
+
161
+ Both APIs call into the same aggregator layer.
162
+
163
+ ---
164
+
165
+ ## Data Flow
166
+
167
+ Here’s how a typical request moves through the system:
168
+
169
+ ```
170
+ User Request
171
+
172
+ Public API (stateful or functional)
173
+
174
+ Aggregator (rates, tracking, address, shipment)
175
+
176
+ API Layer (request builder + converter)
177
+
178
+ Generated Client (raw HTTP)
179
+
180
+ Carrier API (USPS/FedEx/UPS)
181
+
182
+ Response normalized and returned
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Why This Architecture?
188
+
189
+ ### Predictability
190
+ Every carrier behaves differently — Shipstack makes them behave the same.
191
+
192
+ ### Extensibility
193
+ Adding a new carrier only requires:
194
+ - Generated client
195
+ - Request builders
196
+ - Converters
197
+ - Aggregator hooks
198
+
199
+ ### Safety
200
+ Staged shipments allow safe, frontend‑compatible workflows.
201
+
202
+ ### Performance
203
+ Batching, concurrency limits, and request shaping ensure efficient API usage.
204
+
205
+ ### Portability
206
+ No reliance on Node.js APIs — works in any JS runtime.
207
+
208
+ ---
209
+
210
+ ## Summary
211
+
212
+ Shipstack’s architecture provides:
213
+
214
+ - A clean separation between raw carrier APIs and normalized workflows
215
+ - A consistent developer experience across USPS, FedEx, and UPS
216
+ - Safe and advanced shipment modes
217
+ - Strong type‑safety at every layer
218
+ - A runtime‑agnostic design suitable for modern web platforms
219
+
220
+ ---
@@ -0,0 +1,180 @@
1
+ # Carrier‑Specific Behavior in Shipstack
2
+
3
+ Shipstack provides a unified API across USPS, FedEx, and UPS — but each carrier has unique rules, quirks, and constraints.
4
+ This document explains those differences and how Shipstack handles them internally.
5
+
6
+ ---
7
+
8
+ # Overview
9
+
10
+ Shipstack normalizes:
11
+
12
+ - Rate structures
13
+ - Tracking events
14
+ - Address validation responses
15
+ - Shipment payloads
16
+ - Error formats
17
+
18
+ But each carrier still has its own behaviors that developers should understand.
19
+
20
+ This guide covers:
21
+
22
+ - USPS
23
+ - FedEx
24
+ - UPS
25
+ - How Shipstack smooths out inconsistencies
26
+
27
+ ---
28
+
29
+ # USPS
30
+
31
+ USPS is the most rigid and rule‑driven carrier.
32
+ It has strict formatting requirements and limited metadata in some APIs.
33
+
34
+ ## Strengths
35
+
36
+ - Excellent address correction
37
+ - Fast, lightweight APIs
38
+ - Strong support for small parcels
39
+ - Predictable service codes
40
+
41
+ ## Limitations
42
+
43
+ - Tracking events may lack timestamps
44
+ - Delivery estimates are not always provided
45
+ - Bulk tracking limit: **35 items**
46
+ - Some services ignore dimensions
47
+
48
+ ## Shipstack Behavior
49
+
50
+ - Automatically chunks tracking requests into 35‑item batches
51
+ - Normalizes missing timestamps to `null`
52
+ - Maps USPS service names to consistent `serviceCode` values
53
+ - Normalizes address corrections into a unified structure
54
+
55
+ ---
56
+
57
+ # FedEx
58
+
59
+ FedEx provides the richest data across all carriers, especially for tracking and shipments.
60
+
61
+ ## Strengths
62
+
63
+ - Detailed tracking events
64
+ - Reliable delivery estimates
65
+ - Strong international support
66
+ - Rich shipment metadata
67
+
68
+ ## Limitations
69
+
70
+ - Requires `accountNumber` for almost all operations
71
+ - Rate requests may require dimensions even for small parcels
72
+ - Tracking batch limit: **30 items**
73
+
74
+ ## Shipstack Behavior
75
+
76
+ - Automatically chunks tracking requests into 30‑item batches
77
+ - Normalizes FedEx’s verbose response structure
78
+ - Maps FedEx service codes to unified names
79
+ - Converts FedEx timestamps into ISO strings
80
+
81
+ ---
82
+
83
+ # UPS
84
+
85
+ UPS is the strictest carrier in terms of authentication and rate limiting.
86
+
87
+ ## Strengths
88
+
89
+ - Reliable tracking
90
+ - Strong commercial shipping support
91
+ - Good dimensional pricing
92
+
93
+ ## Limitations
94
+
95
+ - Tracking is **single‑inquiry only**
96
+ - Aggressive rate limiting
97
+ - Requires `accountNumber` for most services
98
+ - OAuth tokens expire quickly
99
+
100
+ ## Shipstack Behavior
101
+
102
+ - Automatically throttles UPS tracking to avoid lockouts
103
+ - Ensures single‑inquiry tracking is respected
104
+ - Normalizes UPS’s deeply nested response structure
105
+ - Converts UPS error codes into `ShipstackError`
106
+
107
+ ---
108
+
109
+ # Comparison Table
110
+
111
+ | Feature | USPS | FedEx | UPS |
112
+ |--------|------|--------|------|
113
+ | Tracking batch size | 35 | 30 | 1 |
114
+ | Address correction | Strong | Moderate | Moderate |
115
+ | Delivery estimates | Inconsistent | Strong | Strong |
116
+ | Requires account number | No | Yes | Yes |
117
+ | Rate limiting | Low | Moderate | High |
118
+ | International support | Limited | Strong | Strong |
119
+
120
+ ---
121
+
122
+ # How Shipstack Normalizes Carrier Differences
123
+
124
+ Shipstack smooths out inconsistencies by:
125
+
126
+ ### 1. Normalizing Field Names
127
+ All carriers return:
128
+
129
+ - `serviceCode`
130
+ - `serviceName`
131
+ - `deliveryDays`
132
+ - `cost.amount`
133
+ - `cost.currency`
134
+
135
+ ### 2. Normalizing Tracking Events
136
+ Regardless of carrier, tracking events become:
137
+
138
+ ```ts
139
+ {
140
+ description: string;
141
+ dateTime: string;
142
+ city?: string;
143
+ stateOrProvinceCode?: string;
144
+ postalCode?: string;
145
+ countryCode?: string;
146
+ }
147
+ ```
148
+
149
+ ### 3. Handling Concurrency and Batching
150
+ Shipstack automatically:
151
+
152
+ - Chunks USPS and FedEx tracking
153
+ - Throttles UPS tracking
154
+ - Ensures safe parallel execution
155
+
156
+ ### 4. Mapping Errors
157
+ All carrier errors become `ShipstackError` with:
158
+
159
+ - `carrier`
160
+ - `message`
161
+ - `cause`
162
+
163
+ ### 5. Unifying Shipment Workflows
164
+ Both staged and actual shipments use the same request structure, regardless of carrier.
165
+
166
+ ---
167
+
168
+ # Summary
169
+
170
+ Shipstack abstracts away the differences between USPS, FedEx, and UPS by:
171
+
172
+ - Normalizing data
173
+ - Handling batching and throttling
174
+ - Mapping errors
175
+ - Providing consistent request/response structures
176
+ - Supporting both staged and actual shipments
177
+
178
+ This allows you to build multi‑carrier workflows without worrying about carrier‑specific quirks.
179
+
180
+ ---
@@ -0,0 +1,32 @@
1
+ import type { ShippingConfig } from "@teralabs/shipstack";
2
+
3
+ /**
4
+ * Shipstack Example Configuration
5
+ *
6
+ * This file demonstrates the configuration structure required
7
+ * for USPS, FedEx, and UPS. All values shown here are placeholders.
8
+ */
9
+
10
+ export const config: ShippingConfig = {
11
+ environment: "sandbox",
12
+ usps: {
13
+ enabled: true,
14
+ clientId: "YOUR_USPS_CLIENT_ID",
15
+ clientSecret: "YOUR_USPS_CLIENT_SECRET",
16
+ baseUrl: "https://sandbox.api.usps.com"
17
+ },
18
+
19
+ fedex: {
20
+ enabled: true,
21
+ clientId: "YOUR_FEDEX_CLIENT_ID",
22
+ clientSecret: "YOUR_FEDEX_CLIENT_SECRET",
23
+ accountNumber: "YOUR_FEDEX_ACCOUNT_NUMBER"
24
+ },
25
+
26
+ ups: {
27
+ enabled: true,
28
+ clientId: "YOUR_UPS_CLIENT_ID",
29
+ clientSecret: "YOUR_UPS_CLIENT_SECRET",
30
+ accountNumber: "YOUR_UPS_ACCOUNT_NUMBER"
31
+ }
32
+ };